]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/dolphintabwidget.cpp
Merge remote-tracking branch 'origin/release/20.04'
[dolphin.git] / src / dolphintabwidget.cpp
index 4dd7df7426043d40a4489434b70d4fce633478d1..fba6fe084992dc50a7b20f8ce21d984b2afc4bc3 100644 (file)
 
 #include "dolphintabwidget.h"
 
+#include "dolphin_generalsettings.h"
 #include "dolphintabbar.h"
 #include "dolphintabpage.h"
 #include "dolphinviewcontainer.h"
-#include "dolphin_generalsettings.h"
-#include "views/draganddrophelper.h"
 
-#include <QApplication>
 #include <KConfigGroup>
+#include <KShell>
 #include <kio/global.h>
-#include <KRun>
+#include <KIO/CommandLauncherJob>
+#include <KAcceleratorManager>
+
+#include <QApplication>
+#include <QDropEvent>
 
 DolphinTabWidget::DolphinTabWidget(QWidget* parent) :
     QTabWidget(parent),
-    m_placesSelectorVisible(true)
+    m_placesSelectorVisible(true),
+    m_lastViewedTab(0)
 {
-    connect(this, SIGNAL(tabCloseRequested(int)),
-            this, SLOT(closeTab(int)));
-    connect(this, SIGNAL(currentChanged(int)),
-            this, SLOT(currentTabChanged(int)));
+    KAcceleratorManager::setNoAccel(this);
+
+    connect(this, &DolphinTabWidget::tabCloseRequested,
+            this, QOverload<int>::of(&DolphinTabWidget::closeTab));
+    connect(this, &DolphinTabWidget::currentChanged,
+            this, &DolphinTabWidget::currentTabChanged);
 
     DolphinTabBar* tabBar = new DolphinTabBar(this);
-    connect(tabBar, SIGNAL(openNewActivatedTab(int)),
-            this, SLOT(openNewActivatedTab(int)));
-    connect(tabBar, SIGNAL(tabDropEvent(int,QDropEvent*)),
-            this, SLOT(tabDropEvent(int,QDropEvent*)));
-    connect(tabBar, SIGNAL(tabDetachRequested(int)),
-            this, SLOT(detachTab(int)));
+    connect(tabBar, &DolphinTabBar::openNewActivatedTab,
+            this, QOverload<int>::of(&DolphinTabWidget::openNewActivatedTab));
+    connect(tabBar, &DolphinTabBar::tabDropEvent,
+            this, &DolphinTabWidget::tabDropEvent);
+    connect(tabBar, &DolphinTabBar::tabDetachRequested,
+            this, &DolphinTabWidget::detachTab);
     tabBar->hide();
 
     setTabBar(tabBar);
@@ -59,6 +65,18 @@ DolphinTabPage* DolphinTabWidget::currentTabPage() const
     return tabPageAt(currentIndex());
 }
 
+DolphinTabPage* DolphinTabWidget::nextTabPage() const
+{
+    const int index = currentIndex() + 1;
+    return tabPageAt(index < count() ? index : 0);
+}
+
+DolphinTabPage* DolphinTabWidget::prevTabPage() const
+{
+    const int index = currentIndex() - 1;
+    return tabPageAt(index >= 0 ? index : (count() - 1));
+}
+
 DolphinTabPage* DolphinTabWidget::tabPageAt(const int index) const
 {
     return static_cast<DolphinTabPage*>(widget(index));
@@ -100,12 +118,26 @@ void DolphinTabWidget::readProperties(const KConfigGroup& group)
 
 void DolphinTabWidget::refreshViews()
 {
+    // Left-elision is better when showing full paths, since you care most
+    // about the current directory which is on the right
+    if (GeneralSettings::showFullPathInTitlebar()) {
+        setElideMode(Qt::ElideLeft);
+    } else {
+        setElideMode(Qt::ElideRight);
+    }
+
     const int tabCount = count();
     for (int i = 0; i < tabCount; ++i) {
+        tabBar()->setTabText(i, tabName(tabPageAt(i)));
         tabPageAt(i)->refreshViews();
     }
 }
 
+bool DolphinTabWidget::isUrlOpen(const QUrl &url) const
+{
+    return indexByUrl(url).first >= 0;
+}
+
 void DolphinTabWidget::openNewActivatedTab()
 {
     const DolphinViewContainer* oldActiveViewContainer = currentTabPage()->activeViewContainer();
@@ -120,33 +152,34 @@ void DolphinTabWidget::openNewActivatedTab()
 
     // The URL navigator of the new tab should have the same editable state
     // as the current tab
-    KUrlNavigator* navigator = newActiveViewContainer->urlNavigator();
-    navigator->setUrlEditable(isUrlEditable);
+    newActiveViewContainer->urlNavigator()->setUrlEditable(isUrlEditable);
 
-    if (isUrlEditable) {
-        // If a new tab is opened and the URL is editable, assure that
-        // the user can edit the URL without manually setting the focus
-        navigator->setFocus();
-    }
+    // Always focus the new tab's view
+    newActiveViewContainer->view()->setFocus();
 }
 
-void DolphinTabWidget::openNewActivatedTab(const KUrl& primaryUrl, const KUrl& secondaryUrl)
+void DolphinTabWidget::openNewActivatedTab(const QUrl& primaryUrl, const QUrl& secondaryUrl)
 {
     openNewTab(primaryUrl, secondaryUrl);
     setCurrentIndex(count() - 1);
 }
 
-void DolphinTabWidget::openNewTab(const KUrl& primaryUrl, const KUrl& secondaryUrl)
+void DolphinTabWidget::openNewTab(const QUrl& primaryUrl, const QUrl& secondaryUrl, TabPlacement tabPlacement)
 {
     QWidget* focusWidget = QApplication::focusWidget();
 
     DolphinTabPage* tabPage = new DolphinTabPage(primaryUrl, secondaryUrl, this);
+    tabPage->setActive(false);
     tabPage->setPlacesSelectorVisible(m_placesSelectorVisible);
-    connect(tabPage, SIGNAL(activeViewChanged(DolphinViewContainer*)),
-            this, SIGNAL(activeViewChanged(DolphinViewContainer*)));
-    connect(tabPage, SIGNAL(activeViewUrlChanged(KUrl)),
-            this, SLOT(tabUrlChanged(KUrl)));
-    addTab(tabPage, QIcon::fromTheme(KIO::iconNameForUrl(primaryUrl)), tabName(primaryUrl));
+    connect(tabPage, &DolphinTabPage::activeViewChanged,
+            this, &DolphinTabWidget::activeViewChanged);
+    connect(tabPage, &DolphinTabPage::activeViewUrlChanged,
+            this, &DolphinTabWidget::tabUrlChanged);
+    int newTabIndex = -1;
+    if (tabPlacement == AfterCurrentTab) {
+        newTabIndex = currentIndex() + 1;
+    }
+    insertTab(newTabIndex, tabPage, QIcon() /* loaded in tabInserted */, tabName(tabPage));
 
     if (focusWidget) {
         // The DolphinViewContainer grabbed the keyboard focus. As the tab is opened
@@ -155,52 +188,69 @@ void DolphinTabWidget::openNewTab(const KUrl& primaryUrl, const KUrl& secondaryU
     }
 }
 
-void DolphinTabWidget::openDirectories(const QList<KUrl>& dirs)
+void DolphinTabWidget::openDirectories(const QList<QUrl>& dirs, bool splitView)
 {
-    const bool hasSplitView = GeneralSettings::splitView();
+    Q_ASSERT(dirs.size() > 0);
 
-    // Open each directory inside a new tab. If the "split view" option has been enabled,
-    // always show two directories within one tab.
-    QList<KUrl>::const_iterator it = dirs.constBegin();
+    QList<QUrl>::const_iterator it = dirs.constBegin();
     while (it != dirs.constEnd()) {
-        const KUrl& primaryUrl = *(it++);
-        if (hasSplitView && (it != dirs.constEnd())) {
-            const KUrl& secondaryUrl = *(it++);
-            openNewTab(primaryUrl, secondaryUrl);
+        const QUrl& primaryUrl = *(it++);
+        const QPair<int, bool> indexInfo = indexByUrl(primaryUrl);
+        const int index = indexInfo.first;
+        const bool isInPrimaryView = indexInfo.second;
+        if (index >= 0) {
+            setCurrentIndex(index);
+            const auto tabPage = tabPageAt(index);
+            if (isInPrimaryView) {
+                tabPage->primaryViewContainer()->setActive(true);
+            } else {
+                tabPage->secondaryViewContainer()->setActive(true);
+            }
+            // BUG: 417230
+            // Required for updateViewState() call in openFiles() to work as expected
+            // If there is a selection, updateViewState() calls are effectively a no-op
+            tabPage->activeViewContainer()->view()->clearSelection();
+            continue;
+        }
+        if (splitView && (it != dirs.constEnd())) {
+            const QUrl& secondaryUrl = *(it++);
+            openNewActivatedTab(primaryUrl, secondaryUrl);
         } else {
-            openNewTab(primaryUrl);
+            openNewActivatedTab(primaryUrl);
         }
     }
 }
 
-void DolphinTabWidget::openFiles(const QList<KUrl>& files)
+void DolphinTabWidget::openFiles(const QList<QUrl>& files, bool splitView)
 {
-    if (files.isEmpty()) {
-        return;
-    }
+    Q_ASSERT(files.size() > 0);
 
     // Get all distinct directories from 'files' and open a tab
     // for each directory. If the "split view" option is enabled, two
     // directories are shown inside one tab (see openDirectories()).
-    QList<KUrl> dirs;
-    foreach (const KUrl& url, files) {
-        const KUrl dir(url.directory());
+    QList<QUrl> dirs;
+    foreach (const QUrl& url, files) {
+        const QUrl dir(url.adjusted(QUrl::RemoveFilename));
         if (!dirs.contains(dir)) {
             dirs.append(dir);
         }
     }
 
     const int oldTabCount = count();
-    openDirectories(dirs);
+    openDirectories(dirs, splitView);
     const int tabCount = count();
 
     // Select the files. Although the files can be split between several
     // tabs, there is no need to split 'files' accordingly, as
     // the DolphinView will just ignore invalid selections.
-    for (int i = oldTabCount; i < tabCount; ++i) {
+    for (int i = 0; i < tabCount; ++i) {
         DolphinTabPage* tabPage = tabPageAt(i);
         tabPage->markUrlsAsSelected(files);
         tabPage->markUrlAsCurrent(files.first());
+        if (i < oldTabCount) {
+            // Force selection of file if directory was already open, BUG: 417230
+            tabPage->activeViewContainer()->view()->updateViewState();
+        }
     }
 }
 
@@ -215,7 +265,8 @@ void DolphinTabWidget::closeTab(const int index)
     Q_ASSERT(index < count());
 
     if (count() < 2) {
-        // Never close the last tab.
+        // Close Dolphin when closing the last tab.
+        parentWidget()->close();
         return;
     }
 
@@ -226,6 +277,18 @@ void DolphinTabWidget::closeTab(const int index)
     tabPage->deleteLater();
 }
 
+void DolphinTabWidget::activateTab(const int index)
+{
+    if (index < count()) {
+        setCurrentIndex(index);
+    }
+}
+
+void DolphinTabWidget::activateLastTab()
+{
+    setCurrentIndex(count() - 1);
+}
+
 void DolphinTabWidget::activateNextTab()
 {
     const int index = currentIndex() + 1;
@@ -261,17 +324,19 @@ void DolphinTabWidget::detachTab(int index)
 {
     Q_ASSERT(index >= 0);
 
-    const QString separator(QLatin1Char(' '));
-    QString command = QLatin1String("dolphin");
+    QStringList args;
 
     const DolphinTabPage* tabPage = tabPageAt(index);
-    command += separator + tabPage->primaryViewContainer()->url().url();
+    args << tabPage->primaryViewContainer()->url().url();
     if (tabPage->splitViewEnabled()) {
-        command += separator + tabPage->secondaryViewContainer()->url().url();
-        command += separator + QLatin1String("-split");
+        args << tabPage->secondaryViewContainer()->url().url();
+        args << QStringLiteral("--split");
     }
+    args << QStringLiteral("--new-window");
 
-    KRun::runCommand(command, this);
+    KIO::CommandLauncherJob *job = new KIO::CommandLauncherJob("dolphin", args, this);
+    job->setDesktopName(QStringLiteral("org.kde.dolphin"));
+    job->start();
 
     closeTab(index);
 }
@@ -286,22 +351,22 @@ void DolphinTabWidget::openNewActivatedTab(int index)
 void DolphinTabWidget::tabDropEvent(int index, QDropEvent* event)
 {
     if (index >= 0) {
-        const DolphinView* view = tabPageAt(index)->activeViewContainer()->view();
-
-        QString error;
-        DragAndDropHelper::dropUrls(view->rootItem(), view->url(), event, error);
-        if (!error.isEmpty()) {
-            currentTabPage()->activeViewContainer()->showMessage(error, DolphinViewContainer::Error);
-        }
+        DolphinView* view = tabPageAt(index)->activeViewContainer()->view();
+        view->dropUrls(view->url(), event, view);
     }
 }
 
-void DolphinTabWidget::tabUrlChanged(const KUrl& url)
+void DolphinTabWidget::tabUrlChanged(const QUrl& url)
 {
     const int index = indexOf(qobject_cast<QWidget*>(sender()));
     if (index >= 0) {
-        tabBar()->setTabText(index, tabName(url));
-        tabBar()->setTabIcon(index, QIcon::fromTheme(KIO::iconNameForUrl(url)));
+        tabBar()->setTabText(index, tabName(tabPageAt(index)));
+        if (tabBar()->isVisible()) {
+            tabBar()->setTabIcon(index, QIcon::fromTheme(KIO::iconNameForUrl(url)));
+        } else {
+            // Mark as dirty, actually load once the tab bar actually gets shown
+            tabBar()->setTabIcon(index, QIcon());
+        }
 
         // Emit the currentUrlChanged signal if the url of the current tab has been changed.
         if (index == currentIndex()) {
@@ -312,9 +377,16 @@ void DolphinTabWidget::tabUrlChanged(const KUrl& url)
 
 void DolphinTabWidget::currentTabChanged(int index)
 {
-    DolphinViewContainer* viewContainer = tabPageAt(index)->activeViewContainer();
+    // last-viewed tab deactivation
+    if (DolphinTabPage* tabPage = tabPageAt(m_lastViewedTab)) {
+        tabPage->setActive(false);
+    }
+    DolphinTabPage* tabPage = tabPageAt(index);
+    DolphinViewContainer* viewContainer = tabPage->activeViewContainer();
     emit activeViewChanged(viewContainer);
     emit currentUrlChanged(viewContainer->url());
+    tabPage->setActive(true);
+    m_lastViewedTab = index;
 }
 
 void DolphinTabWidget::tabInserted(int index)
@@ -322,6 +394,13 @@ void DolphinTabWidget::tabInserted(int index)
     QTabWidget::tabInserted(index);
 
     if (count() > 1) {
+        // Resolve all pending tab icons
+        for (int i = 0; i < count(); ++i) {
+            if (tabBar()->tabIcon(i).isNull()) {
+                tabBar()->setTabIcon(i, QIcon::fromTheme(KIO::iconNameForUrl(tabPageAt(i)->activeViewContainer()->url())));
+            }
+        }
+
         tabBar()->show();
     }
 
@@ -341,20 +420,28 @@ void DolphinTabWidget::tabRemoved(int index)
     emit tabCountChanged(count());
 }
 
-QString DolphinTabWidget::tabName(const KUrl& url) const
+QString DolphinTabWidget::tabName(DolphinTabPage* tabPage) const
 {
-    QString name;
-    if (url.equals(KUrl("file:///"))) {
-        name = '/';
-    } else {
-        name = url.fileName();
-        if (name.isEmpty()) {
-            name = url.protocol();
-        } else {
-            // Make sure that a '&' inside the directory name is displayed correctly
-            // and not misinterpreted as a keyboard shortcut in QTabBar::setTabText()
-            name.replace('&', "&&");
+    if (!tabPage) {
+        return QString();
+    }
+    QString name = tabPage->activeViewContainer()->caption();
+    // Make sure that a '&' inside the directory name is displayed correctly
+    // and not misinterpreted as a keyboard shortcut in QTabBar::setTabText()
+    return name.replace('&', QLatin1String("&&"));
+}
+
+QPair<int, bool> DolphinTabWidget::indexByUrl(const QUrl& url) const
+{
+    for (int i = 0; i < count(); i++) {
+        const auto tabPage = tabPageAt(i);
+        if (url == tabPage->primaryViewContainer()->url()) {
+            return qMakePair(i, true);
+        }
+
+        if (tabPage->splitViewEnabled() && url == tabPage->secondaryViewContainer()->url()) {
+            return qMakePair(i, false);
         }
     }
-    return name;
+    return qMakePair(-1, false);
 }