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 event->ignore(); 00552 if (d->appletAt(event->scenePos())) { 00553 return; //no unexpected click-throughs 00554 } 00555 00556 if (d->wallpaper && d->wallpaper->isInitialized() && !event->isAccepted()) { 00557 d->wallpaper->mousePressEvent(event); 00558 } 00559 00560 if (event->isAccepted()) { 00561 setFocus(Qt::MouseFocusReason); 00562 } else if (event->button() == Qt::RightButton && event->modifiers() == Qt::NoModifier) { 00563 // we'll catch this in the context menu even 00564 Applet::mousePressEvent(event); 00565 } else { 00566 QString trigger = ContainmentActions::eventToString(event); 00567 if (d->prepareContainmentActions(trigger, event->screenPos())) { 00568 d->actionPlugins()->value(trigger)->contextEvent(event); 00569 } 00570 00571 if (!event->isAccepted()) { 00572 Applet::mousePressEvent(event); 00573 } 00574 } 00575 } 00576 00577 void Containment::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) 00578 { 00579 event->ignore(); 00580 if (d->appletAt(event->scenePos())) { 00581 return; //no unexpected click-throughs 00582 } 00583 00584 QString trigger = ContainmentActions::eventToString(event); 00585 00586 if (d->wallpaper && d->wallpaper->isInitialized()) { 00587 d->wallpaper->mouseReleaseEvent(event); 00588 } 00589 00590 if (!event->isAccepted() && isContainment()) { 00591 if (d->prepareContainmentActions(trigger, event->screenPos())) { 00592 d->actionPlugins()->value(trigger)->contextEvent(event); 00593 } 00594 00595 event->accept(); 00596 Applet::mouseReleaseEvent(event); 00597 } 00598 } 00599 00600 void Containment::showDropZone(const QPoint pos) 00601 { 00602 Q_UNUSED(pos) 00603 //Base implementation does nothing, don't put code here 00604 } 00605 00606 void Containment::showContextMenu(const QPointF &containmentPos, const QPoint &screenPos) 00607 { 00608 //kDebug() << containmentPos << screenPos; 00609 QGraphicsSceneContextMenuEvent gvevent; 00610 gvevent.setScreenPos(screenPos); 00611 gvevent.setScenePos(mapToScene(containmentPos)); 00612 gvevent.setPos(containmentPos); 00613 gvevent.setReason(QGraphicsSceneContextMenuEvent::Mouse); 00614 gvevent.setWidget(view()); 00615 contextMenuEvent(&gvevent); 00616 } 00617 00618 void Containment::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) 00619 { 00620 if (!isContainment() || !KAuthorized::authorizeKAction("plasma/containment_context_menu")) { 00621 Applet::contextMenuEvent(event); 00622 return; 00623 } 00624 00625 KMenu desktopMenu; 00626 Applet *applet = d->appletAt(event->scenePos()); 00627 //kDebug() << "context menu event " << (QObject*)applet; 00628 00629 if (applet) { 00630 d->addAppletActions(desktopMenu, applet, event); 00631 } else { 00632 d->addContainmentActions(desktopMenu, event); 00633 } 00634 00635 //kDebug() << "executing at" << screenPos; 00636 QMenu *menu = &desktopMenu; 00637 //kDebug() << "showing menu, actions" << desktopMenu.actions().size() << desktopMenu.actions().first()->menu(); 00638 if (desktopMenu.actions().size() == 1 && desktopMenu.actions().first()->menu()) { 00639 // we have a menu with a single top level menu; just show that top level menu instad. 00640 menu = desktopMenu.actions().first()->menu(); 00641 } 00642 00643 if (!menu->isEmpty()) { 00644 QPoint pos = event->screenPos(); 00645 if (applet && d->isPanelContainment()) { 00646 menu->adjustSize(); 00647 pos = applet->popupPosition(menu->size()); 00648 if (event->reason() == QGraphicsSceneContextMenuEvent::Mouse) { 00649 // if the menu pops up way away from the mouse press, then move it 00650 // to the mouse press 00651 if (d->formFactor == Vertical) { 00652 if (pos.y() + menu->height() < event->screenPos().y()) { 00653 pos.setY(event->screenPos().y()); 00654 } 00655 } else if (d->formFactor == Horizontal) { 00656 if (pos.x() + menu->width() < event->screenPos().x()) { 00657 pos.setX(event->screenPos().x()); 00658 } 00659 } 00660 } 00661 } 00662 00663 menu->exec(pos); 00664 event->accept(); 00665 } else { 00666 Applet::contextMenuEvent(event); 00667 } 00668 } 00669 00670 void ContainmentPrivate::addContainmentActions(KMenu &desktopMenu, QEvent *event) 00671 { 00672 if (static_cast<Corona*>(q->scene())->immutability() != Mutable && 00673 !KAuthorized::authorizeKAction("plasma/containment_actions")) { 00674 //kDebug() << "immutability"; 00675 return; 00676 } 00677 00678 const QString trigger = ContainmentActions::eventToString(event); 00679 prepareContainmentActions(trigger, QPoint(), &desktopMenu); 00680 } 00681 00682 void ContainmentPrivate::addAppletActions(KMenu &desktopMenu, Applet *applet, QEvent *event) 00683 { 00684 foreach (QAction *action, applet->contextualActions()) { 00685 if (action) { 00686 desktopMenu.addAction(action); 00687 } 00688 } 00689 00690 if (!applet->d->failed) { 00691 QAction *configureApplet = applet->d->actions->action("configure"); 00692 if (configureApplet && configureApplet->isEnabled()) { 00693 desktopMenu.addAction(configureApplet); 00694 } 00695 00696 QAction *runAssociatedApplication = applet->d->actions->action("run associated application"); 00697 if (runAssociatedApplication && runAssociatedApplication->isEnabled()) { 00698 desktopMenu.addAction(runAssociatedApplication); 00699 } 00700 } 00701 00702 KMenu *containmentMenu = new KMenu(i18nc("%1 is the name of the containment", "%1 Options", q->name()), &desktopMenu); 00703 addContainmentActions(*containmentMenu, event); 00704 if (!containmentMenu->isEmpty()) { 00705 int enabled = 0; 00706 //count number of real actions 00707 QListIterator<QAction *> actionsIt(containmentMenu->actions()); 00708 while (enabled < 3 && actionsIt.hasNext()) { 00709 QAction *action = actionsIt.next(); 00710 if (action->isVisible() && !action->isSeparator()) { 00711 ++enabled; 00712 } 00713 } 00714 00715 if (enabled) { 00716 //if there is only one, don't create a submenu 00717 if (enabled < 2) { 00718 foreach (QAction *action, containmentMenu->actions()) { 00719 if (action->isVisible() && !action->isSeparator()) { 00720 desktopMenu.addAction(action); 00721 } 00722 } 00723 } else { 00724 desktopMenu.addMenu(containmentMenu); 00725 } 00726 } 00727 } 00728 00729 if (q->immutability() == Mutable) { 00730 QAction *closeApplet = applet->d->actions->action("remove"); 00731 //kDebug() << "checking for removal" << closeApplet; 00732 if (closeApplet) { 00733 if (!desktopMenu.isEmpty()) { 00734 desktopMenu.addSeparator(); 00735 } 00736 00737 //kDebug() << "adding close action" << closeApplet->isEnabled() << closeApplet->isVisible(); 00738 desktopMenu.addAction(closeApplet); 00739 } 00740 } 00741 } 00742 00743 Applet* ContainmentPrivate::appletAt(const QPointF &point) 00744 { 00745 Applet *applet = 0; 00746 00747 QGraphicsItem *item = q->scene()->itemAt(point); 00748 if (item == q) { 00749 item = 0; 00750 } 00751 00752 while (item) { 00753 if (item->isWidget()) { 00754 applet = qobject_cast<Applet*>(static_cast<QGraphicsWidget*>(item)); 00755 if (applet) { 00756 if (applet->isContainment()) { 00757 applet = 0; 00758 } 00759 break; 00760 } 00761 } 00762 AppletHandle *handle = dynamic_cast<AppletHandle*>(item); 00763 if (handle) { 00764 //pretend it was on the applet 00765 applet = handle->applet(); 00766 break; 00767 } 00768 item = item->parentItem(); 00769 } 00770 return applet; 00771 } 00772 00773 void Containment::setFormFactor(FormFactor formFactor) 00774 { 00775 if (d->formFactor == formFactor) { 00776 return; 00777 } 00778 00779 //kDebug() << "switching FF to " << formFactor; 00780 d->formFactor = formFactor; 00781 00782 if (isContainment() && 00783 (d->type == PanelContainment || d->type == CustomPanelContainment)) { 00784 // we are a panel and we have chaged our orientation 00785 d->positionPanel(true); 00786 } 00787 00788 QMetaObject::invokeMethod(d->toolBox.data(), "reposition"); 00789 00790 updateConstraints(Plasma::FormFactorConstraint); 00791 00792 KConfigGroup c = config(); 00793 c.writeEntry("formfactor", (int)formFactor); 00794 emit configNeedsSaving(); 00795 } 00796 00797 void Containment::setLocation(Location location) 00798 { 00799 if (d->location == location) { 00800 return; 00801 } 00802 00803 bool emitGeomChange = false; 00804 00805 if ((location == TopEdge || location == BottomEdge) && 00806 (d->location == TopEdge || d->location == BottomEdge)) { 00807 emitGeomChange = true; 00808 } 00809 00810 if ((location == RightEdge || location == LeftEdge) && 00811 (d->location == RightEdge || d->location == LeftEdge)) { 00812 emitGeomChange = true; 00813 } 00814 00815 d->location = location; 00816 00817 foreach (Applet *applet, d->applets) { 00818 applet->updateConstraints(Plasma::LocationConstraint); 00819 } 00820 00821 if (emitGeomChange) { 00822 // our geometry on the scene will not actually change, 00823 // but for the purposes of views it has 00824 emit geometryChanged(); 00825 } 00826 00827 updateConstraints(Plasma::LocationConstraint); 00828 00829 KConfigGroup c = config(); 00830 c.writeEntry("location", (int)location); 00831 emit configNeedsSaving(); 00832 } 00833 00834 void Containment::addSiblingContainment() 00835 { 00836 emit addSiblingContainment(this); 00837 } 00838 00839 void Containment::clearApplets() 00840 { 00841 foreach (Applet *applet, d->applets) { 00842 applet->d->cleanUpAndDelete(); 00843 } 00844 00845 d->applets.clear(); 00846 } 00847 00848 Applet *Containment::addApplet(const QString &name, const QVariantList &args, 00849 const QRectF &appletGeometry) 00850 { 00851 return d->addApplet(name, args, appletGeometry); 00852 } 00853 00854 void Containment::addApplet(Applet *applet, const QPointF &pos, bool delayInit) 00855 { 00856 if (!isContainment() || (!delayInit && immutability() != Mutable)) { 00857 return; 00858 } 00859 00860 if (!applet) { 00861 kDebug() << "adding null applet!?!"; 00862 return; 00863 } 00864 00865 if (d->applets.contains(applet)) { 00866 kDebug() << "already have this applet!"; 00867 } 00868 00869 Containment *currentContainment = applet->containment(); 00870 00871 if (d->type == PanelContainment) { 00872 //panels don't want backgrounds, which is important when setting geometry 00873 setBackgroundHints(NoBackground); 00874 } 00875 00876 if (currentContainment && currentContainment != this) { 00877 emit currentContainment->appletRemoved(applet); 00878 if (currentContainment->d->focusedApplet == applet) { 00879 currentContainment->d->focusedApplet = 0; 00880 } 00881 00882 disconnect(applet, 0, currentContainment, 0); 00883 KConfigGroup oldConfig = applet->config(); 00884 currentContainment->d->applets.removeAll(applet); 00885 applet->setParentItem(this); 00886 applet->setParent(this); 00887 00888 // now move the old config to the new location 00889 //FIXME: this doesn't seem to get the actual main config group containing plugin=, etc 00890 KConfigGroup c = config().group("Applets").group(QString::number(applet->id())); 00891 oldConfig.reparent(&c); 00892 applet->d->resetConfigurationObject(); 00893 00894 disconnect(applet, SIGNAL(activate()), currentContainment, SIGNAL(activate())); 00895 } else { 00896 applet->setParentItem(this); 00897 applet->setParent(this); 00898 } 00899 00900 d->applets << applet; 00901 00902 connect(applet, SIGNAL(configNeedsSaving()), this, SIGNAL(configNeedsSaving())); 00903 connect(applet, SIGNAL(releaseVisualFocus()), this, SIGNAL(releaseVisualFocus())); 00904 connect(applet, SIGNAL(appletDestroyed(Plasma::Applet*)), this, SLOT(appletDestroyed(Plasma::Applet*))); 00905 connect(applet, SIGNAL(newStatus(Plasma::ItemStatus)), this, SLOT(checkStatus(Plasma::ItemStatus))); 00906 connect(applet, SIGNAL(activate()), this, SIGNAL(activate())); 00907 00908 if (pos != QPointF(-1, -1)) { 00909 applet->setPos(pos); 00910 } 00911 00912 if (!delayInit && !currentContainment) { 00913 applet->restore(*applet->d->mainConfigGroup()); 00914 applet->init(); 00915 Plasma::Animation *anim = Plasma::Animator::create(Plasma::Animator::AppearAnimation); 00916 if (anim) { 00917 connect(anim, SIGNAL(finished()), this, SLOT(appletAppearAnimationComplete())); 00918 anim->setTargetWidget(applet); 00919 //FIXME: small hack until we have proper js anim support; allows 'zoom' to work in the 00920 //'right' direction for appearance 00921 anim->setDirection(QAbstractAnimation::Backward); 00922 anim->start(QAbstractAnimation::DeleteWhenStopped); 00923 } else { 00924 d->appletAppeared(applet); 00925 } 00926 } 00927 00928 applet->setFlag(QGraphicsItem::ItemIsMovable, true); 00929 applet->updateConstraints(Plasma::AllConstraints); 00930 if (!delayInit) { 00931 applet->flushPendingConstraintsEvents(); 00932 } 00933 emit appletAdded(applet, pos); 00934 00935 if (!currentContainment) { 00936 applet->updateConstraints(Plasma::StartupCompletedConstraint); 00937 if (!delayInit) { 00938 applet->flushPendingConstraintsEvents(); 00939 } 00940 } 00941 00942 if (!delayInit) { 00943 applet->d->scheduleModificationNotification(); 00944 } 00945 } 00946 00947 Applet::List Containment::applets() const 00948 { 00949 return d->applets; 00950 } 00951 00952 void Containment::setScreen(int newScreen, int newDesktop) 00953 { 00954 d->setScreen(newScreen, newDesktop); 00955 } 00956 00957 void ContainmentPrivate::setScreen(int newScreen, int newDesktop, bool preventInvalidDesktops) 00958 { 00959 // What we want to do in here is: 00960 // * claim the screen as our own 00961 // * signal whatever may be watching this containment about the switch 00962 // * if we are a full screen containment, then: 00963 // * resize to match the screen if we're that kind of containment 00964 // * kick other full-screen containments off this screen 00965 // * if we had a screen, then give our screen to the containment 00966 // we kick out 00967 // 00968 // a screen of -1 means no associated screen. 00969 Corona *corona = q->corona(); 00970 Q_ASSERT(corona); 00971 00972 //if it's an offscreen widget, don't allow to claim a screen, after all it's *off*screen 00973 if (corona->offscreenWidgets().contains(q)) { 00974 return; 00975 } 00976 00977 int numScreens = corona->numScreens(); 00978 if (newScreen < -1) { 00979 newScreen = -1; 00980 } 00981 00982 // -1 == All desktops 00983 if (newDesktop < -1 || (preventInvalidDesktops && newDesktop > KWindowSystem::numberOfDesktops() - 1)) { 00984 newDesktop = -1; 00985 } 00986 00987 //kDebug() << activity() << "setting screen to " << newScreen << newDesktop << "and type is" << type; 00988 00989 Containment *swapScreensWith(0); 00990 const bool isDesktopContainment = type == Containment::DesktopContainment || 00991 type == Containment::CustomContainment; 00992 if (isDesktopContainment) { 00993 // we want to listen to changes in work area if our screen changes 00994 if (toolBox) { 00995 if (screen < 0 && newScreen > -1) { 00996 QObject::connect(KWindowSystem::self(), SIGNAL(workAreaChanged()), toolBox.data(), SLOT(reposition()), Qt::UniqueConnection); 00997 } else if (newScreen < 0) { 00998 QObject::disconnect(KWindowSystem::self(), SIGNAL(workAreaChanged()), toolBox.data(), SLOT(reposition())); 00999 } 01000 } 01001 01002 if (newScreen > -1) { 01003 // sanity check to make sure someone else doesn't have this screen already! 01004 Containment *currently = corona->containmentForScreen(newScreen, newDesktop); 01005 if (currently && currently != q) { 01006 kDebug() << "currently is on screen" << currently->screen() 01007 << "desktop" << currently->desktop() 01008 << "and is" << currently->activity() 01009 << (QObject*)currently << "i'm" << (QObject*)q; 01010 currently->setScreen(-1, currently->desktop()); 01011 swapScreensWith = currently; 01012 } 01013 } 01014 } 01015 01016 if (newScreen < numScreens && newScreen > -1 && isDesktopContainment) { 01017 q->resize(corona->screenGeometry(newScreen).size()); 01018 } 01019 01020 int oldDesktop = desktop; 01021 desktop = newDesktop; 01022 01023 int oldScreen = screen; 01024 screen = newScreen; 01025 01026 q->updateConstraints(Plasma::ScreenConstraint); 01027 01028 if (oldScreen != newScreen || oldDesktop != newDesktop) { 01029 /* 01030 kDebug() << "going to signal change for" << q 01031 << ", old screen & desktop:" << oldScreen << oldDesktop 01032 << ", new:" << screen << desktop; 01033 */ 01034 KConfigGroup c = q->config(); 01035 c.writeEntry("screen", screen); 01036 c.writeEntry("desktop", desktop); 01037 if (newScreen != -1) { 01038 lastScreen = newScreen; 01039 lastDesktop = newDesktop; 01040 c.writeEntry("lastScreen", lastScreen); 01041 c.writeEntry("lastDesktop", lastDesktop); 01042 } 01043 emit q->configNeedsSaving(); 01044 emit q->screenChanged(oldScreen, newScreen, q); 01045 } 01046 01047 if (swapScreensWith) { 01048 //kDebug() << "setScreen due to swap, part 2"; 01049 swapScreensWith->setScreen(oldScreen, oldDesktop); 01050 } 01051 01052 checkRemoveAction(); 01053 01054 if (newScreen >= 0) { 01055 emit q->activate(); 01056 } 01057 } 01058 01059 int Containment::screen() const 01060 { 01061 return d->screen; 01062 } 01063 01064 int Containment::lastScreen() const 01065 { 01066 return d->lastScreen; 01067 } 01068 01069 int Containment::desktop() const 01070 { 01071 return d->desktop; 01072 } 01073 01074 int Containment::lastDesktop() const 01075 { 01076 return d->lastDesktop; 01077 } 01078 01079 KPluginInfo::List Containment::listContainments(const QString &category, 01080 const QString &parentApp) 01081 { 01082 return listContainmentsOfType(QString(), category, parentApp); 01083 } 01084 01085 01086 KPluginInfo::List Containment::listContainmentsOfType(const QString &type, 01087 const QString &category, 01088 const QString &parentApp) 01089 { 01090 QString constraint; 01091 01092 if (parentApp.isEmpty()) { 01093 constraint.append("(not exist [X-KDE-ParentApp] or [X-KDE-ParentApp] == '')"); 01094 } else { 01095 constraint.append("[X-KDE-ParentApp] == '").append(parentApp).append("'"); 01096 } 01097 01098 if (!type.isEmpty()) { 01099 if (!constraint.isEmpty()) { 01100 constraint.append(" and "); 01101 } 01102 01103 constraint.append("'").append(type).append("' ~in [X-Plasma-ContainmentCategories]"); 01104 } 01105 01106 if (!category.isEmpty()) { 01107 if (!constraint.isEmpty()) { 01108 constraint.append(" and "); 01109 } 01110 01111 constraint.append("[X-KDE-PluginInfo-Category] == '").append(category).append("'"); 01112 if (category == "Miscellaneous") { 01113 constraint.append(" or (not exist [X-KDE-PluginInfo-Category] or [X-KDE-PluginInfo-Category] == '')"); 01114 } 01115 } 01116 01117 KService::List offers = KServiceTypeTrader::self()->query("Plasma/Containment", constraint); 01118 //kDebug() << "constraint was" << constraint << "which got us" << offers.count() << "matches"; 01119 return KPluginInfo::fromServices(offers); 01120 } 01121 01122 KPluginInfo::List Containment::listContainmentsForMimetype(const QString &mimetype) 01123 { 01124 const QString constraint = QString("'%1' in [X-Plasma-DropMimeTypes]").arg(mimetype); 01125 //kDebug() << mimetype << constraint; 01126 const KService::List offers = KServiceTypeTrader::self()->query("Plasma/Containment", constraint); 01127 return KPluginInfo::fromServices(offers); 01128 } 01129 01130 QStringList Containment::listContainmentTypes() 01131 { 01132 KPluginInfo::List containmentInfos = listContainments(); 01133 QSet<QString> types; 01134 01135 foreach (const KPluginInfo &containmentInfo, containmentInfos) { 01136 QStringList theseTypes = containmentInfo.service()->property("X-Plasma-ContainmentCategories").toStringList(); 01137 foreach (const QString &type, theseTypes) { 01138 types.insert(type); 01139 } 01140 } 01141 01142 return types.toList(); 01143 } 01144 01145 void Containment::dragEnterEvent(QGraphicsSceneDragDropEvent *event) 01146 { 01147 //kDebug() << immutability() << Mutable << (immutability() == Mutable); 01148 event->setAccepted(immutability() == Mutable && 01149 (event->mimeData()->hasFormat(static_cast<Corona*>(scene())->appletMimeType()) || 01150 KUrl::List::canDecode(event->mimeData()) || 01151 event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType()))); 01152 01153 if (!event->isAccepted()) { 01154 // check to see if we have an applet that accepts the format. 01155 QStringList formats = event->mimeData()->formats(); 01156 01157 foreach (const QString &format, formats) { 01158 KPluginInfo::List appletList = Applet::listAppletInfoForMimetype(format); 01159 if (!appletList.isEmpty()) { 01160 event->setAccepted(true); 01161 break; 01162 } 01163 } 01164 01165 if (!event->isAccepted()) { 01166 foreach (const QString &format, formats) { 01167 KPluginInfo::List wallpaperList = Wallpaper::listWallpaperInfoForMimetype(format); 01168 if (!wallpaperList.isEmpty()) { 01169 event->setAccepted(true); 01170 break; 01171 } 01172 } 01173 } 01174 } 01175 01176 if (event->isAccepted()) { 01177 if (d->dropZoneStarted) { 01178 showDropZone(event->pos().toPoint()); 01179 } else { 01180 if (!d->showDropZoneDelayTimer) { 01181 d->showDropZoneDelayTimer = new QTimer(this); 01182 d->showDropZoneDelayTimer->setInterval(300); 01183 d->showDropZoneDelayTimer->setSingleShot(true); 01184 connect(d->showDropZoneDelayTimer, SIGNAL(timeout()), this, SLOT(showDropZoneDelayed())); 01185 } 01186 01187 d->dropPoints.insert(0, event->pos()); 01188 d->showDropZoneDelayTimer->start(); 01189 } 01190 } 01191 } 01192 01193 void Containment::dragLeaveEvent(QGraphicsSceneDragDropEvent *event) 01194 { 01195 //kDebug() << event->pos() << size().height() << size().width(); 01196 if (d->showDropZoneDelayTimer) { 01197 d->showDropZoneDelayTimer->stop(); 01198 } 01199 01200 if (event->pos().y() < 1 || event->pos().y() > size().height() || 01201 event->pos().x() < 1 || event->pos().x() > size().width()) { 01202 showDropZone(QPoint()); 01203 d->dropZoneStarted = false; 01204 } 01205 } 01206 01207 void ContainmentPrivate::showDropZoneDelayed() 01208 { 01209 dropZoneStarted = true; 01210 q->showDropZone(dropPoints.value(0).toPoint()); 01211 dropPoints.remove(0); 01212 } 01213 01214 void Containment::dragMoveEvent(QGraphicsSceneDragDropEvent *event) 01215 { 01216 QGraphicsItem *item = scene()->itemAt(event->scenePos()); 01217 event->setAccepted(item == this || item == d->toolBox.data() || !item); 01218 //kDebug() << event->isAccepted() << d->showDropZoneDelayTimer->isActive(); 01219 if (!event->isAccepted()) { 01220 if (d->showDropZoneDelayTimer) { 01221 d->showDropZoneDelayTimer->stop(); 01222 } 01223 } else if (!d->showDropZoneDelayTimer->isActive() && immutability() == Plasma::Mutable) { 01224 showDropZone(event->pos().toPoint()); 01225 } 01226 } 01227 01228 void Containment::dropEvent(QGraphicsSceneDragDropEvent *event) 01229 { 01230 if (isContainment()) { 01231 d->dropData(event->scenePos(), event->screenPos(), event); 01232 } else { 01233 Applet::dropEvent(event); 01234 } 01235 } 01236 01237 void ContainmentPrivate::dropData(QPointF scenePos, QPoint screenPos, QGraphicsSceneDragDropEvent *dropEvent) 01238 { 01239 if (q->immutability() != Mutable) { 01240 return; 01241 } 01242 01243 QPointF pos = q->mapFromScene(scenePos); 01244 const QMimeData *mimeData = 0; 01245 01246 if (dropEvent) { 01247 mimeData = dropEvent->mimeData(); 01248 } else { 01249 QClipboard *clipboard = QApplication::clipboard(); 01250 mimeData = clipboard->mimeData(QClipboard::Selection); 01251 //TODO if that's not supported (ie non-linux) should we try clipboard instead of selection? 01252 } 01253 01254 if (!mimeData) { 01255 //Selection is either empty or not supported on this OS 01256 kDebug() << "no mime data"; 01257 return; 01258 } 01259 01260 //kDebug() << event->mimeData()->text(); 01261 01262 QString appletMimetype(q->corona() ? q->corona()->appletMimeType() : QString()); 01263 01264 if (!appletMimetype.isEmpty() && mimeData->hasFormat(appletMimetype)) { 01265 QString data = mimeData->data(appletMimetype); 01266 const QStringList appletNames = data.split('\n', QString::SkipEmptyParts); 01267 foreach (const QString &appletName, appletNames) { 01268 //kDebug() << "doing" << appletName; 01269 QRectF geom(pos, QSize(0, 0)); 01270 q->addApplet(appletName, QVariantList(), geom); 01271 } 01272 if (dropEvent) { 01273 dropEvent->acceptProposedAction(); 01274 } 01275 } else if (mimeData->hasFormat(ExtenderItemMimeData::mimeType())) { 01276 kDebug() << "mimetype plasma/extenderitem is dropped, creating internal:extender"; 01277 //Handle dropping extenderitems. 01278 const ExtenderItemMimeData *extenderData = qobject_cast<const ExtenderItemMimeData*>(mimeData); 01279 if (extenderData) { 01280 ExtenderItem *item = extenderData->extenderItem(); 01281 QRectF geometry(pos - extenderData->pointerOffset(), item->size()); 01282 kDebug() << "desired geometry: " << geometry; 01283 Applet *applet = qobject_cast<ExtenderApplet *>(item->extender() ? item->extender()->applet() : 0); 01284 if (applet) { 01285 qreal left, top, right, bottom; 01286 applet->getContentsMargins(&left, &top, &right, &bottom); 01287 applet->setPos(geometry.topLeft() - QPointF(int(left), int(top))); 01288 applet->show(); 01289 } else { 01290 applet = addApplet("internal:extender", QVariantList(), geometry, 0, true); 01291 applet->hide(); 01292 applet->init(); 01293 appletAppeared(applet); 01294 applet->flushPendingConstraintsEvents(); 01295 applet->d->scheduleModificationNotification(); 01296 applet->adjustSize(); 01297 applet->show(); 01298 } 01299 item->setExtender(applet->extender()); 01300 } 01301 } else if (KUrl::List::canDecode(mimeData)) { 01302 //TODO: collect the mimetypes of available script engines and offer 01303 // to create widgets out of the matching URLs, if any 01304 const KUrl::List urls = KUrl::List::fromMimeData(mimeData); 01305 foreach (const KUrl &url, urls) { 01306 if (AccessManager::supportedProtocols().contains(url.protocol())) { 01307 AccessAppletJob *job = AccessManager::self()->accessRemoteApplet(url); 01308 if (dropEvent) { 01309 dropPoints[job] = dropEvent->pos(); 01310 } else { 01311 dropPoints[job] = scenePos; 01312 } 01313 QObject::connect(AccessManager::self(), SIGNAL(finished(Plasma::AccessAppletJob*)), 01314 q, SLOT(remoteAppletReady(Plasma::AccessAppletJob*))); 01315 } 01316 #ifndef PLASMA_NO_KIO 01317 else { 01318 KMimeType::Ptr mime = KMimeType::findByUrl(url); 01319 QString mimeName = mime->name(); 01320 QRectF geom(pos, QSize()); 01321 QVariantList args; 01322 args << url.url(); 01323 kDebug() << "can decode" << mimeName << args; 01324 01325 // It may be a directory or a file, let's stat 01326 KIO::JobFlags flags = KIO::HideProgressInfo; 01327 KIO::MimetypeJob *job = KIO::mimetype(url, flags); 01328 if (dropEvent) { 01329 dropPoints[job] = dropEvent->pos(); 01330 } else { 01331 dropPoints[job] = scenePos; 01332 } 01333 01334 QObject::connect(job, SIGNAL(result(KJob*)), q, SLOT(dropJobResult(KJob*))); 01335 QObject::connect(job, SIGNAL(mimetype(KIO::Job*,QString)), 01336 q, SLOT(mimeTypeRetrieved(KIO::Job*,QString))); 01337 01338 KMenu *choices = new KMenu("Content dropped"); 01339 choices->addAction(KIcon("process-working"), i18n("Fetching file type...")); 01340 if (dropEvent) { 01341 choices->popup(dropEvent->screenPos()); 01342 } else { 01343 choices->popup(screenPos); 01344 } 01345 01346 dropMenus[job] = choices; 01347 } 01348 #endif 01349 } 01350 01351 if (dropEvent) { 01352 dropEvent->acceptProposedAction(); 01353 } 01354 } else { 01355 QStringList formats = mimeData->formats(); 01356 QHash<QString, KPluginInfo> seenPlugins; 01357 QHash<QString, QString> pluginFormats; 01358 01359 foreach (const QString &format, formats) { 01360 KPluginInfo::List plugins = Applet::listAppletInfoForMimetype(format); 01361 01362 foreach (const KPluginInfo &plugin, plugins) { 01363 if (seenPlugins.contains(plugin.pluginName())) { 01364 continue; 01365 } 01366 01367 seenPlugins.insert(plugin.pluginName(), plugin); 01368 pluginFormats.insert(plugin.pluginName(), format); 01369 } 01370 } 01371 //kDebug() << "Mimetype ..." << formats << seenPlugins.keys() << pluginFormats.values(); 01372 01373 QString selectedPlugin; 01374 01375 if (seenPlugins.isEmpty()) { 01376 // do nothing 01377 } else if (seenPlugins.count() == 1) { 01378 selectedPlugin = seenPlugins.constBegin().key(); 01379 } else { 01380 KMenu choices; 01381 QHash<QAction *, QString> actionsToPlugins; 01382 foreach (const KPluginInfo &info, seenPlugins) { 01383 QAction *action; 01384 if (!info.icon().isEmpty()) { 01385 action = choices.addAction(KIcon(info.icon()), info.name()); 01386 } else { 01387 action = choices.addAction(info.name()); 01388 } 01389 01390 actionsToPlugins.insert(action, info.pluginName()); 01391 } 01392 01393 QAction *choice = choices.exec(screenPos); 01394 if (choice) { 01395 selectedPlugin = actionsToPlugins[choice]; 01396 } 01397 } 01398 01399 if (!selectedPlugin.isEmpty()) { 01400 if (!dropEvent) { 01401 // since we may have entered an event loop up above with the menu, 01402 // the clipboard item may no longer be valid, as QClipboard resets 01403 // the object behind the back of the application with a zero timer 01404 // so we fetch it again here 01405 QClipboard *clipboard = QApplication::clipboard(); 01406 mimeData = clipboard->mimeData(QClipboard::Selection); 01407 } 01408 01409 KTemporaryFile tempFile; 01410 if (mimeData && tempFile.open()) { 01411 //TODO: what should we do with files after the applet is done with them?? 01412 tempFile.setAutoRemove(false); 01413 01414 { 01415 QDataStream stream(&tempFile); 01416 QByteArray data = mimeData->data(pluginFormats[selectedPlugin]); 01417 stream.writeRawData(data, data.size()); 01418 } 01419 01420 QRectF geom(pos, QSize()); 01421 QVariantList args; 01422 args << tempFile.fileName(); 01423 kDebug() << args; 01424 tempFile.close(); 01425 01426 q->addApplet(selectedPlugin, args, geom); 01427 } 01428 } 01429 } 01430 } 01431 01432 void ContainmentPrivate::clearDataForMimeJob(KIO::Job *job) 01433 { 01434 #ifndef PLASMA_NO_KIO 01435 QObject::disconnect(job, 0, q, 0); 01436 dropPoints.remove(job); 01437 KMenu *choices = dropMenus.take(job); 01438 delete choices; 01439 job->kill(); 01440 #endif // PLASMA_NO_KIO 01441 } 01442 01443 void ContainmentPrivate::remoteAppletReady(Plasma::AccessAppletJob *job) 01444 { 01445 QPointF pos = dropPoints.take(job); 01446 if (job->error()) { 01447 //TODO: nice user visible error handling (knotification probably?) 01448 kDebug() << "remote applet access failed: " << job->errorText(); 01449 return; 01450 } 01451 01452 if (!job->applet()) { 01453 kDebug() << "how did we end up here? if applet is null, the job->error should be nonzero"; 01454 return; 01455 } 01456 01457 q->addApplet(job->applet(), pos); 01458 } 01459 01460 void ContainmentPrivate::dropJobResult(KJob *job) 01461 { 01462 #ifndef PLASMA_NO_KIO 01463 KIO::TransferJob* tjob = dynamic_cast<KIO::TransferJob*>(job); 01464 if (!tjob) { 01465 kDebug() << "job is not a KIO::TransferJob, won't handle the drop..."; 01466 clearDataForMimeJob(tjob); 01467 return; 01468 } 01469 if (job->error()) { 01470 kDebug() << "ERROR" << tjob->error() << ' ' << tjob->errorString(); 01471 } 01472 // We call mimetypeRetrieved since there might be other mechanisms 01473 // for finding suitable applets. Cleanup happens there as well. 01474 mimeTypeRetrieved(qobject_cast<KIO::Job *>(job), QString()); 01475 #endif // PLASMA_NO_KIO 01476 } 01477 01478 void ContainmentPrivate::mimeTypeRetrieved(KIO::Job *job, const QString &mimetype) 01479 { 01480 #ifndef PLASMA_NO_KIO 01481 kDebug() << "Mimetype Job returns." << mimetype; 01482 KIO::TransferJob* tjob = dynamic_cast<KIO::TransferJob*>(job); 01483 if (!tjob) { 01484 kDebug() << "job should be a TransferJob, but isn't"; 01485 clearDataForMimeJob(job); 01486 return; 01487 } 01488 KPluginInfo::List appletList = Applet::listAppletInfoForUrl(tjob->url()); 01489 if (mimetype.isEmpty() && !appletList.count()) { 01490 clearDataForMimeJob(job); 01491 kDebug() << "No applets found matching the url (" << tjob->url() << ") or the mimetype (" << mimetype << ")"; 01492 return; 01493 } else { 01494 01495 QPointF posi; // will be overwritten with the event's position 01496 if (dropPoints.keys().contains(tjob)) { 01497 posi = dropPoints[tjob]; 01498 kDebug() << "Received a suitable dropEvent at" << posi; 01499 } else { 01500 kDebug() << "Bailing out. Cannot find associated dropEvent related to the TransferJob"; 01501 clearDataForMimeJob(job); 01502 return; 01503 } 01504 01505 KMenu *choices = dropMenus.value(tjob); 01506 if (!choices) { 01507 kDebug() << "Bailing out. No QMenu found for this job."; 01508 clearDataForMimeJob(job); 01509 return; 01510 } 01511 01512 QVariantList args; 01513 args << tjob->url().url() << mimetype; 01514 01515 kDebug() << "Creating menu for:" << mimetype << posi << args; 01516 01517 appletList << Applet::listAppletInfoForMimetype(mimetype); 01518 KPluginInfo::List wallpaperList; 01519 if (drawWallpaper) { 01520 if (wallpaper && wallpaper->supportsMimetype(mimetype)) { 01521 wallpaperList << wallpaper->d->wallpaperDescription; 01522 } else { 01523 wallpaperList = Wallpaper::listWallpaperInfoForMimetype(mimetype); 01524 } 01525 } 01526 01527 if (!appletList.isEmpty() || !wallpaperList.isEmpty()) { 01528 choices->clear(); 01529 QHash<QAction *, QString> actionsToApplets; 01530 choices->addTitle(i18n("Widgets")); 01531 foreach (const KPluginInfo &info, appletList) { 01532 kDebug() << info.name(); 01533 QAction *action; 01534 if (!info.icon().isEmpty()) { 01535 action = choices->addAction(KIcon(info.icon()), info.name()); 01536 } else { 01537 action = choices->addAction(info.name()); 01538 } 01539 01540 actionsToApplets.insert(action, info.pluginName()); 01541 kDebug() << info.pluginName(); 01542 } 01543 actionsToApplets.insert(choices->addAction(i18n("Icon")), "icon"); 01544 01545 QHash<QAction *, QString> actionsToWallpapers; 01546 if (!wallpaperList.isEmpty()) { 01547 choices->addTitle(i18n("Wallpaper")); 01548 01549 QMap<QString, KPluginInfo> sorted; 01550 foreach (const KPluginInfo &info, appletList) { 01551 sorted.insert(info.name(), info); 01552 } 01553 01554 foreach (const KPluginInfo &info, wallpaperList) { 01555 QAction *action; 01556 if (!info.icon().isEmpty()) { 01557 action = choices->addAction(KIcon(info.icon()), info.name()); 01558 } else { 01559 action = choices->addAction(info.name()); 01560 } 01561 01562 actionsToWallpapers.insert(action, info.pluginName()); 01563 } 01564 } 01565 01566 QAction *choice = choices->exec(); 01567 if (choice) { 01568 // Put the job on hold so it can be recycled to fetch the actual content, 01569 // which is to be expected when something's dropped onto the desktop and 01570 // an applet is to be created with this URL 01571 if (!mimetype.isEmpty() && !tjob->error()) { 01572 tjob->putOnHold(); 01573 KIO::Scheduler::publishSlaveOnHold(); 01574 } 01575 QString plugin = actionsToApplets.value(choice); 01576 if (plugin.isEmpty()) { 01577 //set wallpapery stuff 01578 plugin = actionsToWallpapers.value(choice); 01579 if (!wallpaper || plugin != wallpaper->pluginName()) { 01580 kDebug() << "Wallpaper dropped:" << tjob->url(); 01581 q->setWallpaper(plugin); 01582 } 01583 01584 if (wallpaper) { 01585 kDebug() << "Wallpaper dropped:" << tjob->url(); 01586 wallpaper->setUrls(KUrl::List() << tjob->url()); 01587 } 01588 } else { 01589 addApplet(actionsToApplets[choice], args, QRectF(posi, QSize())); 01590 } 01591 01592 clearDataForMimeJob(job); 01593 return; 01594 } 01595 } else { 01596 // we can at least create an icon as a link to the URL 01597 addApplet("icon", args, QRectF(posi, QSize())); 01598 } 01599 } 01600 01601 clearDataForMimeJob(job); 01602 #endif // PLASMA_NO_KIO 01603 } 01604 01605 #ifndef KDE_NO_DEPRECATED 01606 const QGraphicsItem *Containment::toolBoxItem() const 01607 { 01608 return d->toolBox.data(); 01609 } 01610 #endif 01611 01612 void Containment::setToolBox(AbstractToolBox *toolBox) 01613 { 01614 if (d->toolBox.data()) { 01615 d->toolBox.data()->deleteLater(); 01616 } 01617 d->toolBox = toolBox; 01618 } 01619 01620 AbstractToolBox *Containment::toolBox() const 01621 { 01622 return d->toolBox.data(); 01623 } 01624 01625 void Containment::resizeEvent(QGraphicsSceneResizeEvent *event) 01626 { 01627 Applet::resizeEvent(event); 01628 01629 if (isContainment()) { 01630 if (d->isPanelContainment()) { 01631 d->positionPanel(); 01632 } else if (corona()) { 01633 QMetaObject::invokeMethod(corona(), "layoutContainments"); 01634 } 01635 01636 if (d->wallpaper) { 01637 d->wallpaper->setBoundingRect(QRectF(QPointF(0, 0), size())); 01638 } 01639 } 01640 } 01641 01642 void Containment::keyPressEvent(QKeyEvent *event) 01643 { 01644 //kDebug() << "keyPressEvent with" << event->key() 01645 // << "and hoping and wishing for a" << Qt::Key_Tab; 01646 if (event->key() == Qt::Key_Tab) { // && event->modifiers() == 0) { 01647 if (!d->applets.isEmpty()) { 01648 kDebug() << "let's give focus to...." << (QObject*)d->applets.first(); 01649 d->applets.first()->setFocus(Qt::TabFocusReason); 01650 } 01651 } 01652 } 01653 01654 void Containment::wheelEvent(QGraphicsSceneWheelEvent *event) 01655 { 01656 event->ignore(); 01657 if (d->appletAt(event->scenePos())) { 01658 return; //no unexpected click-throughs 01659 } 01660 01661 if (d->wallpaper && d->wallpaper->isInitialized()) { 01662 QGraphicsItem *item = scene()->itemAt(event->scenePos()); 01663 if (item == this) { 01664 event->ignore(); 01665 d->wallpaper->wheelEvent(event); 01666 01667 if (event->isAccepted()) { 01668 return; 01669 } 01670 } 01671 } 01672 01673 QString trigger = ContainmentActions::eventToString(event); 01674 01675 if (d->prepareContainmentActions(trigger, event->screenPos())) { 01676 d->actionPlugins()->value(trigger)->contextEvent(event); 01677 event->accept(); 01678 } else { 01679 event->ignore(); 01680 Applet::wheelEvent(event); 01681 } 01682 } 01683 01684 bool Containment::sceneEventFilter(QGraphicsItem *watched, QEvent *event) 01685 { 01686 return Applet::sceneEventFilter(watched, event); 01687 } 01688 01689 QVariant Containment::itemChange(GraphicsItemChange change, const QVariant &value) 01690 { 01691 //FIXME if the applet is moved to another containment we need to unfocus it 01692 01693 if (isContainment() && 01694 (change == QGraphicsItem::ItemSceneHasChanged || 01695 change == QGraphicsItem::ItemPositionHasChanged)) { 01696 switch (d->type) { 01697 case PanelContainment: 01698 case CustomPanelContainment: 01699 d->positionPanel(); 01700 break; 01701 default: 01702 if (corona()) { 01703 QMetaObject::invokeMethod(corona(), "layoutContainments"); 01704 } 01705 break; 01706 } 01707 } 01708 01709 return Applet::itemChange(change, value); 01710 } 01711 01712 void Containment::enableAction(const QString &name, bool enable) 01713 { 01714 QAction *action = this->action(name); 01715 if (action) { 01716 action->setEnabled(enable); 01717 action->setVisible(enable); 01718 } 01719 } 01720 01721 void Containment::addToolBoxAction(QAction *action) 01722 { 01723 d->createToolBox(); 01724 if (d->toolBox) { 01725 d->toolBox.data()->addTool(action); 01726 } 01727 } 01728 01729 void Containment::removeToolBoxAction(QAction *action) 01730 { 01731 if (d->toolBox) { 01732 d->toolBox.data()->removeTool(action); 01733 } 01734 } 01735 01736 void Containment::setToolBoxOpen(bool open) 01737 { 01738 if (open) { 01739 openToolBox(); 01740 } else { 01741 closeToolBox(); 01742 } 01743 } 01744 01745 bool Containment::isToolBoxOpen() const 01746 { 01747 return (d->toolBox && d->toolBox.data()->isShowing()); 01748 } 01749 01750 void Containment::openToolBox() 01751 { 01752 if (d->toolBox && !d->toolBox.data()->isShowing()) { 01753 d->toolBox.data()->setShowing(true); 01754 emit toolBoxVisibilityChanged(true); 01755 } 01756 } 01757 01758 void Containment::closeToolBox() 01759 { 01760 if (d->toolBox && d->toolBox.data()->isShowing()) { 01761 d->toolBox.data()->setShowing(false); 01762 emit toolBoxVisibilityChanged(false); 01763 } 01764 } 01765 01766 void Containment::addAssociatedWidget(QWidget *widget) 01767 { 01768 Applet::addAssociatedWidget(widget); 01769 if (d->focusedApplet) { 01770 d->focusedApplet->addAssociatedWidget(widget); 01771 } 01772 01773 foreach (const Applet *applet, d->applets) { 01774 if (applet->d->activationAction) { 01775 widget->addAction(applet->d->activationAction); 01776 } 01777 } 01778 } 01779 01780 void Containment::removeAssociatedWidget(QWidget *widget) 01781 { 01782 Applet::removeAssociatedWidget(widget); 01783 if (d->focusedApplet) { 01784 d->focusedApplet->removeAssociatedWidget(widget); 01785 } 01786 01787 foreach (const Applet *applet, d->applets) { 01788 if (applet->d->activationAction) { 01789 widget->removeAction(applet->d->activationAction); 01790 } 01791 } 01792 } 01793 01794 void Containment::setDrawWallpaper(bool drawWallpaper) 01795 { 01796 d->drawWallpaper = drawWallpaper; 01797 if (drawWallpaper) { 01798 KConfigGroup cfg = config(); 01799 const QString wallpaper = cfg.readEntry("wallpaperplugin", defaultWallpaper); 01800 const QString mode = cfg.readEntry("wallpaperpluginmode", defaultWallpaperMode); 01801 setWallpaper(wallpaper, mode); 01802 } else { 01803 delete d->wallpaper; 01804 d->wallpaper = 0; 01805 } 01806 } 01807 01808 bool Containment::drawWallpaper() 01809 { 01810 return d->drawWallpaper; 01811 } 01812 01813 void Containment::setWallpaper(const QString &pluginName, const QString &mode) 01814 { 01815 KConfigGroup cfg = config(); 01816 bool newPlugin = true; 01817 bool newMode = true; 01818 01819 if (d->drawWallpaper) { 01820 if (d->wallpaper) { 01821 // we have a wallpaper, so let's decide whether we need to swap it out 01822 if (d->wallpaper->pluginName() != pluginName) { 01823 delete d->wallpaper; 01824 d->wallpaper = 0; 01825 } else { 01826 // it's the same plugin, so let's save its state now so when 01827 // we call restore later on we're safe 01828 newMode = d->wallpaper->renderingMode().name() != mode; 01829 newPlugin = false; 01830 } 01831 } 01832 01833 if (!pluginName.isEmpty() && !d->wallpaper) { 01834 d->wallpaper = Plasma::Wallpaper::load(pluginName); 01835 } 01836 01837 if (d->wallpaper) { 01838 d->wallpaper->setParent(this); 01839 d->wallpaper->setBoundingRect(QRectF(QPointF(0, 0), size())); 01840 d->wallpaper->setRenderingMode(mode); 01841 01842 if (newPlugin) { 01843 cfg.writeEntry("wallpaperplugin", pluginName); 01844 } 01845 01846 if (d->wallpaper->isInitialized()) { 01847 KConfigGroup wallpaperConfig = KConfigGroup(&cfg, "Wallpaper"); 01848 wallpaperConfig = KConfigGroup(&wallpaperConfig, pluginName); 01849 d->wallpaper->restore(wallpaperConfig); 01850 } 01851 01852 if (newMode) { 01853 cfg.writeEntry("wallpaperpluginmode", mode); 01854 } 01855 } 01856 01857 update(); 01858 } 01859 01860 if (!d->wallpaper) { 01861 cfg.deleteEntry("wallpaperplugin"); 01862 cfg.deleteEntry("wallpaperpluginmode"); 01863 } 01864 01865 if (newPlugin || newMode) { 01866 if (newPlugin && d->wallpaper) { 01867 connect(d->wallpaper, SIGNAL(configureRequested()), this, SLOT(requestConfiguration())); 01868 connect(d->wallpaper, SIGNAL(configNeedsSaving()), this, SIGNAL(configNeedsSaving())); 01869 } 01870 01871 emit configNeedsSaving(); 01872 } 01873 } 01874 01875 Plasma::Wallpaper *Containment::wallpaper() const 01876 { 01877 return d->wallpaper; 01878 } 01879 01880 void Containment::setContainmentActions(const QString &trigger, const QString &pluginName) 01881 { 01882 KConfigGroup cfg = containmentActionsConfig(); 01883 ContainmentActions *plugin = 0; 01884 01885 if (d->actionPlugins()->contains(trigger)) { 01886 plugin = d->actionPlugins()->value(trigger); 01887 if (plugin->pluginName() != pluginName) { 01888 d->actionPlugins()->remove(trigger); 01889 delete plugin; 01890 plugin=0; 01891 } 01892 } 01893 if (pluginName.isEmpty()) { 01894 cfg.deleteEntry(trigger); 01895 } else if (plugin) { 01896 //it already existed, just reload config 01897 if (plugin->isInitialized()) { 01898 plugin->setContainment(this); //to be safe 01899 //FIXME make a truly unique config group 01900 KConfigGroup pluginConfig = KConfigGroup(&cfg, trigger); 01901 plugin->restore(pluginConfig); 01902 } 01903 } else { 01904 switch (d->containmentActionsSource) { 01905 case ContainmentPrivate::Activity: 01906 //FIXME 01907 case ContainmentPrivate::Local: 01908 plugin = ContainmentActions::load(this, pluginName); 01909 break; 01910 default: 01911 plugin = ContainmentActions::load(0, pluginName); 01912 } 01913 if (plugin) { 01914 cfg.writeEntry(trigger, pluginName); 01915 d->actionPlugins()->insert(trigger, plugin); 01916 } else { 01917 //bad plugin... gets removed. is this a feature or a bug? 01918 cfg.deleteEntry(trigger); 01919 } 01920 } 01921 01922 emit configNeedsSaving(); 01923 } 01924 01925 QStringList Containment::containmentActionsTriggers() 01926 { 01927 return d->actionPlugins()->keys(); 01928 } 01929 01930 QString Containment::containmentActions(const QString &trigger) 01931 { 01932 ContainmentActions *c = d->actionPlugins()->value(trigger); 01933 return c ? c->pluginName() : QString(); 01934 } 01935 01936 void Containment::setActivity(const QString &activity) 01937 { 01938 Context *context = d->context(); 01939 if (context->currentActivity() != activity) { 01940 context->setCurrentActivity(activity); 01941 } 01942 } 01943 01944 void ContainmentPrivate::onContextChanged(Plasma::Context *con) 01945 { 01946 foreach (Applet *a, applets) { 01947 a->updateConstraints(ContextConstraint); 01948 } 01949 01950 KConfigGroup c = q->config(); 01951 QString act = con->currentActivityId(); 01952 01953 //save anything that's been set (boy I hope this avoids overwriting things) 01954 //FIXME of course if the user sets the name to an empty string we have a bug 01955 //but once we get context retrieving the name as soon as the id is set, this issue should go away 01956 if (!act.isEmpty()) { 01957 c.writeEntry("activityId", act); 01958 } 01959 act = con->currentActivity(); 01960 if (!act.isEmpty()) { 01961 c.writeEntry("activity", act); 01962 } 01963 01964 if (toolBox) { 01965 toolBox.data()->update(); 01966 } 01967 emit q->configNeedsSaving(); 01968 emit q->contextChanged(con); 01969 } 01970 01971 QString Containment::activity() const 01972 { 01973 return d->context()->currentActivity(); 01974 } 01975 01976 Context *Containment::context() const 01977 { 01978 return d->context(); 01979 } 01980 01981 Context *ContainmentPrivate::context() 01982 { 01983 if (!con) { 01984 con = new Context(q); 01985 q->connect(con, SIGNAL(changed(Plasma::Context*)), 01986 q, SLOT(onContextChanged(Plasma::Context*))); 01987 } 01988 01989 return con; 01990 } 01991 01992 KActionCollection* ContainmentPrivate::actions() 01993 { 01994 return static_cast<Applet*>(q)->d->actions; 01995 } 01996 01997 void ContainmentPrivate::focusApplet(Plasma::Applet *applet) 01998 { 01999 if (focusedApplet == applet) { 02000 return; 02001 } 02002 02003 QList<QWidget *> widgets = actions()->associatedWidgets(); 02004 if (focusedApplet) { 02005 foreach (QWidget *w, widgets) { 02006 focusedApplet->removeAssociatedWidget(w); 02007 } 02008 } 02009 02010 if (applet && applets.contains(applet)) { 02011 //kDebug() << "switching to" << applet->name(); 02012 focusedApplet = applet; 02013 foreach (QWidget *w, widgets) { 02014 focusedApplet->addAssociatedWidget(w); 02015 } 02016 02017 if (!focusedApplet->hasFocus()) { 02018 focusedApplet->setFocus(Qt::ShortcutFocusReason); 02019 } 02020 } else { 02021 focusedApplet = 0; 02022 } 02023 } 02024 02025 void Containment::focusNextApplet() 02026 { 02027 if (d->applets.isEmpty()) { 02028 return; 02029 } 02030 int index = d->focusedApplet ? d->applets.indexOf(d->focusedApplet) + 1 : 0; 02031 if (index >= d->applets.size()) { 02032 index = 0; 02033 } 02034 kDebug() << "index" << index; 02035 d->focusApplet(d->applets.at(index)); 02036 } 02037 02038 void Containment::focusPreviousApplet() 02039 { 02040 if (d->applets.isEmpty()) { 02041 return; 02042 } 02043 int index = d->focusedApplet ? d->applets.indexOf(d->focusedApplet) - 1 : -1; 02044 if (index < 0) { 02045 index = d->applets.size() - 1; 02046 } 02047 kDebug() << "index" << index; 02048 d->focusApplet(d->applets.at(index)); 02049 } 02050 02051 void Containment::destroy() 02052 { 02053 destroy(true); 02054 } 02055 02056 void Containment::showConfigurationInterface() 02057 { 02058 Applet::showConfigurationInterface(); 02059 } 02060 02061 void Containment::configChanged() 02062 { 02063 } 02064 02065 void ContainmentPrivate::configChanged() 02066 { 02067 if (drawWallpaper) { 02068 KConfigGroup group = q->config(); 02069 q->setWallpaper(group.readEntry("wallpaperplugin", defaultWallpaper), 02070 group.readEntry("wallpaperpluginmode", defaultWallpaperMode)); 02071 } 02072 } 02073 02074 void ContainmentPrivate::requestConfiguration() 02075 { 02076 emit q->configureRequested(q); 02077 } 02078 02079 void ContainmentPrivate::checkStatus(Plasma::ItemStatus appletStatus) 02080 { 02081 //kDebug() << "================== "<< appletStatus << q->status(); 02082 if (appletStatus == q->status()) { 02083 emit q->newStatus(appletStatus); 02084 return; 02085 } 02086 02087 if (appletStatus < q->status()) { 02088 // check to see if any other applet has a higher status, and stick with that 02089 // if we do 02090 foreach (Applet *applet, applets) { 02091 if (applet->status() > appletStatus) { 02092 appletStatus = applet->status(); 02093 } 02094 } 02095 } 02096 02097 q->setStatus(appletStatus); 02098 } 02099 02100 void Containment::destroy(bool confirm) 02101 { 02102 if (immutability() != Mutable || Applet::d->transient) { 02103 return; 02104 } 02105 02106 if (isContainment() && confirm) { 02107 //FIXME: should not be blocking 02108 const QString title = i18nc("@title:window %1 is the name of the containment", "Remove %1", name()); 02109 KGuiItem remove = KStandardGuiItem::remove(); 02110 remove.setText(title); 02111 if (KMessageBox::warningContinueCancel(view(), 02112 i18nc("%1 is the name of the containment", "Do you really want to remove this %1?", name()), 02113 title, remove) != KMessageBox::Continue) { 02114 return; 02115 } 02116 } 02117 02118 Applet::destroy(); 02119 } 02120 02121 void ContainmentPrivate::createToolBox() 02122 { 02123 if (!toolBox && KAuthorized::authorizeKAction("plasma/containment_context_menu")) { 02124 toolBox = Plasma::AbstractToolBox::load(q->corona()->preferredToolBoxPlugin(type), QVariantList(), q); 02125 02126 if (toolBox) { 02127 QObject::connect(toolBox.data(), SIGNAL(toggled()), q, SIGNAL(toolBoxToggled())); 02128 QObject::connect(toolBox.data(), SIGNAL(toggled()), q, SLOT(updateToolBoxVisibility())); 02129 02130 positionToolBox(); 02131 } 02132 } 02133 } 02134 02135 void ContainmentPrivate::positionToolBox() 02136 { 02137 QMetaObject::invokeMethod(toolBox.data(), "reposition"); 02138 } 02139 02140 void ContainmentPrivate::updateToolBoxVisibility() 02141 { 02142 emit q->toolBoxVisibilityChanged(toolBox.data()->isShowing()); 02143 } 02144 02145 void ContainmentPrivate::triggerShowAddWidgets() 02146 { 02147 emit q->showAddWidgetsInterface(QPointF()); 02148 } 02149 02150 void ContainmentPrivate::containmentConstraintsEvent(Plasma::Constraints constraints) 02151 { 02152 if (!q->isContainment()) { 02153 return; 02154 } 02155 02156 //kDebug() << "got containmentConstraintsEvent" << constraints << (QObject*)toolBox; 02157 if (constraints & Plasma::ImmutableConstraint) { 02158 //update actions 02159 checkRemoveAction(); 02160 const bool unlocked = q->immutability() == Mutable; 02161 q->setAcceptDrops(unlocked); 02162 q->enableAction("add widgets", unlocked); 02163 02164 // tell the applets too 02165 foreach (Applet *a, applets) { 02166 a->setImmutability(q->immutability()); 02167 a->updateConstraints(ImmutableConstraint); 02168 } 02169 } 02170 02171 // pass on the constraints that are relevant here 02172 Constraints appletConstraints = NoConstraint; 02173 if (constraints & FormFactorConstraint) { 02174 appletConstraints |= FormFactorConstraint; 02175 } 02176 02177 if (constraints & ScreenConstraint) { 02178 appletConstraints |= ScreenConstraint; 02179 } 02180 02181 if (appletConstraints != NoConstraint) { 02182 foreach (Applet *applet, applets) { 02183 applet->updateConstraints(appletConstraints); 02184 } 02185 } 02186 02187 if (toolBox && (constraints & Plasma::SizeConstraint || 02188 constraints & Plasma::FormFactorConstraint || 02189 constraints & Plasma::ScreenConstraint || 02190 constraints & Plasma::StartupCompletedConstraint)) { 02191 //kDebug() << "Positioning toolbox"; 02192 positionToolBox(); 02193 } 02194 02195 if (constraints & Plasma::StartupCompletedConstraint && type < Containment::CustomContainment) { 02196 q->addToolBoxAction(q->action("remove")); 02197 checkRemoveAction(); 02198 } 02199 } 02200 02201 Applet *ContainmentPrivate::addApplet(const QString &name, const QVariantList &args, 02202 const QRectF &appletGeometry, uint id, bool delayInit) 02203 { 02204 if (!q->isContainment()) { 02205 return 0; 02206 } 02207 02208 if (!delayInit && q->immutability() != Mutable) { 02209 kDebug() << "addApplet for" << name << "requested, but we're currently immutable!"; 02210 return 0; 02211 } 02212 02213 QGraphicsView *v = q->view(); 02214 if (v) { 02215 v->setCursor(Qt::BusyCursor); 02216 } 02217 02218 Applet *applet = Applet::load(name, id, args); 02219 if (v) { 02220 v->unsetCursor(); 02221 } 02222 02223 if (!applet) { 02224 kDebug() << "Applet" << name << "could not be loaded."; 02225 applet = new Applet(0, QString(), id); 02226 applet->setFailedToLaunch(true, i18n("Could not find requested component: %1", name)); 02227 } 02228 02229 //kDebug() << applet->name() << "sizehint:" << applet->sizeHint() << "geometry:" << applet->geometry(); 02230 02231 q->addApplet(applet, appletGeometry.topLeft(), delayInit); 02232 return applet; 02233 } 02234 02235 bool ContainmentPrivate::regionIsEmpty(const QRectF ®ion, Applet *ignoredApplet) const 02236 { 02237 foreach (Applet *applet, applets) { 02238 if (applet != ignoredApplet && applet->geometry().intersects(region)) { 02239 return false; 02240 } 02241 } 02242 return true; 02243 } 02244 02245 void ContainmentPrivate::appletDestroyed(Plasma::Applet *applet) 02246 { 02247 applets.removeAll(applet); 02248 if (focusedApplet == applet) { 02249 focusedApplet = 0; 02250 } 02251 02252 emit q->appletRemoved(applet); 02253 emit q->configNeedsSaving(); 02254 } 02255 02256 void ContainmentPrivate::appletAppearAnimationComplete() 02257 { 02258 Animation *anim = qobject_cast<Animation *>(q->sender()); 02259 if (anim) { 02260 Applet *applet = qobject_cast<Applet*>(anim->targetWidget()); 02261 if (applet) { 02262 appletAppeared(applet); 02263 } 02264 } 02265 } 02266 02267 void ContainmentPrivate::appletAppeared(Applet *applet) 02268 { 02269 //kDebug() << type << Containment::DesktopContainment; 02270 KConfigGroup *cg = applet->d->mainConfigGroup(); 02271 applet->save(*cg); 02272 emit q->configNeedsSaving(); 02273 } 02274 02275 void ContainmentPrivate::positionPanel(bool force) 02276 { 02277 if (!q->scene()) { 02278 kDebug() << "no scene yet"; 02279 return; 02280 } 02281 02282 // already positioning the panel - avoid infinite loops 02283 if (ContainmentPrivate::s_positioningPanels) { 02284 return; 02285 } 02286 02287 // we position panels in negative coordinates, and stack all horizontal 02288 // and all vertical panels with each other. 02289 02290 02291 const QPointF p = q->pos(); 02292 02293 if (!force && 02294 p.y() + q->size().height() < -INTER_CONTAINMENT_MARGIN && 02295 q->scene()->collidingItems(q).isEmpty()) { 02296 // already positioned and not running into any other panels 02297 return; 02298 } 02299 02300 02301 QPointF newPos = preferredPanelPos(q->corona()); 02302 if (p != newPos) { 02303 ContainmentPrivate::s_positioningPanels = true; 02304 q->setPos(newPos); 02305 ContainmentPrivate::s_positioningPanels = false; 02306 } 02307 } 02308 02309 bool ContainmentPrivate::isPanelContainment() const 02310 { 02311 return type == Containment::PanelContainment || type == Containment::CustomPanelContainment; 02312 } 02313 02314 QPointF ContainmentPrivate::preferredPos(Corona *corona) const 02315 { 02316 Q_ASSERT(corona); 02317 02318 if (isPanelContainment()) { 02319 //kDebug() << "is a panel, so put it at" << preferredPanelPos(corona); 02320 return preferredPanelPos(corona); 02321 } 02322 02323 QPointF pos(0, 0); 02324 QTransform t; 02325 while (QGraphicsItem *i = corona->itemAt(pos, t)) { 02326 pos.setX(i->scenePos().x() + i->boundingRect().width() + 10); 02327 } 02328 02329 //kDebug() << "not a panel, put it at" << pos; 02330 return pos; 02331 } 02332 02333 QPointF ContainmentPrivate::preferredPanelPos(Corona *corona) const 02334 { 02335 Q_ASSERT(corona); 02336 02337 //TODO: research how non-Horizontal, non-Vertical (e.g. Planar) panels behave here 02338 bool horiz = formFactor == Plasma::Horizontal; 02339 qreal bottom = horiz ? 0 : VERTICAL_STACKING_OFFSET; 02340 qreal lastHeight = 0; 02341 02342 // this should be ok for small numbers of panels, but if we ever end 02343 // up managing hundreds of them, this simplistic alogrithm will 02344 // likely be too slow. 02345 foreach (const Containment *other, corona->containments()) { 02346 if (other == q || 02347 !other->d->isPanelContainment() || 02348 horiz != (other->formFactor() == Plasma::Horizontal)) { 02349 // only line up with panels of the same orientation 02350 continue; 02351 } 02352 02353 if (horiz) { 02354 qreal y = other->pos().y(); 02355 if (y < bottom) { 02356 lastHeight = other->size().height(); 02357 bottom = y; 02358 } 02359 } else { 02360 qreal width = other->size().width(); 02361 qreal x = other->pos().x() + width; 02362 if (x > bottom) { 02363 lastHeight = width; 02364 bottom = x + lastHeight; 02365 } 02366 } 02367 } 02368 02369 // give a space equal to the height again of the last item so there is 02370 // room to grow. 02371 QPointF newPos; 02372 if (horiz) { 02373 bottom -= lastHeight + INTER_CONTAINMENT_MARGIN; 02374 //TODO: fix x position for non-flush-left panels 02375 kDebug() << "moved to" << QPointF(0, bottom - q->size().height()); 02376 newPos = QPointF(0, bottom - q->size().height()); 02377 } else { 02378 bottom += lastHeight + INTER_CONTAINMENT_MARGIN; 02379 //TODO: fix y position for non-flush-top panels 02380 kDebug() << "moved to" << QPointF(bottom + q->size().width(), -INTER_CONTAINMENT_MARGIN - q->size().height()); 02381 newPos = QPointF(bottom + q->size().width(), -INTER_CONTAINMENT_MARGIN - q->size().height()); 02382 } 02383 02384 return newPos; 02385 } 02386 02387 02388 bool ContainmentPrivate::prepareContainmentActions(const QString &trigger, const QPoint &screenPos, KMenu *menu) 02389 { 02390 ContainmentActions *plugin = actionPlugins()->value(trigger); 02391 if (!plugin) { 02392 return false; 02393 } 02394 plugin->setContainment(q); 02395 02396 if (!plugin->isInitialized()) { 02397 KConfigGroup cfg = q->containmentActionsConfig(); 02398 KConfigGroup pluginConfig = KConfigGroup(&cfg, trigger); 02399 plugin->restore(pluginConfig); 02400 } 02401 02402 if (plugin->configurationRequired()) { 02403 KMenu *localMenu = menu ? menu : new KMenu(); 02404 02405 localMenu->addTitle(i18n("This plugin needs to be configured")); 02406 localMenu->addAction(q->action("configure")); 02407 02408 if (!menu) { 02409 localMenu->exec(screenPos); 02410 delete localMenu; 02411 } 02412 02413 return false; 02414 } else if (menu) { 02415 QList<QAction*> actions = plugin->contextualActions(); 02416 if (actions.isEmpty()) { 02417 //it probably didn't bother implementing the function. give the user a chance to set 02418 //a better plugin. note that if the user sets no-plugin this won't happen... 02419 if (!isPanelContainment() && q->action("configure")) { 02420 menu->addAction(q->action("configure")); 02421 } 02422 } else { 02423 menu->addActions(actions); 02424 } 02425 } 02426 02427 return true; 02428 } 02429 02430 KConfigGroup Containment::containmentActionsConfig() 02431 { 02432 KConfigGroup cfg; 02433 switch (d->containmentActionsSource) { 02434 case ContainmentPrivate::Local: 02435 cfg = config(); 02436 cfg = KConfigGroup(&cfg, "ActionPlugins"); 02437 break; 02438 case ContainmentPrivate::Activity: 02439 cfg = KConfigGroup(corona()->config(), "Activities"); 02440 cfg = KConfigGroup(&cfg, d->context()->currentActivityId()); 02441 cfg = KConfigGroup(&cfg, "ActionPlugins"); 02442 break; 02443 default: 02444 cfg = KConfigGroup(corona()->config(), "ActionPlugins"); 02445 } 02446 return cfg; 02447 } 02448 02449 QHash<QString, ContainmentActions*> * ContainmentPrivate::actionPlugins() 02450 { 02451 switch (containmentActionsSource) { 02452 case Activity: 02453 //FIXME 02454 case Local: 02455 return &localActionPlugins; 02456 default: 02457 return &globalActionPlugins; 02458 } 02459 } 02460 02461 } // Plasma namespace 02462 02463 #include "containment.moc" 02464
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu May 10 2012 20:51:35 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu May 10 2012 20:51:35 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.