/* * Copyright 2009 by Chani Armitage * * 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 "menu.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "kworkspace.h" #include "krunner_interface.h" #include "screensaver_interface.h" #ifdef Q_OS_WIN #define _WIN32_WINNT 0x0500 // require NT 5.0 (win 2k pro) #include #endif // Q_OS_WIN ContextMenu::ContextMenu(QObject *parent, const QVariantList &args) : Plasma::ContainmentActions(parent, args), m_runCommandAction(0), m_lockScreenAction(0), m_logoutAction(0), m_separator1(0), m_separator2(0), m_separator3(0), m_buttons(0) { } ContextMenu::~ContextMenu() { } void ContextMenu::restore(const KConfigGroup &config) { Plasma::Containment *c = containment(); Q_ASSERT(c); m_actions.clear(); m_actionOrder.clear(); QHash actions; QSet disabled; if (c->containmentType() == Plasma::Types::PanelContainment || c->containmentType() == Plasma::Types::CustomPanelContainment) { m_actionOrder << "add widgets" << "_add panel" << "lock widgets" << "_context" << "configure" << "remove"; } else { actions.insert("configure shortcuts", false); m_actionOrder << "_context" << "_run_command" << "add widgets" << "_add panel" << "manage activities" << "remove" << "lock widgets" << "_sep1" <<"_lock_screen" << "_logout" << "_sep2" << "run associated application" << "configure" << "configure shortcuts" << "_sep3" << "_wallpaper"; disabled.insert("configure shortcuts"); } foreach (const QString &name, m_actionOrder) { actions.insert(name, !disabled.contains(name)); } QHashIterator it(actions); while (it.hasNext()) { it.next(); m_actions.insert(it.key(), config.readEntry(it.key(), it.value())); } // everything below should only happen once, so check for it if (!m_runCommandAction) { m_runCommandAction = new QAction(i18nc("plasma_containmentactions_contextmenu", "Run Command..."), this); m_runCommandAction->setIcon(QIcon::fromTheme("system-run")); setGlobalActionShortcut(m_runCommandAction, "krunner", "run command"); connect(m_runCommandAction, &QAction::triggered, this, &ContextMenu::runCommand); m_lockScreenAction = new QAction(i18nc("plasma_containmentactions_contextmenu", "Lock Screen"), this); m_lockScreenAction->setIcon(QIcon::fromTheme("system-lock-screen")); setGlobalActionShortcut(m_lockScreenAction, "ksmserver", "Lock Session"); connect(m_lockScreenAction, &QAction::triggered, this, &ContextMenu::lockScreen); m_logoutAction = new QAction(i18nc("plasma_containmentactions_contextmenu", "Leave..."), this); m_logoutAction->setIcon(QIcon::fromTheme("system-log-out")); setGlobalActionShortcut(m_logoutAction, "ksmserver", "Log Out"); connect(m_logoutAction, &QAction::triggered, this, &ContextMenu::startLogout); m_separator1 = new QAction(this); m_separator1->setSeparator(true); m_separator2 = new QAction(this); m_separator2->setSeparator(true); m_separator3 = new QAction(this); m_separator3->setSeparator(true); } } QList ContextMenu::contextualActions() { Plasma::Containment *c = containment(); Q_ASSERT(c); QList actions; foreach (const QString &name, m_actionOrder) { if (!m_actions.value(name)) { continue; } if (name == "_context") { actions << c->contextualActions(); } if (name == "_wallpaper") { if (!c->wallpaper().isEmpty()) { QObject *wallpaperGraphicsObject = c->property("wallpaperGraphicsObject").value(); if (wallpaperGraphicsObject) { actions << wallpaperGraphicsObject->property("contextualActions").value >(); } } } else if (QAction *a = action(name)) { actions << a; } } return actions; } QAction *ContextMenu::action(const QString &name) { Plasma::Containment *c = containment(); Q_ASSERT(c); if (name == "_sep1") { return m_separator1; } else if (name == "_sep2") { return m_separator2; } else if (name == "_sep3") { return m_separator3; } else if (name == "_add panel") { if (c->corona() && c->corona()->immutability() == Plasma::Types::Mutable) { return c->corona()->actions()->action("add panel"); } } else if (name == "_run_command") { if (KAuthorized::authorizeKAction("run_command")) { return m_runCommandAction; } } else if (name == "_lock_screen") { if (KAuthorized::authorizeKAction("lock_screen")) { return m_lockScreenAction; } } else if (name == "_logout") { if (KAuthorized::authorizeKAction("logout")) { return m_logoutAction; } } else if (name == "lock widgets") { if (c->corona() && KAuthorized::authorizeKAction("lock widgets")) { return c->corona()->actions()->action("lock widgets"); } } else if (name == "manage activities") { if (c->corona()) { return c->corona()->actions()->action("manage activities"); } } else { //FIXME: remove action: make removal of current activity possible return c->actions()->action(name); } return 0; } void ContextMenu::runCommand() { if (!KAuthorized::authorizeKAction("run_command")) { return; } QString interface("org.kde.krunner"); org::kde::krunner::App krunner(interface, "/App", QDBusConnection::sessionBus()); krunner.display(); } void ContextMenu::lockScreen() { if (!KAuthorized::authorizeKAction("lock_screen")) { return; } #ifndef Q_OS_WIN QString interface("org.freedesktop.ScreenSaver"); org::freedesktop::ScreenSaver screensaver(interface, "/ScreenSaver", QDBusConnection::sessionBus()); if (screensaver.isValid()) { screensaver.Lock(); } #else LockWorkStation(); #endif // !Q_OS_WIN } void ContextMenu::startLogout() { // this short delay is due to two issues: // a) KWorkSpace's DBus alls are all syncronous // b) the destrution of the menu that this action is in is delayed // // (a) leads to the menu hanging out where everyone can see it because // the even loop doesn't get returned to allowing it to close. // // (b) leads to a 0ms timer not working since a 0ms timer just appends to // the event queue, and then the menu closing event gets appended to that. // // ergo a timer with small timeout QTimer::singleShot(10, this, SLOT(logout())); } void ContextMenu::logout() { if (!KAuthorized::authorizeKAction("logout")) { return; } KWorkSpace::requestShutDown(); } // TODO port to KGlobalAccel::globalShortcut(const QString& componentName, const QString& actionId) const // available in kf5 >= 5.10 void ContextMenu::setGlobalActionShortcut(QAction * action, const QString &component, const QString &actionId) { if (!action) return; QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kglobalaccel"), QStringLiteral("/kglobalaccel"), QStringLiteral("org.kde.KGlobalAccel"), QStringLiteral("shortcut")); const QStringList args = {component, actionId, QString(), QString()}; msg << args; const auto call = QDBusConnection::sessionBus().asyncCall(msg); QDBusPendingCallWatcher *replyWatcher = new QDBusPendingCallWatcher(call, this); QObject::connect(replyWatcher, &QDBusPendingCallWatcher::finished, this, [action, this](QDBusPendingCallWatcher *watcher) { QDBusPendingReply> reply = *watcher; if (!reply.isError()) { const auto &value = reply.value(); if (!value.isEmpty()) { action->setShortcut(value.first()); } } else { qWarning() << Q_FUNC_INFO << reply.error().message() << reply.error().name(); } watcher->deleteLater(); }); } QWidget* ContextMenu::createConfigurationInterface(QWidget* parent) { QWidget *widget = new QWidget(parent); QVBoxLayout *lay = new QVBoxLayout(); widget->setLayout(lay); widget->setWindowTitle(i18nc("plasma_containmentactions_contextmenu", "Configure Contextual Menu Plugin")); m_buttons = new QButtonGroup(widget); m_buttons->setExclusive(false); foreach (const QString &name, m_actionOrder) { QCheckBox *item = 0; if (name == "_context") { item = new QCheckBox(widget); //FIXME better text item->setText(i18nc("plasma_containmentactions_contextmenu", "[Other Actions]")); } else if (name == "_wallpaper") { item = new QCheckBox(widget); item->setText(i18nc("plasma_containmentactions_contextmenu", "Wallpaper Actions")); item->setIcon(QIcon::fromTheme("user-desktop")); } else if (name == "_sep1" || name =="_sep2" || name == "_sep3") { item = new QCheckBox(widget); item->setText(i18nc("plasma_containmentactions_contextmenu", "[Separator]")); } else { QAction *a = action(name); if (a) { item = new QCheckBox(widget); item->setText(a->text()); item->setIcon(a->icon()); } } if (item) { item->setChecked(m_actions.value(name)); item->setProperty("actionName", name); lay->addWidget(item); m_buttons->addButton(item); } } return widget; } void ContextMenu::configurationAccepted() { QList buttons = m_buttons->buttons(); QListIterator it(buttons); while (it.hasNext()) { QAbstractButton *b = it.next(); if (b) { m_actions.insert(b->property("actionName").toString(), b->isChecked()); } } } void ContextMenu::save(KConfigGroup &config) { QHashIterator it(m_actions); while (it.hasNext()) { it.next(); config.writeEntry(it.key(), it.value()); } } K_EXPORT_PLASMA_CONTAINMENTACTIONS_WITH_JSON(contextmenu, ContextMenu, "plasma-containmentactions-contextmenu.json") #include "menu.moc"