Plasma
applet.cpp
Go to the documentation of this file.
00001 /* 00002 * Copyright 2005 by Aaron Seigo <aseigo@kde.org> 00003 * Copyright 2007 by Riccardo Iaconelli <riccardo@kde.org> 00004 * Copyright 2008 by Ménard Alexis <darktears31@gmail.com> 00005 * Copyright (c) 2009 Chani Armitage <chani@kde.org> 00006 * 00007 * This program is free software; you can redistribute it and/or modify 00008 * it under the terms of the GNU Library General Public License as 00009 * published by the Free Software Foundation; either version 2, or 00010 * (at your option) any later version. 00011 * 00012 * This program is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00015 * GNU General Public License for more details 00016 * 00017 * You should have received a copy of the GNU Library General Public 00018 * License along with this program; if not, write to the 00019 * Free Software Foundation, Inc., 00020 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00021 */ 00022 00023 #include "applet.h" 00024 #include "private/applet_p.h" 00025 00026 #include "config-plasma.h" 00027 00028 #include <plasma/animations/animation.h> 00029 00030 #include <cmath> 00031 #include <limits> 00032 00033 #include <QApplication> 00034 #include <QEvent> 00035 #include <QFile> 00036 #include <QGraphicsGridLayout> 00037 #include <QGraphicsSceneMouseEvent> 00038 #include <QGraphicsView> 00039 #include <QHostInfo> 00040 #include <QLabel> 00041 #include <QList> 00042 #include <QGraphicsLinearLayout> 00043 #include <QPainter> 00044 #include <QRegExp> 00045 #include <QSize> 00046 #include <QStyleOptionGraphicsItem> 00047 #include <QTextDocument> 00048 #include <QUiLoader> 00049 #include <QVBoxLayout> 00050 #include <QWidget> 00051 00052 #include <kaction.h> 00053 #include <kactioncollection.h> 00054 #include <kauthorized.h> 00055 #include <kcolorscheme.h> 00056 #include <kdialog.h> 00057 #include <kicon.h> 00058 #include <kiconloader.h> 00059 #include <kkeysequencewidget.h> 00060 #include <kplugininfo.h> 00061 #include <kstandarddirs.h> 00062 #include <kservice.h> 00063 #include <kservicetypetrader.h> 00064 #include <kshortcut.h> 00065 #include <kwindowsystem.h> 00066 #include <kpushbutton.h> 00067 00068 #ifndef PLASMA_NO_SOLID 00069 #include <solid/powermanagement.h> 00070 #endif 00071 00072 #include "abstracttoolbox.h" 00073 #include "authorizationmanager.h" 00074 #include "authorizationrule.h" 00075 #include "configloader.h" 00076 #include "containment.h" 00077 #include "corona.h" 00078 #include "dataenginemanager.h" 00079 #include "dialog.h" 00080 #include "extenders/extender.h" 00081 #include "extenders/extenderitem.h" 00082 #include "package.h" 00083 #include "plasma.h" 00084 #include "scripting/appletscript.h" 00085 #include "svg.h" 00086 #include "framesvg.h" 00087 #include "popupapplet.h" 00088 #include "private/applethandle_p.h" 00089 #include "private/extenderitem_p.h" 00090 #include "private/framesvg_p.h" 00091 #include "theme.h" 00092 #include "view.h" 00093 #include "widgets/iconwidget.h" 00094 #include "widgets/label.h" 00095 #include "widgets/pushbutton.h" 00096 #include "widgets/busywidget.h" 00097 #include "tooltipmanager.h" 00098 #include "wallpaper.h" 00099 #include "paintutils.h" 00100 #include "abstractdialogmanager.h" 00101 #include "pluginloader.h" 00102 00103 #include "private/associatedapplicationmanager_p.h" 00104 #include "private/authorizationmanager_p.h" 00105 #include "private/containment_p.h" 00106 #include "private/extenderapplet_p.h" 00107 #include "private/package_p.h" 00108 #include "private/packages_p.h" 00109 #include "private/plasmoidservice_p.h" 00110 #include "private/popupapplet_p.h" 00111 #include "private/remotedataengine_p.h" 00112 #include "private/service_p.h" 00113 #include "ui_publish.h" 00114 00115 00116 namespace Plasma 00117 { 00118 00119 Applet::Applet(const KPluginInfo &info, QGraphicsItem *parent, uint appletId) 00120 : QGraphicsWidget(parent), 00121 d(new AppletPrivate(KService::Ptr(), &info, appletId, this)) 00122 { 00123 // WARNING: do not access config() OR globalConfig() in this method! 00124 // that requires a scene, which is not available at this point 00125 d->init(); 00126 } 00127 00128 Applet::Applet(QGraphicsItem *parent, const QString &serviceID, uint appletId) 00129 : QGraphicsWidget(parent), 00130 d(new AppletPrivate(KService::serviceByStorageId(serviceID), 0, appletId, this)) 00131 { 00132 // WARNING: do not access config() OR globalConfig() in this method! 00133 // that requires a scene, which is not available at this point 00134 d->init(); 00135 } 00136 00137 Applet::Applet(QGraphicsItem *parent, 00138 const QString &serviceID, 00139 uint appletId, 00140 const QVariantList &args) 00141 : QGraphicsWidget(parent), 00142 d(new AppletPrivate(KService::serviceByStorageId(serviceID), 0, appletId, this)) 00143 { 00144 // WARNING: do not access config() OR globalConfig() in this method! 00145 // that requires a scene, which is not available at this point 00146 00147 QVariantList &mutableArgs = const_cast<QVariantList &>(args); 00148 if (!mutableArgs.isEmpty()) { 00149 mutableArgs.removeFirst(); 00150 00151 if (!mutableArgs.isEmpty()) { 00152 mutableArgs.removeFirst(); 00153 } 00154 } 00155 00156 d->args = mutableArgs; 00157 00158 d->init(); 00159 } 00160 00161 Applet::Applet(QObject *parentObject, const QVariantList &args) 00162 : QGraphicsWidget(0), 00163 d(new AppletPrivate( 00164 KService::serviceByStorageId(args.count() > 0 ? args[0].toString() : QString()), 0, 00165 args.count() > 1 ? args[1].toInt() : 0, this)) 00166 { 00167 // now remove those first two items since those are managed by Applet and subclasses shouldn't 00168 // need to worry about them. yes, it violates the constness of this var, but it lets us add 00169 // or remove items later while applets can just pretend that their args always start at 0 00170 QVariantList &mutableArgs = const_cast<QVariantList &>(args); 00171 if (!mutableArgs.isEmpty()) { 00172 mutableArgs.removeFirst(); 00173 00174 if (!mutableArgs.isEmpty()) { 00175 mutableArgs.removeFirst(); 00176 } 00177 } 00178 00179 d->args = mutableArgs; 00180 00181 setParent(parentObject); 00182 00183 // WARNING: do not access config() OR globalConfig() in this method! 00184 // that requires a scene, which is not available at this point 00185 d->init(); 00186 00187 // the brain damage seen in the initialization list is due to the 00188 // inflexibility of KService::createInstance 00189 } 00190 00191 Applet::Applet(const QString &packagePath, uint appletId, const QVariantList &args) 00192 : QGraphicsWidget(0), 00193 d(new AppletPrivate(KService::Ptr(new KService(packagePath + "/metadata.desktop")), 0, appletId, this)) 00194 { 00195 Q_UNUSED(args) // FIXME? 00196 d->init(packagePath); 00197 } 00198 00199 Applet::~Applet() 00200 { 00201 //let people know that i will die 00202 emit appletDestroyed(this); 00203 00204 if (!d->transient && d->extender) { 00205 //This would probably be nicer if it was located in extender. But in it's dtor, this won't 00206 //work since when that get's called, the applet's config() isn't accessible anymore. (same 00207 //problem with calling saveState(). Doing this in saveState() might be a possibility, but 00208 //that would require every extender savestate implementation to call it's parent function, 00209 //which isn't very nice. 00210 d->extender.data()->saveState(); 00211 00212 foreach (ExtenderItem *item, d->extender.data()->attachedItems()) { 00213 if (item->autoExpireDelay()) { 00214 //destroy temporary extender items, or items that aren't detached, so their 00215 //configuration won't linger after a plasma restart. 00216 item->destroy(); 00217 } 00218 } 00219 } 00220 00221 // clean up our config dialog, if any 00222 delete KConfigDialog::exists(d->configDialogId()); 00223 delete d; 00224 } 00225 00226 PackageStructure::Ptr Applet::packageStructure() 00227 { 00228 if (!AppletPrivate::packageStructure) { 00229 AppletPrivate::packageStructure = new PlasmoidPackage(); 00230 } 00231 00232 return AppletPrivate::packageStructure; 00233 } 00234 00235 void Applet::init() 00236 { 00237 setFlag(ItemIsMovable, true); 00238 if (d->script) { 00239 d->setupScriptSupport(); 00240 00241 if (!d->script->init() && !d->failed) { 00242 setFailedToLaunch(true, i18n("Script initialization failed")); 00243 } 00244 } 00245 } 00246 00247 uint Applet::id() const 00248 { 00249 return d->appletId; 00250 } 00251 00252 void Applet::save(KConfigGroup &g) const 00253 { 00254 if (d->transient) { 00255 return; 00256 } 00257 00258 KConfigGroup group = g; 00259 if (!group.isValid()) { 00260 group = *d->mainConfigGroup(); 00261 } 00262 00263 //kDebug() << "saving to" << group.name(); 00264 // we call the dptr member directly for locked since isImmutable() 00265 // also checks kiosk and parent containers 00266 group.writeEntry("immutability", (int)d->immutability); 00267 group.writeEntry("plugin", pluginName()); 00268 00269 group.writeEntry("geometry", geometry()); 00270 group.writeEntry("zvalue", zValue()); 00271 00272 if (!d->started) { 00273 return; 00274 } 00275 00276 //FIXME: for containments, we need to have some special values here w/regards to 00277 // screen affinity (e.g. "bottom of screen 0") 00278 //kDebug() << pluginName() << "geometry is" << geometry() 00279 // << "pos is" << pos() << "bounding rect is" << boundingRect(); 00280 if (transform() == QTransform()) { 00281 group.deleteEntry("transform"); 00282 } else { 00283 QList<qreal> m; 00284 QTransform t = transform(); 00285 m << t.m11() << t.m12() << t.m13() << t.m21() << t.m22() << t.m23() << t.m31() << t.m32() << t.m33(); 00286 group.writeEntry("transform", m); 00287 //group.writeEntry("transform", transformToString(transform())); 00288 } 00289 00290 KConfigGroup appletConfigGroup(&group, "Configuration"); 00291 saveState(appletConfigGroup); 00292 00293 if (d->configLoader) { 00294 // we're saving so we know its changed, we don't need or want the configChanged 00295 // signal bubbling up at this point due to that 00296 disconnect(d->configLoader, SIGNAL(configChanged()), this, SLOT(configChanged())); 00297 d->configLoader->writeConfig(); 00298 connect(d->configLoader, SIGNAL(configChanged()), this, SLOT(configChanged())); 00299 } 00300 } 00301 00302 void Applet::restore(KConfigGroup &group) 00303 { 00304 QList<qreal> m = group.readEntry("transform", QList<qreal>()); 00305 if (m.count() == 9) { 00306 QTransform t(m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]); 00307 setTransform(t); 00308 } 00309 00310 qreal z = group.readEntry("zvalue", 0); 00311 00312 if (z >= AppletPrivate::s_maxZValue) { 00313 AppletPrivate::s_maxZValue = z; 00314 } 00315 00316 if (z > 0) { 00317 setZValue(z); 00318 } 00319 00320 setImmutability((ImmutabilityType)group.readEntry("immutability", (int)Mutable)); 00321 00322 QRectF geom = group.readEntry("geometry", QRectF()); 00323 if (geom.isValid()) { 00324 setGeometry(geom); 00325 } 00326 00327 KConfigGroup shortcutConfig(&group, "Shortcuts"); 00328 QString shortcutText = shortcutConfig.readEntryUntranslated("global", QString()); 00329 if (!shortcutText.isEmpty()) { 00330 setGlobalShortcut(KShortcut(shortcutText)); 00331 /* 00332 kDebug() << "got global shortcut for" << name() << "of" << QKeySequence(shortcutText); 00333 kDebug() << "set to" << d->activationAction->objectName() 00334 << d->activationAction->globalShortcut().primary(); 00335 */ 00336 } 00337 00338 // local shortcut, if any 00339 //TODO: implement; the shortcut will need to be registered with the containment 00340 /* 00341 #include "accessmanager.h" 00342 #include "private/plasmoidservice_p.h" 00343 #include "authorizationmanager.h" 00344 #include "authorizationmanager.h" 00345 shortcutText = shortcutConfig.readEntryUntranslated("local", QString()); 00346 if (!shortcutText.isEmpty()) { 00347 //TODO: implement; the shortcut 00348 } 00349 */ 00350 } 00351 00352 void AppletPrivate::setFocus() 00353 { 00354 //kDebug() << "setting focus"; 00355 q->setFocus(Qt::ShortcutFocusReason); 00356 } 00357 00358 void Applet::setFailedToLaunch(bool failed, const QString &reason) 00359 { 00360 if (d->failed == failed) { 00361 if (failed && !reason.isEmpty()) { 00362 foreach (QGraphicsItem *item, QGraphicsItem::children()) { 00363 Label *l = dynamic_cast<Label *>(item); 00364 if (l) { 00365 l->setText(d->visibleFailureText(reason)); 00366 } 00367 } 00368 } 00369 return; 00370 } 00371 00372 d->failed = failed; 00373 prepareGeometryChange(); 00374 00375 foreach (QGraphicsItem *item, childItems()) { 00376 if (!dynamic_cast<AppletHandle *>(item)) { 00377 delete item; 00378 } 00379 } 00380 00381 d->messageOverlay = 0; 00382 if (d->messageDialog) { 00383 d->messageDialog.data()->deleteLater(); 00384 d->messageDialog.clear(); 00385 } 00386 00387 setLayout(0); 00388 00389 if (failed) { 00390 setBackgroundHints(d->backgroundHints|StandardBackground); 00391 00392 QGraphicsLinearLayout *failureLayout = new QGraphicsLinearLayout(this); 00393 failureLayout->setContentsMargins(0, 0, 0, 0); 00394 00395 IconWidget *failureIcon = new IconWidget(this); 00396 failureIcon->setIcon(KIcon("dialog-error")); 00397 failureLayout->addItem(failureIcon); 00398 00399 Label *failureWidget = new Plasma::Label(this); 00400 failureWidget->setText(d->visibleFailureText(reason)); 00401 QLabel *label = failureWidget->nativeWidget(); 00402 label->setWordWrap(true); 00403 failureLayout->addItem(failureWidget); 00404 00405 Plasma::ToolTipManager::self()->registerWidget(failureIcon); 00406 Plasma::ToolTipContent data(i18n("Unable to load the widget"), reason, 00407 KIcon("dialog-error")); 00408 Plasma::ToolTipManager::self()->setContent(failureIcon, data); 00409 00410 setLayout(failureLayout); 00411 resize(300, 250); 00412 d->background->resizeFrame(geometry().size()); 00413 } 00414 00415 update(); 00416 } 00417 00418 void Applet::saveState(KConfigGroup &group) const 00419 { 00420 if (d->script) { 00421 emit d->script->saveState(group); 00422 } 00423 00424 if (group.config()->name() != config().config()->name()) { 00425 // we're being saved to a different file! 00426 // let's just copy the current values in our configuration over 00427 KConfigGroup c = config(); 00428 c.copyTo(&group); 00429 } 00430 } 00431 00432 KConfigGroup Applet::config(const QString &group) const 00433 { 00434 if (d->transient) { 00435 return KConfigGroup(KGlobal::config(), "PlasmaTransientsConfig"); 00436 } 00437 00438 KConfigGroup cg = config(); 00439 return KConfigGroup(&cg, group); 00440 } 00441 00442 KConfigGroup Applet::config() const 00443 { 00444 if (d->transient) { 00445 return KConfigGroup(KGlobal::config(), "PlasmaTransientsConfig"); 00446 } 00447 00448 if (d->isContainment) { 00449 return *(d->mainConfigGroup()); 00450 } 00451 00452 return KConfigGroup(d->mainConfigGroup(), "Configuration"); 00453 } 00454 00455 KConfigGroup Applet::globalConfig() const 00456 { 00457 KConfigGroup globalAppletConfig; 00458 QString group = isContainment() ? "ContainmentGlobals" : "AppletGlobals"; 00459 00460 Corona *corona = qobject_cast<Corona*>(scene()); 00461 if (corona) { 00462 KSharedConfig::Ptr coronaConfig = corona->config(); 00463 globalAppletConfig = KConfigGroup(coronaConfig, group); 00464 } else { 00465 globalAppletConfig = KConfigGroup(KGlobal::config(), group); 00466 } 00467 00468 return KConfigGroup(&globalAppletConfig, d->globalName()); 00469 } 00470 00471 void Applet::destroy() 00472 { 00473 if (immutability() != Mutable || d->transient || !d->started) { 00474 return; //don't double delete 00475 } 00476 00477 d->transient = true; 00478 00479 if (isContainment()) { 00480 d->cleanUpAndDelete(); 00481 } else { 00482 Animation *zoomAnim = Plasma::Animator::create(Plasma::Animator::ZoomAnimation); 00483 connect(zoomAnim, SIGNAL(finished()), this, SLOT(cleanUpAndDelete())); 00484 zoomAnim->setTargetWidget(this); 00485 zoomAnim->start(); 00486 } 00487 } 00488 00489 bool Applet::destroyed() const 00490 { 00491 return d->transient; 00492 } 00493 00494 void AppletPrivate::selectItemToDestroy() 00495 { 00496 //FIXME: this will not work nicely with multiple screens and being zoomed out! 00497 if (isContainment) { 00498 QGraphicsView *view = q->view(); 00499 if (view && view->transform().isScaling() && 00500 q->scene()->focusItem() != q) { 00501 QGraphicsItem *focus = q->scene()->focusItem(); 00502 00503 if (focus) { 00504 Containment *toDestroy = dynamic_cast<Containment*>(focus->topLevelItem()); 00505 00506 if (toDestroy) { 00507 toDestroy->destroy(); 00508 return; 00509 } 00510 } 00511 } 00512 } 00513 00514 q->destroy(); 00515 } 00516 00517 void AppletPrivate::updateRect(const QRectF &rect) 00518 { 00519 q->update(rect); 00520 } 00521 00522 void AppletPrivate::cleanUpAndDelete() 00523 { 00524 //kDebug() << "???????????????? DESTROYING APPLET" << q->name() << q->scene() << " ???????????????????????????"; 00525 QGraphicsWidget *parent = dynamic_cast<QGraphicsWidget *>(q->parentItem()); 00526 //it probably won't matter, but right now if there are applethandles, *they* are the parent. 00527 //not the containment. 00528 00529 //is the applet in a containment and does the containment have a layout? 00530 //if yes, we remove the applet in the layout 00531 if (parent && parent->layout()) { 00532 QGraphicsLayout *l = parent->layout(); 00533 for (int i = 0; i < l->count(); ++i) { 00534 if (q == l->itemAt(i)) { 00535 l->removeAt(i); 00536 break; 00537 } 00538 } 00539 } 00540 00541 if (configLoader) { 00542 configLoader->setDefaults(); 00543 } 00544 00545 resetConfigurationObject(); 00546 00547 if (q->scene()) { 00548 if (isContainment) { 00549 // prematurely emit our destruction if we are a Containment, 00550 // giving Corona a chance to remove this Containment from its collection 00551 emit q->QObject::destroyed(q); 00552 } 00553 00554 q->scene()->removeItem(q); 00555 } 00556 00557 q->deleteLater(); 00558 } 00559 00560 void AppletPrivate::createMessageOverlay(bool usePopup) 00561 { 00562 if (messageOverlay) { 00563 qDeleteAll(messageOverlay->children()); 00564 messageOverlay->setLayout(0); 00565 } 00566 00567 PopupApplet *popup = qobject_cast<Plasma::PopupApplet*>(q); 00568 00569 if (!messageOverlay) { 00570 if (usePopup && popup) { 00571 if (popup->widget()) { 00572 messageOverlayProxy = new QGraphicsProxyWidget(q); 00573 messageOverlayProxy->setWidget(popup->widget()); 00574 messageOverlay = new AppletOverlayWidget(messageOverlayProxy); 00575 } else if (popup->graphicsWidget() && 00576 popup->graphicsWidget() != extender.data()) { 00577 messageOverlay = new AppletOverlayWidget(popup->graphicsWidget()); 00578 } 00579 } 00580 00581 if (!messageOverlay) { 00582 messageOverlay = new AppletOverlayWidget(q); 00583 } 00584 } 00585 00586 positionMessageOverlay(); 00587 } 00588 00589 void AppletPrivate::positionMessageOverlay() 00590 { 00591 if (!messageOverlay) { 00592 return; 00593 } 00594 00595 PopupApplet *popup = qobject_cast<Plasma::PopupApplet*>(q); 00596 const bool usePopup = popup && (messageOverlay->parentItem() != q); 00597 QGraphicsItem *topItem = q; 00598 00599 if (usePopup && popup->widget()) { 00600 // popupapplet with widget() 00601 topItem = popup->d->proxy.data(); 00602 messageOverlay->setGeometry(popup->widget()->contentsRect()); 00603 } else if (usePopup && popup->graphicsWidget() && popup->graphicsWidget() != extender.data()) { 00604 // popupapplet with graphicsWidget() 00605 topItem = popup->graphicsWidget(); 00606 QGraphicsWidget *w = dynamic_cast<QGraphicsWidget *>(topItem); 00607 messageOverlay->setGeometry(w ? w->contentsRect() : topItem->boundingRect()); 00608 } else { 00609 // normal applet 00610 messageOverlay->setGeometry(q->contentsRect()); 00611 } 00612 00613 // raise the overlay above all the other children! 00614 int zValue = 100; 00615 foreach (QGraphicsItem *child, topItem->children()) { 00616 if (child->zValue() > zValue) { 00617 zValue = child->zValue() + 1; 00618 } 00619 } 00620 messageOverlay->setZValue(zValue); 00621 } 00622 00623 void AppletPrivate::destroyMessageOverlay() 00624 { 00625 if (messageDialog) { 00626 messageDialog.data()->animatedHide(Plasma::locationToInverseDirection(q->location())); 00627 //messageDialog.data()->deleteLater(); 00628 messageDialog.clear(); 00629 } 00630 00631 if (!messageOverlay) { 00632 return; 00633 } 00634 00635 messageOverlay->destroy(); 00636 messageOverlay = 0; 00637 00638 if (messageOverlayProxy) { 00639 messageOverlayProxy->setWidget(0); 00640 delete messageOverlayProxy; 00641 messageOverlayProxy = 0; 00642 } 00643 00644 MessageButton buttonCode = ButtonNo; 00645 //find out if we're disappearing because of a button press 00646 PushButton *button = qobject_cast<PushButton *>(q->sender()); 00647 if (button) { 00648 if (button->text() == i18n("&Ok")) { 00649 buttonCode = ButtonOk; 00650 } 00651 if (button->text() == i18n("&Yes")) { 00652 buttonCode = ButtonYes; 00653 } 00654 if (button->text() == i18n("&No")) { 00655 buttonCode = ButtonNo; 00656 } 00657 if (button->text() == i18n("&Cancel")) { 00658 buttonCode = ButtonCancel; 00659 } 00660 00661 emit q->messageButtonPressed(buttonCode); 00662 } else if (q->sender() == messageOverlay) { 00663 emit q->messageButtonPressed(ButtonCancel); 00664 } 00665 } 00666 00667 ConfigLoader *Applet::configScheme() const 00668 { 00669 return d->configLoader; 00670 } 00671 00672 DataEngine *Applet::dataEngine(const QString &name) const 00673 { 00674 if (!d->remoteLocation.isEmpty()) { 00675 return d->remoteDataEngine(KUrl(d->remoteLocation), name); 00676 } else if (!package() || package()->metadata().remoteLocation().isEmpty()) { 00677 return d->dataEngine(name); 00678 } else { 00679 return d->remoteDataEngine(KUrl(package()->metadata().remoteLocation()), name); 00680 } 00681 } 00682 00683 const Package *Applet::package() const 00684 { 00685 return d->package; 00686 } 00687 00688 QGraphicsView *Applet::view() const 00689 { 00690 // It's assumed that we won't be visible on more than one view here. 00691 // Anything that actually needs view() should only really care about 00692 // one of them anyway though. 00693 if (!scene()) { 00694 return 0; 00695 } 00696 00697 QGraphicsView *found = 0; 00698 QGraphicsView *possibleFind = 0; 00699 //kDebug() << "looking through" << scene()->views().count() << "views"; 00700 foreach (QGraphicsView *view, scene()->views()) { 00701 //kDebug() << " checking" << view << view->sceneRect() 00702 // << "against" << sceneBoundingRect() << scenePos(); 00703 if (view->sceneRect().intersects(sceneBoundingRect()) || 00704 view->sceneRect().contains(scenePos())) { 00705 //kDebug() << " found something!" << view->isActiveWindow(); 00706 if (view->isActiveWindow()) { 00707 found = view; 00708 } else { 00709 possibleFind = view; 00710 } 00711 } 00712 } 00713 00714 return found ? found : possibleFind; 00715 } 00716 00717 QRectF Applet::mapFromView(const QGraphicsView *view, const QRect &rect) const 00718 { 00719 // Why is this adjustment needed? Qt calculation error? 00720 return mapFromScene(view->mapToScene(rect)).boundingRect().adjusted(0, 0, 1, 1); 00721 } 00722 00723 QRect Applet::mapToView(const QGraphicsView *view, const QRectF &rect) const 00724 { 00725 // Why is this adjustment needed? Qt calculation error? 00726 return view->mapFromScene(mapToScene(rect)).boundingRect().adjusted(0, 0, -1, -1); 00727 } 00728 00729 QPoint Applet::popupPosition(const QSize &s) const 00730 { 00731 return popupPosition(s, Qt::AlignLeft); 00732 } 00733 00734 QPoint Applet::popupPosition(const QSize &s, Qt::AlignmentFlag alignment) const 00735 { 00736 Corona * corona = qobject_cast<Corona*>(scene()); 00737 Q_ASSERT(corona); 00738 00739 return corona->popupPosition(this, s, alignment); 00740 } 00741 00742 void Applet::updateConstraints(Plasma::Constraints constraints) 00743 { 00744 d->scheduleConstraintsUpdate(constraints); 00745 } 00746 00747 void Applet::constraintsEvent(Plasma::Constraints constraints) 00748 { 00749 //NOTE: do NOT put any code in here that reacts to constraints updates 00750 // as it will not get called for any applet that reimplements constraintsEvent 00751 // without calling the Applet:: version as well, which it shouldn't need to. 00752 // INSTEAD put such code into flushPendingConstraintsEvents 00753 Q_UNUSED(constraints) 00754 //kDebug() << constraints << "constraints are FormFactor: " << formFactor() 00755 // << ", Location: " << location(); 00756 if (d->script) { 00757 d->script->constraintsEvent(constraints); 00758 } 00759 } 00760 00761 void Applet::initExtenderItem(ExtenderItem *item) 00762 { 00763 if (d->script) { 00764 emit extenderItemRestored(item); 00765 } else { 00766 kWarning() << "Missing implementation of initExtenderItem in the applet " 00767 << item->config().readEntry("SourceAppletPluginName", "") 00768 << "!\n Any applet that uses extenders should implement initExtenderItem to " 00769 << "instantiate a widget. Destroying the item..."; 00770 item->destroy(); 00771 } 00772 } 00773 00774 Extender *Applet::extender() const 00775 { 00776 if (!d->extender) { 00777 new Extender(const_cast<Applet*>(this)); 00778 } 00779 00780 return d->extender.data(); 00781 } 00782 00783 void Applet::setBusy(bool busy) 00784 { 00785 if (busy) { 00786 if (!d->busyWidget && !d->busyWidgetTimer.isActive()) { 00787 d->busyWidgetTimer.start(500, this); 00788 } 00789 } else { 00790 d->busyWidgetTimer.stop(); 00791 if (d->busyWidget) { 00792 d->busyWidget = 0; 00793 d->destroyMessageOverlay(); 00794 } 00795 } 00796 } 00797 00798 bool Applet::isBusy() const 00799 { 00800 return d->busyWidgetTimer.isActive() || (d->busyWidget && d->busyWidget->isVisible()); 00801 } 00802 00803 QString Applet::name() const 00804 { 00805 if (d->isContainment) { 00806 const Containment *c = qobject_cast<const Containment*>(this); 00807 if (c && c->d->isPanelContainment()) { 00808 return i18n("Panel"); 00809 } else if (!d->appletDescription.isValid()) { 00810 return i18n("Unknown"); 00811 } else { 00812 return d->appletDescription.name(); 00813 } 00814 } else if (!d->appletDescription.isValid()) { 00815 return i18n("Unknown Widget"); 00816 } 00817 00818 return d->appletDescription.name(); 00819 } 00820 00821 QFont Applet::font() const 00822 { 00823 return QApplication::font(); 00824 } 00825 00826 QString Applet::icon() const 00827 { 00828 if (!d->appletDescription.isValid()) { 00829 return QString(); 00830 } 00831 00832 return d->appletDescription.icon(); 00833 } 00834 00835 QString Applet::pluginName() const 00836 { 00837 if (!d->appletDescription.isValid()) { 00838 return QString(); 00839 } 00840 00841 return d->appletDescription.pluginName(); 00842 } 00843 00844 bool Applet::shouldConserveResources() const 00845 { 00846 #ifndef PLASMA_NO_SOLID 00847 return Solid::PowerManagement::appShouldConserveResources(); 00848 #else 00849 return true; 00850 #endif 00851 } 00852 00853 QString Applet::category() const 00854 { 00855 if (!d->appletDescription.isValid()) { 00856 return i18nc("misc category", "Miscellaneous"); 00857 } 00858 00859 return d->appletDescription.category(); 00860 } 00861 00862 QString Applet::category(const KPluginInfo &applet) 00863 { 00864 return applet.property("X-KDE-PluginInfo-Category").toString(); 00865 } 00866 00867 QString Applet::category(const QString &appletName) 00868 { 00869 if (appletName.isEmpty()) { 00870 return QString(); 00871 } 00872 00873 const QString constraint = QString("[X-KDE-PluginInfo-Name] == '%1'").arg(appletName); 00874 KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint); 00875 00876 if (offers.isEmpty()) { 00877 return QString(); 00878 } 00879 00880 return offers.first()->property("X-KDE-PluginInfo-Category").toString(); 00881 } 00882 00883 ImmutabilityType Applet::immutability() const 00884 { 00885 // if this object is itself system immutable, then just return that; it's the most 00886 // restrictive setting possible and will override anything that might be happening above it 00887 // in the Corona->Containment->Applet hierarchy 00888 if (d->transient || (d->mainConfig && d->mainConfig->isImmutable())) { 00889 return SystemImmutable; 00890 } 00891 00892 //Returning the more strict immutability between the applet immutability, Containment and Corona 00893 ImmutabilityType upperImmutability = Mutable; 00894 Containment *cont = d->isContainment ? 0 : containment(); 00895 00896 if (cont) { 00897 upperImmutability = cont->immutability(); 00898 } else if (Corona *corona = qobject_cast<Corona*>(scene())) { 00899 upperImmutability = corona->immutability(); 00900 } 00901 00902 if (upperImmutability != Mutable) { 00903 // it's either system or user immutable, and we already check for local system immutability, 00904 // so upperImmutability is guaranteed to be as or more severe as this object's immutability 00905 return upperImmutability; 00906 } else { 00907 return d->immutability; 00908 } 00909 } 00910 00911 void Applet::setImmutability(const ImmutabilityType immutable) 00912 { 00913 if (d->immutability == immutable || immutable == Plasma::SystemImmutable) { 00914 // we do not store system immutability in d->immutability since that gets saved 00915 // out to the config file; instead, we check with 00916 // the config group itself for this information at all times. this differs from 00917 // corona, where SystemImmutability is stored in d->immutability. 00918 return; 00919 } 00920 00921 d->immutability = immutable; 00922 updateConstraints(ImmutableConstraint); 00923 } 00924 00925 Applet::BackgroundHints Applet::backgroundHints() const 00926 { 00927 return d->backgroundHints; 00928 } 00929 00930 void Applet::setBackgroundHints(const BackgroundHints hints) 00931 { 00932 if (d->backgroundHints == hints) { 00933 return; 00934 } 00935 00936 d->backgroundHints = hints; 00937 d->preferredBackgroundHints = hints; 00938 00939 //Draw the standard background? 00940 if ((hints & StandardBackground) || (hints & TranslucentBackground)) { 00941 if (!d->background) { 00942 d->background = new Plasma::FrameSvg(this); 00943 QObject::connect(d->background, SIGNAL(repaintNeeded()), this, SLOT(themeChanged())); 00944 } 00945 00946 if ((hints & TranslucentBackground) && 00947 Plasma::Theme::defaultTheme()->currentThemeHasImage("widgets/translucentbackground")) { 00948 d->background->setImagePath("widgets/translucentbackground"); 00949 } else { 00950 d->background->setImagePath("widgets/background"); 00951 } 00952 00953 d->background->setEnabledBorders(Plasma::FrameSvg::AllBorders); 00954 qreal left, top, right, bottom; 00955 d->background->getMargins(left, top, right, bottom); 00956 setContentsMargins(left, right, top, bottom); 00957 QSizeF fitSize(left + right, top + bottom); 00958 d->background->resizeFrame(boundingRect().size()); 00959 00960 //if the background has an "overlay" element decide a random position for it and then save it so it's consistent across plasma starts 00961 if (d->background->hasElement("overlay")) { 00962 QSize overlaySize = d->background->elementSize("overlay"); 00963 00964 //position is in the boundaries overlaySize.width()*2, overlaySize.height() 00965 qsrand(id()); 00966 d->background->d->overlayPos.rx() = - (overlaySize.width() /2) + (overlaySize.width() /4) * (qrand() % (4 + 1)); 00967 d->background->d->overlayPos.ry() = (- (overlaySize.height() /2) + (overlaySize.height() /4) * (qrand() % (4 + 1)))/2; 00968 } 00969 } else if (d->background) { 00970 qreal left, top, right, bottom; 00971 d->background->getMargins(left, top, right, bottom); 00972 00973 delete d->background; 00974 d->background = 0; 00975 setContentsMargins(0, 0, 0, 0); 00976 } 00977 00978 update(); 00979 } 00980 00981 bool Applet::hasFailedToLaunch() const 00982 { 00983 return d->failed; 00984 } 00985 00986 void Applet::paintWindowFrame(QPainter *painter, 00987 const QStyleOptionGraphicsItem *option, QWidget *widget) 00988 { 00989 Q_UNUSED(painter) 00990 Q_UNUSED(option) 00991 Q_UNUSED(widget) 00992 //Here come the code for the window frame 00993 //kDebug() << windowFrameGeometry(); 00994 //painter->drawRoundedRect(windowFrameGeometry(), 5, 5); 00995 } 00996 00997 bool Applet::configurationRequired() const 00998 { 00999 return d->needsConfig; 01000 } 01001 01002 void Applet::setConfigurationRequired(bool needsConfig, const QString &reason) 01003 { 01004 if (d->needsConfig == needsConfig) { 01005 return; 01006 } 01007 01008 d->needsConfig = needsConfig; 01009 01010 if (!needsConfig) { 01011 d->destroyMessageOverlay(); 01012 return; 01013 } 01014 01015 d->createMessageOverlay(true); 01016 d->messageOverlay->opacity = 0.4; 01017 01018 QGraphicsGridLayout *configLayout = new QGraphicsGridLayout(d->messageOverlay); 01019 configLayout->setContentsMargins(0, 0, 0, 0); 01020 01021 // configLayout->addStretch(); 01022 configLayout->setColumnStretchFactor(0, 5); 01023 configLayout->setColumnStretchFactor(2, 5); 01024 configLayout->setRowStretchFactor(0, 5); 01025 configLayout->setRowStretchFactor(3, 5); 01026 01027 int row = 1; 01028 if (!reason.isEmpty()) { 01029 Label *explanation = new Label(d->messageOverlay); 01030 explanation->setText(reason); 01031 configLayout->addItem(explanation, row, 1); 01032 configLayout->setColumnStretchFactor(1, 5); 01033 ++row; 01034 configLayout->setAlignment(explanation, Qt::AlignBottom | Qt::AlignCenter); 01035 } 01036 01037 PushButton *configWidget = new PushButton(d->messageOverlay); 01038 if (!qobject_cast<Plasma::PopupApplet *>(this) && (formFactor() == Plasma::Horizontal || formFactor() == Plasma::Vertical)) { 01039 configWidget->setImage("widgets/configuration-icons", "configure"); 01040 configWidget->setMaximumSize(24,24); 01041 configWidget->setMinimumSize(24,24); 01042 } else { 01043 configWidget->setText(i18n("Configure...")); 01044 } 01045 connect(configWidget, SIGNAL(clicked()), this, SLOT(showConfigurationInterface())); 01046 configLayout->addItem(configWidget, row, 1); 01047 01048 //configLayout->setAlignment(configWidget, Qt::AlignTop | Qt::AlignCenter); 01049 //configLayout->addStretch(); 01050 01051 d->messageOverlay->show(); 01052 } 01053 01054 void Applet::showMessage(const QIcon &icon, const QString &message, const MessageButtons buttons) 01055 { 01056 if (message.isEmpty()) { 01057 d->destroyMessageOverlay(); 01058 return; 01059 } 01060 01061 Corona *corona = qobject_cast<Corona *>(scene()); 01062 QGraphicsWidget *mainWidget = new QGraphicsWidget; 01063 01064 QGraphicsLinearLayout *mainLayout = new QGraphicsLinearLayout(mainWidget); 01065 mainLayout->setOrientation(Qt::Vertical); 01066 mainLayout->addStretch(); 01067 01068 QGraphicsLinearLayout *messageLayout = new QGraphicsLinearLayout(); 01069 messageLayout->setOrientation(Qt::Horizontal); 01070 01071 QGraphicsLinearLayout *buttonLayout = new QGraphicsLinearLayout(); 01072 buttonLayout->setOrientation(Qt::Horizontal); 01073 01074 mainLayout->addItem(messageLayout); 01075 mainLayout->addItem(buttonLayout); 01076 mainLayout->addStretch(); 01077 01078 IconWidget *messageIcon = new IconWidget(mainWidget); 01079 Label *messageText = new Label(mainWidget); 01080 messageText->nativeWidget()->setWordWrap(true); 01081 01082 messageLayout->addStretch(); 01083 messageLayout->addItem(messageIcon); 01084 messageLayout->addItem(messageText); 01085 messageLayout->addStretch(); 01086 01087 messageIcon->setIcon(icon); 01088 messageText->setText(message); 01089 01090 buttonLayout->addStretch(); 01091 01092 if (buttons & ButtonOk) { 01093 PushButton *ok = new PushButton(mainWidget); 01094 ok->setText(i18n("&OK")); 01095 ok->setIcon(KIcon("dialog-ok")); 01096 buttonLayout->addItem(ok); 01097 connect(ok, SIGNAL(clicked()), this, SLOT(destroyMessageOverlay())); 01098 } 01099 01100 if (buttons & ButtonYes) { 01101 PushButton *yes = new PushButton(mainWidget); 01102 yes->setText(i18n("&Yes")); 01103 buttonLayout->addItem(yes); 01104 connect(yes, SIGNAL(clicked()), this, SLOT(destroyMessageOverlay())); 01105 } 01106 01107 if (buttons & ButtonNo) { 01108 PushButton *no = new PushButton(mainWidget); 01109 no->setText(i18n("&No")); 01110 buttonLayout->addItem(no); 01111 connect(no, SIGNAL(clicked()), this, SLOT(destroyMessageOverlay())); 01112 } 01113 01114 if (buttons & ButtonCancel) { 01115 PushButton *cancel = new PushButton(mainWidget); 01116 cancel->setText(i18n("&Cancel")); 01117 cancel->setIcon(KIcon("dialog-cancel")); 01118 buttonLayout->addItem(cancel); 01119 connect(cancel, SIGNAL(clicked()), this, SLOT(destroyMessageOverlay())); 01120 } 01121 01122 d->messageCloseAction = new QAction(d->messageOverlay); 01123 d->messageCloseAction.data()->setShortcut(Qt::Key_Escape); 01124 mainWidget->addAction(d->messageCloseAction.data()); 01125 connect(d->messageCloseAction.data(), SIGNAL(triggered()), this, SLOT(destroyMessageOverlay())); 01126 01127 buttonLayout->addStretch(); 01128 01129 mainWidget->adjustSize(); 01130 QSizeF hint = mainWidget->preferredSize(); 01131 if (hint.height() > size().height() || hint.width() > size().width()) { 01132 // either a collapsed popup in h/v form factor or just too small, 01133 // so show it in a dialog associated with ourselves 01134 if (corona) { 01135 corona->addOffscreenWidget(mainWidget); 01136 } 01137 01138 if (d->messageDialog) { 01139 delete d->messageDialog.data()->graphicsWidget(); 01140 } else { 01141 d->messageDialog = new Plasma::Dialog; 01142 } 01143 01144 ToolTipManager::self()->hide(this); 01145 KWindowSystem::setOnAllDesktops(d->messageDialog.data()->winId(), true); 01146 KWindowSystem::setState(d->messageDialog.data()->winId(), NET::SkipTaskbar | NET::SkipPager); 01147 d->messageDialog.data()->setGraphicsWidget(mainWidget); 01148 connect(d->messageDialog.data(), SIGNAL(destroyed(QObject*)), mainWidget, SLOT(deleteLater())); 01149 01150 // if we are going to show it in a popup, then at least make sure it can be dismissed 01151 if (buttonLayout->count() < 1) { 01152 PushButton *ok = new PushButton(mainWidget); 01153 ok->setText(i18n("OK")); 01154 ok->setIcon(KIcon("dialog-ok")); 01155 buttonLayout->addItem(ok); 01156 connect(ok, SIGNAL(clicked()), this, SLOT(destroyMessageOverlay())); 01157 } 01158 } else { 01159 delete d->messageDialog.data(); 01160 d->createMessageOverlay(); 01161 d->messageOverlay->opacity = 0.8; 01162 mainWidget->setParentItem(d->messageOverlay); 01163 QGraphicsLinearLayout *l = new QGraphicsLinearLayout(d->messageOverlay); 01164 l->addItem(mainWidget); 01165 } 01166 01167 if (d->messageDialog) { 01168 QPoint pos = geometry().topLeft().toPoint(); 01169 if (corona) { 01170 pos = corona->popupPosition(this, d->messageDialog.data()->size()); 01171 } 01172 01173 d->messageDialog.data()->move(pos); 01174 d->messageDialog.data()->animatedShow(locationToDirection(location())); 01175 } else { 01176 d->messageOverlay->show(); 01177 } 01178 } 01179 01180 QVariantList Applet::startupArguments() const 01181 { 01182 return d->args; 01183 } 01184 01185 ItemStatus Applet::status() const 01186 { 01187 return d->itemStatus; 01188 } 01189 01190 void Applet::setStatus(const ItemStatus status) 01191 { 01192 d->itemStatus = status; 01193 emit newStatus(status); 01194 } 01195 01196 void Applet::flushPendingConstraintsEvents() 01197 { 01198 if (d->pendingConstraints == NoConstraint) { 01199 return; 01200 } 01201 01202 if (d->constraintsTimer.isActive()) { 01203 d->constraintsTimer.stop(); 01204 } 01205 01206 //kDebug() << "fushing constraints: " << d->pendingConstraints << "!!!!!!!!!!!!!!!!!!!!!!!!!!!"; 01207 Plasma::Constraints c = d->pendingConstraints; 01208 d->pendingConstraints = NoConstraint; 01209 01210 if (c & Plasma::StartupCompletedConstraint) { 01211 //common actions 01212 bool unlocked = immutability() == Mutable; 01213 QAction *closeApplet = d->actions->action("remove"); 01214 if (closeApplet) { 01215 closeApplet->setEnabled(unlocked); 01216 closeApplet->setVisible(unlocked); 01217 connect(closeApplet, SIGNAL(triggered(bool)), this, SLOT(selectItemToDestroy()), Qt::UniqueConnection); 01218 } 01219 01220 QAction *configAction = d->actions->action("configure"); 01221 if (configAction) { 01222 if (d->isContainment) { 01223 connect(configAction, SIGNAL(triggered(bool)), this, SLOT(requestConfiguration()), Qt::UniqueConnection); 01224 } else { 01225 connect(configAction, SIGNAL(triggered(bool)), this, SLOT(showConfigurationInterface()), Qt::UniqueConnection); 01226 } 01227 01228 if (d->hasConfigurationInterface) { 01229 bool canConfig = unlocked || KAuthorized::authorize("plasma/allow_configure_when_locked"); 01230 configAction->setVisible(canConfig); 01231 configAction->setEnabled(canConfig); 01232 } 01233 } 01234 01235 QAction *runAssociatedApplication = d->actions->action("run associated application"); 01236 if (runAssociatedApplication) { 01237 connect(runAssociatedApplication, SIGNAL(triggered(bool)), this, SLOT(runAssociatedApplication()), Qt::UniqueConnection); 01238 } 01239 01240 d->updateShortcuts(); 01241 Corona * corona = qobject_cast<Corona*>(scene()); 01242 if (corona) { 01243 connect(corona, SIGNAL(shortcutsChanged()), this, SLOT(updateShortcuts()), Qt::UniqueConnection); 01244 } 01245 } 01246 01247 if (c & Plasma::ImmutableConstraint) { 01248 bool unlocked = immutability() == Mutable; 01249 QAction *action = d->actions->action("remove"); 01250 if (action) { 01251 action->setVisible(unlocked); 01252 action->setEnabled(unlocked); 01253 } 01254 01255 action = d->actions->action("configure"); 01256 if (action && d->hasConfigurationInterface) { 01257 bool canConfig = unlocked || KAuthorized::authorize("plasma/allow_configure_when_locked"); 01258 action->setVisible(canConfig); 01259 action->setEnabled(canConfig); 01260 } 01261 01262 if (d->extender) { 01263 foreach (ExtenderItem *item, d->extender.data()->attachedItems()) { 01264 item->d->setMovable(unlocked); 01265 } 01266 } 01267 01268 emit immutabilityChanged(immutability()); 01269 } 01270 01271 if (c & Plasma::SizeConstraint) { 01272 d->positionMessageOverlay(); 01273 01274 if (d->started && layout()) { 01275 layout()->updateGeometry(); 01276 } 01277 } 01278 01279 if (c & Plasma::FormFactorConstraint) { 01280 FormFactor f = formFactor(); 01281 if (!d->isContainment && f != Vertical && f != Horizontal) { 01282 setBackgroundHints(d->preferredBackgroundHints); 01283 } else { 01284 BackgroundHints hints = d->preferredBackgroundHints; 01285 setBackgroundHints(NoBackground); 01286 d->preferredBackgroundHints = hints; 01287 } 01288 01289 if (d->failed) { 01290 if (f == Vertical || f == Horizontal) { 01291 QGraphicsLayoutItem *item = layout()->itemAt(1); 01292 layout()->removeAt(1); 01293 delete item; 01294 } 01295 } 01296 01297 // avoid putting rotated applets in panels 01298 if (f == Vertical || f == Horizontal) { 01299 QTransform at; 01300 at.rotateRadians(0); 01301 setTransform(at); 01302 } 01303 01304 //was a size saved for a particular form factor? 01305 if (d->sizeForFormFactor.contains(f)) { 01306 resize(d->sizeForFormFactor.value(f)); 01307 } 01308 } 01309 01310 if (!size().isEmpty() && 01311 ((c & Plasma::StartupCompletedConstraint) || (c & Plasma::SizeConstraint && !(c & Plasma::FormFactorConstraint)))) { 01312 d->sizeForFormFactor[formFactor()] = size(); 01313 } 01314 01315 if (c & Plasma::SizeConstraint || c & Plasma::FormFactorConstraint) { 01316 if (aspectRatioMode() == Plasma::Square || aspectRatioMode() == Plasma::ConstrainedSquare) { 01317 // enforce square size in panels 01318 //save the old size policy. since ignored doesn't (yet) have a valid use case in containments, use it as special unset value 01319 if (d->preferredSizePolicy == QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored)) { 01320 d->preferredSizePolicy = sizePolicy(); 01321 } 01322 if (formFactor() == Horizontal) { 01323 setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding)); 01324 } else if (formFactor() == Vertical) { 01325 setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); 01326 } else if (d->preferredSizePolicy != QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored)) { 01327 setSizePolicy(d->preferredSizePolicy); 01328 } 01329 } 01330 updateGeometry(); 01331 } 01332 01333 // now take care of constraints in special subclasses: Contaiment and PopupApplet 01334 Containment* containment = qobject_cast<Plasma::Containment*>(this); 01335 if (d->isContainment && containment) { 01336 containment->d->containmentConstraintsEvent(c); 01337 } 01338 01339 PopupApplet* popup = qobject_cast<Plasma::PopupApplet*>(this); 01340 if (popup) { 01341 popup->d->popupConstraintsEvent(c); 01342 } 01343 01344 // pass the constraint on to the actual subclass 01345 constraintsEvent(c); 01346 01347 if (c & StartupCompletedConstraint) { 01348 // start up is done, we can now go do a mod timer 01349 if (d->modificationsTimer) { 01350 if (d->modificationsTimer->isActive()) { 01351 d->modificationsTimer->stop(); 01352 } 01353 } else { 01354 d->modificationsTimer = new QBasicTimer; 01355 } 01356 } 01357 } 01358 01359 int Applet::type() const 01360 { 01361 return Type; 01362 } 01363 01364 QList<QAction*> Applet::contextualActions() 01365 { 01366 //kDebug() << "empty context actions"; 01367 return d->script ? d->script->contextualActions() : QList<QAction*>(); 01368 } 01369 01370 QAction *Applet::action(QString name) const 01371 { 01372 return d->actions->action(name); 01373 } 01374 01375 void Applet::addAction(QString name, QAction *action) 01376 { 01377 d->actions->addAction(name, action); 01378 } 01379 01380 void Applet::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) 01381 { 01382 if (!d->started) { 01383 //kDebug() << "not started"; 01384 return; 01385 } 01386 01387 if (transform().isRotating()) { 01388 painter->setRenderHint(QPainter::SmoothPixmapTransform); 01389 painter->setRenderHint(QPainter::Antialiasing); 01390 } 01391 01392 if (d->background && 01393 formFactor() != Plasma::Vertical && 01394 formFactor() != Plasma::Horizontal) { 01395 //kDebug() << "option rect is" << option->rect; 01396 d->background->paintFrame(painter); 01397 } 01398 01399 if (d->failed) { 01400 //kDebug() << "failed!"; 01401 return; 01402 } 01403 01404 qreal left, top, right, bottom; 01405 getContentsMargins(&left, &top, &right, &bottom); 01406 QRect contentsRect = QRectF(QPointF(0, 0), 01407 boundingRect().size()).adjusted(left, top, -right, -bottom).toRect(); 01408 01409 if (widget && d->isContainment) { 01410 // note that the widget we get is actually the viewport of the view, not the view itself 01411 View* v = qobject_cast<Plasma::View*>(widget->parent()); 01412 Containment* c = qobject_cast<Plasma::Containment*>(this); 01413 01414 if (!v || v->isWallpaperEnabled()) { 01415 01416 // paint the wallpaper 01417 if (c && c->drawWallpaper() && c->wallpaper()) { 01418 Wallpaper *w = c->wallpaper(); 01419 if (!w->isInitialized()) { 01420 // delayed paper initialization 01421 KConfigGroup wallpaperConfig = c->config(); 01422 wallpaperConfig = KConfigGroup(&wallpaperConfig, "Wallpaper"); 01423 wallpaperConfig = KConfigGroup(&wallpaperConfig, w->pluginName()); 01424 w->restore(wallpaperConfig); 01425 disconnect(w, SIGNAL(update(const QRectF&)), this, SLOT(updateRect(const QRectF&))); 01426 connect(w, SIGNAL(update(const QRectF&)), this, SLOT(updateRect(const QRectF&))); 01427 } 01428 01429 painter->save(); 01430 c->wallpaper()->paint(painter, option->exposedRect); 01431 painter->restore(); 01432 } 01433 01434 // .. and now paint the actual containment interface, but with 01435 // a Containment style option based on the one we get 01436 // the view must be assigned only if its containment is actually our own 01437 Containment::StyleOption coption(*option); 01438 if (v && v->containment() == containment()) { 01439 coption.view = v; 01440 } 01441 paintInterface(painter, &coption, contentsRect); 01442 } 01443 } else { 01444 //kDebug() << "paint interface of" << (QObject*) this; 01445 // paint the applet's interface 01446 paintInterface(painter, option, contentsRect); 01447 } 01448 } 01449 01450 void Applet::paintInterface(QPainter *painter, const QStyleOptionGraphicsItem *option, const QRect &contentsRect) 01451 { 01452 if (d->script) { 01453 d->script->paintInterface(painter, option, contentsRect); 01454 } else { 01455 //kDebug() << "Applet::paintInterface() default impl"; 01456 } 01457 } 01458 01459 FormFactor Applet::formFactor() const 01460 { 01461 Containment *c = containment(); 01462 QGraphicsWidget *pw = qobject_cast<QGraphicsWidget *>(parent()); 01463 if (!pw) { 01464 pw = dynamic_cast<QGraphicsWidget *>(parentItem()); 01465 } 01466 Plasma::Applet *parentApplet = qobject_cast<Plasma::Applet *>(pw); 01467 //assumption: this loop is usually is -really- short or doesn't run at all 01468 while (!parentApplet && pw && pw->parentWidget()) { 01469 QGraphicsWidget *parentWidget = qobject_cast<QGraphicsWidget *>(pw->parent()); 01470 if (!parentWidget) { 01471 parentWidget = dynamic_cast<QGraphicsWidget *>(pw->parentItem()); 01472 } 01473 pw = parentWidget; 01474 parentApplet = qobject_cast<Plasma::Applet *>(pw); 01475 } 01476 01477 01478 const PopupApplet *pa = dynamic_cast<const PopupApplet *>(this); 01479 01480 //if the applet is in a widget that isn't a containment 01481 //try to retrieve the formFactor from the parent size 01482 //we can't use our own sizeHint here because it needs formFactor, so endless recursion. 01483 // a popupapplet can always be constrained. 01484 // a normal applet should to but 01485 //FIXME: not always constrained to not break systemmonitor 01486 if (parentApplet && parentApplet != c && c != this && (pa || layout())) { 01487 if (pa || (parentApplet->size().height() < layout()->effectiveSizeHint(Qt::MinimumSize).height())) { 01488 return Plasma::Horizontal; 01489 } else if (pa || (parentApplet->size().width() < layout()->effectiveSizeHint(Qt::MinimumSize).width())) { 01490 return Plasma::Vertical; 01491 } 01492 return parentApplet->formFactor(); 01493 } 01494 01495 return c ? c->d->formFactor : Plasma::Planar; 01496 } 01497 01498 Containment *Applet::containment() const 01499 { 01500 if (d->isContainment) { 01501 Containment *c = qobject_cast<Containment*>(const_cast<Applet*>(this)); 01502 if (c) { 01503 return c; 01504 } 01505 } 01506 01507 QGraphicsItem *parent = parentItem(); 01508 Containment *c = 0; 01509 01510 while (parent) { 01511 Containment *possibleC = dynamic_cast<Containment*>(parent); 01512 if (possibleC && possibleC->Applet::d->isContainment) { 01513 c = possibleC; 01514 break; 01515 } 01516 parent = parent->parentItem(); 01517 } 01518 01519 if (!c) { 01520 //if the applet is an offscreen widget its parentItem will be 0, while its parent 01521 //will be its parentWidget, so here we check the QObject hierarchy. 01522 QObject *objParent = this->parent(); 01523 while (objParent) { 01524 Containment *possibleC = qobject_cast<Containment*>(objParent); 01525 if (possibleC && possibleC->Applet::d->isContainment) { 01526 c = possibleC; 01527 break; 01528 } 01529 objParent = objParent->parent(); 01530 } 01531 } 01532 01533 return c; 01534 } 01535 01536 void Applet::setGlobalShortcut(const KShortcut &shortcut) 01537 { 01538 if (!d->activationAction) { 01539 d->activationAction = new KAction(this); 01540 d->activationAction->setText(i18n("Activate %1 Widget", name())); 01541 d->activationAction->setObjectName(QString("activate widget %1").arg(id())); // NO I18N 01542 connect(d->activationAction, SIGNAL(triggered()), this, SIGNAL(activate())); 01543 connect(d->activationAction, SIGNAL(globalShortcutChanged(QKeySequence)), 01544 this, SLOT(globalShortcutChanged())); 01545 01546 QList<QWidget *> widgets = d->actions->associatedWidgets(); 01547 foreach (QWidget *w, widgets) { 01548 w->addAction(d->activationAction); 01549 } 01550 } 01551 01552 //kDebug() << "before" << shortcut.primary() << d->activationAction->globalShortcut().primary(); 01553 d->activationAction->setGlobalShortcut( 01554 shortcut, 01555 KAction::ShortcutTypes(KAction::ActiveShortcut | KAction::DefaultShortcut), 01556 KAction::NoAutoloading); 01557 d->globalShortcutChanged(); 01558 } 01559 01560 void AppletPrivate::globalShortcutChanged() 01561 { 01562 if (!activationAction) { 01563 return; 01564 } 01565 01566 KConfigGroup shortcutConfig(mainConfigGroup(), "Shortcuts"); 01567 shortcutConfig.writeEntry("global", activationAction->globalShortcut().toString()); 01568 scheduleModificationNotification(); 01569 //kDebug() << "after" << shortcut.primary() << d->activationAction->globalShortcut().primary(); 01570 } 01571 01572 KShortcut Applet::globalShortcut() const 01573 { 01574 if (d->activationAction) { 01575 return d->activationAction->globalShortcut(); 01576 } 01577 01578 return KShortcut(); 01579 } 01580 01581 bool Applet::isPopupShowing() const 01582 { 01583 return false; 01584 } 01585 01586 void Applet::addAssociatedWidget(QWidget *widget) 01587 { 01588 d->actions->addAssociatedWidget(widget); 01589 } 01590 01591 void Applet::removeAssociatedWidget(QWidget *widget) 01592 { 01593 d->actions->removeAssociatedWidget(widget); 01594 } 01595 01596 Location Applet::location() const 01597 { 01598 Containment *c = containment(); 01599 return c ? c->d->location : Plasma::Desktop; 01600 } 01601 01602 Context *Applet::context() const 01603 { 01604 Containment *c = containment(); 01605 Q_ASSERT(c); 01606 return c->d->context(); 01607 } 01608 01609 Plasma::AspectRatioMode Applet::aspectRatioMode() const 01610 { 01611 return d->aspectRatioMode; 01612 } 01613 01614 void Applet::setAspectRatioMode(Plasma::AspectRatioMode mode) 01615 { 01616 PopupApplet *popup = qobject_cast<PopupApplet *>(this); 01617 if (popup && popup->d->dialogPtr) { 01618 popup->d->dialogPtr.data()->setAspectRatioMode(mode); 01619 popup->d->savedAspectRatio = mode; 01620 } 01621 01622 d->aspectRatioMode = mode; 01623 } 01624 01625 void Applet::registerAsDragHandle(QGraphicsItem *item) 01626 { 01627 if (!item || d->registeredAsDragHandle.contains(item)) { 01628 return; 01629 } 01630 01631 d->registeredAsDragHandle.insert(item); 01632 item->installSceneEventFilter(this); 01633 } 01634 01635 void Applet::unregisterAsDragHandle(QGraphicsItem *item) 01636 { 01637 if (!item) { 01638 return; 01639 } 01640 01641 if (d->registeredAsDragHandle.remove(item)) { 01642 item->removeSceneEventFilter(this); 01643 } 01644 } 01645 01646 bool Applet::isRegisteredAsDragHandle(QGraphicsItem *item) 01647 { 01648 return d->registeredAsDragHandle.contains(item); 01649 } 01650 01651 bool Applet::hasConfigurationInterface() const 01652 { 01653 return d->hasConfigurationInterface; 01654 } 01655 01656 void Applet::publish(AnnouncementMethods methods, const QString &resourceName) 01657 { 01658 if (d->package) { 01659 d->package->d->publish(methods); 01660 } else if (d->appletDescription.isValid()) { 01661 if (!d->service) { 01662 d->service = new PlasmoidService(this); 01663 } 01664 01665 kDebug() << "publishing package under name " << resourceName; 01666 PackageMetadata pm; 01667 pm.setName(d->appletDescription.name()); 01668 pm.setDescription(d->appletDescription.comment()); 01669 d->service->d->publish(methods, resourceName, pm); 01670 } else { 01671 kDebug() << "Can not publish invalid applets."; 01672 } 01673 } 01674 01675 void Applet::unpublish() 01676 { 01677 if (d->package) { 01678 d->package->d->unpublish(); 01679 } else { 01680 if (d->service) { 01681 d->service->d->unpublish(); 01682 } 01683 } 01684 } 01685 01686 bool Applet::isPublished() const 01687 { 01688 if (d->package) { 01689 return d->package->d->isPublished(); 01690 } else { 01691 if (d->service) { 01692 return d->service->d->isPublished(); 01693 } else { 01694 return false; 01695 } 01696 } 01697 } 01698 01699 void Applet::setHasConfigurationInterface(bool hasInterface) 01700 { 01701 if (hasInterface == d->hasConfigurationInterface) { 01702 return; 01703 } 01704 01705 QAction *configAction = d->actions->action("configure"); 01706 if (configAction) { 01707 bool enable = hasInterface; 01708 if (enable) { 01709 const bool unlocked = immutability() == Mutable; 01710 enable = unlocked || KAuthorized::authorize("plasma/allow_configure_when_locked"); 01711 } 01712 configAction->setEnabled(enable); 01713 } 01714 01715 d->hasConfigurationInterface = hasInterface; 01716 } 01717 01718 KActionCollection* AppletPrivate::defaultActions(QObject *parent) 01719 { 01720 KActionCollection *actions = new KActionCollection(parent); 01721 actions->setConfigGroup("Shortcuts-Applet"); 01722 01723 KAction *configAction = actions->addAction("configure"); 01724 configAction->setAutoRepeat(false); 01725 configAction->setText(i18n("Widget Settings")); 01726 configAction->setIcon(KIcon("configure")); 01727 configAction->setShortcut(KShortcut("alt+d, s")); 01728 configAction->setData(AbstractToolBox::ConfigureTool); 01729 01730 KAction *closeApplet = actions->addAction("remove"); 01731 closeApplet->setAutoRepeat(false); 01732 closeApplet->setText(i18n("Remove this Widget")); 01733 closeApplet->setIcon(KIcon("edit-delete")); 01734 closeApplet->setShortcut(KShortcut("alt+d, r")); 01735 closeApplet->setData(AbstractToolBox::DestructiveTool); 01736 01737 KAction *runAssociatedApplication = actions->addAction("run associated application"); 01738 runAssociatedApplication->setAutoRepeat(false); 01739 runAssociatedApplication->setText(i18n("Run the Associated Application")); 01740 runAssociatedApplication->setIcon(KIcon("system-run")); 01741 runAssociatedApplication->setShortcut(KShortcut("alt+d, t")); 01742 runAssociatedApplication->setVisible(false); 01743 runAssociatedApplication->setEnabled(false); 01744 runAssociatedApplication->setData(AbstractToolBox::ControlTool); 01745 01746 return actions; 01747 } 01748 01749 bool Applet::eventFilter(QObject *o, QEvent *e) 01750 { 01751 return QObject::eventFilter(o, e); 01752 } 01753 01754 bool Applet::sceneEventFilter(QGraphicsItem *watched, QEvent *event) 01755 { 01756 switch (event->type()) { 01757 case QEvent::GraphicsSceneMouseMove: 01758 case QEvent::GraphicsSceneMousePress: 01759 case QEvent::GraphicsSceneMouseRelease: 01760 { 01761 // don't move when the containment is not mutable, 01762 // in the rare case the containment doesn't exists consider it as mutable 01763 if ((flags() & ItemIsMovable) && d->registeredAsDragHandle.contains(watched)) { 01764 Containment *c = containment(); 01765 if (!c || c->immutability() == Mutable) { 01766 scene()->sendEvent(this, event); 01767 return false; 01768 } 01769 } 01770 break; 01771 } 01772 01773 default: 01774 break; 01775 } 01776 01777 return QGraphicsItem::sceneEventFilter(watched, event); 01778 } 01779 01780 void Applet::mouseMoveEvent(QGraphicsSceneMouseEvent *event) 01781 { 01782 if (immutability() == Mutable && formFactor() == Plasma::Planar && (flags() & ItemIsMovable)) { 01783 QGraphicsWidget::mouseMoveEvent(event); 01784 } 01785 } 01786 01787 void Applet::focusInEvent(QFocusEvent *event) 01788 { 01789 if (!isContainment() && containment()) { 01790 //focusing an applet may trigger this event again, but we won't be here more than twice 01791 containment()->d->focusApplet(this); 01792 } 01793 01794 QGraphicsWidget::focusInEvent(event); 01795 } 01796 01797 void Applet::resizeEvent(QGraphicsSceneResizeEvent *event) 01798 { 01799 QGraphicsWidget::resizeEvent(event); 01800 01801 if (d->background) { 01802 d->background->resizeFrame(boundingRect().size()); 01803 } 01804 01805 updateConstraints(Plasma::SizeConstraint); 01806 01807 d->scheduleModificationNotification(); 01808 emit geometryChanged(); 01809 } 01810 01811 bool Applet::isUserConfiguring() const 01812 { 01813 return KConfigDialog::exists(d->configDialogId()); 01814 } 01815 01816 void Applet::showConfigurationInterface() 01817 { 01818 if (!hasConfigurationInterface()) { 01819 return; 01820 } 01821 01822 if (immutability() != Mutable && !KAuthorized::authorize("plasma/allow_configure_when_locked")) { 01823 //FIXME: in 4.3 add an explanatory dialog 01824 return; 01825 } 01826 01827 KConfigDialog *dlg = KConfigDialog::exists(d->configDialogId()); 01828 01829 if (dlg) { 01830 KWindowSystem::setOnDesktop(dlg->winId(), KWindowSystem::currentDesktop()); 01831 dlg->show(); 01832 KWindowSystem::activateWindow(dlg->winId()); 01833 return; 01834 } 01835 01836 d->publishUI.publishCheckbox = 0; 01837 if (d->package && d->configLoader) { 01838 KConfigDialog *dialog = 0; 01839 01840 QString uiFile = d->package->filePath("mainconfigui"); 01841 if (!uiFile.isEmpty()) { 01842 QFile f(uiFile); 01843 QUiLoader loader; 01844 QWidget *w = loader.load(&f); 01845 if (w) { 01846 dialog = new AppletConfigDialog(0, d->configDialogId(), d->configLoader); 01847 dialog->setWindowTitle(d->configWindowTitle()); 01848 dialog->setAttribute(Qt::WA_DeleteOnClose, true); 01849 dialog->addPage(w, i18n("Settings"), icon(), i18n("%1 Settings", name())); 01850 d->addGlobalShortcutsPage(dialog); 01851 d->addPublishPage(dialog); 01852 dialog->show(); 01853 } 01854 } 01855 01856 if (!dialog && d->script) { 01857 d->script->showConfigurationInterface(); 01858 } 01859 } else if (d->script) { 01860 d->script->showConfigurationInterface(); 01861 } else { 01862 KConfigDialog *dialog = d->generateGenericConfigDialog(); 01863 //createConfigurationInterface(dialog); 01864 d->addStandardConfigurationPages(dialog); 01865 showConfigurationInterface(dialog); 01866 } 01867 01868 emit releaseVisualFocus(); 01869 } 01870 01871 void Applet::showConfigurationInterface(QWidget *widget) 01872 { 01873 if (!containment() || !containment()->corona() || 01874 !containment()->corona()->dialogManager()) { 01875 widget->show(); 01876 return; 01877 } 01878 01879 QMetaObject::invokeMethod(containment()->corona()->dialogManager(), "showDialog", Q_ARG(QWidget *, widget), Q_ARG(Plasma::Applet *, this)); 01880 } 01881 01882 QString AppletPrivate::configDialogId() const 01883 { 01884 return QString("%1settings%2").arg(appletId).arg(q->name()); 01885 } 01886 01887 QString AppletPrivate::configWindowTitle() const 01888 { 01889 return i18nc("@title:window", "%1 Settings", q->name()); 01890 } 01891 01892 QSet<QString> AppletPrivate::knownCategories() 01893 { 01894 // this is to trick the tranlsation tools into making the correct 01895 // strings for translation 01896 QSet<QString> categories = s_customCategories; 01897 categories << QString(I18N_NOOP("Accessibility")).toLower() 01898 << QString(I18N_NOOP("Application Launchers")).toLower() 01899 << QString(I18N_NOOP("Astronomy")).toLower() 01900 << QString(I18N_NOOP("Date and Time")).toLower() 01901 << QString(I18N_NOOP("Development Tools")).toLower() 01902 << QString(I18N_NOOP("Education")).toLower() 01903 << QString(I18N_NOOP("Environment and Weather")).toLower() 01904 << QString(I18N_NOOP("Examples")).toLower() 01905 << QString(I18N_NOOP("File System")).toLower() 01906 << QString(I18N_NOOP("Fun and Games")).toLower() 01907 << QString(I18N_NOOP("Graphics")).toLower() 01908 << QString(I18N_NOOP("Language")).toLower() 01909 << QString(I18N_NOOP("Mapping")).toLower() 01910 << QString(I18N_NOOP("Miscellaneous")).toLower() 01911 << QString(I18N_NOOP("Multimedia")).toLower() 01912 << QString(I18N_NOOP("Online Services")).toLower() 01913 << QString(I18N_NOOP("Productivity")).toLower() 01914 << QString(I18N_NOOP("System Information")).toLower() 01915 << QString(I18N_NOOP("Utilities")).toLower() 01916 << QString(I18N_NOOP("Windows and Tasks")).toLower(); 01917 return categories; 01918 } 01919 01920 KConfigDialog *AppletPrivate::generateGenericConfigDialog() 01921 { 01922 KConfigSkeleton *nullManager = new KConfigSkeleton(0); 01923 KConfigDialog *dialog = new AppletConfigDialog(0, configDialogId(), nullManager); 01924 dialog->setFaceType(KPageDialog::Auto); 01925 dialog->setWindowTitle(configWindowTitle()); 01926 dialog->setAttribute(Qt::WA_DeleteOnClose, true); 01927 q->createConfigurationInterface(dialog); 01928 //TODO: Apply button does not correctly work for now, so do not show it 01929 dialog->showButton(KDialog::Apply, false); 01930 dialog->showButton(KDialog::Default, false); 01931 QObject::connect(dialog, SIGNAL(applyClicked()), q, SLOT(configDialogFinished())); 01932 QObject::connect(dialog, SIGNAL(okClicked()), q, SLOT(configDialogFinished())); 01933 QObject::connect(dialog, SIGNAL(finished()), nullManager, SLOT(deleteLater())); 01934 return dialog; 01935 } 01936 01937 void AppletPrivate::addStandardConfigurationPages(KConfigDialog *dialog) 01938 { 01939 addGlobalShortcutsPage(dialog); 01940 addPublishPage(dialog); 01941 } 01942 01943 void AppletPrivate::addGlobalShortcutsPage(KConfigDialog *dialog) 01944 { 01945 if (isContainment) { 01946 return; 01947 } 01948 01949 QWidget *page = new QWidget; 01950 QVBoxLayout *layout = new QVBoxLayout(page); 01951 01952 if (!shortcutEditor) { 01953 shortcutEditor = new KKeySequenceWidget(page); 01954 QObject::connect(shortcutEditor, SIGNAL(destroyed(QObject*)), q, SLOT(clearShortcutEditorPtr())); 01955 } 01956 01957 shortcutEditor->setKeySequence(q->globalShortcut().primary()); 01958 layout->addWidget(shortcutEditor); 01959 layout->addStretch(); 01960 dialog->addPage(page, i18n("Keyboard Shortcut"), "preferences-desktop-keyboard"); 01961 01962 //TODO: Apply button does not correctly work for now, so do not show it 01963 dialog->showButton(KDialog::Apply, false); 01964 QObject::connect(dialog, SIGNAL(applyClicked()), q, SLOT(configDialogFinished()), Qt::UniqueConnection); 01965 QObject::connect(dialog, SIGNAL(okClicked()), q, SLOT(configDialogFinished()), Qt::UniqueConnection); 01966 } 01967 01968 void AppletPrivate::addPublishPage(KConfigDialog *dialog) 01969 { 01970 #ifdef ENABLE_REMOTE_WIDGETS 01971 QWidget *page = new QWidget; 01972 publishUI.setupUi(page); 01973 publishUI.publishCheckbox->setChecked(q->isPublished()); 01974 publishUI.allUsersCheckbox->setEnabled(q->isPublished()); 01975 01976 QString resourceName = 01977 i18nc("%1 is the name of a plasmoid, %2 the name of the machine that plasmoid is published on", 01978 "%1 on %2", q->name(), QHostInfo::localHostName()); 01979 if (AuthorizationManager::self()->d->matchingRule(resourceName, Credentials())) { 01980 publishUI.allUsersCheckbox->setChecked(true); 01981 } else { 01982 publishUI.allUsersCheckbox->setChecked(false); 01983 } 01984 01985 q->connect(publishUI.publishCheckbox, SIGNAL(stateChanged(int)), 01986 q, SLOT(publishCheckboxStateChanged(int))); 01987 dialog->addPage(page, i18n("Share"), "applications-internet"); 01988 #endif 01989 } 01990 01991 void AppletPrivate::publishCheckboxStateChanged(int state) 01992 { 01993 if (state == Qt::Checked) { 01994 publishUI.allUsersCheckbox->setEnabled(true); 01995 } else { 01996 publishUI.allUsersCheckbox->setEnabled(false); 01997 } 01998 } 01999 02000 void AppletPrivate::clearShortcutEditorPtr() 02001 { 02002 shortcutEditor = 0; 02003 } 02004 02005 void AppletPrivate::configDialogFinished() 02006 { 02007 if (shortcutEditor) { 02008 QKeySequence sequence = shortcutEditor->keySequence(); 02009 if (sequence != q->globalShortcut().primary()) { 02010 q->setGlobalShortcut(KShortcut(sequence)); 02011 emit q->configNeedsSaving(); 02012 } 02013 } 02014 02015 #ifdef ENABLE_REMOTE_WIDGETS 02016 if (publishUI.publishCheckbox) { 02017 q->config().writeEntry("Share", publishUI.publishCheckbox->isChecked()); 02018 02019 if (publishUI.publishCheckbox->isChecked()) { 02020 QString resourceName = 02021 i18nc("%1 is the name of a plasmoid, %2 the name of the machine that plasmoid is published on", 02022 "%1 on %2", q->name(), QHostInfo::localHostName()); 02023 q->publish(Plasma::ZeroconfAnnouncement, resourceName); 02024 if (publishUI.allUsersCheckbox->isChecked()) { 02025 if (!AuthorizationManager::self()->d->matchingRule(resourceName, Credentials())) { 02026 AuthorizationRule *rule = new AuthorizationRule(resourceName, ""); 02027 rule->setPolicy(AuthorizationRule::Allow); 02028 rule->setTargets(AuthorizationRule::AllUsers); 02029 AuthorizationManager::self()->d->rules.append(rule); 02030 } 02031 } else { 02032 AuthorizationRule *matchingRule = 02033 AuthorizationManager::self()->d->matchingRule(resourceName, Credentials()); 02034 if (matchingRule) { 02035 AuthorizationManager::self()->d->rules.removeAll(matchingRule); 02036 } 02037 } 02038 } else { 02039 q->unpublish(); 02040 } 02041 } 02042 #endif 02043 02044 if (!configLoader) { 02045 // the config loader will trigger this for us, so we don't need to. 02046 q->configChanged(); 02047 } 02048 } 02049 02050 void AppletPrivate::updateShortcuts() 02051 { 02052 if (isContainment) { 02053 //a horrible hack to avoid clobbering corona settings 02054 //we pull them out, then read, then put them back 02055 QList<QString> names; 02056 QList<QAction*> qactions; 02057 names << "add sibling containment" << "configure shortcuts" << "lock widgets"; 02058 foreach (const QString &name, names) { 02059 QAction *a = actions->action(name); 02060 actions->takeAction(a); //FIXME this is stupid, KActionCollection needs a takeAction(QString) method 02061 qactions << a; 02062 } 02063 02064 actions->readSettings(); 02065 02066 for (int i = 0; i < names.size(); ++i) { 02067 QAction *a = qactions.at(i); 02068 if (a) { 02069 actions->addAction(names.at(i), a); 02070 } 02071 } 02072 } else { 02073 actions->readSettings(); 02074 } 02075 } 02076 02077 void Applet::configChanged() 02078 { 02079 if (d->script && d->configLoader) { 02080 d->configLoader->readConfig(); 02081 d->script->configChanged(); 02082 } 02083 } 02084 02085 void Applet::createConfigurationInterface(KConfigDialog *parent) 02086 { 02087 Q_UNUSED(parent) 02088 // virtual method reimplemented by subclasses. 02089 // do not put anything here ... 02090 } 02091 02092 bool Applet::hasAuthorization(const QString &constraint) const 02093 { 02094 KConfigGroup constraintGroup(KGlobal::config(), "Constraints"); 02095 return constraintGroup.readEntry(constraint, true); 02096 } 02097 02098 void Applet::setAssociatedApplication(const QString &string) 02099 { 02100 AssociatedApplicationManager::self()->setApplication(this, string); 02101 02102 QAction *runAssociatedApplication = d->actions->action("run associated application"); 02103 if (runAssociatedApplication) { 02104 bool valid = AssociatedApplicationManager::self()->appletHasValidAssociatedApplication(this); 02105 valid = valid && hasAuthorization("LaunchApp"); //obey security! 02106 runAssociatedApplication->setVisible(valid); 02107 runAssociatedApplication->setEnabled(valid); 02108 } 02109 } 02110 02111 void Applet::setAssociatedApplicationUrls(const KUrl::List &urls) 02112 { 02113 AssociatedApplicationManager::self()->setUrls(this, urls); 02114 02115 QAction *runAssociatedApplication = d->actions->action("run associated application"); 02116 if (runAssociatedApplication) { 02117 bool valid = AssociatedApplicationManager::self()->appletHasValidAssociatedApplication(this); 02118 valid = valid && hasAuthorization("LaunchApp"); //obey security! 02119 runAssociatedApplication->setVisible(valid); 02120 runAssociatedApplication->setEnabled(valid); 02121 } 02122 } 02123 02124 QString Applet::associatedApplication() const 02125 { 02126 return AssociatedApplicationManager::self()->application(this); 02127 } 02128 02129 KUrl::List Applet::associatedApplicationUrls() const 02130 { 02131 return AssociatedApplicationManager::self()->urls(this); 02132 } 02133 02134 void Applet::runAssociatedApplication() 02135 { 02136 if (hasAuthorization("LaunchApp")) { 02137 AssociatedApplicationManager::self()->run(this); 02138 } 02139 } 02140 02141 bool Applet::hasValidAssociatedApplication() const 02142 { 02143 return AssociatedApplicationManager::self()->appletHasValidAssociatedApplication(this); 02144 } 02145 02146 void AppletPrivate::filterOffers(QList<KService::Ptr> &offers) 02147 { 02148 KConfigGroup constraintGroup(KGlobal::config(), "Constraints"); 02149 foreach (const QString &key, constraintGroup.keyList()) { 02150 //kDebug() << "security constraint" << key; 02151 if (constraintGroup.readEntry(key, true)) { 02152 continue; 02153 } 02154 02155 //ugh. a qlist of ksharedptr<kservice> 02156 QMutableListIterator<KService::Ptr> it(offers); 02157 while (it.hasNext()) { 02158 KService::Ptr p = it.next(); 02159 QString prop = QString("X-Plasma-Requires-").append(key); 02160 QVariant req = p->property(prop, QVariant::String); 02161 //valid values: Required/Optional/Unused 02162 QString reqValue; 02163 if (req.isValid()) { 02164 reqValue = req.toString(); 02165 } else if (p->property("X-Plasma-API").toString().toLower() == "javascript") { 02166 //TODO: be able to check whether or not a script engine provides "controled" 02167 //bindings; for now we just give a pass to the qscript ones 02168 reqValue = "Unused"; 02169 } 02170 02171 if (!(reqValue == "Optional" || reqValue == "Unused")) { 02172 //if (reqValue == "Required") { 02173 it.remove(); 02174 } 02175 } 02176 } 02177 } 02178 02179 QString AppletPrivate::parentAppConstraint(const QString &parentApp) 02180 { 02181 if (parentApp.isEmpty()) { 02182 return QString("((not exist [X-KDE-ParentApp] or [X-KDE-ParentApp] == '') or [X-KDE-ParentApp] == '%1')") 02183 .arg(KGlobal::mainComponent().aboutData()->appName()); 02184 } 02185 02186 return QString("[X-KDE-ParentApp] == '%1'").arg(parentApp); 02187 } 02188 02189 KPluginInfo::List Applet::listAppletInfo(const QString &category, const QString &parentApp) 02190 { 02191 return PluginLoader::pluginLoader()->listAppletInfo(category, parentApp); 02192 } 02193 02194 KPluginInfo::List Applet::listAppletInfoForMimetype(const QString &mimetype) 02195 { 02196 QString constraint = AppletPrivate::parentAppConstraint(); 02197 constraint.append(QString(" and '%1' in [X-Plasma-DropMimeTypes]").arg(mimetype)); 02198 //kDebug() << "listAppletInfoForMimetype with" << mimetype << constraint; 02199 KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint); 02200 AppletPrivate::filterOffers(offers); 02201 return KPluginInfo::fromServices(offers); 02202 } 02203 02204 KPluginInfo::List Applet::listAppletInfoForUrl(const QUrl &url) 02205 { 02206 QString constraint = AppletPrivate::parentAppConstraint(); 02207 constraint.append(" and exist [X-Plasma-DropUrlPatterns]"); 02208 KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint); 02209 AppletPrivate::filterOffers(offers); 02210 02211 KPluginInfo::List allApplets = KPluginInfo::fromServices(offers); 02212 KPluginInfo::List filtered; 02213 foreach (const KPluginInfo &info, allApplets) { 02214 QStringList urlPatterns = info.property("X-Plasma-DropUrlPatterns").toStringList(); 02215 foreach (const QString &glob, urlPatterns) { 02216 QRegExp rx(glob); 02217 rx.setPatternSyntax(QRegExp::Wildcard); 02218 if (rx.exactMatch(url.toString())) { 02219 kDebug() << info.name() << "matches" << glob << url; 02220 filtered << info; 02221 } 02222 } 02223 } 02224 02225 return filtered; 02226 } 02227 02228 QStringList Applet::listCategories(const QString &parentApp, bool visibleOnly) 02229 { 02230 QString constraint = AppletPrivate::parentAppConstraint(parentApp); 02231 constraint.append(" and exist [X-KDE-PluginInfo-Category]"); 02232 02233 KConfigGroup group(KGlobal::config(), "General"); 02234 const QStringList excluded = group.readEntry("ExcludeCategories", QStringList()); 02235 foreach (const QString &category, excluded) { 02236 constraint.append(" and [X-KDE-PluginInfo-Category] != '").append(category).append("'"); 02237 } 02238 02239 KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint); 02240 AppletPrivate::filterOffers(offers); 02241 02242 QStringList categories; 02243 QSet<QString> known = AppletPrivate::knownCategories(); 02244 foreach (const KService::Ptr &applet, offers) { 02245 QString appletCategory = applet->property("X-KDE-PluginInfo-Category").toString(); 02246 if (visibleOnly && applet->noDisplay()) { 02247 // we don't want to show the hidden category 02248 continue; 02249 } 02250 02251 //kDebug() << " and we have " << appletCategory; 02252 if (!appletCategory.isEmpty() && !known.contains(appletCategory.toLower())) { 02253 kDebug() << "Unknown category: " << applet->name() << "says it is in the" 02254 << appletCategory << "category which is unknown to us"; 02255 appletCategory.clear(); 02256 } 02257 02258 if (appletCategory.isEmpty()) { 02259 if (!categories.contains(i18nc("misc category", "Miscellaneous"))) { 02260 categories << i18nc("misc category", "Miscellaneous"); 02261 } 02262 } else if (!categories.contains(appletCategory)) { 02263 categories << appletCategory; 02264 } 02265 } 02266 02267 categories.sort(); 02268 return categories; 02269 } 02270 02271 void Applet::setCustomCategories(const QStringList &categories) 02272 { 02273 AppletPrivate::s_customCategories = QSet<QString>::fromList(categories); 02274 } 02275 02276 QStringList Applet::customCategories() 02277 { 02278 return AppletPrivate::s_customCategories.toList(); 02279 } 02280 02281 Applet *Applet::loadPlasmoid(const QString &path, uint appletId, const QVariantList &args) 02282 { 02283 if (QFile::exists(path + "/metadata.desktop")) { 02284 KService service(path + "/metadata.desktop"); 02285 const QStringList& types = service.serviceTypes(); 02286 02287 if (types.contains("Plasma/Containment")) { 02288 return new Containment(path, appletId, args); 02289 } else if (types.contains("Plasma/PopupApplet")) { 02290 return new PopupApplet(path, appletId, args); 02291 } else { 02292 return new Applet(path, appletId, args); 02293 } 02294 } 02295 02296 return 0; 02297 } 02298 02299 Applet *Applet::load(const QString &appletName, uint appletId, const QVariantList &args) 02300 { 02301 return PluginLoader::pluginLoader()->loadApplet(appletName, appletId, args); 02302 } 02303 02304 Applet *Applet::load(const KPluginInfo &info, uint appletId, const QVariantList &args) 02305 { 02306 if (!info.isValid()) { 02307 return 0; 02308 } 02309 02310 return load(info.pluginName(), appletId, args); 02311 } 02312 02313 QVariant Applet::itemChange(GraphicsItemChange change, const QVariant &value) 02314 { 02315 QVariant ret = QGraphicsWidget::itemChange(change, value); 02316 02317 //kDebug() << change; 02318 switch (change) { 02319 case ItemSceneHasChanged: { 02320 Corona *newCorona = qobject_cast<Corona *>(qvariant_cast<QGraphicsScene*>(value)); 02321 if (newCorona && newCorona->immutability() != Mutable) { 02322 updateConstraints(ImmutableConstraint); 02323 } 02324 } 02325 break; 02326 case ItemParentChange: 02327 if (!d->isContainment) { 02328 Containment *c = containment(); 02329 if (d->mainConfig && !c) { 02330 kWarning() << "Configuration object was requested prior to init(), which is too early. " 02331 "Please fix this item:" << parentItem() << value.value<QGraphicsItem *>() 02332 << name(); 02333 02334 Applet *newC = dynamic_cast<Applet*>(value.value<QGraphicsItem *>()); 02335 if (newC) { 02336 // if this is an applet, and we've just been assigned to our first containment, 02337 // but the applet did something stupid like ask for the config() object prior to 02338 // this happening (e.g. inits ctor) then let's repair that situation for them. 02339 KConfigGroup *old = d->mainConfig; 02340 KConfigGroup appletConfig = newC->config(); 02341 appletConfig = KConfigGroup(&appletConfig, "Applets"); 02342 d->mainConfig = new KConfigGroup(&appletConfig, QString::number(d->appletId)); 02343 old->copyTo(d->mainConfig); 02344 old->deleteGroup(); 02345 delete old; 02346 } 02347 } 02348 } 02349 break; 02350 case ItemPositionHasChanged: 02351 emit geometryChanged(); 02352 // fall through! 02353 case ItemTransformHasChanged: 02354 d->scheduleModificationNotification(); 02355 break; 02356 default: 02357 break; 02358 }; 02359 02360 return ret; 02361 } 02362 02363 QPainterPath Applet::shape() const 02364 { 02365 if (d->script) { 02366 return d->script->shape(); 02367 } 02368 02369 return QGraphicsWidget::shape(); 02370 } 02371 02372 QSizeF Applet::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const 02373 { 02374 QSizeF hint = QGraphicsWidget::sizeHint(which, constraint); 02375 02376 //in panels make sure that the contents won't exit from the panel 02377 if (formFactor() == Horizontal && which == Qt::MinimumSize) { 02378 hint.setHeight(0); 02379 } else if (formFactor() == Vertical && which == Qt::MinimumSize) { 02380 hint.setWidth(0); 02381 } 02382 02383 // enforce a square size in panels 02384 if (d->aspectRatioMode == Plasma::Square) { 02385 if (formFactor() == Horizontal) { 02386 hint.setWidth(size().height()); 02387 } else if (formFactor() == Vertical) { 02388 hint.setHeight(size().width()); 02389 } 02390 } else if (d->aspectRatioMode == Plasma::ConstrainedSquare) { 02391 //enforce a size not wider than tall 02392 if (formFactor() == Horizontal && 02393 (which == Qt::MaximumSize || size().height() <= KIconLoader::SizeLarge)) { 02394 hint.setWidth(size().height()); 02395 //enforce a size not taller than wide 02396 } else if (formFactor() == Vertical && 02397 (which == Qt::MaximumSize || size().width() <= KIconLoader::SizeLarge)) { 02398 hint.setHeight(size().width()); 02399 } 02400 } 02401 02402 return hint; 02403 } 02404 02405 void Applet::hoverEnterEvent(QGraphicsSceneHoverEvent *event) 02406 { 02407 Q_UNUSED(event) 02408 } 02409 02410 void Applet::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) 02411 { 02412 Q_UNUSED(event) 02413 } 02414 02415 void Applet::timerEvent(QTimerEvent *event) 02416 { 02417 if (d->transient) { 02418 d->constraintsTimer.stop(); 02419 d->busyWidgetTimer.stop(); 02420 if (d->modificationsTimer) { 02421 d->modificationsTimer->stop(); 02422 } 02423 return; 02424 } 02425 02426 if (event->timerId() == d->constraintsTimer.timerId()) { 02427 d->constraintsTimer.stop(); 02428 02429 // Don't flushPendingConstraints if we're just starting up 02430 // flushPendingConstraints will be called by Corona 02431 if(!(d->pendingConstraints & Plasma::StartupCompletedConstraint)) { 02432 flushPendingConstraintsEvents(); 02433 } 02434 } else if (d->modificationsTimer && event->timerId() == d->modificationsTimer->timerId()) { 02435 d->modificationsTimer->stop(); 02436 // invalid group, will result in save using the default group 02437 KConfigGroup cg; 02438 02439 save(cg); 02440 emit configNeedsSaving(); 02441 } else if (event->timerId() == d->busyWidgetTimer.timerId()) { 02442 if (!d->busyWidget) { 02443 d->createMessageOverlay(false); 02444 d->messageOverlay->opacity = 0; 02445 02446 QGraphicsLinearLayout *mainLayout = new QGraphicsLinearLayout(d->messageOverlay); 02447 d->busyWidget = new Plasma::BusyWidget(d->messageOverlay); 02448 d->busyWidget->setAcceptHoverEvents(false); 02449 d->busyWidget->setAcceptedMouseButtons(Qt::NoButton); 02450 d->messageOverlay->setAcceptHoverEvents(false); 02451 02452 mainLayout->addStretch(); 02453 mainLayout->addItem(d->busyWidget); 02454 mainLayout->addStretch(); 02455 } 02456 } 02457 } 02458 02459 QRect Applet::screenRect() const 02460 { 02461 QGraphicsView *v = view(); 02462 02463 if (v) { 02464 QPointF bottomRight = pos(); 02465 bottomRight.rx() += size().width(); 02466 bottomRight.ry() += size().height(); 02467 02468 QPoint tL = v->mapToGlobal(v->mapFromScene(pos())); 02469 QPoint bR = v->mapToGlobal(v->mapFromScene(bottomRight)); 02470 return QRect(QPoint(tL.x(), tL.y()), QSize(bR.x() - tL.x(), bR.y() - tL.y())); 02471 } 02472 02473 //The applet doesn't have a view on it. 02474 //So a screenRect isn't relevant. 02475 return QRect(QPoint(0, 0), QSize(0, 0)); 02476 } 02477 02478 void Applet::raise() 02479 { 02480 setZValue(++AppletPrivate::s_maxZValue); 02481 } 02482 02483 void Applet::lower() 02484 { 02485 setZValue(--AppletPrivate::s_minZValue); 02486 } 02487 02488 void AppletPrivate::setIsContainment(bool nowIsContainment, bool forceUpdate) 02489 { 02490 if (isContainment == nowIsContainment && !forceUpdate) { 02491 return; 02492 } 02493 02494 isContainment = nowIsContainment; 02495 //FIXME I do not like this function. 02496 //currently it's only called before ctmt/applet init, with (true,true), and I'm going to assume it stays that way. 02497 //if someone calls it at some other time it'll cause headaches. :P 02498 02499 delete mainConfig; 02500 mainConfig = 0; 02501 02502 Containment *c = q->containment(); 02503 if (c) { 02504 c->d->checkContainmentFurniture(); 02505 } 02506 } 02507 02508 bool Applet::isContainment() const 02509 { 02510 return d->isContainment; 02511 } 02512 02513 // PRIVATE CLASS IMPLEMENTATION 02514 02515 AppletPrivate::AppletPrivate(KService::Ptr service, const KPluginInfo *info, int uniqueID, Applet *applet) 02516 : appletId(uniqueID), 02517 q(applet), 02518 service(0), 02519 preferredBackgroundHints(Applet::StandardBackground), 02520 backgroundHints(Applet::NoBackground), 02521 aspectRatioMode(Plasma::KeepAspectRatio), 02522 immutability(Mutable), 02523 appletDescription(info ? *info : KPluginInfo(service)), 02524 background(0), 02525 mainConfig(0), 02526 pendingConstraints(NoConstraint), 02527 messageOverlay(0), 02528 messageOverlayProxy(0), 02529 busyWidget(0), 02530 script(0), 02531 package(0), 02532 configLoader(0), 02533 actions(AppletPrivate::defaultActions(applet)), 02534 activationAction(0), 02535 shortcutEditor(0), 02536 itemStatus(UnknownStatus), 02537 preferredSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored), 02538 modificationsTimer(0), 02539 hasConfigurationInterface(false), 02540 failed(false), 02541 isContainment(false), 02542 transient(false), 02543 needsConfig(false), 02544 started(false) 02545 { 02546 if (appletId == 0) { 02547 appletId = ++s_maxAppletId; 02548 } else if (appletId > s_maxAppletId) { 02549 s_maxAppletId = appletId; 02550 } 02551 } 02552 02553 AppletPrivate::~AppletPrivate() 02554 { 02555 if (activationAction && activationAction->isGlobalShortcutEnabled()) { 02556 //kDebug() << "reseting global action for" << q->name() << activationAction->objectName(); 02557 activationAction->forgetGlobalShortcut(); 02558 } 02559 02560 delete extender.data(); 02561 02562 delete script; 02563 script = 0; 02564 delete package; 02565 package = 0; 02566 delete configLoader; 02567 configLoader = 0; 02568 delete mainConfig; 02569 mainConfig = 0; 02570 delete modificationsTimer; 02571 } 02572 02573 void AppletPrivate::init(const QString &packagePath) 02574 { 02575 // WARNING: do not access config() OR globalConfig() in this method! 02576 // that requires a scene, which is not available at this point 02577 q->setCacheMode(Applet::DeviceCoordinateCache); 02578 q->setAcceptsHoverEvents(true); 02579 q->setFlag(QGraphicsItem::ItemIsFocusable, true); 02580 q->setFocusPolicy(Qt::ClickFocus); 02581 // FIXME: adding here because nothing seems to be doing it in QGraphicsView, 02582 // but it doesn't actually work anyways =/ 02583 q->setLayoutDirection(qApp->layoutDirection()); 02584 02585 //set a default size before any saved settings are read 02586 QSize size(200, 200); 02587 q->setBackgroundHints(Applet::DefaultBackground); 02588 q->setHasConfigurationInterface(true); //FIXME why not default it to true in the constructor? 02589 02590 QAction *closeApplet = actions->action("remove"); 02591 if (closeApplet) { 02592 closeApplet->setText(i18nc("%1 is the name of the applet", "Remove this %1", q->name())); 02593 } 02594 02595 QAction *configAction = actions->action("configure"); 02596 if (configAction) { 02597 configAction->setText(i18nc("%1 is the name of the applet", "%1 Settings", q->name())); 02598 } 02599 02600 QObject::connect(q, SIGNAL(activate()), q, SLOT(setFocus())); 02601 if (!appletDescription.isValid()) { 02602 kDebug() << "Check your constructor! " 02603 << "You probably want to be passing in a Service::Ptr " 02604 << "or a QVariantList with a valid storageid as arg[0]."; 02605 q->resize(size); 02606 return; 02607 } 02608 02609 QVariant s = appletDescription.property("X-Plasma-DefaultSize"); 02610 if (s.isValid()) { 02611 size = s.toSize(); 02612 } 02613 //kDebug() << "size" << size; 02614 q->resize(size); 02615 02616 QString api = appletDescription.property("X-Plasma-API").toString(); 02617 02618 // we have a scripted plasmoid 02619 if (!api.isEmpty()) { 02620 // find where the Package is 02621 QString path = packagePath; 02622 if (path.isEmpty()) { 02623 QString subPath = q->packageStructure()->defaultPackageRoot() + '/' + appletDescription.pluginName() + '/'; 02624 path = KStandardDirs::locate("data", subPath + "metadata.desktop"); 02625 if (path.isEmpty()) { 02626 path = KStandardDirs::locate("data", subPath); 02627 } else { 02628 path.remove(QString("metadata.desktop")); 02629 } 02630 } else if (!path.endsWith('/')) { 02631 path.append('/'); 02632 } 02633 02634 if (path.isEmpty()) { 02635 q->setFailedToLaunch( 02636 true, 02637 i18nc("Package file, name of the widget", 02638 "Could not locate the %1 package required for the %2 widget.", 02639 appletDescription.pluginName(), appletDescription.name())); 02640 } else { 02641 // create the package and see if we have something real 02642 //kDebug() << "trying for" << path; 02643 PackageStructure::Ptr structure = Plasma::packageStructure(api, Plasma::AppletComponent); 02644 structure->setPath(path); 02645 package = new Package(path, structure); 02646 02647 if (package->isValid()) { 02648 // now we try and set up the script engine. 02649 // it will be parented to this applet and so will get 02650 // deleted when the applet does 02651 02652 script = Plasma::loadScriptEngine(api, q); 02653 if (!script) { 02654 delete package; 02655 package = 0; 02656 q->setFailedToLaunch(true, 02657 i18nc("API or programming language the widget was written in, name of the widget", 02658 "Could not create a %1 ScriptEngine for the %2 widget.", 02659 api, appletDescription.name())); 02660 } 02661 } else { 02662 q->setFailedToLaunch(true, i18nc("Package file, name of the widget", 02663 "Could not open the %1 package required for the %2 widget.", 02664 appletDescription.pluginName(), appletDescription.name())); 02665 delete package; 02666 package = 0; 02667 } 02668 } 02669 } 02670 } 02671 02672 // put all setup routines for script here. at this point we can assume that 02673 // package exists and that we have a script engine 02674 void AppletPrivate::setupScriptSupport() 02675 { 02676 if (!package) { 02677 return; 02678 } 02679 02680 kDebug() << "setting up script support, package is in" << package->path() 02681 << "which is a" << package->structure()->type() << "package" 02682 << ", main script is" << package->filePath("mainscript"); 02683 02684 QString translationsPath = package->filePath("translations"); 02685 if (!translationsPath.isEmpty()) { 02686 //FIXME: we should _probably_ use a KComponentData to segregate the applets 02687 // from each other; but I want to get the basics working first :) 02688 KGlobal::dirs()->addResourceDir("locale", translationsPath); 02689 KGlobal::locale()->insertCatalog(package->metadata().pluginName()); 02690 } 02691 02692 QString xmlPath = package->filePath("mainconfigxml"); 02693 if (!xmlPath.isEmpty()) { 02694 QFile file(xmlPath); 02695 KConfigGroup config = q->config(); 02696 configLoader = new ConfigLoader(&config, &file); 02697 QObject::connect(configLoader, SIGNAL(configChanged()), q, SLOT(configChanged())); 02698 } 02699 02700 if (!package->filePath("mainconfigui").isEmpty()) { 02701 q->setHasConfigurationInterface(true); 02702 } 02703 } 02704 02705 QString AppletPrivate::globalName() const 02706 { 02707 if (!appletDescription.isValid()) { 02708 return QString(); 02709 } 02710 02711 return appletDescription.service()->library(); 02712 } 02713 02714 QString AppletPrivate::instanceName() 02715 { 02716 if (!appletDescription.isValid()) { 02717 return QString(); 02718 } 02719 02720 return appletDescription.service()->library() + QString::number(appletId); 02721 } 02722 02723 void AppletPrivate::scheduleConstraintsUpdate(Plasma::Constraints c) 02724 { 02725 // Don't start up a timer if we're just starting up 02726 // flushPendingConstraints will be called by Corona 02727 if (started && !constraintsTimer.isActive() && !(c & Plasma::StartupCompletedConstraint)) { 02728 constraintsTimer.start(0, q); 02729 } 02730 02731 if (c & Plasma::StartupCompletedConstraint) { 02732 started = true; 02733 } 02734 02735 pendingConstraints |= c; 02736 } 02737 02738 void AppletPrivate::scheduleModificationNotification() 02739 { 02740 // modificationsTimer is not allocated until we get our notice of being started 02741 if (modificationsTimer) { 02742 // schedule a save 02743 if (modificationsTimer->isActive()) { 02744 modificationsTimer->stop(); 02745 } 02746 02747 modificationsTimer->start(1000, q); 02748 } 02749 } 02750 02751 KConfigGroup *AppletPrivate::mainConfigGroup() 02752 { 02753 if (mainConfig) { 02754 return mainConfig; 02755 } 02756 02757 bool newGroup = false; 02758 if (isContainment) { 02759 Corona *corona = qobject_cast<Corona*>(q->scene()); 02760 KConfigGroup containmentConfig; 02761 //kDebug() << "got a corona, baby?" << (QObject*)corona << (QObject*)q; 02762 02763 if (corona) { 02764 containmentConfig = KConfigGroup(corona->config(), "Containments"); 02765 } else { 02766 containmentConfig = KConfigGroup(KGlobal::config(), "Containments"); 02767 } 02768 02769 if (package && !containmentConfig.hasGroup(QString::number(appletId))) { 02770 newGroup = true; 02771 } 02772 02773 mainConfig = new KConfigGroup(&containmentConfig, QString::number(appletId)); 02774 } else { 02775 KConfigGroup appletConfig; 02776 02777 Containment *c = q->containment(); 02778 Applet *parentApplet = qobject_cast<Applet *>(q->parent()); 02779 if (parentApplet && parentApplet != static_cast<Applet *>(c)) { 02780 // this applet is nested inside another applet! use it's config 02781 // as the parent group in the config 02782 appletConfig = parentApplet->config(); 02783 appletConfig = KConfigGroup(&appletConfig, "Applets"); 02784 } else if (c) { 02785 // applet directly in a Containment, as usual 02786 appletConfig = c->config(); 02787 appletConfig = KConfigGroup(&appletConfig, "Applets"); 02788 } else { 02789 kWarning() << "requesting config for" << q->name() << "without a containment!"; 02790 appletConfig = KConfigGroup(KGlobal::config(), "Applets"); 02791 } 02792 02793 if (package && !appletConfig.hasGroup(QString::number(appletId))) { 02794 newGroup = true; 02795 } 02796 02797 mainConfig = new KConfigGroup(&appletConfig, QString::number(appletId)); 02798 } 02799 02800 if (newGroup) { 02801 //see if we have a default configuration in our package 02802 const QString defaultConfigFile = q->package()->filePath("defaultconfig"); 02803 if (!defaultConfigFile.isEmpty()) { 02804 kDebug() << "copying default config: " << q->package()->filePath("defaultconfig"); 02805 KConfigGroup defaultConfig(KSharedConfig::openConfig(defaultConfigFile)->group("Configuration")); 02806 defaultConfig.copyTo(mainConfig); 02807 } 02808 } 02809 02810 return mainConfig; 02811 } 02812 02813 QString AppletPrivate::visibleFailureText(const QString &reason) 02814 { 02815 QString text; 02816 02817 if (reason.isEmpty()) { 02818 text = i18n("This object could not be created."); 02819 } else { 02820 text = i18n("This object could not be created for the following reason:<p><b>%1</b></p>", reason); 02821 } 02822 02823 return text; 02824 } 02825 02826 void AppletPrivate::themeChanged() 02827 { 02828 if (background) { 02829 //do again the translucent background fallback 02830 q->setBackgroundHints(backgroundHints); 02831 02832 qreal left; 02833 qreal right; 02834 qreal top; 02835 qreal bottom; 02836 background->getMargins(left, top, right, bottom); 02837 q->setContentsMargins(left, right, top, bottom); 02838 } 02839 q->update(); 02840 } 02841 02842 void AppletPrivate::resetConfigurationObject() 02843 { 02844 // make sure mainConfigGroup exists in all cases 02845 mainConfigGroup(); 02846 02847 mainConfig->deleteGroup(); 02848 delete mainConfig; 02849 mainConfig = 0; 02850 02851 Corona * corona = qobject_cast<Corona*>(q->scene()); 02852 if (corona) { 02853 corona->requireConfigSync(); 02854 } 02855 } 02856 02857 uint AppletPrivate::s_maxAppletId = 0; 02858 int AppletPrivate::s_maxZValue = 0; 02859 int AppletPrivate::s_minZValue = 0; 02860 PackageStructure::Ptr AppletPrivate::packageStructure(0); 02861 QSet<QString> AppletPrivate::s_customCategories; 02862 02863 AppletOverlayWidget::AppletOverlayWidget(QGraphicsWidget *parent) 02864 : QGraphicsWidget(parent), 02865 opacity(0.4) 02866 { 02867 resize(parent->size()); 02868 } 02869 02870 void AppletOverlayWidget::destroy() 02871 { 02872 Animation *anim = Plasma::Animator::create(Plasma::Animator::DisappearAnimation); 02873 if (anim) { 02874 connect(anim, SIGNAL(finished()), this, SLOT(overlayAnimationComplete())); 02875 anim->setTargetWidget(this); 02876 anim->start(); 02877 } else { 02878 overlayAnimationComplete(); 02879 } 02880 } 02881 02882 void AppletOverlayWidget::mousePressEvent(QGraphicsSceneMouseEvent *event) 02883 { 02884 event->accept(); 02885 } 02886 02887 void AppletOverlayWidget::overlayAnimationComplete() 02888 { 02889 if (scene()) { 02890 scene()->removeItem(this); 02891 } 02892 deleteLater(); 02893 } 02894 02895 void AppletOverlayWidget::paint(QPainter *painter, 02896 const QStyleOptionGraphicsItem *option, 02897 QWidget *widget) 02898 { 02899 Q_UNUSED(option) 02900 Q_UNUSED(widget) 02901 02902 if (qFuzzyCompare(1, 1+opacity)) { 02903 return; 02904 } 02905 02906 QColor wash = Plasma::Theme::defaultTheme()->color(Theme::BackgroundColor); 02907 wash.setAlphaF(opacity); 02908 02909 Applet *applet = qobject_cast<Applet *>(parentWidget()); 02910 02911 02912 QPainterPath backgroundShape; 02913 if (!applet || applet->backgroundHints() & Applet::StandardBackground) { 02914 //FIXME: a resize here is nasty, but perhaps still better than an eventfilter just for that.. 02915 if (parentWidget()->contentsRect().size() != size()) { 02916 resize(parentWidget()->contentsRect().size()); 02917 } 02918 backgroundShape = PaintUtils::roundedRectangle(contentsRect(), 5); 02919 } else { 02920 backgroundShape = shape(); 02921 } 02922 02923 painter->setRenderHints(QPainter::Antialiasing); 02924 painter->fillPath(backgroundShape, wash); 02925 } 02926 02927 #if QT_VERSION >= 0x040700 02928 // in QGraphicsWidget now; preserve BC by implementing it as a protected method 02929 void Applet::geometryChanged() 02930 { 02931 emit QGraphicsWidget::geometryChanged(); 02932 } 02933 #endif 02934 02935 } // Plasma namespace 02936 02937 #include "applet.moc" 02938 #include "private/applet_p.moc"
KDE 4.6 API Reference