1 /* This file is part of the KDE project
2    SPDX-FileCopyrightText: 2001 Christoph Cullmann <cullmann@kde.org>
3    SPDX-FileCopyrightText: 2001 Joseph Wenninger <jowenn@kde.org>
4    SPDX-FileCopyrightText: 2001 Anders Lund <anders.lund@lund.tdcadsl.dk>
5    SPDX-FileCopyrightText: 2007 Flavio Castelli <flavio.castelli@gmail.com>
6 
7    SPDX-License-Identifier: LGPL-2.0-only
8 */
9 
10 // BEGIN Includes
11 #include "katemainwindow.h"
12 
13 #include "kateapp.h"
14 #include "katecolorschemechooser.h"
15 #include "katecommandbar.h"
16 #include "kateconfigdialog.h"
17 #include "kateconfigplugindialogpage.h"
18 #include "katedebug.h"
19 #include "katedocmanager.h"
20 #include "katefileactions.h"
21 #include "katemwmodonhddialog.h"
22 #include "kateoutputview.h"
23 #include "katepluginmanager.h"
24 #include "katequickopen.h"
25 #include "katesavemodifieddialog.h"
26 #include "katesessionmanager.h"
27 #include "katesessionsaction.h"
28 #include "katestashmanager.h"
29 #include "kateupdatedisabler.h"
30 #include "kateviewspace.h"
31 
32 #include <KAboutData>
33 #include <KActionCollection>
34 #include <KActionMenu>
35 #include <KApplicationTrader>
36 #include <KConfigGroup>
37 #include <KEditToolBar>
38 #include <KFileItem>
39 #include <KHelpClient>
40 #include <KIO/ApplicationLauncherJob>
41 #include <KIO/Job>
42 #include <KIO/JobUiDelegate>
43 #include <KLocalizedString>
44 #include <KMessageBox>
45 #include <KMultiTabBar>
46 #include <KOpenWithDialog>
47 #include <KRecentDocument>
48 #include <KRecentFilesAction>
49 #include <KSharedConfig>
50 #include <KShortcutsDialog>
51 #include <KStandardAction>
52 #include <KToggleFullScreenAction>
53 #include <KToolBar>
54 #include <KWindowConfig>
55 #include <KXMLGUIFactory>
56 
57 #include <QApplication>
58 #include <QDesktopWidget>
59 #include <QDir>
60 #include <QFontDatabase>
61 #include <QList>
62 #include <QMenu>
63 #include <QMenuBar>
64 #include <QMimeData>
65 #include <QMimeDatabase>
66 #include <QStyle>
67 #include <QTimer>
68 #include <QToolButton>
69 
70 #include <ktexteditor/sessionconfiginterface.h>
71 
72 // END
73 
74 KateMwModOnHdDialog *KateMainWindow::s_modOnHdDialog = nullptr;
75 
KateContainerStackedLayout(QWidget * parent)76 KateContainerStackedLayout::KateContainerStackedLayout(QWidget *parent)
77     : QStackedLayout(parent)
78 {
79 }
80 
sizeHint() const81 QSize KateContainerStackedLayout::sizeHint() const
82 {
83     if (currentWidget()) {
84         return currentWidget()->sizeHint();
85     }
86     return QStackedLayout::sizeHint();
87 }
88 
minimumSize() const89 QSize KateContainerStackedLayout::minimumSize() const
90 {
91     if (currentWidget()) {
92         return currentWidget()->minimumSize();
93     }
94     return QStackedLayout::minimumSize();
95 }
96 
KateMainWindow(KConfig * sconfig,const QString & sgroup)97 KateMainWindow::KateMainWindow(KConfig *sconfig, const QString &sgroup)
98     : KateMDI::MainWindow(nullptr)
99     , m_modignore(false)
100     , m_wrapper(new KTextEditor::MainWindow(this))
101 {
102     /**
103      * we don't want any flicker here
104      */
105     KateUpdateDisabler disableUpdates(this);
106 
107     /**
108      * get and set config revision
109      */
110     static const int currentConfigRevision = 10;
111     const int readConfigRevision = KConfigGroup(KSharedConfig::openConfig(), "General").readEntry("Config Revision", 0);
112     KConfigGroup(KSharedConfig::openConfig(), "General").writeEntry("Config Revision", currentConfigRevision);
113     const bool firstStart = readConfigRevision < currentConfigRevision;
114 
115     // start session restore if needed
116     startRestore(sconfig, sgroup);
117 
118     // setup most important actions first, needed by setupMainWindow
119     setupImportantActions();
120 
121     // setup the most important widgets
122     setupMainWindow();
123 
124     // setup the actions
125     setupActions();
126 
127     setStandardToolBarMenuEnabled(true);
128     setXMLFile(QStringLiteral("kateui.rc"));
129     createShellGUI(true);
130 
131     // qCDebug(LOG_KATE) << "****************************************************************************" << sconfig;
132 
133     // register mainwindow in app
134     KateApp::self()->addMainWindow(this);
135 
136     // enable plugin guis
137     KateApp::self()->pluginManager()->enableAllPluginsGUI(this, sconfig);
138 
139     // caption update
140     const auto documents = KateApp::self()->documentManager()->documentList();
141     for (auto doc : documents) {
142         slotDocumentCreated(doc);
143     }
144 
145     connect(KateApp::self()->documentManager(), &KateDocManager::documentCreated, this, &KateMainWindow::slotDocumentCreated);
146 
147     readOptions();
148 
149     if (sconfig) {
150         m_viewManager->restoreViewConfiguration(KConfigGroup(sconfig, sgroup));
151     }
152 
153     // unstash
154     // KateStashManager().popStash(m_viewManager);
155 
156     finishRestore();
157 
158     m_fileOpenRecent->loadEntries(KConfigGroup(sconfig, "Recent Files"));
159 
160     setAcceptDrops(true);
161 
162     connect(KateApp::self()->sessionManager(), SIGNAL(sessionChanged()), this, SLOT(updateCaption()));
163 
164     connect(this, &KateMDI::MainWindow::sigShowPluginConfigPage, this, &KateMainWindow::showPluginConfigPage);
165 
166     // prior to this there was (possibly) no view, therefore not context menu.
167     // Hence, we have to take care of the menu bar here
168     toggleShowMenuBar(false);
169 
170     // on first start: deactivate toolbar
171     if (firstStart) {
172         toolBar(QStringLiteral("mainToolBar"))->hide();
173     }
174 
175     // in all cases: avoid that arbitrary plugin toolviews get focus, like terminal, bug 412227
176     // we need to delay this a bit due to lazy view creation (and lazy e.g. terminal widget creation)
177     QTimer::singleShot(0, centralWidget(), SLOT(setFocus()));
178 }
179 
~KateMainWindow()180 KateMainWindow::~KateMainWindow()
181 {
182     // first, save our fallback window size ;)
183     KConfigGroup cfg(KSharedConfig::openConfig(), "MainWindow");
184     KWindowConfig::saveWindowSize(windowHandle(), cfg);
185 
186     // save other options ;=)
187     saveOptions();
188 
189     // unregister mainwindow in app
190     KateApp::self()->removeMainWindow(this);
191 
192     // disable all plugin guis, delete all pluginViews
193     KateApp::self()->pluginManager()->disableAllPluginsGUI(this);
194 
195     // delete the view manager, before KateMainWindow's wrapper is dead
196     delete m_viewManager;
197     m_viewManager = nullptr;
198 
199     // kill the wrapper object, now that all views are dead
200     delete m_wrapper;
201     m_wrapper = nullptr;
202 }
203 
sizeHint() const204 QSize KateMainWindow::sizeHint() const
205 {
206     /**
207      * have some useful size hint, else we have mini windows per default
208      */
209     return (QSize(640, 480).expandedTo(minimumSizeHint()));
210 }
211 
setupImportantActions()212 void KateMainWindow::setupImportantActions()
213 {
214     m_paShowStatusBar = KStandardAction::showStatusbar(this, SLOT(toggleShowStatusBar()), actionCollection());
215     m_paShowStatusBar->setWhatsThis(i18n("Use this command to show or hide the view's statusbar"));
216     m_paShowMenuBar = KStandardAction::showMenubar(this, SLOT(toggleShowMenuBar()), actionCollection());
217 
218     m_paShowTabBar = new KToggleAction(i18n("Show &Tabs"), this);
219     actionCollection()->addAction(QStringLiteral("settings_show_tab_bar"), m_paShowTabBar);
220     connect(m_paShowTabBar, &QAction::toggled, this, &KateMainWindow::toggleShowTabBar);
221     m_paShowTabBar->setWhatsThis(i18n("Use this command to show or hide the tabs for the views"));
222 
223     m_paShowPath = new KToggleAction(i18n("Sho&w Path in Titlebar"), this);
224     actionCollection()->addAction(QStringLiteral("settings_show_full_path"), m_paShowPath);
225     connect(m_paShowPath, SIGNAL(toggled(bool)), this, SLOT(updateCaption()));
226     m_paShowPath->setWhatsThis(i18n("Show the complete document path in the window caption"));
227 
228     // Load themes
229     actionCollection()->addAction(QStringLiteral("colorscheme_menu"), new KateColorSchemeChooser(actionCollection()));
230 
231     QAction *a = actionCollection()->addAction(KStandardAction::Back, QStringLiteral("view_prev_tab"));
232     a->setText(i18n("&Previous Tab"));
233     a->setWhatsThis(i18n("Focus the previous tab."));
234     actionCollection()->setDefaultShortcuts(a, a->shortcuts() << KStandardShortcut::tabPrev());
235     connect(a, &QAction::triggered, this, &KateMainWindow::slotFocusPrevTab);
236 
237     a = actionCollection()->addAction(KStandardAction::Forward, QStringLiteral("view_next_tab"));
238     a->setText(i18n("&Next Tab"));
239     a->setWhatsThis(i18n("Focus the next tab."));
240     actionCollection()->setDefaultShortcuts(a, a->shortcuts() << KStandardShortcut::tabNext());
241     connect(a, &QAction::triggered, this, &KateMainWindow::slotFocusNextTab);
242 
243     // the quick open action is used by the KateViewSpace "quick open button"
244     a = actionCollection()->addAction(QStringLiteral("view_quick_open"));
245     a->setIcon(QIcon::fromTheme(QStringLiteral("quickopen")));
246     a->setText(i18n("&Quick Open"));
247     actionCollection()->setDefaultShortcut(a, QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_O));
248     connect(a, &QAction::triggered, this, &KateMainWindow::slotQuickOpen);
249     a->setWhatsThis(i18n("Open a form to quick open documents."));
250 
251     // kate command bar, only add this if we don't already have the generic one from KXMLGui
252     // https://invent.kde.org/frameworks/kxmlgui/-/merge_requests/54
253     // FIXME: remove after we require Frameworks >= 5.83
254     if (!actionCollection()->action(QStringLiteral("open_kcommand_bar"))) {
255         a = actionCollection()->addAction(QStringLiteral("view_commandbar_open"));
256         a->setText(i18n("&Command Bar"));
257         actionCollection()->setDefaultShortcut(a, QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_I));
258         connect(a, &QAction::triggered, this, &KateMainWindow::slotCommandBarOpen);
259     }
260 }
261 
setupMainWindow()262 void KateMainWindow::setupMainWindow()
263 {
264     setToolViewStyle(KMultiTabBar::KDEV3ICON);
265 
266     /**
267      * create central stacked widget with its children
268      */
269     m_mainStackedWidget = new QStackedWidget(centralWidget());
270     centralWidget()->layout()->addWidget(m_mainStackedWidget);
271     (static_cast<QBoxLayout *>(centralWidget()->layout()))->setStretchFactor(m_mainStackedWidget, 100);
272 
273     m_viewManager = new KateViewManager(m_mainStackedWidget, this);
274     m_mainStackedWidget->addWidget(m_viewManager);
275 
276     // make view manager default visible!
277     m_mainStackedWidget->setCurrentWidget(m_viewManager);
278 
279     m_bottomViewBarContainer = new QWidget(centralWidget());
280     centralWidget()->layout()->addWidget(m_bottomViewBarContainer);
281     m_bottomContainerStack = new KateContainerStackedLayout(m_bottomViewBarContainer);
282 
283     /**
284      * create generic output tool view
285      * is used to display output of e.g. plugins
286      */
287     m_toolViewOutput = createToolView(nullptr /* toolview has no plugin it belongs to */,
288                                       QStringLiteral("output"),
289                                       KTextEditor::MainWindow::Bottom,
290                                       QIcon::fromTheme(QStringLiteral("output_win")),
291                                       i18n("Output"));
292     m_outputView = new KateOutputView(this, m_toolViewOutput);
293 }
294 
setupActions()295 void KateMainWindow::setupActions()
296 {
297     QAction *a;
298 
299     actionCollection()
300         ->addAction(KStandardAction::New, QStringLiteral("file_new"), m_viewManager, SLOT(slotDocumentNew()))
301         ->setWhatsThis(i18n("Create a new document"));
302     actionCollection()
303         ->addAction(KStandardAction::Open, QStringLiteral("file_open"), m_viewManager, SLOT(slotDocumentOpen()))
304         ->setWhatsThis(i18n("Open an existing document for editing"));
305 
306     m_fileOpenRecent = KStandardAction::openRecent(m_viewManager, SLOT(openUrl(QUrl)), this);
307     m_fileOpenRecent->setMaxItems(KateConfigDialog::recentFilesMaxCount());
308     actionCollection()->addAction(m_fileOpenRecent->objectName(), m_fileOpenRecent);
309     m_fileOpenRecent->setWhatsThis(i18n("This lists files which you have opened recently, and allows you to easily open them again."));
310 
311     a = actionCollection()->addAction(QStringLiteral("file_save_all"));
312     a->setIcon(QIcon::fromTheme(QStringLiteral("document-save-all")));
313     a->setText(i18n("Save A&ll"));
314     actionCollection()->setDefaultShortcut(a, QKeySequence(Qt::CTRL | Qt::Key_L));
315     connect(a, &QAction::triggered, KateApp::self()->documentManager(), &KateDocManager::saveAll);
316     a->setWhatsThis(i18n("Save all open, modified documents to disk."));
317 
318     a = actionCollection()->addAction(QStringLiteral("file_reload_all"));
319     a->setText(i18n("&Reload All"));
320     connect(a, &QAction::triggered, KateApp::self()->documentManager(), &KateDocManager::reloadAll);
321     a->setWhatsThis(i18n("Reload all open documents."));
322 
323     a = actionCollection()->addAction(QStringLiteral("file_copy_filepath"));
324     a->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy")));
325     a->setText(i18n("Copy File &Path"));
326     connect(a, &QAction::triggered, KateApp::self()->documentManager(), [this]() {
327         auto &&view = viewManager()->activeView();
328         KateFileActions::copyFilePathToClipboard(view->document());
329     });
330     a->setWhatsThis(i18n("Copies the file path of the current file to clipboard."));
331 
332     a = actionCollection()->addAction(QStringLiteral("file_open_containing_folder"));
333     a->setIcon(QIcon::fromTheme(QStringLiteral("document-open-folder")));
334     a->setText(i18n("&Open Containing Folder"));
335     connect(a, &QAction::triggered, KateApp::self()->documentManager(), [this]() {
336         auto &&view = viewManager()->activeView();
337         KateFileActions::openContainingFolder(view->document());
338     });
339     a->setWhatsThis(i18n("Copies the file path of the current file to clipboard."));
340 
341     a = actionCollection()->addAction(QStringLiteral("file_rename"));
342     a->setIcon(QIcon::fromTheme(QStringLiteral("edit-rename")));
343     a->setText(i18nc("@action:inmenu", "Rename..."));
344     connect(a, &QAction::triggered, KateApp::self()->documentManager(), [this]() {
345         auto &&view = viewManager()->activeView();
346         KateFileActions::renameDocumentFile(this, view->document());
347     });
348     a->setWhatsThis(i18n("Renames the file belonging to the current document."));
349 
350     a = actionCollection()->addAction(QStringLiteral("file_delete"));
351     a->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete")));
352     a->setText(i18nc("@action:inmenu", "Delete"));
353     connect(a, &QAction::triggered, KateApp::self()->documentManager(), [this]() {
354         auto &&view = viewManager()->activeView();
355         KateFileActions::deleteDocumentFile(this, view->document());
356     });
357     a->setWhatsThis(i18n("Deletes the file belonging to the current document."));
358 
359     a = actionCollection()->addAction(QStringLiteral("file_properties"));
360     a->setIcon(QIcon::fromTheme(QStringLiteral("dialog-object-properties")));
361     a->setText(i18n("Properties"));
362     connect(a, &QAction::triggered, KateApp::self()->documentManager(), [this]() {
363         auto &&view = viewManager()->activeView();
364         KateFileActions::openFilePropertiesDialog(view->document());
365     });
366     a->setWhatsThis(i18n("Deletes the file belonging to the current document."));
367 
368     a = actionCollection()->addAction(QStringLiteral("file_compare"));
369     a->setText(i18n("Compare"));
370     connect(a, &QAction::triggered, KateApp::self()->documentManager(), [this]() {
371         QMessageBox::information(this, i18n("Compare"), i18n("Use the Tabbar context menu to compare two documents"));
372     });
373     a->setWhatsThis(i18n("Shows a hint how to compare documents."));
374 
375     a = actionCollection()->addAction(QStringLiteral("file_close_orphaned"));
376     a->setText(i18n("Close Orphaned"));
377     connect(a, &QAction::triggered, KateApp::self()->documentManager(), &KateDocManager::closeOrphaned);
378     a->setWhatsThis(i18n("Close all documents in the file list that could not be reopened, because they are not accessible anymore."));
379 
380     a = actionCollection()->addAction(KStandardAction::Close, QStringLiteral("file_close"), m_viewManager, SLOT(slotDocumentClose()));
381     a->setIcon(QIcon::fromTheme(QStringLiteral("document-close")));
382     a->setWhatsThis(i18n("Close the current document."));
383 
384     a = actionCollection()->addAction(QStringLiteral("file_close_other"));
385     a->setText(i18n("Close Other"));
386     connect(a, SIGNAL(triggered()), this, SLOT(slotDocumentCloseOther()));
387     a->setWhatsThis(i18n("Close other open documents."));
388 
389     a = actionCollection()->addAction(QStringLiteral("file_close_all"));
390     a->setText(i18n("Clos&e All"));
391     connect(a, &QAction::triggered, this, &KateMainWindow::slotDocumentCloseAll);
392     a->setWhatsThis(i18n("Close all open documents."));
393 
394     a = actionCollection()->addAction(KStandardAction::Quit, QStringLiteral("file_quit"));
395     // Qt::QueuedConnection: delay real shutdown, as we are inside menu action handling (bug #185708)
396     connect(a, &QAction::triggered, this, &KateMainWindow::slotFileQuit, Qt::QueuedConnection);
397     a->setWhatsThis(i18n("Close this window"));
398 
399     a = actionCollection()->addAction(QStringLiteral("view_new_view"));
400     a->setIcon(QIcon::fromTheme(QStringLiteral("window-new")));
401     a->setText(i18n("&New Window"));
402     connect(a, &QAction::triggered, this, &KateMainWindow::newWindow);
403     a->setWhatsThis(i18n("Create a new Kate view (a new window with the same document list)."));
404 
405     m_showFullScreenAction = KStandardAction::fullScreen(nullptr, nullptr, this, this);
406     actionCollection()->addAction(m_showFullScreenAction->objectName(), m_showFullScreenAction);
407     actionCollection()->setDefaultShortcut(m_showFullScreenAction, Qt::Key_F11);
408     connect(m_showFullScreenAction, &QAction::toggled, this, &KateMainWindow::slotFullScreen);
409 
410     documentOpenWith = new KActionMenu(i18n("Open W&ith"), this);
411     actionCollection()->addAction(QStringLiteral("file_open_with"), documentOpenWith);
412     documentOpenWith->setWhatsThis(i18n("Open the current document using another application registered for its file type, or an application of your choice."));
413     connect(documentOpenWith->menu(), &QMenu::aboutToShow, this, &KateMainWindow::mSlotFixOpenWithMenu);
414     connect(documentOpenWith->menu(), &QMenu::triggered, this, &KateMainWindow::slotOpenWithMenuAction);
415 
416     a = KStandardAction::keyBindings(this, SLOT(editKeys()), actionCollection());
417     a->setWhatsThis(i18n("Configure the application's keyboard shortcut assignments."));
418 
419     a = KStandardAction::configureToolbars(this, SLOT(slotEditToolbars()), actionCollection());
420     a->setWhatsThis(i18n("Configure which items should appear in the toolbar(s)."));
421 
422     QAction *settingsConfigure = KStandardAction::preferences(this, SLOT(slotConfigure()), actionCollection());
423     settingsConfigure->setWhatsThis(i18n("Configure various aspects of this application and the editing component."));
424 
425     if (KateApp::self()->pluginManager()->pluginList().count() > 0) {
426         a = actionCollection()->addAction(QStringLiteral("help_plugins_contents"));
427         a->setText(i18n("&Plugins Handbook"));
428         connect(a, &QAction::triggered, this, &KateMainWindow::pluginHelp);
429         a->setWhatsThis(i18n("This shows help files for various available plugins."));
430     }
431 
432     connect(m_viewManager, &KateViewManager::viewChanged, this, &KateMainWindow::slotWindowActivated);
433     connect(m_viewManager, &KateViewManager::viewChanged, this, &KateMainWindow::slotUpdateOpenWith);
434     connect(m_viewManager, &KateViewManager::viewChanged, this, &KateMainWindow::slotUpdateActionsNeedingUrl);
435     connect(m_viewManager, &KateViewManager::viewChanged, this, &KateMainWindow::slotUpdateBottomViewBar);
436 
437     // re-route signals to our wrapper
438     connect(m_viewManager, &KateViewManager::viewChanged, m_wrapper, &KTextEditor::MainWindow::viewChanged);
439     connect(m_viewManager, &KateViewManager::viewCreated, m_wrapper, &KTextEditor::MainWindow::viewCreated);
440     connect(this, &KateMainWindow::unhandledShortcutOverride, m_wrapper, &KTextEditor::MainWindow::unhandledShortcutOverride);
441 
442     slotWindowActivated();
443 
444     // session actions
445     a = actionCollection()->addAction(QStringLiteral("sessions_new"));
446     a->setIcon(QIcon::fromTheme(QStringLiteral("document-new")));
447     a->setText(i18nc("Menu entry Session->New Session", "&New Session"));
448     // Qt::QueuedConnection to avoid deletion of code that is executed when reducing the amount of mainwindows. (bug #227008)
449     connect(a, &QAction::triggered, KateApp::self()->sessionManager(), &KateSessionManager::sessionNew, Qt::QueuedConnection);
450     a = actionCollection()->addAction(QStringLiteral("sessions_save"));
451     a->setIcon(QIcon::fromTheme(QStringLiteral("document-save")));
452     a->setText(i18n("&Save Session"));
453     connect(a, &QAction::triggered, KateApp::self()->sessionManager(), &KateSessionManager::sessionSave);
454     a = actionCollection()->addAction(QStringLiteral("sessions_save_as"));
455     a->setIcon(QIcon::fromTheme(QStringLiteral("document-save-as")));
456     a->setText(i18n("Save Session &As..."));
457     connect(a, &QAction::triggered, KateApp::self()->sessionManager(), &KateSessionManager::sessionSaveAs);
458     a = actionCollection()->addAction(QStringLiteral("sessions_manage"));
459     a->setIcon(QIcon::fromTheme(QStringLiteral("view-choose")));
460     a->setText(i18n("&Manage Sessions..."));
461     // Qt::QueuedConnection to avoid deletion of code that is executed when reducing the amount of mainwindows. (bug #227008)
462     connect(a, &QAction::triggered, KateApp::self()->sessionManager(), &KateSessionManager::sessionManage, Qt::QueuedConnection);
463 
464     // quick open menu ;)
465     a = new KateSessionsAction(i18n("&Quick Open Session"), this);
466     actionCollection()->addAction(QStringLiteral("sessions_list"), a);
467 
468     // location history actions
469     a = actionCollection()->addAction(QStringLiteral("view_history_back"));
470     a->setIcon(QIcon::fromTheme(QStringLiteral("arrow-left")));
471     a->setText(i18n("Go back"));
472     connect(a, &QAction::triggered, this, [this] {
473         m_viewManager->activeViewSpace()->goBack();
474     });
475     connect(this->m_viewManager, &KateViewManager::historyBackEnabled, a, &QAction::setEnabled);
476 
477     a = actionCollection()->addAction(QStringLiteral("view_history_forward"));
478     a->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right")));
479     a->setText(i18n("Go forward"));
480     connect(a, &QAction::triggered, this, [this] {
481         m_viewManager->activeViewSpace()->goForward();
482     });
483     connect(this->m_viewManager, &KateViewManager::historyForwardEnabled, a, &QAction::setEnabled);
484 }
485 
slotDocumentCloseAll()486 void KateMainWindow::slotDocumentCloseAll()
487 {
488     if (!KateApp::self()->documentManager()->documentList().empty()
489         && KMessageBox::warningContinueCancel(this,
490                                               i18n("This will close all open documents. Are you sure you want to continue?"),
491                                               i18n("Close all documents"),
492                                               KStandardGuiItem::cont(),
493                                               KStandardGuiItem::cancel(),
494                                               QStringLiteral("closeAll"))
495             != KMessageBox::Cancel) {
496         if (queryClose_internal()) {
497             KateApp::self()->documentManager()->closeAllDocuments(false);
498         }
499     }
500 }
501 
slotDocumentCloseOther(KTextEditor::Document * document)502 void KateMainWindow::slotDocumentCloseOther(KTextEditor::Document *document)
503 {
504     if (KateApp::self()->documentManager()->documentList().size() > 1
505         && KMessageBox::warningContinueCancel(this,
506                                               i18n("This will close all open documents beside the current one. Are you sure you want to continue?"),
507                                               i18n("Close all documents beside current one"),
508                                               KStandardGuiItem::cont(),
509                                               KStandardGuiItem::cancel(),
510                                               QStringLiteral("closeOther"))
511             != KMessageBox::Cancel) {
512         if (queryClose_internal(document)) {
513             KateApp::self()->documentManager()->closeOtherDocuments(document);
514         }
515     }
516 }
517 
slotDocumentCloseSelected(const QList<KTextEditor::Document * > & docList)518 void KateMainWindow::slotDocumentCloseSelected(const QList<KTextEditor::Document *> &docList)
519 {
520     QList<KTextEditor::Document *> documents;
521     for (KTextEditor::Document *doc : docList) {
522         if (queryClose_internal(doc)) {
523             documents.push_back(doc);
524         }
525     }
526 
527     KateApp::self()->documentManager()->closeDocuments(documents);
528 }
529 
slotDocumentCloseOther()530 void KateMainWindow::slotDocumentCloseOther()
531 {
532     slotDocumentCloseOther(m_viewManager->activeView()->document());
533 }
534 
queryClose_internal(KTextEditor::Document * doc)535 bool KateMainWindow::queryClose_internal(KTextEditor::Document *doc)
536 {
537     const auto documentCount = KateApp::self()->documentManager()->documentList().size();
538 
539     if (!showModOnDiskPrompt(PromptEdited)) {
540         return false;
541     }
542 
543     std::vector<KTextEditor::Document *> modifiedDocuments = KateApp::self()->documentManager()->modifiedDocumentList();
544     modifiedDocuments.erase(std::remove(modifiedDocuments.begin(), modifiedDocuments.end(), doc), modifiedDocuments.end());
545 
546     // filter out what the stashManager will itself stash
547     auto m = modifiedDocuments.begin();
548     while (m != modifiedDocuments.end()) {
549         if (KateApp::self()->stashManager()->willStashDoc(*m)) {
550             m = modifiedDocuments.erase(m);
551         } else {
552             ++m;
553         }
554     }
555 
556     bool shutdown = modifiedDocuments.empty();
557 
558     if (!shutdown) {
559         shutdown = KateSaveModifiedDialog::queryClose(this, modifiedDocuments);
560     }
561 
562     if (KateApp::self()->documentManager()->documentList().size() > documentCount) {
563         KMessageBox::information(this, i18n("New file opened while trying to close Kate, closing aborted."), i18n("Closing Aborted"));
564         shutdown = false;
565     }
566 
567     return shutdown;
568 }
569 
570 /**
571  * queryClose(), take care that after the last mainwindow the stuff is closed
572  */
queryClose()573 bool KateMainWindow::queryClose()
574 {
575     // session saving, can we close all views ?
576     // just test, not close them actually
577     if (qApp->isSavingSession()) {
578         return queryClose_internal();
579     }
580 
581     // normal closing of window
582     // allow to close all windows until the last without restrictions
583     if (KateApp::self()->mainWindowsCount() > 1) {
584         return true;
585     }
586 
587     // last one: check if we can close all documents, try run
588     // and save docs if we really close down !
589     if (queryClose_internal()) {
590         KateApp::self()->sessionManager()->saveActiveSession(true);
591         KateApp::self()->stashManager()->stashDocuments(KateApp::self()->sessionManager()->activeSession()->config(),
592                                                         KateApp::self()->documentManager()->documentList());
593         return true;
594     }
595 
596     return false;
597 }
598 
newWindow()599 void KateMainWindow::newWindow()
600 {
601     KateApp::self()->newMainWindow(KateApp::self()->sessionManager()->activeSession()->config());
602 }
603 
slotEditToolbars()604 void KateMainWindow::slotEditToolbars()
605 {
606     KConfigGroup cfg(KSharedConfig::openConfig(), "MainWindow");
607     saveMainWindowSettings(cfg);
608 
609     KEditToolBar dlg(factory());
610 
611     connect(&dlg, &KEditToolBar::newToolBarConfig, this, &KateMainWindow::slotNewToolbarConfig);
612     dlg.exec();
613 }
614 
reloadXmlGui()615 void KateMainWindow::reloadXmlGui()
616 {
617     for (KTextEditor::Document *doc : KateApp::self()->documentManager()->documentList()) {
618         doc->reloadXML();
619         const auto views = doc->views();
620         for (KTextEditor::View *view : views) {
621             view->reloadXML();
622         }
623     }
624 }
625 
slotNewToolbarConfig()626 void KateMainWindow::slotNewToolbarConfig()
627 {
628     applyMainWindowSettings(KConfigGroup(KSharedConfig::openConfig(), "MainWindow"));
629 
630     // we need to reload all View's XML Gui from disk to ensure toolbar
631     // changes are applied to all views.
632     reloadXmlGui();
633 }
634 
slotFileQuit()635 void KateMainWindow::slotFileQuit()
636 {
637     KateApp::self()->shutdownKate(this);
638 }
639 
slotFileClose()640 void KateMainWindow::slotFileClose()
641 {
642     m_viewManager->slotDocumentClose();
643 }
644 
slotOpenDocument(const QUrl & url)645 void KateMainWindow::slotOpenDocument(const QUrl &url)
646 {
647     m_viewManager->openUrl(url, QString(), true, false);
648 }
649 
readOptions()650 void KateMainWindow::readOptions()
651 {
652     KSharedConfig::Ptr config = KSharedConfig::openConfig();
653 
654     const KConfigGroup generalGroup(config, "General");
655     m_modNotification = generalGroup.readEntry("Modified Notification", false);
656     m_modCloseAfterLast = generalGroup.readEntry("Close After Last", false);
657     KateApp::self()->documentManager()->setSaveMetaInfos(generalGroup.readEntry("Save Meta Infos", true));
658     KateApp::self()->documentManager()->setDaysMetaInfos(generalGroup.readEntry("Days Meta Infos", 30));
659 
660     KateApp::self()->stashManager()->setStashUnsavedChanges(generalGroup.readEntry("Stash unsaved file changes", false));
661     KateApp::self()->stashManager()->setStashNewUnsavedFiles(generalGroup.readEntry("Stash new unsaved files", true));
662 
663     m_paShowPath->setChecked(generalGroup.readEntry("Show Full Path in Title", false));
664     m_paShowStatusBar->setChecked(generalGroup.readEntry("Show Status Bar", true));
665     m_paShowMenuBar->setChecked(generalGroup.readEntry("Show Menu Bar", true));
666     m_paShowTabBar->setChecked(generalGroup.readEntry("Show Tab Bar", true));
667 
668     // emit signal to hide/show statusbars
669     toggleShowStatusBar();
670     toggleShowTabBar();
671 }
672 
saveOptions()673 void KateMainWindow::saveOptions()
674 {
675     KSharedConfig::Ptr config = KSharedConfig::openConfig();
676 
677     KConfigGroup generalGroup(config, "General");
678 
679     generalGroup.writeEntry("Save Meta Infos", KateApp::self()->documentManager()->getSaveMetaInfos());
680 
681     generalGroup.writeEntry("Days Meta Infos", KateApp::self()->documentManager()->getDaysMetaInfos());
682 
683     generalGroup.writeEntry("Show Full Path in Title", m_paShowPath->isChecked());
684     generalGroup.writeEntry("Show Status Bar", m_paShowStatusBar->isChecked());
685     generalGroup.writeEntry("Show Menu Bar", m_paShowMenuBar->isChecked());
686     generalGroup.writeEntry("Show Tab Bar", m_paShowTabBar->isChecked());
687 }
688 
toggleShowMenuBar(bool showMessage)689 void KateMainWindow::toggleShowMenuBar(bool showMessage)
690 {
691     if (m_paShowMenuBar->isChecked()) {
692         menuBar()->show();
693         if (m_viewManager->activeView() && m_viewManager->activeView()->contextMenu()) {
694             m_viewManager->activeView()->contextMenu()->removeAction(m_paShowMenuBar);
695         }
696     } else {
697         if (showMessage) {
698             const QString accel = m_paShowMenuBar->shortcut().toString();
699             KMessageBox::information(this,
700                                      i18n("This will hide the menu bar completely."
701                                           " You can show it again by typing %1.",
702                                           accel),
703                                      i18n("Hide menu bar"),
704                                      QStringLiteral("HideMenuBarWarning"));
705         }
706         menuBar()->hide();
707         if (m_viewManager->activeView() && m_viewManager->activeView()->contextMenu()) {
708             m_viewManager->activeView()->contextMenu()->addAction(m_paShowMenuBar);
709         }
710     }
711 }
712 
toggleShowStatusBar()713 void KateMainWindow::toggleShowStatusBar()
714 {
715     Q_EMIT statusBarToggled();
716 }
717 
showStatusBar()718 bool KateMainWindow::showStatusBar()
719 {
720     return m_paShowStatusBar->isChecked();
721 }
722 
toggleShowTabBar()723 void KateMainWindow::toggleShowTabBar()
724 {
725     Q_EMIT tabBarToggled();
726 }
727 
showTabBar()728 bool KateMainWindow::showTabBar()
729 {
730     return m_paShowTabBar->isChecked();
731 }
732 
slotWindowActivated()733 void KateMainWindow::slotWindowActivated()
734 {
735     if (m_viewManager->activeView()) {
736         updateCaption(m_viewManager->activeView()->document());
737     }
738 
739     // show view manager in any case
740     if (m_mainStackedWidget->currentWidget() != m_viewManager) {
741         m_mainStackedWidget->setCurrentWidget(m_viewManager);
742     }
743 
744     // update proxy
745     centralWidget()->setFocusProxy(m_viewManager->activeView());
746 }
747 
slotUpdateOpenWith()748 void KateMainWindow::slotUpdateOpenWith()
749 {
750     if (m_viewManager->activeView()) {
751         documentOpenWith->setEnabled(!m_viewManager->activeView()->document()->url().isEmpty());
752     } else {
753         documentOpenWith->setEnabled(false);
754     }
755 }
756 
slotUpdateActionsNeedingUrl()757 void KateMainWindow::slotUpdateActionsNeedingUrl()
758 {
759     auto &&view = viewManager()->activeView();
760     const bool hasUrl = view && !view->document()->url().isEmpty();
761 
762     action("file_copy_filepath")->setEnabled(hasUrl);
763     action("file_open_containing_folder")->setEnabled(hasUrl);
764     action("file_rename")->setEnabled(hasUrl);
765     action("file_delete")->setEnabled(hasUrl);
766     action("file_properties")->setEnabled(hasUrl);
767 }
768 
dragEnterEvent(QDragEnterEvent * event)769 void KateMainWindow::dragEnterEvent(QDragEnterEvent *event)
770 {
771     if (!event->mimeData()) {
772         return;
773     }
774     const bool accept = event->mimeData()->hasUrls() || event->mimeData()->hasText();
775     event->setAccepted(accept);
776 }
777 
dropEvent(QDropEvent * event)778 void KateMainWindow::dropEvent(QDropEvent *event)
779 {
780     slotDropEvent(event);
781 }
782 
slotDropEvent(QDropEvent * event)783 void KateMainWindow::slotDropEvent(QDropEvent *event)
784 {
785     if (event->mimeData() == nullptr) {
786         return;
787     }
788 
789     //
790     // are we dropping files?
791     //
792 
793     if (event->mimeData()->hasUrls()) {
794         QList<QUrl> textlist = event->mimeData()->urls();
795 
796         // Try to get the KTextEditor::View that sent this, and activate it, so that the file opens in the
797         // view where it was dropped
798         KTextEditor::View *kVsender = qobject_cast<KTextEditor::View *>(QObject::sender());
799         if (kVsender != nullptr) {
800             QWidget *parent = kVsender->parentWidget();
801             if (parent != nullptr) {
802                 KateViewSpace *vs = qobject_cast<KateViewSpace *>(parent->parentWidget());
803                 if (vs != nullptr) {
804                     m_viewManager->setActiveSpace(vs);
805                 }
806             }
807         }
808 
809         for (const QUrl &url : qAsConst(textlist)) {
810             // if url has no file component, try and recursively scan dir
811             KFileItem kitem(url);
812             kitem.setDelayedMimeTypes(true);
813             if (kitem.isDir()) {
814                 if (KMessageBox::questionYesNo(this,
815                                                i18n("You dropped the directory %1 into Kate. "
816                                                     "Do you want to load all files contained in it ?",
817                                                     url.url()),
818                                                i18n("Load files recursively?"))
819                     == KMessageBox::Yes) {
820                     KIO::ListJob *list_job = KIO::listRecursive(url, KIO::DefaultFlags, false);
821                     connect(list_job, &KIO::ListJob::entries, this, &KateMainWindow::slotListRecursiveEntries);
822                 }
823             } else {
824                 m_viewManager->openUrl(url);
825             }
826         }
827     }
828     //
829     // or are we dropping text?
830     //
831     else if (event->mimeData()->hasText()) {
832         KTextEditor::Document *doc = KateApp::self()->documentManager()->createDoc();
833         doc->setText(event->mimeData()->text());
834         m_viewManager->activateView(doc);
835     }
836 }
837 
slotListRecursiveEntries(KIO::Job * job,const KIO::UDSEntryList & list)838 void KateMainWindow::slotListRecursiveEntries(KIO::Job *job, const KIO::UDSEntryList &list)
839 {
840     const QUrl dir = static_cast<KIO::SimpleJob *>(job)->url();
841     for (const KIO::UDSEntry &entry : list) {
842         if (!entry.isDir()) {
843             QUrl url(dir);
844             url = url.adjusted(QUrl::StripTrailingSlash);
845             url.setPath(url.path() + QLatin1Char('/') + entry.stringValue(KIO::UDSEntry::UDS_NAME));
846             m_viewManager->openUrl(url);
847         }
848     }
849 }
850 
editKeys()851 void KateMainWindow::editKeys()
852 {
853     KShortcutsDialog dlg(KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsAllowed, this);
854 
855     const QList<KXMLGUIClient *> clients = guiFactory()->clients();
856 
857     for (KXMLGUIClient *client : clients) {
858         // FIXME there appear to be invalid clients after session switching
859         //     qCDebug(LOG_KATE)<<"adding client to shortcut editor";
860         //     qCDebug(LOG_KATE)<<client;
861         //     qCDebug(LOG_KATE)<<client->actionCollection();
862         //     qCDebug(LOG_KATE)<<client->componentData().aboutData();
863         //     qCDebug(LOG_KATE)<<client->componentData().aboutData()->programName();
864         dlg.addCollection(client->actionCollection(), client->componentName());
865     }
866     dlg.configure();
867 
868     // reloadXML gui clients, to ensure all clients are up-to-date
869     reloadXmlGui();
870 }
871 
openUrl(const QString & name)872 void KateMainWindow::openUrl(const QString &name)
873 {
874     m_viewManager->openUrl(QUrl(name));
875 }
876 
slotConfigure()877 void KateMainWindow::slotConfigure()
878 {
879     showPluginConfigPage(nullptr, 0);
880 }
881 
showPluginConfigPage(KTextEditor::Plugin * configpageinterface,int id)882 bool KateMainWindow::showPluginConfigPage(KTextEditor::Plugin *configpageinterface, int id)
883 {
884     if (!m_viewManager->activeView()) {
885         return false;
886     }
887 
888     KateConfigDialog *dlg = new KateConfigDialog(this);
889     if (configpageinterface) {
890         dlg->showAppPluginPage(configpageinterface, id);
891     }
892 
893     if (dlg->exec() == QDialog::Accepted) {
894         m_fileOpenRecent->setMaxItems(KateConfigDialog::recentFilesMaxCount());
895     }
896 
897     delete dlg;
898 
899     m_viewManager->reactivateActiveView(); // gui (toolbars...) needs to be updated, because
900     // of possible changes that the configure dialog
901     // could have done on it, specially for plugins.
902 
903     return true;
904 }
905 
activeDocumentUrl()906 QUrl KateMainWindow::activeDocumentUrl()
907 {
908     // anders: i make this one safe, as it may be called during
909     // startup (by the file selector)
910     KTextEditor::View *v = m_viewManager->activeView();
911     if (v) {
912         return v->document()->url();
913     }
914     return QUrl();
915 }
916 
mSlotFixOpenWithMenu()917 void KateMainWindow::mSlotFixOpenWithMenu()
918 {
919     // dh: in bug #307699, this slot is called when launching the Kate application
920     // unfortunately, no one ever could reproduce except users.
921     KTextEditor::View *activeView = m_viewManager->activeView();
922     if (!activeView) {
923         return;
924     }
925 
926     // cleanup menu
927     QMenu *menu = documentOpenWith->menu();
928     menu->clear();
929 
930     // get a list of appropriate services.
931     QMimeDatabase db;
932     QMimeType mime = db.mimeTypeForName(activeView->document()->mimeType());
933     // qCDebug(LOG_KATE) << "mime type: " << mime.name();
934 
935     QAction *a = nullptr;
936     const KService::List offers = KApplicationTrader::queryByMimeType(mime.name());
937     // add all default open-with-actions except "Kate"
938     for (const auto &service : offers) {
939         if (service->name() == QLatin1String("Kate")) {
940             continue;
941         }
942         a = menu->addAction(QIcon::fromTheme(service->icon()), service->name());
943         a->setData(service->entryPath());
944     }
945     // append "Other..." to call the KDE "open with" dialog.
946     a = documentOpenWith->menu()->addAction(i18n("&Other..."));
947     a->setData(QString());
948 }
949 
slotOpenWithMenuAction(QAction * a)950 void KateMainWindow::slotOpenWithMenuAction(QAction *a)
951 {
952     const QList<QUrl> list({m_viewManager->activeView()->document()->url()});
953     KService::Ptr app = KService::serviceByDesktopPath(a->data().toString());
954     // If app is null, ApplicationLauncherJob will invoke the open-with dialog
955     auto *job = new KIO::ApplicationLauncherJob(app);
956     job->setUrls(list);
957     job->setUiDelegate(new KIO::JobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, this));
958     job->start();
959 }
960 
pluginHelp()961 void KateMainWindow::pluginHelp()
962 {
963     KHelpClient::invokeHelp(QString(), QStringLiteral("kate-plugins"));
964 }
965 
slotFullScreen(bool t)966 void KateMainWindow::slotFullScreen(bool t)
967 {
968     KToggleFullScreenAction::setFullScreen(this, t);
969     QMenuBar *mb = menuBar();
970     if (t) {
971         QToolButton *b = new QToolButton(mb);
972         b->setDefaultAction(m_showFullScreenAction);
973         b->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Ignored));
974         b->setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
975         mb->setCornerWidget(b, Qt::TopRightCorner);
976         b->setVisible(true);
977         b->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
978     } else {
979         QWidget *w = mb->cornerWidget(Qt::TopRightCorner);
980         if (w) {
981             w->deleteLater();
982         }
983     }
984 }
985 
showModOnDiskPrompt(ModOnDiskMode mode)986 bool KateMainWindow::showModOnDiskPrompt(ModOnDiskMode mode)
987 {
988     const auto documents = KateApp::self()->documentManager()->documentList();
989     DocVector list;
990     list.reserve(documents.size());
991     for (auto doc : documents) {
992         if (KateApp::self()->documentManager()->documentInfo(doc)->modifiedOnDisc && (doc->isModified() || mode == PromptAll)) {
993             list.append(doc);
994         }
995     }
996 
997     if (!list.isEmpty() && !m_modignore) {
998         KateMwModOnHdDialog mhdlg(list, this);
999         m_modignore = true;
1000         bool res = mhdlg.exec();
1001         m_modignore = false;
1002 
1003         return res;
1004     }
1005     return true;
1006 }
1007 
slotDocumentCreated(KTextEditor::Document * doc)1008 void KateMainWindow::slotDocumentCreated(KTextEditor::Document *doc)
1009 {
1010     connect(doc, &KTextEditor::Document::modifiedChanged, this, QOverload<KTextEditor::Document *>::of(&KateMainWindow::updateCaption));
1011     connect(doc, &KTextEditor::Document::readWriteChanged, this, QOverload<KTextEditor::Document *>::of(&KateMainWindow::updateCaption));
1012     connect(doc, &KTextEditor::Document::documentNameChanged, this, QOverload<KTextEditor::Document *>::of(&KateMainWindow::updateCaption));
1013     connect(doc, &KTextEditor::Document::documentUrlChanged, this, QOverload<KTextEditor::Document *>::of(&KateMainWindow::updateCaption));
1014     connect(doc, &KTextEditor::Document::documentUrlChanged, this, &KateMainWindow::slotUpdateOpenWith);
1015     connect(doc, &KTextEditor::Document::documentUrlChanged, this, &KateMainWindow::slotUpdateActionsNeedingUrl);
1016 
1017     updateCaption(doc);
1018 }
1019 
updateCaption()1020 void KateMainWindow::updateCaption()
1021 {
1022     if (m_viewManager->activeView()) {
1023         updateCaption(m_viewManager->activeView()->document());
1024     }
1025 }
1026 
updateCaption(KTextEditor::Document * doc)1027 void KateMainWindow::updateCaption(KTextEditor::Document *doc)
1028 {
1029     if (!m_viewManager->activeView()) {
1030         setCaption(QString(), false);
1031         return;
1032     }
1033 
1034     // block signals from inactive docs
1035     if (m_viewManager->activeView()->document() != doc) {
1036         return;
1037     }
1038 
1039     QString c;
1040     if (m_viewManager->activeView()->document()->url().isEmpty() || (!m_paShowPath || !m_paShowPath->isChecked())) {
1041         c = m_viewManager->activeView()->document()->documentName();
1042     } else {
1043         c = m_viewManager->activeView()->document()->url().toString(QUrl::PreferLocalFile);
1044 
1045         const QString homePath = QDir::homePath();
1046         if (c.startsWith(homePath)) {
1047             c = QLatin1String("~") + c.right(c.length() - homePath.length());
1048         }
1049     }
1050 
1051     QString sessName = KateApp::self()->sessionManager()->activeSession()->name();
1052     if (!sessName.isEmpty()) {
1053         sessName = QStringLiteral("%1: ").arg(sessName);
1054     }
1055 
1056     QString readOnlyCaption;
1057     if (!m_viewManager->activeView()->document()->isReadWrite()) {
1058         readOnlyCaption = i18n(" [read only]");
1059     }
1060 
1061     setCaption(sessName + c + readOnlyCaption + QStringLiteral(" [*]"), m_viewManager->activeView()->document()->isModified());
1062 }
1063 
saveProperties(KConfigGroup & config)1064 void KateMainWindow::saveProperties(KConfigGroup &config)
1065 {
1066     saveSession(config);
1067 
1068     // store all plugin view states
1069     int id = KateApp::self()->mainWindowID(this);
1070     const auto plugins = KateApp::self()->pluginManager()->pluginList();
1071     for (const KatePluginInfo &item : plugins) {
1072         if (item.plugin && pluginViews().contains(item.plugin)) {
1073             if (auto interface = qobject_cast<KTextEditor::SessionConfigInterface *>(pluginViews().value(item.plugin))) {
1074                 KConfigGroup group(config.config(), QStringLiteral("Plugin:%1:MainWindow:%2").arg(item.saveName()).arg(id));
1075                 interface->writeSessionConfig(group);
1076             }
1077         }
1078     }
1079 
1080     saveOpenRecent(config.config());
1081     m_viewManager->saveViewConfiguration(config);
1082 }
1083 
readProperties(const KConfigGroup & config)1084 void KateMainWindow::readProperties(const KConfigGroup &config)
1085 {
1086     // KDE5: TODO startRestore should take a const KConfigBase*, or even just a const KConfigGroup&,
1087     // but this propagates down to interfaces/kate/plugin.h so all plugins have to be ported
1088     KConfigBase *configBase = const_cast<KConfig *>(config.config());
1089     startRestore(configBase, config.name());
1090 
1091     // perhaps enable plugin guis
1092     KateApp::self()->pluginManager()->enableAllPluginsGUI(this, configBase);
1093 
1094     finishRestore();
1095 
1096     loadOpenRecent(config.config());
1097     m_viewManager->restoreViewConfiguration(config);
1098 }
1099 
saveOpenRecent(KConfig * config)1100 void KateMainWindow::saveOpenRecent(KConfig *config)
1101 {
1102     m_fileOpenRecent->saveEntries(KConfigGroup(config, "Recent Files"));
1103 }
1104 
loadOpenRecent(const KConfig * config)1105 void KateMainWindow::loadOpenRecent(const KConfig *config)
1106 {
1107     m_fileOpenRecent->loadEntries(KConfigGroup(config, "Recent Files"));
1108 }
1109 
saveGlobalProperties(KConfig * sessionConfig)1110 void KateMainWindow::saveGlobalProperties(KConfig *sessionConfig)
1111 {
1112     KateApp::self()->documentManager()->saveDocumentList(sessionConfig);
1113 
1114     KConfigGroup cg(sessionConfig, "General");
1115     cg.writeEntry("Last Session", KateApp::self()->sessionManager()->activeSession()->name());
1116 
1117     // save plugin config !!
1118     KateApp::self()->pluginManager()->writeConfig(sessionConfig);
1119 }
1120 
saveWindowConfig(const KConfigGroup & _config)1121 void KateMainWindow::saveWindowConfig(const KConfigGroup &_config)
1122 {
1123     KConfigGroup config(_config);
1124     saveMainWindowSettings(config);
1125     config.writeEntry("WindowState", static_cast<int>(windowState()));
1126     config.sync();
1127 }
1128 
restoreWindowConfig(const KConfigGroup & config)1129 void KateMainWindow::restoreWindowConfig(const KConfigGroup &config)
1130 {
1131     setWindowState(Qt::WindowNoState);
1132     applyMainWindowSettings(config);
1133     setWindowState(QFlags<Qt::WindowState>(config.readEntry("WindowState", int(Qt::WindowActive))));
1134 }
1135 
slotUpdateBottomViewBar()1136 void KateMainWindow::slotUpdateBottomViewBar()
1137 {
1138     // qCDebug(LOG_KATE)<<"slotUpdateHorizontalViewBar()"<<endl;
1139     KTextEditor::View *view = m_viewManager->activeView();
1140     BarState bs = m_bottomViewBarMapping[view];
1141     if (bs.bar() && bs.state()) {
1142         m_bottomContainerStack->setCurrentWidget(bs.bar());
1143         m_bottomContainerStack->currentWidget()->show();
1144         m_bottomViewBarContainer->show();
1145     } else {
1146         QWidget *wid = m_bottomContainerStack->currentWidget();
1147         if (wid) {
1148             wid->hide();
1149         }
1150         // qCDebug(LOG_KATE)<<wid<<"hiding container"<<endl;
1151         m_bottomViewBarContainer->hide();
1152     }
1153 }
1154 
queueModifiedOnDisc(KTextEditor::Document * doc)1155 void KateMainWindow::queueModifiedOnDisc(KTextEditor::Document *doc)
1156 {
1157     if (!m_modNotification) {
1158         return;
1159     }
1160 
1161     KateDocumentInfo *docInfo = KateApp::self()->documentManager()->documentInfo(doc);
1162     if (!docInfo) {
1163         return;
1164     }
1165     bool modOnDisk = static_cast<uint>(docInfo->modifiedOnDisc);
1166 
1167     if (s_modOnHdDialog == nullptr && modOnDisk) {
1168         DocVector list;
1169         list.append(doc);
1170 
1171         s_modOnHdDialog = new KateMwModOnHdDialog(list, this);
1172         m_modignore = true;
1173         connect(s_modOnHdDialog, &KateMwModOnHdDialog::requestOpenDiffDocument, this, [this](const QUrl &url) {
1174             viewManager()->openUrl(url, QString(), true, true);
1175         });
1176         s_modOnHdDialog->exec();
1177         delete s_modOnHdDialog; // s_modOnHdDialog is set to 0 in destructor of KateMwModOnHdDialog (jowenn!!!)
1178         m_modignore = false;
1179     } else if (s_modOnHdDialog != nullptr) {
1180         s_modOnHdDialog->addDocument(doc);
1181     }
1182 }
1183 
event(QEvent * e)1184 bool KateMainWindow::event(QEvent *e)
1185 {
1186     if (e->type() == QEvent::ShortcutOverride) {
1187         QKeyEvent *k = static_cast<QKeyEvent *>(e);
1188         Q_EMIT unhandledShortcutOverride(k);
1189 
1190         if (k->key() == Qt::Key_Escape && k->modifiers() == Qt::NoModifier && !m_toolViewOutput->isHidden()) {
1191             hideToolView(m_toolViewOutput);
1192         }
1193     }
1194     return KateMDI::MainWindow::event(e);
1195 }
1196 
pluginView(const QString & name)1197 QObject *KateMainWindow::pluginView(const QString &name)
1198 {
1199     KTextEditor::Plugin *plugin = KateApp::self()->pluginManager()->plugin(name);
1200     if (!plugin) {
1201         return nullptr;
1202     }
1203 
1204     return m_pluginViews.contains(plugin) ? m_pluginViews.value(plugin) : nullptr;
1205 }
1206 
addWidgetAsTab(QWidget * widget)1207 void KateMainWindow::addWidgetAsTab(QWidget *widget)
1208 {
1209     auto vs = m_viewManager->activeViewSpace();
1210     vs->addWidgetAsTab(widget);
1211 }
1212 
mousePressEvent(QMouseEvent * e)1213 void KateMainWindow::mousePressEvent(QMouseEvent *e)
1214 {
1215     switch (e->button()) {
1216     case Qt::ForwardButton:
1217         slotFocusNextTab();
1218         break;
1219     case Qt::BackButton:
1220         slotFocusPrevTab();
1221         break;
1222     default:;
1223     }
1224 }
1225 
slotFocusPrevTab()1226 void KateMainWindow::slotFocusPrevTab()
1227 {
1228     if (m_viewManager->activeViewSpace()) {
1229         m_viewManager->activeViewSpace()->focusPrevTab();
1230     }
1231 }
1232 
slotFocusNextTab()1233 void KateMainWindow::slotFocusNextTab()
1234 {
1235     if (m_viewManager->activeViewSpace()) {
1236         m_viewManager->activeViewSpace()->focusNextTab();
1237     }
1238 }
1239 
slotQuickOpen()1240 void KateMainWindow::slotQuickOpen()
1241 {
1242     /**
1243      * show quick open and pass focus to it
1244      */
1245     KateQuickOpen quickOpen(this);
1246     centralWidget()->setFocusProxy(&quickOpen);
1247     quickOpen.exec();
1248 }
1249 
slotCommandBarOpen()1250 void KateMainWindow::slotCommandBarOpen()
1251 {
1252     QList<KActionCollection *> actionCollections;
1253 
1254     auto clients = guiFactory()->clients();
1255     int actionsCount = 0;
1256     for (const KXMLGUIClient *c : clients) {
1257         if (!c) {
1258             continue;
1259         }
1260         if (auto collection = c->actionCollection()) {
1261             actionCollections.append(collection);
1262             actionsCount += collection->count();
1263         }
1264     }
1265 
1266     KateCommandBar commandBar(this);
1267     commandBar.setLastUsedCmdBarActions(m_lastUsedCmdBarActions);
1268     commandBar.updateBar(actionCollections, actionsCount);
1269     centralWidget()->setFocusProxy(&commandBar);
1270     commandBar.exec();
1271     m_lastUsedCmdBarActions = commandBar.lastUsedCmdBarActions();
1272 }
1273 
createToolView(KTextEditor::Plugin * plugin,const QString & identifier,KTextEditor::MainWindow::ToolViewPosition pos,const QIcon & icon,const QString & text)1274 QWidget *KateMainWindow::createToolView(KTextEditor::Plugin *plugin,
1275                                         const QString &identifier,
1276                                         KTextEditor::MainWindow::ToolViewPosition pos,
1277                                         const QIcon &icon,
1278                                         const QString &text)
1279 {
1280     return KateMDI::MainWindow::createToolView(plugin, identifier, static_cast<KMultiTabBar::KMultiTabBarPosition>(pos), icon, text);
1281 }
1282 
moveToolView(QWidget * widget,KTextEditor::MainWindow::ToolViewPosition pos)1283 bool KateMainWindow::moveToolView(QWidget *widget, KTextEditor::MainWindow::ToolViewPosition pos)
1284 {
1285     if (!qobject_cast<KateMDI::ToolView *>(widget)) {
1286         return false;
1287     }
1288 
1289     return KateMDI::MainWindow::moveToolView(qobject_cast<KateMDI::ToolView *>(widget), static_cast<KMultiTabBar::KMultiTabBarPosition>(pos));
1290 }
1291 
showToolView(QWidget * widget)1292 bool KateMainWindow::showToolView(QWidget *widget)
1293 {
1294     if (!qobject_cast<KateMDI::ToolView *>(widget)) {
1295         return false;
1296     }
1297 
1298     return KateMDI::MainWindow::showToolView(qobject_cast<KateMDI::ToolView *>(widget));
1299 }
1300 
hideToolView(QWidget * widget)1301 bool KateMainWindow::hideToolView(QWidget *widget)
1302 {
1303     if (!qobject_cast<KateMDI::ToolView *>(widget)) {
1304         return false;
1305     }
1306 
1307     return KateMDI::MainWindow::hideToolView(qobject_cast<KateMDI::ToolView *>(widget));
1308 }
1309 
addRecentOpenedFile(const QUrl & url)1310 void KateMainWindow::addRecentOpenedFile(const QUrl &url)
1311 {
1312     // skip non-existing urls for untitled documents
1313     if (url.isEmpty()) {
1314         return;
1315     }
1316     // skip files in /subltmp
1317     if (url.path().startsWith(QDir::tempPath())) {
1318         return;
1319     }
1320 
1321     // to our local list, aka menu
1322     m_fileOpenRecent->addUrl(url);
1323 
1324     // to the global "Recent Document Menu", see bug 420504
1325     KRecentDocument::add(url);
1326 }
1327