/* * Copyright 2007 by Aaron Seigo * Copyright 2008 by Ménard Alexis * Copyright 2009 Chani Armitage * Copyright 2012 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "containment.h" #include "private/containment_p.h" #include "config-plasma.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef PLASMA_NO_KIO #include "kio/jobclasses.h" // for KIO::JobFlags #include "kio/job.h" #include "kio/scheduler.h" #endif #include "abstracttoolbox.h" #include "animator.h" #include "containmentactions.h" #include "containmentactionspluginsconfig.h" #include "corona.h" #include "pluginloader.h" #include "svg.h" #include "wallpaper.h" #include "remote/accessappletjob.h" #include "remote/accessmanager.h" #include "private/applet_p.h" #include "private/containmentactionspluginsconfig_p.h" #include "private/wallpaper_p.h" #include "plasma/plasma.h" namespace Plasma { Containment::StyleOption::StyleOption() : QStyleOptionGraphicsItem(), view(0) { version = Version; type = Type; } Containment::StyleOption::StyleOption(const Containment::StyleOption & other) : QStyleOptionGraphicsItem(other), view(other.view) { version = Version; type = Type; } Containment::StyleOption::StyleOption(const QStyleOptionGraphicsItem &other) : QStyleOptionGraphicsItem(other), view(0) { version = Version; type = Type; } Containment::Containment(QGraphicsItem *parent, const QString &serviceId, uint containmentId) : Applet(parent, serviceId, containmentId), d(new ContainmentPrivate(this)) { // WARNING: do not access config() OR globalConfig() in this method! // that requires a scene, which is not available at this point setPos(0, 0); setBackgroundHints(NoBackground); setContainmentType(CustomContainment); setHasConfigurationInterface(false); } Containment::Containment(QObject *parent, const QVariantList &args) : Applet(parent, args), d(new ContainmentPrivate(this)) { // WARNING: do not access config() OR globalConfig() in this method! // that requires a scene, which is not available at this point setPos(0, 0); setBackgroundHints(NoBackground); setHasConfigurationInterface(false); } Containment::Containment(const QString &packagePath, uint appletId, const QVariantList &args) : Applet(packagePath, appletId, args), d(new ContainmentPrivate(this)) { // WARNING: do not access config() OR globalConfig() in this method! // that requires a scene, which is not available at this point setPos(0, 0); setBackgroundHints(NoBackground); setHasConfigurationInterface(false); } Containment::~Containment() { delete d; // Applet touches our dptr if we are a containment and is the superclass (think of dtors) // so we reset this as we exit the building Applet::d->isContainment = false; } void Containment::init() { Applet::init(); if (!isContainment()) { return; } setCacheMode(NoCache); setFlag(QGraphicsItem::ItemIsMovable, false); setFlag(QGraphicsItem::ItemClipsChildrenToShape, true); setAcceptDrops(true); setAcceptsHoverEvents(true); if (d->type == NoContainmentType) { setContainmentType(DesktopContainment); } //connect actions ContainmentPrivate::addDefaultActions(d->actions(), this); bool unlocked = immutability() == Mutable; //fix the text of the actions that need name() //btw, do we really want to use name() when it's a desktopcontainment? QAction *closeApplet = action("remove"); if (closeApplet) { closeApplet->setText(i18nc("%1 is the name of the applet", "Remove this %1", name())); } QAction *configAction = action("configure"); if (configAction) { configAction->setText(i18nc("%1 is the name of the applet", "%1 Settings", name())); } QAction *appletBrowserAction = action("add widgets"); if (appletBrowserAction) { appletBrowserAction->setVisible(unlocked); appletBrowserAction->setEnabled(unlocked); connect(appletBrowserAction, SIGNAL(triggered()), this, SLOT(triggerShowAddWidgets())); } QAction *act = action("next applet"); if (act) { connect(act, SIGNAL(triggered()), this, SLOT(focusNextApplet())); } act = action("previous applet"); if (act) { connect(act, SIGNAL(triggered()), this, SLOT(focusPreviousApplet())); } if (immutability() != SystemImmutable && corona()) { QAction *lockDesktopAction = corona()->action("lock widgets"); //keep a pointer so nobody notices it moved to corona if (lockDesktopAction) { d->actions()->addAction("lock widgets", lockDesktopAction); } } if (d->type != PanelContainment && d->type != CustomPanelContainment) { if (corona()) { //FIXME this is just here because of the darn keyboard shortcut :/ act = corona()->action("manage activities"); if (act) { d->actions()->addAction("manage activities", act); } //a stupid hack to make this one's keyboard shortcut work act = corona()->action("configure shortcuts"); if (act) { d->actions()->addAction("configure shortcuts", act); } } if (d->type == DesktopContainment) { addToolBoxAction(action("add widgets")); //TODO: do we need some way to allow this be overridden? // it's always available because shells rely on this // to offer their own custom configuration as well QAction *configureContainment = action("configure"); if (configureContainment) { addToolBoxAction(configureContainment); } } } } // helper function for sorting the list of applets bool appletConfigLessThan(const KConfigGroup &c1, const KConfigGroup &c2) { QPointF p1 = c1.readEntry("geometry", QRectF()).topLeft(); QPointF p2 = c2.readEntry("geometry", QRectF()).topLeft(); if (!qFuzzyCompare(p1.x(), p2.x())) { if (QApplication::layoutDirection() == Qt::RightToLeft) { return p1.x() > p2.x(); } return p1.x() < p2.x(); } return qFuzzyCompare(p1.y(), p2.y()) || p1.y() < p2.y(); } void Containment::restore(KConfigGroup &group) { /* #ifndef NDEBUG kDebug() << "!!!!!!!!!!!!initConstraints" << group.name() << d->type; kDebug() << " location:" << group.readEntry("location", (int)d->location); kDebug() << " geom:" << group.readEntry("geometry", geometry()); kDebug() << " formfactor:" << group.readEntry("formfactor", (int)d->formFactor); kDebug() << " screen:" << group.readEntry("screen", d->screen); #endif */ if (!isContainment()) { Applet::restore(group); return; } QRectF geo = group.readEntry("geometry", geometry()); //override max/min //this ensures panels are set to their saved size even when they have max & min set to prevent //resizing if (geo.size() != geo.size().boundedTo(maximumSize())) { setMaximumSize(maximumSize().expandedTo(geo.size())); } if (geo.size() != geo.size().expandedTo(minimumSize())) { setMinimumSize(minimumSize().boundedTo(geo.size())); } resize(geo.size()); //are we an offscreen containment? if (containmentType() != PanelContainment && containmentType() != CustomPanelContainment && geo.right() < 0) { corona()->addOffscreenWidget(this); } setLocation((Plasma::Location)group.readEntry("location", (int)d->location)); setFormFactor((Plasma::FormFactor)group.readEntry("formfactor", (int)d->formFactor)); //kDebug() << "setScreen from restore"; d->lastScreen = group.readEntry("lastScreen", d->lastScreen); d->lastDesktop = group.readEntry("lastDesktop", d->lastDesktop); d->setScreen(group.readEntry("screen", d->screen), group.readEntry("desktop", d->desktop), false); d->activityId = group.readEntry("activityId", QString()); flushPendingConstraintsEvents(); restoreContents(group); setImmutability((ImmutabilityType)group.readEntry("immutability", (int)Mutable)); setWallpaper(group.readEntry("wallpaperplugin", ContainmentPrivate::defaultWallpaper), group.readEntry("wallpaperpluginmode", ContainmentPrivate::defaultWallpaperMode)); if (d->toolBox) { d->toolBox.data()->restore(group); } KConfigGroup cfg; if (containmentType() == PanelContainment || containmentType() == CustomPanelContainment) { //don't let global desktop actions conflict with panels //this also prevents panels from sharing config with each other //but the panels aren't configurable anyways, and I doubt that'll change. d->containmentActionsSource = ContainmentPrivate::Local; cfg = KConfigGroup(&group, "ActionPlugins"); } else { const QString source = group.readEntry("ActionPluginsSource", QString()); if (source == "Global") { cfg = KConfigGroup(corona()->config(), "ActionPlugins"); d->containmentActionsSource = ContainmentPrivate::Global; } else if (source == "Activity") { cfg = KConfigGroup(corona()->config(), "Activities"); cfg = KConfigGroup(&cfg, d->activityId); cfg = KConfigGroup(&cfg, "ActionPlugins"); d->containmentActionsSource = ContainmentPrivate::Activity; } else if (source == "Local") { cfg = group; d->containmentActionsSource = ContainmentPrivate::Local; } else { //default to global //but, if there is no global config, try copying it from local. cfg = KConfigGroup(corona()->config(), "ActionPlugins"); if (!cfg.exists()) { cfg = KConfigGroup(&group, "ActionPlugins"); } d->containmentActionsSource = ContainmentPrivate::Global; group.writeEntry("ActionPluginsSource", "Global"); } } //kDebug() << cfg.keyList(); if (cfg.exists()) { foreach (const QString &key, cfg.keyList()) { //kDebug() << "loading" << key; setContainmentActions(key, cfg.readEntry(key, QString())); } } else { //shell defaults ContainmentActionsPluginsConfig conf = corona()->containmentActionsDefaults(d->type); //steal the data directly, for efficiency QHash defaults = conf.d->plugins; for (QHash::const_iterator it = defaults.constBegin(), end = defaults.constEnd(); it != end; ++it) { setContainmentActions(it.key(), it.value()); } } /* #ifndef NDEBUG kDebug() << "Containment" << id() << #endif "screen" << screen() << "geometry is" << geometry() << "wallpaper" << ((d->wallpaper) ? d->wallpaper->pluginName() : QString()) << "wallpaper mode" << wallpaperMode() << "config entries" << group.entryMap(); */ } void Containment::save(KConfigGroup &g) const { if (Applet::d->transient) { return; } KConfigGroup group = g; if (!group.isValid()) { group = config(); } // locking is saved in Applet::save Applet::save(group); if (!isContainment()) { return; } group.writeEntry("screen", d->screen); group.writeEntry("lastScreen", d->lastScreen); group.writeEntry("desktop", d->desktop); group.writeEntry("lastDesktop", d->lastDesktop); group.writeEntry("formfactor", (int)d->formFactor); group.writeEntry("location", (int)d->location); group.writeEntry("activityId", d->activityId); if (d->toolBox) { d->toolBox.data()->save(group); } if (d->wallpaper) { group.writeEntry("wallpaperplugin", d->wallpaper->pluginName()); group.writeEntry("wallpaperpluginmode", d->wallpaper->renderingMode().name()); if (d->wallpaper->isInitialized()) { KConfigGroup wallpaperConfig(&group, "Wallpaper"); wallpaperConfig = KConfigGroup(&wallpaperConfig, d->wallpaper->pluginName()); d->wallpaper->save(wallpaperConfig); } } saveContents(group); } void Containment::saveContents(KConfigGroup &group) const { KConfigGroup applets(&group, "Applets"); foreach (const Applet *applet, d->applets) { KConfigGroup appletConfig(&applets, QString::number(applet->id())); applet->save(appletConfig); } } void Containment::restoreContents(KConfigGroup &group) { KConfigGroup applets(&group, "Applets"); // Sort the applet configs in order of geometry to ensure that applets // are added from left to right or top to bottom for a panel containment QList appletConfigs; foreach (const QString &appletGroup, applets.groupList()) { //kDebug() << "reading from applet group" << appletGroup; KConfigGroup appletConfig(&applets, appletGroup); appletConfigs.append(appletConfig); } qStableSort(appletConfigs.begin(), appletConfigs.end(), appletConfigLessThan); QMutableListIterator it(appletConfigs); while (it.hasNext()) { KConfigGroup &appletConfig = it.next(); int appId = appletConfig.name().toUInt(); QString plugin = appletConfig.readEntry("plugin", QString()); if (plugin.isEmpty()) { continue; } d->addApplet(plugin, QVariantList(), appletConfig.readEntry("geometry", QRectF()), appId, true); } } Containment::Type Containment::containmentType() const { return d->type; } void Containment::setContainmentType(Containment::Type type) { if (d->type == type) { return; } delete d->toolBox.data(); d->type = type; d->checkContainmentFurniture(); } Corona *Containment::corona() const { return qobject_cast(scene()); } void Containment::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { event->ignore(); if (d->wallpaper && d->wallpaper->isInitialized()) { QGraphicsItem *item = scene()->itemAt(event->scenePos()); if (item == this) { d->wallpaper->mouseMoveEvent(event); } } if (!event->isAccepted()) { event->accept(); Applet::mouseMoveEvent(event); } } void Containment::mousePressEvent(QGraphicsSceneMouseEvent *event) { //close a toolbox if exists, to emulate qmenu behavior if (d->toolBox) { d->toolBox.data()->setShowing(false); } event->ignore(); if (d->appletAt(event->scenePos())) { return; //no unexpected click-throughs } if (d->wallpaper && d->wallpaper->isInitialized() && !event->isAccepted()) { d->wallpaper->mousePressEvent(event); } if (event->isAccepted()) { setFocus(Qt::MouseFocusReason); } else if (event->button() == Qt::RightButton && event->modifiers() == Qt::NoModifier) { // we'll catch this in the context menu even Applet::mousePressEvent(event); } else { QString trigger = ContainmentActions::eventToString(event); if (d->prepareContainmentActions(trigger, event->screenPos())) { d->actionPlugins()->value(trigger)->contextEvent(event); } if (!event->isAccepted()) { Applet::mousePressEvent(event); } } } void Containment::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { event->ignore(); if (d->appletAt(event->scenePos())) { return; //no unexpected click-throughs } QString trigger = ContainmentActions::eventToString(event); if (d->wallpaper && d->wallpaper->isInitialized()) { d->wallpaper->mouseReleaseEvent(event); } if (!event->isAccepted() && isContainment()) { if (d->prepareContainmentActions(trigger, event->screenPos())) { d->actionPlugins()->value(trigger)->contextEvent(event); } event->accept(); Applet::mouseReleaseEvent(event); } } void Containment::showDropZone(const QPoint pos) { Q_UNUSED(pos) //Base implementation does nothing, don't put code here } void Containment::showContextMenu(const QPointF &containmentPos, const QPoint &screenPos) { //kDebug() << containmentPos << screenPos; QGraphicsSceneContextMenuEvent gvevent; gvevent.setScreenPos(screenPos); gvevent.setScenePos(mapToScene(containmentPos)); gvevent.setPos(containmentPos); gvevent.setReason(QGraphicsSceneContextMenuEvent::Mouse); gvevent.setWidget(view()); contextMenuEvent(&gvevent); } void Containment::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { if (!isContainment() || !KAuthorized::authorizeKAction("plasma/containment_context_menu")) { Applet::contextMenuEvent(event); return; } KMenu desktopMenu; Applet *applet = d->appletAt(event->scenePos()); //kDebug() << "context menu event " << (QObject*)applet; if (applet) { d->addAppletActions(desktopMenu, applet, event); } else { d->addContainmentActions(desktopMenu, event); } //kDebug() << "executing at" << screenPos; QMenu *menu = &desktopMenu; //kDebug() << "showing menu, actions" << desktopMenu.actions().size() << desktopMenu.actions().first()->menu(); if (desktopMenu.actions().size() == 1 && desktopMenu.actions().first()->menu()) { // we have a menu with a single top level menu; just show that top level menu instad. menu = desktopMenu.actions().first()->menu(); } if (!menu->isEmpty()) { QPoint pos = event->screenPos(); if (applet && d->isPanelContainment()) { menu->adjustSize(); pos = applet->popupPosition(menu->size()); if (event->reason() == QGraphicsSceneContextMenuEvent::Mouse) { // if the menu pops up way away from the mouse press, then move it // to the mouse press if (d->formFactor == Vertical) { if (pos.y() + menu->height() < event->screenPos().y()) { pos.setY(event->screenPos().y()); } } else if (d->formFactor == Horizontal) { if (pos.x() + menu->width() < event->screenPos().x()) { pos.setX(event->screenPos().x()); } } } } menu->exec(pos); event->accept(); } else { Applet::contextMenuEvent(event); } } void Containment::setFormFactor(FormFactor formFactor) { if (d->formFactor == formFactor) { return; } //kDebug() << "switching FF to " << formFactor; d->formFactor = formFactor; if (isContainment() && (d->type == PanelContainment || d->type == CustomPanelContainment)) { // we are a panel and we have chaged our orientation d->positionPanel(true); } if (d->toolBox) { d->toolBox.data()->reposition(); } updateConstraints(Plasma::FormFactorConstraint); KConfigGroup c = config(); c.writeEntry("formfactor", (int)formFactor); emit configNeedsSaving(); } void Containment::setLocation(Location location) { if (d->location == location) { return; } bool emitGeomChange = false; if ((location == TopEdge || location == BottomEdge) && (d->location == TopEdge || d->location == BottomEdge)) { emitGeomChange = true; } if ((location == RightEdge || location == LeftEdge) && (d->location == RightEdge || d->location == LeftEdge)) { emitGeomChange = true; } d->location = location; foreach (Applet *applet, d->applets) { applet->updateConstraints(Plasma::LocationConstraint); } if (emitGeomChange) { // our geometry on the scene will not actually change, // but for the purposes of views it has emit geometryChanged(); } updateConstraints(Plasma::LocationConstraint); KConfigGroup c = config(); c.writeEntry("location", (int)location); emit configNeedsSaving(); } void Containment::addSiblingContainment() { emit addSiblingContainment(this); } void Containment::clearApplets() { foreach (Applet *applet, d->applets) { applet->d->cleanUpAndDelete(); } d->applets.clear(); } Applet *Containment::addApplet(const QString &name, const QVariantList &args, const QRectF &appletGeometry) { return d->addApplet(name, args, appletGeometry); } void Containment::addApplet(Applet *applet, const QPointF &pos, bool delayInit) { if (!isContainment() || (!delayInit && immutability() != Mutable)) { return; } if (!applet) { #ifndef NDEBUG kDebug() << "adding null applet!?!"; #endif return; } if (d->applets.contains(applet)) { #ifndef NDEBUG kDebug() << "already have this applet!"; #endif } Containment *currentContainment = applet->containment(); if (d->type == PanelContainment) { //panels don't want backgrounds, which is important when setting geometry setBackgroundHints(NoBackground); } if (currentContainment && currentContainment != this) { emit currentContainment->appletRemoved(applet); if (currentContainment->d->focusedApplet == applet) { currentContainment->d->focusedApplet = 0; } disconnect(applet, 0, currentContainment, 0); KConfigGroup oldConfig = applet->config(); currentContainment->d->applets.removeAll(applet); applet->setParentItem(this); applet->setParent(this); // now move the old config to the new location //FIXME: this doesn't seem to get the actual main config group containing plugin=, etc KConfigGroup c = config().group("Applets").group(QString::number(applet->id())); oldConfig.reparent(&c); applet->d->resetConfigurationObject(); disconnect(applet, SIGNAL(activate()), currentContainment, SIGNAL(activate())); } else { applet->setParentItem(this); applet->setParent(this); } d->applets << applet; connect(applet, SIGNAL(configNeedsSaving()), this, SIGNAL(configNeedsSaving())); connect(applet, SIGNAL(releaseVisualFocus()), this, SIGNAL(releaseVisualFocus())); connect(applet, SIGNAL(appletDestroyed(Plasma::Applet*)), this, SLOT(appletDestroyed(Plasma::Applet*))); connect(applet, SIGNAL(newStatus(Plasma::ItemStatus)), this, SLOT(checkStatus(Plasma::ItemStatus))); connect(applet, SIGNAL(activate()), this, SIGNAL(activate())); if (pos != QPointF(-1, -1)) { applet->setPos(pos); } if (!delayInit && !currentContainment) { applet->restore(*applet->d->mainConfigGroup()); applet->init(); //FIXME: an on-appear animation would be nice to have again d->appletAppeared(applet); } applet->setFlag(QGraphicsItem::ItemIsMovable, true); applet->updateConstraints(Plasma::AllConstraints); if (!delayInit) { applet->flushPendingConstraintsEvents(); } emit appletAdded(applet, pos); if (!currentContainment) { applet->updateConstraints(Plasma::StartupCompletedConstraint); if (!delayInit) { applet->flushPendingConstraintsEvents(); } } if (!delayInit) { applet->d->scheduleModificationNotification(); } } Applet::List Containment::applets() const { return d->applets; } void Containment::setScreen(int newScreen, int newDesktop) { d->setScreen(newScreen, newDesktop); } int Containment::screen() const { return d->screen; } int Containment::lastScreen() const { return d->lastScreen; } int Containment::desktop() const { return d->desktop; } int Containment::lastDesktop() const { return d->lastDesktop; } KPluginInfo::List Containment::listContainments(const QString &category, const QString &parentApp) { return listContainmentsOfType(QString(), category, parentApp); } KPluginInfo::List Containment::listContainmentsOfType(const QString &type, const QString &category, const QString &parentApp) { QString constraint; if (parentApp.isEmpty()) { constraint.append("(not exist [X-KDE-ParentApp] or [X-KDE-ParentApp] == '')"); } else { constraint.append("[X-KDE-ParentApp] == '").append(parentApp).append("'"); } if (!type.isEmpty()) { if (!constraint.isEmpty()) { constraint.append(" and "); } constraint.append("'").append(type).append("' ~in [X-Plasma-ContainmentCategories]"); } if (!category.isEmpty()) { if (!constraint.isEmpty()) { constraint.append(" and "); } constraint.append("[X-KDE-PluginInfo-Category] == '").append(category).append("'"); if (category == "Miscellaneous") { constraint.append(" or (not exist [X-KDE-PluginInfo-Category] or [X-KDE-PluginInfo-Category] == '')"); } } KService::List offers = KServiceTypeTrader::self()->query("Plasma/Containment", constraint); //kDebug() << "constraint was" << constraint << "which got us" << offers.count() << "matches"; return KPluginInfo::fromServices(offers); } KPluginInfo::List Containment::listContainmentsForMimeType(const QString &mimeType) { const QString constraint = QString("'%1' in [X-Plasma-DropMimeTypes]").arg(mimeType); //kDebug() << mimeType << constraint; const KService::List offers = KServiceTypeTrader::self()->query("Plasma/Containment", constraint); return KPluginInfo::fromServices(offers); } QStringList Containment::listContainmentTypes() { KPluginInfo::List containmentInfos = listContainments(); QSet types; foreach (const KPluginInfo &containmentInfo, containmentInfos) { QStringList theseTypes = containmentInfo.service()->property("X-Plasma-ContainmentCategories").toStringList(); foreach (const QString &type, theseTypes) { types.insert(type); } } return types.toList(); } void Containment::dragEnterEvent(QGraphicsSceneDragDropEvent *event) { //kDebug() << immutability() << Mutable << (immutability() == Mutable); event->setAccepted(immutability() == Mutable && (event->mimeData()->hasFormat(static_cast(scene())->appletMimeType()) || event->mimeData()->hasUrls())); if (!event->isAccepted()) { // check to see if we have an applet that accepts the format. QStringList formats = event->mimeData()->formats(); foreach (const QString &format, formats) { KPluginInfo::List appletList = Applet::listAppletInfoForMimeType(format); if (!appletList.isEmpty()) { event->setAccepted(true); break; } } if (!event->isAccepted()) { foreach (const QString &format, formats) { KPluginInfo::List wallpaperList = Wallpaper::listWallpaperInfoForMimetype(format); if (!wallpaperList.isEmpty()) { event->setAccepted(true); break; } } } } if (event->isAccepted()) { if (d->dropZoneStarted) { showDropZone(event->pos().toPoint()); } else { if (!d->showDropZoneDelayTimer) { d->showDropZoneDelayTimer = new QTimer(this); d->showDropZoneDelayTimer->setInterval(300); d->showDropZoneDelayTimer->setSingleShot(true); connect(d->showDropZoneDelayTimer, SIGNAL(timeout()), this, SLOT(showDropZoneDelayed())); } d->dropPoints.insert(0, event->pos()); d->showDropZoneDelayTimer->start(); } } } void Containment::dragLeaveEvent(QGraphicsSceneDragDropEvent *event) { //kDebug() << event->pos() << size().height() << size().width(); if (d->showDropZoneDelayTimer) { d->showDropZoneDelayTimer->stop(); } if (event->pos().y() < 1 || event->pos().y() > size().height() || event->pos().x() < 1 || event->pos().x() > size().width()) { showDropZone(QPoint()); d->dropZoneStarted = false; } } void Containment::dragMoveEvent(QGraphicsSceneDragDropEvent *event) { QGraphicsItem *item = scene()->itemAt(event->scenePos()); event->setAccepted(item == this || item == d->toolBox.data() || !item); //kDebug() << event->isAccepted() << d->showDropZoneDelayTimer->isActive(); if (!event->isAccepted()) { if (d->showDropZoneDelayTimer) { d->showDropZoneDelayTimer->stop(); } } else if (!d->showDropZoneDelayTimer->isActive() && immutability() == Plasma::Mutable) { showDropZone(event->pos().toPoint()); } } void Containment::dropEvent(QGraphicsSceneDragDropEvent *event) { if (isContainment()) { d->dropData(event->scenePos(), event->screenPos(), event); } else { Applet::dropEvent(event); } } void Containment::setToolBox(AbstractToolBox *toolBox) { if (d->toolBox.data()) { d->toolBox.data()->deleteLater(); } d->toolBox = toolBox; } AbstractToolBox *Containment::toolBox() const { return d->toolBox.data(); } void Containment::resizeEvent(QGraphicsSceneResizeEvent *event) { Applet::resizeEvent(event); if (isContainment()) { if (d->isPanelContainment()) { d->positionPanel(); } else if (corona()) { corona()->layoutContainments(); } if (d->wallpaper) { d->wallpaper->setBoundingRect(QRectF(QPointF(0, 0), size())); } } } void Containment::keyPressEvent(QKeyEvent *event) { //kDebug() << "keyPressEvent with" << event->key() // << "and hoping and wishing for a" << Qt::Key_Tab; if (event->key() == Qt::Key_Tab) { // && event->modifiers() == 0) { if (!d->applets.isEmpty()) { #ifndef NDEBUG kDebug() << "let's give focus to...." << (QObject*)d->applets.first(); #endif d->applets.first()->setFocus(Qt::TabFocusReason); } } } void Containment::wheelEvent(QGraphicsSceneWheelEvent *event) { event->ignore(); if (d->appletAt(event->scenePos())) { return; //no unexpected click-throughs } if (d->wallpaper && d->wallpaper->isInitialized()) { QGraphicsItem *item = scene()->itemAt(event->scenePos()); if (item == this) { event->ignore(); d->wallpaper->wheelEvent(event); if (event->isAccepted()) { return; } } } QString trigger = ContainmentActions::eventToString(event); if (d->prepareContainmentActions(trigger, event->screenPos())) { d->actionPlugins()->value(trigger)->contextEvent(event); event->accept(); } else { event->ignore(); Applet::wheelEvent(event); } } QVariant Containment::itemChange(GraphicsItemChange change, const QVariant &value) { //FIXME if the applet is moved to another containment we need to unfocus it if (isContainment() && (change == QGraphicsItem::ItemSceneHasChanged || change == QGraphicsItem::ItemPositionHasChanged)) { switch (d->type) { case PanelContainment: case CustomPanelContainment: d->positionPanel(); break; default: if (corona()) { corona()->layoutContainments(); } break; } } return Applet::itemChange(change, value); } void Containment::enableAction(const QString &name, bool enable) { QAction *action = this->action(name); if (action) { action->setEnabled(enable); action->setVisible(enable); } } void Containment::addToolBoxAction(QAction *action) { d->createToolBox(); if (d->toolBox) { d->toolBox.data()->addTool(action); } } void Containment::removeToolBoxAction(QAction *action) { if (d->toolBox) { d->toolBox.data()->removeTool(action); } } void Containment::setToolBoxOpen(bool open) { if (open) { openToolBox(); } else { closeToolBox(); } } bool Containment::isToolBoxOpen() const { return (d->toolBox && d->toolBox.data()->isShowing()); } void Containment::openToolBox() { if (d->toolBox && !d->toolBox.data()->isShowing()) { d->toolBox.data()->setShowing(true); emit toolBoxVisibilityChanged(true); } } void Containment::closeToolBox() { if (d->toolBox && d->toolBox.data()->isShowing()) { d->toolBox.data()->setShowing(false); emit toolBoxVisibilityChanged(false); } } void Containment::addAssociatedWidget(QWidget *widget) { Applet::addAssociatedWidget(widget); if (d->focusedApplet) { d->focusedApplet->addAssociatedWidget(widget); } foreach (const Applet *applet, d->applets) { if (applet->d->activationAction) { widget->addAction(applet->d->activationAction); } } } void Containment::removeAssociatedWidget(QWidget *widget) { Applet::removeAssociatedWidget(widget); if (d->focusedApplet) { d->focusedApplet->removeAssociatedWidget(widget); } foreach (const Applet *applet, d->applets) { if (applet->d->activationAction) { widget->removeAction(applet->d->activationAction); } } } void Containment::setDrawWallpaper(bool drawWallpaper) { d->drawWallpaper = drawWallpaper; if (drawWallpaper) { KConfigGroup cfg = config(); const QString wallpaper = cfg.readEntry("wallpaperplugin", ContainmentPrivate::defaultWallpaper); const QString mode = cfg.readEntry("wallpaperpluginmode", ContainmentPrivate::defaultWallpaperMode); setWallpaper(wallpaper, mode); } else { delete d->wallpaper; d->wallpaper = 0; } } bool Containment::drawWallpaper() { return d->drawWallpaper; } void Containment::setWallpaper(const QString &pluginName, const QString &mode) { KConfigGroup cfg = config(); bool newPlugin = true; bool newMode = true; if (d->drawWallpaper) { if (d->wallpaper) { // we have a wallpaper, so let's decide whether we need to swap it out if (d->wallpaper->pluginName() != pluginName) { delete d->wallpaper; d->wallpaper = 0; } else { // it's the same plugin, so let's save its state now so when // we call restore later on we're safe newMode = d->wallpaper->renderingMode().name() != mode; newPlugin = false; } } if (!pluginName.isEmpty() && !d->wallpaper) { d->wallpaper = Plasma::Wallpaper::load(pluginName); } if (d->wallpaper) { d->wallpaper->setParent(this); d->wallpaper->setBoundingRect(QRectF(QPointF(0, 0), size())); d->wallpaper->setRenderingMode(mode); if (newPlugin) { cfg.writeEntry("wallpaperplugin", pluginName); } if (d->wallpaper->isInitialized()) { KConfigGroup wallpaperConfig = KConfigGroup(&cfg, "Wallpaper"); wallpaperConfig = KConfigGroup(&wallpaperConfig, pluginName); d->wallpaper->restore(wallpaperConfig); } if (newMode) { cfg.writeEntry("wallpaperpluginmode", mode); } } update(); } if (!d->wallpaper) { cfg.deleteEntry("wallpaperplugin"); cfg.deleteEntry("wallpaperpluginmode"); } if (newPlugin || newMode) { if (newPlugin && d->wallpaper) { connect(d->wallpaper, SIGNAL(configureRequested()), this, SLOT(requestConfiguration())); connect(d->wallpaper, SIGNAL(configNeedsSaving()), this, SIGNAL(configNeedsSaving())); } emit configNeedsSaving(); } } Plasma::Wallpaper *Containment::wallpaper() const { return d->wallpaper; } void Containment::setContainmentActions(const QString &trigger, const QString &pluginName) { KConfigGroup cfg = containmentActionsConfig(); ContainmentActions *plugin = 0; if (d->actionPlugins()->contains(trigger)) { plugin = d->actionPlugins()->value(trigger); if (plugin->pluginName() != pluginName) { d->actionPlugins()->remove(trigger); delete plugin; plugin=0; } } if (pluginName.isEmpty()) { cfg.deleteEntry(trigger); } else if (plugin) { //it already existed, just reload config if (plugin->isInitialized()) { plugin->setContainment(this); //to be safe //FIXME make a truly unique config group KConfigGroup pluginConfig = KConfigGroup(&cfg, trigger); plugin->restore(pluginConfig); } } else { switch (d->containmentActionsSource) { case ContainmentPrivate::Activity: //FIXME case ContainmentPrivate::Local: plugin = PluginLoader::self()->loadContainmentActions(this, pluginName); break; default: plugin = PluginLoader::self()->loadContainmentActions(0, pluginName); } if (plugin) { cfg.writeEntry(trigger, pluginName); d->actionPlugins()->insert(trigger, plugin); } else { //bad plugin... gets removed. is this a feature or a bug? cfg.deleteEntry(trigger); } } emit configNeedsSaving(); } QStringList Containment::containmentActionsTriggers() { return d->actionPlugins()->keys(); } QString Containment::containmentActions(const QString &trigger) { ContainmentActions *c = d->actionPlugins()->value(trigger); return c ? c->pluginName() : QString(); } void Containment::setActivity(const QString &activityId) { if (activityId.isEmpty()) { return; } d->activityId = activityId; KConfigGroup c = config(); c.writeEntry("activityId", activityId); if (d->toolBox) { d->toolBox.data()->update(); } emit configNeedsSaving(); } QString Containment::activity() const { return d->activityId; } void Containment::focusNextApplet() { if (d->applets.isEmpty()) { return; } int index = d->focusedApplet ? d->applets.indexOf(d->focusedApplet) + 1 : 0; if (index >= d->applets.size()) { index = 0; } #ifndef NDEBUG kDebug() << "index" << index; #endif d->focusApplet(d->applets.at(index)); } void Containment::focusPreviousApplet() { if (d->applets.isEmpty()) { return; } int index = d->focusedApplet ? d->applets.indexOf(d->focusedApplet) - 1 : -1; if (index < 0) { index = d->applets.size() - 1; } #ifndef NDEBUG kDebug() << "index" << index; #endif d->focusApplet(d->applets.at(index)); } void Containment::destroy() { destroy(true); } void Containment::showConfigurationInterface() { Applet::showConfigurationInterface(); } void Containment::destroy(bool confirm) { if (immutability() != Mutable || Applet::d->transient) { return; } if (isContainment() && confirm) { //FIXME: should not be blocking const QString title = i18nc("@title:window %1 is the name of the containment", "Remove %1", name()); KGuiItem remove = KStandardGuiItem::remove(); remove.setText(title); if (KMessageBox::warningContinueCancel(view(), i18nc("%1 is the name of the containment", "Do you really want to remove this %1?", name()), title, remove) != KMessageBox::Continue) { return; } } Applet::destroy(); } KConfigGroup Containment::containmentActionsConfig() { KConfigGroup cfg; switch (d->containmentActionsSource) { case ContainmentPrivate::Local: cfg = config(); cfg = KConfigGroup(&cfg, "ActionPlugins"); break; case ContainmentPrivate::Activity: cfg = KConfigGroup(corona()->config(), "Activities"); cfg = KConfigGroup(&cfg, d->activityId); cfg = KConfigGroup(&cfg, "ActionPlugins"); break; default: cfg = KConfigGroup(corona()->config(), "ActionPlugins"); } return cfg; } } // Plasma namespace #include "moc_containment.cpp"