1873 lines
65 KiB
C++
1873 lines
65 KiB
C++
/*
|
|
KWin - the KDE window manager
|
|
This file is part of the KDE project.
|
|
|
|
SPDX-FileCopyrightText: 1999, 2000 Matthias Ettrich <ettrich@kde.org>
|
|
SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
|
|
SPDX-FileCopyrightText: 2022 Natalie Clarius <natalie_clarius@yahoo.de>
|
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
/*
|
|
|
|
This file contains things relevant to direct user actions, such as
|
|
responses to global keyboard shortcuts, or selecting actions
|
|
from the window operations menu.
|
|
|
|
*/
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// NOTE: if you change the menu, keep
|
|
// plasma-desktop/applets/taskmanager/package/contents/ui/ContextMenu.qml
|
|
// in sync
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include <config-kwin.h>
|
|
|
|
#include "core/output.h"
|
|
#include "cursor.h"
|
|
#include "input.h"
|
|
#include "options.h"
|
|
#include "scripting/scripting.h"
|
|
#include "useractions.h"
|
|
#include "virtualdesktops.h"
|
|
#include "workspace.h"
|
|
#include "x11window.h"
|
|
|
|
#if KWIN_BUILD_ACTIVITIES
|
|
#include "activities.h"
|
|
#include <PlasmaActivities/Info>
|
|
#endif
|
|
#include "appmenu.h"
|
|
|
|
#include <KProcess>
|
|
|
|
#include <QAction>
|
|
#include <QCheckBox>
|
|
#include <QPushButton>
|
|
|
|
#include <KGlobalAccel>
|
|
#include <KLazyLocalizedString>
|
|
#include <KLocalizedString>
|
|
#include <QAction>
|
|
#include <QActionGroup>
|
|
#include <QMenu>
|
|
#include <QRegularExpression>
|
|
#include <kauthorized.h>
|
|
#include <kconfig.h>
|
|
|
|
#include "killwindow.h"
|
|
#if KWIN_BUILD_TABBOX
|
|
#include "tabbox/tabbox.h"
|
|
#endif
|
|
|
|
namespace KWin
|
|
{
|
|
|
|
UserActionsMenu::UserActionsMenu(QObject *parent)
|
|
: QObject(parent)
|
|
, m_menu(nullptr)
|
|
, m_desktopMenu(nullptr)
|
|
, m_multipleDesktopsMenu(nullptr)
|
|
, m_screenMenu(nullptr)
|
|
, m_activityMenu(nullptr)
|
|
, m_scriptsMenu(nullptr)
|
|
, m_resizeOperation(nullptr)
|
|
, m_moveOperation(nullptr)
|
|
, m_maximizeOperation(nullptr)
|
|
, m_shadeOperation(nullptr)
|
|
, m_keepAboveOperation(nullptr)
|
|
, m_keepBelowOperation(nullptr)
|
|
, m_fullScreenOperation(nullptr)
|
|
, m_noBorderOperation(nullptr)
|
|
, m_minimizeOperation(nullptr)
|
|
, m_closeOperation(nullptr)
|
|
, m_shortcutOperation(nullptr)
|
|
{
|
|
}
|
|
|
|
UserActionsMenu::~UserActionsMenu()
|
|
{
|
|
discard();
|
|
}
|
|
|
|
bool UserActionsMenu::isShown() const
|
|
{
|
|
return m_menu && m_menu->isVisible();
|
|
}
|
|
|
|
bool UserActionsMenu::hasWindow() const
|
|
{
|
|
return m_window && isShown();
|
|
}
|
|
|
|
void UserActionsMenu::close()
|
|
{
|
|
if (!m_menu) {
|
|
return;
|
|
}
|
|
m_menu->close();
|
|
}
|
|
|
|
bool UserActionsMenu::isMenuWindow(const Window *window) const
|
|
{
|
|
return window && window == m_window;
|
|
}
|
|
|
|
void UserActionsMenu::show(const QRect &pos, Window *window)
|
|
{
|
|
Q_ASSERT(window);
|
|
QPointer<Window> windowPtr(window);
|
|
// Presumably window will never be nullptr,
|
|
// but play it safe and make sure not to crash.
|
|
if (windowPtr.isNull()) {
|
|
return;
|
|
}
|
|
if (isShown()) { // recursion
|
|
return;
|
|
}
|
|
if (windowPtr->isDesktop() || windowPtr->isDock()) {
|
|
return;
|
|
}
|
|
if (!KAuthorized::authorizeAction(QStringLiteral("kwin_rmb"))) {
|
|
return;
|
|
}
|
|
m_window = windowPtr;
|
|
init();
|
|
m_menu->popup(pos.bottomLeft());
|
|
}
|
|
|
|
void UserActionsMenu::grabInput()
|
|
{
|
|
m_menu->windowHandle()->setMouseGrabEnabled(true);
|
|
m_menu->windowHandle()->setKeyboardGrabEnabled(true);
|
|
}
|
|
|
|
void UserActionsMenu::helperDialog(const QString &message)
|
|
{
|
|
QStringList args;
|
|
QString type;
|
|
auto shortcut = [](const QString &name) {
|
|
QAction *action = Workspace::self()->findChild<QAction *>(name);
|
|
Q_ASSERT(action != nullptr);
|
|
const auto shortcuts = KGlobalAccel::self()->shortcut(action);
|
|
return QStringLiteral("%1 (%2)").arg(action->text(), shortcuts.isEmpty() ? QString() : shortcuts.first().toString(QKeySequence::NativeText));
|
|
};
|
|
if (message == QStringLiteral("noborderaltf3")) {
|
|
args << QStringLiteral("--msgbox") << i18n("You have selected to show a window without its border.\n"
|
|
"Without the border, you will not be able to enable the border "
|
|
"again using the mouse: use the window operations menu instead, "
|
|
"activated using the %1 keyboard shortcut.",
|
|
shortcut(QStringLiteral("Window Operations Menu")));
|
|
type = QStringLiteral("altf3warning");
|
|
} else if (message == QLatin1String("fullscreenaltf3")) {
|
|
args << QStringLiteral("--msgbox") << i18n("You have selected to show a window in fullscreen mode.\n"
|
|
"If the application itself does not have an option to turn the fullscreen "
|
|
"mode off you will not be able to disable it "
|
|
"again using the mouse: use the window operations menu instead, "
|
|
"activated using the %1 keyboard shortcut.",
|
|
shortcut(QStringLiteral("Window Operations Menu")));
|
|
type = QStringLiteral("altf3warning");
|
|
} else {
|
|
Q_UNREACHABLE();
|
|
}
|
|
if (!type.isEmpty()) {
|
|
KConfig cfg(QStringLiteral("kwin_dialogsrc"));
|
|
KConfigGroup cg(&cfg, "Notification Messages"); // Depends on KMessageBox
|
|
if (!cg.readEntry(type, true)) {
|
|
return;
|
|
}
|
|
args << QStringLiteral("--dontagain") << QLatin1String("kwin_dialogsrc:") + type;
|
|
}
|
|
KProcess::startDetached(QStringLiteral("kdialog"), args);
|
|
}
|
|
|
|
void UserActionsMenu::init()
|
|
{
|
|
if (m_menu) {
|
|
return;
|
|
}
|
|
m_menu = new QMenu;
|
|
connect(m_menu, &QMenu::aboutToShow, this, &UserActionsMenu::menuAboutToShow);
|
|
|
|
// the toplevel menu gets closed before a submenu's action is invoked
|
|
connect(m_menu, &QMenu::aboutToHide, this, &UserActionsMenu::menuAboutToHide, Qt::QueuedConnection);
|
|
connect(m_menu, &QMenu::triggered, this, &UserActionsMenu::slotWindowOperation);
|
|
|
|
QMenu *advancedMenu = new QMenu(m_menu);
|
|
connect(advancedMenu, &QMenu::aboutToShow, this, [this, advancedMenu]() {
|
|
if (m_window) {
|
|
advancedMenu->setPalette(m_window->palette());
|
|
}
|
|
});
|
|
|
|
auto setShortcut = [](QAction *action, const QString &actionName) {
|
|
const auto shortcuts = KGlobalAccel::self()->shortcut(Workspace::self()->findChild<QAction *>(actionName));
|
|
if (!shortcuts.isEmpty()) {
|
|
action->setShortcut(shortcuts.first());
|
|
}
|
|
};
|
|
|
|
m_moveOperation = advancedMenu->addAction(i18n("&Move"));
|
|
m_moveOperation->setIcon(QIcon::fromTheme(QStringLiteral("transform-move")));
|
|
setShortcut(m_moveOperation, QStringLiteral("Window Move"));
|
|
m_moveOperation->setData(Options::UnrestrictedMoveOp);
|
|
|
|
m_resizeOperation = advancedMenu->addAction(i18n("&Resize"));
|
|
m_resizeOperation->setIcon(QIcon::fromTheme(QStringLiteral("transform-scale")));
|
|
setShortcut(m_resizeOperation, QStringLiteral("Window Resize"));
|
|
m_resizeOperation->setData(Options::ResizeOp);
|
|
|
|
m_keepAboveOperation = advancedMenu->addAction(i18n("Keep &Above Others"));
|
|
m_keepAboveOperation->setIcon(QIcon::fromTheme(QStringLiteral("window-keep-above")));
|
|
setShortcut(m_keepAboveOperation, QStringLiteral("Window Above Other Windows"));
|
|
m_keepAboveOperation->setCheckable(true);
|
|
m_keepAboveOperation->setData(Options::KeepAboveOp);
|
|
|
|
m_keepBelowOperation = advancedMenu->addAction(i18n("Keep &Below Others"));
|
|
m_keepBelowOperation->setIcon(QIcon::fromTheme(QStringLiteral("window-keep-below")));
|
|
setShortcut(m_keepBelowOperation, QStringLiteral("Window Below Other Windows"));
|
|
m_keepBelowOperation->setCheckable(true);
|
|
m_keepBelowOperation->setData(Options::KeepBelowOp);
|
|
|
|
m_fullScreenOperation = advancedMenu->addAction(i18n("&Fullscreen"));
|
|
m_fullScreenOperation->setIcon(QIcon::fromTheme(QStringLiteral("view-fullscreen")));
|
|
setShortcut(m_fullScreenOperation, QStringLiteral("Window Fullscreen"));
|
|
m_fullScreenOperation->setCheckable(true);
|
|
m_fullScreenOperation->setData(Options::FullScreenOp);
|
|
|
|
m_shadeOperation = advancedMenu->addAction(i18n("&Shade"));
|
|
m_shadeOperation->setIcon(QIcon::fromTheme(QStringLiteral("window-shade")));
|
|
setShortcut(m_shadeOperation, QStringLiteral("Window Shade"));
|
|
m_shadeOperation->setCheckable(true);
|
|
m_shadeOperation->setData(Options::ShadeOp);
|
|
|
|
m_noBorderOperation = advancedMenu->addAction(i18n("&No Titlebar and Frame"));
|
|
m_noBorderOperation->setIcon(QIcon::fromTheme(QStringLiteral("edit-none-border")));
|
|
setShortcut(m_noBorderOperation, QStringLiteral("Window No Border"));
|
|
m_noBorderOperation->setCheckable(true);
|
|
m_noBorderOperation->setData(Options::NoBorderOp);
|
|
|
|
advancedMenu->addSeparator();
|
|
|
|
m_shortcutOperation = advancedMenu->addAction(i18n("Set Window Short&cut…"));
|
|
m_shortcutOperation->setIcon(QIcon::fromTheme(QStringLiteral("configure-shortcuts")));
|
|
setShortcut(m_shortcutOperation, QStringLiteral("Setup Window Shortcut"));
|
|
m_shortcutOperation->setData(Options::SetupWindowShortcutOp);
|
|
|
|
#if KWIN_BUILD_KCMS
|
|
QAction *action = advancedMenu->addAction(i18n("Configure Special &Window Settings…"));
|
|
action->setIcon(QIcon::fromTheme(QStringLiteral("preferences-system-windows-actions")));
|
|
action->setData(Options::WindowRulesOp);
|
|
m_rulesOperation = action;
|
|
|
|
action = advancedMenu->addAction(i18n("Configure S&pecial Application Settings…"));
|
|
action->setIcon(QIcon::fromTheme(QStringLiteral("preferences-system-windows-actions")));
|
|
action->setData(Options::ApplicationRulesOp);
|
|
m_applicationRulesOperation = action;
|
|
#endif
|
|
|
|
m_maximizeOperation = m_menu->addAction(i18n("Ma&ximize"));
|
|
m_maximizeOperation->setIcon(QIcon::fromTheme(QStringLiteral("window-maximize")));
|
|
setShortcut(m_maximizeOperation, QStringLiteral("Window Maximize"));
|
|
m_maximizeOperation->setCheckable(true);
|
|
m_maximizeOperation->setData(Options::MaximizeOp);
|
|
|
|
m_minimizeOperation = m_menu->addAction(i18n("Mi&nimize"));
|
|
m_minimizeOperation->setIcon(QIcon::fromTheme(QStringLiteral("window-minimize")));
|
|
setShortcut(m_minimizeOperation, QStringLiteral("Window Minimize"));
|
|
m_minimizeOperation->setData(Options::MinimizeOp);
|
|
|
|
QAction *overflowAction = m_menu->addMenu(advancedMenu);
|
|
overflowAction->setText(i18n("&More Actions"));
|
|
overflowAction->setIcon(QIcon::fromTheme(QStringLiteral("overflow-menu")));
|
|
|
|
m_menu->addSeparator();
|
|
|
|
m_closeOperation = m_menu->addAction(i18n("&Close"));
|
|
m_closeOperation->setIcon(QIcon::fromTheme(QStringLiteral("window-close")));
|
|
setShortcut(m_closeOperation, QStringLiteral("Window Close"));
|
|
m_closeOperation->setData(Options::CloseOp);
|
|
}
|
|
|
|
void UserActionsMenu::discard()
|
|
{
|
|
delete m_menu;
|
|
m_menu = nullptr;
|
|
m_desktopMenu = nullptr;
|
|
m_multipleDesktopsMenu = nullptr;
|
|
m_screenMenu = nullptr;
|
|
m_activityMenu = nullptr;
|
|
m_scriptsMenu = nullptr;
|
|
}
|
|
|
|
void UserActionsMenu::menuAboutToShow()
|
|
{
|
|
if (m_window.isNull() || !m_menu) {
|
|
return;
|
|
}
|
|
|
|
m_window->blockActivityUpdates(true);
|
|
|
|
if (VirtualDesktopManager::self()->count() == 1) {
|
|
delete m_desktopMenu;
|
|
m_desktopMenu = nullptr;
|
|
delete m_multipleDesktopsMenu;
|
|
m_multipleDesktopsMenu = nullptr;
|
|
} else {
|
|
initDesktopPopup();
|
|
}
|
|
if (workspace()->outputs().count() == 1 || (!m_window->isMovable() && !m_window->isMovableAcrossScreens())) {
|
|
delete m_screenMenu;
|
|
m_screenMenu = nullptr;
|
|
} else {
|
|
initScreenPopup();
|
|
}
|
|
|
|
m_menu->setPalette(m_window->palette());
|
|
m_resizeOperation->setEnabled(m_window->isResizable());
|
|
m_moveOperation->setEnabled(m_window->isMovableAcrossScreens());
|
|
m_maximizeOperation->setEnabled(m_window->isMaximizable());
|
|
m_maximizeOperation->setChecked(m_window->maximizeMode() == MaximizeFull);
|
|
m_shadeOperation->setEnabled(m_window->isShadeable());
|
|
m_shadeOperation->setChecked(m_window->shadeMode() != ShadeNone);
|
|
m_keepAboveOperation->setChecked(m_window->keepAbove());
|
|
m_keepBelowOperation->setChecked(m_window->keepBelow());
|
|
m_fullScreenOperation->setEnabled(m_window->isFullScreenable());
|
|
m_fullScreenOperation->setChecked(m_window->isFullScreen());
|
|
m_noBorderOperation->setEnabled(m_window->userCanSetNoBorder());
|
|
m_noBorderOperation->setChecked(m_window->noBorder());
|
|
m_minimizeOperation->setEnabled(m_window->isMinimizable());
|
|
m_closeOperation->setEnabled(m_window->isCloseable());
|
|
m_shortcutOperation->setEnabled(m_window->rules()->checkShortcut(QString()).isNull());
|
|
|
|
// drop the existing scripts menu
|
|
delete m_scriptsMenu;
|
|
m_scriptsMenu = nullptr;
|
|
// ask scripts whether they want to add entries for the given window
|
|
QList<QAction *> scriptActions = Scripting::self()->actionsForUserActionMenu(m_window.data(), m_scriptsMenu);
|
|
if (!scriptActions.isEmpty()) {
|
|
m_scriptsMenu = new QMenu(m_menu);
|
|
m_scriptsMenu->setPalette(m_window->palette());
|
|
m_scriptsMenu->addActions(scriptActions);
|
|
|
|
QAction *action = m_scriptsMenu->menuAction();
|
|
// set it as the first item after desktop
|
|
m_menu->insertAction(m_closeOperation, action);
|
|
action->setText(i18n("&Extensions"));
|
|
}
|
|
|
|
if (m_rulesOperation) {
|
|
m_rulesOperation->setEnabled(m_window->supportsWindowRules());
|
|
}
|
|
if (m_applicationRulesOperation) {
|
|
m_applicationRulesOperation->setEnabled(m_window->supportsWindowRules());
|
|
}
|
|
|
|
showHideActivityMenu();
|
|
}
|
|
|
|
void UserActionsMenu::menuAboutToHide()
|
|
{
|
|
if (m_window) {
|
|
m_window->blockActivityUpdates(false);
|
|
m_window.clear();
|
|
}
|
|
}
|
|
|
|
void UserActionsMenu::showHideActivityMenu()
|
|
{
|
|
#if KWIN_BUILD_ACTIVITIES
|
|
if (!Workspace::self()->activities()) {
|
|
return;
|
|
}
|
|
const QStringList &openActivities_ = Workspace::self()->activities()->running();
|
|
qCDebug(KWIN_CORE) << "activities:" << openActivities_.size();
|
|
if (openActivities_.size() < 2) {
|
|
delete m_activityMenu;
|
|
m_activityMenu = nullptr;
|
|
} else {
|
|
initActivityPopup();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void UserActionsMenu::initDesktopPopup()
|
|
{
|
|
if (kwinApp()->operationMode() == Application::OperationModeWaylandOnly || kwinApp()->operationMode() == Application::OperationModeXwayland) {
|
|
if (m_multipleDesktopsMenu) {
|
|
return;
|
|
}
|
|
|
|
m_multipleDesktopsMenu = new QMenu(m_menu);
|
|
connect(m_multipleDesktopsMenu, &QMenu::aboutToShow, this, &UserActionsMenu::multipleDesktopsPopupAboutToShow);
|
|
|
|
QAction *action = m_multipleDesktopsMenu->menuAction();
|
|
// set it as the first item
|
|
m_menu->insertAction(m_maximizeOperation, action);
|
|
action->setText(i18n("&Desktops"));
|
|
action->setIcon(QIcon::fromTheme(QStringLiteral("virtual-desktops")));
|
|
|
|
} else {
|
|
if (m_desktopMenu) {
|
|
return;
|
|
}
|
|
|
|
m_desktopMenu = new QMenu(m_menu);
|
|
connect(m_desktopMenu, &QMenu::aboutToShow, this, &UserActionsMenu::desktopPopupAboutToShow);
|
|
|
|
QAction *action = m_desktopMenu->menuAction();
|
|
// set it as the first item
|
|
m_menu->insertAction(m_maximizeOperation, action);
|
|
action->setText(i18n("Move to &Desktop"));
|
|
action->setIcon(QIcon::fromTheme(QStringLiteral("virtual-desktops")));
|
|
}
|
|
}
|
|
|
|
void UserActionsMenu::initScreenPopup()
|
|
{
|
|
if (m_screenMenu) {
|
|
return;
|
|
}
|
|
|
|
m_screenMenu = new QMenu(m_menu);
|
|
connect(m_screenMenu, &QMenu::aboutToShow, this, &UserActionsMenu::screenPopupAboutToShow);
|
|
|
|
QAction *action = m_screenMenu->menuAction();
|
|
// set it as the first item after desktop
|
|
m_menu->insertAction(m_activityMenu ? m_activityMenu->menuAction() : m_minimizeOperation, action);
|
|
action->setText(i18n("Move to &Screen"));
|
|
action->setIcon(QIcon::fromTheme(QStringLiteral("computer")));
|
|
}
|
|
|
|
void UserActionsMenu::initActivityPopup()
|
|
{
|
|
if (m_activityMenu) {
|
|
return;
|
|
}
|
|
|
|
m_activityMenu = new QMenu(m_menu);
|
|
connect(m_activityMenu, &QMenu::aboutToShow, this, &UserActionsMenu::activityPopupAboutToShow);
|
|
|
|
QAction *action = m_activityMenu->menuAction();
|
|
// set it as the first item
|
|
m_menu->insertAction(m_maximizeOperation, action);
|
|
action->setText(i18n("Show in &Activities"));
|
|
action->setIcon(QIcon::fromTheme(QStringLiteral("activities")));
|
|
}
|
|
|
|
void UserActionsMenu::desktopPopupAboutToShow()
|
|
{
|
|
if (!m_desktopMenu) {
|
|
return;
|
|
}
|
|
const VirtualDesktopManager *vds = VirtualDesktopManager::self();
|
|
|
|
m_desktopMenu->clear();
|
|
if (m_window) {
|
|
m_desktopMenu->setPalette(m_window->palette());
|
|
}
|
|
QActionGroup *group = new QActionGroup(m_desktopMenu);
|
|
|
|
QAction *action = m_desktopMenu->addAction(i18n("Move &To Current Desktop"));
|
|
action->setEnabled(m_window && (m_window->isOnAllDesktops() || !m_window->isOnDesktop(vds->currentDesktop())));
|
|
connect(action, &QAction::triggered, this, [this]() {
|
|
if (!m_window) {
|
|
return;
|
|
}
|
|
VirtualDesktopManager *vds = VirtualDesktopManager::self();
|
|
workspace()->sendWindowToDesktops(m_window, {vds->currentDesktop()}, false);
|
|
});
|
|
|
|
action = m_desktopMenu->addAction(i18n("&All Desktops"));
|
|
connect(action, &QAction::triggered, this, [this]() {
|
|
if (m_window) {
|
|
m_window->setOnAllDesktops(!m_window->isOnAllDesktops());
|
|
}
|
|
});
|
|
action->setCheckable(true);
|
|
if (m_window && m_window->isOnAllDesktops()) {
|
|
action->setChecked(true);
|
|
}
|
|
group->addAction(action);
|
|
|
|
m_desktopMenu->addSeparator();
|
|
|
|
const uint BASE = 10;
|
|
|
|
const auto desktops = vds->desktops();
|
|
for (VirtualDesktop *desktop : desktops) {
|
|
const uint legacyId = desktop->x11DesktopNumber();
|
|
|
|
QString basic_name(QStringLiteral("%1 %2"));
|
|
if (legacyId < BASE) {
|
|
basic_name.prepend(QLatin1Char('&'));
|
|
}
|
|
action = m_desktopMenu->addAction(basic_name.arg(legacyId).arg(desktop->name().replace(QLatin1Char('&'), QStringLiteral("&&"))));
|
|
connect(action, &QAction::triggered, this, [this, desktop]() {
|
|
if (m_window) {
|
|
workspace()->sendWindowToDesktops(m_window, {desktop}, false);
|
|
}
|
|
});
|
|
action->setCheckable(true);
|
|
group->addAction(action);
|
|
|
|
if (m_window && !m_window->isOnAllDesktops() && m_window->isOnDesktop(desktop)) {
|
|
action->setChecked(true);
|
|
}
|
|
}
|
|
|
|
m_desktopMenu->addSeparator();
|
|
|
|
action = m_desktopMenu->addAction(i18nc("Create a new desktop and move the window there", "&New Desktop"));
|
|
action->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
|
|
connect(action, &QAction::triggered, this, [this]() {
|
|
if (!m_window) {
|
|
return;
|
|
}
|
|
VirtualDesktopManager *vds = VirtualDesktopManager::self();
|
|
VirtualDesktop *desktop = vds->createVirtualDesktop(vds->count());
|
|
if (desktop) {
|
|
workspace()->sendWindowToDesktops(m_window, {desktop}, false);
|
|
}
|
|
});
|
|
action->setEnabled(vds->count() < vds->maximum());
|
|
}
|
|
|
|
void UserActionsMenu::multipleDesktopsPopupAboutToShow()
|
|
{
|
|
if (!m_multipleDesktopsMenu) {
|
|
return;
|
|
}
|
|
VirtualDesktopManager *vds = VirtualDesktopManager::self();
|
|
|
|
m_multipleDesktopsMenu->clear();
|
|
if (m_window) {
|
|
m_multipleDesktopsMenu->setPalette(m_window->palette());
|
|
}
|
|
|
|
QAction *action = m_multipleDesktopsMenu->addAction(i18n("&All Desktops"));
|
|
connect(action, &QAction::triggered, this, [this]() {
|
|
if (m_window) {
|
|
m_window->setOnAllDesktops(!m_window->isOnAllDesktops());
|
|
}
|
|
});
|
|
action->setCheckable(true);
|
|
if (m_window && m_window->isOnAllDesktops()) {
|
|
action->setChecked(true);
|
|
}
|
|
|
|
m_multipleDesktopsMenu->addSeparator();
|
|
|
|
const uint BASE = 10;
|
|
|
|
const auto desktops = vds->desktops();
|
|
for (VirtualDesktop *desktop : desktops) {
|
|
const uint legacyId = desktop->x11DesktopNumber();
|
|
|
|
QString basic_name(QStringLiteral("%1 %2"));
|
|
if (legacyId < BASE) {
|
|
basic_name.prepend(QLatin1Char('&'));
|
|
}
|
|
|
|
QAction *action = m_multipleDesktopsMenu->addAction(basic_name.arg(legacyId).arg(desktop->name().replace(QLatin1Char('&'), QStringLiteral("&&"))));
|
|
connect(action, &QAction::triggered, this, [this, desktop]() {
|
|
if (m_window) {
|
|
if (m_window->desktops().contains(desktop)) {
|
|
m_window->leaveDesktop(desktop);
|
|
} else {
|
|
m_window->enterDesktop(desktop);
|
|
}
|
|
}
|
|
});
|
|
action->setCheckable(true);
|
|
if (m_window && !m_window->isOnAllDesktops() && m_window->isOnDesktop(desktop)) {
|
|
action->setChecked(true);
|
|
}
|
|
}
|
|
|
|
m_multipleDesktopsMenu->addSeparator();
|
|
|
|
for (VirtualDesktop *desktop : desktops) {
|
|
const uint legacyId = desktop->x11DesktopNumber();
|
|
QString name = i18n("Move to %1 %2", legacyId, desktop->name());
|
|
QAction *action = m_multipleDesktopsMenu->addAction(name);
|
|
connect(action, &QAction::triggered, this, [this, desktop]() {
|
|
if (m_window) {
|
|
m_window->setDesktops({desktop});
|
|
}
|
|
});
|
|
}
|
|
|
|
m_multipleDesktopsMenu->addSeparator();
|
|
|
|
bool allowNewDesktops = vds->count() < vds->maximum();
|
|
|
|
action = m_multipleDesktopsMenu->addAction(i18nc("Create a new desktop and add the window to that desktop", "Add to &New Desktop"));
|
|
connect(action, &QAction::triggered, this, [this, vds]() {
|
|
if (!m_window) {
|
|
return;
|
|
}
|
|
VirtualDesktop *desktop = vds->createVirtualDesktop(vds->count());
|
|
if (desktop) {
|
|
m_window->enterDesktop(desktop);
|
|
}
|
|
});
|
|
action->setEnabled(allowNewDesktops);
|
|
|
|
action = m_multipleDesktopsMenu->addAction(i18nc("Create a new desktop and move the window to that desktop", "Move to New Desktop"));
|
|
connect(action, &QAction::triggered, this, [this, vds]() {
|
|
if (!m_window) {
|
|
return;
|
|
}
|
|
VirtualDesktop *desktop = vds->createVirtualDesktop(vds->count());
|
|
if (desktop) {
|
|
m_window->setDesktops({desktop});
|
|
}
|
|
});
|
|
action->setEnabled(allowNewDesktops);
|
|
}
|
|
|
|
void UserActionsMenu::screenPopupAboutToShow()
|
|
{
|
|
if (!m_screenMenu) {
|
|
return;
|
|
}
|
|
m_screenMenu->clear();
|
|
|
|
if (!m_window) {
|
|
return;
|
|
}
|
|
m_screenMenu->setPalette(m_window->palette());
|
|
QActionGroup *group = new QActionGroup(m_screenMenu);
|
|
|
|
const auto outputs = workspace()->outputs();
|
|
for (int i = 0; i < outputs.count(); ++i) {
|
|
Output *output = outputs[i];
|
|
// assumption: there are not more than 9 screens attached.
|
|
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)",
|
|
"Screen &%1 (%2)", (i + 1), output->name()));
|
|
connect(action, &QAction::triggered, this, [this, output]() {
|
|
workspace()->sendWindowToOutput(m_window, output);
|
|
});
|
|
action->setCheckable(true);
|
|
if (m_window && output == m_window->output()) {
|
|
action->setChecked(true);
|
|
}
|
|
group->addAction(action);
|
|
}
|
|
}
|
|
|
|
void UserActionsMenu::activityPopupAboutToShow()
|
|
{
|
|
if (!m_activityMenu) {
|
|
return;
|
|
}
|
|
|
|
#if KWIN_BUILD_ACTIVITIES
|
|
if (!Workspace::self()->activities()) {
|
|
return;
|
|
}
|
|
m_activityMenu->clear();
|
|
if (m_window) {
|
|
m_activityMenu->setPalette(m_window->palette());
|
|
}
|
|
QAction *action = m_activityMenu->addAction(i18n("&All Activities"));
|
|
action->setCheckable(true);
|
|
connect(action, &QAction::triggered, this, [this]() {
|
|
if (m_window) {
|
|
m_window->setOnAllActivities(!m_window->isOnAllActivities());
|
|
}
|
|
});
|
|
static QPointer<QActionGroup> allActivitiesGroup;
|
|
if (!allActivitiesGroup) {
|
|
allActivitiesGroup = new QActionGroup(m_activityMenu);
|
|
}
|
|
allActivitiesGroup->addAction(action);
|
|
|
|
if (m_window && m_window->isOnAllActivities()) {
|
|
action->setChecked(true);
|
|
}
|
|
m_activityMenu->addSeparator();
|
|
|
|
const auto activities = Workspace::self()->activities()->running();
|
|
for (const QString &id : activities) {
|
|
KActivities::Info activity(id);
|
|
QString name = activity.name();
|
|
name.replace('&', "&&");
|
|
auto action = m_activityMenu->addAction(name);
|
|
action->setCheckable(true);
|
|
const QString icon = activity.icon();
|
|
if (!icon.isEmpty()) {
|
|
action->setIcon(QIcon::fromTheme(icon));
|
|
}
|
|
m_activityMenu->addAction(action);
|
|
connect(action, &QAction::triggered, this, [this, id]() {
|
|
if (m_window) {
|
|
Workspace::self()->activities()->toggleWindowOnActivity(m_window, id, false);
|
|
}
|
|
});
|
|
|
|
if (m_window && !m_window->isOnAllActivities() && m_window->isOnActivity(id)) {
|
|
action->setChecked(true);
|
|
}
|
|
}
|
|
|
|
m_activityMenu->addSeparator();
|
|
for (const QString &id : activities) {
|
|
const KActivities::Info activity(id);
|
|
if (m_window->activities().size() == 1 && m_window->activities().front() == id) {
|
|
// no need to show a button that doesn't do anything
|
|
continue;
|
|
}
|
|
const QString name = i18n("Move to %1", activity.name().replace('&', "&&"));
|
|
const auto action = m_activityMenu->addAction(name);
|
|
if (const QString icon = activity.icon(); !icon.isEmpty()) {
|
|
action->setIcon(QIcon::fromTheme(icon));
|
|
}
|
|
connect(action, &QAction::triggered, this, [this, id] {
|
|
m_window->setOnActivities({id});
|
|
});
|
|
m_activityMenu->addAction(action);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void UserActionsMenu::slotWindowOperation(QAction *action)
|
|
{
|
|
if (!action->data().isValid()) {
|
|
return;
|
|
}
|
|
|
|
Options::WindowOperation op = static_cast<Options::WindowOperation>(action->data().toInt());
|
|
QPointer<Window> c = m_window ? m_window : QPointer<Window>(Workspace::self()->activeWindow());
|
|
if (c.isNull()) {
|
|
return;
|
|
}
|
|
QString type;
|
|
switch (op) {
|
|
case Options::FullScreenOp:
|
|
if (!c->isFullScreen() && c->isFullScreenable()) {
|
|
type = QStringLiteral("fullscreenaltf3");
|
|
}
|
|
break;
|
|
case Options::NoBorderOp:
|
|
if (!c->noBorder() && c->userCanSetNoBorder()) {
|
|
type = QStringLiteral("noborderaltf3");
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (!type.isEmpty()) {
|
|
helperDialog(type);
|
|
}
|
|
// need to delay performing the window operation as we need to have the
|
|
// user actions menu closed before we destroy the decoration. Otherwise Qt crashes
|
|
QMetaObject::invokeMethod(
|
|
workspace(), [c, op]() {
|
|
workspace()->performWindowOperation(c, op);
|
|
},
|
|
Qt::QueuedConnection);
|
|
}
|
|
|
|
//****************************************
|
|
// ShortcutDialog
|
|
//****************************************
|
|
ShortcutDialog::ShortcutDialog(const QKeySequence &cut)
|
|
: _shortcut(cut)
|
|
{
|
|
m_ui.setupUi(this);
|
|
m_ui.keySequenceEdit->setKeySequence(cut);
|
|
m_ui.warning->hide();
|
|
|
|
// Listen to changed shortcuts
|
|
connect(m_ui.keySequenceEdit, &QKeySequenceEdit::editingFinished, this, &ShortcutDialog::keySequenceChanged);
|
|
connect(m_ui.clearButton, &QToolButton::clicked, this, [this] {
|
|
_shortcut = QKeySequence();
|
|
});
|
|
m_ui.keySequenceEdit->setFocus();
|
|
|
|
setWindowFlags(Qt::Popup | Qt::X11BypassWindowManagerHint);
|
|
}
|
|
|
|
void ShortcutDialog::accept()
|
|
{
|
|
QKeySequence seq = shortcut();
|
|
if (!seq.isEmpty()) {
|
|
if (seq[0] == QKeyCombination(Qt::Key_Escape)) {
|
|
reject();
|
|
return;
|
|
}
|
|
if (seq[0] == QKeyCombination(Qt::Key_Space) || seq[0].keyboardModifiers() == Qt::NoModifier) {
|
|
// clear
|
|
m_ui.keySequenceEdit->clear();
|
|
QDialog::accept();
|
|
return;
|
|
}
|
|
}
|
|
QDialog::accept();
|
|
}
|
|
|
|
void ShortcutDialog::done(int r)
|
|
{
|
|
QDialog::done(r);
|
|
Q_EMIT dialogDone(r == Accepted);
|
|
}
|
|
|
|
void ShortcutDialog::keySequenceChanged()
|
|
{
|
|
activateWindow(); // where is the kbd focus lost? cause of popup state?
|
|
QKeySequence seq = m_ui.keySequenceEdit->keySequence();
|
|
if (_shortcut == seq) {
|
|
return; // don't try to update the same
|
|
}
|
|
|
|
if (seq.isEmpty()) { // clear
|
|
_shortcut = seq;
|
|
return;
|
|
}
|
|
if (seq.count() > 1) {
|
|
seq = QKeySequence(seq[0]);
|
|
m_ui.keySequenceEdit->setKeySequence(seq);
|
|
}
|
|
|
|
// Check if the key sequence is used currently
|
|
QString sc = seq.toString();
|
|
// NOTICE - seq.toString() & the entries in "conflicting" randomly get invalidated after the next call (if no sc has been set & conflicting isn't empty?!)
|
|
QList<KGlobalShortcutInfo> conflicting = KGlobalAccel::globalShortcutsByKey(seq);
|
|
if (!conflicting.isEmpty()) {
|
|
const KGlobalShortcutInfo &conflict = conflicting.at(0);
|
|
m_ui.warning->setText(i18nc("'%1' is a keyboard shortcut like 'ctrl+w'",
|
|
"<b>%1</b> is already in use", sc));
|
|
m_ui.warning->setToolTip(i18nc("keyboard shortcut '%1' is used by action '%2' in application '%3'",
|
|
"<b>%1</b> is used by %2 in %3", sc, conflict.friendlyName(), conflict.componentFriendlyName()));
|
|
m_ui.warning->show();
|
|
m_ui.keySequenceEdit->setKeySequence(shortcut());
|
|
} else if (seq != _shortcut) {
|
|
m_ui.warning->hide();
|
|
if (QPushButton *ok = m_ui.buttonBox->button(QDialogButtonBox::Ok)) {
|
|
ok->setFocus();
|
|
}
|
|
}
|
|
|
|
_shortcut = seq;
|
|
}
|
|
|
|
QKeySequence ShortcutDialog::shortcut() const
|
|
{
|
|
return _shortcut;
|
|
}
|
|
|
|
//****************************************
|
|
// Workspace
|
|
//****************************************
|
|
|
|
void Workspace::slotIncreaseWindowOpacity()
|
|
{
|
|
if (!m_activeWindow) {
|
|
return;
|
|
}
|
|
m_activeWindow->setOpacity(std::min(m_activeWindow->opacity() + 0.05, 1.0));
|
|
}
|
|
|
|
void Workspace::slotLowerWindowOpacity()
|
|
{
|
|
if (!m_activeWindow) {
|
|
return;
|
|
}
|
|
m_activeWindow->setOpacity(std::max(m_activeWindow->opacity() - 0.05, 0.05));
|
|
}
|
|
|
|
void Workspace::closeActivePopup()
|
|
{
|
|
if (active_popup) {
|
|
active_popup->close();
|
|
active_popup = nullptr;
|
|
m_activePopupWindow = nullptr;
|
|
}
|
|
m_userActionsMenu->close();
|
|
}
|
|
|
|
template<typename Slot>
|
|
void Workspace::initShortcut(const QString &actionName, const QString &description, const QKeySequence &shortcut, Slot slot)
|
|
{
|
|
initShortcut(actionName, description, shortcut, this, slot);
|
|
}
|
|
|
|
template<typename T, typename Slot>
|
|
void Workspace::initShortcut(const QString &actionName, const QString &description, const QKeySequence &shortcut, T *receiver, Slot slot)
|
|
{
|
|
QAction *a = new QAction(this);
|
|
a->setProperty("componentName", QStringLiteral("kwin"));
|
|
a->setObjectName(actionName);
|
|
a->setText(description);
|
|
KGlobalAccel::self()->setDefaultShortcut(a, QList<QKeySequence>() << shortcut);
|
|
KGlobalAccel::self()->setShortcut(a, QList<QKeySequence>() << shortcut);
|
|
connect(a, &QAction::triggered, receiver, slot);
|
|
}
|
|
|
|
/**
|
|
* Creates the global accel object \c keys.
|
|
*/
|
|
void Workspace::initShortcuts()
|
|
{
|
|
// The first argument to initShortcut() is the id for the shortcut in the user's
|
|
// config file, while the second argumewnt is its user-visible text.
|
|
// Normally these should be identical, but if the user-visible text for a new
|
|
// shortcut that you're adding is super long, it is permissible to use a shorter
|
|
// string for name.
|
|
|
|
// PLEASE NOTE: Never change the ID of an existing shortcut! It will cause users'
|
|
// custom shortcuts to be lost. Instead, only change the description
|
|
|
|
initShortcut("Window Operations Menu", i18n("Window Operations Menu"),
|
|
Qt::ALT | Qt::Key_F3, &Workspace::slotWindowOperations);
|
|
initShortcut("Window Close", i18n("Close Window"),
|
|
Qt::ALT | Qt::Key_F4, &Workspace::slotWindowClose);
|
|
initShortcut("Window Maximize", i18n("Maximize Window"),
|
|
Qt::META | Qt::Key_PageUp, &Workspace::slotWindowMaximize);
|
|
initShortcut("Window Maximize Vertical", i18n("Maximize Window Vertically"),
|
|
0, &Workspace::slotWindowMaximizeVertical);
|
|
initShortcut("Window Maximize Horizontal", i18n("Maximize Window Horizontally"),
|
|
0, &Workspace::slotWindowMaximizeHorizontal);
|
|
initShortcut("Window Minimize", i18n("Minimize Window"),
|
|
Qt::META | Qt::Key_PageDown, &Workspace::slotWindowMinimize);
|
|
initShortcut("Window Shade", i18n("Shade Window"),
|
|
0, &Workspace::slotWindowShade);
|
|
initShortcut("Window Move", i18n("Move Window"),
|
|
0, &Workspace::slotWindowMove);
|
|
initShortcut("Window Resize", i18n("Resize Window"),
|
|
0, &Workspace::slotWindowResize);
|
|
initShortcut("Window Raise", i18n("Raise Window"),
|
|
0, &Workspace::slotWindowRaise);
|
|
initShortcut("Window Lower", i18n("Lower Window"),
|
|
0, &Workspace::slotWindowLower);
|
|
initShortcut("Toggle Window Raise/Lower", i18n("Toggle Window Raise/Lower"),
|
|
0, &Workspace::slotWindowRaiseOrLower);
|
|
initShortcut("Window Fullscreen", i18n("Make Window Fullscreen"),
|
|
0, &Workspace::slotWindowFullScreen);
|
|
initShortcut("Window No Border", i18n("Toggle Window Titlebar and Frame"),
|
|
0, &Workspace::slotWindowNoBorder);
|
|
initShortcut("Window Above Other Windows", i18n("Keep Window Above Others"),
|
|
0, &Workspace::slotWindowAbove);
|
|
initShortcut("Window Below Other Windows", i18n("Keep Window Below Others"),
|
|
0, &Workspace::slotWindowBelow);
|
|
initShortcut("Activate Window Demanding Attention", i18n("Activate Window Demanding Attention"),
|
|
Qt::META | Qt::CTRL | Qt::Key_A, &Workspace::slotActivateAttentionWindow);
|
|
initShortcut("Setup Window Shortcut", i18n("Setup Window Shortcut"),
|
|
0, &Workspace::slotSetupWindowShortcut);
|
|
initShortcut("Window Move Center", i18n("Move Window to the Center"), 0,
|
|
&Workspace::slotWindowCenter);
|
|
initShortcut("Window Pack Right", i18n("Move Window Right"),
|
|
0, &Workspace::slotWindowMoveRight);
|
|
initShortcut("Window Pack Left", i18n("Move Window Left"),
|
|
0, &Workspace::slotWindowMoveLeft);
|
|
initShortcut("Window Pack Up", i18n("Move Window Up"),
|
|
0, &Workspace::slotWindowMoveUp);
|
|
initShortcut("Window Pack Down", i18n("Move Window Down"),
|
|
0, &Workspace::slotWindowMoveDown);
|
|
initShortcut("Window Grow Horizontal", i18n("Expand Window Horizontally"),
|
|
0, &Workspace::slotWindowExpandHorizontal);
|
|
initShortcut("Window Grow Vertical", i18n("Expand Window Vertically"),
|
|
0, &Workspace::slotWindowExpandVertical);
|
|
initShortcut("Window Shrink Horizontal", i18n("Shrink Window Horizontally"),
|
|
0, &Workspace::slotWindowShrinkHorizontal);
|
|
initShortcut("Window Shrink Vertical", i18n("Shrink Window Vertically"),
|
|
0, &Workspace::slotWindowShrinkVertical);
|
|
initShortcut("Window Quick Tile Left", i18n("Quick Tile Window to the Left"),
|
|
Qt::META | Qt::Key_Left, std::bind(&Workspace::quickTileWindow, this, QuickTileFlag::Left));
|
|
initShortcut("Window Quick Tile Right", i18n("Quick Tile Window to the Right"),
|
|
Qt::META | Qt::Key_Right, std::bind(&Workspace::quickTileWindow, this, QuickTileFlag::Right));
|
|
initShortcut("Window Quick Tile Top", i18n("Quick Tile Window to the Top"),
|
|
Qt::META | Qt::Key_Up, std::bind(&Workspace::quickTileWindow, this, QuickTileFlag::Top));
|
|
initShortcut("Window Quick Tile Bottom", i18n("Quick Tile Window to the Bottom"),
|
|
Qt::META | Qt::Key_Down, std::bind(&Workspace::quickTileWindow, this, QuickTileFlag::Bottom));
|
|
initShortcut("Window Quick Tile Top Left", i18n("Quick Tile Window to the Top Left"),
|
|
0, std::bind(&Workspace::quickTileWindow, this, QuickTileFlag::Top | QuickTileFlag::Left));
|
|
initShortcut("Window Quick Tile Bottom Left", i18n("Quick Tile Window to the Bottom Left"),
|
|
0, std::bind(&Workspace::quickTileWindow, this, QuickTileFlag::Bottom | QuickTileFlag::Left));
|
|
initShortcut("Window Quick Tile Top Right", i18n("Quick Tile Window to the Top Right"),
|
|
0, std::bind(&Workspace::quickTileWindow, this, QuickTileFlag::Top | QuickTileFlag::Right));
|
|
initShortcut("Window Quick Tile Bottom Right", i18n("Quick Tile Window to the Bottom Right"),
|
|
0, std::bind(&Workspace::quickTileWindow, this, QuickTileFlag::Bottom | QuickTileFlag::Right));
|
|
initShortcut("Switch Window Up", i18n("Switch to Window Above"),
|
|
Qt::META | Qt::ALT | Qt::Key_Up, std::bind(static_cast<void (Workspace::*)(Direction)>(&Workspace::switchWindow), this, DirectionNorth));
|
|
initShortcut("Switch Window Down", i18n("Switch to Window Below"),
|
|
Qt::META | Qt::ALT | Qt::Key_Down, std::bind(static_cast<void (Workspace::*)(Direction)>(&Workspace::switchWindow), this, DirectionSouth));
|
|
initShortcut("Switch Window Right", i18n("Switch to Window to the Right"),
|
|
Qt::META | Qt::ALT | Qt::Key_Right, std::bind(static_cast<void (Workspace::*)(Direction)>(&Workspace::switchWindow), this, DirectionEast));
|
|
initShortcut("Switch Window Left", i18n("Switch to Window to the Left"),
|
|
Qt::META | Qt::ALT | Qt::Key_Left, std::bind(static_cast<void (Workspace::*)(Direction)>(&Workspace::switchWindow), this, DirectionWest));
|
|
initShortcut("Increase Opacity", i18n("Increase Opacity of Active Window by 5%"),
|
|
0, &Workspace::slotIncreaseWindowOpacity);
|
|
initShortcut("Decrease Opacity", i18n("Decrease Opacity of Active Window by 5%"),
|
|
0, &Workspace::slotLowerWindowOpacity);
|
|
|
|
initShortcut("Window On All Desktops", i18n("Keep Window on All Desktops"),
|
|
0, &Workspace::slotWindowOnAllDesktops);
|
|
|
|
VirtualDesktopManager *vds = VirtualDesktopManager::self();
|
|
for (uint i = 0; i < vds->maximum(); ++i) {
|
|
auto handler = [this, i]() {
|
|
const QList<VirtualDesktop *> desktops = VirtualDesktopManager::self()->desktops();
|
|
if (i < uint(desktops.count())) {
|
|
slotWindowToDesktop(desktops[i]);
|
|
}
|
|
};
|
|
initShortcut(QStringLiteral("Window to Desktop %1").arg(i + 1), i18n("Window to Desktop %1", i + 1), 0, handler);
|
|
}
|
|
initShortcut("Window to Next Desktop", i18n("Window to Next Desktop"), 0, &Workspace::slotWindowToNextDesktop);
|
|
initShortcut("Window to Previous Desktop", i18n("Window to Previous Desktop"), 0, &Workspace::slotWindowToPreviousDesktop);
|
|
initShortcut("Window One Desktop to the Right", i18n("Window One Desktop to the Right"),
|
|
Qt::META | Qt::CTRL | Qt::SHIFT | Qt::Key_Right, &Workspace::slotWindowToDesktopRight);
|
|
initShortcut("Window One Desktop to the Left", i18n("Window One Desktop to the Left"),
|
|
Qt::META | Qt::CTRL | Qt::SHIFT | Qt::Key_Left, &Workspace::slotWindowToDesktopLeft);
|
|
initShortcut("Window One Desktop Up", i18n("Window One Desktop Up"),
|
|
Qt::META | Qt::CTRL | Qt::SHIFT | Qt::Key_Up, &Workspace::slotWindowToDesktopUp);
|
|
initShortcut("Window One Desktop Down", i18n("Window One Desktop Down"),
|
|
Qt::META | Qt::CTRL | Qt::SHIFT | Qt::Key_Down, &Workspace::slotWindowToDesktopDown);
|
|
|
|
for (int i = 0; i < 8; ++i) {
|
|
initShortcut(QStringLiteral("Window to Screen %1").arg(i), i18n("Move Window to Screen %1", i), 0, [this, i]() {
|
|
Output *output = outputs().value(i);
|
|
if (output) {
|
|
slotWindowToScreen(output);
|
|
}
|
|
});
|
|
}
|
|
initShortcut("Window to Next Screen", i18n("Move Window to Next Screen"),
|
|
Qt::META | Qt::SHIFT | Qt::Key_Right, &Workspace::slotWindowToNextScreen);
|
|
initShortcut("Window to Previous Screen", i18n("Move Window to Previous Screen"),
|
|
Qt::META | Qt::SHIFT | Qt::Key_Left, &Workspace::slotWindowToPrevScreen);
|
|
initShortcut("Window One Screen to the Right", i18n("Move Window One Screen to the Right"),
|
|
0, &Workspace::slotWindowToRightScreen);
|
|
initShortcut("Window One Screen to the Left", i18n("Move Window One Screen to the Left"),
|
|
0, &Workspace::slotWindowToLeftScreen);
|
|
initShortcut("Window One Screen Up", i18n("Move Window One Screen Up"),
|
|
0, &Workspace::slotWindowToAboveScreen);
|
|
initShortcut("Window One Screen Down", i18n("Move Window One Screen Down"),
|
|
0, &Workspace::slotWindowToBelowScreen);
|
|
|
|
for (int i = 0; i < 8; ++i) {
|
|
initShortcut(QStringLiteral("Switch to Screen %1").arg(i), i18n("Switch to Screen %1", i), 0, [this, i]() {
|
|
Output *output = outputs().value(i);
|
|
if (output) {
|
|
slotSwitchToScreen(output);
|
|
}
|
|
});
|
|
}
|
|
initShortcut("Switch to Next Screen", i18n("Switch to Next Screen"), 0, &Workspace::slotSwitchToNextScreen);
|
|
initShortcut("Switch to Previous Screen", i18n("Switch to Previous Screen"), 0, &Workspace::slotSwitchToPrevScreen);
|
|
initShortcut("Switch to Screen to the Right", i18n("Switch to Screen to the Right"),
|
|
0, &Workspace::slotSwitchToRightScreen);
|
|
initShortcut("Switch to Screen to the Left", i18n("Switch to Screen to the Left"),
|
|
0, &Workspace::slotSwitchToLeftScreen);
|
|
initShortcut("Switch to Screen Above", i18n("Switch to Screen Above"),
|
|
0, &Workspace::slotSwitchToAboveScreen);
|
|
initShortcut("Switch to Screen Below", i18n("Switch to Screen Below"),
|
|
0, &Workspace::slotSwitchToBelowScreen);
|
|
|
|
initShortcut("Show Desktop", i18n("Peek at Desktop"),
|
|
Qt::META | Qt::Key_D, &Workspace::slotToggleShowDesktop);
|
|
|
|
initShortcut("Kill Window", i18n("Kill Window"), Qt::META | Qt::CTRL | Qt::Key_Escape, &Workspace::slotKillWindow);
|
|
|
|
#if KWIN_BUILD_TABBOX
|
|
m_tabbox->initShortcuts();
|
|
#endif
|
|
vds->initShortcuts();
|
|
m_userActionsMenu->discard(); // so that it's recreated next time
|
|
}
|
|
|
|
void Workspace::setupWindowShortcut(Window *window)
|
|
{
|
|
Q_ASSERT(m_windowKeysDialog == nullptr);
|
|
// TODO: PORT ME (KGlobalAccel related)
|
|
// keys->setEnabled( false );
|
|
// disable_shortcuts_keys->setEnabled( false );
|
|
// client_keys->setEnabled( false );
|
|
m_windowKeysDialog = new ShortcutDialog(window->shortcut());
|
|
m_windowKeysWindow = window;
|
|
connect(m_windowKeysDialog, &ShortcutDialog::dialogDone, this, &Workspace::setupWindowShortcutDone);
|
|
QRect r = clientArea(ScreenArea, window).toRect();
|
|
QSize size = m_windowKeysDialog->sizeHint();
|
|
QPointF pos(window->frameGeometry().left() + window->frameMargins().left(),
|
|
window->frameGeometry().top() + window->frameMargins().top());
|
|
if (pos.x() + size.width() >= r.right()) {
|
|
pos.setX(r.right() - size.width());
|
|
}
|
|
if (pos.y() + size.height() >= r.bottom()) {
|
|
pos.setY(r.bottom() - size.height());
|
|
}
|
|
m_windowKeysDialog->move(pos.toPoint());
|
|
m_windowKeysDialog->show();
|
|
active_popup = m_windowKeysDialog;
|
|
m_activePopupWindow = window;
|
|
}
|
|
|
|
void Workspace::setupWindowShortcutDone(bool ok)
|
|
{
|
|
// keys->setEnabled( true );
|
|
// disable_shortcuts_keys->setEnabled( true );
|
|
// client_keys->setEnabled( true );
|
|
if (ok) {
|
|
m_windowKeysWindow->setShortcut(m_windowKeysDialog->shortcut().toString());
|
|
}
|
|
closeActivePopup();
|
|
m_windowKeysDialog->deleteLater();
|
|
m_windowKeysDialog = nullptr;
|
|
m_windowKeysWindow = nullptr;
|
|
if (m_activeWindow) {
|
|
m_activeWindow->takeFocus();
|
|
}
|
|
}
|
|
|
|
void Workspace::windowShortcutUpdated(Window *window)
|
|
{
|
|
QString key = QStringLiteral("_k_session:%1").arg(window->internalId().toString());
|
|
QAction *action = findChild<QAction *>(key);
|
|
if (!window->shortcut().isEmpty()) {
|
|
if (action == nullptr) { // new shortcut
|
|
action = new QAction(this);
|
|
action->setProperty("componentName", QStringLiteral("kwin"));
|
|
action->setObjectName(key);
|
|
action->setText(i18n("Activate Window (%1)", window->caption()));
|
|
connect(action, &QAction::triggered, window, std::bind(&Workspace::activateWindow, this, window, true));
|
|
}
|
|
|
|
// no autoloading, since it's configured explicitly here and is not meant to be reused
|
|
// (the key is the window id anyway, which is kind of random)
|
|
KGlobalAccel::self()->setShortcut(action, QList<QKeySequence>() << window->shortcut(),
|
|
KGlobalAccel::NoAutoloading);
|
|
action->setEnabled(true);
|
|
} else {
|
|
KGlobalAccel::self()->removeAllShortcuts(action);
|
|
delete action;
|
|
}
|
|
}
|
|
|
|
void Workspace::performWindowOperation(Window *window, Options::WindowOperation op)
|
|
{
|
|
if (!window) {
|
|
return;
|
|
}
|
|
if (op == Options::MoveOp || op == Options::UnrestrictedMoveOp) {
|
|
Cursors::self()->mouse()->setPos(window->frameGeometry().center());
|
|
}
|
|
if (op == Options::ResizeOp || op == Options::UnrestrictedResizeOp) {
|
|
Cursors::self()->mouse()->setPos(window->frameGeometry().bottomRight());
|
|
}
|
|
switch (op) {
|
|
case Options::MoveOp:
|
|
window->performMouseCommand(Options::MouseMove, Cursors::self()->mouse()->pos());
|
|
break;
|
|
case Options::UnrestrictedMoveOp:
|
|
window->performMouseCommand(Options::MouseUnrestrictedMove, Cursors::self()->mouse()->pos());
|
|
break;
|
|
case Options::ResizeOp:
|
|
window->performMouseCommand(Options::MouseResize, Cursors::self()->mouse()->pos());
|
|
break;
|
|
case Options::UnrestrictedResizeOp:
|
|
window->performMouseCommand(Options::MouseUnrestrictedResize, Cursors::self()->mouse()->pos());
|
|
break;
|
|
case Options::CloseOp:
|
|
QMetaObject::invokeMethod(window, &Window::closeWindow, Qt::QueuedConnection);
|
|
break;
|
|
case Options::MaximizeOp:
|
|
window->maximize(window->maximizeMode() == MaximizeFull
|
|
? MaximizeRestore
|
|
: MaximizeFull);
|
|
takeActivity(window, ActivityFocus | ActivityRaise);
|
|
break;
|
|
case Options::HMaximizeOp:
|
|
window->maximize(window->maximizeMode() ^ MaximizeHorizontal);
|
|
takeActivity(window, ActivityFocus | ActivityRaise);
|
|
break;
|
|
case Options::VMaximizeOp:
|
|
window->maximize(window->maximizeMode() ^ MaximizeVertical);
|
|
takeActivity(window, ActivityFocus | ActivityRaise);
|
|
break;
|
|
case Options::RestoreOp:
|
|
window->maximize(MaximizeRestore);
|
|
takeActivity(window, ActivityFocus | ActivityRaise);
|
|
break;
|
|
case Options::MinimizeOp:
|
|
window->setMinimized(true);
|
|
break;
|
|
case Options::ShadeOp:
|
|
window->performMouseCommand(Options::MouseShade, Cursors::self()->mouse()->pos());
|
|
break;
|
|
case Options::OnAllDesktopsOp:
|
|
window->setOnAllDesktops(!window->isOnAllDesktops());
|
|
break;
|
|
case Options::FullScreenOp:
|
|
window->setFullScreen(!window->isFullScreen());
|
|
break;
|
|
case Options::NoBorderOp:
|
|
if (window->userCanSetNoBorder()) {
|
|
window->setNoBorder(!window->noBorder());
|
|
}
|
|
break;
|
|
case Options::KeepAboveOp: {
|
|
StackingUpdatesBlocker blocker(this);
|
|
bool was = window->keepAbove();
|
|
window->setKeepAbove(!window->keepAbove());
|
|
if (was && !window->keepAbove()) {
|
|
raiseWindow(window);
|
|
}
|
|
break;
|
|
}
|
|
case Options::KeepBelowOp: {
|
|
StackingUpdatesBlocker blocker(this);
|
|
bool was = window->keepBelow();
|
|
window->setKeepBelow(!window->keepBelow());
|
|
if (was && !window->keepBelow()) {
|
|
lowerWindow(window);
|
|
}
|
|
break;
|
|
}
|
|
case Options::OperationsOp:
|
|
window->performMouseCommand(Options::MouseShade, Cursors::self()->mouse()->pos());
|
|
break;
|
|
case Options::WindowRulesOp:
|
|
m_rulebook->edit(window, false);
|
|
break;
|
|
case Options::ApplicationRulesOp:
|
|
m_rulebook->edit(window, true);
|
|
break;
|
|
case Options::SetupWindowShortcutOp:
|
|
setupWindowShortcut(window);
|
|
break;
|
|
case Options::LowerOp:
|
|
lowerWindow(window);
|
|
break;
|
|
case Options::NoOp:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Workspace::slotActivateAttentionWindow()
|
|
{
|
|
if (attention_chain.count() > 0) {
|
|
activateWindow(attention_chain.first());
|
|
}
|
|
}
|
|
|
|
#define USABLE_ACTIVE_WINDOW (m_activeWindow && !(m_activeWindow->isDesktop() || m_activeWindow->isDock()))
|
|
|
|
void Workspace::slotWindowToDesktop(VirtualDesktop *desktop)
|
|
{
|
|
if (USABLE_ACTIVE_WINDOW) {
|
|
sendWindowToDesktops(m_activeWindow, {desktop}, true);
|
|
}
|
|
}
|
|
|
|
static bool screenSwitchImpossible()
|
|
{
|
|
if (!options->activeMouseScreen()) {
|
|
return false;
|
|
}
|
|
QStringList args;
|
|
args << QStringLiteral("--passivepopup") << i18n("The window manager is configured to consider the screen with the mouse on it as active one.\n"
|
|
"Therefore it is not possible to switch to a screen explicitly.")
|
|
<< QStringLiteral("20");
|
|
KProcess::startDetached(QStringLiteral("kdialog"), args);
|
|
return true;
|
|
}
|
|
|
|
void Workspace::slotSwitchToScreen(Output *output)
|
|
{
|
|
if (!screenSwitchImpossible()) {
|
|
switchToOutput(output);
|
|
}
|
|
}
|
|
|
|
void Workspace::slotSwitchToLeftScreen()
|
|
{
|
|
if (!screenSwitchImpossible()) {
|
|
switchToOutput(findOutput(activeOutput(), Direction::DirectionWest, true));
|
|
}
|
|
}
|
|
|
|
void Workspace::slotSwitchToRightScreen()
|
|
{
|
|
if (!screenSwitchImpossible()) {
|
|
switchToOutput(findOutput(activeOutput(), Direction::DirectionEast, true));
|
|
}
|
|
}
|
|
|
|
void Workspace::slotSwitchToAboveScreen()
|
|
{
|
|
if (!screenSwitchImpossible()) {
|
|
switchToOutput(findOutput(activeOutput(), Direction::DirectionNorth, true));
|
|
}
|
|
}
|
|
|
|
void Workspace::slotSwitchToBelowScreen()
|
|
{
|
|
if (!screenSwitchImpossible()) {
|
|
switchToOutput(findOutput(activeOutput(), Direction::DirectionSouth, true));
|
|
}
|
|
}
|
|
|
|
void Workspace::slotSwitchToPrevScreen()
|
|
{
|
|
if (!screenSwitchImpossible()) {
|
|
switchToOutput(findOutput(activeOutput(), Direction::DirectionPrev, true));
|
|
}
|
|
}
|
|
|
|
void Workspace::slotSwitchToNextScreen()
|
|
{
|
|
if (!screenSwitchImpossible()) {
|
|
switchToOutput(findOutput(activeOutput(), Direction::DirectionNext, true));
|
|
}
|
|
}
|
|
|
|
void Workspace::slotWindowToScreen(Output *output)
|
|
{
|
|
if (USABLE_ACTIVE_WINDOW) {
|
|
sendWindowToOutput(m_activeWindow, output);
|
|
}
|
|
}
|
|
|
|
void Workspace::slotWindowToLeftScreen()
|
|
{
|
|
if (USABLE_ACTIVE_WINDOW) {
|
|
sendWindowToOutput(m_activeWindow, findOutput(m_activeWindow->output(), Direction::DirectionWest, true));
|
|
}
|
|
}
|
|
|
|
void Workspace::slotWindowToRightScreen()
|
|
{
|
|
if (USABLE_ACTIVE_WINDOW) {
|
|
sendWindowToOutput(m_activeWindow, findOutput(m_activeWindow->output(), Direction::DirectionEast, true));
|
|
}
|
|
}
|
|
|
|
void Workspace::slotWindowToAboveScreen()
|
|
{
|
|
if (USABLE_ACTIVE_WINDOW) {
|
|
sendWindowToOutput(m_activeWindow, findOutput(m_activeWindow->output(), Direction::DirectionNorth, true));
|
|
}
|
|
}
|
|
|
|
void Workspace::slotWindowToBelowScreen()
|
|
{
|
|
if (USABLE_ACTIVE_WINDOW) {
|
|
sendWindowToOutput(m_activeWindow, findOutput(m_activeWindow->output(), Direction::DirectionSouth, true));
|
|
}
|
|
}
|
|
|
|
void Workspace::slotWindowToPrevScreen()
|
|
{
|
|
if (USABLE_ACTIVE_WINDOW) {
|
|
sendWindowToOutput(m_activeWindow, findOutput(m_activeWindow->output(), Direction::DirectionPrev, true));
|
|
}
|
|
}
|
|
|
|
void Workspace::slotWindowToNextScreen()
|
|
{
|
|
if (USABLE_ACTIVE_WINDOW) {
|
|
sendWindowToOutput(m_activeWindow, findOutput(m_activeWindow->output(), Direction::DirectionNext, true));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Maximizes the active window.
|
|
*/
|
|
void Workspace::slotWindowMaximize()
|
|
{
|
|
if (USABLE_ACTIVE_WINDOW) {
|
|
performWindowOperation(m_activeWindow, Options::MaximizeOp);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Maximizes the active window vertically.
|
|
*/
|
|
void Workspace::slotWindowMaximizeVertical()
|
|
{
|
|
if (USABLE_ACTIVE_WINDOW) {
|
|
performWindowOperation(m_activeWindow, Options::VMaximizeOp);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Maximizes the active window horiozontally.
|
|
*/
|
|
void Workspace::slotWindowMaximizeHorizontal()
|
|
{
|
|
if (USABLE_ACTIVE_WINDOW) {
|
|
performWindowOperation(m_activeWindow, Options::HMaximizeOp);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Minimizes the active window.
|
|
*/
|
|
void Workspace::slotWindowMinimize()
|
|
{
|
|
if (USABLE_ACTIVE_WINDOW) {
|
|
performWindowOperation(m_activeWindow, Options::MinimizeOp);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Shades/unshades the active window respectively.
|
|
*/
|
|
void Workspace::slotWindowShade()
|
|
{
|
|
if (USABLE_ACTIVE_WINDOW) {
|
|
performWindowOperation(m_activeWindow, Options::ShadeOp);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Raises the active window.
|
|
*/
|
|
void Workspace::slotWindowRaise()
|
|
{
|
|
if (USABLE_ACTIVE_WINDOW) {
|
|
raiseWindow(m_activeWindow);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Lowers the active window.
|
|
*/
|
|
void Workspace::slotWindowLower()
|
|
{
|
|
if (USABLE_ACTIVE_WINDOW) {
|
|
lowerWindow(m_activeWindow);
|
|
// As this most likely makes the window no longer visible change the
|
|
// keyboard focus to the next available window.
|
|
// activateNextWindow( c ); // Doesn't work when we lower a child window
|
|
if (m_activeWindow->isActive() && options->focusPolicyIsReasonable()) {
|
|
if (options->isNextFocusPrefersMouse()) {
|
|
Window *next = windowUnderMouse(m_activeWindow->output());
|
|
if (next && next != m_activeWindow) {
|
|
requestFocus(next, false);
|
|
}
|
|
} else {
|
|
activateWindow(topWindowOnDesktop(VirtualDesktopManager::self()->currentDesktop()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Does a toggle-raise-and-lower on the active window.
|
|
*/
|
|
void Workspace::slotWindowRaiseOrLower()
|
|
{
|
|
if (USABLE_ACTIVE_WINDOW) {
|
|
raiseOrLowerWindow(m_activeWindow);
|
|
}
|
|
}
|
|
|
|
void Workspace::slotWindowOnAllDesktops()
|
|
{
|
|
if (USABLE_ACTIVE_WINDOW) {
|
|
m_activeWindow->setOnAllDesktops(!m_activeWindow->isOnAllDesktops());
|
|
}
|
|
}
|
|
|
|
void Workspace::slotWindowFullScreen()
|
|
{
|
|
if (USABLE_ACTIVE_WINDOW) {
|
|
performWindowOperation(m_activeWindow, Options::FullScreenOp);
|
|
}
|
|
}
|
|
|
|
void Workspace::slotWindowNoBorder()
|
|
{
|
|
if (USABLE_ACTIVE_WINDOW) {
|
|
performWindowOperation(m_activeWindow, Options::NoBorderOp);
|
|
}
|
|
}
|
|
|
|
void Workspace::slotWindowAbove()
|
|
{
|
|
if (USABLE_ACTIVE_WINDOW) {
|
|
performWindowOperation(m_activeWindow, Options::KeepAboveOp);
|
|
}
|
|
}
|
|
|
|
void Workspace::slotWindowBelow()
|
|
{
|
|
if (USABLE_ACTIVE_WINDOW) {
|
|
performWindowOperation(m_activeWindow, Options::KeepBelowOp);
|
|
}
|
|
}
|
|
void Workspace::slotSetupWindowShortcut()
|
|
{
|
|
if (USABLE_ACTIVE_WINDOW) {
|
|
performWindowOperation(m_activeWindow, Options::SetupWindowShortcutOp);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Toggles show desktop.
|
|
*/
|
|
void Workspace::slotToggleShowDesktop()
|
|
{
|
|
setShowingDesktop(!showingDesktop());
|
|
}
|
|
|
|
void windowToDesktop(Window *window, VirtualDesktopManager::Direction direction)
|
|
{
|
|
VirtualDesktopManager *vds = VirtualDesktopManager::self();
|
|
Workspace *ws = Workspace::self();
|
|
// TODO: why is options->isRollOverDesktops() not honored?
|
|
const auto desktop = vds->inDirection(nullptr, direction, true);
|
|
if (window && !window->isDesktop()
|
|
&& !window->isDock()) {
|
|
ws->setMoveResizeWindow(window);
|
|
vds->setCurrent(desktop);
|
|
ws->setMoveResizeWindow(nullptr);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Moves the active window to the next desktop.
|
|
*/
|
|
void Workspace::slotWindowToNextDesktop()
|
|
{
|
|
if (USABLE_ACTIVE_WINDOW) {
|
|
windowToNextDesktop(m_activeWindow);
|
|
}
|
|
}
|
|
|
|
void Workspace::windowToNextDesktop(Window *window)
|
|
{
|
|
windowToDesktop(window, VirtualDesktopManager::Direction::Next);
|
|
}
|
|
|
|
/**
|
|
* Moves the active window to the previous desktop.
|
|
*/
|
|
void Workspace::slotWindowToPreviousDesktop()
|
|
{
|
|
if (USABLE_ACTIVE_WINDOW) {
|
|
windowToPreviousDesktop(m_activeWindow);
|
|
}
|
|
}
|
|
|
|
void Workspace::windowToPreviousDesktop(Window *window)
|
|
{
|
|
windowToDesktop(window, VirtualDesktopManager::Direction::Previous);
|
|
}
|
|
|
|
void activeWindowToDesktop(VirtualDesktopManager::Direction direction)
|
|
{
|
|
VirtualDesktopManager *vds = VirtualDesktopManager::self();
|
|
Workspace *ws = Workspace::self();
|
|
VirtualDesktop *current = vds->currentDesktop();
|
|
VirtualDesktop *newCurrent = VirtualDesktopManager::self()->inDirection(current, direction, options->isRollOverDesktops());
|
|
if (newCurrent == current) {
|
|
return;
|
|
}
|
|
ws->setMoveResizeWindow(ws->activeWindow());
|
|
vds->setCurrent(newCurrent);
|
|
ws->setMoveResizeWindow(nullptr);
|
|
}
|
|
|
|
void Workspace::slotWindowToDesktopRight()
|
|
{
|
|
if (USABLE_ACTIVE_WINDOW) {
|
|
activeWindowToDesktop(VirtualDesktopManager::Direction::Right);
|
|
}
|
|
}
|
|
|
|
void Workspace::slotWindowToDesktopLeft()
|
|
{
|
|
if (USABLE_ACTIVE_WINDOW) {
|
|
activeWindowToDesktop(VirtualDesktopManager::Direction::Left);
|
|
}
|
|
}
|
|
|
|
void Workspace::slotWindowToDesktopUp()
|
|
{
|
|
if (USABLE_ACTIVE_WINDOW) {
|
|
activeWindowToDesktop(VirtualDesktopManager::Direction::Up);
|
|
}
|
|
}
|
|
|
|
void Workspace::slotWindowToDesktopDown()
|
|
{
|
|
if (USABLE_ACTIVE_WINDOW) {
|
|
activeWindowToDesktop(VirtualDesktopManager::Direction::Down);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Kill Window feature, similar to xkill.
|
|
*/
|
|
void Workspace::slotKillWindow()
|
|
{
|
|
if (!m_windowKiller) {
|
|
m_windowKiller = std::make_unique<KillWindow>();
|
|
}
|
|
m_windowKiller->start();
|
|
}
|
|
|
|
/**
|
|
* Switches to the nearest window in given direction.
|
|
*/
|
|
void Workspace::switchWindow(Direction direction)
|
|
{
|
|
if (!m_activeWindow) {
|
|
return;
|
|
}
|
|
Window *window = m_activeWindow;
|
|
VirtualDesktop *desktop = VirtualDesktopManager::self()->currentDesktop();
|
|
|
|
// Centre of the active window
|
|
QPoint curPos(window->x() + window->width() / 2, window->y() + window->height() / 2);
|
|
|
|
if (!switchWindow(window, direction, curPos, desktop)) {
|
|
auto opposite = [&] {
|
|
switch (direction) {
|
|
case DirectionNorth:
|
|
return QPoint(curPos.x(), geometry().height());
|
|
case DirectionSouth:
|
|
return QPoint(curPos.x(), 0);
|
|
case DirectionEast:
|
|
return QPoint(0, curPos.y());
|
|
case DirectionWest:
|
|
return QPoint(geometry().width(), curPos.y());
|
|
default:
|
|
Q_UNREACHABLE();
|
|
}
|
|
};
|
|
|
|
switchWindow(window, direction, opposite(), desktop);
|
|
}
|
|
}
|
|
|
|
bool Workspace::switchWindow(Window *window, Direction direction, QPoint curPos, VirtualDesktop *desktop)
|
|
{
|
|
Window *switchTo = nullptr;
|
|
int bestScore = 0;
|
|
|
|
QList<Window *> clist = stackingOrder();
|
|
for (auto i = clist.rbegin(); i != clist.rend(); ++i) {
|
|
auto other = *i;
|
|
if (!other->isClient()) {
|
|
continue;
|
|
}
|
|
if (other->wantsTabFocus() && *i != window && other->isOnDesktop(desktop) && !other->isMinimized() && (*i)->isOnCurrentActivity()) {
|
|
// Centre of the other window
|
|
const QPoint otherCenter(other->x() + other->width() / 2, other->y() + other->height() / 2);
|
|
|
|
int distance;
|
|
int offset;
|
|
switch (direction) {
|
|
case DirectionNorth:
|
|
distance = curPos.y() - otherCenter.y();
|
|
offset = std::abs(otherCenter.x() - curPos.x());
|
|
break;
|
|
case DirectionEast:
|
|
distance = otherCenter.x() - curPos.x();
|
|
offset = std::abs(otherCenter.y() - curPos.y());
|
|
break;
|
|
case DirectionSouth:
|
|
distance = otherCenter.y() - curPos.y();
|
|
offset = std::abs(otherCenter.x() - curPos.x());
|
|
break;
|
|
case DirectionWest:
|
|
distance = curPos.x() - otherCenter.x();
|
|
offset = std::abs(otherCenter.y() - curPos.y());
|
|
break;
|
|
default:
|
|
distance = -1;
|
|
offset = -1;
|
|
}
|
|
|
|
if (distance > 0) {
|
|
// Inverse score
|
|
int score = distance + offset + ((offset * offset) / distance);
|
|
if (score < bestScore || !switchTo) {
|
|
switchTo = other;
|
|
bestScore = score;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (switchTo) {
|
|
activateWindow(switchTo);
|
|
}
|
|
|
|
return switchTo;
|
|
}
|
|
|
|
/**
|
|
* Shows the window operations popup menu for the active window.
|
|
*/
|
|
void Workspace::slotWindowOperations()
|
|
{
|
|
if (!m_activeWindow) {
|
|
return;
|
|
}
|
|
const QPoint pos(m_activeWindow->frameGeometry().left() + m_activeWindow->frameMargins().left(),
|
|
m_activeWindow->frameGeometry().top() + m_activeWindow->frameMargins().top());
|
|
showWindowMenu(QRect(pos, pos), m_activeWindow);
|
|
}
|
|
|
|
void Workspace::showWindowMenu(const QRect &pos, Window *window)
|
|
{
|
|
m_userActionsMenu->show(pos, window);
|
|
}
|
|
|
|
void Workspace::showApplicationMenu(const QRect &pos, Window *window, int actionId)
|
|
{
|
|
Workspace::self()->applicationMenu()->showApplicationMenu(window->pos().toPoint() + pos.bottomLeft(), window, actionId);
|
|
}
|
|
|
|
/**
|
|
* Closes the active window.
|
|
*/
|
|
void Workspace::slotWindowClose()
|
|
{
|
|
// TODO: why?
|
|
// if ( tab_box->isVisible())
|
|
// return;
|
|
if (USABLE_ACTIVE_WINDOW) {
|
|
performWindowOperation(m_activeWindow, Options::CloseOp);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Starts keyboard move mode for the active window.
|
|
*/
|
|
void Workspace::slotWindowMove()
|
|
{
|
|
if (USABLE_ACTIVE_WINDOW) {
|
|
performWindowOperation(m_activeWindow, Options::UnrestrictedMoveOp);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Starts keyboard resize mode for the active window.
|
|
*/
|
|
void Workspace::slotWindowResize()
|
|
{
|
|
if (USABLE_ACTIVE_WINDOW) {
|
|
performWindowOperation(m_activeWindow, Options::UnrestrictedResizeOp);
|
|
}
|
|
}
|
|
|
|
#undef USABLE_ACTIVE_WINDOW
|
|
|
|
void Window::setShortcut(const QString &_cut)
|
|
{
|
|
QString cut = rules()->checkShortcut(_cut);
|
|
auto updateShortcut = [this](const QKeySequence &cut = QKeySequence()) {
|
|
if (_shortcut == cut) {
|
|
return;
|
|
}
|
|
_shortcut = cut;
|
|
setShortcutInternal();
|
|
};
|
|
if (cut.isEmpty()) {
|
|
updateShortcut();
|
|
return;
|
|
}
|
|
if (cut == shortcut().toString()) {
|
|
return; // no change
|
|
}
|
|
// Format:
|
|
// base+(abcdef)<space>base+(abcdef)
|
|
// E.g. Alt+Ctrl+(ABCDEF);Meta+X,Meta+(ABCDEF)
|
|
if (!cut.contains(QLatin1Char('(')) && !cut.contains(QLatin1Char(')')) && !cut.contains(QLatin1String(" - "))) {
|
|
if (workspace()->shortcutAvailable(cut, this)) {
|
|
updateShortcut(QKeySequence(cut));
|
|
} else {
|
|
updateShortcut();
|
|
}
|
|
return;
|
|
}
|
|
static const QRegularExpression reg(QStringLiteral("(.*\\+)\\((.*)\\)"));
|
|
QList<QKeySequence> keys;
|
|
const QStringList groups = cut.split(QStringLiteral(" - "));
|
|
for (auto it = groups.begin(); it != groups.end(); ++it) {
|
|
const QRegularExpressionMatch match = reg.match(*it);
|
|
if (match.hasMatch()) {
|
|
const QString base = match.captured(1);
|
|
const QString list = match.captured(2);
|
|
for (int i = 0; i < list.length(); ++i) {
|
|
QKeySequence c(base + list[i]);
|
|
if (!c.isEmpty()) {
|
|
keys.append(c);
|
|
}
|
|
}
|
|
} else {
|
|
// regexp doesn't match, so it should be a normal shortcut
|
|
QKeySequence c(*it);
|
|
if (!c.isEmpty()) {
|
|
keys.append(c);
|
|
}
|
|
}
|
|
}
|
|
for (auto it = keys.constBegin(); it != keys.cend(); ++it) {
|
|
if (_shortcut == *it) { // current one is in the list
|
|
return;
|
|
}
|
|
}
|
|
for (auto it = keys.cbegin(); it != keys.cend(); ++it) {
|
|
if (workspace()->shortcutAvailable(*it, this)) {
|
|
updateShortcut(*it);
|
|
return;
|
|
}
|
|
}
|
|
updateShortcut();
|
|
}
|
|
|
|
void Window::setShortcutInternal()
|
|
{
|
|
updateCaption();
|
|
workspace()->windowShortcutUpdated(this);
|
|
}
|
|
|
|
void X11Window::setShortcutInternal()
|
|
{
|
|
updateCaption();
|
|
#if 0
|
|
workspace()->windowShortcutUpdated(this);
|
|
#else
|
|
// Workaround for kwin<->kglobalaccel deadlock, when KWin has X grab and the kded
|
|
// kglobalaccel module tries to create the key grab. KWin should preferably grab
|
|
// they keys itself anyway :(.
|
|
QTimer::singleShot(0, this, std::bind(&Workspace::windowShortcutUpdated, workspace(), this));
|
|
#endif
|
|
}
|
|
|
|
bool Workspace::shortcutAvailable(const QKeySequence &cut, Window *ignore) const
|
|
{
|
|
if (ignore && cut == ignore->shortcut()) {
|
|
return true;
|
|
}
|
|
|
|
// Check if the shortcut is already registered
|
|
const QList<KGlobalShortcutInfo> registeredShortcuts = KGlobalAccel::globalShortcutsByKey(cut);
|
|
for (const auto &shortcut : registeredShortcuts) {
|
|
// Only return "not available" if is not a window activation shortcut, as it may be no longer valid
|
|
if (!shortcut.uniqueName().startsWith(QStringLiteral("_k_session:"))) {
|
|
return false;
|
|
}
|
|
}
|
|
// Check now conflicts with activation shortcuts for current windows
|
|
for (const auto window : std::as_const(m_windows)) {
|
|
if (window != ignore && window->shortcut() == cut) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
#include "moc_useractions.cpp"
|