diff --git a/CMakeLists.txt b/CMakeLists.txt index 9a44e360c..db64adb8b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,91 +1,40 @@ # 3.1 is required for `target_sources`. -cmake_minimum_required (VERSION 3.1 FATAL_ERROR) +cmake_minimum_required(VERSION 3.1 FATAL_ERROR) -# Kate project -project (kate) +project(kate) -set (QT_MIN_VERSION "5.4.0") +set(QT_MIN_VERSION "5.4.0") set(KF5_DEP_VERSION "5.40.0") -# KDE Application Version, managed by release script -set (KDE_APPLICATIONS_VERSION_MAJOR "19") -set (KDE_APPLICATIONS_VERSION_MINOR "11") -set (KDE_APPLICATIONS_VERSION_MICRO "70") -set (KDE_APPLICATIONS_VERSION "${KDE_APPLICATIONS_VERSION_MAJOR}.${KDE_APPLICATIONS_VERSION_MINOR}.${KDE_APPLICATIONS_VERSION_MICRO}") +# KDE Applications version, managed by release script. +set(KDE_APPLICATIONS_VERSION_MAJOR "19") +set(KDE_APPLICATIONS_VERSION_MINOR "11") +set(KDE_APPLICATIONS_VERSION_MICRO "70") +set(KDE_APPLICATIONS_VERSION "${KDE_APPLICATIONS_VERSION_MAJOR}.${KDE_APPLICATIONS_VERSION_MINOR}.${KDE_APPLICATIONS_VERSION_MICRO}") -# we need some parts of the ECM CMake helpers -find_package (ECM ${KF5_DEP_VERSION} REQUIRED NO_MODULE) -set (CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) -include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) -include(ECMOptionalAddSubdirectory) -include(ECMInstallIcons) -include(ECMSetupVersion) -include(ECMMarkNonGuiExecutable) -include(ECMGenerateHeaders) -include(ECMAddAppIcon) -include(GenerateExportHeader) - -include(CMakePackageConfigHelpers) -include(FeatureSummary) -include(WriteBasicConfigVersionFile) -include (CheckFunctionExists) - -include(KDEInstallDirs) -include(KDECMakeSettings) - -find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED Core DBus Widgets Sql) - -if(BUILD_TESTING) - find_package(Qt5Test ${QT_MIN_VERSION} CONFIG REQUIRED) -endif() +# We need some parts of the ECM CMake helpers. +find_package(ECM ${KF5_DEP_VERSION} QUIET REQUIRED NO_MODULE) -# Load the frameworks we need -find_package(KF5 "${KF5_DEP_VERSION}" REQUIRED COMPONENTS - Config - Crash - I18n - JobWidgets - KIO - Parts - TextEditor - WindowSystem - XmlGui - IconThemes -) - -# some optional deps, to make compiling on some OSes easier -find_package(KF5 "${KF5_DEP_VERSION}" COMPONENTS - Activities - DocTools -) - -if (KF5Activities_FOUND) - add_definitions(-DKActivities_FOUND) -endif () - -# config.h -configure_file(config.h.cmake config.h) - -# let our config.h be found first in any case -include_directories (BEFORE ${CMAKE_CURRENT_BINARY_DIR}) +# We append to the module path so modules can be overriden from the command line. +list(APPEND CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) # Allow adding Qt resource files with `add_executable` or `target_sources` instead of # `qt5_add_resources`. See https://cmake.org/cmake/help/v3.0/manual/cmake-qt.7.html#autorcc. set(CMAKE_AUTORCC ON) -# kwrite -ecm_optional_add_subdirectory (kwrite) +include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) +include(KDEInstallDirs) +include(KDECMakeSettings) -# kate application -ecm_optional_add_subdirectory (kate) +include(ECMOptionalAddSubdirectory) +include(ECMAddAppIcon) +include(ECMInstallIcons) -# addons, e.g. kate plugins, plasma applets, ... -ecm_optional_add_subdirectory (addons) +include(FeatureSummary) -# docs, if doc tools around -if (KF5DocTools_FOUND) - ecm_optional_add_subdirectory (doc) -endif () +ecm_optional_add_subdirectory(addons) +ecm_optional_add_subdirectory(kwrite) +ecm_optional_add_subdirectory(kate) +ecm_optional_add_subdirectory(doc) -# tell about our features (and what is missing) -feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) +feature_summary(INCLUDE_QUIET_PACKAGES WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/addons/backtracebrowser/CMakeLists.txt b/addons/backtracebrowser/CMakeLists.txt index a63940bcb..8ca06ff4f 100644 --- a/addons/backtracebrowser/CMakeLists.txt +++ b/addons/backtracebrowser/CMakeLists.txt @@ -1,22 +1,22 @@ add_library(katebacktracebrowserplugin MODULE "") target_compile_definitions(katebacktracebrowserplugin PRIVATE TRANSLATION_DOMAIN="katebacktracebrowserplugin") target_link_libraries(katebacktracebrowserplugin PRIVATE KF5::TextEditor) -ki18n_wrap_ui(UI_SRCS btbrowserwidget.ui btconfigwidget.ui) -target_sources(katebacktracebrowserplugin PRIVATE ${UI_SRCS}) +ki18n_wrap_ui(UI_SOURCES btbrowserwidget.ui btconfigwidget.ui) +target_sources(katebacktracebrowserplugin PRIVATE ${UI_SOURCES}) target_sources( katebacktracebrowserplugin PRIVATE katebacktracebrowser.cpp btparser.cpp btfileindexer.cpp btdatabase.cpp ) kcoreaddons_desktop_to_json(katebacktracebrowserplugin katebacktracebrowserplugin.desktop) install(TARGETS katebacktracebrowserplugin DESTINATION ${PLUGIN_INSTALL_DIR}/ktexteditor) if(BUILD_TESTING) add_subdirectory(autotests) endif() diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index dc18327b0..d1ffed20f 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -1,4 +1,9 @@ -# docs -ecm_optional_add_subdirectory( kate ) -ecm_optional_add_subdirectory( katepart ) -ecm_optional_add_subdirectory( kwrite ) +find_package(KF5DocTools QUIET) + +if(NOT KF5DocTools_FOUND) + return() +endif() + +ecm_optional_add_subdirectory(kate) +ecm_optional_add_subdirectory(katepart) +ecm_optional_add_subdirectory(kwrite) diff --git a/doc/kate/CMakeLists.txt b/doc/kate/CMakeLists.txt index 9ba3c4b8f..ecb940f51 100644 --- a/doc/kate/CMakeLists.txt +++ b/doc/kate/CMakeLists.txt @@ -1,4 +1,10 @@ -########### install files ############### +kdoctools_create_handbook( + index.docbook + INSTALL_DESTINATION ${HTML_INSTALL_DIR}/en + SUBDIR kate +) -kdoctools_create_handbook (index.docbook INSTALL_DESTINATION ${HTML_INSTALL_DIR}/en SUBDIR kate) -kdoctools_create_manpage (man-kate.1.docbook 1 INSTALL_DESTINATION ${MAN_INSTALL_DIR}) +kdoctools_create_manpage( + man-kate.1.docbook 1 + INSTALL_DESTINATION ${MAN_INSTALL_DIR} +) diff --git a/doc/katepart/CMakeLists.txt b/doc/katepart/CMakeLists.txt index 6b7fc4323..77c4f6d55 100644 --- a/doc/katepart/CMakeLists.txt +++ b/doc/katepart/CMakeLists.txt @@ -1,4 +1,5 @@ -########### install files ############### -# -# -kdoctools_create_handbook (index.docbook INSTALL_DESTINATION ${HTML_INSTALL_DIR}/en SUBDIR katepart) +kdoctools_create_handbook( + index.docbook + INSTALL_DESTINATION ${HTML_INSTALL_DIR}/en + SUBDIR katepart +) diff --git a/doc/kwrite/CMakeLists.txt b/doc/kwrite/CMakeLists.txt index 5f8071fe3..6bf40bfb9 100644 --- a/doc/kwrite/CMakeLists.txt +++ b/doc/kwrite/CMakeLists.txt @@ -1,3 +1,5 @@ -########### install files ############### - -kdoctools_create_handbook (index.docbook INSTALL_DESTINATION ${HTML_INSTALL_DIR}/en SUBDIR kwrite) +kdoctools_create_handbook( + index.docbook + INSTALL_DESTINATION ${HTML_INSTALL_DIR}/en + SUBDIR kwrite +) diff --git a/kate/CMakeLists.txt b/kate/CMakeLists.txt index 107c77270..9f705a257 100644 --- a/kate/CMakeLists.txt +++ b/kate/CMakeLists.txt @@ -1,154 +1,168 @@ -# -# 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 -) +# We use an intermediate static library because linking tests directly to an executable is not +# possible with CMake yet. +add_library(kate STATIC "") + +configure_file(config.h.in config.h) -# 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/katesessionsaction.cpp - session/katesessionmanager.cpp - session/katesessionmanagedialog.cpp - session/katesession.cpp - - katemdi.cpp - katerunninginstanceinfo.cpp - katequickopen.cpp - katewaiter.h +include(GenerateExportHeader) +generate_export_header( + kate + EXPORT_FILE_NAME katetests_export.h + EXPORT_MACRO_NAME KATE_TESTS_EXPORT ) -ki18n_wrap_ui(KATE_LIBRARY_SRCS - ui/sessionconfigwidget.ui - session/katesessionmanagedialog.ui +target_include_directories( + kate + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/session + ${CMAKE_CURRENT_SOURCE_DIR}/qtsingleapplication + ${CMAKE_CURRENT_BINARY_DIR} # katetests_export.h + config.h ) -qt5_add_resources( KATE_LIBRARY_SRCS data/kate.qrc ) +find_package( + KF5 + QUIET + REQUIRED + COMPONENTS + TextEditor + IconThemes + WindowSystem + DBusAddons + Crash + OPTIONAL_COMPONENTS + Activities +) -add_library(kdeinit_kate STATIC ${KATE_LIBRARY_SRCS}) -target_link_libraries(kdeinit_kate -PUBLIC +target_link_libraries( + kate + PUBLIC KF5::TextEditor - KF5::I18n KF5::IconThemes - KF5::WindowSystem - KF5::GuiAddons + KF5::WindowSystem KF5::DBusAddons - KF5::Crash) + KF5::Crash +) if(KF5Activities_FOUND) - target_link_libraries(kdeinit_kate - PUBLIC - KF5::Activities) + target_link_libraries(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 -) +ki18n_wrap_ui(UI_SOURCES ui/sessionconfigwidget.ui session/katesessionmanagedialog.ui) +target_sources(kate PRIVATE ${UI_SOURCES}) -set(KATE_ICONS_SVG -${CMAKE_CURRENT_SOURCE_DIR}/icons/sc-apps-kate.svgz +set(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 ) -# application only sources -set (KATE_APP_SRCS - main.cpp +set(ICONS_SVG ${CMAKE_CURRENT_SOURCE_DIR}/icons/sc-apps-kate.svgz) + +# Add icon files to the application's source files to have CMake bundle them in the executable. +ecm_add_app_icon(ICONS_SOURCES ICONS ${ICONS_PNG}) +target_sources(kate PRIVATE ${ICONS_SOURCES}) + +target_sources( + kate + PRIVATE + data/kate.qrc + + session/katesession.cpp + session/katesessionmanagedialog.cpp + session/katesessionmanager.cpp + session/katesessionsaction.cpp + + kateapp.cpp + kateappadaptor.cpp + katecolorschemechooser.cpp + kateconfigdialog.cpp + kateconfigplugindialogpage.cpp + katedocmanager.cpp + katefileactions.cpp + katemainwindow.cpp + katemdi.cpp + katemwmodonhddialog.cpp + katepluginmanager.cpp + katequickopen.cpp + katequickopenmodel.cpp + katerunninginstanceinfo.cpp + katesavemodifieddialog.cpp + katetabbar.cpp + katetabbutton.cpp + kateviewmanager.cpp + kateviewspace.cpp + katewaiter.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}) +# Use a single application on MacOS + Windows instead of dbus. +if(APPLE OR WIN32) + target_compile_definitions(kate PRIVATE USE_QT_SINGLE_APP) + + target_sources( + kate + PRIVATE + qtsingleapplication/qtlocalpeer.cpp + qtsingleapplication/qtsingleapplication.cpp + qtsingleapplication/qtlockedfile.cpp + ) + + if(WIN32) + target_sources(kate PRIVATE qtsingleapplication/qtlockedfile_win.cpp) + else() + target_sources(kate PRIVATE qtsingleapplication/qtlockedfile_unix.cpp) + endif() endif() -# add icons to application sources, to have them bundled -ecm_add_app_icon(KATE_APP_SRCS ICONS ${KATE_ICONS_PNG}) +# Executable only adds the main definition. +add_executable(kate-bin main.cpp) +target_link_libraries(kate-bin PRIVATE kate) -# create executable -add_executable(kate ${KATE_APP_SRCS}) -target_link_libraries(kate kdeinit_kate) +set_property( + TARGET kate-bin + PROPERTY OUTPUT_NAME kate +) -# own plist magic for mac os +# See https://cmake.org/cmake/help/v3.15/prop_tgt/MACOSX_BUNDLE_INFO_PLIST.html 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") + set_property( + TARGET kate-bin + PROPERTY MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/data/MacOSXBundleInfo.plist.in + ) + + # These are substituted by CMake into plist.in. + set(MACOSX_BUNDLE_DISPLAY_NAME "Kate") + set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.kde.Kate") + set(MACOSX_BUNDLE_BUNDLE_NAME "Kate") + set(MACOSX_BUNDLE_DISPLAY_NAME "Kate") + set(MACOSX_BUNDLE_INFO_STRING "Kate - Advanced Text Editor") + set(MACOSX_BUNDLE_LONG_VERSION_STRING "Kate ${KDE_APPLICATIONS_VERSION}") + set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${KDE_APPLICATIONS_VERSION_MAJOR}.${KDE_APPLICATIONS_VERSION_MINOR}") + set(MACOSX_BUNDLE_BUNDLE_VERSION "${KDE_APPLICATIONS_VERSION}") + set(MACOSX_BUNDLE_COPYRIGHT "2000-2016 The Kate Authors") endif() -# install executable -install(TARGETS kate ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) +install(TARGETS kate-bin ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) -# desktop file -install(PROGRAMS ${CMAKE_CURRENT_SOURCE_DIR}/data/org.kde.kate.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}) +ecm_install_icons( + ICONS ${ICONS_PNG} ${ICONS_SVG} + DESTINATION ${ICON_INSTALL_DIR} + THEME hicolor +) -# appdata -install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/data/org.kde.kate.appdata.xml DESTINATION ${CMAKE_INSTALL_METAINFODIR}) +install( + PROGRAMS ${CMAKE_CURRENT_SOURCE_DIR}/data/org.kde.kate.desktop + DESTINATION ${XDG_APPS_INSTALL_DIR} +) -# install icons -ecm_install_icons(ICONS ${KATE_ICONS_PNG} ${KATE_ICONS_SVG} DESTINATION ${ICON_INSTALL_DIR} THEME hicolor) +install( + FILES ${CMAKE_CURRENT_SOURCE_DIR}/data/org.kde.kate.appdata.xml + DESTINATION ${CMAKE_INSTALL_METAINFODIR} +) -# automatic unit tests -if (BUILD_TESTING) - add_subdirectory(autotests) +if(BUILD_TESTING) + add_subdirectory(autotests) endif() diff --git a/kate/autotests/CMakeLists.txt b/kate/autotests/CMakeLists.txt index efc57c085..4c387947e 100644 --- a/kate/autotests/CMakeLists.txt +++ b/kate/autotests/CMakeLists.txt @@ -1,17 +1,25 @@ - include(ECMMarkAsTest) +find_package(Qt5Test QUIET REQUIRED) + macro(kate_executable_tests) foreach(_testname ${ARGN}) add_executable(${_testname} ${_testname}.cpp) + + target_link_libraries( + ${_testname} + PRIVATE + kate + Qt5::Test + ) + add_test(NAME kateapp-${_testname} COMMAND ${_testname}) - target_link_libraries(${_testname} kdeinit_kate Qt5::Test) ecm_mark_as_test(${_testname}) - endforeach(_testname) -endmacro(kate_executable_tests) + endforeach() +endmacro() kate_executable_tests( session_test session_manager_test sessions_action_test ) diff --git a/config.h.cmake b/kate/config.h.in similarity index 82% rename from config.h.cmake rename to kate/config.h.in index 1cb4de911..47f3bca97 100644 --- a/config.h.cmake +++ b/kate/config.h.in @@ -1,10 +1,10 @@ #ifndef KATE_CONFIG_H #define KATE_CONFIG_H /* config.h. Generated by cmake from config.h.cmake */ #define KATE_VERSION "${KDE_APPLICATIONS_VERSION}" -#cmakedefine KActivities_FOUND 1 +#cmakedefine KF5Activities_FOUND #endif diff --git a/kate/kateapp.h b/kate/kateapp.h index f30ec7b52..29ddee44d 100644 --- a/kate/kateapp.h +++ b/kate/kateapp.h @@ -1,376 +1,376 @@ /* This file is part of the KDE project Copyright (C) 2001 Christoph Cullmann Copyright (C) 2002 Joseph Wenninger 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_APP_H__ #define __KATE_APP_H__ #include -#include "kateprivate_export.h" +#include "katetests_export.h" #include "katemainwindow.h" #include "katedocmanager.h" #include "katepluginmanager.h" #include "katesessionmanager.h" #include "kateappadaptor.h" #include #include class KateSessionManager; class KateMainWindow; class KatePluginManager; class KateDocManager; class KateAppCommands; class KateAppAdaptor; class QCommandLineParser; /** * Kate Application * This class represents the core kate application object */ class KATE_TESTS_EXPORT KateApp : public QObject { Q_OBJECT /** * constructors & accessor to app object + plugin interface for it */ public: /** * application constructor */ KateApp(const QCommandLineParser &args); /** * get kate inited * @return false, if application should exit */ bool init(); /** * application destructor */ ~KateApp() override; /** * static accessor to avoid casting ;) * @return app instance */ static KateApp *self(); /** * KTextEditor::Application wrapper * @return KTextEditor::Application wrapper. */ KTextEditor::Application *wrapper() { return &m_wrapper; } /** * kate init */ private: /** * restore a old kate session */ void restoreKate(); /** * try to start kate * @return success, if false, kate should exit */ bool startupKate(); /** * kate shutdown */ public: /** * shutdown kate application * @param win mainwindow which is used for dialogs */ void shutdownKate(KateMainWindow *win); /** * other accessors for global unique instances */ public: /** * accessor to plugin manager * @return plugin manager instance */ KatePluginManager *pluginManager(); /** * accessor to document manager * @return document manager instance */ KateDocManager *documentManager(); /** * accessor to session manager * @return session manager instance */ KateSessionManager *sessionManager(); /** * window management */ public: /** * create a new main window, use given config if any for restore * @param sconfig session config object * @param sgroup session group for this window * @return new constructed main window */ KateMainWindow *newMainWindow(KConfig *sconfig = nullptr, const QString &sgroup = QString()); /** * add the mainwindow given * should be called in mainwindow constructor * @param mainWindow window to remove */ void addMainWindow(KateMainWindow *mainWindow); /** * removes the mainwindow given, DOES NOT DELETE IT * should be called in mainwindow destructor * @param mainWindow window to remove */ void removeMainWindow(KateMainWindow *mainWindow); /** * give back current active main window * can only be 0 at app start or exit * @return current active main window */ KateMainWindow *activeKateMainWindow(); /** * give back number of existing main windows * @return number of main windows */ int mainWindowsCount() const; /** * give back the window you want * @param n window index * @return requested main window */ KateMainWindow *mainWindow(int n); int mainWindowID(KateMainWindow *window); /** * some stuff for the dcop API */ public: /** * open url with given encoding * used by kate if --use given * @param url filename * @param encoding encoding name * @param isTempFile whether the file is temporary * @return success */ bool openUrl(const QUrl &url, const QString &encoding, bool isTempFile); /** * checks if the current instance is in a given activity * @param activity activity to check * @return true if the window is in the given activity, false otherwise */ bool isOnActivity(const QString &activity); KTextEditor::Document *openDocUrl(const QUrl &url, const QString &encoding, bool isTempFile); void emitDocumentClosed(const QString &token); /** * position cursor in current active view * will clear selection * @param line line to set * @param column column to set * @return success */ bool setCursor(int line, int column); /** * helper to handle stdin input * open a new document/view, fill it with the text given * @param text text to fill in the new doc/view * @param encoding encoding to set for the document, if any * @return success */ bool openInput(const QString &text, const QString &encoding); // // KTextEditor::Application interface, called by wrappers via invokeMethod // public Q_SLOTS: /** * Get a list of all main windows. * @return all main windows */ QList mainWindows() { // assemble right list QList windows; windows.reserve(m_mainWindows.size()); for (const auto mainWindow : qAsConst(m_mainWindows)) { windows.push_back(mainWindow->wrapper()); } return windows; } /** * Accessor to the active main window. * \return a pointer to the active mainwindow */ KTextEditor::MainWindow *activeMainWindow() { // either return wrapper or nullptr if (KateMainWindow *a = activeKateMainWindow()) { return a->wrapper(); } return nullptr; } /** * Get a list of all documents that are managed by the application. * This might contain less documents than the editor has in his documents () list. * @return all documents the application manages */ QList documents() { return m_docManager.documentList(); } /** * Get the document with the URL \p url. * if multiple documents match the searched url, return the first found one... * \param url the document's URL * \return the document with the given \p url or NULL, if none found */ KTextEditor::Document *findUrl(const QUrl &url) { return m_docManager.findDocument(url); } /** * Open the document \p url with the given \p encoding. * if the url is empty, a new empty document will be created * \param url the document's url * \param encoding the preferred encoding. If encoding is QString() the * encoding will be guessed or the default encoding will be used. * \return a pointer to the created document */ KTextEditor::Document *openUrl(const QUrl &url, const QString &encoding = QString()) { return m_docManager.openUrl(url, encoding); } /** * Close the given \p document. If the document is modified, user will be asked if he wants that. * \param document the document to be closed * \return \e true on success, otherwise \e false */ bool closeDocument(KTextEditor::Document *document) { return m_docManager.closeDocument(document); } /** * Close a list of documents. If any of them are modified, user will be asked if he wants that. * Use this, if you want to close multiple documents at once, as the application might * be able to group the "do you really want that" dialogs into one. * \param documents list of documents to be closed * \return \e true on success, otherwise \e false */ bool closeDocuments(const QList &documents) { return m_docManager.closeDocumentList(documents); } /** * Get a plugin for the plugin with with identifier \p name. * \param name the plugin's name * \return pointer to the plugin if a plugin with \p name is loaded, otherwise nullptr */ KTextEditor::Plugin *plugin(const QString &name); /** * Ask app to quit. The app might interact with the user and decide that * quitting is not possible and return false. * * \return true if the app could quit */ bool quit() { shutdownKate(activeKateMainWindow()); return true; } /** * A message is received from an external instance, if we use QtSingleApplication * * \p message is a serialized message (at the moment just the file list separated by ';') * \p socket is the QLocalSocket used for the communication */ void remoteMessageReceived(const QString &message, QObject *socket); protected: /** * Event filter for QApplication to handle mac os like file open */ bool eventFilter(QObject *obj, QEvent *event) override; private: /** * kate's command line args */ const QCommandLineParser &m_args; /** * known main windows */ QList m_mainWindows; /** * Wrapper of application for KTextEditor */ KTextEditor::Application m_wrapper; /** * document manager */ KateDocManager m_docManager; /** * dbus interface */ KateAppAdaptor m_adaptor; /** * plugin manager */ KatePluginManager m_pluginManager; /** * session manager */ KateSessionManager m_sessionManager; }; #endif diff --git a/kate/katepluginmanager.cpp b/kate/katepluginmanager.cpp index 9ed5507be..6fea0cb96 100644 --- a/kate/katepluginmanager.cpp +++ b/kate/katepluginmanager.cpp @@ -1,360 +1,358 @@ /* This file is part of the KDE project Copyright (C) 2001 Christoph Cullmann Copyright (C) 2001 Joseph Wenninger Copyright (C) 2001 Anders Lund 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. */ -#include "config.h" - #include "katepluginmanager.h" #include "kateapp.h" #include "katemainwindow.h" #include "katedebug.h" #include #include #include #include #include #include #include QString KatePluginInfo::saveName() const { return QFileInfo(metaData.fileName()).baseName(); } bool KatePluginInfo::operator<(const KatePluginInfo &other) const { if (sortOrder != other.sortOrder) return sortOrder < other.sortOrder; return saveName() < other.saveName(); } KatePluginManager::KatePluginManager(QObject *parent) : QObject(parent) { setupPluginList(); } KatePluginManager::~KatePluginManager() { unloadAllPlugins(); } void KatePluginManager::setupPluginList() { // activate a hand-picked list of plugins per default, give them a hand-picked sort order for loading const QMap defaultPlugins { { QStringLiteral("katefiletreeplugin"), -1000 } , { QStringLiteral("katesearchplugin"), -900 } , { QStringLiteral("kateprojectplugin"), -800 } , { QStringLiteral("tabswitcherplugin"), -100 } , { QStringLiteral("textfilterplugin"), -100 } #ifndef WIN32 , { QStringLiteral("katefilebrowserplugin"), -100 } // currently works badly on Windows , { QStringLiteral("katekonsoleplugin"), -100 } // currently does not work on Windows at all #endif }; // handle all install KTextEditor plugins m_pluginList.clear(); QSet unique; const QVector plugins = KPluginLoader::findPlugins(QStringLiteral("ktexteditor"), [](const KPluginMetaData & md) { return md.serviceTypes().contains(QStringLiteral("KTextEditor/Plugin")); }); for (const auto &pluginMetaData : plugins) { KatePluginInfo info; info.metaData = pluginMetaData; // only load plugins once, even if found multiple times! if (unique.contains(info.saveName())) continue; info.defaultLoad = defaultPlugins.contains(info.saveName()); info.sortOrder = defaultPlugins.value(info.saveName()); info.load = false; info.plugin = nullptr; m_pluginList.push_back(info); unique.insert (info.saveName()); } // sort to ensure some deterministic plugin load order, this is important for tool-view creation order std::sort(m_pluginList.begin(), m_pluginList.end()); // construct fast lookup map, do this after vector has final size, resize will invalidate the pointers! m_name2Plugin.clear(); for (auto& pluginInfo : m_pluginList) { m_name2Plugin[pluginInfo.saveName()] = &pluginInfo; } } void KatePluginManager::loadConfig(KConfig *config) { // first: unload the plugins unloadAllPlugins(); /** * ask config object */ if (config) { KConfigGroup cg = KConfigGroup(config, QStringLiteral("Kate Plugins")); // disable all plugin if no config, beside the ones marked as default load for (auto& pluginInfo: m_pluginList) { pluginInfo.load = cg.readEntry(pluginInfo.saveName(), pluginInfo.defaultLoad); } } /** * load plugins */ for (auto& pluginInfo : m_pluginList) { if (pluginInfo.load) { /** * load plugin + trigger update of GUI for already existing main windows */ loadPlugin(&pluginInfo); enablePluginGUI(&pluginInfo); // restore config if (auto interface = qobject_cast (pluginInfo.plugin)) { KConfigGroup group(config, QStringLiteral("Plugin:%1:").arg(pluginInfo.saveName())); interface->readSessionConfig(group); } } } } void KatePluginManager::writeConfig(KConfig *config) { Q_ASSERT(config); KConfigGroup cg = KConfigGroup(config, QStringLiteral("Kate Plugins")); foreach(const KatePluginInfo & plugin, m_pluginList) { QString saveName = plugin.saveName(); cg.writeEntry(saveName, plugin.load); // save config if (auto interface = qobject_cast (plugin.plugin)) { KConfigGroup group(config, QStringLiteral("Plugin:%1:").arg(saveName)); interface->writeSessionConfig(group); } } } void KatePluginManager::unloadAllPlugins() { for (auto& pluginInfo : m_pluginList) { if (pluginInfo.plugin) { unloadPlugin(&pluginInfo); } } } void KatePluginManager::enableAllPluginsGUI(KateMainWindow *win, KConfigBase *config) { for (auto& pluginInfo : m_pluginList) { if (pluginInfo.plugin) { enablePluginGUI(&pluginInfo, win, config); } } } void KatePluginManager::disableAllPluginsGUI(KateMainWindow *win) { for (auto& pluginInfo : m_pluginList) { if (pluginInfo.plugin) { disablePluginGUI(&pluginInfo, win); } } } bool KatePluginManager::loadPlugin(KatePluginInfo *item) { /** * try to load the plugin */ auto factory = KPluginLoader(item->metaData.fileName()).factory(); if (factory) { item->plugin = factory->create(this, QVariantList() << item->saveName()); } item->load = item->plugin != nullptr; /** * tell the world about the success */ if (item->plugin) { emit KateApp::self()->wrapper()->pluginCreated(item->saveName(), item->plugin); } return item->plugin != nullptr; } void KatePluginManager::unloadPlugin(KatePluginInfo *item) { disablePluginGUI(item); delete item->plugin; KTextEditor::Plugin *plugin = item->plugin; item->plugin = nullptr; item->load = false; emit KateApp::self()->wrapper()->pluginDeleted(item->saveName(), plugin); } void KatePluginManager::enablePluginGUI(KatePluginInfo *item, KateMainWindow *win, KConfigBase *config) { // plugin around at all? if (!item->plugin) { return; } // lookup if there is already a view for it.. QObject *createdView = nullptr; if (!win->pluginViews().contains(item->plugin)) { // create the view + try to correctly load shortcuts, if it's a GUI Client createdView = item->plugin->createView(win->wrapper()); if (createdView) { win->pluginViews().insert(item->plugin, createdView); } } // load session config if needed if (config && win->pluginViews().contains(item->plugin)) { if (auto interface = qobject_cast (win->pluginViews().value(item->plugin))) { KConfigGroup group(config, QStringLiteral("Plugin:%1:MainWindow:0").arg(item->saveName())); interface->readSessionConfig(group); } } if (createdView) { emit win->wrapper()->pluginViewCreated(item->saveName(), createdView); } } void KatePluginManager::enablePluginGUI(KatePluginInfo *item) { // plugin around at all? if (!item->plugin) { return; } // enable the gui for all mainwindows... for (int i = 0; i < KateApp::self()->mainWindowsCount(); i++) { enablePluginGUI(item, KateApp::self()->mainWindow(i), nullptr); } } void KatePluginManager::disablePluginGUI(KatePluginInfo *item, KateMainWindow *win) { // plugin around at all? if (!item->plugin) { return; } // lookup if there is a view for it.. if (!win->pluginViews().contains(item->plugin)) { return; } // really delete the view of this plugin QObject *deletedView = win->pluginViews().value(item->plugin); delete deletedView; win->pluginViews().remove(item->plugin); emit win->wrapper()->pluginViewDeleted(item->saveName(), deletedView); } void KatePluginManager::disablePluginGUI(KatePluginInfo *item) { // plugin around at all? if (!item->plugin) { return; } // disable the gui for all mainwindows... for (int i = 0; i < KateApp::self()->mainWindowsCount(); i++) { disablePluginGUI(item, KateApp::self()->mainWindow(i)); } } KTextEditor::Plugin *KatePluginManager::plugin(const QString &name) { /** * name known? */ if (!m_name2Plugin.contains(name)) { return nullptr; } /** * real plugin instance, if any ;) */ return m_name2Plugin.value(name)->plugin; } bool KatePluginManager::pluginAvailable(const QString &name) { return m_name2Plugin.contains(name); } class KTextEditor::Plugin *KatePluginManager::loadPlugin(const QString &name, bool permanent) { /** * name known? */ if (!m_name2Plugin.contains(name)) { return nullptr; } /** * load, bail out on error */ loadPlugin(m_name2Plugin.value(name)); if (!m_name2Plugin.value(name)->plugin) { return nullptr; } /** * perhaps patch not load again back to "ok, load it once again on next loadConfig" */ m_name2Plugin.value(name)->load = permanent; return m_name2Plugin.value(name)->plugin; } void KatePluginManager::unloadPlugin(const QString &name, bool permanent) { /** * name known? */ if (!m_name2Plugin.contains(name)) { return; } /** * unload */ unloadPlugin(m_name2Plugin.value(name)); /** * perhaps patch load again back to "ok, load it once again on next loadConfig" */ m_name2Plugin.value(name)->load = !permanent; } diff --git a/kate/kateviewmanager.cpp b/kate/kateviewmanager.cpp index f27a64e00..e69b86f1e 100644 --- a/kate/kateviewmanager.cpp +++ b/kate/kateviewmanager.cpp @@ -1,1256 +1,1255 @@ /* This file is part of the KDE project Copyright (C) 2001 Christoph Cullmann Copyright (C) 2001 Joseph Wenninger Copyright (C) 2001 Anders Lund 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 "kateviewmanager.h" +#include "config.h" #include "kateapp.h" #include "katemainwindow.h" #include "kateviewspace.h" #include "kateupdatedisabler.h" #include #include #include #include #include #include #include #include #include #include #include -#include - -#ifdef KActivities_FOUND +#ifdef KF5Activities_FOUND #include #endif #include #include //END Includes static const qint64 FileSizeAboveToAskUserIfProceedWithOpen = 10 * 1024 * 1024; // 10MB should suffice KateViewManager::KateViewManager(QWidget *parentW, KateMainWindow *parent) : QSplitter(parentW) , m_mainWindow(parent) , m_blockViewCreationAndActivation(false) , m_activeViewRunning(false) , m_minAge(0) , m_guiMergedView(nullptr) { // while init m_init = true; // we don't allow full collapse, see bug 366014 setChildrenCollapsible(false); // important, set them up, as we use them in other methodes setupActions(); KateViewSpace *vs = new KateViewSpace(this, nullptr); addWidget(vs); vs->setActive(true); m_viewSpaceList.append(vs); connect(this, &KateViewManager::viewChanged, this, &KateViewManager::slotViewChanged); connect(KateApp::self()->documentManager(), &KateDocManager::documentCreatedViewManager, this, &KateViewManager::documentCreated); /** * before document is really deleted: cleanup all views! */ connect(KateApp::self()->documentManager(), &KateDocManager::documentWillBeDeleted , this, &KateViewManager::documentWillBeDeleted); /** * handle document deletion transactions * disable view creation in between * afterwards ensure we have views ;) */ connect(KateApp::self()->documentManager(), &KateDocManager::aboutToDeleteDocuments , this, &KateViewManager::aboutToDeleteDocuments); connect(KateApp::self()->documentManager(), &KateDocManager::documentsDeleted , this, &KateViewManager::documentsDeleted); // register all already existing documents m_blockViewCreationAndActivation = true; const QList &docs = KateApp::self()->documentManager()->documentList(); foreach(KTextEditor::Document * doc, docs) { documentCreated(doc); } m_blockViewCreationAndActivation = false; // init done m_init = false; } KateViewManager::~KateViewManager() { /** * remove the single client that is registered at the factory, if any */ if (m_guiMergedView) { mainWindow()->guiFactory()->removeClient(m_guiMergedView); m_guiMergedView = nullptr; } } void KateViewManager::setupActions() { /** * view splitting */ m_splitViewVert = m_mainWindow->actionCollection()->addAction(QStringLiteral("view_split_vert")); m_splitViewVert->setIcon(QIcon::fromTheme(QStringLiteral("view-split-left-right"))); m_splitViewVert->setText(i18n("Split Ve&rtical")); m_mainWindow->actionCollection()->setDefaultShortcut(m_splitViewVert, Qt::CTRL + Qt::SHIFT + Qt::Key_L); connect(m_splitViewVert, &QAction::triggered, this, &KateViewManager::slotSplitViewSpaceVert); m_splitViewVert->setWhatsThis(i18n("Split the currently active view vertically into two views.")); m_splitViewHoriz = m_mainWindow->actionCollection()->addAction(QStringLiteral("view_split_horiz")); m_splitViewHoriz->setIcon(QIcon::fromTheme(QStringLiteral("view-split-top-bottom"))); m_splitViewHoriz->setText(i18n("Split &Horizontal")); m_mainWindow->actionCollection()->setDefaultShortcut(m_splitViewHoriz, Qt::CTRL + Qt::SHIFT + Qt::Key_T); connect(m_splitViewHoriz, &QAction::triggered, this, &KateViewManager::slotSplitViewSpaceHoriz); m_splitViewHoriz->setWhatsThis(i18n("Split the currently active view horizontally into two views.")); m_closeView = m_mainWindow->actionCollection()->addAction(QStringLiteral("view_close_current_space")); m_closeView->setIcon(QIcon::fromTheme(QStringLiteral("view-close"))); m_closeView->setText(i18n("Cl&ose Current View")); m_mainWindow->actionCollection()->setDefaultShortcut(m_closeView, Qt::CTRL + Qt::SHIFT + Qt::Key_R); connect(m_closeView, &QAction::triggered, this, &KateViewManager::slotCloseCurrentViewSpace, Qt::QueuedConnection); m_closeView->setWhatsThis(i18n("Close the currently active split view")); m_closeOtherViews = m_mainWindow->actionCollection()->addAction(QStringLiteral("view_close_others")); m_closeOtherViews->setIcon(QIcon::fromTheme(QStringLiteral("view-close"))); m_closeOtherViews->setText(i18n("Close Inactive Views")); connect(m_closeOtherViews, &QAction::triggered, this, &KateViewManager::slotCloseOtherViews, Qt::QueuedConnection); m_closeOtherViews->setWhatsThis(i18n("Close every view but the active one")); m_hideOtherViews = m_mainWindow->actionCollection()->addAction(QStringLiteral("view_hide_others")); m_hideOtherViews->setIcon(QIcon::fromTheme(QStringLiteral("view-fullscreen"))); m_hideOtherViews->setText(i18n("Hide Inactive Views")); m_hideOtherViews->setCheckable(true); connect(m_hideOtherViews, &QAction::triggered, this, &KateViewManager::slotHideOtherViews, Qt::QueuedConnection); m_hideOtherViews->setWhatsThis(i18n("Hide every view but the active one")); m_toggleSplitterOrientation = m_mainWindow->actionCollection()->addAction(QStringLiteral("view_split_toggle")); m_toggleSplitterOrientation->setText(i18n("Toggle Orientation")); connect(m_toggleSplitterOrientation, &QAction::triggered, this, &KateViewManager::toggleSplitterOrientation, Qt::QueuedConnection); m_toggleSplitterOrientation->setWhatsThis(i18n("Toggles the orientation of the current split view")); goNext = m_mainWindow->actionCollection()->addAction(QStringLiteral("go_next_split_view")); goNext->setText(i18n("Next Split View")); m_mainWindow->actionCollection()->setDefaultShortcut(goNext, Qt::Key_F8); connect(goNext, &QAction::triggered, this, &KateViewManager::activateNextView); goNext->setWhatsThis(i18n("Make the next split view the active one.")); goPrev = m_mainWindow->actionCollection()->addAction(QStringLiteral("go_prev_split_view")); goPrev->setText(i18n("Previous Split View")); m_mainWindow->actionCollection()->setDefaultShortcut(goPrev, Qt::SHIFT + Qt::Key_F8); connect(goPrev, &QAction::triggered, this, &KateViewManager::activatePrevView); goPrev->setWhatsThis(i18n("Make the previous split view the active one.")); QAction * a = m_mainWindow->actionCollection()->addAction(QStringLiteral("view_split_move_right")); a->setText(i18n("Move Splitter Right")); connect(a, &QAction::triggered, this, &KateViewManager::moveSplitterRight); a->setWhatsThis(i18n("Move the splitter of the current view to the right")); a = m_mainWindow->actionCollection()->addAction(QStringLiteral("view_split_move_left")); a->setText(i18n("Move Splitter Left")); connect(a, &QAction::triggered, this, &KateViewManager::moveSplitterLeft); a->setWhatsThis(i18n("Move the splitter of the current view to the left")); a = m_mainWindow->actionCollection()->addAction(QStringLiteral("view_split_move_up")); a->setText(i18n("Move Splitter Up")); connect(a, &QAction::triggered, this, &KateViewManager::moveSplitterUp); a->setWhatsThis(i18n("Move the splitter of the current view up")); a = m_mainWindow->actionCollection()->addAction(QStringLiteral("view_split_move_down")); a->setText(i18n("Move Splitter Down")); connect(a, &QAction::triggered, this, &KateViewManager::moveSplitterDown); a->setWhatsThis(i18n("Move the splitter of the current view down")); } void KateViewManager::updateViewSpaceActions() { m_closeView->setEnabled(m_viewSpaceList.count() > 1); m_closeOtherViews->setEnabled(m_viewSpaceList.count() > 1); m_toggleSplitterOrientation->setEnabled(m_viewSpaceList.count() > 1); goNext->setEnabled(m_viewSpaceList.count() > 1); goPrev->setEnabled(m_viewSpaceList.count() > 1); } void KateViewManager::slotDocumentNew() { createView(); } void KateViewManager::slotDocumentOpen() { // try to start dialog in useful dir: either dir of current doc or last used one KTextEditor::View * const cv = activeView(); QUrl startUrl = cv ? cv->document()->url() : QUrl(); if (startUrl.isValid()) { m_lastOpenDialogUrl = startUrl; } else { startUrl = m_lastOpenDialogUrl; } const QList urls = QFileDialog::getOpenFileUrls(m_mainWindow, i18n("Open File"), startUrl); /** * emit size warning, for local files */ QString fileListWithTooLargeFiles; Q_FOREACH(const QUrl & url, urls) { if (!url.isLocalFile()) { continue; } const auto size = QFile(url.toLocalFile()).size(); if (size > FileSizeAboveToAskUserIfProceedWithOpen) { fileListWithTooLargeFiles += QStringLiteral("
  • %1 (%2MB)
  • ").arg(url.fileName()).arg(size / 1024 / 1024); } } if (!fileListWithTooLargeFiles.isEmpty()) { const QString text = i18n("

    You are attempting to open one or more large files:

      %1

    Do you want to proceed?

    Beware that kate may stop responding for some time when opening large files.

    " , fileListWithTooLargeFiles); const auto ret = KMessageBox::warningYesNo(this, text, i18n("Opening Large File"), KStandardGuiItem::cont(), KStandardGuiItem::stop()); if (ret == KMessageBox::No) { return; } } // activate view of last opened document KateDocumentInfo docInfo; docInfo.openedByUser = true; if (KTextEditor::Document *lastID = openUrls(urls, QString(), false, docInfo)) { activateView(lastID); } } void KateViewManager::slotDocumentClose(KTextEditor::Document *document) { bool shutdownKate = m_mainWindow->modCloseAfterLast() && KateApp::self()->documentManager()->documentList().size() == 1; // close document if (KateApp::self()->documentManager()->closeDocument(document) && shutdownKate) { KateApp::self()->shutdownKate(m_mainWindow); } } void KateViewManager::slotDocumentClose() { // no active view, do nothing if (!activeView()) { return; } slotDocumentClose(activeView()->document()); } KTextEditor::Document *KateViewManager::openUrl(const QUrl &url, const QString &encoding, bool activate, bool isTempFile, const KateDocumentInfo &docInfo) { KTextEditor::Document *doc = KateApp::self()->documentManager()->openUrl(url, encoding, isTempFile, docInfo); if (!doc->url().isEmpty()) { m_mainWindow->fileOpenRecent()->addUrl(doc->url()); } if (activate) { activateView(doc); } return doc; } KTextEditor::Document *KateViewManager::openUrls(const QList &urls, const QString &encoding, bool isTempFile, const KateDocumentInfo &docInfo) { QList docs = KateApp::self()->documentManager()->openUrls(urls, encoding, isTempFile, docInfo); foreach(const KTextEditor::Document * doc, docs) { if (!doc->url().isEmpty()) { m_mainWindow->fileOpenRecent()->addUrl(doc->url()); } } return docs.isEmpty() ? nullptr : docs.last(); } KTextEditor::View *KateViewManager::openUrlWithView(const QUrl &url, const QString &encoding) { KTextEditor::Document *doc = KateApp::self()->documentManager()->openUrl(url, encoding); if (!doc) { return nullptr; } if (!doc->url().isEmpty()) { m_mainWindow->fileOpenRecent()->addUrl(doc->url()); } activateView(doc); return activeView(); } void KateViewManager::openUrl(const QUrl &url) { openUrl(url, QString()); } KateMainWindow *KateViewManager::mainWindow() { return m_mainWindow; } void KateViewManager::documentCreated(KTextEditor::Document *doc) { // forward to currently active view space activeViewSpace()->registerDocument(doc); // to update open recent files on saving connect(doc, &KTextEditor::Document::documentSavedOrUploaded, this, &KateViewManager::documentSavedOrUploaded); if (m_blockViewCreationAndActivation) { return; } if (!activeView()) { activateView(doc); } /** * check if we have any empty viewspaces and give them a view */ Q_FOREACH(KateViewSpace * vs, m_viewSpaceList) { if (!vs->currentView()) { createView(activeView()->document(), vs); } } } void KateViewManager::aboutToDeleteDocuments(const QList &) { /** * block view creation until the transaction is done * this shall not stack! */ Q_ASSERT (!m_blockViewCreationAndActivation); m_blockViewCreationAndActivation = true; /** * disable updates hard (we can't use KateUpdateDisabler here, we have delayed signal */ mainWindow()->setUpdatesEnabled(false); } void KateViewManager::documentsDeleted(const QList &) { /** * again allow view creation */ m_blockViewCreationAndActivation = false; /** * try to have active view around! */ if (!activeView() && !KateApp::self()->documentManager()->documentList().isEmpty()) { createView(KateApp::self()->documentManager()->documentList().last()); } /** * if we have one now, show them in all viewspaces that got empty! */ if (KTextEditor::View *const newActiveView = activeView()) { /** * check if we have any empty viewspaces and give them a view */ Q_FOREACH(KateViewSpace * vs, m_viewSpaceList) { if (!vs->currentView()) { createView(newActiveView->document(), vs); } } emit viewChanged(newActiveView); } /** * enable updates hard (we can't use KateUpdateDisabler here, we have delayed signal */ mainWindow()->setUpdatesEnabled(true); } void KateViewManager::documentSavedOrUploaded(KTextEditor::Document *doc, bool) { if (!doc->url().isEmpty()) { m_mainWindow->fileOpenRecent()->addUrl(doc->url()); } } KTextEditor::View *KateViewManager::createView(KTextEditor::Document *doc, KateViewSpace *vs) { if (m_blockViewCreationAndActivation) { return nullptr; } // create doc if (!doc) { doc = KateApp::self()->documentManager()->createDoc(); } /** * create view, registers its XML gui itself * pass the view the correct main window */ KTextEditor::View *view = (vs ? vs : activeViewSpace())->createView(doc); /** * remember this view, active == false, min age set * create activity resource */ m_views[view].active = false; m_views[view].lruAge = m_minAge--; -#ifdef KActivities_FOUND +#ifdef KF5Activities_FOUND m_views[view].activityResource = new KActivities::ResourceInstance(view->window()->winId(), view); m_views[view].activityResource->setUri(doc->url()); #endif // disable settings dialog action delete view->actionCollection()->action(QStringLiteral("set_confdlg")); delete view->actionCollection()->action(QStringLiteral("editor_options")); connect(view, SIGNAL(dropEventPass(QDropEvent*)), mainWindow(), SLOT(slotDropEvent(QDropEvent*))); connect(view, &KTextEditor::View::focusIn, this, &KateViewManager::activateSpace); viewCreated(view); if (!vs) { activateView(view); } return view; } bool KateViewManager::deleteView(KTextEditor::View *view) { if (!view) { return true; } KateViewSpace *viewspace = static_cast(view->parentWidget()->parentWidget()); viewspace->removeView(view); /** * deregister if needed */ if (m_guiMergedView == view) { mainWindow()->guiFactory()->removeClient(m_guiMergedView); m_guiMergedView = nullptr; } // remove view from mapping and memory !! m_views.remove(view); delete view; return true; } KateViewSpace *KateViewManager::activeViewSpace() { for (QList::const_iterator it = m_viewSpaceList.constBegin(); it != m_viewSpaceList.constEnd(); ++it) { if ((*it)->isActiveSpace()) { return *it; } } // none active, so use the first we grab if (!m_viewSpaceList.isEmpty()) { m_viewSpaceList.first()->setActive(true); return m_viewSpaceList.first(); } Q_ASSERT(false); return nullptr; } KTextEditor::View *KateViewManager::activeView() { if (m_activeViewRunning) { return nullptr; } m_activeViewRunning = true; QHashIterator it(m_views); while (it.hasNext()) { it.next(); if (it.value().active) { m_activeViewRunning = false; return it.key(); } } // if we get to here, no view isActive() // first, try to get one from activeViewSpace() KateViewSpace *vs = activeViewSpace(); if (vs && vs->currentView()) { activateView(vs->currentView()); m_activeViewRunning = false; return vs->currentView(); } // last attempt: just pick first if (!m_views.isEmpty()) { KTextEditor::View *v = m_views.begin().key(); activateView(v); m_activeViewRunning = false; return v; } m_activeViewRunning = false; // no views exists! return nullptr; } void KateViewManager::setActiveSpace(KateViewSpace *vs) { if (activeViewSpace()) { activeViewSpace()->setActive(false); } vs->setActive(true); } void KateViewManager::setActiveView(KTextEditor::View *view) { if (activeView()) { m_views[activeView()].active = false; } if (view) { m_views[view].active = true; } } void KateViewManager::activateSpace(KTextEditor::View *v) { if (!v) { return; } KateViewSpace *vs = static_cast(v->parentWidget()->parentWidget()); if (!vs->isActiveSpace()) { setActiveSpace(vs); activateView(v); } } void KateViewManager::reactivateActiveView() { KTextEditor::View *view = activeView(); if (view) { m_views[view].active = false; activateView(view); } } void KateViewManager::activateView(KTextEditor::View *view) { if (!view) { return; } Q_ASSERT (m_views.contains(view)); if (!m_views[view].active) { // avoid flicker KateUpdateDisabler disableUpdates (mainWindow()); if (!activeViewSpace()->showView(view)) { // since it wasn't found, give'em a new one createView(view->document()); return; } setActiveView(view); bool toolbarVisible = mainWindow()->toolBar()->isVisible(); if (toolbarVisible) { mainWindow()->toolBar()->hide(); // hide to avoid toolbar flickering } if (m_guiMergedView) { mainWindow()->guiFactory()->removeClient(m_guiMergedView); m_guiMergedView = nullptr; } if (!m_blockViewCreationAndActivation) { mainWindow()->guiFactory()->addClient(view); m_guiMergedView = view; } if (toolbarVisible) { mainWindow()->toolBar()->show(); } // remember age of this view m_views[view].lruAge = m_minAge--; emit viewChanged(view); -#ifdef KActivities_FOUND +#ifdef KF5Activities_FOUND // inform activity manager m_views[view].activityResource->setUri(view->document()->url()); m_views[view].activityResource->notifyFocusedIn(); #endif } } KTextEditor::View *KateViewManager::activateView(KTextEditor::Document *d) { // no doc with this id found if (!d) { return activeView(); } // activate existing view if possible if (activeViewSpace()->showView(d)) { activateView(activeViewSpace()->currentView()); return activeView(); } // create new view otherwise createView(d); return activeView(); } void KateViewManager::slotViewChanged() { if (activeView() && !activeView()->hasFocus()) { activeView()->setFocus(); } } void KateViewManager::activateNextView() { int i = m_viewSpaceList.indexOf(activeViewSpace()) + 1; if (i >= m_viewSpaceList.count()) { i = 0; } setActiveSpace(m_viewSpaceList.at(i)); activateView(m_viewSpaceList.at(i)->currentView()); } void KateViewManager::activatePrevView() { int i = m_viewSpaceList.indexOf(activeViewSpace()) - 1; if (i < 0) { i = m_viewSpaceList.count() - 1; } setActiveSpace(m_viewSpaceList.at(i)); activateView(m_viewSpaceList.at(i)->currentView()); } void KateViewManager::documentWillBeDeleted(KTextEditor::Document *doc) { /** * collect all views of that document that belong to this manager */ QList closeList; Q_FOREACH (KTextEditor::View *v, doc->views()) { if (m_views.contains(v)) { closeList.append(v); } } while (!closeList.isEmpty()) { deleteView(closeList.takeFirst()); } } void KateViewManager::closeView(KTextEditor::View *view) { /** * kill view we want to kill */ deleteView(view); /** * try to have active view around! */ if (!activeView() && !KateApp::self()->documentManager()->documentList().isEmpty()) { createView(KateApp::self()->documentManager()->documentList().last()); } /** * if we have one now, show them in all viewspaces that got empty! */ if (KTextEditor::View *const newActiveView = activeView()) { /** * check if we have any empty viewspaces and give them a view */ Q_FOREACH(KateViewSpace * vs, m_viewSpaceList) { if (!vs->currentView()) { createView(newActiveView->document(), vs); } } emit viewChanged(newActiveView); } } void KateViewManager::splitViewSpace(KateViewSpace *vs, // = 0 Qt::Orientation o) // = Qt::Horizontal { // emergency: fallback to activeViewSpace, and if still invalid, abort if (!vs) { vs = activeViewSpace(); } if (!vs) { return; } // get current splitter, and abort if null QSplitter *currentSplitter = qobject_cast(vs->parentWidget()); if (!currentSplitter) { return; } // avoid flicker KateUpdateDisabler disableUpdates (mainWindow()); // index where to insert new splitter/viewspace const int index = currentSplitter->indexOf(vs); // create new viewspace KateViewSpace *vsNew = new KateViewSpace(this); // only 1 children -> we are the root container. So simply set the orientation // and add the new view space, then correct the sizes to 50%:50% if (currentSplitter->count() == 1) { if (currentSplitter->orientation() != o) { currentSplitter->setOrientation(o); } QList sizes = currentSplitter->sizes(); sizes << (sizes.first() - currentSplitter->handleWidth()) / 2; sizes[0] = sizes[1]; currentSplitter->insertWidget(index, vsNew); currentSplitter->setSizes(sizes); } else { // create a new QSplitter and replace vs with the splitter. vs and newVS are // the new children of the new QSplitter QSplitter *newContainer = new QSplitter(o); // we don't allow full collapse, see bug 366014 newContainer->setChildrenCollapsible(false); QList currentSizes = currentSplitter->sizes(); newContainer->addWidget(vs); newContainer->addWidget(vsNew); currentSplitter->insertWidget(index, newContainer); newContainer->show(); // fix sizes of children of old and new splitter currentSplitter->setSizes(currentSizes); QList newSizes = newContainer->sizes(); newSizes[0] = (newSizes[0] + newSizes[1] - newContainer->handleWidth()) / 2; newSizes[1] = newSizes[0]; newContainer->setSizes(newSizes); } m_viewSpaceList.append(vsNew); activeViewSpace()->setActive(false); vsNew->setActive(true); vsNew->show(); createView((KTextEditor::Document *)activeView()->document()); updateViewSpaceActions(); } void KateViewManager::closeViewSpace(KTextEditor::View *view) { KateViewSpace *space; if (view) { space = static_cast(view->parentWidget()->parentWidget()); } else { space = activeViewSpace(); } removeViewSpace(space); } void KateViewManager::toggleSplitterOrientation() { KateViewSpace *vs = activeViewSpace(); if (!vs) { return; } // get current splitter, and abort if null QSplitter *currentSplitter = qobject_cast(vs->parentWidget()); if (!currentSplitter || (currentSplitter->count() == 1)) { return; } // avoid flicker KateUpdateDisabler disableUpdates (mainWindow()); // toggle orientation if (currentSplitter->orientation() == Qt::Horizontal) { currentSplitter->setOrientation(Qt::Vertical); } else { currentSplitter->setOrientation(Qt::Horizontal); } } bool KateViewManager::viewsInSameViewSpace(KTextEditor::View *view1, KTextEditor::View *view2) { if (!view1 || !view2) { return false; } if (m_viewSpaceList.size() == 1) { return true; } KateViewSpace *vs1 = static_cast(view1->parentWidget()->parentWidget()); KateViewSpace *vs2 = static_cast(view2->parentWidget()->parentWidget()); return vs1 && (vs1 == vs2); } void KateViewManager::removeViewSpace(KateViewSpace *viewspace) { // abort if viewspace is 0 if (!viewspace) { return; } // abort if this is the last viewspace if (m_viewSpaceList.count() < 2) { return; } // get current splitter QSplitter *currentSplitter = qobject_cast(viewspace->parentWidget()); // no splitter found, bah if (!currentSplitter) { return; } // // 1. get LRU document list from current viewspace // 2. delete current view space // 3. add LRU documents from deleted viewspace to new active viewspace // // backup LRU list const QVector lruDocumntsList = viewspace->lruDocumentList(); // avoid flicker KateUpdateDisabler disableUpdates (mainWindow()); // delete views of the viewspace while (viewspace->currentView()) { deleteView(viewspace->currentView()); } // cu viewspace m_viewSpaceList.removeAt(m_viewSpaceList.indexOf(viewspace)); delete viewspace; // at this point, the splitter has exactly 1 child Q_ASSERT(currentSplitter->count() == 1); // if we are not the root splitter, move the child one level up and delete // the splitter then. if (currentSplitter != this) { // get parent splitter QSplitter *parentSplitter = qobject_cast(currentSplitter->parentWidget()); // only do magic if found ;) if (parentSplitter) { int index = parentSplitter->indexOf(currentSplitter); // save current splitter size, as the removal of currentSplitter looses the info QList parentSizes = parentSplitter->sizes(); parentSplitter->insertWidget(index, currentSplitter->widget(0)); delete currentSplitter; // now restore the sizes again parentSplitter->setSizes(parentSizes); } } else if (QSplitter *splitter = qobject_cast(currentSplitter->widget(0))) { // we are the root splitter and have only one child, which is also a splitter // -> eliminate the redundant splitter and move both children into the root splitter QList sizes = splitter->sizes(); // adapt splitter orientation to the splitter we are about to delete currentSplitter->setOrientation(splitter->orientation()); currentSplitter->addWidget(splitter->widget(0)); currentSplitter->addWidget(splitter->widget(0)); delete splitter; currentSplitter->setSizes(sizes); } // merge docuemnts of closed view space activeViewSpace()->mergeLruList(lruDocumntsList); // find the view that is now active. KTextEditor::View *v = activeViewSpace()->currentView(); if (v) { activateView(v); } updateViewSpaceActions(); emit viewChanged(v); } void KateViewManager::slotCloseOtherViews() { // avoid flicker KateUpdateDisabler disableUpdates(mainWindow()); const KateViewSpace *active = activeViewSpace(); foreach(KateViewSpace * v, m_viewSpaceList) { if (active != v) { removeViewSpace(v); } } } void KateViewManager::slotHideOtherViews(bool hideOthers) { // avoid flicker KateUpdateDisabler disableUpdates(mainWindow()); const KateViewSpace *active = activeViewSpace(); foreach(KateViewSpace * v, m_viewSpaceList) { if (active != v) { v->setVisible(!hideOthers); } } // disable the split actions, if we are in single-view-mode m_splitViewVert->setDisabled(hideOthers); m_splitViewHoriz->setDisabled(hideOthers); m_closeView->setDisabled(hideOthers); m_closeOtherViews->setDisabled(hideOthers); m_toggleSplitterOrientation->setDisabled(hideOthers); } /** * session config functions */ void KateViewManager::saveViewConfiguration(KConfigGroup &config) { // set Active ViewSpace to 0, just in case there is none active (would be // strange) and config somehow has previous value set config.writeEntry("Active ViewSpace", 0); m_splitterIndex = 0; saveSplitterConfig(this, config.config(), config.name()); } void KateViewManager::restoreViewConfiguration(const KConfigGroup &config) { /** * remove the single client that is registered at the factory, if any */ if (m_guiMergedView) { mainWindow()->guiFactory()->removeClient(m_guiMergedView); m_guiMergedView = nullptr; } /** * delete viewspaces, they will delete the views */ qDeleteAll(m_viewSpaceList); m_viewSpaceList.clear(); /** * delete mapping of now deleted views */ m_views.clear(); /** * kill all previous existing sub-splitters, just to be sure * e.g. important if one restores a config in an existing window with some splitters */ while (count() > 0) { delete widget(0); } // reset lru history, too! m_minAge = 0; // start recursion for the root splitter (Splitter 0) restoreSplitter(config.config(), config.name() + QStringLiteral("-Splitter 0"), this, config.name()); // finally, make the correct view from the last session active int lastViewSpace = config.readEntry("Active ViewSpace", 0); if (lastViewSpace > m_viewSpaceList.size()) { lastViewSpace = 0; } if (lastViewSpace >= 0 && lastViewSpace < m_viewSpaceList.size()) { setActiveSpace(m_viewSpaceList.at(lastViewSpace)); // activate correct view (wish #195435, #188764) activateView(m_viewSpaceList.at(lastViewSpace)->currentView()); // give view the focus to avoid focus stealing by toolviews / plugins m_viewSpaceList.at(lastViewSpace)->currentView()->setFocus(); } // emergency if (m_viewSpaceList.empty()) { // kill bad children while (count()) { delete widget(0); } KateViewSpace *vs = new KateViewSpace(this, nullptr); addWidget(vs); vs->setActive(true); m_viewSpaceList.append(vs); /** * activate at least one document! */ activateView(KateApp::self()->documentManager()->documentList().last()); if (!vs->currentView()) { createView(activeView()->document(), vs); } } updateViewSpaceActions(); } QString KateViewManager::saveSplitterConfig(QSplitter *s, KConfigBase *configBase, const QString &viewConfGrp) { /** * avoid to export invisible view spaces * else they will stick around for ever in sessions * bug 358266 - code initially done during load * bug 381433 - moved code to save */ /** * create new splitter name, might be not used */ const auto grp = QString(viewConfGrp + QStringLiteral("-Splitter %1")).arg(m_splitterIndex); ++m_splitterIndex; // a QSplitter has two children, either QSplitters and/or KateViewSpaces // special case: root splitter might have only one child (just for info) QStringList childList; const auto sizes = s->sizes(); for (int it = 0; it < s->count(); ++it) { // skip empty sized invisible ones, if not last one, we need one thing at least if ((sizes[it] == 0) && ((it + 1 < s->count()) || !childList.empty())) continue; // For KateViewSpaces, ask them to save the file list. auto obj = s->widget(it); if (auto kvs = qobject_cast(obj)) { childList.append(QString(viewConfGrp + QStringLiteral("-ViewSpace %1")).arg(m_viewSpaceList.indexOf(kvs))); kvs->saveConfig(configBase, m_viewSpaceList.indexOf(kvs), viewConfGrp); // save active viewspace if (kvs->isActiveSpace()) { KConfigGroup viewConfGroup(configBase, viewConfGrp); viewConfGroup.writeEntry("Active ViewSpace", m_viewSpaceList.indexOf(kvs)); } } // for QSplitters, recurse else if (auto splitter = qobject_cast(obj)) { childList.append(saveSplitterConfig(splitter, configBase, viewConfGrp)); } } // if only one thing, skip splitter config export, if not top splitter if ((s != this) && (childList.size() == 1)) return childList.at(0); // Save sizes, orient, children for this splitter KConfigGroup config(configBase, grp); config.writeEntry("Sizes", sizes); config.writeEntry("Orientation", int(s->orientation())); config.writeEntry("Children", childList); return grp; } void KateViewManager::restoreSplitter(const KConfigBase *configBase, const QString &group, QSplitter *parent, const QString &viewConfGrp) { KConfigGroup config(configBase, group); parent->setOrientation((Qt::Orientation)config.readEntry("Orientation", int(Qt::Horizontal))); const QStringList children = config.readEntry("Children", QStringList()); for (const auto& str : children) { // for a viewspace, create it and open all documents therein. if (str.startsWith(viewConfGrp + QStringLiteral("-ViewSpace"))) { KateViewSpace *vs = new KateViewSpace(this, nullptr); m_viewSpaceList.append(vs); // make active so that the view created in restoreConfig has this // new view space as parent. setActiveSpace(vs); parent->addWidget(vs); vs->restoreConfig(this, configBase, str); vs->show(); } else { // for a splitter, recurse auto newContainer = new QSplitter(parent); // we don't allow full collapse, see bug 366014 newContainer->setChildrenCollapsible(false); restoreSplitter(configBase, str, newContainer, viewConfGrp); } } // set sizes parent->setSizes(config.readEntry("Sizes", QList())); parent->show(); } void KateViewManager::moveSplitter(Qt::Key key, int repeats) { if (repeats < 1) { return; } KateViewSpace *vs = activeViewSpace(); if (!vs) { return; } QSplitter *currentSplitter = qobject_cast(vs->parentWidget()); if (!currentSplitter) { return; } if (currentSplitter->count() == 1) { return; } int move = 4 * repeats; // try to use font height in pixel to move splitter { KTextEditor::Attribute::Ptr attrib(vs->currentView()->defaultStyleAttribute(KTextEditor::dsNormal)); QFontMetrics fm(attrib->font()); move = fm.height() * repeats; } QWidget *currentWidget = (QWidget *)vs; bool foundSplitter = false; // find correct splitter to be moved while (currentSplitter && currentSplitter->count() != 1) { if (currentSplitter->orientation() == Qt::Horizontal && (key == Qt::Key_Right || key == Qt::Key_Left)) { foundSplitter = true; } if (currentSplitter->orientation() == Qt::Vertical && (key == Qt::Key_Up || key == Qt::Key_Down)) { foundSplitter = true; } // if the views within the current splitter can be resized, resize them if (foundSplitter) { QList currentSizes = currentSplitter->sizes(); int index = currentSplitter->indexOf(currentWidget); if ((index == 0 && (key == Qt::Key_Left || key == Qt::Key_Up)) || (index == 1 && (key == Qt::Key_Right || key == Qt::Key_Down))) { currentSizes[index] -= move; } if ((index == 0 && (key == Qt::Key_Right || key == Qt::Key_Down)) || (index == 1 && (key == Qt::Key_Left || key == Qt::Key_Up))) { currentSizes[index] += move; } if (index == 0 && (key == Qt::Key_Right || key == Qt::Key_Down)) { currentSizes[index + 1] -= move; } if (index == 0 && (key == Qt::Key_Left || key == Qt::Key_Up)) { currentSizes[index + 1] += move; } if (index == 1 && (key == Qt::Key_Right || key == Qt::Key_Down)) { currentSizes[index - 1] += move; } if (index == 1 && (key == Qt::Key_Left || key == Qt::Key_Up)) { currentSizes[index - 1] -= move; } currentSplitter->setSizes(currentSizes); break; } currentWidget = (QWidget *)currentSplitter; // the parent of the current splitter will become the current splitter currentSplitter = qobject_cast(currentSplitter->parentWidget()); } } diff --git a/kate/kateviewmanager.h b/kate/kateviewmanager.h index ea0e27c03..955453754 100644 --- a/kate/kateviewmanager.h +++ b/kate/kateviewmanager.h @@ -1,351 +1,349 @@ /* This file is part of the KDE project Copyright (C) 2001 Christoph Cullmann Copyright (C) 2001 Joseph Wenninger Copyright (C) 2001 Anders Lund 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. */ #ifndef __KATE_VIEWMANAGER_H__ #define __KATE_VIEWMANAGER_H__ #include "katedocmanager.h" #include #include #include #include -#include - namespace KActivities { class ResourceInstance; } namespace KTextEditor { class View; class Document; } class KateDocumentInfo; class KConfigGroup; class KConfigBase; class KateMainWindow; class KateViewSpace; class KateViewManager : public QSplitter { Q_OBJECT public: KateViewManager(QWidget *parentW, KateMainWindow *parent); ~KateViewManager() override; private: /** * create all actions needed for the view manager */ void setupActions(); void updateViewSpaceActions(); public: /* This will save the splitter configuration */ void saveViewConfiguration(KConfigGroup &group); /* restore it */ void restoreViewConfiguration(const KConfigGroup &group); KTextEditor::Document *openUrl(const QUrl &url, const QString &encoding, bool activate = true, bool isTempFile = false, const KateDocumentInfo &docInfo = KateDocumentInfo()); KTextEditor::Document *openUrls(const QList &url, const QString &encoding, bool isTempFile = false, const KateDocumentInfo &docInfo = KateDocumentInfo()); KTextEditor::View *openUrlWithView(const QUrl &url, const QString &encoding); public Q_SLOTS: void openUrl(const QUrl &url); public: void closeView(KTextEditor::View *view); KateMainWindow *mainWindow(); private Q_SLOTS: void activateView(KTextEditor::View *view); void activateSpace(KTextEditor::View *v); public Q_SLOTS: void slotDocumentNew(); void slotDocumentOpen(); void slotDocumentClose(); void slotDocumentClose(KTextEditor::Document *document); void setActiveSpace(KateViewSpace *vs); void setActiveView(KTextEditor::View *view); void activateNextView(); void activatePrevView(); Q_SIGNALS: void viewChanged(KTextEditor::View *); void viewCreated(KTextEditor::View *); public: /** * create and activate a new view for doc, if doc == 0, then * create a new document. * Can return NULL. */ KTextEditor::View *createView(KTextEditor::Document *doc = nullptr, KateViewSpace *vs = nullptr); private: bool deleteView(KTextEditor::View *view); void moveViewtoSplit(KTextEditor::View *view); void moveViewtoStack(KTextEditor::View *view); /* Save the configuration of a single splitter. * If child splitters are found, it calls it self with those as the argument. * If a viewspace child is found, it is asked to save its filelist. */ QString saveSplitterConfig(QSplitter *s, KConfigBase *config, const QString &viewConfGrp); /** Restore a single splitter. * This is all the work is done for @see saveSplitterConfig() */ void restoreSplitter(const KConfigBase *config, const QString &group, QSplitter *parent, const QString &viewConfGrp); void removeViewSpace(KateViewSpace *viewspace); public: KTextEditor::View *activeView(); KateViewSpace *activeViewSpace(); private Q_SLOTS: void slotViewChanged(); void documentCreated(KTextEditor::Document *doc); void documentWillBeDeleted(KTextEditor::Document *doc); void documentSavedOrUploaded(KTextEditor::Document *document, bool saveAs); /** * This signal is emitted before the documents batch is going to be deleted * * note that the batch can be interrupted in the middle and only some * of the documents may be actually deleted. See documentsDeleted() signal. * * @param documents documents we want to delete, may not be deleted */ void aboutToDeleteDocuments(const QList &documents); /** * This signal is emitted after the documents batch was deleted * * This is the batch closing signal for aboutToDeleteDocuments * @param documents the documents that weren't deleted after all */ void documentsDeleted(const QList &documents); public Q_SLOTS: /** * Splits a KateViewSpace into two in the following steps: * 1. create a QSplitter in the parent of the KateViewSpace to be split * 2. move the to-be-split KateViewSpace to the new splitter * 3. create new KateViewSpace and added to the new splitter * 4. create KateView to populate the new viewspace. * 5. The new KateView is made the active one, because createView() does that. * If no viewspace is provided, the result of activeViewSpace() is used. * The orientation of the new splitter is determined by the value of o. * Note: horizontal splitter means vertically aligned views. */ void splitViewSpace(KateViewSpace *vs = nullptr, Qt::Orientation o = Qt::Horizontal); /** * Close the view space that contains the given view. If no view was * given, then the active view space will be closed instead. */ void closeViewSpace(KTextEditor::View *view = nullptr); /** * @returns true of the two given views share the same view space. */ bool viewsInSameViewSpace(KTextEditor::View *view1, KTextEditor::View *view2); /** * activate view for given document * @param doc document to activate view for */ KTextEditor::View *activateView(KTextEditor::Document *doc); /** Splits the active viewspace horizontally */ void slotSplitViewSpaceHoriz() { splitViewSpace(nullptr, Qt::Vertical); } /** Splits the active viewspace vertically */ void slotSplitViewSpaceVert() { splitViewSpace(); } /** moves the splitter according to the key that has been pressed */ void moveSplitter(Qt::Key key, int repeats = 1); /** moves the splitter to the right */ void moveSplitterRight() { moveSplitter(Qt::Key_Right); } /** moves the splitter to the left */ void moveSplitterLeft() { moveSplitter(Qt::Key_Left); } /** moves the splitter up */ void moveSplitterUp() { moveSplitter(Qt::Key_Up); } /** moves the splitter down */ void moveSplitterDown() { moveSplitter(Qt::Key_Down); } /** closes the current view space. */ void slotCloseCurrentViewSpace() { closeViewSpace(); } /** closes every view but the active one */ void slotCloseOtherViews(); /** hide every view but the active one */ void slotHideOtherViews(bool hideOthers); void reactivateActiveView(); /** * Toogle the orientation of current split view */ void toggleSplitterOrientation(); /** * Get a list of all views. * @return all views */ QList views() const { return m_views.keys(); } /** * get views in lru order * @return views in lru order */ QList sortedViews() const { QMap sortedViews; QHashIterator i(m_views); while (i.hasNext()) { i.next(); sortedViews[i.value().lruAge] = i.key(); } return sortedViews.values(); } private: KateMainWindow *m_mainWindow; bool m_init; QAction *m_splitViewVert; QAction *m_splitViewHoriz; QAction *m_closeView; QAction *m_closeOtherViews; QAction *m_toggleSplitterOrientation; QAction *m_hideOtherViews; QAction *goNext; QAction *goPrev; QList m_viewSpaceList; bool m_blockViewCreationAndActivation; bool m_activeViewRunning; int m_splitterIndex; // used during saving splitter config. /** * View meta data */ class ViewData { public: /** * Default constructor */ ViewData() : active(false) , lruAge(0) , activityResource(Q_NULLPTR) { } /** * view active? */ bool active; /** * lru age of the view * important: smallest age ==> latest used view */ qint64 lruAge; /** * activity resource for the view */ KActivities::ResourceInstance *activityResource; }; /** * central storage of all views known in the view manager * maps the view to meta data */ QHash m_views; /** * current minimal age */ qint64 m_minAge; /** * the view that is ATM merged to the xml gui factory */ QPointer m_guiMergedView; /** * last url of open file dialog, used if current document has no valid url */ QUrl m_lastOpenDialogUrl; }; #endif diff --git a/kate/katewaiter.cpp b/kate/katewaiter.cpp new file mode 100644 index 000000000..d7fcdab56 --- /dev/null +++ b/kate/katewaiter.cpp @@ -0,0 +1,47 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2001 Joseph Wenninger + Copyright (C) 2001, 2005 Anders Lund + + 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. +*/ + +#include "katewaiter.h" + +KateWaiter::KateWaiter(const QString &service, const QStringList &tokens) + : QObject(QCoreApplication::instance()) + , m_tokens(tokens) + , m_watcher(service, QDBusConnection::sessionBus()) +{ + connect(&m_watcher, &QDBusServiceWatcher::serviceOwnerChanged, this, &KateWaiter::serviceOwnerChanged); +} + +void KateWaiter::exiting() +{ + QCoreApplication::instance()->quit(); +} + +void KateWaiter::documentClosed(const QString &token) +{ + m_tokens.removeAll(token); + if (m_tokens.count() == 0) { + QCoreApplication::instance()->quit(); + } +} + +void KateWaiter::serviceOwnerChanged(const QString &, const QString &, const QString &) +{ + QCoreApplication::instance()->quit(); +} \ No newline at end of file diff --git a/kate/katewaiter.h b/kate/katewaiter.h index fe2f8a590..8aa59d718 100644 --- a/kate/katewaiter.h +++ b/kate/katewaiter.h @@ -1,67 +1,49 @@ /* This file is part of the KDE libraries Copyright (C) 2005 Christoph Cullmann Copyright (C) 2002, 2003 Joseph Wenninger 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_WAITER_H__ #define __KATE_WAITER_H__ #include #include #include #include #include class KateWaiter : public QObject { Q_OBJECT public: - KateWaiter(const QString &service, const QStringList &tokens) - : QObject(QCoreApplication::instance()) - , m_tokens(tokens) - , m_watcher(service, QDBusConnection::sessionBus()) - { - connect(&m_watcher, &QDBusServiceWatcher::serviceOwnerChanged, this, &KateWaiter::serviceOwnerChanged); - } + KateWaiter(const QString &service, const QStringList &tokens); public Q_SLOTS: - void exiting() - { - QCoreApplication::instance()->quit(); - } + void exiting(); - void documentClosed(const QString &token) - { - m_tokens.removeAll(token); - if (m_tokens.count() == 0) { - QCoreApplication::instance()->quit(); - } - } + void documentClosed(const QString &token); - void serviceOwnerChanged(const QString &, const QString &, const QString &) - { - QCoreApplication::instance()->quit(); - } + void serviceOwnerChanged(const QString &, const QString &, const QString &); private: QStringList m_tokens; QDBusServiceWatcher m_watcher; }; #endif diff --git a/kate/session/katesession.h b/kate/session/katesession.h index 80f13ebc3..c568afd18 100644 --- a/kate/session/katesession.h +++ b/kate/session/katesession.h @@ -1,136 +1,137 @@ /* 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_H__ #define __KATE_SESSION_H__ +#include "katetests_export.h" + #include #include #include -#include "kateprivate_export.h" class KConfig; class KATE_TESTS_EXPORT KateSession : public QSharedData { public: /** * Define a Shared-Pointer type */ typedef QExplicitlySharedDataPointer Ptr; public: ~KateSession(); /** * session name * @return name for this session */ const QString &name() const { return m_name; } /** * session config * on first access, will create the config object, delete will be done automagic * return 0 if we have no file to read config from atm * @return correct KConfig, never null * @note never delete configRead(), because the return value might be * KSharedConfig::openConfig(). Only delete the member variables directly. */ KConfig *config(); /** * count of documents in this session * @return documents count */ unsigned int documents() const { return m_documents; } /** * update \p number of opened documents in session */ void setDocuments(const unsigned int number); /** * @return true if this is anonymous/new session */ bool isAnonymous() const { return m_anonymous; } /** * @return path to session file */ const QString &file() const; /** * returns last save time of this session */ const QDateTime ×tamp() const { return m_timestamp; } /** * Factories */ public: static KateSession::Ptr create(const QString &file, const QString &name); static KateSession::Ptr createFrom(const KateSession::Ptr &session, const QString &file, const QString &name); static KateSession::Ptr createAnonymous(const QString &file); static KateSession::Ptr createAnonymousFrom(const KateSession::Ptr &session, const QString &file); static bool compareByName(const KateSession::Ptr &s1, const KateSession::Ptr &s2); static bool compareByTimeDesc(const KateSession::Ptr &s1, const KateSession::Ptr &s2); private: friend class KateSessionManager; friend class KateSessionTest; /** * set session name */ void setName(const QString &name); /** * set's new session file to @filename */ void setFile(const QString &filename); /** * create a session from given @file * @param file configuration file * @param name name of this session * @param anonymous anonymous flag * @param config if specified, the session will copy configuration from the KConfig instead of opening the file */ KateSession(const QString &file, const QString &name, const bool anonymous, const KConfig *config = nullptr); private: QString m_name; QString m_file; bool m_anonymous; unsigned int m_documents; KConfig *m_config; QDateTime m_timestamp; }; #endif diff --git a/kate/session/katesessionmanager.cpp b/kate/session/katesessionmanager.cpp index 97150d2fc..55751f65e 100644 --- a/kate/session/katesessionmanager.cpp +++ b/kate/session/katesessionmanager.cpp @@ -1,653 +1,651 @@ /* 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 "katesessionmanagedialog.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, &KDirWatch::dirty, this, &KateSessionManager::updateSessionList); updateSessionList(); } 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); bool changed = false; // 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; } } if (changed) { emit sessionListChanged(); } } bool KateSessionManager::activateSession(KateSession::Ptr session, const bool closeAndSaveLast, const bool loadNew) { 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"); 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); } if (c.readEntry("Restore Window Configuration", true)) { 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, QStringLiteral("MainWindow%1").arg(i)); } else { KateApp::self()->mainWindow(i)->readProperties(KConfigGroup(cfg, QStringLiteral("MainWindow%1").arg(i))); } KateApp::self()->mainWindow(i)->restoreWindowConfig(KConfigGroup(cfg, QStringLiteral("MainWindow%1 Settings").arg(i))); } // remove mainwindows we need no longer... if (wCount > 0) { while (wCount < KateApp::self()->mainWindowsCount()) { delete KateApp::self()->mainWindow(KateApp::self()->mainWindowsCount() - 1); } } } else { // load recent files for all existing windows, see bug 408499 for (int i = 0; i < KateApp::self()->mainWindowsCount(); ++i) { KateApp::self()->mainWindow(i)->loadOpenRecent(cfg); } } // ensure we have at least one window, always! load recent files for it, too, see bug 408499 if (KateApp::self()->mainWindowsCount() == 0) { auto w = KateApp::self()->newMainWindow(); w->loadOpenRecent(cfg); } if (delete_cfg) { delete cfg; } // we shall always have some existing windows here! Q_ASSERT(KateApp::self()->mainWindowsCount() > 0); } 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; } bool KateSessionManager::deleteSession(KateSession::Ptr session) { 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; } QString KateSessionManager::copySession(const KateSession::Ptr &session, const QString &newName) { const QString name = askForNewSessionName(session, newName); if (name.isEmpty()) { return name; } const QString newFile = sessionFileForName(name); 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 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 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, QStringLiteral("MainWindow%1").arg(i)); // saveProperties() handles saving the "open recent" files list KateApp::self()->mainWindow(i)->saveProperties(cg); if (saveWindowConfig) { KateApp::self()->mainWindow(i)->saveWindowConfig(KConfigGroup(sc, QStringLiteral("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 && !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")) { return activateSession(lastSession, false); } // start with empty new session or in case no sessions exist if (sesStart == QStringLiteral("new") || sessionList().size() == 0) { return activateAnonymousSession(); } // else: ask the user return QScopedPointer(new KateSessionManageDialog(nullptr, lastSession))->exec(); } void KateSessionManager::sessionNew() { activateSession(giveSession(QString())); } void KateSessionManager::sessionSave() { if (activeSession() && activeSession()->isAnonymous()) { sessionSaveAs(); } else { saveActiveSession(); } } void KateSessionManager::sessionSaveAs() { const QString newName = askForNewSessionName(activeSession()); if (newName.isEmpty()) { return; } activeSession()->config()->sync(); KateSession::Ptr ns = KateSession::createFrom(activeSession(), sessionFileForName(newName), newName); m_activeSession = ns; saveActiveSession(); emit sessionChanged(); } QString KateSessionManager::askForNewSessionName(KateSession::Ptr session, const QString &newName) { if (session->name() == newName && !session->isAnonymous()) { return QString(); } 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."); QString messageTotal = messagePrompt; QString name = newName; 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; } 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 (!ok) { return QString(); } } } QString KateSessionManager::suggestNewSessionName(const QString &target) { 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::sessionManage() { QScopedPointer(new KateSessionManageDialog(KateApp::self()->activeKateMainWindow()))->exec(); } bool KateSessionManager::sessionIsActive(const QString &session) { // Try to avoid unneed action if (activeSession() && activeSession()->name() == session) { return true; } QDBusConnectionInterface *i = QDBusConnection::sessionBus().interface(); if (!i) { return false; } // look up all running kate instances and there sessions QDBusReply servicesReply = i->registeredServiceNames(); QStringList services; if (servicesReply.isValid()) { services = servicesReply.value(); } for (const QString &s : qAsConst(services)) { if (!s.startsWith(QStringLiteral("org.kde.kate-"))) { continue; } KateRunningInstanceInfo rii(s); if (rii.valid && rii.sessionName == session) { return true; } } return false; } 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/katesessionsaction.h b/kate/session/katesessionsaction.h index aaa449ec9..03f01e87d 100644 --- a/kate/session/katesessionsaction.h +++ b/kate/session/katesessionsaction.h @@ -1,50 +1,50 @@ /* 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_SESSIONS_ACTION_H__ #define __KATE_SESSIONS_ACTION_H__ -#include "kateprivate_export.h" +#include "katetests_export.h" #include class KateSessionManager; class KATE_TESTS_EXPORT KateSessionsAction : public KActionMenu { Q_OBJECT public: KateSessionsAction(const QString &text, QObject *parent, KateSessionManager *manager = nullptr); ~KateSessionsAction() override { } public Q_SLOTS: void slotAboutToShow(); void openSession(QAction *action); void slotSessionChanged(); private: friend class KateSessionsActionTest; // tfuj QActionGroup *sessionsGroup; KateSessionManager *m_manager; }; #endif diff --git a/kwrite/CMakeLists.txt b/kwrite/CMakeLists.txt index f95836403..898ccd993 100644 --- a/kwrite/CMakeLists.txt +++ b/kwrite/CMakeLists.txt @@ -1,72 +1,88 @@ -# KWrite -project (kwrite) +add_executable(kwrite "") -# Load the frameworks we need -find_package(KF5 REQUIRED COMPONENTS DBusAddons) +configure_file(config.h.in config.h) +target_include_directories(kwrite PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) # config.h -# collect icons -set(KWRITE_ICONS_PNG - ${CMAKE_CURRENT_SOURCE_DIR}/icons/16-apps-kwrite.png - ${CMAKE_CURRENT_SOURCE_DIR}/icons/22-apps-kwrite.png - ${CMAKE_CURRENT_SOURCE_DIR}/icons/32-apps-kwrite.png - ${CMAKE_CURRENT_SOURCE_DIR}/icons/48-apps-kwrite.png - ${CMAKE_CURRENT_SOURCE_DIR}/icons/64-apps-kwrite.png - ${CMAKE_CURRENT_SOURCE_DIR}/icons/128-apps-kwrite.png +find_package( + KF5 + QUIET + REQUIRED + COMPONENTS + TextEditor + DBusAddons + Crash + OPTIONAL_COMPONENTS + Activities ) -set(KWRITE_ICONS_SVG -${CMAKE_CURRENT_SOURCE_DIR}/icons/sc-apps-kwrite.svgz +target_link_libraries( + kwrite + PUBLIC + KF5::TextEditor + KF5::DBusAddons + KF5::Crash ) -# collect the sources -set (KWRITE_APPLICATION_SRCS main.cpp kwrite.cpp kwriteapplication.cpp) -qt5_add_resources(KWRITE_APPLICATION_SRCS data/kwrite.qrc) - -# add icons to application sources, to have them bundled -ecm_add_app_icon(KWRITE_APPLICATION_SRCS ICONS ${KWRITE_ICONS_PNG}) +if(KF5Activities_FOUND) + target_link_libraries(kwrite PUBLIC KF5::Activities) +endif() -# build KWrite application -add_executable(kwrite ${KWRITE_APPLICATION_SRCS}) -target_link_libraries(kwrite -PUBLIC - KF5::TextEditor - KF5::I18n - KF5::DBusAddons - KF5::Crash +set(ICONS_PNG + ${CMAKE_CURRENT_SOURCE_DIR}/icons/16-apps-kwrite.png + ${CMAKE_CURRENT_SOURCE_DIR}/icons/22-apps-kwrite.png + ${CMAKE_CURRENT_SOURCE_DIR}/icons/32-apps-kwrite.png + ${CMAKE_CURRENT_SOURCE_DIR}/icons/48-apps-kwrite.png + ${CMAKE_CURRENT_SOURCE_DIR}/icons/64-apps-kwrite.png + ${CMAKE_CURRENT_SOURCE_DIR}/icons/128-apps-kwrite.png ) -if(KF5Activities_FOUND) - target_link_libraries(kwrite - PUBLIC - KF5::Activities) -endif() +set(ICONS_SVG ${CMAKE_CURRENT_SOURCE_DIR}/icons/sc-apps-kwrite.svgz) + +# Add icon files to the application's source files to have CMake bundle them in the executable. +ecm_add_app_icon(ICONS_SOURCES ICONS ${ICONS_PNG}) +target_sources(kwrite PRIVATE ${ICONS_SOURCES}) -# own plist magic for mac os +target_sources( + kwrite + PRIVATE + data/kwrite.qrc + kwrite.cpp + kwriteapplication.cpp + main.cpp +) + +# See https://cmake.org/cmake/help/v3.15/prop_tgt/MACOSX_BUNDLE_INFO_PLIST.html if(APPLE) - # own plist template - set_target_properties (kwrite PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/data/MacOSXBundleInfo.plist.in) + set_property( + TARGET kwrite + PROPERTY 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 KWrite) - set_target_properties(kwrite PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER "org.kde.KWrite") - set_target_properties(kwrite PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "KWrite") - set_target_properties(kwrite PROPERTIES MACOSX_BUNDLE_DISPLAY_NAME "KWrite") - set_target_properties(kwrite PROPERTIES MACOSX_BUNDLE_INFO_STRING "KWrite - Text Editor") - set_target_properties(kwrite PROPERTIES MACOSX_BUNDLE_LONG_VERSION_STRING "KWrite ${KDE_APPLICATIONS_VERSION}") - set_target_properties(kwrite PROPERTIES MACOSX_BUNDLE_SHORT_VERSION_STRING "${KDE_APPLICATIONS_VERSION_MAJOR}.${KDE_APPLICATIONS_VERSION_MINOR}") - set_target_properties(kwrite PROPERTIES MACOSX_BUNDLE_BUNDLE_VERSION "${KDE_APPLICATIONS_VERSION}") - set_target_properties(kwrite PROPERTIES MACOSX_BUNDLE_COPYRIGHT "2000-2016 The KWrite Authors") + # These are substituted by CMake into plist.in. + set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.kde.KWrite") + set(MACOSX_BUNDLE_BUNDLE_NAME "KWrite") + set(MACOSX_BUNDLE_DISPLAY_NAME "KWrite") + set(MACOSX_BUNDLE_INFO_STRING "KWrite - Text Editor") + set(MACOSX_BUNDLE_LONG_VERSION_STRING "KWrite ${KDE_APPLICATIONS_VERSION}") + set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${KDE_APPLICATIONS_VERSION_MAJOR}.${KDE_APPLICATIONS_VERSION_MINOR}") + set(MACOSX_BUNDLE_BUNDLE_VERSION "${KDE_APPLICATIONS_VERSION}") + set(MACOSX_BUNDLE_COPYRIGHT "2000-2016 The KWrite Authors") endif() -# install executable install(TARGETS kwrite ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) -# desktop file -install(PROGRAMS ${CMAKE_CURRENT_SOURCE_DIR}/data/org.kde.kwrite.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}) +install( + FILES ${CMAKE_CURRENT_SOURCE_DIR}/data/org.kde.kwrite.desktop + DESTINATION ${XDG_APPS_INSTALL_DIR} +) -# appdata -install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/data/org.kde.kwrite.appdata.xml DESTINATION ${CMAKE_INSTALL_METAINFODIR}) +install( + FILES ${CMAKE_CURRENT_SOURCE_DIR}/data/org.kde.kwrite.appdata.xml + DESTINATION ${CMAKE_INSTALL_METAINFODIR} +) -# install icons -ecm_install_icons(ICONS ${KWRITE_ICONS_PNG} ${KWRITE_ICONS_SVG} DESTINATION ${ICON_INSTALL_DIR} THEME hicolor) +ecm_install_icons( + ICONS ${ICONS_PNG} ${ICONS_SVG} + DESTINATION ${ICON_INSTALL_DIR} + THEME hicolor +) diff --git a/kwrite/config.h.in b/kwrite/config.h.in new file mode 100644 index 000000000..e644f1ab5 --- /dev/null +++ b/kwrite/config.h.in @@ -0,0 +1,10 @@ +#ifndef KWRITE_CONFIG_H +#define KWRITE_CONFIG_H + +/* config.h. Generated by cmake from config.h.cmake */ + +#define KWRITE_VERSION "${KDE_APPLICATIONS_VERSION}" + +#cmakedefine KF5Activities_FOUND + +#endif diff --git a/kwrite/kwrite.cpp b/kwrite/kwrite.cpp index 651e86cff..0c9591a9c 100644 --- a/kwrite/kwrite.cpp +++ b/kwrite/kwrite.cpp @@ -1,529 +1,528 @@ /* This file is part of the KDE project Copyright (C) 2001 Christoph Cullmann Copyright (C) 2001 Joseph Wenninger Copyright (C) 2001 Anders Lund 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. */ #include "kwrite.h" +#include "kwriteapplication.h" +#include "config.h" + #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include - -#ifdef KActivities_FOUND +#ifdef KF5Activities_FOUND #include #endif #include #include #include #include #include #include #include #include #include #include -#include "kwriteapplication.h" - KWrite::KWrite(KTextEditor::Document *doc, KWriteApplication *app) : m_view(nullptr) , m_recentFiles(nullptr) , m_paShowPath(nullptr) , m_paShowMenuBar(nullptr) , m_paShowStatusBar(nullptr) , m_activityResource(nullptr) , m_app(app) , m_mainWindow(this) { if (!doc) { doc = KTextEditor::Editor::instance()->createDocument(nullptr); // enable the modified on disk warning dialogs if any if (qobject_cast(doc)) { qobject_cast(doc)->setModifiedOnDiskWarning(true); } m_app->addDocument(doc); } m_view = doc->createView(this); setCentralWidget(m_view); setupActions(); // signals for the statusbar connect(m_view->document(), &KTextEditor::Document::modifiedChanged, this, &KWrite::modifiedChanged); connect(m_view->document(), &KTextEditor::Document::documentNameChanged, this, &KWrite::documentNameChanged); connect(m_view->document(), &KTextEditor::Document::readWriteChanged, this, &KWrite::documentNameChanged); connect(m_view->document(), &KTextEditor::Document::documentUrlChanged, this, &KWrite::urlChanged); setAcceptDrops(true); connect(m_view, SIGNAL(dropEventPass(QDropEvent*)), this, SLOT(slotDropEvent(QDropEvent*))); setXMLFile(QStringLiteral("kwriteui.rc")); createShellGUI(true); guiFactory()->addClient(m_view); // FIXME: make sure the config dir exists, any idea how to do it more cleanly? QDir(QStandardPaths::writableLocation(QStandardPaths::DataLocation)).mkpath(QStringLiteral(".")); // call it as last thing, must be sure everything is already set up ;) setAutoSaveSettings(); readConfig(); documentNameChanged(); show(); // give view focus m_view->setFocus(Qt::OtherFocusReason); /** * handle mac os x like file open request via event filter */ qApp->installEventFilter(this); } KWrite::~KWrite() { m_app->removeWindow(this); guiFactory()->removeClient(m_view); KTextEditor::Document *doc = m_view->document(); delete m_view; // kill document, if last view is closed if (doc->views().isEmpty()) { m_app->removeDocument(doc); delete doc; } KSharedConfig::openConfig()->sync(); } QSize KWrite::sizeHint () const { /** * have some useful size hint, else we have mini windows per default */ return (QSize(640, 480).expandedTo(minimumSizeHint())); } void KWrite::setupActions() { m_closeAction = actionCollection()->addAction(KStandardAction::Close, QStringLiteral("file_close"), this, SLOT(slotFlush())); m_closeAction->setIcon(QIcon::fromTheme(QStringLiteral("document-close"))); m_closeAction->setWhatsThis(i18n("Use this command to close the current document")); m_closeAction->setDisabled(true); // setup File menu actionCollection()->addAction(KStandardAction::New, QStringLiteral("file_new"), this, SLOT(slotNew())) ->setWhatsThis(i18n("Use this command to create a new document")); actionCollection()->addAction(KStandardAction::Open, QStringLiteral("file_open"), this, SLOT(slotOpen())) ->setWhatsThis(i18n("Use this command to open an existing document for editing")); m_recentFiles = KStandardAction::openRecent(this, SLOT(slotOpen(QUrl)), this); actionCollection()->addAction(m_recentFiles->objectName(), m_recentFiles); m_recentFiles->setWhatsThis(i18n("This lists files which you have opened recently, and allows you to easily open them again.")); QAction *a = actionCollection()->addAction(QStringLiteral("view_new_view")); a->setIcon(QIcon::fromTheme(QStringLiteral("window-new"))); a->setText(i18n("&New Window")); connect(a, &QAction::triggered, this, &KWrite::newView); a->setWhatsThis(i18n("Create another view containing the current document")); actionCollection()->addAction(KStandardAction::Quit, this, SLOT(close())) ->setWhatsThis(i18n("Close the current document view")); // setup Settings menu setStandardToolBarMenuEnabled(true); m_paShowMenuBar = KStandardAction::showMenubar(this, SLOT(toggleMenuBar()), actionCollection()); m_paShowStatusBar = KStandardAction::showStatusbar(this, SLOT(toggleStatusBar()), this); actionCollection()->addAction(m_paShowStatusBar->objectName(), m_paShowStatusBar); m_paShowStatusBar->setWhatsThis(i18n("Use this command to show or hide the view's statusbar")); m_paShowPath = new KToggleAction(i18n("Sho&w Path in Titlebar"), this); actionCollection()->addAction(QStringLiteral("set_showPath"), m_paShowPath); connect(m_paShowPath, &QAction::triggered, this, &KWrite::documentNameChanged); m_paShowPath->setWhatsThis(i18n("Show the complete document path in the window caption")); a = actionCollection()->addAction(KStandardAction::KeyBindings, this, SLOT(editKeys())); a->setWhatsThis(i18n("Configure the application's keyboard shortcut assignments.")); a = actionCollection()->addAction(KStandardAction::ConfigureToolbars, QStringLiteral("options_configure_toolbars"), this, SLOT(editToolbars())); a->setWhatsThis(i18n("Configure which items should appear in the toolbar(s).")); a = actionCollection()->addAction(QStringLiteral("help_about_editor")); a->setText(i18n("&About Editor Component")); connect(a, &QAction::triggered, this, &KWrite::aboutEditor); } // load on url void KWrite::loadURL(const QUrl &url) { m_view->document()->openUrl(url); -#ifdef KActivities_FOUND +#ifdef KF5Activities_FOUND if (!m_activityResource) { m_activityResource = new KActivities::ResourceInstance(winId(), this); } m_activityResource->setUri(m_view->document()->url()); #endif m_closeAction->setEnabled(true); } // is closing the window wanted by user ? bool KWrite::queryClose() { if (m_view->document()->views().count() > 1) { return true; } if (m_view->document()->queryClose()) { writeConfig(); return true; } return false; } void KWrite::slotFlush() { if (m_view->document()->closeUrl()) { m_closeAction->setDisabled(true); } } void KWrite::modifiedChanged() { documentNameChanged(); m_closeAction->setEnabled(true); } void KWrite::slotNew() { m_app->newWindow(); } void KWrite::slotOpen() { const QList urls = QFileDialog::getOpenFileUrls(this, i18n("Open File"), m_view->document()->url()); Q_FOREACH(QUrl url, urls) { slotOpen(url); } } void KWrite::slotOpen(const QUrl &url) { if (url.isEmpty()) { return; } if (m_view->document()->isModified() || !m_view->document()->url().isEmpty()) { KWrite *t = m_app->newWindow(); t->loadURL(url); } else { loadURL(url); } } void KWrite::urlChanged() { if (! m_view->document()->url().isEmpty()) { m_recentFiles->addUrl(m_view->document()->url()); } // update caption documentNameChanged(); } void KWrite::newView() { m_app->newWindow(m_view->document()); } void KWrite::toggleMenuBar(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"), QStringLiteral("HideMenuBarWarning")); } menuBar()->hide(); addMenuBarActionToContextMenu(); } } void KWrite::addMenuBarActionToContextMenu() { m_view->contextMenu()->addAction(m_paShowMenuBar); } void KWrite::removeMenuBarActionFromContextMenu() { m_view->contextMenu()->removeAction(m_paShowMenuBar); } void KWrite::toggleStatusBar() { m_view->setStatusBarEnabled(m_paShowStatusBar->isChecked()); } void KWrite::editKeys() { KShortcutsDialog dlg(KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsAllowed, this); dlg.addCollection(actionCollection()); if (m_view) { dlg.addCollection(m_view->actionCollection()); } dlg.configure(); } void KWrite::editToolbars() { KConfigGroup cfg = KSharedConfig::openConfig()->group("MainWindow"); saveMainWindowSettings(cfg); KEditToolBar dlg(guiFactory(), this); connect(&dlg, &KEditToolBar::newToolBarConfig, this, &KWrite::slotNewToolbarConfig); dlg.exec(); } void KWrite::slotNewToolbarConfig() { applyMainWindowSettings(KSharedConfig::openConfig()->group("MainWindow")); } void KWrite::dragEnterEvent(QDragEnterEvent *event) { const QList uriList = event->mimeData()->urls(); event->setAccepted(! uriList.isEmpty()); } void KWrite::dropEvent(QDropEvent *event) { slotDropEvent(event); } void KWrite::slotDropEvent(QDropEvent *event) { const QList textlist = event->mimeData()->urls(); foreach(const QUrl & url, textlist) slotOpen(url); } void KWrite::slotEnableActions(bool enable) { QList actions = actionCollection()->actions(); QList::ConstIterator it = actions.constBegin(); QList::ConstIterator end = actions.constEnd(); for (; it != end; ++it) { (*it)->setEnabled(enable); } actions = m_view->actionCollection()->actions(); it = actions.constBegin(); end = actions.constEnd(); for (; it != end; ++it) { (*it)->setEnabled(enable); } } //common config void KWrite::readConfig(KSharedConfigPtr config) { KConfigGroup cfg(config, "General Options"); m_paShowMenuBar->setChecked(cfg.readEntry("ShowMenuBar", true)); m_paShowStatusBar->setChecked(cfg.readEntry("ShowStatusBar", true)); m_paShowPath->setChecked(cfg.readEntry("ShowPath", false)); m_recentFiles->loadEntries(config->group("Recent Files")); // update visibility of menu bar and status bar toggleMenuBar(false); m_view->setStatusBarEnabled(m_paShowStatusBar->isChecked()); } void KWrite::writeConfig(KSharedConfigPtr config) { KConfigGroup generalOptions(config, "General Options"); generalOptions.writeEntry("ShowMenuBar", m_paShowMenuBar->isChecked()); generalOptions.writeEntry("ShowStatusBar", m_paShowStatusBar->isChecked()); generalOptions.writeEntry("ShowPath", m_paShowPath->isChecked()); m_recentFiles->saveEntries(KConfigGroup(config, "Recent Files")); config->sync(); } //config file void KWrite::readConfig() { readConfig(KSharedConfig::openConfig()); } void KWrite::writeConfig() { writeConfig(KSharedConfig::openConfig()); } // session management void KWrite::restore(KConfig *config, int n) { readPropertiesInternal(config, n); } void KWrite::readProperties(const KConfigGroup &config) { readConfig(); m_view->readSessionConfig(KConfigGroup(&config, QStringLiteral("General Options"))); } void KWrite::saveProperties(KConfigGroup &config) { writeConfig(); config.writeEntry("DocumentNumber", m_app->documents().indexOf(m_view->document()) + 1); KConfigGroup cg(&config, QStringLiteral("General Options")); m_view->writeSessionConfig(cg); } void KWrite::saveGlobalProperties(KConfig *config) //save documents { m_app->saveProperties(config); } void KWrite::aboutEditor() { KAboutApplicationDialog dlg(KTextEditor::Editor::instance()->aboutData(), this); dlg.exec(); } void KWrite::documentNameChanged() { QString readOnlyCaption; if (!m_view->document()->isReadWrite()) { readOnlyCaption = i18n(" [read only]"); } if (m_view->document()->url().isEmpty()) { setCaption(i18n("Untitled") + readOnlyCaption + QStringLiteral(" [*]"), m_view->document()->isModified()); return; } QString c; if (m_paShowPath->isChecked()) { c = m_view->document()->url().toString(QUrl::PreferLocalFile); const QString homePath = QDir::homePath(); if (c.startsWith(homePath)) { c = QStringLiteral("~") + c.right(c.length() - homePath.length()); } //File name shouldn't be too long - Maciek if (c.length() > 64) { c = QStringLiteral("...") + c.right(64); } } else { c = m_view->document()->url().fileName(); //File name shouldn't be too long - Maciek if (c.length() > 64) { c = c.left(64) + QStringLiteral("..."); } } setCaption(c + readOnlyCaption + QStringLiteral(" [*]"), m_view->document()->isModified()); } bool KWrite::eventFilter(QObject *obj, QEvent *event) { /** * handle mac os like file open */ if (event->type() == QEvent::FileOpen) { /** * try to open and activate the new document, like we would do for stuff * opened via file dialog */ QFileOpenEvent *foe = static_cast(event); slotOpen(foe->url()); return true; } /** * else: pass over to default implementation */ return KParts::MainWindow::eventFilter(obj, event); } QList KWrite::views() { QList list; list.append(m_view); return list; } KTextEditor::View *KWrite::activateView(KTextEditor::Document *document) { if (m_view->document() == document) { return m_view; } return nullptr; } diff --git a/kwrite/kwrite.h b/kwrite/kwrite.h index b7aece7e7..46c4c4b02 100644 --- a/kwrite/kwrite.h +++ b/kwrite/kwrite.h @@ -1,147 +1,145 @@ /* This file is part of the KDE project Copyright (C) 2001 Christoph Cullmann Copyright (C) 2001 Joseph Wenninger Copyright (C) 2001 Anders Lund 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. */ #ifndef KWRITE_MAIN_H #define KWRITE_MAIN_H #include #include #include #include #include #include -#include - class QLabel; namespace KActivities { class ResourceInstance; } class KToggleAction; class KRecentFilesAction; class KSqueezedTextLabel; class KWriteApplication; class KWrite : public KParts::MainWindow { Q_OBJECT public: KWrite(KTextEditor::Document * = nullptr, KWriteApplication *app = nullptr); ~KWrite() override; void loadURL(const QUrl &url); private: void setupActions(); void addMenuBarActionToContextMenu(); void removeMenuBarActionFromContextMenu(); bool queryClose() override; void dragEnterEvent(QDragEnterEvent *) override; void dropEvent(QDropEvent *) override; public Q_SLOTS: void slotNew(); void slotFlush(); void slotOpen(); void slotOpen(const QUrl &url); void newView(); void toggleStatusBar(); void toggleMenuBar(bool showMessage = true); void editKeys(); void editToolbars(); void aboutEditor(); void modifiedChanged(); private Q_SLOTS: void slotNewToolbarConfig(); public Q_SLOTS: void slotDropEvent(QDropEvent *); void slotEnableActions(bool enable); /** * adds a changed URL to the recent files */ void urlChanged(); /** * Overwrite size hint for better default window sizes * @return size hint */ QSize sizeHint () const override; //config file functions public: void readConfig(KSharedConfigPtr); void writeConfig(KSharedConfigPtr); void readConfig(); void writeConfig(); //session management public: void restore(KConfig *, int); public: KTextEditor::MainWindow *mainWindow() { return &m_mainWindow; } public Q_SLOTS: QWidget *window() { return this; } QList views(); KTextEditor::View *activeView() { return m_view; } KTextEditor::View *activateView(KTextEditor::Document *document); private: void readProperties(const KConfigGroup &) override; void saveProperties(KConfigGroup &) override; void saveGlobalProperties(KConfig *) override; private: KTextEditor::View *m_view; KRecentFilesAction *m_recentFiles; KToggleAction *m_paShowPath; KToggleAction *m_paShowMenuBar; KToggleAction *m_paShowStatusBar; QAction *m_closeAction; KActivities::ResourceInstance *m_activityResource; KWriteApplication *m_app; KTextEditor::MainWindow m_mainWindow; public Q_SLOTS: void documentNameChanged(); protected: /** * Event filter for QApplication to handle mac os like file open */ bool eventFilter(QObject *obj, QEvent *event) override; }; #endif diff --git a/kwrite/main.cpp b/kwrite/main.cpp index 97287139a..599eeb5c6 100644 --- a/kwrite/main.cpp +++ b/kwrite/main.cpp @@ -1,314 +1,316 @@ /* This file is part of the KDE project Copyright (C) 2001 Christoph Cullmann Copyright (C) 2001 Joseph Wenninger Copyright (C) 2001 Anders Lund 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. */ +#include "config.h" + #include "kwrite.h" #include "kwriteapplication.h" #include #include #include #include #include // for KAboutData::setDesktopFileName() #include #include #include #include #if KCrash_VERSION >= QT_VERSION_CHECK(5, 15, 0) #include #endif // KCrash >= 5.15 #include #include #include #include #include #include #include "../urlinfo.h" #ifndef Q_OS_WIN #include #endif #include extern "C" Q_DECL_EXPORT int main(int argc, char **argv) { #ifndef Q_OS_WIN // Prohibit using sudo or kdesu (but allow using the root user directly) if (getuid() == 0) { if (!qEnvironmentVariableIsEmpty("SUDO_USER")) { std::cout << "Executing KWrite with sudo is not possible due to unfixable security vulnerabilities." << std::endl; return EXIT_FAILURE; } else if (!qEnvironmentVariableIsEmpty("KDESU_USER")) { std::cout << "Executing KWrite with kdesu is not possible due to unfixable security vulnerabilities." << std::endl; return EXIT_FAILURE; } } #endif /** * Create application first * Enforce application name even if the executable is renamed */ QApplication app(argc, argv); app.setApplicationName(QStringLiteral("kwrite")); /** * enable high dpi support */ app.setAttribute(Qt::AA_UseHighDpiPixmaps, true); /** * Enable crash handling through KCrash. */ #if KCrash_VERSION >= QT_VERSION_CHECK(5, 15, 0) KCrash::initialize(); #endif /** * Connect application with translation catalogs */ KLocalizedString::setApplicationDomain("kwrite"); /** * then use i18n and co */ KAboutData aboutData(QStringLiteral("kwrite"), i18n("KWrite"), - QStringLiteral(KATE_VERSION), + QStringLiteral(KWRITE_VERSION), i18n("KWrite - Text Editor"), KAboutLicense::LGPL_V2, i18n("(c) 2000-2019 The Kate Authors"), QString(), QStringLiteral("https://kate-editor.org")); /** * right dbus prefix == org.kde. */ aboutData.setOrganizationDomain(QByteArray("kde.org")); /** * desktop file association to make application icon work (e.g. in Wayland window decoration) */ #if KCOREADDONS_VERSION >= QT_VERSION_CHECK(5, 16, 0) aboutData.setDesktopFileName(QStringLiteral("org.kde.kwrite")); #endif aboutData.addAuthor(i18n("Christoph Cullmann"), i18n("Maintainer"), QStringLiteral("cullmann@kde.org"), QStringLiteral("https://cullmann.io")); aboutData.addAuthor(i18n("Dominik Haumann"), i18n("Core Developer"), QStringLiteral("dhaumann@kde.org")); aboutData.addAuthor(i18n("Anders Lund"), i18n("Core Developer"), QStringLiteral("anders@alweb.dk"), QStringLiteral("http://www.alweb.dk")); aboutData.addAuthor(i18n("Joseph Wenninger"), i18n("Core Developer"), QStringLiteral("jowenn@kde.org"), QStringLiteral("http://stud3.tuwien.ac.at/~e9925371")); aboutData.addAuthor(i18n("Hamish Rodda"), i18n("Core Developer"), QStringLiteral("rodda@kde.org")); aboutData.addAuthor(i18n("Waldo Bastian"), i18n("The cool buffersystem"), QStringLiteral("bastian@kde.org")); aboutData.addAuthor(i18n("Charles Samuels"), i18n("The Editing Commands"), QStringLiteral("charles@kde.org")); aboutData.addAuthor(i18n("Matt Newell"), i18nc("Credit text for someone that did testing and some other similar things", "Testing, ..."), QStringLiteral("newellm@proaxis.com")); aboutData.addAuthor(i18n("Michael Bartl"), i18n("Former Core Developer"), QStringLiteral("michael.bartl1@chello.at")); aboutData.addAuthor(i18n("Michael McCallum"), i18n("Core Developer"), QStringLiteral("gholam@xtra.co.nz")); aboutData.addAuthor(i18n("Jochen Wilhemly"), i18n("KWrite Author"), QStringLiteral("digisnap@cs.tu-berlin.de")); aboutData.addAuthor(i18n("Michael Koch"), i18n("KWrite port to KParts"), QStringLiteral("koch@kde.org")); aboutData.addAuthor(i18n("Christian Gebauer"), QString(), QStringLiteral("gebauer@kde.org")); aboutData.addAuthor(i18n("Simon Hausmann"), QString(), QStringLiteral("hausmann@kde.org")); aboutData.addAuthor(i18n("Glen Parker"), i18n("KWrite Undo History, Kspell integration"), QStringLiteral("glenebob@nwlink.com")); aboutData.addAuthor(i18n("Scott Manson"), i18n("KWrite XML Syntax highlighting support"), QStringLiteral("sdmanson@alltel.net")); aboutData.addAuthor(i18n("John Firebaugh"), i18n("Patches and more"), QStringLiteral("jfirebaugh@kde.org")); aboutData.addAuthor(i18n("Gerald Senarclens de Grancy"), i18n("QA and Scripting"), QStringLiteral("oss@senarclens.eu"), QStringLiteral("http://find-santa.eu/")); aboutData.addCredit(i18n("Matteo Merli"), i18n("Highlighting for RPM Spec-Files, Perl, Diff and more"), QStringLiteral("merlim@libero.it")); aboutData.addCredit(i18n("Rocky Scaletta"), i18n("Highlighting for VHDL"), QStringLiteral("rocky@purdue.edu")); aboutData.addCredit(i18n("Yury Lebedev"), i18n("Highlighting for SQL")); aboutData.addCredit(i18n("Chris Ross"), i18n("Highlighting for Ferite")); aboutData.addCredit(i18n("Nick Roux"), i18n("Highlighting for ILERPG")); aboutData.addCredit(i18n("Carsten Niehaus"), i18n("Highlighting for LaTeX")); aboutData.addCredit(i18n("Per Wigren"), i18n("Highlighting for Makefiles, Python")); aboutData.addCredit(i18n("Jan Fritz"), i18n("Highlighting for Python")); aboutData.addCredit(i18n("Daniel Naber")); aboutData.addCredit(i18n("Roland Pabel"), i18n("Highlighting for Scheme")); aboutData.addCredit(i18n("Cristi Dumitrescu"), i18n("PHP Keyword/Datatype list")); aboutData.addCredit(i18n("Carsten Pfeiffer"), i18nc("Credit text for someone that helped a lot", "Very nice help")); aboutData.addCredit(i18n("All people who have contributed and I have forgotten to mention")); /** * bugzilla */ aboutData.setProductName(QByteArray("kate/kwrite")); /** * set and register app about data */ KAboutData::setApplicationData(aboutData); /** * set the program icon */ QApplication::setWindowIcon(QIcon::fromTheme(QStringLiteral("accessories-text-editor"), app.windowIcon())); /** * Create command line parser and feed it with known options */ QCommandLineParser parser; aboutData.setupCommandLine(&parser); // -e/--encoding option const QCommandLineOption useEncoding(QStringList() << QStringLiteral("e") << QStringLiteral("encoding"), i18n("Set encoding for the file to open."), i18n("encoding")); parser.addOption(useEncoding); // -l/--line option const QCommandLineOption gotoLine(QStringList() << QStringLiteral("l") << QStringLiteral("line"), i18n("Navigate to this line."), i18n("line")); parser.addOption(gotoLine); // -c/--column option const QCommandLineOption gotoColumn(QStringList() << QStringLiteral("c") << QStringLiteral("column"), i18n("Navigate to this column."), i18n("column")); parser.addOption(gotoColumn); // -i/--stdin option const QCommandLineOption readStdIn(QStringList() << QStringLiteral("i") << QStringLiteral("stdin"), i18n("Read the contents of stdin.")); parser.addOption(readStdIn); // --tempfile option const QCommandLineOption tempfile(QStringList() << QStringLiteral("tempfile"), i18n("The files/URLs opened by the application will be deleted after use")); parser.addOption(tempfile); // urls to open parser.addPositionalArgument(QStringLiteral("urls"), i18n("Documents to open."), i18n("[urls...]")); /** * do the command line parsing */ parser.process(app); /** * handle standard options */ aboutData.processCommandLine(&parser); KWriteApplication kapp; if (app.isSessionRestored()) { kapp.restore(); } else { bool nav = false; int line = 0, column = 0; QTextCodec *codec = parser.isSet(QStringLiteral("encoding")) ? QTextCodec::codecForName(parser.value(QStringLiteral("encoding")).toLocal8Bit()) : nullptr; if (parser.isSet(QStringLiteral("line"))) { line = parser.value(QStringLiteral("line")).toInt() - 1; nav = true; } if (parser.isSet(QStringLiteral("column"))) { column = parser.value(QStringLiteral("column")).toInt() - 1; nav = true; } if (parser.positionalArguments().count() == 0) { KWrite *t = kapp.newWindow(); if (parser.isSet(QStringLiteral("stdin"))) { QTextStream input(stdin, QIODevice::ReadOnly); // set chosen codec if (codec) { input.setCodec(codec); } QString line; QString text; do { line = input.readLine(); text.append(line + QLatin1Char('\n')); } while (!line.isNull()); KTextEditor::Document *doc = t->activeView()->document(); if (doc) { // remember codec in document, e.g. to show the right one if (codec) { doc->setEncoding(QString::fromLatin1(codec->name())); } doc->setText(text); } } if (nav && t->activeView()) { t->activeView()->setCursorPosition(KTextEditor::Cursor(line, column)); } } else { int docs_opened = 0; Q_FOREACH(const QString positionalArgument, parser.positionalArguments()) { UrlInfo info(positionalArgument); if (nav) { info.cursor = KTextEditor::Cursor(line, column); } // this file is no local dir, open it, else warn bool noDir = !info.url.isLocalFile() || !QFileInfo(info.url.toLocalFile()).isDir(); if (noDir) { ++docs_opened; KWrite *t = kapp.newWindow(); if (codec) { t->activeView()->document()->setEncoding(QString::fromLatin1(codec->name())); } t->loadURL(info.url); if (info.cursor.isValid()) { t->activeView()->setCursorPosition(info.cursor); } else if (info.url.hasQuery()) { QUrlQuery q(info.url); QString lineStr = q.queryItemValue(QStringLiteral("line")); QString columnStr = q.queryItemValue(QStringLiteral("column")); line = lineStr.toInt(); if (line > 0) line--; column = columnStr.toInt(); if (column > 0) column--; t->activeView()->setCursorPosition(KTextEditor::Cursor(line, column)); } } else { KMessageBox::sorry(nullptr, i18n("The file '%1' could not be opened: it is not a normal file, it is a folder.", info.url.toString())); } } if (!docs_opened) { ::exit(1); // see http://bugs.kde.org/show_bug.cgi?id=124708 } } } // no window there, uh, ohh, for example borked session config !!! // create at least one !! if (kapp.noWindows()) { kapp.newWindow(); } /** * finally register this kwrite instance for dbus, don't die if no dbus is around! */ const KDBusService dbusService(KDBusService::Multiple | KDBusService::NoExitOnFailure); /** * Run the event loop */ return app.exec(); }