File indexing completed on 2024-04-21 05:51:24
0001 /* 0002 SPDX-FileCopyrightText: 2006-2008 Robert Knight <robertknight@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 // Own 0008 #include "MainWindow.h" 0009 0010 // Qt 0011 #include <QMenu> 0012 #include <QMenuBar> 0013 #include <QMouseEvent> 0014 #include <QScreen> 0015 #include <QWindow> 0016 0017 // KDE 0018 #include <KAcceleratorManager> 0019 #include <KActionCollection> 0020 #include <KActionMenu> 0021 #include <KColorSchemeManager> 0022 #include <KColorSchemeMenu> 0023 #include <KCrash> 0024 #include <KHamburgerMenu> 0025 #include <KIconUtils> 0026 #include <KLocalizedString> 0027 #include <KMessageBox> 0028 #include <KNotifyConfigWidget> 0029 #include <KShortcutsDialog> 0030 #include <KStandardAction> 0031 #include <KStandardGuiItem> 0032 #include <KToolBar> 0033 #include <KWindowConfig> 0034 #include <KWindowEffects> 0035 #include <KWindowSystem> 0036 #include <KXMLGUIFactory> 0037 0038 #if HAVE_X11 0039 #include <KX11Extras> 0040 #endif 0041 0042 // Konsole 0043 #include "BookmarkHandler.h" 0044 #include "KonsoleSettings.h" 0045 #include "ViewManager.h" 0046 #include "WindowSystemInfo.h" 0047 0048 #include "profile/ProfileList.h" 0049 #include "profile/ProfileManager.h" 0050 0051 #include "session/Session.h" 0052 #include "session/SessionController.h" 0053 #include "session/SessionManager.h" 0054 0055 #include "settings/ConfigurationDialog.h" 0056 #include "settings/GeneralSettings.h" 0057 #include "settings/MemorySettings.h" 0058 #include "settings/ProfileSettings.h" 0059 #include "settings/TabBarSettings.h" 0060 #include "settings/TemporaryFilesSettings.h" 0061 #include "settings/ThumbnailsSettings.h" 0062 0063 #include "terminalDisplay/TerminalDisplay.h" 0064 #include "widgets/ViewContainer.h" 0065 0066 #include <konsoledebug.h> 0067 0068 using namespace Konsole; 0069 0070 MainWindow::MainWindow() 0071 : KXmlGuiWindow() 0072 , _viewManager(nullptr) 0073 , _bookmarkHandler(nullptr) 0074 , _toggleMenuBarAction(nullptr) 0075 , _newTabMenuAction(nullptr) 0076 , _pluggedController(nullptr) 0077 { 0078 // Set the WA_NativeWindow attribute to force the creation of the QWindow. 0079 // Without this QWidget::windowHandle() returns 0. 0080 // See https://phabricator.kde.org/D23108 0081 setAttribute(Qt::WA_NativeWindow); 0082 0083 updateUseTransparency(); 0084 0085 // create actions for menus 0086 setupActions(); 0087 0088 // create view manager 0089 _viewManager = new ViewManager(this, actionCollection()); 0090 connect(_viewManager, &Konsole::ViewManager::empty, this, &QWidget::close); 0091 // QueuedConnection so that KHamburgerMenu showed up properly on the first tab 0092 connect(_viewManager, &Konsole::ViewManager::activeViewChanged, this, &Konsole::MainWindow::activeViewChanged, Qt::QueuedConnection); 0093 connect(_viewManager, &Konsole::ViewManager::unplugController, this, &Konsole::MainWindow::disconnectController); 0094 connect(_viewManager, &Konsole::ViewManager::viewPropertiesChanged, bookmarkHandler(), &Konsole::BookmarkHandler::setViews); 0095 connect(_viewManager, &Konsole::ViewManager::blurSettingChanged, this, &Konsole::MainWindow::setBlur); 0096 0097 connect(_viewManager, &Konsole::ViewManager::updateWindowIcon, this, &Konsole::MainWindow::updateWindowIcon); 0098 connect(_viewManager, &Konsole::ViewManager::newViewWithProfileRequest, this, &Konsole::MainWindow::newFromProfile); 0099 connect(_viewManager, &Konsole::ViewManager::newViewRequest, this, &Konsole::MainWindow::newTab); 0100 connect(_viewManager, &Konsole::ViewManager::terminalsDetached, this, &Konsole::MainWindow::terminalsDetached); 0101 connect(_viewManager, &Konsole::ViewManager::activationRequest, this, &Konsole::MainWindow::activationRequest); 0102 0103 setCentralWidget(_viewManager->widget()); 0104 0105 // disable automatically generated accelerators in top-level 0106 // menu items - to avoid conflicting with Alt+[Letter] shortcuts 0107 // in terminal applications 0108 KAcceleratorManager::setNoAccel(menuBar()); 0109 0110 constexpr KXmlGuiWindow::StandardWindowOptions guiOpts = ToolBar | Keys | Save | Create; 0111 const QString xmlFile = componentName() + QLatin1String("ui.rc"); // Typically "konsoleui.rc" 0112 // The "Create" flag will make it call createGUI() 0113 setupGUI(guiOpts, xmlFile); 0114 0115 // remember the original menu accelerators for later use 0116 rememberMenuAccelerators(); 0117 0118 // replace standard shortcuts which cannot be used in a terminal 0119 // emulator (as they are reserved for use by terminal applications) 0120 correctStandardShortcuts(); 0121 0122 setProfileList(new ProfileList(true, this)); 0123 0124 // this must come at the end 0125 applyKonsoleSettings(); 0126 connect(KonsoleSettings::self(), &Konsole::KonsoleSettings::configChanged, this, &Konsole::MainWindow::applyKonsoleSettings); 0127 0128 KCrash::initialize(); 0129 } 0130 0131 bool MainWindow::wasWindowGeometrySaved() const 0132 { 0133 KSharedConfigPtr konsoleConfig = KSharedConfig::openConfig(QStringLiteral("konsolerc")); 0134 KConfigGroup cg = konsoleConfig->group(QStringLiteral("MainWindow")); 0135 if (!cg.exists()) { // First run, no existing konsolerc? 0136 return false; 0137 } 0138 0139 return KWindowConfig::hasSavedWindowSize(cg) || KWindowConfig::hasSavedWindowPosition(cg); 0140 } 0141 0142 void MainWindow::updateUseTransparency() 0143 { 0144 if (!WindowSystemInfo::HAVE_TRANSPARENCY) { 0145 return; 0146 } 0147 0148 bool useTranslucency = WindowSystemInfo::compositingActive(); 0149 0150 setAttribute(Qt::WA_TranslucentBackground, useTranslucency); 0151 setAttribute(Qt::WA_NoSystemBackground, false); 0152 WindowSystemInfo::HAVE_TRANSPARENCY = useTranslucency; 0153 } 0154 0155 void MainWindow::activationRequest(const QString &xdgActivationToken) 0156 { 0157 KWindowSystem::setCurrentXdgActivationToken(xdgActivationToken); 0158 0159 if (KWindowSystem::isPlatformX11()) { 0160 #if HAVE_X11 0161 KX11Extras::forceActiveWindow(winId()); 0162 #endif 0163 } else { 0164 KWindowSystem::activateWindow(windowHandle()); 0165 } 0166 } 0167 0168 void MainWindow::rememberMenuAccelerators() 0169 { 0170 const QList<QAction *> actions = menuBar()->actions(); 0171 for (QAction *menuItem : actions) { 0172 QString itemText = menuItem->text(); 0173 menuItem->setData(itemText); 0174 } 0175 } 0176 0177 // remove accelerators for standard menu items (eg. &File, &View, &Edit) 0178 // etc. which are defined in kdelibs/kdeui/xmlgui/ui_standards.rc, again, 0179 // to avoid conflicting with Alt+[Letter] terminal shortcuts 0180 // 0181 // TODO - Modify XMLGUI so that it allows the text for standard actions 0182 // defined in ui_standards.rc to be re-defined in the local application 0183 // XMLGUI file (konsoleui.rc in this case) - the text for standard items 0184 // can then be redefined there to exclude the standard accelerators 0185 void MainWindow::removeMenuAccelerators() 0186 { 0187 const QList<QAction *> actions = menuBar()->actions(); 0188 for (QAction *menuItem : actions) { 0189 menuItem->setText(menuItem->text().replace(QLatin1Char('&'), QString())); 0190 } 0191 } 0192 0193 void MainWindow::restoreMenuAccelerators() 0194 { 0195 const QList<QAction *> actions = menuBar()->actions(); 0196 for (QAction *menuItem : actions) { 0197 QString itemText = menuItem->data().toString(); 0198 menuItem->setText(itemText); 0199 } 0200 } 0201 0202 void MainWindow::correctStandardShortcuts() 0203 { 0204 // replace F1 shortcut for help contents 0205 QAction *helpAction = actionCollection()->action(QStringLiteral("help_contents")); 0206 if (helpAction != nullptr) { 0207 actionCollection()->setDefaultShortcut(helpAction, QKeySequence()); 0208 } 0209 } 0210 0211 ViewManager *MainWindow::viewManager() const 0212 { 0213 return _viewManager; 0214 } 0215 0216 void MainWindow::disconnectController(SessionController *controller) 0217 { 0218 disconnect(controller, &Konsole::SessionController::titleChanged, this, &Konsole::MainWindow::activeViewTitleChanged); 0219 disconnect(controller, &Konsole::SessionController::rawTitleChanged, this, &Konsole::MainWindow::updateWindowCaption); 0220 disconnect(controller, &Konsole::SessionController::iconChanged, this, &Konsole::MainWindow::updateWindowIcon); 0221 0222 if (auto view = controller->view()) { 0223 view->removeEventFilter(this); 0224 } 0225 0226 // KXmlGuiFactory::removeClient() will try to access actions associated 0227 // with the controller internally, which may not be valid after the controller 0228 // itself is no longer valid (after the associated session and or view have 0229 // been destroyed) 0230 if (controller->isValid()) { 0231 guiFactory()->removeClient(controller); 0232 } 0233 0234 if (_pluggedController == controller) { 0235 _pluggedController = nullptr; 0236 } 0237 } 0238 0239 void MainWindow::activeViewChanged(SessionController *controller) 0240 { 0241 if (!SessionManager::instance()->sessionProfile(controller->session())) { 0242 return; 0243 } 0244 // associate bookmark menu with current session 0245 bookmarkHandler()->setActiveView(controller); 0246 disconnect(bookmarkHandler(), &Konsole::BookmarkHandler::openUrl, nullptr, nullptr); 0247 connect(bookmarkHandler(), &Konsole::BookmarkHandler::openUrl, controller, &Konsole::SessionController::openUrl); 0248 0249 if (!_pluggedController.isNull()) { 0250 disconnectController(_pluggedController); 0251 } 0252 0253 Q_ASSERT(controller); 0254 _pluggedController = controller; 0255 _pluggedController->view()->installEventFilter(this); 0256 0257 setBlur(ViewManager::profileHasBlurEnabled(SessionManager::instance()->sessionProfile(_pluggedController->session()))); 0258 0259 // listen for title changes from the current session 0260 connect(controller, &Konsole::SessionController::titleChanged, this, &Konsole::MainWindow::activeViewTitleChanged); 0261 connect(controller, &Konsole::SessionController::rawTitleChanged, this, &Konsole::MainWindow::updateWindowCaption); 0262 connect(controller, &Konsole::SessionController::iconChanged, this, &Konsole::MainWindow::updateWindowIcon); 0263 0264 // to prevent shortcuts conflict 0265 if (auto hamburgerMenu = _hamburgerMenu->menu()) { 0266 hamburgerMenu->clear(); 0267 } 0268 0269 controller->setShowMenuAction(_toggleMenuBarAction); 0270 guiFactory()->addClient(controller); 0271 0272 // update session title to match newly activated session 0273 activeViewTitleChanged(controller); 0274 0275 // Update window icon to newly activated session's icon 0276 updateWindowIcon(); 0277 0278 for (IKonsolePlugin *plugin : _plugins) { 0279 plugin->activeViewChanged(controller, this); 0280 } 0281 } 0282 0283 void MainWindow::activeViewTitleChanged(ViewProperties *properties) 0284 { 0285 Q_UNUSED(properties) 0286 updateWindowCaption(); 0287 } 0288 0289 void MainWindow::updateWindowCaption() 0290 { 0291 if (_pluggedController.isNull()) { 0292 return; 0293 } 0294 0295 const QString &title = _pluggedController->title(); 0296 const QString &userTitle = _pluggedController->userTitle(); 0297 0298 // use tab title as caption by default 0299 QString caption = title; 0300 0301 // use window title as caption when this setting is enabled 0302 // if the userTitle is empty, use a blank space (using an empty string 0303 // removes the dash — before the application name; leaving the dash 0304 // looks better) 0305 if (KonsoleSettings::showWindowTitleOnTitleBar()) { 0306 !userTitle.isEmpty() ? caption = userTitle : caption = QStringLiteral(" "); 0307 } 0308 0309 setCaption(caption); 0310 } 0311 0312 void MainWindow::updateWindowIcon() 0313 { 0314 if ((!_pluggedController.isNull()) && !_pluggedController->icon().isNull()) { 0315 setWindowIcon(_pluggedController->icon()); 0316 } 0317 } 0318 0319 void MainWindow::setupActions() 0320 { 0321 KActionCollection *collection = actionCollection(); 0322 0323 // File Menu 0324 _newTabMenuAction = new KActionMenu(QIcon::fromTheme(QStringLiteral("tab-new")), i18nc("@action:inmenu", "&New Tab"), collection); 0325 collection->setDefaultShortcut(_newTabMenuAction, Konsole::ACCEL | Qt::Key_T); 0326 collection->setShortcutsConfigurable(_newTabMenuAction, true); 0327 _newTabMenuAction->setAutoRepeat(false); 0328 connect(_newTabMenuAction, &KActionMenu::triggered, this, &MainWindow::newTab); 0329 collection->addAction(QStringLiteral("new-tab"), _newTabMenuAction); 0330 collection->setShortcutsConfigurable(_newTabMenuAction, true); 0331 0332 QAction *menuAction = collection->addAction(QStringLiteral("clone-tab")); 0333 menuAction->setIcon(QIcon::fromTheme(QStringLiteral("tab-duplicate"))); 0334 menuAction->setText(i18nc("@action:inmenu", "&Clone Tab")); 0335 collection->setDefaultShortcut(menuAction, QKeySequence()); 0336 menuAction->setAutoRepeat(false); 0337 connect(menuAction, &QAction::triggered, this, &Konsole::MainWindow::cloneTab); 0338 0339 menuAction = collection->addAction(QStringLiteral("new-window")); 0340 menuAction->setIcon(QIcon::fromTheme(QStringLiteral("window-new"))); 0341 menuAction->setText(i18nc("@action:inmenu", "New &Window")); 0342 collection->setDefaultShortcut(menuAction, Konsole::ACCEL | Qt::Key_N); 0343 menuAction->setAutoRepeat(false); 0344 connect(menuAction, &QAction::triggered, this, &Konsole::MainWindow::newWindow); 0345 0346 menuAction = collection->addAction(QStringLiteral("close-window")); 0347 menuAction->setIcon(QIcon::fromTheme(QStringLiteral("window-close"))); 0348 menuAction->setText(i18nc("@action:inmenu", "Close Window")); 0349 collection->setDefaultShortcut(menuAction, Konsole::ACCEL | Qt::Key_Q); 0350 connect(menuAction, &QAction::triggered, this, &Konsole::MainWindow::close); 0351 0352 // Bookmark Menu 0353 KActionMenu *bookmarkMenu = new KActionMenu(i18nc("@title:menu", "&Bookmarks"), collection); 0354 bookmarkMenu->setPopupMode(QToolButton::InstantPopup); // Don't require press+hold 0355 _bookmarkHandler = new BookmarkHandler(collection, bookmarkMenu->menu(), true, this); 0356 collection->addAction(QStringLiteral("bookmark"), bookmarkMenu); 0357 connect(_bookmarkHandler, &Konsole::BookmarkHandler::openUrls, this, &Konsole::MainWindow::openUrls); 0358 0359 // Settings Menu 0360 _toggleMenuBarAction = KStandardAction::showMenubar(menuBar(), &QMenuBar::setVisible, collection); 0361 collection->setDefaultShortcut(_toggleMenuBarAction, Konsole::ACCEL | Qt::Key_M); 0362 connect(_toggleMenuBarAction, &QAction::triggered, [=] { 0363 // Remove menubar icons set for the hamburger menu, so they don't override 0364 // the text when they appear in the in-window menubar 0365 collection->action(QStringLiteral("bookmark"))->setIcon(QIcon()); 0366 static_cast<QMenu *>(factory()->container(QStringLiteral("plugins"), this))->setIcon(QIcon()); 0367 }); 0368 0369 // Set up themes 0370 auto *manager = new KColorSchemeManager(actionCollection()); 0371 manager->setAutosaveChanges(true); 0372 KActionMenu *selectionMenu = KColorSchemeMenu::createMenu(manager, this); 0373 auto winColorSchemeMenu = new QAction(this); 0374 winColorSchemeMenu->setMenu(selectionMenu->menu()); 0375 winColorSchemeMenu->menu()->setIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-color"))); 0376 winColorSchemeMenu->menu()->setTitle(i18n("&Window Color Scheme")); 0377 actionCollection()->addAction(QStringLiteral("window-colorscheme-menu"), winColorSchemeMenu); 0378 0379 // Full Screen 0380 menuAction = KStandardAction::fullScreen(this, &MainWindow::viewFullScreen, this, collection); 0381 collection->setDefaultShortcut(menuAction, Qt::Key_F11); 0382 0383 KStandardAction::configureNotifications(this, &MainWindow::configureNotifications, collection); 0384 KStandardAction::keyBindings(this, &MainWindow::showShortcutsDialog, collection); 0385 KStandardAction::preferences(this, &MainWindow::showSettingsDialog, collection); 0386 0387 menuAction = collection->addAction(QStringLiteral("manage-profiles")); 0388 menuAction->setText(i18nc("@action:inmenu", "Manage Profiles...")); 0389 menuAction->setIcon(QIcon::fromTheme(QStringLiteral("configure"))); 0390 connect(menuAction, &QAction::triggered, this, &Konsole::MainWindow::showManageProfilesDialog); 0391 0392 // Set up an shortcut-only action for activating menu bar. 0393 menuAction = collection->addAction(QStringLiteral("activate-menu")); 0394 menuAction->setText(i18nc("@item", "Activate Menu")); 0395 collection->setDefaultShortcut(menuAction, Konsole::ACCEL | Qt::Key_F10); 0396 connect(menuAction, &QAction::triggered, this, &Konsole::MainWindow::activateMenuBar); 0397 0398 auto action = collection->addAction(QStringLiteral("save-layout")); 0399 action->setEnabled(true); 0400 action->setText(i18nc("@action:inmenu", "Save Tab Layout...")); 0401 connect(action, &QAction::triggered, this, [this]() { 0402 if (viewManager()) { 0403 viewManager()->saveLayoutFile(); 0404 } 0405 }); 0406 0407 action = collection->addAction(QStringLiteral("load-layout")); 0408 action->setEnabled(true); 0409 action->setText(i18nc("@action:inmenu", "Load Tab Layout...")); 0410 connect(action, &QAction::triggered, this, [this]() { 0411 if (viewManager()) { 0412 viewManager()->loadLayoutFile(); 0413 } 0414 }); 0415 0416 // Hamburger menu for when the menubar is hidden 0417 _hamburgerMenu = KStandardAction::hamburgerMenu(nullptr, nullptr, collection); 0418 _hamburgerMenu->setShowMenuBarAction(_toggleMenuBarAction); 0419 _hamburgerMenu->setMenuBar(menuBar()); 0420 connect(_hamburgerMenu, &KHamburgerMenu::aboutToShowMenu, this, &MainWindow::updateHamburgerMenu); 0421 } 0422 0423 void MainWindow::updateHamburgerMenu() 0424 { 0425 KActionCollection *collection = actionCollection(); 0426 KActionCollection *controllerCollection = _pluggedController->actionCollection(); 0427 0428 QMenu *menu = _hamburgerMenu->menu(); 0429 if (!menu) { 0430 menu = new QMenu(widget()); 0431 _hamburgerMenu->setMenu(menu); 0432 } else { 0433 menu->clear(); 0434 } 0435 0436 menu->addAction(collection->action(QStringLiteral("new-window"))); 0437 menu->addAction(collection->action(QStringLiteral("new-tab"))); 0438 menu->addAction(controllerCollection->action(QStringLiteral("file_save_as"))); 0439 0440 menu->addSeparator(); 0441 0442 menu->addAction(controllerCollection->action(QStringLiteral("edit_copy"))); 0443 menu->addAction(controllerCollection->action(QStringLiteral("edit_paste"))); 0444 menu->addAction(controllerCollection->action(QStringLiteral("edit_find"))); 0445 0446 menu->addSeparator(); 0447 0448 menu->addAction(collection->action(KStandardAction::name(KStandardAction::FullScreen))); 0449 menu->addAction(collection->action(QStringLiteral("split-view"))); 0450 menu->addAction(controllerCollection->action(QStringLiteral("clear-history-and-reset"))); 0451 menu->addAction(controllerCollection->action(QStringLiteral("enlarge-font"))); 0452 menu->addAction(controllerCollection->action(QStringLiteral("reset-font-size"))); 0453 menu->addAction(controllerCollection->action(QStringLiteral("shrink-font"))); 0454 0455 menu->addSeparator(); 0456 0457 menu->addAction(controllerCollection->action(QStringLiteral("edit-current-profile"))); 0458 menu->addAction(controllerCollection->action(QStringLiteral("switch-profile"))); 0459 0460 menu->addSeparator(); 0461 0462 auto monitorMenu = 0463 menu->addMenu(QIcon::fromTheme(QStringLiteral("visibility")), static_cast<QMenu *>(factory()->container(QStringLiteral("view"), nullptr))->title()); 0464 monitorMenu->addAction(controllerCollection->action(QStringLiteral("monitor-silence"))); 0465 monitorMenu->addAction(controllerCollection->action(QStringLiteral("monitor-activity"))); 0466 monitorMenu->addAction(controllerCollection->action(QStringLiteral("monitor-process-finish"))); 0467 menu->addMenu(monitorMenu); 0468 _hamburgerMenu->hideActionsOf(monitorMenu); 0469 0470 auto bookmarkMenu = collection->action(QStringLiteral("bookmark")); 0471 bookmarkMenu->setIcon(QIcon::fromTheme(QStringLiteral("bookmarks"))); // Icon will be removed again when the menu bar is enabled. 0472 menu->addAction(bookmarkMenu); 0473 0474 auto pluginsMenu = static_cast<QMenu *>(factory()->container(QStringLiteral("plugins"), this)); 0475 pluginsMenu->setIcon(QIcon::fromTheme(QStringLiteral("plugins"))); // Icon will be removed again when the menu bar is enabled. 0476 menu->addMenu(pluginsMenu); 0477 0478 auto configureMenu = 0479 menu->addMenu(QIcon::fromTheme(QStringLiteral("configure")), static_cast<QMenu *>(factory()->container(QStringLiteral("settings"), nullptr))->title()); 0480 configureMenu->addAction(toolBarMenuAction()); 0481 configureMenu->addSeparator(); 0482 configureMenu->addAction(collection->action(KStandardAction::name(KStandardAction::SwitchApplicationLanguage))); 0483 configureMenu->addAction(collection->action(KStandardAction::name(KStandardAction::KeyBindings))); 0484 configureMenu->addAction(collection->action(KStandardAction::name(KStandardAction::ConfigureToolbars))); 0485 configureMenu->addAction(collection->action(KStandardAction::name(KStandardAction::ConfigureNotifications))); 0486 configureMenu->addAction(collection->action(KStandardAction::name(KStandardAction::Preferences))); 0487 _hamburgerMenu->hideActionsOf(configureMenu); 0488 0489 _hamburgerMenu->hideActionsOf(toolBar()); 0490 } 0491 0492 void MainWindow::viewFullScreen(bool fullScreen) 0493 { 0494 if (fullScreen) { 0495 setWindowState(windowState() | Qt::WindowFullScreen); 0496 } else { 0497 setWindowState(windowState() & ~Qt::WindowFullScreen); 0498 } 0499 } 0500 0501 void MainWindow::applyMainWindowSettings(const KConfigGroup &config) 0502 { 0503 KMainWindow::applyMainWindowSettings(config); 0504 0505 // Override the menubar state from the config file 0506 if (_windowArgsMenuBarVisible.enabled) { 0507 menuBar()->setVisible(_windowArgsMenuBarVisible.showMenuBar); 0508 } 0509 0510 _toggleMenuBarAction->setChecked(menuBar()->isVisibleTo(this)); 0511 } 0512 0513 BookmarkHandler *MainWindow::bookmarkHandler() const 0514 { 0515 return _bookmarkHandler; 0516 } 0517 0518 void MainWindow::setProfileList(ProfileList *list) 0519 { 0520 profileListChanged(list->actions()); 0521 0522 connect(list, &Konsole::ProfileList::profileSelected, this, &MainWindow::newFromProfile); 0523 connect(list, &Konsole::ProfileList::actionsChanged, this, &Konsole::MainWindow::profileListChanged); 0524 } 0525 0526 void MainWindow::profileListChanged(const QList<QAction *> &sessionActions) 0527 { 0528 // Update the 'New Tab' KActionMenu 0529 _newTabMenuAction->menu()->clear(); 0530 for (QAction *sessionAction : sessionActions) { 0531 _newTabMenuAction->menu()->addAction(sessionAction); 0532 0533 auto setActionFontBold = [sessionAction](bool isBold) { 0534 QFont actionFont = sessionAction->font(); 0535 actionFont.setBold(isBold); 0536 sessionAction->setFont(actionFont); 0537 }; 0538 0539 Profile::Ptr profile = ProfileManager::instance()->defaultProfile(); 0540 if (profile && profile->name() == sessionAction->text().remove(QLatin1Char('&'))) { 0541 QIcon icon = KIconUtils::addOverlay(QIcon::fromTheme(profile->icon()), QIcon::fromTheme(QStringLiteral("emblem-favorite")), Qt::BottomRightCorner); 0542 sessionAction->setIcon(icon); 0543 setActionFontBold(true); 0544 } else { 0545 setActionFontBold(false); 0546 } 0547 } 0548 } 0549 0550 QString MainWindow::activeSessionDir() const 0551 { 0552 if (!_pluggedController.isNull()) { 0553 if (Session *session = _pluggedController->session()) { 0554 // For new tabs to get the correct working directory, 0555 // force the updating of the currentWorkingDirectory. 0556 session->getDynamicTitle(); 0557 } 0558 return _pluggedController->currentDir(); 0559 } 0560 return QString(); 0561 } 0562 0563 void MainWindow::openUrls(const QList<QUrl> &urls) 0564 { 0565 Profile::Ptr defaultProfile = ProfileManager::instance()->defaultProfile(); 0566 0567 for (const auto &url : urls) { 0568 if (url.isLocalFile()) { 0569 createSession(defaultProfile, url.path()); 0570 } else if (url.scheme() == QLatin1String("ssh")) { 0571 createSSHSession(defaultProfile, url); 0572 } 0573 } 0574 } 0575 0576 void MainWindow::newTab() 0577 { 0578 Profile::Ptr defaultProfile = ProfileManager::instance()->defaultProfile(); 0579 createSession(defaultProfile, activeSessionDir()); 0580 } 0581 0582 void MainWindow::setPluginsActions(const QList<QAction *> &actions) 0583 { 0584 _pluginsActions = actions; 0585 unplugActionList(QStringLiteral("plugin-submenu")); 0586 plugActionList(QStringLiteral("plugin-submenu"), _pluginsActions); 0587 } 0588 0589 void MainWindow::addPlugin(IKonsolePlugin *plugin) 0590 { 0591 Q_ASSERT(std::find(_plugins.cbegin(), _plugins.cend(), plugin) == _plugins.cend()); 0592 _plugins.push_back(plugin); 0593 } 0594 0595 void MainWindow::cloneTab() 0596 { 0597 Q_ASSERT(_pluggedController); 0598 0599 Session *session = _pluggedController->session(); 0600 Profile::Ptr profile = SessionManager::instance()->sessionProfile(session); 0601 if (profile) { 0602 createSession(profile, activeSessionDir()); 0603 } else { 0604 // something must be wrong: every session should be associated with profile 0605 Q_ASSERT(false); 0606 newTab(); 0607 } 0608 } 0609 0610 Session *MainWindow::createSession(Profile::Ptr profile, const QString &directory) 0611 { 0612 if (!profile) { 0613 profile = ProfileManager::instance()->defaultProfile(); 0614 } 0615 0616 const QString newSessionDirectory = profile->startInCurrentSessionDir() ? directory : QString(); 0617 Session *session = _viewManager->createSession(profile, newSessionDirectory); 0618 0619 // create view before starting the session process so that the session 0620 // doesn't suffer a change in terminal size right after the session 0621 // starts. Some applications such as GNU Screen and Midnight Commander 0622 // don't like this happening 0623 auto newView = _viewManager->createView(session); 0624 _viewManager->activeContainer()->addView(newView); 0625 0626 _viewManager->activeViewController()->actionCollection()->addActions({_hamburgerMenu}); 0627 0628 return session; 0629 } 0630 0631 Session *MainWindow::createSSHSession(Profile::Ptr profile, const QUrl &url) 0632 { 0633 if (!profile) { 0634 profile = ProfileManager::instance()->defaultProfile(); 0635 } 0636 0637 Session *session = SessionManager::instance()->createSession(profile); 0638 0639 QString sshCommand = QStringLiteral("ssh "); 0640 if (url.port() > -1) { 0641 sshCommand += QStringLiteral("-p %1 ").arg(url.port()); 0642 } 0643 if (!url.userName().isEmpty()) { 0644 sshCommand += (url.userName() + QLatin1Char('@')); 0645 } 0646 if (!url.host().isEmpty()) { 0647 sshCommand += url.host(); 0648 } 0649 0650 session->sendTextToTerminal(sshCommand, QLatin1Char('\r')); 0651 0652 // create view before starting the session process so that the session 0653 // doesn't suffer a change in terminal size right after the session 0654 // starts. some applications such as GNU Screen and Midnight Commander 0655 // don't like this happening 0656 auto newView = _viewManager->createView(session); 0657 _viewManager->activeContainer()->addView(newView); 0658 return session; 0659 } 0660 0661 void MainWindow::setFocus() 0662 { 0663 _viewManager->activeView()->setFocus(); 0664 } 0665 0666 void MainWindow::newWindow() 0667 { 0668 Profile::Ptr defaultProfile = ProfileManager::instance()->defaultProfile(); 0669 Q_EMIT newWindowRequest(defaultProfile, activeSessionDir()); 0670 } 0671 0672 bool MainWindow::queryClose() 0673 { 0674 // Do not ask for confirmation during log out and power off 0675 // TODO: rework the dealing of this case to make it has its own confirmation 0676 // dialog. 0677 if (qApp->isSavingSession()) { 0678 return true; 0679 } 0680 0681 // Check what processes are running, excluding the shell 0682 QStringList processesRunning; 0683 // Need to make a local copy so the begin() and end() point to the same QList 0684 const QList<Session *> sessionList = _viewManager->sessions(); 0685 const QSet<Session *> uniqueSessions(sessionList.begin(), sessionList.end()); 0686 0687 for (Session *session : uniqueSessions) { 0688 if ((session == nullptr) || !session->isForegroundProcessActive()) { 0689 continue; 0690 } 0691 0692 const QString defaultProc = session->program().split(QLatin1Char('/')).last(); 0693 const QString currentProc = session->foregroundProcessName().split(QLatin1Char('/')).last(); 0694 0695 if (currentProc.isEmpty()) { 0696 continue; 0697 } 0698 0699 if (defaultProc != currentProc) { 0700 processesRunning.append(currentProc); 0701 } 0702 } 0703 0704 // Get number of open tabs 0705 const int openTabs = _viewManager->viewProperties().count(); 0706 0707 // If no processes running (except the shell) and no extra tabs, just close 0708 if (processesRunning.count() == 0 && openTabs < 2) { 0709 return true; 0710 } 0711 0712 // NOTE: Some, if not all, of the below KWindowSystem calls are only 0713 // implemented under x11 (KDE4.8 kdelibs/kdeui/windowmanagement). 0714 0715 #if HAVE_X11 0716 // make sure the window is shown on current desktop and is not minimized 0717 KX11Extras::setOnDesktop(winId(), KX11Extras::currentDesktop()); 0718 #endif 0719 int result; 0720 0721 if (!processesRunning.isEmpty()) { 0722 if (openTabs == 1) { 0723 result = KMessageBox::warningTwoActionsList(this, 0724 i18ncp("@info", 0725 "There is a process running in this window. " 0726 "Do you still want to quit?", 0727 "There are %1 processes running in this window. " 0728 "Do you still want to quit?", 0729 processesRunning.count()), 0730 processesRunning, 0731 i18nc("@title", "Confirm Close"), 0732 KGuiItem(i18nc("@action:button", "Close &Window"), QStringLiteral("window-close")), 0733 KStandardGuiItem::cancel(), 0734 // don't ask again name is wrong but I can't update. 0735 // this is not about tabs anymore. it's about empty tabs *or* splits. 0736 QStringLiteral("CloseAllTabs")); 0737 if (result == KMessageBox::SecondaryAction) // SecondaryAction is equal to cancel closing 0738 result = KMessageBox::Cancel; 0739 } else { 0740 result = KMessageBox::warningTwoActionsCancelList(this, 0741 i18ncp("@info", 0742 "There is a process running in this window. " 0743 "Do you still want to quit?", 0744 "There are %1 processes running in this window. " 0745 "Do you still want to quit?", 0746 processesRunning.count()), 0747 processesRunning, 0748 i18nc("@title", "Confirm Close"), 0749 KGuiItem(i18nc("@action:button", "Close &Window"), QStringLiteral("window-close")), 0750 KGuiItem(i18nc("@action:button", "Close Current &Tab"), QStringLiteral("tab-close")), 0751 KStandardGuiItem::cancel(), 0752 // don't ask again name is wrong but I can't update. 0753 // this is not about tabs anymore. it's about empty tabs *or* splits. 0754 QStringLiteral("CloseAllTabs")); 0755 } 0756 } else { 0757 result = KMessageBox::warningTwoActionsCancel(this, 0758 i18nc("@info", 0759 "There are %1 open terminals in this window. " 0760 "Do you still want to quit?", 0761 openTabs), 0762 i18nc("@title", "Confirm Close"), 0763 KGuiItem(i18nc("@action:button", "Close &Window"), QStringLiteral("window-close")), 0764 KGuiItem(i18nc("@action:button", "Close Current &Tab"), QStringLiteral("tab-close")), 0765 KStandardGuiItem::cancel(), 0766 // don't ask again name is wrong but I can't update. 0767 // this is not about tabs anymore. it's about empty tabs *or* splits. 0768 QStringLiteral("CloseAllEmptyTabs")); 0769 } 0770 0771 switch (result) { 0772 case KMessageBox::PrimaryAction: 0773 return true; 0774 case KMessageBox::SecondaryAction: 0775 if ((!_pluggedController.isNull()) && (!_pluggedController->session().isNull())) { 0776 if (!(_pluggedController->session()->closeInNormalWay())) { 0777 if (_pluggedController->confirmForceClose()) { 0778 _pluggedController->session()->closeInForceWay(); 0779 } 0780 } 0781 } 0782 return false; 0783 case KMessageBox::Cancel: 0784 return false; 0785 } 0786 0787 return true; 0788 } 0789 0790 void MainWindow::saveProperties(KConfigGroup &group) 0791 { 0792 _viewManager->saveSessions(group); 0793 } 0794 0795 void MainWindow::readProperties(const KConfigGroup &group) 0796 { 0797 _viewManager->restoreSessions(group); 0798 } 0799 0800 void MainWindow::saveGlobalProperties(KConfig *config) 0801 { 0802 SessionManager::instance()->saveSessions(config); 0803 } 0804 0805 void MainWindow::readGlobalProperties(KConfig *config) 0806 { 0807 SessionManager::instance()->restoreSessions(config); 0808 } 0809 0810 void MainWindow::syncActiveShortcuts(KActionCollection *dest, const KActionCollection *source) 0811 { 0812 const QList<QAction *> actionsList = source->actions(); 0813 for (QAction *qAction : actionsList) { 0814 if (QAction *destQAction = dest->action(qAction->objectName())) { 0815 destQAction->setShortcut(qAction->shortcut()); 0816 } 0817 } 0818 } 0819 0820 void MainWindow::showShortcutsDialog() 0821 { 0822 KShortcutsDialog dialog(KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsDisallowed, this); 0823 0824 // add actions from this window and the current session controller 0825 const QList<KXMLGUIClient *> clientsList = guiFactory()->clients(); 0826 for (KXMLGUIClient *client : clientsList) { 0827 dialog.addCollection(client->actionCollection()); 0828 } 0829 0830 if (dialog.configure()) { 0831 // sync shortcuts for non-session actions (defined in "konsoleui.rc") in other main windows 0832 const QList<QWidget *> widgets = QApplication::topLevelWidgets(); 0833 for (QWidget *mainWindowWidget : widgets) { 0834 auto *mainWindow = qobject_cast<MainWindow *>(mainWindowWidget); 0835 if ((mainWindow != nullptr) && mainWindow != this) { 0836 syncActiveShortcuts(mainWindow->actionCollection(), actionCollection()); 0837 } 0838 } 0839 // sync shortcuts for session actions (defined in "sessionui.rc") in other session controllers. 0840 // Controllers which are currently plugged in (ie. their actions are part of the current menu) 0841 // must be updated immediately via syncActiveShortcuts(). Other controllers will be updated 0842 // when they are plugged into a main window. 0843 const QSet<SessionController *> allControllers = SessionController::allControllers(); 0844 for (SessionController *controller : allControllers) { 0845 controller->reloadXML(); 0846 if ((controller->factory() != nullptr) && controller != _pluggedController) { 0847 syncActiveShortcuts(controller->actionCollection(), _pluggedController->actionCollection()); 0848 } 0849 } 0850 } 0851 } 0852 0853 void MainWindow::newFromProfile(const Profile::Ptr &profile) 0854 { 0855 createSession(profile, activeSessionDir()); 0856 } 0857 0858 void MainWindow::showManageProfilesDialog() 0859 { 0860 showSettingsDialog(true); 0861 } 0862 0863 void MainWindow::showSettingsDialog(const bool showProfilePage) 0864 { 0865 ConfigurationDialog *confDialog = findChild<ConfigurationDialog *>(); 0866 const QString profilePageName = i18nc("@title Preferences page name", "Profiles"); 0867 0868 if (confDialog != nullptr) { 0869 if (showProfilePage) { 0870 for (auto page : confDialog->findChildren<KPageWidgetItem *>()) { 0871 if (page->name().contains(profilePageName)) { 0872 confDialog->setCurrentPage(page); 0873 break; 0874 } 0875 } 0876 } 0877 confDialog->show(); 0878 return; 0879 } 0880 0881 confDialog = new ConfigurationDialog(this, KonsoleSettings::self()); 0882 0883 const QString generalPageName = i18nc("@title Preferences page name", "General"); 0884 auto *generalPage = new KPageWidgetItem(new GeneralSettings(confDialog), generalPageName); 0885 generalPage->setIcon(QIcon::fromTheme(QStringLiteral("utilities-terminal"))); 0886 confDialog->addPage(generalPage, true); 0887 0888 auto *profileSettings = new ProfileSettings(confDialog); 0889 auto *profilePage = new KPageWidgetItem(profileSettings, profilePageName); 0890 profilePage->setIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-theme"))); 0891 confDialog->addPage(profilePage, true); 0892 connect(confDialog, &QDialog::accepted, profileSettings, &ProfileSettings::slotAccepted); 0893 0894 #if defined(Q_OS_LINUX) 0895 const QString memoryPageName = i18nc("@title Preferences page name", "Memory"); 0896 auto memoryPage = new KPageWidgetItem(new MemorySettings(confDialog), memoryPageName); 0897 memoryPage->setIcon(QIcon::fromTheme(QStringLiteral("memory"))); 0898 confDialog->addPage(memoryPage, true); 0899 #endif 0900 0901 const QString tabBarPageName = i18nc("@title Preferences page name", "Tab Bar / Splitters"); 0902 auto tabBarPage = new KPageWidgetItem(new TabBarSettings(confDialog), tabBarPageName); 0903 tabBarPage->setIcon(QIcon::fromTheme(QStringLiteral("preferences-tabs"))); 0904 confDialog->addPage(tabBarPage, true); 0905 0906 const QString temporaryFilesPageName = i18nc("@title Preferences page name", "Temporary Files"); 0907 auto temporaryFilesPage = new KPageWidgetItem(new TemporaryFilesSettings(confDialog), temporaryFilesPageName); 0908 temporaryFilesPage->setIcon(QIcon::fromTheme(QStringLiteral("folder-temp"))); 0909 confDialog->addPage(temporaryFilesPage, true); 0910 0911 const QString thumbnailPageName = i18nc("@title Preferences page name", "Thumbnails"); 0912 auto thumbnailPage = new KPageWidgetItem(new ThumbnailsSettings(confDialog), thumbnailPageName); 0913 thumbnailPage->setIcon(QIcon::fromTheme(QStringLiteral("image-jpeg"))); 0914 confDialog->addPage(thumbnailPage, true); 0915 0916 if (showProfilePage) { 0917 confDialog->setCurrentPage(profilePage); 0918 } 0919 0920 confDialog->show(); 0921 } 0922 0923 void MainWindow::applyKonsoleSettings() 0924 { 0925 setRemoveWindowTitleBarAndFrame(KonsoleSettings::removeWindowTitleBarAndFrame()); 0926 if (KonsoleSettings::allowMenuAccelerators()) { 0927 restoreMenuAccelerators(); 0928 } else { 0929 removeMenuAccelerators(); 0930 } 0931 0932 _viewManager->activeContainer()->setNavigationBehavior(KonsoleSettings::newTabBehavior()); 0933 0934 // Save the toolbar/menu/dockwidget states and the window geometry 0935 setAutoSaveSettings(); 0936 0937 updateWindowCaption(); 0938 } 0939 0940 void MainWindow::activateMenuBar() 0941 { 0942 const QList<QAction *> menuActions = menuBar()->actions(); 0943 0944 if (menuActions.isEmpty()) { 0945 return; 0946 } 0947 0948 // Show menubar if it is hidden at the moment 0949 if (menuBar()->isHidden()) { 0950 menuBar()->setVisible(true); 0951 _toggleMenuBarAction->setChecked(true); 0952 } 0953 0954 // First menu action should be 'File' 0955 QAction *menuAction = menuActions.first(); 0956 0957 // TODO: Handle when menubar is top level (MacOS) 0958 menuBar()->setActiveAction(menuAction); 0959 } 0960 0961 void MainWindow::configureNotifications() 0962 { 0963 KNotifyConfigWidget::configure(this); 0964 } 0965 0966 void MainWindow::setBlur(bool blur) 0967 { 0968 if (_pluggedController.isNull()) { 0969 return; 0970 } 0971 0972 // Saves 70-100ms when starting 0973 if (blur == _blurEnabled) { 0974 return; 0975 } 0976 _blurEnabled = blur; 0977 0978 if (!_pluggedController->isKonsolePart()) { 0979 if (QWindow *window = windowHandle()) { 0980 KWindowEffects::enableBlurBehind(window, blur); 0981 } else { 0982 qCWarning(KonsoleDebug) << "Blur effect couldn't be enabled."; 0983 } 0984 } 0985 } 0986 0987 void MainWindow::setMenuBarInitialVisibility(bool showMenuBar) 0988 { 0989 _windowArgsMenuBarVisible.enabled = true; 0990 _windowArgsMenuBarVisible.showMenuBar = showMenuBar; 0991 } 0992 0993 void MainWindow::setRemoveWindowTitleBarAndFrame(bool frameless) 0994 { 0995 Qt::WindowFlags newFlags = frameless ? Qt::FramelessWindowHint : Qt::Window; 0996 0997 // The window is not yet visible 0998 if (!isVisible()) { 0999 setWindowFlags(newFlags); 1000 1001 // The window is visible and the setting changed 1002 } else if (windowFlags().testFlag(Qt::FramelessWindowHint) != frameless) { 1003 if (KWindowSystem::isPlatformX11()) { 1004 #if HAVE_X11 1005 const auto oldGeometry = saveGeometry(); 1006 // This happens for every Konsole window. It depends on 1007 // the fact that every window is processed in single thread 1008 const auto oldActiveWindow = KX11Extras::activeWindow(); 1009 1010 setWindowFlags(newFlags); 1011 1012 // The setWindowFlags() has hidden the window. Show it again 1013 // with previous geometry 1014 restoreGeometry(oldGeometry); 1015 setVisible(true); 1016 KX11Extras::activateWindow(oldActiveWindow); 1017 #endif 1018 } else { 1019 // Restoring geometry ourselves doesn't work on Wayland 1020 setWindowFlags(newFlags); 1021 // The setWindowFlags() has hidden the window. Show it again 1022 setVisible(true); 1023 } 1024 } 1025 } 1026 1027 void MainWindow::showEvent(QShowEvent *event) 1028 { 1029 // Apply this code on first show only 1030 if (_firstShowEvent) { 1031 _firstShowEvent = false; 1032 1033 if (!KonsoleSettings::rememberWindowSize() || !wasWindowGeometrySaved()) { 1034 // Delay resizing to here, so that the other parts of the UI 1035 // (ViewManager, TabbedViewContainer, TerminalDisplay ... etc) 1036 // have been created and TabbedViewContainer::sizeHint() returns 1037 // a usable size. 1038 1039 // Remove the WindowMaximized state to override the Window-Maximized 1040 // config key 1041 if (QWindow *window = windowHandle()) { 1042 window->setWindowStates(window->windowStates() & ~Qt::WindowMaximized); 1043 } 1044 1045 resize(sizeHint()); 1046 } 1047 } 1048 1049 // Call parent method 1050 KXmlGuiWindow::showEvent(event); 1051 } 1052 1053 void MainWindow::triggerAction(const QString &name) const 1054 { 1055 if (auto action = actionCollection()->action(name)) { 1056 if (action->isEnabled()) { 1057 action->trigger(); 1058 } 1059 } 1060 } 1061 1062 bool MainWindow::eventFilter(QObject *obj, QEvent *event) 1063 { 1064 if (!_pluggedController.isNull() && obj == _pluggedController->view()) { 1065 switch (event->type()) { 1066 case QEvent::MouseButtonPress: 1067 case QEvent::MouseButtonDblClick: 1068 switch (static_cast<QMouseEvent *>(event)->button()) { 1069 case Qt::ForwardButton: 1070 triggerAction(QStringLiteral("next-view")); 1071 break; 1072 case Qt::BackButton: 1073 triggerAction(QStringLiteral("previous-view")); 1074 break; 1075 default:; 1076 } 1077 default:; 1078 } 1079 } 1080 1081 return KXmlGuiWindow::eventFilter(obj, event); 1082 } 1083 1084 bool MainWindow::focusNextPrevChild(bool v) 1085 { 1086 if (qobject_cast<TerminalDisplay *>(focusWidget())) { 1087 return false; 1088 } 1089 1090 return QMainWindow::focusNextPrevChild(v); 1091 } 1092 1093 void MainWindow::saveNewToolbarConfig() 1094 { 1095 KXmlGuiWindow::saveNewToolbarConfig(); 1096 1097 unplugActionList(QStringLiteral("plugin-submenu")); 1098 plugActionList(QStringLiteral("plugin-submenu"), _pluginsActions); 1099 } 1100 1101 #include "moc_MainWindow.cpp"