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