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

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