23 #include "private/containment_p.h"
25 #include "config-plasma.h"
27 #include <QApplication>
30 #include <QGraphicsSceneContextMenuEvent>
31 #include <QGraphicsView>
34 #include <QStyleOptionGraphicsItem>
35 #include <QGraphicsLayout>
36 #include <QGraphicsLinearLayout>
39 #include <kauthorized.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>
50 #include "kio/jobclasses.h"
52 #include "kio/scheduler.h"
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"
81 bool ContainmentPrivate::s_positioningPanels =
false;
82 QHash<QString, ContainmentActions*> ContainmentPrivate::globalActionPlugins;
111 const QString &serviceId,
113 :
Applet(parent, serviceId, containmentId),
126 d(new ContainmentPrivate(this))
136 : Plasma::
Applet(packagePath, appletId, args),
137 d(new ContainmentPrivate(this))
151 Applet::d->isContainment =
false;
162 setFlag(QGraphicsItem::ItemIsMovable,
false);
163 setFlag(QGraphicsItem::ItemClipsChildrenToShape,
true);
164 setAcceptDrops(
true);
165 setAcceptsHoverEvents(
true);
172 ContainmentPrivate::addDefaultActions(d->actions(),
this);
177 QAction *closeApplet =
action(
"remove");
179 closeApplet->setText(i18nc(
"%1 is the name of the applet",
"Remove this %1",
name()));
182 QAction *configAction =
action(
"configure");
184 configAction->setText(i18nc(
"%1 is the name of the applet",
"%1 Settings",
name()));
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()));
194 QAction *act =
action(
"next applet");
199 act =
action(
"previous applet");
205 QAction *lockDesktopAction =
corona()->
action(
"lock widgets");
207 if (lockDesktopAction) {
208 d->actions()->addAction(
"lock widgets", lockDesktopAction);
216 d->actions()->addAction(
"manage activities", act);
221 d->actions()->addAction(
"configure shortcuts", act);
231 QAction *configureContainment =
action(
"configure");
232 if (configureContainment) {
239 void ContainmentPrivate::addDefaultActions(KActionCollection *actions,
Containment *c)
241 actions->setConfigGroup(
"Shortcuts-Containment");
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"));
249 appAction->setText(i18n(
"Remove this Activity"));
252 appAction = qobject_cast<KAction*>(actions->action(
"configure"));
254 appAction->setShortcut(KShortcut(
"alt+d, alt+s"));
255 appAction->setText(i18n(
"Activity Settings"));
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"));
266 KAction *action = actions->addAction(
"next applet");
267 action->setText(i18n(
"Next Widget"));
269 action->setShortcut(KShortcut(
"alt+d, n"));
272 action = actions->addAction(
"previous applet");
273 action->setText(i18n(
"Previous Widget"));
275 action->setShortcut(KShortcut(
"alt+d, p"));
282 QPointF p1 = c1.readEntry(
"geometry", QRectF()).topLeft();
283 QPointF p2 = c2.readEntry(
"geometry", QRectF()).topLeft();
285 if (!qFuzzyCompare(p1.x(), p2.x())) {
286 if (QApplication::layoutDirection() == Qt::RightToLeft) {
287 return p1.x() > p2.x();
290 return p1.x() < p2.x();
293 return qFuzzyCompare(p1.y(), p2.y()) || p1.y() < p2.y();
308 QRectF geo = group.readEntry(
"geometry",
geometry());
312 if (geo.size() != geo.size().boundedTo(maximumSize())) {
313 setMaximumSize(maximumSize().expandedTo(geo.size()));
316 if (geo.size() != geo.size().expandedTo(minimumSize())) {
317 setMinimumSize(minimumSize().boundedTo(geo.size()));
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);
337 setActivity(group.readEntry(
"activity", QString()));
346 QMetaObject::invokeMethod(d->toolBox.data(),
"restore", Q_ARG(KConfigGroup, group));
353 d->containmentActionsSource = ContainmentPrivate::Local;
354 cfg = KConfigGroup(&group,
"ActionPlugins");
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") {
362 cfg = KConfigGroup(&cfg, activityId);
363 cfg = KConfigGroup(&cfg,
"ActionPlugins");
364 d->containmentActionsSource = ContainmentPrivate::Activity;
365 }
else if (source ==
"Local") {
367 d->containmentActionsSource = ContainmentPrivate::Local;
371 cfg = KConfigGroup(
corona()->
config(),
"ActionPlugins");
373 cfg = KConfigGroup(&group,
"ActionPlugins");
375 d->containmentActionsSource = ContainmentPrivate::Global;
376 group.writeEntry(
"ActionPluginsSource",
"Global");
381 foreach (
const QString &key, cfg.keyList()) {
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) {
407 if (Applet::d->
transient) {
411 KConfigGroup group = g;
412 if (!group.isValid()) {
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());
433 QMetaObject::invokeMethod(d->toolBox.data(),
"save", Q_ARG(KConfigGroup, group));
437 group.writeEntry(
"wallpaperplugin", d->wallpaper->pluginName());
438 group.writeEntry(
"wallpaperpluginmode", d->wallpaper->renderingMode().name());
440 if (d->wallpaper->isInitialized()) {
441 KConfigGroup wallpaperConfig(&group,
"Wallpaper");
442 wallpaperConfig = KConfigGroup(&wallpaperConfig, d->wallpaper->pluginName());
443 d->wallpaper->save(wallpaperConfig);
452 KConfigGroup
applets(&group,
"Applets");
453 foreach (
const Applet *applet, d->applets) {
454 KConfigGroup appletConfig(&applets, QString::number(applet->
id()));
455 applet->
save(appletConfig);
459 void ContainmentPrivate::initApplets()
461 foreach (
Applet *applet, applets) {
462 applet->
restore(*applet->d->mainConfigGroup());
464 kDebug() <<
"!!{} STARTUP TIME" << QTime().msecsTo(QTime::currentTime()) <<
"Applet" << applet->
name();
467 q->flushPendingConstraintsEvents();
469 foreach (Applet *applet, applets) {
470 applet->flushPendingConstraintsEvents();
473 kDebug() <<
"!!{} STARTUP TIME" << QTime().msecsTo(QTime::currentTime()) <<
"Containment's applets initialized" << q->name();
478 KConfigGroup
applets(&group,
"Applets");
482 QList<KConfigGroup> appletConfigs;
483 foreach (
const QString &appletGroup, applets.groupList()) {
485 KConfigGroup appletConfig(&applets, appletGroup);
486 appletConfigs.append(appletConfig);
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());
496 if (plugin.isEmpty()) {
500 d->addApplet(plugin, QVariantList(), appletConfig.readEntry(
"geometry", QRectF()), appId,
true);
511 if (d->type == type) {
515 delete d->toolBox.data();
517 d->checkContainmentFurniture();
520 void ContainmentPrivate::checkContainmentFurniture()
522 if (q->isContainment() &&
530 return qobject_cast<
Corona*>(scene());
536 if (d->wallpaper && d->wallpaper->isInitialized()) {
537 QGraphicsItem *item = scene()->itemAt(event->scenePos());
539 d->wallpaper->mouseMoveEvent(event);
543 if (!event->isAccepted()) {
552 if (d->appletAt(event->scenePos())) {
556 if (d->wallpaper && d->wallpaper->isInitialized() && !
event->isAccepted()) {
557 d->wallpaper->mousePressEvent(event);
560 if (event->isAccepted()) {
561 setFocus(Qt::MouseFocusReason);
562 }
else if (event->button() == Qt::RightButton &&
event->modifiers() == Qt::NoModifier) {
567 if (d->prepareContainmentActions(trigger, event->screenPos())) {
568 d->actionPlugins()->value(trigger)->contextEvent(event);
571 if (!event->isAccepted()) {
580 if (d->appletAt(event->scenePos())) {
586 if (d->wallpaper && d->wallpaper->isInitialized()) {
587 d->wallpaper->mouseReleaseEvent(event);
591 if (d->prepareContainmentActions(trigger, event->screenPos())) {
592 d->actionPlugins()->value(trigger)->contextEvent(event);
609 QGraphicsSceneContextMenuEvent gvevent;
610 gvevent.setScreenPos(screenPos);
611 gvevent.setScenePos(mapToScene(containmentPos));
612 gvevent.setPos(containmentPos);
613 gvevent.setReason(QGraphicsSceneContextMenuEvent::Mouse);
614 gvevent.setWidget(
view());
620 if (!
isContainment() || !KAuthorized::authorizeKAction(
"plasma/containment_context_menu")) {
626 Applet *applet = d->appletAt(event->scenePos());
630 d->addAppletActions(desktopMenu, applet, event);
632 d->addContainmentActions(desktopMenu, event);
636 QMenu *menu = &desktopMenu;
638 if (desktopMenu.actions().size() == 1 && desktopMenu.actions().first()->menu()) {
640 menu = desktopMenu.actions().first()->menu();
643 if (!menu->isEmpty()) {
644 QPoint pos =
event->screenPos();
645 if (applet && d->isPanelContainment()) {
648 if (event->reason() == QGraphicsSceneContextMenuEvent::Mouse) {
652 if (pos.y() + menu->height() <
event->screenPos().y()) {
653 pos.setY(event->screenPos().y());
656 if (pos.x() + menu->width() <
event->screenPos().x()) {
657 pos.setX(event->screenPos().x());
670 void ContainmentPrivate::addContainmentActions(KMenu &desktopMenu, QEvent *event)
672 if (static_cast<Corona*>(q->scene())->immutability() !=
Mutable &&
673 !KAuthorized::authorizeKAction(
"plasma/containment_actions")) {
679 prepareContainmentActions(trigger, QPoint(), &desktopMenu);
682 void ContainmentPrivate::addAppletActions(KMenu &desktopMenu, Applet *applet, QEvent *event)
684 foreach (QAction *action, applet->contextualActions()) {
686 desktopMenu.addAction(action);
690 if (!applet->d->failed) {
691 QAction *configureApplet = applet->d->actions->action(
"configure");
692 if (configureApplet && configureApplet->isEnabled()) {
693 desktopMenu.addAction(configureApplet);
696 QAction *runAssociatedApplication = applet->d->actions->action(
"run associated application");
697 if (runAssociatedApplication && runAssociatedApplication->isEnabled()) {
698 desktopMenu.addAction(runAssociatedApplication);
702 KMenu *containmentMenu =
new KMenu(i18nc(
"%1 is the name of the containment",
"%1 Options", q->name()), &desktopMenu);
703 addContainmentActions(*containmentMenu, event);
704 if (!containmentMenu->isEmpty()) {
707 QListIterator<QAction *> actionsIt(containmentMenu->actions());
708 while (enabled < 3 && actionsIt.hasNext()) {
709 QAction *action = actionsIt.next();
710 if (action->isVisible() && !action->isSeparator()) {
718 foreach (QAction *action, containmentMenu->actions()) {
719 if (action->isVisible() && !action->isSeparator()) {
720 desktopMenu.addAction(action);
724 desktopMenu.addMenu(containmentMenu);
729 if (q->immutability() ==
Mutable) {
730 QAction *closeApplet = applet->d->actions->action(
"remove");
733 if (!desktopMenu.isEmpty()) {
734 desktopMenu.addSeparator();
738 desktopMenu.addAction(closeApplet);
743 Applet* ContainmentPrivate::appletAt(
const QPointF &point)
747 QGraphicsItem *item = q->scene()->itemAt(point);
753 if (item->isWidget()) {
756 if (applet->isContainment()) {
762 AppletHandle *handle =
dynamic_cast<AppletHandle*
>(item);
765 applet = handle->applet();
768 item = item->parentItem();
775 if (d->formFactor == formFactor) {
785 d->positionPanel(
true);
788 QMetaObject::invokeMethod(d->toolBox.data(),
"reposition");
792 KConfigGroup c =
config();
793 c.writeEntry(
"formfactor", (
int)formFactor);
799 if (d->location == location) {
803 bool emitGeomChange =
false;
807 emitGeomChange =
true;
812 emitGeomChange =
true;
817 foreach (
Applet *applet, d->applets) {
821 if (emitGeomChange) {
829 KConfigGroup c =
config();
830 c.writeEntry(
"location", (
int)location);
841 foreach (
Applet *applet, d->applets) {
842 applet->d->cleanUpAndDelete();
849 const QRectF &appletGeometry)
851 return d->addApplet(name, args, appletGeometry);
861 kDebug() <<
"adding null applet!?!";
865 if (d->applets.contains(applet)) {
866 kDebug() <<
"already have this applet!";
876 if (currentContainment && currentContainment !=
this) {
878 if (currentContainment->d->focusedApplet == applet) {
879 currentContainment->d->focusedApplet = 0;
882 disconnect(applet, 0, currentContainment, 0);
883 KConfigGroup oldConfig = applet->
config();
884 currentContainment->d->applets.removeAll(applet);
885 applet->setParentItem(
this);
886 applet->setParent(
this);
890 KConfigGroup c =
config().group(
"Applets").group(QString::number(applet->
id()));
891 oldConfig.reparent(&c);
892 applet->d->resetConfigurationObject();
894 disconnect(applet, SIGNAL(
activate()), currentContainment, SIGNAL(
activate()));
896 applet->setParentItem(
this);
897 applet->setParent(
this);
900 d->applets << applet;
908 if (pos != QPointF(-1, -1)) {
912 if (!delayInit && !currentContainment) {
913 applet->
restore(*applet->d->mainConfigGroup());
917 connect(anim, SIGNAL(finished()),
this, SLOT(appletAppearAnimationComplete()));
921 anim->setDirection(QAbstractAnimation::Backward);
922 anim->start(QAbstractAnimation::DeleteWhenStopped);
924 d->appletAppeared(applet);
928 applet->setFlag(QGraphicsItem::ItemIsMovable,
true);
935 if (!currentContainment) {
943 applet->d->scheduleModificationNotification();
954 d->setScreen(newScreen, newDesktop);
957 void ContainmentPrivate::setScreen(
int newScreen,
int newDesktop,
bool preventInvalidDesktops)
969 Corona *corona = q->corona();
978 if (newScreen < -1) {
983 if (newDesktop < -1 || (preventInvalidDesktops && newDesktop > KWindowSystem::numberOfDesktops() - 1)) {
989 Containment *swapScreensWith(0);
992 if (isDesktopContainment) {
995 if (screen < 0 && newScreen > -1) {
996 QObject::connect(KWindowSystem::self(), SIGNAL(workAreaChanged()), toolBox.data(), SLOT(reposition()), Qt::UniqueConnection);
997 }
else if (newScreen < 0) {
998 QObject::disconnect(KWindowSystem::self(), SIGNAL(workAreaChanged()), toolBox.data(), SLOT(reposition()));
1002 if (newScreen > -1) {
1005 if (currently && currently != q) {
1006 kDebug() <<
"currently is on screen" << currently->
screen()
1007 <<
"desktop" << currently->desktop()
1008 <<
"and is" << currently->activity()
1010 currently->setScreen(-1, currently->desktop());
1011 swapScreensWith = currently;
1016 if (newScreen < numScreens && newScreen > -1 && isDesktopContainment) {
1020 int oldDesktop = desktop;
1021 desktop = newDesktop;
1023 int oldScreen = screen;
1028 if (oldScreen != newScreen || oldDesktop != newDesktop) {
1034 KConfigGroup c = q->
config();
1035 c.writeEntry(
"screen", screen);
1036 c.writeEntry(
"desktop", desktop);
1037 if (newScreen != -1) {
1038 lastScreen = newScreen;
1039 lastDesktop = newDesktop;
1040 c.writeEntry(
"lastScreen", lastScreen);
1041 c.writeEntry(
"lastDesktop", lastDesktop);
1043 emit q->configNeedsSaving();
1044 emit q->screenChanged(oldScreen, newScreen, q);
1047 if (swapScreensWith) {
1049 swapScreensWith->setScreen(oldScreen, oldDesktop);
1052 checkRemoveAction();
1054 if (newScreen >= 0) {
1066 return d->lastScreen;
1076 return d->lastDesktop;
1080 const QString &parentApp)
1087 const QString &category,
1088 const QString &parentApp)
1092 if (parentApp.isEmpty()) {
1093 constraint.append(
"(not exist [X-KDE-ParentApp] or [X-KDE-ParentApp] == '')");
1095 constraint.append(
"[X-KDE-ParentApp] == '").append(parentApp).append(
"'");
1098 if (!type.isEmpty()) {
1099 if (!constraint.isEmpty()) {
1100 constraint.append(
" and ");
1103 constraint.append(
"'").append(type).append(
"' ~in [X-Plasma-ContainmentCategories]");
1106 if (!category.isEmpty()) {
1107 if (!constraint.isEmpty()) {
1108 constraint.append(
" and ");
1111 constraint.append(
"[X-KDE-PluginInfo-Category] == '").append(category).append(
"'");
1112 if (category ==
"Miscellaneous") {
1113 constraint.append(
" or (not exist [X-KDE-PluginInfo-Category] or [X-KDE-PluginInfo-Category] == '')");
1117 KService::List offers = KServiceTypeTrader::self()->query(
"Plasma/Containment", constraint);
1119 return KPluginInfo::fromServices(offers);
1124 const QString constraint = QString(
"'%1' in [X-Plasma-DropMimeTypes]").arg(mimetype);
1126 const KService::List offers = KServiceTypeTrader::self()->query(
"Plasma/Containment", constraint);
1127 return KPluginInfo::fromServices(offers);
1133 QSet<QString> types;
1135 foreach (
const KPluginInfo &containmentInfo, containmentInfos) {
1136 QStringList theseTypes = containmentInfo.service()->property(
"X-Plasma-ContainmentCategories").toStringList();
1137 foreach (
const QString &
type, theseTypes) {
1142 return types.toList();
1149 (event->mimeData()->hasFormat(static_cast<Corona*>(scene())->appletMimeType()) ||
1150 KUrl::List::canDecode(event->mimeData()) ||
1151 event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())));
1153 if (!event->isAccepted()) {
1155 QStringList formats =
event->mimeData()->formats();
1157 foreach (
const QString &format, formats) {
1159 if (!appletList.isEmpty()) {
1160 event->setAccepted(
true);
1165 if (!event->isAccepted()) {
1166 foreach (
const QString &format, formats) {
1168 if (!wallpaperList.isEmpty()) {
1169 event->setAccepted(
true);
1176 if (event->isAccepted()) {
1177 if (d->dropZoneStarted) {
1180 if (!d->showDropZoneDelayTimer) {
1181 d->showDropZoneDelayTimer =
new QTimer(
this);
1182 d->showDropZoneDelayTimer->setInterval(300);
1183 d->showDropZoneDelayTimer->setSingleShot(
true);
1184 connect(d->showDropZoneDelayTimer, SIGNAL(timeout()),
this, SLOT(showDropZoneDelayed()));
1187 d->dropPoints.insert(0, event->pos());
1188 d->showDropZoneDelayTimer->start();
1196 if (d->showDropZoneDelayTimer) {
1197 d->showDropZoneDelayTimer->stop();
1200 if (event->pos().y() < 1 ||
event->pos().y() > size().height() ||
1201 event->pos().x() < 1 ||
event->pos().x() > size().width()) {
1203 d->dropZoneStarted =
false;
1207 void ContainmentPrivate::showDropZoneDelayed()
1209 dropZoneStarted =
true;
1210 q->showDropZone(dropPoints.value(0).toPoint());
1211 dropPoints.remove(0);
1216 QGraphicsItem *item = scene()->itemAt(event->scenePos());
1217 event->setAccepted(item ==
this || item == d->toolBox.data() || !item);
1219 if (!event->isAccepted()) {
1220 if (d->showDropZoneDelayTimer) {
1221 d->showDropZoneDelayTimer->stop();
1231 d->dropData(event->scenePos(),
event->screenPos(), event);
1237 void ContainmentPrivate::dropData(QPointF scenePos, QPoint screenPos, QGraphicsSceneDragDropEvent *dropEvent)
1239 if (q->immutability() !=
Mutable) {
1243 QPointF pos = q->mapFromScene(scenePos);
1244 const QMimeData *mimeData = 0;
1247 mimeData = dropEvent->mimeData();
1249 QClipboard *clipboard = QApplication::clipboard();
1250 mimeData = clipboard->mimeData(QClipboard::Selection);
1256 kDebug() <<
"no mime data";
1262 QString appletMimetype(q->corona() ? q->corona()->appletMimeType() : QString());
1264 if (!appletMimetype.isEmpty() && mimeData->hasFormat(appletMimetype)) {
1265 QString data = mimeData->data(appletMimetype);
1266 const QStringList appletNames = data.split(
'\n', QString::SkipEmptyParts);
1267 foreach (
const QString &appletName, appletNames) {
1269 QRectF geom(pos, QSize(0, 0));
1270 q->addApplet(appletName, QVariantList(), geom);
1273 dropEvent->acceptProposedAction();
1275 }
else if (mimeData->hasFormat(ExtenderItemMimeData::mimeType())) {
1276 kDebug() <<
"mimetype plasma/extenderitem is dropped, creating internal:extender";
1278 const ExtenderItemMimeData *extenderData = qobject_cast<
const ExtenderItemMimeData*>(mimeData);
1280 ExtenderItem *item = extenderData->extenderItem();
1281 QRectF geometry(pos - extenderData->pointerOffset(), item->size());
1282 kDebug() <<
"desired geometry: " << geometry;
1283 Applet *applet = qobject_cast<ExtenderApplet *>(item->extender() ? item->extender()->applet() : 0);
1285 qreal left, top, right, bottom;
1286 applet->getContentsMargins(&left, &top, &right, &bottom);
1287 applet->setPos(geometry.topLeft() - QPointF(
int(left),
int(top)));
1290 applet = addApplet(
"internal:extender", QVariantList(), geometry, 0,
true);
1293 appletAppeared(applet);
1294 applet->flushPendingConstraintsEvents();
1295 applet->d->scheduleModificationNotification();
1296 applet->adjustSize();
1299 item->setExtender(applet->extender());
1301 }
else if (KUrl::List::canDecode(mimeData)) {
1304 const KUrl::List urls = KUrl::List::fromMimeData(mimeData);
1305 foreach (
const KUrl &url, urls) {
1309 dropPoints[job] = dropEvent->pos();
1311 dropPoints[job] = scenePos;
1316 #ifndef PLASMA_NO_KIO
1318 KMimeType::Ptr mime = KMimeType::findByUrl(url);
1319 QString mimeName = mime->name();
1320 QRectF geom(pos, QSize());
1323 kDebug() <<
"can decode" << mimeName << args;
1326 KIO::JobFlags flags = KIO::HideProgressInfo;
1327 KIO::MimetypeJob *job = KIO::mimetype(url, flags);
1329 dropPoints[job] = dropEvent->pos();
1331 dropPoints[job] = scenePos;
1334 QObject::connect(job, SIGNAL(result(
KJob*)), q, SLOT(dropJobResult(
KJob*)));
1335 QObject::connect(job, SIGNAL(mimetype(KIO::Job*,QString)),
1336 q, SLOT(mimeTypeRetrieved(KIO::Job*,QString)));
1338 KMenu *choices =
new KMenu(
"Content dropped");
1339 choices->addAction(KIcon(
"process-working"), i18n(
"Fetching file type..."));
1341 choices->popup(dropEvent->screenPos());
1343 choices->popup(screenPos);
1346 dropMenus[job] = choices;
1352 dropEvent->acceptProposedAction();
1355 QStringList formats = mimeData->formats();
1356 QHash<QString, KPluginInfo> seenPlugins;
1357 QHash<QString, QString> pluginFormats;
1359 foreach (
const QString &format, formats) {
1362 foreach (
const KPluginInfo &plugin, plugins) {
1363 if (seenPlugins.contains(plugin.pluginName())) {
1367 seenPlugins.insert(plugin.pluginName(), plugin);
1368 pluginFormats.insert(plugin.pluginName(), format);
1373 QString selectedPlugin;
1375 if (seenPlugins.isEmpty()) {
1377 }
else if (seenPlugins.count() == 1) {
1378 selectedPlugin = seenPlugins.constBegin().key();
1381 QHash<QAction *, QString> actionsToPlugins;
1382 foreach (
const KPluginInfo &info, seenPlugins) {
1384 if (!info.icon().isEmpty()) {
1385 action = choices.addAction(KIcon(info.icon()), info.name());
1387 action = choices.addAction(info.name());
1390 actionsToPlugins.insert(action, info.pluginName());
1393 QAction *choice = choices.exec(screenPos);
1395 selectedPlugin = actionsToPlugins[choice];
1399 if (!selectedPlugin.isEmpty()) {
1405 QClipboard *clipboard = QApplication::clipboard();
1406 mimeData = clipboard->mimeData(QClipboard::Selection);
1409 KTemporaryFile tempFile;
1410 if (mimeData && tempFile.open()) {
1412 tempFile.setAutoRemove(
false);
1415 QDataStream stream(&tempFile);
1416 QByteArray data = mimeData->data(pluginFormats[selectedPlugin]);
1417 stream.writeRawData(data, data.size());
1420 QRectF geom(pos, QSize());
1422 args << tempFile.fileName();
1426 q->addApplet(selectedPlugin, args, geom);
1432 void ContainmentPrivate::clearDataForMimeJob(KIO::Job *job)
1434 #ifndef PLASMA_NO_KIO
1435 QObject::disconnect(job, 0, q, 0);
1436 dropPoints.remove(job);
1437 KMenu *choices = dropMenus.take(job);
1440 #endif // PLASMA_NO_KIO
1445 QPointF pos = dropPoints.take(job);
1448 kDebug() <<
"remote applet access failed: " << job->errorText();
1453 kDebug() <<
"how did we end up here? if applet is null, the job->error should be nonzero";
1457 q->addApplet(job->
applet(), pos);
1460 void ContainmentPrivate::dropJobResult(
KJob *job)
1462 #ifndef PLASMA_NO_KIO
1463 KIO::TransferJob* tjob =
dynamic_cast<KIO::TransferJob*
>(job);
1465 kDebug() <<
"job is not a KIO::TransferJob, won't handle the drop...";
1466 clearDataForMimeJob(tjob);
1470 kDebug() <<
"ERROR" << tjob->error() <<
' ' << tjob->errorString();
1474 mimeTypeRetrieved(qobject_cast<KIO::Job *>(job), QString());
1475 #endif // PLASMA_NO_KIO
1478 void ContainmentPrivate::mimeTypeRetrieved(KIO::Job *job,
const QString &mimetype)
1480 #ifndef PLASMA_NO_KIO
1481 kDebug() <<
"Mimetype Job returns." << mimetype;
1482 KIO::TransferJob* tjob =
dynamic_cast<KIO::TransferJob*
>(job);
1484 kDebug() <<
"job should be a TransferJob, but isn't";
1485 clearDataForMimeJob(job);
1489 if (mimetype.isEmpty() && !appletList.count()) {
1490 clearDataForMimeJob(job);
1491 kDebug() <<
"No applets found matching the url (" << tjob->url() <<
") or the mimetype (" << mimetype <<
")";
1496 if (dropPoints.keys().contains(tjob)) {
1497 posi = dropPoints[tjob];
1498 kDebug() <<
"Received a suitable dropEvent at" << posi;
1500 kDebug() <<
"Bailing out. Cannot find associated dropEvent related to the TransferJob";
1501 clearDataForMimeJob(job);
1505 KMenu *choices = dropMenus.value(tjob);
1507 kDebug() <<
"Bailing out. No QMenu found for this job.";
1508 clearDataForMimeJob(job);
1513 args << tjob->url().url() << mimetype;
1515 kDebug() <<
"Creating menu for:" << mimetype << posi << args;
1518 KPluginInfo::List wallpaperList;
1519 if (drawWallpaper) {
1520 if (wallpaper && wallpaper->supportsMimetype(mimetype)) {
1521 wallpaperList << wallpaper->d->wallpaperDescription;
1527 if (!appletList.isEmpty() || !wallpaperList.isEmpty()) {
1529 QHash<QAction *, QString> actionsToApplets;
1530 choices->addTitle(i18n(
"Widgets"));
1531 foreach (
const KPluginInfo &info, appletList) {
1532 kDebug() << info.name();
1534 if (!info.icon().isEmpty()) {
1535 action = choices->addAction(KIcon(info.icon()), info.name());
1537 action = choices->addAction(info.name());
1540 actionsToApplets.insert(action, info.pluginName());
1541 kDebug() << info.pluginName();
1543 actionsToApplets.insert(choices->addAction(i18n(
"Icon")),
"icon");
1545 QHash<QAction *, QString> actionsToWallpapers;
1546 if (!wallpaperList.isEmpty()) {
1547 choices->addTitle(i18n(
"Wallpaper"));
1549 QMap<QString, KPluginInfo> sorted;
1550 foreach (
const KPluginInfo &info, appletList) {
1551 sorted.insert(info.name(), info);
1554 foreach (
const KPluginInfo &info, wallpaperList) {
1556 if (!info.icon().isEmpty()) {
1557 action = choices->addAction(KIcon(info.icon()), info.name());
1559 action = choices->addAction(info.name());
1562 actionsToWallpapers.insert(action, info.pluginName());
1566 QAction *choice = choices->exec();
1571 if (!mimetype.isEmpty() && !tjob->error()) {
1573 KIO::Scheduler::publishSlaveOnHold();
1575 QString plugin = actionsToApplets.value(choice);
1576 if (plugin.isEmpty()) {
1578 plugin = actionsToWallpapers.value(choice);
1579 if (!wallpaper || plugin != wallpaper->pluginName()) {
1580 kDebug() <<
"Wallpaper dropped:" << tjob->url();
1581 q->setWallpaper(plugin);
1585 kDebug() <<
"Wallpaper dropped:" << tjob->url();
1586 wallpaper->setUrls(KUrl::List() << tjob->url());
1589 addApplet(actionsToApplets[choice], args, QRectF(posi, QSize()));
1592 clearDataForMimeJob(job);
1597 addApplet(
"icon", args, QRectF(posi, QSize()));
1601 clearDataForMimeJob(job);
1602 #endif // PLASMA_NO_KIO
1605 #ifndef KDE_NO_DEPRECATED
1608 return d->toolBox.data();
1614 if (d->toolBox.data()) {
1615 d->toolBox.data()->deleteLater();
1622 return d->toolBox.data();
1630 if (d->isPanelContainment()) {
1633 QMetaObject::invokeMethod(
corona(),
"layoutContainments");
1637 d->wallpaper->setBoundingRect(QRectF(QPointF(0, 0), size()));
1646 if (event->key() == Qt::Key_Tab) {
1647 if (!d->applets.isEmpty()) {
1648 kDebug() <<
"let's give focus to...." << (
QObject*)d->applets.first();
1649 d->applets.first()->setFocus(Qt::TabFocusReason);
1657 if (d->appletAt(event->scenePos())) {
1661 if (d->wallpaper && d->wallpaper->isInitialized()) {
1662 QGraphicsItem *item = scene()->itemAt(event->scenePos());
1665 d->wallpaper->wheelEvent(event);
1667 if (event->isAccepted()) {
1675 if (d->prepareContainmentActions(trigger, event->screenPos())) {
1676 d->actionPlugins()->value(trigger)->contextEvent(event);
1694 (change == QGraphicsItem::ItemSceneHasChanged ||
1695 change == QGraphicsItem::ItemPositionHasChanged)) {
1703 QMetaObject::invokeMethod(
corona(),
"layoutContainments");
1714 QAction *action = this->
action(name);
1716 action->setEnabled(enable);
1717 action->setVisible(enable);
1725 d->toolBox.data()->addTool(action);
1732 d->toolBox.data()->removeTool(action);
1747 return (d->toolBox && d->toolBox.data()->isShowing());
1752 if (d->toolBox && !d->toolBox.data()->isShowing()) {
1753 d->toolBox.data()->setShowing(
true);
1760 if (d->toolBox && d->toolBox.data()->isShowing()) {
1761 d->toolBox.data()->setShowing(
false);
1769 if (d->focusedApplet) {
1770 d->focusedApplet->addAssociatedWidget(widget);
1773 foreach (
const Applet *applet, d->applets) {
1774 if (applet->d->activationAction) {
1775 widget->addAction(applet->d->activationAction);
1783 if (d->focusedApplet) {
1784 d->focusedApplet->removeAssociatedWidget(widget);
1787 foreach (
const Applet *applet, d->applets) {
1788 if (applet->d->activationAction) {
1789 widget->removeAction(applet->d->activationAction);
1797 if (drawWallpaper) {
1798 KConfigGroup cfg =
config();
1803 delete d->wallpaper;
1810 return d->drawWallpaper;
1815 KConfigGroup cfg =
config();
1816 bool newPlugin =
true;
1817 bool newMode =
true;
1819 if (d->drawWallpaper) {
1822 if (d->wallpaper->pluginName() !=
pluginName) {
1823 delete d->wallpaper;
1828 newMode = d->wallpaper->renderingMode().name() != mode;
1833 if (!pluginName.isEmpty() && !d->wallpaper) {
1838 d->wallpaper->setParent(
this);
1839 d->wallpaper->setBoundingRect(QRectF(QPointF(0, 0), size()));
1840 d->wallpaper->setRenderingMode(mode);
1843 cfg.writeEntry(
"wallpaperplugin", pluginName);
1846 if (d->wallpaper->isInitialized()) {
1847 KConfigGroup wallpaperConfig = KConfigGroup(&cfg,
"Wallpaper");
1848 wallpaperConfig = KConfigGroup(&wallpaperConfig, pluginName);
1849 d->wallpaper->restore(wallpaperConfig);
1853 cfg.writeEntry(
"wallpaperpluginmode", mode);
1860 if (!d->wallpaper) {
1861 cfg.deleteEntry(
"wallpaperplugin");
1862 cfg.deleteEntry(
"wallpaperpluginmode");
1865 if (newPlugin || newMode) {
1866 if (newPlugin && d->wallpaper) {
1867 connect(d->wallpaper, SIGNAL(
configureRequested()),
this, SLOT(requestConfiguration()));
1877 return d->wallpaper;
1885 if (d->actionPlugins()->contains(trigger)) {
1886 plugin = d->actionPlugins()->value(trigger);
1888 d->actionPlugins()->remove(trigger);
1893 if (pluginName.isEmpty()) {
1894 cfg.deleteEntry(trigger);
1895 }
else if (plugin) {
1900 KConfigGroup pluginConfig = KConfigGroup(&cfg, trigger);
1901 plugin->
restore(pluginConfig);
1904 switch (d->containmentActionsSource) {
1905 case ContainmentPrivate::Activity:
1907 case ContainmentPrivate::Local:
1914 cfg.writeEntry(trigger, pluginName);
1915 d->actionPlugins()->insert(trigger, plugin);
1918 cfg.deleteEntry(trigger);
1927 return d->actionPlugins()->keys();
1946 foreach (
Applet *a, applets) {
1950 KConfigGroup c = q->config();
1956 if (!act.isEmpty()) {
1957 c.writeEntry(
"activityId", act);
1960 if (!act.isEmpty()) {
1961 c.writeEntry(
"activity", act);
1965 toolBox.data()->update();
1967 emit q->configNeedsSaving();
1968 emit q->contextChanged(con);
1973 return d->context()->currentActivity();
1978 return d->context();
1981 Context *ContainmentPrivate::context()
1992 KActionCollection* ContainmentPrivate::actions()
1994 return static_cast<Applet*
>(q)->d->actions;
1999 if (focusedApplet == applet) {
2003 QList<QWidget *> widgets = actions()->associatedWidgets();
2004 if (focusedApplet) {
2005 foreach (
QWidget *w, widgets) {
2006 focusedApplet->removeAssociatedWidget(w);
2010 if (applet && applets.contains(applet)) {
2012 focusedApplet = applet;
2013 foreach (
QWidget *w, widgets) {
2017 if (!focusedApplet->hasFocus()) {
2018 focusedApplet->setFocus(Qt::ShortcutFocusReason);
2027 if (d->applets.isEmpty()) {
2030 int index = d->focusedApplet ? d->applets.indexOf(d->focusedApplet) + 1 : 0;
2031 if (index >= d->applets.size()) {
2034 kDebug() <<
"index" << index;
2035 d->focusApplet(d->applets.at(index));
2040 if (d->applets.isEmpty()) {
2043 int index = d->focusedApplet ? d->applets.indexOf(d->focusedApplet) - 1 : -1;
2045 index = d->applets.size() - 1;
2047 kDebug() <<
"index" << index;
2048 d->focusApplet(d->applets.at(index));
2065 void ContainmentPrivate::configChanged()
2067 if (drawWallpaper) {
2068 KConfigGroup group = q->config();
2074 void ContainmentPrivate::requestConfiguration()
2076 emit q->configureRequested(q);
2082 if (appletStatus == q->status()) {
2083 emit q->newStatus(appletStatus);
2087 if (appletStatus < q->status()) {
2090 foreach (Applet *applet, applets) {
2091 if (applet->status() > appletStatus) {
2092 appletStatus = applet->status();
2097 q->setStatus(appletStatus);
2108 const QString title = i18nc(
"@title:window %1 is the name of the containment",
"Remove %1",
name());
2109 KGuiItem
remove = KStandardGuiItem::remove();
2110 remove.setText(title);
2111 if (KMessageBox::warningContinueCancel(
view(),
2112 i18nc(
"%1 is the name of the containment",
"Do you really want to remove this %1?",
name()),
2113 title,
remove) != KMessageBox::Continue) {
2121 void ContainmentPrivate::createToolBox()
2123 if (!toolBox && KAuthorized::authorizeKAction(
"plasma/containment_context_menu")) {
2127 QObject::connect(toolBox.data(), SIGNAL(toggled()), q, SIGNAL(toolBoxToggled()));
2128 QObject::connect(toolBox.data(), SIGNAL(toggled()), q, SLOT(updateToolBoxVisibility()));
2135 void ContainmentPrivate::positionToolBox()
2137 QMetaObject::invokeMethod(toolBox.data(),
"reposition");
2140 void ContainmentPrivate::updateToolBoxVisibility()
2142 emit q->toolBoxVisibilityChanged(toolBox.data()->isShowing());
2145 void ContainmentPrivate::triggerShowAddWidgets()
2147 emit q->showAddWidgetsInterface(QPointF());
2150 void ContainmentPrivate::containmentConstraintsEvent(Plasma::Constraints constraints)
2152 if (!q->isContainment()) {
2159 checkRemoveAction();
2160 const bool unlocked = q->immutability() ==
Mutable;
2161 q->setAcceptDrops(unlocked);
2162 q->enableAction(
"add widgets", unlocked);
2165 foreach (Applet *a, applets) {
2166 a->setImmutability(q->immutability());
2182 foreach (Applet *applet, applets) {
2183 applet->updateConstraints(appletConstraints);
2196 q->addToolBoxAction(q->action(
"remove"));
2197 checkRemoveAction();
2201 Applet *ContainmentPrivate::addApplet(
const QString &name,
const QVariantList &args,
2202 const QRectF &appletGeometry, uint
id,
bool delayInit)
2204 if (!q->isContainment()) {
2208 if (!delayInit && q->immutability() !=
Mutable) {
2209 kDebug() <<
"addApplet for" << name <<
"requested, but we're currently immutable!";
2215 v->setCursor(Qt::BusyCursor);
2224 kDebug() <<
"Applet" << name <<
"could not be loaded.";
2225 applet =
new Applet(0, QString(),
id);
2226 applet->setFailedToLaunch(
true, i18n(
"Could not find requested component: %1", name));
2231 q->addApplet(applet, appletGeometry.topLeft(), delayInit);
2235 bool ContainmentPrivate::regionIsEmpty(
const QRectF ®ion, Applet *ignoredApplet)
const
2237 foreach (Applet *applet, applets) {
2238 if (applet != ignoredApplet && applet->geometry().intersects(region)) {
2247 applets.removeAll(applet);
2248 if (focusedApplet == applet) {
2252 emit q->appletRemoved(applet);
2253 emit q->configNeedsSaving();
2256 void ContainmentPrivate::appletAppearAnimationComplete()
2258 Animation *anim = qobject_cast<Animation *>(q->sender());
2260 Applet *applet = qobject_cast<Applet*>(anim->targetWidget());
2262 appletAppeared(applet);
2267 void ContainmentPrivate::appletAppeared(Applet *applet)
2270 KConfigGroup *cg = applet->d->mainConfigGroup();
2272 emit q->configNeedsSaving();
2275 void ContainmentPrivate::positionPanel(
bool force)
2278 kDebug() <<
"no scene yet";
2283 if (ContainmentPrivate::s_positioningPanels) {
2291 const QPointF p = q->pos();
2294 p.y() + q->size().height() < -INTER_CONTAINMENT_MARGIN &&
2295 q->scene()->collidingItems(q).isEmpty()) {
2301 QPointF newPos = preferredPanelPos(q->corona());
2303 ContainmentPrivate::s_positioningPanels =
true;
2305 ContainmentPrivate::s_positioningPanels =
false;
2309 bool ContainmentPrivate::isPanelContainment()
const
2314 QPointF ContainmentPrivate::preferredPos(Corona *corona)
const
2318 if (isPanelContainment()) {
2320 return preferredPanelPos(corona);
2325 while (QGraphicsItem *i = corona->itemAt(pos, t)) {
2326 pos.setX(i->scenePos().x() + i->boundingRect().width() + 10);
2333 QPointF ContainmentPrivate::preferredPanelPos(Corona *corona)
const
2339 qreal bottom = horiz ? 0 : VERTICAL_STACKING_OFFSET;
2340 qreal lastHeight = 0;
2345 foreach (
const Containment *other, corona->containments()) {
2347 !other->d->isPanelContainment() ||
2354 qreal y = other->pos().y();
2356 lastHeight = other->size().height();
2360 qreal width = other->size().width();
2361 qreal x = other->pos().x() + width;
2364 bottom = x + lastHeight;
2373 bottom -= lastHeight + INTER_CONTAINMENT_MARGIN;
2375 kDebug() <<
"moved to" << QPointF(0, bottom - q->size().height());
2376 newPos = QPointF(0, bottom - q->size().height());
2378 bottom += lastHeight + INTER_CONTAINMENT_MARGIN;
2380 kDebug() <<
"moved to" << QPointF(bottom + q->size().width(), -INTER_CONTAINMENT_MARGIN - q->size().height());
2381 newPos = QPointF(bottom + q->size().width(), -INTER_CONTAINMENT_MARGIN - q->size().height());
2388 bool ContainmentPrivate::prepareContainmentActions(
const QString &trigger,
const QPoint &screenPos, KMenu *menu)
2390 ContainmentActions *plugin = actionPlugins()->value(trigger);
2394 plugin->setContainment(q);
2396 if (!plugin->isInitialized()) {
2397 KConfigGroup cfg = q->containmentActionsConfig();
2398 KConfigGroup pluginConfig = KConfigGroup(&cfg, trigger);
2399 plugin->restore(pluginConfig);
2402 if (plugin->configurationRequired()) {
2403 KMenu *localMenu = menu ? menu :
new KMenu();
2405 localMenu->addTitle(i18n(
"This plugin needs to be configured"));
2406 localMenu->addAction(q->action(
"configure"));
2409 localMenu->exec(screenPos);
2415 QList<QAction*> actions = plugin->contextualActions();
2416 if (actions.isEmpty()) {
2419 if (!isPanelContainment() && q->action(
"configure")) {
2420 menu->addAction(q->action(
"configure"));
2423 menu->addActions(actions);
2433 switch (d->containmentActionsSource) {
2434 case ContainmentPrivate::Local:
2436 cfg = KConfigGroup(&cfg,
"ActionPlugins");
2438 case ContainmentPrivate::Activity:
2440 cfg = KConfigGroup(&cfg, d->context()->currentActivityId());
2441 cfg = KConfigGroup(&cfg,
"ActionPlugins");
2444 cfg = KConfigGroup(
corona()->
config(),
"ActionPlugins");
2449 QHash<QString, ContainmentActions*> * ContainmentPrivate::actionPlugins()
2451 switch (containmentActionsSource) {
2455 return &localActionPlugins;
2457 return &globalActionPlugins;
2463 #include "containment.moc"