File indexing completed on 2024-05-12 05:37:16
0001 /* 0002 SPDX-FileCopyrightText: 2009 Chani Armitage <chani@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "menu.h" 0008 0009 #include <QAction> 0010 #include <QCheckBox> 0011 #include <QDBusPendingReply> 0012 #include <QVBoxLayout> 0013 0014 #include <KAuthorized> 0015 #include <KGlobalAccel> 0016 #include <KIO/ApplicationLauncherJob> 0017 #include <KLocalizedString> 0018 #include <KService> 0019 #include <KTerminalLauncherJob> 0020 #include <PlasmaActivities/Consumer> 0021 #include <QDebug> 0022 #include <QIcon> 0023 #include <QQmlListProperty> 0024 0025 #include <Plasma/Containment> 0026 #include <Plasma/Corona> 0027 0028 #include "krunner_interface.h" 0029 #include "kworkspace.h" 0030 #include <sessionmanagement.h> 0031 0032 using namespace Qt::StringLiterals; 0033 0034 ContextMenu::ContextMenu(QObject *parent, const QVariantList &args) 0035 : Plasma::ContainmentActions(parent, args) 0036 , m_session(new SessionManagement(this)) 0037 { 0038 } 0039 0040 ContextMenu::~ContextMenu() 0041 { 0042 } 0043 0044 void ContextMenu::restore(const KConfigGroup &config) 0045 { 0046 Plasma::Containment *c = containment(); 0047 Q_ASSERT(c); 0048 0049 m_actions.clear(); 0050 m_actionOrder.clear(); 0051 QHash<QString, bool> actions; 0052 QSet<QString> disabled; 0053 0054 // clang-format off 0055 // because it really wants to mangle this nice aligned list 0056 if (c->containmentType() == Plasma::Containment::Panel || c->containmentType() == Plasma::Containment::CustomPanel) { 0057 m_actionOrder << QStringLiteral("add widgets") 0058 << QStringLiteral("_context") 0059 << QStringLiteral("configure") 0060 << QStringLiteral("remove"); 0061 } else { 0062 actions.insert(QStringLiteral("configure shortcuts"), false); 0063 m_actionOrder << QStringLiteral("configure") 0064 << QStringLiteral("_display_settings") 0065 << QStringLiteral("configure shortcuts") 0066 << QStringLiteral("_sep1") 0067 << QStringLiteral("_context") 0068 << QStringLiteral("_open_terminal") 0069 << QStringLiteral("_run_command") 0070 << QStringLiteral("manage activities") 0071 << QStringLiteral("remove") 0072 << QStringLiteral("edit mode") 0073 << QStringLiteral("_sep2") 0074 << QStringLiteral("_lock_screen") 0075 << QStringLiteral("_logout") 0076 << QStringLiteral("_sep3") 0077 << QStringLiteral("_wallpaper"); 0078 disabled.insert(QStringLiteral("add widgets")); 0079 disabled.insert(QStringLiteral("add panel")); 0080 disabled.insert(QStringLiteral("configure shortcuts")); 0081 disabled.insert(QStringLiteral("_open_terminal")); 0082 disabled.insert(QStringLiteral("_run_command")); 0083 disabled.insert(QStringLiteral("_lock_screen")); 0084 disabled.insert(QStringLiteral("_logout")); 0085 } 0086 // clang-format on 0087 0088 for (const QString &name : std::as_const(m_actionOrder)) { 0089 actions.insert(name, !disabled.contains(name)); 0090 } 0091 0092 QHashIterator<QString, bool> it(actions); 0093 while (it.hasNext()) { 0094 it.next(); 0095 m_actions.insert(it.key(), config.readEntry(it.key(), it.value())); 0096 } 0097 0098 // everything below should only happen once, so check for it 0099 if (!m_runCommandAction) { 0100 m_runCommandAction = new QAction(i18nc("plasma_containmentactions_contextmenu", "Show KRunner"), this); 0101 m_runCommandAction->setIcon(QIcon::fromTheme(QStringLiteral("plasma-search"))); 0102 m_runCommandAction->setShortcut(KGlobalAccel::self()->globalShortcut(QStringLiteral("krunner.desktop"), QStringLiteral("_launch")).value(0)); 0103 connect(m_runCommandAction, &QAction::triggered, this, &ContextMenu::runCommand); 0104 0105 m_openTerminalAction = new QAction(i18n("Open Terminal"), this); 0106 m_openTerminalAction->setIcon(QIcon::fromTheme("utilities-terminal")); 0107 connect(m_openTerminalAction, &QAction::triggered, this, &ContextMenu::openTerminal); 0108 0109 m_lockScreenAction = new QAction(i18nc("plasma_containmentactions_contextmenu", "Lock Screen"), this); 0110 m_lockScreenAction->setIcon(QIcon::fromTheme(QStringLiteral("system-lock-screen"))); 0111 m_lockScreenAction->setShortcut(KGlobalAccel::self()->globalShortcut(QStringLiteral("ksmserver"), QStringLiteral("Lock Session")).value(0)); 0112 m_lockScreenAction->setEnabled(m_session->canLock()); 0113 connect(m_session, &SessionManagement::canLockChanged, this, [this]() { 0114 m_lockScreenAction->setEnabled(m_session->canLock()); 0115 }); 0116 connect(m_lockScreenAction, &QAction::triggered, m_session, &SessionManagement::lock); 0117 0118 m_logoutAction = new QAction(i18nc("plasma_containmentactions_contextmenu", "Leaveā¦"), this); 0119 m_logoutAction->setIcon(QIcon::fromTheme(QStringLiteral("system-log-out"))); 0120 m_logoutAction->setShortcut(KGlobalAccel::self()->globalShortcut(QStringLiteral("ksmserver"), QStringLiteral("Show Logout Prompt")).value(0)); 0121 connect(m_logoutAction, &QAction::triggered, this, &ContextMenu::startLogout); 0122 0123 m_configureDisplaysAction = new QAction(i18nc("plasma_containmentactions_contextmenu", "Configure Display Settingsā¦"), this); 0124 m_configureDisplaysAction->setIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-display"))); 0125 connect(m_configureDisplaysAction, &QAction::triggered, this, &ContextMenu::configureDisplays); 0126 0127 m_separator1 = new QAction(this); 0128 m_separator1->setSeparator(true); 0129 m_separator2 = new QAction(this); 0130 m_separator2->setSeparator(true); 0131 m_separator3 = new QAction(this); 0132 m_separator3->setSeparator(true); 0133 } 0134 } 0135 0136 QList<QAction *> ContextMenu::contextualActions() 0137 { 0138 Plasma::Containment *cont = containment(); 0139 Q_ASSERT(cont); 0140 QList<QAction *> actions; 0141 for (const QString &name : std::as_const(m_actionOrder)) { 0142 if (!m_actions.value(name)) { 0143 continue; 0144 } 0145 0146 if (name == QLatin1String("_context")) { 0147 actions << cont->contextualActions(); 0148 } 0149 if (name == QLatin1String("_wallpaper")) { 0150 if (!cont->wallpaperPlugin().isEmpty()) { 0151 QObject *wallpaperGraphicsObject = cont->property("wallpaperGraphicsObject").value<QObject *>(); 0152 if (wallpaperGraphicsObject) { 0153 QQmlListProperty<QAction> l = wallpaperGraphicsObject->property("contextualActions").value<QQmlListProperty<QAction>>(); 0154 int nActions = l.count(&l); 0155 for (int i = 0; i < nActions; ++i) { 0156 actions << l.at(&l, i); 0157 } 0158 } 0159 } 0160 } else if (QAction *a = action(name)) { 0161 // Bug 364292: show "Remove this Panel" option only when panelcontroller is opened 0162 if (name != QLatin1String("remove") || cont->isUserConfiguring() 0163 || (cont->containmentType() != Plasma::Containment::Panel && cont->containmentType() != Plasma::Containment::CustomPanel 0164 && cont->corona()->immutability() != Plasma::Types::Mutable)) { 0165 actions << a; 0166 } 0167 } 0168 } 0169 0170 return actions; 0171 } 0172 0173 QAction *ContextMenu::action(const QString &name) 0174 { 0175 Plasma::Containment *c = containment(); 0176 Q_ASSERT(c); 0177 if (name == QLatin1String("_sep1")) { 0178 return m_separator1; 0179 } else if (name == QLatin1String("_sep2")) { 0180 return m_separator2; 0181 } else if (name == QLatin1String("_sep3")) { 0182 return m_separator3; 0183 } else if (name == QLatin1String("_add panel")) { 0184 if (c->corona() && c->corona()->immutability() == Plasma::Types::Mutable) { 0185 return c->corona()->action(QStringLiteral("add panel")); 0186 } 0187 } else if (name == QLatin1String("_run_command")) { 0188 if (KAuthorized::authorizeAction(QStringLiteral("run_command")) && KAuthorized::authorize(QStringLiteral("run_command"))) { 0189 return m_runCommandAction; 0190 } 0191 } else if (name == QLatin1String("_open_terminal")) { 0192 if (KAuthorized::authorizeAction(QStringLiteral("shell_access"))) { 0193 return m_openTerminalAction; 0194 } 0195 } else if (name == QLatin1String("_lock_screen")) { 0196 if (KAuthorized::authorizeAction(QStringLiteral("lock_screen"))) { 0197 return m_lockScreenAction; 0198 } 0199 } else if (name == QLatin1String("_logout")) { 0200 if (KAuthorized::authorize(QStringLiteral("logout"))) { 0201 return m_logoutAction; 0202 } 0203 } else if (name == QLatin1String("_display_settings")) { 0204 if (KAuthorized::authorizeControlModule(QStringLiteral("kcm_kscreen.desktop")) && KService::serviceByStorageId(QStringLiteral("kcm_kscreen"))) { 0205 return m_configureDisplaysAction; 0206 } 0207 } else if (name == QLatin1String("edit mode")) { 0208 if (c->corona()) { 0209 return c->corona()->action(QStringLiteral("edit mode")); 0210 } 0211 } else if (name == QLatin1String("manage activities")) { 0212 if (c->corona()) { 0213 // Don't show the action if there's only one activity since in this 0214 // case it's clear that the user doesn't use activities 0215 if (KActivities::Consumer().activities().length() == 1) { 0216 return nullptr; 0217 } 0218 0219 return c->corona()->action(QStringLiteral("manage activities")); 0220 } 0221 } else { 0222 // FIXME: remove action: make removal of current activity possible 0223 return c->internalAction(name); 0224 } 0225 return nullptr; 0226 } 0227 0228 void ContextMenu::openTerminal() 0229 { 0230 if (!KAuthorized::authorizeAction(QStringLiteral("shell_access"))) { 0231 return; 0232 } 0233 auto job = new KTerminalLauncherJob(QString()); 0234 job->setWorkingDirectory(QDir::homePath()); 0235 job->start(); 0236 } 0237 0238 void ContextMenu::runCommand() 0239 { 0240 if (!KAuthorized::authorizeAction(QStringLiteral("run_command"))) { 0241 return; 0242 } 0243 0244 QString interface(QStringLiteral("org.kde.krunner")); 0245 org::kde::krunner::App krunner(interface, QStringLiteral("/App"), QDBusConnection::sessionBus()); 0246 krunner.display(); 0247 } 0248 0249 void ContextMenu::startLogout() 0250 { 0251 KConfig config(u"ksmserverrc"_s); 0252 const auto group = config.group(u"General"_s); 0253 switch (group.readEntry("shutdownType", int(KWorkSpace::ShutdownTypeDefault))) { 0254 case int(KWorkSpace::ShutdownTypeHalt): 0255 m_session->requestShutdown(); 0256 break; 0257 case int(KWorkSpace::ShutdownTypeReboot): 0258 m_session->requestReboot(); 0259 break; 0260 case int(KWorkSpace::ShutdownTypeLogout): 0261 m_session->requestLogout(); 0262 break; 0263 case int(KWorkSpace::ShutdownTypeDefault): 0264 default: 0265 m_session->requestLogoutPrompt(); 0266 break; 0267 } 0268 } 0269 0270 void ContextMenu::configureDisplays() 0271 { 0272 if (auto service = KService::serviceByDesktopName(QStringLiteral("kcm_kscreen"))) { 0273 auto job = new KIO::ApplicationLauncherJob(service); 0274 job->start(); 0275 } 0276 } 0277 0278 QWidget *ContextMenu::createConfigurationInterface(QWidget *parent) 0279 { 0280 QWidget *widget = new QWidget(parent); 0281 QVBoxLayout *lay = new QVBoxLayout(); 0282 widget->setLayout(lay); 0283 widget->setWindowTitle(i18nc("plasma_containmentactions_contextmenu", "Configure Contextual Menu Plugin")); 0284 m_buttons = new QButtonGroup(widget); 0285 m_buttons->setExclusive(false); 0286 0287 foreach (const QString &name, m_actionOrder) { 0288 QCheckBox *item = nullptr; 0289 0290 if (name == QLatin1String("_context")) { 0291 item = new QCheckBox(widget); 0292 // FIXME better text 0293 item->setText(i18nc("plasma_containmentactions_contextmenu", "[Other Actions]")); 0294 } else if (name == QLatin1String("_wallpaper")) { 0295 item = new QCheckBox(widget); 0296 item->setText(i18nc("plasma_containmentactions_contextmenu", "Wallpaper Actions")); 0297 item->setIcon(QIcon::fromTheme(QStringLiteral("user-desktop"))); 0298 } else if (name == QLatin1String("_sep1") || name == QLatin1String("_sep2") || name == QLatin1String("_sep3")) { 0299 item = new QCheckBox(widget); 0300 item->setText(i18nc("plasma_containmentactions_contextmenu", "[Separator]")); 0301 } else { 0302 QAction *a = action(name); 0303 if (a) { 0304 item = new QCheckBox(widget); 0305 item->setText(a->text()); 0306 item->setIcon(a->icon()); 0307 } 0308 } 0309 0310 if (item) { 0311 item->setChecked(m_actions.value(name)); 0312 item->setProperty("actionName", name); 0313 lay->addWidget(item); 0314 m_buttons->addButton(item); 0315 } 0316 } 0317 0318 return widget; 0319 } 0320 0321 void ContextMenu::configurationAccepted() 0322 { 0323 QList<QAbstractButton *> buttons = m_buttons->buttons(); 0324 QListIterator<QAbstractButton *> it(buttons); 0325 while (it.hasNext()) { 0326 QAbstractButton *b = it.next(); 0327 if (b) { 0328 m_actions.insert(b->property("actionName").toString(), b->isChecked()); 0329 } 0330 } 0331 } 0332 0333 void ContextMenu::save(KConfigGroup &config) 0334 { 0335 QHashIterator<QString, bool> it(m_actions); 0336 while (it.hasNext()) { 0337 it.next(); 0338 config.writeEntry(it.key(), it.value()); 0339 } 0340 } 0341 0342 K_PLUGIN_CLASS_WITH_JSON(ContextMenu, "plasma-containmentactions-contextmenu.json") 0343 0344 #include "menu.moc" 0345 #include "moc_menu.cpp"