• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.10.3 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  QAction *closeApplet = applet->d->actions->action("remove");
733  //kDebug() << "checking for removal" << closeApplet;
734  if (closeApplet) {
735  if (!desktopMenu.isEmpty()) {
736  desktopMenu.addSeparator();
737  }
738 
739  //kDebug() << "adding close action" << closeApplet->isEnabled() << closeApplet->isVisible();
740  desktopMenu.addAction(closeApplet);
741  }
742  }
743 }
744 
745 Applet* ContainmentPrivate::appletAt(const QPointF &point)
746 {
747  Applet *applet = 0;
748 
749  QGraphicsItem *item = q->scene()->itemAt(point);
750  if (item == q) {
751  item = 0;
752  }
753 
754  while (item) {
755  if (item->isWidget()) {
756  applet = qobject_cast<Applet*>(static_cast<QGraphicsWidget*>(item));
757  if (applet) {
758  if (applet->isContainment()) {
759  applet = 0;
760  }
761  break;
762  }
763  }
764  AppletHandle *handle = dynamic_cast<AppletHandle*>(item);
765  if (handle) {
766  //pretend it was on the applet
767  applet = handle->applet();
768  break;
769  }
770  item = item->parentItem();
771  }
772  return applet;
773 }
774 
775 void Containment::setFormFactor(FormFactor formFactor)
776 {
777  if (d->formFactor == formFactor) {
778  return;
779  }
780 
781  //kDebug() << "switching FF to " << formFactor;
782  d->formFactor = formFactor;
783 
784  if (isContainment() &&
785  (d->type == PanelContainment || d->type == CustomPanelContainment)) {
786  // we are a panel and we have chaged our orientation
787  d->positionPanel(true);
788  }
789 
790  QMetaObject::invokeMethod(d->toolBox.data(), "reposition");
791 
792  updateConstraints(Plasma::FormFactorConstraint);
793 
794  KConfigGroup c = config();
795  c.writeEntry("formfactor", (int)formFactor);
796  emit configNeedsSaving();
797 }
798 
799 void Containment::setLocation(Location location)
800 {
801  if (d->location == location) {
802  return;
803  }
804 
805  bool emitGeomChange = false;
806 
807  if ((location == TopEdge || location == BottomEdge) &&
808  (d->location == TopEdge || d->location == BottomEdge)) {
809  emitGeomChange = true;
810  }
811 
812  if ((location == RightEdge || location == LeftEdge) &&
813  (d->location == RightEdge || d->location == LeftEdge)) {
814  emitGeomChange = true;
815  }
816 
817  d->location = location;
818 
819  foreach (Applet *applet, d->applets) {
820  applet->updateConstraints(Plasma::LocationConstraint);
821  }
822 
823  if (emitGeomChange) {
824  // our geometry on the scene will not actually change,
825  // but for the purposes of views it has
826  emit geometryChanged();
827  }
828 
829  updateConstraints(Plasma::LocationConstraint);
830 
831  KConfigGroup c = config();
832  c.writeEntry("location", (int)location);
833  emit configNeedsSaving();
834 }
835 
836 void Containment::addSiblingContainment()
837 {
838  emit addSiblingContainment(this);
839 }
840 
841 void Containment::clearApplets()
842 {
843  foreach (Applet *applet, d->applets) {
844  applet->d->cleanUpAndDelete();
845  }
846 
847  d->applets.clear();
848 }
849 
850 Applet *Containment::addApplet(const QString &name, const QVariantList &args,
851  const QRectF &appletGeometry)
852 {
853  return d->addApplet(name, args, appletGeometry);
854 }
855 
856 void Containment::addApplet(Applet *applet, const QPointF &pos, bool delayInit)
857 {
858  if (!isContainment() || (!delayInit && immutability() != Mutable)) {
859  return;
860  }
861 
862  if (!applet) {
863  kDebug() << "adding null applet!?!";
864  return;
865  }
866 
867  if (d->applets.contains(applet)) {
868  kDebug() << "already have this applet!";
869  }
870 
871  Containment *currentContainment = applet->containment();
872 
873  if (d->type == PanelContainment) {
874  //panels don't want backgrounds, which is important when setting geometry
875  setBackgroundHints(NoBackground);
876  }
877 
878  if (currentContainment && currentContainment != this) {
879  emit currentContainment->appletRemoved(applet);
880  if (currentContainment->d->focusedApplet == applet) {
881  currentContainment->d->focusedApplet = 0;
882  }
883 
884  disconnect(applet, 0, currentContainment, 0);
885  KConfigGroup oldConfig = applet->config();
886  currentContainment->d->applets.removeAll(applet);
887  applet->setParentItem(this);
888  applet->setParent(this);
889 
890  // now move the old config to the new location
891  //FIXME: this doesn't seem to get the actual main config group containing plugin=, etc
892  KConfigGroup c = config().group("Applets").group(QString::number(applet->id()));
893  oldConfig.reparent(&c);
894  applet->d->resetConfigurationObject();
895 
896  disconnect(applet, SIGNAL(activate()), currentContainment, SIGNAL(activate()));
897  } else {
898  applet->setParentItem(this);
899  applet->setParent(this);
900  }
901 
902  d->applets << applet;
903 
904  connect(applet, SIGNAL(configNeedsSaving()), this, SIGNAL(configNeedsSaving()));
905  connect(applet, SIGNAL(releaseVisualFocus()), this, SIGNAL(releaseVisualFocus()));
906  connect(applet, SIGNAL(appletDestroyed(Plasma::Applet*)), this, SLOT(appletDestroyed(Plasma::Applet*)));
907  connect(applet, SIGNAL(newStatus(Plasma::ItemStatus)), this, SLOT(checkStatus(Plasma::ItemStatus)));
908  connect(applet, SIGNAL(activate()), this, SIGNAL(activate()));
909 
910  if (pos != QPointF(-1, -1)) {
911  applet->setPos(pos);
912  }
913 
914  if (!delayInit && !currentContainment) {
915  applet->restore(*applet->d->mainConfigGroup());
916  applet->init();
917  Plasma::Animation *anim = Plasma::Animator::create(Plasma::Animator::AppearAnimation);
918  if (anim) {
919  connect(anim, SIGNAL(finished()), this, SLOT(appletAppearAnimationComplete()));
920  anim->setTargetWidget(applet);
921  //FIXME: small hack until we have proper js anim support; allows 'zoom' to work in the
922  //'right' direction for appearance
923  anim->setDirection(QAbstractAnimation::Backward);
924  anim->start(QAbstractAnimation::DeleteWhenStopped);
925  } else {
926  d->appletAppeared(applet);
927  }
928  }
929 
930  applet->setFlag(QGraphicsItem::ItemIsMovable, true);
931  applet->updateConstraints(Plasma::AllConstraints);
932  if (!delayInit) {
933  applet->flushPendingConstraintsEvents();
934  }
935  emit appletAdded(applet, pos);
936 
937  if (!currentContainment) {
938  applet->updateConstraints(Plasma::StartupCompletedConstraint);
939  if (!delayInit) {
940  applet->flushPendingConstraintsEvents();
941  }
942  }
943 
944  if (!delayInit) {
945  applet->d->scheduleModificationNotification();
946  }
947 }
948 
949 Applet::List Containment::applets() const
950 {
951  return d->applets;
952 }
953 
954 void Containment::setScreen(int newScreen, int newDesktop)
955 {
956  d->setScreen(newScreen, newDesktop);
957 }
958 
959 void ContainmentPrivate::setScreen(int newScreen, int newDesktop, bool preventInvalidDesktops)
960 {
961  // What we want to do in here is:
962  // * claim the screen as our own
963  // * signal whatever may be watching this containment about the switch
964  // * if we are a full screen containment, then:
965  // * resize to match the screen if we're that kind of containment
966  // * kick other full-screen containments off this screen
967  // * if we had a screen, then give our screen to the containment
968  // we kick out
969  //
970  // a screen of -1 means no associated screen.
971  Corona *corona = q->corona();
972  Q_ASSERT(corona);
973 
974  //if it's an offscreen widget, don't allow to claim a screen, after all it's *off*screen
975  if (corona->offscreenWidgets().contains(q)) {
976  return;
977  }
978 
979  int numScreens = corona->numScreens();
980  if (newScreen < -1) {
981  newScreen = -1;
982  }
983 
984  // -1 == All desktops
985  if (newDesktop < -1 || (preventInvalidDesktops && newDesktop > KWindowSystem::numberOfDesktops() - 1)) {
986  newDesktop = -1;
987  }
988 
989  //kDebug() << activity() << "setting screen to " << newScreen << newDesktop << "and type is" << type;
990 
991  Containment *swapScreensWith(0);
992  const bool isDesktopContainment = type == Containment::DesktopContainment ||
993  type == Containment::CustomContainment;
994  if (isDesktopContainment) {
995  // we want to listen to changes in work area if our screen changes
996  if (toolBox) {
997  if (screen < 0 && newScreen > -1) {
998  QObject::connect(KWindowSystem::self(), SIGNAL(workAreaChanged()), toolBox.data(), SLOT(reposition()), Qt::UniqueConnection);
999  } else if (newScreen < 0) {
1000  QObject::disconnect(KWindowSystem::self(), SIGNAL(workAreaChanged()), toolBox.data(), SLOT(reposition()));
1001  }
1002  }
1003 
1004  if (newScreen > -1) {
1005  // sanity check to make sure someone else doesn't have this screen already!
1006  Containment *currently = corona->containmentForScreen(newScreen, newDesktop);
1007  if (currently && currently != q) {
1008  kDebug() << "currently is on screen" << currently->screen()
1009  << "desktop" << currently->desktop()
1010  << "and is" << currently->activity()
1011  << (QObject*)currently << "i'm" << (QObject*)q;
1012  currently->setScreen(-1, currently->desktop());
1013  swapScreensWith = currently;
1014  }
1015  }
1016  }
1017 
1018  if (newScreen < numScreens && newScreen > -1 && isDesktopContainment) {
1019  q->resize(corona->screenGeometry(newScreen).size());
1020  }
1021 
1022  int oldDesktop = desktop;
1023  desktop = newDesktop;
1024 
1025  int oldScreen = screen;
1026  screen = newScreen;
1027 
1028  q->updateConstraints(Plasma::ScreenConstraint);
1029 
1030  if (oldScreen != newScreen || oldDesktop != newDesktop) {
1031  /*
1032  kDebug() << "going to signal change for" << q
1033  << ", old screen & desktop:" << oldScreen << oldDesktop
1034  << ", new:" << screen << desktop;
1035  */
1036  KConfigGroup c = q->config();
1037  c.writeEntry("screen", screen);
1038  c.writeEntry("desktop", desktop);
1039  if (newScreen != -1) {
1040  lastScreen = newScreen;
1041  lastDesktop = newDesktop;
1042  c.writeEntry("lastScreen", lastScreen);
1043  c.writeEntry("lastDesktop", lastDesktop);
1044  }
1045  emit q->configNeedsSaving();
1046  emit q->screenChanged(oldScreen, newScreen, q);
1047  }
1048 
1049  if (swapScreensWith) {
1050  //kDebug() << "setScreen due to swap, part 2";
1051  swapScreensWith->setScreen(oldScreen, oldDesktop);
1052  }
1053 
1054  checkRemoveAction();
1055 
1056  if (newScreen >= 0) {
1057  emit q->activate();
1058  }
1059 }
1060 
1061 int Containment::screen() const
1062 {
1063  return d->screen;
1064 }
1065 
1066 int Containment::lastScreen() const
1067 {
1068  return d->lastScreen;
1069 }
1070 
1071 int Containment::desktop() const
1072 {
1073  return d->desktop;
1074 }
1075 
1076 int Containment::lastDesktop() const
1077 {
1078  return d->lastDesktop;
1079 }
1080 
1081 KPluginInfo::List Containment::listContainments(const QString &category,
1082  const QString &parentApp)
1083 {
1084  return listContainmentsOfType(QString(), category, parentApp);
1085 }
1086 
1087 
1088 KPluginInfo::List Containment::listContainmentsOfType(const QString &type,
1089  const QString &category,
1090  const QString &parentApp)
1091 {
1092  QString constraint;
1093 
1094  if (parentApp.isEmpty()) {
1095  constraint.append("(not exist [X-KDE-ParentApp] or [X-KDE-ParentApp] == '')");
1096  } else {
1097  constraint.append("[X-KDE-ParentApp] == '").append(parentApp).append("'");
1098  }
1099 
1100  if (!type.isEmpty()) {
1101  if (!constraint.isEmpty()) {
1102  constraint.append(" and ");
1103  }
1104 
1105  constraint.append("'").append(type).append("' ~in [X-Plasma-ContainmentCategories]");
1106  }
1107 
1108  if (!category.isEmpty()) {
1109  if (!constraint.isEmpty()) {
1110  constraint.append(" and ");
1111  }
1112 
1113  constraint.append("[X-KDE-PluginInfo-Category] == '").append(category).append("'");
1114  if (category == "Miscellaneous") {
1115  constraint.append(" or (not exist [X-KDE-PluginInfo-Category] or [X-KDE-PluginInfo-Category] == '')");
1116  }
1117  }
1118 
1119  KService::List offers = KServiceTypeTrader::self()->query("Plasma/Containment", constraint);
1120  //kDebug() << "constraint was" << constraint << "which got us" << offers.count() << "matches";
1121  return KPluginInfo::fromServices(offers);
1122 }
1123 
1124 KPluginInfo::List Containment::listContainmentsForMimetype(const QString &mimetype)
1125 {
1126  const QString constraint = QString("'%1' in [X-Plasma-DropMimeTypes]").arg(mimetype);
1127  //kDebug() << mimetype << constraint;
1128  const KService::List offers = KServiceTypeTrader::self()->query("Plasma/Containment", constraint);
1129  return KPluginInfo::fromServices(offers);
1130 }
1131 
1132 QStringList Containment::listContainmentTypes()
1133 {
1134  KPluginInfo::List containmentInfos = listContainments();
1135  QSet<QString> types;
1136 
1137  foreach (const KPluginInfo &containmentInfo, containmentInfos) {
1138  QStringList theseTypes = containmentInfo.service()->property("X-Plasma-ContainmentCategories").toStringList();
1139  foreach (const QString &type, theseTypes) {
1140  types.insert(type);
1141  }
1142  }
1143 
1144  return types.toList();
1145 }
1146 
1147 void Containment::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
1148 {
1149  //kDebug() << immutability() << Mutable << (immutability() == Mutable);
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 
1155  if (!event->isAccepted()) {
1156  // check to see if we have an applet that accepts the format.
1157  QStringList formats = event->mimeData()->formats();
1158 
1159  foreach (const QString &format, formats) {
1160  KPluginInfo::List appletList = Applet::listAppletInfoForMimetype(format);
1161  if (!appletList.isEmpty()) {
1162  event->setAccepted(true);
1163  break;
1164  }
1165  }
1166 
1167  if (!event->isAccepted()) {
1168  foreach (const QString &format, formats) {
1169  KPluginInfo::List wallpaperList = Wallpaper::listWallpaperInfoForMimetype(format);
1170  if (!wallpaperList.isEmpty()) {
1171  event->setAccepted(true);
1172  break;
1173  }
1174  }
1175  }
1176  }
1177 
1178  if (event->isAccepted()) {
1179  if (d->dropZoneStarted) {
1180  showDropZone(event->pos().toPoint());
1181  } else {
1182  if (!d->showDropZoneDelayTimer) {
1183  d->showDropZoneDelayTimer = new QTimer(this);
1184  d->showDropZoneDelayTimer->setInterval(300);
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();
1191  }
1192  }
1193 }
1194 
1195 void Containment::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
1196 {
1197  //kDebug() << event->pos() << size().height() << size().width();
1198  if (d->showDropZoneDelayTimer) {
1199  d->showDropZoneDelayTimer->stop();
1200  }
1201 
1202  if (event->pos().y() < 1 || event->pos().y() > size().height() ||
1203  event->pos().x() < 1 || event->pos().x() > size().width()) {
1204  showDropZone(QPoint());
1205  d->dropZoneStarted = false;
1206  }
1207 }
1208 
1209 void ContainmentPrivate::showDropZoneDelayed()
1210 {
1211  dropZoneStarted = true;
1212  q->showDropZone(dropPoints.value(0).toPoint());
1213  dropPoints.remove(0);
1214 }
1215 
1216 void Containment::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
1217 {
1218  QGraphicsItem *item = scene()->itemAt(event->scenePos());
1219  event->setAccepted(item == this || item == d->toolBox.data() || !item);
1220  //kDebug() << event->isAccepted() << d->showDropZoneDelayTimer->isActive();
1221  if (!event->isAccepted()) {
1222  if (d->showDropZoneDelayTimer) {
1223  d->showDropZoneDelayTimer->stop();
1224  }
1225  } else if (!d->showDropZoneDelayTimer->isActive() && immutability() == Plasma::Mutable) {
1226  showDropZone(event->pos().toPoint());
1227  }
1228 }
1229 
1230 void Containment::dropEvent(QGraphicsSceneDragDropEvent *event)
1231 {
1232  if (isContainment()) {
1233  d->dropData(event->scenePos(), event->screenPos(), event);
1234  } else {
1235  Applet::dropEvent(event);
1236  }
1237 }
1238 
1239 void ContainmentPrivate::dropData(QPointF scenePos, QPoint screenPos, QGraphicsSceneDragDropEvent *dropEvent)
1240 {
1241  if (q->immutability() != Mutable) {
1242  return;
1243  }
1244 
1245  QPointF pos = q->mapFromScene(scenePos);
1246  const QMimeData *mimeData = 0;
1247 
1248  if (dropEvent) {
1249  mimeData = dropEvent->mimeData();
1250  } else {
1251  QClipboard *clipboard = QApplication::clipboard();
1252  mimeData = clipboard->mimeData(QClipboard::Selection);
1253  //TODO if that's not supported (ie non-linux) should we try clipboard instead of selection?
1254  }
1255 
1256  if (!mimeData) {
1257  //Selection is either empty or not supported on this OS
1258  kDebug() << "no mime data";
1259  return;
1260  }
1261 
1262  //kDebug() << event->mimeData()->text();
1263 
1264  QString appletMimetype(q->corona() ? q->corona()->appletMimeType() : QString());
1265 
1266  if (!appletMimetype.isEmpty() && mimeData->hasFormat(appletMimetype)) {
1267  QString data = mimeData->data(appletMimetype);
1268  const QStringList appletNames = data.split('\n', QString::SkipEmptyParts);
1269  foreach (const QString &appletName, appletNames) {
1270  //kDebug() << "doing" << appletName;
1271  QRectF geom(pos, QSize(0, 0));
1272  q->addApplet(appletName, QVariantList(), geom);
1273  }
1274  if (dropEvent) {
1275  dropEvent->acceptProposedAction();
1276  }
1277  } else if (mimeData->hasFormat(ExtenderItemMimeData::mimeType())) {
1278  kDebug() << "mimetype plasma/extenderitem is dropped, creating internal:extender";
1279  //Handle dropping extenderitems.
1280  const ExtenderItemMimeData *extenderData = qobject_cast<const ExtenderItemMimeData*>(mimeData);
1281  if (extenderData) {
1282  ExtenderItem *item = extenderData->extenderItem();
1283  QRectF geometry(pos - extenderData->pointerOffset(), item->size());
1284  kDebug() << "desired geometry: " << geometry;
1285  Applet *applet = qobject_cast<ExtenderApplet *>(item->extender() ? item->extender()->applet() : 0);
1286  if (applet) {
1287  qreal left, top, right, bottom;
1288  applet->getContentsMargins(&left, &top, &right, &bottom);
1289  applet->setPos(geometry.topLeft() - QPointF(int(left), int(top)));
1290  applet->show();
1291  } else {
1292  applet = addApplet("internal:extender", QVariantList(), geometry, 0, true);
1293  applet->hide();
1294  applet->init();
1295  appletAppeared(applet);
1296  applet->flushPendingConstraintsEvents();
1297  applet->d->scheduleModificationNotification();
1298  applet->adjustSize();
1299  applet->show();
1300  }
1301  item->setExtender(applet->extender());
1302  }
1303  } else if (KUrl::List::canDecode(mimeData)) {
1304  //TODO: collect the mimetypes of available script engines and offer
1305  // to create widgets out of the matching URLs, if any
1306  const KUrl::List urls = KUrl::List::fromMimeData(mimeData);
1307  foreach (const KUrl &url, urls) {
1308  if (AccessManager::supportedProtocols().contains(url.protocol())) {
1309  AccessAppletJob *job = AccessManager::self()->accessRemoteApplet(url);
1310  if (dropEvent) {
1311  dropPoints[job] = dropEvent->pos();
1312  } else {
1313  dropPoints[job] = scenePos;
1314  }
1315  QObject::connect(AccessManager::self(), SIGNAL(finished(Plasma::AccessAppletJob*)),
1316  q, SLOT(remoteAppletReady(Plasma::AccessAppletJob*)));
1317  }
1318 #ifndef PLASMA_NO_KIO
1319  else {
1320  KMimeType::Ptr mime = KMimeType::findByUrl(url);
1321  QString mimeName = mime->name();
1322  QRectF geom(pos, QSize());
1323  QVariantList args;
1324  args << url.url();
1325  kDebug() << "can decode" << mimeName << args;
1326 
1327  // It may be a directory or a file, let's stat
1328  KIO::JobFlags flags = KIO::HideProgressInfo;
1329  KIO::MimetypeJob *job = KIO::mimetype(url, flags);
1330  if (dropEvent) {
1331  dropPoints[job] = dropEvent->pos();
1332  } else {
1333  dropPoints[job] = scenePos;
1334  }
1335 
1336  QObject::connect(job, SIGNAL(result(KJob*)), q, SLOT(dropJobResult(KJob*)));
1337  QObject::connect(job, SIGNAL(mimetype(KIO::Job*,QString)),
1338  q, SLOT(mimeTypeRetrieved(KIO::Job*,QString)));
1339 
1340  KMenu *choices = new KMenu("Content dropped");
1341  choices->addAction(KIcon("process-working"), i18n("Fetching file type..."));
1342  if (dropEvent) {
1343  choices->popup(dropEvent->screenPos());
1344  } else {
1345  choices->popup(screenPos);
1346  }
1347 
1348  dropMenus[job] = choices;
1349  }
1350 #endif
1351  }
1352 
1353  if (dropEvent) {
1354  dropEvent->acceptProposedAction();
1355  }
1356  } else {
1357  QStringList formats = mimeData->formats();
1358  QHash<QString, KPluginInfo> seenPlugins;
1359  QHash<QString, QString> pluginFormats;
1360 
1361  foreach (const QString &format, formats) {
1362  KPluginInfo::List plugins = Applet::listAppletInfoForMimetype(format);
1363 
1364  foreach (const KPluginInfo &plugin, plugins) {
1365  if (seenPlugins.contains(plugin.pluginName())) {
1366  continue;
1367  }
1368 
1369  seenPlugins.insert(plugin.pluginName(), plugin);
1370  pluginFormats.insert(plugin.pluginName(), format);
1371  }
1372  }
1373  //kDebug() << "Mimetype ..." << formats << seenPlugins.keys() << pluginFormats.values();
1374 
1375  QString selectedPlugin;
1376 
1377  if (seenPlugins.isEmpty()) {
1378  // do nothing
1379  } else if (seenPlugins.count() == 1) {
1380  selectedPlugin = seenPlugins.constBegin().key();
1381  } else {
1382  KMenu choices;
1383  QHash<QAction *, QString> actionsToPlugins;
1384  foreach (const KPluginInfo &info, seenPlugins) {
1385  QAction *action;
1386  if (!info.icon().isEmpty()) {
1387  action = choices.addAction(KIcon(info.icon()), info.name());
1388  } else {
1389  action = choices.addAction(info.name());
1390  }
1391 
1392  actionsToPlugins.insert(action, info.pluginName());
1393  }
1394 
1395  QAction *choice = choices.exec(screenPos);
1396  if (choice) {
1397  selectedPlugin = actionsToPlugins[choice];
1398  }
1399  }
1400 
1401  if (!selectedPlugin.isEmpty()) {
1402  if (!dropEvent) {
1403  // since we may have entered an event loop up above with the menu,
1404  // the clipboard item may no longer be valid, as QClipboard resets
1405  // the object behind the back of the application with a zero timer
1406  // so we fetch it again here
1407  QClipboard *clipboard = QApplication::clipboard();
1408  mimeData = clipboard->mimeData(QClipboard::Selection);
1409  }
1410 
1411  KTemporaryFile tempFile;
1412  if (mimeData && tempFile.open()) {
1413  //TODO: what should we do with files after the applet is done with them??
1414  tempFile.setAutoRemove(false);
1415 
1416  {
1417  QDataStream stream(&tempFile);
1418  QByteArray data = mimeData->data(pluginFormats[selectedPlugin]);
1419  stream.writeRawData(data, data.size());
1420  }
1421 
1422  QRectF geom(pos, QSize());
1423  QVariantList args;
1424  args << tempFile.fileName();
1425  kDebug() << args;
1426  tempFile.close();
1427 
1428  q->addApplet(selectedPlugin, args, geom);
1429  }
1430  }
1431  }
1432 }
1433 
1434 void ContainmentPrivate::clearDataForMimeJob(KIO::Job *job)
1435 {
1436 #ifndef PLASMA_NO_KIO
1437  QObject::disconnect(job, 0, q, 0);
1438  dropPoints.remove(job);
1439  KMenu *choices = dropMenus.take(job);
1440  delete choices;
1441  job->kill();
1442 #endif // PLASMA_NO_KIO
1443 }
1444 
1445 void ContainmentPrivate::remoteAppletReady(Plasma::AccessAppletJob *job)
1446 {
1447  QPointF pos = dropPoints.take(job);
1448  if (job->error()) {
1449  //TODO: nice user visible error handling (knotification probably?)
1450  kDebug() << "remote applet access failed: " << job->errorText();
1451  return;
1452  }
1453 
1454  if (!job->applet()) {
1455  kDebug() << "how did we end up here? if applet is null, the job->error should be nonzero";
1456  return;
1457  }
1458 
1459  q->addApplet(job->applet(), pos);
1460 }
1461 
1462 void ContainmentPrivate::dropJobResult(KJob *job)
1463 {
1464 #ifndef PLASMA_NO_KIO
1465  KIO::TransferJob* tjob = dynamic_cast<KIO::TransferJob*>(job);
1466  if (!tjob) {
1467  kDebug() << "job is not a KIO::TransferJob, won't handle the drop...";
1468  clearDataForMimeJob(tjob);
1469  return;
1470  }
1471  if (job->error()) {
1472  kDebug() << "ERROR" << tjob->error() << ' ' << tjob->errorString();
1473  }
1474  // We call mimetypeRetrieved since there might be other mechanisms
1475  // for finding suitable applets. Cleanup happens there as well.
1476  mimeTypeRetrieved(qobject_cast<KIO::Job *>(job), QString());
1477 #endif // PLASMA_NO_KIO
1478 }
1479 
1480 void ContainmentPrivate::mimeTypeRetrieved(KIO::Job *job, const QString &mimetype)
1481 {
1482 #ifndef PLASMA_NO_KIO
1483  kDebug() << "Mimetype Job returns." << mimetype;
1484  KIO::TransferJob* tjob = dynamic_cast<KIO::TransferJob*>(job);
1485  if (!tjob) {
1486  kDebug() << "job should be a TransferJob, but isn't";
1487  clearDataForMimeJob(job);
1488  return;
1489  }
1490  KPluginInfo::List appletList = Applet::listAppletInfoForUrl(tjob->url());
1491  if (mimetype.isEmpty() && !appletList.count()) {
1492  clearDataForMimeJob(job);
1493  kDebug() << "No applets found matching the url (" << tjob->url() << ") or the mimetype (" << mimetype << ")";
1494  return;
1495  } else {
1496 
1497  QPointF posi; // will be overwritten with the event's position
1498  if (dropPoints.keys().contains(tjob)) {
1499  posi = dropPoints[tjob];
1500  kDebug() << "Received a suitable dropEvent at" << posi;
1501  } else {
1502  kDebug() << "Bailing out. Cannot find associated dropEvent related to the TransferJob";
1503  clearDataForMimeJob(job);
1504  return;
1505  }
1506 
1507  KMenu *choices = dropMenus.value(tjob);
1508  if (!choices) {
1509  kDebug() << "Bailing out. No QMenu found for this job.";
1510  clearDataForMimeJob(job);
1511  return;
1512  }
1513 
1514  QVariantList args;
1515  args << tjob->url().url() << mimetype;
1516 
1517  kDebug() << "Creating menu for:" << mimetype << posi << args;
1518 
1519  appletList << Applet::listAppletInfoForMimetype(mimetype);
1520  KPluginInfo::List wallpaperList;
1521  if (drawWallpaper) {
1522  if (wallpaper && wallpaper->supportsMimetype(mimetype)) {
1523  wallpaperList << wallpaper->d->wallpaperDescription;
1524  } else {
1525  wallpaperList = Wallpaper::listWallpaperInfoForMimetype(mimetype);
1526  }
1527  }
1528 
1529  if (!appletList.isEmpty() || !wallpaperList.isEmpty()) {
1530  choices->clear();
1531  QHash<QAction *, QString> actionsToApplets;
1532  choices->addTitle(i18n("Widgets"));
1533  foreach (const KPluginInfo &info, appletList) {
1534  kDebug() << info.name();
1535  QAction *action;
1536  if (!info.icon().isEmpty()) {
1537  action = choices->addAction(KIcon(info.icon()), info.name());
1538  } else {
1539  action = choices->addAction(info.name());
1540  }
1541 
1542  actionsToApplets.insert(action, info.pluginName());
1543  kDebug() << info.pluginName();
1544  }
1545  actionsToApplets.insert(choices->addAction(i18n("Icon")), "icon");
1546 
1547  QHash<QAction *, QString> actionsToWallpapers;
1548  if (!wallpaperList.isEmpty()) {
1549  choices->addTitle(i18n("Wallpaper"));
1550 
1551  QMap<QString, KPluginInfo> sorted;
1552  foreach (const KPluginInfo &info, appletList) {
1553  sorted.insert(info.name(), info);
1554  }
1555 
1556  foreach (const KPluginInfo &info, wallpaperList) {
1557  QAction *action;
1558  if (!info.icon().isEmpty()) {
1559  action = choices->addAction(KIcon(info.icon()), info.name());
1560  } else {
1561  action = choices->addAction(info.name());
1562  }
1563 
1564  actionsToWallpapers.insert(action, info.pluginName());
1565  }
1566  }
1567 
1568  QAction *choice = choices->exec();
1569  if (choice) {
1570  // Put the job on hold so it can be recycled to fetch the actual content,
1571  // which is to be expected when something's dropped onto the desktop and
1572  // an applet is to be created with this URL
1573  if (!mimetype.isEmpty() && !tjob->error()) {
1574  tjob->putOnHold();
1575  KIO::Scheduler::publishSlaveOnHold();
1576  }
1577  QString plugin = actionsToApplets.value(choice);
1578  if (plugin.isEmpty()) {
1579  //set wallpapery stuff
1580  plugin = actionsToWallpapers.value(choice);
1581  if (!wallpaper || plugin != wallpaper->pluginName()) {
1582  kDebug() << "Wallpaper dropped:" << tjob->url();
1583  q->setWallpaper(plugin);
1584  }
1585 
1586  if (wallpaper) {
1587  kDebug() << "Wallpaper dropped:" << tjob->url();
1588  wallpaper->setUrls(KUrl::List() << tjob->url());
1589  }
1590  } else {
1591  addApplet(actionsToApplets[choice], args, QRectF(posi, QSize()));
1592  }
1593 
1594  clearDataForMimeJob(job);
1595  return;
1596  }
1597  } else {
1598  // we can at least create an icon as a link to the URL
1599  addApplet("icon", args, QRectF(posi, QSize()));
1600  }
1601  }
1602 
1603  clearDataForMimeJob(job);
1604 #endif // PLASMA_NO_KIO
1605 }
1606 
1607 #ifndef KDE_NO_DEPRECATED
1608 const QGraphicsItem *Containment::toolBoxItem() const
1609 {
1610  return d->toolBox.data();
1611 }
1612 #endif
1613 
1614 void Containment::setToolBox(AbstractToolBox *toolBox)
1615 {
1616  if (d->toolBox.data()) {
1617  d->toolBox.data()->deleteLater();
1618  }
1619  d->toolBox = toolBox;
1620 }
1621 
1622 AbstractToolBox *Containment::toolBox() const
1623 {
1624  return d->toolBox.data();
1625 }
1626 
1627 void Containment::resizeEvent(QGraphicsSceneResizeEvent *event)
1628 {
1629  Applet::resizeEvent(event);
1630 
1631  if (isContainment()) {
1632  if (d->isPanelContainment()) {
1633  d->positionPanel();
1634  } else if (corona()) {
1635  QMetaObject::invokeMethod(corona(), "layoutContainments");
1636  }
1637 
1638  if (d->wallpaper) {
1639  d->wallpaper->setBoundingRect(QRectF(QPointF(0, 0), size()));
1640  }
1641  }
1642 }
1643 
1644 void Containment::keyPressEvent(QKeyEvent *event)
1645 {
1646  //kDebug() << "keyPressEvent with" << event->key()
1647  // << "and hoping and wishing for a" << Qt::Key_Tab;
1648  if (event->key() == Qt::Key_Tab) { // && event->modifiers() == 0) {
1649  if (!d->applets.isEmpty()) {
1650  kDebug() << "let's give focus to...." << (QObject*)d->applets.first();
1651  d->applets.first()->setFocus(Qt::TabFocusReason);
1652  }
1653  }
1654 }
1655 
1656 void Containment::wheelEvent(QGraphicsSceneWheelEvent *event)
1657 {
1658  event->ignore();
1659  if (d->appletAt(event->scenePos())) {
1660  return; //no unexpected click-throughs
1661  }
1662 
1663  if (d->wallpaper && d->wallpaper->isInitialized()) {
1664  QGraphicsItem *item = scene()->itemAt(event->scenePos());
1665  if (item == this) {
1666  event->ignore();
1667  d->wallpaper->wheelEvent(event);
1668 
1669  if (event->isAccepted()) {
1670  return;
1671  }
1672  }
1673  }
1674 
1675  QString trigger = ContainmentActions::eventToString(event);
1676 
1677  if (d->prepareContainmentActions(trigger, event->screenPos())) {
1678  d->actionPlugins()->value(trigger)->contextEvent(event);
1679  event->accept();
1680  } else {
1681  event->ignore();
1682  Applet::wheelEvent(event);
1683  }
1684 }
1685 
1686 bool Containment::sceneEventFilter(QGraphicsItem *watched, QEvent *event)
1687 {
1688  return Applet::sceneEventFilter(watched, event);
1689 }
1690 
1691 QVariant Containment::itemChange(GraphicsItemChange change, const QVariant &value)
1692 {
1693  //FIXME if the applet is moved to another containment we need to unfocus it
1694 
1695  if (isContainment() &&
1696  (change == QGraphicsItem::ItemSceneHasChanged ||
1697  change == QGraphicsItem::ItemPositionHasChanged)) {
1698  switch (d->type) {
1699  case PanelContainment:
1700  case CustomPanelContainment:
1701  d->positionPanel();
1702  break;
1703  default:
1704  if (corona()) {
1705  QMetaObject::invokeMethod(corona(), "layoutContainments");
1706  }
1707  break;
1708  }
1709  }
1710 
1711  return Applet::itemChange(change, value);
1712 }
1713 
1714 void Containment::enableAction(const QString &name, bool enable)
1715 {
1716  QAction *action = this->action(name);
1717  if (action) {
1718  action->setEnabled(enable);
1719  action->setVisible(enable);
1720  }
1721 }
1722 
1723 void Containment::addToolBoxAction(QAction *action)
1724 {
1725  d->createToolBox();
1726  if (d->toolBox) {
1727  d->toolBox.data()->addTool(action);
1728  }
1729 }
1730 
1731 void Containment::removeToolBoxAction(QAction *action)
1732 {
1733  if (d->toolBox) {
1734  d->toolBox.data()->removeTool(action);
1735  }
1736 }
1737 
1738 void Containment::setToolBoxOpen(bool open)
1739 {
1740  if (open) {
1741  openToolBox();
1742  } else {
1743  closeToolBox();
1744  }
1745 }
1746 
1747 bool Containment::isToolBoxOpen() const
1748 {
1749  return (d->toolBox && d->toolBox.data()->isShowing());
1750 }
1751 
1752 void Containment::openToolBox()
1753 {
1754  if (d->toolBox && !d->toolBox.data()->isShowing()) {
1755  d->toolBox.data()->setShowing(true);
1756  emit toolBoxVisibilityChanged(true);
1757  }
1758 }
1759 
1760 void Containment::closeToolBox()
1761 {
1762  if (d->toolBox && d->toolBox.data()->isShowing()) {
1763  d->toolBox.data()->setShowing(false);
1764  emit toolBoxVisibilityChanged(false);
1765  }
1766 }
1767 
1768 void Containment::addAssociatedWidget(QWidget *widget)
1769 {
1770  Applet::addAssociatedWidget(widget);
1771  if (d->focusedApplet) {
1772  d->focusedApplet->addAssociatedWidget(widget);
1773  }
1774 
1775  foreach (const Applet *applet, d->applets) {
1776  if (applet->d->activationAction) {
1777  widget->addAction(applet->d->activationAction);
1778  }
1779  }
1780 }
1781 
1782 void Containment::removeAssociatedWidget(QWidget *widget)
1783 {
1784  Applet::removeAssociatedWidget(widget);
1785  if (d->focusedApplet) {
1786  d->focusedApplet->removeAssociatedWidget(widget);
1787  }
1788 
1789  foreach (const Applet *applet, d->applets) {
1790  if (applet->d->activationAction) {
1791  widget->removeAction(applet->d->activationAction);
1792  }
1793  }
1794 }
1795 
1796 void Containment::setDrawWallpaper(bool drawWallpaper)
1797 {
1798  d->drawWallpaper = drawWallpaper;
1799  if (drawWallpaper) {
1800  KConfigGroup cfg = config();
1801  const QString wallpaper = cfg.readEntry("wallpaperplugin", defaultWallpaper);
1802  const QString mode = cfg.readEntry("wallpaperpluginmode", defaultWallpaperMode);
1803  setWallpaper(wallpaper, mode);
1804  } else {
1805  delete d->wallpaper;
1806  d->wallpaper = 0;
1807  }
1808 }
1809 
1810 bool Containment::drawWallpaper()
1811 {
1812  return d->drawWallpaper;
1813 }
1814 
1815 void Containment::setWallpaper(const QString &pluginName, const QString &mode)
1816 {
1817  KConfigGroup cfg = config();
1818  bool newPlugin = true;
1819  bool newMode = true;
1820 
1821  if (d->drawWallpaper) {
1822  if (d->wallpaper) {
1823  // we have a wallpaper, so let's decide whether we need to swap it out
1824  if (d->wallpaper->pluginName() != pluginName) {
1825  delete d->wallpaper;
1826  d->wallpaper = 0;
1827  } else {
1828  // it's the same plugin, so let's save its state now so when
1829  // we call restore later on we're safe
1830  newMode = d->wallpaper->renderingMode().name() != mode;
1831  newPlugin = false;
1832  }
1833  }
1834 
1835  if (!pluginName.isEmpty() && !d->wallpaper) {
1836  d->wallpaper = Plasma::Wallpaper::load(pluginName);
1837  }
1838 
1839  if (d->wallpaper) {
1840  d->wallpaper->setParent(this);
1841  d->wallpaper->setBoundingRect(QRectF(QPointF(0, 0), size()));
1842  d->wallpaper->setRenderingMode(mode);
1843 
1844  if (newPlugin) {
1845  cfg.writeEntry("wallpaperplugin", pluginName);
1846  }
1847 
1848  if (d->wallpaper->isInitialized()) {
1849  KConfigGroup wallpaperConfig = KConfigGroup(&cfg, "Wallpaper");
1850  wallpaperConfig = KConfigGroup(&wallpaperConfig, pluginName);
1851  d->wallpaper->restore(wallpaperConfig);
1852  }
1853 
1854  if (newMode) {
1855  cfg.writeEntry("wallpaperpluginmode", mode);
1856  }
1857  }
1858 
1859  update();
1860  }
1861 
1862  if (!d->wallpaper) {
1863  cfg.deleteEntry("wallpaperplugin");
1864  cfg.deleteEntry("wallpaperpluginmode");
1865  }
1866 
1867  if (newPlugin || newMode) {
1868  if (newPlugin && d->wallpaper) {
1869  connect(d->wallpaper, SIGNAL(configureRequested()), this, SLOT(requestConfiguration()));
1870  connect(d->wallpaper, SIGNAL(configNeedsSaving()), this, SIGNAL(configNeedsSaving()));
1871  }
1872 
1873  emit configNeedsSaving();
1874  }
1875 }
1876 
1877 Plasma::Wallpaper *Containment::wallpaper() const
1878 {
1879  return d->wallpaper;
1880 }
1881 
1882 void Containment::setContainmentActions(const QString &trigger, const QString &pluginName)
1883 {
1884  KConfigGroup cfg = containmentActionsConfig();
1885  ContainmentActions *plugin = 0;
1886 
1887  if (d->actionPlugins()->contains(trigger)) {
1888  plugin = d->actionPlugins()->value(trigger);
1889  if (plugin->pluginName() != pluginName) {
1890  d->actionPlugins()->remove(trigger);
1891  delete plugin;
1892  plugin=0;
1893  }
1894  }
1895  if (pluginName.isEmpty()) {
1896  cfg.deleteEntry(trigger);
1897  } else if (plugin) {
1898  //it already existed, just reload config
1899  if (plugin->isInitialized()) {
1900  plugin->setContainment(this); //to be safe
1901  //FIXME make a truly unique config group
1902  KConfigGroup pluginConfig = KConfigGroup(&cfg, trigger);
1903  plugin->restore(pluginConfig);
1904  }
1905  } else {
1906  switch (d->containmentActionsSource) {
1907  case ContainmentPrivate::Activity:
1908  //FIXME
1909  case ContainmentPrivate::Local:
1910  plugin = ContainmentActions::load(this, pluginName);
1911  break;
1912  default:
1913  plugin = ContainmentActions::load(0, pluginName);
1914  }
1915  if (plugin) {
1916  cfg.writeEntry(trigger, pluginName);
1917  d->actionPlugins()->insert(trigger, plugin);
1918  } else {
1919  //bad plugin... gets removed. is this a feature or a bug?
1920  cfg.deleteEntry(trigger);
1921  }
1922  }
1923 
1924  emit configNeedsSaving();
1925 }
1926 
1927 QStringList Containment::containmentActionsTriggers()
1928 {
1929  return d->actionPlugins()->keys();
1930 }
1931 
1932 QString Containment::containmentActions(const QString &trigger)
1933 {
1934  ContainmentActions *c = d->actionPlugins()->value(trigger);
1935  return c ? c->pluginName() : QString();
1936 }
1937 
1938 void Containment::setActivity(const QString &activity)
1939 {
1940  Context *context = d->context();
1941  if (context->currentActivity() != activity) {
1942  context->setCurrentActivity(activity);
1943  }
1944 }
1945 
1946 void ContainmentPrivate::onContextChanged(Plasma::Context *con)
1947 {
1948  foreach (Applet *a, applets) {
1949  a->updateConstraints(ContextConstraint);
1950  }
1951 
1952  KConfigGroup c = q->config();
1953  QString act = con->currentActivityId();
1954 
1955  //save anything that's been set (boy I hope this avoids overwriting things)
1956  //FIXME of course if the user sets the name to an empty string we have a bug
1957  //but once we get context retrieving the name as soon as the id is set, this issue should go away
1958  if (!act.isEmpty()) {
1959  c.writeEntry("activityId", act);
1960  }
1961  act = con->currentActivity();
1962  if (!act.isEmpty()) {
1963  c.writeEntry("activity", act);
1964  }
1965 
1966  if (toolBox) {
1967  toolBox.data()->update();
1968  }
1969  emit q->configNeedsSaving();
1970  emit q->contextChanged(con);
1971 }
1972 
1973 QString Containment::activity() const
1974 {
1975  return d->context()->currentActivity();
1976 }
1977 
1978 Context *Containment::context() const
1979 {
1980  return d->context();
1981 }
1982 
1983 Context *ContainmentPrivate::context()
1984 {
1985  if (!con) {
1986  con = new Context(q);
1987  q->connect(con, SIGNAL(changed(Plasma::Context*)),
1988  q, SLOT(onContextChanged(Plasma::Context*)));
1989  }
1990 
1991  return con;
1992 }
1993 
1994 KActionCollection* ContainmentPrivate::actions()
1995 {
1996  return static_cast<Applet*>(q)->d->actions;
1997 }
1998 
1999 void ContainmentPrivate::focusApplet(Plasma::Applet *applet)
2000 {
2001  if (focusedApplet == applet) {
2002  return;
2003  }
2004 
2005  QList<QWidget *> widgets = actions()->associatedWidgets();
2006  if (focusedApplet) {
2007  foreach (QWidget *w, widgets) {
2008  focusedApplet->removeAssociatedWidget(w);
2009  }
2010  }
2011 
2012  if (applet && applets.contains(applet)) {
2013  //kDebug() << "switching to" << applet->name();
2014  focusedApplet = applet;
2015  foreach (QWidget *w, widgets) {
2016  focusedApplet->addAssociatedWidget(w);
2017  }
2018 
2019  if (!focusedApplet->hasFocus()) {
2020  focusedApplet->setFocus(Qt::ShortcutFocusReason);
2021  }
2022  } else {
2023  focusedApplet = 0;
2024  }
2025 }
2026 
2027 void Containment::focusNextApplet()
2028 {
2029  if (d->applets.isEmpty()) {
2030  return;
2031  }
2032  int index = d->focusedApplet ? d->applets.indexOf(d->focusedApplet) + 1 : 0;
2033  if (index >= d->applets.size()) {
2034  index = 0;
2035  }
2036  kDebug() << "index" << index;
2037  d->focusApplet(d->applets.at(index));
2038 }
2039 
2040 void Containment::focusPreviousApplet()
2041 {
2042  if (d->applets.isEmpty()) {
2043  return;
2044  }
2045  int index = d->focusedApplet ? d->applets.indexOf(d->focusedApplet) - 1 : -1;
2046  if (index < 0) {
2047  index = d->applets.size() - 1;
2048  }
2049  kDebug() << "index" << index;
2050  d->focusApplet(d->applets.at(index));
2051 }
2052 
2053 void Containment::destroy()
2054 {
2055  destroy(true);
2056 }
2057 
2058 void Containment::showConfigurationInterface()
2059 {
2060  Applet::showConfigurationInterface();
2061 }
2062 
2063 void Containment::configChanged()
2064 {
2065  Applet::configChanged();
2066 }
2067 
2068 void ContainmentPrivate::configChanged()
2069 {
2070  if (drawWallpaper) {
2071  KConfigGroup group = q->config();
2072  q->setWallpaper(group.readEntry("wallpaperplugin", defaultWallpaper),
2073  group.readEntry("wallpaperpluginmode", defaultWallpaperMode));
2074  }
2075 }
2076 
2077 void ContainmentPrivate::requestConfiguration()
2078 {
2079  emit q->configureRequested(q);
2080 }
2081 
2082 void ContainmentPrivate::checkStatus(Plasma::ItemStatus appletStatus)
2083 {
2084  //kDebug() << "================== "<< appletStatus << q->status();
2085  if (appletStatus == q->status()) {
2086  emit q->newStatus(appletStatus);
2087  return;
2088  }
2089 
2090  if (appletStatus < q->status()) {
2091  // check to see if any other applet has a higher status, and stick with that
2092  // if we do
2093  foreach (Applet *applet, applets) {
2094  if (applet->status() > appletStatus) {
2095  appletStatus = applet->status();
2096  }
2097  }
2098  }
2099 
2100  q->setStatus(appletStatus);
2101 }
2102 
2103 void Containment::destroy(bool confirm)
2104 {
2105  if (immutability() != Mutable || Applet::d->transient) {
2106  return;
2107  }
2108 
2109  if (isContainment() && confirm) {
2110  //FIXME: should not be blocking
2111  const QString title = i18nc("@title:window %1 is the name of the containment", "Remove %1", name());
2112  KGuiItem remove = KStandardGuiItem::remove();
2113  remove.setText(title);
2114  if (KMessageBox::warningContinueCancel(view(),
2115  i18nc("%1 is the name of the containment", "Do you really want to remove this %1?", name()),
2116  title, remove) != KMessageBox::Continue) {
2117  return;
2118  }
2119  }
2120 
2121  Applet::destroy();
2122 }
2123 
2124 void ContainmentPrivate::createToolBox()
2125 {
2126  if (!toolBox && KAuthorized::authorizeKAction("plasma/containment_context_menu")) {
2127  toolBox = Plasma::AbstractToolBox::load(q->corona()->preferredToolBoxPlugin(type), QVariantList(), q);
2128 
2129  if (toolBox) {
2130  QObject::connect(toolBox.data(), SIGNAL(toggled()), q, SIGNAL(toolBoxToggled()));
2131  QObject::connect(toolBox.data(), SIGNAL(toggled()), q, SLOT(updateToolBoxVisibility()));
2132 
2133  positionToolBox();
2134  }
2135  }
2136 }
2137 
2138 void ContainmentPrivate::positionToolBox()
2139 {
2140  QMetaObject::invokeMethod(toolBox.data(), "reposition");
2141 }
2142 
2143 void ContainmentPrivate::updateToolBoxVisibility()
2144 {
2145  emit q->toolBoxVisibilityChanged(toolBox.data()->isShowing());
2146 }
2147 
2148 void ContainmentPrivate::triggerShowAddWidgets()
2149 {
2150  emit q->showAddWidgetsInterface(QPointF());
2151 }
2152 
2153 void ContainmentPrivate::containmentConstraintsEvent(Plasma::Constraints constraints)
2154 {
2155  if (!q->isContainment()) {
2156  return;
2157  }
2158 
2159  //kDebug() << "got containmentConstraintsEvent" << constraints << (QObject*)toolBox;
2160  if (constraints & Plasma::ImmutableConstraint) {
2161  //update actions
2162  checkRemoveAction();
2163  const bool unlocked = q->immutability() == Mutable;
2164  q->setAcceptDrops(unlocked);
2165  q->enableAction("add widgets", unlocked);
2166 
2167  // tell the applets too
2168  foreach (Applet *a, applets) {
2169  a->setImmutability(q->immutability());
2170  a->updateConstraints(ImmutableConstraint);
2171  }
2172  }
2173 
2174  // pass on the constraints that are relevant here
2175  Constraints appletConstraints = NoConstraint;
2176  if (constraints & FormFactorConstraint) {
2177  appletConstraints |= FormFactorConstraint;
2178  }
2179 
2180  if (constraints & ScreenConstraint) {
2181  appletConstraints |= ScreenConstraint;
2182  }
2183 
2184  if (appletConstraints != NoConstraint) {
2185  foreach (Applet *applet, applets) {
2186  applet->updateConstraints(appletConstraints);
2187  }
2188  }
2189 
2190  if (toolBox && (constraints & Plasma::SizeConstraint ||
2191  constraints & Plasma::FormFactorConstraint ||
2192  constraints & Plasma::ScreenConstraint ||
2193  constraints & Plasma::StartupCompletedConstraint)) {
2194  //kDebug() << "Positioning toolbox";
2195  positionToolBox();
2196  }
2197 
2198  if (constraints & Plasma::StartupCompletedConstraint && type < Containment::CustomContainment) {
2199  q->addToolBoxAction(q->action("remove"));
2200  checkRemoveAction();
2201  }
2202 }
2203 
2204 Applet *ContainmentPrivate::addApplet(const QString &name, const QVariantList &args,
2205  const QRectF &appletGeometry, uint id, bool delayInit)
2206 {
2207  if (!q->isContainment()) {
2208  return 0;
2209  }
2210 
2211  if (!delayInit && q->immutability() != Mutable) {
2212  kDebug() << "addApplet for" << name << "requested, but we're currently immutable!";
2213  return 0;
2214  }
2215 
2216  QGraphicsView *v = q->view();
2217  if (v) {
2218  v->setCursor(Qt::BusyCursor);
2219  }
2220 
2221  Applet *applet = Applet::load(name, id, args);
2222  if (v) {
2223  v->unsetCursor();
2224  }
2225 
2226  if (!applet) {
2227  kDebug() << "Applet" << name << "could not be loaded.";
2228  applet = new Applet(0, QString(), id);
2229  applet->setFailedToLaunch(true, i18n("Could not find requested component: %1", name));
2230  }
2231 
2232  //kDebug() << applet->name() << "sizehint:" << applet->sizeHint() << "geometry:" << applet->geometry();
2233 
2234  q->addApplet(applet, appletGeometry.topLeft(), delayInit);
2235  return applet;
2236 }
2237 
2238 bool ContainmentPrivate::regionIsEmpty(const QRectF &region, Applet *ignoredApplet) const
2239 {
2240  foreach (Applet *applet, applets) {
2241  if (applet != ignoredApplet && applet->geometry().intersects(region)) {
2242  return false;
2243  }
2244  }
2245  return true;
2246 }
2247 
2248 void ContainmentPrivate::appletDestroyed(Plasma::Applet *applet)
2249 {
2250  applets.removeAll(applet);
2251  if (focusedApplet == applet) {
2252  focusedApplet = 0;
2253  }
2254 
2255  emit q->appletRemoved(applet);
2256  emit q->configNeedsSaving();
2257 }
2258 
2259 void ContainmentPrivate::appletAppearAnimationComplete()
2260 {
2261  Animation *anim = qobject_cast<Animation *>(q->sender());
2262  if (anim) {
2263  Applet *applet = qobject_cast<Applet*>(anim->targetWidget());
2264  if (applet) {
2265  appletAppeared(applet);
2266  }
2267  }
2268 }
2269 
2270 void ContainmentPrivate::appletAppeared(Applet *applet)
2271 {
2272  //kDebug() << type << Containment::DesktopContainment;
2273  KConfigGroup *cg = applet->d->mainConfigGroup();
2274  applet->save(*cg);
2275  emit q->configNeedsSaving();
2276 }
2277 
2278 void ContainmentPrivate::positionPanel(bool force)
2279 {
2280  if (!q->scene()) {
2281  kDebug() << "no scene yet";
2282  return;
2283  }
2284 
2285  // already positioning the panel - avoid infinite loops
2286  if (ContainmentPrivate::s_positioningPanels) {
2287  return;
2288  }
2289 
2290  // we position panels in negative coordinates, and stack all horizontal
2291  // and all vertical panels with each other.
2292 
2293 
2294  const QPointF p = q->pos();
2295 
2296  if (!force &&
2297  p.y() + q->size().height() < -INTER_CONTAINMENT_MARGIN &&
2298  q->scene()->collidingItems(q).isEmpty()) {
2299  // already positioned and not running into any other panels
2300  return;
2301  }
2302 
2303 
2304  QPointF newPos = preferredPanelPos(q->corona());
2305  if (p != newPos) {
2306  ContainmentPrivate::s_positioningPanels = true;
2307  q->setPos(newPos);
2308  ContainmentPrivate::s_positioningPanels = false;
2309  }
2310 }
2311 
2312 bool ContainmentPrivate::isPanelContainment() const
2313 {
2314  return type == Containment::PanelContainment || type == Containment::CustomPanelContainment;
2315 }
2316 
2317 QPointF ContainmentPrivate::preferredPos(Corona *corona) const
2318 {
2319  Q_ASSERT(corona);
2320 
2321  if (isPanelContainment()) {
2322  //kDebug() << "is a panel, so put it at" << preferredPanelPos(corona);
2323  return preferredPanelPos(corona);
2324  }
2325 
2326  QPointF pos(0, 0);
2327  QTransform t;
2328  while (QGraphicsItem *i = corona->itemAt(pos, t)) {
2329  pos.setX(i->scenePos().x() + i->boundingRect().width() + 10);
2330  }
2331 
2332  //kDebug() << "not a panel, put it at" << pos;
2333  return pos;
2334 }
2335 
2336 QPointF ContainmentPrivate::preferredPanelPos(Corona *corona) const
2337 {
2338  Q_ASSERT(corona);
2339 
2340  //TODO: research how non-Horizontal, non-Vertical (e.g. Planar) panels behave here
2341  bool horiz = formFactor == Plasma::Horizontal;
2342  qreal bottom = horiz ? 0 : VERTICAL_STACKING_OFFSET;
2343  qreal lastHeight = 0;
2344 
2345  // this should be ok for small numbers of panels, but if we ever end
2346  // up managing hundreds of them, this simplistic alogrithm will
2347  // likely be too slow.
2348  foreach (const Containment *other, corona->containments()) {
2349  if (other == q ||
2350  !other->d->isPanelContainment() ||
2351  horiz != (other->formFactor() == Plasma::Horizontal)) {
2352  // only line up with panels of the same orientation
2353  continue;
2354  }
2355 
2356  if (horiz) {
2357  qreal y = other->pos().y();
2358  if (y < bottom) {
2359  lastHeight = other->size().height();
2360  bottom = y;
2361  }
2362  } else {
2363  qreal width = other->size().width();
2364  qreal x = other->pos().x() + width;
2365  if (x > bottom) {
2366  lastHeight = width;
2367  bottom = x + lastHeight;
2368  }
2369  }
2370  }
2371 
2372  // give a space equal to the height again of the last item so there is
2373  // room to grow.
2374  QPointF newPos;
2375  if (horiz) {
2376  bottom -= lastHeight + INTER_CONTAINMENT_MARGIN;
2377  //TODO: fix x position for non-flush-left panels
2378  kDebug() << "moved to" << QPointF(0, bottom - q->size().height());
2379  newPos = QPointF(0, bottom - q->size().height());
2380  } else {
2381  bottom += lastHeight + INTER_CONTAINMENT_MARGIN;
2382  //TODO: fix y position for non-flush-top panels
2383  kDebug() << "moved to" << QPointF(bottom + q->size().width(), -INTER_CONTAINMENT_MARGIN - q->size().height());
2384  newPos = QPointF(bottom + q->size().width(), -INTER_CONTAINMENT_MARGIN - q->size().height());
2385  }
2386 
2387  return newPos;
2388 }
2389 
2390 
2391 bool ContainmentPrivate::prepareContainmentActions(const QString &trigger, const QPoint &screenPos, KMenu *menu)
2392 {
2393  ContainmentActions *plugin = actionPlugins()->value(trigger);
2394  if (!plugin) {
2395  return false;
2396  }
2397  plugin->setContainment(q);
2398 
2399  if (!plugin->isInitialized()) {
2400  KConfigGroup cfg = q->containmentActionsConfig();
2401  KConfigGroup pluginConfig = KConfigGroup(&cfg, trigger);
2402  plugin->restore(pluginConfig);
2403  }
2404 
2405  if (plugin->configurationRequired()) {
2406  KMenu *localMenu = menu ? menu : new KMenu();
2407 
2408  localMenu->addTitle(i18n("This plugin needs to be configured"));
2409  localMenu->addAction(q->action("configure"));
2410 
2411  if (!menu) {
2412  localMenu->exec(screenPos);
2413  delete localMenu;
2414  }
2415 
2416  return false;
2417  } else if (menu) {
2418  QList<QAction*> actions = plugin->contextualActions();
2419  if (actions.isEmpty()) {
2420  //it probably didn't bother implementing the function. give the user a chance to set
2421  //a better plugin. note that if the user sets no-plugin this won't happen...
2422  if (!isPanelContainment() && q->action("configure")) {
2423  menu->addAction(q->action("configure"));
2424  }
2425  } else {
2426  menu->addActions(actions);
2427  }
2428  }
2429 
2430  return true;
2431 }
2432 
2433 KConfigGroup Containment::containmentActionsConfig()
2434 {
2435  KConfigGroup cfg;
2436  switch (d->containmentActionsSource) {
2437  case ContainmentPrivate::Local:
2438  cfg = config();
2439  cfg = KConfigGroup(&cfg, "ActionPlugins");
2440  break;
2441  case ContainmentPrivate::Activity:
2442  cfg = KConfigGroup(corona()->config(), "Activities");
2443  cfg = KConfigGroup(&cfg, d->context()->currentActivityId());
2444  cfg = KConfigGroup(&cfg, "ActionPlugins");
2445  break;
2446  default:
2447  cfg = KConfigGroup(corona()->config(), "ActionPlugins");
2448  }
2449  return cfg;
2450 }
2451 
2452 QHash<QString, ContainmentActions*> * ContainmentPrivate::actionPlugins()
2453 {
2454  switch (containmentActionsSource) {
2455  case Activity:
2456  //FIXME
2457  case Local:
2458  return &localActionPlugins;
2459  default:
2460  return &globalActionPlugins;
2461  }
2462 }
2463 
2464 } // Plasma namespace
2465 
2466 #include "containment.moc"
2467 
This file is part of the KDE documentation.
Documentation copyright © 1996-2013 The KDE developers.
Generated on Sat May 18 2013 11:37:51 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.10.3 API Reference

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