File indexing completed on 2024-04-28 09:40:56

0001 /*
0002  * SPDX-FileCopyrightText: 2014 Emmanuel Pescosta <emmanuelpescosta099@gmail.com>
0003  *
0004  * SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "dolphintabwidget.h"
0008 
0009 #include "dolphin_generalsettings.h"
0010 #include "dolphintabbar.h"
0011 #include "dolphinviewcontainer.h"
0012 
0013 #include <KAcceleratorManager>
0014 #include <KConfigGroup>
0015 #include <KIO/CommandLauncherJob>
0016 #include <KLocalizedString>
0017 #include <KShell>
0018 #include <KStringHandler>
0019 #include <kio/global.h>
0020 
0021 #include <QApplication>
0022 #include <QDropEvent>
0023 
0024 DolphinTabWidget::DolphinTabWidget(DolphinNavigatorsWidgetAction *navigatorsWidget, QWidget *parent)
0025     : QTabWidget(parent)
0026     , m_lastViewedTab(nullptr)
0027     , m_navigatorsWidget{navigatorsWidget}
0028 {
0029     KAcceleratorManager::setNoAccel(this);
0030 
0031     connect(this, &DolphinTabWidget::tabCloseRequested, this, QOverload<int>::of(&DolphinTabWidget::closeTab));
0032     connect(this, &DolphinTabWidget::currentChanged, this, &DolphinTabWidget::currentTabChanged);
0033 
0034     DolphinTabBar *tabBar = new DolphinTabBar(this);
0035     connect(tabBar, &DolphinTabBar::openNewActivatedTab, this, QOverload<int>::of(&DolphinTabWidget::openNewActivatedTab));
0036     connect(tabBar, &DolphinTabBar::tabDropEvent, this, &DolphinTabWidget::tabDropEvent);
0037     connect(tabBar, &DolphinTabBar::tabDetachRequested, this, &DolphinTabWidget::detachTab);
0038 
0039     setTabBar(tabBar);
0040     setDocumentMode(true);
0041     setElideMode(Qt::ElideRight);
0042     setUsesScrollButtons(true);
0043     setTabBarAutoHide(true);
0044 }
0045 
0046 DolphinTabPage *DolphinTabWidget::currentTabPage() const
0047 {
0048     return tabPageAt(currentIndex());
0049 }
0050 
0051 DolphinTabPage *DolphinTabWidget::nextTabPage() const
0052 {
0053     const int index = currentIndex() + 1;
0054     return tabPageAt(index < count() ? index : 0);
0055 }
0056 
0057 DolphinTabPage *DolphinTabWidget::prevTabPage() const
0058 {
0059     const int index = currentIndex() - 1;
0060     return tabPageAt(index >= 0 ? index : (count() - 1));
0061 }
0062 
0063 DolphinTabPage *DolphinTabWidget::tabPageAt(const int index) const
0064 {
0065     return static_cast<DolphinTabPage *>(widget(index));
0066 }
0067 
0068 void DolphinTabWidget::saveProperties(KConfigGroup &group) const
0069 {
0070     const int tabCount = count();
0071     group.writeEntry("Tab Count", tabCount);
0072     group.writeEntry("Active Tab Index", currentIndex());
0073 
0074     for (int i = 0; i < tabCount; ++i) {
0075         const DolphinTabPage *tabPage = tabPageAt(i);
0076         group.writeEntry("Tab Data " % QString::number(i), tabPage->saveState());
0077     }
0078 }
0079 
0080 void DolphinTabWidget::readProperties(const KConfigGroup &group)
0081 {
0082     const int tabCount = group.readEntry("Tab Count", 0);
0083     for (int i = 0; i < tabCount; ++i) {
0084         if (i >= count()) {
0085             openNewActivatedTab();
0086         }
0087         const QByteArray state = group.readEntry("Tab Data " % QString::number(i), QByteArray());
0088         tabPageAt(i)->restoreState(state);
0089     }
0090 
0091     const int index = group.readEntry("Active Tab Index", 0);
0092     setCurrentIndex(index);
0093 }
0094 
0095 void DolphinTabWidget::refreshViews()
0096 {
0097     // Left-elision is better when showing full paths, since you care most
0098     // about the current directory which is on the right
0099     if (GeneralSettings::showFullPathInTitlebar()) {
0100         setElideMode(Qt::ElideLeft);
0101     } else {
0102         setElideMode(Qt::ElideRight);
0103     }
0104 
0105     const int tabCount = count();
0106     for (int i = 0; i < tabCount; ++i) {
0107         tabBar()->setTabText(i, tabName(tabPageAt(i)));
0108         tabPageAt(i)->refreshViews();
0109     }
0110 }
0111 
0112 bool DolphinTabWidget::isUrlOpen(const QUrl &url) const
0113 {
0114     return viewOpenAtDirectory(url).has_value();
0115 }
0116 
0117 bool DolphinTabWidget::isItemVisibleInAnyView(const QUrl &urlOfItem) const
0118 {
0119     return viewShowingItem(urlOfItem).has_value();
0120 }
0121 
0122 void DolphinTabWidget::openNewActivatedTab()
0123 {
0124     std::unique_ptr<DolphinUrlNavigator::VisualState> oldNavigatorState;
0125     if (currentTabPage()->primaryViewActive() || !m_navigatorsWidget->secondaryUrlNavigator()) {
0126         oldNavigatorState = m_navigatorsWidget->primaryUrlNavigator()->visualState();
0127     } else {
0128         oldNavigatorState = m_navigatorsWidget->secondaryUrlNavigator()->visualState();
0129     }
0130 
0131     const DolphinViewContainer *oldActiveViewContainer = currentTabPage()->activeViewContainer();
0132     Q_ASSERT(oldActiveViewContainer);
0133 
0134     openNewActivatedTab(oldActiveViewContainer->url());
0135 
0136     DolphinViewContainer *newActiveViewContainer = currentTabPage()->activeViewContainer();
0137     Q_ASSERT(newActiveViewContainer);
0138 
0139     // The URL navigator of the new tab should have the same editable state
0140     // as the current tab
0141     newActiveViewContainer->urlNavigator()->setVisualState(*oldNavigatorState.get());
0142 
0143     // Always focus the new tab's view
0144     newActiveViewContainer->view()->setFocus();
0145 }
0146 
0147 void DolphinTabWidget::openNewActivatedTab(const QUrl &primaryUrl, const QUrl &secondaryUrl)
0148 {
0149     openNewTab(primaryUrl, secondaryUrl);
0150     if (GeneralSettings::openNewTabAfterLastTab()) {
0151         setCurrentIndex(count() - 1);
0152     } else {
0153         setCurrentIndex(currentIndex() + 1);
0154     }
0155 }
0156 
0157 void DolphinTabWidget::openNewTab(const QUrl &primaryUrl, const QUrl &secondaryUrl, DolphinTabWidget::NewTabPosition position)
0158 {
0159     QWidget *focusWidget = QApplication::focusWidget();
0160 
0161     DolphinTabPage *tabPage = new DolphinTabPage(primaryUrl, secondaryUrl, this);
0162     tabPage->setActive(false);
0163     connect(tabPage, &DolphinTabPage::activeViewChanged, this, &DolphinTabWidget::activeViewChanged);
0164     connect(tabPage, &DolphinTabPage::activeViewUrlChanged, this, &DolphinTabWidget::tabUrlChanged);
0165     connect(tabPage->activeViewContainer(), &DolphinViewContainer::captionChanged, this, [this, tabPage]() {
0166         const int tabIndex = indexOf(tabPage);
0167         Q_ASSERT(tabIndex >= 0);
0168         tabBar()->setTabText(tabIndex, tabName(tabPage));
0169     });
0170 
0171     if (position == NewTabPosition::FollowSetting) {
0172         if (GeneralSettings::openNewTabAfterLastTab()) {
0173             position = NewTabPosition::AtEnd;
0174         } else {
0175             position = NewTabPosition::AfterCurrent;
0176         }
0177     }
0178 
0179     int newTabIndex = -1;
0180     if (position == NewTabPosition::AfterCurrent || (position == NewTabPosition::FollowSetting && !GeneralSettings::openNewTabAfterLastTab())) {
0181         newTabIndex = currentIndex() + 1;
0182     }
0183 
0184     insertTab(newTabIndex, tabPage, QIcon() /* loaded in tabInserted */, tabName(tabPage));
0185 
0186     if (focusWidget) {
0187         // The DolphinViewContainer grabbed the keyboard focus. As the tab is opened
0188         // in background, assure that the previous focused widget gets the focus back.
0189         focusWidget->setFocus();
0190     }
0191 }
0192 
0193 void DolphinTabWidget::openDirectories(const QList<QUrl> &dirs, bool splitView)
0194 {
0195     Q_ASSERT(dirs.size() > 0);
0196 
0197     bool somethingWasAlreadyOpen = false;
0198 
0199     QList<QUrl>::const_iterator it = dirs.constBegin();
0200     while (it != dirs.constEnd()) {
0201         const QUrl &primaryUrl = *(it++);
0202         const std::optional<ViewIndex> viewIndexAtDirectory = viewOpenAtDirectory(primaryUrl);
0203 
0204         // When the user asks for a URL that's already open,
0205         // activate it instead of opening a new tab
0206         if (viewIndexAtDirectory.has_value()) {
0207             somethingWasAlreadyOpen = true;
0208             activateViewContainerAt(viewIndexAtDirectory.value());
0209         } else if (splitView && (it != dirs.constEnd())) {
0210             const QUrl &secondaryUrl = *(it++);
0211             if (somethingWasAlreadyOpen) {
0212                 openNewTab(primaryUrl, secondaryUrl);
0213             } else {
0214                 openNewActivatedTab(primaryUrl, secondaryUrl);
0215             }
0216         } else {
0217             if (somethingWasAlreadyOpen) {
0218                 openNewTab(primaryUrl);
0219             } else {
0220                 openNewActivatedTab(primaryUrl);
0221             }
0222         }
0223     }
0224 }
0225 
0226 void DolphinTabWidget::openFiles(const QList<QUrl> &files, bool splitView)
0227 {
0228     Q_ASSERT(files.size() > 0);
0229 
0230     // Get all distinct directories from 'files'.
0231     QList<QUrl> dirsThatNeedToBeOpened;
0232     QList<QUrl> dirsThatWereAlreadyOpen;
0233     for (const QUrl &file : files) {
0234         const QUrl dir(file.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash));
0235         if (dirsThatNeedToBeOpened.contains(dir) || dirsThatWereAlreadyOpen.contains(dir)) {
0236             continue;
0237         }
0238 
0239         // The selecting of files that we do later will not work in views that already have items selected.
0240         // So we check if dir is already open and clear the selection if it is. BUG: 417230
0241         // We also make sure the view will be activated.
0242         auto viewIndex = viewShowingItem(file);
0243         if (viewIndex.has_value()) {
0244             viewContainerAt(viewIndex.value())->view()->clearSelection();
0245             activateViewContainerAt(viewIndex.value());
0246             dirsThatWereAlreadyOpen.append(dir);
0247         } else {
0248             dirsThatNeedToBeOpened.append(dir);
0249         }
0250     }
0251 
0252     const int oldTabCount = count();
0253     // Open a tab for each directory. If the "split view" option is enabled,
0254     // two directories are shown inside one tab (see openDirectories()).
0255     if (dirsThatNeedToBeOpened.size() > 0) {
0256         openDirectories(dirsThatNeedToBeOpened, splitView);
0257     }
0258     const int tabCount = count();
0259 
0260     // Select the files. Although the files can be split between several
0261     // tabs, there is no need to split 'files' accordingly, as
0262     // the DolphinView will just ignore invalid selections.
0263     for (int i = 0; i < tabCount; ++i) {
0264         DolphinTabPage *tabPage = tabPageAt(i);
0265         tabPage->markUrlsAsSelected(files);
0266         tabPage->markUrlAsCurrent(files.first());
0267         if (i < oldTabCount) {
0268             // Force selection of file if directory was already open, BUG: 417230
0269             tabPage->activeViewContainer()->view()->updateViewState();
0270         }
0271     }
0272 }
0273 
0274 void DolphinTabWidget::closeTab()
0275 {
0276     closeTab(currentIndex());
0277 }
0278 
0279 void DolphinTabWidget::closeTab(const int index)
0280 {
0281     Q_ASSERT(index >= 0);
0282     Q_ASSERT(index < count());
0283 
0284     if (count() < 2) {
0285         // Close Dolphin when closing the last tab.
0286         parentWidget()->close();
0287         return;
0288     }
0289 
0290     DolphinTabPage *tabPage = tabPageAt(index);
0291     Q_EMIT rememberClosedTab(tabPage->activeViewContainer()->url(), tabPage->saveState());
0292 
0293     removeTab(index);
0294     tabPage->deleteLater();
0295 }
0296 
0297 void DolphinTabWidget::activateTab(const int index)
0298 {
0299     if (index < count()) {
0300         setCurrentIndex(index);
0301     }
0302 }
0303 
0304 void DolphinTabWidget::activateLastTab()
0305 {
0306     setCurrentIndex(count() - 1);
0307 }
0308 
0309 void DolphinTabWidget::activateNextTab()
0310 {
0311     const int index = currentIndex() + 1;
0312     setCurrentIndex(index < count() ? index : 0);
0313 }
0314 
0315 void DolphinTabWidget::activatePrevTab()
0316 {
0317     const int index = currentIndex() - 1;
0318     setCurrentIndex(index >= 0 ? index : (count() - 1));
0319 }
0320 
0321 void DolphinTabWidget::restoreClosedTab(const QByteArray &state)
0322 {
0323     openNewActivatedTab();
0324     currentTabPage()->restoreState(state);
0325 }
0326 
0327 void DolphinTabWidget::copyToInactiveSplitView()
0328 {
0329     const DolphinTabPage *tabPage = currentTabPage();
0330     if (!tabPage->splitViewEnabled()) {
0331         return;
0332     }
0333 
0334     const KFileItemList selectedItems = tabPage->activeViewContainer()->view()->selectedItems();
0335     if (selectedItems.isEmpty()) {
0336         return;
0337     }
0338 
0339     DolphinView *const inactiveView = tabPage->inactiveViewContainer()->view();
0340     inactiveView->copySelectedItems(selectedItems, inactiveView->url());
0341 }
0342 
0343 void DolphinTabWidget::moveToInactiveSplitView()
0344 {
0345     const DolphinTabPage *tabPage = currentTabPage();
0346     if (!tabPage->splitViewEnabled()) {
0347         return;
0348     }
0349 
0350     const KFileItemList selectedItems = tabPage->activeViewContainer()->view()->selectedItems();
0351     if (selectedItems.isEmpty()) {
0352         return;
0353     }
0354 
0355     DolphinView *const inactiveView = tabPage->inactiveViewContainer()->view();
0356     inactiveView->moveSelectedItems(selectedItems, inactiveView->url());
0357 }
0358 
0359 void DolphinTabWidget::detachTab(int index)
0360 {
0361     Q_ASSERT(index >= 0);
0362 
0363     QStringList args;
0364 
0365     const DolphinTabPage *tabPage = tabPageAt(index);
0366     args << tabPage->primaryViewContainer()->url().url();
0367     if (tabPage->splitViewEnabled()) {
0368         args << tabPage->secondaryViewContainer()->url().url();
0369         args << QStringLiteral("--split");
0370     }
0371     args << QStringLiteral("--new-window");
0372 
0373     KIO::CommandLauncherJob *job = new KIO::CommandLauncherJob("dolphin", args, this);
0374     job->setDesktopName(QStringLiteral("org.kde.dolphin"));
0375     job->start();
0376 
0377     closeTab(index);
0378 }
0379 
0380 void DolphinTabWidget::openNewActivatedTab(int index)
0381 {
0382     Q_ASSERT(index >= 0);
0383     const DolphinTabPage *tabPage = tabPageAt(index);
0384     openNewActivatedTab(tabPage->activeViewContainer()->url());
0385 }
0386 
0387 void DolphinTabWidget::tabDropEvent(int index, QDropEvent *event)
0388 {
0389     if (index >= 0) {
0390         DolphinView *view = tabPageAt(index)->activeViewContainer()->view();
0391         view->dropUrls(view->url(), event, view);
0392     } else {
0393         const auto urls = event->mimeData()->urls();
0394 
0395         for (const QUrl &url : urls) {
0396             auto *job = KIO::stat(url, KIO::StatJob::SourceSide, KIO::StatDetail::StatBasic, KIO::JobFlag::HideProgressInfo);
0397             connect(job, &KJob::result, this, [this, job]() {
0398                 if (!job->error() && job->statResult().isDir()) {
0399                     openNewTab(job->url(), QUrl(), NewTabPosition::AtEnd);
0400                 }
0401             });
0402         }
0403     }
0404 }
0405 
0406 void DolphinTabWidget::tabUrlChanged(const QUrl &url)
0407 {
0408     const int index = indexOf(qobject_cast<QWidget *>(sender()));
0409     if (index >= 0) {
0410         tabBar()->setTabText(index, tabName(tabPageAt(index)));
0411         tabBar()->setTabToolTip(index, url.toDisplayString(QUrl::PreferLocalFile));
0412         if (tabBar()->isVisible()) {
0413             // ensure the path url ends with a slash to have proper folder icon for remote folders
0414             const QUrl pathUrl = QUrl(url.adjusted(QUrl::StripTrailingSlash).toString(QUrl::FullyEncoded).append("/"));
0415             tabBar()->setTabIcon(index, QIcon::fromTheme(KIO::iconNameForUrl(pathUrl)));
0416         } else {
0417             // Mark as dirty, actually load once the tab bar actually gets shown
0418             tabBar()->setTabIcon(index, QIcon());
0419         }
0420 
0421         // Emit the currentUrlChanged signal if the url of the current tab has been changed.
0422         if (index == currentIndex()) {
0423             Q_EMIT currentUrlChanged(url);
0424         }
0425     }
0426 }
0427 
0428 void DolphinTabWidget::currentTabChanged(int index)
0429 {
0430     DolphinTabPage *tabPage = tabPageAt(index);
0431     if (tabPage == m_lastViewedTab) {
0432         return;
0433     }
0434     if (m_lastViewedTab) {
0435         m_lastViewedTab->disconnectNavigators();
0436         m_lastViewedTab->setActive(false);
0437     }
0438     if (tabPage->splitViewEnabled() && !m_navigatorsWidget->secondaryUrlNavigator()) {
0439         m_navigatorsWidget->createSecondaryUrlNavigator();
0440     }
0441     DolphinViewContainer *viewContainer = tabPage->activeViewContainer();
0442     Q_EMIT activeViewChanged(viewContainer);
0443     Q_EMIT currentUrlChanged(viewContainer->url());
0444     tabPage->setActive(true);
0445     tabPage->connectNavigators(m_navigatorsWidget);
0446     m_navigatorsWidget->setSecondaryNavigatorVisible(tabPage->splitViewEnabled());
0447     m_lastViewedTab = tabPage;
0448 }
0449 
0450 void DolphinTabWidget::tabInserted(int index)
0451 {
0452     QTabWidget::tabInserted(index);
0453 
0454     if (tabBar()->isVisible()) {
0455         // Resolve all pending tab icons
0456         for (int i = 0; i < count(); ++i) {
0457             const QUrl url = tabPageAt(i)->activeViewContainer()->url();
0458             if (tabBar()->tabIcon(i).isNull()) {
0459                 // ensure the path url ends with a slash to have proper folder icon for remote folders
0460                 const QUrl pathUrl = QUrl(url.adjusted(QUrl::StripTrailingSlash).toString(QUrl::FullyEncoded).append("/"));
0461                 tabBar()->setTabIcon(i, QIcon::fromTheme(KIO::iconNameForUrl(pathUrl)));
0462             }
0463             if (tabBar()->tabToolTip(i).isEmpty()) {
0464                 tabBar()->setTabToolTip(index, url.toDisplayString(QUrl::PreferLocalFile));
0465             }
0466         }
0467     }
0468 
0469     Q_EMIT tabCountChanged(count());
0470 }
0471 
0472 void DolphinTabWidget::tabRemoved(int index)
0473 {
0474     QTabWidget::tabRemoved(index);
0475 
0476     Q_EMIT tabCountChanged(count());
0477 }
0478 
0479 QString DolphinTabWidget::tabName(DolphinTabPage *tabPage) const
0480 {
0481     if (!tabPage) {
0482         return QString();
0483     }
0484     // clang-format off
0485     QString name;
0486     if (tabPage->splitViewEnabled()) {
0487         if (tabPage->primaryViewActive()) {
0488             // 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
0489             // left in the tab name. In right to left languages the primary view would be on the right so the tab name should match.
0490             name = i18nc("@title:tab Active primary view | (Inactive secondary view)", "%1 | (%2)", tabPage->primaryViewContainer()->caption(), tabPage->secondaryViewContainer()->caption());
0491         } else {
0492             // 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
0493             // left in the tab name. In right to left languages the primary view would be on the right so the tab name should match.
0494             name = i18nc("@title:tab (Inactive primary view) | Active secondary view", "(%1) | %2", tabPage->primaryViewContainer()->caption(), tabPage->secondaryViewContainer()->caption());
0495         }
0496     } else {
0497         name = tabPage->activeViewContainer()->caption();
0498     }
0499     // clang-format on
0500 
0501     // Make sure that a '&' inside the directory name is displayed correctly
0502     // and not misinterpreted as a keyboard shortcut in QTabBar::setTabText()
0503     return KStringHandler::rsqueeze(name.replace('&', QLatin1String("&&")), 40 /* default maximum visible folder name visible */);
0504 }
0505 
0506 DolphinViewContainer *DolphinTabWidget::viewContainerAt(DolphinTabWidget::ViewIndex viewIndex) const
0507 {
0508     const auto tabPage = tabPageAt(viewIndex.tabIndex);
0509     if (!tabPage) {
0510         return nullptr;
0511     }
0512     return viewIndex.isInPrimaryView ? tabPage->primaryViewContainer() : tabPage->secondaryViewContainer();
0513 }
0514 
0515 DolphinViewContainer *DolphinTabWidget::activateViewContainerAt(DolphinTabWidget::ViewIndex viewIndex)
0516 {
0517     activateTab(viewIndex.tabIndex);
0518     auto viewContainer = viewContainerAt(viewIndex);
0519     if (!viewContainer) {
0520         return nullptr;
0521     }
0522     viewContainer->setActive(true);
0523     return viewContainer;
0524 }
0525 
0526 const std::optional<const DolphinTabWidget::ViewIndex> DolphinTabWidget::viewOpenAtDirectory(const QUrl &directory) const
0527 {
0528     int i = currentIndex();
0529     if (i < 0) {
0530         return std::nullopt;
0531     }
0532     // loop over the tabs starting from the current one
0533     do {
0534         const auto tabPage = tabPageAt(i);
0535         if (tabPage->primaryViewContainer()->url() == directory) {
0536             return std::optional(ViewIndex{i, true});
0537         }
0538 
0539         if (tabPage->splitViewEnabled() && tabPage->secondaryViewContainer()->url() == directory) {
0540             return std::optional(ViewIndex{i, false});
0541         }
0542 
0543         i = (i + 1) % count();
0544     } while (i != currentIndex());
0545 
0546     return std::nullopt;
0547 }
0548 
0549 const std::optional<const DolphinTabWidget::ViewIndex> DolphinTabWidget::viewShowingItem(const QUrl &item) const
0550 {
0551     // The item might not be loaded yet even though it exists. So instead
0552     // we check if the folder containing the item is showing its contents.
0553     const QUrl dirContainingItem(item.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash));
0554 
0555     // The dirContainingItem is either open directly or expanded in a tree-style view mode.
0556     // Is dirContainingitem the base url of a view?
0557     auto viewOpenAtContainingDirectory = viewOpenAtDirectory(dirContainingItem);
0558     if (viewOpenAtContainingDirectory.has_value()) {
0559         return viewOpenAtContainingDirectory;
0560     }
0561 
0562     // Is dirContainingItem expanded in some tree-style view?
0563     // The rest of this method is about figuring this out.
0564 
0565     int i = currentIndex();
0566     if (i < 0) {
0567         return std::nullopt;
0568     }
0569     // loop over the tabs starting from the current one
0570     do {
0571         const auto tabPage = tabPageAt(i);
0572         if (tabPage->primaryViewContainer()->url().isParentOf(item)) {
0573             const KFileItem fileItemContainingItem = tabPage->primaryViewContainer()->view()->items().findByUrl(dirContainingItem);
0574             if (!fileItemContainingItem.isNull() && tabPage->primaryViewContainer()->view()->isExpanded(fileItemContainingItem)) {
0575                 return std::optional(ViewIndex{i, true});
0576             }
0577         }
0578 
0579         if (tabPage->splitViewEnabled() && tabPage->secondaryViewContainer()->url().isParentOf(item)) {
0580             const KFileItem fileItemContainingItem = tabPage->secondaryViewContainer()->view()->items().findByUrl(dirContainingItem);
0581             if (!fileItemContainingItem.isNull() && tabPage->secondaryViewContainer()->view()->isExpanded(fileItemContainingItem)) {
0582                 return std::optional(ViewIndex{i, false});
0583             }
0584         }
0585 
0586         i = (i + 1) % count();
0587     } while (i != currentIndex());
0588 
0589     return std::nullopt;
0590 }
0591 
0592 #include "moc_dolphintabwidget.cpp"