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