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

KDE's Doxygen guidelines are available online.

Plasma

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

kdelibs-4.8.5 API Reference

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