KWin
Loading...
Searching...
No Matches
useractions.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 1999, 2000 Matthias Ettrich <ettrich@kde.org>
6 SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
7 SPDX-FileCopyrightText: 2022 Natalie Clarius <natalie_clarius@yahoo.de>
8
9 SPDX-License-Identifier: GPL-2.0-or-later
10*/
11
12/*
13
14 This file contains things relevant to direct user actions, such as
15 responses to global keyboard shortcuts, or selecting actions
16 from the window operations menu.
17
18*/
19
21// NOTE: if you change the menu, keep
22// plasma-desktop/applets/taskmanager/package/contents/ui/ContextMenu.qml
23// in sync
25
26#include <config-kwin.h>
27
28#include "core/output.h"
29#include "cursor.h"
30#include "input.h"
31#include "options.h"
32#include "scripting/scripting.h"
33#include "useractions.h"
34#include "virtualdesktops.h"
35#include "workspace.h"
36#include "x11window.h"
37
38#if KWIN_BUILD_ACTIVITIES
39#include "activities.h"
40#include <PlasmaActivities/Info>
41#endif
42#include "appmenu.h"
43
44#include <KProcess>
45
46#include <QAction>
47#include <QCheckBox>
48#include <QPushButton>
49
50#include <KGlobalAccel>
51#include <KLazyLocalizedString>
52#include <KLocalizedString>
53#include <QAction>
54#include <QActionGroup>
55#include <QMenu>
56#include <QRegularExpression>
57#include <kauthorized.h>
58#include <kconfig.h>
59
60#include "killwindow.h"
61#if KWIN_BUILD_TABBOX
62#include "tabbox/tabbox.h"
63#endif
64
65namespace KWin
66{
67
69 : QObject(parent)
70 , m_menu(nullptr)
71 , m_desktopMenu(nullptr)
72 , m_multipleDesktopsMenu(nullptr)
73 , m_screenMenu(nullptr)
74 , m_activityMenu(nullptr)
75 , m_scriptsMenu(nullptr)
76 , m_resizeOperation(nullptr)
77 , m_moveOperation(nullptr)
78 , m_maximizeOperation(nullptr)
79 , m_shadeOperation(nullptr)
80 , m_keepAboveOperation(nullptr)
81 , m_keepBelowOperation(nullptr)
82 , m_fullScreenOperation(nullptr)
83 , m_noBorderOperation(nullptr)
84 , m_minimizeOperation(nullptr)
85 , m_closeOperation(nullptr)
86 , m_shortcutOperation(nullptr)
87{
88}
89
94
96{
97 return m_menu && m_menu->isVisible();
98}
99
101{
102 return m_window && isShown();
103}
104
106{
107 if (!m_menu) {
108 return;
109 }
110 m_menu->close();
111}
112
113bool UserActionsMenu::isMenuWindow(const Window *window) const
114{
115 return window && window == m_window;
116}
117
118void UserActionsMenu::show(const QRect &pos, Window *window)
119{
120 Q_ASSERT(window);
121 QPointer<Window> windowPtr(window);
122 // Presumably window will never be nullptr,
123 // but play it safe and make sure not to crash.
124 if (windowPtr.isNull()) {
125 return;
126 }
127 if (isShown()) { // recursion
128 return;
129 }
130 if (windowPtr->isDesktop() || windowPtr->isDock()) {
131 return;
132 }
133 if (!KAuthorized::authorizeAction(QStringLiteral("kwin_rmb"))) {
134 return;
135 }
136 m_window = windowPtr;
137 init();
138 m_menu->popup(pos.bottomLeft());
139}
140
142{
143 m_menu->windowHandle()->setMouseGrabEnabled(true);
144 m_menu->windowHandle()->setKeyboardGrabEnabled(true);
145}
146
147void UserActionsMenu::helperDialog(const QString &message)
148{
149 QStringList args;
150 QString type;
151 auto shortcut = [](const QString &name) {
152 QAction *action = Workspace::self()->findChild<QAction *>(name);
153 Q_ASSERT(action != nullptr);
154 const auto shortcuts = KGlobalAccel::self()->shortcut(action);
155 return QStringLiteral("%1 (%2)").arg(action->text(), shortcuts.isEmpty() ? QString() : shortcuts.first().toString(QKeySequence::NativeText));
156 };
157 if (message == QStringLiteral("noborderaltf3")) {
158 args << QStringLiteral("--msgbox") << i18n("You have selected to show a window without its border.\n"
159 "Without the border, you will not be able to enable the border "
160 "again using the mouse: use the window operations menu instead, "
161 "activated using the %1 keyboard shortcut.",
162 shortcut(QStringLiteral("Window Operations Menu")));
163 type = QStringLiteral("altf3warning");
164 } else if (message == QLatin1String("fullscreenaltf3")) {
165 args << QStringLiteral("--msgbox") << i18n("You have selected to show a window in fullscreen mode.\n"
166 "If the application itself does not have an option to turn the fullscreen "
167 "mode off you will not be able to disable it "
168 "again using the mouse: use the window operations menu instead, "
169 "activated using the %1 keyboard shortcut.",
170 shortcut(QStringLiteral("Window Operations Menu")));
171 type = QStringLiteral("altf3warning");
172 } else {
173 Q_UNREACHABLE();
174 }
175 if (!type.isEmpty()) {
176 KConfig cfg(QStringLiteral("kwin_dialogsrc"));
177 KConfigGroup cg(&cfg, QStringLiteral("Notification Messages")); // Depends on KMessageBox
178 if (!cg.readEntry(type, true)) {
179 return;
180 }
181 args << QStringLiteral("--dontagain") << QLatin1String("kwin_dialogsrc:") + type;
182 }
183 KProcess::startDetached(QStringLiteral("kdialog"), args);
184}
185
186void UserActionsMenu::init()
187{
188 if (m_menu) {
189 return;
190 }
191 m_menu = new QMenu;
192 connect(m_menu, &QMenu::aboutToShow, this, &UserActionsMenu::menuAboutToShow);
193
194 // the toplevel menu gets closed before a submenu's action is invoked
195 connect(m_menu, &QMenu::aboutToHide, this, &UserActionsMenu::menuAboutToHide, Qt::QueuedConnection);
196 connect(m_menu, &QMenu::triggered, this, &UserActionsMenu::slotWindowOperation);
197
198 QMenu *advancedMenu = new QMenu(m_menu);
199 connect(advancedMenu, &QMenu::aboutToShow, this, [this, advancedMenu]() {
200 if (m_window) {
201 advancedMenu->setPalette(m_window->palette());
202 }
203 });
204
205 auto setShortcut = [](QAction *action, const QString &actionName) {
206 const auto shortcuts = KGlobalAccel::self()->shortcut(Workspace::self()->findChild<QAction *>(actionName));
207 if (!shortcuts.isEmpty()) {
208 action->setShortcut(shortcuts.first());
209 }
210 };
211
212 m_moveOperation = advancedMenu->addAction(i18n("&Move"));
213 m_moveOperation->setIcon(QIcon::fromTheme(QStringLiteral("transform-move")));
214 setShortcut(m_moveOperation, QStringLiteral("Window Move"));
215 m_moveOperation->setData(Options::UnrestrictedMoveOp);
216
217 m_resizeOperation = advancedMenu->addAction(i18n("&Resize"));
218 m_resizeOperation->setIcon(QIcon::fromTheme(QStringLiteral("transform-scale")));
219 setShortcut(m_resizeOperation, QStringLiteral("Window Resize"));
220 m_resizeOperation->setData(Options::ResizeOp);
221
222 m_keepAboveOperation = advancedMenu->addAction(i18n("Keep &Above Others"));
223 m_keepAboveOperation->setIcon(QIcon::fromTheme(QStringLiteral("window-keep-above")));
224 setShortcut(m_keepAboveOperation, QStringLiteral("Window Above Other Windows"));
225 m_keepAboveOperation->setCheckable(true);
226 m_keepAboveOperation->setData(Options::KeepAboveOp);
227
228 m_keepBelowOperation = advancedMenu->addAction(i18n("Keep &Below Others"));
229 m_keepBelowOperation->setIcon(QIcon::fromTheme(QStringLiteral("window-keep-below")));
230 setShortcut(m_keepBelowOperation, QStringLiteral("Window Below Other Windows"));
231 m_keepBelowOperation->setCheckable(true);
232 m_keepBelowOperation->setData(Options::KeepBelowOp);
233
234 m_fullScreenOperation = advancedMenu->addAction(i18n("&Fullscreen"));
235 m_fullScreenOperation->setIcon(QIcon::fromTheme(QStringLiteral("view-fullscreen")));
236 setShortcut(m_fullScreenOperation, QStringLiteral("Window Fullscreen"));
237 m_fullScreenOperation->setCheckable(true);
238 m_fullScreenOperation->setData(Options::FullScreenOp);
239
240 m_shadeOperation = advancedMenu->addAction(i18n("&Shade"));
241 m_shadeOperation->setIcon(QIcon::fromTheme(QStringLiteral("window-shade")));
242 setShortcut(m_shadeOperation, QStringLiteral("Window Shade"));
243 m_shadeOperation->setCheckable(true);
244 m_shadeOperation->setData(Options::ShadeOp);
245
246 m_noBorderOperation = advancedMenu->addAction(i18n("&No Titlebar and Frame"));
247 m_noBorderOperation->setIcon(QIcon::fromTheme(QStringLiteral("edit-none-border")));
248 setShortcut(m_noBorderOperation, QStringLiteral("Window No Border"));
249 m_noBorderOperation->setCheckable(true);
250 m_noBorderOperation->setData(Options::NoBorderOp);
251
252 advancedMenu->addSeparator();
253
254 m_shortcutOperation = advancedMenu->addAction(i18n("Set Window Short&cut…"));
255 m_shortcutOperation->setIcon(QIcon::fromTheme(QStringLiteral("configure-shortcuts")));
256 setShortcut(m_shortcutOperation, QStringLiteral("Setup Window Shortcut"));
257 m_shortcutOperation->setData(Options::SetupWindowShortcutOp);
258
259#if KWIN_BUILD_KCMS
260 QAction *action = advancedMenu->addAction(i18n("Configure Special &Window Settings…"));
261 action->setIcon(QIcon::fromTheme(QStringLiteral("preferences-system-windows-actions")));
262 action->setData(Options::WindowRulesOp);
263 m_rulesOperation = action;
264
265 action = advancedMenu->addAction(i18n("Configure S&pecial Application Settings…"));
266 action->setIcon(QIcon::fromTheme(QStringLiteral("preferences-system-windows-actions")));
267 action->setData(Options::ApplicationRulesOp);
268 m_applicationRulesOperation = action;
269#endif
270
271 m_maximizeOperation = m_menu->addAction(i18n("Ma&ximize"));
272 m_maximizeOperation->setIcon(QIcon::fromTheme(QStringLiteral("window-maximize")));
273 setShortcut(m_maximizeOperation, QStringLiteral("Window Maximize"));
274 m_maximizeOperation->setCheckable(true);
275 m_maximizeOperation->setData(Options::MaximizeOp);
276
277 m_minimizeOperation = m_menu->addAction(i18n("Mi&nimize"));
278 m_minimizeOperation->setIcon(QIcon::fromTheme(QStringLiteral("window-minimize")));
279 setShortcut(m_minimizeOperation, QStringLiteral("Window Minimize"));
280 m_minimizeOperation->setData(Options::MinimizeOp);
281
282 QAction *overflowAction = m_menu->addMenu(advancedMenu);
283 overflowAction->setText(i18n("&More Actions"));
284 overflowAction->setIcon(QIcon::fromTheme(QStringLiteral("overflow-menu")));
285
286 m_menu->addSeparator();
287
288 m_closeOperation = m_menu->addAction(i18n("&Close"));
289 m_closeOperation->setIcon(QIcon::fromTheme(QStringLiteral("window-close")));
290 setShortcut(m_closeOperation, QStringLiteral("Window Close"));
291 m_closeOperation->setData(Options::CloseOp);
292}
293
295{
296 delete m_menu;
297 m_menu = nullptr;
298 m_desktopMenu = nullptr;
299 m_multipleDesktopsMenu = nullptr;
300 m_screenMenu = nullptr;
301 m_activityMenu = nullptr;
302 m_scriptsMenu = nullptr;
303}
304
305void UserActionsMenu::menuAboutToShow()
306{
307 if (m_window.isNull() || !m_menu) {
308 return;
309 }
310
311 m_window->blockActivityUpdates(true);
312
313 if (VirtualDesktopManager::self()->count() == 1) {
314 delete m_desktopMenu;
315 m_desktopMenu = nullptr;
316 delete m_multipleDesktopsMenu;
317 m_multipleDesktopsMenu = nullptr;
318 } else {
319 initDesktopPopup();
320 }
321 if (workspace()->outputs().count() == 1 || (!m_window->isMovable() && !m_window->isMovableAcrossScreens())) {
322 delete m_screenMenu;
323 m_screenMenu = nullptr;
324 } else {
325 initScreenPopup();
326 }
327
328 m_menu->setPalette(m_window->palette());
329 m_resizeOperation->setEnabled(m_window->isResizable());
330 m_moveOperation->setEnabled(m_window->isMovableAcrossScreens());
331 m_maximizeOperation->setEnabled(m_window->isMaximizable());
332 m_maximizeOperation->setChecked(m_window->maximizeMode() == MaximizeFull);
333 m_shadeOperation->setEnabled(m_window->isShadeable());
334 m_shadeOperation->setChecked(m_window->shadeMode() != ShadeNone);
335 m_keepAboveOperation->setChecked(m_window->keepAbove());
336 m_keepBelowOperation->setChecked(m_window->keepBelow());
337 m_fullScreenOperation->setEnabled(m_window->isFullScreenable());
338 m_fullScreenOperation->setChecked(m_window->isFullScreen());
339 m_noBorderOperation->setEnabled(m_window->userCanSetNoBorder());
340 m_noBorderOperation->setChecked(m_window->noBorder());
341 m_minimizeOperation->setEnabled(m_window->isMinimizable());
342 m_closeOperation->setEnabled(m_window->isCloseable());
343 m_shortcutOperation->setEnabled(m_window->rules()->checkShortcut(QString()).isNull());
344
345 // drop the existing scripts menu
346 delete m_scriptsMenu;
347 m_scriptsMenu = nullptr;
348 // ask scripts whether they want to add entries for the given window
349 QList<QAction *> scriptActions = Scripting::self()->actionsForUserActionMenu(m_window.data(), m_scriptsMenu);
350 if (!scriptActions.isEmpty()) {
351 m_scriptsMenu = new QMenu(m_menu);
352 m_scriptsMenu->setPalette(m_window->palette());
353 m_scriptsMenu->addActions(scriptActions);
354
355 QAction *action = m_scriptsMenu->menuAction();
356 // set it as the first item after desktop
357 m_menu->insertAction(m_closeOperation, action);
358 action->setText(i18n("&Extensions"));
359 }
360
361 if (m_rulesOperation) {
362 m_rulesOperation->setEnabled(m_window->supportsWindowRules());
363 }
364 if (m_applicationRulesOperation) {
365 m_applicationRulesOperation->setEnabled(m_window->supportsWindowRules());
366 }
367
369}
370
371void UserActionsMenu::menuAboutToHide()
372{
373 if (m_window) {
374 m_window->blockActivityUpdates(false);
375 m_window.clear();
376 }
377}
378
380{
381#if KWIN_BUILD_ACTIVITIES
382 if (!Workspace::self()->activities()) {
383 return;
384 }
385 const QStringList &openActivities_ = Workspace::self()->activities()->running();
386 qCDebug(KWIN_CORE) << "activities:" << openActivities_.size();
387 if (openActivities_.size() < 2) {
388 delete m_activityMenu;
389 m_activityMenu = nullptr;
390 } else {
391 initActivityPopup();
392 }
393#endif
394}
395
396void UserActionsMenu::initDesktopPopup()
397{
398 if (kwinApp()->operationMode() == Application::OperationModeWaylandOnly || kwinApp()->operationMode() == Application::OperationModeXwayland) {
399 if (m_multipleDesktopsMenu) {
400 return;
401 }
402
403 m_multipleDesktopsMenu = new QMenu(m_menu);
404 connect(m_multipleDesktopsMenu, &QMenu::aboutToShow, this, &UserActionsMenu::multipleDesktopsPopupAboutToShow);
405
406 QAction *action = m_multipleDesktopsMenu->menuAction();
407 // set it as the first item
408 m_menu->insertAction(m_maximizeOperation, action);
409 action->setText(i18n("&Desktops"));
410 action->setIcon(QIcon::fromTheme(QStringLiteral("virtual-desktops")));
411
412 } else {
413 if (m_desktopMenu) {
414 return;
415 }
416
417 m_desktopMenu = new QMenu(m_menu);
418 connect(m_desktopMenu, &QMenu::aboutToShow, this, &UserActionsMenu::desktopPopupAboutToShow);
419
420 QAction *action = m_desktopMenu->menuAction();
421 // set it as the first item
422 m_menu->insertAction(m_maximizeOperation, action);
423 action->setText(i18n("Move to &Desktop"));
424 action->setIcon(QIcon::fromTheme(QStringLiteral("virtual-desktops")));
425 }
426}
427
428void UserActionsMenu::initScreenPopup()
429{
430 if (m_screenMenu) {
431 return;
432 }
433
434 m_screenMenu = new QMenu(m_menu);
435 connect(m_screenMenu, &QMenu::aboutToShow, this, &UserActionsMenu::screenPopupAboutToShow);
436
437 QAction *action = m_screenMenu->menuAction();
438 // set it as the first item after desktop
439 m_menu->insertAction(m_activityMenu ? m_activityMenu->menuAction() : m_minimizeOperation, action);
440 action->setText(i18n("Move to &Screen"));
441 action->setIcon(QIcon::fromTheme(QStringLiteral("computer")));
442}
443
444void UserActionsMenu::initActivityPopup()
445{
446 if (m_activityMenu) {
447 return;
448 }
449
450 m_activityMenu = new QMenu(m_menu);
451 connect(m_activityMenu, &QMenu::aboutToShow, this, &UserActionsMenu::activityPopupAboutToShow);
452
453 QAction *action = m_activityMenu->menuAction();
454 // set it as the first item
455 m_menu->insertAction(m_maximizeOperation, action);
456 action->setText(i18n("Show in &Activities"));
457 action->setIcon(QIcon::fromTheme(QStringLiteral("activities")));
458}
459
460void UserActionsMenu::desktopPopupAboutToShow()
461{
462 if (!m_desktopMenu) {
463 return;
464 }
465 const VirtualDesktopManager *vds = VirtualDesktopManager::self();
466
467 m_desktopMenu->clear();
468 if (m_window) {
469 m_desktopMenu->setPalette(m_window->palette());
470 }
471 QActionGroup *group = new QActionGroup(m_desktopMenu);
472
473 QAction *action = m_desktopMenu->addAction(i18n("Move &To Current Desktop"));
474 action->setEnabled(m_window && (m_window->isOnAllDesktops() || !m_window->isOnDesktop(vds->currentDesktop())));
475 connect(action, &QAction::triggered, this, [this]() {
476 if (!m_window) {
477 return;
478 }
479 VirtualDesktopManager *vds = VirtualDesktopManager::self();
480 workspace()->sendWindowToDesktops(m_window, {vds->currentDesktop()}, false);
481 });
482
483 action = m_desktopMenu->addAction(i18n("&All Desktops"));
484 connect(action, &QAction::triggered, this, [this]() {
485 if (m_window) {
486 m_window->setOnAllDesktops(!m_window->isOnAllDesktops());
487 }
488 });
489 action->setCheckable(true);
490 if (m_window && m_window->isOnAllDesktops()) {
491 action->setChecked(true);
492 }
493 group->addAction(action);
494
495 m_desktopMenu->addSeparator();
496
497 const uint BASE = 10;
498
499 const auto desktops = vds->desktops();
500 for (VirtualDesktop *desktop : desktops) {
501 const uint legacyId = desktop->x11DesktopNumber();
502
503 QString basic_name(QStringLiteral("%1 %2"));
504 if (legacyId < BASE) {
505 basic_name.prepend(QLatin1Char('&'));
506 }
507 action = m_desktopMenu->addAction(basic_name.arg(legacyId).arg(desktop->name().replace(QLatin1Char('&'), QStringLiteral("&&"))));
508 connect(action, &QAction::triggered, this, [this, desktop]() {
509 if (m_window) {
510 workspace()->sendWindowToDesktops(m_window, {desktop}, false);
511 }
512 });
513 action->setCheckable(true);
514 group->addAction(action);
515
516 if (m_window && !m_window->isOnAllDesktops() && m_window->isOnDesktop(desktop)) {
517 action->setChecked(true);
518 }
519 }
520
521 m_desktopMenu->addSeparator();
522
523 action = m_desktopMenu->addAction(i18nc("Create a new desktop and move the window there", "&New Desktop"));
524 action->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
525 connect(action, &QAction::triggered, this, [this]() {
526 if (!m_window) {
527 return;
528 }
529 VirtualDesktopManager *vds = VirtualDesktopManager::self();
530 VirtualDesktop *desktop = vds->createVirtualDesktop(vds->count());
531 if (desktop) {
532 workspace()->sendWindowToDesktops(m_window, {desktop}, false);
533 }
534 });
535 action->setEnabled(vds->count() < vds->maximum());
536}
537
538void UserActionsMenu::multipleDesktopsPopupAboutToShow()
539{
540 if (!m_multipleDesktopsMenu) {
541 return;
542 }
543 VirtualDesktopManager *vds = VirtualDesktopManager::self();
544
545 m_multipleDesktopsMenu->clear();
546 if (m_window) {
547 m_multipleDesktopsMenu->setPalette(m_window->palette());
548 }
549
550 QAction *action = m_multipleDesktopsMenu->addAction(i18n("&All Desktops"));
551 connect(action, &QAction::triggered, this, [this]() {
552 if (m_window) {
553 m_window->setOnAllDesktops(!m_window->isOnAllDesktops());
554 }
555 });
556 action->setCheckable(true);
557 if (m_window && m_window->isOnAllDesktops()) {
558 action->setChecked(true);
559 }
560
561 m_multipleDesktopsMenu->addSeparator();
562
563 const uint BASE = 10;
564
565 const auto desktops = vds->desktops();
566 for (VirtualDesktop *desktop : desktops) {
567 const uint legacyId = desktop->x11DesktopNumber();
568
569 QString basic_name(QStringLiteral("%1 %2"));
570 if (legacyId < BASE) {
571 basic_name.prepend(QLatin1Char('&'));
572 }
573
574 QAction *action = m_multipleDesktopsMenu->addAction(basic_name.arg(legacyId).arg(desktop->name().replace(QLatin1Char('&'), QStringLiteral("&&"))));
575 connect(action, &QAction::triggered, this, [this, desktop]() {
576 if (m_window) {
577 if (m_window->desktops().contains(desktop)) {
578 m_window->leaveDesktop(desktop);
579 } else {
580 m_window->enterDesktop(desktop);
581 }
582 }
583 });
584 action->setCheckable(true);
585 if (m_window && !m_window->isOnAllDesktops() && m_window->isOnDesktop(desktop)) {
586 action->setChecked(true);
587 }
588 }
589
590 m_multipleDesktopsMenu->addSeparator();
591
592 for (VirtualDesktop *desktop : desktops) {
593 const uint legacyId = desktop->x11DesktopNumber();
594 QString name = i18n("Move to %1 %2", legacyId, desktop->name());
595 QAction *action = m_multipleDesktopsMenu->addAction(name);
596 connect(action, &QAction::triggered, this, [this, desktop]() {
597 if (m_window) {
598 m_window->setDesktops({desktop});
599 }
600 });
601 }
602
603 m_multipleDesktopsMenu->addSeparator();
604
605 bool allowNewDesktops = vds->count() < vds->maximum();
606
607 action = m_multipleDesktopsMenu->addAction(i18nc("Create a new desktop and add the window to that desktop", "Add to &New Desktop"));
608 connect(action, &QAction::triggered, this, [this, vds]() {
609 if (!m_window) {
610 return;
611 }
612 VirtualDesktop *desktop = vds->createVirtualDesktop(vds->count());
613 if (desktop) {
614 m_window->enterDesktop(desktop);
615 }
616 });
617 action->setEnabled(allowNewDesktops);
618
619 action = m_multipleDesktopsMenu->addAction(i18nc("Create a new desktop and move the window to that desktop", "Move to New Desktop"));
620 connect(action, &QAction::triggered, this, [this, vds]() {
621 if (!m_window) {
622 return;
623 }
624 VirtualDesktop *desktop = vds->createVirtualDesktop(vds->count());
625 if (desktop) {
626 m_window->setDesktops({desktop});
627 }
628 });
629 action->setEnabled(allowNewDesktops);
630}
631
632void UserActionsMenu::screenPopupAboutToShow()
633{
634 if (!m_screenMenu) {
635 return;
636 }
637 m_screenMenu->clear();
638
639 if (!m_window) {
640 return;
641 }
642 m_screenMenu->setPalette(m_window->palette());
643 QActionGroup *group = new QActionGroup(m_screenMenu);
644
645 const auto outputs = workspace()->outputs();
646 for (int i = 0; i < outputs.count(); ++i) {
647 Output *output = outputs[i];
648 // assumption: there are not more than 9 screens attached.
649 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)",
650 "Screen &%1 (%2)", (i + 1), output->name()));
651 connect(action, &QAction::triggered, this, [this, output]() {
652 workspace()->sendWindowToOutput(m_window, output);
653 });
654 action->setCheckable(true);
655 if (m_window && output == m_window->output()) {
656 action->setChecked(true);
657 }
658 group->addAction(action);
659 }
660}
661
662void UserActionsMenu::activityPopupAboutToShow()
663{
664 if (!m_activityMenu) {
665 return;
666 }
667
668#if KWIN_BUILD_ACTIVITIES
669 if (!Workspace::self()->activities()) {
670 return;
671 }
672 m_activityMenu->clear();
673 if (m_window) {
674 m_activityMenu->setPalette(m_window->palette());
675 }
676 QAction *action = m_activityMenu->addAction(i18n("&All Activities"));
677 action->setCheckable(true);
678 connect(action, &QAction::triggered, this, [this]() {
679 if (m_window) {
680 m_window->setOnAllActivities(!m_window->isOnAllActivities());
681 }
682 });
683 static QPointer<QActionGroup> allActivitiesGroup;
684 if (!allActivitiesGroup) {
685 allActivitiesGroup = new QActionGroup(m_activityMenu);
686 }
687 allActivitiesGroup->addAction(action);
688
689 if (m_window && m_window->isOnAllActivities()) {
690 action->setChecked(true);
691 }
692 m_activityMenu->addSeparator();
693
694 const auto activities = Workspace::self()->activities()->running();
695 for (const QString &id : activities) {
696 KActivities::Info activity(id);
697 QString name = activity.name();
698 name.replace('&', "&&");
699 auto action = m_activityMenu->addAction(name);
700 action->setCheckable(true);
701 const QString icon = activity.icon();
702 if (!icon.isEmpty()) {
703 action->setIcon(QIcon::fromTheme(icon));
704 }
705 m_activityMenu->addAction(action);
706 connect(action, &QAction::triggered, this, [this, id]() {
707 if (m_window) {
708 Workspace::self()->activities()->toggleWindowOnActivity(m_window, id, false);
709 }
710 });
711
712 if (m_window && !m_window->isOnAllActivities() && m_window->isOnActivity(id)) {
713 action->setChecked(true);
714 }
715 }
716
717 m_activityMenu->addSeparator();
718 for (const QString &id : activities) {
719 const KActivities::Info activity(id);
720 if (m_window->activities().size() == 1 && m_window->activities().front() == id) {
721 // no need to show a button that doesn't do anything
722 continue;
723 }
724 const QString name = i18n("Move to %1", activity.name().replace('&', "&&"));
725 const auto action = m_activityMenu->addAction(name);
726 if (const QString icon = activity.icon(); !icon.isEmpty()) {
727 action->setIcon(QIcon::fromTheme(icon));
728 }
729 connect(action, &QAction::triggered, this, [this, id] {
730 m_window->setOnActivities({id});
731 });
732 m_activityMenu->addAction(action);
733 }
734#endif
735}
736
737void UserActionsMenu::slotWindowOperation(QAction *action)
738{
739 if (!action->data().isValid()) {
740 return;
741 }
742
743 Options::WindowOperation op = static_cast<Options::WindowOperation>(action->data().toInt());
744 QPointer<Window> c = m_window ? m_window : QPointer<Window>(Workspace::self()->activeWindow());
745 if (c.isNull()) {
746 return;
747 }
748 QString type;
749 switch (op) {
751 if (!c->isFullScreen() && c->isFullScreenable()) {
752 type = QStringLiteral("fullscreenaltf3");
753 }
754 break;
756 if (!c->noBorder() && c->userCanSetNoBorder()) {
757 type = QStringLiteral("noborderaltf3");
758 }
759 break;
760 default:
761 break;
762 }
763 if (!type.isEmpty()) {
764 helperDialog(type);
765 }
766 // need to delay performing the window operation as we need to have the
767 // user actions menu closed before we destroy the decoration. Otherwise Qt crashes
768 QMetaObject::invokeMethod(
769 workspace(), [c, op]() {
771 },
772 Qt::QueuedConnection);
773}
774
775//****************************************
776// ShortcutDialog
777//****************************************
778ShortcutDialog::ShortcutDialog(const QKeySequence &cut)
779 : _shortcut(cut)
780{
781 m_ui.setupUi(this);
782 m_ui.keySequenceEdit->setKeySequence(cut);
783 m_ui.warning->hide();
784
785 // Listen to changed shortcuts
786 connect(m_ui.keySequenceEdit, &QKeySequenceEdit::editingFinished, this, &ShortcutDialog::keySequenceChanged);
787 connect(m_ui.clearButton, &QToolButton::clicked, this, [this] {
788 _shortcut = QKeySequence();
789 });
790 m_ui.keySequenceEdit->setFocus();
791
792 setWindowFlags(Qt::Popup | Qt::X11BypassWindowManagerHint);
793}
794
796{
797 QKeySequence seq = shortcut();
798 if (!seq.isEmpty()) {
799 if (seq[0] == QKeyCombination(Qt::Key_Escape)) {
800 reject();
801 return;
802 }
803 if (seq[0] == QKeyCombination(Qt::Key_Space) || seq[0].keyboardModifiers() == Qt::NoModifier) {
804 // clear
805 m_ui.keySequenceEdit->clear();
806 QDialog::accept();
807 return;
808 }
809 }
810 QDialog::accept();
811}
812
814{
815 QDialog::done(r);
816 Q_EMIT dialogDone(r == Accepted);
817}
818
820{
821 activateWindow(); // where is the kbd focus lost? cause of popup state?
822 QKeySequence seq = m_ui.keySequenceEdit->keySequence();
823 if (_shortcut == seq) {
824 return; // don't try to update the same
825 }
826
827 if (seq.isEmpty()) { // clear
828 _shortcut = seq;
829 return;
830 }
831 if (seq.count() > 1) {
832 seq = QKeySequence(seq[0]);
833 m_ui.keySequenceEdit->setKeySequence(seq);
834 }
835
836 // Check if the key sequence is used currently
837 QString sc = seq.toString();
838 // NOTICE - seq.toString() & the entries in "conflicting" randomly get invalidated after the next call (if no sc has been set & conflicting isn't empty?!)
839 QList<KGlobalShortcutInfo> conflicting = KGlobalAccel::globalShortcutsByKey(seq);
840 if (!conflicting.isEmpty()) {
841 const KGlobalShortcutInfo &conflict = conflicting.at(0);
842 m_ui.warning->setText(i18nc("'%1' is a keyboard shortcut like 'ctrl+w'",
843 "<b>%1</b> is already in use", sc));
844 m_ui.warning->setToolTip(i18nc("keyboard shortcut '%1' is used by action '%2' in application '%3'",
845 "<b>%1</b> is used by %2 in %3", sc, conflict.friendlyName(), conflict.componentFriendlyName()));
846 m_ui.warning->show();
847 m_ui.keySequenceEdit->setKeySequence(shortcut());
848 } else if (seq != _shortcut) {
849 m_ui.warning->hide();
850 if (QPushButton *ok = m_ui.buttonBox->button(QDialogButtonBox::Ok)) {
851 ok->setFocus();
852 }
853 }
854
855 _shortcut = seq;
856}
857
858QKeySequence ShortcutDialog::shortcut() const
859{
860 return _shortcut;
861}
862
863//****************************************
864// Workspace
865//****************************************
866
868{
869 if (!m_activeWindow) {
870 return;
871 }
872 m_activeWindow->setOpacity(std::min(m_activeWindow->opacity() + 0.05, 1.0));
873}
874
876{
877 if (!m_activeWindow) {
878 return;
879 }
880 m_activeWindow->setOpacity(std::max(m_activeWindow->opacity() - 0.05, 0.05));
881}
882
883void Workspace::closeActivePopup()
884{
885 if (active_popup) {
886 active_popup->close();
887 active_popup = nullptr;
888 m_activePopupWindow = nullptr;
889 }
890 m_userActionsMenu->close();
891}
892
893template<typename Slot>
894void Workspace::initShortcut(const QString &actionName, const QString &description, const QKeySequence &shortcut, Slot slot)
895{
896 initShortcut(actionName, description, shortcut, this, slot);
897}
898
899template<typename T, typename Slot>
900void Workspace::initShortcut(const QString &actionName, const QString &description, const QKeySequence &shortcut, T *receiver, Slot slot)
901{
902 QAction *a = new QAction(this);
903 a->setProperty("componentName", QStringLiteral("kwin"));
904 a->setObjectName(actionName);
905 a->setText(description);
906 KGlobalAccel::self()->setDefaultShortcut(a, QList<QKeySequence>() << shortcut);
907 KGlobalAccel::self()->setShortcut(a, QList<QKeySequence>() << shortcut);
908 connect(a, &QAction::triggered, receiver, slot);
909}
910
914void Workspace::initShortcuts()
915{
916 // The first argument to initShortcut() is the id for the shortcut in the user's
917 // config file, while the second argumewnt is its user-visible text.
918 // Normally these should be identical, but if the user-visible text for a new
919 // shortcut that you're adding is super long, it is permissible to use a shorter
920 // string for name.
921
922 // PLEASE NOTE: Never change the ID of an existing shortcut! It will cause users'
923 // custom shortcuts to be lost. Instead, only change the description
924
925 initShortcut("Window Operations Menu", i18n("Window Operations Menu"),
926 Qt::ALT | Qt::Key_F3, &Workspace::slotWindowOperations);
927 initShortcut("Window Close", i18n("Close Window"),
928 Qt::ALT | Qt::Key_F4, &Workspace::slotWindowClose);
929 initShortcut("Window Maximize", i18n("Maximize Window"),
930 Qt::META | Qt::Key_PageUp, &Workspace::slotWindowMaximize);
931 initShortcut("Window Maximize Vertical", i18n("Maximize Window Vertically"),
933 initShortcut("Window Maximize Horizontal", i18n("Maximize Window Horizontally"),
935 initShortcut("Window Minimize", i18n("Minimize Window"),
936 Qt::META | Qt::Key_PageDown, &Workspace::slotWindowMinimize);
937 initShortcut("Window Shade", i18n("Shade Window"),
939 initShortcut("Window Move", i18n("Move Window"),
941 initShortcut("Window Resize", i18n("Resize Window"),
943 initShortcut("Window Raise", i18n("Raise Window"),
945 initShortcut("Window Lower", i18n("Lower Window"),
947 initShortcut("Toggle Window Raise/Lower", i18n("Toggle Window Raise/Lower"),
949 initShortcut("Window Fullscreen", i18n("Make Window Fullscreen"),
951 initShortcut("Window No Border", i18n("Toggle Window Titlebar and Frame"),
953 initShortcut("Window Above Other Windows", i18n("Keep Window Above Others"),
955 initShortcut("Window Below Other Windows", i18n("Keep Window Below Others"),
957 initShortcut("Activate Window Demanding Attention", i18n("Activate Window Demanding Attention"),
958 Qt::META | Qt::CTRL | Qt::Key_A, &Workspace::slotActivateAttentionWindow);
959 initShortcut("Setup Window Shortcut", i18n("Setup Window Shortcut"),
961 initShortcut("Window Move Center", i18n("Move Window to the Center"), 0,
963 initShortcut("Window Pack Right", i18n("Move Window Right"),
965 initShortcut("Window Pack Left", i18n("Move Window Left"),
967 initShortcut("Window Pack Up", i18n("Move Window Up"),
969 initShortcut("Window Pack Down", i18n("Move Window Down"),
971 initShortcut("Window Grow Horizontal", i18n("Expand Window Horizontally"),
973 initShortcut("Window Grow Vertical", i18n("Expand Window Vertically"),
975 initShortcut("Window Shrink Horizontal", i18n("Shrink Window Horizontally"),
977 initShortcut("Window Shrink Vertical", i18n("Shrink Window Vertically"),
979 initShortcut("Window Quick Tile Left", i18n("Quick Tile Window to the Left"),
980 Qt::META | Qt::Key_Left, std::bind(&Workspace::quickTileWindow, this, QuickTileFlag::Left));
981 initShortcut("Window Quick Tile Right", i18n("Quick Tile Window to the Right"),
982 Qt::META | Qt::Key_Right, std::bind(&Workspace::quickTileWindow, this, QuickTileFlag::Right));
983 initShortcut("Window Quick Tile Top", i18n("Quick Tile Window to the Top"),
984 Qt::META | Qt::Key_Up, std::bind(&Workspace::quickTileWindow, this, QuickTileFlag::Top));
985 initShortcut("Window Quick Tile Bottom", i18n("Quick Tile Window to the Bottom"),
986 Qt::META | Qt::Key_Down, std::bind(&Workspace::quickTileWindow, this, QuickTileFlag::Bottom));
987 initShortcut("Window Quick Tile Top Left", i18n("Quick Tile Window to the Top Left"),
989 initShortcut("Window Quick Tile Bottom Left", i18n("Quick Tile Window to the Bottom Left"),
991 initShortcut("Window Quick Tile Top Right", i18n("Quick Tile Window to the Top Right"),
993 initShortcut("Window Quick Tile Bottom Right", i18n("Quick Tile Window to the Bottom Right"),
995 initShortcut("Switch Window Up", i18n("Switch to Window Above"),
996 Qt::META | Qt::ALT | Qt::Key_Up, std::bind(static_cast<void (Workspace::*)(Direction)>(&Workspace::switchWindow), this, DirectionNorth));
997 initShortcut("Switch Window Down", i18n("Switch to Window Below"),
998 Qt::META | Qt::ALT | Qt::Key_Down, std::bind(static_cast<void (Workspace::*)(Direction)>(&Workspace::switchWindow), this, DirectionSouth));
999 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%"),
1005 initShortcut("Decrease Opacity", i18n("Decrease Opacity of Active Window by 5%"),
1007
1008 initShortcut("Window On All Desktops", i18n("Keep Window on All Desktops"),
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"),
1046 initShortcut("Window One Screen to the Left", i18n("Move Window One Screen to the Left"),
1048 initShortcut("Window One Screen Up", i18n("Move Window One Screen Up"),
1050 initShortcut("Window One Screen Down", i18n("Move Window One Screen Down"),
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"),
1065 initShortcut("Switch to Screen to the Left", i18n("Switch to Screen to the Left"),
1067 initShortcut("Switch to Screen Above", i18n("Switch to Screen Above"),
1069 initShortcut("Switch to Screen Below", i18n("Switch to Screen Below"),
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
1084void 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
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
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
1157{
1158 if (!window) {
1159 return;
1160 }
1161 if (op == Options::MoveOp || op == Options::UnrestrictedMoveOp) {
1162 Cursors::self()->mouse()->setPos(window->frameGeometry().center());
1163 }
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;
1173 break;
1174 case Options::ResizeOp:
1175 window->performMouseCommand(Options::MouseResize, Cursors::self()->mouse()->pos());
1176 break;
1179 break;
1180 case Options::CloseOp:
1181 QMetaObject::invokeMethod(window, &Window::closeWindow, Qt::QueuedConnection);
1182 break;
1184 window->maximize(window->maximizeMode() == MaximizeFull
1186 : MaximizeFull);
1188 break;
1190 window->maximize(window->maximizeMode() ^ MaximizeHorizontal);
1192 break;
1194 window->maximize(window->maximizeMode() ^ MaximizeVertical);
1196 break;
1197 case Options::RestoreOp:
1198 window->maximize(MaximizeRestore);
1200 break;
1202 window->setMinimized(true);
1203 break;
1204 case Options::ShadeOp:
1205 window->performMouseCommand(Options::MouseShade, Cursors::self()->mouse()->pos());
1206 break;
1208 window->setOnAllDesktops(!window->isOnAllDesktops());
1209 break;
1211 window->setFullScreen(!window->isFullScreen());
1212 break;
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 }
1237 window->performMouseCommand(Options::MouseShade, Cursors::self()->mouse()->pos());
1238 break;
1240 m_rulebook->edit(window, false);
1241 break;
1243 m_rulebook->edit(window, true);
1244 break;
1246 setupWindowShortcut(window);
1247 break;
1248 case Options::LowerOp:
1249 lowerWindow(window);
1250 break;
1251 case Options::NoOp:
1252 break;
1253 }
1254}
1255
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
1266{
1268 sendWindowToDesktops(m_activeWindow, {desktop}, true);
1269 }
1270}
1271
1272static 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
1286{
1287 if (!screenSwitchImpossible()) {
1288 switchToOutput(output);
1289 }
1290}
1291
1293{
1294 if (!screenSwitchImpossible()) {
1296 }
1297}
1298
1300{
1301 if (!screenSwitchImpossible()) {
1303 }
1304}
1305
1307{
1308 if (!screenSwitchImpossible()) {
1310 }
1311}
1312
1314{
1315 if (!screenSwitchImpossible()) {
1317 }
1318}
1319
1321{
1322 if (!screenSwitchImpossible()) {
1324 }
1325}
1326
1328{
1329 if (!screenSwitchImpossible()) {
1331 }
1332}
1333
1335{
1337 sendWindowToOutput(m_activeWindow, output);
1338 }
1339}
1340
1342{
1344 sendWindowToOutput(m_activeWindow, findOutput(m_activeWindow->output(), Direction::DirectionWest, true));
1345 }
1346}
1347
1349{
1351 sendWindowToOutput(m_activeWindow, findOutput(m_activeWindow->output(), Direction::DirectionEast, true));
1352 }
1353}
1354
1356{
1358 sendWindowToOutput(m_activeWindow, findOutput(m_activeWindow->output(), Direction::DirectionNorth, true));
1359 }
1360}
1361
1363{
1365 sendWindowToOutput(m_activeWindow, findOutput(m_activeWindow->output(), Direction::DirectionSouth, true));
1366 }
1367}
1368
1370{
1372 sendWindowToOutput(m_activeWindow, findOutput(m_activeWindow->output(), Direction::DirectionPrev, true));
1373 }
1374}
1375
1377{
1379 sendWindowToOutput(m_activeWindow, findOutput(m_activeWindow->output(), Direction::DirectionNext, true));
1380 }
1381}
1382
1387{
1390 }
1391}
1392
1402
1412
1417{
1420 }
1421}
1422
1427{
1430 }
1431}
1432
1437{
1439 raiseWindow(m_activeWindow);
1440 }
1441}
1442
1447{
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()) {
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
1470{
1472 raiseOrLowerWindow(m_activeWindow);
1473 }
1474}
1475
1477{
1479 m_activeWindow->setOnAllDesktops(!m_activeWindow->isOnAllDesktops());
1480 }
1481}
1482
1489
1491{
1494 }
1495}
1496
1498{
1501 }
1502}
1503
1505{
1508 }
1509}
1516
1524
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
1543{
1545 windowToNextDesktop(m_activeWindow);
1546 }
1547}
1548
1553
1558{
1560 windowToPreviousDesktop(m_activeWindow);
1561 }
1562}
1563
1568
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 }
1579 vds->setCurrent(newCurrent);
1580 ws->setMoveResizeWindow(nullptr);
1581}
1582
1589
1596
1603
1610
1615{
1616 if (!m_windowKiller) {
1617 m_windowKiller = std::make_unique<KillWindow>();
1618 }
1619 m_windowKiller->start();
1620}
1621
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
1656bool 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
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
1725void Workspace::showWindowMenu(const QRect &pos, Window *window)
1726{
1727 m_userActionsMenu->show(pos, window);
1728}
1729
1730void Workspace::showApplicationMenu(const QRect &pos, Window *window, int actionId)
1731{
1732 Workspace::self()->applicationMenu()->showApplicationMenu(window->pos().toPoint() + pos.bottomLeft(), window, actionId);
1733}
1734
1739{
1740 // TODO: why?
1741 // if ( tab_box->isVisible())
1742 // return;
1745 }
1746}
1747
1757
1767
1768#undef USABLE_ACTIVE_WINDOW
1769
1770void 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;
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
1839
1840void X11Window::setShortcutInternal()
1841{
1842 updateCaption();
1843#if 0
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
1853bool 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"
@ OperationModeXwayland
KWin uses Wayland and controls a nested Xwayland server.
Definition main.h:95
@ OperationModeWaylandOnly
KWin uses only Wayland.
Definition main.h:91
void showApplicationMenu(const QPoint &pos, Window *c, int actionId)
Definition appmenu.cpp:93
void setPos(const QPointF &pos)
Definition cursor.cpp:210
static Cursors * self()
Definition cursor.cpp:35
Cursor * mouse() const
Definition cursor.h:266
@ MouseUnrestrictedResize
Definition options.h:460
@ MouseUnrestrictedMove
Definition options.h:456
@ UnrestrictedMoveOp
Definition options.h:405
@ UnrestrictedResizeOp
Definition options.h:407
@ ApplicationRulesOp
Definition options.h:423
@ SetupWindowShortcutOp
Definition options.h:422
bool activeMouseScreen
Definition options.h:110
bool isRollOverDesktops() const
Definition options.h:385
bool focusPolicyIsReasonable
Definition options.h:113
bool isNextFocusPrefersMouse() const
Definition options.h:248
QList< QAction * > actionsForUserActionMenu(Window *c, QMenu *parent)
Invokes all registered callbacks to add actions to the UserActionsMenu.
static Scripting * self()
Definition scripting.h:393
void done(int r) override
void accept() override
QKeySequence shortcut() const
void dialogDone(bool ok)
ShortcutDialog(const QKeySequence &cut)
bool isMenuWindow(const Window *window) const
~UserActionsMenu() override
UserActionsMenu(QObject *parent=nullptr)
void show(const QRect &pos, Window *window)
Shows the menu at the given pos for the given window.
Manages the number of available virtual desktops, the layout of those and which virtual desktop is th...
VirtualDesktop * inDirection(VirtualDesktop *desktop, Direction direction, bool wrap=true)
VirtualDesktop * currentDesktop() const
void setOpacity(qreal opacity)
Definition window.cpp:196
QPointF pos
Definition window.h:79
virtual void updateCaption()=0
void setMinimized(bool set)
Definition window.cpp:984
QRectF frameGeometry
Definition window.h:431
virtual void setShortcutInternal()
qreal width
Definition window.h:99
virtual void setFullScreen(bool set)
Definition window.cpp:3952
bool keepBelow
Definition window.h:337
virtual void maximize(MaximizeMode mode)
Definition window.cpp:1167
bool noBorder
Definition window.h:456
void setKeepBelow(bool)
Definition window.cpp:649
void setOnAllDesktops(bool set)
Definition window.cpp:813
virtual bool isFullScreen() const
Definition window.cpp:3935
QKeySequence _shortcut
Definition window.h:1833
bool isOnAllDesktops() const
Definition window.h:2032
QUuid internalId
Definition window.h:259
virtual bool takeFocus()=0
virtual void closeWindow()=0
qreal y
Definition window.h:94
bool isActive() const
Definition window.h:882
bool performMouseCommand(Options::MouseCommand, const QPointF &globalPos)
Definition window.cpp:2022
bool isDesktop() const
Definition window.h:1922
qreal x
Definition window.h:89
QMargins frameMargins() const
Definition window.cpp:417
QString caption
Definition window.h:392
bool isOnDesktop(VirtualDesktop *desktop) const
Definition window.cpp:843
const WindowRules * rules() const
Definition window.h:1048
qreal height
Definition window.h:104
qreal opacity
Definition window.h:106
bool isDock() const
Definition window.h:1927
virtual void setNoBorder(bool set)
Definition window.cpp:4034
bool keepAbove
Definition window.h:332
void setKeepAbove(bool)
Definition window.cpp:628
const QKeySequence & shortcut() const
Definition window.h:979
KWin::Output * output
Definition window.h:111
virtual bool userCanSetNoBorder() const
Definition window.cpp:4029
virtual MaximizeMode maximizeMode() const
Definition window.cpp:3987
void setShortcut(const QString &cut)
QString checkShortcut(QString s, bool init=false) const
void slotToggleShowDesktop()
void slotSwitchToNextScreen()
void slotWindowToLeftScreen()
void showApplicationMenu(const QRect &pos, Window *window, int actionId)
Window * activeWindow() const
Definition workspace.h:767
ApplicationMenu * applicationMenu() const
void quickTileWindow(QuickTileMode mode)
void windowShortcutUpdated(Window *window)
QRectF clientArea(clientAreaOption, const Output *output, const VirtualDesktop *desktop) const
void activateWindow(Window *window, bool force=false)
const QList< Window * > & stackingOrder() const
Definition workspace.h:788
void sendWindowToOutput(Window *window, Output *output)
void slotWindowOperations()
void windowToNextDesktop(Window *window)
Window * windowUnderMouse(Output *output) const
void slotWindowShrinkHorizontal()
void showWindowMenu(const QRect &pos, Window *cl)
void slotWindowToPrevScreen()
void slotWindowExpandHorizontal()
void slotSwitchToLeftScreen()
void slotWindowMaximizeVertical()
void slotLowerWindowOpacity()
void slotSwitchToRightScreen()
Output * activeOutput() const
void slotWindowToNextDesktop()
void slotWindowMoveUp()
void setupWindowShortcutDone(bool)
void performWindowOperation(KWin::Window *window, Options::WindowOperation op)
void slotWindowCenter()
void switchToOutput(Output *output)
void slotWindowOnAllDesktops()
void raiseWindow(Window *window, bool nogroup=false)
Definition layers.cpp:354
void switchWindow(Direction direction)
void slotWindowToBelowScreen()
void slotWindowToPreviousDesktop()
void slotSwitchToScreen(Output *output)
void slotSwitchToPrevScreen()
static Workspace * self()
Definition workspace.h:91
Window * topWindowOnDesktop(VirtualDesktop *desktop, Output *output=nullptr, bool unconstrained=false, bool only_normal=true) const
Definition layers.cpp:224
void slotWindowToDesktopDown()
void slotWindowFullScreen()
void slotWindowMoveDown()
void slotSwitchToAboveScreen()
void slotWindowToDesktopLeft()
bool showingDesktop() const
Definition workspace.h:804
void slotWindowToNextScreen()
void setMoveResizeWindow(Window *window)
void slotWindowToRightScreen()
void lowerWindow(Window *window, bool nogroup=false)
Definition layers.cpp:296
void slotWindowMoveRight()
void slotWindowShrinkVertical()
QList< Output * > outputs() const
Definition workspace.h:762
void sendWindowToDesktops(Window *window, const QList< VirtualDesktop * > &desktops, bool dont_activate)
void slotWindowToDesktopUp()
bool requestFocus(Window *window, bool force=false)
void slotWindowExpandVertical()
bool takeActivity(Window *window, ActivityFlags flags)
void setShowingDesktop(bool showing, bool animated=true)
void slotWindowMaximizeHorizontal()
QRect geometry() const
void slotWindowToScreen(Output *output)
void slotIncreaseWindowOpacity()
void slotSwitchToBelowScreen()
void slotWindowToAboveScreen()
void slotSetupWindowShortcut()
void windowToPreviousDesktop(Window *window)
bool shortcutAvailable(const QKeySequence &cut, Window *ignore=nullptr) const
Output * findOutput(Output *reference, Direction direction, bool wrapAround=false) const
void slotWindowRaiseOrLower()
void raiseOrLowerWindow(Window *window)
Definition layers.cpp:279
void slotWindowToDesktopRight()
void slotActivateAttentionWindow()
void slotWindowMoveLeft()
void slotWindowToDesktop(VirtualDesktop *desktop)
void updateCaption() override
QList< KWayland::Client::Output * > outputs
@ ShadeNone
Definition common.h:63
void activeWindowToDesktop(VirtualDesktopManager::Direction direction)
Session::Type type
Definition session.cpp:17
@ ScreenArea
Definition globals.h:57
@ MaximizeVertical
The window is maximized vertically.
Definition common.h:76
@ MaximizeRestore
The window is not maximized in any direction.
Definition common.h:75
@ MaximizeHorizontal
Definition common.h:77
@ MaximizeFull
Equal to MaximizeVertical | MaximizeHorizontal.
Definition common.h:79
Workspace * workspace()
Definition workspace.h:830
Options * options
Definition main.cpp:73
bool match(QList< GlobalShortcut > &shortcuts, Args... args)
void windowToDesktop(Window *window, VirtualDesktopManager::Direction direction)
#define USABLE_ACTIVE_WINDOW