]> cloud.milkyroute.net Git - dolphin.git/blob - src/dolphintabwidget.cpp
Add margins to the zoom menu entry
[dolphin.git] / src / dolphintabwidget.cpp
1 /*
2 * SPDX-FileCopyrightText: 2014 Emmanuel Pescosta <emmanuelpescosta099@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 #include "dolphintabwidget.h"
8
9 #include "dolphin_generalsettings.h"
10 #include "dolphintabbar.h"
11 #include "dolphinviewcontainer.h"
12 #include "views/draganddrophelper.h"
13
14 #include <KAcceleratorManager>
15 #include <KConfigGroup>
16 #include <KIO/CommandLauncherJob>
17 #include <KLocalizedString>
18 #include <KShell>
19 #include <KStringHandler>
20 #include <kio/global.h>
21
22 #include <QApplication>
23 #include <QDropEvent>
24 #include <QStackedWidget>
25
26 DolphinTabWidget::DolphinTabWidget(DolphinNavigatorsWidgetAction *navigatorsWidget, QWidget *parent)
27 : QTabWidget(parent)
28 , m_lastViewedTab(nullptr)
29 , m_navigatorsWidget{navigatorsWidget}
30 {
31 KAcceleratorManager::setNoAccel(this);
32
33 connect(this, &DolphinTabWidget::tabCloseRequested, this, QOverload<int>::of(&DolphinTabWidget::closeTab));
34 connect(this, &DolphinTabWidget::currentChanged, this, &DolphinTabWidget::currentTabChanged);
35
36 DolphinTabBar *tabBar = new DolphinTabBar(this);
37 connect(tabBar, &DolphinTabBar::openNewActivatedTab, this, QOverload<int>::of(&DolphinTabWidget::openNewActivatedTab));
38 connect(tabBar, &DolphinTabBar::tabDragMoveEvent, this, &DolphinTabWidget::tabDragMoveEvent);
39 connect(tabBar, &DolphinTabBar::tabDropEvent, this, &DolphinTabWidget::tabDropEvent);
40 connect(tabBar, &DolphinTabBar::tabDetachRequested, this, &DolphinTabWidget::detachTab);
41 connect(tabBar, &DolphinTabBar::tabRenamed, this, &DolphinTabWidget::renameTab);
42
43 setTabBar(tabBar);
44 setDocumentMode(true);
45 setElideMode(Qt::ElideRight);
46 setUsesScrollButtons(true);
47 setTabBarAutoHide(true);
48
49 auto stackWidget{findChild<QStackedWidget *>()};
50 // 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
51 // 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
52 // mode bars, so calling it just a "View" is a bit wrong, but hopefully still gets the point across.
53 stackWidget->setAccessibleName(i18nc("accessible name of Dolphin's view container", "Location View")); // Without this call, the non-descript Qt provided
54 // "Layered Pane" role is announced.
55 }
56
57 DolphinTabPage *DolphinTabWidget::currentTabPage() const
58 {
59 return tabPageAt(currentIndex());
60 }
61
62 DolphinTabPage *DolphinTabWidget::nextTabPage() const
63 {
64 const int index = currentIndex() + 1;
65 return tabPageAt(index < count() ? index : 0);
66 }
67
68 DolphinTabPage *DolphinTabWidget::prevTabPage() const
69 {
70 const int index = currentIndex() - 1;
71 return tabPageAt(index >= 0 ? index : (count() - 1));
72 }
73
74 DolphinTabPage *DolphinTabWidget::tabPageAt(const int index) const
75 {
76 return static_cast<DolphinTabPage *>(widget(index));
77 }
78
79 void DolphinTabWidget::saveProperties(KConfigGroup &group) const
80 {
81 const int tabCount = count();
82 group.writeEntry("Tab Count", tabCount);
83 group.writeEntry("Active Tab Index", currentIndex());
84
85 for (int i = 0; i < tabCount; ++i) {
86 const DolphinTabPage *tabPage = tabPageAt(i);
87 group.writeEntry("Tab Data " % QString::number(i), tabPage->saveState());
88 }
89 }
90
91 void DolphinTabWidget::readProperties(const KConfigGroup &group)
92 {
93 const int tabCount = group.readEntry("Tab Count", 0);
94 for (int i = 0; i < tabCount; ++i) {
95 if (i >= count()) {
96 openNewActivatedTab();
97 }
98 const QByteArray state = group.readEntry("Tab Data " % QString::number(i), QByteArray());
99 tabPageAt(i)->restoreState(state);
100 }
101
102 const int index = group.readEntry("Active Tab Index", 0);
103 setCurrentIndex(index);
104 }
105
106 void DolphinTabWidget::refreshViews()
107 {
108 // Left-elision is better when showing full paths, since you care most
109 // about the current directory which is on the right
110 if (GeneralSettings::showFullPathInTitlebar()) {
111 setElideMode(Qt::ElideLeft);
112 } else {
113 setElideMode(Qt::ElideRight);
114 }
115
116 const int tabCount = count();
117 for (int i = 0; i < tabCount; ++i) {
118 updateTabName(i);
119 tabPageAt(i)->refreshViews();
120 }
121 }
122
123 void DolphinTabWidget::updateTabName(int index)
124 {
125 Q_ASSERT(index >= 0);
126
127 if (!tabPageAt(index)->customLabel().isEmpty()) {
128 QString name = tabPageAt(index)->customLabel();
129 tabBar()->setTabText(index, name);
130 return;
131 }
132
133 tabBar()->setTabText(index, tabName(tabPageAt(index)));
134 }
135
136 bool DolphinTabWidget::isUrlOpen(const QUrl &url) const
137 {
138 return viewOpenAtDirectory(url).has_value();
139 }
140
141 bool DolphinTabWidget::isItemVisibleInAnyView(const QUrl &urlOfItem) const
142 {
143 return viewShowingItem(urlOfItem).has_value();
144 }
145
146 void DolphinTabWidget::openNewActivatedTab()
147 {
148 std::unique_ptr<DolphinUrlNavigator::VisualState> oldNavigatorState;
149 if (currentTabPage()->primaryViewActive() || !m_navigatorsWidget->secondaryUrlNavigator()) {
150 oldNavigatorState = m_navigatorsWidget->primaryUrlNavigator()->visualState();
151 } else {
152 oldNavigatorState = m_navigatorsWidget->secondaryUrlNavigator()->visualState();
153 }
154
155 const DolphinViewContainer *oldActiveViewContainer = currentTabPage()->activeViewContainer();
156 Q_ASSERT(oldActiveViewContainer);
157
158 openNewActivatedTab(oldActiveViewContainer->url());
159
160 DolphinViewContainer *newActiveViewContainer = currentTabPage()->activeViewContainer();
161 Q_ASSERT(newActiveViewContainer);
162
163 // The URL navigator of the new tab should have the same editable state
164 // as the current tab
165 newActiveViewContainer->urlNavigator()->setVisualState(*oldNavigatorState.get());
166
167 // Always focus the new tab's view
168 newActiveViewContainer->view()->setFocus();
169 }
170
171 void DolphinTabWidget::openNewActivatedTab(const QUrl &primaryUrl, const QUrl &secondaryUrl)
172 {
173 openNewTab(primaryUrl, secondaryUrl);
174 if (GeneralSettings::openNewTabAfterLastTab()) {
175 setCurrentIndex(count() - 1);
176 } else {
177 setCurrentIndex(currentIndex() + 1);
178 }
179 }
180
181 DolphinTabPage *DolphinTabWidget::openNewTab(const QUrl &primaryUrl, const QUrl &secondaryUrl, DolphinTabWidget::NewTabPosition position)
182 {
183 QWidget *focusWidget = QApplication::focusWidget();
184
185 DolphinTabPage *tabPage = new DolphinTabPage(primaryUrl, secondaryUrl, this);
186 tabPage->setActive(false);
187 connect(tabPage, &DolphinTabPage::activeViewChanged, this, &DolphinTabWidget::activeViewChanged);
188 connect(tabPage, &DolphinTabPage::activeViewUrlChanged, this, &DolphinTabWidget::tabUrlChanged);
189 connect(tabPage->activeViewContainer(), &DolphinViewContainer::captionChanged, this, [this, tabPage]() {
190 updateTabName(indexOf(tabPage));
191 });
192
193 if (position == NewTabPosition::FollowSetting) {
194 if (GeneralSettings::openNewTabAfterLastTab()) {
195 position = NewTabPosition::AtEnd;
196 } else {
197 position = NewTabPosition::AfterCurrent;
198 }
199 }
200
201 int newTabIndex = -1;
202 if (position == NewTabPosition::AfterCurrent || (position == NewTabPosition::FollowSetting && !GeneralSettings::openNewTabAfterLastTab())) {
203 newTabIndex = currentIndex() + 1;
204 }
205
206 insertTab(newTabIndex, tabPage, QIcon() /* loaded in tabInserted */, tabName(tabPage));
207
208 if (focusWidget) {
209 // The DolphinViewContainer grabbed the keyboard focus. As the tab is opened
210 // in background, assure that the previous focused widget gets the focus back.
211 focusWidget->setFocus();
212 }
213 return tabPage;
214 }
215
216 void DolphinTabWidget::openDirectories(const QList<QUrl> &dirs, bool splitView)
217 {
218 Q_ASSERT(dirs.size() > 0);
219
220 bool somethingWasAlreadyOpen = false;
221
222 QList<QUrl>::const_iterator it = dirs.constBegin();
223 while (it != dirs.constEnd()) {
224 const QUrl &primaryUrl = *(it++);
225 const std::optional<ViewIndex> viewIndexAtDirectory = viewOpenAtDirectory(primaryUrl);
226
227 // When the user asks for a URL that's already open,
228 // activate it instead of opening a new tab
229 if (viewIndexAtDirectory.has_value()) {
230 somethingWasAlreadyOpen = true;
231 activateViewContainerAt(viewIndexAtDirectory.value());
232 } else if (splitView && (it != dirs.constEnd())) {
233 const QUrl &secondaryUrl = *(it++);
234 if (somethingWasAlreadyOpen) {
235 openNewTab(primaryUrl, secondaryUrl);
236 } else {
237 openNewActivatedTab(primaryUrl, secondaryUrl);
238 }
239 } else {
240 if (somethingWasAlreadyOpen) {
241 openNewTab(primaryUrl);
242 } else {
243 openNewActivatedTab(primaryUrl);
244 }
245 }
246 }
247 }
248
249 void DolphinTabWidget::openFiles(const QList<QUrl> &files, bool splitView)
250 {
251 Q_ASSERT(files.size() > 0);
252
253 // Get all distinct directories from 'files'.
254 QList<QUrl> dirsThatNeedToBeOpened;
255 QList<QUrl> dirsThatWereAlreadyOpen;
256 for (const QUrl &file : files) {
257 const QUrl dir(file.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash));
258 if (dirsThatNeedToBeOpened.contains(dir) || dirsThatWereAlreadyOpen.contains(dir)) {
259 continue;
260 }
261
262 // The selecting of files that we do later will not work in views that already have items selected.
263 // So we check if dir is already open and clear the selection if it is. BUG: 417230
264 // We also make sure the view will be activated.
265 auto viewIndex = viewShowingItem(file);
266 if (viewIndex.has_value()) {
267 viewContainerAt(viewIndex.value())->view()->clearSelection();
268 activateViewContainerAt(viewIndex.value());
269 dirsThatWereAlreadyOpen.append(dir);
270 } else {
271 dirsThatNeedToBeOpened.append(dir);
272 }
273 }
274
275 const int oldTabCount = count();
276 // Open a tab for each directory. If the "split view" option is enabled,
277 // two directories are shown inside one tab (see openDirectories()).
278 if (dirsThatNeedToBeOpened.size() > 0) {
279 openDirectories(dirsThatNeedToBeOpened, splitView);
280 }
281 const int tabCount = count();
282
283 // Select the files. Although the files can be split between several
284 // tabs, there is no need to split 'files' accordingly, as
285 // the DolphinView will just ignore invalid selections.
286 for (int i = 0; i < tabCount; ++i) {
287 DolphinTabPage *tabPage = tabPageAt(i);
288 tabPage->markUrlsAsSelected(files);
289 tabPage->markUrlAsCurrent(files.first());
290 if (i < oldTabCount) {
291 // Force selection of file if directory was already open, BUG: 417230
292 tabPage->activeViewContainer()->view()->updateViewState();
293 }
294 }
295 }
296
297 void DolphinTabWidget::closeTab()
298 {
299 closeTab(currentIndex());
300 }
301
302 void DolphinTabWidget::closeTab(const int index)
303 {
304 Q_ASSERT(index >= 0);
305 Q_ASSERT(index < count());
306
307 if (count() < 2) {
308 // Close Dolphin when closing the last tab.
309 parentWidget()->close();
310 return;
311 }
312
313 DolphinTabPage *tabPage = tabPageAt(index);
314 Q_EMIT rememberClosedTab(tabPage->activeViewContainer()->url(), tabPage->saveState());
315
316 removeTab(index);
317 tabPage->deleteLater();
318 }
319
320 void DolphinTabWidget::activateTab(const int index)
321 {
322 if (index < count()) {
323 setCurrentIndex(index);
324 }
325 }
326
327 void DolphinTabWidget::activateLastTab()
328 {
329 setCurrentIndex(count() - 1);
330 }
331
332 void DolphinTabWidget::activateNextTab()
333 {
334 const int index = currentIndex() + 1;
335 setCurrentIndex(index < count() ? index : 0);
336 }
337
338 void DolphinTabWidget::activatePrevTab()
339 {
340 const int index = currentIndex() - 1;
341 setCurrentIndex(index >= 0 ? index : (count() - 1));
342 }
343
344 void DolphinTabWidget::restoreClosedTab(const QByteArray &state)
345 {
346 openNewActivatedTab();
347 currentTabPage()->restoreState(state);
348 }
349
350 void DolphinTabWidget::copyToInactiveSplitView()
351 {
352 const DolphinTabPage *tabPage = currentTabPage();
353 if (!tabPage->splitViewEnabled()) {
354 return;
355 }
356
357 const KFileItemList selectedItems = tabPage->activeViewContainer()->view()->selectedItems();
358 if (selectedItems.isEmpty()) {
359 return;
360 }
361
362 DolphinView *const inactiveView = tabPage->inactiveViewContainer()->view();
363 inactiveView->copySelectedItems(selectedItems, inactiveView->url());
364 }
365
366 void DolphinTabWidget::moveToInactiveSplitView()
367 {
368 const DolphinTabPage *tabPage = currentTabPage();
369 if (!tabPage->splitViewEnabled()) {
370 return;
371 }
372
373 const KFileItemList selectedItems = tabPage->activeViewContainer()->view()->selectedItems();
374 if (selectedItems.isEmpty()) {
375 return;
376 }
377
378 DolphinView *const inactiveView = tabPage->inactiveViewContainer()->view();
379 inactiveView->moveSelectedItems(selectedItems, inactiveView->url());
380 }
381
382 void DolphinTabWidget::detachTab(int index)
383 {
384 Q_ASSERT(index >= 0);
385
386 QStringList args;
387
388 const DolphinTabPage *tabPage = tabPageAt(index);
389 args << tabPage->primaryViewContainer()->url().url();
390 if (tabPage->splitViewEnabled()) {
391 args << tabPage->secondaryViewContainer()->url().url();
392 args << QStringLiteral("--split");
393 }
394 args << QStringLiteral("--new-window");
395
396 KIO::CommandLauncherJob *job = new KIO::CommandLauncherJob("dolphin", args, this);
397 job->setDesktopName(QStringLiteral("org.kde.dolphin"));
398 job->start();
399
400 closeTab(index);
401 }
402
403 void DolphinTabWidget::openNewActivatedTab(int index)
404 {
405 Q_ASSERT(index >= 0);
406 const DolphinTabPage *tabPage = tabPageAt(index);
407 openNewActivatedTab(tabPage->activeViewContainer()->url());
408 }
409
410 void DolphinTabWidget::tabDragMoveEvent(int index, QDragMoveEvent *event)
411 {
412 if (index >= 0) {
413 DolphinView *view = tabPageAt(index)->activeViewContainer()->view();
414 DragAndDropHelper::updateDropAction(event, view->url());
415 }
416 }
417
418 void DolphinTabWidget::tabDropEvent(int index, QDropEvent *event)
419 {
420 if (index >= 0) {
421 DolphinView *view = tabPageAt(index)->activeViewContainer()->view();
422 view->dropUrls(view->url(), event, view);
423 } else {
424 const auto urls = event->mimeData()->urls();
425
426 for (const QUrl &url : urls) {
427 auto *job = KIO::stat(url, KIO::StatJob::SourceSide, KIO::StatDetail::StatBasic, KIO::JobFlag::HideProgressInfo);
428 connect(job, &KJob::result, this, [this, job]() {
429 if (!job->error() && job->statResult().isDir()) {
430 openNewTab(job->url(), QUrl(), NewTabPosition::AtEnd);
431 }
432 });
433 }
434 }
435 }
436
437 void DolphinTabWidget::tabUrlChanged(const QUrl &url)
438 {
439 const int index = indexOf(qobject_cast<QWidget *>(sender()));
440 if (index >= 0) {
441 updateTabName(index);
442 tabBar()->setTabToolTip(index, url.toDisplayString(QUrl::PreferLocalFile));
443 if (tabBar()->isVisible()) {
444 // ensure the path url ends with a slash to have proper folder icon for remote folders
445 const QUrl pathUrl = QUrl(url.adjusted(QUrl::StripTrailingSlash).toString(QUrl::FullyEncoded).append("/"));
446 tabBar()->setTabIcon(index, QIcon::fromTheme(KIO::iconNameForUrl(pathUrl)));
447 } else {
448 // Mark as dirty, actually load once the tab bar actually gets shown
449 tabBar()->setTabIcon(index, QIcon());
450 }
451
452 // Emit the currentUrlChanged signal if the url of the current tab has been changed.
453 if (index == currentIndex()) {
454 Q_EMIT currentUrlChanged(url);
455 }
456
457 Q_EMIT urlChanged(url);
458 }
459 }
460
461 void DolphinTabWidget::currentTabChanged(int index)
462 {
463 DolphinTabPage *tabPage = tabPageAt(index);
464 if (tabPage == m_lastViewedTab) {
465 return;
466 }
467 if (m_lastViewedTab) {
468 m_lastViewedTab->disconnectNavigators();
469 m_lastViewedTab->setActive(false);
470 }
471 if (tabPage->splitViewEnabled() && !m_navigatorsWidget->secondaryUrlNavigator()) {
472 m_navigatorsWidget->createSecondaryUrlNavigator();
473 }
474 DolphinViewContainer *viewContainer = tabPage->activeViewContainer();
475 Q_EMIT activeViewChanged(viewContainer);
476 Q_EMIT currentUrlChanged(viewContainer->url());
477 tabPage->setActive(true);
478 tabPage->connectNavigators(m_navigatorsWidget);
479 m_navigatorsWidget->setSecondaryNavigatorVisible(tabPage->splitViewEnabled());
480 m_lastViewedTab = tabPage;
481 }
482
483 void DolphinTabWidget::renameTab(int index, const QString &name)
484 {
485 tabPageAt(index)->setCustomLabel(name);
486 updateTabName(index);
487 }
488
489 void DolphinTabWidget::tabInserted(int index)
490 {
491 QTabWidget::tabInserted(index);
492
493 if (tabBar()->isVisible()) {
494 // Resolve all pending tab icons
495 for (int i = 0; i < count(); ++i) {
496 const QUrl url = tabPageAt(i)->activeViewContainer()->url();
497 if (tabBar()->tabIcon(i).isNull()) {
498 // ensure the path url ends with a slash to have proper folder icon for remote folders
499 const QUrl pathUrl = QUrl(url.adjusted(QUrl::StripTrailingSlash).toString(QUrl::FullyEncoded).append("/"));
500 tabBar()->setTabIcon(i, QIcon::fromTheme(KIO::iconNameForUrl(pathUrl)));
501 }
502 if (tabBar()->tabToolTip(i).isEmpty()) {
503 tabBar()->setTabToolTip(index, url.toDisplayString(QUrl::PreferLocalFile));
504 }
505 }
506 }
507
508 Q_EMIT tabCountChanged(count());
509 }
510
511 void DolphinTabWidget::tabRemoved(int index)
512 {
513 QTabWidget::tabRemoved(index);
514
515 Q_EMIT tabCountChanged(count());
516 }
517
518 QString DolphinTabWidget::tabName(DolphinTabPage *tabPage) const
519 {
520 if (!tabPage) {
521 return QString();
522 }
523 // clang-format off
524 QString name;
525 if (tabPage->splitViewEnabled()) {
526 if (tabPage->primaryViewActive()) {
527 // 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
528 // left in the tab name. In right to left languages the primary view would be on the right so the tab name should match.
529 name = i18nc("@title:tab Active primary view | (Inactive secondary view)", "%1 | (%2)", tabPage->primaryViewContainer()->caption(), tabPage->secondaryViewContainer()->caption());
530 } else {
531 // 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
532 // left in the tab name. In right to left languages the primary view would be on the right so the tab name should match.
533 name = i18nc("@title:tab (Inactive primary view) | Active secondary view", "(%1) | %2", tabPage->primaryViewContainer()->caption(), tabPage->secondaryViewContainer()->caption());
534 }
535 } else {
536 name = tabPage->activeViewContainer()->caption();
537 }
538 // clang-format on
539
540 // Make sure that a '&' inside the directory name is displayed correctly
541 // and not misinterpreted as a keyboard shortcut in QTabBar::setTabText()
542 return KStringHandler::rsqueeze(name.replace('&', QLatin1String("&&")), 40 /* default maximum visible folder name visible */);
543 }
544
545 DolphinViewContainer *DolphinTabWidget::viewContainerAt(DolphinTabWidget::ViewIndex viewIndex) const
546 {
547 const auto tabPage = tabPageAt(viewIndex.tabIndex);
548 if (!tabPage) {
549 return nullptr;
550 }
551 return viewIndex.isInPrimaryView ? tabPage->primaryViewContainer() : tabPage->secondaryViewContainer();
552 }
553
554 DolphinViewContainer *DolphinTabWidget::activateViewContainerAt(DolphinTabWidget::ViewIndex viewIndex)
555 {
556 activateTab(viewIndex.tabIndex);
557 auto viewContainer = viewContainerAt(viewIndex);
558 if (!viewContainer) {
559 return nullptr;
560 }
561 viewContainer->setActive(true);
562 return viewContainer;
563 }
564
565 const std::optional<const DolphinTabWidget::ViewIndex> DolphinTabWidget::viewOpenAtDirectory(const QUrl &directory) const
566 {
567 int i = currentIndex();
568 if (i < 0) {
569 return std::nullopt;
570 }
571 // loop over the tabs starting from the current one
572 do {
573 const auto tabPage = tabPageAt(i);
574 if (tabPage->primaryViewContainer()->url() == directory) {
575 return std::optional(ViewIndex{i, true});
576 }
577
578 if (tabPage->splitViewEnabled() && tabPage->secondaryViewContainer()->url() == directory) {
579 return std::optional(ViewIndex{i, false});
580 }
581
582 i = (i + 1) % count();
583 } while (i != currentIndex());
584
585 return std::nullopt;
586 }
587
588 const std::optional<const DolphinTabWidget::ViewIndex> DolphinTabWidget::viewShowingItem(const QUrl &item) const
589 {
590 // The item might not be loaded yet even though it exists. So instead
591 // we check if the folder containing the item is showing its contents.
592 const QUrl dirContainingItem(item.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash));
593
594 // The dirContainingItem is either open directly or expanded in a tree-style view mode.
595 // Is dirContainingitem the base url of a view?
596 auto viewOpenAtContainingDirectory = viewOpenAtDirectory(dirContainingItem);
597 if (viewOpenAtContainingDirectory.has_value()) {
598 return viewOpenAtContainingDirectory;
599 }
600
601 // Is dirContainingItem expanded in some tree-style view?
602 // The rest of this method is about figuring this out.
603
604 int i = currentIndex();
605 if (i < 0) {
606 return std::nullopt;
607 }
608 // loop over the tabs starting from the current one
609 do {
610 const auto tabPage = tabPageAt(i);
611 if (tabPage->primaryViewContainer()->url().isParentOf(item)) {
612 const KFileItem fileItemContainingItem = tabPage->primaryViewContainer()->view()->items().findByUrl(dirContainingItem);
613 if (!fileItemContainingItem.isNull() && tabPage->primaryViewContainer()->view()->isExpanded(fileItemContainingItem)) {
614 return std::optional(ViewIndex{i, true});
615 }
616 }
617
618 if (tabPage->splitViewEnabled() && tabPage->secondaryViewContainer()->url().isParentOf(item)) {
619 const KFileItem fileItemContainingItem = tabPage->secondaryViewContainer()->view()->items().findByUrl(dirContainingItem);
620 if (!fileItemContainingItem.isNull() && tabPage->secondaryViewContainer()->view()->isExpanded(fileItemContainingItem)) {
621 return std::optional(ViewIndex{i, false});
622 }
623 }
624
625 i = (i + 1) % count();
626 } while (i != currentIndex());
627
628 return std::nullopt;
629 }
630
631 #include "moc_dolphintabwidget.cpp"