some more win32'fication to fix non-ascii filename handling
[kdelibs.git] / plasma / containment.cpp
blob91ed2b14c78f2c05c52e8787680b231d14bab9d9
1 /*
2 * Copyright 2007 by Aaron Seigo <aseigo@kde.org>
3 * Copyright 2008 by Ménard Alexis <darktears31@gmail.com>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Library General Public License as
7 * published by the Free Software Foundation; either version 2, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details
15 * You should have received a copy of the GNU Library General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include "containment.h"
22 #include "private/containment_p.h"
24 #include <QFile>
25 #include <QGraphicsSceneContextMenuEvent>
26 #include <QGraphicsView>
27 #include <QMimeData>
28 #include <QPainter>
29 #include <QStyleOptionGraphicsItem>
30 #include <QGraphicsLayout>
31 #include <QGraphicsLinearLayout>
33 #include <kaction.h>
34 #include <kapplication.h>
35 #include <kauthorized.h>
36 #include <kicon.h>
37 #include <kmenu.h>
38 #include <kmessagebox.h>
39 #include <kmimetype.h>
40 #include <krun.h>
41 #include <kservicetypetrader.h>
42 #include <kstandarddirs.h>
43 #include <ktemporaryfile.h>
44 #include <kwindowsystem.h>
46 #include "animator.h"
47 #include "context.h"
48 #include "corona.h"
49 #include "extenderitem.h"
50 #include "svg.h"
51 #include "wallpaper.h"
53 #include "private/applet_p.h"
54 #include "private/applethandle_p.h"
55 #include "private/desktoptoolbox_p.h"
56 #include "private/extenderitemmimedata_p.h"
57 #include "private/paneltoolbox_p.h"
59 namespace Plasma
62 bool ContainmentPrivate::s_positioning = false;
63 static const char defaultWallpaper[] = "image";
64 static const char defaultWallpaperMode[] = "SingleImage";
66 Containment::StyleOption::StyleOption()
67 : QStyleOptionGraphicsItem(),
68 view(0)
70 version = Version;
71 type = Type;
74 Containment::StyleOption::StyleOption(const Containment::StyleOption & other)
75 : QStyleOptionGraphicsItem(other),
76 view(other.view)
78 version = Version;
79 type = Type;
82 Containment::StyleOption::StyleOption(const QStyleOptionGraphicsItem &other)
83 : QStyleOptionGraphicsItem(other),
84 view(0)
86 version = Version;
87 type = Type;
90 Containment::Containment(QGraphicsItem *parent,
91 const QString &serviceId,
92 uint containmentId)
93 : Applet(parent, serviceId, containmentId),
94 d(new ContainmentPrivate(this))
96 // WARNING: do not access config() OR globalConfig() in this method!
97 // that requires a scene, which is not available at this point
98 setPos(0, 0);
99 setBackgroundHints(NoBackground);
100 setContainmentType(CustomContainment);
101 setHasConfigurationInterface(false);
104 Containment::Containment(QObject *parent, const QVariantList &args)
105 : Applet(parent, args),
106 d(new ContainmentPrivate(this))
108 // WARNING: do not access config() OR globalConfig() in this method!
109 // that requires a scene, which is not available at this point
110 setPos(0, 0);
111 setBackgroundHints(NoBackground);
112 setHasConfigurationInterface(false);
115 Containment::~Containment()
117 delete d;
120 void Containment::init()
122 if (!isContainment()) {
123 return;
126 setCacheMode(NoCache);
127 setFlag(QGraphicsItem::ItemIsMovable, false);
128 setFlag(QGraphicsItem::ItemClipsChildrenToShape, false);
129 setAcceptDrops(true);
130 setAcceptsHoverEvents(true);
132 //TODO: would be nice to not do this on init, as it causes Animator to init
133 connect(Animator::self(), SIGNAL(animationFinished(QGraphicsItem*,Plasma::Animator::Animation)),
134 this, SLOT(containmentAppletAnimationComplete(QGraphicsItem*,Plasma::Animator::Animation)));
136 if (d->type == NoContainmentType) {
137 setContainmentType(DesktopContainment);
140 //common actions
141 bool unlocked = immutability() == Mutable;
143 KAction *appletBrowserAction = new KAction(i18n("Add Widgets..."), this);
144 appletBrowserAction->setIcon(KIcon("list-add"));
145 appletBrowserAction->setVisible(unlocked);
146 appletBrowserAction->setEnabled(unlocked);
147 connect(appletBrowserAction, SIGNAL(triggered()), this, SLOT(triggerShowAddWidgets()));
148 appletBrowserAction->setShortcut(QKeySequence("alt+d,a"));
149 d->actions().addAction("add widgets", appletBrowserAction);
151 KAction *action = new KAction(i18n("Next Widget"), this);
152 //no icon
153 connect(action, SIGNAL(triggered()), this, SLOT(focusNextApplet()));
154 action->setShortcut(QKeySequence("alt+d,n"));
155 d->actions().addAction("next applet", action);
157 action = new KAction(i18n("Previous Widget"), this);
158 //no icon
159 connect(action, SIGNAL(triggered()), this, SLOT(focusPreviousApplet()));
160 action->setShortcut(QKeySequence("alt+d,p"));
161 d->actions().addAction("previous applet", action);
163 if (immutability() != SystemImmutable && corona()) {
164 QAction *lockDesktopAction = corona()->action("lock widgets");
165 //keep a pointer so nobody notices it moved to corona
166 if (lockDesktopAction) {
167 d->actions().addAction("lock widgets", lockDesktopAction);
171 if (d->type != PanelContainment &&
172 d->type != CustomPanelContainment) {
173 KAction *zoomAction = new KAction(i18n("Zoom In"), this);
174 zoomAction->setIcon(KIcon("zoom-in"));
175 connect(zoomAction, SIGNAL(triggered(bool)), this, SLOT(zoomIn()));
176 //two shortcuts because I hate ctrl-+ but others expect it
177 QList<QKeySequence> keys;
178 keys << QKeySequence("alt+d,+");
179 keys << QKeySequence("alt+d,=");
180 zoomAction->setShortcuts(keys);
181 d->actions().addAction("zoom in", zoomAction);
183 zoomAction = new KAction(i18n("Zoom Out"), this);
184 zoomAction->setIcon(KIcon("zoom-out"));
185 connect(zoomAction, SIGNAL(triggered(bool)), this, SLOT(zoomOut()));
186 zoomAction->setShortcut(QKeySequence("alt+d,-"));
187 d->actions().addAction("zoom out", zoomAction);
189 if (corona()) {
190 QAction *action = corona()->action("add sibling containment");
191 if (action) {
192 d->actions().addAction("add sibling containment", action);
196 if (d->type == DesktopContainment && d->toolBox) {
197 d->toolBox->addTool(this->action("add widgets"));
198 d->toolBox->addTool(this->action("zoom in"));
199 d->toolBox->addTool(this->action("zoom out"));
201 //TODO: do we need some way to allow this be overridden?
202 // it's always available because shells rely on this
203 // to offer their own custom configuration as well
204 QAction *configureContainment = this->action("configure");
205 if (configureContainment) {
206 d->toolBox->addTool(configureContainment);
210 //Set a default wallpaper the first time the containment is created,
211 //for instance from the toolbox by the user
212 if (d->drawWallpaper) {
213 setDrawWallpaper(true);
218 // helper function for sorting the list of applets
219 bool appletConfigLessThan(const KConfigGroup &c1, const KConfigGroup &c2)
221 QPointF p1 = c1.readEntry("geometry", QRectF()).topLeft();
222 QPointF p2 = c2.readEntry("geometry", QRectF()).topLeft();
224 if (!qFuzzyCompare(p1.x(), p2.x())) {
225 return p1.x() < p2.x();
228 return qFuzzyCompare(p1.y(), p2.y()) || p1.y() < p2.y();
231 void Containment::restore(KConfigGroup &group)
233 /*kDebug() << "!!!!!!!!!!!!initConstraints" << group.name() << d->type;
234 kDebug() << " location:" << group.readEntry("location", (int)d->location);
235 kDebug() << " geom:" << group.readEntry("geometry", geometry());
236 kDebug() << " formfactor:" << group.readEntry("formfactor", (int)d->formFactor);
237 kDebug() << " screen:" << group.readEntry("screen", d->screen);*/
238 if (!isContainment()) {
239 Applet::restore(group);
240 return;
243 QRectF geo = group.readEntry("geometry", geometry());
244 //override max/min
245 //this ensures panels are set to their saved size even when they have max & min set to prevent
246 //resizing
247 if (geo.size() != geo.size().boundedTo(maximumSize())) {
248 setMaximumSize(maximumSize().expandedTo(geo.size()));
250 if (geo.size() != geo.size().expandedTo(minimumSize())) {
251 setMinimumSize(minimumSize().boundedTo(geo.size()));
253 setGeometry(geo);
255 setLocation((Plasma::Location)group.readEntry("location", (int)d->location));
256 setFormFactor((Plasma::FormFactor)group.readEntry("formfactor", (int)d->formFactor));
257 setScreen(group.readEntry("screen", d->screen), group.readEntry("desktop", d->desktop));
258 setActivity(group.readEntry("activity", QString()));
260 flushPendingConstraintsEvents();
261 restoreContents(group);
262 setImmutability((ImmutabilityType)group.readEntry("immutability", (int)Mutable));
264 setWallpaper(group.readEntry("wallpaperplugin", defaultWallpaper),
265 group.readEntry("wallpaperpluginmode", defaultWallpaperMode));
267 kDebug() << "Containment" << id() <<
268 "screen" << screen() <<
269 "geometry is" << geometry() <<
270 "wallpaper" << ((d->wallpaper) ? d->wallpaper->pluginName() : QString()) <<
271 "wallpaper mode" << wallpaperMode() <<
272 "config entries" << group.entryMap();
276 void Containment::save(KConfigGroup &g) const
278 if (Applet::d->transient) {
279 return;
282 KConfigGroup group = g;
283 if (!group.isValid()) {
284 group = config();
287 // locking is saved in Applet::save
288 Applet::save(group);
289 group.writeEntry("screen", d->screen);
290 group.writeEntry("desktop", d->desktop);
291 group.writeEntry("formfactor", (int)d->formFactor);
292 group.writeEntry("location", (int)d->location);
293 group.writeEntry("activity", d->context()->currentActivity());
295 if (d->toolBox) {
296 d->toolBox->save(group);
299 if (d->wallpaper) {
300 group.writeEntry("wallpaperplugin", d->wallpaper->pluginName());
301 group.writeEntry("wallpaperpluginmode", d->wallpaper->renderingMode().name());
303 if (d->wallpaper->isInitialized()) {
304 KConfigGroup wallpaperConfig(&group, "Wallpaper");
305 wallpaperConfig = KConfigGroup(&wallpaperConfig, d->wallpaper->pluginName());
306 d->wallpaper->save(wallpaperConfig);
310 saveContents(group);
313 void Containment::saveContents(KConfigGroup &group) const
315 KConfigGroup applets(&group, "Applets");
316 foreach (const Applet *applet, d->applets) {
317 KConfigGroup appletConfig(&applets, QString::number(applet->id()));
318 applet->save(appletConfig);
322 void Containment::restoreContents(KConfigGroup &group)
324 KConfigGroup applets(&group, "Applets");
326 // Sort the applet configs in order of geometry to ensure that applets
327 // are added from left to right or top to bottom for a panel containment
328 QList<KConfigGroup> appletConfigs;
329 foreach (const QString &appletGroup, applets.groupList()) {
330 //kDebug() << "reading from applet group" << appletGroup;
331 KConfigGroup appletConfig(&applets, appletGroup);
332 appletConfigs.append(appletConfig);
334 qStableSort(appletConfigs.begin(), appletConfigs.end(), appletConfigLessThan);
336 QMutableListIterator<KConfigGroup> it(appletConfigs);
337 while (it.hasNext()) {
338 KConfigGroup &appletConfig = it.next();
339 int appId = appletConfig.name().toUInt();
340 QString plugin = appletConfig.readEntry("plugin", QString());
342 if (plugin.isEmpty()) {
343 continue;
346 Applet *applet = d->addApplet(plugin, QVariantList(),
347 appletConfig.readEntry("geometry", QRectF()),
348 appId, true);
349 applet->restore(appletConfig);
353 Containment::Type Containment::containmentType() const
355 return d->type;
358 void Containment::setContainmentType(Containment::Type type)
360 if (d->type == type) {
361 return;
364 delete d->toolBox;
365 d->toolBox = 0;
366 d->type = type;
368 if (!isContainment()) {
369 return;
372 if ((type == DesktopContainment || type == PanelContainment)) {
373 d->createToolBox();
376 d->checkRemoveAction();
379 Corona *Containment::corona() const
381 return dynamic_cast<Corona*>(scene());
384 void Containment::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
386 event->ignore();
387 if (d->wallpaper && d->wallpaper->isInitialized()) {
388 QGraphicsItem *item = scene()->itemAt(event->scenePos());
389 if (item == this) {
390 d->wallpaper->mouseMoveEvent(event);
394 if (!event->isAccepted()) {
395 event->accept();
396 Applet::mouseMoveEvent(event);
400 void Containment::mousePressEvent(QGraphicsSceneMouseEvent *event)
402 event->ignore();
403 if (d->wallpaper && d->wallpaper->isInitialized()) {
404 QGraphicsItem *item = scene()->itemAt(event->scenePos());
405 if (item == this) {
406 d->wallpaper->mousePressEvent(event);
410 if (event->isAccepted()) {
411 setFocus(Qt::MouseFocusReason);
412 } else {
413 event->accept();
414 Applet::mousePressEvent(event);
418 void Containment::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
420 event->ignore();
421 if (d->wallpaper && d->wallpaper->isInitialized()) {
422 QGraphicsItem *item = scene()->itemAt(event->scenePos());
423 if (item == this) {
424 d->wallpaper->mouseReleaseEvent(event);
428 if (!event->isAccepted()) {
429 event->accept();
430 Applet::mouseReleaseEvent(event);
434 void Containment::showDropZone(const QPoint pos)
436 Q_UNUSED(pos)
437 //Base implementation does nothing, don't put code here
440 void Containment::showContextMenu(const QPointF &containmentPos, const QPoint &screenPos)
442 d->showContextMenu(mapToScene(containmentPos), screenPos, false);
445 void Containment::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
447 //kDebug() << "let's see if we manage to get a context menu here, huh";
448 if (!isContainment() || !scene() || !KAuthorized::authorizeKAction("desktop_contextmenu")) {
449 Applet::contextMenuEvent(event);
450 return;
453 if (!d->showContextMenu(event->scenePos(), event->screenPos(), true)) {
454 Applet::contextMenuEvent(event);
455 } else {
456 event->accept();
460 void ContainmentPrivate::containmentActions(KMenu &desktopMenu)
462 if (static_cast<Corona*>(q->scene())->immutability() != Mutable &&
463 !KAuthorized::authorizeKAction("unlock_desktop")) {
464 //kDebug() << "immutability";
465 return;
468 //get base context actions
469 QList<QAction*> actions = q->contextualActions();
471 //find the separator to insert the activity settings before it
472 QAction *separatorAction = 0;
474 //TODO: should a submenu be created if there are too many containment specific
475 // actions? see folderview containment
476 foreach (QAction *action, actions) {
477 if (action) {
478 desktopMenu.addAction(action);
479 if (action->isSeparator()) {
480 separatorAction = action;
485 desktopMenu.addSeparator();
487 if (type == Containment::DesktopContainment) {
488 desktopMenu.addAction(q->action("configure"));
492 void ContainmentPrivate::appletActions(KMenu &desktopMenu, Applet *applet, bool includeApplet)
494 QList<QAction*> actions;
496 if (includeApplet) {
497 actions = applet->contextualActions();
498 if (!actions.isEmpty()) {
499 foreach (QAction *action, actions) {
500 if (action) {
501 desktopMenu.addAction(action);
507 if (applet->hasConfigurationInterface()) {
508 QAction *configureApplet = applet->d->actions.action("configure");
509 if (configureApplet) {
510 desktopMenu.addAction(configureApplet);
514 KMenu *containmentMenu = new KMenu(i18nc("%1 is the name of the containment", "%1 Options", q->name()), &desktopMenu);
515 containmentActions(*containmentMenu);
516 if (!containmentMenu->isEmpty()) {
517 int enabled = 0;
518 //count number of real actions
519 foreach(QAction *action, containmentMenu->actions()) {
520 if(action->isEnabled() && !action->isSeparator()) {
521 enabled++;
525 if (enabled > 0) {
526 desktopMenu.addSeparator();
529 //if there is only one, don't create a submenu
530 if(enabled < 2) {
531 foreach(QAction *action, containmentMenu->actions()) {
532 desktopMenu.addAction(action);
534 } else {
535 desktopMenu.addMenu(containmentMenu);
539 if (static_cast<Corona*>(q->scene())->immutability() == Mutable) {
540 if (!desktopMenu.isEmpty()) {
541 desktopMenu.addSeparator();
544 QAction *closeApplet = applet->d->actions.action("remove");
545 if (!closeApplet) { //unlikely but not impossible
546 closeApplet = new QAction(i18nc("%1 is the name of the applet", "Remove this %1", applet->name()), &desktopMenu);
547 closeApplet->setIcon(KIcon("edit-delete"));
548 QObject::connect(closeApplet, SIGNAL(triggered(bool)), applet, SLOT(destroy()));
550 desktopMenu.addAction(closeApplet);
554 bool ContainmentPrivate::showContextMenu(const QPointF &point,
555 const QPoint &screenPos, bool includeApplet)
557 Applet *applet = 0;
559 QGraphicsItem *item = q->scene()->itemAt(point);
560 if (item == q) {
561 item = 0;
564 while (item) {
565 applet = qgraphicsitem_cast<Applet*>(item);
566 if (applet && !applet->isContainment()) {
567 break;
570 // applet may have a value due to finding a containment!
571 applet = 0;
572 item = item->parentItem();
575 KMenu desktopMenu;
576 //kDebug() << "context menu event " << (QObject*)applet;
577 if (applet) {
578 appletActions(desktopMenu, applet, includeApplet);
579 } else {
580 containmentActions(desktopMenu);
583 if (!desktopMenu.isEmpty()) {
584 //kDebug() << "executing at" << screenPos;
585 desktopMenu.exec(screenPos);
586 return true;
589 return false;
592 void Containment::setFormFactor(FormFactor formFactor)
594 if (d->formFactor == formFactor) {
595 return;
598 //kDebug() << "switching FF to " << formFactor;
599 d->formFactor = formFactor;
601 if (isContainment() &&
602 (d->type == PanelContainment || d->type == CustomPanelContainment)) {
603 // we are a panel and we have chaged our orientation
604 d->positionPanel(true);
607 if (d->toolBox) {
608 if (d->formFactor == Vertical) {
609 d->toolBox->setCorner(ToolBox::Bottom);
610 //defaults to horizontal
611 } else if (QApplication::layoutDirection() == Qt::RightToLeft) {
612 d->toolBox->setCorner(ToolBox::Left);
613 } else {
614 d->toolBox->setCorner(ToolBox::Right);
618 updateConstraints(Plasma::FormFactorConstraint);
620 KConfigGroup c = config();
621 c.writeEntry("formfactor", (int)formFactor);
622 emit configNeedsSaving();
625 void Containment::setLocation(Location location)
627 if (d->location == location) {
628 return;
631 bool emitGeomChange = false;
633 if ((location == TopEdge || location == BottomEdge) &&
634 (d->location == TopEdge || d->location == BottomEdge)) {
635 emitGeomChange = true;
638 if ((location == RightEdge || location == LeftEdge) &&
639 (d->location == RightEdge || d->location == LeftEdge)) {
640 emitGeomChange = true;
643 d->location = location;
645 foreach (Applet *applet, d->applets) {
646 applet->updateConstraints(Plasma::LocationConstraint);
649 if (emitGeomChange) {
650 // our geometry on the scene will not actually change,
651 // but for the purposes of views it has
652 emit geometryChanged();
655 updateConstraints(Plasma::LocationConstraint);
657 KConfigGroup c = config();
658 c.writeEntry("location", (int)location);
659 emit configNeedsSaving();
662 void Containment::addSiblingContainment()
664 emit addSiblingContainment(this);
667 void Containment::clearApplets()
669 foreach (Applet *applet, d->applets) {
670 applet->d->cleanUpAndDelete();
673 d->applets.clear();
676 Applet *Containment::addApplet(const QString &name, const QVariantList &args,
677 const QRectF &appletGeometry)
679 return d->addApplet(name, args, appletGeometry);
682 void Containment::addApplet(Applet *applet, const QPointF &pos, bool delayInit)
684 if (!isContainment() || (!delayInit && immutability() != Mutable)) {
685 return;
688 if (!applet) {
689 kDebug() << "adding null applet!?!";
690 return;
693 if (d->applets.contains(applet)) {
694 kDebug() << "already have this applet!";
697 Containment *currentContainment = applet->containment();
699 if (d->type == PanelContainment) {
700 //panels don't want backgrounds, which is important when setting geometry
701 setBackgroundHints(NoBackground);
704 if (currentContainment && currentContainment != this) {
705 emit currentContainment->appletRemoved(applet);
706 disconnect(applet, 0, currentContainment, 0);
707 applet->removeSceneEventFilter(currentContainment);
708 KConfigGroup oldConfig = applet->config();
709 currentContainment->d->applets.removeAll(applet);
710 if (currentContainment->d->handles.contains(applet)) {
711 currentContainment->d->handles.remove(applet);
713 applet->setParentItem(this);
715 // now move the old config to the new location
716 //FIXME: this doesn't seem to get the actual main config group containing plugin=, etc
717 KConfigGroup c = config().group("Applets").group(QString::number(applet->id()));
718 oldConfig.reparent(&c);
719 applet->d->resetConfigurationObject();
721 disconnect(applet, SIGNAL(activate()), currentContainment, SIGNAL(activate()));
722 } else {
723 applet->setParentItem(this);
726 d->applets << applet;
728 connect(applet, SIGNAL(configNeedsSaving()), this, SIGNAL(configNeedsSaving()));
729 connect(applet, SIGNAL(releaseVisualFocus()), this, SIGNAL(releaseVisualFocus()));
730 connect(applet, SIGNAL(appletDestroyed(Plasma::Applet*)), this, SLOT(appletDestroyed(Plasma::Applet*)));
731 connect(applet, SIGNAL(activate()), this, SIGNAL(activate()));
733 if (pos != QPointF(-1, -1)) {
734 applet->setPos(pos);
737 if (delayInit || currentContainment) {
738 if (d->type == DesktopContainment) {
739 applet->installSceneEventFilter(this);
740 //applet->setWindowFlags(Qt::Window);
742 } else {
743 applet->init();
744 Animator::self()->animateItem(applet, Animator::AppearAnimation);
747 applet->updateConstraints(Plasma::AllConstraints);
749 if (!delayInit) {
750 applet->flushPendingConstraintsEvents();
753 emit appletAdded(applet, pos);
755 if (!currentContainment) {
756 applet->updateConstraints(Plasma::StartupCompletedConstraint);
757 if (!delayInit) {
758 applet->flushPendingConstraintsEvents();
762 if (!delayInit) {
763 applet->d->scheduleModificationNotification();
767 Applet::List Containment::applets() const
769 return d->applets;
772 void Containment::setScreen(int newScreen, int newDesktop)
774 // What we want to do in here is:
775 // * claim the screen as our own
776 // * signal whatever may be watching this containment about the switch
777 // * if we are a full screen containment, then:
778 // * resize to match the screen if we're that kind of containment
779 // * kick other full-screen containments off this screen
780 // * if we had a screen, then give our screen to the containment
781 // we kick out
783 // a screen of -1 means no associated screen.
784 Q_ASSERT(corona());
785 int numScreens = corona()->numScreens();
786 if (newScreen < -1) {
787 newScreen = -1;
790 // -1 == All desktops
791 if (newDesktop < -1 || newDesktop > KWindowSystem::numberOfDesktops() - 1) {
792 newDesktop = -1;
795 kDebug() << "setting screen to " << newScreen << newDesktop << "and type is" << d->type;
797 Containment *swapScreensWith(0);
798 if (d->type == DesktopContainment || d->type >= CustomContainment) {
799 // we want to listen to changes in work area if our screen changes
800 if (d->screen < 0 && newScreen > -1) {
801 connect(KWindowSystem::self(), SIGNAL(workAreaChanged()), this, SLOT(positionToolBox()));
802 } else if (newScreen < 0) {
803 disconnect(KWindowSystem::self(), SIGNAL(workAreaChanged()), this, SLOT(positionToolBox()));
806 if (newScreen > -1 && corona()) {
807 // sanity check to make sure someone else doesn't have this screen already!
808 Containment *currently = corona()->containmentForScreen(newScreen, newDesktop);
809 if (currently && currently != this) {
810 kDebug() << "currently is on screen" << currently->screen()
811 << "and is" << currently->name()
812 << (QObject*)currently << (QObject*)this;
813 currently->setScreen(-1, newDesktop);
814 swapScreensWith = currently;
819 if (newScreen < numScreens && newScreen > -1) {
820 if (d->type == DesktopContainment ||
821 d->type >= CustomContainment) {
822 resize(corona()->screenGeometry(newScreen).size());
826 int oldDesktop = d->desktop;
827 d->desktop = newDesktop;
829 int oldScreen = d->screen;
830 d->screen = newScreen;
832 updateConstraints(Plasma::ScreenConstraint);
834 if (oldScreen != newScreen) {
835 emit screenChanged(oldScreen, newScreen, this);
837 KConfigGroup c = config();
838 c.writeEntry("screen", d->screen);
839 emit configNeedsSaving();
842 if (swapScreensWith) {
843 swapScreensWith->setScreen(oldScreen, oldDesktop);
846 d->checkRemoveAction();
849 int Containment::screen() const
851 return d->screen;
854 int Containment::desktop() const
856 return d->desktop;
859 KPluginInfo::List Containment::listContainments(const QString &category,
860 const QString &parentApp)
862 QString constraint;
864 if (parentApp.isEmpty()) {
865 constraint.append("not exist [X-KDE-ParentApp]");
866 } else {
867 constraint.append("[X-KDE-ParentApp] == '").append(parentApp).append("'");
870 if (!category.isEmpty()) {
871 if (!constraint.isEmpty()) {
872 constraint.append(" and ");
875 constraint.append("[X-KDE-PluginInfo-Category] == '").append(category).append("'");
876 if (category == "Miscellaneous") {
877 constraint.append(" or (not exist [X-KDE-PluginInfo-Category] or [X-KDE-PluginInfo-Category] == '')");
881 KService::List offers = KServiceTypeTrader::self()->query("Plasma/Containment", constraint);
882 //kDebug() << "constraint was" << constraint << "which got us" << offers.count() << "matches";
883 return KPluginInfo::fromServices(offers);
886 KPluginInfo::List Containment::listContainmentsForMimetype(const QString &mimetype)
888 QString constraint = QString("'%1' in [X-Plasma-DropMimeTypes]").arg(mimetype);
889 //kDebug() << mimetype << constraint;
890 KService::List offers = KServiceTypeTrader::self()->query("Plasma/Containment", constraint);
891 return KPluginInfo::fromServices(offers);
894 void Containment::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
896 //kDebug() << immutability() << Mutable << (immutability() == Mutable);
897 event->setAccepted(immutability() == Mutable &&
898 (event->mimeData()->hasFormat(static_cast<Corona*>(scene())->appletMimeType()) ||
899 KUrl::List::canDecode(event->mimeData()) ||
900 event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())));
902 if (!event->isAccepted()) {
903 // check to see if we have an applet that accepts the format.
904 QStringList formats = event->mimeData()->formats();
906 foreach (const QString &format, formats) {
907 KPluginInfo::List appletList = Applet::listAppletInfoForMimetype(format);
908 if (!appletList.isEmpty()) {
909 event->setAccepted(true);
910 break;
915 if (event->isAccepted() && view()) {
916 showDropZone(view()->mapFromScene(event->scenePos()));
920 void Containment::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
922 QGraphicsItem *item = scene()->itemAt(event->scenePos());
923 event->setAccepted(item == this || !item);
924 Plasma::Containment *c = containment();
925 if (c && c->immutability() == Plasma::Mutable &&
926 (event->mimeData()->hasFormat(static_cast<Plasma::Corona*>(scene())->appletMimeType()) ||
927 KUrl::List::canDecode(event->mimeData())) && view()) {
928 showDropZone(view()->mapFromScene(event->scenePos()));
932 void Containment::dropEvent(QGraphicsSceneDragDropEvent *event)
934 //kDebug() << event->mimeData()->text();
935 if (!isContainment()) {
936 Applet::dropEvent(event);
937 return;
940 QString mimetype(static_cast<Corona*>(scene())->appletMimeType());
942 if (event->mimeData()->hasFormat(mimetype) && scene()) {
943 QString data = event->mimeData()->data(mimetype);
944 QStringList appletNames = data.split('\n', QString::SkipEmptyParts);
946 foreach (const QString &appletName, appletNames) {
947 //kDebug() << "doing" << appletName;
948 QRectF geom(mapFromScene(event->scenePos()), QSize(0, 0));
949 addApplet(appletName, QVariantList(), geom);
951 event->acceptProposedAction();
952 } else if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) {
953 kDebug() << "mimetype plasma/extenderitem is dropped, creating internal:extender";
954 //Handle dropping extenderitems.
955 const ExtenderItemMimeData *mimeData = qobject_cast<const ExtenderItemMimeData*>(event->mimeData());
956 if (mimeData) {
957 ExtenderItem *item = mimeData->extenderItem();
958 QRectF geometry(event->pos(), item->size());
959 kDebug() << "desired geometry: " << geometry;
960 Applet *applet = addApplet("internal:extender", QVariantList(), geometry);
961 item->setExtender(applet->extender());
963 } else if (KUrl::List::canDecode(event->mimeData())) {
964 //TODO: collect the mimetypes of available script engines and offer
965 // to create widgets out of the matching URLs, if any
966 KUrl::List urls = KUrl::List::fromMimeData(event->mimeData());
967 foreach (const KUrl &url, urls) {
968 KMimeType::Ptr mime = KMimeType::findByUrl(url);
969 QString mimeName = mime->name();
970 QRectF geom(event->pos(), QSize());
971 QVariantList args;
972 args << url.url();
973 // kDebug() << mimeName;
974 KPluginInfo::List appletList = Applet::listAppletInfoForMimetype(mimeName);
976 if (!appletList.isEmpty()) {
977 //TODO: should we show a dialog here to choose which plasmoid load if
978 //!appletList.isEmpty()
979 QMenu choices;
980 QHash<QAction *, QString> actionsToPlugins;
981 foreach (const KPluginInfo &info, appletList) {
982 QAction *action;
983 if (!info.icon().isEmpty()) {
984 action = choices.addAction(KIcon(info.icon()), info.name());
985 } else {
986 action = choices.addAction(info.name());
989 actionsToPlugins.insert(action, info.pluginName());
992 actionsToPlugins.insert(choices.addAction(i18n("Icon")), "icon");
993 QAction *choice = choices.exec(event->screenPos());
994 if (choice) {
995 addApplet(actionsToPlugins[choice], args, geom);
997 } else if (url.protocol() != "data") {
998 // We don't try to do anything with data: URIs
999 // no special applet associated with this mimetype, let's
1000 addApplet("icon", args, geom);
1003 event->acceptProposedAction();
1004 } else {
1005 QStringList formats = event->mimeData()->formats();
1006 QHash<QString, KPluginInfo> seenPlugins;
1007 QHash<QString, QString> pluginFormats;
1009 foreach (const QString &format, formats) {
1010 KPluginInfo::List plugins = Applet::listAppletInfoForMimetype(format);
1012 foreach (const KPluginInfo &plugin, plugins) {
1013 if (seenPlugins.contains(plugin.pluginName())) {
1014 continue;
1017 seenPlugins.insert(plugin.pluginName(), plugin);
1018 pluginFormats.insert(plugin.pluginName(), format);
1022 QString selectedPlugin;
1024 if (seenPlugins.isEmpty()) {
1025 // do nothing, we have no matches =/
1028 if (seenPlugins.count() == 1) {
1029 selectedPlugin = seenPlugins.constBegin().key();
1030 } else {
1031 QMenu choices;
1032 QHash<QAction *, QString> actionsToPlugins;
1033 foreach (const KPluginInfo &info, seenPlugins) {
1034 QAction *action;
1035 if (!info.icon().isEmpty()) {
1036 action = choices.addAction(KIcon(info.icon()), info.name());
1037 } else {
1038 action = choices.addAction(info.name());
1041 actionsToPlugins.insert(action, info.pluginName());
1044 QAction *choice = choices.exec(event->screenPos());
1045 if (choice) {
1046 selectedPlugin = actionsToPlugins[choice];
1050 if (!selectedPlugin.isEmpty()) {
1051 KTemporaryFile tempFile;
1052 if (tempFile.open()) {
1053 //TODO: what should we do with files after the applet is done with them??
1054 tempFile.setAutoRemove(false);
1057 QDataStream stream(&tempFile);
1058 QByteArray data = event->mimeData()->data(pluginFormats[selectedPlugin]);
1059 stream.writeRawData(data, data.size());
1062 QRectF geom(event->pos(), QSize());
1063 QVariantList args;
1064 args << tempFile.fileName();
1065 kDebug() << args;
1066 tempFile.close();
1068 addApplet(selectedPlugin, args, geom);
1074 const QGraphicsItem *Containment::toolBoxItem() const
1076 return d->toolBox;
1079 void Containment::resizeEvent(QGraphicsSceneResizeEvent *event)
1081 Applet::resizeEvent(event);
1083 if (!ContainmentPrivate::s_positioning) {
1084 switch (d->type) {
1085 case Containment::PanelContainment:
1086 case Containment::CustomPanelContainment:
1087 d->positionPanel();
1088 break;
1089 default:
1090 d->positionContainments();
1091 break;
1095 if (d->wallpaper) {
1096 d->wallpaper->setBoundingRect(boundingRect());
1100 void Containment::keyPressEvent(QKeyEvent *event)
1102 //kDebug() << "keyPressEvent with" << event->key()
1103 // << "and hoping and wishing for a" << Qt::Key_Tab;
1104 if (event->key() == Qt::Key_Tab) { // && event->modifiers() == 0) {
1105 if (!d->applets.isEmpty()) {
1106 kDebug() << "let's give focus to...." << (QObject*)d->applets.first();
1107 d->applets.first()->setFocus(Qt::TabFocusReason);
1112 void Containment::wheelEvent(QGraphicsSceneWheelEvent *event)
1114 if (d->wallpaper && d->wallpaper->isInitialized()) {
1115 QGraphicsItem *item = scene()->itemAt(event->scenePos());
1116 if (item == this) {
1117 event->ignore();
1118 d->wallpaper->wheelEvent(event);
1120 if (event->isAccepted()) {
1121 return;
1124 event->accept();
1128 if (d->type == DesktopContainment) {
1129 QGraphicsItem *item = scene()->itemAt(event->scenePos());
1130 if (item == this) {
1131 int numDesktops = KWindowSystem::numberOfDesktops();
1132 int currentDesktop = KWindowSystem::currentDesktop();
1134 if (event->delta() < 0) {
1135 KWindowSystem::setCurrentDesktop(currentDesktop % numDesktops + 1);
1136 } else {
1137 KWindowSystem::setCurrentDesktop((numDesktops + currentDesktop - 2) % numDesktops + 1);
1140 event->accept();
1141 return;
1145 event->ignore();
1146 Applet::wheelEvent(event);
1149 bool Containment::sceneEventFilter(QGraphicsItem *watched, QEvent *event)
1151 Applet *applet = qgraphicsitem_cast<Applet*>(watched);
1153 // Otherwise we're watching something we shouldn't be...
1154 Q_ASSERT(applet != 0);
1155 if (!d->applets.contains(applet)) {
1156 return false;
1159 //kDebug() << "got sceneEvent";
1160 switch (event->type()) {
1161 case QEvent::GraphicsSceneHoverEnter:
1162 //kDebug() << "got hoverenterEvent" << immutability() << " " << applet->immutability();
1163 if (immutability() == Mutable && applet->immutability() == Mutable) {
1164 QGraphicsSceneHoverEvent *he = static_cast<QGraphicsSceneHoverEvent*>(event);
1165 if (d->handles.contains(applet)) {
1166 AppletHandle *handle = d->handles.value(applet);
1167 if (handle) {
1168 handle->setHoverPos(he->pos());
1170 } else {
1171 //kDebug() << "generated applet handle";
1172 AppletHandle *handle = new AppletHandle(this, applet, he->pos());
1173 d->handles[applet] = handle;
1174 connect(handle, SIGNAL(disappearDone(AppletHandle*)),
1175 this, SLOT(handleDisappeared(AppletHandle*)));
1176 connect(applet, SIGNAL(geometryChanged()),
1177 handle, SLOT(appletResized()));
1180 break;
1181 case QEvent::GraphicsSceneHoverMove:
1182 if (immutability() == Mutable && applet->immutability() == Mutable) {
1183 QGraphicsSceneHoverEvent *he = static_cast<QGraphicsSceneHoverEvent*>(event);
1184 if (d->handles.contains(applet)) {
1185 AppletHandle *handle = d->handles.value(applet);
1186 if (handle) {
1187 handle->setHoverPos(he->pos());
1191 break;
1192 default:
1193 break;
1196 return false;
1199 QVariant Containment::itemChange(GraphicsItemChange change, const QVariant &value)
1201 //FIXME if the applet is moved to another containment we need to unfocus it
1203 if (isContainment() && !ContainmentPrivate::s_positioning &&
1204 (change == QGraphicsItem::ItemSceneHasChanged || change == QGraphicsItem::ItemPositionHasChanged)) {
1205 switch (d->type) {
1206 case PanelContainment:
1207 case CustomPanelContainment:
1208 d->positionPanel();
1209 break;
1210 default:
1211 d->positionContainments();
1212 break;
1216 return Applet::itemChange(change, value);
1219 void Containment::enableAction(const QString &name, bool enable)
1221 QAction *action = this->action(name);
1222 if (action) {
1223 action->setEnabled(enable);
1224 action->setVisible(enable);
1228 void Containment::addToolBoxAction(QAction *action)
1230 if (!d->toolBox && (d->type == CustomPanelContainment || d->type >= CustomContainment)) {
1231 d->createToolBox();
1234 if (d->toolBox) {
1235 d->toolBox->addTool(action);
1239 void Containment::removeToolBoxAction(QAction *action)
1241 if (d->toolBox) {
1242 d->toolBox->removeTool(action);
1246 void Containment::setToolBoxOpen(bool open)
1248 if (open) {
1249 openToolBox();
1250 } else {
1251 closeToolBox();
1255 void Containment::openToolBox()
1257 if (d->toolBox) {
1258 d->toolBox->showToolBox();
1262 void Containment::closeToolBox()
1264 if (d->toolBox) {
1265 d->toolBox->hideToolBox();
1269 void Containment::addAssociatedWidget(QWidget *widget)
1271 Applet::addAssociatedWidget(widget);
1272 if (d->focusedApplet) {
1273 d->focusedApplet->addAssociatedWidget(widget);
1276 foreach (const Applet *applet, d->applets) {
1277 if (applet->d->activationAction) {
1278 widget->addAction(applet->d->activationAction);
1283 void Containment::removeAssociatedWidget(QWidget *widget)
1285 Applet::removeAssociatedWidget(widget);
1286 if (d->focusedApplet) {
1287 d->focusedApplet->removeAssociatedWidget(widget);
1290 foreach (const Applet *applet, d->applets) {
1291 if (applet->d->activationAction) {
1292 widget->removeAction(applet->d->activationAction);
1297 void Containment::setDrawWallpaper(bool drawWallpaper)
1299 d->drawWallpaper = drawWallpaper;
1300 if (drawWallpaper) {
1301 KConfigGroup cfg = config();
1302 QString wallpaper = cfg.readEntry("wallpaperplugin", defaultWallpaper);
1303 QString mode = cfg.readEntry("wallpaperpluginmode", defaultWallpaperMode);
1304 setWallpaper(wallpaper, mode);
1305 } else {
1306 delete d->wallpaper;
1307 d->wallpaper = 0;
1311 bool Containment::drawWallpaper()
1313 return d->drawWallpaper;
1316 void Containment::setWallpaper(const QString &pluginName, const QString &mode)
1318 KConfigGroup cfg = config();
1319 bool newPlugin = true;
1320 bool newMode = true;
1322 if (d->drawWallpaper) {
1323 if (d->wallpaper) {
1324 // we have a wallpaper, so let's decide whether we need to swap it out
1325 if (d->wallpaper->pluginName() != pluginName) {
1326 delete d->wallpaper;
1327 d->wallpaper = 0;
1328 } else {
1329 // it's the same plugin, so let's save its state now so when
1330 // we call restore later on we're safe
1331 newMode = d->wallpaper->renderingMode().name() != mode;
1332 newPlugin = false;
1336 if (!pluginName.isEmpty() && !d->wallpaper) {
1337 d->wallpaper = Plasma::Wallpaper::load(pluginName);
1340 if (d->wallpaper) {
1341 d->wallpaper->setBoundingRect(boundingRect());
1342 d->wallpaper->setRenderingMode(mode);
1344 if (newPlugin) {
1345 connect(d->wallpaper, SIGNAL(update(const QRectF&)),
1346 this, SLOT(updateRect(const QRectF&)));
1347 cfg.writeEntry("wallpaperplugin", pluginName);
1350 if (d->wallpaper->isInitialized()) {
1351 KConfigGroup wallpaperConfig = KConfigGroup(&cfg, "Wallpaper");
1352 wallpaperConfig = KConfigGroup(&wallpaperConfig, pluginName);
1353 d->wallpaper->restore(wallpaperConfig);
1356 if (newMode) {
1357 cfg.writeEntry("wallpaperpluginmode", mode);
1361 update();
1364 if (!d->wallpaper) {
1365 cfg.deleteEntry("wallpaperplugin");
1366 cfg.deleteEntry("wallpaperpluginmode");
1369 if (newPlugin || newMode) {
1370 emit configNeedsSaving();
1374 Plasma::Wallpaper *Containment::wallpaper() const
1376 return d->wallpaper;
1379 void Containment::setActivity(const QString &activity)
1381 Context *context = d->context();
1382 if (context->currentActivity() != activity) {
1383 context->setCurrentActivity(activity);
1385 foreach (Applet *a, d->applets) {
1386 a->updateConstraints(ContextConstraint);
1389 KConfigGroup c = config();
1390 c.writeEntry("activity", activity);
1391 emit configNeedsSaving();
1395 QString Containment::activity() const
1397 return d->context()->currentActivity();
1400 Context *ContainmentPrivate::context()
1402 if (!con) {
1403 con = new Context(q);
1404 q->connect(con, SIGNAL(changed(Plasma::Context*)),
1405 q, SIGNAL(contextChanged(Plasma::Context*)));
1408 return con;
1411 KActionCollection &ContainmentPrivate::actions()
1413 return static_cast<Applet*>(q)->d->actions;
1416 void ContainmentPrivate::focusApplet(Plasma::Applet *applet)
1418 if (focusedApplet == applet) {
1419 return;
1422 QList<QWidget *> widgets = actions().associatedWidgets();
1423 if (focusedApplet) {
1424 foreach (QWidget *w, widgets) {
1425 focusedApplet->removeAssociatedWidget(w);
1429 if (applet && applets.contains(applet)) {
1430 //kDebug() << "switching to" << applet->name();
1431 focusedApplet = applet;
1432 foreach (QWidget *w, widgets) {
1433 focusedApplet->addAssociatedWidget(w);
1436 if (!focusedApplet->hasFocus()) {
1437 focusedApplet->setFocus(Qt::ShortcutFocusReason);
1439 } else {
1440 focusedApplet = 0;
1444 void Containment::focusNextApplet()
1446 if (d->applets.isEmpty()) {
1447 return;
1449 int index = d->focusedApplet ? d->applets.indexOf(d->focusedApplet) + 1 : 0;
1450 if (index >= d->applets.size()) {
1451 index = 0;
1453 kDebug() << "index" << index;
1454 d->focusApplet(d->applets.at(index));
1457 void Containment::focusPreviousApplet()
1459 if (d->applets.isEmpty()) {
1460 return;
1462 int index = d->focusedApplet ? d->applets.indexOf(d->focusedApplet) - 1 : -1;
1463 if (index < 0) {
1464 index = d->applets.size() - 1;
1466 kDebug() << "index" << index;
1467 d->focusApplet(d->applets.at(index));
1470 void Containment::destroy()
1472 destroy(true);
1475 void Containment::showConfigurationInterface()
1477 Applet::showConfigurationInterface();
1480 void ContainmentPrivate::requestConfiguration()
1482 emit q->configureRequested(q);
1485 void Containment::destroy(bool confirm)
1487 if (immutability() != Mutable) {
1488 return;
1491 if (isContainment()) {
1492 //don't remove a desktop that's in use
1493 //FIXME: this should probably be based on whether any views care or not!
1494 // sth like: foreach (view) { view->requires(this); }
1495 Q_ASSERT(corona());
1496 if (d->type != PanelContainment && d->type != CustomPanelContainment &&
1497 (d->screen != -1 || d->screen >= corona()->numScreens())) {
1498 kDebug() << (QObject*)this << "containment has a screen number?" << d->screen;
1499 return;
1502 //FIXME maybe that %1 should be the containment type not the name
1503 if (!confirm ||
1504 KMessageBox::warningContinueCancel(
1505 view(),
1506 i18nc("%1 is the name of the containment", "Do you really want to remove this %1?", name()),
1507 i18nc("@title:window %1 is the name of the containment", "Remove %1", name()), KStandardGuiItem::remove()) == KMessageBox::Continue) {
1508 //clearApplets();
1509 Applet::destroy();
1511 } else {
1512 Applet::destroy();
1516 void ContainmentPrivate::zoomIn()
1518 emit q->zoomRequested(q, Plasma::ZoomIn);
1519 positionToolBox();
1522 void ContainmentPrivate::zoomOut()
1524 emit q->zoomRequested(q, Plasma::ZoomOut);
1525 positionToolBox();
1528 ToolBox *ContainmentPrivate::createToolBox()
1530 if (!toolBox) {
1531 switch (type) {
1532 case Containment::PanelContainment:
1533 case Containment::CustomPanelContainment:
1534 toolBox = new PanelToolBox(q);
1535 toolBox->setSize(KIconLoader::SizeSmallMedium);
1536 toolBox->setIconSize(QSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall));
1537 if (q->immutability() != Mutable) {
1538 toolBox->hide();
1540 break;
1541 default:
1542 toolBox = new DesktopToolBox(q);
1543 toolBox->setSize(KIconLoader::SizeSmallMedium);
1544 toolBox->setIconSize(QSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall));
1545 break;
1548 if (toolBox) {
1549 QObject::connect(toolBox, SIGNAL(toggled()), q, SIGNAL(toolBoxToggled()));
1550 toolBox->load();
1551 positionToolBox();
1555 return toolBox;
1558 void ContainmentPrivate::positionToolBox()
1560 if (toolBox) {
1561 toolBox->reposition();
1565 void ContainmentPrivate::triggerShowAddWidgets()
1567 emit q->showAddWidgetsInterface(QPointF());
1570 void ContainmentPrivate::handleDisappeared(AppletHandle *handle)
1572 if (handles.contains(handle->applet())) {
1573 handles.remove(handle->applet());
1574 handle->detachApplet();
1575 handle->deleteLater();
1579 void ContainmentPrivate::checkRemoveAction()
1581 q->enableAction("remove", (q->immutability() == Mutable &&
1582 (screen == -1 ||
1583 type == Plasma::Containment::PanelContainment ||
1584 type == Plasma::Containment::CustomPanelContainment)));
1587 void ContainmentPrivate::containmentConstraintsEvent(Plasma::Constraints constraints)
1589 if (!q->isContainment()) {
1590 return;
1593 //kDebug() << "got containmentConstraintsEvent" << constraints << (QObject*)toolBox;
1594 if (constraints & Plasma::ImmutableConstraint) {
1595 //update actions
1596 checkRemoveAction();
1597 bool unlocked = q->immutability() == Mutable;
1598 q->setAcceptDrops(unlocked);
1599 q->enableAction("add widgets", unlocked);
1601 // tell the applets too
1602 foreach (Applet *a, applets) {
1603 a->updateConstraints(ImmutableConstraint);
1606 if (toolBox) {
1607 if (type == Containment::PanelContainment || type == Containment::CustomPanelContainment) {
1608 toolBox->setVisible(unlocked);
1609 } else {
1610 toolBox->setIsMovable(unlocked);
1614 //clear handles on lock
1615 if (!unlocked) {
1616 QMap<Applet*, AppletHandle*> h = handles;
1617 handles.clear();
1619 foreach (AppletHandle *handle, h) {
1620 handle->disconnect(q);
1621 handle->deleteLater();
1626 if (constraints & Plasma::FormFactorConstraint) {
1627 foreach (Applet *applet, applets) {
1628 applet->updateConstraints(Plasma::FormFactorConstraint);
1632 if (toolBox && (constraints & Plasma::SizeConstraint ||
1633 constraints & Plasma::FormFactorConstraint ||
1634 constraints & Plasma::ScreenConstraint ||
1635 constraints & Plasma::StartupCompletedConstraint)) {
1636 //kDebug() << "Positioning toolbox";
1637 positionToolBox();
1640 if (toolBox &&
1641 constraints & Plasma::StartupCompletedConstraint &&
1642 type < Containment::CustomContainment) {
1643 toolBox->addTool(q->action("remove"));
1644 checkRemoveAction();
1648 Applet *ContainmentPrivate::addApplet(const QString &name, const QVariantList &args,
1649 const QRectF &appletGeometry, uint id, bool delayInit)
1651 if (!q->isContainment()) {
1652 return 0;
1655 if (!delayInit && q->immutability() != Mutable) {
1656 kDebug() << "addApplet for" << name << "requested, but we're currently immutable!";
1657 return 0;
1660 QGraphicsView *v = q->view();
1661 if (v) {
1662 v->setCursor(Qt::BusyCursor);
1665 Applet *applet = Applet::load(name, id, args);
1666 if (v) {
1667 v->unsetCursor();
1670 if (!applet) {
1671 kDebug() << "Applet" << name << "could not be loaded.";
1672 applet = new Applet(0, QString(), id);
1673 applet->setFailedToLaunch(true, i18n("Could not find requested component: %1", name));
1676 //kDebug() << applet->name() << "sizehint:" << applet->sizeHint() << "geometry:" << applet->geometry();
1678 q->addApplet(applet, appletGeometry.topLeft(), delayInit);
1679 return applet;
1682 bool ContainmentPrivate::regionIsEmpty(const QRectF &region, Applet *ignoredApplet) const
1684 foreach (Applet *applet, applets) {
1685 if (applet != ignoredApplet && applet->geometry().intersects(region)) {
1686 return false;
1689 return true;
1692 void ContainmentPrivate::appletDestroyed(Plasma::Applet *applet)
1694 applets.removeAll(applet);
1695 if (focusedApplet == applet) {
1696 focusedApplet = 0;
1699 if (handles.contains(applet)) {
1700 AppletHandle *handle = handles.value(applet);
1701 handles.remove(applet);
1702 handle->deleteLater();
1705 emit q->appletRemoved(applet);
1706 emit q->configNeedsSaving();
1709 void ContainmentPrivate::containmentAppletAnimationComplete(QGraphicsItem *item, Plasma::Animator::Animation anim)
1711 if (anim == Animator::AppearAnimation &&
1712 item->parentItem() == q) {
1713 Applet *applet = qgraphicsitem_cast<Applet*>(item);
1715 if (applet) {
1716 if (type == Containment::DesktopContainment) {
1717 applet->installSceneEventFilter(q);
1720 KConfigGroup *cg = applet->d->mainConfigGroup();
1721 applet->save(*cg);
1722 emit q->configNeedsSaving();
1723 //applet->setWindowFlags(Qt::Window);
1728 bool containmentSortByPosition(const Containment *c1, const Containment *c2)
1730 return c1->id() < c2->id();
1733 void ContainmentPrivate::positionContainments()
1735 Corona *c = q->corona();
1736 if (!c || ContainmentPrivate::s_positioning) {
1737 return;
1740 ContainmentPrivate::s_positioning = true;
1742 //TODO: we should avoid running this too often; consider compressing requests
1743 // with a timer.
1744 QList<Containment*> containments = c->containments();
1745 QMutableListIterator<Containment*> it(containments);
1747 while (it.hasNext()) {
1748 Containment *containment = it.next();
1749 if (containment->d->type == Containment::PanelContainment ||
1750 containment->d->type == Containment::CustomPanelContainment) {
1751 // weed out all containments we don't care about at all
1752 // e.g. Panels and ourself
1753 it.remove();
1754 continue;
1758 if (containments.isEmpty()) {
1759 ContainmentPrivate::s_positioning = false;
1760 return;
1763 qSort(containments.begin(), containments.end(), containmentSortByPosition);
1764 it.toFront();
1766 int column = 0;
1767 int x = 0;
1768 int y = 0;
1769 int rowHeight = 0;
1770 //int count = 0;
1772 //kDebug() << "+++++++++++++++++++++++++++++++++++++++++++++++++++" << containments.count();
1773 while (it.hasNext()) {
1774 Containment *containment = it.next();
1775 containment->setPos(x, y);
1776 //kDebug() << ++count << "setting to" << x << y;
1778 int height = containment->size().height();
1779 if (height > rowHeight) {
1780 rowHeight = height;
1783 ++column;
1785 if (column == CONTAINMENT_COLUMNS) {
1786 column = 0;
1787 x = 0;
1788 y += rowHeight + INTER_CONTAINMENT_MARGIN + TOOLBOX_MARGIN;
1789 rowHeight = 0;
1790 } else {
1791 x += containment->size().width() + INTER_CONTAINMENT_MARGIN;
1793 //kDebug() << "column: " << column << "; x " << x << "; y" << y << "; width was"
1794 // << containment->size().width();
1796 //kDebug() << "+++++++++++++++++++++++++++++++++++++++++++++++++++";
1798 ContainmentPrivate::s_positioning = false;
1801 void ContainmentPrivate::positionPanel(bool force)
1803 if (!q->scene()) {
1804 kDebug() << "no scene yet";
1805 return;
1808 // we position panels in negative coordinates, and stack all horizontal
1809 // and all vertical panels with each other.
1812 const QPointF p = q->pos();
1814 if (!force &&
1815 p.y() + q->size().height() < -INTER_CONTAINMENT_MARGIN &&
1816 q->scene()->collidingItems(q).isEmpty()) {
1817 // already positioned and not running into any other panels
1818 return;
1821 //TODO: research how non-Horizontal, non-Vertical (e.g. Planar) panels behave here
1822 bool horiz = formFactor == Plasma::Horizontal;
1823 qreal bottom = horiz ? 0 : VERTICAL_STACKING_OFFSET;
1824 qreal lastHeight = 0;
1826 // this should be ok for small numbers of panels, but if we ever end
1827 // up managing hundreds of them, this simplistic alogrithm will
1828 // likely be too slow.
1829 foreach (const Containment *other, q->corona()->containments()) {
1830 if (other == q ||
1831 (other->d->type != Containment::PanelContainment &&
1832 other->d->type != Containment::CustomPanelContainment) ||
1833 horiz != (other->formFactor() == Plasma::Horizontal)) {
1834 // only line up with panels of the same orientation
1835 continue;
1838 if (horiz) {
1839 qreal y = other->pos().y();
1840 if (y < bottom) {
1841 lastHeight = other->size().height();
1842 bottom = y;
1844 } else {
1845 qreal width = other->size().width();
1846 qreal x = other->pos().x() + width;
1847 if (x > bottom) {
1848 lastHeight = width;
1849 bottom = x + lastHeight;
1854 kDebug() << "positioning" << (horiz ? "" : "non-") << "horizontal panel; forced?" << force;
1855 // give a space equal to the height again of the last item so there is
1856 // room to grow.
1857 QPointF newPos;
1858 if (horiz) {
1859 bottom -= lastHeight + INTER_CONTAINMENT_MARGIN;
1860 //TODO: fix x position for non-flush-left panels
1861 kDebug() << "moved to" << QPointF(0, bottom - q->size().height());
1862 newPos = QPointF(0, bottom - q->size().height());
1863 } else {
1864 bottom += lastHeight + INTER_CONTAINMENT_MARGIN;
1865 //TODO: fix y position for non-flush-top panels
1866 kDebug() << "moved to" << QPointF(bottom + q->size().width(), -INTER_CONTAINMENT_MARGIN - q->size().height());
1867 newPos = QPointF(bottom + q->size().width(), -INTER_CONTAINMENT_MARGIN - q->size().height());
1870 if (p != newPos) {
1871 ContainmentPrivate::s_positioning = true;
1872 q->setPos(newPos);
1873 ContainmentPrivate::s_positioning = false;
1877 } // Plasma namespace
1879 #include "containment.moc"