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

Plasma

  • plasma
containment.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2007 by Aaron Seigo <aseigo@kde.org>
3  * Copyright 2008 by Ménard Alexis <darktears31@gmail.com>
4  * Copyright 2009 Chani Armitage <chani@kde.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU Library General Public License as
8  * published by the Free Software Foundation; either version 2, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this program; if not, write to the
18  * Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  */
21 
22 #include "containment.h"
23 #include "private/containment_p.h"
24 
25 #include "config-plasma.h"
26 
27 #include <QApplication>
28 #include <QClipboard>
29 #include <QFile>
30 #include <QGraphicsSceneContextMenuEvent>
31 #include <QGraphicsView>
32 #include <QMimeData>
33 #include <QPainter>
34 #include <QStyleOptionGraphicsItem>
35 #include <QGraphicsLayout>
36 #include <QGraphicsLinearLayout>
37 
38 #include <kaction.h>
39 #include <kauthorized.h>
40 #include <kicon.h>
41 #include <kmenu.h>
42 #include <kmessagebox.h>
43 #include <kmimetype.h>
44 #include <kservicetypetrader.h>
45 #include <kstandarddirs.h>
46 #include <ktemporaryfile.h>
47 #include <kwindowsystem.h>
48 
49 #ifndef PLASMA_NO_KIO
50 #include "kio/jobclasses.h" // for KIO::JobFlags
51 #include "kio/job.h"
52 #include "kio/scheduler.h"
53 #endif
54 
55 #include "abstracttoolbox.h"
56 #include "animator.h"
57 #include "context.h"
58 #include "containmentactions.h"
59 #include "containmentactionspluginsconfig.h"
60 #include "corona.h"
61 #include "extender.h"
62 #include "extenderitem.h"
63 #include "svg.h"
64 #include "wallpaper.h"
65 
66 #include "remote/accessappletjob.h"
67 #include "remote/accessmanager.h"
68 
69 #include "private/applet_p.h"
70 #include "private/containmentactionspluginsconfig_p.h"
71 #include "private/extenderitemmimedata_p.h"
72 #include "private/extenderapplet_p.h"
73 #include "private/wallpaper_p.h"
74 
75 #include "plasma/plasma.h"
76 #include "animations/animation.h"
77 
78 namespace Plasma
79 {
80 
81 bool ContainmentPrivate::s_positioningPanels = false;
82 QHash<QString, ContainmentActions*> ContainmentPrivate::globalActionPlugins;
83 static const char defaultWallpaper[] = "image";
84 static const char defaultWallpaperMode[] = "SingleImage";
85 
86 Containment::StyleOption::StyleOption()
87  : QStyleOptionGraphicsItem(),
88  view(0)
89 {
90  version = Version;
91  type = Type;
92 }
93 
94 Containment::StyleOption::StyleOption(const Containment::StyleOption & other)
95  : QStyleOptionGraphicsItem(other),
96  view(other.view)
97 {
98  version = Version;
99  type = Type;
100 }
101 
102 Containment::StyleOption::StyleOption(const QStyleOptionGraphicsItem &other)
103  : QStyleOptionGraphicsItem(other),
104  view(0)
105 {
106  version = Version;
107  type = Type;
108 }
109 
110 Containment::Containment(QGraphicsItem *parent,
111  const QString &serviceId,
112  uint containmentId)
113  : Applet(parent, serviceId, containmentId),
114  d(new ContainmentPrivate(this))
115 {
116  // WARNING: do not access config() OR globalConfig() in this method!
117  // that requires a scene, which is not available at this point
118  setPos(0, 0);
119  setBackgroundHints(NoBackground);
120  setContainmentType(CustomContainment);
121 }
122 
123 Containment::Containment(QObject *parent, const QVariantList &args)
124  : Applet(parent, args),
125  d(new ContainmentPrivate(this))
126 {
127  // WARNING: do not access config() OR globalConfig() in this method!
128  // that requires a scene, which is not available at this point
129  setPos(0, 0);
130  setBackgroundHints(NoBackground);
131 }
132 
133 Containment::Containment(const QString &packagePath, uint appletId, const QVariantList &args)
134  : Plasma::Applet(packagePath, appletId, args),
135  d(new ContainmentPrivate(this))
136 {
137  // WARNING: do not access config() OR globalConfig() in this method!
138  // that requires a scene, which is not available at this point
139  setPos(0, 0);
140  setBackgroundHints(NoBackground);
141 }
142 
143 Containment::~Containment()
144 {
145  // Applet touches our dptr if we are a containment and is the superclass (think of dtors)
146  // so we reset this as we exit the building
147  Applet::d->isContainment = false;
148  delete d;
149 }
150 
151 void Containment::init()
152 {
153  Applet::init();
154  if (!isContainment()) {
155  return;
156  }
157 
158  setCacheMode(NoCache);
159  setFlag(QGraphicsItem::ItemIsMovable, false);
160  setFlag(QGraphicsItem::ItemClipsChildrenToShape, true);
161  setAcceptDrops(true);
162  setAcceptsHoverEvents(true);
163 
164  if (d->type == NoContainmentType) {
165  setContainmentType(DesktopContainment);
166  }
167 
168  //connect actions
169  ContainmentPrivate::addDefaultActions(d->actions(), this);
170  bool unlocked = immutability() == Mutable;
171 
172  //fix the text of the actions that need name()
173  //btw, do we really want to use name() when it's a desktopcontainment?
174  QAction *closeApplet = action("remove");
175  if (closeApplet) {
176  closeApplet->setText(i18nc("%1 is the name of the applet", "Remove this %1", name()));
177  }
178 
179  QAction *configAction = action("configure");
180  if (configAction) {
181  configAction->setText(i18nc("%1 is the name of the applet", "%1 Settings", name()));
182  }
183 
184  QAction *appletBrowserAction = action("add widgets");
185  if (appletBrowserAction) {
186  appletBrowserAction->setVisible(unlocked);
187  appletBrowserAction->setEnabled(unlocked);
188  connect(appletBrowserAction, SIGNAL(triggered()), this, SLOT(triggerShowAddWidgets()));
189  }
190 
191  QAction *act = action("next applet");
192  if (act) {
193  connect(act, SIGNAL(triggered()), this, SLOT(focusNextApplet()));
194  }
195 
196  act = action("previous applet");
197  if (act) {
198  connect(act, SIGNAL(triggered()), this, SLOT(focusPreviousApplet()));
199  }
200 
201  if (immutability() != SystemImmutable && corona()) {
202  QAction *lockDesktopAction = corona()->action("lock widgets");
203  //keep a pointer so nobody notices it moved to corona
204  if (lockDesktopAction) {
205  d->actions()->addAction("lock widgets", lockDesktopAction);
206  }
207  }
208  if (d->type != PanelContainment && d->type != CustomPanelContainment) {
209  if (corona()) {
210  //FIXME this is just here because of the darn keyboard shortcut :/
211  act = corona()->action("manage activities");
212  if (act) {
213  d->actions()->addAction("manage activities", act);
214  }
215  //a stupid hack to make this one's keyboard shortcut work
216  act = corona()->action("configure shortcuts");
217  if (act) {
218  d->actions()->addAction("configure shortcuts", act);
219  }
220  }
221 
222  if (d->type == DesktopContainment) {
223  addToolBoxAction(action("add widgets"));
224 
225  //TODO: do we need some way to allow this be overridden?
226  // it's always available because shells rely on this
227  // to offer their own custom configuration as well
228  QAction *configureContainment = action("configure");
229  if (configureContainment) {
230  addToolBoxAction(configureContainment);
231  }
232  }
233  }
234 }
235 
236 void ContainmentPrivate::addDefaultActions(KActionCollection *actions, Containment *c)
237 {
238  actions->setConfigGroup("Shortcuts-Containment");
239 
240  //adjust applet actions
241  KAction *appAction = qobject_cast<KAction*>(actions->action("remove"));
242  appAction->setShortcut(KShortcut("alt+d, alt+r"));
243  if (c && c->d->isPanelContainment()) {
244  appAction->setText(i18n("Remove this Panel"));
245  } else {
246  appAction->setText(i18n("Remove this Activity"));
247  }
248 
249  appAction = qobject_cast<KAction*>(actions->action("configure"));
250  if (appAction) {
251  appAction->setShortcut(KShortcut("alt+d, alt+s"));
252  appAction->setText(i18n("Activity Settings"));
253  }
254 
255  //add our own actions
256  KAction *appletBrowserAction = actions->addAction("add widgets");
257  appletBrowserAction->setAutoRepeat(false);
258  appletBrowserAction->setText(i18n("Add Widgets..."));
259  appletBrowserAction->setIcon(KIcon("list-add"));
260  appletBrowserAction->setShortcut(KShortcut("alt+d, a"));
261  appletBrowserAction->setData(AbstractToolBox::AddTool);
262 
263  KAction *action = actions->addAction("next applet");
264  action->setText(i18n("Next Widget"));
265  //no icon
266  action->setShortcut(KShortcut("alt+d, n"));
267  action->setData(AbstractToolBox::ControlTool);
268 
269  action = actions->addAction("previous applet");
270  action->setText(i18n("Previous Widget"));
271  //no icon
272  action->setShortcut(KShortcut("alt+d, p"));
273  action->setData(AbstractToolBox::ControlTool);
274 }
275 
276 // helper function for sorting the list of applets
277 bool appletConfigLessThan(const KConfigGroup &c1, const KConfigGroup &c2)
278 {
279  QPointF p1 = c1.readEntry("geometry", QRectF()).topLeft();
280  QPointF p2 = c2.readEntry("geometry", QRectF()).topLeft();
281 
282  if (!qFuzzyCompare(p1.x(), p2.x())) {
283  if (QApplication::layoutDirection() == Qt::RightToLeft) {
284  return p1.x() > p2.x();
285  }
286 
287  return p1.x() < p2.x();
288  }
289 
290  return qFuzzyCompare(p1.y(), p2.y()) || p1.y() < p2.y();
291 }
292 
293 void Containment::restore(KConfigGroup &group)
294 {
295  /*kDebug() << "!!!!!!!!!!!!initConstraints" << group.name() << d->type;
296  kDebug() << " location:" << group.readEntry("location", (int)d->location);
297  kDebug() << " geom:" << group.readEntry("geometry", geometry());
298  kDebug() << " formfactor:" << group.readEntry("formfactor", (int)d->formFactor);
299  kDebug() << " screen:" << group.readEntry("screen", d->screen);*/
300  if (!isContainment()) {
301  Applet::restore(group);
302  return;
303  }
304 
305  QRectF geo = group.readEntry("geometry", geometry());
306  //override max/min
307  //this ensures panels are set to their saved size even when they have max & min set to prevent
308  //resizing
309  if (geo.size() != geo.size().boundedTo(maximumSize())) {
310  setMaximumSize(maximumSize().expandedTo(geo.size()));
311  }
312 
313  if (geo.size() != geo.size().expandedTo(minimumSize())) {
314  setMinimumSize(minimumSize().boundedTo(geo.size()));
315  }
316 
317 
318  resize(geo.size());
319  //are we an offscreen containment?
320  if (containmentType() != PanelContainment && containmentType() != CustomPanelContainment && geo.right() < 0) {
321  corona()->addOffscreenWidget(this);
322  }
323 
324  setLocation((Plasma::Location)group.readEntry("location", (int)d->location));
325  setFormFactor((Plasma::FormFactor)group.readEntry("formfactor", (int)d->formFactor));
326  //kDebug() << "setScreen from restore";
327  d->lastScreen = group.readEntry("lastScreen", d->lastScreen);
328  d->lastDesktop = group.readEntry("lastDesktop", d->lastDesktop);
329  d->setScreen(group.readEntry("screen", d->screen), group.readEntry("desktop", d->desktop), false);
330  QString activityId = group.readEntry("activityId", QString());
331  if (!activityId.isEmpty()) {
332  d->context()->setCurrentActivityId(activityId);
333  }
334  setActivity(group.readEntry("activity", QString()));
335 
336  flushPendingConstraintsEvents();
337  restoreContents(group);
338  setImmutability((ImmutabilityType)group.readEntry("immutability", (int)Mutable));
339 
340  setWallpaper(group.readEntry("wallpaperplugin", defaultWallpaper),
341  group.readEntry("wallpaperpluginmode", defaultWallpaperMode));
342 
343  QMetaObject::invokeMethod(d->toolBox.data(), "restore", Q_ARG(KConfigGroup, group));
344 
345  KConfigGroup cfg;
346  if (containmentType() == PanelContainment || containmentType() == CustomPanelContainment) {
347  //don't let global desktop actions conflict with panels
348  //this also prevents panels from sharing config with each other
349  //but the panels aren't configurable anyways, and I doubt that'll change.
350  d->containmentActionsSource = ContainmentPrivate::Local;
351  cfg = KConfigGroup(&group, "ActionPlugins");
352  } else {
353  QString source = group.readEntry("ActionPluginsSource", QString());
354  if (source == "Global") {
355  cfg = KConfigGroup(corona()->config(), "ActionPlugins");
356  d->containmentActionsSource = ContainmentPrivate::Global;
357  } else if (source == "Activity") {
358  cfg = KConfigGroup(corona()->config(), "Activities");
359  cfg = KConfigGroup(&cfg, activityId);
360  cfg = KConfigGroup(&cfg, "ActionPlugins");
361  d->containmentActionsSource = ContainmentPrivate::Activity;
362  } else if (source == "Local") {
363  cfg = group;
364  d->containmentActionsSource = ContainmentPrivate::Local;
365  } else {
366  //default to global
367  //but, if there is no global config, try copying it from local.
368  cfg = KConfigGroup(corona()->config(), "ActionPlugins");
369  if (!cfg.exists()) {
370  cfg = KConfigGroup(&group, "ActionPlugins");
371  }
372  d->containmentActionsSource = ContainmentPrivate::Global;
373  group.writeEntry("ActionPluginsSource", "Global");
374  }
375  }
376  //kDebug() << cfg.keyList();
377  if (cfg.exists()) {
378  foreach (const QString &key, cfg.keyList()) {
379  //kDebug() << "loading" << key;
380  setContainmentActions(key, cfg.readEntry(key, QString()));
381  }
382  } else { //shell defaults
383  ContainmentActionsPluginsConfig conf = corona()->containmentActionsDefaults(d->type);
384  //steal the data directly, for efficiency
385  QHash<QString,QString> defaults = conf.d->plugins;
386  for (QHash<QString,QString>::const_iterator it = defaults.constBegin(),
387  end = defaults.constEnd(); it != end; ++it) {
388  setContainmentActions(it.key(), it.value());
389  }
390  }
391 
392  /*
393  kDebug() << "Containment" << id() <<
394  "screen" << screen() <<
395  "geometry is" << geometry() <<
396  "wallpaper" << ((d->wallpaper) ? d->wallpaper->pluginName() : QString()) <<
397  "wallpaper mode" << wallpaperMode() <<
398  "config entries" << group.entryMap();
399  */
400 }
401 
402 void Containment::save(KConfigGroup &g) const
403 {
404  if (Applet::d->transient) {
405  return;
406  }
407 
408  KConfigGroup group = g;
409  if (!group.isValid()) {
410  group = config();
411  }
412 
413  // locking is saved in Applet::save
414  Applet::save(group);
415 
416  if (!isContainment()) {
417  return;
418  }
419 
420  group.writeEntry("screen", d->screen);
421  group.writeEntry("lastScreen", d->lastScreen);
422  group.writeEntry("desktop", d->desktop);
423  group.writeEntry("lastDesktop", d->lastDesktop);
424  group.writeEntry("formfactor", (int)d->formFactor);
425  group.writeEntry("location", (int)d->location);
426  group.writeEntry("activity", d->context()->currentActivity());
427  group.writeEntry("activityId", d->context()->currentActivityId());
428 
429 
430  QMetaObject::invokeMethod(d->toolBox.data(), "save", Q_ARG(KConfigGroup, group));
431 
432 
433  if (d->wallpaper) {
434  group.writeEntry("wallpaperplugin", d->wallpaper->pluginName());
435  group.writeEntry("wallpaperpluginmode", d->wallpaper->renderingMode().name());
436 
437  if (d->wallpaper->isInitialized()) {
438  KConfigGroup wallpaperConfig(&group, "Wallpaper");
439  wallpaperConfig = KConfigGroup(&wallpaperConfig, d->wallpaper->pluginName());
440  d->wallpaper->save(wallpaperConfig);
441  }
442  }
443 
444  saveContents(group);
445 }
446 
447 void Containment::saveContents(KConfigGroup &group) const
448 {
449  KConfigGroup applets(&group, "Applets");
450  foreach (const Applet *applet, d->applets) {
451  KConfigGroup appletConfig(&applets, QString::number(applet->id()));
452  applet->save(appletConfig);
453  }
454 }
455 
456 void ContainmentPrivate::initApplets()
457 {
458  foreach (Applet *applet, applets) {
459  applet->restore(*applet->d->mainConfigGroup());
460  applet->init();
461  kDebug() << "!!{} STARTUP TIME" << QTime().msecsTo(QTime::currentTime()) << "Applet" << applet->name();
462  }
463 
464  q->flushPendingConstraintsEvents();
465 
466  foreach (Applet *applet, applets) {
467  applet->flushPendingConstraintsEvents();
468  }
469 
470  kDebug() << "!!{} STARTUP TIME" << QTime().msecsTo(QTime::currentTime()) << "Containment's applets initialized" << q->name();
471 }
472 
473 void Containment::restoreContents(KConfigGroup &group)
474 {
475  KConfigGroup applets(&group, "Applets");
476 
477  // Sort the applet configs in order of geometry to ensure that applets
478  // are added from left to right or top to bottom for a panel containment
479  QList<KConfigGroup> appletConfigs;
480  foreach (const QString &appletGroup, applets.groupList()) {
481  //kDebug() << "reading from applet group" << appletGroup;
482  KConfigGroup appletConfig(&applets, appletGroup);
483  appletConfigs.append(appletConfig);
484  }
485  qStableSort(appletConfigs.begin(), appletConfigs.end(), appletConfigLessThan);
486 
487  QMutableListIterator<KConfigGroup> it(appletConfigs);
488  while (it.hasNext()) {
489  KConfigGroup &appletConfig = it.next();
490  int appId = appletConfig.name().toUInt();
491  QString plugin = appletConfig.readEntry("plugin", QString());
492 
493  if (plugin.isEmpty()) {
494  continue;
495  }
496 
497  d->addApplet(plugin, QVariantList(), appletConfig.readEntry("geometry", QRectF()), appId, true);
498  }
499 }
500 
501 Containment::Type Containment::containmentType() const
502 {
503  return d->type;
504 }
505 
506 void Containment::setContainmentType(Containment::Type type)
507 {
508  if (d->type == type) {
509  return;
510  }
511 
512  delete d->toolBox.data();
513  d->type = type;
514  d->checkContainmentFurniture();
515 }
516 
517 void ContainmentPrivate::checkContainmentFurniture()
518 {
519  if (q->isContainment() &&
520  (type == Containment::DesktopContainment || type == Containment::PanelContainment)) {
521  createToolBox();
522  }
523 }
524 
525 Corona *Containment::corona() const
526 {
527  return qobject_cast<Corona*>(scene());
528 }
529 
530 void Containment::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
531 {
532  event->ignore();
533  if (d->wallpaper && d->wallpaper->isInitialized()) {
534  QGraphicsItem *item = scene()->itemAt(event->scenePos());
535  if (item == this) {
536  d->wallpaper->mouseMoveEvent(event);
537  }
538  }
539 
540  if (!event->isAccepted()) {
541  event->accept();
542  Applet::mouseMoveEvent(event);
543  }
544 }
545 
546 void Containment::mousePressEvent(QGraphicsSceneMouseEvent *event)
547 {
548  //close a toolbox if exists, to emulate qmenu behavior
549  if (d->toolBox) {
550  d->toolBox.data()->setShowing(false);
551  }
552  event->ignore();
553  if (d->appletAt(event->scenePos())) {
554  return; //no unexpected click-throughs
555  }
556 
557  if (d->wallpaper && d->wallpaper->isInitialized() && !event->isAccepted()) {
558  d->wallpaper->mousePressEvent(event);
559  }
560 
561  if (event->isAccepted()) {
562  setFocus(Qt::MouseFocusReason);
563  } else if (event->button() == Qt::RightButton && event->modifiers() == Qt::NoModifier) {
564  // we'll catch this in the context menu even
565  Applet::mousePressEvent(event);
566  } else {
567  QString trigger = ContainmentActions::eventToString(event);
568  if (d->prepareContainmentActions(trigger, event->screenPos())) {
569  d->actionPlugins()->value(trigger)->contextEvent(event);
570  }
571 
572  if (!event->isAccepted()) {
573  Applet::mousePressEvent(event);
574  }
575  }
576 }
577 
578 void Containment::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
579 {
580  event->ignore();
581 
582  if (d->appletAt(event->scenePos())) {
583  return; //no unexpected click-throughs
584  }
585 
586  QString trigger = ContainmentActions::eventToString(event);
587 
588  if (d->wallpaper && d->wallpaper->isInitialized()) {
589  d->wallpaper->mouseReleaseEvent(event);
590  }
591 
592  if (!event->isAccepted() && isContainment()) {
593  if (d->prepareContainmentActions(trigger, event->screenPos())) {
594  d->actionPlugins()->value(trigger)->contextEvent(event);
595  }
596 
597  event->accept();
598  Applet::mouseReleaseEvent(event);
599  }
600 }
601 
602 void Containment::showDropZone(const QPoint pos)
603 {
604  Q_UNUSED(pos)
605  //Base implementation does nothing, don't put code here
606 }
607 
608 void Containment::showContextMenu(const QPointF &containmentPos, const QPoint &screenPos)
609 {
610  //kDebug() << containmentPos << screenPos;
611  QGraphicsSceneContextMenuEvent gvevent;
612  gvevent.setScreenPos(screenPos);
613  gvevent.setScenePos(mapToScene(containmentPos));
614  gvevent.setPos(containmentPos);
615  gvevent.setReason(QGraphicsSceneContextMenuEvent::Mouse);
616  gvevent.setWidget(view());
617  contextMenuEvent(&gvevent);
618 }
619 
620 void Containment::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
621 {
622  if (!isContainment() || !KAuthorized::authorizeKAction("plasma/containment_context_menu")) {
623  Applet::contextMenuEvent(event);
624  return;
625  }
626 
627  KMenu desktopMenu;
628  Applet *applet = d->appletAt(event->scenePos());
629  //kDebug() << "context menu event " << (QObject*)applet;
630 
631  if (applet) {
632  d->addAppletActions(desktopMenu, applet, event);
633  } else {
634  d->addContainmentActions(desktopMenu, event);
635  }
636 
637  //kDebug() << "executing at" << screenPos;
638  QMenu *menu = &desktopMenu;
639  //kDebug() << "showing menu, actions" << desktopMenu.actions().size() << desktopMenu.actions().first()->menu();
640  if (desktopMenu.actions().size() == 1 && desktopMenu.actions().first()->menu()) {
641  // we have a menu with a single top level menu; just show that top level menu instad.
642  menu = desktopMenu.actions().first()->menu();
643  }
644 
645  if (!menu->isEmpty()) {
646  QPoint pos = event->screenPos();
647  if (applet && d->isPanelContainment()) {
648  menu->adjustSize();
649  pos = applet->popupPosition(menu->size());
650  if (event->reason() == QGraphicsSceneContextMenuEvent::Mouse) {
651  // if the menu pops up way away from the mouse press, then move it
652  // to the mouse press
653  if (d->formFactor == Vertical) {
654  if (pos.y() + menu->height() < event->screenPos().y()) {
655  pos.setY(event->screenPos().y());
656  }
657  } else if (d->formFactor == Horizontal) {
658  if (pos.x() + menu->width() < event->screenPos().x()) {
659  pos.setX(event->screenPos().x());
660  }
661  }
662  }
663  }
664 
665  menu->exec(pos);
666  event->accept();
667  } else {
668  Applet::contextMenuEvent(event);
669  }
670 }
671 
672 void ContainmentPrivate::addContainmentActions(KMenu &desktopMenu, QEvent *event)
673 {
674  if (static_cast<Corona*>(q->scene())->immutability() != Mutable &&
675  !KAuthorized::authorizeKAction("plasma/containment_actions")) {
676  //kDebug() << "immutability";
677  return;
678  }
679 
680  const QString trigger = ContainmentActions::eventToString(event);
681  prepareContainmentActions(trigger, QPoint(), &desktopMenu);
682 }
683 
684 void ContainmentPrivate::addAppletActions(KMenu &desktopMenu, Applet *applet, QEvent *event)
685 {
686  foreach (QAction *action, applet->contextualActions()) {
687  if (action) {
688  desktopMenu.addAction(action);
689  }
690  }
691 
692  if (!applet->d->failed) {
693  QAction *configureApplet = applet->d->actions->action("configure");
694  if (configureApplet && configureApplet->isEnabled()) {
695  desktopMenu.addAction(configureApplet);
696  }
697 
698  QAction *runAssociatedApplication = applet->d->actions->action("run associated application");
699  if (runAssociatedApplication && runAssociatedApplication->isEnabled()) {
700  desktopMenu.addAction(runAssociatedApplication);
701  }
702  }
703 
704  KMenu *containmentMenu = new KMenu(i18nc("%1 is the name of the containment", "%1 Options", q->name()), &desktopMenu);
705  addContainmentActions(*containmentMenu, event);
706  if (!containmentMenu->isEmpty()) {
707  int enabled = 0;
708  //count number of real actions
709  QListIterator<QAction *> actionsIt(containmentMenu->actions());
710  while (enabled < 3 && actionsIt.hasNext()) {
711  QAction *action = actionsIt.next();
712  if (action->isVisible() && !action->isSeparator()) {
713  ++enabled;
714  }
715  }
716 
717  if (enabled) {
718  //if there is only one, don't create a submenu
719  if (enabled < 2) {
720  foreach (QAction *action, containmentMenu->actions()) {
721  if (action->isVisible() && !action->isSeparator()) {
722  desktopMenu.addAction(action);
723  }
724  }
725  } else {
726  desktopMenu.addMenu(containmentMenu);
727  }
728  }
729  }
730 
731  if (q->immutability() == Mutable &&
732  !q->property("hideCloseAppletInContextMenu").toBool()) {
733  QAction *closeApplet = applet->d->actions->action("remove");
734  //kDebug() << "checking for removal" << closeApplet;
735  if (closeApplet) {
736  if (!desktopMenu.isEmpty()) {
737  desktopMenu.addSeparator();
738  }
739 
740  //kDebug() << "adding close action" << closeApplet->isEnabled() << closeApplet->isVisible();
741  desktopMenu.addAction(closeApplet);
742  }
743  }
744 }
745 
746 Applet* ContainmentPrivate::appletAt(const QPointF &point)
747 {
748  Applet *applet = 0;
749 
750  QGraphicsItem *item = q->scene()->itemAt(point);
751  if (item == q) {
752  item = 0;
753  }
754 
755  while (item) {
756  if (item->isWidget()) {
757  applet = qobject_cast<Applet*>(static_cast<QGraphicsWidget*>(item));
758  if (applet) {
759  if (applet->isContainment()) {
760  applet = 0;
761  }
762  break;
763  }
764  }
765  AppletHandle *handle = dynamic_cast<AppletHandle*>(item);
766  if (handle) {
767  //pretend it was on the applet
768  applet = handle->applet();
769  break;
770  }
771  item = item->parentItem();
772  }
773  return applet;
774 }
775 
776 void Containment::setFormFactor(FormFactor formFactor)
777 {
778  if (d->formFactor == formFactor) {
779  return;
780  }
781 
782  //kDebug() << "switching FF to " << formFactor;
783  d->formFactor = formFactor;
784 
785  if (isContainment() &&
786  (d->type == PanelContainment || d->type == CustomPanelContainment)) {
787  // we are a panel and we have chaged our orientation
788  d->positionPanel(true);
789  }
790 
791  QMetaObject::invokeMethod(d->toolBox.data(), "reposition");
792 
793  updateConstraints(Plasma::FormFactorConstraint);
794 
795  KConfigGroup c = config();
796  c.writeEntry("formfactor", (int)formFactor);
797  emit configNeedsSaving();
798 }
799 
800 void Containment::setLocation(Location location)
801 {
802  if (d->location == location) {
803  return;
804  }
805 
806  bool emitGeomChange = false;
807 
808  if ((location == TopEdge || location == BottomEdge) &&
809  (d->location == TopEdge || d->location == BottomEdge)) {
810  emitGeomChange = true;
811  }
812 
813  if ((location == RightEdge || location == LeftEdge) &&
814  (d->location == RightEdge || d->location == LeftEdge)) {
815  emitGeomChange = true;
816  }
817 
818  d->location = location;
819 
820  foreach (Applet *applet, d->applets) {
821  applet->updateConstraints(Plasma::LocationConstraint);
822  }
823 
824  if (emitGeomChange) {
825  // our geometry on the scene will not actually change,
826  // but for the purposes of views it has
827  emit geometryChanged();
828  }
829 
830  updateConstraints(Plasma::LocationConstraint);
831 
832  KConfigGroup c = config();
833  c.writeEntry("location", (int)location);
834  emit configNeedsSaving();
835 }
836 
837 void Containment::addSiblingContainment()
838 {
839  emit addSiblingContainment(this);
840 }
841 
842 void Containment::clearApplets()
843 {
844  foreach (Applet *applet, d->applets) {
845  applet->d->cleanUpAndDelete();
846  }
847 
848  d->applets.clear();
849 }
850 
851 Applet *Containment::addApplet(const QString &name, const QVariantList &args,
852  const QRectF &appletGeometry)
853 {
854  return d->addApplet(name, args, appletGeometry);
855 }
856 
857 void Containment::addApplet(Applet *applet, const QPointF &pos, bool delayInit)
858 {
859  if (!isContainment() || (!delayInit && immutability() != Mutable)) {
860  return;
861  }
862 
863  if (!applet) {
864  kDebug() << "adding null applet!?!";
865  return;
866  }
867 
868  if (d->applets.contains(applet)) {
869  kDebug() << "already have this applet!";
870  }
871 
872  Containment *currentContainment = applet->containment();
873 
874  if (d->type == PanelContainment) {
875  //panels don't want backgrounds, which is important when setting geometry
876  setBackgroundHints(NoBackground);
877  }
878 
879  if (currentContainment && currentContainment != this) {
880  emit currentContainment->appletRemoved(applet);
881  if (currentContainment->d->focusedApplet == applet) {
882  currentContainment->d->focusedApplet = 0;
883  }
884 
885  disconnect(applet, 0, currentContainment, 0);
886  KConfigGroup oldConfig = applet->config();
887  currentContainment->d->applets.removeAll(applet);
888  applet->setParentItem(this);
889  applet->setParent(this);
890 
891  // now move the old config to the new location
892  //FIXME: this doesn't seem to get the actual main config group containing plugin=, etc
893  KConfigGroup c = config().group("Applets").group(QString::number(applet->id()));
894  oldConfig.reparent(&c);
895  applet->d->resetConfigurationObject();
896 
897  disconnect(applet, SIGNAL(activate()), currentContainment, SIGNAL(activate()));
898  } else {
899  applet->setParentItem(this);
900  applet->setParent(this);
901  }
902 
903  d->applets << applet;
904 
905  connect(applet, SIGNAL(configNeedsSaving()), this, SIGNAL(configNeedsSaving()));
906  connect(applet, SIGNAL(releaseVisualFocus()), this, SIGNAL(releaseVisualFocus()));
907  connect(applet, SIGNAL(appletDestroyed(Plasma::Applet*)), this, SLOT(appletDestroyed(Plasma::Applet*)));
908  connect(applet, SIGNAL(newStatus(Plasma::ItemStatus)), this, SLOT(checkStatus(Plasma::ItemStatus)));
909  connect(applet, SIGNAL(activate()), this, SIGNAL(activate()));
910 
911  if (pos != QPointF(-1, -1)) {
912  applet->setPos(pos);
913  }
914 
915  if (!delayInit && !currentContainment) {
916  applet->restore(*applet->d->mainConfigGroup());
917  applet->init();
918  Plasma::Animation *anim = Plasma::Animator::create(Plasma::Animator::AppearAnimation);
919  if (anim) {
920  connect(anim, SIGNAL(finished()), this, SLOT(appletAppearAnimationComplete()));
921  anim->setTargetWidget(applet);
922  //FIXME: small hack until we have proper js anim support; allows 'zoom' to work in the
923  //'right' direction for appearance
924  anim->setDirection(QAbstractAnimation::Backward);
925  anim->start(QAbstractAnimation::DeleteWhenStopped);
926  } else {
927  d->appletAppeared(applet);
928  }
929  }
930 
931  applet->setFlag(QGraphicsItem::ItemIsMovable, true);
932  applet->updateConstraints(Plasma::AllConstraints);
933  if (!delayInit) {
934  applet->flushPendingConstraintsEvents();
935  }
936  emit appletAdded(applet, pos);
937 
938  if (!currentContainment) {
939  applet->updateConstraints(Plasma::StartupCompletedConstraint);
940  if (!delayInit) {
941  applet->flushPendingConstraintsEvents();
942  }
943  }
944 
945  if (!delayInit) {
946  applet->d->scheduleModificationNotification();
947  }
948 }
949 
950 Applet::List Containment::applets() const
951 {
952  return d->applets;
953 }
954 
955 void Containment::setScreen(int newScreen, int newDesktop)
956 {
957  d->setScreen(newScreen, newDesktop);
958 }
959 
960 void ContainmentPrivate::setScreen(int newScreen, int newDesktop, bool preventInvalidDesktops)
961 {
962  // What we want to do in here is:
963  // * claim the screen as our own
964  // * signal whatever may be watching this containment about the switch
965  // * if we are a full screen containment, then:
966  // * resize to match the screen if we're that kind of containment
967  // * kick other full-screen containments off this screen
968  // * if we had a screen, then give our screen to the containment
969  // we kick out
970  //
971  // a screen of -1 means no associated screen.
972  Corona *corona = q->corona();
973  Q_ASSERT(corona);
974 
975  //if it's an offscreen widget, don't allow to claim a screen, after all it's *off*screen
976  if (corona->offscreenWidgets().contains(q)) {
977  return;
978  }
979 
980  int numScreens = corona->numScreens();
981  if (newScreen < -1) {
982  newScreen = -1;
983  }
984 
985  // -1 == All desktops
986  if (newDesktop < -1 || (preventInvalidDesktops && newDesktop > KWindowSystem::numberOfDesktops() - 1)) {
987  newDesktop = -1;
988  }
989 
990  //kDebug() << activity() << "setting screen to " << newScreen << newDesktop << "and type is" << type;
991 
992  Containment *swapScreensWith(0);
993  const bool isDesktopContainment = type == Containment::DesktopContainment ||
994  type == Containment::CustomContainment;
995  if (isDesktopContainment) {
996  // we want to listen to changes in work area if our screen changes
997  if (toolBox) {
998  if (screen < 0 && newScreen > -1) {
999  QObject::connect(KWindowSystem::self(), SIGNAL(workAreaChanged()), toolBox.data(), SLOT(reposition()), Qt::UniqueConnection);
1000  } else if (newScreen < 0) {
1001  QObject::disconnect(KWindowSystem::self(), SIGNAL(workAreaChanged()), toolBox.data(), SLOT(reposition()));
1002  }
1003  }
1004 
1005  if (newScreen > -1) {
1006  // sanity check to make sure someone else doesn't have this screen already!
1007  Containment *currently = corona->containmentForScreen(newScreen, newDesktop);
1008  if (currently && currently != q) {
1009  kDebug() << "currently is on screen" << currently->screen()
1010  << "desktop" << currently->desktop()
1011  << "and is" << currently->activity()
1012  << (QObject*)currently << "i'm" << (QObject*)q;
1013  currently->setScreen(-1, currently->desktop());
1014  swapScreensWith = currently;
1015  }
1016  }
1017  }
1018 
1019  if (newScreen < numScreens && newScreen > -1 && isDesktopContainment) {
1020  q->resize(corona->screenGeometry(newScreen).size());
1021  }
1022 
1023  int oldDesktop = desktop;
1024  desktop = newDesktop;
1025 
1026  int oldScreen = screen;
1027  screen = newScreen;
1028 
1029  q->updateConstraints(Plasma::ScreenConstraint);
1030 
1031  if (oldScreen != newScreen || oldDesktop != newDesktop) {
1032  /*
1033  kDebug() << "going to signal change for" << q
1034  << ", old screen & desktop:" << oldScreen << oldDesktop
1035  << ", new:" << screen << desktop;
1036  */
1037  KConfigGroup c = q->config();
1038  c.writeEntry("screen", screen);
1039  c.writeEntry("desktop", desktop);
1040  if (newScreen != -1) {
1041  lastScreen = newScreen;
1042  lastDesktop = newDesktop;
1043  c.writeEntry("lastScreen", lastScreen);
1044  c.writeEntry("lastDesktop", lastDesktop);
1045  }
1046  emit q->configNeedsSaving();
1047  emit q->screenChanged(oldScreen, newScreen, q);
1048  }
1049 
1050  if (swapScreensWith) {
1051  //kDebug() << "setScreen due to swap, part 2";
1052  swapScreensWith->setScreen(oldScreen, oldDesktop);
1053  }
1054 
1055  checkRemoveAction();
1056 
1057  if (newScreen >= 0) {
1058  emit q->activate();
1059  }
1060 }
1061 
1062 int Containment::screen() const
1063 {
1064  return d->screen;
1065 }
1066 
1067 int Containment::lastScreen() const
1068 {
1069  return d->lastScreen;
1070 }
1071 
1072 int Containment::desktop() const
1073 {
1074  return d->desktop;
1075 }
1076 
1077 int Containment::lastDesktop() const
1078 {
1079  return d->lastDesktop;
1080 }
1081 
1082 KPluginInfo::List Containment::listContainments(const QString &category,
1083  const QString &parentApp)
1084 {
1085  return listContainmentsOfType(QString(), category, parentApp);
1086 }
1087 
1088 
1089 KPluginInfo::List Containment::listContainmentsOfType(const QString &type,
1090  const QString &category,
1091  const QString &parentApp)
1092 {
1093  QString constraint;
1094 
1095  if (parentApp.isEmpty()) {
1096  constraint.append("(not exist [X-KDE-ParentApp] or [X-KDE-ParentApp] == '')");
1097  } else {
1098  constraint.append("[X-KDE-ParentApp] == '").append(parentApp).append("'");
1099  }
1100 
1101  if (!type.isEmpty()) {
1102  if (!constraint.isEmpty()) {
1103  constraint.append(" and ");
1104  }
1105 
1106  constraint.append("'").append(type).append("' ~in [X-Plasma-ContainmentCategories]");
1107  }
1108 
1109  if (!category.isEmpty()) {
1110  if (!constraint.isEmpty()) {
1111  constraint.append(" and ");
1112  }
1113 
1114  constraint.append("[X-KDE-PluginInfo-Category] == '").append(category).append("'");
1115  if (category == "Miscellaneous") {
1116  constraint.append(" or (not exist [X-KDE-PluginInfo-Category] or [X-KDE-PluginInfo-Category] == '')");
1117  }
1118  }
1119 
1120  KService::List offers = KServiceTypeTrader::self()->query("Plasma/Containment", constraint);
1121  //kDebug() << "constraint was" << constraint << "which got us" << offers.count() << "matches";
1122  return KPluginInfo::fromServices(offers);
1123 }
1124 
1125 KPluginInfo::List Containment::listContainmentsForMimetype(const QString &mimetype)
1126 {
1127  const QString constraint = QString("'%1' in [X-Plasma-DropMimeTypes]").arg(mimetype);
1128  //kDebug() << mimetype << constraint;
1129  const KService::List offers = KServiceTypeTrader::self()->query("Plasma/Containment", constraint);
1130  return KPluginInfo::fromServices(offers);
1131 }
1132 
1133 QStringList Containment::listContainmentTypes()
1134 {
1135  KPluginInfo::List containmentInfos = listContainments();
1136  QSet<QString> types;
1137 
1138  foreach (const KPluginInfo &containmentInfo, containmentInfos) {
1139  QStringList theseTypes = containmentInfo.service()->property("X-Plasma-ContainmentCategories").toStringList();
1140  foreach (const QString &type, theseTypes) {
1141  types.insert(type);
1142  }
1143  }
1144 
1145  return types.toList();
1146 }
1147 
1148 void Containment::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
1149 {
1150  event->setAccepted(immutability() == Mutable &&
1151  (event->mimeData()->hasFormat(static_cast<Corona*>(scene())->appletMimeType()) ||
1152  KUrl::List::canDecode(event->mimeData()) ||
1153  event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())));
1154  //kDebug() << immutability() << Mutable << (immutability() == Mutable) << event->isAccepted();
1155 
1156  if (!event->isAccepted()) {
1157  // check to see if we have an applet that accepts the format.
1158  QStringList formats = event->mimeData()->formats();
1159 
1160  foreach (const QString &format, formats) {
1161  KPluginInfo::List appletList = Applet::listAppletInfoForMimetype(format);
1162  if (!appletList.isEmpty()) {
1163  event->setAccepted(true);
1164  break;
1165  }
1166  }
1167 
1168  if (!event->isAccepted()) {
1169  foreach (const QString &format, formats) {
1170  KPluginInfo::List wallpaperList = Wallpaper::listWallpaperInfoForMimetype(format);
1171  if (!wallpaperList.isEmpty()) {
1172  event->setAccepted(true);
1173  break;
1174  }
1175  }
1176  }
1177  }
1178 
1179  if (event->isAccepted()) {
1180  if (d->dropZoneStarted) {
1181  showDropZone(event->pos().toPoint());
1182  } else {
1183  if (!d->showDropZoneDelayTimer) {
1184  d->showDropZoneDelayTimer = new QTimer(this);
1185  d->showDropZoneDelayTimer->setSingleShot(true);
1186  connect(d->showDropZoneDelayTimer, SIGNAL(timeout()), this, SLOT(showDropZoneDelayed()));
1187  }
1188 
1189  d->dropPoints.insert(0, event->pos());
1190  d->showDropZoneDelayTimer->start(300);
1191  }
1192  }
1193 }
1194 
1195 void Containment::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
1196 {
1197  if (event->pos().y() < 1 || event->pos().y() > size().height() ||
1198  event->pos().x() < 1 || event->pos().x() > size().width()) {
1199  if (d->showDropZoneDelayTimer) {
1200  d->showDropZoneDelayTimer->stop();
1201  }
1202 
1203  showDropZone(QPoint());
1204  d->dropZoneStarted = false;
1205  }
1206 }
1207 
1208 void ContainmentPrivate::showDropZoneDelayed()
1209 {
1210  dropZoneStarted = true;
1211  q->showDropZone(dropPoints.value(0).toPoint());
1212  dropPoints.remove(0);
1213 }
1214 
1215 void Containment::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
1216 {
1217  QGraphicsItem *item = scene()->itemAt(event->scenePos());
1218  //event->setAccepted(item == this || item == d->toolBox.data() || !item);
1219  //kDebug() << event->isAccepted() << d->showDropZoneDelayTimer->isActive();
1220  if (!event->isAccepted()) {
1221  if (d->showDropZoneDelayTimer) {
1222  d->showDropZoneDelayTimer->stop();
1223  }
1224  } else if ((!d->showDropZoneDelayTimer || !d->showDropZoneDelayTimer->isActive()) && immutability() == Plasma::Mutable) {
1225  showDropZone(event->pos().toPoint());
1226  }
1227 }
1228 
1229 void Containment::dropEvent(QGraphicsSceneDragDropEvent *event)
1230 {
1231  showDropZone(QPoint());
1232  d->dropZoneStarted = false;
1233  if (isContainment()) {
1234  d->dropData(event->scenePos(), event->screenPos(), event);
1235  } else {
1236  Applet::dropEvent(event);
1237  }
1238 }
1239 
1240 void ContainmentPrivate::dropData(QPointF scenePos, QPoint screenPos, QGraphicsSceneDragDropEvent *dropEvent)
1241 {
1242  if (q->immutability() != Mutable) {
1243  return;
1244  }
1245 
1246  QPointF pos = q->mapFromScene(scenePos);
1247  const QMimeData *mimeData = 0;
1248 
1249  if (dropEvent) {
1250  mimeData = dropEvent->mimeData();
1251  } else {
1252  QClipboard *clipboard = QApplication::clipboard();
1253  mimeData = clipboard->mimeData(QClipboard::Selection);
1254  //TODO if that's not supported (ie non-linux) should we try clipboard instead of selection?
1255  }
1256 
1257  if (!mimeData) {
1258  //Selection is either empty or not supported on this OS
1259  kDebug() << "no mime data";
1260  return;
1261  }
1262 
1263  //kDebug() << event->mimeData()->text();
1264 
1265  QString appletMimetype(q->corona() ? q->corona()->appletMimeType() : QString());
1266 
1267  if (!appletMimetype.isEmpty() && mimeData->hasFormat(appletMimetype)) {
1268  QString data = mimeData->data(appletMimetype);
1269  const QStringList appletNames = data.split('\n', QString::SkipEmptyParts);
1270  foreach (const QString &appletName, appletNames) {
1271  //kDebug() << "doing" << appletName;
1272  QRectF geom(pos, QSize(0, 0));
1273  q->addApplet(appletName, QVariantList(), geom);
1274  }
1275  if (dropEvent) {
1276  dropEvent->acceptProposedAction();
1277  }
1278  } else if (mimeData->hasFormat(ExtenderItemMimeData::mimeType())) {
1279  kDebug() << "mimetype plasma/extenderitem is dropped, creating internal:extender";
1280  //Handle dropping extenderitems.
1281  const ExtenderItemMimeData *extenderData = qobject_cast<const ExtenderItemMimeData*>(mimeData);
1282  if (extenderData) {
1283  ExtenderItem *item = extenderData->extenderItem();
1284  QRectF geometry(pos - extenderData->pointerOffset(), item->size());
1285  kDebug() << "desired geometry: " << geometry;
1286  Applet *applet = qobject_cast<ExtenderApplet *>(item->extender() ? item->extender()->applet() : 0);
1287  if (applet) {
1288  qreal left, top, right, bottom;
1289  applet->getContentsMargins(&left, &top, &right, &bottom);
1290  applet->setPos(geometry.topLeft() - QPointF(int(left), int(top)));
1291  applet->show();
1292  } else {
1293  applet = addApplet("internal:extender", QVariantList(), geometry, 0, true);
1294  applet->hide();
1295  applet->init();
1296  appletAppeared(applet);
1297  applet->flushPendingConstraintsEvents();
1298  applet->d->scheduleModificationNotification();
1299  applet->adjustSize();
1300  applet->show();
1301  }
1302  item->setExtender(applet->extender());
1303  }
1304  } else if (KUrl::List::canDecode(mimeData)) {
1305  //TODO: collect the mimetypes of available script engines and offer
1306  // to create widgets out of the matching URLs, if any
1307  const KUrl::List urls = KUrl::List::fromMimeData(mimeData);
1308  foreach (const KUrl &url, urls) {
1309  if (AccessManager::supportedProtocols().contains(url.protocol())) {
1310  AccessAppletJob *job = AccessManager::self()->accessRemoteApplet(url);
1311  if (dropEvent) {
1312  dropPoints[job] = dropEvent->pos();
1313  } else {
1314  dropPoints[job] = scenePos;
1315  }
1316  QObject::connect(AccessManager::self(), SIGNAL(finished(Plasma::AccessAppletJob*)),
1317  q, SLOT(remoteAppletReady(Plasma::AccessAppletJob*)));
1318  }
1319 #ifndef PLASMA_NO_KIO
1320  else {
1321  KMimeType::Ptr mime = KMimeType::findByUrl(url);
1322  QString mimeName = mime->name();
1323  QRectF geom(pos, QSize());
1324  QVariantList args;
1325  args << url.url();
1326  kDebug() << "can decode" << mimeName << args;
1327 
1328  // It may be a directory or a file, let's stat
1329  KIO::JobFlags flags = KIO::HideProgressInfo;
1330  KIO::MimetypeJob *job = KIO::mimetype(url, flags);
1331  if (dropEvent) {
1332  dropPoints[job] = dropEvent->pos();
1333  } else {
1334  dropPoints[job] = scenePos;
1335  }
1336 
1337  QObject::connect(job, SIGNAL(result(KJob*)), q, SLOT(dropJobResult(KJob*)));
1338  QObject::connect(job, SIGNAL(mimetype(KIO::Job*,QString)),
1339  q, SLOT(mimeTypeRetrieved(KIO::Job*,QString)));
1340 
1341  KMenu *choices = new KMenu("Content dropped");
1342  choices->addAction(KIcon("process-working"), i18n("Fetching file type..."));
1343  if (dropEvent) {
1344  choices->popup(dropEvent->screenPos());
1345  } else {
1346  choices->popup(screenPos);
1347  }
1348 
1349  dropMenus[job] = choices;
1350  }
1351 #endif
1352  }
1353 
1354  if (dropEvent) {
1355  dropEvent->acceptProposedAction();
1356  }
1357  } else {
1358  QStringList formats = mimeData->formats();
1359  QHash<QString, KPluginInfo> seenPlugins;
1360  QHash<QString, QString> pluginFormats;
1361 
1362  foreach (const QString &format, formats) {
1363  KPluginInfo::List plugins = Applet::listAppletInfoForMimetype(format);
1364 
1365  foreach (const KPluginInfo &plugin, plugins) {
1366  if (seenPlugins.contains(plugin.pluginName())) {
1367  continue;
1368  }
1369 
1370  seenPlugins.insert(plugin.pluginName(), plugin);
1371  pluginFormats.insert(plugin.pluginName(), format);
1372  }
1373  }
1374  //kDebug() << "Mimetype ..." << formats << seenPlugins.keys() << pluginFormats.values();
1375 
1376  QString selectedPlugin;
1377 
1378  if (seenPlugins.isEmpty()) {
1379  // do nothing
1380  } else if (seenPlugins.count() == 1) {
1381  selectedPlugin = seenPlugins.constBegin().key();
1382  } else {
1383  KMenu choices;
1384  QHash<QAction *, QString> actionsToPlugins;
1385  foreach (const KPluginInfo &info, seenPlugins) {
1386  QAction *action;
1387  if (!info.icon().isEmpty()) {
1388  action = choices.addAction(KIcon(info.icon()), info.name());
1389  } else {
1390  action = choices.addAction(info.name());
1391  }
1392 
1393  actionsToPlugins.insert(action, info.pluginName());
1394  }
1395 
1396  QAction *choice = choices.exec(screenPos);
1397  if (choice) {
1398  selectedPlugin = actionsToPlugins[choice];
1399  }
1400  }
1401 
1402  if (!selectedPlugin.isEmpty()) {
1403  if (!dropEvent) {
1404  // since we may have entered an event loop up above with the menu,
1405  // the clipboard item may no longer be valid, as QClipboard resets
1406  // the object behind the back of the application with a zero timer
1407  // so we fetch it again here
1408  QClipboard *clipboard = QApplication::clipboard();
1409  mimeData = clipboard->mimeData(QClipboard::Selection);
1410  }
1411 
1412  KTemporaryFile tempFile;
1413  if (mimeData && tempFile.open()) {
1414  //TODO: what should we do with files after the applet is done with them??
1415  tempFile.setAutoRemove(false);
1416 
1417  {
1418  QDataStream stream(&tempFile);
1419  QByteArray data = mimeData->data(pluginFormats[selectedPlugin]);
1420  stream.writeRawData(data, data.size());
1421  }
1422 
1423  QRectF geom(pos, QSize());
1424  QVariantList args;
1425  args << tempFile.fileName();
1426  kDebug() << args;
1427  tempFile.close();
1428 
1429  q->addApplet(selectedPlugin, args, geom);
1430  }
1431  }
1432  }
1433 }
1434 
1435 void ContainmentPrivate::clearDataForMimeJob(KIO::Job *job)
1436 {
1437 #ifndef PLASMA_NO_KIO
1438  QObject::disconnect(job, 0, q, 0);
1439  dropPoints.remove(job);
1440  KMenu *choices = dropMenus.take(job);
1441  delete choices;
1442  job->kill();
1443 #endif // PLASMA_NO_KIO
1444 }
1445 
1446 void ContainmentPrivate::remoteAppletReady(Plasma::AccessAppletJob *job)
1447 {
1448  QPointF pos = dropPoints.take(job);
1449  if (job->error()) {
1450  //TODO: nice user visible error handling (knotification probably?)
1451  kDebug() << "remote applet access failed: " << job->errorText();
1452  return;
1453  }
1454 
1455  if (!job->applet()) {
1456  kDebug() << "how did we end up here? if applet is null, the job->error should be nonzero";
1457  return;
1458  }
1459 
1460  q->addApplet(job->applet(), pos);
1461 }
1462 
1463 void ContainmentPrivate::dropJobResult(KJob *job)
1464 {
1465 #ifndef PLASMA_NO_KIO
1466  KIO::TransferJob* tjob = dynamic_cast<KIO::TransferJob*>(job);
1467  if (!tjob) {
1468  kDebug() << "job is not a KIO::TransferJob, won't handle the drop...";
1469  clearDataForMimeJob(tjob);
1470  return;
1471  }
1472  if (job->error()) {
1473  kDebug() << "ERROR" << tjob->error() << ' ' << tjob->errorString();
1474  }
1475  // We call mimetypeRetrieved since there might be other mechanisms
1476  // for finding suitable applets. Cleanup happens there as well.
1477  mimeTypeRetrieved(qobject_cast<KIO::Job *>(job), QString());
1478 #endif // PLASMA_NO_KIO
1479 }
1480 
1481 void ContainmentPrivate::mimeTypeRetrieved(KIO::Job *job, const QString &mimetype)
1482 {
1483 #ifndef PLASMA_NO_KIO
1484  kDebug() << "Mimetype Job returns." << mimetype;
1485  KIO::TransferJob* tjob = dynamic_cast<KIO::TransferJob*>(job);
1486  if (!tjob) {
1487  kDebug() << "job should be a TransferJob, but isn't";
1488  clearDataForMimeJob(job);
1489  return;
1490  }
1491  KPluginInfo::List appletList = Applet::listAppletInfoForUrl(tjob->url());
1492  if (mimetype.isEmpty() && !appletList.count()) {
1493  clearDataForMimeJob(job);
1494  kDebug() << "No applets found matching the url (" << tjob->url() << ") or the mimetype (" << mimetype << ")";
1495  return;
1496  } else {
1497 
1498  QPointF posi; // will be overwritten with the event's position
1499  if (dropPoints.keys().contains(tjob)) {
1500  posi = dropPoints[tjob];
1501  kDebug() << "Received a suitable dropEvent at" << posi;
1502  } else {
1503  kDebug() << "Bailing out. Cannot find associated dropEvent related to the TransferJob";
1504  clearDataForMimeJob(job);
1505  return;
1506  }
1507 
1508  KMenu *choices = dropMenus.value(tjob);
1509  if (!choices) {
1510  kDebug() << "Bailing out. No QMenu found for this job.";
1511  clearDataForMimeJob(job);
1512  return;
1513  }
1514 
1515  QVariantList args;
1516  args << tjob->url().url() << mimetype;
1517 
1518  kDebug() << "Creating menu for:" << mimetype << posi << args;
1519 
1520  appletList << Applet::listAppletInfoForMimetype(mimetype);
1521  KPluginInfo::List wallpaperList;
1522  if (drawWallpaper) {
1523  if (wallpaper && wallpaper->supportsMimetype(mimetype)) {
1524  wallpaperList << wallpaper->d->wallpaperDescription;
1525  } else {
1526  wallpaperList = Wallpaper::listWallpaperInfoForMimetype(mimetype);
1527  }
1528  }
1529 
1530  if (!appletList.isEmpty() || !wallpaperList.isEmpty()) {
1531  choices->clear();
1532  QHash<QAction *, QString> actionsToApplets;
1533  choices->addTitle(i18n("Widgets"));
1534  foreach (const KPluginInfo &info, appletList) {
1535  kDebug() << info.name();
1536  QAction *action;
1537  if (!info.icon().isEmpty()) {
1538  action = choices->addAction(KIcon(info.icon()), info.name());
1539  } else {
1540  action = choices->addAction(info.name());
1541  }
1542 
1543  actionsToApplets.insert(action, info.pluginName());
1544  kDebug() << info.pluginName();
1545  }
1546  actionsToApplets.insert(choices->addAction(i18n("Icon")), "icon");
1547 
1548  QHash<QAction *, QString> actionsToWallpapers;
1549  if (!wallpaperList.isEmpty()) {
1550  choices->addTitle(i18n("Wallpaper"));
1551 
1552  QMap<QString, KPluginInfo> sorted;
1553  foreach (const KPluginInfo &info, appletList) {
1554  sorted.insert(info.name(), info);
1555  }
1556 
1557  foreach (const KPluginInfo &info, wallpaperList) {
1558  QAction *action;
1559  if (!info.icon().isEmpty()) {
1560  action = choices->addAction(KIcon(info.icon()), info.name());
1561  } else {
1562  action = choices->addAction(info.name());
1563  }
1564 
1565  actionsToWallpapers.insert(action, info.pluginName());
1566  }
1567  }
1568 
1569  QAction *choice = choices->exec();
1570  if (choice) {
1571  // Put the job on hold so it can be recycled to fetch the actual content,
1572  // which is to be expected when something's dropped onto the desktop and
1573  // an applet is to be created with this URL
1574  if (!mimetype.isEmpty() && !tjob->error()) {
1575  tjob->putOnHold();
1576  KIO::Scheduler::publishSlaveOnHold();
1577  }
1578  QString plugin = actionsToApplets.value(choice);
1579  if (plugin.isEmpty()) {
1580  //set wallpapery stuff
1581  plugin = actionsToWallpapers.value(choice);
1582  if (!wallpaper || plugin != wallpaper->pluginName()) {
1583  kDebug() << "Wallpaper dropped:" << tjob->url();
1584  q->setWallpaper(plugin);
1585  }
1586 
1587  if (wallpaper) {
1588  kDebug() << "Wallpaper dropped:" << tjob->url();
1589  wallpaper->setUrls(KUrl::List() << tjob->url());
1590  }
1591  } else {
1592  addApplet(actionsToApplets[choice], args, QRectF(posi, QSize()));
1593  }
1594 
1595  clearDataForMimeJob(job);
1596  return;
1597  }
1598  } else {
1599  // we can at least create an icon as a link to the URL
1600  addApplet("icon", args, QRectF(posi, QSize()));
1601  }
1602  }
1603 
1604  clearDataForMimeJob(job);
1605 #endif // PLASMA_NO_KIO
1606 }
1607 
1608 #ifndef KDE_NO_DEPRECATED
1609 const QGraphicsItem *Containment::toolBoxItem() const
1610 {
1611  return d->toolBox.data();
1612 }
1613 #endif
1614 
1615 void Containment::setToolBox(AbstractToolBox *toolBox)
1616 {
1617  if (d->toolBox.data()) {
1618  d->toolBox.data()->deleteLater();
1619  }
1620  d->toolBox = toolBox;
1621 }
1622 
1623 AbstractToolBox *Containment::toolBox() const
1624 {
1625  return d->toolBox.data();
1626 }
1627 
1628 void Containment::resizeEvent(QGraphicsSceneResizeEvent *event)
1629 {
1630  Applet::resizeEvent(event);
1631 
1632  if (isContainment()) {
1633  if (d->isPanelContainment()) {
1634  d->positionPanel();
1635  } else if (corona()) {
1636  QMetaObject::invokeMethod(corona(), "layoutContainments");
1637  }
1638 
1639  if (d->wallpaper) {
1640  d->wallpaper->setBoundingRect(QRectF(QPointF(0, 0), size()));
1641  }
1642  }
1643 }
1644 
1645 void Containment::keyPressEvent(QKeyEvent *event)
1646 {
1647  //kDebug() << "keyPressEvent with" << event->key()
1648  // << "and hoping and wishing for a" << Qt::Key_Tab;
1649  if (event->key() == Qt::Key_Tab) { // && event->modifiers() == 0) {
1650  if (!d->applets.isEmpty()) {
1651  kDebug() << "let's give focus to...." << (QObject*)d->applets.first();
1652  d->applets.first()->setFocus(Qt::TabFocusReason);
1653  }
1654  }
1655 }
1656 
1657 void Containment::wheelEvent(QGraphicsSceneWheelEvent *event)
1658 {
1659  event->ignore();
1660  if (d->appletAt(event->scenePos())) {
1661  return; //no unexpected click-throughs
1662  }
1663 
1664  if (d->wallpaper && d->wallpaper->isInitialized()) {
1665  QGraphicsItem *item = scene()->itemAt(event->scenePos());
1666  if (item == this) {
1667  event->ignore();
1668  d->wallpaper->wheelEvent(event);
1669 
1670  if (event->isAccepted()) {
1671  return;
1672  }
1673  }
1674  }
1675 
1676  QString trigger = ContainmentActions::eventToString(event);
1677 
1678  if (d->prepareContainmentActions(trigger, event->screenPos())) {
1679  d->actionPlugins()->value(trigger)->contextEvent(event);
1680  event->accept();
1681  } else {
1682  event->ignore();
1683  Applet::wheelEvent(event);
1684  }
1685 }
1686 
1687 bool Containment::sceneEventFilter(QGraphicsItem *watched, QEvent *event)
1688 {
1689  return Applet::sceneEventFilter(watched, event);
1690 }
1691 
1692 QVariant Containment::itemChange(GraphicsItemChange change, const QVariant &value)
1693 {
1694  //FIXME if the applet is moved to another containment we need to unfocus it
1695 
1696  if (isContainment() &&
1697  (change == QGraphicsItem::ItemSceneHasChanged ||
1698  change == QGraphicsItem::ItemPositionHasChanged)) {
1699  switch (d->type) {
1700  case PanelContainment:
1701  case CustomPanelContainment:
1702  d->positionPanel();
1703  break;
1704  default:
1705  if (corona()) {
1706  QMetaObject::invokeMethod(corona(), "layoutContainments");
1707  }
1708  break;
1709  }
1710  }
1711 
1712  return Applet::itemChange(change, value);
1713 }
1714 
1715 void Containment::enableAction(const QString &name, bool enable)
1716 {
1717  QAction *action = this->action(name);
1718  if (action) {
1719  action->setEnabled(enable);
1720  action->setVisible(enable);
1721  }
1722 }
1723 
1724 void Containment::addToolBoxAction(QAction *action)
1725 {
1726  d->createToolBox();
1727  if (d->toolBox) {
1728  d->toolBox.data()->addTool(action);
1729  }
1730 }
1731 
1732 void Containment::removeToolBoxAction(QAction *action)
1733 {
1734  if (d->toolBox) {
1735  d->toolBox.data()->removeTool(action);
1736  }
1737 }
1738 
1739 void Containment::setToolBoxOpen(bool open)
1740 {
1741  if (open) {
1742  openToolBox();
1743  } else {
1744  closeToolBox();
1745  }
1746 }
1747 
1748 bool Containment::isToolBoxOpen() const
1749 {
1750  return (d->toolBox && d->toolBox.data()->isShowing());
1751 }
1752 
1753 void Containment::openToolBox()
1754 {
1755  if (d->toolBox && !d->toolBox.data()->isShowing()) {
1756  d->toolBox.data()->setShowing(true);
1757  emit toolBoxVisibilityChanged(true);
1758  }
1759 }
1760 
1761 void Containment::closeToolBox()
1762 {
1763  if (d->toolBox && d->toolBox.data()->isShowing()) {
1764  d->toolBox.data()->setShowing(false);
1765  emit toolBoxVisibilityChanged(false);
1766  }
1767 }
1768 
1769 void Containment::addAssociatedWidget(QWidget *widget)
1770 {
1771  Applet::addAssociatedWidget(widget);
1772  if (d->focusedApplet) {
1773  d->focusedApplet->addAssociatedWidget(widget);
1774  }
1775 
1776  foreach (const Applet *applet, d->applets) {
1777  if (applet->d->activationAction) {
1778  widget->addAction(applet->d->activationAction);
1779  }
1780  }
1781 }
1782 
1783 void Containment::removeAssociatedWidget(QWidget *widget)
1784 {
1785  Applet::removeAssociatedWidget(widget);
1786  if (d->focusedApplet) {
1787  d->focusedApplet->removeAssociatedWidget(widget);
1788  }
1789 
1790  foreach (const Applet *applet, d->applets) {
1791  if (applet->d->activationAction) {
1792  widget->removeAction(applet->d->activationAction);
1793  }
1794  }
1795 }
1796 
1797 void Containment::setDrawWallpaper(bool drawWallpaper)
1798 {
1799  d->drawWallpaper = drawWallpaper;
1800  if (drawWallpaper) {
1801  KConfigGroup cfg = config();
1802  const QString wallpaper = cfg.readEntry("wallpaperplugin", defaultWallpaper);
1803  const QString mode = cfg.readEntry("wallpaperpluginmode", defaultWallpaperMode);
1804  setWallpaper(wallpaper, mode);
1805  } else {
1806  delete d->wallpaper;
1807  d->wallpaper = 0;
1808  }
1809 }
1810 
1811 bool Containment::drawWallpaper()
1812 {
1813  return d->drawWallpaper;
1814 }
1815 
1816 void Containment::setWallpaper(const QString &pluginName, const QString &mode)
1817 {
1818  KConfigGroup cfg = config();
1819  bool newPlugin = true;
1820  bool newMode = true;
1821 
1822  if (d->drawWallpaper) {
1823  if (d->wallpaper) {
1824  // we have a wallpaper, so let's decide whether we need to swap it out
1825  if (d->wallpaper->pluginName() != pluginName) {
1826  delete d->wallpaper;
1827  d->wallpaper = 0;
1828  } else {
1829  // it's the same plugin, so let's save its state now so when
1830  // we call restore later on we're safe
1831  newMode = d->wallpaper->renderingMode().name() != mode;
1832  newPlugin = false;
1833  }
1834  }
1835 
1836  if (!pluginName.isEmpty() && !d->wallpaper) {
1837  d->wallpaper = Plasma::Wallpaper::load(pluginName);
1838  }
1839 
1840  if (d->wallpaper) {
1841  d->wallpaper->setParent(this);
1842  d->wallpaper->setBoundingRect(QRectF(QPointF(0, 0), size()));
1843  d->wallpaper->setRenderingMode(mode);
1844 
1845  if (newPlugin) {
1846  cfg.writeEntry("wallpaperplugin", pluginName);
1847  }
1848 
1849  if (d->wallpaper->isInitialized()) {
1850  KConfigGroup wallpaperConfig = KConfigGroup(&cfg, "Wallpaper");
1851  wallpaperConfig = KConfigGroup(&wallpaperConfig, pluginName);
1852  d->wallpaper->restore(wallpaperConfig);
1853  }
1854 
1855  if (newMode) {
1856  cfg.writeEntry("wallpaperpluginmode", mode);
1857  }
1858  }
1859 
1860  update();
1861  }
1862 
1863  if (!d->wallpaper) {
1864  cfg.deleteEntry("wallpaperplugin");
1865  cfg.deleteEntry("wallpaperpluginmode");
1866  }
1867 
1868  if (newPlugin || newMode) {
1869  if (newPlugin && d->wallpaper) {
1870  connect(d->wallpaper, SIGNAL(configureRequested()), this, SLOT(requestConfiguration()));
1871  connect(d->wallpaper, SIGNAL(configNeedsSaving()), this, SIGNAL(configNeedsSaving()));
1872  }
1873 
1874  emit configNeedsSaving();
1875  }
1876 }
1877 
1878 Plasma::Wallpaper *Containment::wallpaper() const
1879 {
1880  return d->wallpaper;
1881 }
1882 
1883 void Containment::setContainmentActions(const QString &trigger, const QString &pluginName)
1884 {
1885  KConfigGroup cfg = containmentActionsConfig();
1886  ContainmentActions *plugin = 0;
1887 
1888  if (d->actionPlugins()->contains(trigger)) {
1889  plugin = d->actionPlugins()->value(trigger);
1890  if (plugin->pluginName() != pluginName) {
1891  d->actionPlugins()->remove(trigger);
1892  delete plugin;
1893  plugin=0;
1894  }
1895  }
1896  if (pluginName.isEmpty()) {
1897  cfg.deleteEntry(trigger);
1898  } else if (plugin) {
1899  //it already existed, just reload config
1900  if (plugin->isInitialized()) {
1901  plugin->setContainment(this); //to be safe
1902  //FIXME make a truly unique config group
1903  KConfigGroup pluginConfig = KConfigGroup(&cfg, trigger);
1904  plugin->restore(pluginConfig);
1905  }
1906  } else {
1907  switch (d->containmentActionsSource) {
1908  case ContainmentPrivate::Activity:
1909  //FIXME
1910  case ContainmentPrivate::Local:
1911  plugin = ContainmentActions::load(this, pluginName);
1912  break;
1913  default:
1914  plugin = ContainmentActions::load(0, pluginName);
1915  }
1916  if (plugin) {
1917  cfg.writeEntry(trigger, pluginName);
1918  d->actionPlugins()->insert(trigger, plugin);
1919  } else {
1920  //bad plugin... gets removed. is this a feature or a bug?
1921  cfg.deleteEntry(trigger);
1922  }
1923  }
1924 
1925  emit configNeedsSaving();
1926 }
1927 
1928 QStringList Containment::containmentActionsTriggers()
1929 {
1930  return d->actionPlugins()->keys();
1931 }
1932 
1933 QString Containment::containmentActions(const QString &trigger)
1934 {
1935  ContainmentActions *c = d->actionPlugins()->value(trigger);
1936  return c ? c->pluginName() : QString();
1937 }
1938 
1939 void Containment::setActivity(const QString &activity)
1940 {
1941  Context *context = d->context();
1942  if (context->currentActivity() != activity) {
1943  context->setCurrentActivity(activity);
1944  }
1945 }
1946 
1947 void ContainmentPrivate::onContextChanged(Plasma::Context *con)
1948 {
1949  foreach (Applet *a, applets) {
1950  a->updateConstraints(ContextConstraint);
1951  }
1952 
1953  KConfigGroup c = q->config();
1954  QString act = con->currentActivityId();
1955 
1956  //save anything that's been set (boy I hope this avoids overwriting things)
1957  //FIXME of course if the user sets the name to an empty string we have a bug
1958  //but once we get context retrieving the name as soon as the id is set, this issue should go away
1959  if (!act.isEmpty()) {
1960  c.writeEntry("activityId", act);
1961  }
1962  act = con->currentActivity();
1963  if (!act.isEmpty()) {
1964  c.writeEntry("activity", act);
1965  }
1966 
1967  if (toolBox) {
1968  toolBox.data()->update();
1969  }
1970  emit q->configNeedsSaving();
1971  emit q->contextChanged(con);
1972 }
1973 
1974 QString Containment::activity() const
1975 {
1976  return d->context()->currentActivity();
1977 }
1978 
1979 Context *Containment::context() const
1980 {
1981  return d->context();
1982 }
1983 
1984 Context *ContainmentPrivate::context()
1985 {
1986  if (!con) {
1987  con = new Context(q);
1988  q->connect(con, SIGNAL(changed(Plasma::Context*)),
1989  q, SLOT(onContextChanged(Plasma::Context*)));
1990  }
1991 
1992  return con;
1993 }
1994 
1995 KActionCollection* ContainmentPrivate::actions()
1996 {
1997  return static_cast<Applet*>(q)->d->actions;
1998 }
1999 
2000 void ContainmentPrivate::focusApplet(Plasma::Applet *applet)
2001 {
2002  if (focusedApplet == applet) {
2003  return;
2004  }
2005 
2006  QList<QWidget *> widgets = actions()->associatedWidgets();
2007  if (focusedApplet) {
2008  foreach (QWidget *w, widgets) {
2009  focusedApplet->removeAssociatedWidget(w);
2010  }
2011  }
2012 
2013  if (applet && applets.contains(applet)) {
2014  //kDebug() << "switching to" << applet->name();
2015  focusedApplet = applet;
2016  foreach (QWidget *w, widgets) {
2017  focusedApplet->addAssociatedWidget(w);
2018  }
2019 
2020  if (!focusedApplet->hasFocus()) {
2021  focusedApplet->setFocus(Qt::ShortcutFocusReason);
2022  }
2023  } else {
2024  focusedApplet = 0;
2025  }
2026 }
2027 
2028 void Containment::focusNextApplet()
2029 {
2030  if (d->applets.isEmpty()) {
2031  return;
2032  }
2033  int index = d->focusedApplet ? d->applets.indexOf(d->focusedApplet) + 1 : 0;
2034  if (index >= d->applets.size()) {
2035  index = 0;
2036  }
2037  kDebug() << "index" << index;
2038  d->focusApplet(d->applets.at(index));
2039 }
2040 
2041 void Containment::focusPreviousApplet()
2042 {
2043  if (d->applets.isEmpty()) {
2044  return;
2045  }
2046  int index = d->focusedApplet ? d->applets.indexOf(d->focusedApplet) - 1 : -1;
2047  if (index < 0) {
2048  index = d->applets.size() - 1;
2049  }
2050  kDebug() << "index" << index;
2051  d->focusApplet(d->applets.at(index));
2052 }
2053 
2054 void Containment::destroy()
2055 {
2056  destroy(true);
2057 }
2058 
2059 void Containment::showConfigurationInterface()
2060 {
2061  Applet::showConfigurationInterface();
2062 }
2063 
2064 void Containment::configChanged()
2065 {
2066  Applet::configChanged();
2067 }
2068 
2069 void ContainmentPrivate::configChanged()
2070 {
2071  if (drawWallpaper) {
2072  KConfigGroup group = q->config();
2073  q->setWallpaper(group.readEntry("wallpaperplugin", defaultWallpaper),
2074  group.readEntry("wallpaperpluginmode", defaultWallpaperMode));
2075  }
2076 }
2077 
2078 void ContainmentPrivate::requestConfiguration()
2079 {
2080  emit q->configureRequested(q);
2081 }
2082 
2083 void ContainmentPrivate::checkStatus(Plasma::ItemStatus appletStatus)
2084 {
2085  //kDebug() << "================== "<< appletStatus << q->status();
2086  if (appletStatus == q->status()) {
2087  emit q->newStatus(appletStatus);
2088  return;
2089  }
2090 
2091  if (appletStatus < q->status()) {
2092  // check to see if any other applet has a higher status, and stick with that
2093  // if we do
2094  foreach (Applet *applet, applets) {
2095  if (applet->status() > appletStatus) {
2096  appletStatus = applet->status();
2097  }
2098  }
2099  }
2100 
2101  q->setStatus(appletStatus);
2102 }
2103 
2104 void Containment::destroy(bool confirm)
2105 {
2106  if (immutability() != Mutable || Applet::d->transient) {
2107  return;
2108  }
2109 
2110  if (isContainment() && confirm) {
2111  //FIXME: should not be blocking
2112  const QString title = i18nc("@title:window %1 is the name of the containment", "Remove %1", name());
2113  KGuiItem remove = KStandardGuiItem::remove();
2114  remove.setText(title);
2115  if (KMessageBox::warningContinueCancel(view(),
2116  i18nc("%1 is the name of the containment", "Do you really want to remove this %1?", name()),
2117  title, remove) != KMessageBox::Continue) {
2118  return;
2119  }
2120  }
2121 
2122  Applet::destroy();
2123 }
2124 
2125 void ContainmentPrivate::createToolBox()
2126 {
2127  if (!toolBox && KAuthorized::authorizeKAction("plasma/containment_context_menu")) {
2128  toolBox = Plasma::AbstractToolBox::load(q->corona()->preferredToolBoxPlugin(type), QVariantList(), q);
2129 
2130  if (toolBox) {
2131  QObject::connect(toolBox.data(), SIGNAL(toggled()), q, SIGNAL(toolBoxToggled()));
2132  QObject::connect(toolBox.data(), SIGNAL(toggled()), q, SLOT(updateToolBoxVisibility()));
2133 
2134  positionToolBox();
2135  }
2136  }
2137 }
2138 
2139 void ContainmentPrivate::positionToolBox()
2140 {
2141  QMetaObject::invokeMethod(toolBox.data(), "reposition");
2142 }
2143 
2144 void ContainmentPrivate::updateToolBoxVisibility()
2145 {
2146  emit q->toolBoxVisibilityChanged(toolBox.data()->isShowing());
2147 }
2148 
2149 void ContainmentPrivate::triggerShowAddWidgets()
2150 {
2151  emit q->showAddWidgetsInterface(QPointF());
2152 }
2153 
2154 void ContainmentPrivate::containmentConstraintsEvent(Plasma::Constraints constraints)
2155 {
2156  if (!q->isContainment()) {
2157  return;
2158  }
2159 
2160  //kDebug() << "got containmentConstraintsEvent" << constraints << (QObject*)toolBox;
2161  if (constraints & Plasma::ImmutableConstraint) {
2162  //update actions
2163  checkRemoveAction();
2164  const bool unlocked = q->immutability() == Mutable;
2165  q->setAcceptDrops(unlocked);
2166  q->enableAction("add widgets", unlocked);
2167 
2168  // tell the applets too
2169  foreach (Applet *a, applets) {
2170  a->setImmutability(q->immutability());
2171  a->updateConstraints(ImmutableConstraint);
2172  }
2173  }
2174 
2175  // pass on the constraints that are relevant here
2176  Constraints appletConstraints = NoConstraint;
2177  if (constraints & FormFactorConstraint) {
2178  appletConstraints |= FormFactorConstraint;
2179  }
2180 
2181  if (constraints & ScreenConstraint) {
2182  appletConstraints |= ScreenConstraint;
2183  }
2184 
2185  if (appletConstraints != NoConstraint) {
2186  foreach (Applet *applet, applets) {
2187  applet->updateConstraints(appletConstraints);
2188  }
2189  }
2190 
2191  if (toolBox && (constraints & Plasma::SizeConstraint ||
2192  constraints & Plasma::FormFactorConstraint ||
2193  constraints & Plasma::ScreenConstraint ||
2194  constraints & Plasma::StartupCompletedConstraint)) {
2195  //kDebug() << "Positioning toolbox";
2196  positionToolBox();
2197  }
2198 
2199  if (constraints & Plasma::StartupCompletedConstraint && type < Containment::CustomContainment) {
2200  q->addToolBoxAction(q->action("remove"));
2201  checkRemoveAction();
2202  }
2203 }
2204 
2205 Applet *ContainmentPrivate::addApplet(const QString &name, const QVariantList &args,
2206  const QRectF &appletGeometry, uint id, bool delayInit)
2207 {
2208  if (!q->isContainment()) {
2209  return 0;
2210  }
2211 
2212  if (!delayInit && q->immutability() != Mutable) {
2213  kDebug() << "addApplet for" << name << "requested, but we're currently immutable!";
2214  return 0;
2215  }
2216 
2217  QGraphicsView *v = q->view();
2218  if (v) {
2219  v->setCursor(Qt::BusyCursor);
2220  }
2221 
2222  Applet *applet = Applet::load(name, id, args);
2223  if (v) {
2224  v->unsetCursor();
2225  }
2226 
2227  if (!applet) {
2228  kDebug() << "Applet" << name << "could not be loaded.";
2229  applet = new Applet(0, QString(), id);
2230  applet->setFailedToLaunch(true, i18n("Could not find requested component: %1", name));
2231  }
2232 
2233  //kDebug() << applet->name() << "sizehint:" << applet->sizeHint() << "geometry:" << applet->geometry();
2234 
2235  q->addApplet(applet, appletGeometry.topLeft(), delayInit);
2236  return applet;
2237 }
2238 
2239 bool ContainmentPrivate::regionIsEmpty(const QRectF &region, Applet *ignoredApplet) const
2240 {
2241  foreach (Applet *applet, applets) {
2242  if (applet != ignoredApplet && applet->geometry().intersects(region)) {
2243  return false;
2244  }
2245  }
2246  return true;
2247 }
2248 
2249 void ContainmentPrivate::appletDestroyed(Plasma::Applet *applet)
2250 {
2251  applets.removeAll(applet);
2252  if (focusedApplet == applet) {
2253  focusedApplet = 0;
2254  }
2255 
2256  emit q->appletRemoved(applet);
2257  emit q->configNeedsSaving();
2258 }
2259 
2260 void ContainmentPrivate::appletAppearAnimationComplete()
2261 {
2262  Animation *anim = qobject_cast<Animation *>(q->sender());
2263  if (anim) {
2264  Applet *applet = qobject_cast<Applet*>(anim->targetWidget());
2265  if (applet) {
2266  appletAppeared(applet);
2267  }
2268  }
2269 }
2270 
2271 void ContainmentPrivate::appletAppeared(Applet *applet)
2272 {
2273  //kDebug() << type << Containment::DesktopContainment;
2274  KConfigGroup *cg = applet->d->mainConfigGroup();
2275  applet->save(*cg);
2276  emit q->configNeedsSaving();
2277 }
2278 
2279 void ContainmentPrivate::positionPanel(bool force)
2280 {
2281  if (!q->scene()) {
2282  kDebug() << "no scene yet";
2283  return;
2284  }
2285 
2286  // already positioning the panel - avoid infinite loops
2287  if (ContainmentPrivate::s_positioningPanels) {
2288  return;
2289  }
2290 
2291  // we position panels in negative coordinates, and stack all horizontal
2292  // and all vertical panels with each other.
2293 
2294 
2295  const QPointF p = q->pos();
2296 
2297  if (!force &&
2298  p.y() + q->size().height() < -INTER_CONTAINMENT_MARGIN &&
2299  q->scene()->collidingItems(q).isEmpty()) {
2300  // already positioned and not running into any other panels
2301  return;
2302  }
2303 
2304 
2305  QPointF newPos = preferredPanelPos(q->corona());
2306  if (p != newPos) {
2307  ContainmentPrivate::s_positioningPanels = true;
2308  q->setPos(newPos);
2309  ContainmentPrivate::s_positioningPanels = false;
2310  }
2311 }
2312 
2313 bool ContainmentPrivate::isPanelContainment() const
2314 {
2315  return type == Containment::PanelContainment || type == Containment::CustomPanelContainment;
2316 }
2317 
2318 QPointF ContainmentPrivate::preferredPos(Corona *corona) const
2319 {
2320  Q_ASSERT(corona);
2321 
2322  if (isPanelContainment()) {
2323  //kDebug() << "is a panel, so put it at" << preferredPanelPos(corona);
2324  return preferredPanelPos(corona);
2325  }
2326 
2327  QPointF pos(0, 0);
2328  QTransform t;
2329  while (QGraphicsItem *i = corona->itemAt(pos, t)) {
2330  pos.setX(i->scenePos().x() + i->boundingRect().width() + 10);
2331  }
2332 
2333  //kDebug() << "not a panel, put it at" << pos;
2334  return pos;
2335 }
2336 
2337 QPointF ContainmentPrivate::preferredPanelPos(Corona *corona) const
2338 {
2339  Q_ASSERT(corona);
2340 
2341  //TODO: research how non-Horizontal, non-Vertical (e.g. Planar) panels behave here
2342  bool horiz = formFactor == Plasma::Horizontal;
2343  qreal bottom = horiz ? 0 : VERTICAL_STACKING_OFFSET;
2344  qreal lastHeight = 0;
2345 
2346  // this should be ok for small numbers of panels, but if we ever end
2347  // up managing hundreds of them, this simplistic alogrithm will
2348  // likely be too slow.
2349  foreach (const Containment *other, corona->containments()) {
2350  if (other == q ||
2351  !other->d->isPanelContainment() ||
2352  horiz != (other->formFactor() == Plasma::Horizontal)) {
2353  // only line up with panels of the same orientation
2354  continue;
2355  }
2356 
2357  if (horiz) {
2358  qreal y = other->pos().y();
2359  if (y < bottom) {
2360  lastHeight = other->size().height();
2361  bottom = y;
2362  }
2363  } else {
2364  qreal width = other->size().width();
2365  qreal x = other->pos().x() + width;
2366  if (x > bottom) {
2367  lastHeight = width;
2368  bottom = x + lastHeight;
2369  }
2370  }
2371  }
2372 
2373  // give a space equal to the height again of the last item so there is
2374  // room to grow.
2375  QPointF newPos;
2376  if (horiz) {
2377  bottom -= lastHeight + INTER_CONTAINMENT_MARGIN;
2378  //TODO: fix x position for non-flush-left panels
2379  kDebug() << "moved to" << QPointF(0, bottom - q->size().height());
2380  newPos = QPointF(0, bottom - q->size().height());
2381  } else {
2382  bottom += lastHeight + INTER_CONTAINMENT_MARGIN;
2383  //TODO: fix y position for non-flush-top panels
2384  kDebug() << "moved to" << QPointF(bottom + q->size().width(), -INTER_CONTAINMENT_MARGIN - q->size().height());
2385  newPos = QPointF(bottom + q->size().width(), -INTER_CONTAINMENT_MARGIN - q->size().height());
2386  }
2387 
2388  return newPos;
2389 }
2390 
2391 
2392 bool ContainmentPrivate::prepareContainmentActions(const QString &trigger, const QPoint &screenPos, KMenu *menu)
2393 {
2394  ContainmentActions *plugin = actionPlugins()->value(trigger);
2395  if (!plugin) {
2396  return false;
2397  }
2398  plugin->setContainment(q);
2399 
2400  if (!plugin->isInitialized()) {
2401  KConfigGroup cfg = q->containmentActionsConfig();
2402  KConfigGroup pluginConfig = KConfigGroup(&cfg, trigger);
2403  plugin->restore(pluginConfig);
2404  }
2405 
2406  if (plugin->configurationRequired()) {
2407  KMenu *localMenu = menu ? menu : new KMenu();
2408 
2409  localMenu->addTitle(i18n("This plugin needs to be configured"));
2410  localMenu->addAction(q->action("configure"));
2411 
2412  if (!menu) {
2413  localMenu->exec(screenPos);
2414  delete localMenu;
2415  }
2416 
2417  return false;
2418  } else if (menu) {
2419  QList<QAction*> actions = plugin->contextualActions();
2420  if (actions.isEmpty()) {
2421  //it probably didn't bother implementing the function. give the user a chance to set
2422  //a better plugin. note that if the user sets no-plugin this won't happen...
2423  if (!isPanelContainment() && q->action("configure")) {
2424  menu->addAction(q->action("configure"));
2425  }
2426  } else {
2427  menu->addActions(actions);
2428  }
2429  }
2430 
2431  return true;
2432 }
2433 
2434 KConfigGroup Containment::containmentActionsConfig()
2435 {
2436  KConfigGroup cfg;
2437  switch (d->containmentActionsSource) {
2438  case ContainmentPrivate::Local:
2439  cfg = config();
2440  cfg = KConfigGroup(&cfg, "ActionPlugins");
2441  break;
2442  case ContainmentPrivate::Activity:
2443  cfg = KConfigGroup(corona()->config(), "Activities");
2444  cfg = KConfigGroup(&cfg, d->context()->currentActivityId());
2445  cfg = KConfigGroup(&cfg, "ActionPlugins");
2446  break;
2447  default:
2448  cfg = KConfigGroup(corona()->config(), "ActionPlugins");
2449  }
2450  return cfg;
2451 }
2452 
2453 QHash<QString, ContainmentActions*> * ContainmentPrivate::actionPlugins()
2454 {
2455  switch (containmentActionsSource) {
2456  case Activity:
2457  //FIXME
2458  case Local:
2459  return &localActionPlugins;
2460  default:
2461  return &globalActionPlugins;
2462  }
2463 }
2464 
2465 } // Plasma namespace
2466 
2467 #include "containment.moc"
2468 
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Thu Sep 25 2014 04:19:20 by doxygen 1.8.3.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.11.5 API Reference

Skip menu "kdelibs-4.11.5 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