diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,6 +64,7 @@ TextWidgets Notifications Crash + WindowSystem ) find_package(KF5 ${KF5_MIN_VERSION} OPTIONAL_COMPONENTS Activities diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -147,6 +147,7 @@ KF5::ConfigCore KF5::NewStuff KF5::Parts + KF5::WindowSystem ) if(HAVE_BALOO) diff --git a/src/dbusinterface.cpp b/src/dbusinterface.cpp --- a/src/dbusinterface.cpp +++ b/src/dbusinterface.cpp @@ -19,10 +19,13 @@ #include "dbusinterface.h" #include "global.h" +#include "dolphin_generalsettings.h" #include +#include #include +#include #include DBusInterface::DBusInterface() : @@ -41,7 +44,8 @@ if (urls.isEmpty()) { return; } - Dolphin::openNewWindow(urls); + if (!Dolphin::attachToExistingInstance(QUrl::toStringList(urls), false, GeneralSettings::splitView())) + Dolphin::openNewWindow(urls); } void DBusInterface::ShowItems(const QStringList& uriList, const QString& startUpId) @@ -51,7 +55,8 @@ if (urls.isEmpty()) { return; } - Dolphin::openNewWindow(urls, nullptr, Dolphin::OpenNewWindowFlag::Select); + if (!Dolphin::attachToExistingInstance(QUrl::toStringList(urls), true, GeneralSettings::splitView())) + Dolphin::openNewWindow(urls, nullptr, Dolphin::OpenNewWindowFlag::Select); } void DBusInterface::ShowItemProperties(const QStringList& uriList, const QString& startUpId) diff --git a/src/dolphinmainwindow.h b/src/dolphinmainwindow.h --- a/src/dolphinmainwindow.h +++ b/src/dolphinmainwindow.h @@ -70,29 +70,34 @@ */ DolphinViewContainer* activeViewContainer() const; + /** + * Returns the 'Create New...' sub menu which also can be shared + * with other menus (e. g. a context menu). + */ + KNewFileMenu* newFileMenu() const; + + void setTabsToHomeIfMountPathOpen(const QString& mountPath); + +public slots: /** * Opens each directory in \p dirs in a separate tab. If \a splitView is set, * 2 directories are collected within one tab. * \pre \a dirs must contain at least one url. */ - void openDirectories(const QList &dirs, bool splitView); + void openDirectories(const QStringList &dirs, bool splitView); /** * Opens the directories which contain the files \p files and selects all files. * If \a splitView is set, 2 directories are collected within one tab. * \pre \a files must contain at least one url. */ - void openFiles(const QList& files, bool splitView); - + void openFiles(const QStringList &files, bool splitView); + /** - * Returns the 'Create New...' sub menu which also can be shared - * with other menus (e. g. a context menu). + * Tries to raise/activate the Dolphin window. */ - KNewFileMenu* newFileMenu() const; - - void setTabsToHomeIfMountPathOpen(const QString& mountPath); - -public slots: + void tryRaise(); + /** * Pastes the clipboard data into the currently selected folder * of the active view. If not exactly one folder is selected, @@ -116,7 +121,7 @@ /** Stores all settings and quits Dolphin. */ void quit(); - + signals: /** * Is sent if the selection of the currently active view has diff --git a/src/dolphinmainwindow.cpp b/src/dolphinmainwindow.cpp --- a/src/dolphinmainwindow.cpp +++ b/src/dolphinmainwindow.cpp @@ -64,6 +64,7 @@ #include #include #include +#include #include #include @@ -183,14 +184,19 @@ { } -void DolphinMainWindow::openDirectories(const QList& dirs, bool splitView) +void DolphinMainWindow::openDirectories(const QStringList& dirs, bool splitView) { - m_tabWidget->openDirectories(dirs, splitView); + m_tabWidget->openDirectories(QUrl::fromStringList(dirs), splitView); } -void DolphinMainWindow::openFiles(const QList& files, bool splitView) +void DolphinMainWindow::openFiles(const QStringList& files, bool splitView) { - m_tabWidget->openFiles(files, splitView); + m_tabWidget->openFiles(QUrl::fromStringList(files), splitView); +} + +void DolphinMainWindow::tryRaise() +{ + KWindowSystem::forceActiveWindow( window()->effectiveWinId() ); } void DolphinMainWindow::showCommand(CommandType command) diff --git a/src/dolphintabwidget.cpp b/src/dolphintabwidget.cpp --- a/src/dolphintabwidget.cpp +++ b/src/dolphintabwidget.cpp @@ -179,9 +179,9 @@ const QUrl& primaryUrl = *(it++); if (splitView && (it != dirs.constEnd())) { const QUrl& secondaryUrl = *(it++); - openNewTab(primaryUrl, secondaryUrl); + openNewActivatedTab(primaryUrl, secondaryUrl); } else { - openNewTab(primaryUrl); + openNewActivatedTab(primaryUrl); } } } @@ -281,6 +281,7 @@ args << tabPage->secondaryViewContainer()->url().url(); args << QStringLiteral("--split"); } + args << QStringLiteral("--new-window"); const QString command = QStringLiteral("dolphin %1").arg(KShell::joinArgs(args)); KRun::runCommand(command, this); diff --git a/src/global.h b/src/global.h --- a/src/global.h +++ b/src/global.h @@ -41,6 +41,12 @@ * Opens a new Dolphin window */ void openNewWindow(const QList &urls = {}, QWidget *window = nullptr, const OpenNewWindowFlags &flags = OpenNewWindowFlag::None); + + /** + * Attaches URLs to an existing Dolphin instance if possible. + * Returns true if URLs were successfully attached + */ + bool attachToExistingInstance(const QStringList urls, bool openFiles, bool splitView); /** * TODO: Move this somewhere global to all KDE apps, not just Dolphin diff --git a/src/global.cpp b/src/global.cpp --- a/src/global.cpp +++ b/src/global.cpp @@ -26,6 +26,8 @@ #include #include +#include +#include QList Dolphin::validateUris(const QStringList& uriList) { @@ -50,14 +52,42 @@ void Dolphin::openNewWindow(const QList &urls, QWidget *window, const OpenNewWindowFlags &flags) { QString command = QStringLiteral("dolphin"); + command.append(QLatin1String(" --new-window")); if (flags.testFlag(OpenNewWindowFlag::Select)) { command.append(QLatin1String(" --select")); } if (!urls.isEmpty()) { command.append(QLatin1String(" %U")); } - + KRun::run(command, urls, window, qApp->applicationDisplayName(), qApp->windowIcon().name()); } + +bool Dolphin::attachToExistingInstance(const QStringList urls, bool openFiles, bool splitView) +{ + const QStringList services = QDBusConnection::sessionBus().interface()->registeredServiceNames().value(); + + // Don't match the service without trailing "-" (unique instance) + const QString pattern = QStringLiteral("org.kde.dolphin-"); + const QString myPid = QString::number(qApp->applicationPid()); + QScopedPointer bestService; + + // Select the first instance that isn't us + for (const QString& service: services) + { + if (service.startsWith(pattern) && !service.endsWith(myPid)) + { + // Check if instance can handle our URLs + bestService.reset(new QDBusInterface(service, QStringLiteral("/dolphin/Dolphin_1"), QStringLiteral("org.kde.dolphin.MainWindow"))); + if(!bestService->isValid()) + break; + + bestService->call(openFiles ? QStringLiteral("openFiles") : QStringLiteral("openDirectories"), urls, splitView); + bestService->call(QStringLiteral("tryRaise")); + return true; + } + } + return false; +} diff --git a/src/main.cpp b/src/main.cpp --- a/src/main.cpp +++ b/src/main.cpp @@ -39,6 +39,10 @@ #include #endif #include +#include +#include +#include +#include extern "C" Q_DECL_EXPORT int kdemain(int argc, char **argv) { @@ -119,33 +123,41 @@ parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("select"), i18nc("@info:shell", "The files and folders passed as arguments " "will be selected."))); parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("split"), i18nc("@info:shell", "Dolphin will get started with a split view."))); + parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("new-window"), i18nc("@info:shell", "Dolphin will explicitly open in a new window."))); parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("daemon"), i18nc("@info:shell", "Start Dolphin Daemon (only required for DBus Interface)"))); parser.addPositionalArgument(QStringLiteral("+[Url]"), i18nc("@info:shell", "Document to open")); parser.process(app); aboutData.processCommandLine(&parser); + const bool splitView = parser.isSet(QStringLiteral("split")) || GeneralSettings::splitView(); + const bool openFiles = parser.isSet(QStringLiteral("select")); + const QStringList args = parser.positionalArguments(); + QStringList urls = QUrl::toStringList(Dolphin::validateUris(args)); + if (parser.isSet(QStringLiteral("daemon"))) { return app.exec(); } - - const QStringList args = parser.positionalArguments(); - QList urls = Dolphin::validateUris(args); - + if (urls.isEmpty()) { // We need at least one URL to open Dolphin - urls.append(Dolphin::homeUrl()); + urls.push_back(Dolphin::homeUrl().toString()); } - const bool splitView = parser.isSet(QStringLiteral("split")) || GeneralSettings::splitView(); if (splitView && urls.size() < 2) { // Split view does only make sense if we have at least 2 URLs - urls.append(urls.last()); + urls.push_back(urls.last()); + } + + if (GeneralSettings::openExternallyCalledFolderInNewTab() && !parser.isSet(QStringLiteral("new-window"))) { + if (Dolphin::attachToExistingInstance(urls, openFiles, splitView)) + // Successfully attached to existing instance of Dolphin + return 0; } DolphinMainWindow* mainWindow = new DolphinMainWindow(); - if (parser.isSet(QStringLiteral("select"))) { + if (openFiles) { mainWindow->openFiles(urls, splitView); } else { mainWindow->openDirectories(urls, splitView); diff --git a/src/settings/dolphin_generalsettings.kcfg b/src/settings/dolphin_generalsettings.kcfg --- a/src/settings/dolphin_generalsettings.kcfg +++ b/src/settings/dolphin_generalsettings.kcfg @@ -26,6 +26,10 @@ false + + + true + 0 diff --git a/src/settings/startup/startupsettingspage.h b/src/settings/startup/startupsettingspage.h --- a/src/settings/startup/startupsettingspage.h +++ b/src/settings/startup/startupsettingspage.h @@ -64,6 +64,7 @@ QCheckBox* m_showFullPath; QCheckBox* m_filterBar; QCheckBox* m_showFullPathInTitlebar; + QCheckBox* m_openExternallyCalledFolderInNewTab; }; #endif diff --git a/src/settings/startup/startupsettingspage.cpp b/src/settings/startup/startupsettingspage.cpp --- a/src/settings/startup/startupsettingspage.cpp +++ b/src/settings/startup/startupsettingspage.cpp @@ -43,7 +43,8 @@ m_editableUrl(nullptr), m_showFullPath(nullptr), m_filterBar(nullptr), - m_showFullPathInTitlebar(nullptr) + m_showFullPathInTitlebar(nullptr), + m_openExternallyCalledFolderInNewTab(nullptr) { QFormLayout* topLayout = new QFormLayout(this); @@ -100,6 +101,8 @@ topLayout->addRow(QString(), m_filterBar); m_showFullPathInTitlebar = new QCheckBox(i18nc("@option:check Startup Settings", "Show full path in title bar")); topLayout->addRow(QString(), m_showFullPathInTitlebar); + m_openExternallyCalledFolderInNewTab = new QCheckBox(i18nc("@option:check Startup Settings", "Open externally called folder in new tab")); + topLayout->addRow(QString(), m_openExternallyCalledFolderInNewTab); loadSettings(); @@ -110,6 +113,7 @@ connect(m_showFullPath, &QCheckBox::toggled, this, &StartupSettingsPage::slotSettingsChanged); connect(m_filterBar, &QCheckBox::toggled, this, &StartupSettingsPage::slotSettingsChanged); connect(m_showFullPathInTitlebar, &QCheckBox::toggled, this, &StartupSettingsPage::slotSettingsChanged); + connect(m_openExternallyCalledFolderInNewTab, &QCheckBox::toggled, this, &StartupSettingsPage::slotSettingsChanged); } StartupSettingsPage::~StartupSettingsPage() @@ -133,6 +137,7 @@ settings->setShowFullPath(m_showFullPath->isChecked()); settings->setFilterBar(m_filterBar->isChecked()); settings->setShowFullPathInTitlebar(m_showFullPathInTitlebar->isChecked()); + settings->setOpenExternallyCalledFolderInNewTab(m_openExternallyCalledFolderInNewTab->isChecked()); settings->save(); } @@ -183,4 +188,5 @@ m_showFullPath->setChecked(GeneralSettings::showFullPath()); m_filterBar->setChecked(GeneralSettings::filterBar()); m_showFullPathInTitlebar->setChecked(GeneralSettings::showFullPathInTitlebar()); + m_openExternallyCalledFolderInNewTab->setChecked(GeneralSettings::openExternallyCalledFolderInNewTab()); } diff --git a/src/tests/dolphinmainwindowtest.cpp b/src/tests/dolphinmainwindowtest.cpp --- a/src/tests/dolphinmainwindowtest.cpp +++ b/src/tests/dolphinmainwindowtest.cpp @@ -62,7 +62,7 @@ // See https://bugs.kde.org/show_bug.cgi?id=379135 void DolphinMainWindowTest::testClosingTabsWithSearchBoxVisible() { - m_mainWindow->openDirectories({ QUrl::fromLocalFile(QDir::homePath()) }, false); + m_mainWindow->openDirectories({ QUrl::fromLocalFile(QDir::homePath()).toString() }, false); m_mainWindow->show(); // Without this call the searchbox doesn't get FocusIn events. QVERIFY(QTest::qWaitForWindowExposed(m_mainWindow.data())); @@ -92,7 +92,7 @@ void DolphinMainWindowTest::testActiveViewAfterClosingSplitView() { - m_mainWindow->openDirectories({ QUrl::fromLocalFile(QDir::homePath()) }, false); + m_mainWindow->openDirectories({ QUrl::fromLocalFile(QDir::homePath()).toString() }, false); m_mainWindow->show(); QVERIFY(QTest::qWaitForWindowExposed(m_mainWindow.data())); QVERIFY(m_mainWindow->isVisible()); @@ -137,7 +137,7 @@ // Test case for bug #385111 void DolphinMainWindowTest::testUpdateWindowTitleAfterClosingSplitView() { - m_mainWindow->openDirectories({ QUrl::fromLocalFile(QDir::homePath()) }, false); + m_mainWindow->openDirectories({ QUrl::fromLocalFile(QDir::homePath()).toString() }, false); m_mainWindow->show(); QVERIFY(QTest::qWaitForWindowExposed(m_mainWindow.data())); QVERIFY(m_mainWindow->isVisible()); @@ -178,7 +178,7 @@ // Test case for bug #397910 void DolphinMainWindowTest::testOpenInNewTabTitle() { - m_mainWindow->openDirectories({ QUrl::fromLocalFile(QDir::homePath()) }, false); + m_mainWindow->openDirectories({ QUrl::fromLocalFile(QDir::homePath()).toString() }, false); m_mainWindow->show(); QVERIFY(QTest::qWaitForWindowExposed(m_mainWindow.data())); QVERIFY(m_mainWindow->isVisible()); @@ -207,7 +207,7 @@ void DolphinMainWindowTest::testNewFileMenuEnabled() { QFETCH(QUrl, activeViewUrl); - m_mainWindow->openDirectories({ activeViewUrl }, false); + m_mainWindow->openDirectories({ activeViewUrl.toString() }, false); m_mainWindow->show(); QVERIFY(QTest::qWaitForWindowExposed(m_mainWindow.data())); QVERIFY(m_mainWindow->isVisible());