File indexing completed on 2024-04-28 05:49:30
0001 /* This file is part of the KDE project 0002 SPDX-FileCopyrightText: 2001 Christoph Cullmann <cullmann@kde.org> 0003 SPDX-FileCopyrightText: 2001 Joseph Wenninger <jowenn@kde.org> 0004 SPDX-FileCopyrightText: 2001 Anders Lund <anders.lund@lund.tdcadsl.dk> 0005 SPDX-FileCopyrightText: 2007 Flavio Castelli <flavio.castelli@gmail.com> 0006 0007 SPDX-License-Identifier: LGPL-2.0-only 0008 */ 0009 0010 // BEGIN Includes 0011 #include "katemainwindow.h" 0012 #include <KColorSchemeMenu> 0013 0014 #include "diagnostics/diagnosticview.h" 0015 #include "filehistorywidget.h" 0016 #include "kateapp.h" 0017 #include "kateconfigdialog.h" 0018 #include "katedocmanager.h" 0019 #include "katefileactions.h" 0020 #include "katemwmodonhddialog.h" 0021 #include "kateoutputview.h" 0022 #include "katepluginmanager.h" 0023 #include "katequickopen.h" 0024 #include "katesavemodifieddialog.h" 0025 #include "katesessionmanager.h" 0026 #include "katesessionsaction.h" 0027 #include "katestashmanager.h" 0028 #include "kateupdatedisabler.h" 0029 #include "kateviewspace.h" 0030 #include "ktexteditor_utils.h" 0031 #include "texthint/KateTextHintManager.h" 0032 0033 #include <KActionCollection> 0034 #include <KActionMenu> 0035 #include <KColorSchemeManager> 0036 #include <KConfigGroup> 0037 #include <KEditToolBar> 0038 #include <KFileItem> 0039 #include <KHelpClient> 0040 #include <KIO/ListJob> 0041 #include <KLocalizedString> 0042 #include <KMessageBox> 0043 #include <KMultiTabBar> 0044 #include <KOpenWithDialog> 0045 #include <KRecentDocument> 0046 #include <KRecentFilesAction> 0047 #include <KSharedConfig> 0048 #include <KShortcutsDialog> 0049 #include <KStandardAction> 0050 #include <KToggleFullScreenAction> 0051 #include <KToolBar> 0052 #include <KWindowConfig> 0053 #include <KXMLGUIFactory> 0054 #include <kconfigwidgets_version.h> 0055 #include <kwidgetsaddons_version.h> 0056 0057 #include <QApplication> 0058 #include <QDir> 0059 #include <QFontDatabase> 0060 #include <QKeySequence> 0061 #include <QList> 0062 #include <QMenu> 0063 #include <QMenuBar> 0064 #include <QMimeData> 0065 #include <QMimeDatabase> 0066 #include <QScreen> 0067 #include <QStackedWidget> 0068 #include <QTimer> 0069 #include <QToolButton> 0070 0071 #include <ktexteditor/sessionconfiginterface.h> 0072 0073 // END 0074 0075 // shall windows close the documents only visible inside them if the are closed? 0076 static bool winClosesDocuments() 0077 { 0078 const auto config = KSharedConfig::openConfig(); 0079 const KConfigGroup cgGeneral(config, QStringLiteral("General")); 0080 return cgGeneral.readEntry("Close documents with window", true); 0081 } 0082 0083 KateMwModOnHdDialog *KateMainWindow::s_modOnHdDialog = nullptr; 0084 0085 KateContainerStackedLayout::KateContainerStackedLayout(QWidget *parent) 0086 : QStackedLayout(parent) 0087 { 0088 } 0089 0090 QSize KateContainerStackedLayout::sizeHint() const 0091 { 0092 if (currentWidget()) { 0093 return currentWidget()->sizeHint(); 0094 } 0095 return QStackedLayout::sizeHint(); 0096 } 0097 0098 QSize KateContainerStackedLayout::minimumSize() const 0099 { 0100 if (currentWidget()) { 0101 return currentWidget()->minimumSize(); 0102 } 0103 return QStackedLayout::minimumSize(); 0104 } 0105 0106 KateMainWindow::KateMainWindow(KConfig *sconfig, const QString &sgroup, bool userTriggered) 0107 : KateMDI::MainWindow(nullptr) 0108 , m_wrapper(new KTextEditor::MainWindow(this)) 0109 { 0110 /** 0111 * we don't want any flicker here 0112 */ 0113 KateUpdateDisabler disableUpdates(this); 0114 0115 // start session restore if needed 0116 startRestore(sconfig, sgroup); 0117 0118 // setup most important actions first, needed by setupMainWindow 0119 setupImportantActions(); 0120 0121 // setup the most important widgets 0122 setupMainWindow(); 0123 0124 // setup the actions 0125 setupActions(); 0126 0127 setStandardToolBarMenuEnabled(true); 0128 setXMLFile(QStringLiteral("kateui.rc")); 0129 createShellGUI(true); 0130 0131 // Has to be after the setXMLFile() call above, so that m_diagView's actions are 0132 // merged after the Tools menu has been populated with the default actions 0133 setupDiagnosticsView(sconfig); 0134 0135 // qCDebug(LOG_KATE) << "****************************************************************************" << sconfig; 0136 0137 // register mainwindow in app 0138 KateApp::self()->addMainWindow(this); 0139 0140 // enable plugin guis 0141 KateApp::self()->pluginManager()->enableAllPluginsGUI(this, sconfig); 0142 0143 // caption update 0144 const auto documents = KateApp::self()->documentManager()->documentList(); 0145 for (auto doc : documents) { 0146 slotDocumentCreated(doc); 0147 } 0148 0149 connect(KateApp::self()->documentManager(), &KateDocManager::documentCreated, this, &KateMainWindow::slotDocumentCreated); 0150 connect(KateApp::self(), &KateApp::configurationChanged, this, &KateMainWindow::readOptions); 0151 0152 readOptions(); 0153 0154 if (sconfig && !userTriggered) { 0155 m_viewManager->restoreViewConfiguration(KConfigGroup(sconfig, sgroup)); 0156 } 0157 0158 // unstash 0159 // KateStashManager().popStash(m_viewManager); 0160 0161 finishRestore(); 0162 0163 m_fileOpenRecent->loadEntries(KConfigGroup(sconfig, QStringLiteral("Recent Files"))); 0164 0165 setAcceptDrops(true); 0166 0167 connect(KateApp::self()->sessionManager(), SIGNAL(sessionChanged()), this, SLOT(updateCaption())); 0168 0169 connect(this, &KateMDI::MainWindow::sigShowPluginConfigPage, this, &KateMainWindow::showPluginConfigPage); 0170 0171 connect(qApp, &QApplication::applicationStateChanged, this, &KateMainWindow::onApplicationStateChanged); 0172 0173 // prior to this there was (possibly) no view, therefore not context menu. 0174 // Hence, we have to take care of the menu bar here 0175 toggleShowMenuBar(false); 0176 0177 ensureHamburgerBarSize(); 0178 0179 // trigger proper focus restore 0180 m_viewManager->triggerActiveViewFocus(); 0181 } 0182 0183 KateMainWindow::~KateMainWindow() 0184 { 0185 // first, save our fallback window size ;) 0186 KConfigGroup cfg(KSharedConfig::openConfig(), QStringLiteral("MainWindow")); 0187 KWindowConfig::saveWindowSize(windowHandle(), cfg); 0188 0189 // save other options ;=) 0190 saveOptions(); 0191 0192 // close all documents not visible in other windows, we did ask for permission in queryClose 0193 if (winClosesDocuments()) { 0194 auto docs = KateApp::self()->documentManager()->documentList(); 0195 const bool canStash = KateApp::self()->stashManager()->canStash(); 0196 docs.erase(std::remove_if(docs.begin(), 0197 docs.end(), 0198 [this, canStash](auto doc) { 0199 if (canStash && (doc->isModified() || doc->url().isEmpty())) { 0200 return true; 0201 } 0202 return KateApp::self()->documentVisibleInOtherWindows(doc, this); 0203 }), 0204 docs.end()); 0205 KateApp::self()->documentManager()->closeDocuments(docs, false); 0206 } 0207 0208 // Delete diagnostics view earlier so that destruction is faster 0209 // If we delay it then each provider will get unregisted one by one 0210 // and slow down the destruction as we will be clearing diagnostics 0211 // for each provider individually 0212 delete m_diagView; 0213 0214 // unregister mainwindow in app 0215 KateApp::self()->removeMainWindow(this); 0216 0217 // disable all plugin guis, delete all pluginViews 0218 KateApp::self()->pluginManager()->disableAllPluginsGUI(this); 0219 0220 // delete the view manager, before KateMainWindow's wrapper is dead 0221 delete m_viewManager; 0222 m_viewManager = nullptr; 0223 0224 // kill the wrapper object, now that all views are dead 0225 delete m_wrapper; 0226 m_wrapper = nullptr; 0227 } 0228 0229 QSize KateMainWindow::sizeHint() const 0230 { 0231 // ensure some proper sizing per default 0232 return (QSize(800, 600).expandedTo(minimumSizeHint())).expandedTo(screen()->availableSize() * 0.6).boundedTo(screen()->availableSize()); 0233 } 0234 0235 void KateMainWindow::setupImportantActions() 0236 { 0237 m_paShowStatusBar = KStandardAction::showStatusbar(this, SLOT(toggleShowStatusBar()), actionCollection()); 0238 m_paShowStatusBar->setWhatsThis(i18n("Use this command to show or hide the view's statusbar")); 0239 m_paShowMenuBar = KStandardAction::showMenubar(this, SLOT(toggleShowMenuBar()), actionCollection()); 0240 0241 m_paShowTabBar = new KToggleAction(i18n("Show &Tabs"), this); 0242 actionCollection()->addAction(QStringLiteral("settings_show_tab_bar"), m_paShowTabBar); 0243 connect(m_paShowTabBar, &QAction::toggled, this, &KateMainWindow::toggleShowTabBar); 0244 m_paShowTabBar->setWhatsThis(i18n("Use this command to show or hide the tabs for the views")); 0245 0246 m_paShowPath = new KToggleAction(i18n("Sho&w Path in Titlebar"), this); 0247 actionCollection()->addAction(QStringLiteral("settings_show_full_path"), m_paShowPath); 0248 connect(m_paShowPath, SIGNAL(toggled(bool)), this, SLOT(updateCaption())); 0249 m_paShowPath->setWhatsThis(i18n("Show the complete document path in the window caption")); 0250 0251 m_paShowUrlNavBar = new KToggleAction(i18n("Show Navigation Bar"), this); 0252 actionCollection()->addAction(QStringLiteral("settings_show_url_nav_bar"), m_paShowUrlNavBar); 0253 connect(m_paShowUrlNavBar, &QAction::toggled, this, [this](bool v) { 0254 m_viewManager->setShowUrlNavBar(v); 0255 }); 0256 0257 // Load themes 0258 KColorSchemeManager *manager = new KColorSchemeManager(this); 0259 auto *colorSelectionMenu = KColorSchemeMenu::createMenu(manager, this); 0260 colorSelectionMenu->menu()->setTitle(i18n("&Window Color Scheme")); 0261 actionCollection()->addAction(QStringLiteral("colorscheme_menu"), colorSelectionMenu); 0262 0263 QAction *a = actionCollection()->addAction(KStandardAction::Back, QStringLiteral("view_prev_tab")); 0264 a->setText(i18n("&Previous Tab")); 0265 a->setWhatsThis(i18n("Focus the previous tab.")); 0266 actionCollection()->setDefaultShortcuts(a, a->shortcuts() << KStandardShortcut::tabPrev()); 0267 connect(a, &QAction::triggered, this, &KateMainWindow::slotFocusPrevTab); 0268 0269 a = actionCollection()->addAction(KStandardAction::Forward, QStringLiteral("view_next_tab")); 0270 a->setText(i18n("&Next Tab")); 0271 a->setWhatsThis(i18n("Focus the next tab.")); 0272 actionCollection()->setDefaultShortcuts(a, a->shortcuts() << KStandardShortcut::tabNext()); 0273 connect(a, &QAction::triggered, this, &KateMainWindow::slotFocusNextTab); 0274 0275 // the quick open action is used by the KateViewSpace "quick open button" 0276 a = actionCollection()->addAction(QStringLiteral("view_quick_open")); 0277 a->setIcon(QIcon::fromTheme(QStringLiteral("quickopen"))); 0278 a->setText(i18n("&Quick Open")); 0279 actionCollection()->setDefaultShortcut(a, QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_O)); 0280 connect(a, &QAction::triggered, this, &KateMainWindow::slotQuickOpen); 0281 a->setWhatsThis(i18n("Open a form to quick open documents.")); 0282 0283 // enable hamburger menu 0284 auto hamburgerMenu = static_cast<KHamburgerMenu *>(actionCollection()->addAction(KStandardAction::HamburgerMenu, QStringLiteral("hamburger_menu"))); 0285 hamburgerMenu->setMenuBar(menuBar()); 0286 hamburgerMenu->setShowMenuBarAction(m_paShowMenuBar); 0287 } 0288 0289 void KateMainWindow::setupMainWindow() 0290 { 0291 m_viewManager = new KateViewManager(centralWidget(), this); 0292 centralWidget()->layout()->addWidget(m_viewManager); 0293 (static_cast<QBoxLayout *>(centralWidget()->layout()))->setStretchFactor(m_viewManager, 100); 0294 0295 m_bottomViewBarContainer = new QWidget(centralWidget()); 0296 centralWidget()->layout()->addWidget(m_bottomViewBarContainer); 0297 m_bottomContainerStack = new KateContainerStackedLayout(m_bottomViewBarContainer); 0298 0299 if (KateApp::isKWrite()) { 0300 // Kwrite has nothing other than the view manager 0301 return; 0302 } 0303 0304 /** 0305 * create generic output tool view 0306 * is used to display output of e.g. plugins 0307 */ 0308 m_toolViewOutput = createToolView(nullptr /* toolview has no plugin it belongs to */, 0309 QStringLiteral("output"), 0310 KTextEditor::MainWindow::Bottom, 0311 QIcon::fromTheme(QStringLiteral("output_win")), 0312 i18n("Output")); 0313 m_outputView = new KateOutputView(this, m_toolViewOutput); 0314 } 0315 0316 void KateMainWindow::setupActions() 0317 { 0318 QAction *a; 0319 0320 actionCollection() 0321 ->addAction(KStandardAction::New, QStringLiteral("file_new"), m_viewManager, SLOT(slotDocumentNew())) 0322 ->setWhatsThis(i18n("Create a new document")); 0323 actionCollection() 0324 ->addAction(KStandardAction::Open, QStringLiteral("file_open"), m_viewManager, SLOT(slotDocumentOpen())) 0325 ->setWhatsThis(i18n("Open an existing document for editing")); 0326 0327 m_fileOpenRecent = KStandardAction::openRecent( 0328 m_viewManager, 0329 [this](const QUrl &url) { 0330 viewManager()->openUrlOrProject(url); 0331 }, 0332 this); 0333 m_fileOpenRecent->setMaxItems(KateConfigDialog::recentFilesMaxCount()); 0334 actionCollection()->addAction(m_fileOpenRecent->objectName(), m_fileOpenRecent); 0335 m_fileOpenRecent->setWhatsThis(i18n("This lists files which you have opened recently, and allows you to easily open them again.")); 0336 0337 a = actionCollection()->addAction(QStringLiteral("file_save_all")); 0338 a->setIcon(QIcon::fromTheme(QStringLiteral("document-save-all"))); 0339 a->setText(i18n("Save A&ll")); 0340 actionCollection()->setDefaultShortcut(a, QKeySequence(Qt::CTRL | Qt::Key_L)); 0341 connect(a, &QAction::triggered, KateApp::self()->documentManager(), &KateDocManager::saveAll); 0342 a->setWhatsThis(i18n("Save all open, modified documents to disk.")); 0343 0344 a = actionCollection()->addAction(QStringLiteral("file_reload_all")); 0345 a->setText(i18n("&Reload All")); 0346 connect(a, &QAction::triggered, KateApp::self()->documentManager(), &KateDocManager::reloadAll); 0347 a->setWhatsThis(i18n("Reload all open documents.")); 0348 0349 a = actionCollection()->addAction(QStringLiteral("file_copy_filepath")); 0350 a->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy-path"))); 0351 a->setText(i18n("Copy Location")); 0352 connect(a, &QAction::triggered, KateApp::self()->documentManager(), [this]() { 0353 auto &&view = viewManager()->activeView(); 0354 KateFileActions::copyFilePathToClipboard(view->document()); 0355 }); 0356 a->setWhatsThis(i18n("Copies the file path of the current file to clipboard.")); 0357 0358 a = actionCollection()->addAction(QStringLiteral("file_open_containing_folder")); 0359 a->setIcon(QIcon::fromTheme(QStringLiteral("document-open-folder"))); 0360 a->setText(i18n("&Open Containing Folder")); 0361 connect(a, &QAction::triggered, KateApp::self()->documentManager(), [this]() { 0362 auto &&view = viewManager()->activeView(); 0363 KateFileActions::openContainingFolder(view->document()); 0364 }); 0365 a->setWhatsThis(i18n("Copies the file path of the current file to clipboard.")); 0366 0367 a = actionCollection()->addAction(QStringLiteral("file_rename")); 0368 a->setIcon(QIcon::fromTheme(QStringLiteral("edit-rename"))); 0369 a->setText(i18nc("@action:inmenu", "Rename...")); 0370 connect(a, &QAction::triggered, KateApp::self()->documentManager(), [this]() { 0371 auto &&view = viewManager()->activeView(); 0372 KateFileActions::renameDocumentFile(this, view->document()); 0373 }); 0374 a->setWhatsThis(i18n("Renames the file belonging to the current document.")); 0375 0376 a = actionCollection()->addAction(QStringLiteral("file_delete")); 0377 a->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete"))); 0378 a->setText(i18nc("@action:inmenu", "Delete")); 0379 connect(a, &QAction::triggered, KateApp::self()->documentManager(), [this]() { 0380 auto &&view = viewManager()->activeView(); 0381 KateFileActions::deleteDocumentFile(this, view->document()); 0382 }); 0383 a->setWhatsThis(i18n("Deletes the file belonging to the current document.")); 0384 0385 a = actionCollection()->addAction(QStringLiteral("file_properties")); 0386 a->setIcon(QIcon::fromTheme(QStringLiteral("dialog-object-properties"))); 0387 a->setText(i18n("Properties")); 0388 connect(a, &QAction::triggered, KateApp::self()->documentManager(), [this]() { 0389 auto &&view = viewManager()->activeView(); 0390 KateFileActions::openFilePropertiesDialog(this, view->document()); 0391 }); 0392 a->setWhatsThis(i18n("Deletes the file belonging to the current document.")); 0393 0394 a = actionCollection()->addAction(QStringLiteral("file_compare")); 0395 a->setText(i18n("Compare")); 0396 connect(a, &QAction::triggered, KateApp::self()->documentManager(), [this]() { 0397 QMessageBox::information(this, i18n("Compare"), i18n("Use the Tabbar context menu to compare two documents")); 0398 }); 0399 a->setWhatsThis(i18n("Shows a hint how to compare documents.")); 0400 0401 a = actionCollection()->addAction(QStringLiteral("file_close_orphaned")); 0402 a->setText(i18n("Close Orphaned")); 0403 connect(a, &QAction::triggered, KateApp::self()->documentManager(), &KateDocManager::closeOrphaned); 0404 a->setWhatsThis(i18n("Close all documents in the file list that could not be reopened, because they are not accessible anymore.")); 0405 0406 a = actionCollection()->addAction(KStandardAction::Close, QStringLiteral("file_close"), m_viewManager, SLOT(slotDocumentClose())); 0407 a->setIcon(QIcon::fromTheme(QStringLiteral("document-close"))); 0408 a->setWhatsThis(i18n("Close the current document.")); 0409 0410 a = actionCollection()->addAction(QStringLiteral("file_close_other")); 0411 a->setText(i18n("Close Other")); 0412 connect(a, SIGNAL(triggered()), this, SLOT(slotDocumentCloseOther())); 0413 a->setWhatsThis(i18n("Close other open documents.")); 0414 0415 a = actionCollection()->addAction(QStringLiteral("file_close_all")); 0416 a->setText(i18n("Clos&e All")); 0417 connect(a, &QAction::triggered, this, &KateMainWindow::slotDocumentCloseAll); 0418 a->setWhatsThis(i18n("Close all open documents.")); 0419 0420 a = actionCollection()->addAction(KStandardAction::Quit, QStringLiteral("file_quit")); 0421 // Qt::QueuedConnection: delay real shutdown, as we are inside menu action handling (bug #185708) 0422 connect(a, &QAction::triggered, this, &KateMainWindow::slotFileQuit, Qt::QueuedConnection); 0423 a->setWhatsThis(i18n("Close this window")); 0424 0425 a = actionCollection()->addAction(QStringLiteral("view_new_view")); 0426 a->setIcon(QIcon::fromTheme(QStringLiteral("window-new"))); 0427 a->setText(i18n("&New Window")); 0428 actionCollection()->setDefaultShortcut(a, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_N)); 0429 connect(a, &QAction::triggered, this, &KateMainWindow::newWindow); 0430 a->setWhatsThis(i18n("Create a new window.")); 0431 0432 m_showFullScreenAction = KStandardAction::fullScreen(nullptr, nullptr, this, this); 0433 actionCollection()->addAction(m_showFullScreenAction->objectName(), m_showFullScreenAction); 0434 actionCollection()->setDefaultShortcut(m_showFullScreenAction, Qt::Key_F11); 0435 connect(m_showFullScreenAction, &QAction::toggled, this, &KateMainWindow::slotFullScreen); 0436 0437 documentOpenWith = new KActionMenu(i18n("Open W&ith"), this); 0438 actionCollection()->addAction(QStringLiteral("file_open_with"), documentOpenWith); 0439 documentOpenWith->setWhatsThis(i18n("Open the current document using another application registered for its file type, or an application of your choice.")); 0440 connect(documentOpenWith->menu(), &QMenu::aboutToShow, this, &KateMainWindow::mSlotFixOpenWithMenu); 0441 connect(documentOpenWith->menu(), &QMenu::triggered, this, &KateMainWindow::slotOpenWithMenuAction); 0442 0443 // no open with for KWrite ATM 0444 documentOpenWith->setVisible(KateApp::isKate()); 0445 0446 a = KStandardAction::keyBindings(this, SLOT(editKeys()), actionCollection()); 0447 a->setWhatsThis(i18n("Configure the application's keyboard shortcut assignments.")); 0448 0449 a = KStandardAction::configureToolbars(this, SLOT(slotEditToolbars()), actionCollection()); 0450 a->setWhatsThis(i18n("Configure which items should appear in the toolbar(s).")); 0451 0452 QAction *settingsConfigure = KStandardAction::preferences(this, SLOT(slotConfigure()), actionCollection()); 0453 settingsConfigure->setWhatsThis(i18n("Configure various aspects of this application and the editing component.")); 0454 0455 if (KateApp::self()->pluginManager()->pluginList().size() > 0) { 0456 a = actionCollection()->addAction(QStringLiteral("help_plugins_contents")); 0457 a->setText(i18n("&Plugins Handbook")); 0458 connect(a, &QAction::triggered, this, &KateMainWindow::pluginHelp); 0459 a->setWhatsThis(i18n("This shows help files for various available plugins.")); 0460 } 0461 0462 connect(m_viewManager, &KateViewManager::viewChanged, this, &KateMainWindow::slotWindowActivated); 0463 connect(m_viewManager, &KateViewManager::viewChanged, this, &KateMainWindow::slotUpdateActionsNeedingUrl); 0464 connect(m_viewManager, &KateViewManager::viewChanged, this, &KateMainWindow::slotUpdateBottomViewBar); 0465 0466 // re-route signals to our wrapper 0467 connect(m_viewManager, &KateViewManager::viewChanged, m_wrapper, &KTextEditor::MainWindow::viewChanged); 0468 connect(m_viewManager, &KateViewManager::viewCreated, m_wrapper, &KTextEditor::MainWindow::viewCreated); 0469 connect(this, &KateMainWindow::unhandledShortcutOverride, m_wrapper, &KTextEditor::MainWindow::unhandledShortcutOverride); 0470 0471 slotWindowActivated(); 0472 0473 // session actions, not for KWrite, create the full menu to be able to properly hide it 0474 if (KateApp::isKate()) { 0475 auto sessionsMenu = actionCollection()->addAction(QStringLiteral("sessions")); 0476 sessionsMenu->setText(i18n("Sess&ions")); 0477 sessionsMenu->setMenu(new QMenu(this)); 0478 0479 a = actionCollection()->addAction(QStringLiteral("sessions_new")); 0480 sessionsMenu->menu()->addAction(a); 0481 a->setIcon(QIcon::fromTheme(QStringLiteral("document-new"))); 0482 a->setText(i18nc("Menu entry Session->New Session", "&New Session")); 0483 // Qt::QueuedConnection to avoid deletion of code that is executed when reducing the amount of mainwindows. (bug #227008) 0484 connect(a, &QAction::triggered, KateApp::self()->sessionManager(), &KateSessionManager::sessionNew, Qt::QueuedConnection); 0485 0486 // recent sessions menu 0487 a = new KateSessionsAction(i18n("&Recent Sessions"), this, KateApp::self()->sessionManager(), false); 0488 sessionsMenu->menu()->addAction(a); 0489 actionCollection()->addAction(QStringLiteral("session_open_recent"), a); 0490 0491 // session menu 0492 a = new KateSessionsAction(i18n("&All Sessions"), this, KateApp::self()->sessionManager(), true); 0493 sessionsMenu->menu()->addAction(a); 0494 actionCollection()->addAction(QStringLiteral("session_open_session"), a); 0495 0496 a = actionCollection()->addAction(QStringLiteral("sessions_manage")); 0497 sessionsMenu->menu()->addAction(a); 0498 a->setIcon(QIcon::fromTheme(QStringLiteral("view-choose"))); 0499 a->setText(i18n("&Manage Sessions...")); 0500 // Qt::QueuedConnection to avoid deletion of code that is executed when reducing the amount of mainwindows. (bug #227008) 0501 connect(a, &QAction::triggered, KateApp::self()->sessionManager(), &KateSessionManager::sessionManage, Qt::QueuedConnection); 0502 0503 sessionsMenu->menu()->addSeparator(); 0504 0505 a = actionCollection()->addAction(QStringLiteral("sessions_save")); 0506 sessionsMenu->menu()->addAction(a); 0507 a->setIcon(QIcon::fromTheme(QStringLiteral("document-save"))); 0508 a->setText(i18n("&Save Session")); 0509 connect(a, &QAction::triggered, KateApp::self()->sessionManager(), &KateSessionManager::sessionSave); 0510 0511 a = actionCollection()->addAction(QStringLiteral("sessions_save_as")); 0512 sessionsMenu->menu()->addAction(a); 0513 a->setIcon(QIcon::fromTheme(QStringLiteral("document-save-as"))); 0514 a->setText(i18n("Save Session &As...")); 0515 connect(a, &QAction::triggered, KateApp::self()->sessionManager(), &KateSessionManager::sessionSaveAs); 0516 } 0517 0518 // location history actions 0519 a = actionCollection()->addAction(QStringLiteral("view_history_back")); 0520 a->setIcon(QIcon::fromTheme(QStringLiteral("arrow-left"))); 0521 a->setText(i18n("Go to Previous Location")); 0522 actionCollection()->setDefaultShortcut(a, QKeySequence(Qt::CTRL | Qt::Key_1)); 0523 connect(a, &QAction::triggered, this, [this] { 0524 m_viewManager->activeViewSpace()->goBack(); 0525 }); 0526 connect(this->m_viewManager, &KateViewManager::historyBackEnabled, a, &QAction::setEnabled); 0527 0528 a = actionCollection()->addAction(QStringLiteral("view_history_forward")); 0529 a->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right"))); 0530 a->setText(i18n("Go to Next Location")); 0531 actionCollection()->setDefaultShortcut(a, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_1)); 0532 connect(a, &QAction::triggered, this, [this] { 0533 m_viewManager->activeViewSpace()->goForward(); 0534 }); 0535 connect(this->m_viewManager, &KateViewManager::historyForwardEnabled, a, &QAction::setEnabled); 0536 0537 a = actionCollection()->addAction(QStringLiteral("git_show_file_history")); 0538 a->setText(i18n("Show File Git History")); 0539 connect(a, &QAction::triggered, this, [this] { 0540 if (activeView()) { 0541 auto url = activeView()->document()->url(); 0542 if (url.isValid() && url.isLocalFile()) { 0543 FileHistory::showFileHistory(url.toLocalFile(), m_wrapper); 0544 } 0545 } 0546 }); 0547 } 0548 0549 void KateMainWindow::setupDiagnosticsView(KConfig *sconfig) 0550 { 0551 if (KateApp::isKWrite()) { 0552 return; 0553 } 0554 0555 m_diagView = DiagnosticsView::instance(wrapper()); 0556 m_diagView->readSessionConfig(KConfigGroup(sconfig, QStringLiteral("Kate Diagnostics"))); 0557 // See comment in DiagnosticsView::DiagnosticsView() 0558 m_diagView->actionCollection()->addAssociatedWidget(m_viewManager); 0559 } 0560 0561 void KateMainWindow::ensureHamburgerBarSize() 0562 { 0563 // Ensure the hamburger menu never gets pushed into the toolbar overflow 0564 // by setting a minimum size on the bar based on the button's size. 0565 if (auto *hamburgerBar = toolBar(QStringLiteral("hamburgerBar"))) { 0566 auto *hamburgerMenu = actionCollection()->action(QStringLiteral("hamburger_menu")); 0567 if (auto *hamburgerButton = hamburgerBar->widgetForAction(hamburgerMenu)) { 0568 int neededButtonWidth = hamburgerButton->minimumSizeHint().width(); 0569 // Add toolbar margins. 0570 const QMargins combinedMargins = hamburgerBar->contentsMargins() + hamburgerBar->layout()->contentsMargins(); 0571 neededButtonWidth += combinedMargins.left(); 0572 neededButtonWidth += combinedMargins.right(); 0573 0574 // The dynamic spacer is also an action leading to spacing being added. 0575 // Not observable with Breeze but e.g. Fusion style has toolbar button spacing. 0576 if (hamburgerBar->actions().count() > 1) { 0577 neededButtonWidth += hamburgerBar->layout()->spacing(); 0578 } 0579 0580 QSize minimumSize = hamburgerBar->minimumSize(); 0581 minimumSize.setWidth(std::max(minimumSize.width(), neededButtonWidth)); 0582 hamburgerBar->setMinimumSize(minimumSize); 0583 } 0584 } 0585 } 0586 0587 void KateMainWindow::slotDocumentCloseAll() 0588 { 0589 if (!KateApp::self()->documentManager()->documentList().empty() 0590 && KMessageBox::warningContinueCancel(this, 0591 i18n("This will close all open documents. Are you sure you want to continue?"), 0592 i18n("Close all documents"), 0593 KStandardGuiItem::cont(), 0594 KStandardGuiItem::cancel(), 0595 QStringLiteral("closeAll")) 0596 != KMessageBox::Cancel) { 0597 if (queryClose_internal()) { 0598 KateApp::self()->documentManager()->closeAllDocuments(false); 0599 } 0600 } 0601 } 0602 0603 void KateMainWindow::slotDocumentCloseOther(KTextEditor::Document *document) 0604 { 0605 if (KateApp::self()->documentManager()->documentList().size() > 1 0606 && KMessageBox::warningContinueCancel(this, 0607 i18n("This will close all open documents beside the current one. Are you sure you want to continue?"), 0608 i18n("Close all documents beside current one"), 0609 KStandardGuiItem::cont(), 0610 KStandardGuiItem::cancel(), 0611 QStringLiteral("closeOther")) 0612 != KMessageBox::Cancel) { 0613 if (queryClose_internal(document)) { 0614 KateApp::self()->documentManager()->closeOtherDocuments(document); 0615 } 0616 } 0617 } 0618 0619 void KateMainWindow::slotDocumentCloseSelected(const QList<KTextEditor::Document *> &docList) 0620 { 0621 QList<KTextEditor::Document *> documents; 0622 for (KTextEditor::Document *doc : docList) { 0623 if (queryClose_internal(doc)) { 0624 documents.push_back(doc); 0625 } 0626 } 0627 0628 KateApp::self()->documentManager()->closeDocuments(documents); 0629 } 0630 0631 void KateMainWindow::slotDocumentCloseOther() 0632 { 0633 slotDocumentCloseOther(m_viewManager->activeView()->document()); 0634 } 0635 0636 bool KateMainWindow::queryClose_internal(KTextEditor::Document *doc, KateMainWindow *win) 0637 { 0638 // we want no auto saving during windows closing, we handle that explicitly 0639 KateSessionManager::AutoSaveBlocker blocker(KateApp::self()->sessionManager()); 0640 0641 const auto documentCount = KateApp::self()->documentManager()->documentList().size(); 0642 0643 if (!showModOnDiskPrompt(PromptEdited)) { 0644 return false; 0645 } 0646 0647 std::vector<KTextEditor::Document *> modifiedDocuments = KateApp::self()->documentManager()->modifiedDocumentList(); 0648 0649 // filter out what the stashManager will itself stash 0650 const bool canStash = KateApp::self()->stashManager()->canStash(); 0651 if (canStash) { 0652 modifiedDocuments.erase(std::remove_if(modifiedDocuments.begin(), 0653 modifiedDocuments.end(), 0654 [](auto doc) { 0655 return KateApp::self()->stashManager()->willStashDoc(doc); 0656 }), 0657 modifiedDocuments.end()); 0658 } 0659 0660 // do we want to ignore some document? 0661 if (doc) { 0662 modifiedDocuments.erase(std::remove(modifiedDocuments.begin(), modifiedDocuments.end(), doc), modifiedDocuments.end()); 0663 } 0664 0665 // do we want to ignore all documents visible in other windows? 0666 if (win) { 0667 modifiedDocuments.erase(std::remove_if(modifiedDocuments.begin(), 0668 modifiedDocuments.end(), 0669 [win, canStash](auto doc) { 0670 if (canStash && (doc->isModified() || doc->url().isEmpty())) { 0671 return true; 0672 } 0673 return KateApp::self()->documentVisibleInOtherWindows(doc, win); 0674 }), 0675 modifiedDocuments.end()); 0676 } 0677 0678 // Remove all documents that can be closed without user confirmation 0679 modifiedDocuments.erase(std::remove_if(modifiedDocuments.begin(), 0680 modifiedDocuments.end(), 0681 [](KTextEditor::Document *d) { 0682 return !d->isModified() || (d->isEmpty() && d->url().isEmpty()); 0683 }), 0684 modifiedDocuments.end()); 0685 0686 bool shutdown = modifiedDocuments.empty(); 0687 if (!shutdown) { 0688 shutdown = KateSaveModifiedDialog::queryClose(this, modifiedDocuments); 0689 } 0690 0691 if (KateApp::self()->documentManager()->documentList().size() > documentCount) { 0692 KMessageBox::information(this, i18n("New file opened while trying to close Kate, closing aborted."), i18n("Closing Aborted")); 0693 shutdown = false; 0694 } 0695 0696 return shutdown; 0697 } 0698 0699 /** 0700 * queryClose(), take care that after the last mainwindow the stuff is closed 0701 */ 0702 bool KateMainWindow::queryClose() 0703 { 0704 // we want no auto saving during windows closing, we handle that explicitly 0705 KateSessionManager::AutoSaveBlocker blocker(KateApp::self()->sessionManager()); 0706 0707 // session saving, can we close all views ? 0708 // just test, not close them actually 0709 if (qApp->isSavingSession()) { 0710 return queryClose_internal(); 0711 } 0712 0713 // normal closing of window 0714 // if we are not the last window, just close the documents we own 0715 if (KateApp::self()->mainWindowsCount() > 1) { 0716 return winClosesDocuments() ? queryClose_internal(nullptr, this) : true; 0717 } 0718 0719 // last one: check if we can close all documents, try run 0720 // and save docs if we really close down ! 0721 if (queryClose_internal()) { 0722 KateApp::self()->sessionManager()->saveActiveSession(true); 0723 KateApp::self()->stashManager()->stashDocuments(KateApp::self()->sessionManager()->activeSession()->config(), 0724 KateApp::self()->documentManager()->documentList()); 0725 return true; 0726 } 0727 0728 return false; 0729 } 0730 0731 KateMainWindow *KateMainWindow::newWindow() const 0732 { 0733 // create new window with current session 0734 // derive size from current one 0735 auto win = KateApp::self()->newMainWindow(KateApp::self()->sessionManager()->activeSession()->config(), {}, true); 0736 win->resize(size()); 0737 return win; 0738 } 0739 0740 void KateMainWindow::slotEditToolbars() 0741 { 0742 KConfigGroup cfg(KSharedConfig::openConfig(), QStringLiteral("MainWindow")); 0743 saveMainWindowSettings(cfg); 0744 0745 KEditToolBar dlg(factory(), this); 0746 0747 connect(&dlg, &KEditToolBar::newToolBarConfig, this, &KateMainWindow::slotNewToolbarConfig); 0748 dlg.exec(); 0749 } 0750 0751 void KateMainWindow::reloadXmlGui() 0752 { 0753 for (KTextEditor::Document *doc : KateApp::self()->documentManager()->documentList()) { 0754 doc->reloadXML(); 0755 const auto views = doc->views(); 0756 for (KTextEditor::View *view : views) { 0757 view->reloadXML(); 0758 } 0759 } 0760 } 0761 0762 void KateMainWindow::slotNewToolbarConfig() 0763 { 0764 applyMainWindowSettings(KConfigGroup(KSharedConfig::openConfig(), QStringLiteral("MainWindow"))); 0765 0766 // we need to reload all View's XML Gui from disk to ensure toolbar 0767 // changes are applied to all views. 0768 reloadXmlGui(); 0769 } 0770 0771 void KateMainWindow::slotFileQuit() 0772 { 0773 KateApp::self()->shutdownKate(this); 0774 } 0775 0776 void KateMainWindow::slotFileClose() 0777 { 0778 m_viewManager->slotDocumentClose(); 0779 } 0780 0781 void KateMainWindow::slotOpenDocument(const QUrl &url) 0782 { 0783 m_viewManager->openUrl(url, QString(), true); 0784 } 0785 0786 void KateMainWindow::readOptions() 0787 { 0788 KSharedConfig::Ptr config = KSharedConfig::openConfig(); 0789 0790 const KConfigGroup generalGroup(config, QStringLiteral("General")); 0791 m_modNotification = generalGroup.readEntry("Modified Notification", false); 0792 m_modCloseAfterLast = generalGroup.readEntry("Close After Last", KateApp::isKWrite()); 0793 KateApp::self()->documentManager()->setSaveMetaInfos(generalGroup.readEntry("Save Meta Infos", true)); 0794 KateApp::self()->documentManager()->setDaysMetaInfos(generalGroup.readEntry("Days Meta Infos", 30)); 0795 0796 KateApp::self()->stashManager()->setStashUnsavedChanges(generalGroup.readEntry("Stash unsaved file changes", false)); 0797 KateApp::self()->stashManager()->setStashNewUnsavedFiles(generalGroup.readEntry("Stash new unsaved files", true)); 0798 0799 m_paShowPath->setChecked(generalGroup.readEntry("Show Full Path in Title", false)); 0800 m_paShowStatusBar->setChecked(generalGroup.readEntry("Show Status Bar", true)); 0801 m_paShowMenuBar->setChecked(generalGroup.readEntry("Show Menu Bar", true)); 0802 m_paShowTabBar->setChecked(generalGroup.readEntry("Show Tab Bar", true)); 0803 m_paShowUrlNavBar->setChecked(generalGroup.readEntry("Show Url Nav Bar", KateApp::isKate())); 0804 0805 for (auto a : {m_paShowMenuBar, m_paShowTabBar, m_paShowPath, m_paShowUrlNavBar, m_paShowStatusBar}) { 0806 connect(a, &QAction::toggled, this, &KateMainWindow::saveOptions); 0807 } 0808 0809 m_mouseButtonBackAction = (MouseBackButtonAction)generalGroup.readEntry("Mouse back button action", 0); 0810 m_mouseButtonForwardAction = (MouseForwardButtonAction)generalGroup.readEntry("Mouse forward button action", 0); 0811 0812 // emit signal to hide/show statusbars 0813 toggleShowStatusBar(); 0814 toggleShowTabBar(); 0815 m_viewManager->setShowUrlNavBar(m_paShowUrlNavBar->isChecked()); 0816 } 0817 0818 void KateMainWindow::saveOptions() 0819 { 0820 KSharedConfig::Ptr config = KSharedConfig::openConfig(); 0821 0822 KConfigGroup generalGroup(config, QStringLiteral("General")); 0823 0824 generalGroup.writeEntry("Save Meta Infos", KateApp::self()->documentManager()->getSaveMetaInfos()); 0825 0826 generalGroup.writeEntry("Days Meta Infos", KateApp::self()->documentManager()->getDaysMetaInfos()); 0827 0828 generalGroup.writeEntry("Show Full Path in Title", m_paShowPath->isChecked()); 0829 generalGroup.writeEntry("Show Status Bar", m_paShowStatusBar->isChecked()); 0830 generalGroup.writeEntry("Show Menu Bar", m_paShowMenuBar->isChecked()); 0831 generalGroup.writeEntry("Show Tab Bar", m_paShowTabBar->isChecked()); 0832 generalGroup.writeEntry("Show Url Nav Bar", m_paShowUrlNavBar->isChecked()); 0833 } 0834 0835 void KateMainWindow::toggleShowMenuBar(bool showMessage) 0836 { 0837 if (m_paShowMenuBar->isChecked()) { 0838 menuBar()->show(); 0839 if (m_viewManager->activeView() && m_viewManager->activeView()->contextMenu()) { 0840 m_viewManager->activeView()->contextMenu()->removeAction(m_paShowMenuBar); 0841 } 0842 } else { 0843 // we have a hamburger button in the toolbar, we can avoid the message if that is still visible 0844 if (showMessage && toolBar()->isHidden()) { 0845 const QString accel = m_paShowMenuBar->shortcut().toString(); 0846 KMessageBox::information(this, 0847 i18n("This will hide the menu bar completely." 0848 " You can show it again by typing %1.", 0849 accel), 0850 i18n("Hide menu bar"), 0851 QStringLiteral("HideMenuBarWarning")); 0852 } 0853 menuBar()->hide(); 0854 if (m_viewManager->activeView() && m_viewManager->activeView()->contextMenu()) { 0855 m_viewManager->activeView()->contextMenu()->addAction(m_paShowMenuBar); 0856 } 0857 } 0858 } 0859 0860 void KateMainWindow::toggleShowStatusBar() 0861 { 0862 // just hide or show the status bar stack 0863 statusBarStackedWidget()->setVisible(showStatusBar()); 0864 } 0865 0866 bool KateMainWindow::showStatusBar() 0867 { 0868 return m_paShowStatusBar->isChecked(); 0869 } 0870 0871 void KateMainWindow::toggleShowTabBar() 0872 { 0873 Q_EMIT tabBarToggled(); 0874 } 0875 0876 bool KateMainWindow::showTabBar() 0877 { 0878 return m_paShowTabBar->isChecked(); 0879 } 0880 0881 void KateMainWindow::slotWindowActivated() 0882 { 0883 if (m_viewManager->activeView()) { 0884 updateCaption(m_viewManager->activeView()->document()); 0885 } 0886 0887 // update proxy 0888 centralWidget()->setFocusProxy(m_viewManager->activeView()); 0889 } 0890 0891 void KateMainWindow::slotUpdateActionsNeedingUrl() 0892 { 0893 auto &&view = viewManager()->activeView(); 0894 const bool hasUrl = view && !view->document()->url().isEmpty(); 0895 0896 action(QStringLiteral("file_copy_filepath"))->setEnabled(hasUrl); 0897 action(QStringLiteral("file_open_containing_folder"))->setEnabled(hasUrl); 0898 action(QStringLiteral("file_rename"))->setEnabled(hasUrl); 0899 action(QStringLiteral("file_delete"))->setEnabled(hasUrl); 0900 action(QStringLiteral("file_properties"))->setEnabled(hasUrl); 0901 documentOpenWith->setEnabled(hasUrl); 0902 } 0903 0904 void KateMainWindow::dragEnterEvent(QDragEnterEvent *event) 0905 { 0906 if (!event->mimeData()) { 0907 return; 0908 } 0909 const bool accept = event->mimeData()->hasUrls() || event->mimeData()->hasText(); 0910 event->setAccepted(accept); 0911 } 0912 0913 void KateMainWindow::dropEvent(QDropEvent *event) 0914 { 0915 slotDropEvent(event); 0916 } 0917 0918 void KateMainWindow::slotDropEvent(QDropEvent *event) 0919 { 0920 if (event->mimeData() == nullptr) { 0921 return; 0922 } 0923 0924 // 0925 // are we dropping files? 0926 // 0927 0928 if (event->mimeData()->hasUrls()) { 0929 QList<QUrl> textlist = event->mimeData()->urls(); 0930 0931 // Try to get the KTextEditor::View that sent this, and activate it, so that the file opens in the 0932 // view where it was dropped 0933 KTextEditor::View *kVsender = qobject_cast<KTextEditor::View *>(QObject::sender()); 0934 if (kVsender != nullptr) { 0935 if (auto parent = kVsender->parent()) { 0936 KateViewSpace *vs = qobject_cast<KateViewSpace *>(parent->parent()); 0937 if (vs != nullptr) { 0938 m_viewManager->setActiveSpace(vs); 0939 } 0940 } 0941 } 0942 0943 for (const QUrl &url : qAsConst(textlist)) { 0944 // if url has no file component, try and recursively scan dir 0945 KFileItem kitem(url); 0946 kitem.setDelayedMimeTypes(true); 0947 if (kitem.isDir()) { 0948 if (KMessageBox::questionTwoActions(this, 0949 i18n("You dropped the directory %1 into Kate. " 0950 "Do you want to open all files contained in it?", 0951 url.url()), 0952 i18nc("@title:window", "Open Files Recursively"), 0953 KGuiItem(i18nc("@action:button", "Open All Files"), QStringLiteral("document-open")), 0954 KStandardGuiItem::cancel()) 0955 == KMessageBox::PrimaryAction) { 0956 KIO::ListJob *list_job = KIO::listRecursive(url, KIO::DefaultFlags, KIO::ListJob::ListFlags{}); 0957 connect(list_job, &KIO::ListJob::entries, this, &KateMainWindow::slotListRecursiveEntries); 0958 } 0959 } else { 0960 m_viewManager->openUrl(url); 0961 } 0962 } 0963 } 0964 // 0965 // or are we dropping text? 0966 // 0967 else if (event->mimeData()->hasText()) { 0968 KTextEditor::Document *doc = KateApp::self()->documentManager()->createDoc(); 0969 doc->setText(event->mimeData()->text()); 0970 m_viewManager->activateView(doc); 0971 } 0972 } 0973 0974 void KateMainWindow::slotListRecursiveEntries(KIO::Job *job, const KIO::UDSEntryList &list) 0975 { 0976 const QUrl dir = static_cast<KIO::SimpleJob *>(job)->url(); 0977 for (const KIO::UDSEntry &entry : list) { 0978 if (!entry.isDir()) { 0979 QUrl url(dir); 0980 url = url.adjusted(QUrl::StripTrailingSlash); 0981 url.setPath(url.path() + QLatin1Char('/') + entry.stringValue(KIO::UDSEntry::UDS_NAME)); 0982 m_viewManager->openUrl(url); 0983 } 0984 } 0985 } 0986 0987 void KateMainWindow::editKeys() 0988 { 0989 KShortcutsDialog dlg(KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsAllowed, this); 0990 0991 const QList<KXMLGUIClient *> clients = guiFactory()->clients(); 0992 0993 for (KXMLGUIClient *client : clients) { 0994 // FIXME there appear to be invalid clients after session switching 0995 // qCDebug(LOG_KATE)<<"adding client to shortcut editor"; 0996 // qCDebug(LOG_KATE)<<client; 0997 // qCDebug(LOG_KATE)<<client->actionCollection(); 0998 // qCDebug(LOG_KATE)<<client->componentData().aboutData(); 0999 // qCDebug(LOG_KATE)<<client->componentData().aboutData()->programName(); 1000 dlg.addCollection(client->actionCollection(), client->componentName()); 1001 } 1002 dlg.configure(); 1003 1004 // reloadXML gui clients, to ensure all clients are up-to-date 1005 reloadXmlGui(); 1006 } 1007 1008 void KateMainWindow::openUrl(const QString &name) 1009 { 1010 m_viewManager->openUrl(QUrl(name)); 1011 } 1012 1013 void KateMainWindow::slotConfigure() 1014 { 1015 showPluginConfigPage(nullptr, 0); 1016 } 1017 1018 bool KateMainWindow::showPluginConfigPage(KTextEditor::Plugin *configpageinterface, int id) 1019 { 1020 KateConfigDialog *dlg = new KateConfigDialog(this); 1021 1022 if (configpageinterface) { 1023 dlg->showAppPluginPage(configpageinterface, id); 1024 } 1025 1026 if (dlg->exec() == QDialog::Accepted) { 1027 m_fileOpenRecent->setMaxItems(KateConfigDialog::recentFilesMaxCount()); 1028 } 1029 delete dlg; 1030 1031 m_viewManager->replugActiveView(); // gui (toolbars...) needs to be updated, because 1032 // of possible changes that the configure dialog 1033 // could have done on it, specially for plugins. 1034 1035 return true; 1036 } 1037 1038 QUrl KateMainWindow::activeDocumentUrl() 1039 { 1040 // anders: i make this one safe, as it may be called during 1041 // startup (by the file selector) 1042 KTextEditor::View *v = m_viewManager->activeView(); 1043 if (v) { 1044 return v->document()->url(); 1045 } 1046 return QUrl(); 1047 } 1048 1049 void KateMainWindow::mSlotFixOpenWithMenu() 1050 { 1051 KTextEditor::View *activeView = m_viewManager->activeView(); 1052 if (!activeView) { 1053 return; 1054 } 1055 1056 KTextEditor::Document *doc = activeView->document(); 1057 if (!doc) { 1058 return; 1059 } 1060 1061 KateFileActions::prepareOpenWithMenu(doc->url(), documentOpenWith->menu()); 1062 } 1063 1064 void KateMainWindow::slotOpenWithMenuAction(QAction *a) 1065 { 1066 auto activeView = m_viewManager->activeView(); 1067 if (!activeView) { 1068 return; 1069 } 1070 1071 auto doc = activeView->document(); 1072 if (!doc) { 1073 return; 1074 } 1075 1076 KateFileActions::showOpenWithMenu(this, doc->url(), a); 1077 } 1078 1079 void KateMainWindow::pluginHelp() 1080 { 1081 KHelpClient::invokeHelp(QString(), QStringLiteral("kate-plugins")); 1082 } 1083 1084 void KateMainWindow::slotFullScreen(bool t) 1085 { 1086 KToggleFullScreenAction::setFullScreen(this, t); 1087 QMenuBar *mb = menuBar(); 1088 if (t) { 1089 QToolButton *b = new QToolButton(mb); 1090 b->setDefaultAction(m_showFullScreenAction); 1091 b->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Ignored)); 1092 b->setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont)); 1093 mb->setCornerWidget(b, Qt::TopRightCorner); 1094 b->setVisible(true); 1095 b->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); 1096 } else { 1097 QWidget *w = mb->cornerWidget(Qt::TopRightCorner); 1098 if (w) { 1099 w->deleteLater(); 1100 } 1101 } 1102 } 1103 1104 bool KateMainWindow::showModOnDiskPrompt(ModOnDiskMode mode) 1105 { 1106 const auto documents = KateApp::self()->documentManager()->documentList(); 1107 QList<KTextEditor::Document *> list; 1108 list.reserve(documents.size()); 1109 for (auto doc : documents) { 1110 if (KateApp::self()->documentManager()->documentInfo(doc)->modifiedOnDisc && (doc->isModified() || mode == PromptAll)) { 1111 list.append(doc); 1112 } 1113 } 1114 1115 if (!list.isEmpty() && !m_modignore) { 1116 KateMwModOnHdDialog mhdlg(list, this); 1117 m_modignore = true; 1118 bool res = mhdlg.exec(); 1119 m_modignore = false; 1120 1121 return res; 1122 } 1123 return true; 1124 } 1125 1126 void KateMainWindow::slotDocumentCreated(KTextEditor::Document *doc) 1127 { 1128 connect(doc, &KTextEditor::Document::modifiedChanged, this, QOverload<KTextEditor::Document *>::of(&KateMainWindow::updateCaption)); 1129 connect(doc, &KTextEditor::Document::readWriteChanged, this, QOverload<KTextEditor::Document *>::of(&KateMainWindow::updateCaption)); 1130 connect(doc, &KTextEditor::Document::documentNameChanged, this, QOverload<KTextEditor::Document *>::of(&KateMainWindow::updateCaption)); 1131 connect(doc, &KTextEditor::Document::documentUrlChanged, this, QOverload<KTextEditor::Document *>::of(&KateMainWindow::updateCaption)); 1132 connect(doc, &KTextEditor::Document::documentUrlChanged, this, &KateMainWindow::slotUpdateActionsNeedingUrl); 1133 1134 updateCaption(doc); 1135 } 1136 1137 void KateMainWindow::updateCaption() 1138 { 1139 if (m_viewManager->activeView()) { 1140 updateCaption(m_viewManager->activeView()->document()); 1141 } 1142 } 1143 1144 void KateMainWindow::updateCaption(KTextEditor::Document *doc) 1145 { 1146 if (!m_viewManager->activeView()) { 1147 setCaption(QString(), false); 1148 setWindowFilePath(QString()); 1149 return; 1150 } 1151 1152 // block signals from inactive docs 1153 if (m_viewManager->activeView()->document() != doc) { 1154 return; 1155 } 1156 1157 QString c; 1158 const auto url = m_viewManager->activeView()->document()->url(); 1159 if (m_viewManager->activeView()->document()->url().isEmpty() || (!m_paShowPath || !m_paShowPath->isChecked())) { 1160 c = m_viewManager->activeView()->document()->documentName(); 1161 } else { 1162 // we want some filename @ folder output to have chance to keep important stuff even on elide 1163 c = Utils::niceFileNameWithPath(url); 1164 } 1165 1166 setWindowFilePath(url.toString(QUrl::PreferLocalFile)); 1167 1168 QString sessName = KateApp::self()->sessionManager()->activeSession()->name(); 1169 if (!sessName.isEmpty()) { 1170 sessName = QStringLiteral("%1: ").arg(sessName); 1171 } 1172 1173 QString readOnlyCaption; 1174 if (!m_viewManager->activeView()->document()->isReadWrite()) { 1175 readOnlyCaption = i18n(" [read only]"); 1176 } 1177 1178 setCaption(sessName + c + readOnlyCaption + QStringLiteral(" [*]"), m_viewManager->activeView()->document()->isModified()); 1179 } 1180 1181 void KateMainWindow::saveProperties(KConfigGroup &config, bool includeViewConfig) 1182 { 1183 saveSession(config); 1184 1185 // store all plugin view states 1186 int id = KateApp::self()->mainWindowID(this); 1187 const auto plugins = KateApp::self()->pluginManager()->pluginList(); 1188 for (const KatePluginInfo &item : plugins) { 1189 if (item.plugin && pluginViews().contains(item.plugin)) { 1190 if (auto interface = qobject_cast<KTextEditor::SessionConfigInterface *>(pluginViews().value(item.plugin))) { 1191 KConfigGroup group(config.config(), QStringLiteral("Plugin:%1:MainWindow:%2").arg(item.saveName()).arg(id)); 1192 interface->writeSessionConfig(group); 1193 } 1194 } 1195 } 1196 1197 saveOpenRecent(config.config()); 1198 1199 // allow to skip the view manager config, this is needed for KWrite, see bug 463139 1200 if (includeViewConfig) { 1201 m_viewManager->saveViewConfiguration(config); 1202 } 1203 } 1204 1205 void KateMainWindow::readProperties(const KConfigGroup &config) 1206 { 1207 // KDE5: TODO startRestore should take a const KConfigBase*, or even just a const KConfigGroup&, 1208 // but this propagates down to interfaces/kate/plugin.h so all plugins have to be ported 1209 KConfigBase *configBase = const_cast<KConfig *>(config.config()); 1210 startRestore(configBase, config.name()); 1211 1212 // perhaps enable plugin guis 1213 KateApp::self()->pluginManager()->enableAllPluginsGUI(this, configBase); 1214 1215 finishRestore(); 1216 1217 loadOpenRecent(config.config()); 1218 m_viewManager->restoreViewConfiguration(config); 1219 } 1220 1221 void KateMainWindow::saveOpenRecent(KConfig *config) 1222 { 1223 m_fileOpenRecent->saveEntries(KConfigGroup(config, QStringLiteral("Recent Files"))); 1224 } 1225 1226 void KateMainWindow::loadOpenRecent(const KConfig *config) 1227 { 1228 m_fileOpenRecent->loadEntries(KConfigGroup(config, QStringLiteral("Recent Files"))); 1229 } 1230 1231 void KateMainWindow::saveGlobalProperties(KConfig *sessionConfig) 1232 { 1233 KateApp::self()->documentManager()->saveDocumentList(sessionConfig); 1234 1235 KConfigGroup cg(sessionConfig, QStringLiteral("General")); 1236 cg.writeEntry("Last Session", KateApp::self()->sessionManager()->activeSession()->name()); 1237 1238 // save plugin config !! 1239 KateApp::self()->pluginManager()->writeConfig(sessionConfig); 1240 1241 if (m_diagView) { 1242 KConfigGroup cg(sessionConfig, QStringLiteral("Kate Diagnostics")); 1243 m_diagView->writeSessionConfig(cg); 1244 } 1245 } 1246 1247 void KateMainWindow::saveWindowConfig(const KConfigGroup &_config) 1248 { 1249 KConfigGroup config(_config); 1250 saveMainWindowSettings(config); 1251 config.writeEntry("WindowState", static_cast<int>(windowState())); 1252 config.sync(); 1253 } 1254 1255 void KateMainWindow::restoreWindowConfig(const KConfigGroup &config) 1256 { 1257 setWindowState(Qt::WindowNoState); 1258 applyMainWindowSettings(config); 1259 setWindowState(QFlags<Qt::WindowState>(config.readEntry("WindowState", int(Qt::WindowActive)))); 1260 } 1261 1262 void KateMainWindow::slotUpdateBottomViewBar() 1263 { 1264 // get active view if any, if none => just hide bar 1265 KTextEditor::View *view = m_viewManager->activeView(); 1266 if (!view) { 1267 if (auto wid = m_bottomContainerStack->currentWidget()) { 1268 wid->hide(); 1269 } 1270 m_bottomViewBarContainer->hide(); 1271 return; 1272 } 1273 1274 // get bar state, we must have a bar widget here, KateView always creates one! 1275 BarState &bs = m_bottomViewBarMapping[view]; 1276 Q_ASSERT(bs.bar()); 1277 1278 // extract statusbar if not already done 1279 if (!bs.statusBar()) { 1280 // we search for the status bar by class, this MUST work, we ensure that it is enabled in the view 1281 const auto widgets = bs.bar()->findChildren<QWidget *>(QString(), Qt::FindChildrenRecursively); 1282 for (auto *w : widgets) { 1283 if (w && w->metaObject()->className() == QByteArrayLiteral("KateStatusBar")) { 1284 bs.setStatusBar(w); 1285 } 1286 } 1287 Q_ASSERT(bs.statusBar()); 1288 1289 // ensure we don't mess up the vertical sizing 1290 bs.statusBar()->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); 1291 1292 // add the status bar to our status bar stack, there we will show/hide it 1293 statusBarStackedWidget()->addWidget(bs.statusBar()); 1294 } 1295 1296 // always activate the current statusbar 1297 Q_ASSERT(bs.statusBar()->parent() == statusBarStackedWidget()); 1298 statusBarStackedWidget()->setCurrentWidget(bs.statusBar()); 1299 1300 // hide or show the bar 1301 if (bs.state()) { 1302 m_bottomContainerStack->setCurrentWidget(bs.bar()); 1303 m_bottomContainerStack->currentWidget()->show(); 1304 m_bottomViewBarContainer->show(); 1305 } else { 1306 if (auto wid = m_bottomContainerStack->currentWidget()) { 1307 wid->hide(); 1308 } 1309 m_bottomViewBarContainer->hide(); 1310 } 1311 } 1312 1313 void KateMainWindow::queueModifiedOnDisc(KTextEditor::Document *doc) 1314 { 1315 if (!m_modNotification) { 1316 return; 1317 } 1318 1319 KateDocumentInfo *docInfo = KateApp::self()->documentManager()->documentInfo(doc); 1320 if (!docInfo) { 1321 return; 1322 } 1323 bool modOnDisk = static_cast<uint>(docInfo->modifiedOnDisc); 1324 1325 if (s_modOnHdDialog == nullptr && modOnDisk) { 1326 QList<KTextEditor::Document *> list; 1327 list.append(doc); 1328 1329 s_modOnHdDialog = new KateMwModOnHdDialog(list, this); 1330 m_modignore = true; 1331 connect(s_modOnHdDialog, &KateMwModOnHdDialog::requestOpenDiffDocument, KateApp::self(), [](const QUrl &url) { 1332 // use open with isTempFile == true 1333 KateApp::self()->openDocUrl(url, QString(), true); 1334 }); 1335 1336 // Someone modified a doc outside and now we are here 1337 // but Kate isn't the active app. Delay the dialog exec 1338 // otherwise it will bring us front interrupting user's 1339 // work. 1340 if (qApp->applicationState() != Qt::ApplicationActive) { 1341 m_modignore = false; 1342 s_modOnHdDialog->setShowOnWindowActivation(true); 1343 // hopefully this shows an alert to the user in task bar 1344 // that something changed in Kate 1345 qApp->alert(this, 3000); 1346 return; 1347 } 1348 1349 s_modOnHdDialog->exec(); 1350 delete s_modOnHdDialog; // s_modOnHdDialog is set to 0 in destructor of KateMwModOnHdDialog (jowenn!!!) 1351 m_modignore = false; 1352 } else if (s_modOnHdDialog != nullptr) { 1353 s_modOnHdDialog->addDocument(doc); 1354 } 1355 } 1356 1357 bool KateMainWindow::event(QEvent *e) 1358 { 1359 if (e->type() == QEvent::ShortcutOverride) { 1360 QKeyEvent *k = static_cast<QKeyEvent *>(e); 1361 Q_EMIT unhandledShortcutOverride(k); 1362 1363 if (KateApp::isKate() && k->key() == Qt::Key_Escape && k->modifiers() == Qt::NoModifier) { 1364 if (!m_toolViewOutput->isHidden()) { 1365 hideToolView(m_toolViewOutput); 1366 } 1367 } 1368 } 1369 1370 return KateMDI::MainWindow::event(e); 1371 } 1372 1373 QObject *KateMainWindow::pluginView(const QString &name) 1374 { 1375 KTextEditor::Plugin *plugin = KateApp::self()->pluginManager()->plugin(name); 1376 if (!plugin) { 1377 return nullptr; 1378 } 1379 1380 return m_pluginViews.contains(plugin) ? m_pluginViews.value(plugin) : nullptr; 1381 } 1382 1383 bool KateMainWindow::addWidget(QWidget *widget) 1384 { 1385 if (!widget) { 1386 qWarning() << Q_FUNC_INFO << "Unexpected null widget!"; 1387 return false; 1388 } 1389 1390 auto vs = m_viewManager->activeViewSpace(); 1391 vs->addWidgetAsTab(widget); 1392 Q_EMIT widgetAdded(widget); 1393 m_viewManager->activateView(widget); 1394 return true; 1395 } 1396 1397 bool KateMainWindow::removeWidget(QWidget *widget) 1398 { 1399 return m_viewManager->removeWidget(widget); 1400 } 1401 1402 QWidget *KateMainWindow::activeWidget() 1403 { 1404 auto vs = m_viewManager->activeViewSpace(); 1405 if (auto w = vs->currentWidget()) { 1406 return w; 1407 } 1408 return activeView(); 1409 } 1410 1411 void KateMainWindow::activateWidget(QWidget *widget) 1412 { 1413 if (!m_viewManager->activateWidget(widget)) { 1414 addWidget(widget); 1415 } 1416 } 1417 1418 void KateMainWindow::showMessage(const QVariantMap &map) 1419 { 1420 if (!m_outputView) { 1421 return; 1422 } 1423 m_outputView->slotMessage(map); 1424 } 1425 1426 void KateMainWindow::addPositionToHistory(const QUrl &url, KTextEditor::Cursor c) 1427 { 1428 m_viewManager->addPositionToHistory(url, c); 1429 } 1430 1431 QWidgetList KateMainWindow::widgets() const 1432 { 1433 return m_viewManager->widgets(); 1434 } 1435 1436 void KateMainWindow::mousePressEvent(QMouseEvent *e) 1437 { 1438 switch (e->button()) { 1439 case Qt::ForwardButton: 1440 handleForwardButtonAction(); 1441 break; 1442 case Qt::BackButton: 1443 handleBackButtonAction(); 1444 break; 1445 default:; 1446 } 1447 } 1448 1449 void KateMainWindow::slotFocusPrevTab() 1450 { 1451 if (m_viewManager->activeViewSpace()) { 1452 m_viewManager->activeViewSpace()->focusPrevTab(); 1453 } 1454 } 1455 1456 void KateMainWindow::slotFocusNextTab() 1457 { 1458 if (m_viewManager->activeViewSpace()) { 1459 m_viewManager->activeViewSpace()->focusNextTab(); 1460 } 1461 } 1462 1463 void KateMainWindow::handleBackButtonAction() 1464 { 1465 if (m_viewManager->activeViewSpace()) { 1466 switch (m_mouseButtonBackAction) { 1467 case PreviousTab: 1468 m_viewManager->activeViewSpace()->focusPrevTab(); 1469 break; 1470 case HistoryBack: 1471 m_viewManager->activeViewSpace()->goBack(); 1472 break; 1473 default:; 1474 } 1475 } 1476 } 1477 1478 void KateMainWindow::handleForwardButtonAction() 1479 { 1480 if (m_viewManager->activeViewSpace()) { 1481 switch (m_mouseButtonForwardAction) { 1482 case NextTab: 1483 m_viewManager->activeViewSpace()->focusNextTab(); 1484 break; 1485 case HistoryForward: 1486 m_viewManager->activeViewSpace()->goForward(); 1487 break; 1488 default:; 1489 } 1490 } 1491 } 1492 1493 void KateMainWindow::slotQuickOpen() 1494 { 1495 /** 1496 * show quick open and pass focus to it 1497 */ 1498 KateQuickOpen *quickOpen = new KateQuickOpen(this); 1499 centralWidget()->setFocusProxy(quickOpen); 1500 quickOpen->raise(); 1501 quickOpen->show(); 1502 } 1503 1504 QWidget *KateMainWindow::createToolView(KTextEditor::Plugin *plugin, 1505 const QString &identifier, 1506 KTextEditor::MainWindow::ToolViewPosition pos, 1507 const QIcon &icon, 1508 const QString &text) 1509 { 1510 return KateMDI::MainWindow::createToolView(plugin, identifier, static_cast<KMultiTabBar::KMultiTabBarPosition>(pos), icon, text); 1511 } 1512 1513 bool KateMainWindow::moveToolView(QWidget *widget, KTextEditor::MainWindow::ToolViewPosition pos) 1514 { 1515 if (!qobject_cast<KateMDI::ToolView *>(widget)) { 1516 return false; 1517 } 1518 1519 return KateMDI::MainWindow::moveToolView(qobject_cast<KateMDI::ToolView *>(widget), static_cast<KMultiTabBar::KMultiTabBarPosition>(pos)); 1520 } 1521 1522 bool KateMainWindow::showToolView(QWidget *widget) 1523 { 1524 if (!qobject_cast<KateMDI::ToolView *>(widget)) { 1525 return false; 1526 } 1527 1528 return KateMDI::MainWindow::showToolView(qobject_cast<KateMDI::ToolView *>(widget)); 1529 } 1530 1531 bool KateMainWindow::hideToolView(QWidget *widget) 1532 { 1533 if (!qobject_cast<KateMDI::ToolView *>(widget)) { 1534 return false; 1535 } 1536 1537 return KateMDI::MainWindow::hideToolView(qobject_cast<KateMDI::ToolView *>(widget)); 1538 } 1539 1540 void KateMainWindow::addRecentOpenedFile(const QUrl &url) 1541 { 1542 // skip non-existing urls for untitled documents 1543 if (url.isEmpty()) { 1544 return; 1545 } 1546 // skip files in /subltmp 1547 if (url.path().startsWith(QDir::tempPath())) { 1548 return; 1549 } 1550 1551 // to our local list, aka menu 1552 m_fileOpenRecent->addUrl(url); 1553 1554 /** FIXME Disabled because this can be too slow 100ms/doc sometimes, 1555 renable when it is 0/ms again*/ 1556 // to the global "Recent Document Menu", see bug 420504 1557 // KRecentDocument::add(url); 1558 } 1559 1560 void KateMainWindow::onApplicationStateChanged(Qt::ApplicationState) 1561 { 1562 // After queueModifiedOnDisc, the app wasn't active but now it got 1563 // active. Show the dialog. 1564 if (s_modOnHdDialog && s_modOnHdDialog->showOnWindowActivation() && qApp->applicationState() == Qt::ApplicationActive) { 1565 // Must set this to false, we want only one exec to happen. 1566 s_modOnHdDialog->setShowOnWindowActivation(false); 1567 // do it on next event loop iteration to avoid blocking here 1568 QTimer::singleShot(1, this, [] { 1569 s_modOnHdDialog->exec(); 1570 delete s_modOnHdDialog; 1571 }); 1572 } 1573 } 1574 1575 #include "moc_katemainwindow.cpp"