diff --git a/kate/CMakeLists.txt b/kate/CMakeLists.txt index 665961974..107c77270 100644 --- a/kate/CMakeLists.txt +++ b/kate/CMakeLists.txt @@ -1,155 +1,154 @@ # # The Kate Application # project(kate) # Load the frameworks we need find_package(KF5 REQUIRED COMPONENTS DBusAddons GuiAddons) # includes include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/session ${CMAKE_CURRENT_SOURCE_DIR}/qtsingleapplication ) # collect the needed source files set (KATE_LIBRARY_SRCS kateappadaptor.cpp kateapp.cpp kateconfigdialog.cpp kateconfigplugindialogpage.cpp katedocmanager.cpp katefileactions.cpp katemainwindow.cpp katepluginmanager.cpp kateviewmanager.cpp kateviewspace.cpp katesavemodifieddialog.cpp katemwmodonhddialog.cpp katecolorschemechooser.cpp katequickopenmodel.cpp katetabbutton.cpp katetabbar.cpp # session - session/katesessionchooser.cpp session/katesessionsaction.cpp session/katesessionmanager.cpp session/katesessionmanagedialog.cpp - session/katesessionopendialog.cpp session/katesession.cpp katemdi.cpp katerunninginstanceinfo.cpp katequickopen.cpp katewaiter.h ) ki18n_wrap_ui(KATE_LIBRARY_SRCS ui/sessionconfigwidget.ui + session/katesessionmanagedialog.ui ) qt5_add_resources( KATE_LIBRARY_SRCS data/kate.qrc ) add_library(kdeinit_kate STATIC ${KATE_LIBRARY_SRCS}) target_link_libraries(kdeinit_kate PUBLIC KF5::TextEditor KF5::I18n KF5::IconThemes KF5::WindowSystem KF5::GuiAddons KF5::DBusAddons KF5::Crash) if(KF5Activities_FOUND) target_link_libraries(kdeinit_kate PUBLIC KF5::Activities) endif() generate_export_header(kdeinit_kate EXPORT_FILE_NAME kateprivate_export.h EXPORT_MACRO_NAME KATE_TESTS_EXPORT ) # collect icons set(KATE_ICONS_PNG ${CMAKE_CURRENT_SOURCE_DIR}/icons/16-apps-kate.png ${CMAKE_CURRENT_SOURCE_DIR}/icons/22-apps-kate.png ${CMAKE_CURRENT_SOURCE_DIR}/icons/32-apps-kate.png ${CMAKE_CURRENT_SOURCE_DIR}/icons/48-apps-kate.png ${CMAKE_CURRENT_SOURCE_DIR}/icons/64-apps-kate.png ${CMAKE_CURRENT_SOURCE_DIR}/icons/128-apps-kate.png ) set(KATE_ICONS_SVG ${CMAKE_CURRENT_SOURCE_DIR}/icons/sc-apps-kate.svgz ) # application only sources set (KATE_APP_SRCS main.cpp ) # use single application instead of dbus on mac + windows if (APPLE OR WIN32) set(singleapp_SRCS qtsingleapplication/qtlocalpeer.cpp qtsingleapplication/qtsingleapplication.cpp qtsingleapplication/qtlockedfile.cpp ) if(WIN32) set(singleapp_SRCS ${singleapp_SRCS} qtsingleapplication/qtlockedfile_win.cpp) else() set(singleapp_SRCS ${singleapp_SRCS} qtsingleapplication/qtlockedfile_unix.cpp) endif() add_definitions("-DUSE_QT_SINGLE_APP") set(KATE_APP_SRCS ${KATE_APP_SRCS} ${singleapp_SRCS}) endif() # add icons to application sources, to have them bundled ecm_add_app_icon(KATE_APP_SRCS ICONS ${KATE_ICONS_PNG}) # create executable add_executable(kate ${KATE_APP_SRCS}) target_link_libraries(kate kdeinit_kate) # own plist magic for mac os if(APPLE) # own plist template set_target_properties (kate PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/data/MacOSXBundleInfo.plist.in) # the MacOSX bundle display name property (CFBundleDisplayName) is not currently supported by cmake, # so has to be set for all targets in this cmake file set(MACOSX_BUNDLE_DISPLAY_NAME Kate) set_target_properties(kate PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER "org.kde.Kate") set_target_properties(kate PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Kate") set_target_properties(kate PROPERTIES MACOSX_BUNDLE_DISPLAY_NAME "Kate") set_target_properties(kate PROPERTIES MACOSX_BUNDLE_INFO_STRING "Kate - Advanced Text Editor") set_target_properties(kate PROPERTIES MACOSX_BUNDLE_LONG_VERSION_STRING "Kate ${KDE_APPLICATIONS_VERSION}") set_target_properties(kate PROPERTIES MACOSX_BUNDLE_SHORT_VERSION_STRING "${KDE_APPLICATIONS_VERSION_MAJOR}.${KDE_APPLICATIONS_VERSION_MINOR}") set_target_properties(kate PROPERTIES MACOSX_BUNDLE_BUNDLE_VERSION "${KDE_APPLICATIONS_VERSION}") set_target_properties(kate PROPERTIES MACOSX_BUNDLE_COPYRIGHT "2000-2016 The Kate Authors") endif() # install executable install(TARGETS kate ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) # desktop file install(PROGRAMS ${CMAKE_CURRENT_SOURCE_DIR}/data/org.kde.kate.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}) # appdata install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/data/org.kde.kate.appdata.xml DESTINATION ${CMAKE_INSTALL_METAINFODIR}) # install icons ecm_install_icons(ICONS ${KATE_ICONS_PNG} ${KATE_ICONS_SVG} DESTINATION ${ICON_INSTALL_DIR} THEME hicolor) # automatic unit tests if (BUILD_TESTING) add_subdirectory(autotests) endif() diff --git a/kate/data/kateui.rc b/kate/data/kateui.rc index 39e12660f..9073f46b7 100644 --- a/kate/data/kateui.rc +++ b/kate/data/kateui.rc @@ -1,159 +1,157 @@ - + &File &Edit &View Split View &Tools Sess&ions - + - - &Settings &Help Main Toolbar &Status Bar Items diff --git a/kate/katemainwindow.cpp b/kate/katemainwindow.cpp index 53e9dadd3..b51bb00e3 100644 --- a/kate/katemainwindow.cpp +++ b/kate/katemainwindow.cpp @@ -1,1266 +1,1261 @@ /* This file is part of the KDE project Copyright (C) 2001 Christoph Cullmann Copyright (C) 2001 Joseph Wenninger Copyright (C) 2001 Anders Lund Copyright (C) 2007 Flavio Castelli This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ //BEGIN Includes #include "katemainwindow.h" #include "kateconfigdialog.h" #include "katedocmanager.h" #include "katepluginmanager.h" #include "kateconfigplugindialogpage.h" #include "kateviewmanager.h" #include "kateapp.h" #include "katesavemodifieddialog.h" #include "katemwmodonhddialog.h" #include "katesessionsaction.h" #include "katesessionmanager.h" #include "kateviewspace.h" #include "katequickopen.h" #include "kateupdatedisabler.h" #include "katedebug.h" #include "katecolorschemechooser.h" #include "katefileactions.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //END KateMwModOnHdDialog *KateMainWindow::s_modOnHdDialog = nullptr; KateContainerStackedLayout::KateContainerStackedLayout(QWidget *parent) : QStackedLayout(parent) {} QSize KateContainerStackedLayout::sizeHint() const { if (currentWidget()) { return currentWidget()->sizeHint(); } return QStackedLayout::sizeHint(); } QSize KateContainerStackedLayout::minimumSize() const { if (currentWidget()) { return currentWidget()->minimumSize(); } return QStackedLayout::minimumSize(); } KateMainWindow::KateMainWindow(KConfig *sconfig, const QString &sgroup) : KateMDI::MainWindow(nullptr) , m_modignore(false) , m_wrapper(new KTextEditor::MainWindow(this)) { /** * we don't want any flicker here */ KateUpdateDisabler disableUpdates (this); /** * get and set config revision */ static const int currentConfigRevision = 10; const int readConfigRevision = KConfigGroup(KSharedConfig::openConfig(), "General").readEntry("Config Revision", 0); KConfigGroup(KSharedConfig::openConfig(), "General").writeEntry("Config Revision", currentConfigRevision); const bool firstStart = readConfigRevision < currentConfigRevision; // start session restore if needed startRestore(sconfig, sgroup); // setup most important actions first, needed by setupMainWindow setupImportantActions(); // setup the most important widgets setupMainWindow(); // setup the actions setupActions(); setStandardToolBarMenuEnabled(true); setXMLFile(QStringLiteral("kateui.rc")); createShellGUI(true); //qCDebug(LOG_KATE) << "****************************************************************************" << sconfig; // register mainwindow in app KateApp::self()->addMainWindow(this); // enable plugin guis KateApp::self()->pluginManager()->enableAllPluginsGUI(this, sconfig); // caption update Q_FOREACH (auto doc, KateApp::self()->documentManager()->documentList()) { slotDocumentCreated(doc); } connect(KateApp::self()->documentManager(), SIGNAL(documentCreated(KTextEditor::Document*)), this, SLOT(slotDocumentCreated(KTextEditor::Document*))); readOptions(); if (sconfig) { m_viewManager->restoreViewConfiguration(KConfigGroup(sconfig, sgroup)); } finishRestore(); m_fileOpenRecent->loadEntries(KConfigGroup(sconfig, "Recent Files")); setAcceptDrops(true); connect(KateApp::self()->sessionManager(), SIGNAL(sessionChanged()), this, SLOT(updateCaption())); connect(this, SIGNAL(sigShowPluginConfigPage(KTextEditor::Plugin*,uint)), this, SLOT(showPluginConfigPage(KTextEditor::Plugin*,uint))); // prior to this there was (possibly) no view, therefore not context menu. // Hence, we have to take care of the menu bar here toggleShowMenuBar(false); // on first start: deactivate toolbar if (firstStart) toolBar(QLatin1String("mainToolBar"))->hide(); // pass focus to first view! if (m_viewManager->activeView()) m_viewManager->activeView()->setFocus(); } KateMainWindow::~KateMainWindow() { // first, save our fallback window size ;) KConfigGroup cfg(KSharedConfig::openConfig(), "MainWindow"); KWindowConfig::saveWindowSize(windowHandle(), cfg); // save other options ;=) saveOptions(); // unregister mainwindow in app KateApp::self()->removeMainWindow(this); // disable all plugin guis, delete all pluginViews KateApp::self()->pluginManager()->disableAllPluginsGUI(this); // delete the view manager, before KateMainWindow's wrapper is dead delete m_viewManager; m_viewManager = nullptr; // kill the wrapper object, now that all views are dead delete m_wrapper; m_wrapper = nullptr; } QSize KateMainWindow::sizeHint() const { /** * have some useful size hint, else we have mini windows per default */ return (QSize(640, 480).expandedTo(minimumSizeHint())); } void KateMainWindow::setupImportantActions() { m_paShowStatusBar = KStandardAction::showStatusbar(this, SLOT(toggleShowStatusBar()), actionCollection()); m_paShowStatusBar->setWhatsThis(i18n("Use this command to show or hide the view's statusbar")); m_paShowMenuBar = KStandardAction::showMenubar(this, SLOT(toggleShowMenuBar()), actionCollection()); m_paShowTabBar = new KToggleAction(i18n("Show &Tabs"), this); actionCollection()->addAction(QStringLiteral("settings_show_tab_bar"), m_paShowTabBar); connect(m_paShowTabBar, SIGNAL(toggled(bool)), this, SLOT(toggleShowTabBar())); m_paShowTabBar->setWhatsThis(i18n("Use this command to show or hide the tabs for the views")); m_paShowPath = new KToggleAction(i18n("Sho&w Path in Titlebar"), this); actionCollection()->addAction(QStringLiteral("settings_show_full_path"), m_paShowPath); connect(m_paShowPath, SIGNAL(toggled(bool)), this, SLOT(updateCaption())); m_paShowPath->setWhatsThis(i18n("Show the complete document path in the window caption")); // Load themes actionCollection()->addAction(QStringLiteral("colorscheme_menu"), new KateColorSchemeChooser(actionCollection())); QAction * a = actionCollection()->addAction(KStandardAction::Back, QStringLiteral("view_prev_tab")); a->setText(i18n("&Previous Tab")); a->setWhatsThis(i18n("Focus the previous tab.")); actionCollection()->setDefaultShortcuts(a, a->shortcuts() << KStandardShortcut::tabPrev()); connect(a, SIGNAL(triggered()), this, SLOT(slotFocusPrevTab())); a = actionCollection()->addAction(KStandardAction::Forward, QStringLiteral("view_next_tab")); a->setText(i18n("&Next Tab")); a->setWhatsThis(i18n("Focus the next tab.")); actionCollection()->setDefaultShortcuts(a, a->shortcuts() << KStandardShortcut::tabNext()); connect(a, SIGNAL(triggered()), this, SLOT(slotFocusNextTab())); // the quick open action is used by the KateViewSpace "quick open button" a = actionCollection()->addAction(QStringLiteral("view_quick_open")); a->setIcon(QIcon::fromTheme(QStringLiteral("quickopen"))); a->setText(i18n("&Quick Open")); actionCollection()->setDefaultShortcut(a, QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_O)); connect(a, SIGNAL(triggered()), this, SLOT(slotQuickOpen())); a->setWhatsThis(i18n("Open a form to quick open documents.")); } void KateMainWindow::setupMainWindow() { setToolViewStyle(KMultiTabBar::KDEV3ICON); /** * create central stacked widget with its children */ m_mainStackedWidget = new QStackedWidget(centralWidget()); centralWidget()->layout()->addWidget(m_mainStackedWidget); (static_cast(centralWidget()->layout()))->setStretchFactor(m_mainStackedWidget, 100); m_quickOpen = new KateQuickOpen(m_mainStackedWidget, this); m_mainStackedWidget->addWidget(m_quickOpen); m_viewManager = new KateViewManager(m_mainStackedWidget, this); m_mainStackedWidget->addWidget(m_viewManager); // make view manager default visible! m_mainStackedWidget->setCurrentWidget(m_viewManager); m_bottomViewBarContainer = new QWidget(centralWidget()); centralWidget()->layout()->addWidget(m_bottomViewBarContainer); m_bottomContainerStack = new KateContainerStackedLayout(m_bottomViewBarContainer); } void KateMainWindow::setupActions() { QAction *a; actionCollection()->addAction(KStandardAction::New, QStringLiteral("file_new"), m_viewManager, SLOT(slotDocumentNew())) ->setWhatsThis(i18n("Create a new document")); actionCollection()->addAction(KStandardAction::Open, QStringLiteral("file_open"), m_viewManager, SLOT(slotDocumentOpen())) ->setWhatsThis(i18n("Open an existing document for editing")); m_fileOpenRecent = KStandardAction::openRecent(m_viewManager, SLOT(openUrl(QUrl)), this); m_fileOpenRecent->setMaxItems(KateConfigDialog::recentFilesMaxCount()); actionCollection()->addAction(m_fileOpenRecent->objectName(), m_fileOpenRecent); m_fileOpenRecent->setWhatsThis(i18n("This lists files which you have opened recently, and allows you to easily open them again.")); a = actionCollection()->addAction(QStringLiteral("file_save_all")); a->setIcon(QIcon::fromTheme(QStringLiteral("document-save-all"))); a->setText(i18n("Save A&ll")); actionCollection()->setDefaultShortcut(a, QKeySequence(Qt::CTRL + Qt::Key_L)); connect(a, SIGNAL(triggered()), KateApp::self()->documentManager(), SLOT(saveAll())); a->setWhatsThis(i18n("Save all open, modified documents to disk.")); a = actionCollection()->addAction(QStringLiteral("file_reload_all")); a->setText(i18n("&Reload All")); connect(a, SIGNAL(triggered()), KateApp::self()->documentManager(), SLOT(reloadAll())); a->setWhatsThis(i18n("Reload all open documents.")); a = actionCollection()->addAction(QStringLiteral("file_copy_filepath")); a->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy"))); a->setText(i18n("Copy File &Path")); connect(a, &QAction::triggered, KateApp::self()->documentManager(), [this]() { auto&& view = viewManager()->activeView(); KateFileActions::copyFilePathToClipboard(view->document()); }); a->setWhatsThis(i18n("Copies the file path of the current file to clipboard.")); a = actionCollection()->addAction(QStringLiteral("file_open_containing_folder")); a->setIcon(QIcon::fromTheme(QStringLiteral("document-open-folder"))); a->setText(i18n("&Open Containing Folder")); connect(a, &QAction::triggered, KateApp::self()->documentManager(), [this]() { auto&& view = viewManager()->activeView(); KateFileActions::openContainingFolder(view->document()); }); a->setWhatsThis(i18n("Copies the file path of the current file to clipboard.")); a = actionCollection()->addAction(QStringLiteral("file_rename")); a->setIcon(QIcon::fromTheme(QStringLiteral("edit-rename"))); a->setText(i18nc("@action:inmenu", "Rename File...")); connect(a, &QAction::triggered, KateApp::self()->documentManager(), [this]() { auto&& view = viewManager()->activeView(); KateFileActions::renameDocumentFile(this, view->document()); }); a->setWhatsThis(i18n("Renames the file belonging to the current document.")); a = actionCollection()->addAction(QStringLiteral("file_delete")); a->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete-shred"))); a->setText(i18nc("@action:inmenu", "Delete File")); connect(a, &QAction::triggered, KateApp::self()->documentManager(), [this]() { auto&& view = viewManager()->activeView(); KateFileActions::deleteDocumentFile(this, view->document()); }); a->setWhatsThis(i18n("Deletes the file belonging to the current document.")); a = actionCollection()->addAction(QStringLiteral("file_properties")); a->setIcon(QIcon::fromTheme(QStringLiteral("dialog-object-properties"))); a->setText(i18n("Properties")); connect(a, &QAction::triggered, KateApp::self()->documentManager(), [this]() { auto&& view = viewManager()->activeView(); KateFileActions::openFilePropertiesDialog(view->document()); }); a->setWhatsThis(i18n("Deletes the file belonging to the current document.")); a = actionCollection()->addAction(QStringLiteral("file_compare")); a->setText(i18n("Compare")); connect(a, &QAction::triggered, KateApp::self()->documentManager(), [this]() { QMessageBox::information(this, i18n("Compare"), i18n("Use the Tabbar context menu to compare two documents")); }); a->setWhatsThis(i18n("Shows a hint how to compare documents.")); a = actionCollection()->addAction(QStringLiteral("file_close_orphaned")); a->setText(i18n("Close Orphaned")); connect(a, SIGNAL(triggered()), KateApp::self()->documentManager(), SLOT(closeOrphaned())); a->setWhatsThis(i18n("Close all documents in the file list that could not be reopened, because they are not accessible anymore.")); a = actionCollection()->addAction(KStandardAction::Close, QStringLiteral("file_close"), m_viewManager, SLOT(slotDocumentClose())); a->setIcon(QIcon::fromTheme(QStringLiteral("document-close"))); a->setWhatsThis(i18n("Close the current document.")); a = actionCollection()->addAction(QStringLiteral("file_close_other")); a->setText(i18n("Close Other")); connect(a, SIGNAL(triggered()), this, SLOT(slotDocumentCloseOther())); a->setWhatsThis(i18n("Close other open documents.")); a = actionCollection()->addAction(QStringLiteral("file_close_all")); a->setText(i18n("Clos&e All")); connect(a, SIGNAL(triggered()), this, SLOT(slotDocumentCloseAll())); a->setWhatsThis(i18n("Close all open documents.")); a = actionCollection()->addAction(KStandardAction::Quit, QStringLiteral("file_quit")); // Qt::QueuedConnection: delay real shutdown, as we are inside menu action handling (bug #185708) connect(a, SIGNAL(triggered()), this, SLOT(slotFileQuit()), Qt::QueuedConnection); a->setWhatsThis(i18n("Close this window")); a = actionCollection()->addAction(QStringLiteral("view_new_view")); a->setIcon(QIcon::fromTheme(QStringLiteral("window-new"))); a->setText(i18n("&New Window")); connect(a, SIGNAL(triggered()), this, SLOT(newWindow())); a->setWhatsThis(i18n("Create a new Kate view (a new window with the same document list).")); m_showFullScreenAction = KStandardAction::fullScreen(nullptr, nullptr, this, this); actionCollection()->addAction(m_showFullScreenAction->objectName(), m_showFullScreenAction); connect(m_showFullScreenAction, SIGNAL(toggled(bool)), this, SLOT(slotFullScreen(bool))); documentOpenWith = new KActionMenu(i18n("Open W&ith"), this); actionCollection()->addAction(QStringLiteral("file_open_with"), documentOpenWith); documentOpenWith->setWhatsThis(i18n("Open the current document using another application registered for its file type, or an application of your choice.")); connect(documentOpenWith->menu(), SIGNAL(aboutToShow()), this, SLOT(mSlotFixOpenWithMenu())); connect(documentOpenWith->menu(), SIGNAL(triggered(QAction*)), this, SLOT(slotOpenWithMenuAction(QAction*))); a = KStandardAction::keyBindings(this, SLOT(editKeys()), actionCollection()); a->setWhatsThis(i18n("Configure the application's keyboard shortcut assignments.")); a = KStandardAction::configureToolbars(this, SLOT(slotEditToolbars()), actionCollection()); a->setWhatsThis(i18n("Configure which items should appear in the toolbar(s).")); QAction *settingsConfigure = KStandardAction::preferences(this, SLOT(slotConfigure()), actionCollection()); settingsConfigure->setWhatsThis(i18n("Configure various aspects of this application and the editing component.")); if (KateApp::self()->pluginManager()->pluginList().count() > 0) { a = actionCollection()->addAction(QStringLiteral("help_plugins_contents")); a->setText(i18n("&Plugins Handbook")); connect(a, SIGNAL(triggered()), this, SLOT(pluginHelp())); a->setWhatsThis(i18n("This shows help files for various available plugins.")); } a = actionCollection()->addAction(QStringLiteral("help_about_editor")); a->setText(i18n("&About Editor Component")); connect(a, SIGNAL(triggered()), this, SLOT(aboutEditor())); connect(m_viewManager, SIGNAL(viewChanged(KTextEditor::View*)), this, SLOT(slotWindowActivated())); connect(m_viewManager, SIGNAL(viewChanged(KTextEditor::View*)), this, SLOT(slotUpdateOpenWith())); connect(m_viewManager, &KateViewManager::viewChanged, this, &KateMainWindow::slotUpdateActionsNeedingUrl); connect(m_viewManager, SIGNAL(viewChanged(KTextEditor::View*)), this, SLOT(slotUpdateBottomViewBar())); // re-route signals to our wrapper connect(m_viewManager, SIGNAL(viewChanged(KTextEditor::View*)), m_wrapper, SIGNAL(viewChanged(KTextEditor::View*))); connect(m_viewManager, SIGNAL(viewCreated(KTextEditor::View*)), m_wrapper, SIGNAL(viewCreated(KTextEditor::View*))); connect(this, SIGNAL(unhandledShortcutOverride(QEvent*)), m_wrapper, SIGNAL(unhandledShortcutOverride(QEvent*))); slotWindowActivated(); // session actions a = actionCollection()->addAction(QStringLiteral("sessions_new")); a->setIcon(QIcon::fromTheme(QStringLiteral("document-new"))); a->setText(i18nc("Menu entry Session->New", "&New")); // Qt::QueuedConnection to avoid deletion of code that is executed when reducing the amount of mainwindows. (bug #227008) connect(a, SIGNAL(triggered()), KateApp::self()->sessionManager(), SLOT(sessionNew()), Qt::QueuedConnection); - a = actionCollection()->addAction(QStringLiteral("sessions_open")); - a->setIcon(QIcon::fromTheme(QStringLiteral("document-open"))); - a->setText(i18n("&Open Session")); - // Qt::QueuedConnection to avoid deletion of code that is executed when reducing the amount of mainwindows. (bug #227008) - connect(a, SIGNAL(triggered()), KateApp::self()->sessionManager(), SLOT(sessionOpen()), Qt::QueuedConnection); a = actionCollection()->addAction(QStringLiteral("sessions_save")); a->setIcon(QIcon::fromTheme(QStringLiteral("document-save"))); a->setText(i18n("&Save Session")); connect(a, SIGNAL(triggered()), KateApp::self()->sessionManager(), SLOT(sessionSave())); a = actionCollection()->addAction(QStringLiteral("sessions_save_as")); a->setIcon(QIcon::fromTheme(QStringLiteral("document-save-as"))); a->setText(i18n("Save Session &As...")); connect(a, SIGNAL(triggered()), KateApp::self()->sessionManager(), SLOT(sessionSaveAs())); a = actionCollection()->addAction(QStringLiteral("sessions_manage")); a->setIcon(QIcon::fromTheme(QStringLiteral("view-choose"))); a->setText(i18n("&Manage Sessions...")); // Qt::QueuedConnection to avoid deletion of code that is executed when reducing the amount of mainwindows. (bug #227008) connect(a, SIGNAL(triggered()), KateApp::self()->sessionManager(), SLOT(sessionManage()), Qt::QueuedConnection); // quick open menu ;) a = new KateSessionsAction(i18n("&Quick Open Session"), this); actionCollection()->addAction(QStringLiteral("sessions_list"), a); } void KateMainWindow::slotDocumentCloseAll() { if (KateApp::self()->documentManager()->documentList().size() >= 1 && KMessageBox::warningContinueCancel(this, i18n("This will close all open documents. Are you sure you want to continue?"), i18n("Close all documents"), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QStringLiteral("closeAll")) != KMessageBox::Cancel) { if (queryClose_internal()) { KateApp::self()->documentManager()->closeAllDocuments(false); } } } void KateMainWindow::slotDocumentCloseOther(KTextEditor::Document *document) { if (KateApp::self()->documentManager()->documentList().size() > 1 && KMessageBox::warningContinueCancel(this, i18n("This will close all open documents beside the current one. Are you sure you want to continue?"), i18n("Close all documents beside current one"), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QStringLiteral("closeOther")) != KMessageBox::Cancel) { if (queryClose_internal(document)) { KateApp::self()->documentManager()->closeOtherDocuments(document); } } } void KateMainWindow::slotDocumentCloseSelected(const QList &docList) { QList documents; foreach(KTextEditor::Document * doc, docList) { if (queryClose_internal(doc)) { documents.append(doc); } } KateApp::self()->documentManager()->closeDocuments(documents); } void KateMainWindow::slotDocumentCloseOther() { slotDocumentCloseOther(m_viewManager->activeView()->document()); } bool KateMainWindow::queryClose_internal(KTextEditor::Document *doc) { int documentCount = KateApp::self()->documentManager()->documentList().size(); if (! showModOnDiskPrompt()) { return false; } QList modifiedDocuments = KateApp::self()->documentManager()->modifiedDocumentList(); modifiedDocuments.removeAll(doc); bool shutdown = (modifiedDocuments.count() == 0); if (!shutdown) { shutdown = KateSaveModifiedDialog::queryClose(this, modifiedDocuments); } if (KateApp::self()->documentManager()->documentList().size() > documentCount) { KMessageBox::information(this, i18n("New file opened while trying to close Kate, closing aborted."), i18n("Closing Aborted")); shutdown = false; } return shutdown; } /** * queryClose(), take care that after the last mainwindow the stuff is closed */ bool KateMainWindow::queryClose() { // session saving, can we close all views ? // just test, not close them actually if (qApp->isSavingSession()) { return queryClose_internal(); } // normal closing of window // allow to close all windows until the last without restrictions if (KateApp::self()->mainWindowsCount() > 1) { return true; } // last one: check if we can close all documents, try run // and save docs if we really close down ! if (queryClose_internal()) { KateApp::self()->sessionManager()->saveActiveSession(true); return true; } return false; } void KateMainWindow::newWindow() { KateApp::self()->newMainWindow(KateApp::self()->sessionManager()->activeSession()->config()); } void KateMainWindow::slotEditToolbars() { KConfigGroup cfg(KSharedConfig::openConfig(), "MainWindow"); saveMainWindowSettings(cfg); KEditToolBar dlg(factory()); connect(&dlg, SIGNAL(newToolBarConfig()), this, SLOT(slotNewToolbarConfig())); dlg.exec(); } void KateMainWindow::reloadXmlGui() { for (KTextEditor::Document* doc : KateApp::self()->documentManager()->documentList()) { doc->reloadXML(); for (KTextEditor::View* view : doc->views()) { view->reloadXML(); } } } void KateMainWindow::slotNewToolbarConfig() { applyMainWindowSettings(KConfigGroup(KSharedConfig::openConfig(), "MainWindow")); // we need to reload all View's XML Gui from disk to ensure toolbar // changes are applied to all views. reloadXmlGui(); } void KateMainWindow::slotFileQuit() { KateApp::self()->shutdownKate(this); } void KateMainWindow::slotFileClose() { m_viewManager->slotDocumentClose(); } void KateMainWindow::slotOpenDocument(QUrl url) { m_viewManager->openUrl(url, QString(), true, false); } void KateMainWindow::readOptions() { KSharedConfig::Ptr config = KSharedConfig::openConfig(); const KConfigGroup generalGroup(config, "General"); m_modNotification = generalGroup.readEntry("Modified Notification", false); m_modCloseAfterLast = generalGroup.readEntry("Close After Last", false); KateApp::self()->documentManager()->setSaveMetaInfos(generalGroup.readEntry("Save Meta Infos", true)); KateApp::self()->documentManager()->setDaysMetaInfos(generalGroup.readEntry("Days Meta Infos", 30)); m_paShowPath->setChecked(generalGroup.readEntry("Show Full Path in Title", false)); m_paShowStatusBar->setChecked(generalGroup.readEntry("Show Status Bar", true)); m_paShowMenuBar->setChecked(generalGroup.readEntry("Show Menu Bar", true)); m_paShowTabBar->setChecked(generalGroup.readEntry("Show Tab Bar", true)); // emit signal to hide/show statusbars toggleShowStatusBar(); toggleShowTabBar(); } void KateMainWindow::saveOptions() { KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup generalGroup(config, "General"); generalGroup.writeEntry("Save Meta Infos", KateApp::self()->documentManager()->getSaveMetaInfos()); generalGroup.writeEntry("Days Meta Infos", KateApp::self()->documentManager()->getDaysMetaInfos()); generalGroup.writeEntry("Show Full Path in Title", m_paShowPath->isChecked()); generalGroup.writeEntry("Show Status Bar", m_paShowStatusBar->isChecked()); generalGroup.writeEntry("Show Menu Bar", m_paShowMenuBar->isChecked()); generalGroup.writeEntry("Show Tab Bar", m_paShowTabBar->isChecked()); } void KateMainWindow::toggleShowMenuBar(bool showMessage) { if (m_paShowMenuBar->isChecked()) { menuBar()->show(); removeMenuBarActionFromContextMenu(); } else { if (showMessage) { const QString accel = m_paShowMenuBar->shortcut().toString(); KMessageBox::information(this, i18n("This will hide the menu bar completely." " You can show it again by typing %1.", accel), i18n("Hide menu bar"), QLatin1String("HideMenuBarWarning")); } menuBar()->hide(); addMenuBarActionToContextMenu(); } } void KateMainWindow::addMenuBarActionToContextMenu() { if (m_viewManager->activeView()) { m_viewManager->activeView()->contextMenu()->addAction(m_paShowMenuBar); } } void KateMainWindow::removeMenuBarActionFromContextMenu() { if (m_viewManager->activeView()) { m_viewManager->activeView()->contextMenu()->removeAction(m_paShowMenuBar); } } void KateMainWindow::toggleShowStatusBar() { emit statusBarToggled(); } bool KateMainWindow::showStatusBar() { return m_paShowStatusBar->isChecked(); } void KateMainWindow::toggleShowTabBar() { emit tabBarToggled(); } bool KateMainWindow::showTabBar() { return m_paShowTabBar->isChecked(); } void KateMainWindow::slotWindowActivated() { if (m_viewManager->activeView()) { updateCaption(m_viewManager->activeView()->document()); } // show view manager in any case if (m_mainStackedWidget->currentWidget() != m_viewManager) { m_mainStackedWidget->setCurrentWidget(m_viewManager); } // update proxy centralWidget()->setFocusProxy(m_viewManager->activeView()); } void KateMainWindow::slotUpdateOpenWith() { if (m_viewManager->activeView()) { documentOpenWith->setEnabled(!m_viewManager->activeView()->document()->url().isEmpty()); } else { documentOpenWith->setEnabled(false); } } void KateMainWindow::slotUpdateActionsNeedingUrl() { auto&& view = viewManager()->activeView(); const bool hasUrl = view && !view->document()->url().isEmpty(); action("file_copy_filepath")->setEnabled(hasUrl); action("file_open_containing_folder")->setEnabled(hasUrl); action("file_rename")->setEnabled(hasUrl); action("file_delete")->setEnabled(hasUrl); action("file_properties")->setEnabled(hasUrl); } void KateMainWindow::dragEnterEvent(QDragEnterEvent *event) { if (!event->mimeData()) { return; } const bool accept = event->mimeData()->hasUrls() || event->mimeData()->hasText(); event->setAccepted(accept); } void KateMainWindow::dropEvent(QDropEvent *event) { slotDropEvent(event); } void KateMainWindow::slotDropEvent(QDropEvent *event) { if (event->mimeData() == nullptr) { return; } // // are we dropping files? // if (event->mimeData()->hasUrls()) { QList textlist = event->mimeData()->urls(); // Try to get the KTextEditor::View that sent this, and activate it, so that the file opens in the // view where it was dropped KTextEditor::View *kVsender = qobject_cast(QObject::sender()); if (kVsender != nullptr) { QWidget *parent = kVsender->parentWidget(); if (parent != nullptr) { KateViewSpace *vs = qobject_cast(parent->parentWidget()); if (vs != nullptr) { m_viewManager->setActiveSpace(vs); } } } foreach(const QUrl & url, textlist) { // if url has no file component, try and recursively scan dir KFileItem kitem(url); kitem.setDelayedMimeTypes(true); if (kitem.isDir()) { if (KMessageBox::questionYesNo(this, i18n("You dropped the directory %1 into Kate. " "Do you want to load all files contained in it ?", url.url()), i18n("Load files recursively?")) == KMessageBox::Yes) { KIO::ListJob *list_job = KIO::listRecursive(url, KIO::DefaultFlags, false); connect(list_job, SIGNAL(entries(KIO::Job*,KIO::UDSEntryList)), this, SLOT(slotListRecursiveEntries(KIO::Job*,KIO::UDSEntryList))); } } else { m_viewManager->openUrl(url); } } } // // or are we dropping text? // else if (event->mimeData()->hasText()) { KTextEditor::Document *doc = KateApp::self()->documentManager()->createDoc(); doc->setText(event->mimeData()->text()); m_viewManager->activateView(doc); } } void KateMainWindow::slotListRecursiveEntries(KIO::Job *job, const KIO::UDSEntryList &list) { const QUrl dir = static_cast(job)->url(); foreach(const KIO::UDSEntry & entry, list) { if (!entry.isDir()) { QUrl url(dir); url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + QLatin1Char('/') + entry.stringValue(KIO::UDSEntry::UDS_NAME)); m_viewManager->openUrl(url); } } } void KateMainWindow::editKeys() { KShortcutsDialog dlg(KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsAllowed, this); QList clients = guiFactory()->clients(); foreach(KXMLGUIClient * client, clients) { // FIXME there appear to be invalid clients after session switching // qCDebug(LOG_KATE)<<"adding client to shortcut editor"; // qCDebug(LOG_KATE)<actionCollection(); // qCDebug(LOG_KATE)<componentData().aboutData(); // qCDebug(LOG_KATE)<componentData().aboutData()->programName(); dlg.addCollection(client->actionCollection(), client->componentName()); } dlg.configure(); // reloadXML gui clients, to ensure all clients are up-to-date reloadXmlGui(); } void KateMainWindow::openUrl(const QString &name) { m_viewManager->openUrl(QUrl(name)); } void KateMainWindow::slotConfigure() { showPluginConfigPage(nullptr, 0); } void KateMainWindow::showPluginConfigPage(KTextEditor::Plugin *configpageinterface, uint id) { if (!m_viewManager->activeView()) { return; } KateConfigDialog *dlg = new KateConfigDialog(this, m_viewManager->activeView()); if (configpageinterface) { dlg->showAppPluginPage(configpageinterface, id); } if (dlg->exec() == QDialog::Accepted) { m_fileOpenRecent->setMaxItems(KateConfigDialog::recentFilesMaxCount()); } delete dlg; m_viewManager->reactivateActiveView(); // gui (toolbars...) needs to be updated, because // of possible changes that the configure dialog // could have done on it, specially for plugins. } QUrl KateMainWindow::activeDocumentUrl() { // anders: i make this one safe, as it may be called during // startup (by the file selector) KTextEditor::View *v = m_viewManager->activeView(); if (v) { return v->document()->url(); } return QUrl(); } void KateMainWindow::mSlotFixOpenWithMenu() { // dh: in bug #307699, this slot is called when launching the Kate application // unfortunately, no one ever could reproduce except users. KTextEditor::View *activeView = m_viewManager->activeView(); if (! activeView) { return; } // cleanup menu QMenu *menu = documentOpenWith->menu(); menu->clear(); // get a list of appropriate services. QMimeDatabase db; QMimeType mime = db.mimeTypeForName(activeView->document()->mimeType()); //qCDebug(LOG_KATE) << "mime type: " << mime.name(); QAction *a = nullptr; KService::List offers = KMimeTypeTrader::self()->query(mime.name(), QStringLiteral("Application")); // add all default open-with-actions except "Kate" for (KService::List::Iterator it = offers.begin(); it != offers.end(); ++it) { KService::Ptr service = *it; if (service->name() == QStringLiteral("Kate")) { continue; } a = menu->addAction(QIcon::fromTheme(service->icon()), service->name()); a->setData(service->entryPath()); } // append "Other..." to call the KDE "open with" dialog. a = documentOpenWith->menu()->addAction(i18n("&Other...")); a->setData(QString()); } void KateMainWindow::slotOpenWithMenuAction(QAction *a) { QList list; list.append(m_viewManager->activeView()->document()->url()); const QString openWith = a->data().toString(); if (openWith.isEmpty()) { // display "open with" dialog KOpenWithDialog dlg(list); if (dlg.exec()) { KRun::runService(*dlg.service(), list, this); } return; } KService::Ptr app = KService::serviceByDesktopPath(openWith); if (app) { KRun::runService(*app, list, this); } else { KMessageBox::error(this, i18n("Application '%1' not found.", openWith), i18n("Application not found")); } } void KateMainWindow::pluginHelp() { KHelpClient::invokeHelp(QString(), QStringLiteral("kate-plugins")); } void KateMainWindow::aboutEditor() { KAboutApplicationDialog ad(KTextEditor::Editor::instance()->aboutData(), this); ad.exec(); } void KateMainWindow::slotFullScreen(bool t) { KToggleFullScreenAction::setFullScreen(this, t); QMenuBar *mb = menuBar(); if (t) { QToolButton *b = new QToolButton(mb); b->setDefaultAction(m_showFullScreenAction); b->setSizePolicy(QSizePolicy(QSizePolicy::Minimum,QSizePolicy::Ignored)); b->setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont)); mb->setCornerWidget(b,Qt::TopRightCorner); b->setVisible(true); b->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); } else { QWidget *w=mb->cornerWidget(Qt::TopRightCorner); if (w) w->deleteLater(); } } bool KateMainWindow::showModOnDiskPrompt() { KTextEditor::Document *doc; DocVector list; list.reserve(KateApp::self()->documentManager()->documentList().size()); foreach(doc, KateApp::self()->documentManager()->documentList()) { if (KateApp::self()->documentManager()->documentInfo(doc)->modifiedOnDisc) { list.append(doc); } } if (!list.isEmpty() && !m_modignore) { KateMwModOnHdDialog mhdlg(list, this); m_modignore = true; bool res = mhdlg.exec(); m_modignore = false; return res; } return true; } void KateMainWindow::slotDocumentCreated(KTextEditor::Document *doc) { connect(doc, SIGNAL(modifiedChanged(KTextEditor::Document*)), this, SLOT(updateCaption(KTextEditor::Document*))); connect(doc, SIGNAL(readWriteChanged(KTextEditor::Document*)), this, SLOT(updateCaption(KTextEditor::Document*))); connect(doc, SIGNAL(documentNameChanged(KTextEditor::Document*)), this, SLOT(updateCaption(KTextEditor::Document*))); connect(doc, SIGNAL(documentUrlChanged(KTextEditor::Document*)), this, SLOT(updateCaption(KTextEditor::Document*))); connect(doc, SIGNAL(documentNameChanged(KTextEditor::Document*)), this, SLOT(slotUpdateOpenWith())); updateCaption(doc); } void KateMainWindow::updateCaption() { if (m_viewManager->activeView()) { updateCaption(m_viewManager->activeView()->document()); } } void KateMainWindow::updateCaption(KTextEditor::Document *doc) { if (!m_viewManager->activeView()) { setCaption(QString(), false); return; } // block signals from inactive docs if (!((KTextEditor::Document *)m_viewManager->activeView()->document() == doc)) { return; } QString c; if (m_viewManager->activeView()->document()->url().isEmpty() || (!m_paShowPath || !m_paShowPath->isChecked())) { c = ((KTextEditor::Document *)m_viewManager->activeView()->document())->documentName(); } else { c = m_viewManager->activeView()->document()->url().toString(QUrl::PreferLocalFile); const QString homePath = QDir::homePath(); if (c.startsWith(homePath)) { c = QStringLiteral("~") + c.right(c.length() - homePath.length()); } } QString sessName = KateApp::self()->sessionManager()->activeSession()->name(); if (!sessName.isEmpty()) { sessName = QString::fromLatin1("%1: ").arg(sessName); } QString readOnlyCaption; if (!m_viewManager->activeView()->document()->isReadWrite()) { readOnlyCaption = i18n(" [read only]"); } setCaption(sessName + c + readOnlyCaption + QStringLiteral(" [*]"), m_viewManager->activeView()->document()->isModified()); } void KateMainWindow::saveProperties(KConfigGroup &config) { saveSession(config); // store all plugin view states int id = KateApp::self()->mainWindowID(this); foreach(const KatePluginInfo & item, KateApp::self()->pluginManager()->pluginList()) { if (item.plugin && pluginViews().contains(item.plugin)) { if (auto interface = qobject_cast (pluginViews().value(item.plugin))) { KConfigGroup group(config.config(), QString::fromLatin1("Plugin:%1:MainWindow:%2").arg(item.saveName()).arg(id)); interface->writeSessionConfig(group); } } } m_fileOpenRecent->saveEntries(KConfigGroup(config.config(), "Recent Files")); m_viewManager->saveViewConfiguration(config); } void KateMainWindow::readProperties(const KConfigGroup &config) { // KDE5: TODO startRestore should take a const KConfigBase*, or even just a const KConfigGroup&, // but this propagates down to interfaces/kate/plugin.h so all plugins have to be ported KConfigBase *configBase = const_cast(config.config()); startRestore(configBase, config.name()); // perhaps enable plugin guis KateApp::self()->pluginManager()->enableAllPluginsGUI(this, configBase); finishRestore(); m_fileOpenRecent->loadEntries(KConfigGroup(config.config(), "Recent Files")); m_viewManager->restoreViewConfiguration(config); } void KateMainWindow::saveGlobalProperties(KConfig *sessionConfig) { KateApp::self()->documentManager()->saveDocumentList(sessionConfig); KConfigGroup cg(sessionConfig, "General"); cg.writeEntry("Last Session", KateApp::self()->sessionManager()->activeSession()->name()); // save plugin config !! KateApp::self()->pluginManager()->writeConfig(sessionConfig); } void KateMainWindow::saveWindowConfig(const KConfigGroup &_config) { KConfigGroup config(_config); saveMainWindowSettings(config); KWindowConfig::saveWindowSize(windowHandle(), config); config.writeEntry("WindowState", int(((KParts::MainWindow *)this)->windowState())); config.sync(); } void KateMainWindow::restoreWindowConfig(const KConfigGroup &config) { setWindowState(Qt::WindowNoState); applyMainWindowSettings(config); KWindowConfig::restoreWindowSize(windowHandle(), config); setWindowState(QFlags(config.readEntry("WindowState", int(Qt::WindowActive)))); } void KateMainWindow::slotUpdateBottomViewBar() { //qCDebug(LOG_KATE)<<"slotUpdateHorizontalViewBar()"<activeView(); BarState bs = m_bottomViewBarMapping[view]; if (bs.bar() && bs.state()) { m_bottomContainerStack->setCurrentWidget(bs.bar()); m_bottomContainerStack->currentWidget()->show(); m_bottomViewBarContainer->show(); } else { QWidget *wid = m_bottomContainerStack->currentWidget(); if (wid) { wid->hide(); } //qCDebug(LOG_KATE)<hide(); } } void KateMainWindow::queueModifiedOnDisc(KTextEditor::Document *doc) { if (!m_modNotification) { return; } KateDocumentInfo *docInfo = KateApp::self()->documentManager()->documentInfo(doc); if (!docInfo) { return; } bool modOnDisk = (uint)docInfo->modifiedOnDisc; if (s_modOnHdDialog == nullptr && modOnDisk) { DocVector list; list.append(doc); s_modOnHdDialog = new KateMwModOnHdDialog(list, this); m_modignore = true; KWindowSystem::setOnAllDesktops(s_modOnHdDialog->winId(), true); s_modOnHdDialog->exec(); delete s_modOnHdDialog; // s_modOnHdDialog is set to 0 in destructor of KateMwModOnHdDialog (jowenn!!!) m_modignore = false; } else if (s_modOnHdDialog != nullptr) { s_modOnHdDialog->addDocument(doc); } } bool KateMainWindow::event(QEvent *e) { if (e->type() == QEvent::ShortcutOverride) { QKeyEvent *k = static_cast(e); emit unhandledShortcutOverride(k); } return KateMDI::MainWindow::event(e); } QObject *KateMainWindow::pluginView(const QString &name) { KTextEditor::Plugin *plugin = KateApp::self()->pluginManager()->plugin(name); if (!plugin) { return nullptr; } return m_pluginViews.contains(plugin) ? m_pluginViews.value(plugin) : nullptr; } void KateMainWindow::mousePressEvent(QMouseEvent *e) { switch(e->button()) { case Qt::ForwardButton: slotFocusNextTab(); break; case Qt::BackButton: slotFocusPrevTab(); break; default: ; } } void KateMainWindow::slotFocusPrevTab() { if (m_viewManager->activeViewSpace()) { m_viewManager->activeViewSpace()->focusPrevTab(); } } void KateMainWindow::slotFocusNextTab() { if (m_viewManager->activeViewSpace()) { m_viewManager->activeViewSpace()->focusNextTab(); } } void KateMainWindow::slotQuickOpen() { /** * toggle back to view manager when when quick open is already shown */ if (m_mainStackedWidget->currentWidget() == m_quickOpen) { m_mainStackedWidget->setCurrentWidget(m_viewManager); centralWidget()->setFocusProxy(m_viewManager); return; } /** * show quick open and pass focus to it */ m_quickOpen->update(); m_mainStackedWidget->setCurrentWidget(m_quickOpen); centralWidget()->setFocusProxy(m_quickOpen); m_quickOpen->setFocus(); } QWidget *KateMainWindow::createToolView(KTextEditor::Plugin *plugin, const QString &identifier, KTextEditor::MainWindow::ToolViewPosition pos, const QIcon &icon, const QString &text) { // FIXME KF5 return KateMDI::MainWindow::createToolView(plugin, identifier, (KMultiTabBar::KMultiTabBarPosition)(pos), icon.pixmap(QSize(16, 16)), text); } bool KateMainWindow::moveToolView(QWidget *widget, KTextEditor::MainWindow::ToolViewPosition pos) { if (!qobject_cast(widget)) { return false; } // FIXME KF5 return KateMDI::MainWindow::moveToolView(qobject_cast(widget), (KMultiTabBar::KMultiTabBarPosition)(pos)); } bool KateMainWindow::showToolView(QWidget *widget) { if (!qobject_cast(widget)) { return false; } return KateMDI::MainWindow::showToolView(qobject_cast(widget)); } bool KateMainWindow::hideToolView(QWidget *widget) { if (!qobject_cast(widget)) { return false; } return KateMDI::MainWindow::hideToolView(qobject_cast(widget)); } diff --git a/kate/session/katesessionchooser.cpp b/kate/session/katesessionchooser.cpp deleted file mode 100644 index 857a3d845..000000000 --- a/kate/session/katesessionchooser.cpp +++ /dev/null @@ -1,187 +0,0 @@ -/* This file is part of the KDE project - * - * Copyright (C) 2005 Christoph Cullmann - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "katesessionchooser.h" - -#include "kateapp.h" -#include "katesessionmanager.h" -#include "katesessionchooseritem.h" -#include "katedebug.h" - -#include -#include - -#include -#include -#include -#include -#include -#include - -//BEGIN CHOOSER DIALOG - -KateSessionChooser::KateSessionChooser(QWidget *parent, const QString &lastSession) - : QDialog(parent) -{ - setWindowTitle(i18n("Session Chooser")); - QVBoxLayout *mainLayout = new QVBoxLayout(this); - - m_sessions = new QTreeWidget(this); - m_sessions->setMinimumSize(400, 200); - mainLayout->addWidget(m_sessions); - QStringList header; - header << i18n("Session Name"); - header << i18nc("The number of open documents", "Open Documents"); - header << QString(); - m_sessions->setHeaderLabels(header); - m_sessions->header()->setStretchLastSection(false); - m_sessions->header()->setSectionResizeMode(0, QHeaderView::Stretch); - m_sessions->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); - m_sessions->header()->setSectionResizeMode(2, QHeaderView::Fixed); - m_sessions->header()->resizeSection(2, 32); - m_sessions->setRootIsDecorated(false); - m_sessions->setItemsExpandable(false); - m_sessions->setAllColumnsShowFocus(true); - m_sessions->setSelectionBehavior(QAbstractItemView::SelectRows); - m_sessions->setSelectionMode(QAbstractItemView::SingleSelection); - - qCDebug(LOG_KATE) << "Last session is:" << lastSession; - - KateSessionList slist = KateApp::self()->sessionManager()->sessionList(); - qSort(slist.begin(), slist.end(), KateSession::compareByName); - - foreach(const KateSession::Ptr & session, slist) { - KateSessionChooserItem *item = new KateSessionChooserItem(m_sessions, session); - QPushButton *tmp = new QPushButton(QIcon::fromTheme(QStringLiteral("document")), QString(), m_sessions); - QMenu *popup = new QMenu(tmp); - QAction *a = popup->addAction(i18n("Clone session settings")); - a->setData(QVariant::fromValue((void *)item)); - connect(a, SIGNAL(triggered()), this, SLOT(slotCopySession())); - a = popup->addAction(i18n("Delete this session")); - a->setData(QVariant::fromValue((void *)item)); - connect(a, SIGNAL(triggered()), this, SLOT(slotDeleteSession())); - tmp->setMenu(popup); - m_sessions->setItemWidget(item, 2, tmp); - - if (session->name() == lastSession) { - m_sessions->setCurrentItem(item); - } - } - - connect(m_sessions, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(selectionChanged(QTreeWidgetItem*,QTreeWidgetItem*))); - connect(m_sessions, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(slotOpen())); - - // bottom box - QHBoxLayout *hb = new QHBoxLayout(); - hb->setMargin(0); - mainLayout->addLayout(hb); - - m_useLast = new QCheckBox(i18n("&Always use this choice"), this); - hb->addWidget(m_useLast); - - // buttons - QDialogButtonBox *buttonBox = new QDialogButtonBox(this); - hb->addWidget(buttonBox); - - QPushButton *cancelButton = new QPushButton(); - KGuiItem::assign(cancelButton, KStandardGuiItem::quit()); - connect(cancelButton, SIGNAL(clicked()), this, SLOT(slotCancel())); - buttonBox->addButton(cancelButton, QDialogButtonBox::RejectRole); - - m_openButton = new QPushButton(QIcon::fromTheme(QStringLiteral("document-open")), i18n("Open Session")); - m_openButton->setEnabled(m_sessions->currentIndex().isValid()); - m_openButton->setDefault(true); - m_openButton->setFocus(); - buttonBox->addButton(m_openButton, QDialogButtonBox::ActionRole); - connect(m_openButton, SIGNAL(clicked()), this, SLOT(slotOpen())); - - QPushButton *newButton = new QPushButton(QIcon::fromTheme(QStringLiteral("document-new")), i18n("New Session")); - buttonBox->addButton(newButton, QDialogButtonBox::ActionRole); - connect(newButton, SIGNAL(clicked()), this, SLOT(slotNew())); - - setResult(resultNone); - selectionChanged(nullptr, nullptr); -} - -KateSessionChooser::~KateSessionChooser() -{} - -void KateSessionChooser::slotCopySession() -{ - m_sessions->setCurrentItem((KateSessionChooserItem *)((QAction *)sender())->data().value()); - Q_ASSERT(static_cast(m_sessions->currentItem())); - done(resultCopy); -} - -void KateSessionChooser::slotDeleteSession() -{ - KateSessionChooserItem *item = (KateSessionChooserItem *)((QAction *)sender())->data().value(); - if (!item) { - return; - } - - KateApp::self()->sessionManager()->deleteSession(item->session); - m_sessions->removeItemWidget(item, 2); - delete item; - -} - -KateSession::Ptr KateSessionChooser::selectedSession() -{ - KateSessionChooserItem *item = static_cast(m_sessions->currentItem()); - - Q_ASSERT(item || ((result() != resultOpen) && (result() != resultCopy))); - - if (!item) { - return KateSession::Ptr(); - } - - return item->session; -} - -bool KateSessionChooser::reopenLastSession() -{ - return m_useLast->isChecked(); -} - -void KateSessionChooser::slotOpen() -{ - Q_ASSERT(static_cast(m_sessions->currentItem())); - done(resultOpen); -} - -void KateSessionChooser::slotNew() -{ - done(resultNew); -} - -void KateSessionChooser::slotCancel() -{ - done(resultQuit); -} - -void KateSessionChooser::selectionChanged(QTreeWidgetItem *current, QTreeWidgetItem *) -{ - Q_UNUSED(current); - m_openButton->setEnabled(true); -} - -//END CHOOSER DIALOG - diff --git a/kate/session/katesessionchooser.h b/kate/session/katesessionchooser.h deleted file mode 100644 index 71018b99b..000000000 --- a/kate/session/katesessionchooser.h +++ /dev/null @@ -1,71 +0,0 @@ -/* This file is part of the KDE project - * - * Copyright (C) 2005 Christoph Cullmann - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __KATE_SESSION_CHOOSER_H__ -#define __KATE_SESSION_CHOOSER_H__ - -#include - -class QPushButton; -class QCheckBox; -class QTreeWidget; -class QTreeWidgetItem; - -#include "katesession.h" - -class KateSessionChooser : public QDialog -{ - Q_OBJECT - -public: - KateSessionChooser(QWidget *parent, const QString &lastSession); - ~KateSessionChooser() override; - - KateSession::Ptr selectedSession(); - bool reopenLastSession(); - - enum { - resultQuit = QDialog::Rejected, - resultOpen, - resultNew, - resultNone, - resultCopy - }; - -protected Q_SLOTS: - void slotCancel(); - void slotOpen(); - void slotNew(); - void slotCopySession(); - void slotDeleteSession(); - - /** - * selection has changed - */ - void selectionChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous); - -private: - QTreeWidget *m_sessions; - QCheckBox *m_useLast; - QPushButton *m_openButton; -}; - -#endif - diff --git a/kate/session/katesessionmanagedialog.cpp b/kate/session/katesessionmanagedialog.cpp index de8a6ea4b..4e4ad6dc3 100644 --- a/kate/session/katesessionmanagedialog.cpp +++ b/kate/session/katesessionmanagedialog.cpp @@ -1,181 +1,506 @@ /* This file is part of the KDE project * * Copyright (C) 2005 Christoph Cullmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "katesessionmanagedialog.h" #include "kateapp.h" -#include "katesessionmanager.h" #include "katesessionchooseritem.h" +#include "katesessionmanager.h" +#include +#include #include #include +#include #include -#include -#include -#include #include -#include -#include -#include KateSessionManageDialog::KateSessionManageDialog(QWidget *parent) : QDialog(parent) { + setupUi(this); setWindowTitle(i18n("Manage Sessions")); + m_dontAskCheckBox->hide(); - QVBoxLayout *mainLayout = new QVBoxLayout(this); - setLayout(mainLayout); + m_sessionList->installEventFilter(this); + connect(m_sessionList, &QTreeWidget::currentItemChanged, this, &KateSessionManageDialog::selectionChanged); + connect(m_sessionList, &QTreeWidget::itemDoubleClicked, this, &KateSessionManageDialog::openSession); + m_sessionList->header()->moveSection(0, 1); // Re-order columns to "Files, Sessions" - QHBoxLayout *hb = new QHBoxLayout(); - mainLayout->addLayout(hb); + m_filterBox->installEventFilter(this); + connect(m_filterBox, &QLineEdit::textChanged, this, &KateSessionManageDialog::filterChanged); + connect(m_sortButton, &QPushButton::clicked, this, &KateSessionManageDialog::changeSortOrder); - m_sessions = new QTreeWidget(this); - m_sessions->setMinimumSize(400, 200); - hb->addWidget(m_sessions); - m_sessions->setColumnCount(2); - QStringList header; - header << i18n("Session Name"); - header << i18nc("The number of open documents", "Open Documents"); - m_sessions->setHeaderLabels(header); - m_sessions->setRootIsDecorated(false); - m_sessions->setItemsExpandable(false); - m_sessions->setAllColumnsShowFocus(true); - m_sessions->setSelectionBehavior(QAbstractItemView::SelectRows); - m_sessions->setSelectionMode(QAbstractItemView::SingleSelection); + connect(m_newButton, &QPushButton::clicked, this, &KateSessionManageDialog::openNewSession); - connect(m_sessions, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(selectionChanged(QTreeWidgetItem*,QTreeWidgetItem*))); + KGuiItem::assign(m_openButton, KStandardGuiItem::open()); + m_openButton->setDefault(true); + connect(m_openButton, &QPushButton::clicked, this, &KateSessionManageDialog::openSession); - updateSessionList(); - m_sessions->resizeColumnToContents(0); + connect(m_templateButton, &QPushButton::clicked, this, &KateSessionManageDialog::openSessionAsTemplate); - // right column buttons - QDialogButtonBox *rightButtons = new QDialogButtonBox(this); - rightButtons->setOrientation(Qt::Vertical); - hb->addWidget(rightButtons); + connect(m_copyButton, &QPushButton::clicked, this, &KateSessionManageDialog::copySession); - m_rename = new QPushButton(i18n("&Rename...")); - connect(m_rename, SIGNAL(clicked()), this, SLOT(rename())); - rightButtons->addButton(m_rename, QDialogButtonBox::ApplyRole); + connect(m_renameButton, &QPushButton::clicked, this, &KateSessionManageDialog::editBegin); - m_del = new QPushButton(); - KGuiItem::assign(m_del, KStandardGuiItem::del()); - connect(m_del, SIGNAL(clicked()), this, SLOT(del())); - rightButtons->addButton(m_del, QDialogButtonBox::ApplyRole); + connect(m_deleteButton, &QPushButton::clicked, this, &KateSessionManageDialog::updateDeleteList); - // dialog buttons - QDialogButtonBox *bottomButtons = new QDialogButtonBox(this); - mainLayout->addWidget(bottomButtons); + KGuiItem::assign(m_closeButton, KStandardGuiItem::close()); + connect(m_closeButton, &QPushButton::clicked, this, &KateSessionManageDialog::closeDialog); - QPushButton *closeButton = new QPushButton; - KGuiItem::assign(closeButton, KStandardGuiItem::close()); - closeButton->setDefault(true); - bottomButtons->addButton(closeButton, QDialogButtonBox::RejectRole); - connect(closeButton, SIGNAL(clicked()), this, SLOT(slotClose())); + connect(KateApp::self()->sessionManager(), &KateSessionManager::sessionListChanged, this, &KateSessionManageDialog::updateSessionList); + + changeSortOrder(); // Set order to SortAlphabetical, set button text and fill session list +} - m_openButton = new QPushButton(QIcon::fromTheme(QStringLiteral("document-open")), i18n("&Open")); - bottomButtons->addButton(m_openButton, QDialogButtonBox::AcceptRole); - connect(m_openButton, SIGNAL(clicked()), this, SLOT(open())); - // trigger action update - selectionChanged(nullptr, nullptr); +KateSessionManageDialog::KateSessionManageDialog(QWidget *parent, const QString &lastSession) + : KateSessionManageDialog(parent) +{ + setWindowTitle(i18n("Session Chooser")); + m_dontAskCheckBox->show(); + m_chooserMode = true; + connect(m_dontAskCheckBox, &QCheckBox::toggled, this, &KateSessionManageDialog::dontAskToggled); + + m_prefferedSession = lastSession; + changeSortOrder(); // Set order to SortChronological } + KateSessionManageDialog::~KateSessionManageDialog() {} -void KateSessionManageDialog::slotClose() + +void KateSessionManageDialog::dontAskToggled() { - done(0); + m_templateButton->setEnabled(!m_dontAskCheckBox->isChecked()); } -void KateSessionManageDialog::selectionChanged(QTreeWidgetItem *current, QTreeWidgetItem *) + +void KateSessionManageDialog::changeSortOrder() { - const bool validItem = (current != nullptr); + switch (m_sortOrder) { + case SortAlphabetical: + m_sortOrder = SortChronological; + m_sortButton->setText(i18n("Sort Alpabetical")); + //m_sortButton->setIcon(QIcon::fromTheme(QStringLiteral("FIXME"))); + break; + case SortChronological: + m_sortOrder = SortAlphabetical; + m_sortButton->setText(i18n("Sort Last Used")); + //m_sortButton->setIcon(QIcon::fromTheme(QStringLiteral("FIXME"))); + break; + } - m_rename->setEnabled(validItem); - m_del->setEnabled(validItem && (static_cast(current))->session != KateApp::self()->sessionManager()->activeSession()); - m_openButton->setEnabled(true); + updateSessionList(); } -void KateSessionManageDialog::rename() + +void KateSessionManageDialog::filterChanged() { - KateSessionChooserItem *item = static_cast(m_sessions->currentItem()); + static QPointer delay; - if (!item) { + if (!delay) { + delay = new QTimer(this); // Should be auto cleard by Qt when we die + delay->setSingleShot(true); + delay->setInterval(400); + connect(delay, &QTimer::timeout, this, &KateSessionManageDialog::updateSessionList); + } + + delay->start(); +} + + +void KateSessionManageDialog::done(int result) +{ + for (auto session : qAsConst(m_deleteList)) { + KateApp::self()->sessionManager()->deleteSession(session); + } + m_deleteList.clear(); // May not needed, but anyway + + if (ResultQuit == result) { + QDialog::done(0); return; } - bool ok = false; - QString name = QInputDialog::getText(QApplication::activeWindow(), // nasty trick:) - i18n("Specify New Name for Session"), i18n("Session name:"), - QLineEdit::Normal, item->session->name(), &ok); + if (m_chooserMode && m_dontAskCheckBox->isChecked()) { + // write back our nice boolean :) + KConfigGroup generalConfig(KSharedConfig::openConfig(), QStringLiteral("General")); + switch (result) { + case ResultOpen: + generalConfig.writeEntry("Startup Session", "last"); + break; + case ResultNew: + generalConfig.writeEntry("Startup Session", "new"); + break; + default: + break; + } + generalConfig.sync(); + } - if (!ok) { + QDialog::done(1); +} + + +void KateSessionManageDialog::selectionChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous) +{ + Q_UNUSED(previous); + + if (m_editByUser) { + editDone(); // Field was left unchanged, no need to apply return; } - if (name.isEmpty()) { - KMessageBox::sorry(this, i18n("To save a session, you must specify a name."), i18n("Missing Session Name")); + if (!current) { + m_openButton->setEnabled(false); + m_templateButton->setEnabled(false); + m_copyButton->setEnabled(false); + m_renameButton->setEnabled(false); + m_deleteButton->setEnabled(false); return; } - if (KateApp::self()->sessionManager()->renameSession(item->session, name)) { - updateSessionList(); + const KateSession::Ptr activeSession = KateApp::self()->sessionManager()->activeSession(); + const bool notActiveSession = !KateApp::self()->sessionManager()->sessionIsActive(currentSelectedSession()->name()); + + m_deleteButton->setEnabled(notActiveSession); + + if (m_deleteList.contains(currentSelectedSession())) { + m_deleteButton->setText(i18n("Restore")); + m_openButton->setEnabled(false); + m_templateButton->setEnabled(false); + m_copyButton->setEnabled(true); // Looks a little strange but is OK + m_renameButton->setEnabled(false); + } else { + KGuiItem::assign(m_deleteButton, KStandardGuiItem::del()); + m_openButton->setEnabled(currentSelectedSession() != activeSession); + m_templateButton->setEnabled(true); + m_copyButton->setEnabled(true); + m_renameButton->setEnabled(true); } } -void KateSessionManageDialog::del() + +void KateSessionManageDialog::disableButtons() +{ + m_openButton->setEnabled(false); + m_newButton->setEnabled(false); + m_templateButton->setEnabled(false); + m_dontAskCheckBox->setEnabled(false); + m_copyButton->setEnabled(false); + m_renameButton->setEnabled(false); + m_deleteButton->setEnabled(false); + m_closeButton->setEnabled(false); + m_sortButton->setEnabled(false); + m_filterBox->setEnabled(false); +} + + +void KateSessionManageDialog::editBegin() { - KateSessionChooserItem *item = static_cast(m_sessions->currentItem()); + if (m_editByUser) { + return; + } + + KateSessionChooserItem *item = currentSessionItem(); if (!item) { return; } - KateApp::self()->sessionManager()->deleteSession(item->session); + disableButtons(); + + item->setFlags(item->flags() | Qt::ItemIsEditable); + m_sessionList->clearSelection(); + m_sessionList->editItem(item, 0); + + // Always apply changes user did, like Dolphin + connect(m_sessionList, &QTreeWidget::itemChanged, this, &KateSessionManageDialog::editApply); + connect(m_sessionList->itemWidget(item, 0), &QObject::destroyed, this, &KateSessionManageDialog::editApply); + + m_editByUser = item; // Do it last to block eventFilter() actions until we are ready +} + + +void KateSessionManageDialog::editDone() +{ + m_editByUser = nullptr; + disconnect(m_sessionList, &QTreeWidget::itemChanged, this, &KateSessionManageDialog::editApply); updateSessionList(); + + m_newButton->setEnabled(true); + m_dontAskCheckBox->setEnabled(true); + m_closeButton->setEnabled(true); + m_sortButton->setEnabled(true); + m_filterBox->setEnabled(true); + + m_sessionList->setFocus(); +} + + +void KateSessionManageDialog::editApply() +{ + if (!m_editByUser) { + return; + } + + KateApp::self()->sessionManager()->renameSession(m_editByUser->session, m_editByUser->text(0)); + editDone(); } -void KateSessionManageDialog::open() + +void KateSessionManageDialog::copySession() { - KateSessionChooserItem *item = static_cast(m_sessions->currentItem()); + KateSessionChooserItem *item = currentSessionItem(); + + if (!item) { + return; + } + + m_prefferedSession = KateApp::self()->sessionManager()->copySession(item->session); + m_sessionList->setFocus(); // Only needed when user abort +} + + +void KateSessionManageDialog::openSession() +{ + KateSessionChooserItem *item = currentSessionItem(); if (!item) { return; } hide(); KateApp::self()->sessionManager()->activateSession(item->session); - done(0); + done(ResultOpen); } + +void KateSessionManageDialog::openSessionAsTemplate() +{ + KateSessionChooserItem *item = currentSessionItem(); + + if (!item) { + return; + } + + hide(); + + KateSessionManager *sm = KateApp::self()->sessionManager(); + KateSession::Ptr ns = KateSession::createAnonymousFrom(item->session, sm->anonymousSessionFile()); + sm->activateSession(ns); + + done(ResultOpen); +} + + +void KateSessionManageDialog::openNewSession() +{ + hide(); + KateApp::self()->sessionManager()->sessionNew(); + done(ResultNew); +} + + +void KateSessionManageDialog::updateDeleteList() +{ + KateSessionChooserItem *item = currentSessionItem(); + + if (!item) { + return; + } + + const KateSession::Ptr session = item->session; + if (m_deleteList.contains(session)) { + m_deleteList.remove(session); + item->setForeground(0, QBrush(KColorScheme(QPalette::Active).foreground(KColorScheme::NormalText).color())); + item->setIcon(0, QIcon()); + item->setToolTip(0, QString()); + } else { + m_deleteList.insert(session); + markItemAsToBeDeleted(item); + } + + // To ease multiple deletions, move the selection + QTreeWidgetItem *newItem = m_sessionList->itemBelow(item) ? m_sessionList->itemBelow(item) : m_sessionList->topLevelItem(0); + m_sessionList->setCurrentItem(newItem); + m_sessionList->setFocus(); +} + + +void KateSessionManageDialog::markItemAsToBeDeleted(QTreeWidgetItem *item) +{ + item->setForeground(0, QBrush(KColorScheme(QPalette::Active).foreground(KColorScheme::InactiveText).color())); + item->setIcon(0, QIcon::fromTheme(QStringLiteral("emblem-warning"))); + item->setToolTip(0, i18n("Session will be deleted on dialog close")); +} + + +void KateSessionManageDialog::closeDialog() +{ + done(ResultQuit); +} + + void KateSessionManageDialog::updateSessionList() { - m_sessions->clear(); + if (m_editByUser) { + // Don't crash accidentally an ongoing edit + return; + } + + KateSession::Ptr currSelSession = currentSelectedSession(); + KateSession::Ptr activeSession = KateApp::self()->sessionManager()->activeSession(); + + m_sessionList->clear(); KateSessionList slist = KateApp::self()->sessionManager()->sessionList(); - qSort(slist.begin(), slist.end(), KateSession::compareByName); + switch (m_sortOrder) { + case SortAlphabetical: std::sort (slist.begin(), slist.end(), KateSession::compareByName); break; + case SortChronological: std::sort (slist.begin(), slist.end(), KateSession::compareByTimeDesc); break; + } + + KateSessionChooserItem *prefferedItem = nullptr; + KateSessionChooserItem *currSessionItem = nullptr; + KateSessionChooserItem *activeSessionItem= nullptr; + + for (const KateSession::Ptr &session : qAsConst(slist)) { + if (!m_filterBox->text().isEmpty()) { + if (!session->name().contains(m_filterBox->text(), Qt::CaseInsensitive)) { + continue; + } + } + + KateSessionChooserItem *item = new KateSessionChooserItem(m_sessionList, session); + if (session == currSelSession) { + currSessionItem = item; + } else if (session == activeSession) { + activeSessionItem = item; + } else if (session->name() == m_prefferedSession) { + prefferedItem = item; + m_prefferedSession.clear(); + } + + if (m_deleteList.contains(session)) { + markItemAsToBeDeleted(item); + } + } + + m_sessionList->resizeColumnToContents(1); // Minimize "Files" column + + if (!prefferedItem) { + prefferedItem = currSessionItem ? currSessionItem : activeSessionItem; + } + + if (prefferedItem) { + m_sessionList->setCurrentItem(prefferedItem); + m_sessionList->scrollToItem(prefferedItem); + } else if (m_sessionList->topLevelItemCount() > 0) { + m_sessionList->setCurrentItem(m_sessionList->topLevelItem(0)); + } + + if (m_filterBox->hasFocus()){ + return; + } + + if (m_sessionList->topLevelItemCount() == 0) { + m_newButton->setFocus(); + } else { + m_sessionList->setFocus(); + } +} + + +KateSessionChooserItem *KateSessionManageDialog::currentSessionItem() const +{ + return static_cast(m_sessionList->currentItem()); +} + - foreach(const KateSession::Ptr & session, slist) { - new KateSessionChooserItem(m_sessions, session); +KateSession::Ptr KateSessionManageDialog::currentSelectedSession() const +{ + KateSessionChooserItem *item = currentSessionItem(); + + if (!item) { + return KateSession::Ptr(); } + + return item->session; } + +bool KateSessionManageDialog::eventFilter(QObject *object, QEvent *event) +{ + QKeyEvent *ke = static_cast(event); + + if (object == m_sessionList) { + if (!m_editByUser) { // No need for further action + return false; + } + + if (event->type() == QEvent::KeyPress) { + switch (ke->key()) { + // Avoid to apply changes with untypical keys/don't left edit field this way + case Qt::Key_Up : + case Qt::Key_Down : + case Qt::Key_PageUp : + case Qt::Key_PageDown : + return true; + default: + break; + } + + } else if (event->type() == QEvent::KeyRelease) { + switch (ke->key()) { + case Qt::Key_Escape : + editDone(); // Abort edit + break; + case Qt::Key_Return : + editApply(); + break; + default: + break; + } + } + + } else if (object == m_filterBox) { + // Catch Return key to avoid to finish the dialog + if (event->type() == QEvent::KeyPress && (ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter)) { + updateSessionList(); + m_sessionList->setFocus(); + return true; + } + } + + return false; +} + + +void KateSessionManageDialog::closeEvent(QCloseEvent *event) +{ + Q_UNUSED(event); + + if (m_editByUser) { + // We must catch closeEvent here due to connected signal of QLineEdit::destroyed->editApply()->crash! + editDone(); // editApply() don't work, m_editByUser->text(0) will not updated from QLineEdit + } +} diff --git a/kate/session/katesessionmanagedialog.h b/kate/session/katesessionmanagedialog.h index f9caf35b5..122a27fe5 100644 --- a/kate/session/katesessionmanagedialog.h +++ b/kate/session/katesessionmanagedialog.h @@ -1,78 +1,217 @@ /* This file is part of the KDE project * * Copyright (C) 2005 Christoph Cullmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef __KATE_SESSION_MANAGE_DIALOG_H__ #define __KATE_SESSION_MANAGE_DIALOG_H__ #include class QPushButton; class QTreeWidget; class QTreeWidgetItem; +class KateSessionChooserItem; + +#include "katesession.h" +#include "ui_katesessionmanagedialog.h" class KateSessionManageDialog : public QDialog + , public Ui::KateSessionManageDialogUi { Q_OBJECT public: + /** + * The normal ctor for manage mode + */ KateSessionManageDialog(QWidget *parent); + + /** + * The special ctor for chooser mode + * Set a differend window title, enables some extra widget and try to select + * the @p lastSession in the session list. + */ + KateSessionManageDialog(QWidget *parent, const QString &lastSession); ~KateSessionManageDialog() override; protected Q_SLOTS: /** - * close pressed + * Re-implemented to save in chooser mode users choice when needed and to + * exit the dialog with a return code of @c 0/1 fitting to the code of + * @p result to indicate that the user chose a session to open not. + * @see KateSessionManager::chooseSession() + * @param result has to be one of enum @c ResultCode */ - void slotClose(); + void done(int result) override; /** - * selection has changed + * To update the button states */ void selectionChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous); /** - * try to rename session + * Close the dialog and open the selected session */ - void rename(); + void openSession(); /** - * try to delete session + * Use the selected session as template for a new session */ - void del(); + void openSessionAsTemplate(); /** - * close dialog and open the selected session + * Open new anonymous session */ - void open() override; + void openNewSession(); + + /** + * Copy the selected session + */ + void copySession(); + + /** + * Try to rename the session hold by @c m_editByUser + * @see editDone(), editBegin(), m_editByUser + */ + void editApply(); + + /** + * Open the inline editor on the selected session, set @c m_editByUser to + * the selected session and connect @c QTreeWidget::itemChanged signal to + * @c editApply(). + * @see editDone(), editApply(), m_editByUser + */ + void editBegin(); + + /** + * Finish the edit process, reset intern settings. + * Calling this function without to call @c editApply() will abort edit. + */ + void editDone(); -private: /** - * update our list + * To close the dialog + */ + void closeDialog(); + + /** + * Slot for the delete button + * @see m_deleteList, deleteSessions() + */ + void updateDeleteList(); + + /** + * Update the list of sessions in @c m_sessionList and trigger some needed + * actions belong to the editing of session names. */ void updateSessionList(); + /** + * To enable/disable not useful buttons + */ + void dontAskToggled(); + + /** + * To change the sort order of the session list + */ + void changeSortOrder(); + + /** + * Slot for @c m_filterField + */ + void filterChanged(); + private: - QTreeWidget *m_sessions; - QPushButton *m_rename; - QPushButton *m_del; - QPushButton *m_openButton; + /** + * Result codes used to call @c done() + */ + enum ResultCode { + ResultQuit = QDialog::Rejected, + ResultOpen, + ResultNew, + }; + + /** + * Sort order of the session list + */ + enum SortOrder { + SortAlphabetical, + SortChronological, + }; + + /** + * Re-implemented to avoid crash when in edit state + */ + void closeEvent(QCloseEvent *event) override; + + /** + * Disables all buttons on the "normal" button stack page and the close button + */ + void disableButtons(); + + /** + * To handle the rename process + */ + bool eventFilter(QObject *object, QEvent *event) override; + + /** + * @return current selected item in @c m_sessionList or @c nullptr + */ + KateSessionChooserItem *currentSessionItem() const; + + /** + * @return current selected session in @c m_sessionList or empty @c KateSession::Ptr() + */ + KateSession::Ptr currentSelectedSession() const; + + /** + * Display @p item in a striking way to indicate that the session represent + * by @p item will be deleted + */ + void markItemAsToBeDeleted(QTreeWidgetItem *item); + + /** + * The item which is currently edited by the user or @c nullptr to indicate + * that nothing is on edit. + */ + KateSessionChooserItem *m_editByUser = nullptr; + + /** + * Used by @c updateSessionList() to choose a new current item + */ + QString m_prefferedSession; + + /** + * How the list of sessions has to be ordered + */ + int m_sortOrder = SortChronological; + + /** + * Used in dtor to do some savings or not + */ + bool m_chooserMode = false; + + /** + * Will filled with sessions to be deleted by @c updateDeleteList() and process + * by @c deleteSessions() + */ + QSet m_deleteList; }; #endif - diff --git a/kate/session/katesessionmanagedialog.ui b/kate/session/katesessionmanagedialog.ui new file mode 100644 index 000000000..34edc86be --- /dev/null +++ b/kate/session/katesessionmanagedialog.ui @@ -0,0 +1,246 @@ + + + KateSessionManageDialogUi + + + + 0 + 0 + 758 + 475 + + + + Form + + + + + + + + + + + 1 + 0 + + + + + + + Filter Sessions + + + true + + + + + + + + 0 + 0 + + + + Sort + + + + + + + + + false + + + false + + + true + + + + Session Name + + + + + Files + + + AlignLeading|AlignVCenter + + + + + + + + + + 0 + + + + + + + + + &Open + + + + .. + + + + + + + New Session + + + + + + + + + + Open as Template + + + + + + + Don't ask again + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Duplicate... + + + + + + + &Rename... + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Delete + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Close + + + + + + + Qt::Horizontal + + + QSizePolicy::MinimumExpanding + + + + 220 + 0 + + + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + + m_newButton + m_templateButton + m_dontAskCheckBox + m_copyButton + m_renameButton + m_deleteButton + m_closeButton + m_filterBox + m_sessionList + m_sortButton + m_openButton + + + + diff --git a/kate/session/katesessionmanager.cpp b/kate/session/katesessionmanager.cpp index bd5826538..1ed8f9dcf 100644 --- a/kate/session/katesessionmanager.cpp +++ b/kate/session/katesessionmanager.cpp @@ -1,588 +1,635 @@ /* This file is part of the KDE project * * Copyright (C) 2005 Christoph Cullmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "config.h" #include "katesessionmanager.h" -#include "katesessionchooser.h" #include "katesessionmanagedialog.h" -#include "katesessionopendialog.h" #include "kateapp.h" #include "katepluginmanager.h" #include "katerunninginstanceinfo.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef Q_OS_WIN #include #endif //BEGIN KateSessionManager KateSessionManager::KateSessionManager(QObject *parent, const QString &sessionsDir) : QObject(parent) { if (sessionsDir.isEmpty()) { m_sessionsDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/kate/sessions"); } else { m_sessionsDir = sessionsDir; } // create dir if needed QDir().mkpath(m_sessionsDir); m_dirWatch = new KDirWatch(this); m_dirWatch->addDir(m_sessionsDir); connect(m_dirWatch, SIGNAL(dirty(QString)), this, SLOT(updateSessionList())); updateSessionList(); - - m_activeSession = KateSession::createAnonymous(anonymousSessionFile()); } KateSessionManager::~KateSessionManager() { delete m_dirWatch; } void KateSessionManager::updateSessionList() { QStringList list; // Let's get a list of all session we have atm QDir dir(m_sessionsDir, QStringLiteral("*.katesession"), QDir::Time); for (unsigned int i = 0; i < dir.count(); ++i) { QString name = dir[i]; name.chop(12); // .katesession list << QUrl::fromPercentEncoding(name.toLatin1()); } // write jump list actions to disk in the kate.desktop file updateJumpListActions(list); - // delete old items; - QMutableHashIterator i(m_sessions); + bool changed = false; - while (i.hasNext()) { - i.next(); - const int idx = list.indexOf(i.key()); - if (idx == -1) { // the key is invalid, remove it from m_session - if (i.value() != m_activeSession) { // if active, ignore missing config - i.remove(); - } - } else { // remove it from scan list - list.removeAt(idx); + // Add new sessions to our list + for (const QString session : qAsConst(list)) { + if (!m_sessions.contains(session)) { + const QString file = sessionFileForName(session); + m_sessions.insert(session, KateSession::create(file, session)); + changed = true; + } + } + // Remove gone sessions from our list + for (const QString session : m_sessions.keys()) { + if ((list.indexOf(session) < 0) && (m_sessions.value(session) != activeSession())) { + m_sessions.remove(session); + changed = true; } } - // load the new ones - foreach(const QString & newName, list) { - const QString file = sessionFileForName(newName); - m_sessions[newName] = KateSession::create(file, newName); + if (changed) { + emit sessionListChanged(); } } bool KateSessionManager::activateSession(KateSession::Ptr session, const bool closeAndSaveLast, const bool loadNew) { - if (m_activeSession == session) { + if (activeSession() == session) { return true; } if (!session->isAnonymous()) { //check if the requested session is already open in another instance KateRunningInstanceMap instances; if (!fillinRunningKateAppInstances(&instances)) { KMessageBox::error(nullptr, i18n("Internal error: there is more than one instance open for a given session.")); return false; } if (instances.contains(session->name())) { if (KMessageBox::questionYesNo(nullptr, i18n("Session '%1' is already opened in another kate instance, change there instead of reopening?", session->name()), QString(), KStandardGuiItem::yes(), KStandardGuiItem::no(), QStringLiteral("katesessionmanager_switch_instance")) == KMessageBox::Yes) { instances[session->name()]->dbus_if->call(QStringLiteral("activate")); cleanupRunningKateAppInstanceMap(&instances); return false; } } cleanupRunningKateAppInstanceMap(&instances); } // try to close and save last session if (closeAndSaveLast) { if (KateApp::self()->activeKateMainWindow()) { if (!KateApp::self()->activeKateMainWindow()->queryClose_internal()) { return true; } } // save last session or not? saveActiveSession(); // really close last KateApp::self()->documentManager()->closeAllDocuments(); } // set the new session m_activeSession = session; // there is one case in which we don't want the restoration and that is // when restoring session from session manager. // In that case the restore is handled by the caller if (loadNew) { loadSession(session); } emit sessionChanged(); return true; } void KateSessionManager::loadSession(const KateSession::Ptr &session) const { // open the new session KSharedConfigPtr sharedConfig = KSharedConfig::openConfig(); KConfig *sc = session->config(); const bool loadDocs = !session->isAnonymous(); // do not load docs for new sessions // if we have no session config object, try to load the default // (anonymous/unnamed sessions) // load plugin config + plugins KateApp::self()->pluginManager()->loadConfig(sc); if (loadDocs) { KateApp::self()->documentManager()->restoreDocumentList(sc); } // window config KConfigGroup c(sharedConfig, "General"); if (c.readEntry("Restore Window Configuration", true)) { KConfig *cfg = sc; bool delete_cfg = false; // a new, named session, read settings of the default session. if (! sc->hasGroup("Open MainWindows")) { delete_cfg = true; cfg = new KConfig(anonymousSessionFile(), KConfig::SimpleConfig); } int wCount = cfg->group("Open MainWindows").readEntry("Count", 1); for (int i = 0; i < wCount; ++i) { if (i >= KateApp::self()->mainWindowsCount()) { KateApp::self()->newMainWindow(cfg, QString::fromLatin1("MainWindow%1").arg(i)); } else { KateApp::self()->mainWindow(i)->readProperties(KConfigGroup(cfg, QString::fromLatin1("MainWindow%1").arg(i))); } KateApp::self()->mainWindow(i)->restoreWindowConfig(KConfigGroup(cfg, QString::fromLatin1("MainWindow%1 Settings").arg(i))); } if (delete_cfg) { delete cfg; } // remove mainwindows we need no longer... if (wCount > 0) { while (wCount < KateApp::self()->mainWindowsCount()) { delete KateApp::self()->mainWindow(KateApp::self()->mainWindowsCount() - 1); } } } } bool KateSessionManager::activateSession(const QString &name, const bool closeAndSaveLast, const bool loadNew) { return activateSession(giveSession(name), closeAndSaveLast, loadNew); } bool KateSessionManager::activateAnonymousSession() { return activateSession(QString(), false); } KateSession::Ptr KateSessionManager::giveSession(const QString &name) { if (name.isEmpty()) { return KateSession::createAnonymous(anonymousSessionFile()); } if (m_sessions.contains(name)) { return m_sessions.value(name); } KateSession::Ptr s = KateSession::create(sessionFileForName(name), name); saveSessionTo(s->config()); m_sessions[name] = s; + // Due to this add to m_sessions will updateSessionList() no signal emit, + // but it's importand to add. Otherwise could it be happen that m_activeSession + // is not part of m_sessions but a double + emit sessionListChanged(); + return s; } -void KateSessionManager::deleteSession(KateSession::Ptr session) +bool KateSessionManager::deleteSession(KateSession::Ptr session) { - QFile::remove(session->file()); - if (session != activeSession()) { - m_sessions.remove(session->name()); + if (sessionIsActive(session->name())) { + return false; } + + QFile::remove(session->file()); + m_sessions.remove(session->name()); + // Due to this remove from m_sessions will updateSessionList() no signal emit, + // but this way is there no delay between deletion and information + emit sessionListChanged(); + + return true; } -bool KateSessionManager::renameSession(KateSession::Ptr session, const QString &newName) +QString KateSessionManager::copySession(KateSession::Ptr session, const QString &newName) { - Q_ASSERT(!newName.isEmpty()); + const QString name = askForNewSessionName(session, newName); - if (session->name() == newName) { - return true; + if (name.isEmpty()) { + return name; } - const QString newFile = sessionFileForName(newName); + const QString newFile = sessionFileForName(name); - if (QFile::exists(newFile)) { - KMessageBox::sorry(QApplication::activeWindow(), - i18n("The session could not be renamed to \"%1\", there already exists another session with the same name", newName), - i18n("Session Renaming")); - return false; + KateSession::Ptr ns = KateSession::createFrom(session, newFile, name); + ns->config()->sync(); + + return name; +} + +QString KateSessionManager::renameSession(KateSession::Ptr session, const QString &newName) +{ + const QString name = askForNewSessionName(session, newName); + + if (name.isEmpty()) { + return name; } + const QString newFile = sessionFileForName(name); + session->config()->sync(); const QUrl srcUrl = QUrl::fromLocalFile(session->file()); const QUrl dstUrl = QUrl::fromLocalFile(newFile); KIO::CopyJob *job = KIO::move(srcUrl, dstUrl, KIO::HideProgressInfo); if (!job->exec()) { KMessageBox::sorry(QApplication::activeWindow(), i18n("The session could not be renamed to \"%1\". Failed to write to \"%2\"", newName, newFile), i18n("Session Renaming")); - return false; + return QString(); } m_sessions[newName] = m_sessions.take(session->name()); session->setName(newName); session->setFile(newFile); + session->config()->sync(); + // updateSessionList() will this edit not notice, so force signal + emit sessionListChanged(); if (session == activeSession()) { emit sessionChanged(); } - return true; + return name; } void KateSessionManager::saveSessionTo(KConfig *sc) const { // Clear the session file to avoid to accumulate outdated entries for (auto group : sc->groupList()) { sc->deleteGroup(group); } // save plugin configs and which plugins to load KateApp::self()->pluginManager()->writeConfig(sc); // save document configs + which documents to load KateApp::self()->documentManager()->saveDocumentList(sc); sc->group("Open MainWindows").writeEntry("Count", KateApp::self()->mainWindowsCount()); // save config for all windows around ;) bool saveWindowConfig = KConfigGroup(KSharedConfig::openConfig(), "General").readEntry("Restore Window Configuration", true); for (int i = 0; i < KateApp::self()->mainWindowsCount(); ++i) { KConfigGroup cg(sc, QString::fromLatin1("MainWindow%1").arg(i)); KateApp::self()->mainWindow(i)->saveProperties(cg); if (saveWindowConfig) { KateApp::self()->mainWindow(i)->saveWindowConfig(KConfigGroup(sc, QString::fromLatin1("MainWindow%1 Settings").arg(i))); } } sc->sync(); /** * try to sync file to disk */ QFile fileToSync(sc->name()); if (fileToSync.open(QIODevice::ReadOnly)) { #ifndef Q_OS_WIN // ensure that the file is written to disk #ifdef HAVE_FDATASYNC fdatasync(fileToSync.handle()); #else fsync(fileToSync.handle()); #endif #endif } } bool KateSessionManager::saveActiveSession(bool rememberAsLast) { + if (!activeSession()) { + return false; + } + KConfig *sc = activeSession()->config(); saveSessionTo(sc); - if (rememberAsLast) { + if (rememberAsLast && !activeSession()->isAnonymous()) { KSharedConfigPtr c = KSharedConfig::openConfig(); c->group("General").writeEntry("Last Session", activeSession()->name()); c->sync(); } return true; } bool KateSessionManager::chooseSession() { const KConfigGroup c(KSharedConfig::openConfig(), "General"); // get last used session, default to default session const QString lastSession(c.readEntry("Last Session", QString())); const QString sesStart(c.readEntry("Startup Session", "manual")); // uhh, just open last used session, show no chooser if (sesStart == QStringLiteral("last")) { activateSession(lastSession, false); return true; } // start with empty new session or in case no sessions exist if (sesStart == QStringLiteral("new") || sessionList().size() == 0) { activateAnonymousSession(); return true; } - QScopedPointer chooser(new KateSessionChooser(nullptr, lastSession)); - const int res = chooser->exec(); - bool success = true; + return QScopedPointer(new KateSessionManageDialog(nullptr, lastSession))->exec(); +} - switch (res) { - case KateSessionChooser::resultOpen: { - KateSession::Ptr s = chooser->selectedSession(); // dialog guarantees this to be valid - success = activateSession(s, false); - break; - } +void KateSessionManager::sessionNew() +{ + activateSession(giveSession(QString())); +} - case KateSessionChooser::resultCopy: { - KateSession::Ptr s = chooser->selectedSession(); // dialog guarantees this to be valid - KateSession::Ptr ns = KateSession::createAnonymousFrom(s, anonymousSessionFile()); - activateSession(ns, false); - break; - } +void KateSessionManager::sessionSave() +{ + saveActiveSession(); // this is the optional point to handle saveSessionAs for anonymous session +} - // exit the app lateron - case KateSessionChooser::resultQuit: - return false; +void KateSessionManager::sessionSaveAs() +{ + const QString newName = askForNewSessionName(activeSession()); - case KateSessionChooser::resultNew: - default: - activateAnonymousSession(); - break; + if (newName.isEmpty()) { + return; } - // write back our nice boolean :) - if (success && chooser->reopenLastSession()) { - KConfigGroup generalConfig(KSharedConfig::openConfig(), QStringLiteral("General")); - - if (res == KateSessionChooser::resultOpen) { - generalConfig.writeEntry("Startup Session", "last"); - } else if (res == KateSessionChooser::resultNew) { - generalConfig.writeEntry("Startup Session", "new"); - } + activeSession()->config()->sync(); - generalConfig.sync(); - } + KateSession::Ptr ns = KateSession::createFrom(activeSession(), sessionFileForName(newName), newName); + m_activeSession = ns; + saveActiveSession(); - return success; + emit sessionChanged(); } -void KateSessionManager::sessionNew() +QString KateSessionManager::askForNewSessionName(KateSession::Ptr session, const QString &newName) { - activateSession(giveSession(QString())); -} + if (session->name() == newName && !session->isAnonymous()) { + return QString(); + } -void KateSessionManager::sessionOpen() -{ - QScopedPointer chooser(new KateSessionOpenDialog(nullptr)); + const QString messagePrompt = i18n("Session name:"); + const KLocalizedString messageExist = ki18n("There is already an existing session with your chosen name: %1\n" + "Please choose a different one."); + const QString messageEmpty = i18n("To save a session, you must specify a name."); - const int res = chooser->exec(); + QString messageTotal = messagePrompt; + QString name = newName; - if (res == KateSessionOpenDialog::resultCancel) { - return; - } + while (true) { + QString preset = name; + + if (name.isEmpty()) { + preset = suggestNewSessionName(session->name()); + messageTotal = messageEmpty + QStringLiteral("\n\n") + messagePrompt; + + } else if (QFile::exists(sessionFileForName(name))) { + preset = suggestNewSessionName(name); + if (preset.isEmpty()) { + // Very unlikely, but as fall back we keep users input + preset = name; + } + messageTotal = messageExist.subs(name).toString() + QStringLiteral("\n\n") + messagePrompt; + + } else { + return name; + } - KateSession::Ptr s = chooser->selectedSession(); + QInputDialog dlg(KateApp::self()->activeKateMainWindow()); + dlg.setInputMode(QInputDialog::TextInput); + if (session->isAnonymous()) { + dlg.setWindowTitle(i18n("Specify a name for this session")); + } else { + dlg.setWindowTitle(i18n("Specify a new name for session: %1", session->name())); + } + dlg.setLabelText(messageTotal); + dlg.setTextValue(preset); + dlg.resize(900,100); // FIXME Calc somehow a proper size + bool ok = dlg.exec(); + name = dlg.textValue(); - if (s) { - activateSession(s); + if (!ok) { + return QString(); + } } } -void KateSessionManager::sessionSave() +QString KateSessionManager::suggestNewSessionName(const QString &target) { - saveActiveSession(); // this is the optional point to handle saveSessionAs for anonymous session + if (target.isEmpty()) { + // Here could also a default name set or the current session name used + return QString(); + } + + const QString mask = QStringLiteral("%1 (%2)"); + QString name; + + for (int i = 2; i < 1000000; i++) { // Should be enough to get an unique name + name = mask.arg(target).arg(i); + + if (!QFile::exists(sessionFileForName(name))) { + return name; + } + } + + return QString(); } -void KateSessionManager::sessionSaveAs() +void KateSessionManager::sessionManage() { - if (newSessionName()) { - saveActiveSession(); - emit sessionChanged(); - } + QScopedPointer(new KateSessionManageDialog(KateApp::self()->activeKateMainWindow()))->exec(); } -bool KateSessionManager::newSessionName() + +bool KateSessionManager::sessionIsActive(const QString &session) { - bool alreadyExists = false; + // Try to avoid unneed action + if (activeSession() && activeSession()->name() == session) { + return true; + } - do { - bool ok = false; - const QString name = QInputDialog::getText(QApplication::activeWindow(), - i18n("Specify New Name for Current Session"), - alreadyExists ? i18n("There is already an existing session with your chosen name.\nPlease choose a different one\nSession name:") : i18n("Session name:"), - QLineEdit::Normal, activeSession()->name(), &ok); + QDBusConnectionInterface *i = QDBusConnection::sessionBus().interface(); + if (!i) { + return false; + } - if (!ok) { - return false; - } + // look up all running kate instances and there sessions + QDBusReply servicesReply = i->registeredServiceNames(); + QStringList services; + if (servicesReply.isValid()) { + services = servicesReply.value(); + } - if (name.isEmpty()) { - KMessageBox::sorry(nullptr, i18n("To save a session, you must specify a name."), i18n("Missing Session Name")); + for (const QString &s : qAsConst(services)) { + if (!s.startsWith(QStringLiteral("org.kde.kate-"))) { continue; } - const QString file = sessionFileForName(name); - if (QFile::exists(file)) { - alreadyExists = true; - continue; + KateRunningInstanceInfo rii(s); + if (rii.valid && rii.sessionName == session) { + return true; } + } - activeSession()->config()->sync(); - KateSession::Ptr ns = KateSession::createFrom(activeSession(), file, name); - m_activeSession = ns; - - emit sessionChanged(); - - alreadyExists = false; - } while (alreadyExists); - return true; + return false; } -void KateSessionManager::sessionManage() -{ - QScopedPointer(new KateSessionManageDialog(nullptr))->exec(); -} QString KateSessionManager::anonymousSessionFile() const { const QString file = m_sessionsDir + QStringLiteral("/../anonymous.katesession"); return QDir().cleanPath(file); } QString KateSessionManager::sessionFileForName(const QString &name) const { Q_ASSERT(!name.isEmpty()); const QString sname = QString::fromLatin1(QUrl::toPercentEncoding(name, QByteArray(), QByteArray("."))); return m_sessionsDir + QStringLiteral("/") + sname + QStringLiteral(".katesession"); } KateSessionList KateSessionManager::sessionList() { return m_sessions.values(); } void KateSessionManager::updateJumpListActions(const QStringList &sessionList) { #if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) KService::Ptr service = KService::serviceByStorageId(qApp->desktopFileName()); if (!service) { return; } QScopedPointer df(new KDesktopFile(service->entryPath())); QStringList newActions = df->readActions(); // try to keep existing custom actions intact, only remove our "Session" actions and add them back later newActions.erase(std::remove_if(newActions.begin(), newActions.end(), [](const QString &action) { return action.startsWith(QLatin1String("Session ")); }), newActions.end()); // Limit the number of list entries we like to offer const int maxEntryCount = std::min(sessionList.count(), 10); // sessionList is ordered by time, but we like it alphabetical to avoid even more a needed update QStringList sessionSubList = sessionList.mid(0, maxEntryCount); sessionSubList.sort(); // we compute the new group names in advance so we can tell whether we changed something // and avoid touching the desktop file leading to an expensive ksycoca recreation QStringList sessionActions; sessionActions.reserve(maxEntryCount); for (int i = 0; i < maxEntryCount; ++i) { sessionActions << QStringLiteral("Session %1").arg(QString::fromLatin1(QCryptographicHash::hash(sessionSubList.at(i).toUtf8() , QCryptographicHash::Md5).toHex())); } newActions += sessionActions; // nothing to do if (df->readActions() == newActions) { return; } const QString &localPath = service->locateLocal(); if (service->entryPath() != localPath) { df.reset(df->copyTo(localPath)); } // remove all Session action groups first to not leave behind any cruft for (const QString &action : df->readActions()) { if (action.startsWith(QLatin1String("Session "))) { // TODO is there no deleteGroup(KConfigGroup)? df->deleteGroup(df->actionGroup(action).name()); } } for (int i = 0; i < maxEntryCount; ++i) { const QString &action = sessionActions.at(i); // is a transform of sessionSubList, so count and order is identical const QString &session = sessionSubList.at(i); KConfigGroup grp = df->actionGroup(action); grp.writeEntry(QStringLiteral("Name"), session); grp.writeEntry(QStringLiteral("Exec"), QStringLiteral("kate -s %1").arg(KShell::quoteArg(session))); // TODO proper executable name? } df->desktopGroup().writeXdgListEntry("Actions", newActions); #endif } //END KateSessionManager diff --git a/kate/session/katesessionmanager.h b/kate/session/katesessionmanager.h index 3a3f19b8c..44e00e8d8 100644 --- a/kate/session/katesessionmanager.h +++ b/kate/session/katesessionmanager.h @@ -1,216 +1,249 @@ /* This file is part of the KDE project * * Copyright (C) 2005 Christoph Cullmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef __KATE_SESSION_MANAGER_H__ #define __KATE_SESSION_MANAGER_H__ #include "katesession.h" #include #include typedef QList KateSessionList; class KATE_TESTS_EXPORT KateSessionManager : public QObject { Q_OBJECT + friend class KateSessionManageDialog; + public: KateSessionManager(QObject *parent = nullptr, const QString &sessionsDir = QString()); ~KateSessionManager() override; /** * allow access to the session list * kept up to date by watching the dir */ KateSessionList sessionList(); /** * activate session by \p name * first, it will look if a session with this name exists in list * if yes, it will use this session, else it will create a new session file * @param name name of the session to activate * @param closeAndSaveLast try to close and save last session or not? * @param loadNew load new session stuff? * @return false==session has been delegated, true==session has been activated in this distance */ bool activateSession(const QString &name, const bool closeAndSaveLast = true, const bool loadNew = true); /** * activates new/anonymous session */ bool activateAnonymousSession(); /** * save current session * @param rememberAsLast remember this session as last used? * @return success */ bool saveActiveSession(bool rememberAsLast = false); /** * return the current active session * sessionFile == empty means we have no session around for this instance of kate * @return session active atm */ inline KateSession::Ptr activeSession() { return m_activeSession; } /** * session dir * @return global session dir */ inline const QString &sessionsDir() const { return m_sessionsDir; } /** * initial session chooser, on app start * @return success, if false, app should exit */ bool chooseSession(); public Q_SLOTS: /** * try to start a new session * asks user first for name */ void sessionNew(); - /** - * try to open a existing session - */ - void sessionOpen(); - /** * try to save current session */ void sessionSave(); /** * try to save as current session */ void sessionSaveAs(); /** * show dialog to manage our sessions */ void sessionManage(); Q_SIGNALS: /** * Emitted, whenever the session changes, e.g. when it was renamed. */ void sessionChanged(); + /** + * Emitted whenever the session list has changed. + * @see sessionList() + */ + void sessionListChanged(); + /** * module internal APIs */ public: /** * return session with given name * if no existing session matches, create new one with this name * @param name session name */ KateSession::Ptr giveSession(const QString &name); /** - * deletes session file and removes the session from sessions list + * Try to delete the @p session and removes the session from sessions list + * @param the session to delete + * @return true on success, false if @p session is currently in use + */ + bool deleteSession(KateSession::Ptr session); + + /** + * Try to copy the @p session to a new session @p newName. + * Will ask by @c askForNewSessionName() for a differend name when @p newName is already in use or is an + * empty string. + * @param newName is wished name of the new session + * @return the new session name on success, otherwise an empty string + * @see askForNewSessionName() */ - void deleteSession(KateSession::Ptr session); + QString copySession(KateSession::Ptr session, const QString &newName = QString()); /** - * renames the session to \p newName - * @param session pointer to the session - * @param newName new name of the session - * @return true if successful + * Try to rename the @p session to @p newName. + * Will ask by @c askForNewSessionName() for a differend name when @p newName is already in use or is an + * empty string. + * @param newName is wished new name of the session + * @return the new session name on success, otherwise an empty string + * @see askForNewSessionName() */ - bool renameSession(KateSession::Ptr session, const QString &newName); + QString renameSession(KateSession::Ptr session, const QString &newName = QString()); /** * activate a session * first, it will look if a session with this name exists in list * if yes, it will use this session, else it will create a new session file * @param session session to activate * @param closeAndSaveLast try to close and save last session or not? * @param loadNew load new session stuff? * @return false==session has been delegated, true==session has been activated in this distance */ bool activateSession(KateSession::Ptr session, const bool closeAndSaveLast = true, const bool loadNew = true); private Q_SLOTS: /** * trigger update of session list */ void updateSessionList(); private: /** - * Asks the user for a new session name. Used by save as for example. + * Ask the user for a new session name, when needed. + * @param session is the session to rename or copy + * @param newName is a preset value. Is @p newName not already in use is nothing asked + * @return a (currently) not used new session name or an empty string when + * user aborted or when @p newName is the current session name. + */ + QString askForNewSessionName(KateSession::Ptr session, const QString &newName = QString()); + + /** + * Try to generate a new session name from @p target by a number suffix. + * @param target is the base name + * @return a (currently) not used session name or an empty string */ - bool newSessionName(); + QString suggestNewSessionName(const QString &target); /** * returns session config file according to policy */ QString sessionFileForName(const QString &name) const; + /** + * @return true when @p session is active in any Kate instance, otherwise false + */ + bool sessionIsActive(const QString &session); + /** * returns session file for anonymous session */ QString anonymousSessionFile() const; /** * helper function to save the session to a given config object */ void saveSessionTo(KConfig *sc) const; /** * restore sessions documents, windows, etc... */ void loadSession(const KateSession::Ptr &session) const; /** * Writes sessions as jump list actions to the kate.desktop file */ void updateJumpListActions(const QStringList &sessionList); private: /** * absolute path to dir in home dir where to store the sessions */ QString m_sessionsDir; /** * list of current available sessions */ QHash m_sessions; /** * current active session */ KateSession::Ptr m_activeSession; class KDirWatch *m_dirWatch; }; #endif diff --git a/kate/session/katesessionopendialog.cpp b/kate/session/katesessionopendialog.cpp deleted file mode 100644 index 2b43aebec..000000000 --- a/kate/session/katesessionopendialog.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/* This file is part of the KDE project - * - * Copyright (C) 2005 Christoph Cullmann - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "katesessionopendialog.h" - -#include "kateapp.h" -#include "katesessionmanager.h" -#include "katesessionchooseritem.h" - -#include -#include - -#include -#include -#include -#include - -KateSessionOpenDialog::KateSessionOpenDialog(QWidget *parent) - : QDialog(parent) - -{ - setWindowTitle(i18n("Open Session")); - - QVBoxLayout *mainLayout = new QVBoxLayout(this); - setLayout(mainLayout); - - m_sessions = new QTreeWidget(this); - m_sessions->setMinimumSize(400, 200); - mainLayout->addWidget(m_sessions); - - QStringList header; - header << i18n("Session Name"); - header << i18nc("The number of open documents", "Open Documents"); - m_sessions->setHeaderLabels(header); - m_sessions->setRootIsDecorated(false); - m_sessions->setItemsExpandable(false); - m_sessions->setAllColumnsShowFocus(true); - m_sessions->setSelectionBehavior(QAbstractItemView::SelectRows); - m_sessions->setSelectionMode(QAbstractItemView::SingleSelection); - - KateSessionList slist = KateApp::self()->sessionManager()->sessionList(); - qSort(slist.begin(), slist.end(), KateSession::compareByName); - - foreach(const KateSession::Ptr & session, slist) { - new KateSessionChooserItem(m_sessions, session); - } - - m_sessions->resizeColumnToContents(0); - - connect(m_sessions, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(selectionChanged(QTreeWidgetItem*,QTreeWidgetItem*))); - connect(m_sessions, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(slotOpen())); - - // buttons - QDialogButtonBox *buttons = new QDialogButtonBox(this); - mainLayout->addWidget(buttons); - - QPushButton *cancelButton = new QPushButton; - KGuiItem::assign(cancelButton, KStandardGuiItem::cancel()); - connect(cancelButton, SIGNAL(clicked()), this, SLOT(slotCanceled())); - buttons->addButton(cancelButton, QDialogButtonBox::RejectRole); - - m_openButton = new QPushButton(QIcon::fromTheme(QStringLiteral("document-open")), i18n("&Open")); - m_openButton->setDefault(true); - m_openButton->setEnabled(false); - connect(m_openButton, SIGNAL(clicked()), this, SLOT(slotOpen())); - buttons->addButton(m_openButton, QDialogButtonBox::AcceptRole); - - setResult(resultCancel); -} - -KateSessionOpenDialog::~KateSessionOpenDialog() -{} - -KateSession::Ptr KateSessionOpenDialog::selectedSession() -{ - KateSessionChooserItem *item = static_cast(m_sessions->currentItem()); - - if (!item) { - return KateSession::Ptr(); - } - - return item->session; -} - -void KateSessionOpenDialog::slotCanceled() -{ - done(resultCancel); -} - -void KateSessionOpenDialog::slotOpen() -{ - done(resultOk); -} - -void KateSessionOpenDialog::selectionChanged(QTreeWidgetItem *current, QTreeWidgetItem *) -{ - Q_UNUSED(current); - m_openButton->setEnabled(true); -} - diff --git a/kate/session/katesessionopendialog.h b/kate/session/katesessionopendialog.h deleted file mode 100644 index 34ca173e8..000000000 --- a/kate/session/katesessionopendialog.h +++ /dev/null @@ -1,62 +0,0 @@ -/* This file is part of the KDE project - * - * Copyright (C) 2005 Christoph Cullmann - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __KATE_SESSION_OPEN_DIALOG_H__ -#define __KATE_SESSION_OPEN_DIALOG_H__ - -#include - -class QPushButton; -class QTreeWidget; -class QTreeWidgetItem; - -#include "katesession.h" - -class KateSessionOpenDialog : public QDialog -{ - Q_OBJECT - -public: - KateSessionOpenDialog(QWidget *parent); - ~KateSessionOpenDialog() override; - - KateSession::Ptr selectedSession(); - - enum { - resultOk, - resultCancel - }; - -protected Q_SLOTS: - void slotCanceled(); - void slotOpen(); - - /** - * selection has changed - */ - void selectionChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous); - -private: - QTreeWidget *m_sessions; - QPushButton *m_openButton; -}; - -#endif -