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"