diff --git a/addons/CMakeLists.txt b/addons/CMakeLists.txt index d26d67e88..c0b8f2692 100644 --- a/addons/CMakeLists.txt +++ b/addons/CMakeLists.txt @@ -1,88 +1,86 @@ # detect additional frameworks find_package(KF5 "${KF5_DEP_VERSION}" OPTIONAL_COMPONENTS Wallet Plasma Service ItemModels ThreadWeaver NewStuff IconThemes GuiAddons) set_package_properties(KF5Wallet PROPERTIES PURPOSE "Required to build the katesql addon") set_package_properties(KF5Plasma PROPERTIES PURPOSE "Required to build the sessionapplet addon") set_package_properties(KF5Service PROPERTIES PURPOSE "Required to build the sessionapplet addon") set_package_properties(KF5ItemModels PROPERTIES PURPOSE "Required to build the project, konsole addon") set_package_properties(KF5ThreadWeaver PROPERTIES PURPOSE "Required to build the project addon") set_package_properties(KF5NewStuff PROPERTIES PURPOSE "Required to build the snippets and project addons") # document switcher ecm_optional_add_subdirectory (filetree) # search in open documents and files ecm_optional_add_subdirectory (search) # ALT+Tab like tab switcher ecm_optional_add_subdirectory (tabswitcher) # ctags ecm_optional_add_subdirectory (kate-ctags) # backtrace ecm_optional_add_subdirectory (backtracebrowser) -# file browser, atm guarded until we have a check for KIOWidgets -if (NOT WIN32) - ecm_optional_add_subdirectory (filebrowser) -endif() +# file browser +ecm_optional_add_subdirectory (filebrowser) # xml completion ecm_optional_add_subdirectory (xmltools) # XML Validation plugin ecm_optional_add_subdirectory (xmlcheck) # open header matching to current file ecm_optional_add_subdirectory (openheader) # debugger plugin, needs windows love, guarded until ported to win32 if (NOT WIN32) ecm_optional_add_subdirectory (gdbplugin) endif () # list symbols and functions in a file ecm_optional_add_subdirectory (symbolviewer) # replicode integration ecm_optional_add_subdirectory (replicode) # pipe text through some external command ecm_optional_add_subdirectory (textfilter) # Rust complection plugin ecm_optional_add_subdirectory (rustcompletion) # D completion plugin ecm_optional_add_subdirectory (lumen) # build plugin ecm_optional_add_subdirectory (katebuild-plugin) # close document except this one (or similar) ecm_optional_add_subdirectory (close-except-like) if(KF5Wallet_FOUND) # kate sql ecm_optional_add_subdirectory (katesql) endif() if(KF5NewStuff_FOUND) # snippets ecm_optional_add_subdirectory (snippets) endif() -if(KF5Service_FOUND) - # terminal tool view +# terminal tool view +if(KF5Service_FOUND AND NOT WIN32) ecm_optional_add_subdirectory (konsole) endif() if(KF5ItemModels_FOUND AND KF5ThreadWeaver_FOUND AND KF5NewStuff_FOUND) # small & smart project manager ecm_optional_add_subdirectory (project) endif() if (KF5Plasma_FOUND AND KF5Service_FOUND) ecm_optional_add_subdirectory (sessionapplet) endif() diff --git a/kate/CMakeLists.txt b/kate/CMakeLists.txt index 74b2f78e7..b7e3c37fe 100644 --- a/kate/CMakeLists.txt +++ b/kate/CMakeLists.txt @@ -1,153 +1,151 @@ # # 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 ) -option(USE_QT_SINGLE_APP "Use QtSingleApplication for single application in stead of DBus" OFF) - - # collect the needed source files set (KATE_LIBRARY_SRCS kateappadaptor.cpp kateapp.cpp kateconfigdialog.cpp kateconfigplugindialogpage.cpp katedocmanager.cpp katemainwindow.cpp katepluginmanager.cpp kateviewmanager.cpp kateviewspace.cpp katesavemodifieddialog.cpp katemwmodonhddialog.cpp katetabbutton.cpp katetabbar.cpp # session session/katesessionchooser.cpp session/katesessionsaction.cpp session/katesessionmanager.cpp session/katesessionmanagedialog.cpp session/katesessionopendialog.cpp session/katesession.cpp katemdi.cpp katerunninginstanceinfo.cpp katequickopen.cpp katewaiter.h ) ki18n_wrap_ui(KATE_LIBRARY_SRCS ui/sessionconfigwidget.ui ) qt5_add_resources( KATE_LIBRARY_SRCS data/kate.qrc ) add_library(kdeinit_kate STATIC ${KATE_LIBRARY_SRCS}) target_link_libraries(kdeinit_kate PUBLIC KF5::TextEditor KF5::I18n KF5::IconThemes KF5::WindowSystem KF5::GuiAddons KF5::DBusAddons KF5::Crash) if(KF5Activities_FOUND) target_link_libraries(kdeinit_kate PUBLIC KF5::Activities) endif() generate_export_header(kdeinit_kate EXPORT_FILE_NAME kateprivate_export.h EXPORT_MACRO_NAME KATE_TESTS_EXPORT ) # collect icons set(KATE_ICONS_PNG ${CMAKE_CURRENT_SOURCE_DIR}/icons/16-apps-kate.png ${CMAKE_CURRENT_SOURCE_DIR}/icons/22-apps-kate.png ${CMAKE_CURRENT_SOURCE_DIR}/icons/32-apps-kate.png ${CMAKE_CURRENT_SOURCE_DIR}/icons/48-apps-kate.png ${CMAKE_CURRENT_SOURCE_DIR}/icons/64-apps-kate.png ${CMAKE_CURRENT_SOURCE_DIR}/icons/128-apps-kate.png ) set(KATE_ICONS_SVG ${CMAKE_CURRENT_SOURCE_DIR}/icons/sc-apps-kate.svgz ) # application only sources set (KATE_APP_SRCS main.cpp ) -if (USE_QT_SINGLE_APP) +# use single application instead of dbus on mac + windows +if (APPLE OR WIN32) set(singleapp_SRCS qtsingleapplication/qtlocalpeer.cpp qtsingleapplication/qtsingleapplication.cpp qtsingleapplication/qtlockedfile.cpp ) if(WIN32) set(singleapp_SRCS ${singleapp_SRCS} qtsingleapplication/qtlockedfile_win.cpp) else() set(singleapp_SRCS ${singleapp_SRCS} qtsingleapplication/qtlockedfile_unix.cpp) endif() add_definitions("-DUSE_QT_SINGLE_APP") set(KATE_APP_SRCS ${KATE_APP_SRCS} ${singleapp_SRCS}) endif() # add icons to application sources, to have them bundled ecm_add_app_icon(KATE_APP_SRCS ICONS ${KATE_ICONS_PNG}) # create executable add_executable(kate ${KATE_APP_SRCS}) target_link_libraries(kate kdeinit_kate) # own plist magic for mac os if(APPLE) # own plist template set_target_properties (kate PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/data/MacOSXBundleInfo.plist.in) # the MacOSX bundle display name property (CFBundleDisplayName) is not currently supported by cmake, # so has to be set for all targets in this cmake file set(MACOSX_BUNDLE_DISPLAY_NAME Kate) set_target_properties(kate PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER "org.kde.Kate") set_target_properties(kate PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Kate") set_target_properties(kate PROPERTIES MACOSX_BUNDLE_DISPLAY_NAME "Kate") set_target_properties(kate PROPERTIES MACOSX_BUNDLE_INFO_STRING "Kate - Advanced Text Editor") set_target_properties(kate PROPERTIES MACOSX_BUNDLE_LONG_VERSION_STRING "Kate ${KDE_APPLICATIONS_VERSION}") set_target_properties(kate PROPERTIES MACOSX_BUNDLE_SHORT_VERSION_STRING "${KDE_APPLICATIONS_VERSION_MAJOR}.${KDE_APPLICATIONS_VERSION_MINOR}") set_target_properties(kate PROPERTIES MACOSX_BUNDLE_BUNDLE_VERSION "${KDE_APPLICATIONS_VERSION}") set_target_properties(kate PROPERTIES MACOSX_BUNDLE_COPYRIGHT "2000-2016 The Kate Authors") endif() # install executable install(TARGETS kate ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) # desktop file install(PROGRAMS ${CMAKE_CURRENT_SOURCE_DIR}/data/org.kde.kate.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}) # appdata install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/data/org.kde.kate.appdata.xml DESTINATION ${CMAKE_INSTALL_METAINFODIR}) # install icons ecm_install_icons(ICONS ${KATE_ICONS_PNG} ${KATE_ICONS_SVG} DESTINATION ${ICON_INSTALL_DIR} THEME hicolor) # automatic unit tests ecm_optional_add_subdirectory (autotests) diff --git a/kate/kateapp.cpp b/kate/kateapp.cpp index 5c84e339b..cea0a2715 100644 --- a/kate/kateapp.cpp +++ b/kate/kateapp.cpp @@ -1,443 +1,449 @@ /* 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 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 "kateapp.h" #include "kateviewmanager.h" #include "katemainwindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "../../urlinfo.h" /** * singleton instance pointer */ static KateApp *appSelf = Q_NULLPTR; Q_LOGGING_CATEGORY(LOG_KATE, "kate", QtWarningMsg) KateApp::KateApp(const QCommandLineParser &args) : m_args(args) , m_wrapper(appSelf = this) , m_docManager(this) , m_adaptor(this) , m_pluginManager(this) , m_sessionManager(this) { /** * re-route some signals to application wrapper */ connect(&m_docManager, &KateDocManager::documentCreated, &m_wrapper, &KTextEditor::Application::documentCreated); connect(&m_docManager, &KateDocManager::documentWillBeDeleted, &m_wrapper, &KTextEditor::Application::documentWillBeDeleted); connect(&m_docManager, &KateDocManager::documentDeleted, &m_wrapper, &KTextEditor::Application::documentDeleted); connect(&m_docManager, &KateDocManager::aboutToCreateDocuments, &m_wrapper, &KTextEditor::Application::aboutToCreateDocuments); connect(&m_docManager, &KateDocManager::documentsCreated, &m_wrapper, &KTextEditor::Application::documentsCreated); /** * handle mac os x like file open request via event filter */ qApp->installEventFilter(this); } KateApp::~KateApp() { /** * unregister from dbus before we get unusable... */ if (QDBusConnection::sessionBus().interface()) { m_adaptor.emitExiting(); QDBusConnection::sessionBus().unregisterObject(QStringLiteral("/MainApplication")); } } KateApp *KateApp::self() { return appSelf; } bool KateApp::init() { // set KATE_PID for use in child processes qputenv("KATE_PID", QString::fromLatin1("%1").arg(QCoreApplication::applicationPid()).toLatin1().constData()); // handle restore different if (qApp->isSessionRestored()) { restoreKate(); } else { // let us handle our command line args and co ;) // we can exit here if session chooser decides if (!startupKate()) { // session chooser telled to exit kate return false; } } // application dbus interface if (QDBusConnection::sessionBus().interface()) { QDBusConnection::sessionBus().registerObject(QStringLiteral("/MainApplication"), this); } return true; } void KateApp::restoreKate() { KConfig *sessionConfig = KConfigGui::sessionConfig(); // activate again correct session!!! QString lastSession(sessionConfig->group("General").readEntry("Last Session", QString())); sessionManager()->activateSession(lastSession, false, false); // plugins KateApp::self()->pluginManager()->loadConfig(sessionConfig); // restore the files we need m_docManager.restoreDocumentList(sessionConfig); // restore all windows ;) for (int n = 1; KMainWindow::canBeRestored(n); n++) { newMainWindow(sessionConfig, QString::number(n)); } // oh, no mainwindow, create one, should not happen, but make sure ;) if (mainWindowsCount() == 0) { newMainWindow(); } } bool KateApp::startupKate() { // user specified session to open if (m_args.isSet(QStringLiteral("start"))) { sessionManager()->activateSession(m_args.value(QStringLiteral("start")), false); } else if (m_args.isSet(QStringLiteral("startanon"))) { sessionManager()->activateAnonymousSession(); } else if (!m_args.isSet(QStringLiteral("stdin")) && (m_args.positionalArguments().count() == 0)) { // only start session if no files specified // let the user choose session if possible if (!sessionManager()->chooseSession()) { // we will exit kate now, notify the rest of the world we are done KStartupInfo::appStarted(KStartupInfo::startupId()); return false; } } else { sessionManager()->activateAnonymousSession(); } // oh, no mainwindow, create one, should not happen, but make sure ;) if (mainWindowsCount() == 0) { newMainWindow(); } // notify about start KStartupInfo::setNewStartupId(activeKateMainWindow(), KStartupInfo::startupId()); QTextCodec *codec = m_args.isSet(QStringLiteral("encoding")) ? QTextCodec::codecForName(m_args.value(QStringLiteral("encoding")).toUtf8()) : 0; bool tempfileSet = m_args.isSet(QStringLiteral("tempfile")); KTextEditor::Document *doc = 0; const QString codec_name = codec ? QString::fromLatin1(codec->name()) : QString(); Q_FOREACH(const QString positionalArgument, m_args.positionalArguments()) { UrlInfo info(positionalArgument); // this file is no local dir, open it, else warn bool noDir = !info.url.isLocalFile() || !QFileInfo(info.url.toLocalFile()).isDir(); if (noDir) { doc = openDocUrl(info.url, codec_name, tempfileSet); if (info.cursor.isValid()) { setCursor(info.cursor.line(), info.cursor.column()); } } else { KMessageBox::sorry(activeKateMainWindow(), i18n("The file '%1' could not be opened: it is not a normal file, it is a folder.", info.url.toString())); } } // handle stdin input if (m_args.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()); openInput(text); } else if (doc) { activeKateMainWindow()->viewManager()->activateView(doc); } int line = 0; int column = 0; bool nav = false; if (m_args.isSet(QStringLiteral("line"))) { line = m_args.value(QStringLiteral("line")).toInt() - 1; nav = true; } if (m_args.isSet(QStringLiteral("column"))) { column = m_args.value(QStringLiteral("column")).toInt() - 1; nav = true; } if (nav && activeKateMainWindow()->viewManager()->activeView()) { activeKateMainWindow()->viewManager()->activeView()->setCursorPosition(KTextEditor::Cursor(line, column)); } activeKateMainWindow()->setAutoSaveSettings(); return true; } void KateApp::shutdownKate(KateMainWindow *win) { if (!win->queryClose_internal()) { return; } sessionManager()->saveActiveSession(true); // cu main windows while (!m_mainWindows.isEmpty()) { // mainwindow itself calls KateApp::removeMainWindow(this) delete m_mainWindows[0]; } QApplication::quit(); } KatePluginManager *KateApp::pluginManager() { return &m_pluginManager; } KateDocManager *KateApp::documentManager() { return &m_docManager; } KateSessionManager *KateApp::sessionManager() { return &m_sessionManager; } bool KateApp::openUrl(const QUrl &url, const QString &encoding, bool isTempFile) { return openDocUrl(url, encoding, isTempFile); } KTextEditor::Document *KateApp::openDocUrl(const QUrl &url, const QString &encoding, bool isTempFile) { KateMainWindow *mainWindow = activeKateMainWindow(); if (!mainWindow) { return 0; } QTextCodec *codec = encoding.isEmpty() ? 0 : QTextCodec::codecForName(encoding.toLatin1()); // this file is no local dir, open it, else warn bool noDir = !url.isLocalFile() || !QFileInfo(url.toLocalFile()).isDir(); KTextEditor::Document *doc = 0; if (noDir) { // open a normal file if (codec) { doc = mainWindow->viewManager()->openUrl(url, QString::fromLatin1(codec->name()), true, isTempFile); } else { doc = mainWindow->viewManager()->openUrl(url, QString(), true, isTempFile); } } else KMessageBox::sorry(mainWindow, i18n("The file '%1' could not be opened: it is not a normal file, it is a folder.", url.url())); return doc; } bool KateApp::setCursor(int line, int column) { KateMainWindow *mainWindow = activeKateMainWindow(); if (!mainWindow) { return false; } if (auto v = mainWindow->viewManager()->activeView()) { v->removeSelection(); v->setCursorPosition(KTextEditor::Cursor(line, column)); } return true; } bool KateApp::openInput(const QString &text) { activeKateMainWindow()->viewManager()->openUrl(QUrl(), QString(), true); if (!activeKateMainWindow()->viewManager()->activeView()) { return false; } KTextEditor::Document *doc = activeKateMainWindow()->viewManager()->activeView()->document(); if (!doc) { return false; } return doc->setText(text); } KateMainWindow *KateApp::newMainWindow(KConfig *sconfig_, const QString &sgroup_) { KConfig *sconfig = sconfig_ ? sconfig_ : KSharedConfig::openConfig().data(); QString sgroup = !sgroup_.isEmpty() ? sgroup_ : QStringLiteral("MainWindow0"); KateMainWindow *mainWindow = new KateMainWindow(sconfig, sgroup); mainWindow->show(); return mainWindow; } void KateApp::addMainWindow(KateMainWindow *mainWindow) { m_mainWindows.push_back(mainWindow); } void KateApp::removeMainWindow(KateMainWindow *mainWindow) { m_mainWindows.removeAll(mainWindow); } KateMainWindow *KateApp::activeKateMainWindow() { if (m_mainWindows.isEmpty()) { return 0; } int n = m_mainWindows.indexOf(static_cast((static_cast(QCoreApplication::instance())->activeWindow()))); if (n < 0) { n = 0; } return m_mainWindows[n]; } int KateApp::mainWindowsCount() const { return m_mainWindows.size(); } int KateApp::mainWindowID(KateMainWindow *window) { return m_mainWindows.indexOf(window); } KateMainWindow *KateApp::mainWindow(int n) { if (n < m_mainWindows.size()) { return m_mainWindows[n]; } return 0; } void KateApp::emitDocumentClosed(const QString &token) { m_adaptor.emitDocumentClosed(token); } KTextEditor::Plugin *KateApp::plugin(const QString &name) { return m_pluginManager.plugin(name); } bool KateApp::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 dbus */ QFileOpenEvent *foe = static_cast(event); KTextEditor::Document *doc = openDocUrl(foe->url(), QString(), false); if (doc && activeKateMainWindow()) { activeKateMainWindow()->viewManager()->activateView(doc); } return true; } - + /** * else: pass over to default implementation */ return QObject::eventFilter(obj, event); } -#ifdef USE_QT_SINGLE_APP -void KateApp::remoteMessageReceived(const QString &message, QObject *socket) +void KateApp::remoteMessageReceived(const QString &message, QObject *) { - //qDebug() << message; - QStringList urlInfos = message.split(QLatin1Char(';'), QString::SkipEmptyParts); - Q_FOREACH(QString urlInfo, urlInfos) { - QStringList info = urlInfo.split(QStringLiteral("||")); - //qDebug() << urlInfo << info; - if (info.size() != 3) { - continue; - } - QUrl url = QUrl::fromUserInput(info[0]); - bool ok; - int line = info[1].toInt(&ok); - int column = info[2].toInt(); + /** + * try to parse message, ignore if no object + */ + const QJsonDocument jsonMessage = QJsonDocument::fromJson(message.toUtf8()); + if (!jsonMessage.isObject()) + return; + /** + * open all passed urls + */ + const QJsonArray urls = jsonMessage.object().value(QLatin1String("urls")).toArray(); + Q_FOREACH(QJsonValue urlObject, urls) { + /** + * get url meta data + */ + const QUrl url = urlObject.toObject().value(QLatin1String("url")).toVariant().toUrl(); + const int line = urlObject.toObject().value(QLatin1String("line")).toVariant().toInt(); + const int column = urlObject.toObject().value(QLatin1String("column")).toVariant().toInt(); + + /** + * open file + set line/column if requested + */ openUrl(url, QString(), false); - if (ok) { + if (line >= 0 && column >= 0) { setCursor(line, column); } + } } -#endif - - diff --git a/kate/kateapp.h b/kate/kateapp.h index 8cc96ba7c..692614e9e 100644 --- a/kate/kateapp.h +++ b/kate/kateapp.h @@ -1,366 +1,365 @@ /* 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 "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(); /** * 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 = 0, 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 * @return success */ bool openUrl(const QUrl &url, const QString &encoding, bool isTempFile); 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 * @return success */ bool openInput(const QString &text); // // 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; for (int i = 0; i < m_mainWindows.size(); ++i) { windows.push_back(m_mainWindows[i]->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; } -#ifdef USE_QT_SINGLE_APP /** - * A message is received from an external instance + * 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); -#endif - + protected: /** * Event filter for QApplication to handle mac os like file open */ bool eventFilter(QObject *obj, QEvent *event); 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/katetabbar.h b/kate/katetabbar.h index 2e5f5cb81..3744de179 100644 --- a/kate/katetabbar.h +++ b/kate/katetabbar.h @@ -1,247 +1,247 @@ /* This file is part of the KDE project * * Copyright (C) 2014 Dominik Haumann * * 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_TAB_BAR_H #define KATE_TAB_BAR_H #include #include #include #include class KateTabButton; class KateTabBarPrivate; /** * The \p KateTabBar class provides a tab bar, e.g. for tabbed documents. * * The API closely follows the API of QTabBar. * * @author Dominik Haumann */ class KateTabBar : public QWidget { Q_OBJECT Q_PROPERTY(bool isActive READ isActive WRITE setActive) public: explicit KateTabBar(QWidget *parent = 0); virtual ~KateTabBar(); /** * Adds a new tab with \a text. Returns the new tab's id. */ int addTab(const QString &text); /** * Insert a tab at \p position with \a text. Returns the new tab's id. * @param position index of the tab, i.e. 0, ..., count() */ int insertTab(int position, const QString & text); /** * Removes the tab with ID \a id. * @return the position where the tab was */ int removeTab(int index); /** * Get the ID of the tab bar's activated tab. Returns -1 if no tab is activated. */ int currentTab() const; /** * Get the ID of the tab that is located left of the current tab. * The return value is -1, if there is no previous tab. */ int prevTab() const; /** * Get the ID of the tab that is located right of the current tab. * The return value is -1, if there is no next tab. */ int nextTab() const; public Q_SLOTS: /** * Activate the tab with \p id. No signal is emitted. */ void setCurrentTab(int index); // does not emit signal public: /** * Returns whether a tab with ID \a id exists. */ bool containsTab(int index) const; /** * Set the button @p id's tool tip to @p tip. */ void setTabToolTip(int index, const QString &tip); /** * Get the button @p id's url. Result is QStrint() if not available. */ QString tabToolTip(int index) const; /** * Sets the text of the tab with ID \a id to \a text. * \see tabText() */ void setTabText(int index, const QString &text); /** * Returns the text of the tab with ID \a id. If the button id does not * exist \a QString() is returned. * \see setTabText() */ QString tabText(int index) const; /** * Sets the icon of the tab with ID \a id to \a icon. * \see tabIcon() */ void setTabIcon(int index, const QIcon &pixmap); /** * Returns the icon of the tab with ID \a id. If the button id does not * exist \a QIcon() is returned. * \see setTabIcon() */ QIcon tabIcon(int index) const; /** * Returns the number of tabs in the tab bar. */ int count() const; /** * Return the maximum amount of tabs that fit into the tab bar given * the minimumTabWidth(). */ int maxTabCount() const; /** * Marks this tabbar as active. That is, current-tab indicators are * properly highlighted, indicating that child widgets of this tabbar * will get input. * * This concept is mostly useful, if your application has multiple tabbars. * Inactive tabbars are grayed out. */ void setActive(bool active); /** * Returns whether this tabbar is active. */ bool isActive() const; Q_SIGNALS: /** * This signal is emitted whenever the current activated tab changes. */ void currentChanged(int id); /** * This signal is emitted whenever tab @p id should be closed. */ void closeTabRequested(int id); /** * This signal is emitted whenever the context menu is requested for * button @p id at position @p globalPos. * @param id the button, or -1 if the context menu was requested on * at a place where no tab exists * @param globalPos the position of the context menu in global coordinates */ void contextMenuRequest(int id, const QPoint & globalPos); /** * This signal is emitted whenever the tab bar's width allows to * show more tabs than currently available. In other words, * you can safely add @p count tabs which are guaranteed to be visible. */ void moreTabsRequested(int count); /** * This signal is emitted whenever the tab bar's width is too small, * such that not all tabs can be shown. * Therefore, @p count tabs should be removed. */ void lessTabsRequested(int count); /** * This signal is emitted whenever the users double clicks on the free * space next to the tab bar. Typically, a new document should be * created. */ void newTabRequested(); /** * This signal is emitted whenever the tab bar was clicked while the * tab bar is not the active view space tab bar. */ void activateViewSpaceRequested(); protected Q_SLOTS: /** * Active button changed. Emit signal \p currentChanged() with the button's ID. */ void tabButtonActivated(KateTabButton *tabButton); /** * If the user wants to close a tab with the context menu, it sends a close * request. */ void tabButtonCloseRequest(KateTabButton *tabButton); protected: //! Recalculate geometry for all tabs. void resizeEvent(QResizeEvent *event) override; //! Override to avoid requesting a new tab. void mouseDoubleClickEvent(QMouseEvent *event) override; //! Override to request making the tab bar active. void mousePressEvent(QMouseEvent *event) override; //! trigger repaint on hover leave event void leaveEvent(QEvent *event) override; //! Paint tab separators void paintEvent(QPaintEvent *event) override; //! Request context menu - void contextMenuEvent(QContextMenuEvent *ev); + void contextMenuEvent(QContextMenuEvent *ev) override; //! Cycle through tabs void wheelEvent(QWheelEvent * event) override; //! Support for drag & drop of tabs void dragEnterEvent(QDragEnterEvent *event) override; void dragMoveEvent(QDragMoveEvent *event) override; void dropEvent(QDropEvent *event) override; private: // pimpl data holder KateTabBarPrivate * const d; }; #endif // KATE_TAB_BAR_H diff --git a/kate/main.cpp b/kate/main.cpp index e07b9701c..fd3cff518 100644 --- a/kate/main.cpp +++ b/kate/main.cpp @@ -1,521 +1,561 @@ /* 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 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 "kateapp.h" #include "katerunninginstanceinfo.h" #include "katewaiter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../urlinfo.h" #ifdef USE_QT_SINGLE_APP #include "qtsingleapplication/qtsingleapplication.h" #endif int main(int argc, char **argv) { /** * init resources from our static lib */ Q_INIT_RESOURCE(kate); /** * Create application first */ #ifdef USE_QT_SINGLE_APP SharedTools::QtSingleApplication app(QStringLiteral("kate"),argc, argv); #else QApplication app(argc, argv); #endif /** * Enforce application name even if the executable is renamed */ app.setApplicationName(QStringLiteral("kate")); /** * enable high dpi support */ app.setAttribute(Qt::AA_UseHighDpiPixmaps, true); /** * Connect application with translation catalogs */ KLocalizedString::setApplicationDomain("kate"); /** * construct about data for Kate */ KAboutData aboutData(QStringLiteral("kate"), i18n("Kate"), QStringLiteral(KATE_VERSION), i18n("Kate - Advanced Text Editor"), KAboutLicense::LGPL_V2, i18n("(c) 2000-2016 The Kate Authors"), QString(), QStringLiteral("http://kate-editor.org")); /** * right dbus prefix == org.kde. */ aboutData.setOrganizationDomain("kde.org"); aboutData.addAuthor(i18n("Christoph Cullmann"), i18n("Maintainer"), QStringLiteral("cullmann@kde.org"), QStringLiteral("http://www.cullmann.io")); 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("Dominik Haumann"), i18n("Developer & Highlight wizard"), QStringLiteral("dhdev@gmx.de")); aboutData.addAuthor(i18n("Kåre Särs"), i18n("Developer"), QStringLiteral("kare.sars@iki.fi")); aboutData.addAuthor(i18n("Alexander Neundorf"), i18n("Developer"), QStringLiteral("neundorf@kde.org")); aboutData.addAuthor(i18n("Sven Brauch"), i18n("Developer"), QStringLiteral("mail@svenbrauch.de")); 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"), i18n("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("Pablo Martín"), i18n("Python Plugin Developer"), QStringLiteral("goinnn@gmail.com"), QStringLiteral("http://github.com/goinnn/")); 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"), i18n("Very nice help")); aboutData.addCredit(i18n("All people who have contributed and I have forgotten to mention")); /** * set the new Kate mascot */ aboutData.setProgramLogo (QImage(QLatin1String(":/kate/mascot.png"))); /** * register about data */ KAboutData::setApplicationData(aboutData); /** * take component name and org. name from KAboutData */ app.setApplicationName(aboutData.componentName()); app.setApplicationDisplayName(aboutData.displayName()); app.setOrganizationDomain(aboutData.organizationDomain()); app.setApplicationVersion(aboutData.version()); /** * set the program icon */ QApplication::setWindowIcon(QIcon::fromTheme(QLatin1String("kate"))); /** * Create command line parser and feed it with known options */ QCommandLineParser parser; aboutData.setupCommandLine(&parser); parser.setApplicationDescription(aboutData.shortDescription()); parser.addHelpOption(); parser.addVersionOption(); // -s/--start session option const QCommandLineOption startSessionOption(QStringList() << QStringLiteral("s") << QStringLiteral("start"), i18n("Start Kate with a given session."), QStringLiteral("session")); parser.addOption(startSessionOption); // --startanon session option const QCommandLineOption startAnonymousSessionOption(QStringList() << QStringLiteral("startanon"), i18n("Start Kate with a new anonymous session, implies '-n'.")); parser.addOption(startAnonymousSessionOption); // -n/--new option const QCommandLineOption startNewInstanceOption(QStringList() << QStringLiteral("n") << QStringLiteral("new"), i18n("Force start of a new kate instance (is ignored if start is used and another kate instance already has the given session opened), forced if no parameters and no URLs are given at all.")); parser.addOption(startNewInstanceOption); // -b/--block option const QCommandLineOption startBlockingOption(QStringList() << QStringLiteral("b") << QStringLiteral("block"), i18n("If using an already running kate instance, block until it exits, if URLs given to open.")); parser.addOption(startBlockingOption); // -p/--pid option const QCommandLineOption usePidOption(QStringList() << QStringLiteral("p") << QStringLiteral("pid"), i18n("Only try to reuse kate instance with this pid (is ignored if start is used and another kate instance already has the given session opened)."), QStringLiteral("pid")); parser.addOption(usePidOption); // -e/--encoding option const QCommandLineOption useEncodingOption(QStringList() << QStringLiteral("e") << QStringLiteral("encoding"), i18n("Set encoding for the file to open."), QStringLiteral("encoding")); parser.addOption(useEncodingOption); // -l/--line option const QCommandLineOption gotoLineOption(QStringList() << QStringLiteral("l") << QStringLiteral("line"), i18n("Navigate to this line."), QStringLiteral("line")); parser.addOption(gotoLineOption); // -c/--column option const QCommandLineOption gotoColumnOption(QStringList() << QStringLiteral("c") << QStringLiteral("column"), i18n("Navigate to this column."), QStringLiteral("column")); parser.addOption(gotoColumnOption); // -i/--stdin option const QCommandLineOption readStdInOption(QStringList() << QStringLiteral("i") << QStringLiteral("stdin"), i18n("Read the contents of stdin.")); parser.addOption(readStdInOption); // --tempfile option const QCommandLineOption tempfileOption(QStringList() << QStringLiteral("tempfile"), i18n("The files/URLs opened by the application will be deleted after use")); parser.addOption(tempfileOption); // urls to open parser.addPositionalArgument(QStringLiteral("urls"), i18n("Documents to open."), QStringLiteral("[urls...]")); /** * do the command line parsing */ parser.process(app); /** * handle standard options */ aboutData.processCommandLine(&parser); + /** + * remember the urls we shall open + */ + const QStringList urls = parser.positionalArguments(); -#ifndef USE_QT_SINGLE_APP /** - * use dbus, if available + * compute if we shall start a new instance or reuse + * an old one + * this will later be updated once more after detecting some + * things about already running kate's, like their sessions + */ + bool force_new = parser.isSet(startNewInstanceOption); + if (!force_new) { + if (!( + parser.isSet(startSessionOption) || + parser.isSet(startNewInstanceOption) || + parser.isSet(usePidOption) || + parser.isSet(useEncodingOption) || + parser.isSet(gotoLineOption) || + parser.isSet(gotoColumnOption) || + parser.isSet(readStdInOption) + ) && (urls.isEmpty())) { + force_new = true; + } + } + + /** + * only block, if files to open there.... + */ + const bool needToBlock = parser.isSet(startBlockingOption) && !urls.isEmpty(); + + /** + * use dbus, if available for linux and co. * allows for resuse of running Kate instances */ +#ifndef USE_QT_SINGLE_APP if (QDBusConnectionInterface * const sessionBusInterface = QDBusConnection::sessionBus().interface()) { /** * try to get the current running kate instances */ KateRunningInstanceMap mapSessionRii; if (!fillinRunningKateAppInstances(&mapSessionRii)) { return 1; } QStringList kateServices; for (KateRunningInstanceMap::const_iterator it = mapSessionRii.constBegin(); it != mapSessionRii.constEnd(); ++it) { kateServices << (*it)->serviceName; } QString serviceName; - const QStringList urls = parser.positionalArguments(); - - bool force_new = parser.isSet(startNewInstanceOption); - - if (!force_new) { - if (!( - parser.isSet(startSessionOption) || - parser.isSet(startNewInstanceOption) || - parser.isSet(usePidOption) || - parser.isSet(useEncodingOption) || - parser.isSet(gotoLineOption) || - parser.isSet(gotoColumnOption) || - parser.isSet(readStdInOption) - ) && (urls.isEmpty())) { - force_new = true; - } - } - QString start_session; bool session_already_opened = false; //check if we try to start an already opened session if (parser.isSet(startAnonymousSessionOption)) { force_new = true; } else if (parser.isSet(startSessionOption)) { start_session = parser.value(startSessionOption); if (mapSessionRii.contains(start_session)) { serviceName = mapSessionRii[start_session]->serviceName; force_new = false; session_already_opened = true; } } //cleanup map cleanupRunningKateAppInstanceMap(&mapSessionRii); //if no new instance is forced and no already opened session is requested, //check if a pid is given, which should be reused. // two possibilities: pid given or not... if ((!force_new) && serviceName.isEmpty()) { if ((parser.isSet(usePidOption)) || (!qgetenv("KATE_PID").isEmpty())) { QString usePid = (parser.isSet(usePidOption)) ? parser.value(usePidOption) : QString::fromLocal8Bit(qgetenv("KATE_PID")); serviceName = QStringLiteral("org.kde.kate-") + usePid; if (!kateServices.contains(serviceName)) { serviceName.clear(); } } } // prefer the Kate instance running on the current virtual desktop bool foundRunningService = false; if ((!force_new) && (serviceName.isEmpty())) { const int desktopnumber = KWindowSystem::currentDesktop(); int sessionDesktopNumber; for (int s = 0; s < kateServices.count(); s++) { serviceName = kateServices[s]; if (!serviceName.isEmpty()) { QDBusReply there = sessionBusInterface->isServiceRegistered(serviceName); if (there.isValid() && there.value()) { sessionDesktopNumber = -1; // query instance current desktop QDBusMessage m = QDBusMessage::createMethodCall(serviceName, QStringLiteral("/MainApplication"), QStringLiteral("org.kde.Kate.Application"), QStringLiteral("desktopNumber")); QDBusMessage res = QDBusConnection::sessionBus().call(m); QList answer = res.arguments(); if (answer.size() == 1) { sessionDesktopNumber = answer.at(0).toInt(); if (sessionDesktopNumber == desktopnumber) { // stop searching. a candidate instance in the current desktop has been found foundRunningService = true; break; } } } } serviceName.clear(); } } //check again if service is still running foundRunningService = false; if (!serviceName.isEmpty()) { QDBusReply there = sessionBusInterface->isServiceRegistered(serviceName); foundRunningService = there.isValid() && there.value(); } if (foundRunningService) { // open given session if (parser.isSet(startSessionOption) && (!session_already_opened)) { QDBusMessage m = QDBusMessage::createMethodCall(serviceName, QStringLiteral("/MainApplication"), QStringLiteral("org.kde.Kate.Application"), QStringLiteral("activateSession")); QList dbusargs; dbusargs.append(parser.value(startSessionOption)); m.setArguments(dbusargs); QDBusConnection::sessionBus().call(m); } QString enc = parser.isSet(useEncodingOption) ? parser.value(useEncodingOption) : QString(); bool tempfileSet = parser.isSet(tempfileOption); - // only block, if files to open there.... - bool needToBlock = parser.isSet(startBlockingOption) && !urls.isEmpty(); - QStringList tokens; // open given files... foreach(const QString & url, urls) { QDBusMessage m = QDBusMessage::createMethodCall(serviceName, QStringLiteral("/MainApplication"), QStringLiteral("org.kde.Kate.Application"), QStringLiteral("tokenOpenUrlAt")); UrlInfo info(url); QList dbusargs; // convert to an url dbusargs.append(info.url.toString()); dbusargs.append(info.cursor.line()); dbusargs.append(info.cursor.column()); dbusargs.append(enc); dbusargs.append(tempfileSet); m.setArguments(dbusargs); QDBusMessage res = QDBusConnection::sessionBus().call(m); if (res.type() == QDBusMessage::ReplyMessage) { if (res.arguments().count() == 1) { QVariant v = res.arguments()[0]; if (v.isValid()) { QString s = v.toString(); if ((!s.isEmpty()) && (s != QStringLiteral("ERROR"))) { tokens << s; } } } } } if (parser.isSet(readStdInOption)) { QTextStream input(stdin, QIODevice::ReadOnly); // set chosen codec QTextCodec *codec = parser.isSet(useEncodingOption) ? QTextCodec::codecForName(parser.value(useEncodingOption).toUtf8()) : 0; if (codec) { input.setCodec(codec); } QString line; QString text; do { line = input.readLine(); text.append(line + QLatin1Char('\n')); } while (!line.isNull()); QDBusMessage m = QDBusMessage::createMethodCall(serviceName, QStringLiteral("/MainApplication"), QStringLiteral("org.kde.Kate.Application"), QStringLiteral("openInput")); QList dbusargs; dbusargs.append(text); m.setArguments(dbusargs); QDBusConnection::sessionBus().call(m); } int line = 0; int column = 0; bool nav = false; if (parser.isSet(gotoLineOption)) { line = parser.value(gotoLineOption).toInt() - 1; nav = true; } if (parser.isSet(gotoColumnOption)) { column = parser.value(gotoColumnOption).toInt() - 1; nav = true; } if (nav) { QDBusMessage m = QDBusMessage::createMethodCall(serviceName, QStringLiteral("/MainApplication"), QStringLiteral("org.kde.Kate.Application"), QStringLiteral("setCursor")); QList args; args.append(line); args.append(column); m.setArguments(args); QDBusConnection::sessionBus().call(m); } // activate the used instance QDBusMessage activateMsg = QDBusMessage::createMethodCall(serviceName, QStringLiteral("/MainApplication"), QStringLiteral("org.kde.Kate.Application"), QStringLiteral("activate")); QDBusConnection::sessionBus().call(activateMsg); // connect dbus signal if (needToBlock) { KateWaiter *waiter = new KateWaiter(serviceName, tokens); QDBusConnection::sessionBus().connect(serviceName, QStringLiteral("/MainApplication"), QStringLiteral("org.kde.Kate.Application"), QStringLiteral("exiting"), waiter, SLOT(exiting())); QDBusConnection::sessionBus().connect(serviceName, QStringLiteral("/MainApplication"), QStringLiteral("org.kde.Kate.Application"), QStringLiteral("documentClosed"), waiter, SLOT(documentClosed(QString))); } // KToolInvocation (and KRun) will wait until we register on dbus KDBusService dbusService(KDBusService::Multiple); dbusService.unregister(); // make the world happy, we are started, kind of... KStartupInfo::appStarted(); // this will wait until exiting is emitted by the used instance, if wanted... return needToBlock ? app.exec() : 0; } } - #else // USE_QT_SINGLE_APP - - if (!parser.isSet(startNewInstanceOption)) { - QString urlsSerialized; - const QStringList urls = parser.positionalArguments(); - foreach(const QString & url, urls) { - UrlInfo info(url); - //qDebug() << info.url.toString()<< info.cursor.line() << info.cursor.column(); - urlsSerialized += QStringLiteral("%1||%2||%3;") - .arg(info.url.toString()) - .arg(info.cursor.line()) - .arg(info.cursor.column()); - } - if (app.sendMessage(urlsSerialized)) { - //qDebug() << "kate is already running"; - return 0; + + /** + * for mac & windows: use QtSingleApplication + */ +#else + /** + * only try to reuse existing kate instances if not already forbidden by arguments + */ + if (!force_new) { + /** + * any instance running we can use? + * later we could do here pid checks and stuff + */ + bool instanceFound = app.isRunning(); + + /** + * if instance was found, send over all urls to be opened + */ + if (instanceFound) { + /** + * tell single application to block if needed + */ + app.setBlock(needToBlock); + + /** + * construct one big message with all urls to open + * later we will add additional data to this + */ + QVariantMap message; + QVariantList messageUrls; + foreach(const QString & url, urls) { + /** + * get url info and pack them into the message as extra element in urls list + */ + UrlInfo info(url); + QVariantMap urlMessagePart; + urlMessagePart[QLatin1String("url")] = info.url; + urlMessagePart[QLatin1String("line")] = info.cursor.line(); + urlMessagePart[QLatin1String("column")] = info.cursor.column(); + messageUrls.append(urlMessagePart); + } + message[QLatin1String("urls")] = messageUrls; + + /** + * try to send message, return success + */ + return !app.sendMessage(QString::fromUtf8(QJsonDocument::fromVariant(QVariant(message)).toJson())); } } - - #endif // USE_QT_SINGLE_APP +#endif // USE_QT_SINGLE_APP /** * if we arrive here, we need to start a new kate instance! */ /** * construct the real kate app object ;) * behaves like a singleton, one unique instance * we are passing our local command line parser to it */ KateApp kateApp(parser); /** * init kate * if this returns false, we shall exit * else we may enter the main event loop */ if (!kateApp.init()) { return 0; } - #ifndef USE_QT_SINGLE_APP +#ifndef USE_QT_SINGLE_APP /** * finally register this kate instance for dbus, don't die if no dbus is around! */ const KDBusService dbusService(KDBusService::Multiple | KDBusService::NoExitOnFailure); - - #else - +#else + /** + * else: connect the single application notifications + */ QObject::connect(&app, &SharedTools::QtSingleApplication::messageReceived, &kateApp, &KateApp::remoteMessageReceived); QObject::connect(&app, SIGNAL(messageReceived(QString,QObject*)), &app, SLOT(activateWindow())); - #endif +#endif /** * start main event loop for our application */ return app.exec(); }