File indexing completed on 2024-03-24 17:22:46

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