File indexing completed on 2024-11-10 04:57:58
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 1999, 2000 Matthias Ettrich <ettrich@kde.org> 0006 SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org> 0007 SPDX-FileCopyrightText: 2022 Natalie Clarius <natalie_clarius@yahoo.de> 0008 0009 SPDX-License-Identifier: GPL-2.0-or-later 0010 */ 0011 0012 /* 0013 0014 This file contains things relevant to direct user actions, such as 0015 responses to global keyboard shortcuts, or selecting actions 0016 from the window operations menu. 0017 0018 */ 0019 0020 /////////////////////////////////////////////////////////////////////////////// 0021 // NOTE: if you change the menu, keep 0022 // plasma-desktop/applets/taskmanager/package/contents/ui/ContextMenu.qml 0023 // in sync 0024 ////////////////////////////////////////////////////////////////////////////// 0025 0026 #include <config-kwin.h> 0027 0028 #include "core/output.h" 0029 #include "cursor.h" 0030 #include "input.h" 0031 #include "options.h" 0032 #include "scripting/scripting.h" 0033 #include "useractions.h" 0034 #include "virtualdesktops.h" 0035 #include "workspace.h" 0036 #include "x11window.h" 0037 0038 #if KWIN_BUILD_ACTIVITIES 0039 #include "activities.h" 0040 #include <PlasmaActivities/Info> 0041 #endif 0042 #include "appmenu.h" 0043 0044 #include <KProcess> 0045 0046 #include <QAction> 0047 #include <QCheckBox> 0048 #include <QPushButton> 0049 0050 #include <KGlobalAccel> 0051 #include <KLazyLocalizedString> 0052 #include <KLocalizedString> 0053 #include <QAction> 0054 #include <QActionGroup> 0055 #include <QMenu> 0056 #include <QRegularExpression> 0057 #include <kauthorized.h> 0058 #include <kconfig.h> 0059 0060 #include "killwindow.h" 0061 #if KWIN_BUILD_TABBOX 0062 #include "tabbox/tabbox.h" 0063 #endif 0064 0065 namespace KWin 0066 { 0067 0068 UserActionsMenu::UserActionsMenu(QObject *parent) 0069 : QObject(parent) 0070 , m_menu(nullptr) 0071 , m_desktopMenu(nullptr) 0072 , m_multipleDesktopsMenu(nullptr) 0073 , m_screenMenu(nullptr) 0074 , m_activityMenu(nullptr) 0075 , m_scriptsMenu(nullptr) 0076 , m_resizeOperation(nullptr) 0077 , m_moveOperation(nullptr) 0078 , m_maximizeOperation(nullptr) 0079 , m_shadeOperation(nullptr) 0080 , m_keepAboveOperation(nullptr) 0081 , m_keepBelowOperation(nullptr) 0082 , m_fullScreenOperation(nullptr) 0083 , m_noBorderOperation(nullptr) 0084 , m_minimizeOperation(nullptr) 0085 , m_closeOperation(nullptr) 0086 , m_shortcutOperation(nullptr) 0087 { 0088 } 0089 0090 UserActionsMenu::~UserActionsMenu() 0091 { 0092 discard(); 0093 } 0094 0095 bool UserActionsMenu::isShown() const 0096 { 0097 return m_menu && m_menu->isVisible(); 0098 } 0099 0100 bool UserActionsMenu::hasWindow() const 0101 { 0102 return m_window && isShown(); 0103 } 0104 0105 void UserActionsMenu::close() 0106 { 0107 if (!m_menu) { 0108 return; 0109 } 0110 m_menu->close(); 0111 } 0112 0113 bool UserActionsMenu::isMenuWindow(const Window *window) const 0114 { 0115 return window && window == m_window; 0116 } 0117 0118 void UserActionsMenu::show(const QRect &pos, Window *window) 0119 { 0120 Q_ASSERT(window); 0121 QPointer<Window> windowPtr(window); 0122 // Presumably window will never be nullptr, 0123 // but play it safe and make sure not to crash. 0124 if (windowPtr.isNull()) { 0125 return; 0126 } 0127 if (isShown()) { // recursion 0128 return; 0129 } 0130 if (windowPtr->isDesktop() || windowPtr->isDock()) { 0131 return; 0132 } 0133 if (!KAuthorized::authorizeAction(QStringLiteral("kwin_rmb"))) { 0134 return; 0135 } 0136 m_window = windowPtr; 0137 init(); 0138 m_menu->popup(pos.bottomLeft()); 0139 } 0140 0141 void UserActionsMenu::grabInput() 0142 { 0143 m_menu->windowHandle()->setMouseGrabEnabled(true); 0144 m_menu->windowHandle()->setKeyboardGrabEnabled(true); 0145 } 0146 0147 void UserActionsMenu::helperDialog(const QString &message) 0148 { 0149 QStringList args; 0150 QString type; 0151 auto shortcut = [](const QString &name) { 0152 QAction *action = Workspace::self()->findChild<QAction *>(name); 0153 Q_ASSERT(action != nullptr); 0154 const auto shortcuts = KGlobalAccel::self()->shortcut(action); 0155 return QStringLiteral("%1 (%2)").arg(action->text(), shortcuts.isEmpty() ? QString() : shortcuts.first().toString(QKeySequence::NativeText)); 0156 }; 0157 if (message == QStringLiteral("noborderaltf3")) { 0158 args << QStringLiteral("--msgbox") << i18n("You have selected to show a window without its border.\n" 0159 "Without the border, you will not be able to enable the border " 0160 "again using the mouse: use the window operations menu instead, " 0161 "activated using the %1 keyboard shortcut.", 0162 shortcut(QStringLiteral("Window Operations Menu"))); 0163 type = QStringLiteral("altf3warning"); 0164 } else if (message == QLatin1String("fullscreenaltf3")) { 0165 args << QStringLiteral("--msgbox") << i18n("You have selected to show a window in fullscreen mode.\n" 0166 "If the application itself does not have an option to turn the fullscreen " 0167 "mode off you will not be able to disable it " 0168 "again using the mouse: use the window operations menu instead, " 0169 "activated using the %1 keyboard shortcut.", 0170 shortcut(QStringLiteral("Window Operations Menu"))); 0171 type = QStringLiteral("altf3warning"); 0172 } else { 0173 Q_UNREACHABLE(); 0174 } 0175 if (!type.isEmpty()) { 0176 KConfig cfg(QStringLiteral("kwin_dialogsrc")); 0177 KConfigGroup cg(&cfg, QStringLiteral("Notification Messages")); // Depends on KMessageBox 0178 if (!cg.readEntry(type, true)) { 0179 return; 0180 } 0181 args << QStringLiteral("--dontagain") << QLatin1String("kwin_dialogsrc:") + type; 0182 } 0183 KProcess::startDetached(QStringLiteral("kdialog"), args); 0184 } 0185 0186 void UserActionsMenu::init() 0187 { 0188 if (m_menu) { 0189 return; 0190 } 0191 m_menu = new QMenu; 0192 connect(m_menu, &QMenu::aboutToShow, this, &UserActionsMenu::menuAboutToShow); 0193 0194 // the toplevel menu gets closed before a submenu's action is invoked 0195 connect(m_menu, &QMenu::aboutToHide, this, &UserActionsMenu::menuAboutToHide, Qt::QueuedConnection); 0196 connect(m_menu, &QMenu::triggered, this, &UserActionsMenu::slotWindowOperation); 0197 0198 QMenu *advancedMenu = new QMenu(m_menu); 0199 connect(advancedMenu, &QMenu::aboutToShow, this, [this, advancedMenu]() { 0200 if (m_window) { 0201 advancedMenu->setPalette(m_window->palette()); 0202 } 0203 }); 0204 0205 auto setShortcut = [](QAction *action, const QString &actionName) { 0206 const auto shortcuts = KGlobalAccel::self()->shortcut(Workspace::self()->findChild<QAction *>(actionName)); 0207 if (!shortcuts.isEmpty()) { 0208 action->setShortcut(shortcuts.first()); 0209 } 0210 }; 0211 0212 m_moveOperation = advancedMenu->addAction(i18n("&Move")); 0213 m_moveOperation->setIcon(QIcon::fromTheme(QStringLiteral("transform-move"))); 0214 setShortcut(m_moveOperation, QStringLiteral("Window Move")); 0215 m_moveOperation->setData(Options::UnrestrictedMoveOp); 0216 0217 m_resizeOperation = advancedMenu->addAction(i18n("&Resize")); 0218 m_resizeOperation->setIcon(QIcon::fromTheme(QStringLiteral("transform-scale"))); 0219 setShortcut(m_resizeOperation, QStringLiteral("Window Resize")); 0220 m_resizeOperation->setData(Options::ResizeOp); 0221 0222 m_keepAboveOperation = advancedMenu->addAction(i18n("Keep &Above Others")); 0223 m_keepAboveOperation->setIcon(QIcon::fromTheme(QStringLiteral("window-keep-above"))); 0224 setShortcut(m_keepAboveOperation, QStringLiteral("Window Above Other Windows")); 0225 m_keepAboveOperation->setCheckable(true); 0226 m_keepAboveOperation->setData(Options::KeepAboveOp); 0227 0228 m_keepBelowOperation = advancedMenu->addAction(i18n("Keep &Below Others")); 0229 m_keepBelowOperation->setIcon(QIcon::fromTheme(QStringLiteral("window-keep-below"))); 0230 setShortcut(m_keepBelowOperation, QStringLiteral("Window Below Other Windows")); 0231 m_keepBelowOperation->setCheckable(true); 0232 m_keepBelowOperation->setData(Options::KeepBelowOp); 0233 0234 m_fullScreenOperation = advancedMenu->addAction(i18n("&Fullscreen")); 0235 m_fullScreenOperation->setIcon(QIcon::fromTheme(QStringLiteral("view-fullscreen"))); 0236 setShortcut(m_fullScreenOperation, QStringLiteral("Window Fullscreen")); 0237 m_fullScreenOperation->setCheckable(true); 0238 m_fullScreenOperation->setData(Options::FullScreenOp); 0239 0240 m_shadeOperation = advancedMenu->addAction(i18n("&Shade")); 0241 m_shadeOperation->setIcon(QIcon::fromTheme(QStringLiteral("window-shade"))); 0242 setShortcut(m_shadeOperation, QStringLiteral("Window Shade")); 0243 m_shadeOperation->setCheckable(true); 0244 m_shadeOperation->setData(Options::ShadeOp); 0245 0246 m_noBorderOperation = advancedMenu->addAction(i18n("&No Titlebar and Frame")); 0247 m_noBorderOperation->setIcon(QIcon::fromTheme(QStringLiteral("edit-none-border"))); 0248 setShortcut(m_noBorderOperation, QStringLiteral("Window No Border")); 0249 m_noBorderOperation->setCheckable(true); 0250 m_noBorderOperation->setData(Options::NoBorderOp); 0251 0252 advancedMenu->addSeparator(); 0253 0254 m_shortcutOperation = advancedMenu->addAction(i18n("Set Window Short&cut…")); 0255 m_shortcutOperation->setIcon(QIcon::fromTheme(QStringLiteral("configure-shortcuts"))); 0256 setShortcut(m_shortcutOperation, QStringLiteral("Setup Window Shortcut")); 0257 m_shortcutOperation->setData(Options::SetupWindowShortcutOp); 0258 0259 #if KWIN_BUILD_KCMS 0260 QAction *action = advancedMenu->addAction(i18n("Configure Special &Window Settings…")); 0261 action->setIcon(QIcon::fromTheme(QStringLiteral("preferences-system-windows-actions"))); 0262 action->setData(Options::WindowRulesOp); 0263 m_rulesOperation = action; 0264 0265 action = advancedMenu->addAction(i18n("Configure S&pecial Application Settings…")); 0266 action->setIcon(QIcon::fromTheme(QStringLiteral("preferences-system-windows-actions"))); 0267 action->setData(Options::ApplicationRulesOp); 0268 m_applicationRulesOperation = action; 0269 #endif 0270 0271 m_maximizeOperation = m_menu->addAction(i18n("Ma&ximize")); 0272 m_maximizeOperation->setIcon(QIcon::fromTheme(QStringLiteral("window-maximize"))); 0273 setShortcut(m_maximizeOperation, QStringLiteral("Window Maximize")); 0274 m_maximizeOperation->setCheckable(true); 0275 m_maximizeOperation->setData(Options::MaximizeOp); 0276 0277 m_minimizeOperation = m_menu->addAction(i18n("Mi&nimize")); 0278 m_minimizeOperation->setIcon(QIcon::fromTheme(QStringLiteral("window-minimize"))); 0279 setShortcut(m_minimizeOperation, QStringLiteral("Window Minimize")); 0280 m_minimizeOperation->setData(Options::MinimizeOp); 0281 0282 QAction *overflowAction = m_menu->addMenu(advancedMenu); 0283 overflowAction->setText(i18n("&More Actions")); 0284 overflowAction->setIcon(QIcon::fromTheme(QStringLiteral("overflow-menu"))); 0285 0286 m_menu->addSeparator(); 0287 0288 m_closeOperation = m_menu->addAction(i18n("&Close")); 0289 m_closeOperation->setIcon(QIcon::fromTheme(QStringLiteral("window-close"))); 0290 setShortcut(m_closeOperation, QStringLiteral("Window Close")); 0291 m_closeOperation->setData(Options::CloseOp); 0292 } 0293 0294 void UserActionsMenu::discard() 0295 { 0296 delete m_menu; 0297 m_menu = nullptr; 0298 m_desktopMenu = nullptr; 0299 m_multipleDesktopsMenu = nullptr; 0300 m_screenMenu = nullptr; 0301 m_activityMenu = nullptr; 0302 m_scriptsMenu = nullptr; 0303 } 0304 0305 void UserActionsMenu::menuAboutToShow() 0306 { 0307 if (m_window.isNull() || !m_menu) { 0308 return; 0309 } 0310 0311 m_window->blockActivityUpdates(true); 0312 0313 if (VirtualDesktopManager::self()->count() == 1) { 0314 delete m_desktopMenu; 0315 m_desktopMenu = nullptr; 0316 delete m_multipleDesktopsMenu; 0317 m_multipleDesktopsMenu = nullptr; 0318 } else { 0319 initDesktopPopup(); 0320 } 0321 if (workspace()->outputs().count() == 1 || (!m_window->isMovable() && !m_window->isMovableAcrossScreens())) { 0322 delete m_screenMenu; 0323 m_screenMenu = nullptr; 0324 } else { 0325 initScreenPopup(); 0326 } 0327 0328 m_menu->setPalette(m_window->palette()); 0329 m_resizeOperation->setEnabled(m_window->isResizable()); 0330 m_moveOperation->setEnabled(m_window->isMovableAcrossScreens()); 0331 m_maximizeOperation->setEnabled(m_window->isMaximizable()); 0332 m_maximizeOperation->setChecked(m_window->maximizeMode() == MaximizeFull); 0333 m_shadeOperation->setEnabled(m_window->isShadeable()); 0334 m_shadeOperation->setChecked(m_window->shadeMode() != ShadeNone); 0335 m_keepAboveOperation->setChecked(m_window->keepAbove()); 0336 m_keepBelowOperation->setChecked(m_window->keepBelow()); 0337 m_fullScreenOperation->setEnabled(m_window->isFullScreenable()); 0338 m_fullScreenOperation->setChecked(m_window->isFullScreen()); 0339 m_noBorderOperation->setEnabled(m_window->userCanSetNoBorder()); 0340 m_noBorderOperation->setChecked(m_window->noBorder()); 0341 m_minimizeOperation->setEnabled(m_window->isMinimizable()); 0342 m_closeOperation->setEnabled(m_window->isCloseable()); 0343 m_shortcutOperation->setEnabled(m_window->rules()->checkShortcut(QString()).isNull()); 0344 0345 // drop the existing scripts menu 0346 delete m_scriptsMenu; 0347 m_scriptsMenu = nullptr; 0348 // ask scripts whether they want to add entries for the given window 0349 QList<QAction *> scriptActions = Scripting::self()->actionsForUserActionMenu(m_window.data(), m_scriptsMenu); 0350 if (!scriptActions.isEmpty()) { 0351 m_scriptsMenu = new QMenu(m_menu); 0352 m_scriptsMenu->setPalette(m_window->palette()); 0353 m_scriptsMenu->addActions(scriptActions); 0354 0355 QAction *action = m_scriptsMenu->menuAction(); 0356 // set it as the first item after desktop 0357 m_menu->insertAction(m_closeOperation, action); 0358 action->setText(i18n("&Extensions")); 0359 } 0360 0361 if (m_rulesOperation) { 0362 m_rulesOperation->setEnabled(m_window->supportsWindowRules()); 0363 } 0364 if (m_applicationRulesOperation) { 0365 m_applicationRulesOperation->setEnabled(m_window->supportsWindowRules()); 0366 } 0367 0368 showHideActivityMenu(); 0369 } 0370 0371 void UserActionsMenu::menuAboutToHide() 0372 { 0373 if (m_window) { 0374 m_window->blockActivityUpdates(false); 0375 m_window.clear(); 0376 } 0377 } 0378 0379 void UserActionsMenu::showHideActivityMenu() 0380 { 0381 #if KWIN_BUILD_ACTIVITIES 0382 if (!Workspace::self()->activities()) { 0383 return; 0384 } 0385 const QStringList &openActivities_ = Workspace::self()->activities()->running(); 0386 qCDebug(KWIN_CORE) << "activities:" << openActivities_.size(); 0387 if (openActivities_.size() < 2) { 0388 delete m_activityMenu; 0389 m_activityMenu = nullptr; 0390 } else { 0391 initActivityPopup(); 0392 } 0393 #endif 0394 } 0395 0396 void UserActionsMenu::initDesktopPopup() 0397 { 0398 if (kwinApp()->operationMode() == Application::OperationModeWaylandOnly || kwinApp()->operationMode() == Application::OperationModeXwayland) { 0399 if (m_multipleDesktopsMenu) { 0400 return; 0401 } 0402 0403 m_multipleDesktopsMenu = new QMenu(m_menu); 0404 connect(m_multipleDesktopsMenu, &QMenu::aboutToShow, this, &UserActionsMenu::multipleDesktopsPopupAboutToShow); 0405 0406 QAction *action = m_multipleDesktopsMenu->menuAction(); 0407 // set it as the first item 0408 m_menu->insertAction(m_maximizeOperation, action); 0409 action->setText(i18n("&Desktops")); 0410 action->setIcon(QIcon::fromTheme(QStringLiteral("virtual-desktops"))); 0411 0412 } else { 0413 if (m_desktopMenu) { 0414 return; 0415 } 0416 0417 m_desktopMenu = new QMenu(m_menu); 0418 connect(m_desktopMenu, &QMenu::aboutToShow, this, &UserActionsMenu::desktopPopupAboutToShow); 0419 0420 QAction *action = m_desktopMenu->menuAction(); 0421 // set it as the first item 0422 m_menu->insertAction(m_maximizeOperation, action); 0423 action->setText(i18n("Move to &Desktop")); 0424 action->setIcon(QIcon::fromTheme(QStringLiteral("virtual-desktops"))); 0425 } 0426 } 0427 0428 void UserActionsMenu::initScreenPopup() 0429 { 0430 if (m_screenMenu) { 0431 return; 0432 } 0433 0434 m_screenMenu = new QMenu(m_menu); 0435 connect(m_screenMenu, &QMenu::aboutToShow, this, &UserActionsMenu::screenPopupAboutToShow); 0436 0437 QAction *action = m_screenMenu->menuAction(); 0438 // set it as the first item after desktop 0439 m_menu->insertAction(m_activityMenu ? m_activityMenu->menuAction() : m_minimizeOperation, action); 0440 action->setText(i18n("Move to &Screen")); 0441 action->setIcon(QIcon::fromTheme(QStringLiteral("computer"))); 0442 } 0443 0444 void UserActionsMenu::initActivityPopup() 0445 { 0446 if (m_activityMenu) { 0447 return; 0448 } 0449 0450 m_activityMenu = new QMenu(m_menu); 0451 connect(m_activityMenu, &QMenu::aboutToShow, this, &UserActionsMenu::activityPopupAboutToShow); 0452 0453 QAction *action = m_activityMenu->menuAction(); 0454 // set it as the first item 0455 m_menu->insertAction(m_maximizeOperation, action); 0456 action->setText(i18n("Show in &Activities")); 0457 action->setIcon(QIcon::fromTheme(QStringLiteral("activities"))); 0458 } 0459 0460 void UserActionsMenu::desktopPopupAboutToShow() 0461 { 0462 if (!m_desktopMenu) { 0463 return; 0464 } 0465 const VirtualDesktopManager *vds = VirtualDesktopManager::self(); 0466 0467 m_desktopMenu->clear(); 0468 if (m_window) { 0469 m_desktopMenu->setPalette(m_window->palette()); 0470 } 0471 QActionGroup *group = new QActionGroup(m_desktopMenu); 0472 0473 QAction *action = m_desktopMenu->addAction(i18n("Move &To Current Desktop")); 0474 action->setEnabled(m_window && (m_window->isOnAllDesktops() || !m_window->isOnDesktop(vds->currentDesktop()))); 0475 connect(action, &QAction::triggered, this, [this]() { 0476 if (!m_window) { 0477 return; 0478 } 0479 VirtualDesktopManager *vds = VirtualDesktopManager::self(); 0480 workspace()->sendWindowToDesktops(m_window, {vds->currentDesktop()}, false); 0481 }); 0482 0483 action = m_desktopMenu->addAction(i18n("&All Desktops")); 0484 connect(action, &QAction::triggered, this, [this]() { 0485 if (m_window) { 0486 m_window->setOnAllDesktops(!m_window->isOnAllDesktops()); 0487 } 0488 }); 0489 action->setCheckable(true); 0490 if (m_window && m_window->isOnAllDesktops()) { 0491 action->setChecked(true); 0492 } 0493 group->addAction(action); 0494 0495 m_desktopMenu->addSeparator(); 0496 0497 const uint BASE = 10; 0498 0499 const auto desktops = vds->desktops(); 0500 for (VirtualDesktop *desktop : desktops) { 0501 const uint legacyId = desktop->x11DesktopNumber(); 0502 0503 QString basic_name(QStringLiteral("%1 %2")); 0504 if (legacyId < BASE) { 0505 basic_name.prepend(QLatin1Char('&')); 0506 } 0507 action = m_desktopMenu->addAction(basic_name.arg(legacyId).arg(desktop->name().replace(QLatin1Char('&'), QStringLiteral("&&")))); 0508 connect(action, &QAction::triggered, this, [this, desktop]() { 0509 if (m_window) { 0510 workspace()->sendWindowToDesktops(m_window, {desktop}, false); 0511 } 0512 }); 0513 action->setCheckable(true); 0514 group->addAction(action); 0515 0516 if (m_window && !m_window->isOnAllDesktops() && m_window->isOnDesktop(desktop)) { 0517 action->setChecked(true); 0518 } 0519 } 0520 0521 m_desktopMenu->addSeparator(); 0522 0523 action = m_desktopMenu->addAction(i18nc("Create a new desktop and move the window there", "&New Desktop")); 0524 action->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); 0525 connect(action, &QAction::triggered, this, [this]() { 0526 if (!m_window) { 0527 return; 0528 } 0529 VirtualDesktopManager *vds = VirtualDesktopManager::self(); 0530 VirtualDesktop *desktop = vds->createVirtualDesktop(vds->count()); 0531 if (desktop) { 0532 workspace()->sendWindowToDesktops(m_window, {desktop}, false); 0533 } 0534 }); 0535 action->setEnabled(vds->count() < vds->maximum()); 0536 } 0537 0538 void UserActionsMenu::multipleDesktopsPopupAboutToShow() 0539 { 0540 if (!m_multipleDesktopsMenu) { 0541 return; 0542 } 0543 VirtualDesktopManager *vds = VirtualDesktopManager::self(); 0544 0545 m_multipleDesktopsMenu->clear(); 0546 if (m_window) { 0547 m_multipleDesktopsMenu->setPalette(m_window->palette()); 0548 } 0549 0550 QAction *action = m_multipleDesktopsMenu->addAction(i18n("&All Desktops")); 0551 connect(action, &QAction::triggered, this, [this]() { 0552 if (m_window) { 0553 m_window->setOnAllDesktops(!m_window->isOnAllDesktops()); 0554 } 0555 }); 0556 action->setCheckable(true); 0557 if (m_window && m_window->isOnAllDesktops()) { 0558 action->setChecked(true); 0559 } 0560 0561 m_multipleDesktopsMenu->addSeparator(); 0562 0563 const uint BASE = 10; 0564 0565 const auto desktops = vds->desktops(); 0566 for (VirtualDesktop *desktop : desktops) { 0567 const uint legacyId = desktop->x11DesktopNumber(); 0568 0569 QString basic_name(QStringLiteral("%1 %2")); 0570 if (legacyId < BASE) { 0571 basic_name.prepend(QLatin1Char('&')); 0572 } 0573 0574 QAction *action = m_multipleDesktopsMenu->addAction(basic_name.arg(legacyId).arg(desktop->name().replace(QLatin1Char('&'), QStringLiteral("&&")))); 0575 connect(action, &QAction::triggered, this, [this, desktop]() { 0576 if (m_window) { 0577 if (m_window->desktops().contains(desktop)) { 0578 m_window->leaveDesktop(desktop); 0579 } else { 0580 m_window->enterDesktop(desktop); 0581 } 0582 } 0583 }); 0584 action->setCheckable(true); 0585 if (m_window && !m_window->isOnAllDesktops() && m_window->isOnDesktop(desktop)) { 0586 action->setChecked(true); 0587 } 0588 } 0589 0590 m_multipleDesktopsMenu->addSeparator(); 0591 0592 for (VirtualDesktop *desktop : desktops) { 0593 const uint legacyId = desktop->x11DesktopNumber(); 0594 QString name = i18n("Move to %1 %2", legacyId, desktop->name()); 0595 QAction *action = m_multipleDesktopsMenu->addAction(name); 0596 connect(action, &QAction::triggered, this, [this, desktop]() { 0597 if (m_window) { 0598 m_window->setDesktops({desktop}); 0599 } 0600 }); 0601 } 0602 0603 m_multipleDesktopsMenu->addSeparator(); 0604 0605 bool allowNewDesktops = vds->count() < vds->maximum(); 0606 0607 action = m_multipleDesktopsMenu->addAction(i18nc("Create a new desktop and add the window to that desktop", "Add to &New Desktop")); 0608 connect(action, &QAction::triggered, this, [this, vds]() { 0609 if (!m_window) { 0610 return; 0611 } 0612 VirtualDesktop *desktop = vds->createVirtualDesktop(vds->count()); 0613 if (desktop) { 0614 m_window->enterDesktop(desktop); 0615 } 0616 }); 0617 action->setEnabled(allowNewDesktops); 0618 0619 action = m_multipleDesktopsMenu->addAction(i18nc("Create a new desktop and move the window to that desktop", "Move to New Desktop")); 0620 connect(action, &QAction::triggered, this, [this, vds]() { 0621 if (!m_window) { 0622 return; 0623 } 0624 VirtualDesktop *desktop = vds->createVirtualDesktop(vds->count()); 0625 if (desktop) { 0626 m_window->setDesktops({desktop}); 0627 } 0628 }); 0629 action->setEnabled(allowNewDesktops); 0630 } 0631 0632 void UserActionsMenu::screenPopupAboutToShow() 0633 { 0634 if (!m_screenMenu) { 0635 return; 0636 } 0637 m_screenMenu->clear(); 0638 0639 if (!m_window) { 0640 return; 0641 } 0642 m_screenMenu->setPalette(m_window->palette()); 0643 QActionGroup *group = new QActionGroup(m_screenMenu); 0644 0645 const auto outputs = workspace()->outputs(); 0646 for (int i = 0; i < outputs.count(); ++i) { 0647 Output *output = outputs[i]; 0648 // assumption: there are not more than 9 screens attached. 0649 QAction *action = m_screenMenu->addAction(i18nc("@item:inmenu List of all Screens to send a window to. First argument is a number, second the output identifier. E.g. Screen 1 (HDMI1)", 0650 "Screen &%1 (%2)", (i + 1), output->name())); 0651 connect(action, &QAction::triggered, this, [this, output]() { 0652 workspace()->sendWindowToOutput(m_window, output); 0653 }); 0654 action->setCheckable(true); 0655 if (m_window && output == m_window->output()) { 0656 action->setChecked(true); 0657 } 0658 group->addAction(action); 0659 } 0660 } 0661 0662 void UserActionsMenu::activityPopupAboutToShow() 0663 { 0664 if (!m_activityMenu) { 0665 return; 0666 } 0667 0668 #if KWIN_BUILD_ACTIVITIES 0669 if (!Workspace::self()->activities()) { 0670 return; 0671 } 0672 m_activityMenu->clear(); 0673 if (m_window) { 0674 m_activityMenu->setPalette(m_window->palette()); 0675 } 0676 QAction *action = m_activityMenu->addAction(i18n("&All Activities")); 0677 action->setCheckable(true); 0678 connect(action, &QAction::triggered, this, [this]() { 0679 if (m_window) { 0680 m_window->setOnAllActivities(!m_window->isOnAllActivities()); 0681 } 0682 }); 0683 static QPointer<QActionGroup> allActivitiesGroup; 0684 if (!allActivitiesGroup) { 0685 allActivitiesGroup = new QActionGroup(m_activityMenu); 0686 } 0687 allActivitiesGroup->addAction(action); 0688 0689 if (m_window && m_window->isOnAllActivities()) { 0690 action->setChecked(true); 0691 } 0692 m_activityMenu->addSeparator(); 0693 0694 const auto activities = Workspace::self()->activities()->running(); 0695 for (const QString &id : activities) { 0696 KActivities::Info activity(id); 0697 QString name = activity.name(); 0698 name.replace('&', "&&"); 0699 auto action = m_activityMenu->addAction(name); 0700 action->setCheckable(true); 0701 const QString icon = activity.icon(); 0702 if (!icon.isEmpty()) { 0703 action->setIcon(QIcon::fromTheme(icon)); 0704 } 0705 m_activityMenu->addAction(action); 0706 connect(action, &QAction::triggered, this, [this, id]() { 0707 if (m_window) { 0708 Workspace::self()->activities()->toggleWindowOnActivity(m_window, id, false); 0709 } 0710 }); 0711 0712 if (m_window && !m_window->isOnAllActivities() && m_window->isOnActivity(id)) { 0713 action->setChecked(true); 0714 } 0715 } 0716 0717 m_activityMenu->addSeparator(); 0718 for (const QString &id : activities) { 0719 const KActivities::Info activity(id); 0720 if (m_window->activities().size() == 1 && m_window->activities().front() == id) { 0721 // no need to show a button that doesn't do anything 0722 continue; 0723 } 0724 const QString name = i18n("Move to %1", activity.name().replace('&', "&&")); 0725 const auto action = m_activityMenu->addAction(name); 0726 if (const QString icon = activity.icon(); !icon.isEmpty()) { 0727 action->setIcon(QIcon::fromTheme(icon)); 0728 } 0729 connect(action, &QAction::triggered, this, [this, id] { 0730 m_window->setOnActivities({id}); 0731 }); 0732 m_activityMenu->addAction(action); 0733 } 0734 #endif 0735 } 0736 0737 void UserActionsMenu::slotWindowOperation(QAction *action) 0738 { 0739 if (!action->data().isValid()) { 0740 return; 0741 } 0742 0743 Options::WindowOperation op = static_cast<Options::WindowOperation>(action->data().toInt()); 0744 QPointer<Window> c = m_window ? m_window : QPointer<Window>(Workspace::self()->activeWindow()); 0745 if (c.isNull()) { 0746 return; 0747 } 0748 QString type; 0749 switch (op) { 0750 case Options::FullScreenOp: 0751 if (!c->isFullScreen() && c->isFullScreenable()) { 0752 type = QStringLiteral("fullscreenaltf3"); 0753 } 0754 break; 0755 case Options::NoBorderOp: 0756 if (!c->noBorder() && c->userCanSetNoBorder()) { 0757 type = QStringLiteral("noborderaltf3"); 0758 } 0759 break; 0760 default: 0761 break; 0762 } 0763 if (!type.isEmpty()) { 0764 helperDialog(type); 0765 } 0766 // need to delay performing the window operation as we need to have the 0767 // user actions menu closed before we destroy the decoration. Otherwise Qt crashes 0768 QMetaObject::invokeMethod( 0769 workspace(), [c, op]() { 0770 workspace()->performWindowOperation(c, op); 0771 }, 0772 Qt::QueuedConnection); 0773 } 0774 0775 //**************************************** 0776 // ShortcutDialog 0777 //**************************************** 0778 ShortcutDialog::ShortcutDialog(const QKeySequence &cut) 0779 : _shortcut(cut) 0780 { 0781 m_ui.setupUi(this); 0782 m_ui.keySequenceEdit->setKeySequence(cut); 0783 m_ui.warning->hide(); 0784 0785 // Listen to changed shortcuts 0786 connect(m_ui.keySequenceEdit, &QKeySequenceEdit::editingFinished, this, &ShortcutDialog::keySequenceChanged); 0787 connect(m_ui.clearButton, &QToolButton::clicked, this, [this] { 0788 _shortcut = QKeySequence(); 0789 }); 0790 m_ui.keySequenceEdit->setFocus(); 0791 0792 setWindowFlags(Qt::Popup | Qt::X11BypassWindowManagerHint); 0793 } 0794 0795 void ShortcutDialog::accept() 0796 { 0797 QKeySequence seq = shortcut(); 0798 if (!seq.isEmpty()) { 0799 if (seq[0] == QKeyCombination(Qt::Key_Escape)) { 0800 reject(); 0801 return; 0802 } 0803 if (seq[0] == QKeyCombination(Qt::Key_Space) || seq[0].keyboardModifiers() == Qt::NoModifier) { 0804 // clear 0805 m_ui.keySequenceEdit->clear(); 0806 QDialog::accept(); 0807 return; 0808 } 0809 } 0810 QDialog::accept(); 0811 } 0812 0813 void ShortcutDialog::done(int r) 0814 { 0815 QDialog::done(r); 0816 Q_EMIT dialogDone(r == Accepted); 0817 } 0818 0819 void ShortcutDialog::keySequenceChanged() 0820 { 0821 activateWindow(); // where is the kbd focus lost? cause of popup state? 0822 QKeySequence seq = m_ui.keySequenceEdit->keySequence(); 0823 if (_shortcut == seq) { 0824 return; // don't try to update the same 0825 } 0826 0827 if (seq.isEmpty()) { // clear 0828 _shortcut = seq; 0829 return; 0830 } 0831 if (seq.count() > 1) { 0832 seq = QKeySequence(seq[0]); 0833 m_ui.keySequenceEdit->setKeySequence(seq); 0834 } 0835 0836 // Check if the key sequence is used currently 0837 QString sc = seq.toString(); 0838 // NOTICE - seq.toString() & the entries in "conflicting" randomly get invalidated after the next call (if no sc has been set & conflicting isn't empty?!) 0839 QList<KGlobalShortcutInfo> conflicting = KGlobalAccel::globalShortcutsByKey(seq); 0840 if (!conflicting.isEmpty()) { 0841 const KGlobalShortcutInfo &conflict = conflicting.at(0); 0842 m_ui.warning->setText(i18nc("'%1' is a keyboard shortcut like 'ctrl+w'", 0843 "<b>%1</b> is already in use", sc)); 0844 m_ui.warning->setToolTip(i18nc("keyboard shortcut '%1' is used by action '%2' in application '%3'", 0845 "<b>%1</b> is used by %2 in %3", sc, conflict.friendlyName(), conflict.componentFriendlyName())); 0846 m_ui.warning->show(); 0847 m_ui.keySequenceEdit->setKeySequence(shortcut()); 0848 } else if (seq != _shortcut) { 0849 m_ui.warning->hide(); 0850 if (QPushButton *ok = m_ui.buttonBox->button(QDialogButtonBox::Ok)) { 0851 ok->setFocus(); 0852 } 0853 } 0854 0855 _shortcut = seq; 0856 } 0857 0858 QKeySequence ShortcutDialog::shortcut() const 0859 { 0860 return _shortcut; 0861 } 0862 0863 //**************************************** 0864 // Workspace 0865 //**************************************** 0866 0867 void Workspace::slotIncreaseWindowOpacity() 0868 { 0869 if (!m_activeWindow) { 0870 return; 0871 } 0872 m_activeWindow->setOpacity(std::min(m_activeWindow->opacity() + 0.05, 1.0)); 0873 } 0874 0875 void Workspace::slotLowerWindowOpacity() 0876 { 0877 if (!m_activeWindow) { 0878 return; 0879 } 0880 m_activeWindow->setOpacity(std::max(m_activeWindow->opacity() - 0.05, 0.05)); 0881 } 0882 0883 void Workspace::closeActivePopup() 0884 { 0885 if (active_popup) { 0886 active_popup->close(); 0887 active_popup = nullptr; 0888 m_activePopupWindow = nullptr; 0889 } 0890 m_userActionsMenu->close(); 0891 } 0892 0893 template<typename Slot> 0894 void Workspace::initShortcut(const QString &actionName, const QString &description, const QKeySequence &shortcut, Slot slot) 0895 { 0896 initShortcut(actionName, description, shortcut, this, slot); 0897 } 0898 0899 template<typename T, typename Slot> 0900 void Workspace::initShortcut(const QString &actionName, const QString &description, const QKeySequence &shortcut, T *receiver, Slot slot) 0901 { 0902 QAction *a = new QAction(this); 0903 a->setProperty("componentName", QStringLiteral("kwin")); 0904 a->setObjectName(actionName); 0905 a->setText(description); 0906 KGlobalAccel::self()->setDefaultShortcut(a, QList<QKeySequence>() << shortcut); 0907 KGlobalAccel::self()->setShortcut(a, QList<QKeySequence>() << shortcut); 0908 connect(a, &QAction::triggered, receiver, slot); 0909 } 0910 0911 /** 0912 * Creates the global accel object \c keys. 0913 */ 0914 void Workspace::initShortcuts() 0915 { 0916 // The first argument to initShortcut() is the id for the shortcut in the user's 0917 // config file, while the second argumewnt is its user-visible text. 0918 // Normally these should be identical, but if the user-visible text for a new 0919 // shortcut that you're adding is super long, it is permissible to use a shorter 0920 // string for name. 0921 0922 // PLEASE NOTE: Never change the ID of an existing shortcut! It will cause users' 0923 // custom shortcuts to be lost. Instead, only change the description 0924 0925 initShortcut("Window Operations Menu", i18n("Window Operations Menu"), 0926 Qt::ALT | Qt::Key_F3, &Workspace::slotWindowOperations); 0927 initShortcut("Window Close", i18n("Close Window"), 0928 Qt::ALT | Qt::Key_F4, &Workspace::slotWindowClose); 0929 initShortcut("Window Maximize", i18n("Maximize Window"), 0930 Qt::META | Qt::Key_PageUp, &Workspace::slotWindowMaximize); 0931 initShortcut("Window Maximize Vertical", i18n("Maximize Window Vertically"), 0932 0, &Workspace::slotWindowMaximizeVertical); 0933 initShortcut("Window Maximize Horizontal", i18n("Maximize Window Horizontally"), 0934 0, &Workspace::slotWindowMaximizeHorizontal); 0935 initShortcut("Window Minimize", i18n("Minimize Window"), 0936 Qt::META | Qt::Key_PageDown, &Workspace::slotWindowMinimize); 0937 initShortcut("Window Shade", i18n("Shade Window"), 0938 0, &Workspace::slotWindowShade); 0939 initShortcut("Window Move", i18n("Move Window"), 0940 0, &Workspace::slotWindowMove); 0941 initShortcut("Window Resize", i18n("Resize Window"), 0942 0, &Workspace::slotWindowResize); 0943 initShortcut("Window Raise", i18n("Raise Window"), 0944 0, &Workspace::slotWindowRaise); 0945 initShortcut("Window Lower", i18n("Lower Window"), 0946 0, &Workspace::slotWindowLower); 0947 initShortcut("Toggle Window Raise/Lower", i18n("Toggle Window Raise/Lower"), 0948 0, &Workspace::slotWindowRaiseOrLower); 0949 initShortcut("Window Fullscreen", i18n("Make Window Fullscreen"), 0950 0, &Workspace::slotWindowFullScreen); 0951 initShortcut("Window No Border", i18n("Toggle Window Titlebar and Frame"), 0952 0, &Workspace::slotWindowNoBorder); 0953 initShortcut("Window Above Other Windows", i18n("Keep Window Above Others"), 0954 0, &Workspace::slotWindowAbove); 0955 initShortcut("Window Below Other Windows", i18n("Keep Window Below Others"), 0956 0, &Workspace::slotWindowBelow); 0957 initShortcut("Activate Window Demanding Attention", i18n("Activate Window Demanding Attention"), 0958 Qt::META | Qt::CTRL | Qt::Key_A, &Workspace::slotActivateAttentionWindow); 0959 initShortcut("Setup Window Shortcut", i18n("Setup Window Shortcut"), 0960 0, &Workspace::slotSetupWindowShortcut); 0961 initShortcut("Window Move Center", i18n("Move Window to the Center"), 0, 0962 &Workspace::slotWindowCenter); 0963 initShortcut("Window Pack Right", i18n("Move Window Right"), 0964 0, &Workspace::slotWindowMoveRight); 0965 initShortcut("Window Pack Left", i18n("Move Window Left"), 0966 0, &Workspace::slotWindowMoveLeft); 0967 initShortcut("Window Pack Up", i18n("Move Window Up"), 0968 0, &Workspace::slotWindowMoveUp); 0969 initShortcut("Window Pack Down", i18n("Move Window Down"), 0970 0, &Workspace::slotWindowMoveDown); 0971 initShortcut("Window Grow Horizontal", i18n("Expand Window Horizontally"), 0972 0, &Workspace::slotWindowExpandHorizontal); 0973 initShortcut("Window Grow Vertical", i18n("Expand Window Vertically"), 0974 0, &Workspace::slotWindowExpandVertical); 0975 initShortcut("Window Shrink Horizontal", i18n("Shrink Window Horizontally"), 0976 0, &Workspace::slotWindowShrinkHorizontal); 0977 initShortcut("Window Shrink Vertical", i18n("Shrink Window Vertically"), 0978 0, &Workspace::slotWindowShrinkVertical); 0979 initShortcut("Window Quick Tile Left", i18n("Quick Tile Window to the Left"), 0980 Qt::META | Qt::Key_Left, std::bind(&Workspace::quickTileWindow, this, QuickTileFlag::Left)); 0981 initShortcut("Window Quick Tile Right", i18n("Quick Tile Window to the Right"), 0982 Qt::META | Qt::Key_Right, std::bind(&Workspace::quickTileWindow, this, QuickTileFlag::Right)); 0983 initShortcut("Window Quick Tile Top", i18n("Quick Tile Window to the Top"), 0984 Qt::META | Qt::Key_Up, std::bind(&Workspace::quickTileWindow, this, QuickTileFlag::Top)); 0985 initShortcut("Window Quick Tile Bottom", i18n("Quick Tile Window to the Bottom"), 0986 Qt::META | Qt::Key_Down, std::bind(&Workspace::quickTileWindow, this, QuickTileFlag::Bottom)); 0987 initShortcut("Window Quick Tile Top Left", i18n("Quick Tile Window to the Top Left"), 0988 0, std::bind(&Workspace::quickTileWindow, this, QuickTileFlag::Top | QuickTileFlag::Left)); 0989 initShortcut("Window Quick Tile Bottom Left", i18n("Quick Tile Window to the Bottom Left"), 0990 0, std::bind(&Workspace::quickTileWindow, this, QuickTileFlag::Bottom | QuickTileFlag::Left)); 0991 initShortcut("Window Quick Tile Top Right", i18n("Quick Tile Window to the Top Right"), 0992 0, std::bind(&Workspace::quickTileWindow, this, QuickTileFlag::Top | QuickTileFlag::Right)); 0993 initShortcut("Window Quick Tile Bottom Right", i18n("Quick Tile Window to the Bottom Right"), 0994 0, std::bind(&Workspace::quickTileWindow, this, QuickTileFlag::Bottom | QuickTileFlag::Right)); 0995 initShortcut("Switch Window Up", i18n("Switch to Window Above"), 0996 Qt::META | Qt::ALT | Qt::Key_Up, std::bind(static_cast<void (Workspace::*)(Direction)>(&Workspace::switchWindow), this, DirectionNorth)); 0997 initShortcut("Switch Window Down", i18n("Switch to Window Below"), 0998 Qt::META | Qt::ALT | Qt::Key_Down, std::bind(static_cast<void (Workspace::*)(Direction)>(&Workspace::switchWindow), this, DirectionSouth)); 0999 initShortcut("Switch Window Right", i18n("Switch to Window to the Right"), 1000 Qt::META | Qt::ALT | Qt::Key_Right, std::bind(static_cast<void (Workspace::*)(Direction)>(&Workspace::switchWindow), this, DirectionEast)); 1001 initShortcut("Switch Window Left", i18n("Switch to Window to the Left"), 1002 Qt::META | Qt::ALT | Qt::Key_Left, std::bind(static_cast<void (Workspace::*)(Direction)>(&Workspace::switchWindow), this, DirectionWest)); 1003 initShortcut("Increase Opacity", i18n("Increase Opacity of Active Window by 5%"), 1004 0, &Workspace::slotIncreaseWindowOpacity); 1005 initShortcut("Decrease Opacity", i18n("Decrease Opacity of Active Window by 5%"), 1006 0, &Workspace::slotLowerWindowOpacity); 1007 1008 initShortcut("Window On All Desktops", i18n("Keep Window on All Desktops"), 1009 0, &Workspace::slotWindowOnAllDesktops); 1010 1011 VirtualDesktopManager *vds = VirtualDesktopManager::self(); 1012 for (uint i = 0; i < vds->maximum(); ++i) { 1013 auto handler = [this, i]() { 1014 const QList<VirtualDesktop *> desktops = VirtualDesktopManager::self()->desktops(); 1015 if (i < uint(desktops.count())) { 1016 slotWindowToDesktop(desktops[i]); 1017 } 1018 }; 1019 initShortcut(QStringLiteral("Window to Desktop %1").arg(i + 1), i18n("Window to Desktop %1", i + 1), 0, handler); 1020 } 1021 initShortcut("Window to Next Desktop", i18n("Window to Next Desktop"), 0, &Workspace::slotWindowToNextDesktop); 1022 initShortcut("Window to Previous Desktop", i18n("Window to Previous Desktop"), 0, &Workspace::slotWindowToPreviousDesktop); 1023 initShortcut("Window One Desktop to the Right", i18n("Window One Desktop to the Right"), 1024 Qt::META | Qt::CTRL | Qt::SHIFT | Qt::Key_Right, &Workspace::slotWindowToDesktopRight); 1025 initShortcut("Window One Desktop to the Left", i18n("Window One Desktop to the Left"), 1026 Qt::META | Qt::CTRL | Qt::SHIFT | Qt::Key_Left, &Workspace::slotWindowToDesktopLeft); 1027 initShortcut("Window One Desktop Up", i18n("Window One Desktop Up"), 1028 Qt::META | Qt::CTRL | Qt::SHIFT | Qt::Key_Up, &Workspace::slotWindowToDesktopUp); 1029 initShortcut("Window One Desktop Down", i18n("Window One Desktop Down"), 1030 Qt::META | Qt::CTRL | Qt::SHIFT | Qt::Key_Down, &Workspace::slotWindowToDesktopDown); 1031 1032 for (int i = 0; i < 8; ++i) { 1033 initShortcut(QStringLiteral("Window to Screen %1").arg(i), i18n("Move Window to Screen %1", i), 0, [this, i]() { 1034 Output *output = outputs().value(i); 1035 if (output) { 1036 slotWindowToScreen(output); 1037 } 1038 }); 1039 } 1040 initShortcut("Window to Next Screen", i18n("Move Window to Next Screen"), 1041 Qt::META | Qt::SHIFT | Qt::Key_Right, &Workspace::slotWindowToNextScreen); 1042 initShortcut("Window to Previous Screen", i18n("Move Window to Previous Screen"), 1043 Qt::META | Qt::SHIFT | Qt::Key_Left, &Workspace::slotWindowToPrevScreen); 1044 initShortcut("Window One Screen to the Right", i18n("Move Window One Screen to the Right"), 1045 0, &Workspace::slotWindowToRightScreen); 1046 initShortcut("Window One Screen to the Left", i18n("Move Window One Screen to the Left"), 1047 0, &Workspace::slotWindowToLeftScreen); 1048 initShortcut("Window One Screen Up", i18n("Move Window One Screen Up"), 1049 0, &Workspace::slotWindowToAboveScreen); 1050 initShortcut("Window One Screen Down", i18n("Move Window One Screen Down"), 1051 0, &Workspace::slotWindowToBelowScreen); 1052 1053 for (int i = 0; i < 8; ++i) { 1054 initShortcut(QStringLiteral("Switch to Screen %1").arg(i), i18n("Switch to Screen %1", i), 0, [this, i]() { 1055 Output *output = outputs().value(i); 1056 if (output) { 1057 slotSwitchToScreen(output); 1058 } 1059 }); 1060 } 1061 initShortcut("Switch to Next Screen", i18n("Switch to Next Screen"), 0, &Workspace::slotSwitchToNextScreen); 1062 initShortcut("Switch to Previous Screen", i18n("Switch to Previous Screen"), 0, &Workspace::slotSwitchToPrevScreen); 1063 initShortcut("Switch to Screen to the Right", i18n("Switch to Screen to the Right"), 1064 0, &Workspace::slotSwitchToRightScreen); 1065 initShortcut("Switch to Screen to the Left", i18n("Switch to Screen to the Left"), 1066 0, &Workspace::slotSwitchToLeftScreen); 1067 initShortcut("Switch to Screen Above", i18n("Switch to Screen Above"), 1068 0, &Workspace::slotSwitchToAboveScreen); 1069 initShortcut("Switch to Screen Below", i18n("Switch to Screen Below"), 1070 0, &Workspace::slotSwitchToBelowScreen); 1071 1072 initShortcut("Show Desktop", i18n("Peek at Desktop"), 1073 Qt::META | Qt::Key_D, &Workspace::slotToggleShowDesktop); 1074 1075 initShortcut("Kill Window", i18n("Kill Window"), Qt::META | Qt::CTRL | Qt::Key_Escape, &Workspace::slotKillWindow); 1076 1077 #if KWIN_BUILD_TABBOX 1078 m_tabbox->initShortcuts(); 1079 #endif 1080 vds->initShortcuts(); 1081 m_userActionsMenu->discard(); // so that it's recreated next time 1082 } 1083 1084 void Workspace::setupWindowShortcut(Window *window) 1085 { 1086 Q_ASSERT(m_windowKeysDialog == nullptr); 1087 // TODO: PORT ME (KGlobalAccel related) 1088 // keys->setEnabled( false ); 1089 // disable_shortcuts_keys->setEnabled( false ); 1090 // client_keys->setEnabled( false ); 1091 m_windowKeysDialog = new ShortcutDialog(window->shortcut()); 1092 m_windowKeysWindow = window; 1093 connect(m_windowKeysDialog, &ShortcutDialog::dialogDone, this, &Workspace::setupWindowShortcutDone); 1094 QRect r = clientArea(ScreenArea, window).toRect(); 1095 QSize size = m_windowKeysDialog->sizeHint(); 1096 QPointF pos(window->frameGeometry().left() + window->frameMargins().left(), 1097 window->frameGeometry().top() + window->frameMargins().top()); 1098 if (pos.x() + size.width() >= r.right()) { 1099 pos.setX(r.right() - size.width()); 1100 } 1101 if (pos.y() + size.height() >= r.bottom()) { 1102 pos.setY(r.bottom() - size.height()); 1103 } 1104 m_windowKeysDialog->move(pos.toPoint()); 1105 m_windowKeysDialog->show(); 1106 active_popup = m_windowKeysDialog; 1107 m_activePopupWindow = window; 1108 } 1109 1110 void Workspace::setupWindowShortcutDone(bool ok) 1111 { 1112 // keys->setEnabled( true ); 1113 // disable_shortcuts_keys->setEnabled( true ); 1114 // client_keys->setEnabled( true ); 1115 if (ok) { 1116 m_windowKeysWindow->setShortcut(m_windowKeysDialog->shortcut().toString()); 1117 } 1118 closeActivePopup(); 1119 m_windowKeysDialog->deleteLater(); 1120 m_windowKeysDialog = nullptr; 1121 m_windowKeysWindow = nullptr; 1122 if (m_activeWindow) { 1123 m_activeWindow->takeFocus(); 1124 } 1125 } 1126 1127 void Workspace::windowShortcutUpdated(Window *window) 1128 { 1129 QString key = QStringLiteral("_k_session:%1").arg(window->internalId().toString()); 1130 QAction *action = findChild<QAction *>(key); 1131 if (!window->shortcut().isEmpty()) { 1132 if (action == nullptr) { // new shortcut 1133 action = new QAction(this); 1134 connect(window, &Window::closed, action, [action]() { 1135 KGlobalAccel::self()->removeAllShortcuts(action); 1136 delete action; 1137 }); 1138 1139 action->setProperty("componentName", QStringLiteral("kwin")); 1140 action->setObjectName(key); 1141 action->setText(i18n("Activate Window (%1)", window->caption())); 1142 connect(action, &QAction::triggered, window, std::bind(&Workspace::activateWindow, this, window, true)); 1143 } 1144 1145 // no autoloading, since it's configured explicitly here and is not meant to be reused 1146 // (the key is the window id anyway, which is kind of random) 1147 KGlobalAccel::self()->setShortcut(action, QList<QKeySequence>() << window->shortcut(), 1148 KGlobalAccel::NoAutoloading); 1149 action->setEnabled(true); 1150 } else { 1151 KGlobalAccel::self()->removeAllShortcuts(action); 1152 delete action; 1153 } 1154 } 1155 1156 void Workspace::performWindowOperation(Window *window, Options::WindowOperation op) 1157 { 1158 if (!window) { 1159 return; 1160 } 1161 if (op == Options::MoveOp || op == Options::UnrestrictedMoveOp) { 1162 Cursors::self()->mouse()->setPos(window->frameGeometry().center()); 1163 } 1164 if (op == Options::ResizeOp || op == Options::UnrestrictedResizeOp) { 1165 Cursors::self()->mouse()->setPos(window->frameGeometry().bottomRight()); 1166 } 1167 switch (op) { 1168 case Options::MoveOp: 1169 window->performMouseCommand(Options::MouseMove, Cursors::self()->mouse()->pos()); 1170 break; 1171 case Options::UnrestrictedMoveOp: 1172 window->performMouseCommand(Options::MouseUnrestrictedMove, Cursors::self()->mouse()->pos()); 1173 break; 1174 case Options::ResizeOp: 1175 window->performMouseCommand(Options::MouseResize, Cursors::self()->mouse()->pos()); 1176 break; 1177 case Options::UnrestrictedResizeOp: 1178 window->performMouseCommand(Options::MouseUnrestrictedResize, Cursors::self()->mouse()->pos()); 1179 break; 1180 case Options::CloseOp: 1181 QMetaObject::invokeMethod(window, &Window::closeWindow, Qt::QueuedConnection); 1182 break; 1183 case Options::MaximizeOp: 1184 window->maximize(window->maximizeMode() == MaximizeFull 1185 ? MaximizeRestore 1186 : MaximizeFull); 1187 takeActivity(window, ActivityFocus | ActivityRaise); 1188 break; 1189 case Options::HMaximizeOp: 1190 window->maximize(window->maximizeMode() ^ MaximizeHorizontal); 1191 takeActivity(window, ActivityFocus | ActivityRaise); 1192 break; 1193 case Options::VMaximizeOp: 1194 window->maximize(window->maximizeMode() ^ MaximizeVertical); 1195 takeActivity(window, ActivityFocus | ActivityRaise); 1196 break; 1197 case Options::RestoreOp: 1198 window->maximize(MaximizeRestore); 1199 takeActivity(window, ActivityFocus | ActivityRaise); 1200 break; 1201 case Options::MinimizeOp: 1202 window->setMinimized(true); 1203 break; 1204 case Options::ShadeOp: 1205 window->performMouseCommand(Options::MouseShade, Cursors::self()->mouse()->pos()); 1206 break; 1207 case Options::OnAllDesktopsOp: 1208 window->setOnAllDesktops(!window->isOnAllDesktops()); 1209 break; 1210 case Options::FullScreenOp: 1211 window->setFullScreen(!window->isFullScreen()); 1212 break; 1213 case Options::NoBorderOp: 1214 if (window->userCanSetNoBorder()) { 1215 window->setNoBorder(!window->noBorder()); 1216 } 1217 break; 1218 case Options::KeepAboveOp: { 1219 StackingUpdatesBlocker blocker(this); 1220 bool was = window->keepAbove(); 1221 window->setKeepAbove(!window->keepAbove()); 1222 if (was && !window->keepAbove()) { 1223 raiseWindow(window); 1224 } 1225 break; 1226 } 1227 case Options::KeepBelowOp: { 1228 StackingUpdatesBlocker blocker(this); 1229 bool was = window->keepBelow(); 1230 window->setKeepBelow(!window->keepBelow()); 1231 if (was && !window->keepBelow()) { 1232 lowerWindow(window); 1233 } 1234 break; 1235 } 1236 case Options::OperationsOp: 1237 window->performMouseCommand(Options::MouseShade, Cursors::self()->mouse()->pos()); 1238 break; 1239 case Options::WindowRulesOp: 1240 m_rulebook->edit(window, false); 1241 break; 1242 case Options::ApplicationRulesOp: 1243 m_rulebook->edit(window, true); 1244 break; 1245 case Options::SetupWindowShortcutOp: 1246 setupWindowShortcut(window); 1247 break; 1248 case Options::LowerOp: 1249 lowerWindow(window); 1250 break; 1251 case Options::NoOp: 1252 break; 1253 } 1254 } 1255 1256 void Workspace::slotActivateAttentionWindow() 1257 { 1258 if (attention_chain.count() > 0) { 1259 activateWindow(attention_chain.first()); 1260 } 1261 } 1262 1263 #define USABLE_ACTIVE_WINDOW (m_activeWindow && !(m_activeWindow->isDesktop() || m_activeWindow->isDock())) 1264 1265 void Workspace::slotWindowToDesktop(VirtualDesktop *desktop) 1266 { 1267 if (USABLE_ACTIVE_WINDOW) { 1268 sendWindowToDesktops(m_activeWindow, {desktop}, true); 1269 } 1270 } 1271 1272 static bool screenSwitchImpossible() 1273 { 1274 if (!options->activeMouseScreen()) { 1275 return false; 1276 } 1277 QStringList args; 1278 args << QStringLiteral("--passivepopup") << i18n("The window manager is configured to consider the screen with the mouse on it as active one.\n" 1279 "Therefore it is not possible to switch to a screen explicitly.") 1280 << QStringLiteral("20"); 1281 KProcess::startDetached(QStringLiteral("kdialog"), args); 1282 return true; 1283 } 1284 1285 void Workspace::slotSwitchToScreen(Output *output) 1286 { 1287 if (!screenSwitchImpossible()) { 1288 switchToOutput(output); 1289 } 1290 } 1291 1292 void Workspace::slotSwitchToLeftScreen() 1293 { 1294 if (!screenSwitchImpossible()) { 1295 switchToOutput(findOutput(activeOutput(), Direction::DirectionWest, true)); 1296 } 1297 } 1298 1299 void Workspace::slotSwitchToRightScreen() 1300 { 1301 if (!screenSwitchImpossible()) { 1302 switchToOutput(findOutput(activeOutput(), Direction::DirectionEast, true)); 1303 } 1304 } 1305 1306 void Workspace::slotSwitchToAboveScreen() 1307 { 1308 if (!screenSwitchImpossible()) { 1309 switchToOutput(findOutput(activeOutput(), Direction::DirectionNorth, true)); 1310 } 1311 } 1312 1313 void Workspace::slotSwitchToBelowScreen() 1314 { 1315 if (!screenSwitchImpossible()) { 1316 switchToOutput(findOutput(activeOutput(), Direction::DirectionSouth, true)); 1317 } 1318 } 1319 1320 void Workspace::slotSwitchToPrevScreen() 1321 { 1322 if (!screenSwitchImpossible()) { 1323 switchToOutput(findOutput(activeOutput(), Direction::DirectionPrev, true)); 1324 } 1325 } 1326 1327 void Workspace::slotSwitchToNextScreen() 1328 { 1329 if (!screenSwitchImpossible()) { 1330 switchToOutput(findOutput(activeOutput(), Direction::DirectionNext, true)); 1331 } 1332 } 1333 1334 void Workspace::slotWindowToScreen(Output *output) 1335 { 1336 if (USABLE_ACTIVE_WINDOW) { 1337 sendWindowToOutput(m_activeWindow, output); 1338 } 1339 } 1340 1341 void Workspace::slotWindowToLeftScreen() 1342 { 1343 if (USABLE_ACTIVE_WINDOW) { 1344 sendWindowToOutput(m_activeWindow, findOutput(m_activeWindow->output(), Direction::DirectionWest, true)); 1345 } 1346 } 1347 1348 void Workspace::slotWindowToRightScreen() 1349 { 1350 if (USABLE_ACTIVE_WINDOW) { 1351 sendWindowToOutput(m_activeWindow, findOutput(m_activeWindow->output(), Direction::DirectionEast, true)); 1352 } 1353 } 1354 1355 void Workspace::slotWindowToAboveScreen() 1356 { 1357 if (USABLE_ACTIVE_WINDOW) { 1358 sendWindowToOutput(m_activeWindow, findOutput(m_activeWindow->output(), Direction::DirectionNorth, true)); 1359 } 1360 } 1361 1362 void Workspace::slotWindowToBelowScreen() 1363 { 1364 if (USABLE_ACTIVE_WINDOW) { 1365 sendWindowToOutput(m_activeWindow, findOutput(m_activeWindow->output(), Direction::DirectionSouth, true)); 1366 } 1367 } 1368 1369 void Workspace::slotWindowToPrevScreen() 1370 { 1371 if (USABLE_ACTIVE_WINDOW) { 1372 sendWindowToOutput(m_activeWindow, findOutput(m_activeWindow->output(), Direction::DirectionPrev, true)); 1373 } 1374 } 1375 1376 void Workspace::slotWindowToNextScreen() 1377 { 1378 if (USABLE_ACTIVE_WINDOW) { 1379 sendWindowToOutput(m_activeWindow, findOutput(m_activeWindow->output(), Direction::DirectionNext, true)); 1380 } 1381 } 1382 1383 /** 1384 * Maximizes the active window. 1385 */ 1386 void Workspace::slotWindowMaximize() 1387 { 1388 if (USABLE_ACTIVE_WINDOW) { 1389 performWindowOperation(m_activeWindow, Options::MaximizeOp); 1390 } 1391 } 1392 1393 /** 1394 * Maximizes the active window vertically. 1395 */ 1396 void Workspace::slotWindowMaximizeVertical() 1397 { 1398 if (USABLE_ACTIVE_WINDOW) { 1399 performWindowOperation(m_activeWindow, Options::VMaximizeOp); 1400 } 1401 } 1402 1403 /** 1404 * Maximizes the active window horiozontally. 1405 */ 1406 void Workspace::slotWindowMaximizeHorizontal() 1407 { 1408 if (USABLE_ACTIVE_WINDOW) { 1409 performWindowOperation(m_activeWindow, Options::HMaximizeOp); 1410 } 1411 } 1412 1413 /** 1414 * Minimizes the active window. 1415 */ 1416 void Workspace::slotWindowMinimize() 1417 { 1418 if (USABLE_ACTIVE_WINDOW) { 1419 performWindowOperation(m_activeWindow, Options::MinimizeOp); 1420 } 1421 } 1422 1423 /** 1424 * Shades/unshades the active window respectively. 1425 */ 1426 void Workspace::slotWindowShade() 1427 { 1428 if (USABLE_ACTIVE_WINDOW) { 1429 performWindowOperation(m_activeWindow, Options::ShadeOp); 1430 } 1431 } 1432 1433 /** 1434 * Raises the active window. 1435 */ 1436 void Workspace::slotWindowRaise() 1437 { 1438 if (USABLE_ACTIVE_WINDOW) { 1439 raiseWindow(m_activeWindow); 1440 } 1441 } 1442 1443 /** 1444 * Lowers the active window. 1445 */ 1446 void Workspace::slotWindowLower() 1447 { 1448 if (USABLE_ACTIVE_WINDOW) { 1449 lowerWindow(m_activeWindow); 1450 // As this most likely makes the window no longer visible change the 1451 // keyboard focus to the next available window. 1452 // activateNextWindow( c ); // Doesn't work when we lower a child window 1453 if (m_activeWindow->isActive() && options->focusPolicyIsReasonable()) { 1454 if (options->isNextFocusPrefersMouse()) { 1455 Window *next = windowUnderMouse(m_activeWindow->output()); 1456 if (next && next != m_activeWindow) { 1457 requestFocus(next, false); 1458 } 1459 } else { 1460 activateWindow(topWindowOnDesktop(VirtualDesktopManager::self()->currentDesktop())); 1461 } 1462 } 1463 } 1464 } 1465 1466 /** 1467 * Does a toggle-raise-and-lower on the active window. 1468 */ 1469 void Workspace::slotWindowRaiseOrLower() 1470 { 1471 if (USABLE_ACTIVE_WINDOW) { 1472 raiseOrLowerWindow(m_activeWindow); 1473 } 1474 } 1475 1476 void Workspace::slotWindowOnAllDesktops() 1477 { 1478 if (USABLE_ACTIVE_WINDOW) { 1479 m_activeWindow->setOnAllDesktops(!m_activeWindow->isOnAllDesktops()); 1480 } 1481 } 1482 1483 void Workspace::slotWindowFullScreen() 1484 { 1485 if (USABLE_ACTIVE_WINDOW) { 1486 performWindowOperation(m_activeWindow, Options::FullScreenOp); 1487 } 1488 } 1489 1490 void Workspace::slotWindowNoBorder() 1491 { 1492 if (USABLE_ACTIVE_WINDOW) { 1493 performWindowOperation(m_activeWindow, Options::NoBorderOp); 1494 } 1495 } 1496 1497 void Workspace::slotWindowAbove() 1498 { 1499 if (USABLE_ACTIVE_WINDOW) { 1500 performWindowOperation(m_activeWindow, Options::KeepAboveOp); 1501 } 1502 } 1503 1504 void Workspace::slotWindowBelow() 1505 { 1506 if (USABLE_ACTIVE_WINDOW) { 1507 performWindowOperation(m_activeWindow, Options::KeepBelowOp); 1508 } 1509 } 1510 void Workspace::slotSetupWindowShortcut() 1511 { 1512 if (USABLE_ACTIVE_WINDOW) { 1513 performWindowOperation(m_activeWindow, Options::SetupWindowShortcutOp); 1514 } 1515 } 1516 1517 /** 1518 * Toggles show desktop. 1519 */ 1520 void Workspace::slotToggleShowDesktop() 1521 { 1522 setShowingDesktop(!showingDesktop()); 1523 } 1524 1525 void windowToDesktop(Window *window, VirtualDesktopManager::Direction direction) 1526 { 1527 VirtualDesktopManager *vds = VirtualDesktopManager::self(); 1528 Workspace *ws = Workspace::self(); 1529 // TODO: why is options->isRollOverDesktops() not honored? 1530 const auto desktop = vds->inDirection(nullptr, direction, true); 1531 if (window && !window->isDesktop() 1532 && !window->isDock()) { 1533 ws->setMoveResizeWindow(window); 1534 vds->setCurrent(desktop); 1535 ws->setMoveResizeWindow(nullptr); 1536 } 1537 } 1538 1539 /** 1540 * Moves the active window to the next desktop. 1541 */ 1542 void Workspace::slotWindowToNextDesktop() 1543 { 1544 if (USABLE_ACTIVE_WINDOW) { 1545 windowToNextDesktop(m_activeWindow); 1546 } 1547 } 1548 1549 void Workspace::windowToNextDesktop(Window *window) 1550 { 1551 windowToDesktop(window, VirtualDesktopManager::Direction::Next); 1552 } 1553 1554 /** 1555 * Moves the active window to the previous desktop. 1556 */ 1557 void Workspace::slotWindowToPreviousDesktop() 1558 { 1559 if (USABLE_ACTIVE_WINDOW) { 1560 windowToPreviousDesktop(m_activeWindow); 1561 } 1562 } 1563 1564 void Workspace::windowToPreviousDesktop(Window *window) 1565 { 1566 windowToDesktop(window, VirtualDesktopManager::Direction::Previous); 1567 } 1568 1569 void activeWindowToDesktop(VirtualDesktopManager::Direction direction) 1570 { 1571 VirtualDesktopManager *vds = VirtualDesktopManager::self(); 1572 Workspace *ws = Workspace::self(); 1573 VirtualDesktop *current = vds->currentDesktop(); 1574 VirtualDesktop *newCurrent = VirtualDesktopManager::self()->inDirection(current, direction, options->isRollOverDesktops()); 1575 if (newCurrent == current) { 1576 return; 1577 } 1578 ws->setMoveResizeWindow(ws->activeWindow()); 1579 vds->setCurrent(newCurrent); 1580 ws->setMoveResizeWindow(nullptr); 1581 } 1582 1583 void Workspace::slotWindowToDesktopRight() 1584 { 1585 if (USABLE_ACTIVE_WINDOW) { 1586 activeWindowToDesktop(VirtualDesktopManager::Direction::Right); 1587 } 1588 } 1589 1590 void Workspace::slotWindowToDesktopLeft() 1591 { 1592 if (USABLE_ACTIVE_WINDOW) { 1593 activeWindowToDesktop(VirtualDesktopManager::Direction::Left); 1594 } 1595 } 1596 1597 void Workspace::slotWindowToDesktopUp() 1598 { 1599 if (USABLE_ACTIVE_WINDOW) { 1600 activeWindowToDesktop(VirtualDesktopManager::Direction::Up); 1601 } 1602 } 1603 1604 void Workspace::slotWindowToDesktopDown() 1605 { 1606 if (USABLE_ACTIVE_WINDOW) { 1607 activeWindowToDesktop(VirtualDesktopManager::Direction::Down); 1608 } 1609 } 1610 1611 /** 1612 * Kill Window feature, similar to xkill. 1613 */ 1614 void Workspace::slotKillWindow() 1615 { 1616 if (!m_windowKiller) { 1617 m_windowKiller = std::make_unique<KillWindow>(); 1618 } 1619 m_windowKiller->start(); 1620 } 1621 1622 /** 1623 * Switches to the nearest window in given direction. 1624 */ 1625 void Workspace::switchWindow(Direction direction) 1626 { 1627 if (!m_activeWindow) { 1628 return; 1629 } 1630 Window *window = m_activeWindow; 1631 VirtualDesktop *desktop = VirtualDesktopManager::self()->currentDesktop(); 1632 1633 // Centre of the active window 1634 QPoint curPos(window->x() + window->width() / 2, window->y() + window->height() / 2); 1635 1636 if (!switchWindow(window, direction, curPos, desktop)) { 1637 auto opposite = [&] { 1638 switch (direction) { 1639 case DirectionNorth: 1640 return QPoint(curPos.x(), geometry().height()); 1641 case DirectionSouth: 1642 return QPoint(curPos.x(), 0); 1643 case DirectionEast: 1644 return QPoint(0, curPos.y()); 1645 case DirectionWest: 1646 return QPoint(geometry().width(), curPos.y()); 1647 default: 1648 Q_UNREACHABLE(); 1649 } 1650 }; 1651 1652 switchWindow(window, direction, opposite(), desktop); 1653 } 1654 } 1655 1656 bool Workspace::switchWindow(Window *window, Direction direction, QPoint curPos, VirtualDesktop *desktop) 1657 { 1658 Window *switchTo = nullptr; 1659 int bestScore = 0; 1660 1661 QList<Window *> clist = stackingOrder(); 1662 for (auto i = clist.rbegin(); i != clist.rend(); ++i) { 1663 auto other = *i; 1664 if (!other->isClient()) { 1665 continue; 1666 } 1667 if (other->wantsTabFocus() && *i != window && other->isOnDesktop(desktop) && !other->isMinimized() && (*i)->isOnCurrentActivity()) { 1668 // Centre of the other window 1669 const QPoint otherCenter(other->x() + other->width() / 2, other->y() + other->height() / 2); 1670 1671 int distance; 1672 int offset; 1673 switch (direction) { 1674 case DirectionNorth: 1675 distance = curPos.y() - otherCenter.y(); 1676 offset = std::abs(otherCenter.x() - curPos.x()); 1677 break; 1678 case DirectionEast: 1679 distance = otherCenter.x() - curPos.x(); 1680 offset = std::abs(otherCenter.y() - curPos.y()); 1681 break; 1682 case DirectionSouth: 1683 distance = otherCenter.y() - curPos.y(); 1684 offset = std::abs(otherCenter.x() - curPos.x()); 1685 break; 1686 case DirectionWest: 1687 distance = curPos.x() - otherCenter.x(); 1688 offset = std::abs(otherCenter.y() - curPos.y()); 1689 break; 1690 default: 1691 distance = -1; 1692 offset = -1; 1693 } 1694 1695 if (distance > 0) { 1696 // Inverse score 1697 int score = distance + offset + ((offset * offset) / distance); 1698 if (score < bestScore || !switchTo) { 1699 switchTo = other; 1700 bestScore = score; 1701 } 1702 } 1703 } 1704 } 1705 if (switchTo) { 1706 activateWindow(switchTo); 1707 } 1708 1709 return switchTo; 1710 } 1711 1712 /** 1713 * Shows the window operations popup menu for the active window. 1714 */ 1715 void Workspace::slotWindowOperations() 1716 { 1717 if (!m_activeWindow) { 1718 return; 1719 } 1720 const QPoint pos(m_activeWindow->frameGeometry().left() + m_activeWindow->frameMargins().left(), 1721 m_activeWindow->frameGeometry().top() + m_activeWindow->frameMargins().top()); 1722 showWindowMenu(QRect(pos, pos), m_activeWindow); 1723 } 1724 1725 void Workspace::showWindowMenu(const QRect &pos, Window *window) 1726 { 1727 m_userActionsMenu->show(pos, window); 1728 } 1729 1730 void Workspace::showApplicationMenu(const QRect &pos, Window *window, int actionId) 1731 { 1732 Workspace::self()->applicationMenu()->showApplicationMenu(window->pos().toPoint() + pos.bottomLeft(), window, actionId); 1733 } 1734 1735 /** 1736 * Closes the active window. 1737 */ 1738 void Workspace::slotWindowClose() 1739 { 1740 // TODO: why? 1741 // if ( tab_box->isVisible()) 1742 // return; 1743 if (USABLE_ACTIVE_WINDOW) { 1744 performWindowOperation(m_activeWindow, Options::CloseOp); 1745 } 1746 } 1747 1748 /** 1749 * Starts keyboard move mode for the active window. 1750 */ 1751 void Workspace::slotWindowMove() 1752 { 1753 if (USABLE_ACTIVE_WINDOW) { 1754 performWindowOperation(m_activeWindow, Options::UnrestrictedMoveOp); 1755 } 1756 } 1757 1758 /** 1759 * Starts keyboard resize mode for the active window. 1760 */ 1761 void Workspace::slotWindowResize() 1762 { 1763 if (USABLE_ACTIVE_WINDOW) { 1764 performWindowOperation(m_activeWindow, Options::UnrestrictedResizeOp); 1765 } 1766 } 1767 1768 #undef USABLE_ACTIVE_WINDOW 1769 1770 void Window::setShortcut(const QString &_cut) 1771 { 1772 QString cut = rules()->checkShortcut(_cut); 1773 auto updateShortcut = [this](const QKeySequence &cut = QKeySequence()) { 1774 if (_shortcut == cut) { 1775 return; 1776 } 1777 _shortcut = cut; 1778 setShortcutInternal(); 1779 }; 1780 if (cut.isEmpty()) { 1781 updateShortcut(); 1782 return; 1783 } 1784 if (cut == shortcut().toString()) { 1785 return; // no change 1786 } 1787 // Format: 1788 // base+(abcdef)<space>base+(abcdef) 1789 // E.g. Alt+Ctrl+(ABCDEF);Meta+X,Meta+(ABCDEF) 1790 if (!cut.contains(QLatin1Char('(')) && !cut.contains(QLatin1Char(')')) && !cut.contains(QLatin1String(" - "))) { 1791 if (workspace()->shortcutAvailable(cut, this)) { 1792 updateShortcut(QKeySequence(cut)); 1793 } else { 1794 updateShortcut(); 1795 } 1796 return; 1797 } 1798 static const QRegularExpression reg(QStringLiteral("(.*\\+)\\((.*)\\)")); 1799 QList<QKeySequence> keys; 1800 const QStringList groups = cut.split(QStringLiteral(" - ")); 1801 for (auto it = groups.begin(); it != groups.end(); ++it) { 1802 const QRegularExpressionMatch match = reg.match(*it); 1803 if (match.hasMatch()) { 1804 const QString base = match.captured(1); 1805 const QString list = match.captured(2); 1806 for (int i = 0; i < list.length(); ++i) { 1807 QKeySequence c(base + list[i]); 1808 if (!c.isEmpty()) { 1809 keys.append(c); 1810 } 1811 } 1812 } else { 1813 // regexp doesn't match, so it should be a normal shortcut 1814 QKeySequence c(*it); 1815 if (!c.isEmpty()) { 1816 keys.append(c); 1817 } 1818 } 1819 } 1820 for (auto it = keys.constBegin(); it != keys.cend(); ++it) { 1821 if (_shortcut == *it) { // current one is in the list 1822 return; 1823 } 1824 } 1825 for (auto it = keys.cbegin(); it != keys.cend(); ++it) { 1826 if (workspace()->shortcutAvailable(*it, this)) { 1827 updateShortcut(*it); 1828 return; 1829 } 1830 } 1831 updateShortcut(); 1832 } 1833 1834 void Window::setShortcutInternal() 1835 { 1836 updateCaption(); 1837 workspace()->windowShortcutUpdated(this); 1838 } 1839 1840 void X11Window::setShortcutInternal() 1841 { 1842 updateCaption(); 1843 #if 0 1844 workspace()->windowShortcutUpdated(this); 1845 #else 1846 // Workaround for kwin<->kglobalaccel deadlock, when KWin has X grab and the kded 1847 // kglobalaccel module tries to create the key grab. KWin should preferably grab 1848 // they keys itself anyway :(. 1849 QTimer::singleShot(0, this, std::bind(&Workspace::windowShortcutUpdated, workspace(), this)); 1850 #endif 1851 } 1852 1853 bool Workspace::shortcutAvailable(const QKeySequence &cut, Window *ignore) const 1854 { 1855 if (ignore && cut == ignore->shortcut()) { 1856 return true; 1857 } 1858 1859 // Check if the shortcut is already registered 1860 const QList<KGlobalShortcutInfo> registeredShortcuts = KGlobalAccel::globalShortcutsByKey(cut); 1861 for (const auto &shortcut : registeredShortcuts) { 1862 // Only return "not available" if is not a window activation shortcut, as it may be no longer valid 1863 if (!shortcut.uniqueName().startsWith(QStringLiteral("_k_session:"))) { 1864 return false; 1865 } 1866 } 1867 // Check now conflicts with activation shortcuts for current windows 1868 for (const auto window : std::as_const(m_windows)) { 1869 if (window != ignore && window->shortcut() == cut) { 1870 return false; 1871 } 1872 } 1873 return true; 1874 } 1875 1876 } // namespace 1877 1878 #include "moc_useractions.cpp"