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);
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()
}
}
-void DolphinTabWidget::openNewTab(const QUrl& primaryUrl, const QUrl& secondaryUrl)
+void DolphinTabWidget::openNewTab(const QUrl& primaryUrl, const QUrl& secondaryUrl, DolphinTabWidget::NewTabPosition position)
{
QWidget* focusWidget = QApplication::focusWidget();
this, &DolphinTabWidget::activeViewChanged);
connect(tabPage, &DolphinTabPage::activeViewUrlChanged,
this, &DolphinTabWidget::tabUrlChanged);
+ connect(tabPage->activeViewContainer(), &DolphinViewContainer::captionChanged, this, [this, tabPage]() {
+ const int tabIndex = indexOf(tabPage);
+ Q_ASSERT(tabIndex >= 0);
+ tabBar()->setTabText(tabIndex, tabName(tabPage));
+ });
+
+ if (position == NewTabPosition::FollowSetting) {
+ if (GeneralSettings::openNewTabAfterLastTab()) {
+ position = NewTabPosition::AtEnd;
+ } else {
+ position = NewTabPosition::AfterCurrent;
+ }
+ }
+
int newTabIndex = -1;
- if (!GeneralSettings::openNewTabAfterLastTab()) {
+ if (position == NewTabPosition::AfterCurrent || (position == NewTabPosition::FollowSetting && !GeneralSettings::openNewTabAfterLastTab())) {
newTabIndex = currentIndex() + 1;
}
+
insertTab(newTabIndex, tabPage, QIcon() /* loaded in tabInserted */, tabName(tabPage));
if (focusWidget) {
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;
+ const std::optional<ViewIndex> viewIndexAtDirectory = viewOpenAtDirectory(primaryUrl);
- // When the user asks for a URL that's already open, activate it instead
- // of opening a second copy
- if (index >= 0) {
+ // 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;
- activateTab(index);
- const auto tabPage = tabPageAt(index);
- if (isInPrimaryView) {
- tabPage->primaryViewContainer()->setActive(true);
- } else {
- tabPage->secondaryViewContainer()->setActive(true);
- }
- // BUG: 417230
- // Required for updateViewState() call in openFiles() to work as expected
- // If there is a selection, updateViewState() calls are effectively a no-op
- tabPage->activeViewContainer()->view()->clearSelection();
- } else if (splitView) {
+ activateViewContainerAt(viewIndexAtDirectory.value());
+ } else if (splitView && (it != dirs.constEnd())) {
const QUrl& secondaryUrl = *(it++);
if (somethingWasAlreadyOpen) {
openNewTab(primaryUrl, secondaryUrl);
{
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;
- for (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
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::statDetails(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);
+ }
+ });
+ }
}
}
tabBar()->setTabText(index, tabName(tabPageAt(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());
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.toDisplayString(QUrl::PreferLocalFile));
return name.replace('&', QLatin1String("&&"));
}
-QPair<int, bool> DolphinTabWidget::indexByUrl(const QUrl& url) const
+DolphinViewContainer *DolphinTabWidget::viewContainerAt(DolphinTabWidget::ViewIndex viewIndex) const
{
- for (int i = 0; i < count(); i++) {
+ 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;
+}
+
+const std::optional<const DolphinTabWidget::ViewIndex> DolphinTabWidget::viewOpenAtDirectory(const QUrl& directory) const
+{
+ 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;
}
- return qMakePair(-1, false);
+
+ // 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;
+ }
+ // 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;
}