File indexing completed on 2024-05-05 05:56:59

0001 /*
0002   SPDX-FileCopyrightText: 2008-2014 Eike Hein <hein@kde.org>
0003   SPDX-FileCopyrightText: 2009 Juan Carlos Torres <carlosdgtorres@gmail.com>
0004   SPDX-FileCopyrightText: 2020 Ryan McCoskrie <work@ryanmccoskrie.me>
0005 
0006   SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0007 */
0008 
0009 #include "mainwindow.h"
0010 #include "config/appearancesettings.h"
0011 #include "config/windowsettings.h"
0012 #include "firstrundialog.h"
0013 #include "sessionstack.h"
0014 #include "settings.h"
0015 #include "skin.h"
0016 #include "tabbar.h"
0017 #include "terminal.h"
0018 #include "titlebar.h"
0019 #include "ui_behaviorsettings.h"
0020 
0021 #include <KAboutData>
0022 #include <KActionCollection>
0023 #include <KConfigDialog>
0024 #include <KGlobalAccel>
0025 #include <KHelpMenu>
0026 #include <KLocalizedString>
0027 #include <KMessageBox>
0028 #include <KNotification>
0029 #include <KNotifyConfigWidget>
0030 #include <KShortcutsDialog>
0031 #include <KStandardAction>
0032 #include <KStatusNotifierItem>
0033 #include <KToggleFullScreenAction>
0034 #include <KWindowEffects>
0035 #include <KWindowInfo>
0036 #include <KWindowSystem>
0037 #include <KX11Extras>
0038 
0039 #include <QApplication>
0040 #include <QDBusConnection>
0041 #include <QDBusPendingReply>
0042 #include <QDBusReply>
0043 #include <QMenu>
0044 #include <QPainter>
0045 #include <QScreen>
0046 #include <QWhatsThis>
0047 #include <QWindow>
0048 
0049 #if HAVE_X11
0050 #include <private/qtx11extras_p.h>
0051 
0052 #include <X11/Xlib.h>
0053 #include <fixx11h.h>
0054 #endif
0055 
0056 #if HAVE_KWAYLAND
0057 #include <KWayland/Client/connection_thread.h>
0058 #include <KWayland/Client/plasmashell.h>
0059 #include <KWayland/Client/registry.h>
0060 #include <KWayland/Client/surface.h>
0061 #endif
0062 
0063 MainWindow::MainWindow(QWidget *parent)
0064     : KMainWindow(parent, Qt::CustomizeWindowHint | Qt::FramelessWindowHint | Qt::Tool)
0065 {
0066     QDBusConnection::sessionBus().registerObject(QStringLiteral("/yakuake/window"), this, QDBusConnection::ExportScriptableSlots);
0067 
0068     setAttribute(Qt::WA_TranslucentBackground, true);
0069     setAttribute(Qt::WA_DeleteOnClose, false);
0070     setAttribute(Qt::WA_QuitOnClose, true);
0071 
0072     m_skin = new Skin();
0073     m_menu = new QMenu(this);
0074     m_helpMenu = new KHelpMenu(this, KAboutData::applicationData());
0075     m_sessionStack = new SessionStack(this);
0076     m_titleBar = new TitleBar(this);
0077     m_tabBar = new TabBar(this);
0078     m_notifierItem = nullptr;
0079 
0080     m_firstRunDialog = nullptr;
0081     m_isFullscreen = false;
0082 
0083 #if HAVE_X11
0084     m_kwinAssistPropSet = false;
0085     m_isX11 = KWindowSystem::isPlatformX11();
0086 #else
0087     m_isX11 = false;
0088 #endif
0089     m_isWayland = KWindowSystem::isPlatformWayland();
0090 #if HAVE_KWAYLAND
0091     m_plasmaShell = nullptr;
0092     m_plasmaShellSurface = nullptr;
0093     initWayland();
0094 #endif
0095 
0096     m_toggleLock = false;
0097 
0098     setupActions();
0099     setupMenu();
0100 
0101     connect(m_tabBar, SIGNAL(newTabRequested()), m_sessionStack, SLOT(addSession()));
0102     connect(m_tabBar, SIGNAL(lastTabClosed()), m_tabBar, SIGNAL(newTabRequested()));
0103     connect(m_tabBar, SIGNAL(lastTabClosed()), this, SLOT(handleLastTabClosed()));
0104     connect(m_tabBar, SIGNAL(tabSelected(int)), m_sessionStack, SLOT(raiseSession(int)));
0105     connect(m_tabBar, SIGNAL(tabClosed(int)), m_sessionStack, SLOT(removeSession(int)));
0106     connect(m_tabBar, &TabBar::tabTitleEdited, m_sessionStack, [&](int, QString) {
0107         m_sessionStack->raiseSession(m_sessionStack->activeSessionId());
0108     });
0109     connect(m_tabBar, SIGNAL(requestTerminalHighlight(int)), m_sessionStack, SLOT(handleTerminalHighlightRequest(int)));
0110     connect(m_tabBar, SIGNAL(requestRemoveTerminalHighlight()), m_sessionStack, SIGNAL(removeTerminalHighlight()));
0111     connect(m_tabBar, SIGNAL(tabContextMenuClosed()), m_sessionStack, SIGNAL(removeTerminalHighlight()));
0112 
0113     connect(m_sessionStack, SIGNAL(sessionAdded(int, QString)), m_tabBar, SLOT(addTab(int, QString)));
0114     connect(m_sessionStack, SIGNAL(sessionRaised(int)), m_tabBar, SLOT(selectTab(int)));
0115     connect(m_sessionStack, SIGNAL(sessionRemoved(int)), m_tabBar, SLOT(removeTab(int)));
0116     connect(m_sessionStack, SIGNAL(activeTitleChanged(QString)), m_titleBar, SLOT(setTitle(QString)));
0117     connect(m_sessionStack, SIGNAL(activeTitleChanged(QString)), this, SLOT(setWindowTitle(QString)));
0118     connect(m_sessionStack, &SessionStack::wantsBlurChanged, this, &MainWindow::applyWindowProperties);
0119 
0120     connect(&m_mousePoller, SIGNAL(timeout()), this, SLOT(pollMouse()));
0121 
0122     if (KWindowSystem::isPlatformX11()) {
0123         connect(KX11Extras::self(), &KX11Extras::workAreaChanged, this, &MainWindow::applyWindowGeometry);
0124     }
0125     connect(qApp, &QGuiApplication::screenAdded, this, &MainWindow::updateScreenMenu);
0126     connect(qApp, &QGuiApplication::screenRemoved, this, &MainWindow::updateScreenMenu);
0127 
0128     applySettings();
0129 
0130     m_sessionStack->addSession();
0131 
0132     if (Settings::firstRun()) {
0133         QMetaObject::invokeMethod(this, "toggleWindowState", Qt::QueuedConnection);
0134         QMetaObject::invokeMethod(this, "showFirstRunDialog", Qt::QueuedConnection);
0135     } else {
0136         if (Settings::pollMouse())
0137             toggleMousePoll(true);
0138     }
0139 
0140     if (Settings::openAfterStart())
0141         QMetaObject::invokeMethod(this, "toggleWindowState", Qt::QueuedConnection);
0142 }
0143 
0144 MainWindow::~MainWindow()
0145 {
0146     Settings::self()->save();
0147 
0148     delete m_skin;
0149 }
0150 
0151 #if HAVE_KWAYLAND
0152 void MainWindow::initWayland()
0153 {
0154     if (!m_isWayland) {
0155         return;
0156     }
0157 
0158     using namespace KWayland::Client;
0159     auto connection = ConnectionThread::fromApplication(this);
0160     if (!connection) {
0161         return;
0162     }
0163     Registry *registry = new Registry(this);
0164     registry->create(connection);
0165     QObject::connect(registry, &Registry::interfacesAnnounced, this, [registry, this] {
0166         const auto interface = registry->interface(Registry::Interface::PlasmaShell);
0167         if (interface.name != 0) {
0168             m_plasmaShell = registry->createPlasmaShell(interface.name, interface.version, this);
0169         }
0170     });
0171 
0172     registry->setup();
0173     connection->roundtrip();
0174 }
0175 
0176 void MainWindow::initWaylandSurface()
0177 {
0178     if (m_plasmaShellSurface) {
0179         m_plasmaShellSurface->setPosition(pos());
0180         return;
0181     }
0182     if (!m_plasmaShell) {
0183         return;
0184     }
0185     if (auto surface = KWayland::Client::Surface::fromWindow(windowHandle())) {
0186         m_plasmaShellSurface = m_plasmaShell->createSurface(surface, this);
0187         m_plasmaShellSurface->setPosition(pos());
0188         m_plasmaShellSurface->setSkipTaskbar(true);
0189         m_plasmaShellSurface->setSkipSwitcher(true);
0190     }
0191 }
0192 
0193 #endif
0194 
0195 bool MainWindow::queryClose()
0196 {
0197     bool confirmQuit = Settings::confirmQuit();
0198     bool hasUnclosableSessions = m_sessionStack->hasUnclosableSessions();
0199 
0200     QString closeQuestion = xi18nc("@info", "Are you sure you want to quit?");
0201     QString warningMessage;
0202 
0203     if ((confirmQuit && m_sessionStack->count() > 1) || hasUnclosableSessions) {
0204         if (confirmQuit && m_sessionStack->count() > 1) {
0205             if (hasUnclosableSessions)
0206                 warningMessage = xi18nc("@info",
0207                                         "<warning>There are multiple open sessions, <emphasis>some of which you have locked to prevent closing them "
0208                                         "accidentally.</emphasis> These will be killed if you continue.</warning>");
0209             else
0210                 warningMessage = xi18nc("@info", "<warning>There are multiple open sessions. These will be killed if you continue.</warning>");
0211         } else if (hasUnclosableSessions) {
0212             warningMessage = xi18nc("@info",
0213                                     "<warning>There are one or more open sessions that you have locked to prevent closing them accidentally. These will be "
0214                                     "killed if you continue.</warning>");
0215         }
0216 
0217         int result = KMessageBox::warningContinueCancel(this,
0218                                                         warningMessage + QStringLiteral("<br /><br />") + closeQuestion,
0219                                                         xi18nc("@title:window", "Really Quit?"),
0220                                                         KStandardGuiItem::quit(),
0221                                                         KStandardGuiItem::cancel());
0222 
0223         return result != KMessageBox::Cancel;
0224     }
0225 
0226     return true;
0227 }
0228 
0229 void MainWindow::setupActions()
0230 {
0231     m_actionCollection = new KActionCollection(this);
0232 
0233     KToggleFullScreenAction *fullScreenAction = new KToggleFullScreenAction(this);
0234     fullScreenAction->setWindow(this);
0235     actionCollection()->setDefaultShortcut(fullScreenAction, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_F11));
0236     m_actionCollection->addAction(QStringLiteral("view-full-screen"), fullScreenAction);
0237     connect(fullScreenAction, SIGNAL(toggled(bool)), this, SLOT(setFullScreen(bool)));
0238 
0239     QAction *action = KStandardAction::quit(this, SLOT(close()), actionCollection());
0240     actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_Q));
0241     action = KStandardAction::aboutApp(m_helpMenu, SLOT(aboutApplication()), actionCollection());
0242     action = KStandardAction::reportBug(m_helpMenu, SLOT(reportBug()), actionCollection());
0243     action = KStandardAction::aboutKDE(m_helpMenu, SLOT(aboutKDE()), actionCollection());
0244     action = KStandardAction::keyBindings(this, SLOT(configureKeys()), actionCollection());
0245     action = KStandardAction::configureNotifications(this, SLOT(configureNotifications()), actionCollection());
0246     action = KStandardAction::preferences(this, SLOT(configureApp()), actionCollection());
0247 
0248     action = KStandardAction::whatsThis(this, SLOT(whatsThis()), actionCollection());
0249 
0250     action = actionCollection()->addAction(QStringLiteral("toggle-window-state"));
0251     action->setText(xi18nc("@action", "Open/Retract Yakuake"));
0252     action->setIcon(QIcon::fromTheme(QStringLiteral("yakuake")));
0253     KGlobalAccel::self()->setGlobalShortcut(action, QList<QKeySequence>() << QKeySequence(Qt::Key_F12));
0254     connect(action, SIGNAL(triggered()), this, SLOT(toggleWindowState()));
0255     connect(action, SIGNAL(changed()), this, SLOT(updateTrayTooltip()));
0256     connect(KGlobalAccel::self(), SIGNAL(globalShortcutChanged(QAction *, const QKeySequence &)), this, SLOT(updateTrayTooltip()));
0257     updateTrayTooltip();
0258 
0259     action = actionCollection()->addAction(QStringLiteral("keep-open"));
0260     action->setText(xi18nc("@action", "Keep window open when it loses focus"));
0261     action->setCheckable(true);
0262     connect(action, SIGNAL(toggled(bool)), this, SLOT(setKeepOpen(bool)));
0263 
0264     action = actionCollection()->addAction(QStringLiteral("manage-profiles"));
0265     action->setText(xi18nc("@action", "Manage Profiles..."));
0266     action->setIcon(QIcon::fromTheme(QStringLiteral("configure")));
0267     connect(action, SIGNAL(triggered()), m_sessionStack, SIGNAL(manageProfiles()));
0268 
0269     action = actionCollection()->addAction(QStringLiteral("edit-profile"));
0270     action->setText(xi18nc("@action", "Edit Current Profile..."));
0271     action->setIcon(QIcon::fromTheme(QStringLiteral("document-properties")));
0272     connect(action, SIGNAL(triggered()), this, SLOT(handleContextDependentAction()));
0273     m_contextDependentActions << action;
0274 
0275     action = actionCollection()->addAction(QStringLiteral("increase-window-width"));
0276     action->setText(xi18nc("@action", "Increase Window Width"));
0277     actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::ALT | Qt::SHIFT | Qt::Key_Right));
0278     connect(action, SIGNAL(triggered()), this, SLOT(increaseWindowWidth()));
0279 
0280     action = actionCollection()->addAction(QStringLiteral("decrease-window-width"));
0281     action->setText(xi18nc("@action", "Decrease Window Width"));
0282     actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::ALT | Qt::SHIFT | Qt::Key_Left));
0283     connect(action, SIGNAL(triggered()), this, SLOT(decreaseWindowWidth()));
0284 
0285     action = actionCollection()->addAction(QStringLiteral("increase-window-height"));
0286     action->setText(xi18nc("@action", "Increase Window Height"));
0287     actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::ALT | Qt::SHIFT | Qt::Key_Down));
0288     connect(action, SIGNAL(triggered()), this, SLOT(increaseWindowHeight()));
0289 
0290     action = actionCollection()->addAction(QStringLiteral("decrease-window-height"));
0291     action->setText(xi18nc("@action", "Decrease Window Height"));
0292     actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::ALT | Qt::SHIFT | Qt::Key_Up));
0293     connect(action, SIGNAL(triggered()), this, SLOT(decreaseWindowHeight()));
0294 
0295     action = actionCollection()->addAction(QStringLiteral("new-session"));
0296     action->setText(xi18nc("@action", "New Session"));
0297     action->setIcon(QIcon::fromTheme(QStringLiteral("tab-new")));
0298     actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_T));
0299     connect(action, SIGNAL(triggered()), m_sessionStack, SLOT(addSession()));
0300 
0301     action = actionCollection()->addAction(QStringLiteral("new-session-two-horizontal"));
0302     action->setText(xi18nc("@action", "Two Terminals, Horizontally"));
0303     action->setIcon(QIcon::fromTheme(QStringLiteral("tab-new")));
0304     connect(action, SIGNAL(triggered()), m_sessionStack, SLOT(addSessionTwoHorizontal()));
0305 
0306     action = actionCollection()->addAction(QStringLiteral("new-session-two-vertical"));
0307     action->setText(xi18nc("@action", "Two Terminals, Vertically"));
0308     action->setIcon(QIcon::fromTheme(QStringLiteral("tab-new")));
0309     connect(action, SIGNAL(triggered()), m_sessionStack, SLOT(addSessionTwoVertical()));
0310 
0311     action = actionCollection()->addAction(QStringLiteral("new-session-quad"));
0312     action->setText(xi18nc("@action", "Four Terminals, Grid"));
0313     action->setIcon(QIcon::fromTheme(QStringLiteral("tab-new")));
0314     connect(action, SIGNAL(triggered()), m_sessionStack, SLOT(addSessionQuad()));
0315 
0316     action = actionCollection()->addAction(QStringLiteral("close-session"));
0317     action->setText(xi18nc("@action", "Close Session"));
0318     action->setIcon(QIcon::fromTheme(QStringLiteral("tab-close")));
0319     actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_W));
0320     connect(action, SIGNAL(triggered()), this, SLOT(handleContextDependentAction()));
0321     m_contextDependentActions << action;
0322 
0323     action = actionCollection()->addAction(QStringLiteral("previous-session"));
0324     action->setText(xi18nc("@action", "Previous Session"));
0325     action->setIcon(QIcon::fromTheme(QStringLiteral("go-previous")));
0326     actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::SHIFT | Qt::Key_Left));
0327     connect(action, SIGNAL(triggered()), m_tabBar, SLOT(selectPreviousTab()));
0328 
0329     action = actionCollection()->addAction(QStringLiteral("next-session"));
0330     action->setText(xi18nc("@action", "Next Session"));
0331     action->setIcon(QIcon::fromTheme(QStringLiteral("go-next")));
0332     actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::SHIFT | Qt::Key_Right));
0333     connect(action, SIGNAL(triggered()), m_tabBar, SLOT(selectNextTab()));
0334 
0335     action = actionCollection()->addAction(QStringLiteral("move-session-left"));
0336     action->setText(xi18nc("@action", "Move Session Left"));
0337     action->setIcon(QIcon::fromTheme(QStringLiteral("arrow-left")));
0338     actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_Left));
0339     connect(action, SIGNAL(triggered()), this, SLOT(handleContextDependentAction()));
0340     m_contextDependentActions << action;
0341 
0342     action = actionCollection()->addAction(QStringLiteral("move-session-right"));
0343     action->setText(xi18nc("@action", "Move Session Right"));
0344     action->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right")));
0345     actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_Right));
0346     connect(action, SIGNAL(triggered()), this, SLOT(handleContextDependentAction()));
0347     m_contextDependentActions << action;
0348 
0349     action = actionCollection()->addAction(QStringLiteral("grow-terminal-right"));
0350     action->setText(xi18nc("@action", "Grow Terminal to the Right"));
0351     action->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right")));
0352     actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_Right));
0353     connect(action, SIGNAL(triggered()), this, SLOT(handleContextDependentAction()));
0354     m_contextDependentActions << action;
0355 
0356     action = actionCollection()->addAction(QStringLiteral("grow-terminal-left"));
0357     action->setText(xi18nc("@action", "Grow Terminal to the Left"));
0358     action->setIcon(QIcon::fromTheme(QStringLiteral("arrow-left")));
0359     actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_Left));
0360     connect(action, SIGNAL(triggered()), this, SLOT(handleContextDependentAction()));
0361     m_contextDependentActions << action;
0362 
0363     action = actionCollection()->addAction(QStringLiteral("grow-terminal-top"));
0364     action->setText(xi18nc("@action", "Grow Terminal to the Top"));
0365     action->setIcon(QIcon::fromTheme(QStringLiteral("arrow-up")));
0366     actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_Up));
0367     connect(action, SIGNAL(triggered()), this, SLOT(handleContextDependentAction()));
0368     m_contextDependentActions << action;
0369 
0370     action = actionCollection()->addAction(QStringLiteral("grow-terminal-bottom"));
0371     action->setText(xi18nc("@action", "Grow Terminal to the Bottom"));
0372     action->setIcon(QIcon::fromTheme(QStringLiteral("arrow-down")));
0373     actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_Down));
0374     connect(action, SIGNAL(triggered()), this, SLOT(handleContextDependentAction()));
0375     m_contextDependentActions << action;
0376 
0377     action = actionCollection()->addAction(QStringLiteral("rename-session"));
0378     action->setText(xi18nc("@action", "Rename Session..."));
0379     action->setIcon(QIcon::fromTheme(QStringLiteral("edit-rename")));
0380     actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_S));
0381     connect(action, SIGNAL(triggered()), this, SLOT(handleContextDependentAction()));
0382     m_contextDependentActions << action;
0383 
0384     action = actionCollection()->addAction(QStringLiteral("previous-terminal"));
0385     action->setText(xi18nc("@action", "Previous Terminal"));
0386     action->setIcon(QIcon::fromTheme(QStringLiteral("go-previous")));
0387     actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_Tab));
0388     connect(action, SIGNAL(triggered()), m_sessionStack, SIGNAL(previousTerminal()));
0389 
0390     action = actionCollection()->addAction(QStringLiteral("next-terminal"));
0391     action->setText(xi18nc("@action", "Next Terminal"));
0392     action->setIcon(QIcon::fromTheme(QStringLiteral("go-next")));
0393     actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL | Qt::Key_Tab));
0394     connect(action, SIGNAL(triggered()), m_sessionStack, SIGNAL(nextTerminal()));
0395 
0396     action = actionCollection()->addAction(QStringLiteral("close-active-terminal"));
0397     action->setText(xi18nc("@action", "Close Active Terminal"));
0398     action->setIcon(QIcon::fromTheme(QStringLiteral("view-close")));
0399     actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_R));
0400     connect(action, SIGNAL(triggered()), this, SLOT(handleContextDependentAction()));
0401     m_contextDependentActions << action;
0402 
0403     action = actionCollection()->addAction(QStringLiteral("split-left-right"));
0404     action->setText(xi18nc("@action", "Split Left/Right"));
0405     action->setIcon(QIcon::fromTheme(QStringLiteral("view-split-left-right")));
0406     actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL | Qt::Key_ParenLeft));
0407     connect(action, SIGNAL(triggered()), this, SLOT(handleContextDependentAction()));
0408     m_contextDependentActions << action;
0409 
0410     action = actionCollection()->addAction(QStringLiteral("split-top-bottom"));
0411     action->setText(xi18nc("@action", "Split Top/Bottom"));
0412     action->setIcon(QIcon::fromTheme(QStringLiteral("view-split-top-bottom")));
0413     actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL | Qt::Key_ParenRight));
0414     connect(action, SIGNAL(triggered()), this, SLOT(handleContextDependentAction()));
0415     m_contextDependentActions << action;
0416 
0417     action = actionCollection()->addAction(QStringLiteral("toggle-session-prevent-closing"));
0418     action->setText(xi18nc("@action", "Prevent Closing"));
0419     action->setCheckable(true);
0420     connect(action, SIGNAL(triggered(bool)), this, SLOT(handleContextDependentToggleAction(bool)));
0421     m_contextDependentActions << action;
0422 
0423     action = actionCollection()->addAction(QStringLiteral("toggle-session-keyboard-input"));
0424     action->setText(xi18nc("@action", "Disable Keyboard Input"));
0425     action->setCheckable(true);
0426     connect(action, SIGNAL(triggered(bool)), this, SLOT(handleContextDependentToggleAction(bool)));
0427     m_contextDependentActions << action;
0428 
0429     action = actionCollection()->addAction(QStringLiteral("toggle-session-monitor-activity"));
0430     action->setText(xi18nc("@action", "Monitor for Activity"));
0431     actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_A));
0432     action->setCheckable(true);
0433     connect(action, SIGNAL(triggered(bool)), this, SLOT(handleContextDependentToggleAction(bool)));
0434     m_contextDependentActions << action;
0435 
0436     action = actionCollection()->addAction(QStringLiteral("toggle-session-monitor-silence"));
0437     action->setText(xi18nc("@action", "Monitor for Silence"));
0438     actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_I));
0439     action->setCheckable(true);
0440     connect(action, SIGNAL(triggered(bool)), this, SLOT(handleContextDependentToggleAction(bool)));
0441     m_contextDependentActions << action;
0442 
0443     action = actionCollection()->addAction(QStringLiteral("toggle-titlebar"));
0444     action->setText(xi18nc("@action", "Toggle Titlebar"));
0445     actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_M));
0446     action->setCheckable(true);
0447     connect(action, SIGNAL(triggered()), this, SLOT(handleToggleTitlebar()));
0448 
0449     for (uint i = 1; i <= 10; ++i) {
0450         action = actionCollection()->addAction(QStringLiteral("switch-to-session-%1").arg(i));
0451         action->setText(xi18nc("@action", "Switch to Session %1", i));
0452         action->setData(i - 1);
0453         connect(action, SIGNAL(triggered()), this, SLOT(handleSwitchToAction()));
0454 
0455         if (i < 10) {
0456             // add default shortcut bindings for the first 9 sessions
0457             actionCollection()->setDefaultShortcut(action, QStringLiteral("Alt+%1").arg(i));
0458         } else {
0459             // add default shortcut bindings for the 10th session
0460             actionCollection()->setDefaultShortcut(action, Qt::ALT | Qt::Key_0);
0461         }
0462     }
0463 
0464     m_actionCollection->associateWidget(this);
0465     m_actionCollection->readSettings();
0466 }
0467 
0468 void MainWindow::handleContextDependentAction(QAction *action, int sessionId)
0469 {
0470     if (sessionId == -1)
0471         sessionId = m_sessionStack->activeSessionId();
0472     if (sessionId == -1)
0473         return;
0474 
0475     if (!action)
0476         action = qobject_cast<QAction *>(QObject::sender());
0477 
0478     if (action == actionCollection()->action(QStringLiteral("edit-profile")))
0479         m_sessionStack->editProfile(sessionId);
0480 
0481     if (action == actionCollection()->action(QStringLiteral("close-session")))
0482         m_sessionStack->removeSession(sessionId);
0483 
0484     if (action == actionCollection()->action(QStringLiteral("move-session-left")))
0485         m_tabBar->moveTabLeft(sessionId);
0486 
0487     if (action == actionCollection()->action(QStringLiteral("move-session-right")))
0488         m_tabBar->moveTabRight(sessionId);
0489 
0490     if (action == actionCollection()->action(QStringLiteral("rename-session")))
0491         m_tabBar->interactiveRename(sessionId);
0492 
0493     if (action == actionCollection()->action(QStringLiteral("close-active-terminal")))
0494         m_sessionStack->closeActiveTerminal(sessionId);
0495 
0496     if (action == actionCollection()->action(QStringLiteral("split-left-right")))
0497         m_sessionStack->splitSessionLeftRight(sessionId);
0498 
0499     if (action == actionCollection()->action(QStringLiteral("split-top-bottom")))
0500         m_sessionStack->splitSessionTopBottom(sessionId);
0501 
0502     if (action == actionCollection()->action(QStringLiteral("grow-terminal-right")))
0503         m_sessionStack->tryGrowTerminalRight(m_sessionStack->activeTerminalId());
0504 
0505     if (action == actionCollection()->action(QStringLiteral("grow-terminal-left")))
0506         m_sessionStack->tryGrowTerminalLeft(m_sessionStack->activeTerminalId());
0507 
0508     if (action == actionCollection()->action(QStringLiteral("grow-terminal-top")))
0509         m_sessionStack->tryGrowTerminalTop(m_sessionStack->activeTerminalId());
0510 
0511     if (action == actionCollection()->action(QStringLiteral("grow-terminal-bottom")))
0512         m_sessionStack->tryGrowTerminalBottom(m_sessionStack->activeTerminalId());
0513 }
0514 
0515 void MainWindow::handleContextDependentToggleAction(bool checked, QAction *action, int sessionId)
0516 {
0517     if (sessionId == -1)
0518         sessionId = m_sessionStack->activeSessionId();
0519     if (sessionId == -1)
0520         return;
0521 
0522     if (!action)
0523         action = qobject_cast<QAction *>(QObject::sender());
0524 
0525     if (action == actionCollection()->action(QStringLiteral("toggle-session-prevent-closing"))) {
0526         m_sessionStack->setSessionClosable(sessionId, !checked);
0527 
0528         // Repaint the tab bar when the Prevent Closing action is toggled
0529         // so the lock icon is added to or removed from the tab label.
0530         m_tabBar->repaint();
0531     }
0532 
0533     if (action == actionCollection()->action(QStringLiteral("toggle-session-keyboard-input")))
0534         m_sessionStack->setSessionKeyboardInputEnabled(sessionId, !checked);
0535 
0536     if (action == actionCollection()->action(QStringLiteral("toggle-session-monitor-activity")))
0537         m_sessionStack->setSessionMonitorActivityEnabled(sessionId, checked);
0538 
0539     if (action == actionCollection()->action(QStringLiteral("toggle-session-monitor-silence")))
0540         m_sessionStack->setSessionMonitorSilenceEnabled(sessionId, checked);
0541 }
0542 
0543 void MainWindow::setContextDependentActionsQuiet(bool quiet)
0544 {
0545     QListIterator<QAction *> i(m_contextDependentActions);
0546 
0547     while (i.hasNext())
0548         i.next()->blockSignals(quiet);
0549 }
0550 
0551 void MainWindow::handleToggleTerminalKeyboardInput(bool checked)
0552 {
0553     QAction *action = qobject_cast<QAction *>(QObject::sender());
0554 
0555     if (!action || action->data().isNull())
0556         return;
0557 
0558     bool ok = false;
0559     int terminalId = action->data().toInt(&ok);
0560     if (!ok)
0561         return;
0562 
0563     m_sessionStack->setTerminalKeyboardInputEnabled(terminalId, !checked);
0564 }
0565 
0566 void MainWindow::handleToggleTerminalMonitorActivity(bool checked)
0567 {
0568     QAction *action = qobject_cast<QAction *>(QObject::sender());
0569 
0570     if (!action || action->data().isNull())
0571         return;
0572 
0573     bool ok = false;
0574     int terminalId = action->data().toInt(&ok);
0575     if (!ok)
0576         return;
0577 
0578     m_sessionStack->setTerminalMonitorActivityEnabled(terminalId, checked);
0579 }
0580 
0581 void MainWindow::handleToggleTerminalMonitorSilence(bool checked)
0582 {
0583     QAction *action = qobject_cast<QAction *>(QObject::sender());
0584 
0585     if (!action || action->data().isNull())
0586         return;
0587 
0588     bool ok = false;
0589     int terminalId = action->data().toInt(&ok);
0590     if (!ok)
0591         return;
0592 
0593     m_sessionStack->setTerminalMonitorSilenceEnabled(terminalId, checked);
0594 }
0595 
0596 void MainWindow::handleTerminalActivity(Terminal *terminal)
0597 {
0598     Session *session = qobject_cast<Session *>(sender());
0599 
0600     if (session) {
0601         disconnect(terminal, SIGNAL(activityDetected(Terminal *)), session, SIGNAL(activityDetected(Terminal *)));
0602 
0603         QString message(xi18nc("@info", "Activity detected in monitored terminal in session \"%1\".", m_tabBar->tabTitle(session->id())));
0604 
0605         KNotification *n = new KNotification(QLatin1String("activity"), KNotification::CloseWhenWindowActivated);
0606         n->setWindow(terminal->partWidget()->window()->windowHandle());
0607         n->setText(message);
0608         n->sendEvent();
0609     }
0610 }
0611 
0612 void MainWindow::handleTerminalSilence(Terminal *terminal)
0613 {
0614     Session *session = qobject_cast<Session *>(sender());
0615 
0616     if (session) {
0617         QString message(xi18nc("@info", "Silence detected in monitored terminal in session \"%1\".", m_tabBar->tabTitle(session->id())));
0618 
0619         KNotification *n = new KNotification(QLatin1String("silence"), KNotification::CloseWhenWindowActivated);
0620         n->setWindow(terminal->partWidget()->window()->windowHandle());
0621         n->setText(message);
0622         n->sendEvent();
0623     }
0624 }
0625 
0626 void MainWindow::handleLastTabClosed()
0627 {
0628     if (isVisible() && !Settings::keepOpenAfterLastSessionCloses())
0629         toggleWindowState();
0630 }
0631 
0632 void MainWindow::handleSwitchToAction()
0633 {
0634     QAction *action = qobject_cast<QAction *>(QObject::sender());
0635 
0636     if (action && !action->data().isNull())
0637         m_sessionStack->raiseSession(m_tabBar->sessionAtTab(action->data().toInt()));
0638 }
0639 
0640 void MainWindow::handleToggleTitlebar()
0641 {
0642     auto toggleFunc = [this]() {
0643         bool showTitleBar = !Settings::showTitleBar();
0644         m_titleBar->setVisible(showTitleBar);
0645         Settings::setShowTitleBar(showTitleBar);
0646         Settings::self()->save();
0647         applyWindowGeometry();
0648     };
0649 
0650     if (Settings::showTitleBar()) { // If the title bar is hidden don't ask if toggling is ok
0651 
0652         const char *message =
0653             "You are about to hide the title bar. This will keep you "
0654             "from accessing the settings menu via the mouse. To show "
0655             "the title bar again press the keyboard shortcut (default "
0656             "Ctrl+Shift+m) or access the settings menu via keyboard "
0657             "shortcut (default: Ctrl+Shift+,).";
0658 
0659         const int result = KMessageBox::warningContinueCancel(this,
0660                                                               xi18nc("@info", message),
0661                                                               xi18nc("@title:window", "Hiding Title Bar"),
0662                                                               KStandardGuiItem::cont(),
0663                                                               KStandardGuiItem::cancel(),
0664                                                               QStringLiteral("hinding_title_bar"));
0665 
0666         if (result == KMessageBox::ButtonCode::Continue) {
0667             toggleFunc();
0668         }
0669     } else {
0670         toggleFunc();
0671     }
0672 }
0673 
0674 void MainWindow::setupMenu()
0675 {
0676     m_menu->insertSection(nullptr, xi18nc("@title:menu", "Help"));
0677     m_menu->addAction(actionCollection()->action(KStandardAction::name(KStandardAction::WhatsThis)));
0678     m_menu->addAction(actionCollection()->action(KStandardAction::name(KStandardAction::ReportBug)));
0679     m_menu->addAction(actionCollection()->action(KStandardAction::name(KStandardAction::AboutApp)));
0680     m_menu->addAction(actionCollection()->action(KStandardAction::name(KStandardAction::AboutKDE)));
0681 
0682     m_menu->insertSection(nullptr, xi18nc("@title:menu", "Quick Options"));
0683     m_menu->addAction(actionCollection()->action(QStringLiteral("view-full-screen")));
0684     m_menu->addAction(actionCollection()->action(QStringLiteral("keep-open")));
0685 
0686     m_screenMenu = new QMenu(this);
0687     connect(m_screenMenu, SIGNAL(triggered(QAction *)), this, SLOT(setScreen(QAction *)));
0688     m_screenMenu->setTitle(xi18nc("@title:menu", "Screen"));
0689     m_menu->addMenu(m_screenMenu);
0690 
0691     m_windowWidthMenu = new QMenu(this);
0692     connect(m_windowWidthMenu, SIGNAL(triggered(QAction *)), this, SLOT(setWindowWidth(QAction *)));
0693     m_windowWidthMenu->setTitle(xi18nc("@title:menu", "Width"));
0694     m_menu->addMenu(m_windowWidthMenu);
0695 
0696     m_windowHeightMenu = new QMenu(this);
0697     connect(m_windowHeightMenu, SIGNAL(triggered(QAction *)), this, SLOT(setWindowHeight(QAction *)));
0698     m_windowHeightMenu->setTitle(xi18nc("@title:menu", "Height"));
0699     m_menu->addMenu(m_windowHeightMenu);
0700 
0701     m_menu->insertSection(nullptr, xi18nc("@title:menu", "Settings"));
0702     m_menu->addAction(actionCollection()->action(QStringLiteral("manage-profiles")));
0703     m_menu->addAction(actionCollection()->action(KStandardAction::name(KStandardAction::KeyBindings)));
0704     m_menu->addAction(actionCollection()->action(KStandardAction::name(KStandardAction::ConfigureNotifications)));
0705     m_menu->addAction(actionCollection()->action(KStandardAction::name(KStandardAction::Preferences)));
0706 
0707     m_menu->addSeparator();
0708     m_menu->addAction(actionCollection()->action(KStandardAction::name(KStandardAction::Quit)));
0709 }
0710 
0711 void MainWindow::updateScreenMenu()
0712 {
0713     QAction *action;
0714 
0715     m_screenMenu->clear();
0716 
0717     action = m_screenMenu->addAction(xi18nc("@item:inmenu", "At mouse location"));
0718     action->setCheckable(true);
0719     action->setData(0);
0720     action->setChecked(Settings::screen() == 0);
0721 
0722     for (int i = 1; i <= QGuiApplication::screens().count(); i++) {
0723         action = m_screenMenu->addAction(xi18nc("@item:inmenu", "Screen %1", i));
0724         action->setCheckable(true);
0725         action->setData(i);
0726         action->setChecked(i == Settings::screen());
0727     }
0728 
0729     action = m_screenMenu->menuAction();
0730     action->setVisible(QGuiApplication::screens().count() > 1);
0731 }
0732 
0733 void MainWindow::updateWindowSizeMenus()
0734 {
0735     updateWindowWidthMenu();
0736     updateWindowHeightMenu();
0737 }
0738 
0739 void MainWindow::updateWindowWidthMenu()
0740 {
0741     QAction *action = nullptr;
0742 
0743     if (m_windowWidthMenu->isEmpty()) {
0744         for (int i = 10; i <= 100; i += 10) {
0745             action = m_windowWidthMenu->addAction(i18n("%1%", i));
0746             action->setCheckable(true);
0747             action->setData(i);
0748             action->setChecked(i == Settings::width());
0749         }
0750     } else {
0751         QListIterator<QAction *> i(m_windowWidthMenu->actions());
0752 
0753         while (i.hasNext()) {
0754             action = i.next();
0755 
0756             action->setChecked(action->data().toInt() == Settings::width());
0757         }
0758     }
0759 }
0760 
0761 void MainWindow::updateWindowHeightMenu()
0762 {
0763     QAction *action = nullptr;
0764 
0765     if (m_windowHeightMenu->isEmpty()) {
0766         for (int i = 10; i <= 100; i += 10) {
0767             action = m_windowHeightMenu->addAction(i18n("%1%", i));
0768             action->setCheckable(true);
0769             action->setData(i);
0770             action->setChecked(i == Settings::height());
0771         }
0772     } else {
0773         QListIterator<QAction *> i(m_windowHeightMenu->actions());
0774 
0775         while (i.hasNext()) {
0776             action = i.next();
0777 
0778             action->setChecked(action->data().toInt() == Settings::height());
0779         }
0780     }
0781 }
0782 
0783 void MainWindow::configureKeys()
0784 {
0785     KShortcutsDialog dialog(KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsAllowed, this);
0786     dialog.addCollection(actionCollection());
0787 
0788     const auto collections = m_sessionStack->getPartActionCollections();
0789 
0790     if (collections.size() >= 1) {
0791         dialog.addCollection(collections.at(0), QStringLiteral("Konsolepart"));
0792     }
0793 
0794     if (!dialog.configure()) {
0795         return;
0796     }
0797 
0798     if (collections.size() >= 1) {
0799         // We need to update all the other collections
0800         // rootCollection is the collection which got updatet by the dialog
0801         const auto rootCollection = collections.at(0);
0802 
0803         // For all the other collections
0804         for (auto i = 1; i < collections.size(); ++i) {
0805             // Update all the action they share with rootCollection
0806             const auto rootActions = rootCollection->actions();
0807             for (const auto *action : rootActions) {
0808                 if (auto *destaction = collections.at(i)->action(action->objectName())) {
0809                     destaction->setShortcuts(action->shortcuts());
0810                 }
0811             }
0812         }
0813     }
0814 }
0815 
0816 void MainWindow::configureNotifications()
0817 {
0818     KNotifyConfigWidget::configure(this);
0819 }
0820 
0821 void MainWindow::configureApp()
0822 {
0823     if (KConfigDialog::showDialog(QStringLiteral("settings")))
0824         return;
0825 
0826     KConfigDialog *settingsDialog = new KConfigDialog(this, QStringLiteral("settings"), Settings::self());
0827     settingsDialog->setMinimumHeight(560);
0828     settingsDialog->setFaceType(KPageDialog::List);
0829     connect(settingsDialog, &KConfigDialog::settingsChanged, this, &MainWindow::applySettings);
0830 
0831     WindowSettings *windowSettings = new WindowSettings(settingsDialog);
0832     settingsDialog->addPage(windowSettings, xi18nc("@title Preferences page name", "Window"), QStringLiteral("preferences-system-windows-move"));
0833     connect(windowSettings, SIGNAL(updateWindowGeometry(int, int, int)), this, SLOT(setWindowGeometry(int, int, int)));
0834 
0835     QWidget *behaviorSettings = new QWidget(settingsDialog);
0836     Ui::BehaviorSettings behaviorSettingsUi;
0837     behaviorSettingsUi.setupUi(behaviorSettings);
0838     settingsDialog->addPage(behaviorSettings, xi18nc("@title Preferences page name", "Behavior"), QStringLiteral("preferences-system-windows-actions"));
0839 
0840     AppearanceSettings *appearanceSettings = new AppearanceSettings(settingsDialog);
0841     settingsDialog->addPage(appearanceSettings, xi18nc("@title Preferences page name", "Appearance"), QStringLiteral("preferences-desktop-theme"));
0842     connect(settingsDialog, &QDialog::rejected, appearanceSettings, &AppearanceSettings::resetSelection);
0843 
0844     settingsDialog->button(QDialogButtonBox::Help)->hide();
0845     settingsDialog->button(QDialogButtonBox::Cancel)->setFocus();
0846 
0847     connect(settingsDialog, &QDialog::finished, [=, this]() {
0848         m_toggleLock = true;
0849         KWindowSystem::activateWindow(windowHandle());
0850 
0851         if (KWindowSystem::isPlatformX11()) {
0852             KX11Extras::forceActiveWindow(winId());
0853         }
0854     });
0855 
0856     settingsDialog->show();
0857 }
0858 
0859 void MainWindow::applySettings()
0860 {
0861     if (Settings::dynamicTabTitles()) {
0862         connect(m_sessionStack, SIGNAL(titleChanged(int, QString)), m_tabBar, SLOT(setTabTitleAutomated(int, QString)));
0863 
0864         m_sessionStack->emitTitles();
0865     } else {
0866         disconnect(m_sessionStack, SIGNAL(titleChanged(int, QString)), m_tabBar, SLOT(setTabTitleAutomated(int, QString)));
0867     }
0868 
0869     m_animationTimer.setInterval(Settings::frames() ? 10 : 0);
0870 
0871     m_tabBar->setVisible(Settings::showTabBar());
0872     m_titleBar->setVisible(Settings::showTitleBar());
0873 
0874     if (!Settings::showSystrayIcon() && m_notifierItem) {
0875         delete m_notifierItem;
0876         m_notifierItem = nullptr;
0877 
0878         // Removing the notifier item deletes the menu
0879         // add a new one
0880         m_menu = new QMenu(this);
0881         setupMenu();
0882         m_titleBar->updateMenu();
0883     } else if (Settings::showSystrayIcon() && !m_notifierItem) {
0884         m_notifierItem = new KStatusNotifierItem(this);
0885         m_notifierItem->setStandardActionsEnabled(false);
0886         m_notifierItem->setIconByName(QStringLiteral("yakuake-symbolic"));
0887         m_notifierItem->setStatus(KStatusNotifierItem::Active);
0888         m_notifierItem->setContextMenu(m_menu);
0889 
0890         // Prevent the default implementation of showing
0891         // and instead run toggleWindowState
0892         m_notifierItem->setAssociatedWindow(nullptr);
0893         connect(m_notifierItem, &KStatusNotifierItem::activateRequested, this, &MainWindow::toggleWindowState);
0894         updateTrayTooltip();
0895     }
0896 
0897     repaint(); // used to repaint skin borders if Settings::hideSkinBorders has been changed
0898 
0899     setKeepOpen(Settings::keepOpen());
0900 
0901     updateScreenMenu();
0902     updateWindowSizeMenus();
0903 
0904     updateUseTranslucency();
0905 
0906     applySkin();
0907     applyWindowGeometry();
0908     applyWindowProperties();
0909 }
0910 
0911 void MainWindow::applySkin()
0912 {
0913     bool gotSkin = m_skin->load(Settings::skin(), Settings::skinInstalledWithKns());
0914 
0915     if (!gotSkin) {
0916         Settings::setSkin(QStringLiteral("default"));
0917         gotSkin = m_skin->load(Settings::skin());
0918     }
0919 
0920     if (!gotSkin) {
0921         KMessageBox::error(parentWidget(),
0922                            xi18nc("@info",
0923                                   "<application>Yakuake</application> was unable to load a skin. It is likely that it was installed incorrectly.<nl/><nl/>"
0924                                   "The application will now quit."),
0925                            xi18nc("@title:window", "Cannot Load Skin"));
0926 
0927         QMetaObject::invokeMethod(qApp, "quit", Qt::QueuedConnection);
0928     }
0929 
0930     m_titleBar->applySkin();
0931     m_tabBar->applySkin();
0932 }
0933 
0934 void MainWindow::applyWindowProperties()
0935 {
0936     if (m_isX11) {
0937         if (Settings::keepOpen() && !Settings::keepAbove()) {
0938             KX11Extras::clearState(winId(), NET::KeepAbove);
0939             KX11Extras::setState(winId(), NET::SkipTaskbar | NET::SkipPager);
0940         } else {
0941             KX11Extras::setState(winId(), NET::KeepAbove | NET::SkipTaskbar | NET::SkipPager);
0942         }
0943         KX11Extras::setOnAllDesktops(winId(), Settings::showOnAllDesktops());
0944     }
0945 
0946 #if HAVE_KWAYLAND
0947     if (m_isWayland && m_plasmaShellSurface) {
0948         m_plasmaShellSurface->setSkipTaskbar(true);
0949         m_plasmaShellSurface->setSkipSwitcher(true);
0950     }
0951 #endif
0952 
0953     winId(); // make sure windowHandle() is created
0954     KWindowEffects::enableBlurBehind(windowHandle(), m_sessionStack->wantsBlur());
0955 }
0956 
0957 void MainWindow::applyWindowGeometry()
0958 {
0959     int width, height;
0960 
0961     QAction *action = actionCollection()->action(QStringLiteral("view-full-screen"));
0962 
0963     if (action->isChecked()) {
0964         width = 100;
0965         height = 100;
0966     } else {
0967         width = Settings::width();
0968         height = Settings::height();
0969     }
0970 
0971     setWindowGeometry(width, height, Settings::position());
0972 }
0973 
0974 void MainWindow::setWindowGeometry(int newWidth, int newHeight, int newPosition)
0975 {
0976     QRect workArea = getDesktopGeometry();
0977 
0978     int maxHeight = workArea.height() * newHeight / 100;
0979 
0980     int targetWidth = workArea.width() * newWidth / 100;
0981 
0982     setGeometry(workArea.x() + workArea.width() * newPosition * (100 - newWidth) / 10000, workArea.y(), targetWidth, maxHeight);
0983 #if HAVE_KWAYLAND
0984     initWaylandSurface();
0985 #endif
0986 
0987     maxHeight -= m_titleBar->height();
0988     m_titleBar->setGeometry(0, maxHeight, targetWidth, m_titleBar->height());
0989     if (!isVisible())
0990         m_titleBar->updateMask();
0991 
0992     if (Settings::frames() > 0)
0993         m_animationStepSize = maxHeight / Settings::frames();
0994     else
0995         m_animationStepSize = maxHeight;
0996 
0997     auto borderWidth = Settings::hideSkinBorders() ? 0 : m_skin->borderWidth();
0998 
0999     if (Settings::showTabBar()) {
1000         if (m_skin->tabBarCompact()) {
1001             m_tabBar->setGeometry(m_skin->tabBarLeft(), maxHeight, width() - m_skin->tabBarLeft() - m_skin->tabBarRight(), m_tabBar->height());
1002         } else {
1003             maxHeight -= m_tabBar->height();
1004             m_tabBar->setGeometry(borderWidth, maxHeight - borderWidth, width() - 2 * borderWidth, m_tabBar->height());
1005         }
1006     }
1007 
1008     m_sessionStack->setGeometry(borderWidth, 0, width() - 2 * borderWidth, maxHeight - borderWidth);
1009 
1010     updateMask();
1011 }
1012 
1013 void MainWindow::setScreen(QAction *action)
1014 {
1015     Settings::setScreen(action->data().toInt());
1016     Settings::self()->save();
1017 
1018     applyWindowGeometry();
1019 
1020     updateScreenMenu();
1021 }
1022 
1023 void MainWindow::setWindowWidth(int width)
1024 {
1025     Settings::setWidth(width);
1026     Settings::self()->save();
1027 
1028     applyWindowGeometry();
1029 
1030     updateWindowWidthMenu();
1031 }
1032 
1033 void MainWindow::setWindowHeight(int height)
1034 {
1035     Settings::setHeight(height);
1036     Settings::self()->save();
1037 
1038     applyWindowGeometry();
1039 
1040     updateWindowHeightMenu();
1041 }
1042 
1043 void MainWindow::setWindowWidth(QAction *action)
1044 {
1045     setWindowWidth(action->data().toInt());
1046 }
1047 
1048 void MainWindow::setWindowHeight(QAction *action)
1049 {
1050     setWindowHeight(action->data().toInt());
1051 }
1052 
1053 void MainWindow::increaseWindowWidth()
1054 {
1055     if (Settings::width() <= 90)
1056         setWindowWidth(Settings::width() + 10);
1057 }
1058 
1059 void MainWindow::decreaseWindowWidth()
1060 {
1061     if (Settings::width() >= 20)
1062         setWindowWidth(Settings::width() - 10);
1063 }
1064 
1065 void MainWindow::increaseWindowHeight()
1066 {
1067     if (Settings::height() <= 90)
1068         setWindowHeight(Settings::height() + 10);
1069 }
1070 
1071 void MainWindow::decreaseWindowHeight()
1072 {
1073     if (Settings::height() >= 20)
1074         setWindowHeight(Settings::height() - 10);
1075 }
1076 
1077 void MainWindow::updateMask()
1078 {
1079     QRegion region = m_titleBar->mask();
1080 
1081     region.translate(0, m_titleBar->y());
1082 
1083     region += QRegion(0, 0, width(), m_titleBar->y());
1084 
1085     setMask(region);
1086 }
1087 
1088 void MainWindow::paintEvent(QPaintEvent *event)
1089 {
1090     QPainter painter(this);
1091 
1092     if (useTranslucency()) {
1093         painter.setOpacity(qreal(Settings::backgroundColorOpacity()) / 100);
1094         painter.fillRect(rect(), Settings::backgroundColor());
1095         painter.setOpacity(1.0);
1096     } else
1097         painter.fillRect(rect(), Settings::backgroundColor());
1098 
1099     if (!Settings::hideSkinBorders()) {
1100         const QRect leftBorder(0, 0, m_skin->borderWidth(), height() - m_titleBar->height());
1101         painter.fillRect(leftBorder, m_skin->borderColor());
1102 
1103         const QRect rightBorder(width() - m_skin->borderWidth(), 0, m_skin->borderWidth(), height() - m_titleBar->height());
1104         painter.fillRect(rightBorder, m_skin->borderColor());
1105 
1106         const QRect bottomBorder(0, height() - m_skin->borderWidth() - m_titleBar->height(), width(), m_skin->borderWidth());
1107         painter.fillRect(bottomBorder, m_skin->borderColor());
1108     }
1109 
1110     KMainWindow::paintEvent(event);
1111 }
1112 
1113 void MainWindow::moveEvent(QMoveEvent *event)
1114 {
1115     const QList<QScreen *> screens = qApp->screens();
1116     const QScreen *widgetScreen = QGuiApplication::screenAt(pos());
1117     auto it = std::find(screens.begin(), screens.end(), widgetScreen);
1118     const int currentScreenNumber = std::distance(screens.begin(), it);
1119 
1120     if (Settings::screen() && currentScreenNumber != -1 && currentScreenNumber != getScreen()) {
1121         Settings::setScreen(currentScreenNumber + 1);
1122 
1123         updateScreenMenu();
1124 
1125         applyWindowGeometry();
1126     }
1127 
1128     KMainWindow::moveEvent(event);
1129 }
1130 
1131 void MainWindow::closeEvent(QCloseEvent *event)
1132 {
1133     KMainWindow::closeEvent(event);
1134     if (event->isAccepted()) {
1135         QApplication::quit();
1136     }
1137 }
1138 
1139 void MainWindow::wmActiveWindowChanged()
1140 {
1141     if (m_toggleLock) {
1142         m_toggleLock = false;
1143         return;
1144     }
1145 
1146     const QWindow *focusWindow = QGuiApplication::focusWindow();
1147 
1148     // Don't retract when opening one of our config windows
1149     if (focusWindow && focusWindow->transientParent() == windowHandle()) {
1150         return;
1151     }
1152 
1153     if (!Settings::keepOpen() && isVisible() && !isActiveWindow()) {
1154         toggleWindowState();
1155     }
1156 }
1157 
1158 void MainWindow::changeEvent(QEvent *event)
1159 {
1160     if (event->type() == QEvent::WindowStateChange && !m_isFullscreen) {
1161         if (windowState().testFlag(Qt::WindowMaximized)) {
1162             // Don't alter settings to new size so unmaximizing restores previous geometry.
1163             setWindowGeometry(100, 100, Settings::position());
1164             setWindowState(Qt::WindowMaximized);
1165         } else {
1166             setWindowGeometry(Settings::width(), Settings::height(), Settings::position());
1167         }
1168     }
1169 
1170     KMainWindow::changeEvent(event);
1171 }
1172 
1173 bool MainWindow::focusNextPrevChild(bool)
1174 {
1175     return false;
1176 }
1177 
1178 void MainWindow::toggleWindowState()
1179 {
1180     if (m_isWayland) {
1181         auto message = QDBusMessage::createMethodCall(QStringLiteral("org.kde.plasmashell"),
1182                                                       QStringLiteral("/StrutManager"),
1183                                                       QStringLiteral("org.kde.PlasmaShell.StrutManager"),
1184                                                       QStringLiteral("availableScreenRect"));
1185         message.setArguments({QGuiApplication::screens().at(getScreen())->name()});
1186         QDBusPendingCall call = QDBusConnection::sessionBus().asyncCall(message);
1187         QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, this);
1188 
1189         QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, [=, this]() {
1190             QDBusPendingReply<QRect> reply = *watcher;
1191             m_availableScreenRect = reply.isValid() ? reply.value() : QRect();
1192             setWindowGeometry(Settings::width(), Settings::height(), Settings::position());
1193             watcher->deleteLater();
1194         });
1195 
1196         _toggleWindowState();
1197     } else {
1198         _toggleWindowState();
1199     }
1200 }
1201 
1202 void MainWindow::_toggleWindowState()
1203 {
1204     bool visible = isVisible();
1205 
1206     if (visible && !isActiveWindow() && Settings::keepOpen()) {
1207         // Window is open but doesn't have focus; it's set to stay open
1208         // regardless of focus loss.
1209 
1210         if (Settings::toggleToFocus()) {
1211             // The open/retract action is set to focus the window when it's
1212             // open but lacks focus. The following will cause it to receive
1213             // focus, and in an environment with multiple virtual desktops
1214             // will also cause the window manager to switch to the virtual
1215             // desktop the window resides on.
1216 
1217             KWindowSystem::activateWindow(windowHandle());
1218             KX11Extras::forceActiveWindow(winId());
1219 
1220             return;
1221         } else if (!Settings::showOnAllDesktops() && KWindowInfo(winId(), NET::WMDesktop).desktop() != KX11Extras::currentDesktop()) {
1222             // The open/restrict action isn't set to focus the window, but
1223             // the window is currently on another virtual desktop (the option
1224             // to show it on all of them is disabled), so closing it doesn't
1225             // make sense and we're opting to show it instead to avoid
1226             // requiring the user to invoke the action twice to get to see
1227             // Yakuake. Just forcing focus would cause the window manager to
1228             // switch to the virtual desktop the window currently resides on,
1229             // so move the window to the current desktop before doing so.
1230 
1231             KX11Extras::setOnDesktop(winId(), KX11Extras::currentDesktop());
1232 
1233             KWindowSystem::activateWindow(windowHandle());
1234             KX11Extras::forceActiveWindow(winId());
1235 
1236             return;
1237         }
1238     }
1239 
1240 #if HAVE_X11
1241     if (!Settings::useWMAssist() && m_kwinAssistPropSet)
1242         kwinAssistPropCleanup();
1243 
1244     if (m_isX11 && Settings::useWMAssist() && KX11Extras::compositingActive())
1245         kwinAssistToggleWindowState(visible);
1246     else
1247 #endif
1248         if (!m_isWayland) {
1249         xshapeToggleWindowState(visible);
1250     } else {
1251         if (visible) {
1252             sharedPreHideWindow();
1253 
1254             hide();
1255 
1256             sharedAfterHideWindow();
1257         } else {
1258             sharedPreOpenWindow();
1259             if (!m_isWayland) {
1260                 slideWindow();
1261             }
1262 
1263             show();
1264             if (m_isWayland) {
1265                 slideWindow();
1266             }
1267 
1268             sharedAfterOpenWindow();
1269         }
1270     }
1271 }
1272 
1273 void MainWindow::slideWindow()
1274 {
1275     if (KWindowEffects::isEffectAvailable(KWindowEffects::Slide)) {
1276         KWindowEffects::slideWindow(windowHandle(), KWindowEffects::TopEdge);
1277     }
1278 }
1279 
1280 #if HAVE_X11
1281 void MainWindow::kwinAssistToggleWindowState(bool visible)
1282 {
1283     bool gotEffect = false;
1284 
1285     Display *display = QX11Info::display();
1286     Atom atom = XInternAtom(display, "_KDE_SLIDE", false);
1287     int count;
1288     Atom *list = XListProperties(display, DefaultRootWindow(display), &count);
1289 
1290     if (list != nullptr) {
1291         gotEffect = (std::find(list, list + count, atom) != list + count);
1292 
1293         XFree(list);
1294     }
1295 
1296     if (gotEffect) {
1297         Atom atom = XInternAtom(display, "_KDE_SLIDE", false);
1298 
1299         if (Settings::frames() > 0) {
1300             QVarLengthArray<long, 1024> data(4);
1301 
1302             data[0] = 0;
1303             data[1] = 1;
1304             data[2] = Settings::frames() * 10;
1305             data[3] = Settings::frames() * 10;
1306 
1307             XChangeProperty(display, winId(), atom, atom, 32, PropModeReplace, reinterpret_cast<unsigned char *>(data.data()), data.size());
1308 
1309             m_kwinAssistPropSet = true;
1310         } else
1311             XDeleteProperty(display, winId(), atom);
1312 
1313         if (visible) {
1314             sharedPreHideWindow();
1315 
1316             hide();
1317 
1318             sharedAfterHideWindow();
1319         } else {
1320             sharedPreOpenWindow();
1321 
1322             show();
1323 
1324             sharedAfterOpenWindow();
1325         }
1326 
1327         return;
1328     }
1329 
1330     // Fall back to legacy animation strategy if kwin doesn't have the
1331     // effect loaded.
1332     xshapeToggleWindowState(visible);
1333 }
1334 
1335 void MainWindow::kwinAssistPropCleanup()
1336 {
1337     if (!QX11Info::isPlatformX11())
1338         return;
1339 
1340     Display *display = QX11Info::display();
1341     Atom atom = XInternAtom(display, "_KDE_SLIDE", false);
1342 
1343     XDeleteProperty(display, winId(), atom);
1344 
1345     m_kwinAssistPropSet = false;
1346 }
1347 #endif
1348 
1349 void MainWindow::xshapeToggleWindowState(bool visible)
1350 {
1351     if (m_animationTimer.isActive())
1352         return;
1353 
1354     if (visible) {
1355         sharedPreHideWindow();
1356 
1357         m_animationFrame = Settings::frames();
1358 
1359         connect(&m_animationTimer, SIGNAL(timeout()), this, SLOT(xshapeRetractWindow()));
1360         m_animationTimer.start();
1361     } else {
1362         m_animationFrame = 0;
1363 
1364         connect(&m_animationTimer, SIGNAL(timeout()), this, SLOT(xshapeOpenWindow()));
1365         m_animationTimer.start();
1366     }
1367 }
1368 
1369 void MainWindow::xshapeOpenWindow()
1370 {
1371     if (m_animationFrame == 0) {
1372         sharedPreOpenWindow();
1373 
1374         show();
1375 
1376         sharedAfterOpenWindow();
1377     }
1378 
1379     if (m_animationFrame == Settings::frames()) {
1380         m_animationTimer.stop();
1381         m_animationTimer.disconnect();
1382 
1383         m_titleBar->move(0, height() - m_titleBar->height());
1384         updateMask();
1385     } else {
1386         int maskHeight = m_animationStepSize * m_animationFrame;
1387 
1388         QRegion newMask = m_titleBar->mask();
1389         newMask.translate(0, maskHeight);
1390         newMask += QRegion(0, 0, width(), maskHeight);
1391 
1392         m_titleBar->move(0, maskHeight);
1393         setMask(newMask);
1394 
1395         m_animationFrame++;
1396     }
1397 }
1398 
1399 void MainWindow::xshapeRetractWindow()
1400 {
1401     if (m_animationFrame == 0) {
1402         m_animationTimer.stop();
1403         m_animationTimer.disconnect();
1404 
1405         hide();
1406 
1407         sharedAfterHideWindow();
1408     } else {
1409         m_titleBar->move(0, m_titleBar->y() - m_animationStepSize);
1410         setMask(QRegion(mask()).translated(0, -m_animationStepSize));
1411 
1412         --m_animationFrame;
1413     }
1414 }
1415 
1416 void MainWindow::sharedPreOpenWindow()
1417 {
1418     applyWindowGeometry();
1419 
1420     updateUseTranslucency();
1421 
1422     if (Settings::pollMouse())
1423         toggleMousePoll(false);
1424     if (Settings::rememberFullscreen())
1425         setFullScreen(m_isFullscreen);
1426 }
1427 
1428 void MainWindow::sharedAfterOpenWindow()
1429 {
1430     if (!Settings::firstRun() && KWindowSystem::isPlatformX11()) {
1431         KX11Extras::forceActiveWindow(winId());
1432     }
1433 
1434     connect(qGuiApp, &QGuiApplication::focusWindowChanged, this, &MainWindow::wmActiveWindowChanged);
1435 
1436     applyWindowProperties();
1437 
1438 #if HAVE_KWAYLAND
1439     initWaylandSurface();
1440 #endif
1441 
1442     Q_EMIT windowOpened();
1443 }
1444 
1445 void MainWindow::sharedPreHideWindow()
1446 {
1447     disconnect(qGuiApp, &QGuiApplication::focusWindowChanged, this, &MainWindow::wmActiveWindowChanged);
1448 }
1449 
1450 void MainWindow::sharedAfterHideWindow()
1451 {
1452     if (Settings::pollMouse())
1453         toggleMousePoll(true);
1454 
1455 #if HAVE_KWAYLAND
1456     delete m_plasmaShellSurface;
1457     m_plasmaShellSurface = nullptr;
1458 #endif
1459 
1460     Q_EMIT windowClosed();
1461 }
1462 
1463 void MainWindow::activate()
1464 {
1465     KWindowSystem::activateWindow(windowHandle());
1466 }
1467 
1468 void MainWindow::toggleMousePoll(bool poll)
1469 {
1470     if (poll)
1471         m_mousePoller.start(Settings::pollInterval());
1472     else
1473         m_mousePoller.stop();
1474 }
1475 
1476 void MainWindow::pollMouse()
1477 {
1478     QPoint pos = QCursor::pos();
1479     QRect workArea = getDesktopGeometry();
1480 
1481     int windowX = workArea.x() + workArea.width() * Settings::position() * (100 - Settings::width()) / 10000;
1482     int windowWidth = workArea.width() * Settings::width() / 100;
1483 
1484     if (pos.y() == 0 && pos.x() >= windowX && pos.x() <= (windowX + windowWidth))
1485         toggleWindowState();
1486 }
1487 
1488 void MainWindow::setKeepOpen(bool keepOpen)
1489 {
1490     if (Settings::keepOpen() != keepOpen) {
1491         Settings::setKeepOpen(keepOpen);
1492         Settings::self()->save();
1493 
1494         applyWindowProperties();
1495     }
1496 
1497     actionCollection()->action(QStringLiteral("keep-open"))->setChecked(keepOpen);
1498     m_titleBar->setFocusButtonState(keepOpen);
1499 }
1500 
1501 void MainWindow::setFullScreen(bool state)
1502 {
1503     if (isVisible())
1504         m_isFullscreen = state;
1505     if (state) {
1506         setWindowState(windowState() | Qt::WindowFullScreen);
1507         setWindowGeometry(100, 100, Settings::position());
1508     } else {
1509         setWindowState(windowState() & ~Qt::WindowFullScreen);
1510         setWindowGeometry(Settings::width(), Settings::height(), Settings::position());
1511     }
1512 }
1513 
1514 int MainWindow::getScreen()
1515 {
1516     if (Settings::screen() <= 0 || Settings::screen() > QGuiApplication::screens().length()) {
1517         auto message = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KWin"),
1518                                                       QStringLiteral("/KWin"),
1519                                                       QStringLiteral("org.kde.KWin"),
1520                                                       QStringLiteral("activeOutputName"));
1521         QDBusReply<QString> reply = QDBusConnection::sessionBus().call(message);
1522 
1523         if (reply.isValid()) {
1524             const auto screens = QGuiApplication::screens();
1525             for (int i = 0; i < screens.size(); ++i) {
1526                 if (screens[i]->name() == reply.value())
1527                     return i;
1528             }
1529         }
1530         // Right after unplugging an external monitor and the Yakuake window was on
1531         // that monitor, QGuiApplication::screenAt() can return nullptr so we fallback on
1532         // the first monitor.
1533         QScreen *screen = QGuiApplication::screenAt(QCursor::pos());
1534         return screen ? QGuiApplication::screens().indexOf(screen) : 0;
1535     } else {
1536         return Settings::screen() - 1;
1537     }
1538 }
1539 
1540 QRect MainWindow::getScreenGeometry()
1541 {
1542     QScreen *screen = QGuiApplication::screens().at(getScreen());
1543     QRect screenGeometry = screen->geometry();
1544     screenGeometry.moveTo(screenGeometry.topLeft() / screen->devicePixelRatio());
1545     return screenGeometry;
1546 }
1547 
1548 QRect MainWindow::getDesktopGeometry()
1549 {
1550     QRect screenGeometry = getScreenGeometry();
1551 
1552     QAction *action = actionCollection()->action(QStringLiteral("view-full-screen"));
1553 
1554     if (action->isChecked())
1555         return screenGeometry;
1556 
1557     if (m_isWayland) {
1558         // on Wayland it's not possible to get the work area from KWindowSystem
1559         // but plasmashell provides this through dbus
1560         return m_availableScreenRect.isValid() ? m_availableScreenRect : screenGeometry;
1561     }
1562 
1563     if (QGuiApplication::screens().count() > 1) {
1564         const QList<WId> allWindows = KX11Extras::windows();
1565         QList<WId> offScreenWindows;
1566 
1567         QListIterator<WId> i(allWindows);
1568 
1569         while (i.hasNext()) {
1570             WId windowId = i.next();
1571 
1572             if (KX11Extras::hasWId(windowId)) {
1573                 KWindowInfo windowInfo = KWindowInfo(windowId, NET::WMDesktop | NET::WMGeometry, NET::WM2ExtendedStrut);
1574 
1575                 // If windowInfo is valid and the window is located at the same (current)
1576                 // desktop with the yakuake window...
1577                 if (windowInfo.valid() && windowInfo.isOnCurrentDesktop()) {
1578                     NETExtendedStrut strut = windowInfo.extendedStrut();
1579 
1580                     // Get the area covered by each strut.
1581                     QRect topStrut(strut.top_start, 0, strut.top_end - strut.top_start, strut.top_width);
1582                     QRect bottomStrut(strut.bottom_start,
1583                                       screenGeometry.bottom() - strut.bottom_width,
1584                                       strut.bottom_end - strut.bottom_start,
1585                                       strut.bottom_width);
1586                     QRect leftStrut(0, strut.left_start, strut.left_width, strut.left_end - strut.left_start);
1587                     QRect rightStrut(screenGeometry.right() - strut.right_width, strut.right_start, strut.right_width, strut.right_end - strut.right_start);
1588 
1589                     // If the window has no strut, no need to bother further.
1590                     if (topStrut.isEmpty() && bottomStrut.isEmpty() && leftStrut.isEmpty() && rightStrut.isEmpty())
1591                         continue;
1592 
1593                     // If any of the strut and the window itself intersects with our screen geometry,
1594                     // it will be correctly handled by workArea(). If the window doesn't intersect
1595                     // with our screen geometry it's most likely a plasma panel and can/should be
1596                     // ignored
1597                     if ((topStrut.intersects(screenGeometry) || bottomStrut.intersects(screenGeometry) || leftStrut.intersects(screenGeometry)
1598                          || rightStrut.intersects(screenGeometry))
1599                         && windowInfo.geometry().intersects(screenGeometry)) {
1600                         continue;
1601                     }
1602 
1603                     // This window has a strut on the same desktop as us but which does not cover our screen
1604                     // geometry. It should be ignored, otherwise the returned work area will wrongly include
1605                     // the strut.
1606                     offScreenWindows << windowId;
1607                 }
1608             }
1609         }
1610 
1611         return KX11Extras::workArea(offScreenWindows).intersected(screenGeometry);
1612     }
1613 
1614 #if HAVE_X11
1615     return KX11Extras::workArea();
1616 #else
1617     return QRect();
1618 #endif
1619 }
1620 
1621 void MainWindow::whatsThis()
1622 {
1623     QWhatsThis::enterWhatsThisMode();
1624 }
1625 
1626 void MainWindow::showFirstRunDialog()
1627 {
1628     if (!m_firstRunDialog) {
1629         m_firstRunDialog = new FirstRunDialog(this);
1630 
1631         connect(m_firstRunDialog, &QDialog::finished, this, &MainWindow::firstRunDialogFinished);
1632         connect(m_firstRunDialog, &QDialog::accepted, this, &MainWindow::firstRunDialogOk);
1633     }
1634 
1635     m_firstRunDialog->show();
1636 }
1637 
1638 void MainWindow::firstRunDialogFinished()
1639 {
1640     Settings::setFirstRun(false);
1641     Settings::self()->save();
1642 
1643     m_firstRunDialog->deleteLater();
1644 
1645     if (KWindowSystem::isPlatformX11()) {
1646         KX11Extras::forceActiveWindow(winId());
1647     }
1648 }
1649 
1650 void MainWindow::firstRunDialogOk()
1651 {
1652     QAction *action = static_cast<QAction *>(actionCollection()->action(QStringLiteral("toggle-window-state")));
1653 
1654     KGlobalAccel::self()->setShortcut(action, QList<QKeySequence>() << m_firstRunDialog->keySequence(), KGlobalAccel::NoAutoloading);
1655 
1656     actionCollection()->writeSettings();
1657 }
1658 
1659 void MainWindow::updateUseTranslucency()
1660 {
1661     m_useTranslucency = (Settings::translucency() && (m_isX11 ? KX11Extras::compositingActive() : true));
1662 }
1663 
1664 void MainWindow::updateTrayTooltip()
1665 {
1666     if (!m_notifierItem) {
1667         return;
1668     }
1669 
1670     auto *action = actionCollection()->action(QStringLiteral("toggle-window-state"));
1671     const QList<QKeySequence> &shortcuts = KGlobalAccel::self()->shortcut(action);
1672     if (!shortcuts.isEmpty()) {
1673         const QString shortcut(shortcuts.first().toString(QKeySequence::NativeText));
1674         m_notifierItem->setToolTip(QStringLiteral("yakuake"), QStringLiteral("Yakuake"), xi18nc("@info", "Press <shortcut>%1</shortcut> to open", shortcut));
1675     }
1676 }
1677 
1678 #include "moc_mainwindow.cpp"