• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.8.4 API Reference
  • KDE Home
  • Contact Us
 

Plasma

  • plasma
applet.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2005 by Aaron Seigo <aseigo@kde.org>
3  * Copyright 2007 by Riccardo Iaconelli <riccardo@kde.org>
4  * Copyright 2008 by Ménard Alexis <darktears31@gmail.com>
5  * Copyright (c) 2009 Chani Armitage <chani@kde.org>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU Library General Public License as
9  * published by the Free Software Foundation; either version 2, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this program; if not, write to the
19  * Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  */
22 
23 #include "applet.h"
24 #include "private/applet_p.h"
25 
26 #include "config-plasma.h"
27 
28 #include <plasma/animations/animation.h>
29 
30 #include <cmath>
31 #include <limits>
32 
33 #include <QApplication>
34 #include <QEvent>
35 #include <QFile>
36 #include <QGraphicsGridLayout>
37 #include <QGraphicsSceneMouseEvent>
38 #include <QGraphicsView>
39 #include <QHostInfo>
40 #include <QLabel>
41 #include <QList>
42 #include <QGraphicsLinearLayout>
43 #include <QPainter>
44 #include <QRegExp>
45 #include <QSize>
46 #include <QStyleOptionGraphicsItem>
47 #include <QTextDocument>
48 #include <QUiLoader>
49 #include <QVBoxLayout>
50 #include <QWidget>
51 
52 #include <kaction.h>
53 #include <kactioncollection.h>
54 #include <kauthorized.h>
55 #include <kcolorscheme.h>
56 #include <kdialog.h>
57 #include <kdesktopfile.h>
58 #include <kicon.h>
59 #include <kiconloader.h>
60 #include <kkeysequencewidget.h>
61 #include <kplugininfo.h>
62 #include <kstandarddirs.h>
63 #include <kservice.h>
64 #include <kservicetypetrader.h>
65 #include <kshortcut.h>
66 #include <kwindowsystem.h>
67 #include <kpushbutton.h>
68 
69 #ifndef PLASMA_NO_KUTILS
70 #include <kcmoduleinfo.h>
71 #include <kcmoduleproxy.h>
72 #else
73 #include <kcmodule.h>
74 #endif
75 
76 #ifndef PLASMA_NO_SOLID
77 #include <solid/powermanagement.h>
78 #endif
79 
80 #include "abstracttoolbox.h"
81 #include "authorizationmanager.h"
82 #include "authorizationrule.h"
83 #include "configloader.h"
84 #include "containment.h"
85 #include "corona.h"
86 #include "dataenginemanager.h"
87 #include "dialog.h"
88 #include "extenders/extender.h"
89 #include "extenders/extenderitem.h"
90 #include "package.h"
91 #include "plasma.h"
92 #include "scripting/appletscript.h"
93 #include "svg.h"
94 #include "framesvg.h"
95 #include "popupapplet.h"
96 #include "private/applethandle_p.h"
97 #include "private/extenderitem_p.h"
98 #include "private/framesvg_p.h"
99 #include "theme.h"
100 #include "view.h"
101 #include "widgets/iconwidget.h"
102 #include "widgets/label.h"
103 #include "widgets/pushbutton.h"
104 #include "widgets/busywidget.h"
105 #include "tooltipmanager.h"
106 #include "wallpaper.h"
107 #include "paintutils.h"
108 #include "abstractdialogmanager.h"
109 #include "pluginloader.h"
110 
111 #include "private/associatedapplicationmanager_p.h"
112 #include "private/authorizationmanager_p.h"
113 #include "private/containment_p.h"
114 #include "private/extenderapplet_p.h"
115 #include "private/package_p.h"
116 #include "private/packages_p.h"
117 #include "private/plasmoidservice_p.h"
118 #include "private/popupapplet_p.h"
119 #include "private/remotedataengine_p.h"
120 #include "private/service_p.h"
121 #include "ui_publish.h"
122 
123 
124 namespace Plasma
125 {
126 
127 Applet::Applet(const KPluginInfo &info, QGraphicsItem *parent, uint appletId)
128  : QGraphicsWidget(parent),
129  d(new AppletPrivate(KService::Ptr(), &info, appletId, this))
130 {
131  // WARNING: do not access config() OR globalConfig() in this method!
132  // that requires a scene, which is not available at this point
133  d->init();
134 }
135 
136 Applet::Applet(QGraphicsItem *parent, const QString &serviceID, uint appletId)
137  : QGraphicsWidget(parent),
138  d(new AppletPrivate(KService::serviceByStorageId(serviceID), 0, appletId, this))
139 {
140  // WARNING: do not access config() OR globalConfig() in this method!
141  // that requires a scene, which is not available at this point
142  d->init();
143 }
144 
145 Applet::Applet(QGraphicsItem *parent,
146  const QString &serviceID,
147  uint appletId,
148  const QVariantList &args)
149  : QGraphicsWidget(parent),
150  d(new AppletPrivate(KService::serviceByStorageId(serviceID), 0, appletId, this))
151 {
152  // WARNING: do not access config() OR globalConfig() in this method!
153  // that requires a scene, which is not available at this point
154 
155  QVariantList &mutableArgs = const_cast<QVariantList &>(args);
156  if (!mutableArgs.isEmpty()) {
157  mutableArgs.removeFirst();
158 
159  if (!mutableArgs.isEmpty()) {
160  mutableArgs.removeFirst();
161  }
162  }
163 
164  d->args = mutableArgs;
165 
166  d->init();
167 }
168 
169 Applet::Applet(QObject *parentObject, const QVariantList &args)
170  : QGraphicsWidget(0),
171  d(new AppletPrivate(
172  KService::serviceByStorageId(args.count() > 0 ? args[0].toString() : QString()), 0,
173  args.count() > 1 ? args[1].toInt() : 0, this))
174 {
175  // now remove those first two items since those are managed by Applet and subclasses shouldn't
176  // need to worry about them. yes, it violates the constness of this var, but it lets us add
177  // or remove items later while applets can just pretend that their args always start at 0
178  QVariantList &mutableArgs = const_cast<QVariantList &>(args);
179  if (!mutableArgs.isEmpty()) {
180  mutableArgs.removeFirst();
181 
182  if (!mutableArgs.isEmpty()) {
183  mutableArgs.removeFirst();
184  }
185  }
186 
187  d->args = mutableArgs;
188 
189  setParent(parentObject);
190 
191  // WARNING: do not access config() OR globalConfig() in this method!
192  // that requires a scene, which is not available at this point
193  d->init();
194 
195  // the brain damage seen in the initialization list is due to the
196  // inflexibility of KService::createInstance
197 }
198 
199 Applet::Applet(const QString &packagePath, uint appletId, const QVariantList &args)
200  : QGraphicsWidget(0),
201  d(new AppletPrivate(KService::Ptr(new KService(packagePath + "/metadata.desktop")), 0, appletId, this))
202 {
203  Q_UNUSED(args) // FIXME?
204  d->init(packagePath);
205 }
206 
207 Applet::~Applet()
208 {
209  //let people know that i will die
210  emit appletDestroyed(this);
211 
212  if (!d->transient && d->extender) {
213  //This would probably be nicer if it was located in extender. But in it's dtor, this won't
214  //work since when that get's called, the applet's config() isn't accessible anymore. (same
215  //problem with calling saveState(). Doing this in saveState() might be a possibility, but
216  //that would require every extender savestate implementation to call it's parent function,
217  //which isn't very nice.
218  d->extender.data()->saveState();
219 
220  foreach (ExtenderItem *item, d->extender.data()->attachedItems()) {
221  if (item->autoExpireDelay()) {
222  //destroy temporary extender items, or items that aren't detached, so their
223  //configuration won't linger after a plasma restart.
224  item->destroy();
225  }
226  }
227  }
228 
229  // clean up our config dialog, if any
230  delete KConfigDialog::exists(d->configDialogId());
231  delete d;
232 }
233 
234 PackageStructure::Ptr Applet::packageStructure()
235 {
236  if (!AppletPrivate::packageStructure) {
237  AppletPrivate::packageStructure = new PlasmoidPackage();
238  }
239 
240  return AppletPrivate::packageStructure;
241 }
242 
243 void Applet::init()
244 {
245  setFlag(ItemIsMovable, true);
246  if (d->script) {
247  d->setupScriptSupport();
248 
249  if (!d->script->init() && !d->failed) {
250  setFailedToLaunch(true, i18n("Script initialization failed"));
251  }
252  }
253 }
254 
255 uint Applet::id() const
256 {
257  return d->appletId;
258 }
259 
260 void Applet::save(KConfigGroup &g) const
261 {
262  if (d->transient) {
263  return;
264  }
265 
266  KConfigGroup group = g;
267  if (!group.isValid()) {
268  group = *d->mainConfigGroup();
269  }
270 
271  //kDebug() << "saving" << pluginName() << "to" << group.name();
272  // we call the dptr member directly for locked since isImmutable()
273  // also checks kiosk and parent containers
274  group.writeEntry("immutability", (int)d->immutability);
275  group.writeEntry("plugin", pluginName());
276 
277  group.writeEntry("geometry", geometry());
278  group.writeEntry("zvalue", zValue());
279 
280  if (!d->started) {
281  return;
282  }
283 
284  //FIXME: for containments, we need to have some special values here w/regards to
285  // screen affinity (e.g. "bottom of screen 0")
286  //kDebug() << pluginName() << "geometry is" << geometry()
287  // << "pos is" << pos() << "bounding rect is" << boundingRect();
288  if (transform() == QTransform()) {
289  group.deleteEntry("transform");
290  } else {
291  QList<qreal> m;
292  QTransform t = transform();
293  m << t.m11() << t.m12() << t.m13() << t.m21() << t.m22() << t.m23() << t.m31() << t.m32() << t.m33();
294  group.writeEntry("transform", m);
295  //group.writeEntry("transform", transformToString(transform()));
296  }
297 
298  KConfigGroup appletConfigGroup(&group, "Configuration");
299  saveState(appletConfigGroup);
300 
301  if (d->configLoader) {
302  // we're saving so we know its changed, we don't need or want the configChanged
303  // signal bubbling up at this point due to that
304  disconnect(d->configLoader, SIGNAL(configChanged()), this, SLOT(propagateConfigChanged()));
305  d->configLoader->writeConfig();
306  connect(d->configLoader, SIGNAL(configChanged()), this, SLOT(propagateConfigChanged()));
307  }
308 }
309 
310 void Applet::restore(KConfigGroup &group)
311 {
312  QList<qreal> m = group.readEntry("transform", QList<qreal>());
313  if (m.count() == 9) {
314  QTransform t(m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
315  setTransform(t);
316  }
317 
318  qreal z = group.readEntry("zvalue", 0);
319 
320  if (z >= AppletPrivate::s_maxZValue) {
321  AppletPrivate::s_maxZValue = z;
322  }
323 
324  if (z > 0) {
325  setZValue(z);
326  }
327 
328  setImmutability((ImmutabilityType)group.readEntry("immutability", (int)Mutable));
329 
330  QRectF geom = group.readEntry("geometry", QRectF());
331  if (geom.isValid()) {
332  setGeometry(geom);
333  }
334 
335  KConfigGroup shortcutConfig(&group, "Shortcuts");
336  QString shortcutText = shortcutConfig.readEntryUntranslated("global", QString());
337  if (!shortcutText.isEmpty()) {
338  setGlobalShortcut(KShortcut(shortcutText));
339  /*
340  kDebug() << "got global shortcut for" << name() << "of" << QKeySequence(shortcutText);
341  kDebug() << "set to" << d->activationAction->objectName()
342  << d->activationAction->globalShortcut().primary();
343  */
344  }
345 
346  // local shortcut, if any
347  //TODO: implement; the shortcut will need to be registered with the containment
348  /*
349 #include "accessmanager.h"
350 #include "private/plasmoidservice_p.h"
351 #include "authorizationmanager.h"
352 #include "authorizationmanager.h"
353  shortcutText = shortcutConfig.readEntryUntranslated("local", QString());
354  if (!shortcutText.isEmpty()) {
355  //TODO: implement; the shortcut
356  }
357  */
358 }
359 
360 void AppletPrivate::setFocus()
361 {
362  //kDebug() << "setting focus";
363  q->setFocus(Qt::ShortcutFocusReason);
364 }
365 
366 void Applet::setFailedToLaunch(bool failed, const QString &reason)
367 {
368  if (d->failed == failed) {
369  if (failed && !reason.isEmpty()) {
370  foreach (QGraphicsItem *item, QGraphicsItem::children()) {
371  Label *l = dynamic_cast<Label *>(item);
372  if (l) {
373  l->setText(d->visibleFailureText(reason));
374  }
375  }
376  }
377  return;
378  }
379 
380  d->failed = failed;
381  prepareGeometryChange();
382 
383  foreach (QGraphicsItem *item, childItems()) {
384  if (!dynamic_cast<AppletHandle *>(item)) {
385  delete item;
386  }
387  }
388 
389  d->messageOverlay = 0;
390  if (d->messageDialog) {
391  d->messageDialog.data()->deleteLater();
392  d->messageDialog.clear();
393  }
394 
395  setLayout(0);
396 
397  if (failed) {
398  setBackgroundHints(d->backgroundHints|StandardBackground);
399 
400  QGraphicsLinearLayout *failureLayout = new QGraphicsLinearLayout(this);
401  failureLayout->setContentsMargins(0, 0, 0, 0);
402 
403  IconWidget *failureIcon = new IconWidget(this);
404  failureIcon->setIcon(KIcon("dialog-error"));
405  failureLayout->addItem(failureIcon);
406 
407  Label *failureWidget = new Plasma::Label(this);
408  failureWidget->setText(d->visibleFailureText(reason));
409  QLabel *label = failureWidget->nativeWidget();
410  label->setWordWrap(true);
411  failureLayout->addItem(failureWidget);
412 
413  Plasma::ToolTipManager::self()->registerWidget(failureIcon);
414  Plasma::ToolTipContent data(i18n("Unable to load the widget"), reason,
415  KIcon("dialog-error"));
416  Plasma::ToolTipManager::self()->setContent(failureIcon, data);
417 
418  setLayout(failureLayout);
419  resize(300, 250);
420  d->background->resizeFrame(geometry().size());
421  }
422 
423  update();
424 }
425 
426 void Applet::saveState(KConfigGroup &group) const
427 {
428  if (d->script) {
429  emit d->script->saveState(group);
430  }
431 
432  if (group.config()->name() != config().config()->name()) {
433  // we're being saved to a different file!
434  // let's just copy the current values in our configuration over
435  KConfigGroup c = config();
436  c.copyTo(&group);
437  }
438 }
439 
440 KConfigGroup Applet::config(const QString &group) const
441 {
442  if (d->transient) {
443  return KConfigGroup(KGlobal::config(), "PlasmaTransientsConfig");
444  }
445 
446  KConfigGroup cg = config();
447  return KConfigGroup(&cg, group);
448 }
449 
450 KConfigGroup Applet::config() const
451 {
452  if (d->transient) {
453  return KConfigGroup(KGlobal::config(), "PlasmaTransientsConfig");
454  }
455 
456  if (d->isContainment) {
457  return *(d->mainConfigGroup());
458  }
459 
460  return KConfigGroup(d->mainConfigGroup(), "Configuration");
461 }
462 
463 KConfigGroup Applet::globalConfig() const
464 {
465  KConfigGroup globalAppletConfig;
466  QString group = isContainment() ? "ContainmentGlobals" : "AppletGlobals";
467 
468  Corona *corona = qobject_cast<Corona*>(scene());
469  if (corona) {
470  KSharedConfig::Ptr coronaConfig = corona->config();
471  globalAppletConfig = KConfigGroup(coronaConfig, group);
472  } else {
473  globalAppletConfig = KConfigGroup(KGlobal::config(), group);
474  }
475 
476  return KConfigGroup(&globalAppletConfig, d->globalName());
477 }
478 
479 void Applet::destroy()
480 {
481  if (immutability() != Mutable || d->transient || !d->started) {
482  return; //don't double delete
483  }
484 
485  d->transient = true;
486 
487  if (isContainment()) {
488  d->cleanUpAndDelete();
489  } else {
490  Animation *zoomAnim = Plasma::Animator::create(Plasma::Animator::ZoomAnimation);
491  connect(zoomAnim, SIGNAL(finished()), this, SLOT(cleanUpAndDelete()));
492  zoomAnim->setTargetWidget(this);
493  zoomAnim->start();
494  }
495 }
496 
497 bool Applet::destroyed() const
498 {
499  return d->transient;
500 }
501 
502 void AppletPrivate::selectItemToDestroy()
503 {
504  //FIXME: this will not work nicely with multiple screens and being zoomed out!
505  if (isContainment) {
506  QGraphicsView *view = q->view();
507  if (view && view->transform().isScaling() &&
508  q->scene()->focusItem() != q) {
509  QGraphicsItem *focus = q->scene()->focusItem();
510 
511  if (focus) {
512  Containment *toDestroy = dynamic_cast<Containment*>(focus->topLevelItem());
513 
514  if (toDestroy) {
515  toDestroy->destroy();
516  return;
517  }
518  }
519  }
520  }
521 
522  q->destroy();
523 }
524 
525 void AppletPrivate::updateRect(const QRectF &rect)
526 {
527  q->update(rect);
528 }
529 
530 void AppletPrivate::cleanUpAndDelete()
531 {
532  //kDebug() << "???????????????? DESTROYING APPLET" << q->name() << q->scene() << " ???????????????????????????";
533  QGraphicsWidget *parent = dynamic_cast<QGraphicsWidget *>(q->parentItem());
534  //it probably won't matter, but right now if there are applethandles, *they* are the parent.
535  //not the containment.
536 
537  //is the applet in a containment and does the containment have a layout?
538  //if yes, we remove the applet in the layout
539  if (parent && parent->layout()) {
540  QGraphicsLayout *l = parent->layout();
541  for (int i = 0; i < l->count(); ++i) {
542  if (q == l->itemAt(i)) {
543  l->removeAt(i);
544  break;
545  }
546  }
547  }
548 
549  if (configLoader) {
550  configLoader->setDefaults();
551  }
552 
553  resetConfigurationObject();
554 
555  if (q->scene()) {
556  if (isContainment) {
557  // prematurely emit our destruction if we are a Containment,
558  // giving Corona a chance to remove this Containment from its collection
559  emit q->QObject::destroyed(q);
560  }
561 
562  q->scene()->removeItem(q);
563  }
564 
565  q->deleteLater();
566 }
567 
568 void AppletPrivate::createMessageOverlay(bool usePopup)
569 {
570  if (messageOverlay) {
571  qDeleteAll(messageOverlay->children());
572  messageOverlay->setLayout(0);
573  }
574 
575  PopupApplet *popup = qobject_cast<Plasma::PopupApplet*>(q);
576 
577  if (!messageOverlay) {
578  if (usePopup && popup) {
579  if (popup->widget()) {
580  messageOverlayProxy = new QGraphicsProxyWidget(q);
581  messageOverlayProxy->setWidget(popup->widget());
582  messageOverlay = new AppletOverlayWidget(messageOverlayProxy);
583  } else if (popup->graphicsWidget() &&
584  popup->graphicsWidget() != extender.data()) {
585  messageOverlay = new AppletOverlayWidget(popup->graphicsWidget());
586  }
587  }
588 
589  if (!messageOverlay) {
590  messageOverlay = new AppletOverlayWidget(q);
591  }
592  }
593 
594  positionMessageOverlay();
595 }
596 
597 void AppletPrivate::positionMessageOverlay()
598 {
599  if (!messageOverlay) {
600  return;
601  }
602 
603  PopupApplet *popup = qobject_cast<Plasma::PopupApplet*>(q);
604  const bool usePopup = popup && (messageOverlay->parentItem() != q);
605  QGraphicsItem *topItem = q;
606 
607  if (usePopup && popup->widget()) {
608  // popupapplet with widget()
609  topItem = popup->d->proxy.data();
610  messageOverlay->setGeometry(popup->widget()->contentsRect());
611  } else if (usePopup && popup->graphicsWidget() && popup->graphicsWidget() != extender.data()) {
612  // popupapplet with graphicsWidget()
613  topItem = popup->graphicsWidget();
614  QGraphicsWidget *w = dynamic_cast<QGraphicsWidget *>(topItem);
615  messageOverlay->setGeometry(w ? w->contentsRect() : topItem->boundingRect());
616  } else {
617  // normal applet
618  messageOverlay->setGeometry(q->contentsRect());
619  }
620 
621  // raise the overlay above all the other children!
622  int zValue = 100;
623  foreach (QGraphicsItem *child, topItem->children()) {
624  if (child->zValue() > zValue) {
625  zValue = child->zValue() + 1;
626  }
627  }
628  messageOverlay->setZValue(zValue);
629 }
630 
631 void AppletPrivate::destroyMessageOverlay()
632 {
633  if (messageDialog) {
634  messageDialog.data()->animatedHide(Plasma::locationToInverseDirection(q->location()));
635  //messageDialog.data()->deleteLater();
636  messageDialog.clear();
637  }
638 
639  if (!messageOverlay) {
640  return;
641  }
642 
643  messageOverlay->destroy();
644  messageOverlay = 0;
645 
646  if (messageOverlayProxy) {
647  messageOverlayProxy->setWidget(0);
648  delete messageOverlayProxy;
649  messageOverlayProxy = 0;
650  }
651 
652  MessageButton buttonCode = ButtonNo;
653  //find out if we're disappearing because of a button press
654  PushButton *button = qobject_cast<PushButton *>(q->sender());
655  if (button) {
656  if (button == messageOkButton.data()) {
657  buttonCode = ButtonOk;
658  }
659  if (button == messageYesButton.data()) {
660  buttonCode = ButtonYes;
661  }
662  if (button == messageNoButton.data()) {
663  buttonCode = ButtonNo;
664  }
665  if (button == messageCancelButton.data()) {
666  buttonCode = ButtonCancel;
667  }
668 
669  emit q->messageButtonPressed(buttonCode);
670  } else if (q->sender() == messageOverlay) {
671  emit q->messageButtonPressed(ButtonCancel);
672  }
673 }
674 
675 ConfigLoader *Applet::configScheme() const
676 {
677  return d->configLoader;
678 }
679 
680 DataEngine *Applet::dataEngine(const QString &name) const
681 {
682  if (!d->remoteLocation.isEmpty()) {
683  return d->remoteDataEngine(KUrl(d->remoteLocation), name);
684  } else if (!package() || package()->metadata().remoteLocation().isEmpty()) {
685  return d->dataEngine(name);
686  } else {
687  return d->remoteDataEngine(KUrl(package()->metadata().remoteLocation()), name);
688  }
689 }
690 
691 const Package *Applet::package() const
692 {
693  return d->package;
694 }
695 
696 QGraphicsView *Applet::view() const
697 {
698  // It's assumed that we won't be visible on more than one view here.
699  // Anything that actually needs view() should only really care about
700  // one of them anyway though.
701  if (!scene()) {
702  return 0;
703  }
704 
705  QGraphicsView *found = 0;
706  QGraphicsView *possibleFind = 0;
707  //kDebug() << "looking through" << scene()->views().count() << "views";
708  foreach (QGraphicsView *view, scene()->views()) {
709  //kDebug() << " checking" << view << view->sceneRect()
710  // << "against" << sceneBoundingRect() << scenePos();
711  if (view->sceneRect().intersects(sceneBoundingRect()) ||
712  view->sceneRect().contains(scenePos())) {
713  //kDebug() << " found something!" << view->isActiveWindow();
714  if (view->isActiveWindow()) {
715  found = view;
716  } else {
717  possibleFind = view;
718  }
719  }
720  }
721 
722  return found ? found : possibleFind;
723 }
724 
725 QRectF Applet::mapFromView(const QGraphicsView *view, const QRect &rect) const
726 {
727  // Why is this adjustment needed? Qt calculation error?
728  return mapFromScene(view->mapToScene(rect)).boundingRect().adjusted(0, 0, 1, 1);
729 }
730 
731 QRect Applet::mapToView(const QGraphicsView *view, const QRectF &rect) const
732 {
733  // Why is this adjustment needed? Qt calculation error?
734  return view->mapFromScene(mapToScene(rect)).boundingRect().adjusted(0, 0, -1, -1);
735 }
736 
737 QPoint Applet::popupPosition(const QSize &s) const
738 {
739  return popupPosition(s, Qt::AlignLeft);
740 }
741 
742 QPoint Applet::popupPosition(const QSize &s, Qt::AlignmentFlag alignment) const
743 {
744  Corona * corona = qobject_cast<Corona*>(scene());
745  Q_ASSERT(corona);
746 
747  return corona->popupPosition(this, s, alignment);
748 }
749 
750 void Applet::updateConstraints(Plasma::Constraints constraints)
751 {
752  d->scheduleConstraintsUpdate(constraints);
753 }
754 
755 void Applet::constraintsEvent(Plasma::Constraints constraints)
756 {
757  //NOTE: do NOT put any code in here that reacts to constraints updates
758  // as it will not get called for any applet that reimplements constraintsEvent
759  // without calling the Applet:: version as well, which it shouldn't need to.
760  // INSTEAD put such code into flushPendingConstraintsEvents
761  Q_UNUSED(constraints)
762  //kDebug() << constraints << "constraints are FormFactor: " << formFactor()
763  // << ", Location: " << location();
764  if (d->script) {
765  d->script->constraintsEvent(constraints);
766  }
767 }
768 
769 void Applet::initExtenderItem(ExtenderItem *item)
770 {
771  if (d->script) {
772  emit extenderItemRestored(item);
773  } else {
774  kWarning() << "Missing implementation of initExtenderItem in the applet "
775  << item->config().readEntry("SourceAppletPluginName", "")
776  << "!\n Any applet that uses extenders should implement initExtenderItem to "
777  << "instantiate a widget. Destroying the item...";
778  item->destroy();
779  }
780 }
781 
782 Extender *Applet::extender() const
783 {
784  if (!d->extender) {
785  new Extender(const_cast<Applet*>(this));
786  }
787 
788  return d->extender.data();
789 }
790 
791 void Applet::setBusy(bool busy)
792 {
793  if (busy) {
794  if (!d->busyWidget && !d->busyWidgetTimer.isActive()) {
795  d->busyWidgetTimer.start(500, this);
796  }
797  } else {
798  d->busyWidgetTimer.stop();
799  if (d->busyWidget) {
800  d->busyWidget = 0;
801  d->destroyMessageOverlay();
802  }
803  }
804 }
805 
806 bool Applet::isBusy() const
807 {
808  return d->busyWidgetTimer.isActive() || (d->busyWidget && d->busyWidget->isVisible());
809 }
810 
811 QString Applet::name() const
812 {
813  if (d->isContainment) {
814  const Containment *c = qobject_cast<const Containment*>(this);
815  if (c && c->d->isPanelContainment()) {
816  return i18n("Panel");
817  } else if (!d->appletDescription.isValid()) {
818  return i18n("Unknown");
819  } else {
820  return d->appletDescription.name();
821  }
822  } else if (!d->appletDescription.isValid()) {
823  return i18n("Unknown Widget");
824  }
825 
826  return d->appletDescription.name();
827 }
828 
829 QFont Applet::font() const
830 {
831  return QApplication::font();
832 }
833 
834 QString Applet::icon() const
835 {
836  if (!d->appletDescription.isValid()) {
837  return QString();
838  }
839 
840  return d->appletDescription.icon();
841 }
842 
843 QString Applet::pluginName() const
844 {
845  if (!d->appletDescription.isValid()) {
846  return d->mainConfigGroup()->readEntry("plugin", QString());
847  }
848 
849  return d->appletDescription.pluginName();
850 }
851 
852 bool Applet::shouldConserveResources() const
853 {
854 #ifndef PLASMA_NO_SOLID
855  return Solid::PowerManagement::appShouldConserveResources();
856 #else
857  return true;
858 #endif
859 }
860 
861 QString Applet::category() const
862 {
863  if (!d->appletDescription.isValid()) {
864  return i18nc("misc category", "Miscellaneous");
865  }
866 
867  return d->appletDescription.category();
868 }
869 
870 QString Applet::category(const KPluginInfo &applet)
871 {
872  return applet.property("X-KDE-PluginInfo-Category").toString();
873 }
874 
875 QString Applet::category(const QString &appletName)
876 {
877  if (appletName.isEmpty()) {
878  return QString();
879  }
880 
881  const QString constraint = QString("[X-KDE-PluginInfo-Name] == '%1'").arg(appletName);
882  KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint);
883 
884  if (offers.isEmpty()) {
885  return QString();
886  }
887 
888  return offers.first()->property("X-KDE-PluginInfo-Category").toString();
889 }
890 
891 ImmutabilityType Applet::immutability() const
892 {
893  // if this object is itself system immutable, then just return that; it's the most
894  // restrictive setting possible and will override anything that might be happening above it
895  // in the Corona->Containment->Applet hierarchy
896  if (d->transient || (d->mainConfig && d->mainConfig->isImmutable())) {
897  return SystemImmutable;
898  }
899 
900  //Returning the more strict immutability between the applet immutability, Containment and Corona
901  ImmutabilityType upperImmutability = Mutable;
902  Containment *cont = d->isContainment ? 0 : containment();
903 
904  if (cont) {
905  upperImmutability = cont->immutability();
906  } else if (Corona *corona = qobject_cast<Corona*>(scene())) {
907  upperImmutability = corona->immutability();
908  }
909 
910  if (upperImmutability != Mutable) {
911  // it's either system or user immutable, and we already check for local system immutability,
912  // so upperImmutability is guaranteed to be as or more severe as this object's immutability
913  return upperImmutability;
914  } else {
915  return d->immutability;
916  }
917 }
918 
919 void Applet::setImmutability(const ImmutabilityType immutable)
920 {
921  if (d->immutability == immutable || immutable == Plasma::SystemImmutable) {
922  // we do not store system immutability in d->immutability since that gets saved
923  // out to the config file; instead, we check with
924  // the config group itself for this information at all times. this differs from
925  // corona, where SystemImmutability is stored in d->immutability.
926  return;
927  }
928 
929  d->immutability = immutable;
930  updateConstraints(ImmutableConstraint);
931 }
932 
933 Applet::BackgroundHints Applet::backgroundHints() const
934 {
935  return d->backgroundHints;
936 }
937 
938 void Applet::setBackgroundHints(const BackgroundHints hints)
939 {
940  if (d->backgroundHints == hints) {
941  return;
942  }
943 
944  d->backgroundHints = hints;
945  d->preferredBackgroundHints = hints;
946 
947  //Draw the standard background?
948  if ((hints & StandardBackground) || (hints & TranslucentBackground)) {
949  if (!d->background) {
950  d->background = new Plasma::FrameSvg(this);
951  QObject::connect(d->background, SIGNAL(repaintNeeded()), this, SLOT(themeChanged()));
952  }
953 
954  if ((hints & TranslucentBackground) &&
955  Plasma::Theme::defaultTheme()->currentThemeHasImage("widgets/translucentbackground")) {
956  d->background->setImagePath("widgets/translucentbackground");
957  } else {
958  d->background->setImagePath("widgets/background");
959  }
960 
961  d->background->setEnabledBorders(Plasma::FrameSvg::AllBorders);
962  qreal left, top, right, bottom;
963  d->background->getMargins(left, top, right, bottom);
964  setContentsMargins(left, right, top, bottom);
965  QSizeF fitSize(left + right, top + bottom);
966  d->background->resizeFrame(boundingRect().size());
967 
968  //if the background has an "overlay" element decide a random position for it and then save it so it's consistent across plasma starts
969  if (d->background->hasElement("overlay")) {
970  QSize overlaySize = d->background->elementSize("overlay");
971 
972  //position is in the boundaries overlaySize.width()*2, overlaySize.height()
973  qsrand(id());
974  d->background->d->overlayPos.rx() = - (overlaySize.width() /2) + (overlaySize.width() /4) * (qrand() % (4 + 1));
975  d->background->d->overlayPos.ry() = (- (overlaySize.height() /2) + (overlaySize.height() /4) * (qrand() % (4 + 1)))/2;
976  }
977  } else if (d->background) {
978  qreal left, top, right, bottom;
979  d->background->getMargins(left, top, right, bottom);
980 
981  delete d->background;
982  d->background = 0;
983  setContentsMargins(0, 0, 0, 0);
984  }
985 
986  update();
987 }
988 
989 bool Applet::hasFailedToLaunch() const
990 {
991  return d->failed;
992 }
993 
994 void Applet::paintWindowFrame(QPainter *painter,
995  const QStyleOptionGraphicsItem *option, QWidget *widget)
996 {
997  Q_UNUSED(painter)
998  Q_UNUSED(option)
999  Q_UNUSED(widget)
1000  //Here come the code for the window frame
1001  //kDebug() << windowFrameGeometry();
1002  //painter->drawRoundedRect(windowFrameGeometry(), 5, 5);
1003 }
1004 
1005 bool Applet::configurationRequired() const
1006 {
1007  return d->needsConfig;
1008 }
1009 
1010 void Applet::setConfigurationRequired(bool needsConfig, const QString &reason)
1011 {
1012  if (d->needsConfig == needsConfig) {
1013  return;
1014  }
1015 
1016  d->needsConfig = needsConfig;
1017 
1018  if (!needsConfig) {
1019  d->destroyMessageOverlay();
1020  return;
1021  }
1022 
1023  d->createMessageOverlay(true);
1024  d->messageOverlay->opacity = 0.4;
1025 
1026  QGraphicsGridLayout *configLayout = new QGraphicsGridLayout(d->messageOverlay);
1027  configLayout->setContentsMargins(0, 0, 0, 0);
1028 
1029  // configLayout->addStretch();
1030  configLayout->setColumnStretchFactor(0, 5);
1031  configLayout->setColumnStretchFactor(2, 5);
1032  configLayout->setRowStretchFactor(0, 5);
1033  configLayout->setRowStretchFactor(3, 5);
1034 
1035  int row = 1;
1036  if (!reason.isEmpty()) {
1037  Label *explanation = new Label(d->messageOverlay);
1038  explanation->setText(reason);
1039  configLayout->addItem(explanation, row, 1);
1040  configLayout->setColumnStretchFactor(1, 5);
1041  ++row;
1042  configLayout->setAlignment(explanation, Qt::AlignBottom | Qt::AlignCenter);
1043  }
1044 
1045  PushButton *configWidget = new PushButton(d->messageOverlay);
1046  if (!qobject_cast<Plasma::PopupApplet *>(this) && (formFactor() == Plasma::Horizontal || formFactor() == Plasma::Vertical)) {
1047  configWidget->setImage("widgets/configuration-icons", "configure");
1048  configWidget->setMaximumSize(24,24);
1049  configWidget->setMinimumSize(24,24);
1050  } else {
1051  configWidget->setText(i18n("Configure..."));
1052  }
1053  connect(configWidget, SIGNAL(clicked()), this, SLOT(showConfigurationInterface()));
1054  configLayout->addItem(configWidget, row, 1);
1055 
1056  //configLayout->setAlignment(configWidget, Qt::AlignTop | Qt::AlignCenter);
1057  //configLayout->addStretch();
1058 
1059  d->messageOverlay->show();
1060 }
1061 
1062 void Applet::showMessage(const QIcon &icon, const QString &message, const MessageButtons buttons)
1063 {
1064  if (message.isEmpty()) {
1065  d->destroyMessageOverlay();
1066  return;
1067  }
1068 
1069  Corona *corona = qobject_cast<Corona *>(scene());
1070  QGraphicsWidget *mainWidget = new QGraphicsWidget;
1071 
1072  QGraphicsLinearLayout *mainLayout = new QGraphicsLinearLayout(mainWidget);
1073  mainLayout->setOrientation(Qt::Vertical);
1074  mainLayout->addStretch();
1075 
1076  QGraphicsLinearLayout *messageLayout = new QGraphicsLinearLayout();
1077  messageLayout->setOrientation(Qt::Horizontal);
1078 
1079  QGraphicsLinearLayout *buttonLayout = new QGraphicsLinearLayout();
1080  buttonLayout->setOrientation(Qt::Horizontal);
1081 
1082  mainLayout->addItem(messageLayout);
1083  mainLayout->addItem(buttonLayout);
1084  mainLayout->addStretch();
1085 
1086  IconWidget *messageIcon = new IconWidget(mainWidget);
1087  Label *messageText = new Label(mainWidget);
1088  messageText->nativeWidget()->setWordWrap(true);
1089 
1090  messageLayout->addStretch();
1091  messageLayout->addItem(messageIcon);
1092  messageLayout->addItem(messageText);
1093  messageLayout->addStretch();
1094 
1095  messageIcon->setIcon(icon);
1096  messageText->setText(message);
1097 
1098  buttonLayout->addStretch();
1099 
1100  if (buttons & ButtonOk) {
1101  d->messageOkButton = new PushButton(mainWidget);
1102  d->messageOkButton.data()->setText(i18n("&OK"));
1103  d->messageOkButton.data()->setIcon(KIcon("dialog-ok"));
1104  buttonLayout->addItem(d->messageOkButton.data());
1105  connect(d->messageOkButton.data(), SIGNAL(clicked()), this, SLOT(destroyMessageOverlay()));
1106  }
1107 
1108  if (buttons & ButtonYes) {
1109  d->messageYesButton = new PushButton(mainWidget);
1110  d->messageYesButton.data()->setText(i18n("&Yes"));
1111  buttonLayout->addItem(d->messageYesButton.data());
1112  connect(d->messageYesButton.data(), SIGNAL(clicked()), this, SLOT(destroyMessageOverlay()));
1113  }
1114 
1115  if (buttons & ButtonNo) {
1116  d->messageNoButton = new PushButton(mainWidget);
1117  d->messageNoButton.data()->setText(i18n("&No"));
1118  buttonLayout->addItem(d->messageNoButton.data());
1119  connect(d->messageNoButton.data(), SIGNAL(clicked()), this, SLOT(destroyMessageOverlay()));
1120  }
1121 
1122  if (buttons & ButtonCancel) {
1123  d->messageCancelButton = new PushButton(mainWidget);
1124  d->messageCancelButton.data()->setText(i18n("&Cancel"));
1125  d->messageCancelButton.data()->setIcon(KIcon("dialog-cancel"));
1126  buttonLayout->addItem(d->messageCancelButton.data());
1127  connect(d->messageCancelButton.data(), SIGNAL(clicked()), this, SLOT(destroyMessageOverlay()));
1128  }
1129 
1130  d->messageCloseAction = new QAction(d->messageOverlay);
1131  d->messageCloseAction.data()->setShortcut(Qt::Key_Escape);
1132  mainWidget->addAction(d->messageCloseAction.data());
1133  connect(d->messageCloseAction.data(), SIGNAL(triggered()), this, SLOT(destroyMessageOverlay()));
1134 
1135  buttonLayout->addStretch();
1136 
1137  mainWidget->adjustSize();
1138  QSizeF hint = mainWidget->preferredSize();
1139  if (hint.height() > size().height() || hint.width() > size().width()) {
1140  // either a collapsed popup in h/v form factor or just too small,
1141  // so show it in a dialog associated with ourselves
1142  if (corona) {
1143  corona->addOffscreenWidget(mainWidget);
1144  }
1145 
1146  if (d->messageDialog) {
1147  delete d->messageDialog.data()->graphicsWidget();
1148  } else {
1149  d->messageDialog = new Plasma::Dialog;
1150  }
1151 
1152  ToolTipManager::self()->hide(this);
1153  KWindowSystem::setOnAllDesktops(d->messageDialog.data()->winId(), true);
1154  KWindowSystem::setState(d->messageDialog.data()->winId(), NET::SkipTaskbar | NET::SkipPager);
1155  d->messageDialog.data()->setGraphicsWidget(mainWidget);
1156  connect(d->messageDialog.data(), SIGNAL(destroyed(QObject*)), mainWidget, SLOT(deleteLater()));
1157 
1158  // if we are going to show it in a popup, then at least make sure it can be dismissed
1159  if (buttonLayout->count() < 1) {
1160  PushButton *ok = new PushButton(mainWidget);
1161  ok->setText(i18n("OK"));
1162  ok->setIcon(KIcon("dialog-ok"));
1163  buttonLayout->addItem(ok);
1164  connect(ok, SIGNAL(clicked()), this, SLOT(destroyMessageOverlay()));
1165  }
1166  } else {
1167  delete d->messageDialog.data();
1168  d->createMessageOverlay();
1169  d->messageOverlay->opacity = 0.8;
1170  mainWidget->setParentItem(d->messageOverlay);
1171  QGraphicsLinearLayout *l = new QGraphicsLinearLayout(d->messageOverlay);
1172  l->addItem(mainWidget);
1173  }
1174 
1175  if (d->messageDialog) {
1176  QPoint pos = geometry().topLeft().toPoint();
1177  if (corona) {
1178  pos = corona->popupPosition(this, d->messageDialog.data()->size());
1179  }
1180 
1181  d->messageDialog.data()->move(pos);
1182  d->messageDialog.data()->animatedShow(locationToDirection(location()));
1183  } else {
1184  d->messageOverlay->show();
1185  }
1186 }
1187 
1188 QVariantList Applet::startupArguments() const
1189 {
1190  return d->args;
1191 }
1192 
1193 ItemStatus Applet::status() const
1194 {
1195  return d->itemStatus;
1196 }
1197 
1198 void Applet::setStatus(const ItemStatus status)
1199 {
1200  d->itemStatus = status;
1201  emit newStatus(status);
1202 }
1203 
1204 void Applet::flushPendingConstraintsEvents()
1205 {
1206  if (d->pendingConstraints == NoConstraint) {
1207  return;
1208  }
1209 
1210  if (d->constraintsTimer.isActive()) {
1211  d->constraintsTimer.stop();
1212  }
1213 
1214  //kDebug() << "fushing constraints: " << d->pendingConstraints << "!!!!!!!!!!!!!!!!!!!!!!!!!!!";
1215  Plasma::Constraints c = d->pendingConstraints;
1216  d->pendingConstraints = NoConstraint;
1217 
1218  if (c & Plasma::StartupCompletedConstraint) {
1219  //common actions
1220  bool unlocked = immutability() == Mutable;
1221  QAction *closeApplet = d->actions->action("remove");
1222  if (closeApplet) {
1223  closeApplet->setEnabled(unlocked);
1224  closeApplet->setVisible(unlocked);
1225  connect(closeApplet, SIGNAL(triggered(bool)), this, SLOT(selectItemToDestroy()), Qt::UniqueConnection);
1226  }
1227 
1228  QAction *configAction = d->actions->action("configure");
1229  if (configAction) {
1230  if (d->isContainment) {
1231  connect(configAction, SIGNAL(triggered(bool)), this, SLOT(requestConfiguration()), Qt::UniqueConnection);
1232  } else {
1233  connect(configAction, SIGNAL(triggered(bool)), this, SLOT(showConfigurationInterface()), Qt::UniqueConnection);
1234  }
1235 
1236  if (d->hasConfigurationInterface) {
1237  bool canConfig = unlocked || KAuthorized::authorize("plasma/allow_configure_when_locked");
1238  configAction->setVisible(canConfig);
1239  configAction->setEnabled(canConfig);
1240  }
1241  }
1242 
1243  QAction *runAssociatedApplication = d->actions->action("run associated application");
1244  if (runAssociatedApplication) {
1245  connect(runAssociatedApplication, SIGNAL(triggered(bool)), this, SLOT(runAssociatedApplication()), Qt::UniqueConnection);
1246  }
1247 
1248  d->updateShortcuts();
1249  Corona * corona = qobject_cast<Corona*>(scene());
1250  if (corona) {
1251  connect(corona, SIGNAL(shortcutsChanged()), this, SLOT(updateShortcuts()), Qt::UniqueConnection);
1252  }
1253  }
1254 
1255  if (c & Plasma::ImmutableConstraint) {
1256  bool unlocked = immutability() == Mutable;
1257  QAction *action = d->actions->action("remove");
1258  if (action) {
1259  action->setVisible(unlocked);
1260  action->setEnabled(unlocked);
1261  }
1262 
1263  action = d->actions->action("configure");
1264  if (action && d->hasConfigurationInterface) {
1265  bool canConfig = unlocked || KAuthorized::authorize("plasma/allow_configure_when_locked");
1266  action->setVisible(canConfig);
1267  action->setEnabled(canConfig);
1268  }
1269 
1270  if (d->extender) {
1271  foreach (ExtenderItem *item, d->extender.data()->attachedItems()) {
1272  item->d->setMovable(unlocked);
1273  }
1274  }
1275 
1276  if (!unlocked && d->handle) {
1277  AppletHandle *h = d->handle.data();
1278  disconnect(this);
1279 
1280  QGraphicsScene *s = scene();
1281  if (s && h->scene() == s) {
1282  s->removeItem(h);
1283  }
1284 
1285  h->deleteLater();
1286  }
1287 
1288  emit immutabilityChanged(immutability());
1289  }
1290 
1291  if (c & Plasma::SizeConstraint) {
1292  d->positionMessageOverlay();
1293 
1294  if (d->started && layout()) {
1295  layout()->updateGeometry();
1296  }
1297  }
1298 
1299  if (c & Plasma::FormFactorConstraint) {
1300  FormFactor f = formFactor();
1301  if (!d->isContainment && f != Vertical && f != Horizontal) {
1302  setBackgroundHints(d->preferredBackgroundHints);
1303  } else {
1304  BackgroundHints hints = d->preferredBackgroundHints;
1305  setBackgroundHints(NoBackground);
1306  d->preferredBackgroundHints = hints;
1307  }
1308 
1309  if (d->failed) {
1310  if (f == Vertical || f == Horizontal) {
1311  QGraphicsLayoutItem *item = layout()->itemAt(1);
1312  layout()->removeAt(1);
1313  delete item;
1314  }
1315  }
1316 
1317  // avoid putting rotated applets in panels
1318  if (f == Vertical || f == Horizontal) {
1319  QTransform at;
1320  at.rotateRadians(0);
1321  setTransform(at);
1322  }
1323 
1324  //was a size saved for a particular form factor?
1325  if (d->sizeForFormFactor.contains(f)) {
1326  resize(d->sizeForFormFactor.value(f));
1327  }
1328  }
1329 
1330  if (!size().isEmpty() &&
1331  ((c & Plasma::StartupCompletedConstraint) || (c & Plasma::SizeConstraint && !(c & Plasma::FormFactorConstraint)))) {
1332  d->sizeForFormFactor[formFactor()] = size();
1333  }
1334 
1335  if (c & Plasma::SizeConstraint || c & Plasma::FormFactorConstraint) {
1336  if (aspectRatioMode() == Plasma::Square || aspectRatioMode() == Plasma::ConstrainedSquare) {
1337  // enforce square size in panels
1338  //save the old size policy. since ignored doesn't (yet) have a valid use case in containments, use it as special unset value
1339  if (d->preferredSizePolicy == QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored)) {
1340  d->preferredSizePolicy = sizePolicy();
1341  }
1342  if (formFactor() == Horizontal) {
1343  setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding));
1344  } else if (formFactor() == Vertical) {
1345  setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));
1346  } else if (d->preferredSizePolicy != QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored)) {
1347  setSizePolicy(d->preferredSizePolicy);
1348  }
1349  }
1350  updateGeometry();
1351  }
1352 
1353  // now take care of constraints in special subclasses: Contaiment and PopupApplet
1354  Containment* containment = qobject_cast<Plasma::Containment*>(this);
1355  if (d->isContainment && containment) {
1356  containment->d->containmentConstraintsEvent(c);
1357  }
1358 
1359  PopupApplet* popup = qobject_cast<Plasma::PopupApplet*>(this);
1360  if (popup) {
1361  popup->d->popupConstraintsEvent(c);
1362  }
1363 
1364  // pass the constraint on to the actual subclass
1365  constraintsEvent(c);
1366 
1367  if (c & StartupCompletedConstraint) {
1368  // start up is done, we can now go do a mod timer
1369  if (d->modificationsTimer) {
1370  if (d->modificationsTimer->isActive()) {
1371  d->modificationsTimer->stop();
1372  }
1373  } else {
1374  d->modificationsTimer = new QBasicTimer;
1375  }
1376  }
1377 }
1378 
1379 int Applet::type() const
1380 {
1381  return Type;
1382 }
1383 
1384 QList<QAction*> Applet::contextualActions()
1385 {
1386  //kDebug() << "empty context actions";
1387  return d->script ? d->script->contextualActions() : QList<QAction*>();
1388 }
1389 
1390 QAction *Applet::action(QString name) const
1391 {
1392  return d->actions->action(name);
1393 }
1394 
1395 void Applet::addAction(QString name, QAction *action)
1396 {
1397  d->actions->addAction(name, action);
1398 }
1399 
1400 void Applet::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
1401 {
1402  if (!d->started) {
1403  //kDebug() << "not started";
1404  return;
1405  }
1406 
1407  if (transform().isRotating()) {
1408  painter->setRenderHint(QPainter::SmoothPixmapTransform);
1409  painter->setRenderHint(QPainter::Antialiasing);
1410  }
1411 
1412  if (d->background &&
1413  formFactor() != Plasma::Vertical &&
1414  formFactor() != Plasma::Horizontal) {
1415  //kDebug() << "option rect is" << option->rect;
1416  d->background->paintFrame(painter);
1417  }
1418 
1419  if (d->failed) {
1420  //kDebug() << "failed!";
1421  return;
1422  }
1423 
1424  qreal left, top, right, bottom;
1425  getContentsMargins(&left, &top, &right, &bottom);
1426  QRect contentsRect = QRectF(QPointF(0, 0),
1427  boundingRect().size()).adjusted(left, top, -right, -bottom).toRect();
1428 
1429  if (widget && d->isContainment) {
1430  // note that the widget we get is actually the viewport of the view, not the view itself
1431  View* v = qobject_cast<Plasma::View*>(widget->parent());
1432  Containment* c = qobject_cast<Plasma::Containment*>(this);
1433 
1434  if (!v || v->isWallpaperEnabled()) {
1435 
1436  // paint the wallpaper
1437  if (c && c->drawWallpaper() && c->wallpaper()) {
1438  Wallpaper *w = c->wallpaper();
1439  if (!w->isInitialized()) {
1440  // delayed paper initialization
1441  KConfigGroup wallpaperConfig = c->config();
1442  wallpaperConfig = KConfigGroup(&wallpaperConfig, "Wallpaper");
1443  wallpaperConfig = KConfigGroup(&wallpaperConfig, w->pluginName());
1444  w->restore(wallpaperConfig);
1445  disconnect(w, SIGNAL(update(QRectF)), this, SLOT(updateRect(QRectF)));
1446  connect(w, SIGNAL(update(QRectF)), this, SLOT(updateRect(QRectF)));
1447  }
1448 
1449  painter->save();
1450  c->wallpaper()->paint(painter, option->exposedRect);
1451  painter->restore();
1452  }
1453 
1454  // .. and now paint the actual containment interface, but with
1455  // a Containment style option based on the one we get
1456  // the view must be assigned only if its containment is actually our own
1457  Containment::StyleOption coption(*option);
1458  if (v && v->containment() == containment()) {
1459  coption.view = v;
1460  }
1461  paintInterface(painter, &coption, contentsRect);
1462  }
1463  } else {
1464  //kDebug() << "paint interface of" << (QObject*) this;
1465  // paint the applet's interface
1466  paintInterface(painter, option, contentsRect);
1467  }
1468 }
1469 
1470 void Applet::paintInterface(QPainter *painter, const QStyleOptionGraphicsItem *option, const QRect &contentsRect)
1471 {
1472  if (d->script) {
1473  d->script->paintInterface(painter, option, contentsRect);
1474  } else {
1475  //kDebug() << "Applet::paintInterface() default impl";
1476  }
1477 }
1478 
1479 FormFactor Applet::formFactor() const
1480 {
1481  Containment *c = containment();
1482  QGraphicsWidget *pw = qobject_cast<QGraphicsWidget *>(parent());
1483  if (!pw) {
1484  pw = dynamic_cast<QGraphicsWidget *>(parentItem());
1485  }
1486  Plasma::Applet *parentApplet = qobject_cast<Plasma::Applet *>(pw);
1487  //assumption: this loop is usually is -really- short or doesn't run at all
1488  while (!parentApplet && pw && pw->parentWidget()) {
1489  QGraphicsWidget *parentWidget = qobject_cast<QGraphicsWidget *>(pw->parent());
1490  if (!parentWidget) {
1491  parentWidget = dynamic_cast<QGraphicsWidget *>(pw->parentItem());
1492  }
1493  pw = parentWidget;
1494  parentApplet = qobject_cast<Plasma::Applet *>(pw);
1495  }
1496 
1497 
1498  const PopupApplet *pa = dynamic_cast<const PopupApplet *>(this);
1499 
1500  //if the applet is in a widget that isn't a containment
1501  //try to retrieve the formFactor from the parent size
1502  //we can't use our own sizeHint here because it needs formFactor, so endless recursion.
1503  // a popupapplet can always be constrained.
1504  // a normal applet should to but
1505  //FIXME: not always constrained to not break systemmonitor
1506  if (parentApplet && parentApplet != c && c != this && (pa || layout())) {
1507  if (pa || (parentApplet->size().height() < layout()->effectiveSizeHint(Qt::MinimumSize).height())) {
1508  return Plasma::Horizontal;
1509  } else if (pa || (parentApplet->size().width() < layout()->effectiveSizeHint(Qt::MinimumSize).width())) {
1510  return Plasma::Vertical;
1511  }
1512  return parentApplet->formFactor();
1513  }
1514 
1515  return c ? c->d->formFactor : Plasma::Planar;
1516 }
1517 
1518 Containment *Applet::containment() const
1519 {
1520  if (d->isContainment) {
1521  Containment *c = qobject_cast<Containment*>(const_cast<Applet*>(this));
1522  if (c) {
1523  return c;
1524  }
1525  }
1526 
1527  QGraphicsItem *parent = parentItem();
1528  Containment *c = 0;
1529 
1530  while (parent) {
1531  Containment *possibleC = dynamic_cast<Containment*>(parent);
1532  if (possibleC && possibleC->Applet::d->isContainment) {
1533  c = possibleC;
1534  break;
1535  }
1536  parent = parent->parentItem();
1537  }
1538 
1539  if (!c) {
1540  //if the applet is an offscreen widget its parentItem will be 0, while its parent
1541  //will be its parentWidget, so here we check the QObject hierarchy.
1542  QObject *objParent = this->parent();
1543  while (objParent) {
1544  Containment *possibleC = qobject_cast<Containment*>(objParent);
1545  if (possibleC && possibleC->Applet::d->isContainment) {
1546  c = possibleC;
1547  break;
1548  }
1549  objParent = objParent->parent();
1550  }
1551  }
1552 
1553  return c;
1554 }
1555 
1556 void Applet::setGlobalShortcut(const KShortcut &shortcut)
1557 {
1558  if (!d->activationAction) {
1559  d->activationAction = new KAction(this);
1560  d->activationAction->setText(i18n("Activate %1 Widget", name()));
1561  d->activationAction->setObjectName(QString("activate widget %1").arg(id())); // NO I18N
1562  connect(d->activationAction, SIGNAL(triggered()), this, SIGNAL(activate()));
1563  connect(d->activationAction, SIGNAL(globalShortcutChanged(QKeySequence)),
1564  this, SLOT(globalShortcutChanged()));
1565 
1566  QList<QWidget *> widgets = d->actions->associatedWidgets();
1567  foreach (QWidget *w, widgets) {
1568  w->addAction(d->activationAction);
1569  }
1570  } else if (d->activationAction->globalShortcut() == shortcut) {
1571  return;
1572  }
1573 
1574  //kDebug() << "before" << shortcut.primary() << d->activationAction->globalShortcut().primary();
1575  d->activationAction->setGlobalShortcut(
1576  shortcut,
1577  KAction::ShortcutTypes(KAction::ActiveShortcut | KAction::DefaultShortcut),
1578  KAction::NoAutoloading);
1579  d->globalShortcutChanged();
1580 }
1581 
1582 void AppletPrivate::globalShortcutChanged()
1583 {
1584  if (!activationAction) {
1585  return;
1586  }
1587 
1588  KConfigGroup shortcutConfig(mainConfigGroup(), "Shortcuts");
1589  shortcutConfig.writeEntry("global", activationAction->globalShortcut().toString());
1590  scheduleModificationNotification();
1591  //kDebug() << "after" << shortcut.primary() << d->activationAction->globalShortcut().primary();
1592 }
1593 
1594 KShortcut Applet::globalShortcut() const
1595 {
1596  if (d->activationAction) {
1597  return d->activationAction->globalShortcut();
1598  }
1599 
1600  return KShortcut();
1601 }
1602 
1603 bool Applet::isPopupShowing() const
1604 {
1605  return false;
1606 }
1607 
1608 void Applet::addAssociatedWidget(QWidget *widget)
1609 {
1610  d->actions->addAssociatedWidget(widget);
1611 }
1612 
1613 void Applet::removeAssociatedWidget(QWidget *widget)
1614 {
1615  d->actions->removeAssociatedWidget(widget);
1616 }
1617 
1618 Location Applet::location() const
1619 {
1620  Containment *c = containment();
1621  return c ? c->d->location : Plasma::Desktop;
1622 }
1623 
1624 Context *Applet::context() const
1625 {
1626  Containment *c = containment();
1627  Q_ASSERT(c);
1628  return c->d->context();
1629 }
1630 
1631 Plasma::AspectRatioMode Applet::aspectRatioMode() const
1632 {
1633  return d->aspectRatioMode;
1634 }
1635 
1636 void Applet::setAspectRatioMode(Plasma::AspectRatioMode mode)
1637 {
1638  PopupApplet *popup = qobject_cast<PopupApplet *>(this);
1639  if (popup && popup->d->dialogPtr) {
1640  popup->d->dialogPtr.data()->setAspectRatioMode(mode);
1641  popup->d->savedAspectRatio = mode;
1642  }
1643 
1644  d->aspectRatioMode = mode;
1645 }
1646 
1647 void Applet::registerAsDragHandle(QGraphicsItem *item)
1648 {
1649  if (!item || d->registeredAsDragHandle.contains(item)) {
1650  return;
1651  }
1652 
1653  d->registeredAsDragHandle.insert(item);
1654  item->installSceneEventFilter(this);
1655 }
1656 
1657 void Applet::unregisterAsDragHandle(QGraphicsItem *item)
1658 {
1659  if (!item) {
1660  return;
1661  }
1662 
1663  if (d->registeredAsDragHandle.remove(item)) {
1664  if (item != this) {
1665  item->removeSceneEventFilter(this);
1666  }
1667  }
1668 }
1669 
1670 bool Applet::isRegisteredAsDragHandle(QGraphicsItem *item)
1671 {
1672  return d->registeredAsDragHandle.contains(item);
1673 }
1674 
1675 bool Applet::hasConfigurationInterface() const
1676 {
1677  return d->hasConfigurationInterface;
1678 }
1679 
1680 void Applet::publish(AnnouncementMethods methods, const QString &resourceName)
1681 {
1682  if (d->package) {
1683  d->package->d->publish(methods);
1684  } else if (d->appletDescription.isValid()) {
1685  if (!d->service) {
1686  d->service = new PlasmoidService(this);
1687  }
1688 
1689  kDebug() << "publishing package under name " << resourceName;
1690  PackageMetadata pm;
1691  pm.setName(d->appletDescription.name());
1692  pm.setDescription(d->appletDescription.comment());
1693  pm.setIcon(d->appletDescription.icon());
1694  d->service->d->publish(methods, resourceName, pm);
1695  } else {
1696  kDebug() << "Can not publish invalid applets.";
1697  }
1698 }
1699 
1700 void Applet::unpublish()
1701 {
1702  if (d->package) {
1703  d->package->d->unpublish();
1704  } else {
1705  if (d->service) {
1706  d->service->d->unpublish();
1707  }
1708  }
1709 }
1710 
1711 bool Applet::isPublished() const
1712 {
1713  if (d->package) {
1714  return d->package->d->isPublished();
1715  } else {
1716  if (d->service) {
1717  return d->service->d->isPublished();
1718  } else {
1719  return false;
1720  }
1721  }
1722 }
1723 
1724 void Applet::setHasConfigurationInterface(bool hasInterface)
1725 {
1726  if (hasInterface == d->hasConfigurationInterface) {
1727  return;
1728  }
1729 
1730  QAction *configAction = d->actions->action("configure");
1731  if (configAction) {
1732  bool enable = hasInterface;
1733  if (enable) {
1734  const bool unlocked = immutability() == Mutable;
1735  enable = unlocked || KAuthorized::authorize("plasma/allow_configure_when_locked");
1736  }
1737  configAction->setEnabled(enable);
1738  }
1739 
1740  d->hasConfigurationInterface = hasInterface;
1741 }
1742 
1743 KActionCollection* AppletPrivate::defaultActions(QObject *parent)
1744 {
1745  KActionCollection *actions = new KActionCollection(parent);
1746  actions->setConfigGroup("Shortcuts-Applet");
1747 
1748  KAction *configAction = actions->addAction("configure");
1749  configAction->setAutoRepeat(false);
1750  configAction->setText(i18n("Widget Settings"));
1751  configAction->setIcon(KIcon("configure"));
1752  configAction->setShortcut(KShortcut("alt+d, s"));
1753  configAction->setData(AbstractToolBox::ConfigureTool);
1754 
1755  KAction *closeApplet = actions->addAction("remove");
1756  closeApplet->setAutoRepeat(false);
1757  closeApplet->setText(i18n("Remove this Widget"));
1758  closeApplet->setIcon(KIcon("edit-delete"));
1759  closeApplet->setShortcut(KShortcut("alt+d, r"));
1760  closeApplet->setData(AbstractToolBox::DestructiveTool);
1761 
1762  KAction *runAssociatedApplication = actions->addAction("run associated application");
1763  runAssociatedApplication->setAutoRepeat(false);
1764  runAssociatedApplication->setText(i18n("Run the Associated Application"));
1765  runAssociatedApplication->setIcon(KIcon("system-run"));
1766  runAssociatedApplication->setShortcut(KShortcut("alt+d, t"));
1767  runAssociatedApplication->setVisible(false);
1768  runAssociatedApplication->setEnabled(false);
1769  runAssociatedApplication->setData(AbstractToolBox::ControlTool);
1770 
1771  return actions;
1772 }
1773 
1774 bool Applet::eventFilter(QObject *o, QEvent *e)
1775 {
1776  return QObject::eventFilter(o, e);
1777 }
1778 
1779 bool Applet::sceneEventFilter(QGraphicsItem *watched, QEvent *event)
1780 {
1781  if (watched == this) {
1782  switch (event->type()) {
1783  case QEvent::GraphicsSceneHoverEnter:
1784  //kDebug() << "got hoverenterEvent" << immutability() << " " << immutability();
1785  if (immutability() == Mutable) {
1786  QGraphicsSceneHoverEvent *he = static_cast<QGraphicsSceneHoverEvent*>(event);
1787  if (d->handle) {
1788  d->handle.data()->setHoverPos(he->pos());
1789  } else {
1790  //kDebug() << "generated applet handle";
1791  AppletHandle *handle = new AppletHandle(containment(), this, he->pos());
1792  connect(handle, SIGNAL(disappearDone(AppletHandle*)),
1793  this, SLOT(handleDisappeared(AppletHandle*)));
1794  connect(this, SIGNAL(geometryChanged()),
1795  handle, SLOT(appletResized()));
1796  d->handle = handle;
1797  }
1798  }
1799  break;
1800 
1801  case QEvent::GraphicsSceneHoverMove:
1802  if (d->handle && !d->handle.data()->shown() && immutability() == Mutable) {
1803  QGraphicsSceneHoverEvent *he = static_cast<QGraphicsSceneHoverEvent*>(event);
1804  d->handle.data()->setHoverPos(he->pos());
1805  }
1806  break;
1807 
1808  default:
1809  break;
1810  }
1811 
1812  }
1813 
1814  switch (event->type()) {
1815  case QEvent::GraphicsSceneMouseMove:
1816  case QEvent::GraphicsSceneMousePress:
1817  case QEvent::GraphicsSceneMouseRelease:
1818  {
1819  // don't move when the containment is not mutable,
1820  // in the rare case the containment doesn't exists consider it as mutable
1821  if ((flags() & ItemIsMovable) && d->registeredAsDragHandle.contains(watched)) {
1822  Containment *c = containment();
1823  if (!c || c->immutability() == Mutable) {
1824  scene()->sendEvent(this, event);
1825  return false;
1826  }
1827  }
1828  break;
1829  }
1830 
1831  default:
1832  break;
1833  }
1834 
1835  return QGraphicsItem::sceneEventFilter(watched, event);
1836 }
1837 
1838 void Applet::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
1839 {
1840  if (immutability() == Mutable && formFactor() == Plasma::Planar && (flags() & ItemIsMovable)) {
1841  QGraphicsWidget::mouseMoveEvent(event);
1842  }
1843 }
1844 
1845 void Applet::focusInEvent(QFocusEvent *event)
1846 {
1847  if (!isContainment() && containment()) {
1848  //focusing an applet may trigger this event again, but we won't be here more than twice
1849  containment()->d->focusApplet(this);
1850  }
1851 
1852  QGraphicsWidget::focusInEvent(event);
1853 }
1854 
1855 void Applet::resizeEvent(QGraphicsSceneResizeEvent *event)
1856 {
1857  QGraphicsWidget::resizeEvent(event);
1858 
1859  if (d->background) {
1860  d->background->resizeFrame(boundingRect().size());
1861  }
1862 
1863  updateConstraints(Plasma::SizeConstraint);
1864 
1865  d->scheduleModificationNotification();
1866  emit geometryChanged();
1867 }
1868 
1869 bool Applet::isUserConfiguring() const
1870 {
1871  return KConfigDialog::exists(d->configDialogId());
1872 }
1873 
1874 void Applet::showConfigurationInterface()
1875 {
1876  if (!hasConfigurationInterface()) {
1877  return;
1878  }
1879 
1880  if (immutability() != Mutable && !KAuthorized::authorize("plasma/allow_configure_when_locked")) {
1881  return;
1882  }
1883 
1884  KConfigDialog *dlg = KConfigDialog::exists(d->configDialogId());
1885 
1886  if (dlg) {
1887  KWindowSystem::setOnDesktop(dlg->winId(), KWindowSystem::currentDesktop());
1888  dlg->show();
1889  KWindowSystem::activateWindow(dlg->winId());
1890  return;
1891  }
1892 
1893  d->publishUI.publishCheckbox = 0;
1894  if (d->package) {
1895  KConfigDialog *dialog = 0;
1896 
1897  const QString uiFile = d->package->filePath("mainconfigui");
1898  KDesktopFile df(d->package->path() + "/metadata.desktop");
1899  const QStringList kcmPlugins = df.desktopGroup().readEntry("X-Plasma-ConfigPlugins", QStringList());
1900  if (!uiFile.isEmpty() || !kcmPlugins.isEmpty()) {
1901  KConfigSkeleton *configLoader = d->configLoader ? d->configLoader : new KConfigSkeleton(0);
1902  dialog = new AppletConfigDialog(0, d->configDialogId(), configLoader);
1903 
1904  if (!d->configLoader) {
1905  // delete the temporary when this dialog is done
1906  configLoader->setParent(dialog);
1907  }
1908 
1909  dialog->setWindowTitle(d->configWindowTitle());
1910  dialog->setAttribute(Qt::WA_DeleteOnClose, true);
1911  bool hasPages = false;
1912 
1913  QFile f(uiFile);
1914  QUiLoader loader;
1915  QWidget *w = loader.load(&f);
1916  if (w) {
1917  dialog->addPage(w, i18n("Settings"), icon(), i18n("%1 Settings", name()));
1918  hasPages = true;
1919  }
1920 
1921  foreach (const QString &kcm, kcmPlugins) {
1922 #ifndef PLASMA_NO_KUTILS
1923  KCModuleProxy *module = new KCModuleProxy(kcm);
1924  if (module->realModule()) {
1925  connect(module, SIGNAL(changed(bool)), dialog, SLOT(settingsModified(bool)));
1926  dialog->addPage(module, module->moduleInfo().moduleName(), module->moduleInfo().icon());
1927  hasPages = true;
1928  } else {
1929  delete module;
1930  }
1931 #else
1932  KService::Ptr service = KService::serviceByStorageId(kcm);
1933  if (service) {
1934  QString error;
1935  KCModule *module = service->createInstance<KCModule>(dialog, QVariantList(), &error);
1936  if (module) {
1937  connect(module, SIGNAL(changed(bool)), dialog, SLOT(settingsModified(bool)));
1938  dialog->addPage(module, service->name(), service->icon());
1939  hasPages = true;
1940  } else {
1941 #ifndef NDEBUG
1942  kDebug() << "failed to load kcm" << kcm << "for" << name();
1943 #endif
1944  }
1945  }
1946 #endif
1947  }
1948 
1949  if (hasPages) {
1950  d->addGlobalShortcutsPage(dialog);
1951  d->addPublishPage(dialog);
1952  dialog->show();
1953  } else {
1954  delete dialog;
1955  dialog = 0;
1956  }
1957  }
1958 
1959  if (!dialog && d->script) {
1960  d->script->showConfigurationInterface();
1961  }
1962  } else if (d->script) {
1963  d->script->showConfigurationInterface();
1964  } else {
1965  KConfigDialog *dialog = d->generateGenericConfigDialog();
1966  d->addStandardConfigurationPages(dialog);
1967  showConfigurationInterface(dialog);
1968  }
1969 
1970  emit releaseVisualFocus();
1971 }
1972 
1973 void Applet::showConfigurationInterface(QWidget *widget)
1974 {
1975  if (!containment() || !containment()->corona() ||
1976  !containment()->corona()->dialogManager()) {
1977  widget->show();
1978  return;
1979  }
1980 
1981  QMetaObject::invokeMethod(containment()->corona()->dialogManager(), "showDialog", Q_ARG(QWidget *, widget), Q_ARG(Plasma::Applet *, this));
1982 }
1983 
1984 QString AppletPrivate::configDialogId() const
1985 {
1986  return QString("%1settings%2").arg(appletId).arg(q->name());
1987 }
1988 
1989 QString AppletPrivate::configWindowTitle() const
1990 {
1991  return i18nc("@title:window", "%1 Settings", q->name());
1992 }
1993 
1994 QSet<QString> AppletPrivate::knownCategories()
1995 {
1996  // this is to trick the tranlsation tools into making the correct
1997  // strings for translation
1998  QSet<QString> categories = s_customCategories;
1999  categories << QString(I18N_NOOP("Accessibility")).toLower()
2000  << QString(I18N_NOOP("Application Launchers")).toLower()
2001  << QString(I18N_NOOP("Astronomy")).toLower()
2002  << QString(I18N_NOOP("Date and Time")).toLower()
2003  << QString(I18N_NOOP("Development Tools")).toLower()
2004  << QString(I18N_NOOP("Education")).toLower()
2005  << QString(I18N_NOOP("Environment and Weather")).toLower()
2006  << QString(I18N_NOOP("Examples")).toLower()
2007  << QString(I18N_NOOP("File System")).toLower()
2008  << QString(I18N_NOOP("Fun and Games")).toLower()
2009  << QString(I18N_NOOP("Graphics")).toLower()
2010  << QString(I18N_NOOP("Language")).toLower()
2011  << QString(I18N_NOOP("Mapping")).toLower()
2012  << QString(I18N_NOOP("Miscellaneous")).toLower()
2013  << QString(I18N_NOOP("Multimedia")).toLower()
2014  << QString(I18N_NOOP("Online Services")).toLower()
2015  << QString(I18N_NOOP("Productivity")).toLower()
2016  << QString(I18N_NOOP("System Information")).toLower()
2017  << QString(I18N_NOOP("Utilities")).toLower()
2018  << QString(I18N_NOOP("Windows and Tasks")).toLower();
2019  return categories;
2020 }
2021 
2022 KConfigDialog *AppletPrivate::generateGenericConfigDialog()
2023 {
2024  KConfigSkeleton *nullManager = new KConfigSkeleton(0);
2025  KConfigDialog *dialog = new AppletConfigDialog(0, configDialogId(), nullManager);
2026  nullManager->setParent(dialog);
2027  dialog->setFaceType(KPageDialog::Auto);
2028  dialog->setWindowTitle(configWindowTitle());
2029  dialog->setAttribute(Qt::WA_DeleteOnClose, true);
2030  q->createConfigurationInterface(dialog);
2031  dialog->showButton(KDialog::Default, false);
2032  dialog->showButton(KDialog::Help, false);
2033  QObject::connect(dialog, SIGNAL(applyClicked()), q, SLOT(configDialogFinished()));
2034  QObject::connect(dialog, SIGNAL(okClicked()), q, SLOT(configDialogFinished()));
2035  return dialog;
2036 }
2037 
2038 void AppletPrivate::addStandardConfigurationPages(KConfigDialog *dialog)
2039 {
2040  addGlobalShortcutsPage(dialog);
2041  addPublishPage(dialog);
2042 }
2043 
2044 void AppletPrivate::addGlobalShortcutsPage(KConfigDialog *dialog)
2045 {
2046 #ifndef PLASMA_NO_GLOBAL_SHORTCUTS
2047  if (isContainment) {
2048  return;
2049  }
2050 
2051  QWidget *page = new QWidget;
2052  QVBoxLayout *layout = new QVBoxLayout(page);
2053 
2054  if (!shortcutEditor) {
2055  shortcutEditor = new KKeySequenceWidget(page);
2056  QObject::connect(shortcutEditor.data(), SIGNAL(keySequenceChanged(QKeySequence)), dialog, SLOT(settingsModified()));
2057  }
2058 
2059  shortcutEditor.data()->setKeySequence(q->globalShortcut().primary());
2060  layout->addWidget(shortcutEditor.data());
2061  layout->addStretch();
2062  dialog->addPage(page, i18n("Keyboard Shortcut"), "preferences-desktop-keyboard");
2063 
2064  QObject::connect(dialog, SIGNAL(applyClicked()), q, SLOT(configDialogFinished()), Qt::UniqueConnection);
2065  QObject::connect(dialog, SIGNAL(okClicked()), q, SLOT(configDialogFinished()), Qt::UniqueConnection);
2066 #endif
2067 }
2068 
2069 void AppletPrivate::addPublishPage(KConfigDialog *dialog)
2070 {
2071 #ifdef ENABLE_REMOTE_WIDGETS
2072  QWidget *page = new QWidget;
2073  publishUI.setupUi(page);
2074  publishUI.publishCheckbox->setChecked(q->isPublished());
2075  QObject::connect(publishUI.publishCheckbox, SIGNAL(clicked(bool)), dialog, SLOT(settingsModified()));
2076  publishUI.allUsersCheckbox->setEnabled(q->isPublished());
2077  QObject::connect(publishUI.allUsersCheckbox, SIGNAL(clicked(bool)), dialog, SLOT(settingsModified()));
2078 
2079  QString resourceName =
2080  i18nc("%1 is the name of a plasmoid, %2 the name of the machine that plasmoid is published on",
2081  "%1 on %2", q->name(), QHostInfo::localHostName());
2082  if (AuthorizationManager::self()->d->matchingRule(resourceName, Credentials())) {
2083  publishUI.allUsersCheckbox->setChecked(true);
2084  } else {
2085  publishUI.allUsersCheckbox->setChecked(false);
2086  }
2087 
2088  q->connect(publishUI.publishCheckbox, SIGNAL(stateChanged(int)),
2089  q, SLOT(publishCheckboxStateChanged(int)));
2090  dialog->addPage(page, i18n("Share"), "applications-internet");
2091 #endif
2092 }
2093 
2094 void AppletPrivate::publishCheckboxStateChanged(int state)
2095 {
2096  if (state == Qt::Checked) {
2097  publishUI.allUsersCheckbox->setEnabled(true);
2098  } else {
2099  publishUI.allUsersCheckbox->setEnabled(false);
2100  }
2101 }
2102 
2103 void AppletPrivate::configDialogFinished()
2104 {
2105  if (shortcutEditor) {
2106  QKeySequence sequence = shortcutEditor.data()->keySequence();
2107  if (sequence != q->globalShortcut().primary()) {
2108  q->setGlobalShortcut(KShortcut(sequence));
2109  emit q->configNeedsSaving();
2110  }
2111  }
2112 
2113 #ifdef ENABLE_REMOTE_WIDGETS
2114  if (KConfigDialog::exists(configDialogId()) && publishUI.publishCheckbox) {
2115  q->config().writeEntry("Share", publishUI.publishCheckbox->isChecked());
2116 
2117  if (publishUI.publishCheckbox->isChecked()) {
2118  QString resourceName =
2119  i18nc("%1 is the name of a plasmoid, %2 the name of the machine that plasmoid is published on",
2120  "%1 on %2", q->name(), QHostInfo::localHostName());
2121  q->publish(Plasma::ZeroconfAnnouncement, resourceName);
2122  if (publishUI.allUsersCheckbox->isChecked()) {
2123  if (!AuthorizationManager::self()->d->matchingRule(resourceName, Credentials())) {
2124  AuthorizationRule *rule = new AuthorizationRule(resourceName, "");
2125  rule->setPolicy(AuthorizationRule::Allow);
2126  rule->setTargets(AuthorizationRule::AllUsers);
2127  AuthorizationManager::self()->d->rules.append(rule);
2128  }
2129  } else {
2130  AuthorizationRule *matchingRule =
2131  AuthorizationManager::self()->d->matchingRule(resourceName, Credentials());
2132  if (matchingRule) {
2133  AuthorizationManager::self()->d->rules.removeAll(matchingRule);
2134  }
2135  }
2136  } else {
2137  q->unpublish();
2138  }
2139  }
2140 #endif
2141 
2142  if (!configLoader) {
2143  // the config loader will trigger this for us, so we don't need to.
2144  propagateConfigChanged();
2145  if (KConfigDialog *dialog = qobject_cast<KConfigDialog *>(q->sender())) {
2146  dialog->enableButton(KDialog::Apply, false);
2147  }
2148  }
2149 }
2150 
2151 void AppletPrivate::updateShortcuts()
2152 {
2153  if (isContainment) {
2154  //a horrible hack to avoid clobbering corona settings
2155  //we pull them out, then read, then put them back
2156  QList<QString> names;
2157  QList<QAction*> qactions;
2158  names << "add sibling containment" << "configure shortcuts" << "lock widgets";
2159  foreach (const QString &name, names) {
2160  QAction *a = actions->action(name);
2161  actions->takeAction(a); //FIXME this is stupid, KActionCollection needs a takeAction(QString) method
2162  qactions << a;
2163  }
2164 
2165  actions->readSettings();
2166 
2167  for (int i = 0; i < names.size(); ++i) {
2168  QAction *a = qactions.at(i);
2169  if (a) {
2170  actions->addAction(names.at(i), a);
2171  }
2172  }
2173  } else {
2174  actions->readSettings();
2175  }
2176 }
2177 
2178 void AppletPrivate::propagateConfigChanged()
2179 {
2180  if (isContainment) {
2181  Containment *c = qobject_cast<Containment *>(q);
2182  if (c) {
2183  c->d->configChanged();
2184  }
2185  }
2186 
2187  q->configChanged();
2188 }
2189 
2190 void Applet::configChanged()
2191 {
2192  if (d->script) {
2193  if (d->configLoader) {
2194  d->configLoader->readConfig();
2195  }
2196  d->script->configChanged();
2197  }
2198 }
2199 
2200 void Applet::createConfigurationInterface(KConfigDialog *parent)
2201 {
2202  Q_UNUSED(parent)
2203  // virtual method reimplemented by subclasses.
2204  // do not put anything here ...
2205 }
2206 
2207 bool Applet::hasAuthorization(const QString &constraint) const
2208 {
2209  KConfigGroup constraintGroup(KGlobal::config(), "Constraints");
2210  return constraintGroup.readEntry(constraint, true);
2211 }
2212 
2213 void Applet::setAssociatedApplication(const QString &string)
2214 {
2215  AssociatedApplicationManager::self()->setApplication(this, string);
2216 
2217  QAction *runAssociatedApplication = d->actions->action("run associated application");
2218  if (runAssociatedApplication) {
2219  bool valid = AssociatedApplicationManager::self()->appletHasValidAssociatedApplication(this);
2220  valid = valid && hasAuthorization("LaunchApp"); //obey security!
2221  runAssociatedApplication->setVisible(valid);
2222  runAssociatedApplication->setEnabled(valid);
2223  }
2224 }
2225 
2226 void Applet::setAssociatedApplicationUrls(const KUrl::List &urls)
2227 {
2228  AssociatedApplicationManager::self()->setUrls(this, urls);
2229 
2230  QAction *runAssociatedApplication = d->actions->action("run associated application");
2231  if (runAssociatedApplication) {
2232  bool valid = AssociatedApplicationManager::self()->appletHasValidAssociatedApplication(this);
2233  valid = valid && hasAuthorization("LaunchApp"); //obey security!
2234  runAssociatedApplication->setVisible(valid);
2235  runAssociatedApplication->setEnabled(valid);
2236  }
2237 }
2238 
2239 QString Applet::associatedApplication() const
2240 {
2241  return AssociatedApplicationManager::self()->application(this);
2242 }
2243 
2244 KUrl::List Applet::associatedApplicationUrls() const
2245 {
2246  return AssociatedApplicationManager::self()->urls(this);
2247 }
2248 
2249 void Applet::runAssociatedApplication()
2250 {
2251  if (hasAuthorization("LaunchApp")) {
2252  AssociatedApplicationManager::self()->run(this);
2253  }
2254 }
2255 
2256 bool Applet::hasValidAssociatedApplication() const
2257 {
2258  return AssociatedApplicationManager::self()->appletHasValidAssociatedApplication(this);
2259 }
2260 
2261 void AppletPrivate::filterOffers(QList<KService::Ptr> &offers)
2262 {
2263  KConfigGroup constraintGroup(KGlobal::config(), "Constraints");
2264  foreach (const QString &key, constraintGroup.keyList()) {
2265  //kDebug() << "security constraint" << key;
2266  if (constraintGroup.readEntry(key, true)) {
2267  continue;
2268  }
2269 
2270  //ugh. a qlist of ksharedptr<kservice>
2271  QMutableListIterator<KService::Ptr> it(offers);
2272  while (it.hasNext()) {
2273  KService::Ptr p = it.next();
2274  QString prop = QString("X-Plasma-Requires-").append(key);
2275  QVariant req = p->property(prop, QVariant::String);
2276  //valid values: Required/Optional/Unused
2277  QString reqValue;
2278  if (req.isValid()) {
2279  reqValue = req.toString();
2280  } else if (p->property("X-Plasma-API").toString().toLower() == "javascript") {
2281  //TODO: be able to check whether or not a script engine provides "controled"
2282  //bindings; for now we just give a pass to the qscript ones
2283  reqValue = "Unused";
2284  }
2285 
2286  if (!(reqValue == "Optional" || reqValue == "Unused")) {
2287  //if (reqValue == "Required") {
2288  it.remove();
2289  }
2290  }
2291  }
2292 }
2293 
2294 QString AppletPrivate::parentAppConstraint(const QString &parentApp)
2295 {
2296  if (parentApp.isEmpty()) {
2297  return QString("((not exist [X-KDE-ParentApp] or [X-KDE-ParentApp] == '') or [X-KDE-ParentApp] == '%1')")
2298  .arg(KGlobal::mainComponent().aboutData()->appName());
2299  }
2300 
2301  return QString("[X-KDE-ParentApp] == '%1'").arg(parentApp);
2302 }
2303 
2304 KPluginInfo::List Applet::listAppletInfo(const QString &category, const QString &parentApp)
2305 {
2306  return PluginLoader::pluginLoader()->listAppletInfo(category, parentApp);
2307 }
2308 
2309 KPluginInfo::List Applet::listAppletInfoForMimetype(const QString &mimetype)
2310 {
2311  QString constraint = AppletPrivate::parentAppConstraint();
2312  constraint.append(QString(" and '%1' in [X-Plasma-DropMimeTypes]").arg(mimetype));
2313  //kDebug() << "listAppletInfoForMimetype with" << mimetype << constraint;
2314  KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint);
2315  AppletPrivate::filterOffers(offers);
2316  return KPluginInfo::fromServices(offers);
2317 }
2318 
2319 KPluginInfo::List Applet::listAppletInfoForUrl(const QUrl &url)
2320 {
2321  QString constraint = AppletPrivate::parentAppConstraint();
2322  constraint.append(" and exist [X-Plasma-DropUrlPatterns]");
2323  KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint);
2324  AppletPrivate::filterOffers(offers);
2325 
2326  KPluginInfo::List allApplets = KPluginInfo::fromServices(offers);
2327  KPluginInfo::List filtered;
2328  foreach (const KPluginInfo &info, allApplets) {
2329  QStringList urlPatterns = info.property("X-Plasma-DropUrlPatterns").toStringList();
2330  foreach (const QString &glob, urlPatterns) {
2331  QRegExp rx(glob);
2332  rx.setPatternSyntax(QRegExp::Wildcard);
2333  if (rx.exactMatch(url.toString())) {
2334  kDebug() << info.name() << "matches" << glob << url;
2335  filtered << info;
2336  }
2337  }
2338  }
2339 
2340  return filtered;
2341 }
2342 
2343 QStringList Applet::listCategories(const QString &parentApp, bool visibleOnly)
2344 {
2345  QString constraint = AppletPrivate::parentAppConstraint(parentApp);
2346  constraint.append(" and exist [X-KDE-PluginInfo-Category]");
2347 
2348  KConfigGroup group(KGlobal::config(), "General");
2349  const QStringList excluded = group.readEntry("ExcludeCategories", QStringList());
2350  foreach (const QString &category, excluded) {
2351  constraint.append(" and [X-KDE-PluginInfo-Category] != '").append(category).append("'");
2352  }
2353 
2354  KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint);
2355  AppletPrivate::filterOffers(offers);
2356 
2357  QStringList categories;
2358  QSet<QString> known = AppletPrivate::knownCategories();
2359  foreach (const KService::Ptr &applet, offers) {
2360  QString appletCategory = applet->property("X-KDE-PluginInfo-Category").toString();
2361  if (visibleOnly && applet->noDisplay()) {
2362  // we don't want to show the hidden category
2363  continue;
2364  }
2365 
2366  //kDebug() << " and we have " << appletCategory;
2367  if (!appletCategory.isEmpty() && !known.contains(appletCategory.toLower())) {
2368  kDebug() << "Unknown category: " << applet->name() << "says it is in the"
2369  << appletCategory << "category which is unknown to us";
2370  appletCategory.clear();
2371  }
2372 
2373  if (appletCategory.isEmpty()) {
2374  if (!categories.contains(i18nc("misc category", "Miscellaneous"))) {
2375  categories << i18nc("misc category", "Miscellaneous");
2376  }
2377  } else if (!categories.contains(appletCategory)) {
2378  categories << appletCategory;
2379  }
2380  }
2381 
2382  categories.sort();
2383  return categories;
2384 }
2385 
2386 void Applet::setCustomCategories(const QStringList &categories)
2387 {
2388  AppletPrivate::s_customCategories = QSet<QString>::fromList(categories);
2389 }
2390 
2391 QStringList Applet::customCategories()
2392 {
2393  return AppletPrivate::s_customCategories.toList();
2394 }
2395 
2396 Applet *Applet::loadPlasmoid(const QString &path, uint appletId, const QVariantList &args)
2397 {
2398  if (QFile::exists(path + "/metadata.desktop")) {
2399  KService service(path + "/metadata.desktop");
2400  const QStringList& types = service.serviceTypes();
2401 
2402  if (types.contains("Plasma/Containment")) {
2403  return new Containment(path, appletId, args);
2404  } else if (types.contains("Plasma/PopupApplet")) {
2405  return new PopupApplet(path, appletId, args);
2406  } else {
2407  return new Applet(path, appletId, args);
2408  }
2409  }
2410 
2411  return 0;
2412 }
2413 
2414 Applet *Applet::load(const QString &appletName, uint appletId, const QVariantList &args)
2415 {
2416  return PluginLoader::pluginLoader()->loadApplet(appletName, appletId, args);
2417 }
2418 
2419 Applet *Applet::load(const KPluginInfo &info, uint appletId, const QVariantList &args)
2420 {
2421  if (!info.isValid()) {
2422  return 0;
2423  }
2424 
2425  return load(info.pluginName(), appletId, args);
2426 }
2427 
2428 QVariant Applet::itemChange(GraphicsItemChange change, const QVariant &value)
2429 {
2430  QVariant ret = QGraphicsWidget::itemChange(change, value);
2431 
2432  //kDebug() << change;
2433  switch (change) {
2434  case ItemSceneHasChanged: {
2435  Corona *newCorona = qobject_cast<Corona *>(qvariant_cast<QGraphicsScene*>(value));
2436  if (newCorona && newCorona->immutability() != Mutable) {
2437  updateConstraints(ImmutableConstraint);
2438  }
2439  }
2440  break;
2441  case ItemParentChange:
2442  if (!d->isContainment) {
2443  Containment *c = containment();
2444  if (d->mainConfig && !c) {
2445  kWarning() << "Configuration object was requested prior to init(), which is too early. "
2446  "Please fix this item:" << parentItem() << value.value<QGraphicsItem *>()
2447  << name();
2448 
2449  Applet *newC = dynamic_cast<Applet*>(value.value<QGraphicsItem *>());
2450  if (newC) {
2451  // if this is an applet, and we've just been assigned to our first containment,
2452  // but the applet did something stupid like ask for the config() object prior to
2453  // this happening (e.g. inits ctor) then let's repair that situation for them.
2454  KConfigGroup *old = d->mainConfig;
2455  KConfigGroup appletConfig = newC->config();
2456  appletConfig = KConfigGroup(&appletConfig, "Applets");
2457  d->mainConfig = new KConfigGroup(&appletConfig, QString::number(d->appletId));
2458  old->copyTo(d->mainConfig);
2459  old->deleteGroup();
2460  delete old;
2461  }
2462  }
2463  }
2464  break;
2465  case ItemParentHasChanged:
2466  {
2467  if (isContainment()) {
2468  removeSceneEventFilter(this);
2469  } else {
2470  Containment *c = containment();
2471  if (c && c->containmentType() == Containment::DesktopContainment) {
2472  installSceneEventFilter(this);
2473  } else {
2474  removeSceneEventFilter(this);
2475  }
2476  }
2477  }
2478  break;
2479  case ItemPositionHasChanged:
2480  emit geometryChanged();
2481  // fall through!
2482  case ItemTransformHasChanged:
2483  d->scheduleModificationNotification();
2484  break;
2485  default:
2486  break;
2487  };
2488 
2489  return ret;
2490 }
2491 
2492 QPainterPath Applet::shape() const
2493 {
2494  if (d->script) {
2495  return d->script->shape();
2496  }
2497 
2498  return QGraphicsWidget::shape();
2499 }
2500 
2501 QSizeF Applet::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
2502 {
2503  QSizeF hint = QGraphicsWidget::sizeHint(which, constraint);
2504  const FormFactor ff = formFactor();
2505 
2506  // in panels make sure that the contents won't exit from the panel
2507  if (which == Qt::MinimumSize) {
2508  if (ff == Horizontal) {
2509  hint.setHeight(0);
2510  } else if (ff == Vertical) {
2511  hint.setWidth(0);
2512  }
2513  }
2514 
2515  // enforce a square size in panels
2516  if (d->aspectRatioMode == Plasma::Square) {
2517  if (ff == Horizontal) {
2518  hint.setWidth(size().height());
2519  } else if (ff == Vertical) {
2520  hint.setHeight(size().width());
2521  }
2522  } else if (d->aspectRatioMode == Plasma::ConstrainedSquare) {
2523  //enforce a size not wider than tall
2524  if (ff == Horizontal) {
2525  hint.setWidth(size().height());
2526  //enforce a size not taller than wide
2527  } else if (ff == Vertical && (which == Qt::MaximumSize || size().width() <= KIconLoader::SizeLarge)) {
2528  hint.setHeight(size().width());
2529  }
2530  }
2531 
2532  return hint;
2533 }
2534 
2535 void Applet::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
2536 {
2537  Q_UNUSED(event)
2538 }
2539 
2540 void Applet::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
2541 {
2542  Q_UNUSED(event)
2543 }
2544 
2545 void Applet::timerEvent(QTimerEvent *event)
2546 {
2547  if (d->transient) {
2548  d->constraintsTimer.stop();
2549  d->busyWidgetTimer.stop();
2550  if (d->modificationsTimer) {
2551  d->modificationsTimer->stop();
2552  }
2553  return;
2554  }
2555 
2556  if (event->timerId() == d->constraintsTimer.timerId()) {
2557  d->constraintsTimer.stop();
2558 
2559  // Don't flushPendingConstraints if we're just starting up
2560  // flushPendingConstraints will be called by Corona
2561  if(!(d->pendingConstraints & Plasma::StartupCompletedConstraint)) {
2562  flushPendingConstraintsEvents();
2563  }
2564  } else if (d->modificationsTimer && event->timerId() == d->modificationsTimer->timerId()) {
2565  d->modificationsTimer->stop();
2566  // invalid group, will result in save using the default group
2567  KConfigGroup cg;
2568 
2569  save(cg);
2570  emit configNeedsSaving();
2571  } else if (event->timerId() == d->busyWidgetTimer.timerId()) {
2572  if (!d->busyWidget) {
2573  d->createMessageOverlay(false);
2574  d->messageOverlay->opacity = 0;
2575 
2576  QGraphicsLinearLayout *mainLayout = new QGraphicsLinearLayout(d->messageOverlay);
2577  d->busyWidget = new Plasma::BusyWidget(d->messageOverlay);
2578  d->busyWidget->setAcceptHoverEvents(false);
2579  d->busyWidget->setAcceptedMouseButtons(Qt::NoButton);
2580  d->messageOverlay->setAcceptHoverEvents(false);
2581  d->messageOverlay->setAcceptedMouseButtons(Qt::NoButton);
2582 
2583  mainLayout->addStretch();
2584  mainLayout->addItem(d->busyWidget);
2585  mainLayout->addStretch();
2586  }
2587  }
2588 }
2589 
2590 QRect Applet::screenRect() const
2591 {
2592  QGraphicsView *v = view();
2593 
2594  if (v) {
2595  QPointF bottomRight = pos();
2596  bottomRight.rx() += size().width();
2597  bottomRight.ry() += size().height();
2598 
2599  QPoint tL = v->mapToGlobal(v->mapFromScene(pos()));
2600  QPoint bR = v->mapToGlobal(v->mapFromScene(bottomRight));
2601  return QRect(QPoint(tL.x(), tL.y()), QSize(bR.x() - tL.x(), bR.y() - tL.y()));
2602  }
2603 
2604  //The applet doesn't have a view on it.
2605  //So a screenRect isn't relevant.
2606  return QRect(QPoint(0, 0), QSize(0, 0));
2607 }
2608 
2609 void Applet::raise()
2610 {
2611  setZValue(++AppletPrivate::s_maxZValue);
2612 }
2613 
2614 void Applet::lower()
2615 {
2616  setZValue(--AppletPrivate::s_minZValue);
2617 }
2618 
2619 void AppletPrivate::setIsContainment(bool nowIsContainment, bool forceUpdate)
2620 {
2621  if (isContainment == nowIsContainment && !forceUpdate) {
2622  return;
2623  }
2624 
2625  isContainment = nowIsContainment;
2626  //FIXME I do not like this function.
2627  //currently it's only called before ctmt/applet init, with (true,true), and I'm going to assume it stays that way.
2628  //if someone calls it at some other time it'll cause headaches. :P
2629 
2630  delete mainConfig;
2631  mainConfig = 0;
2632 
2633  Containment *c = q->containment();
2634  if (c) {
2635  c->d->checkContainmentFurniture();
2636  }
2637 }
2638 
2639 bool Applet::isContainment() const
2640 {
2641  return d->isContainment;
2642 }
2643 
2644 // PRIVATE CLASS IMPLEMENTATION
2645 
2646 AppletPrivate::AppletPrivate(KService::Ptr service, const KPluginInfo *info, int uniqueID, Applet *applet)
2647  : appletId(uniqueID),
2648  q(applet),
2649  service(0),
2650  preferredBackgroundHints(Applet::StandardBackground),
2651  backgroundHints(Applet::NoBackground),
2652  aspectRatioMode(Plasma::KeepAspectRatio),
2653  immutability(Mutable),
2654  appletDescription(info ? *info : KPluginInfo(service)),
2655  background(0),
2656  mainConfig(0),
2657  pendingConstraints(NoConstraint),
2658  messageOverlay(0),
2659  messageOverlayProxy(0),
2660  busyWidget(0),
2661  script(0),
2662  package(0),
2663  configLoader(0),
2664  actions(AppletPrivate::defaultActions(applet)),
2665  activationAction(0),
2666  itemStatus(UnknownStatus),
2667  preferredSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored),
2668  modificationsTimer(0),
2669  hasConfigurationInterface(false),
2670  failed(false),
2671  isContainment(false),
2672  transient(false),
2673  needsConfig(false),
2674  started(false)
2675 {
2676  if (appletId == 0) {
2677  appletId = ++s_maxAppletId;
2678  } else if (appletId > s_maxAppletId) {
2679  s_maxAppletId = appletId;
2680  }
2681 }
2682 
2683 AppletPrivate::~AppletPrivate()
2684 {
2685  if (activationAction && activationAction->isGlobalShortcutEnabled()) {
2686  //kDebug() << "reseting global action for" << q->name() << activationAction->objectName();
2687  activationAction->forgetGlobalShortcut();
2688  }
2689 
2690  delete extender.data();
2691 
2692  delete script;
2693  script = 0;
2694  delete package;
2695  package = 0;
2696  delete configLoader;
2697  configLoader = 0;
2698  delete mainConfig;
2699  mainConfig = 0;
2700  delete modificationsTimer;
2701 }
2702 
2703 void AppletPrivate::init(const QString &packagePath)
2704 {
2705  // WARNING: do not access config() OR globalConfig() in this method!
2706  // that requires a scene, which is not available at this point
2707  q->setCacheMode(Applet::DeviceCoordinateCache);
2708  q->setAcceptsHoverEvents(true);
2709  q->setFlag(QGraphicsItem::ItemIsFocusable, true);
2710  q->setFocusPolicy(Qt::ClickFocus);
2711  // FIXME: adding here because nothing seems to be doing it in QGraphicsView,
2712  // but it doesn't actually work anyways =/
2713  q->setLayoutDirection(qApp->layoutDirection());
2714 
2715  //set a default size before any saved settings are read
2716  QSize size(200, 200);
2717  q->setBackgroundHints(Applet::DefaultBackground);
2718  q->setHasConfigurationInterface(true); //FIXME why not default it to true in the constructor?
2719 
2720  QAction *closeApplet = actions->action("remove");
2721  if (closeApplet) {
2722  closeApplet->setText(i18nc("%1 is the name of the applet", "Remove this %1", q->name()));
2723  }
2724 
2725  QAction *configAction = actions->action("configure");
2726  if (configAction) {
2727  configAction->setText(i18nc("%1 is the name of the applet", "%1 Settings", q->name()));
2728  }
2729 
2730  QObject::connect(q, SIGNAL(activate()), q, SLOT(setFocus()));
2731  if (!appletDescription.isValid()) {
2732  kDebug() << "Check your constructor! "
2733  << "You probably want to be passing in a Service::Ptr "
2734  << "or a QVariantList with a valid storageid as arg[0].";
2735  q->resize(size);
2736  return;
2737  }
2738 
2739  QVariant s = appletDescription.property("X-Plasma-DefaultSize");
2740  if (s.isValid()) {
2741  size = s.toSize();
2742  }
2743  //kDebug() << "size" << size;
2744  q->resize(size);
2745 
2746  QString api = appletDescription.property("X-Plasma-API").toString();
2747 
2748  // we have a scripted plasmoid
2749  if (!api.isEmpty()) {
2750  // find where the Package is
2751  QString path = packagePath;
2752  if (path.isEmpty()) {
2753  QString subPath = q->packageStructure()->defaultPackageRoot() + '/' + appletDescription.pluginName() + '/';
2754  path = KStandardDirs::locate("data", subPath + "metadata.desktop");
2755  if (path.isEmpty()) {
2756  path = KStandardDirs::locate("data", subPath);
2757  } else {
2758  path.remove(QString("metadata.desktop"));
2759  }
2760  } else if (!path.endsWith('/')) {
2761  path.append('/');
2762  }
2763 
2764  if (path.isEmpty()) {
2765  q->setFailedToLaunch(
2766  true,
2767  i18nc("Package file, name of the widget",
2768  "Could not locate the %1 package required for the %2 widget.",
2769  appletDescription.pluginName(), appletDescription.name()));
2770  } else {
2771  // create the package and see if we have something real
2772  //kDebug() << "trying for" << path;
2773  PackageStructure::Ptr structure = Plasma::packageStructure(api, Plasma::AppletComponent);
2774  structure->setPath(path);
2775  package = new Package(path, structure);
2776 
2777  if (package->isValid()) {
2778  // now we try and set up the script engine.
2779  // it will be parented to this applet and so will get
2780  // deleted when the applet does
2781 
2782  script = Plasma::loadScriptEngine(api, q);
2783  if (!script) {
2784  delete package;
2785  package = 0;
2786  q->setFailedToLaunch(true,
2787  i18nc("API or programming language the widget was written in, name of the widget",
2788  "Could not create a %1 ScriptEngine for the %2 widget.",
2789  api, appletDescription.name()));
2790  }
2791  } else {
2792  q->setFailedToLaunch(true, i18nc("Package file, name of the widget",
2793  "Could not open the %1 package required for the %2 widget.",
2794  appletDescription.pluginName(), appletDescription.name()));
2795  delete package;
2796  package = 0;
2797  }
2798  }
2799  }
2800 }
2801 
2802 // put all setup routines for script here. at this point we can assume that
2803 // package exists and that we have a script engine
2804 void AppletPrivate::setupScriptSupport()
2805 {
2806  if (!package) {
2807  return;
2808  }
2809 
2810  kDebug() << "setting up script support, package is in" << package->path()
2811  << "which is a" << package->structure()->type() << "package"
2812  << ", main script is" << package->filePath("mainscript");
2813 
2814  QString translationsPath = package->filePath("translations");
2815  if (!translationsPath.isEmpty()) {
2816  //FIXME: we should _probably_ use a KComponentData to segregate the applets
2817  // from each other; but I want to get the basics working first :)
2818  KGlobal::dirs()->addResourceDir("locale", translationsPath);
2819  KGlobal::locale()->insertCatalog(package->metadata().pluginName());
2820  }
2821 
2822  QString xmlPath = package->filePath("mainconfigxml");
2823  if (!xmlPath.isEmpty()) {
2824  QFile file(xmlPath);
2825  KConfigGroup config = q->config();
2826  configLoader = new ConfigLoader(&config, &file);
2827  QObject::connect(configLoader, SIGNAL(configChanged()), q, SLOT(propagateConfigChanged()));
2828  }
2829 
2830  if (!package->filePath("mainconfigui").isEmpty()) {
2831  q->setHasConfigurationInterface(true);
2832  }
2833 }
2834 
2835 QString AppletPrivate::globalName() const
2836 {
2837  if (!appletDescription.isValid()) {
2838  return QString();
2839  }
2840 
2841  return appletDescription.service()->library();
2842 }
2843 
2844 QString AppletPrivate::instanceName()
2845 {
2846  if (!appletDescription.isValid()) {
2847  return QString();
2848  }
2849 
2850  return appletDescription.service()->library() + QString::number(appletId);
2851 }
2852 
2853 void AppletPrivate::scheduleConstraintsUpdate(Plasma::Constraints c)
2854 {
2855  // Don't start up a timer if we're just starting up
2856  // flushPendingConstraints will be called by Corona
2857  if (started && !constraintsTimer.isActive() && !(c & Plasma::StartupCompletedConstraint)) {
2858  constraintsTimer.start(0, q);
2859  }
2860 
2861  if (c & Plasma::StartupCompletedConstraint) {
2862  started = true;
2863  }
2864 
2865  pendingConstraints |= c;
2866 }
2867 
2868 void AppletPrivate::scheduleModificationNotification()
2869 {
2870  // modificationsTimer is not allocated until we get our notice of being started
2871  if (modificationsTimer) {
2872  // schedule a save
2873  if (modificationsTimer->isActive()) {
2874  modificationsTimer->stop();
2875  }
2876 
2877  modificationsTimer->start(1000, q);
2878  }
2879 }
2880 
2881 KConfigGroup *AppletPrivate::mainConfigGroup()
2882 {
2883  if (mainConfig) {
2884  return mainConfig;
2885  }
2886 
2887  bool newGroup = false;
2888  if (isContainment) {
2889  Corona *corona = qobject_cast<Corona*>(q->scene());
2890  KConfigGroup containmentConfig;
2891  //kDebug() << "got a corona, baby?" << (QObject*)corona << (QObject*)q;
2892 
2893  if (corona) {
2894  containmentConfig = KConfigGroup(corona->config(), "Containments");
2895  } else {
2896  containmentConfig = KConfigGroup(KGlobal::config(), "Containments");
2897  }
2898 
2899  if (package && !containmentConfig.hasGroup(QString::number(appletId))) {
2900  newGroup = true;
2901  }
2902 
2903  mainConfig = new KConfigGroup(&containmentConfig, QString::number(appletId));
2904  } else {
2905  KConfigGroup appletConfig;
2906 
2907  Containment *c = q->containment();
2908  Applet *parentApplet = qobject_cast<Applet *>(q->parent());
2909  if (parentApplet && parentApplet != static_cast<Applet *>(c)) {
2910  // this applet is nested inside another applet! use it's config
2911  // as the parent group in the config
2912  appletConfig = parentApplet->config();
2913  appletConfig = KConfigGroup(&appletConfig, "Applets");
2914  } else if (c) {
2915  // applet directly in a Containment, as usual
2916  appletConfig = c->config();
2917  appletConfig = KConfigGroup(&appletConfig, "Applets");
2918  } else {
2919  kWarning() << "requesting config for" << q->name() << "without a containment!";
2920  appletConfig = KConfigGroup(KGlobal::config(), "Applets");
2921  }
2922 
2923  if (package && !appletConfig.hasGroup(QString::number(appletId))) {
2924  newGroup = true;
2925  }
2926 
2927  mainConfig = new KConfigGroup(&appletConfig, QString::number(appletId));
2928  }
2929 
2930  if (newGroup) {
2931  //see if we have a default configuration in our package
2932  const QString defaultConfigFile = q->package()->filePath("defaultconfig");
2933  if (!defaultConfigFile.isEmpty()) {
2934  kDebug() << "copying default config: " << q->package()->filePath("defaultconfig");
2935  KConfigGroup defaultConfig(KSharedConfig::openConfig(defaultConfigFile)->group("Configuration"));
2936  defaultConfig.copyTo(mainConfig);
2937  }
2938  }
2939 
2940  return mainConfig;
2941 }
2942 
2943 QString AppletPrivate::visibleFailureText(const QString &reason)
2944 {
2945  QString text;
2946 
2947  if (reason.isEmpty()) {
2948  text = i18n("This object could not be created.");
2949  } else {
2950  text = i18n("This object could not be created for the following reason:<p><b>%1</b></p>", reason);
2951  }
2952 
2953  return text;
2954 }
2955 
2956 void AppletPrivate::themeChanged()
2957 {
2958  if (background) {
2959  //do again the translucent background fallback
2960  q->setBackgroundHints(backgroundHints);
2961 
2962  qreal left;
2963  qreal right;
2964  qreal top;
2965  qreal bottom;
2966  background->getMargins(left, top, right, bottom);
2967  q->setContentsMargins(left, right, top, bottom);
2968  }
2969  q->update();
2970 }
2971 
2972 void AppletPrivate::resetConfigurationObject()
2973 {
2974  // make sure mainConfigGroup exists in all cases
2975  mainConfigGroup();
2976 
2977  mainConfig->deleteGroup();
2978  delete mainConfig;
2979  mainConfig = 0;
2980 
2981  Corona * corona = qobject_cast<Corona*>(q->scene());
2982  if (corona) {
2983  corona->requireConfigSync();
2984  }
2985 }
2986 
2987 void AppletPrivate::handleDisappeared(AppletHandle *h)
2988 {
2989  if (h == handle.data()) {
2990  h->detachApplet();
2991  QGraphicsScene *scene = q->scene();
2992  if (scene && h->scene() == scene) {
2993  scene->removeItem(h);
2994  }
2995  h->deleteLater();
2996  }
2997 }
2998 
2999 void ContainmentPrivate::checkRemoveAction()
3000 {
3001  q->enableAction("remove", q->immutability() == Mutable);
3002 }
3003 
3004 
3005 uint AppletPrivate::s_maxAppletId = 0;
3006 int AppletPrivate::s_maxZValue = 0;
3007 int AppletPrivate::s_minZValue = 0;
3008 PackageStructure::Ptr AppletPrivate::packageStructure(0);
3009 QSet<QString> AppletPrivate::s_customCategories;
3010 
3011 AppletOverlayWidget::AppletOverlayWidget(QGraphicsWidget *parent)
3012  : QGraphicsWidget(parent),
3013  opacity(0.4)
3014 {
3015  resize(parent->size());
3016 }
3017 
3018 void AppletOverlayWidget::destroy()
3019 {
3020  Animation *anim = Plasma::Animator::create(Plasma::Animator::DisappearAnimation);
3021  if (anim) {
3022  connect(anim, SIGNAL(finished()), this, SLOT(overlayAnimationComplete()));
3023  anim->setTargetWidget(this);
3024  anim->start();
3025  } else {
3026  overlayAnimationComplete();
3027  }
3028 }
3029 
3030 void AppletOverlayWidget::mousePressEvent(QGraphicsSceneMouseEvent *event)
3031 {
3032  event->accept();
3033 }
3034 
3035 void AppletOverlayWidget::overlayAnimationComplete()
3036 {
3037  if (scene()) {
3038  scene()->removeItem(this);
3039  }
3040  deleteLater();
3041 }
3042 
3043 void AppletOverlayWidget::paint(QPainter *painter,
3044  const QStyleOptionGraphicsItem *option,
3045  QWidget *widget)
3046 {
3047  Q_UNUSED(option)
3048  Q_UNUSED(widget)
3049 
3050  if (qFuzzyCompare(1, 1+opacity)) {
3051  return;
3052  }
3053 
3054  QColor wash = Plasma::Theme::defaultTheme()->color(Theme::BackgroundColor);
3055  wash.setAlphaF(opacity);
3056 
3057  Applet *applet = qobject_cast<Applet *>(parentWidget());
3058 
3059 
3060  QPainterPath backgroundShape;
3061  if (!applet || applet->backgroundHints() & Applet::StandardBackground) {
3062  //FIXME: a resize here is nasty, but perhaps still better than an eventfilter just for that..
3063  if (parentWidget()->contentsRect().size() != size()) {
3064  resize(parentWidget()->contentsRect().size());
3065  }
3066  backgroundShape = PaintUtils::roundedRectangle(contentsRect(), 5);
3067  } else {
3068  backgroundShape = shape();
3069  }
3070 
3071  painter->setRenderHints(QPainter::Antialiasing);
3072  painter->fillPath(backgroundShape, wash);
3073 }
3074 
3075 #if QT_VERSION >= 0x040700
3076 // in QGraphicsWidget now; preserve BC by implementing it as a protected method
3077 void Applet::geometryChanged()
3078 {
3079  emit QGraphicsWidget::geometryChanged();
3080 }
3081 #endif
3082 
3083 } // Plasma namespace
3084 
3085 #include "applet.moc"
3086 #include "private/applet_p.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Tue Jul 17 2012 07:28:47 by doxygen 1.8.1 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

Plasma

Skip menu "Plasma"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdelibs-4.8.4 API Reference

Skip menu "kdelibs-4.8.4 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal