Plasma
containment.cpp
Go to the documentation of this file.
00001 /* 00002 * Copyright 2007 by Aaron Seigo <aseigo@kde.org> 00003 * Copyright 2008 by Ménard Alexis <darktears31@gmail.com> 00004 * Copyright 2009 Chani Armitage <chani@kde.org> 00005 * 00006 * This program is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU Library General Public License as 00008 * published by the Free Software Foundation; either version 2, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU General Public License for more details 00015 * 00016 * You should have received a copy of the GNU Library General Public 00017 * License along with this program; if not, write to the 00018 * Free Software Foundation, Inc., 00019 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00020 */ 00021 00022 #include "containment.h" 00023 #include "private/containment_p.h" 00024 00025 #include "config-plasma.h" 00026 00027 #include <QApplication> 00028 #include <QClipboard> 00029 #include <QFile> 00030 #include <QGraphicsSceneContextMenuEvent> 00031 #include <QGraphicsView> 00032 #include <QMimeData> 00033 #include <QPainter> 00034 #include <QStyleOptionGraphicsItem> 00035 #include <QGraphicsLayout> 00036 #include <QGraphicsLinearLayout> 00037 00038 #include <kaction.h> 00039 #include <kauthorized.h> 00040 #include <kicon.h> 00041 #include <kmenu.h> 00042 #include <kmessagebox.h> 00043 #include <kmimetype.h> 00044 #include <kservicetypetrader.h> 00045 #include <kstandarddirs.h> 00046 #include <ktemporaryfile.h> 00047 #include <kwindowsystem.h> 00048 00049 #ifndef PLASMA_NO_KIO 00050 #include "kio/jobclasses.h" // for KIO::JobFlags 00051 #include "kio/job.h" 00052 #include "kio/scheduler.h" 00053 #endif 00054 00055 #include "abstracttoolbox.h" 00056 #include "animator.h" 00057 #include "context.h" 00058 #include "containmentactions.h" 00059 #include "containmentactionspluginsconfig.h" 00060 #include "corona.h" 00061 #include "extender.h" 00062 #include "extenderitem.h" 00063 #include "svg.h" 00064 #include "wallpaper.h" 00065 00066 #include "remote/accessappletjob.h" 00067 #include "remote/accessmanager.h" 00068 00069 #include "private/applet_p.h" 00070 #include "private/containmentactionspluginsconfig_p.h" 00071 #include "private/extenderitemmimedata_p.h" 00072 #include "private/extenderapplet_p.h" 00073 #include "private/wallpaper_p.h" 00074 00075 #include "plasma/plasma.h" 00076 #include "animations/animation.h" 00077 00078 namespace Plasma 00079 { 00080 00081 bool ContainmentPrivate::s_positioningPanels = false; 00082 QHash<QString, ContainmentActions*> ContainmentPrivate::globalActionPlugins; 00083 static const char defaultWallpaper[] = "image"; 00084 static const char defaultWallpaperMode[] = "SingleImage"; 00085 00086 Containment::StyleOption::StyleOption() 00087 : QStyleOptionGraphicsItem(), 00088 view(0) 00089 { 00090 version = Version; 00091 type = Type; 00092 } 00093 00094 Containment::StyleOption::StyleOption(const Containment::StyleOption & other) 00095 : QStyleOptionGraphicsItem(other), 00096 view(other.view) 00097 { 00098 version = Version; 00099 type = Type; 00100 } 00101 00102 Containment::StyleOption::StyleOption(const QStyleOptionGraphicsItem &other) 00103 : QStyleOptionGraphicsItem(other), 00104 view(0) 00105 { 00106 version = Version; 00107 type = Type; 00108 } 00109 00110 Containment::Containment(QGraphicsItem *parent, 00111 const QString &serviceId, 00112 uint containmentId) 00113 : Applet(parent, serviceId, containmentId), 00114 d(new ContainmentPrivate(this)) 00115 { 00116 // WARNING: do not access config() OR globalConfig() in this method! 00117 // that requires a scene, which is not available at this point 00118 setPos(0, 0); 00119 setBackgroundHints(NoBackground); 00120 setContainmentType(CustomContainment); 00121 setHasConfigurationInterface(false); 00122 } 00123 00124 Containment::Containment(QObject *parent, const QVariantList &args) 00125 : Applet(parent, args), 00126 d(new ContainmentPrivate(this)) 00127 { 00128 // WARNING: do not access config() OR globalConfig() in this method! 00129 // that requires a scene, which is not available at this point 00130 setPos(0, 0); 00131 setBackgroundHints(NoBackground); 00132 setHasConfigurationInterface(false); 00133 } 00134 00135 Containment::Containment(const QString &packagePath, uint appletId, const QVariantList &args) 00136 : Plasma::Applet(packagePath, appletId, args), 00137 d(new ContainmentPrivate(this)) 00138 { 00139 // WARNING: do not access config() OR globalConfig() in this method! 00140 // that requires a scene, which is not available at this point 00141 setPos(0, 0); 00142 setBackgroundHints(NoBackground); 00143 setHasConfigurationInterface(false); 00144 } 00145 00146 Containment::~Containment() 00147 { 00148 delete d; 00149 // Applet touches our dptr if we are a containment and is the superclass (think of dtors) 00150 // so we reset this as we exit the building 00151 Applet::d->isContainment = false; 00152 } 00153 00154 void Containment::init() 00155 { 00156 Applet::init(); 00157 if (!isContainment()) { 00158 return; 00159 } 00160 00161 setCacheMode(NoCache); 00162 setFlag(QGraphicsItem::ItemIsMovable, false); 00163 setFlag(QGraphicsItem::ItemClipsChildrenToShape, true); 00164 setAcceptDrops(true); 00165 setAcceptsHoverEvents(true); 00166 00167 if (d->type == NoContainmentType) { 00168 setContainmentType(DesktopContainment); 00169 } 00170 00171 //connect actions 00172 ContainmentPrivate::addDefaultActions(d->actions(), this); 00173 bool unlocked = immutability() == Mutable; 00174 00175 //fix the text of the actions that need name() 00176 //btw, do we really want to use name() when it's a desktopcontainment? 00177 QAction *closeApplet = action("remove"); 00178 if (closeApplet) { 00179 closeApplet->setText(i18nc("%1 is the name of the applet", "Remove this %1", name())); 00180 } 00181 00182 QAction *configAction = action("configure"); 00183 if (configAction) { 00184 configAction->setText(i18nc("%1 is the name of the applet", "%1 Settings", name())); 00185 } 00186 00187 QAction *appletBrowserAction = action("add widgets"); 00188 if (appletBrowserAction) { 00189 appletBrowserAction->setVisible(unlocked); 00190 appletBrowserAction->setEnabled(unlocked); 00191 connect(appletBrowserAction, SIGNAL(triggered()), this, SLOT(triggerShowAddWidgets())); 00192 } 00193 00194 QAction *act = action("next applet"); 00195 if (act) { 00196 connect(act, SIGNAL(triggered()), this, SLOT(focusNextApplet())); 00197 } 00198 00199 act = action("previous applet"); 00200 if (act) { 00201 connect(act, SIGNAL(triggered()), this, SLOT(focusPreviousApplet())); 00202 } 00203 00204 if (immutability() != SystemImmutable && corona()) { 00205 QAction *lockDesktopAction = corona()->action("lock widgets"); 00206 //keep a pointer so nobody notices it moved to corona 00207 if (lockDesktopAction) { 00208 d->actions()->addAction("lock widgets", lockDesktopAction); 00209 } 00210 } 00211 if (d->type != PanelContainment && d->type != CustomPanelContainment) { 00212 if (corona()) { 00213 //FIXME this is just here because of the darn keyboard shortcut :/ 00214 act = corona()->action("manage activities"); 00215 if (act) { 00216 d->actions()->addAction("manage activities", act); 00217 } 00218 //a stupid hack to make this one's keyboard shortcut work 00219 act = corona()->action("configure shortcuts"); 00220 if (act) { 00221 d->actions()->addAction("configure shortcuts", act); 00222 } 00223 } 00224 00225 if (d->type == DesktopContainment) { 00226 addToolBoxAction(action("add widgets")); 00227 00228 //TODO: do we need some way to allow this be overridden? 00229 // it's always available because shells rely on this 00230 // to offer their own custom configuration as well 00231 QAction *configureContainment = action("configure"); 00232 if (configureContainment) { 00233 addToolBoxAction(configureContainment); 00234 } 00235 } 00236 } 00237 } 00238 00239 void ContainmentPrivate::addDefaultActions(KActionCollection *actions, Containment *c) 00240 { 00241 actions->setConfigGroup("Shortcuts-Containment"); 00242 00243 //adjust applet actions 00244 KAction *appAction = qobject_cast<KAction*>(actions->action("remove")); 00245 appAction->setShortcut(KShortcut("alt+d, alt+r")); 00246 if (c && c->d->isPanelContainment()) { 00247 appAction->setText(i18n("Remove this Panel")); 00248 } else { 00249 appAction->setText(i18n("Remove this Activity")); 00250 } 00251 00252 appAction = qobject_cast<KAction*>(actions->action("configure")); 00253 if (appAction) { 00254 appAction->setShortcut(KShortcut("alt+d, alt+s")); 00255 appAction->setText(i18n("Activity Settings")); 00256 } 00257 00258 //add our own actions 00259 KAction *appletBrowserAction = actions->addAction("add widgets"); 00260 appletBrowserAction->setAutoRepeat(false); 00261 appletBrowserAction->setText(i18n("Add Widgets...")); 00262 appletBrowserAction->setIcon(KIcon("list-add")); 00263 appletBrowserAction->setShortcut(KShortcut("alt+d, a")); 00264 appletBrowserAction->setData(AbstractToolBox::AddTool); 00265 00266 KAction *action = actions->addAction("next applet"); 00267 action->setText(i18n("Next Widget")); 00268 //no icon 00269 action->setShortcut(KShortcut("alt+d, n")); 00270 action->setData(AbstractToolBox::ControlTool); 00271 00272 action = actions->addAction("previous applet"); 00273 action->setText(i18n("Previous Widget")); 00274 //no icon 00275 action->setShortcut(KShortcut("alt+d, p")); 00276 action->setData(AbstractToolBox::ControlTool); 00277 } 00278 00279 // helper function for sorting the list of applets 00280 bool appletConfigLessThan(const KConfigGroup &c1, const KConfigGroup &c2) 00281 { 00282 QPointF p1 = c1.readEntry("geometry", QRectF()).topLeft(); 00283 QPointF p2 = c2.readEntry("geometry", QRectF()).topLeft(); 00284 00285 if (!qFuzzyCompare(p1.x(), p2.x())) { 00286 if (QApplication::layoutDirection() == Qt::RightToLeft) { 00287 return p1.x() > p2.x(); 00288 } 00289 00290 return p1.x() < p2.x(); 00291 } 00292 00293 return qFuzzyCompare(p1.y(), p2.y()) || p1.y() < p2.y(); 00294 } 00295 00296 void Containment::restore(KConfigGroup &group) 00297 { 00298 /*kDebug() << "!!!!!!!!!!!!initConstraints" << group.name() << d->type; 00299 kDebug() << " location:" << group.readEntry("location", (int)d->location); 00300 kDebug() << " geom:" << group.readEntry("geometry", geometry()); 00301 kDebug() << " formfactor:" << group.readEntry("formfactor", (int)d->formFactor); 00302 kDebug() << " screen:" << group.readEntry("screen", d->screen);*/ 00303 if (!isContainment()) { 00304 Applet::restore(group); 00305 return; 00306 } 00307 00308 QRectF geo = group.readEntry("geometry", geometry()); 00309 //override max/min 00310 //this ensures panels are set to their saved size even when they have max & min set to prevent 00311 //resizing 00312 if (geo.size() != geo.size().boundedTo(maximumSize())) { 00313 setMaximumSize(maximumSize().expandedTo(geo.size())); 00314 } 00315 00316 if (geo.size() != geo.size().expandedTo(minimumSize())) { 00317 setMinimumSize(minimumSize().boundedTo(geo.size())); 00318 } 00319 00320 00321 resize(geo.size()); 00322 //are we an offscreen containment? 00323 if (containmentType() != PanelContainment && containmentType() != CustomPanelContainment && geo.right() < 0) { 00324 corona()->addOffscreenWidget(this); 00325 } 00326 00327 setLocation((Plasma::Location)group.readEntry("location", (int)d->location)); 00328 setFormFactor((Plasma::FormFactor)group.readEntry("formfactor", (int)d->formFactor)); 00329 //kDebug() << "setScreen from restore"; 00330 d->lastScreen = group.readEntry("lastScreen", d->lastScreen); 00331 d->lastDesktop = group.readEntry("lastDesktop", d->lastDesktop); 00332 d->setScreen(group.readEntry("screen", d->screen), group.readEntry("desktop", d->desktop), false); 00333 QString activityId = group.readEntry("activityId", QString()); 00334 if (!activityId.isEmpty()) { 00335 d->context()->setCurrentActivityId(activityId); 00336 } 00337 setActivity(group.readEntry("activity", QString())); 00338 00339 flushPendingConstraintsEvents(); 00340 restoreContents(group); 00341 setImmutability((ImmutabilityType)group.readEntry("immutability", (int)Mutable)); 00342 00343 setWallpaper(group.readEntry("wallpaperplugin", defaultWallpaper), 00344 group.readEntry("wallpaperpluginmode", defaultWallpaperMode)); 00345 00346 QMetaObject::invokeMethod(d->toolBox.data(), "restore", Q_ARG(KConfigGroup, group)); 00347 00348 KConfigGroup cfg; 00349 if (containmentType() == PanelContainment || containmentType() == CustomPanelContainment) { 00350 //don't let global desktop actions conflict with panels 00351 //this also prevents panels from sharing config with each other 00352 //but the panels aren't configurable anyways, and I doubt that'll change. 00353 d->containmentActionsSource = ContainmentPrivate::Local; 00354 cfg = KConfigGroup(&group, "ActionPlugins"); 00355 } else { 00356 QString source = group.readEntry("ActionPluginsSource", QString()); 00357 if (source == "Global") { 00358 cfg = KConfigGroup(corona()->config(), "ActionPlugins"); 00359 d->containmentActionsSource = ContainmentPrivate::Global; 00360 } else if (source == "Activity") { 00361 cfg = KConfigGroup(corona()->config(), "Activities"); 00362 cfg = KConfigGroup(&cfg, activityId); 00363 cfg = KConfigGroup(&cfg, "ActionPlugins"); 00364 d->containmentActionsSource = ContainmentPrivate::Activity; 00365 } else if (source == "Local") { 00366 cfg = group; 00367 d->containmentActionsSource = ContainmentPrivate::Local; 00368 } else { 00369 //default to global 00370 //but, if there is no global config, try copying it from local. 00371 cfg = KConfigGroup(corona()->config(), "ActionPlugins"); 00372 if (!cfg.exists()) { 00373 cfg = KConfigGroup(&group, "ActionPlugins"); 00374 } 00375 d->containmentActionsSource = ContainmentPrivate::Global; 00376 group.writeEntry("ActionPluginsSource", "Global"); 00377 } 00378 } 00379 //kDebug() << cfg.keyList(); 00380 if (cfg.exists()) { 00381 foreach (const QString &key, cfg.keyList()) { 00382 //kDebug() << "loading" << key; 00383 setContainmentActions(key, cfg.readEntry(key, QString())); 00384 } 00385 } else { //shell defaults 00386 ContainmentActionsPluginsConfig conf = corona()->containmentActionsDefaults(d->type); 00387 //steal the data directly, for efficiency 00388 QHash<QString,QString> defaults = conf.d->plugins; 00389 for (QHash<QString,QString>::const_iterator it = defaults.constBegin(), 00390 end = defaults.constEnd(); it != end; ++it) { 00391 setContainmentActions(it.key(), it.value()); 00392 } 00393 } 00394 00395 /* 00396 kDebug() << "Containment" << id() << 00397 "screen" << screen() << 00398 "geometry is" << geometry() << 00399 "wallpaper" << ((d->wallpaper) ? d->wallpaper->pluginName() : QString()) << 00400 "wallpaper mode" << wallpaperMode() << 00401 "config entries" << group.entryMap(); 00402 */ 00403 } 00404 00405 void Containment::save(KConfigGroup &g) const 00406 { 00407 if (Applet::d->transient) { 00408 return; 00409 } 00410 00411 KConfigGroup group = g; 00412 if (!group.isValid()) { 00413 group = config(); 00414 } 00415 00416 // locking is saved in Applet::save 00417 Applet::save(group); 00418 00419 if (!isContainment()) { 00420 return; 00421 } 00422 00423 group.writeEntry("screen", d->screen); 00424 group.writeEntry("lastScreen", d->lastScreen); 00425 group.writeEntry("desktop", d->desktop); 00426 group.writeEntry("lastDesktop", d->lastDesktop); 00427 group.writeEntry("formfactor", (int)d->formFactor); 00428 group.writeEntry("location", (int)d->location); 00429 group.writeEntry("activity", d->context()->currentActivity()); 00430 group.writeEntry("activityId", d->context()->currentActivityId()); 00431 00432 00433 QMetaObject::invokeMethod(d->toolBox.data(), "save", Q_ARG(KConfigGroup, group)); 00434 00435 00436 if (d->wallpaper) { 00437 group.writeEntry("wallpaperplugin", d->wallpaper->pluginName()); 00438 group.writeEntry("wallpaperpluginmode", d->wallpaper->renderingMode().name()); 00439 00440 if (d->wallpaper->isInitialized()) { 00441 KConfigGroup wallpaperConfig(&group, "Wallpaper"); 00442 wallpaperConfig = KConfigGroup(&wallpaperConfig, d->wallpaper->pluginName()); 00443 d->wallpaper->save(wallpaperConfig); 00444 } 00445 } 00446 00447 saveContents(group); 00448 } 00449 00450 void Containment::saveContents(KConfigGroup &group) const 00451 { 00452 KConfigGroup applets(&group, "Applets"); 00453 foreach (const Applet *applet, d->applets) { 00454 KConfigGroup appletConfig(&applets, QString::number(applet->id())); 00455 applet->save(appletConfig); 00456 } 00457 } 00458 00459 void ContainmentPrivate::initApplets() 00460 { 00461 foreach (Applet *applet, applets) { 00462 applet->restore(*applet->d->mainConfigGroup()); 00463 applet->init(); 00464 kDebug() << "!!{} STARTUP TIME" << QTime().msecsTo(QTime::currentTime()) << "Applet" << applet->name(); 00465 } 00466 00467 q->flushPendingConstraintsEvents(); 00468 00469 foreach (Applet *applet, applets) { 00470 applet->flushPendingConstraintsEvents(); 00471 } 00472 00473 kDebug() << "!!{} STARTUP TIME" << QTime().msecsTo(QTime::currentTime()) << "Containment's applets initialized" << q->name(); 00474 } 00475 00476 void Containment::restoreContents(KConfigGroup &group) 00477 { 00478 KConfigGroup applets(&group, "Applets"); 00479 00480 // Sort the applet configs in order of geometry to ensure that applets 00481 // are added from left to right or top to bottom for a panel containment 00482 QList<KConfigGroup> appletConfigs; 00483 foreach (const QString &appletGroup, applets.groupList()) { 00484 //kDebug() << "reading from applet group" << appletGroup; 00485 KConfigGroup appletConfig(&applets, appletGroup); 00486 appletConfigs.append(appletConfig); 00487 } 00488 qStableSort(appletConfigs.begin(), appletConfigs.end(), appletConfigLessThan); 00489 00490 QMutableListIterator<KConfigGroup> it(appletConfigs); 00491 while (it.hasNext()) { 00492 KConfigGroup &appletConfig = it.next(); 00493 int appId = appletConfig.name().toUInt(); 00494 QString plugin = appletConfig.readEntry("plugin", QString()); 00495 00496 if (plugin.isEmpty()) { 00497 continue; 00498 } 00499 00500 d->addApplet(plugin, QVariantList(), appletConfig.readEntry("geometry", QRectF()), appId, true); 00501 } 00502 } 00503 00504 Containment::Type Containment::containmentType() const 00505 { 00506 return d->type; 00507 } 00508 00509 void Containment::setContainmentType(Containment::Type type) 00510 { 00511 if (d->type == type) { 00512 return; 00513 } 00514 00515 delete d->toolBox.data(); 00516 d->type = type; 00517 d->checkContainmentFurniture(); 00518 } 00519 00520 void ContainmentPrivate::checkContainmentFurniture() 00521 { 00522 if (q->isContainment() && 00523 (type == Containment::DesktopContainment || type == Containment::PanelContainment)) { 00524 createToolBox(); 00525 } 00526 } 00527 00528 Corona *Containment::corona() const 00529 { 00530 return qobject_cast<Corona*>(scene()); 00531 } 00532 00533 void Containment::mouseMoveEvent(QGraphicsSceneMouseEvent *event) 00534 { 00535 event->ignore(); 00536 if (d->wallpaper && d->wallpaper->isInitialized()) { 00537 QGraphicsItem *item = scene()->itemAt(event->scenePos()); 00538 if (item == this) { 00539 d->wallpaper->mouseMoveEvent(event); 00540 } 00541 } 00542 00543 if (!event->isAccepted()) { 00544 event->accept(); 00545 Applet::mouseMoveEvent(event); 00546 } 00547 } 00548 00549 void Containment::mousePressEvent(QGraphicsSceneMouseEvent *event) 00550 { 00551 //close a toolbox if exists, to emulate qmenu behavior 00552 if (d->toolBox) { 00553 d->toolBox.data()->setShowing(false); 00554 } 00555 event->ignore(); 00556 if (d->appletAt(event->scenePos())) { 00557 return; //no unexpected click-throughs 00558 } 00559 00560 if (d->wallpaper && d->wallpaper->isInitialized() && !event->isAccepted()) { 00561 d->wallpaper->mousePressEvent(event); 00562 } 00563 00564 if (event->isAccepted()) { 00565 setFocus(Qt::MouseFocusReason); 00566 } else if (event->button() == Qt::RightButton && event->modifiers() == Qt::NoModifier) { 00567 // we'll catch this in the context menu even 00568 Applet::mousePressEvent(event); 00569 } else { 00570 QString trigger = ContainmentActions::eventToString(event); 00571 if (d->prepareContainmentActions(trigger, event->screenPos())) { 00572 d->actionPlugins()->value(trigger)->contextEvent(event); 00573 } 00574 00575 if (!event->isAccepted()) { 00576 Applet::mousePressEvent(event); 00577 } 00578 } 00579 } 00580 00581 void Containment::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) 00582 { 00583 event->ignore(); 00584 00585 if (d->appletAt(event->scenePos())) { 00586 return; //no unexpected click-throughs 00587 } 00588 00589 QString trigger = ContainmentActions::eventToString(event); 00590 00591 if (d->wallpaper && d->wallpaper->isInitialized()) { 00592 d->wallpaper->mouseReleaseEvent(event); 00593 } 00594 00595 if (!event->isAccepted() && isContainment()) { 00596 if (d->prepareContainmentActions(trigger, event->screenPos())) { 00597 d->actionPlugins()->value(trigger)->contextEvent(event); 00598 } 00599 00600 event->accept(); 00601 Applet::mouseReleaseEvent(event); 00602 } 00603 } 00604 00605 void Containment::showDropZone(const QPoint pos) 00606 { 00607 Q_UNUSED(pos) 00608 //Base implementation does nothing, don't put code here 00609 } 00610 00611 void Containment::showContextMenu(const QPointF &containmentPos, const QPoint &screenPos) 00612 { 00613 //kDebug() << containmentPos << screenPos; 00614 QGraphicsSceneContextMenuEvent gvevent; 00615 gvevent.setScreenPos(screenPos); 00616 gvevent.setScenePos(mapToScene(containmentPos)); 00617 gvevent.setPos(containmentPos); 00618 gvevent.setReason(QGraphicsSceneContextMenuEvent::Mouse); 00619 gvevent.setWidget(view()); 00620 contextMenuEvent(&gvevent); 00621 } 00622 00623 void Containment::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) 00624 { 00625 if (!isContainment() || !KAuthorized::authorizeKAction("plasma/containment_context_menu")) { 00626 Applet::contextMenuEvent(event); 00627 return; 00628 } 00629 00630 KMenu desktopMenu; 00631 Applet *applet = d->appletAt(event->scenePos()); 00632 //kDebug() << "context menu event " << (QObject*)applet; 00633 00634 if (applet) { 00635 d->addAppletActions(desktopMenu, applet, event); 00636 } else { 00637 d->addContainmentActions(desktopMenu, event); 00638 } 00639 00640 //kDebug() << "executing at" << screenPos; 00641 QMenu *menu = &desktopMenu; 00642 //kDebug() << "showing menu, actions" << desktopMenu.actions().size() << desktopMenu.actions().first()->menu(); 00643 if (desktopMenu.actions().size() == 1 && desktopMenu.actions().first()->menu()) { 00644 // we have a menu with a single top level menu; just show that top level menu instad. 00645 menu = desktopMenu.actions().first()->menu(); 00646 } 00647 00648 if (!menu->isEmpty()) { 00649 QPoint pos = event->screenPos(); 00650 if (applet && d->isPanelContainment()) { 00651 menu->adjustSize(); 00652 pos = applet->popupPosition(menu->size()); 00653 if (event->reason() == QGraphicsSceneContextMenuEvent::Mouse) { 00654 // if the menu pops up way away from the mouse press, then move it 00655 // to the mouse press 00656 if (d->formFactor == Vertical) { 00657 if (pos.y() + menu->height() < event->screenPos().y()) { 00658 pos.setY(event->screenPos().y()); 00659 } 00660 } else if (d->formFactor == Horizontal) { 00661 if (pos.x() + menu->width() < event->screenPos().x()) { 00662 pos.setX(event->screenPos().x()); 00663 } 00664 } 00665 } 00666 } 00667 00668 menu->exec(pos); 00669 event->accept(); 00670 } else { 00671 Applet::contextMenuEvent(event); 00672 } 00673 } 00674 00675 void ContainmentPrivate::addContainmentActions(KMenu &desktopMenu, QEvent *event) 00676 { 00677 if (static_cast<Corona*>(q->scene())->immutability() != Mutable && 00678 !KAuthorized::authorizeKAction("plasma/containment_actions")) { 00679 //kDebug() << "immutability"; 00680 return; 00681 } 00682 00683 const QString trigger = ContainmentActions::eventToString(event); 00684 prepareContainmentActions(trigger, QPoint(), &desktopMenu); 00685 } 00686 00687 void ContainmentPrivate::addAppletActions(KMenu &desktopMenu, Applet *applet, QEvent *event) 00688 { 00689 foreach (QAction *action, applet->contextualActions()) { 00690 if (action) { 00691 desktopMenu.addAction(action); 00692 } 00693 } 00694 00695 if (!applet->d->failed) { 00696 QAction *configureApplet = applet->d->actions->action("configure"); 00697 if (configureApplet && configureApplet->isEnabled()) { 00698 desktopMenu.addAction(configureApplet); 00699 } 00700 00701 QAction *runAssociatedApplication = applet->d->actions->action("run associated application"); 00702 if (runAssociatedApplication && runAssociatedApplication->isEnabled()) { 00703 desktopMenu.addAction(runAssociatedApplication); 00704 } 00705 } 00706 00707 KMenu *containmentMenu = new KMenu(i18nc("%1 is the name of the containment", "%1 Options", q->name()), &desktopMenu); 00708 addContainmentActions(*containmentMenu, event); 00709 if (!containmentMenu->isEmpty()) { 00710 int enabled = 0; 00711 //count number of real actions 00712 QListIterator<QAction *> actionsIt(containmentMenu->actions()); 00713 while (enabled < 3 && actionsIt.hasNext()) { 00714 QAction *action = actionsIt.next(); 00715 if (action->isVisible() && !action->isSeparator()) { 00716 ++enabled; 00717 } 00718 } 00719 00720 if (enabled) { 00721 //if there is only one, don't create a submenu 00722 if (enabled < 2) { 00723 foreach (QAction *action, containmentMenu->actions()) { 00724 if (action->isVisible() && !action->isSeparator()) { 00725 desktopMenu.addAction(action); 00726 } 00727 } 00728 } else { 00729 desktopMenu.addMenu(containmentMenu); 00730 } 00731 } 00732 } 00733 00734 if (q->immutability() == Mutable) { 00735 QAction *closeApplet = applet->d->actions->action("remove"); 00736 //kDebug() << "checking for removal" << closeApplet; 00737 if (closeApplet) { 00738 if (!desktopMenu.isEmpty()) { 00739 desktopMenu.addSeparator(); 00740 } 00741 00742 //kDebug() << "adding close action" << closeApplet->isEnabled() << closeApplet->isVisible(); 00743 desktopMenu.addAction(closeApplet); 00744 } 00745 } 00746 } 00747 00748 Applet* ContainmentPrivate::appletAt(const QPointF &point) 00749 { 00750 Applet *applet = 0; 00751 00752 QGraphicsItem *item = q->scene()->itemAt(point); 00753 if (item == q) { 00754 item = 0; 00755 } 00756 00757 while (item) { 00758 if (item->isWidget()) { 00759 applet = qobject_cast<Applet*>(static_cast<QGraphicsWidget*>(item)); 00760 if (applet) { 00761 if (applet->isContainment()) { 00762 applet = 0; 00763 } 00764 break; 00765 } 00766 } 00767 AppletHandle *handle = dynamic_cast<AppletHandle*>(item); 00768 if (handle) { 00769 //pretend it was on the applet 00770 applet = handle->applet(); 00771 break; 00772 } 00773 item = item->parentItem(); 00774 } 00775 return applet; 00776 } 00777 00778 void Containment::setFormFactor(FormFactor formFactor) 00779 { 00780 if (d->formFactor == formFactor) { 00781 return; 00782 } 00783 00784 //kDebug() << "switching FF to " << formFactor; 00785 d->formFactor = formFactor; 00786 00787 if (isContainment() && 00788 (d->type == PanelContainment || d->type == CustomPanelContainment)) { 00789 // we are a panel and we have chaged our orientation 00790 d->positionPanel(true); 00791 } 00792 00793 QMetaObject::invokeMethod(d->toolBox.data(), "reposition"); 00794 00795 updateConstraints(Plasma::FormFactorConstraint); 00796 00797 KConfigGroup c = config(); 00798 c.writeEntry("formfactor", (int)formFactor); 00799 emit configNeedsSaving(); 00800 } 00801 00802 void Containment::setLocation(Location location) 00803 { 00804 if (d->location == location) { 00805 return; 00806 } 00807 00808 bool emitGeomChange = false; 00809 00810 if ((location == TopEdge || location == BottomEdge) && 00811 (d->location == TopEdge || d->location == BottomEdge)) { 00812 emitGeomChange = true; 00813 } 00814 00815 if ((location == RightEdge || location == LeftEdge) && 00816 (d->location == RightEdge || d->location == LeftEdge)) { 00817 emitGeomChange = true; 00818 } 00819 00820 d->location = location; 00821 00822 foreach (Applet *applet, d->applets) { 00823 applet->updateConstraints(Plasma::LocationConstraint); 00824 } 00825 00826 if (emitGeomChange) { 00827 // our geometry on the scene will not actually change, 00828 // but for the purposes of views it has 00829 emit geometryChanged(); 00830 } 00831 00832 updateConstraints(Plasma::LocationConstraint); 00833 00834 KConfigGroup c = config(); 00835 c.writeEntry("location", (int)location); 00836 emit configNeedsSaving(); 00837 } 00838 00839 void Containment::addSiblingContainment() 00840 { 00841 emit addSiblingContainment(this); 00842 } 00843 00844 void Containment::clearApplets() 00845 { 00846 foreach (Applet *applet, d->applets) { 00847 applet->d->cleanUpAndDelete(); 00848 } 00849 00850 d->applets.clear(); 00851 } 00852 00853 Applet *Containment::addApplet(const QString &name, const QVariantList &args, 00854 const QRectF &appletGeometry) 00855 { 00856 return d->addApplet(name, args, appletGeometry); 00857 } 00858 00859 void Containment::addApplet(Applet *applet, const QPointF &pos, bool delayInit) 00860 { 00861 if (!isContainment() || (!delayInit && immutability() != Mutable)) { 00862 return; 00863 } 00864 00865 if (!applet) { 00866 kDebug() << "adding null applet!?!"; 00867 return; 00868 } 00869 00870 if (d->applets.contains(applet)) { 00871 kDebug() << "already have this applet!"; 00872 } 00873 00874 Containment *currentContainment = applet->containment(); 00875 00876 if (d->type == PanelContainment) { 00877 //panels don't want backgrounds, which is important when setting geometry 00878 setBackgroundHints(NoBackground); 00879 } 00880 00881 if (currentContainment && currentContainment != this) { 00882 emit currentContainment->appletRemoved(applet); 00883 if (currentContainment->d->focusedApplet == applet) { 00884 currentContainment->d->focusedApplet = 0; 00885 } 00886 00887 disconnect(applet, 0, currentContainment, 0); 00888 KConfigGroup oldConfig = applet->config(); 00889 currentContainment->d->applets.removeAll(applet); 00890 applet->setParentItem(this); 00891 applet->setParent(this); 00892 00893 // now move the old config to the new location 00894 //FIXME: this doesn't seem to get the actual main config group containing plugin=, etc 00895 KConfigGroup c = config().group("Applets").group(QString::number(applet->id())); 00896 oldConfig.reparent(&c); 00897 applet->d->resetConfigurationObject(); 00898 00899 disconnect(applet, SIGNAL(activate()), currentContainment, SIGNAL(activate())); 00900 } else { 00901 applet->setParentItem(this); 00902 applet->setParent(this); 00903 } 00904 00905 d->applets << applet; 00906 00907 connect(applet, SIGNAL(configNeedsSaving()), this, SIGNAL(configNeedsSaving())); 00908 connect(applet, SIGNAL(releaseVisualFocus()), this, SIGNAL(releaseVisualFocus())); 00909 connect(applet, SIGNAL(appletDestroyed(Plasma::Applet*)), this, SLOT(appletDestroyed(Plasma::Applet*))); 00910 connect(applet, SIGNAL(newStatus(Plasma::ItemStatus)), this, SLOT(checkStatus(Plasma::ItemStatus))); 00911 connect(applet, SIGNAL(activate()), this, SIGNAL(activate())); 00912 00913 if (pos != QPointF(-1, -1)) { 00914 applet->setPos(pos); 00915 } 00916 00917 if (!delayInit && !currentContainment) { 00918 applet->restore(*applet->d->mainConfigGroup()); 00919 applet->init(); 00920 Plasma::Animation *anim = Plasma::Animator::create(Plasma::Animator::AppearAnimation); 00921 if (anim) { 00922 connect(anim, SIGNAL(finished()), this, SLOT(appletAppearAnimationComplete())); 00923 anim->setTargetWidget(applet); 00924 //FIXME: small hack until we have proper js anim support; allows 'zoom' to work in the 00925 //'right' direction for appearance 00926 anim->setDirection(QAbstractAnimation::Backward); 00927 anim->start(QAbstractAnimation::DeleteWhenStopped); 00928 } else { 00929 d->appletAppeared(applet); 00930 } 00931 } 00932 00933 applet->setFlag(QGraphicsItem::ItemIsMovable, true); 00934 applet->updateConstraints(Plasma::AllConstraints); 00935 if (!delayInit) { 00936 applet->flushPendingConstraintsEvents(); 00937 } 00938 emit appletAdded(applet, pos); 00939 00940 if (!currentContainment) { 00941 applet->updateConstraints(Plasma::StartupCompletedConstraint); 00942 if (!delayInit) { 00943 applet->flushPendingConstraintsEvents(); 00944 } 00945 } 00946 00947 if (!delayInit) { 00948 applet->d->scheduleModificationNotification(); 00949 } 00950 } 00951 00952 Applet::List Containment::applets() const 00953 { 00954 return d->applets; 00955 } 00956 00957 void Containment::setScreen(int newScreen, int newDesktop) 00958 { 00959 d->setScreen(newScreen, newDesktop); 00960 } 00961 00962 void ContainmentPrivate::setScreen(int newScreen, int newDesktop, bool preventInvalidDesktops) 00963 { 00964 // What we want to do in here is: 00965 // * claim the screen as our own 00966 // * signal whatever may be watching this containment about the switch 00967 // * if we are a full screen containment, then: 00968 // * resize to match the screen if we're that kind of containment 00969 // * kick other full-screen containments off this screen 00970 // * if we had a screen, then give our screen to the containment 00971 // we kick out 00972 // 00973 // a screen of -1 means no associated screen. 00974 Corona *corona = q->corona(); 00975 Q_ASSERT(corona); 00976 00977 //if it's an offscreen widget, don't allow to claim a screen, after all it's *off*screen 00978 if (corona->offscreenWidgets().contains(q)) { 00979 return; 00980 } 00981 00982 int numScreens = corona->numScreens(); 00983 if (newScreen < -1) { 00984 newScreen = -1; 00985 } 00986 00987 // -1 == All desktops 00988 if (newDesktop < -1 || (preventInvalidDesktops && newDesktop > KWindowSystem::numberOfDesktops() - 1)) { 00989 newDesktop = -1; 00990 } 00991 00992 //kDebug() << activity() << "setting screen to " << newScreen << newDesktop << "and type is" << type; 00993 00994 Containment *swapScreensWith(0); 00995 const bool isDesktopContainment = type == Containment::DesktopContainment || 00996 type == Containment::CustomContainment; 00997 if (isDesktopContainment) { 00998 // we want to listen to changes in work area if our screen changes 00999 if (toolBox) { 01000 if (screen < 0 && newScreen > -1) { 01001 QObject::connect(KWindowSystem::self(), SIGNAL(workAreaChanged()), toolBox.data(), SLOT(reposition()), Qt::UniqueConnection); 01002 } else if (newScreen < 0) { 01003 QObject::disconnect(KWindowSystem::self(), SIGNAL(workAreaChanged()), toolBox.data(), SLOT(reposition())); 01004 } 01005 } 01006 01007 if (newScreen > -1) { 01008 // sanity check to make sure someone else doesn't have this screen already! 01009 Containment *currently = corona->containmentForScreen(newScreen, newDesktop); 01010 if (currently && currently != q) { 01011 kDebug() << "currently is on screen" << currently->screen() 01012 << "desktop" << currently->desktop() 01013 << "and is" << currently->activity() 01014 << (QObject*)currently << "i'm" << (QObject*)q; 01015 currently->setScreen(-1, currently->desktop()); 01016 swapScreensWith = currently; 01017 } 01018 } 01019 } 01020 01021 if (newScreen < numScreens && newScreen > -1 && isDesktopContainment) { 01022 q->resize(corona->screenGeometry(newScreen).size()); 01023 } 01024 01025 int oldDesktop = desktop; 01026 desktop = newDesktop; 01027 01028 int oldScreen = screen; 01029 screen = newScreen; 01030 01031 q->updateConstraints(Plasma::ScreenConstraint); 01032 01033 if (oldScreen != newScreen || oldDesktop != newDesktop) { 01034 /* 01035 kDebug() << "going to signal change for" << q 01036 << ", old screen & desktop:" << oldScreen << oldDesktop 01037 << ", new:" << screen << desktop; 01038 */ 01039 KConfigGroup c = q->config(); 01040 c.writeEntry("screen", screen); 01041 c.writeEntry("desktop", desktop); 01042 if (newScreen != -1) { 01043 lastScreen = newScreen; 01044 lastDesktop = newDesktop; 01045 c.writeEntry("lastScreen", lastScreen); 01046 c.writeEntry("lastDesktop", lastDesktop); 01047 } 01048 emit q->configNeedsSaving(); 01049 emit q->screenChanged(oldScreen, newScreen, q); 01050 } 01051 01052 if (swapScreensWith) { 01053 //kDebug() << "setScreen due to swap, part 2"; 01054 swapScreensWith->setScreen(oldScreen, oldDesktop); 01055 } 01056 01057 checkRemoveAction(); 01058 01059 if (newScreen >= 0) { 01060 emit q->activate(); 01061 } 01062 } 01063 01064 int Containment::screen() const 01065 { 01066 return d->screen; 01067 } 01068 01069 int Containment::lastScreen() const 01070 { 01071 return d->lastScreen; 01072 } 01073 01074 int Containment::desktop() const 01075 { 01076 return d->desktop; 01077 } 01078 01079 int Containment::lastDesktop() const 01080 { 01081 return d->lastDesktop; 01082 } 01083 01084 KPluginInfo::List Containment::listContainments(const QString &category, 01085 const QString &parentApp) 01086 { 01087 return listContainmentsOfType(QString(), category, parentApp); 01088 } 01089 01090 01091 KPluginInfo::List Containment::listContainmentsOfType(const QString &type, 01092 const QString &category, 01093 const QString &parentApp) 01094 { 01095 QString constraint; 01096 01097 if (parentApp.isEmpty()) { 01098 constraint.append("(not exist [X-KDE-ParentApp] or [X-KDE-ParentApp] == '')"); 01099 } else { 01100 constraint.append("[X-KDE-ParentApp] == '").append(parentApp).append("'"); 01101 } 01102 01103 if (!type.isEmpty()) { 01104 if (!constraint.isEmpty()) { 01105 constraint.append(" and "); 01106 } 01107 01108 constraint.append("'").append(type).append("' ~in [X-Plasma-ContainmentCategories]"); 01109 } 01110 01111 if (!category.isEmpty()) { 01112 if (!constraint.isEmpty()) { 01113 constraint.append(" and "); 01114 } 01115 01116 constraint.append("[X-KDE-PluginInfo-Category] == '").append(category).append("'"); 01117 if (category == "Miscellaneous") { 01118 constraint.append(" or (not exist [X-KDE-PluginInfo-Category] or [X-KDE-PluginInfo-Category] == '')"); 01119 } 01120 } 01121 01122 KService::List offers = KServiceTypeTrader::self()->query("Plasma/Containment", constraint); 01123 //kDebug() << "constraint was" << constraint << "which got us" << offers.count() << "matches"; 01124 return KPluginInfo::fromServices(offers); 01125 } 01126 01127 KPluginInfo::List Containment::listContainmentsForMimetype(const QString &mimetype) 01128 { 01129 const QString constraint = QString("'%1' in [X-Plasma-DropMimeTypes]").arg(mimetype); 01130 //kDebug() << mimetype << constraint; 01131 const KService::List offers = KServiceTypeTrader::self()->query("Plasma/Containment", constraint); 01132 return KPluginInfo::fromServices(offers); 01133 } 01134 01135 QStringList Containment::listContainmentTypes() 01136 { 01137 KPluginInfo::List containmentInfos = listContainments(); 01138 QSet<QString> types; 01139 01140 foreach (const KPluginInfo &containmentInfo, containmentInfos) { 01141 QStringList theseTypes = containmentInfo.service()->property("X-Plasma-ContainmentCategories").toStringList(); 01142 foreach (const QString &type, theseTypes) { 01143 types.insert(type); 01144 } 01145 } 01146 01147 return types.toList(); 01148 } 01149 01150 void Containment::dragEnterEvent(QGraphicsSceneDragDropEvent *event) 01151 { 01152 //kDebug() << immutability() << Mutable << (immutability() == Mutable); 01153 event->setAccepted(immutability() == Mutable && 01154 (event->mimeData()->hasFormat(static_cast<Corona*>(scene())->appletMimeType()) || 01155 KUrl::List::canDecode(event->mimeData()) || 01156 event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType()))); 01157 01158 if (!event->isAccepted()) { 01159 // check to see if we have an applet that accepts the format. 01160 QStringList formats = event->mimeData()->formats(); 01161 01162 foreach (const QString &format, formats) { 01163 KPluginInfo::List appletList = Applet::listAppletInfoForMimetype(format); 01164 if (!appletList.isEmpty()) { 01165 event->setAccepted(true); 01166 break; 01167 } 01168 } 01169 01170 if (!event->isAccepted()) { 01171 foreach (const QString &format, formats) { 01172 KPluginInfo::List wallpaperList = Wallpaper::listWallpaperInfoForMimetype(format); 01173 if (!wallpaperList.isEmpty()) { 01174 event->setAccepted(true); 01175 break; 01176 } 01177 } 01178 } 01179 } 01180 01181 if (event->isAccepted()) { 01182 if (d->dropZoneStarted) { 01183 showDropZone(event->pos().toPoint()); 01184 } else { 01185 if (!d->showDropZoneDelayTimer) { 01186 d->showDropZoneDelayTimer = new QTimer(this); 01187 d->showDropZoneDelayTimer->setInterval(300); 01188 d->showDropZoneDelayTimer->setSingleShot(true); 01189 connect(d->showDropZoneDelayTimer, SIGNAL(timeout()), this, SLOT(showDropZoneDelayed())); 01190 } 01191 01192 d->dropPoints.insert(0, event->pos()); 01193 d->showDropZoneDelayTimer->start(); 01194 } 01195 } 01196 } 01197 01198 void Containment::dragLeaveEvent(QGraphicsSceneDragDropEvent *event) 01199 { 01200 //kDebug() << event->pos() << size().height() << size().width(); 01201 if (d->showDropZoneDelayTimer) { 01202 d->showDropZoneDelayTimer->stop(); 01203 } 01204 01205 if (event->pos().y() < 1 || event->pos().y() > size().height() || 01206 event->pos().x() < 1 || event->pos().x() > size().width()) { 01207 showDropZone(QPoint()); 01208 d->dropZoneStarted = false; 01209 } 01210 } 01211 01212 void ContainmentPrivate::showDropZoneDelayed() 01213 { 01214 dropZoneStarted = true; 01215 q->showDropZone(dropPoints.value(0).toPoint()); 01216 dropPoints.remove(0); 01217 } 01218 01219 void Containment::dragMoveEvent(QGraphicsSceneDragDropEvent *event) 01220 { 01221 QGraphicsItem *item = scene()->itemAt(event->scenePos()); 01222 event->setAccepted(item == this || item == d->toolBox.data() || !item); 01223 //kDebug() << event->isAccepted() << d->showDropZoneDelayTimer->isActive(); 01224 if (!event->isAccepted()) { 01225 if (d->showDropZoneDelayTimer) { 01226 d->showDropZoneDelayTimer->stop(); 01227 } 01228 } else if (!d->showDropZoneDelayTimer->isActive() && immutability() == Plasma::Mutable) { 01229 showDropZone(event->pos().toPoint()); 01230 } 01231 } 01232 01233 void Containment::dropEvent(QGraphicsSceneDragDropEvent *event) 01234 { 01235 if (isContainment()) { 01236 d->dropData(event->scenePos(), event->screenPos(), event); 01237 } else { 01238 Applet::dropEvent(event); 01239 } 01240 } 01241 01242 void ContainmentPrivate::dropData(QPointF scenePos, QPoint screenPos, QGraphicsSceneDragDropEvent *dropEvent) 01243 { 01244 if (q->immutability() != Mutable) { 01245 return; 01246 } 01247 01248 QPointF pos = q->mapFromScene(scenePos); 01249 const QMimeData *mimeData = 0; 01250 01251 if (dropEvent) { 01252 mimeData = dropEvent->mimeData(); 01253 } else { 01254 QClipboard *clipboard = QApplication::clipboard(); 01255 mimeData = clipboard->mimeData(QClipboard::Selection); 01256 //TODO if that's not supported (ie non-linux) should we try clipboard instead of selection? 01257 } 01258 01259 if (!mimeData) { 01260 //Selection is either empty or not supported on this OS 01261 kDebug() << "no mime data"; 01262 return; 01263 } 01264 01265 //kDebug() << event->mimeData()->text(); 01266 01267 QString appletMimetype(q->corona() ? q->corona()->appletMimeType() : QString()); 01268 01269 if (!appletMimetype.isEmpty() && mimeData->hasFormat(appletMimetype)) { 01270 QString data = mimeData->data(appletMimetype); 01271 const QStringList appletNames = data.split('\n', QString::SkipEmptyParts); 01272 foreach (const QString &appletName, appletNames) { 01273 //kDebug() << "doing" << appletName; 01274 QRectF geom(pos, QSize(0, 0)); 01275 q->addApplet(appletName, QVariantList(), geom); 01276 } 01277 if (dropEvent) { 01278 dropEvent->acceptProposedAction(); 01279 } 01280 } else if (mimeData->hasFormat(ExtenderItemMimeData::mimeType())) { 01281 kDebug() << "mimetype plasma/extenderitem is dropped, creating internal:extender"; 01282 //Handle dropping extenderitems. 01283 const ExtenderItemMimeData *extenderData = qobject_cast<const ExtenderItemMimeData*>(mimeData); 01284 if (extenderData) { 01285 ExtenderItem *item = extenderData->extenderItem(); 01286 QRectF geometry(pos - extenderData->pointerOffset(), item->size()); 01287 kDebug() << "desired geometry: " << geometry; 01288 Applet *applet = qobject_cast<ExtenderApplet *>(item->extender() ? item->extender()->applet() : 0); 01289 if (applet) { 01290 qreal left, top, right, bottom; 01291 applet->getContentsMargins(&left, &top, &right, &bottom); 01292 applet->setPos(geometry.topLeft() - QPointF(int(left), int(top))); 01293 applet->show(); 01294 } else { 01295 applet = addApplet("internal:extender", QVariantList(), geometry, 0, true); 01296 applet->hide(); 01297 applet->init(); 01298 appletAppeared(applet); 01299 applet->flushPendingConstraintsEvents(); 01300 applet->d->scheduleModificationNotification(); 01301 applet->adjustSize(); 01302 applet->show(); 01303 } 01304 item->setExtender(applet->extender()); 01305 } 01306 } else if (KUrl::List::canDecode(mimeData)) { 01307 //TODO: collect the mimetypes of available script engines and offer 01308 // to create widgets out of the matching URLs, if any 01309 const KUrl::List urls = KUrl::List::fromMimeData(mimeData); 01310 foreach (const KUrl &url, urls) { 01311 if (AccessManager::supportedProtocols().contains(url.protocol())) { 01312 AccessAppletJob *job = AccessManager::self()->accessRemoteApplet(url); 01313 if (dropEvent) { 01314 dropPoints[job] = dropEvent->pos(); 01315 } else { 01316 dropPoints[job] = scenePos; 01317 } 01318 QObject::connect(AccessManager::self(), SIGNAL(finished(Plasma::AccessAppletJob*)), 01319 q, SLOT(remoteAppletReady(Plasma::AccessAppletJob*))); 01320 } 01321 #ifndef PLASMA_NO_KIO 01322 else { 01323 KMimeType::Ptr mime = KMimeType::findByUrl(url); 01324 QString mimeName = mime->name(); 01325 QRectF geom(pos, QSize()); 01326 QVariantList args; 01327 args << url.url(); 01328 kDebug() << "can decode" << mimeName << args; 01329 01330 // It may be a directory or a file, let's stat 01331 KIO::JobFlags flags = KIO::HideProgressInfo; 01332 KIO::MimetypeJob *job = KIO::mimetype(url, flags); 01333 if (dropEvent) { 01334 dropPoints[job] = dropEvent->pos(); 01335 } else { 01336 dropPoints[job] = scenePos; 01337 } 01338 01339 QObject::connect(job, SIGNAL(result(KJob*)), q, SLOT(dropJobResult(KJob*))); 01340 QObject::connect(job, SIGNAL(mimetype(KIO::Job*,QString)), 01341 q, SLOT(mimeTypeRetrieved(KIO::Job*,QString))); 01342 01343 KMenu *choices = new KMenu("Content dropped"); 01344 choices->addAction(KIcon("process-working"), i18n("Fetching file type...")); 01345 if (dropEvent) { 01346 choices->popup(dropEvent->screenPos()); 01347 } else { 01348 choices->popup(screenPos); 01349 } 01350 01351 dropMenus[job] = choices; 01352 } 01353 #endif 01354 } 01355 01356 if (dropEvent) { 01357 dropEvent->acceptProposedAction(); 01358 } 01359 } else { 01360 QStringList formats = mimeData->formats(); 01361 QHash<QString, KPluginInfo> seenPlugins; 01362 QHash<QString, QString> pluginFormats; 01363 01364 foreach (const QString &format, formats) { 01365 KPluginInfo::List plugins = Applet::listAppletInfoForMimetype(format); 01366 01367 foreach (const KPluginInfo &plugin, plugins) { 01368 if (seenPlugins.contains(plugin.pluginName())) { 01369 continue; 01370 } 01371 01372 seenPlugins.insert(plugin.pluginName(), plugin); 01373 pluginFormats.insert(plugin.pluginName(), format); 01374 } 01375 } 01376 //kDebug() << "Mimetype ..." << formats << seenPlugins.keys() << pluginFormats.values(); 01377 01378 QString selectedPlugin; 01379 01380 if (seenPlugins.isEmpty()) { 01381 // do nothing 01382 } else if (seenPlugins.count() == 1) { 01383 selectedPlugin = seenPlugins.constBegin().key(); 01384 } else { 01385 KMenu choices; 01386 QHash<QAction *, QString> actionsToPlugins; 01387 foreach (const KPluginInfo &info, seenPlugins) { 01388 QAction *action; 01389 if (!info.icon().isEmpty()) { 01390 action = choices.addAction(KIcon(info.icon()), info.name()); 01391 } else { 01392 action = choices.addAction(info.name()); 01393 } 01394 01395 actionsToPlugins.insert(action, info.pluginName()); 01396 } 01397 01398 QAction *choice = choices.exec(screenPos); 01399 if (choice) { 01400 selectedPlugin = actionsToPlugins[choice]; 01401 } 01402 } 01403 01404 if (!selectedPlugin.isEmpty()) { 01405 if (!dropEvent) { 01406 // since we may have entered an event loop up above with the menu, 01407 // the clipboard item may no longer be valid, as QClipboard resets 01408 // the object behind the back of the application with a zero timer 01409 // so we fetch it again here 01410 QClipboard *clipboard = QApplication::clipboard(); 01411 mimeData = clipboard->mimeData(QClipboard::Selection); 01412 } 01413 01414 KTemporaryFile tempFile; 01415 if (mimeData && tempFile.open()) { 01416 //TODO: what should we do with files after the applet is done with them?? 01417 tempFile.setAutoRemove(false); 01418 01419 { 01420 QDataStream stream(&tempFile); 01421 QByteArray data = mimeData->data(pluginFormats[selectedPlugin]); 01422 stream.writeRawData(data, data.size()); 01423 } 01424 01425 QRectF geom(pos, QSize()); 01426 QVariantList args; 01427 args << tempFile.fileName(); 01428 kDebug() << args; 01429 tempFile.close(); 01430 01431 q->addApplet(selectedPlugin, args, geom); 01432 } 01433 } 01434 } 01435 } 01436 01437 void ContainmentPrivate::clearDataForMimeJob(KIO::Job *job) 01438 { 01439 #ifndef PLASMA_NO_KIO 01440 QObject::disconnect(job, 0, q, 0); 01441 dropPoints.remove(job); 01442 KMenu *choices = dropMenus.take(job); 01443 delete choices; 01444 job->kill(); 01445 #endif // PLASMA_NO_KIO 01446 } 01447 01448 void ContainmentPrivate::remoteAppletReady(Plasma::AccessAppletJob *job) 01449 { 01450 QPointF pos = dropPoints.take(job); 01451 if (job->error()) { 01452 //TODO: nice user visible error handling (knotification probably?) 01453 kDebug() << "remote applet access failed: " << job->errorText(); 01454 return; 01455 } 01456 01457 if (!job->applet()) { 01458 kDebug() << "how did we end up here? if applet is null, the job->error should be nonzero"; 01459 return; 01460 } 01461 01462 q->addApplet(job->applet(), pos); 01463 } 01464 01465 void ContainmentPrivate::dropJobResult(KJob *job) 01466 { 01467 #ifndef PLASMA_NO_KIO 01468 KIO::TransferJob* tjob = dynamic_cast<KIO::TransferJob*>(job); 01469 if (!tjob) { 01470 kDebug() << "job is not a KIO::TransferJob, won't handle the drop..."; 01471 clearDataForMimeJob(tjob); 01472 return; 01473 } 01474 if (job->error()) { 01475 kDebug() << "ERROR" << tjob->error() << ' ' << tjob->errorString(); 01476 } 01477 // We call mimetypeRetrieved since there might be other mechanisms 01478 // for finding suitable applets. Cleanup happens there as well. 01479 mimeTypeRetrieved(qobject_cast<KIO::Job *>(job), QString()); 01480 #endif // PLASMA_NO_KIO 01481 } 01482 01483 void ContainmentPrivate::mimeTypeRetrieved(KIO::Job *job, const QString &mimetype) 01484 { 01485 #ifndef PLASMA_NO_KIO 01486 kDebug() << "Mimetype Job returns." << mimetype; 01487 KIO::TransferJob* tjob = dynamic_cast<KIO::TransferJob*>(job); 01488 if (!tjob) { 01489 kDebug() << "job should be a TransferJob, but isn't"; 01490 clearDataForMimeJob(job); 01491 return; 01492 } 01493 KPluginInfo::List appletList = Applet::listAppletInfoForUrl(tjob->url()); 01494 if (mimetype.isEmpty() && !appletList.count()) { 01495 clearDataForMimeJob(job); 01496 kDebug() << "No applets found matching the url (" << tjob->url() << ") or the mimetype (" << mimetype << ")"; 01497 return; 01498 } else { 01499 01500 QPointF posi; // will be overwritten with the event's position 01501 if (dropPoints.keys().contains(tjob)) { 01502 posi = dropPoints[tjob]; 01503 kDebug() << "Received a suitable dropEvent at" << posi; 01504 } else { 01505 kDebug() << "Bailing out. Cannot find associated dropEvent related to the TransferJob"; 01506 clearDataForMimeJob(job); 01507 return; 01508 } 01509 01510 KMenu *choices = dropMenus.value(tjob); 01511 if (!choices) { 01512 kDebug() << "Bailing out. No QMenu found for this job."; 01513 clearDataForMimeJob(job); 01514 return; 01515 } 01516 01517 QVariantList args; 01518 args << tjob->url().url() << mimetype; 01519 01520 kDebug() << "Creating menu for:" << mimetype << posi << args; 01521 01522 appletList << Applet::listAppletInfoForMimetype(mimetype); 01523 KPluginInfo::List wallpaperList; 01524 if (drawWallpaper) { 01525 if (wallpaper && wallpaper->supportsMimetype(mimetype)) { 01526 wallpaperList << wallpaper->d->wallpaperDescription; 01527 } else { 01528 wallpaperList = Wallpaper::listWallpaperInfoForMimetype(mimetype); 01529 } 01530 } 01531 01532 if (!appletList.isEmpty() || !wallpaperList.isEmpty()) { 01533 choices->clear(); 01534 QHash<QAction *, QString> actionsToApplets; 01535 choices->addTitle(i18n("Widgets")); 01536 foreach (const KPluginInfo &info, appletList) { 01537 kDebug() << info.name(); 01538 QAction *action; 01539 if (!info.icon().isEmpty()) { 01540 action = choices->addAction(KIcon(info.icon()), info.name()); 01541 } else { 01542 action = choices->addAction(info.name()); 01543 } 01544 01545 actionsToApplets.insert(action, info.pluginName()); 01546 kDebug() << info.pluginName(); 01547 } 01548 actionsToApplets.insert(choices->addAction(i18n("Icon")), "icon"); 01549 01550 QHash<QAction *, QString> actionsToWallpapers; 01551 if (!wallpaperList.isEmpty()) { 01552 choices->addTitle(i18n("Wallpaper")); 01553 01554 QMap<QString, KPluginInfo> sorted; 01555 foreach (const KPluginInfo &info, appletList) { 01556 sorted.insert(info.name(), info); 01557 } 01558 01559 foreach (const KPluginInfo &info, wallpaperList) { 01560 QAction *action; 01561 if (!info.icon().isEmpty()) { 01562 action = choices->addAction(KIcon(info.icon()), info.name()); 01563 } else { 01564 action = choices->addAction(info.name()); 01565 } 01566 01567 actionsToWallpapers.insert(action, info.pluginName()); 01568 } 01569 } 01570 01571 QAction *choice = choices->exec(); 01572 if (choice) { 01573 // Put the job on hold so it can be recycled to fetch the actual content, 01574 // which is to be expected when something's dropped onto the desktop and 01575 // an applet is to be created with this URL 01576 if (!mimetype.isEmpty() && !tjob->error()) { 01577 tjob->putOnHold(); 01578 KIO::Scheduler::publishSlaveOnHold(); 01579 } 01580 QString plugin = actionsToApplets.value(choice); 01581 if (plugin.isEmpty()) { 01582 //set wallpapery stuff 01583 plugin = actionsToWallpapers.value(choice); 01584 if (!wallpaper || plugin != wallpaper->pluginName()) { 01585 kDebug() << "Wallpaper dropped:" << tjob->url(); 01586 q->setWallpaper(plugin); 01587 } 01588 01589 if (wallpaper) { 01590 kDebug() << "Wallpaper dropped:" << tjob->url(); 01591 wallpaper->setUrls(KUrl::List() << tjob->url()); 01592 } 01593 } else { 01594 addApplet(actionsToApplets[choice], args, QRectF(posi, QSize())); 01595 } 01596 01597 clearDataForMimeJob(job); 01598 return; 01599 } 01600 } else { 01601 // we can at least create an icon as a link to the URL 01602 addApplet("icon", args, QRectF(posi, QSize())); 01603 } 01604 } 01605 01606 clearDataForMimeJob(job); 01607 #endif // PLASMA_NO_KIO 01608 } 01609 01610 #ifndef KDE_NO_DEPRECATED 01611 const QGraphicsItem *Containment::toolBoxItem() const 01612 { 01613 return d->toolBox.data(); 01614 } 01615 #endif 01616 01617 void Containment::setToolBox(AbstractToolBox *toolBox) 01618 { 01619 if (d->toolBox.data()) { 01620 d->toolBox.data()->deleteLater(); 01621 } 01622 d->toolBox = toolBox; 01623 } 01624 01625 AbstractToolBox *Containment::toolBox() const 01626 { 01627 return d->toolBox.data(); 01628 } 01629 01630 void Containment::resizeEvent(QGraphicsSceneResizeEvent *event) 01631 { 01632 Applet::resizeEvent(event); 01633 01634 if (isContainment()) { 01635 if (d->isPanelContainment()) { 01636 d->positionPanel(); 01637 } else if (corona()) { 01638 QMetaObject::invokeMethod(corona(), "layoutContainments"); 01639 } 01640 01641 if (d->wallpaper) { 01642 d->wallpaper->setBoundingRect(QRectF(QPointF(0, 0), size())); 01643 } 01644 } 01645 } 01646 01647 void Containment::keyPressEvent(QKeyEvent *event) 01648 { 01649 //kDebug() << "keyPressEvent with" << event->key() 01650 // << "and hoping and wishing for a" << Qt::Key_Tab; 01651 if (event->key() == Qt::Key_Tab) { // && event->modifiers() == 0) { 01652 if (!d->applets.isEmpty()) { 01653 kDebug() << "let's give focus to...." << (QObject*)d->applets.first(); 01654 d->applets.first()->setFocus(Qt::TabFocusReason); 01655 } 01656 } 01657 } 01658 01659 void Containment::wheelEvent(QGraphicsSceneWheelEvent *event) 01660 { 01661 event->ignore(); 01662 if (d->appletAt(event->scenePos())) { 01663 return; //no unexpected click-throughs 01664 } 01665 01666 if (d->wallpaper && d->wallpaper->isInitialized()) { 01667 QGraphicsItem *item = scene()->itemAt(event->scenePos()); 01668 if (item == this) { 01669 event->ignore(); 01670 d->wallpaper->wheelEvent(event); 01671 01672 if (event->isAccepted()) { 01673 return; 01674 } 01675 } 01676 } 01677 01678 QString trigger = ContainmentActions::eventToString(event); 01679 01680 if (d->prepareContainmentActions(trigger, event->screenPos())) { 01681 d->actionPlugins()->value(trigger)->contextEvent(event); 01682 event->accept(); 01683 } else { 01684 event->ignore(); 01685 Applet::wheelEvent(event); 01686 } 01687 } 01688 01689 bool Containment::sceneEventFilter(QGraphicsItem *watched, QEvent *event) 01690 { 01691 return Applet::sceneEventFilter(watched, event); 01692 } 01693 01694 QVariant Containment::itemChange(GraphicsItemChange change, const QVariant &value) 01695 { 01696 //FIXME if the applet is moved to another containment we need to unfocus it 01697 01698 if (isContainment() && 01699 (change == QGraphicsItem::ItemSceneHasChanged || 01700 change == QGraphicsItem::ItemPositionHasChanged)) { 01701 switch (d->type) { 01702 case PanelContainment: 01703 case CustomPanelContainment: 01704 d->positionPanel(); 01705 break; 01706 default: 01707 if (corona()) { 01708 QMetaObject::invokeMethod(corona(), "layoutContainments"); 01709 } 01710 break; 01711 } 01712 } 01713 01714 return Applet::itemChange(change, value); 01715 } 01716 01717 void Containment::enableAction(const QString &name, bool enable) 01718 { 01719 QAction *action = this->action(name); 01720 if (action) { 01721 action->setEnabled(enable); 01722 action->setVisible(enable); 01723 } 01724 } 01725 01726 void Containment::addToolBoxAction(QAction *action) 01727 { 01728 d->createToolBox(); 01729 if (d->toolBox) { 01730 d->toolBox.data()->addTool(action); 01731 } 01732 } 01733 01734 void Containment::removeToolBoxAction(QAction *action) 01735 { 01736 if (d->toolBox) { 01737 d->toolBox.data()->removeTool(action); 01738 } 01739 } 01740 01741 void Containment::setToolBoxOpen(bool open) 01742 { 01743 if (open) { 01744 openToolBox(); 01745 } else { 01746 closeToolBox(); 01747 } 01748 } 01749 01750 bool Containment::isToolBoxOpen() const 01751 { 01752 return (d->toolBox && d->toolBox.data()->isShowing()); 01753 } 01754 01755 void Containment::openToolBox() 01756 { 01757 if (d->toolBox && !d->toolBox.data()->isShowing()) { 01758 d->toolBox.data()->setShowing(true); 01759 emit toolBoxVisibilityChanged(true); 01760 } 01761 } 01762 01763 void Containment::closeToolBox() 01764 { 01765 if (d->toolBox && d->toolBox.data()->isShowing()) { 01766 d->toolBox.data()->setShowing(false); 01767 emit toolBoxVisibilityChanged(false); 01768 } 01769 } 01770 01771 void Containment::addAssociatedWidget(QWidget *widget) 01772 { 01773 Applet::addAssociatedWidget(widget); 01774 if (d->focusedApplet) { 01775 d->focusedApplet->addAssociatedWidget(widget); 01776 } 01777 01778 foreach (const Applet *applet, d->applets) { 01779 if (applet->d->activationAction) { 01780 widget->addAction(applet->d->activationAction); 01781 } 01782 } 01783 } 01784 01785 void Containment::removeAssociatedWidget(QWidget *widget) 01786 { 01787 Applet::removeAssociatedWidget(widget); 01788 if (d->focusedApplet) { 01789 d->focusedApplet->removeAssociatedWidget(widget); 01790 } 01791 01792 foreach (const Applet *applet, d->applets) { 01793 if (applet->d->activationAction) { 01794 widget->removeAction(applet->d->activationAction); 01795 } 01796 } 01797 } 01798 01799 void Containment::setDrawWallpaper(bool drawWallpaper) 01800 { 01801 d->drawWallpaper = drawWallpaper; 01802 if (drawWallpaper) { 01803 KConfigGroup cfg = config(); 01804 const QString wallpaper = cfg.readEntry("wallpaperplugin", defaultWallpaper); 01805 const QString mode = cfg.readEntry("wallpaperpluginmode", defaultWallpaperMode); 01806 setWallpaper(wallpaper, mode); 01807 } else { 01808 delete d->wallpaper; 01809 d->wallpaper = 0; 01810 } 01811 } 01812 01813 bool Containment::drawWallpaper() 01814 { 01815 return d->drawWallpaper; 01816 } 01817 01818 void Containment::setWallpaper(const QString &pluginName, const QString &mode) 01819 { 01820 KConfigGroup cfg = config(); 01821 bool newPlugin = true; 01822 bool newMode = true; 01823 01824 if (d->drawWallpaper) { 01825 if (d->wallpaper) { 01826 // we have a wallpaper, so let's decide whether we need to swap it out 01827 if (d->wallpaper->pluginName() != pluginName) { 01828 delete d->wallpaper; 01829 d->wallpaper = 0; 01830 } else { 01831 // it's the same plugin, so let's save its state now so when 01832 // we call restore later on we're safe 01833 newMode = d->wallpaper->renderingMode().name() != mode; 01834 newPlugin = false; 01835 } 01836 } 01837 01838 if (!pluginName.isEmpty() && !d->wallpaper) { 01839 d->wallpaper = Plasma::Wallpaper::load(pluginName); 01840 } 01841 01842 if (d->wallpaper) { 01843 d->wallpaper->setParent(this); 01844 d->wallpaper->setBoundingRect(QRectF(QPointF(0, 0), size())); 01845 d->wallpaper->setRenderingMode(mode); 01846 01847 if (newPlugin) { 01848 cfg.writeEntry("wallpaperplugin", pluginName); 01849 } 01850 01851 if (d->wallpaper->isInitialized()) { 01852 KConfigGroup wallpaperConfig = KConfigGroup(&cfg, "Wallpaper"); 01853 wallpaperConfig = KConfigGroup(&wallpaperConfig, pluginName); 01854 d->wallpaper->restore(wallpaperConfig); 01855 } 01856 01857 if (newMode) { 01858 cfg.writeEntry("wallpaperpluginmode", mode); 01859 } 01860 } 01861 01862 update(); 01863 } 01864 01865 if (!d->wallpaper) { 01866 cfg.deleteEntry("wallpaperplugin"); 01867 cfg.deleteEntry("wallpaperpluginmode"); 01868 } 01869 01870 if (newPlugin || newMode) { 01871 if (newPlugin && d->wallpaper) { 01872 connect(d->wallpaper, SIGNAL(configureRequested()), this, SLOT(requestConfiguration())); 01873 connect(d->wallpaper, SIGNAL(configNeedsSaving()), this, SIGNAL(configNeedsSaving())); 01874 } 01875 01876 emit configNeedsSaving(); 01877 } 01878 } 01879 01880 Plasma::Wallpaper *Containment::wallpaper() const 01881 { 01882 return d->wallpaper; 01883 } 01884 01885 void Containment::setContainmentActions(const QString &trigger, const QString &pluginName) 01886 { 01887 KConfigGroup cfg = containmentActionsConfig(); 01888 ContainmentActions *plugin = 0; 01889 01890 if (d->actionPlugins()->contains(trigger)) { 01891 plugin = d->actionPlugins()->value(trigger); 01892 if (plugin->pluginName() != pluginName) { 01893 d->actionPlugins()->remove(trigger); 01894 delete plugin; 01895 plugin=0; 01896 } 01897 } 01898 if (pluginName.isEmpty()) { 01899 cfg.deleteEntry(trigger); 01900 } else if (plugin) { 01901 //it already existed, just reload config 01902 if (plugin->isInitialized()) { 01903 plugin->setContainment(this); //to be safe 01904 //FIXME make a truly unique config group 01905 KConfigGroup pluginConfig = KConfigGroup(&cfg, trigger); 01906 plugin->restore(pluginConfig); 01907 } 01908 } else { 01909 switch (d->containmentActionsSource) { 01910 case ContainmentPrivate::Activity: 01911 //FIXME 01912 case ContainmentPrivate::Local: 01913 plugin = ContainmentActions::load(this, pluginName); 01914 break; 01915 default: 01916 plugin = ContainmentActions::load(0, pluginName); 01917 } 01918 if (plugin) { 01919 cfg.writeEntry(trigger, pluginName); 01920 d->actionPlugins()->insert(trigger, plugin); 01921 } else { 01922 //bad plugin... gets removed. is this a feature or a bug? 01923 cfg.deleteEntry(trigger); 01924 } 01925 } 01926 01927 emit configNeedsSaving(); 01928 } 01929 01930 QStringList Containment::containmentActionsTriggers() 01931 { 01932 return d->actionPlugins()->keys(); 01933 } 01934 01935 QString Containment::containmentActions(const QString &trigger) 01936 { 01937 ContainmentActions *c = d->actionPlugins()->value(trigger); 01938 return c ? c->pluginName() : QString(); 01939 } 01940 01941 void Containment::setActivity(const QString &activity) 01942 { 01943 Context *context = d->context(); 01944 if (context->currentActivity() != activity) { 01945 context->setCurrentActivity(activity); 01946 } 01947 } 01948 01949 void ContainmentPrivate::onContextChanged(Plasma::Context *con) 01950 { 01951 foreach (Applet *a, applets) { 01952 a->updateConstraints(ContextConstraint); 01953 } 01954 01955 KConfigGroup c = q->config(); 01956 QString act = con->currentActivityId(); 01957 01958 //save anything that's been set (boy I hope this avoids overwriting things) 01959 //FIXME of course if the user sets the name to an empty string we have a bug 01960 //but once we get context retrieving the name as soon as the id is set, this issue should go away 01961 if (!act.isEmpty()) { 01962 c.writeEntry("activityId", act); 01963 } 01964 act = con->currentActivity(); 01965 if (!act.isEmpty()) { 01966 c.writeEntry("activity", act); 01967 } 01968 01969 if (toolBox) { 01970 toolBox.data()->update(); 01971 } 01972 emit q->configNeedsSaving(); 01973 emit q->contextChanged(con); 01974 } 01975 01976 QString Containment::activity() const 01977 { 01978 return d->context()->currentActivity(); 01979 } 01980 01981 Context *Containment::context() const 01982 { 01983 return d->context(); 01984 } 01985 01986 Context *ContainmentPrivate::context() 01987 { 01988 if (!con) { 01989 con = new Context(q); 01990 q->connect(con, SIGNAL(changed(Plasma::Context*)), 01991 q, SLOT(onContextChanged(Plasma::Context*))); 01992 } 01993 01994 return con; 01995 } 01996 01997 KActionCollection* ContainmentPrivate::actions() 01998 { 01999 return static_cast<Applet*>(q)->d->actions; 02000 } 02001 02002 void ContainmentPrivate::focusApplet(Plasma::Applet *applet) 02003 { 02004 if (focusedApplet == applet) { 02005 return; 02006 } 02007 02008 QList<QWidget *> widgets = actions()->associatedWidgets(); 02009 if (focusedApplet) { 02010 foreach (QWidget *w, widgets) { 02011 focusedApplet->removeAssociatedWidget(w); 02012 } 02013 } 02014 02015 if (applet && applets.contains(applet)) { 02016 //kDebug() << "switching to" << applet->name(); 02017 focusedApplet = applet; 02018 foreach (QWidget *w, widgets) { 02019 focusedApplet->addAssociatedWidget(w); 02020 } 02021 02022 if (!focusedApplet->hasFocus()) { 02023 focusedApplet->setFocus(Qt::ShortcutFocusReason); 02024 } 02025 } else { 02026 focusedApplet = 0; 02027 } 02028 } 02029 02030 void Containment::focusNextApplet() 02031 { 02032 if (d->applets.isEmpty()) { 02033 return; 02034 } 02035 int index = d->focusedApplet ? d->applets.indexOf(d->focusedApplet) + 1 : 0; 02036 if (index >= d->applets.size()) { 02037 index = 0; 02038 } 02039 kDebug() << "index" << index; 02040 d->focusApplet(d->applets.at(index)); 02041 } 02042 02043 void Containment::focusPreviousApplet() 02044 { 02045 if (d->applets.isEmpty()) { 02046 return; 02047 } 02048 int index = d->focusedApplet ? d->applets.indexOf(d->focusedApplet) - 1 : -1; 02049 if (index < 0) { 02050 index = d->applets.size() - 1; 02051 } 02052 kDebug() << "index" << index; 02053 d->focusApplet(d->applets.at(index)); 02054 } 02055 02056 void Containment::destroy() 02057 { 02058 destroy(true); 02059 } 02060 02061 void Containment::showConfigurationInterface() 02062 { 02063 Applet::showConfigurationInterface(); 02064 } 02065 02066 void Containment::configChanged() 02067 { 02068 } 02069 02070 void ContainmentPrivate::configChanged() 02071 { 02072 if (drawWallpaper) { 02073 KConfigGroup group = q->config(); 02074 q->setWallpaper(group.readEntry("wallpaperplugin", defaultWallpaper), 02075 group.readEntry("wallpaperpluginmode", defaultWallpaperMode)); 02076 } 02077 } 02078 02079 void ContainmentPrivate::requestConfiguration() 02080 { 02081 emit q->configureRequested(q); 02082 } 02083 02084 void ContainmentPrivate::checkStatus(Plasma::ItemStatus appletStatus) 02085 { 02086 //kDebug() << "================== "<< appletStatus << q->status(); 02087 if (appletStatus == q->status()) { 02088 emit q->newStatus(appletStatus); 02089 return; 02090 } 02091 02092 if (appletStatus < q->status()) { 02093 // check to see if any other applet has a higher status, and stick with that 02094 // if we do 02095 foreach (Applet *applet, applets) { 02096 if (applet->status() > appletStatus) { 02097 appletStatus = applet->status(); 02098 } 02099 } 02100 } 02101 02102 q->setStatus(appletStatus); 02103 } 02104 02105 void Containment::destroy(bool confirm) 02106 { 02107 if (immutability() != Mutable || Applet::d->transient) { 02108 return; 02109 } 02110 02111 if (isContainment() && confirm) { 02112 //FIXME: should not be blocking 02113 const QString title = i18nc("@title:window %1 is the name of the containment", "Remove %1", name()); 02114 KGuiItem remove = KStandardGuiItem::remove(); 02115 remove.setText(title); 02116 if (KMessageBox::warningContinueCancel(view(), 02117 i18nc("%1 is the name of the containment", "Do you really want to remove this %1?", name()), 02118 title, remove) != KMessageBox::Continue) { 02119 return; 02120 } 02121 } 02122 02123 Applet::destroy(); 02124 } 02125 02126 void ContainmentPrivate::createToolBox() 02127 { 02128 if (!toolBox && KAuthorized::authorizeKAction("plasma/containment_context_menu")) { 02129 toolBox = Plasma::AbstractToolBox::load(q->corona()->preferredToolBoxPlugin(type), QVariantList(), q); 02130 02131 if (toolBox) { 02132 QObject::connect(toolBox.data(), SIGNAL(toggled()), q, SIGNAL(toolBoxToggled())); 02133 QObject::connect(toolBox.data(), SIGNAL(toggled()), q, SLOT(updateToolBoxVisibility())); 02134 02135 positionToolBox(); 02136 } 02137 } 02138 } 02139 02140 void ContainmentPrivate::positionToolBox() 02141 { 02142 QMetaObject::invokeMethod(toolBox.data(), "reposition"); 02143 } 02144 02145 void ContainmentPrivate::updateToolBoxVisibility() 02146 { 02147 emit q->toolBoxVisibilityChanged(toolBox.data()->isShowing()); 02148 } 02149 02150 void ContainmentPrivate::triggerShowAddWidgets() 02151 { 02152 emit q->showAddWidgetsInterface(QPointF()); 02153 } 02154 02155 void ContainmentPrivate::containmentConstraintsEvent(Plasma::Constraints constraints) 02156 { 02157 if (!q->isContainment()) { 02158 return; 02159 } 02160 02161 //kDebug() << "got containmentConstraintsEvent" << constraints << (QObject*)toolBox; 02162 if (constraints & Plasma::ImmutableConstraint) { 02163 //update actions 02164 checkRemoveAction(); 02165 const bool unlocked = q->immutability() == Mutable; 02166 q->setAcceptDrops(unlocked); 02167 q->enableAction("add widgets", unlocked); 02168 02169 // tell the applets too 02170 foreach (Applet *a, applets) { 02171 a->setImmutability(q->immutability()); 02172 a->updateConstraints(ImmutableConstraint); 02173 } 02174 } 02175 02176 // pass on the constraints that are relevant here 02177 Constraints appletConstraints = NoConstraint; 02178 if (constraints & FormFactorConstraint) { 02179 appletConstraints |= FormFactorConstraint; 02180 } 02181 02182 if (constraints & ScreenConstraint) { 02183 appletConstraints |= ScreenConstraint; 02184 } 02185 02186 if (appletConstraints != NoConstraint) { 02187 foreach (Applet *applet, applets) { 02188 applet->updateConstraints(appletConstraints); 02189 } 02190 } 02191 02192 if (toolBox && (constraints & Plasma::SizeConstraint || 02193 constraints & Plasma::FormFactorConstraint || 02194 constraints & Plasma::ScreenConstraint || 02195 constraints & Plasma::StartupCompletedConstraint)) { 02196 //kDebug() << "Positioning toolbox"; 02197 positionToolBox(); 02198 } 02199 02200 if (constraints & Plasma::StartupCompletedConstraint && type < Containment::CustomContainment) { 02201 q->addToolBoxAction(q->action("remove")); 02202 checkRemoveAction(); 02203 } 02204 } 02205 02206 Applet *ContainmentPrivate::addApplet(const QString &name, const QVariantList &args, 02207 const QRectF &appletGeometry, uint id, bool delayInit) 02208 { 02209 if (!q->isContainment()) { 02210 return 0; 02211 } 02212 02213 if (!delayInit && q->immutability() != Mutable) { 02214 kDebug() << "addApplet for" << name << "requested, but we're currently immutable!"; 02215 return 0; 02216 } 02217 02218 QGraphicsView *v = q->view(); 02219 if (v) { 02220 v->setCursor(Qt::BusyCursor); 02221 } 02222 02223 Applet *applet = Applet::load(name, id, args); 02224 if (v) { 02225 v->unsetCursor(); 02226 } 02227 02228 if (!applet) { 02229 kDebug() << "Applet" << name << "could not be loaded."; 02230 applet = new Applet(0, QString(), id); 02231 applet->setFailedToLaunch(true, i18n("Could not find requested component: %1", name)); 02232 } 02233 02234 //kDebug() << applet->name() << "sizehint:" << applet->sizeHint() << "geometry:" << applet->geometry(); 02235 02236 q->addApplet(applet, appletGeometry.topLeft(), delayInit); 02237 return applet; 02238 } 02239 02240 bool ContainmentPrivate::regionIsEmpty(const QRectF ®ion, Applet *ignoredApplet) const 02241 { 02242 foreach (Applet *applet, applets) { 02243 if (applet != ignoredApplet && applet->geometry().intersects(region)) { 02244 return false; 02245 } 02246 } 02247 return true; 02248 } 02249 02250 void ContainmentPrivate::appletDestroyed(Plasma::Applet *applet) 02251 { 02252 applets.removeAll(applet); 02253 if (focusedApplet == applet) { 02254 focusedApplet = 0; 02255 } 02256 02257 emit q->appletRemoved(applet); 02258 emit q->configNeedsSaving(); 02259 } 02260 02261 void ContainmentPrivate::appletAppearAnimationComplete() 02262 { 02263 Animation *anim = qobject_cast<Animation *>(q->sender()); 02264 if (anim) { 02265 Applet *applet = qobject_cast<Applet*>(anim->targetWidget()); 02266 if (applet) { 02267 appletAppeared(applet); 02268 } 02269 } 02270 } 02271 02272 void ContainmentPrivate::appletAppeared(Applet *applet) 02273 { 02274 //kDebug() << type << Containment::DesktopContainment; 02275 KConfigGroup *cg = applet->d->mainConfigGroup(); 02276 applet->save(*cg); 02277 emit q->configNeedsSaving(); 02278 } 02279 02280 void ContainmentPrivate::positionPanel(bool force) 02281 { 02282 if (!q->scene()) { 02283 kDebug() << "no scene yet"; 02284 return; 02285 } 02286 02287 // already positioning the panel - avoid infinite loops 02288 if (ContainmentPrivate::s_positioningPanels) { 02289 return; 02290 } 02291 02292 // we position panels in negative coordinates, and stack all horizontal 02293 // and all vertical panels with each other. 02294 02295 02296 const QPointF p = q->pos(); 02297 02298 if (!force && 02299 p.y() + q->size().height() < -INTER_CONTAINMENT_MARGIN && 02300 q->scene()->collidingItems(q).isEmpty()) { 02301 // already positioned and not running into any other panels 02302 return; 02303 } 02304 02305 02306 QPointF newPos = preferredPanelPos(q->corona()); 02307 if (p != newPos) { 02308 ContainmentPrivate::s_positioningPanels = true; 02309 q->setPos(newPos); 02310 ContainmentPrivate::s_positioningPanels = false; 02311 } 02312 } 02313 02314 bool ContainmentPrivate::isPanelContainment() const 02315 { 02316 return type == Containment::PanelContainment || type == Containment::CustomPanelContainment; 02317 } 02318 02319 QPointF ContainmentPrivate::preferredPos(Corona *corona) const 02320 { 02321 Q_ASSERT(corona); 02322 02323 if (isPanelContainment()) { 02324 //kDebug() << "is a panel, so put it at" << preferredPanelPos(corona); 02325 return preferredPanelPos(corona); 02326 } 02327 02328 QPointF pos(0, 0); 02329 QTransform t; 02330 while (QGraphicsItem *i = corona->itemAt(pos, t)) { 02331 pos.setX(i->scenePos().x() + i->boundingRect().width() + 10); 02332 } 02333 02334 //kDebug() << "not a panel, put it at" << pos; 02335 return pos; 02336 } 02337 02338 QPointF ContainmentPrivate::preferredPanelPos(Corona *corona) const 02339 { 02340 Q_ASSERT(corona); 02341 02342 //TODO: research how non-Horizontal, non-Vertical (e.g. Planar) panels behave here 02343 bool horiz = formFactor == Plasma::Horizontal; 02344 qreal bottom = horiz ? 0 : VERTICAL_STACKING_OFFSET; 02345 qreal lastHeight = 0; 02346 02347 // this should be ok for small numbers of panels, but if we ever end 02348 // up managing hundreds of them, this simplistic alogrithm will 02349 // likely be too slow. 02350 foreach (const Containment *other, corona->containments()) { 02351 if (other == q || 02352 !other->d->isPanelContainment() || 02353 horiz != (other->formFactor() == Plasma::Horizontal)) { 02354 // only line up with panels of the same orientation 02355 continue; 02356 } 02357 02358 if (horiz) { 02359 qreal y = other->pos().y(); 02360 if (y < bottom) { 02361 lastHeight = other->size().height(); 02362 bottom = y; 02363 } 02364 } else { 02365 qreal width = other->size().width(); 02366 qreal x = other->pos().x() + width; 02367 if (x > bottom) { 02368 lastHeight = width; 02369 bottom = x + lastHeight; 02370 } 02371 } 02372 } 02373 02374 // give a space equal to the height again of the last item so there is 02375 // room to grow. 02376 QPointF newPos; 02377 if (horiz) { 02378 bottom -= lastHeight + INTER_CONTAINMENT_MARGIN; 02379 //TODO: fix x position for non-flush-left panels 02380 kDebug() << "moved to" << QPointF(0, bottom - q->size().height()); 02381 newPos = QPointF(0, bottom - q->size().height()); 02382 } else { 02383 bottom += lastHeight + INTER_CONTAINMENT_MARGIN; 02384 //TODO: fix y position for non-flush-top panels 02385 kDebug() << "moved to" << QPointF(bottom + q->size().width(), -INTER_CONTAINMENT_MARGIN - q->size().height()); 02386 newPos = QPointF(bottom + q->size().width(), -INTER_CONTAINMENT_MARGIN - q->size().height()); 02387 } 02388 02389 return newPos; 02390 } 02391 02392 02393 bool ContainmentPrivate::prepareContainmentActions(const QString &trigger, const QPoint &screenPos, KMenu *menu) 02394 { 02395 ContainmentActions *plugin = actionPlugins()->value(trigger); 02396 if (!plugin) { 02397 return false; 02398 } 02399 plugin->setContainment(q); 02400 02401 if (!plugin->isInitialized()) { 02402 KConfigGroup cfg = q->containmentActionsConfig(); 02403 KConfigGroup pluginConfig = KConfigGroup(&cfg, trigger); 02404 plugin->restore(pluginConfig); 02405 } 02406 02407 if (plugin->configurationRequired()) { 02408 KMenu *localMenu = menu ? menu : new KMenu(); 02409 02410 localMenu->addTitle(i18n("This plugin needs to be configured")); 02411 localMenu->addAction(q->action("configure")); 02412 02413 if (!menu) { 02414 localMenu->exec(screenPos); 02415 delete localMenu; 02416 } 02417 02418 return false; 02419 } else if (menu) { 02420 QList<QAction*> actions = plugin->contextualActions(); 02421 if (actions.isEmpty()) { 02422 //it probably didn't bother implementing the function. give the user a chance to set 02423 //a better plugin. note that if the user sets no-plugin this won't happen... 02424 if (!isPanelContainment() && q->action("configure")) { 02425 menu->addAction(q->action("configure")); 02426 } 02427 } else { 02428 menu->addActions(actions); 02429 } 02430 } 02431 02432 return true; 02433 } 02434 02435 KConfigGroup Containment::containmentActionsConfig() 02436 { 02437 KConfigGroup cfg; 02438 switch (d->containmentActionsSource) { 02439 case ContainmentPrivate::Local: 02440 cfg = config(); 02441 cfg = KConfigGroup(&cfg, "ActionPlugins"); 02442 break; 02443 case ContainmentPrivate::Activity: 02444 cfg = KConfigGroup(corona()->config(), "Activities"); 02445 cfg = KConfigGroup(&cfg, d->context()->currentActivityId()); 02446 cfg = KConfigGroup(&cfg, "ActionPlugins"); 02447 break; 02448 default: 02449 cfg = KConfigGroup(corona()->config(), "ActionPlugins"); 02450 } 02451 return cfg; 02452 } 02453 02454 QHash<QString, ContainmentActions*> * ContainmentPrivate::actionPlugins() 02455 { 02456 switch (containmentActionsSource) { 02457 case Activity: 02458 //FIXME 02459 case Local: 02460 return &localActionPlugins; 02461 default: 02462 return &globalActionPlugins; 02463 } 02464 } 02465 02466 } // Plasma namespace 02467 02468 #include "containment.moc" 02469
This file is part of the KDE documentation.
Documentation copyright © 1996-2019 The KDE developers.
Generated on Mon Jan 21 2019 12:30:43 by doxygen 1.7.5.1 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2019 The KDE developers.
Generated on Mon Jan 21 2019 12:30:43 by doxygen 1.7.5.1 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.