#include "dolphin_generalsettings.h"
#include "dolphintabbar.h"
-#include "dolphintabpage.h"
#include "dolphinviewcontainer.h"
+#include "views/draganddrophelper.h"
+#include <KAcceleratorManager>
#include <KConfigGroup>
+#include <KIO/CommandLauncherJob>
+#include <KLocalizedString>
#include <KShell>
+#include <KStringHandler>
#include <kio/global.h>
-#include <KIO/CommandLauncherJob>
-#include <KAcceleratorManager>
#include <QApplication>
#include <QDropEvent>
+#include <QStackedWidget>
-DolphinTabWidget::DolphinTabWidget(QWidget* parent) :
- QTabWidget(parent),
- m_placesSelectorVisible(true),
- m_lastViewedTab(0)
+DolphinTabWidget::DolphinTabWidget(DolphinNavigatorsWidgetAction *navigatorsWidget, QWidget *parent)
+ : QTabWidget(parent)
+ , m_lastViewedTab(nullptr)
+ , m_navigatorsWidget{navigatorsWidget}
{
KAcceleratorManager::setNoAccel(this);
- connect(this, &DolphinTabWidget::tabCloseRequested,
- this, QOverload<int>::of(&DolphinTabWidget::closeTab));
- connect(this, &DolphinTabWidget::currentChanged,
- this, &DolphinTabWidget::currentTabChanged);
+ connect(this, &DolphinTabWidget::tabCloseRequested, this, QOverload<int>::of(&DolphinTabWidget::closeTab));
+ connect(this, &DolphinTabWidget::currentChanged, this, &DolphinTabWidget::currentTabChanged);
- DolphinTabBar* tabBar = new DolphinTabBar(this);
- 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();
+ DolphinTabBar *tabBar = new DolphinTabBar(this);
+ connect(tabBar, &DolphinTabBar::openNewActivatedTab, this, QOverload<int>::of(&DolphinTabWidget::openNewActivatedTab));
+ connect(tabBar, &DolphinTabBar::tabDragMoveEvent, this, &DolphinTabWidget::tabDragMoveEvent);
+ connect(tabBar, &DolphinTabBar::tabDropEvent, this, &DolphinTabWidget::tabDropEvent);
+ connect(tabBar, &DolphinTabBar::tabDetachRequested, this, &DolphinTabWidget::detachTab);
+ connect(tabBar, &DolphinTabBar::tabRenamed, this, &DolphinTabWidget::renameTab);
setTabBar(tabBar);
setDocumentMode(true);
setElideMode(Qt::ElideRight);
setUsesScrollButtons(true);
+ setTabBarAutoHide(true);
+
+ auto stackWidget{findChild<QStackedWidget *>()};
+ // i18n: This accessible name will be announced any time the user moves keyboard focus e.g. from the toolbar or the places panel towards the main working
+ // area of Dolphin. It gives structure. This container does not only contain the main view but also the status bar, the search panel, filter, and selection
+ // mode bars, so calling it just a "View" is a bit wrong, but hopefully still gets the point across.
+ stackWidget->setAccessibleName(i18nc("accessible name of Dolphin's view container", "Location View")); // Without this call, the non-descript Qt provided
+ // "Layered Pane" role is announced.
}
-DolphinTabPage* DolphinTabWidget::currentTabPage() const
+DolphinTabPage *DolphinTabWidget::currentTabPage() const
{
return tabPageAt(currentIndex());
}
-DolphinTabPage* DolphinTabWidget::nextTabPage() const
+DolphinTabPage *DolphinTabWidget::nextTabPage() const
{
const int index = currentIndex() + 1;
return tabPageAt(index < count() ? index : 0);
}
-DolphinTabPage* DolphinTabWidget::prevTabPage() const
+DolphinTabPage *DolphinTabWidget::prevTabPage() const
{
const int index = currentIndex() - 1;
return tabPageAt(index >= 0 ? index : (count() - 1));
}
-DolphinTabPage* DolphinTabWidget::tabPageAt(const int index) const
+DolphinTabPage *DolphinTabWidget::tabPageAt(const int index) const
{
- return static_cast<DolphinTabPage*>(widget(index));
+ return static_cast<DolphinTabPage *>(widget(index));
}
-void DolphinTabWidget::saveProperties(KConfigGroup& group) const
+void DolphinTabWidget::saveProperties(KConfigGroup &group) const
{
const int tabCount = count();
group.writeEntry("Tab Count", tabCount);
group.writeEntry("Active Tab Index", currentIndex());
for (int i = 0; i < tabCount; ++i) {
- const DolphinTabPage* tabPage = tabPageAt(i);
+ const DolphinTabPage *tabPage = tabPageAt(i);
group.writeEntry("Tab Data " % QString::number(i), tabPage->saveState());
}
}
-void DolphinTabWidget::readProperties(const KConfigGroup& group)
+void DolphinTabWidget::readProperties(const KConfigGroup &group)
{
const int tabCount = group.readEntry("Tab Count", 0);
for (int i = 0; i < tabCount; ++i) {
if (i >= count()) {
openNewActivatedTab();
}
- if (group.hasKey("Tab Data " % QString::number(i))) {
- // Tab state created with Dolphin > 4.14.x
- const QByteArray state = group.readEntry("Tab Data " % QString::number(i), QByteArray());
- tabPageAt(i)->restoreState(state);
- } else {
- // Tab state created with Dolphin <= 4.14.x
- const QByteArray state = group.readEntry("Tab " % QString::number(i), QByteArray());
- tabPageAt(i)->restoreStateV1(state);
- }
+ const QByteArray state = group.readEntry("Tab Data " % QString::number(i), QByteArray());
+ tabPageAt(i)->restoreState(state);
}
const int index = group.readEntry("Active Tab Index", 0);
const int tabCount = count();
for (int i = 0; i < tabCount; ++i) {
- tabBar()->setTabText(i, tabName(tabPageAt(i)));
+ updateTabName(i);
tabPageAt(i)->refreshViews();
}
}
+void DolphinTabWidget::updateTabName(int index)
+{
+ Q_ASSERT(index >= 0);
+
+ if (!tabPageAt(index)->customLabel().isEmpty()) {
+ QString name = tabPageAt(index)->customLabel();
+ tabBar()->setTabText(index, name);
+ return;
+ }
+
+ tabBar()->setTabText(index, tabName(tabPageAt(index)));
+}
+
bool DolphinTabWidget::isUrlOpen(const QUrl &url) const
{
- return indexByUrl(url).first >= 0;
+ return viewOpenAtDirectory(url).has_value();
+}
+
+bool DolphinTabWidget::isItemVisibleInAnyView(const QUrl &urlOfItem) const
+{
+ return viewShowingItem(urlOfItem).has_value();
}
void DolphinTabWidget::openNewActivatedTab()
{
- const DolphinViewContainer* oldActiveViewContainer = currentTabPage()->activeViewContainer();
- Q_ASSERT(oldActiveViewContainer);
+ std::unique_ptr<DolphinUrlNavigator::VisualState> oldNavigatorState;
+ if (currentTabPage()->primaryViewActive() || !m_navigatorsWidget->secondaryUrlNavigator()) {
+ oldNavigatorState = m_navigatorsWidget->primaryUrlNavigator()->visualState();
+ } else {
+ oldNavigatorState = m_navigatorsWidget->secondaryUrlNavigator()->visualState();
+ }
- const bool isUrlEditable = oldActiveViewContainer->urlNavigator()->isUrlEditable();
+ const DolphinViewContainer *oldActiveViewContainer = currentTabPage()->activeViewContainer();
+ Q_ASSERT(oldActiveViewContainer);
openNewActivatedTab(oldActiveViewContainer->url());
- DolphinViewContainer* newActiveViewContainer = currentTabPage()->activeViewContainer();
+ DolphinViewContainer *newActiveViewContainer = currentTabPage()->activeViewContainer();
Q_ASSERT(newActiveViewContainer);
// The URL navigator of the new tab should have the same editable state
// as the current tab
- newActiveViewContainer->urlNavigator()->setUrlEditable(isUrlEditable);
+ newActiveViewContainer->urlNavigator()->setVisualState(*oldNavigatorState.get());
// Always focus the new tab's view
newActiveViewContainer->view()->setFocus();
}
-void DolphinTabWidget::openNewActivatedTab(const QUrl& primaryUrl, const QUrl& secondaryUrl)
+void DolphinTabWidget::openNewActivatedTab(const QUrl &primaryUrl, const QUrl &secondaryUrl)
{
openNewTab(primaryUrl, secondaryUrl);
- setCurrentIndex(count() - 1);
+ if (GeneralSettings::openNewTabAfterLastTab()) {
+ setCurrentIndex(count() - 1);
+ } else {
+ setCurrentIndex(currentIndex() + 1);
+ }
}
-void DolphinTabWidget::openNewTab(const QUrl& primaryUrl, const QUrl& secondaryUrl, TabPlacement tabPlacement)
+DolphinTabPage *DolphinTabWidget::openNewTab(const QUrl &primaryUrl, const QUrl &secondaryUrl, DolphinTabWidget::NewTabPosition position)
{
- QWidget* focusWidget = QApplication::focusWidget();
+ QWidget *focusWidget = QApplication::focusWidget();
- DolphinTabPage* tabPage = new DolphinTabPage(primaryUrl, secondaryUrl, this);
+ DolphinTabPage *tabPage = new DolphinTabPage(primaryUrl, secondaryUrl, this);
tabPage->setActive(false);
- tabPage->setPlacesSelectorVisible(m_placesSelectorVisible);
- connect(tabPage, &DolphinTabPage::activeViewChanged,
- this, &DolphinTabWidget::activeViewChanged);
- connect(tabPage, &DolphinTabPage::activeViewUrlChanged,
- this, &DolphinTabWidget::tabUrlChanged);
+ connect(tabPage, &DolphinTabPage::activeViewChanged, this, &DolphinTabWidget::activeViewChanged);
+ connect(tabPage, &DolphinTabPage::activeViewUrlChanged, this, &DolphinTabWidget::tabUrlChanged);
+ connect(tabPage->activeViewContainer(), &DolphinViewContainer::captionChanged, this, [this, tabPage]() {
+ updateTabName(indexOf(tabPage));
+ });
+
+ if (position == NewTabPosition::FollowSetting) {
+ if (GeneralSettings::openNewTabAfterLastTab()) {
+ position = NewTabPosition::AtEnd;
+ } else {
+ position = NewTabPosition::AfterCurrent;
+ }
+ }
+
int newTabIndex = -1;
- if (tabPlacement == AfterCurrentTab) {
+ if (position == NewTabPosition::AfterCurrent || (position == NewTabPosition::FollowSetting && !GeneralSettings::openNewTabAfterLastTab())) {
newTabIndex = currentIndex() + 1;
}
+
insertTab(newTabIndex, tabPage, QIcon() /* loaded in tabInserted */, tabName(tabPage));
if (focusWidget) {
// in background, assure that the previous focused widget gets the focus back.
focusWidget->setFocus();
}
+ return tabPage;
}
-void DolphinTabWidget::openDirectories(const QList<QUrl>& dirs, bool splitView)
+void DolphinTabWidget::openDirectories(const QList<QUrl> &dirs, bool splitView)
{
Q_ASSERT(dirs.size() > 0);
+ bool somethingWasAlreadyOpen = false;
+
QList<QUrl>::const_iterator it = dirs.constBegin();
while (it != dirs.constEnd()) {
- 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);
+ const QUrl &primaryUrl = *(it++);
+ const std::optional<ViewIndex> viewIndexAtDirectory = viewOpenAtDirectory(primaryUrl);
+
+ // When the user asks for a URL that's already open,
+ // activate it instead of opening a new tab
+ if (viewIndexAtDirectory.has_value()) {
+ somethingWasAlreadyOpen = true;
+ activateViewContainerAt(viewIndexAtDirectory.value());
+ } else if (splitView && (it != dirs.constEnd())) {
+ const QUrl &secondaryUrl = *(it++);
+ if (somethingWasAlreadyOpen) {
+ openNewTab(primaryUrl, secondaryUrl);
} else {
- tabPage->secondaryViewContainer()->setActive(true);
+ openNewActivatedTab(primaryUrl, secondaryUrl);
}
- // 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 {
- openNewActivatedTab(primaryUrl);
+ if (somethingWasAlreadyOpen) {
+ openNewTab(primaryUrl);
+ } else {
+ openNewActivatedTab(primaryUrl);
+ }
}
}
}
-void DolphinTabWidget::openFiles(const QList<QUrl>& files, bool splitView)
+void DolphinTabWidget::openFiles(const QList<QUrl> &files, bool splitView)
{
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<QUrl> dirs;
- foreach (const QUrl& url, files) {
- const QUrl dir(url.adjusted(QUrl::RemoveFilename));
- if (!dirs.contains(dir)) {
- dirs.append(dir);
+ // Get all distinct directories from 'files'.
+ QList<QUrl> dirsThatNeedToBeOpened;
+ QList<QUrl> dirsThatWereAlreadyOpen;
+ for (const QUrl &file : files) {
+ const QUrl dir(file.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash));
+ if (dirsThatNeedToBeOpened.contains(dir) || dirsThatWereAlreadyOpen.contains(dir)) {
+ continue;
+ }
+
+ // The selecting of files that we do later will not work in views that already have items selected.
+ // So we check if dir is already open and clear the selection if it is. BUG: 417230
+ // We also make sure the view will be activated.
+ auto viewIndex = viewShowingItem(file);
+ if (viewIndex.has_value()) {
+ viewContainerAt(viewIndex.value())->view()->clearSelection();
+ activateViewContainerAt(viewIndex.value());
+ dirsThatWereAlreadyOpen.append(dir);
+ } else {
+ dirsThatNeedToBeOpened.append(dir);
}
}
const int oldTabCount = count();
- openDirectories(dirs, splitView);
+ // Open a tab for each directory. If the "split view" option is enabled,
+ // two directories are shown inside one tab (see openDirectories()).
+ if (dirsThatNeedToBeOpened.size() > 0) {
+ openDirectories(dirsThatNeedToBeOpened, 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 = 0; i < tabCount; ++i) {
- DolphinTabPage* tabPage = tabPageAt(i);
+ DolphinTabPage *tabPage = tabPageAt(i);
tabPage->markUrlsAsSelected(files);
tabPage->markUrlAsCurrent(files.first());
if (i < oldTabCount) {
return;
}
- DolphinTabPage* tabPage = tabPageAt(index);
- emit rememberClosedTab(tabPage->activeViewContainer()->url(), tabPage->saveState());
+ DolphinTabPage *tabPage = tabPageAt(index);
+ Q_EMIT rememberClosedTab(tabPage->activeViewContainer()->url(), tabPage->saveState());
removeTab(index);
tabPage->deleteLater();
setCurrentIndex(index >= 0 ? index : (count() - 1));
}
-void DolphinTabWidget::slotPlacesPanelVisibilityChanged(bool visible)
-{
- // The places-selector from the URL navigator should only be shown
- // if the places dock is invisible
- m_placesSelectorVisible = !visible;
-
- const int tabCount = count();
- for (int i = 0; i < tabCount; ++i) {
- DolphinTabPage* tabPage = tabPageAt(i);
- tabPage->setPlacesSelectorVisible(m_placesSelectorVisible);
- }
-}
-
-void DolphinTabWidget::restoreClosedTab(const QByteArray& state)
+void DolphinTabWidget::restoreClosedTab(const QByteArray &state)
{
openNewActivatedTab();
currentTabPage()->restoreState(state);
void DolphinTabWidget::copyToInactiveSplitView()
{
- const DolphinTabPage* tabPage = tabPageAt(currentIndex());
- DolphinViewContainer* activeViewContainer = currentTabPage()->activeViewContainer();
- if (!tabPage->splitViewEnabled() || activeViewContainer->view()->selectedItems().isEmpty()) {
+ const DolphinTabPage *tabPage = currentTabPage();
+ if (!tabPage->splitViewEnabled()) {
return;
}
- if (tabPage->primaryViewActive()) {
- // copy from left panel to right
- activeViewContainer->view()->copySelectedItems(activeViewContainer->view()->selectedItems(), tabPage->secondaryViewContainer()->url());
- } else {
- // copy from right panel to left
- activeViewContainer->view()->copySelectedItems(activeViewContainer->view()->selectedItems(), tabPage->primaryViewContainer()->url());
+ const KFileItemList selectedItems = tabPage->activeViewContainer()->view()->selectedItems();
+ if (selectedItems.isEmpty()) {
+ return;
}
+
+ DolphinView *const inactiveView = tabPage->inactiveViewContainer()->view();
+ inactiveView->copySelectedItems(selectedItems, inactiveView->url());
}
void DolphinTabWidget::moveToInactiveSplitView()
{
- const DolphinTabPage* tabPage = tabPageAt(currentIndex());
- DolphinViewContainer* activeViewContainer = currentTabPage()->activeViewContainer();
- if (!tabPage->splitViewEnabled() || activeViewContainer->view()->selectedItems().isEmpty()) {
+ const DolphinTabPage *tabPage = currentTabPage();
+ if (!tabPage->splitViewEnabled()) {
return;
}
- if (tabPage->primaryViewActive()) {
- // move from left panel to right
- activeViewContainer->view()->moveSelectedItems(activeViewContainer->view()->selectedItems(), tabPage->secondaryViewContainer()->url());
- } else {
- // move from right panel to left
- activeViewContainer->view()->moveSelectedItems(activeViewContainer->view()->selectedItems(), tabPage->primaryViewContainer()->url());
+ const KFileItemList selectedItems = tabPage->activeViewContainer()->view()->selectedItems();
+ if (selectedItems.isEmpty()) {
+ return;
}
+
+ DolphinView *const inactiveView = tabPage->inactiveViewContainer()->view();
+ inactiveView->moveSelectedItems(selectedItems, inactiveView->url());
}
void DolphinTabWidget::detachTab(int index)
QStringList args;
- const DolphinTabPage* tabPage = tabPageAt(index);
+ const DolphinTabPage *tabPage = tabPageAt(index);
args << tabPage->primaryViewContainer()->url().url();
if (tabPage->splitViewEnabled()) {
args << tabPage->secondaryViewContainer()->url().url();
void DolphinTabWidget::openNewActivatedTab(int index)
{
Q_ASSERT(index >= 0);
- const DolphinTabPage* tabPage = tabPageAt(index);
+ const DolphinTabPage *tabPage = tabPageAt(index);
openNewActivatedTab(tabPage->activeViewContainer()->url());
}
-void DolphinTabWidget::tabDropEvent(int index, QDropEvent* event)
+void DolphinTabWidget::tabDragMoveEvent(int index, QDragMoveEvent *event)
{
if (index >= 0) {
- DolphinView* view = tabPageAt(index)->activeViewContainer()->view();
+ DolphinView *view = tabPageAt(index)->activeViewContainer()->view();
+ DragAndDropHelper::updateDropAction(event, view->url());
+ }
+}
+
+void DolphinTabWidget::tabDropEvent(int index, QDropEvent *event)
+{
+ if (index >= 0) {
+ DolphinView *view = tabPageAt(index)->activeViewContainer()->view();
view->dropUrls(view->url(), event, view);
+ } else {
+ const auto urls = event->mimeData()->urls();
+
+ for (const QUrl &url : urls) {
+ auto *job = KIO::stat(url, KIO::StatJob::SourceSide, KIO::StatDetail::StatBasic, KIO::JobFlag::HideProgressInfo);
+ connect(job, &KJob::result, this, [this, job]() {
+ if (!job->error() && job->statResult().isDir()) {
+ openNewTab(job->url(), QUrl(), NewTabPosition::AtEnd);
+ }
+ });
+ }
}
}
-void DolphinTabWidget::tabUrlChanged(const QUrl& url)
+void DolphinTabWidget::tabUrlChanged(const QUrl &url)
{
- const int index = indexOf(qobject_cast<QWidget*>(sender()));
+ const int index = indexOf(qobject_cast<QWidget *>(sender()));
if (index >= 0) {
- tabBar()->setTabText(index, tabName(tabPageAt(index)));
- tabBar()->setTabToolTip(index, url.path());
+ updateTabName(index);
+ tabBar()->setTabToolTip(index, url.toDisplayString(QUrl::PreferLocalFile));
if (tabBar()->isVisible()) {
- tabBar()->setTabIcon(index, QIcon::fromTheme(KIO::iconNameForUrl(url)));
+ // ensure the path url ends with a slash to have proper folder icon for remote folders
+ const QUrl pathUrl = QUrl(url.adjusted(QUrl::StripTrailingSlash).toString(QUrl::FullyEncoded).append("/"));
+ tabBar()->setTabIcon(index, QIcon::fromTheme(KIO::iconNameForUrl(pathUrl)));
} 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()) {
- emit currentUrlChanged(url);
+ Q_EMIT currentUrlChanged(url);
}
+
+ Q_EMIT urlChanged(url);
}
}
void DolphinTabWidget::currentTabChanged(int index)
{
- // last-viewed tab deactivation
- if (DolphinTabPage* tabPage = tabPageAt(m_lastViewedTab)) {
- tabPage->setActive(false);
+ DolphinTabPage *tabPage = tabPageAt(index);
+ if (tabPage == m_lastViewedTab) {
+ return;
+ }
+ if (m_lastViewedTab) {
+ m_lastViewedTab->disconnectNavigators();
+ m_lastViewedTab->setActive(false);
}
- DolphinTabPage* tabPage = tabPageAt(index);
- DolphinViewContainer* viewContainer = tabPage->activeViewContainer();
- emit activeViewChanged(viewContainer);
- emit currentUrlChanged(viewContainer->url());
+ if (tabPage->splitViewEnabled() && !m_navigatorsWidget->secondaryUrlNavigator()) {
+ m_navigatorsWidget->createSecondaryUrlNavigator();
+ }
+ DolphinViewContainer *viewContainer = tabPage->activeViewContainer();
+ Q_EMIT activeViewChanged(viewContainer);
+ Q_EMIT currentUrlChanged(viewContainer->url());
tabPage->setActive(true);
- m_lastViewedTab = index;
+ tabPage->connectNavigators(m_navigatorsWidget);
+ m_navigatorsWidget->setSecondaryNavigatorVisible(tabPage->splitViewEnabled());
+ m_lastViewedTab = tabPage;
+}
+
+void DolphinTabWidget::renameTab(int index, const QString &name)
+{
+ tabPageAt(index)->setCustomLabel(name);
+ updateTabName(index);
}
void DolphinTabWidget::tabInserted(int index)
{
QTabWidget::tabInserted(index);
- if (count() > 1) {
+ if (tabBar()->isVisible()) {
// Resolve all pending tab icons
for (int i = 0; i < count(); ++i) {
const QUrl url = tabPageAt(i)->activeViewContainer()->url();
if (tabBar()->tabIcon(i).isNull()) {
- tabBar()->setTabIcon(i, QIcon::fromTheme(KIO::iconNameForUrl(url)));
+ // ensure the path url ends with a slash to have proper folder icon for remote folders
+ const QUrl pathUrl = QUrl(url.adjusted(QUrl::StripTrailingSlash).toString(QUrl::FullyEncoded).append("/"));
+ tabBar()->setTabIcon(i, QIcon::fromTheme(KIO::iconNameForUrl(pathUrl)));
}
if (tabBar()->tabToolTip(i).isEmpty()) {
- tabBar()->setTabToolTip(index, url.path());
+ tabBar()->setTabToolTip(index, url.toDisplayString(QUrl::PreferLocalFile));
}
}
-
- tabBar()->show();
}
- emit tabCountChanged(count());
+ Q_EMIT tabCountChanged(count());
}
void DolphinTabWidget::tabRemoved(int index)
{
QTabWidget::tabRemoved(index);
- // If only one tab is left, then remove the tab entry so that
- // closing the last tab is not possible.
- if (count() < 2) {
- tabBar()->hide();
- }
-
- emit tabCountChanged(count());
+ Q_EMIT tabCountChanged(count());
}
-QString DolphinTabWidget::tabName(DolphinTabPage* tabPage) const
+QString DolphinTabWidget::tabName(DolphinTabPage *tabPage) const
{
if (!tabPage) {
return QString();
}
- QString name = tabPage->activeViewContainer()->caption();
+ // clang-format off
+ QString name;
+ if (tabPage->splitViewEnabled()) {
+ if (tabPage->primaryViewActive()) {
+ // i18n: %1 is the primary view and %2 the secondary view. For left to right languages the primary view is on the left so we also want it to be on the
+ // left in the tab name. In right to left languages the primary view would be on the right so the tab name should match.
+ name = i18nc("@title:tab Active primary view | (Inactive secondary view)", "%1 | (%2)", tabPage->primaryViewContainer()->caption(), tabPage->secondaryViewContainer()->caption());
+ } else {
+ // i18n: %1 is the primary view and %2 the secondary view. For left to right languages the primary view is on the left so we also want it to be on the
+ // left in the tab name. In right to left languages the primary view would be on the right so the tab name should match.
+ name = i18nc("@title:tab (Inactive primary view) | Active secondary view", "(%1) | %2", tabPage->primaryViewContainer()->caption(), tabPage->secondaryViewContainer()->caption());
+ }
+ } else {
+ name = tabPage->activeViewContainer()->caption();
+ }
+ // clang-format on
+
// 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("&&"));
+ return KStringHandler::rsqueeze(name.replace('&', QLatin1String("&&")), 40 /* default maximum visible folder name visible */);
+}
+
+DolphinViewContainer *DolphinTabWidget::viewContainerAt(DolphinTabWidget::ViewIndex viewIndex) const
+{
+ const auto tabPage = tabPageAt(viewIndex.tabIndex);
+ if (!tabPage) {
+ return nullptr;
+ }
+ return viewIndex.isInPrimaryView ? tabPage->primaryViewContainer() : tabPage->secondaryViewContainer();
+}
+
+DolphinViewContainer *DolphinTabWidget::activateViewContainerAt(DolphinTabWidget::ViewIndex viewIndex)
+{
+ activateTab(viewIndex.tabIndex);
+ auto viewContainer = viewContainerAt(viewIndex);
+ if (!viewContainer) {
+ return nullptr;
+ }
+ viewContainer->setActive(true);
+ return viewContainer;
}
-QPair<int, bool> DolphinTabWidget::indexByUrl(const QUrl& url) const
+const std::optional<const DolphinTabWidget::ViewIndex> DolphinTabWidget::viewOpenAtDirectory(const QUrl &directory) const
{
- for (int i = 0; i < count(); i++) {
+ int i = currentIndex();
+ if (i < 0) {
+ return std::nullopt;
+ }
+ // loop over the tabs starting from the current one
+ do {
const auto tabPage = tabPageAt(i);
- if (url == tabPage->primaryViewContainer()->url()) {
- return qMakePair(i, true);
+ if (tabPage->primaryViewContainer()->url() == directory) {
+ return std::optional(ViewIndex{i, true});
}
- if (tabPage->splitViewEnabled() && url == tabPage->secondaryViewContainer()->url()) {
- return qMakePair(i, false);
+ if (tabPage->splitViewEnabled() && tabPage->secondaryViewContainer()->url() == directory) {
+ return std::optional(ViewIndex{i, false});
}
+
+ i = (i + 1) % count();
+ } while (i != currentIndex());
+
+ return std::nullopt;
+}
+
+const std::optional<const DolphinTabWidget::ViewIndex> DolphinTabWidget::viewShowingItem(const QUrl &item) const
+{
+ // The item might not be loaded yet even though it exists. So instead
+ // we check if the folder containing the item is showing its contents.
+ const QUrl dirContainingItem(item.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash));
+
+ // The dirContainingItem is either open directly or expanded in a tree-style view mode.
+ // Is dirContainingitem the base url of a view?
+ auto viewOpenAtContainingDirectory = viewOpenAtDirectory(dirContainingItem);
+ if (viewOpenAtContainingDirectory.has_value()) {
+ return viewOpenAtContainingDirectory;
+ }
+
+ // Is dirContainingItem expanded in some tree-style view?
+ // The rest of this method is about figuring this out.
+
+ int i = currentIndex();
+ if (i < 0) {
+ return std::nullopt;
}
- return qMakePair(-1, false);
+ // loop over the tabs starting from the current one
+ do {
+ const auto tabPage = tabPageAt(i);
+ if (tabPage->primaryViewContainer()->url().isParentOf(item)) {
+ const KFileItem fileItemContainingItem = tabPage->primaryViewContainer()->view()->items().findByUrl(dirContainingItem);
+ if (!fileItemContainingItem.isNull() && tabPage->primaryViewContainer()->view()->isExpanded(fileItemContainingItem)) {
+ return std::optional(ViewIndex{i, true});
+ }
+ }
+
+ if (tabPage->splitViewEnabled() && tabPage->secondaryViewContainer()->url().isParentOf(item)) {
+ const KFileItem fileItemContainingItem = tabPage->secondaryViewContainer()->view()->items().findByUrl(dirContainingItem);
+ if (!fileItemContainingItem.isNull() && tabPage->secondaryViewContainer()->view()->isExpanded(fileItemContainingItem)) {
+ return std::optional(ViewIndex{i, false});
+ }
+ }
+
+ i = (i + 1) % count();
+ } while (i != currentIndex());
+
+ return std::nullopt;
}
+
+#include "moc_dolphintabwidget.cpp"