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 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 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 QGraphicsSceneHoverEvent *he = static_cast<QGraphicsSceneHoverEvent*>(event); 01787 if (d->handle) { 01788 d->handle.data()->setHoverPos(he->pos()); 01789 } else { 01790 //kDebug() << "generated applet handle"; 01791 AppletHandle *handle = new AppletHandle(containment(), this, he->pos()); 01792 connect(handle, SIGNAL(disappearDone(AppletHandle*)), 01793 this, SLOT(handleDisappeared(AppletHandle*))); 01794 connect(this, SIGNAL(geometryChanged()), 01795 handle, SLOT(appletResized())); 01796 d->handle = handle; 01797 } 01798 } 01799 break; 01800 01801 case QEvent::GraphicsSceneHoverMove: 01802 if (d->handle && !d->handle.data()->shown() && immutability() == Mutable) { 01803 QGraphicsSceneHoverEvent *he = static_cast<QGraphicsSceneHoverEvent*>(event); 01804 d->handle.data()->setHoverPos(he->pos()); 01805 } 01806 break; 01807 01808 default: 01809 break; 01810 } 01811 01812 } 01813 01814 switch (event->type()) { 01815 case QEvent::GraphicsSceneMouseMove: 01816 case QEvent::GraphicsSceneMousePress: 01817 case QEvent::GraphicsSceneMouseRelease: 01818 { 01819 // don't move when the containment is not mutable, 01820 // in the rare case the containment doesn't exists consider it as mutable 01821 if ((flags() & ItemIsMovable) && d->registeredAsDragHandle.contains(watched)) { 01822 Containment *c = containment(); 01823 if (!c || c->immutability() == Mutable) { 01824 scene()->sendEvent(this, event); 01825 return false; 01826 } 01827 } 01828 break; 01829 } 01830 01831 default: 01832 break; 01833 } 01834 01835 return QGraphicsItem::sceneEventFilter(watched, event); 01836 } 01837 01838 void Applet::mouseMoveEvent(QGraphicsSceneMouseEvent *event) 01839 { 01840 if (immutability() == Mutable && formFactor() == Plasma::Planar && (flags() & ItemIsMovable)) { 01841 QGraphicsWidget::mouseMoveEvent(event); 01842 } 01843 } 01844 01845 void Applet::focusInEvent(QFocusEvent *event) 01846 { 01847 if (!isContainment() && containment()) { 01848 //focusing an applet may trigger this event again, but we won't be here more than twice 01849 containment()->d->focusApplet(this); 01850 } 01851 01852 QGraphicsWidget::focusInEvent(event); 01853 } 01854 01855 void Applet::resizeEvent(QGraphicsSceneResizeEvent *event) 01856 { 01857 QGraphicsWidget::resizeEvent(event); 01858 01859 if (d->background) { 01860 d->background->resizeFrame(boundingRect().size()); 01861 } 01862 01863 updateConstraints(Plasma::SizeConstraint); 01864 01865 d->scheduleModificationNotification(); 01866 emit geometryChanged(); 01867 } 01868 01869 bool Applet::isUserConfiguring() const 01870 { 01871 return KConfigDialog::exists(d->configDialogId()); 01872 } 01873 01874 void Applet::showConfigurationInterface() 01875 { 01876 if (!hasConfigurationInterface()) { 01877 return; 01878 } 01879 01880 if (immutability() != Mutable && !KAuthorized::authorize("plasma/allow_configure_when_locked")) { 01881 return; 01882 } 01883 01884 KConfigDialog *dlg = KConfigDialog::exists(d->configDialogId()); 01885 01886 if (dlg) { 01887 KWindowSystem::setOnDesktop(dlg->winId(), KWindowSystem::currentDesktop()); 01888 dlg->show(); 01889 KWindowSystem::activateWindow(dlg->winId()); 01890 return; 01891 } 01892 01893 d->publishUI.publishCheckbox = 0; 01894 if (d->package) { 01895 KConfigDialog *dialog = 0; 01896 01897 const QString uiFile = d->package->filePath("mainconfigui"); 01898 KDesktopFile df(d->package->path() + "/metadata.desktop"); 01899 const QStringList kcmPlugins = df.desktopGroup().readEntry("X-Plasma-ConfigPlugins", QStringList()); 01900 if (!uiFile.isEmpty() || !kcmPlugins.isEmpty()) { 01901 KConfigSkeleton *configLoader = d->configLoader ? d->configLoader : new KConfigSkeleton(0); 01902 dialog = new AppletConfigDialog(0, d->configDialogId(), configLoader); 01903 01904 if (!d->configLoader) { 01905 // delete the temporary when this dialog is done 01906 configLoader->setParent(dialog); 01907 } 01908 01909 dialog->setWindowTitle(d->configWindowTitle()); 01910 dialog->setAttribute(Qt::WA_DeleteOnClose, true); 01911 bool hasPages = false; 01912 01913 QFile f(uiFile); 01914 QUiLoader loader; 01915 QWidget *w = loader.load(&f); 01916 if (w) { 01917 dialog->addPage(w, i18n("Settings"), icon(), i18n("%1 Settings", name())); 01918 hasPages = true; 01919 } 01920 01921 foreach (const QString &kcm, kcmPlugins) { 01922 #ifndef PLASMA_NO_KUTILS 01923 KCModuleProxy *module = new KCModuleProxy(kcm); 01924 if (module->realModule()) { 01925 connect(module, SIGNAL(changed(bool)), dialog, SLOT(settingsModified(bool))); 01926 dialog->addPage(module, module->moduleInfo().moduleName(), module->moduleInfo().icon()); 01927 hasPages = true; 01928 } else { 01929 delete module; 01930 } 01931 #else 01932 KService::Ptr service = KService::serviceByStorageId(kcm); 01933 if (service) { 01934 QString error; 01935 KCModule *module = service->createInstance<KCModule>(dialog, QVariantList(), &error); 01936 if (module) { 01937 connect(module, SIGNAL(changed(bool)), dialog, SLOT(settingsModified(bool))); 01938 dialog->addPage(module, service->name(), service->icon()); 01939 hasPages = true; 01940 } else { 01941 #ifndef NDEBUG 01942 kDebug() << "failed to load kcm" << kcm << "for" << name(); 01943 #endif 01944 } 01945 } 01946 #endif 01947 } 01948 01949 if (hasPages) { 01950 d->addGlobalShortcutsPage(dialog); 01951 d->addPublishPage(dialog); 01952 dialog->show(); 01953 } else { 01954 delete dialog; 01955 dialog = 0; 01956 } 01957 } 01958 01959 if (!dialog && d->script) { 01960 d->script->showConfigurationInterface(); 01961 } 01962 } else if (d->script) { 01963 d->script->showConfigurationInterface(); 01964 } else { 01965 KConfigDialog *dialog = d->generateGenericConfigDialog(); 01966 d->addStandardConfigurationPages(dialog); 01967 showConfigurationInterface(dialog); 01968 } 01969 01970 emit releaseVisualFocus(); 01971 } 01972 01973 void Applet::showConfigurationInterface(QWidget *widget) 01974 { 01975 if (!containment() || !containment()->corona() || 01976 !containment()->corona()->dialogManager()) { 01977 widget->show(); 01978 return; 01979 } 01980 01981 QMetaObject::invokeMethod(containment()->corona()->dialogManager(), "showDialog", Q_ARG(QWidget *, widget), Q_ARG(Plasma::Applet *, this)); 01982 } 01983 01984 QString AppletPrivate::configDialogId() const 01985 { 01986 return QString("%1settings%2").arg(appletId).arg(q->name()); 01987 } 01988 01989 QString AppletPrivate::configWindowTitle() const 01990 { 01991 return i18nc("@title:window", "%1 Settings", q->name()); 01992 } 01993 01994 QSet<QString> AppletPrivate::knownCategories() 01995 { 01996 // this is to trick the tranlsation tools into making the correct 01997 // strings for translation 01998 QSet<QString> categories = s_customCategories; 01999 categories << QString(I18N_NOOP("Accessibility")).toLower() 02000 << QString(I18N_NOOP("Application Launchers")).toLower() 02001 << QString(I18N_NOOP("Astronomy")).toLower() 02002 << QString(I18N_NOOP("Date and Time")).toLower() 02003 << QString(I18N_NOOP("Development Tools")).toLower() 02004 << QString(I18N_NOOP("Education")).toLower() 02005 << QString(I18N_NOOP("Environment and Weather")).toLower() 02006 << QString(I18N_NOOP("Examples")).toLower() 02007 << QString(I18N_NOOP("File System")).toLower() 02008 << QString(I18N_NOOP("Fun and Games")).toLower() 02009 << QString(I18N_NOOP("Graphics")).toLower() 02010 << QString(I18N_NOOP("Language")).toLower() 02011 << QString(I18N_NOOP("Mapping")).toLower() 02012 << QString(I18N_NOOP("Miscellaneous")).toLower() 02013 << QString(I18N_NOOP("Multimedia")).toLower() 02014 << QString(I18N_NOOP("Online Services")).toLower() 02015 << QString(I18N_NOOP("Productivity")).toLower() 02016 << QString(I18N_NOOP("System Information")).toLower() 02017 << QString(I18N_NOOP("Utilities")).toLower() 02018 << QString(I18N_NOOP("Windows and Tasks")).toLower(); 02019 return categories; 02020 } 02021 02022 KConfigDialog *AppletPrivate::generateGenericConfigDialog() 02023 { 02024 KConfigSkeleton *nullManager = new KConfigSkeleton(0); 02025 KConfigDialog *dialog = new AppletConfigDialog(0, configDialogId(), nullManager); 02026 nullManager->setParent(dialog); 02027 dialog->setFaceType(KPageDialog::Auto); 02028 dialog->setWindowTitle(configWindowTitle()); 02029 dialog->setAttribute(Qt::WA_DeleteOnClose, true); 02030 q->createConfigurationInterface(dialog); 02031 dialog->showButton(KDialog::Default, false); 02032 dialog->showButton(KDialog::Help, false); 02033 QObject::connect(dialog, SIGNAL(applyClicked()), q, SLOT(configDialogFinished())); 02034 QObject::connect(dialog, SIGNAL(okClicked()), q, SLOT(configDialogFinished())); 02035 return dialog; 02036 } 02037 02038 void AppletPrivate::addStandardConfigurationPages(KConfigDialog *dialog) 02039 { 02040 addGlobalShortcutsPage(dialog); 02041 addPublishPage(dialog); 02042 } 02043 02044 void AppletPrivate::addGlobalShortcutsPage(KConfigDialog *dialog) 02045 { 02046 #ifndef PLASMA_NO_GLOBAL_SHORTCUTS 02047 if (isContainment) { 02048 return; 02049 } 02050 02051 QWidget *page = new QWidget; 02052 QVBoxLayout *layout = new QVBoxLayout(page); 02053 02054 if (!shortcutEditor) { 02055 shortcutEditor = new KKeySequenceWidget(page); 02056 QObject::connect(shortcutEditor.data(), SIGNAL(keySequenceChanged(QKeySequence)), dialog, SLOT(settingsModified())); 02057 } 02058 02059 shortcutEditor.data()->setKeySequence(q->globalShortcut().primary()); 02060 layout->addWidget(shortcutEditor.data()); 02061 layout->addStretch(); 02062 dialog->addPage(page, i18n("Keyboard Shortcut"), "preferences-desktop-keyboard"); 02063 02064 QObject::connect(dialog, SIGNAL(applyClicked()), q, SLOT(configDialogFinished()), Qt::UniqueConnection); 02065 QObject::connect(dialog, SIGNAL(okClicked()), q, SLOT(configDialogFinished()), Qt::UniqueConnection); 02066 #endif 02067 } 02068 02069 void AppletPrivate::addPublishPage(KConfigDialog *dialog) 02070 { 02071 #ifdef ENABLE_REMOTE_WIDGETS 02072 QWidget *page = new QWidget; 02073 publishUI.setupUi(page); 02074 publishUI.publishCheckbox->setChecked(q->isPublished()); 02075 QObject::connect(publishUI.publishCheckbox, SIGNAL(clicked(bool)), dialog, SLOT(settingsModified())); 02076 publishUI.allUsersCheckbox->setEnabled(q->isPublished()); 02077 QObject::connect(publishUI.allUsersCheckbox, SIGNAL(clicked(bool)), dialog, SLOT(settingsModified())); 02078 02079 QString resourceName = 02080 i18nc("%1 is the name of a plasmoid, %2 the name of the machine that plasmoid is published on", 02081 "%1 on %2", q->name(), QHostInfo::localHostName()); 02082 if (AuthorizationManager::self()->d->matchingRule(resourceName, Credentials())) { 02083 publishUI.allUsersCheckbox->setChecked(true); 02084 } else { 02085 publishUI.allUsersCheckbox->setChecked(false); 02086 } 02087 02088 q->connect(publishUI.publishCheckbox, SIGNAL(stateChanged(int)), 02089 q, SLOT(publishCheckboxStateChanged(int))); 02090 dialog->addPage(page, i18n("Share"), "applications-internet"); 02091 #endif 02092 } 02093 02094 void AppletPrivate::publishCheckboxStateChanged(int state) 02095 { 02096 if (state == Qt::Checked) { 02097 publishUI.allUsersCheckbox->setEnabled(true); 02098 } else { 02099 publishUI.allUsersCheckbox->setEnabled(false); 02100 } 02101 } 02102 02103 void AppletPrivate::configDialogFinished() 02104 { 02105 if (shortcutEditor) { 02106 QKeySequence sequence = shortcutEditor.data()->keySequence(); 02107 if (sequence != q->globalShortcut().primary()) { 02108 q->setGlobalShortcut(KShortcut(sequence)); 02109 emit q->configNeedsSaving(); 02110 } 02111 } 02112 02113 #ifdef ENABLE_REMOTE_WIDGETS 02114 if (KConfigDialog::exists(configDialogId()) && publishUI.publishCheckbox) { 02115 q->config().writeEntry("Share", publishUI.publishCheckbox->isChecked()); 02116 02117 if (publishUI.publishCheckbox->isChecked()) { 02118 QString resourceName = 02119 i18nc("%1 is the name of a plasmoid, %2 the name of the machine that plasmoid is published on", 02120 "%1 on %2", q->name(), QHostInfo::localHostName()); 02121 q->publish(Plasma::ZeroconfAnnouncement, resourceName); 02122 if (publishUI.allUsersCheckbox->isChecked()) { 02123 if (!AuthorizationManager::self()->d->matchingRule(resourceName, Credentials())) { 02124 AuthorizationRule *rule = new AuthorizationRule(resourceName, ""); 02125 rule->setPolicy(AuthorizationRule::Allow); 02126 rule->setTargets(AuthorizationRule::AllUsers); 02127 AuthorizationManager::self()->d->rules.append(rule); 02128 } 02129 } else { 02130 AuthorizationRule *matchingRule = 02131 AuthorizationManager::self()->d->matchingRule(resourceName, Credentials()); 02132 if (matchingRule) { 02133 AuthorizationManager::self()->d->rules.removeAll(matchingRule); 02134 } 02135 } 02136 } else { 02137 q->unpublish(); 02138 } 02139 } 02140 #endif 02141 02142 if (!configLoader) { 02143 // the config loader will trigger this for us, so we don't need to. 02144 propagateConfigChanged(); 02145 if (KConfigDialog *dialog = qobject_cast<KConfigDialog *>(q->sender())) { 02146 dialog->enableButton(KDialog::Apply, false); 02147 } 02148 } 02149 } 02150 02151 void AppletPrivate::updateShortcuts() 02152 { 02153 if (isContainment) { 02154 //a horrible hack to avoid clobbering corona settings 02155 //we pull them out, then read, then put them back 02156 QList<QString> names; 02157 QList<QAction*> qactions; 02158 names << "add sibling containment" << "configure shortcuts" << "lock widgets"; 02159 foreach (const QString &name, names) { 02160 QAction *a = actions->action(name); 02161 actions->takeAction(a); //FIXME this is stupid, KActionCollection needs a takeAction(QString) method 02162 qactions << a; 02163 } 02164 02165 actions->readSettings(); 02166 02167 for (int i = 0; i < names.size(); ++i) { 02168 QAction *a = qactions.at(i); 02169 if (a) { 02170 actions->addAction(names.at(i), a); 02171 } 02172 } 02173 } else { 02174 actions->readSettings(); 02175 } 02176 } 02177 02178 void AppletPrivate::propagateConfigChanged() 02179 { 02180 if (isContainment) { 02181 Containment *c = qobject_cast<Containment *>(q); 02182 if (c) { 02183 c->d->configChanged(); 02184 } 02185 } 02186 02187 q->configChanged(); 02188 } 02189 02190 void Applet::configChanged() 02191 { 02192 if (d->script) { 02193 if (d->configLoader) { 02194 d->configLoader->readConfig(); 02195 } 02196 d->script->configChanged(); 02197 } 02198 } 02199 02200 void Applet::createConfigurationInterface(KConfigDialog *parent) 02201 { 02202 Q_UNUSED(parent) 02203 // virtual method reimplemented by subclasses. 02204 // do not put anything here ... 02205 } 02206 02207 bool Applet::hasAuthorization(const QString &constraint) const 02208 { 02209 KConfigGroup constraintGroup(KGlobal::config(), "Constraints"); 02210 return constraintGroup.readEntry(constraint, true); 02211 } 02212 02213 void Applet::setAssociatedApplication(const QString &string) 02214 { 02215 AssociatedApplicationManager::self()->setApplication(this, string); 02216 02217 QAction *runAssociatedApplication = d->actions->action("run associated application"); 02218 if (runAssociatedApplication) { 02219 bool valid = AssociatedApplicationManager::self()->appletHasValidAssociatedApplication(this); 02220 valid = valid && hasAuthorization("LaunchApp"); //obey security! 02221 runAssociatedApplication->setVisible(valid); 02222 runAssociatedApplication->setEnabled(valid); 02223 } 02224 } 02225 02226 void Applet::setAssociatedApplicationUrls(const KUrl::List &urls) 02227 { 02228 AssociatedApplicationManager::self()->setUrls(this, urls); 02229 02230 QAction *runAssociatedApplication = d->actions->action("run associated application"); 02231 if (runAssociatedApplication) { 02232 bool valid = AssociatedApplicationManager::self()->appletHasValidAssociatedApplication(this); 02233 valid = valid && hasAuthorization("LaunchApp"); //obey security! 02234 runAssociatedApplication->setVisible(valid); 02235 runAssociatedApplication->setEnabled(valid); 02236 } 02237 } 02238 02239 QString Applet::associatedApplication() const 02240 { 02241 return AssociatedApplicationManager::self()->application(this); 02242 } 02243 02244 KUrl::List Applet::associatedApplicationUrls() const 02245 { 02246 return AssociatedApplicationManager::self()->urls(this); 02247 } 02248 02249 void Applet::runAssociatedApplication() 02250 { 02251 if (hasAuthorization("LaunchApp")) { 02252 AssociatedApplicationManager::self()->run(this); 02253 } 02254 } 02255 02256 bool Applet::hasValidAssociatedApplication() const 02257 { 02258 return AssociatedApplicationManager::self()->appletHasValidAssociatedApplication(this); 02259 } 02260 02261 void AppletPrivate::filterOffers(QList<KService::Ptr> &offers) 02262 { 02263 KConfigGroup constraintGroup(KGlobal::config(), "Constraints"); 02264 foreach (const QString &key, constraintGroup.keyList()) { 02265 //kDebug() << "security constraint" << key; 02266 if (constraintGroup.readEntry(key, true)) { 02267 continue; 02268 } 02269 02270 //ugh. a qlist of ksharedptr<kservice> 02271 QMutableListIterator<KService::Ptr> it(offers); 02272 while (it.hasNext()) { 02273 KService::Ptr p = it.next(); 02274 QString prop = QString("X-Plasma-Requires-").append(key); 02275 QVariant req = p->property(prop, QVariant::String); 02276 //valid values: Required/Optional/Unused 02277 QString reqValue; 02278 if (req.isValid()) { 02279 reqValue = req.toString(); 02280 } else if (p->property("X-Plasma-API").toString().toLower() == "javascript") { 02281 //TODO: be able to check whether or not a script engine provides "controled" 02282 //bindings; for now we just give a pass to the qscript ones 02283 reqValue = "Unused"; 02284 } 02285 02286 if (!(reqValue == "Optional" || reqValue == "Unused")) { 02287 //if (reqValue == "Required") { 02288 it.remove(); 02289 } 02290 } 02291 } 02292 } 02293 02294 QString AppletPrivate::parentAppConstraint(const QString &parentApp) 02295 { 02296 if (parentApp.isEmpty()) { 02297 return QString("((not exist [X-KDE-ParentApp] or [X-KDE-ParentApp] == '') or [X-KDE-ParentApp] == '%1')") 02298 .arg(KGlobal::mainComponent().aboutData()->appName()); 02299 } 02300 02301 return QString("[X-KDE-ParentApp] == '%1'").arg(parentApp); 02302 } 02303 02304 KPluginInfo::List Applet::listAppletInfo(const QString &category, const QString &parentApp) 02305 { 02306 return PluginLoader::pluginLoader()->listAppletInfo(category, parentApp); 02307 } 02308 02309 KPluginInfo::List Applet::listAppletInfoForMimetype(const QString &mimetype) 02310 { 02311 QString constraint = AppletPrivate::parentAppConstraint(); 02312 constraint.append(QString(" and '%1' in [X-Plasma-DropMimeTypes]").arg(mimetype)); 02313 //kDebug() << "listAppletInfoForMimetype with" << mimetype << constraint; 02314 KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint); 02315 AppletPrivate::filterOffers(offers); 02316 return KPluginInfo::fromServices(offers); 02317 } 02318 02319 KPluginInfo::List Applet::listAppletInfoForUrl(const QUrl &url) 02320 { 02321 QString constraint = AppletPrivate::parentAppConstraint(); 02322 constraint.append(" and exist [X-Plasma-DropUrlPatterns]"); 02323 KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint); 02324 AppletPrivate::filterOffers(offers); 02325 02326 KPluginInfo::List allApplets = KPluginInfo::fromServices(offers); 02327 KPluginInfo::List filtered; 02328 foreach (const KPluginInfo &info, allApplets) { 02329 QStringList urlPatterns = info.property("X-Plasma-DropUrlPatterns").toStringList(); 02330 foreach (const QString &glob, urlPatterns) { 02331 QRegExp rx(glob); 02332 rx.setPatternSyntax(QRegExp::Wildcard); 02333 if (rx.exactMatch(url.toString())) { 02334 kDebug() << info.name() << "matches" << glob << url; 02335 filtered << info; 02336 } 02337 } 02338 } 02339 02340 return filtered; 02341 } 02342 02343 QStringList Applet::listCategories(const QString &parentApp, bool visibleOnly) 02344 { 02345 QString constraint = AppletPrivate::parentAppConstraint(parentApp); 02346 constraint.append(" and exist [X-KDE-PluginInfo-Category]"); 02347 02348 KConfigGroup group(KGlobal::config(), "General"); 02349 const QStringList excluded = group.readEntry("ExcludeCategories", QStringList()); 02350 foreach (const QString &category, excluded) { 02351 constraint.append(" and [X-KDE-PluginInfo-Category] != '").append(category).append("'"); 02352 } 02353 02354 KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint); 02355 AppletPrivate::filterOffers(offers); 02356 02357 QStringList categories; 02358 QSet<QString> known = AppletPrivate::knownCategories(); 02359 foreach (const KService::Ptr &applet, offers) { 02360 QString appletCategory = applet->property("X-KDE-PluginInfo-Category").toString(); 02361 if (visibleOnly && applet->noDisplay()) { 02362 // we don't want to show the hidden category 02363 continue; 02364 } 02365 02366 //kDebug() << " and we have " << appletCategory; 02367 if (!appletCategory.isEmpty() && !known.contains(appletCategory.toLower())) { 02368 kDebug() << "Unknown category: " << applet->name() << "says it is in the" 02369 << appletCategory << "category which is unknown to us"; 02370 appletCategory.clear(); 02371 } 02372 02373 if (appletCategory.isEmpty()) { 02374 if (!categories.contains(i18nc("misc category", "Miscellaneous"))) { 02375 categories << i18nc("misc category", "Miscellaneous"); 02376 } 02377 } else if (!categories.contains(appletCategory)) { 02378 categories << appletCategory; 02379 } 02380 } 02381 02382 categories.sort(); 02383 return categories; 02384 } 02385 02386 void Applet::setCustomCategories(const QStringList &categories) 02387 { 02388 AppletPrivate::s_customCategories = QSet<QString>::fromList(categories); 02389 } 02390 02391 QStringList Applet::customCategories() 02392 { 02393 return AppletPrivate::s_customCategories.toList(); 02394 } 02395 02396 Applet *Applet::loadPlasmoid(const QString &path, uint appletId, const QVariantList &args) 02397 { 02398 if (QFile::exists(path + "/metadata.desktop")) { 02399 KService service(path + "/metadata.desktop"); 02400 const QStringList& types = service.serviceTypes(); 02401 02402 if (types.contains("Plasma/Containment")) { 02403 return new Containment(path, appletId, args); 02404 } else if (types.contains("Plasma/PopupApplet")) { 02405 return new PopupApplet(path, appletId, args); 02406 } else { 02407 return new Applet(path, appletId, args); 02408 } 02409 } 02410 02411 return 0; 02412 } 02413 02414 Applet *Applet::load(const QString &appletName, uint appletId, const QVariantList &args) 02415 { 02416 return PluginLoader::pluginLoader()->loadApplet(appletName, appletId, args); 02417 } 02418 02419 Applet *Applet::load(const KPluginInfo &info, uint appletId, const QVariantList &args) 02420 { 02421 if (!info.isValid()) { 02422 return 0; 02423 } 02424 02425 return load(info.pluginName(), appletId, args); 02426 } 02427 02428 QVariant Applet::itemChange(GraphicsItemChange change, const QVariant &value) 02429 { 02430 QVariant ret = QGraphicsWidget::itemChange(change, value); 02431 02432 //kDebug() << change; 02433 switch (change) { 02434 case ItemSceneHasChanged: { 02435 Corona *newCorona = qobject_cast<Corona *>(qvariant_cast<QGraphicsScene*>(value)); 02436 if (newCorona && newCorona->immutability() != Mutable) { 02437 updateConstraints(ImmutableConstraint); 02438 } 02439 } 02440 break; 02441 case ItemParentChange: 02442 if (!d->isContainment) { 02443 Containment *c = containment(); 02444 if (d->mainConfig && !c) { 02445 kWarning() << "Configuration object was requested prior to init(), which is too early. " 02446 "Please fix this item:" << parentItem() << value.value<QGraphicsItem *>() 02447 << name(); 02448 02449 Applet *newC = dynamic_cast<Applet*>(value.value<QGraphicsItem *>()); 02450 if (newC) { 02451 // if this is an applet, and we've just been assigned to our first containment, 02452 // but the applet did something stupid like ask for the config() object prior to 02453 // this happening (e.g. inits ctor) then let's repair that situation for them. 02454 KConfigGroup *old = d->mainConfig; 02455 KConfigGroup appletConfig = newC->config(); 02456 appletConfig = KConfigGroup(&appletConfig, "Applets"); 02457 d->mainConfig = new KConfigGroup(&appletConfig, QString::number(d->appletId)); 02458 old->copyTo(d->mainConfig); 02459 old->deleteGroup(); 02460 delete old; 02461 } 02462 } 02463 } 02464 break; 02465 case ItemParentHasChanged: 02466 { 02467 if (isContainment()) { 02468 removeSceneEventFilter(this); 02469 } else { 02470 Containment *c = containment(); 02471 if (c && c->containmentType() == Containment::DesktopContainment) { 02472 installSceneEventFilter(this); 02473 } else { 02474 removeSceneEventFilter(this); 02475 } 02476 } 02477 } 02478 break; 02479 case ItemPositionHasChanged: 02480 emit geometryChanged(); 02481 // fall through! 02482 case ItemTransformHasChanged: 02483 d->scheduleModificationNotification(); 02484 break; 02485 default: 02486 break; 02487 }; 02488 02489 return ret; 02490 } 02491 02492 QPainterPath Applet::shape() const 02493 { 02494 if (d->script) { 02495 return d->script->shape(); 02496 } 02497 02498 return QGraphicsWidget::shape(); 02499 } 02500 02501 QSizeF Applet::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const 02502 { 02503 QSizeF hint = QGraphicsWidget::sizeHint(which, constraint); 02504 const FormFactor ff = formFactor(); 02505 02506 // in panels make sure that the contents won't exit from the panel 02507 if (which == Qt::MinimumSize) { 02508 if (ff == Horizontal) { 02509 hint.setHeight(0); 02510 } else if (ff == Vertical) { 02511 hint.setWidth(0); 02512 } 02513 } 02514 02515 // enforce a square size in panels 02516 if (d->aspectRatioMode == Plasma::Square) { 02517 if (ff == Horizontal) { 02518 hint.setWidth(size().height()); 02519 } else if (ff == Vertical) { 02520 hint.setHeight(size().width()); 02521 } 02522 } else if (d->aspectRatioMode == Plasma::ConstrainedSquare) { 02523 //enforce a size not wider than tall 02524 if (ff == Horizontal) { 02525 hint.setWidth(size().height()); 02526 //enforce a size not taller than wide 02527 } else if (ff == Vertical && (which == Qt::MaximumSize || size().width() <= KIconLoader::SizeLarge)) { 02528 hint.setHeight(size().width()); 02529 } 02530 } 02531 02532 return hint; 02533 } 02534 02535 void Applet::hoverEnterEvent(QGraphicsSceneHoverEvent *event) 02536 { 02537 Q_UNUSED(event) 02538 } 02539 02540 void Applet::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) 02541 { 02542 Q_UNUSED(event) 02543 } 02544 02545 void Applet::timerEvent(QTimerEvent *event) 02546 { 02547 if (d->transient) { 02548 d->constraintsTimer.stop(); 02549 d->busyWidgetTimer.stop(); 02550 if (d->modificationsTimer) { 02551 d->modificationsTimer->stop(); 02552 } 02553 return; 02554 } 02555 02556 if (event->timerId() == d->constraintsTimer.timerId()) { 02557 d->constraintsTimer.stop(); 02558 02559 // Don't flushPendingConstraints if we're just starting up 02560 // flushPendingConstraints will be called by Corona 02561 if(!(d->pendingConstraints & Plasma::StartupCompletedConstraint)) { 02562 flushPendingConstraintsEvents(); 02563 } 02564 } else if (d->modificationsTimer && event->timerId() == d->modificationsTimer->timerId()) { 02565 d->modificationsTimer->stop(); 02566 // invalid group, will result in save using the default group 02567 KConfigGroup cg; 02568 02569 save(cg); 02570 emit configNeedsSaving(); 02571 } else if (event->timerId() == d->busyWidgetTimer.timerId()) { 02572 if (!d->busyWidget) { 02573 d->createMessageOverlay(false); 02574 d->messageOverlay->opacity = 0; 02575 02576 QGraphicsLinearLayout *mainLayout = new QGraphicsLinearLayout(d->messageOverlay); 02577 d->busyWidget = new Plasma::BusyWidget(d->messageOverlay); 02578 d->busyWidget->setAcceptHoverEvents(false); 02579 d->busyWidget->setAcceptedMouseButtons(Qt::NoButton); 02580 d->messageOverlay->setAcceptHoverEvents(false); 02581 d->messageOverlay->setAcceptedMouseButtons(Qt::NoButton); 02582 02583 mainLayout->addStretch(); 02584 mainLayout->addItem(d->busyWidget); 02585 mainLayout->addStretch(); 02586 } 02587 } 02588 } 02589 02590 QRect Applet::screenRect() const 02591 { 02592 QGraphicsView *v = view(); 02593 02594 if (v) { 02595 QPointF bottomRight = pos(); 02596 bottomRight.rx() += size().width(); 02597 bottomRight.ry() += size().height(); 02598 02599 QPoint tL = v->mapToGlobal(v->mapFromScene(pos())); 02600 QPoint bR = v->mapToGlobal(v->mapFromScene(bottomRight)); 02601 return QRect(QPoint(tL.x(), tL.y()), QSize(bR.x() - tL.x(), bR.y() - tL.y())); 02602 } 02603 02604 //The applet doesn't have a view on it. 02605 //So a screenRect isn't relevant. 02606 return QRect(QPoint(0, 0), QSize(0, 0)); 02607 } 02608 02609 void Applet::raise() 02610 { 02611 setZValue(++AppletPrivate::s_maxZValue); 02612 } 02613 02614 void Applet::lower() 02615 { 02616 setZValue(--AppletPrivate::s_minZValue); 02617 } 02618 02619 void AppletPrivate::setIsContainment(bool nowIsContainment, bool forceUpdate) 02620 { 02621 if (isContainment == nowIsContainment && !forceUpdate) { 02622 return; 02623 } 02624 02625 isContainment = nowIsContainment; 02626 //FIXME I do not like this function. 02627 //currently it's only called before ctmt/applet init, with (true,true), and I'm going to assume it stays that way. 02628 //if someone calls it at some other time it'll cause headaches. :P 02629 02630 delete mainConfig; 02631 mainConfig = 0; 02632 02633 Containment *c = q->containment(); 02634 if (c) { 02635 c->d->checkContainmentFurniture(); 02636 } 02637 } 02638 02639 bool Applet::isContainment() const 02640 { 02641 return d->isContainment; 02642 } 02643 02644 // PRIVATE CLASS IMPLEMENTATION 02645 02646 AppletPrivate::AppletPrivate(KService::Ptr service, const KPluginInfo *info, int uniqueID, Applet *applet) 02647 : appletId(uniqueID), 02648 q(applet), 02649 service(0), 02650 preferredBackgroundHints(Applet::StandardBackground), 02651 backgroundHints(Applet::NoBackground), 02652 aspectRatioMode(Plasma::KeepAspectRatio), 02653 immutability(Mutable), 02654 appletDescription(info ? *info : KPluginInfo(service)), 02655 background(0), 02656 mainConfig(0), 02657 pendingConstraints(NoConstraint), 02658 messageOverlay(0), 02659 messageOverlayProxy(0), 02660 busyWidget(0), 02661 script(0), 02662 package(0), 02663 configLoader(0), 02664 actions(AppletPrivate::defaultActions(applet)), 02665 activationAction(0), 02666 itemStatus(UnknownStatus), 02667 preferredSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored), 02668 modificationsTimer(0), 02669 hasConfigurationInterface(false), 02670 failed(false), 02671 isContainment(false), 02672 transient(false), 02673 needsConfig(false), 02674 started(false) 02675 { 02676 if (appletId == 0) { 02677 appletId = ++s_maxAppletId; 02678 } else if (appletId > s_maxAppletId) { 02679 s_maxAppletId = appletId; 02680 } 02681 } 02682 02683 AppletPrivate::~AppletPrivate() 02684 { 02685 if (activationAction && activationAction->isGlobalShortcutEnabled()) { 02686 //kDebug() << "reseting global action for" << q->name() << activationAction->objectName(); 02687 activationAction->forgetGlobalShortcut(); 02688 } 02689 02690 delete extender.data(); 02691 02692 delete script; 02693 script = 0; 02694 delete package; 02695 package = 0; 02696 delete configLoader; 02697 configLoader = 0; 02698 delete mainConfig; 02699 mainConfig = 0; 02700 delete modificationsTimer; 02701 } 02702 02703 void AppletPrivate::init(const QString &packagePath) 02704 { 02705 // WARNING: do not access config() OR globalConfig() in this method! 02706 // that requires a scene, which is not available at this point 02707 q->setCacheMode(Applet::DeviceCoordinateCache); 02708 q->setAcceptsHoverEvents(true); 02709 q->setFlag(QGraphicsItem::ItemIsFocusable, true); 02710 q->setFocusPolicy(Qt::ClickFocus); 02711 // FIXME: adding here because nothing seems to be doing it in QGraphicsView, 02712 // but it doesn't actually work anyways =/ 02713 q->setLayoutDirection(qApp->layoutDirection()); 02714 02715 //set a default size before any saved settings are read 02716 QSize size(200, 200); 02717 q->setBackgroundHints(Applet::DefaultBackground); 02718 q->setHasConfigurationInterface(true); //FIXME why not default it to true in the constructor? 02719 02720 QAction *closeApplet = actions->action("remove"); 02721 if (closeApplet) { 02722 closeApplet->setText(i18nc("%1 is the name of the applet", "Remove this %1", q->name())); 02723 } 02724 02725 QAction *configAction = actions->action("configure"); 02726 if (configAction) { 02727 configAction->setText(i18nc("%1 is the name of the applet", "%1 Settings", q->name())); 02728 } 02729 02730 QObject::connect(q, SIGNAL(activate()), q, SLOT(setFocus())); 02731 if (!appletDescription.isValid()) { 02732 kDebug() << "Check your constructor! " 02733 << "You probably want to be passing in a Service::Ptr " 02734 << "or a QVariantList with a valid storageid as arg[0]."; 02735 q->resize(size); 02736 return; 02737 } 02738 02739 QVariant s = appletDescription.property("X-Plasma-DefaultSize"); 02740 if (s.isValid()) { 02741 size = s.toSize(); 02742 } 02743 //kDebug() << "size" << size; 02744 q->resize(size); 02745 02746 QString api = appletDescription.property("X-Plasma-API").toString(); 02747 02748 // we have a scripted plasmoid 02749 if (!api.isEmpty()) { 02750 // find where the Package is 02751 QString path = packagePath; 02752 if (path.isEmpty()) { 02753 QString subPath = q->packageStructure()->defaultPackageRoot() + '/' + appletDescription.pluginName() + '/'; 02754 path = KStandardDirs::locate("data", subPath + "metadata.desktop"); 02755 if (path.isEmpty()) { 02756 path = KStandardDirs::locate("data", subPath); 02757 } else { 02758 path.remove(QString("metadata.desktop")); 02759 } 02760 } else if (!path.endsWith('/')) { 02761 path.append('/'); 02762 } 02763 02764 if (path.isEmpty()) { 02765 q->setFailedToLaunch( 02766 true, 02767 i18nc("Package file, name of the widget", 02768 "Could not locate the %1 package required for the %2 widget.", 02769 appletDescription.pluginName(), appletDescription.name())); 02770 } else { 02771 // create the package and see if we have something real 02772 //kDebug() << "trying for" << path; 02773 PackageStructure::Ptr structure = Plasma::packageStructure(api, Plasma::AppletComponent); 02774 structure->setPath(path); 02775 package = new Package(path, structure); 02776 02777 if (package->isValid()) { 02778 // now we try and set up the script engine. 02779 // it will be parented to this applet and so will get 02780 // deleted when the applet does 02781 02782 script = Plasma::loadScriptEngine(api, q); 02783 if (!script) { 02784 delete package; 02785 package = 0; 02786 q->setFailedToLaunch(true, 02787 i18nc("API or programming language the widget was written in, name of the widget", 02788 "Could not create a %1 ScriptEngine for the %2 widget.", 02789 api, appletDescription.name())); 02790 } 02791 } else { 02792 q->setFailedToLaunch(true, i18nc("Package file, name of the widget", 02793 "Could not open the %1 package required for the %2 widget.", 02794 appletDescription.pluginName(), appletDescription.name())); 02795 delete package; 02796 package = 0; 02797 } 02798 } 02799 } 02800 } 02801 02802 // put all setup routines for script here. at this point we can assume that 02803 // package exists and that we have a script engine 02804 void AppletPrivate::setupScriptSupport() 02805 { 02806 if (!package) { 02807 return; 02808 } 02809 02810 kDebug() << "setting up script support, package is in" << package->path() 02811 << "which is a" << package->structure()->type() << "package" 02812 << ", main script is" << package->filePath("mainscript"); 02813 02814 QString translationsPath = package->filePath("translations"); 02815 if (!translationsPath.isEmpty()) { 02816 //FIXME: we should _probably_ use a KComponentData to segregate the applets 02817 // from each other; but I want to get the basics working first :) 02818 KGlobal::dirs()->addResourceDir("locale", translationsPath); 02819 KGlobal::locale()->insertCatalog(package->metadata().pluginName()); 02820 } 02821 02822 QString xmlPath = package->filePath("mainconfigxml"); 02823 if (!xmlPath.isEmpty()) { 02824 QFile file(xmlPath); 02825 KConfigGroup config = q->config(); 02826 configLoader = new ConfigLoader(&config, &file); 02827 QObject::connect(configLoader, SIGNAL(configChanged()), q, SLOT(propagateConfigChanged())); 02828 } 02829 02830 if (!package->filePath("mainconfigui").isEmpty()) { 02831 q->setHasConfigurationInterface(true); 02832 } 02833 } 02834 02835 QString AppletPrivate::globalName() const 02836 { 02837 if (!appletDescription.isValid()) { 02838 return QString(); 02839 } 02840 02841 return appletDescription.service()->library(); 02842 } 02843 02844 QString AppletPrivate::instanceName() 02845 { 02846 if (!appletDescription.isValid()) { 02847 return QString(); 02848 } 02849 02850 return appletDescription.service()->library() + QString::number(appletId); 02851 } 02852 02853 void AppletPrivate::scheduleConstraintsUpdate(Plasma::Constraints c) 02854 { 02855 // Don't start up a timer if we're just starting up 02856 // flushPendingConstraints will be called by Corona 02857 if (started && !constraintsTimer.isActive() && !(c & Plasma::StartupCompletedConstraint)) { 02858 constraintsTimer.start(0, q); 02859 } 02860 02861 if (c & Plasma::StartupCompletedConstraint) { 02862 started = true; 02863 } 02864 02865 pendingConstraints |= c; 02866 } 02867 02868 void AppletPrivate::scheduleModificationNotification() 02869 { 02870 // modificationsTimer is not allocated until we get our notice of being started 02871 if (modificationsTimer) { 02872 // schedule a save 02873 if (modificationsTimer->isActive()) { 02874 modificationsTimer->stop(); 02875 } 02876 02877 modificationsTimer->start(1000, q); 02878 } 02879 } 02880 02881 KConfigGroup *AppletPrivate::mainConfigGroup() 02882 { 02883 if (mainConfig) { 02884 return mainConfig; 02885 } 02886 02887 bool newGroup = false; 02888 if (isContainment) { 02889 Corona *corona = qobject_cast<Corona*>(q->scene()); 02890 KConfigGroup containmentConfig; 02891 //kDebug() << "got a corona, baby?" << (QObject*)corona << (QObject*)q; 02892 02893 if (corona) { 02894 containmentConfig = KConfigGroup(corona->config(), "Containments"); 02895 } else { 02896 containmentConfig = KConfigGroup(KGlobal::config(), "Containments"); 02897 } 02898 02899 if (package && !containmentConfig.hasGroup(QString::number(appletId))) { 02900 newGroup = true; 02901 } 02902 02903 mainConfig = new KConfigGroup(&containmentConfig, QString::number(appletId)); 02904 } else { 02905 KConfigGroup appletConfig; 02906 02907 Containment *c = q->containment(); 02908 Applet *parentApplet = qobject_cast<Applet *>(q->parent()); 02909 if (parentApplet && parentApplet != static_cast<Applet *>(c)) { 02910 // this applet is nested inside another applet! use it's config 02911 // as the parent group in the config 02912 appletConfig = parentApplet->config(); 02913 appletConfig = KConfigGroup(&appletConfig, "Applets"); 02914 } else if (c) { 02915 // applet directly in a Containment, as usual 02916 appletConfig = c->config(); 02917 appletConfig = KConfigGroup(&appletConfig, "Applets"); 02918 } else { 02919 kWarning() << "requesting config for" << q->name() << "without a containment!"; 02920 appletConfig = KConfigGroup(KGlobal::config(), "Applets"); 02921 } 02922 02923 if (package && !appletConfig.hasGroup(QString::number(appletId))) { 02924 newGroup = true; 02925 } 02926 02927 mainConfig = new KConfigGroup(&appletConfig, QString::number(appletId)); 02928 } 02929 02930 if (newGroup) { 02931 //see if we have a default configuration in our package 02932 const QString defaultConfigFile = q->package()->filePath("defaultconfig"); 02933 if (!defaultConfigFile.isEmpty()) { 02934 kDebug() << "copying default config: " << q->package()->filePath("defaultconfig"); 02935 KConfigGroup defaultConfig(KSharedConfig::openConfig(defaultConfigFile)->group("Configuration")); 02936 defaultConfig.copyTo(mainConfig); 02937 } 02938 } 02939 02940 return mainConfig; 02941 } 02942 02943 QString AppletPrivate::visibleFailureText(const QString &reason) 02944 { 02945 QString text; 02946 02947 if (reason.isEmpty()) { 02948 text = i18n("This object could not be created."); 02949 } else { 02950 text = i18n("This object could not be created for the following reason:<p><b>%1</b></p>", reason); 02951 } 02952 02953 return text; 02954 } 02955 02956 void AppletPrivate::themeChanged() 02957 { 02958 if (background) { 02959 //do again the translucent background fallback 02960 q->setBackgroundHints(backgroundHints); 02961 02962 qreal left; 02963 qreal right; 02964 qreal top; 02965 qreal bottom; 02966 background->getMargins(left, top, right, bottom); 02967 q->setContentsMargins(left, right, top, bottom); 02968 } 02969 q->update(); 02970 } 02971 02972 void AppletPrivate::resetConfigurationObject() 02973 { 02974 // make sure mainConfigGroup exists in all cases 02975 mainConfigGroup(); 02976 02977 mainConfig->deleteGroup(); 02978 delete mainConfig; 02979 mainConfig = 0; 02980 02981 Corona * corona = qobject_cast<Corona*>(q->scene()); 02982 if (corona) { 02983 corona->requireConfigSync(); 02984 } 02985 } 02986 02987 void AppletPrivate::handleDisappeared(AppletHandle *h) 02988 { 02989 if (h == handle.data()) { 02990 h->detachApplet(); 02991 QGraphicsScene *scene = q->scene(); 02992 if (scene && h->scene() == scene) { 02993 scene->removeItem(h); 02994 } 02995 h->deleteLater(); 02996 } 02997 } 02998 02999 void ContainmentPrivate::checkRemoveAction() 03000 { 03001 q->enableAction("remove", q->immutability() == Mutable); 03002 } 03003 03004 03005 uint AppletPrivate::s_maxAppletId = 0; 03006 int AppletPrivate::s_maxZValue = 0; 03007 int AppletPrivate::s_minZValue = 0; 03008 PackageStructure::Ptr AppletPrivate::packageStructure(0); 03009 QSet<QString> AppletPrivate::s_customCategories; 03010 03011 AppletOverlayWidget::AppletOverlayWidget(QGraphicsWidget *parent) 03012 : QGraphicsWidget(parent), 03013 opacity(0.4) 03014 { 03015 resize(parent->size()); 03016 } 03017 03018 void AppletOverlayWidget::destroy() 03019 { 03020 Animation *anim = Plasma::Animator::create(Plasma::Animator::DisappearAnimation); 03021 if (anim) { 03022 connect(anim, SIGNAL(finished()), this, SLOT(overlayAnimationComplete())); 03023 anim->setTargetWidget(this); 03024 anim->start(); 03025 } else { 03026 overlayAnimationComplete(); 03027 } 03028 } 03029 03030 void AppletOverlayWidget::mousePressEvent(QGraphicsSceneMouseEvent *event) 03031 { 03032 event->accept(); 03033 } 03034 03035 void AppletOverlayWidget::overlayAnimationComplete() 03036 { 03037 if (scene()) { 03038 scene()->removeItem(this); 03039 } 03040 deleteLater(); 03041 } 03042 03043 void AppletOverlayWidget::paint(QPainter *painter, 03044 const QStyleOptionGraphicsItem *option, 03045 QWidget *widget) 03046 { 03047 Q_UNUSED(option) 03048 Q_UNUSED(widget) 03049 03050 if (qFuzzyCompare(1, 1+opacity)) { 03051 return; 03052 } 03053 03054 QColor wash = Plasma::Theme::defaultTheme()->color(Theme::BackgroundColor); 03055 wash.setAlphaF(opacity); 03056 03057 Applet *applet = qobject_cast<Applet *>(parentWidget()); 03058 03059 03060 QPainterPath backgroundShape; 03061 if (!applet || applet->backgroundHints() & Applet::StandardBackground) { 03062 //FIXME: a resize here is nasty, but perhaps still better than an eventfilter just for that.. 03063 if (parentWidget()->contentsRect().size() != size()) { 03064 resize(parentWidget()->contentsRect().size()); 03065 } 03066 backgroundShape = PaintUtils::roundedRectangle(contentsRect(), 5); 03067 } else { 03068 backgroundShape = shape(); 03069 } 03070 03071 painter->setRenderHints(QPainter::Antialiasing); 03072 painter->fillPath(backgroundShape, wash); 03073 } 03074 03075 #if QT_VERSION >= 0x040700 03076 // in QGraphicsWidget now; preserve BC by implementing it as a protected method 03077 void Applet::geometryChanged() 03078 { 03079 emit QGraphicsWidget::geometryChanged(); 03080 } 03081 #endif 03082 03083 } // Plasma namespace 03084 03085 #include "applet.moc" 03086 #include "private/applet_p.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Wed May 2 2012 17:35:56 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Wed May 2 2012 17:35:56 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.