diff --git a/applet.cpp b/applet.cpp index bef21e8b7..87366d136 100644 --- a/applet.cpp +++ b/applet.cpp @@ -1016,30 +1016,45 @@ void Applet::flushPendingConstraintsEvents() //FIXME desktop containments can't be removed while in use. //it's kinda silly to have a keyboard shortcut for something that can only be used when the //shortcut isn't active. - KAction *closeApplet = new KAction(this); - closeApplet->setIcon(KIcon("edit-delete")); - closeApplet->setEnabled(unlocked); - closeApplet->setVisible(unlocked); - closeApplet->setText(i18nc("%1 is the name of the applet", "Remove this %1", name())); - if (d->isContainment) { - closeApplet->setShortcut(QKeySequence("alt+d,alt+r")); - } else { - closeApplet->setShortcut(QKeySequence("alt+d,r")); + QAction *closeApplet = d->actions->action("remove"); + if (closeApplet) { + closeApplet->setEnabled(unlocked); + closeApplet->setVisible(unlocked); + connect(closeApplet, SIGNAL(triggered(bool)), this, SLOT(selectItemToDestroy())); + } + + QAction *configAction = d->actions->action("configure"); + if (configAction) { + //XXX assumption: isContainment won't change after this + if (d->isContainment) { + connect(configAction, SIGNAL(triggered()), this, SLOT(requestConfiguration())); + } else { + connect(configAction, SIGNAL(triggered(bool)), this, SLOT(showConfigurationInterface())); + } + bool canConfig = unlocked || KAuthorized::authorize("PlasmaAllowConfigureWhenLocked"); + canConfig = canConfig && (d->hasConfigurationInterface || d->isContainment); + configAction->setVisible(canConfig); + configAction->setEnabled(canConfig); + } + + d->updateShortcuts(); + Corona * corona = qobject_cast(scene()); + if (corona) { + connect(corona, SIGNAL(shortcutsChanged()), this, SLOT(updateShortcuts())); } - connect(closeApplet, SIGNAL(triggered(bool)), this, SLOT(selectItemToDestroy())); - d->actions.addAction("remove", closeApplet); } if (c & Plasma::ImmutableConstraint) { bool unlocked = immutability() == Mutable; - QAction *action = d->actions.action("remove"); + QAction *action = d->actions->action("remove"); if (action) { action->setVisible(unlocked); action->setEnabled(unlocked); } bool canConfig = unlocked || KAuthorized::authorize("PlasmaAllowConfigureWhenLocked"); - action = d->actions.action("configure"); + canConfig = canConfig && (d->hasConfigurationInterface || d->isContainment); + action = d->actions->action("configure"); if (action) { action->setVisible(canConfig); action->setEnabled(canConfig); @@ -1143,12 +1158,12 @@ QList Applet::contextualActions() QAction *Applet::action(QString name) const { - return d->actions.action(name); + return d->actions->action(name); } void Applet::addAction(QString name, QAction *action) { - d->actions.addAction(name, action); + d->actions->addAction(name, action); } void Applet::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) @@ -1273,7 +1288,7 @@ void Applet::setGlobalShortcut(const KShortcut &shortcut) d->activationAction->setObjectName(QString("activate widget %1").arg(id())); // NO I18N connect(d->activationAction, SIGNAL(triggered()), this, SIGNAL(activate())); - QList widgets = d->actions.associatedWidgets(); + QList widgets = d->actions->associatedWidgets(); foreach (QWidget *w, widgets) { w->addAction(d->activationAction); } @@ -1307,12 +1322,12 @@ bool Applet::isPopupShowing() const void Applet::addAssociatedWidget(QWidget *widget) { - d->actions.addAssociatedWidget(widget); + d->actions->addAssociatedWidget(widget); } void Applet::removeAssociatedWidget(QWidget *widget) { - d->actions.removeAssociatedWidget(widget); + d->actions->removeAssociatedWidget(widget); } Location Applet::location() const @@ -1369,6 +1384,8 @@ bool Applet::hasConfigurationInterface() const return d->hasConfigurationInterface; } +//it bugs me that this can get turned on and off at will. I don't see it being useful and it just +//makes more work for me and more code duplication. void Applet::setHasConfigurationInterface(bool hasInterface) { if (d->hasConfigurationInterface == hasInterface) { @@ -1376,31 +1393,41 @@ void Applet::setHasConfigurationInterface(bool hasInterface) } d->hasConfigurationInterface = hasInterface; - //config action - //TODO respect security when it's implemented (4.2) - KAction *configAction = qobject_cast(d->actions.action("configure")); - if (hasInterface) { - if (!configAction) { //should be always true - configAction = new KAction(i18n("%1 Settings", name()), this); - configAction->setIcon(KIcon("configure")); - //configAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); //don't clash with other views - bool unlocked = immutability() == Mutable; - bool canConfig = unlocked || KAuthorized::authorize("PlasmaAllowConfigureWhenLocked"); - configAction->setVisible(canConfig); - configAction->setEnabled(canConfig); - //XXX these shortcuts are also in setIsContainment. keep them in sync. - if (d->isContainment) { - configAction->setShortcut(QKeySequence("alt+d,alt+s")); - connect(configAction, SIGNAL(triggered()), this, SLOT(requestConfiguration())); - } else { - configAction->setShortcut(QKeySequence("alt+d,s")); - connect(configAction, SIGNAL(triggered(bool)), this, SLOT(showConfigurationInterface())); - } - d->actions.addAction("configure", configAction); - } - } else if (!d->isContainment && !qobject_cast(this)) { - d->actions.removeAction(configAction); + + //FIXME I'm pretty sure this line has issues but I'm preserving current behaviour for now + if (!hasInterface && (d->isContainment || qobject_cast(this))) { + return; } + + //config action + KAction *configAction = qobject_cast(d->actions->action("configure")); + if (configAction) { + bool canConfig = false; + if (hasInterface) { + bool unlocked = immutability() == Mutable; + canConfig = unlocked || KAuthorized::authorize("PlasmaAllowConfigureWhenLocked"); + } + configAction->setVisible(canConfig); + configAction->setEnabled(canConfig); + } +} + +KActionCollection* AppletPrivate::defaultActions(QObject *parent) +{ + KActionCollection *actions = new KActionCollection(parent); + actions->setConfigGroup("Shortcuts-Applet"); + + KAction *configAction = new KAction(i18n("Widget Settings"), actions); + configAction->setIcon(KIcon("configure")); + configAction->setShortcut(KShortcut("alt+d, s")); + actions->addAction("configure", configAction); + + KAction *closeApplet = new KAction("Remove this Widget", actions); + closeApplet->setIcon(KIcon("edit-delete")); + closeApplet->setShortcut(KShortcut("alt+d, r")); + actions->addAction("remove", closeApplet); + + return actions; } bool Applet::eventFilter(QObject *o, QEvent *e) @@ -1635,6 +1662,33 @@ void AppletPrivate::configDialogFinished() q->configChanged(); } +void AppletPrivate::updateShortcuts() +{ + if (isContainment) { + //a horrible hack to avoid clobbering corona settings + //we pull them out, then read, then put them back + QList names; + QList qactions; + names << "zoom out" << "add sibling containment" << "configure shortcuts" << "lock widgets"; + foreach (const QString &name, names) { + QAction *a = actions->action(name); + actions->takeAction(a); //FIXME this is stupid, KActionCollection needs a takeAction(QString) method + qactions << a; + } + + actions->readSettings(); + + for (int i=0; iaddAction(names.at(i), a); + } + } + } else { + actions->readSettings(); + } +} + void Applet::configChanged() { if (d->script) { @@ -2019,24 +2073,12 @@ void AppletPrivate::setIsContainment(bool nowIsContainment, bool forceUpdate) } isContainment = nowIsContainment; + //FIXME I do not like this function. + //currently it's only called before ctmt/applet init, with (true,true), and I'm going to assume it stays that way. + //if someone calls it at some other time it'll cause headaches. :P delete mainConfig; mainConfig = 0; - - KAction *configAction = qobject_cast(actions.action("configure")); - if (configAction) { - QObject::disconnect(configAction, SIGNAL(triggered()), q, SLOT(requestConfiguration())); - QObject::disconnect(configAction, SIGNAL(triggered(bool)), q, SLOT(showConfigurationInterface())); - //XXX these shortcuts are also in setHasConfigurationInterface. keep them in sync. - if (nowIsContainment) { - //kDebug() << "I am a containment"; - configAction->setShortcut(QKeySequence("alt+d,alt+s")); - QObject::connect(configAction, SIGNAL(triggered()), q, SLOT(requestConfiguration())); - } else { - configAction->setShortcut(QKeySequence("alt+d,s")); - QObject::connect(configAction, SIGNAL(triggered(bool)), q, SLOT(showConfigurationInterface())); - } - } } bool Applet::isContainment() const @@ -2062,7 +2104,7 @@ AppletPrivate::AppletPrivate(KService::Ptr service, int uniqueID, Applet *applet pendingConstraints(NoConstraint), aspectRatioMode(Plasma::KeepAspectRatio), immutability(Mutable), - actions(applet), + actions(AppletPrivate::defaultActions(applet)), activationAction(0), shortcutEditor(0), messageOverlayProxy(0), @@ -2179,7 +2221,13 @@ void AppletPrivate::init(const QString &packagePath) } q->setBackgroundHints(Applet::DefaultBackground); - q->setHasConfigurationInterface(true); + q->setHasConfigurationInterface(true); //FIXME why not default it to true in the constructor? + + QAction *closeApplet = actions->action("remove"); + closeApplet->setText(i18nc("%1 is the name of the applet", "Remove this %1", q->name())); + QAction *configAction = actions->action("configure"); + configAction->setText(i18nc("%1 is the name of the applet", "%1 Settings", q->name())); + QObject::connect(Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()), q, SLOT(themeChanged())); QObject::connect(q, SIGNAL(activate()), q, SLOT(setFocus())); } diff --git a/applet.h b/applet.h index 6ea1bcfbc..bb209ad0a 100644 --- a/applet.h +++ b/applet.h @@ -943,6 +943,7 @@ class PLASMA_EXPORT Applet : public QGraphicsWidget Q_PRIVATE_SLOT(d, void destroyMessageOverlay()) Q_PRIVATE_SLOT(d, void clearShortcutEditorPtr()) Q_PRIVATE_SLOT(d, void configDialogFinished()) + Q_PRIVATE_SLOT(d, void updateShortcuts()) /** * Reimplemented from QGraphicsItem diff --git a/containment.cpp b/containment.cpp index 3e34f261f..c26c8441f 100644 --- a/containment.cpp +++ b/containment.cpp @@ -137,57 +137,55 @@ void Containment::init() setContainmentType(DesktopContainment); } - //common actions + //connect actions + ContainmentPrivate::addDefaultActions(d->actions()); bool unlocked = immutability() == Mutable; - KAction *appletBrowserAction = new KAction(i18n("Add Widgets..."), this); - appletBrowserAction->setIcon(KIcon("list-add")); + //fix the text of the actions that need name() + //btw, do we really want to use name() when it's a desktopcontainment? + QAction *closeApplet = d->actions()->action("remove"); + closeApplet->setText(i18nc("%1 is the name of the applet", "Remove this %1", name())); + QAction *configAction = d->actions()->action("configure"); + configAction->setText(i18nc("%1 is the name of the applet", "%1 Settings", name())); + + QAction *appletBrowserAction = action("add widgets"); appletBrowserAction->setVisible(unlocked); appletBrowserAction->setEnabled(unlocked); connect(appletBrowserAction, SIGNAL(triggered()), this, SLOT(triggerShowAddWidgets())); - appletBrowserAction->setShortcut(QKeySequence("alt+d,a")); - d->actions().addAction("add widgets", appletBrowserAction); - KAction *action = new KAction(i18n("Next Widget"), this); - //no icon - connect(action, SIGNAL(triggered()), this, SLOT(focusNextApplet())); - action->setShortcut(QKeySequence("alt+d,n")); - d->actions().addAction("next applet", action); - - action = new KAction(i18n("Previous Widget"), this); - //no icon - connect(action, SIGNAL(triggered()), this, SLOT(focusPreviousApplet())); - action->setShortcut(QKeySequence("alt+d,p")); - d->actions().addAction("previous applet", action); + QAction *act = action("next applet"); + connect(act, SIGNAL(triggered()), this, SLOT(focusNextApplet())); + act = action("previous applet"); + 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); + d->actions()->addAction("lock widgets", lockDesktopAction); } } - if (d->type != PanelContainment && - d->type != CustomPanelContainment) { - KAction *zoomAction = new KAction(i18n("Zoom In"), this); - zoomAction->setIcon(KIcon("zoom-in")); - connect(zoomAction, SIGNAL(triggered(bool)), this, SLOT(zoomIn())); - //two shortcuts because I hate ctrl-+ but others expect it - QList keys; - keys << QKeySequence("alt+d,+"); - keys << QKeySequence("alt+d,="); - zoomAction->setShortcuts(keys); - d->actions().addAction("zoom in", zoomAction); + if (d->type == PanelContainment || + d->type == CustomPanelContainment) { + d->actions()->removeAction(d->actions()->action("zoom in")); + } else { + QAction *zoomAction = action("zoom in"); + connect(zoomAction, SIGNAL(triggered()), this, SLOT(zoomIn())); if (corona()) { QAction *action = corona()->action("zoom out"); if (action) { - d->actions().addAction("zoom out", action); + d->actions()->addAction("zoom out", action); } action = corona()->action("add sibling containment"); if (action) { - d->actions().addAction("add sibling containment", action); + d->actions()->addAction("add sibling containment", action); + } + //a stupid hack to make this one's keyboard shortcut work + action = corona()->action("configure shortcuts"); + if (action) { + d->actions()->addAction("configure shortcuts", action); } } @@ -210,6 +208,45 @@ void Containment::init() setDrawWallpaper(true); } } + +} + +void ContainmentPrivate::addDefaultActions(KActionCollection *actions) +{ + actions->setConfigGroup("Shortcuts-Containment"); + + //adjust applet actions + KAction *appAction = qobject_cast(actions->action("remove")); + appAction->setShortcut(KShortcut("alt+d, alt+r")); + appAction->setText(i18n("Remove this Activity")); + appAction = qobject_cast(actions->action("configure")); + if (appAction) { + appAction->setShortcut(KShortcut("alt+d, alt+s")); + appAction->setText(i18n("Activity Settings")); + } + + //add our own actions + KAction *appletBrowserAction = new KAction(i18n("Add Widgets..."), actions); + appletBrowserAction->setIcon(KIcon("list-add")); + appletBrowserAction->setShortcut(KShortcut("alt+d, a")); + actions->addAction("add widgets", appletBrowserAction); + + KAction *action = new KAction(i18n("Next Widget"), actions); + //no icon + action->setShortcut(KShortcut("alt+d, n")); + actions->addAction("next applet", action); + + action = new KAction(i18n("Previous Widget"), actions); + //no icon + action->setShortcut(KShortcut("alt+d, p")); + actions->addAction("previous applet", action); + + KAction *zoomAction = new KAction(i18n("Zoom In"), actions); + zoomAction->setIcon(KIcon("zoom-in")); + //two shortcuts because I hate ctrl-+ but others expect it + zoomAction->setShortcuts(KShortcut("alt+d, +; alt+d, =")); + actions->addAction("zoom in", zoomAction); + } // helper function for sorting the list of applets @@ -507,7 +544,7 @@ void ContainmentPrivate::appletActions(KMenu &desktopMenu, Applet *applet, bool } if (applet->hasConfigurationInterface()) { - QAction *configureApplet = applet->d->actions.action("configure"); + QAction *configureApplet = applet->d->actions->action("configure"); if (configureApplet) { desktopMenu.addAction(configureApplet); } @@ -519,7 +556,7 @@ void ContainmentPrivate::appletActions(KMenu &desktopMenu, Applet *applet, bool desktopMenu.addSeparator(); } - QAction *closeApplet = applet->d->actions.action("remove"); + QAction *closeApplet = applet->d->actions->action("remove"); if (!closeApplet) { //unlikely but not impossible closeApplet = new QAction(i18nc("%1 is the name of the applet", "Remove this %1", applet->name()), &desktopMenu); closeApplet->setIcon(KIcon("edit-delete")); @@ -1450,7 +1487,7 @@ Context *ContainmentPrivate::context() return con; } -KActionCollection &ContainmentPrivate::actions() +KActionCollection* ContainmentPrivate::actions() { return static_cast(q)->d->actions; } @@ -1461,7 +1498,7 @@ void ContainmentPrivate::focusApplet(Plasma::Applet *applet) return; } - QList widgets = actions().associatedWidgets(); + QList widgets = actions()->associatedWidgets(); if (focusedApplet) { foreach (QWidget *w, widgets) { focusedApplet->removeAssociatedWidget(w); diff --git a/corona.cpp b/corona.cpp index 9ef8feeff..f35729b72 100644 --- a/corona.cpp +++ b/corona.cpp @@ -37,10 +37,12 @@ #include #include #include +#include #include "containment.h" #include "view.h" #include "private/applet_p.h" +#include "private/containment_p.h" #include "tooltipmanager.h" using namespace Plasma; @@ -80,13 +82,48 @@ public: QObject::connect(&configSyncTimer, SIGNAL(timeout()), q, SLOT(syncConfig())); //some common actions + actions.setConfigGroup("Shortcuts"); + KAction *lockAction = new KAction(i18n("Lock Widgets"), q); lockAction->setIcon(KIcon("object-locked")); QObject::connect(lockAction, SIGNAL(triggered(bool)), q, SLOT(toggleImmutability())); - lockAction->setShortcut(QKeySequence("alt+d,l")); + lockAction->setShortcut(KShortcut("alt+d, l")); lockAction->setShortcutContext(Qt::ApplicationShortcut); actions.addAction("lock widgets", lockAction); + + //FIXME this doesn't really belong here. desktop KCM maybe? + //but should the shortcuts be per-app or really-global? + //I don't know how to make kactioncollections use plasmarc + KAction *action = new KAction(i18n("Shortcuts..."), q); + action->setIcon(KIcon("configure")); + QObject::connect(action, SIGNAL(triggered()), + q, SLOT(showShortcutConfig())); + //action->setShortcut(KShortcut("ctrl+h")); + action->setShortcutContext(Qt::ApplicationShortcut); + actions.addAction("configure shortcuts", action); + + actions.readSettings(); + + //fake containment/applet actions + KActionCollection *aActions = AppletPrivate::defaultActions(q); + KActionCollection *cActions = AppletPrivate::defaultActions(q); //containment has to start with applet stuff + ContainmentPrivate::addDefaultActions(cActions); //now it's really containment + //grab the current stuff + cActions->readSettings(); + aActions->readSettings(); + + shortcutsDlg.setModal(false); + shortcutsDlg.addCollection(aActions); + shortcutsDlg.addCollection(cActions); + + QObject::connect(&shortcutsDlg, SIGNAL(saved()), q, SIGNAL(shortcutsChanged())); + } + + void showShortcutConfig() + { + //show a kshortcutsdialog with the actions + shortcutsDlg.configure(); } void toggleImmutability() @@ -214,6 +251,7 @@ public: QList containments; QHash offscreenWidgets; KActionCollection actions; + KShortcutsDialog shortcutsDlg; }; Corona::Corona(QObject *parent) @@ -661,6 +699,17 @@ void Corona::enableAction(const QString &name, bool enable) } } +void Corona::updateShortcuts() +{ + d->actions.readSettings(); + d->shortcutsDlg.addCollection(&d->actions); +} + +void Corona::addShortcuts(KActionCollection *newShortcuts) +{ + d->shortcutsDlg.addCollection(newShortcuts); +} + } // namespace Plasma #include "corona.moc" diff --git a/corona.h b/corona.h index 8ff7637d5..dfd12f4ba 100644 --- a/corona.h +++ b/corona.h @@ -185,6 +185,22 @@ public: */ void enableAction(const QString &name, bool enable); + /** + * @since 4.3 + * Updates keyboard shortcuts for all the corona's actions. + * If you've added actions to the corona you'll need to + * call this for them to be configurable. + */ + void updateShortcuts(); + + /** + * @since 4.3 + * Adds a set of actions to the shortcut config dialog. + * don't use this on actions in the corona's own actioncollection, + * those are handled automatically. this is for stuff outside of that. + */ + void addShortcuts(KActionCollection *newShortcuts); + public Q_SLOTS: /** * Initializes the layout from a config file. This will first clear any existing @@ -281,6 +297,16 @@ Q_SIGNALS: */ void immutabilityChanged(Plasma::ImmutabilityType immutability); + /** + * @since 4.3 + * emitted when the user changes keyboard shortcut settings + * connect to this if you've put some extra shortcuts in your app + * that are NOT in corona's actioncollection. + * if your code's not in shells/ it probably shouldn't be using this function. + * @see addShortcuts + */ + void shortcutsChanged(); + protected: /** * Loads the default (system wide) layout for this user @@ -316,6 +342,7 @@ private: Q_PRIVATE_SLOT(d, void offscreenWidgetDestroyed(QObject *)) Q_PRIVATE_SLOT(d, void syncConfig()) Q_PRIVATE_SLOT(d, void toggleImmutability()) + Q_PRIVATE_SLOT(d, void showShortcutConfig()) friend class CoronaPrivate; }; diff --git a/private/applet_p.h b/private/applet_p.h index e615d551c..457c10b00 100644 --- a/private/applet_p.h +++ b/private/applet_p.h @@ -95,7 +95,9 @@ public: KConfigDialog *generateGenericConfigDialog(); QString configDialogId() const; QString configWindowTitle() const; + void updateShortcuts(); + static KActionCollection* defaultActions(QObject *parent); static QSet knownCategories(); static uint s_maxAppletId; @@ -123,7 +125,7 @@ public: Plasma::Constraints pendingConstraints; Plasma::AspectRatioMode aspectRatioMode; ImmutabilityType immutability; - KActionCollection actions; + KActionCollection *actions; KAction *activationAction; KKeySequenceWidget *shortcutEditor; //TODO: subclass KConfigDialog and encapsulate this in there QGraphicsProxyWidget *messageOverlayProxy; diff --git a/private/containment_p.h b/private/containment_p.h index caea7b361..de719a33f 100644 --- a/private/containment_p.h +++ b/private/containment_p.h @@ -89,7 +89,12 @@ public: const QRectF &geometry = QRectF(-1, -1, -1, -1), uint id = 0, bool delayedInit = false); - KActionCollection &actions(); + KActionCollection *actions(); + + /** + * add the regular actions & keyboard shortcuts onto Applet's collection + */ + static void addDefaultActions(KActionCollection *actions); /** * give keyboard focus to applet within this containment