diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f64c36d4c5..5c9c8c95e0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -153,6 +153,7 @@ set(dolphin_SRCS dolphintabbar.cpp dolphinrecenttabsmenu.cpp dolphintabpage.cpp + dolphintabwidget.cpp filterbar/filterbar.cpp main.cpp panels/information/filemetadataconfigurationdialog.cpp diff --git a/src/dolphin.appdata.xml b/src/dolphin.appdata.xml index 958464976c..fe0c774e64 100644 --- a/src/dolphin.appdata.xml +++ b/src/dolphin.appdata.xml @@ -17,6 +17,7 @@ Dolphin Dolphin Dolphin + Dolphin Dolphin Dolphin Dolphin @@ -32,6 +33,7 @@ Dolphin Dolphin xxDolphinxx + Dolphin File Manager مدير ملفات Gestor de fitxers @@ -46,6 +48,7 @@ Gerente de File Gestore file Failų tvarkyklė + Filbehandler Dateipleger Bestandsbeheerder Zarządzanie plikami @@ -61,6 +64,7 @@ Filhanterare Програма для керування файлами xxFile Managerxx + 檔案管理員

Dolphin is a lightweight file manager. It has been designed with ease of use and simplicity in mind, while still allowing flexibility and customisation. This means that you can do your file management exactly the way you want to do it.

دولفين هو مدير ملفات خفيف. صُمِّم دولفين مع أخذ سهولة الاستخدام والبساطة بعين الاعتبار، مع السماح بالمرونة والتخصيص. يعني هذا أنه يمكنك إدارة ملفاتك كما تريد تماماً.

@@ -73,6 +77,7 @@

Dolphin est un gestionnaire de fichier léger. Il a été conçu en gardant à l'esprit la simplicité et l'aisance à l'usage, tout en permettant flexibilité et personnalisation. Cela signifie que vous pouvez gérer vos fichiers de la manière exacte que vous voulez.

Dolphin es un gerente de file legier. Il ha essite designate con facilitate de uso e simplicitate in le mente, mentre il permitte ancora flexibilitate e personalisation. Isto significa que tu pote facer le gerente de file exactemente como tu lo vole.

Dolphin è un gestore file leggero. È stato progettato per essere facile da utilizzare e pensando alla semplicità, garantendo al contempo flessibilità e personalizzazione. Ciò significa che puoi gestire i tuoi file come meglio desideri.

+

Dolphin er en lettvekts filbehandler. Den er laget for å være enkel og lett å bruke, samtidig som den er fleksibel og kan tilpasses. Det betyr at du kan utføre dine filbehandlingsoppgaver akkurat slik du vil gjøre det.

Dolphin is en slank Dateipleger. Dat wöör buut mit de Idee vun't eenfache Bedenen vör Ogen, bides dat liekers flexibel un topassbor wesen schull. Du kannst Dien Dateien also jüst so plegen, as Du dat wullt.

Dolphin is een lichtgewicht bestandsbeheerder. Het is ontworpen met gebruiksgemak en eenvoud in gedachte en staat toch flexibiliteit en aan te passen toe. Dit betekent dat u uw bestandsbeheer kunt doen precies op de manier zoals u dat wilt.

Dolphin jest lekkim programem do zarządzania plikami. Został on opracowany mając na uwadze łatwość i prostotę obsługi, zapewniając jednocześnie elastyczność i możliwość dostosowania. Oznacza to, że można urządzić zarządzanie plikami w dokładnie taki sposób w jaki jest to pożądane.

@@ -87,6 +92,7 @@

Dolphin är en lättviktig filhanterare. Den har konstruerats med användbarhet och enkelhet i åtanke, men ändå tillåta flexibilitet och anpassning. Det betyder att du kan hantera filer exakt på det sätt som du vill göra det.

Dolphin — невибаглива до ресурсів програма для керування файлами. Її створено простою у користуванні і гнучкою у налаштовуванні. Це означає, що ви можете зробити керування файлами саме таким, як вам потрібно.

xxDolphin is a lightweight file manager. It has been designed with ease of use and simplicity in mind, while still allowing flexibility and customisation. This means that you can do your file management exactly the way you want to do it.xx

+

Dolphin 是一套輕量級的檔案管理員。它設計的理念是易用與簡單,但仍然保持足夠的彈性。這表示您可以用您想要使用的方式來管理您的檔案。

Features:

المزايا:

Característiques:

@@ -101,6 +107,7 @@

Characteristicas:

Funzionalità:

Galimybės

+

Egenskaper:

Markmalen:

Mogelijkheden:

Możliwości:

@@ -116,6 +123,7 @@

Funktioner:

Можливості:

xxFeatures:xx

+

功能:

http://dolphin.kde.org/ diff --git a/src/dolphinmainwindow.cpp b/src/dolphinmainwindow.cpp index 104ade0360..21d1320201 100644 --- a/src/dolphinmainwindow.cpp +++ b/src/dolphinmainwindow.cpp @@ -26,7 +26,7 @@ #include "dolphincontextmenu.h" #include "dolphinnewfilemenu.h" #include "dolphinrecenttabsmenu.h" -#include "dolphintabbar.h" +#include "dolphintabwidget.h" #include "dolphinviewcontainer.h" #include "dolphintabpage.h" #include "panels/folders/folderspanel.h" @@ -98,11 +98,8 @@ namespace { DolphinMainWindow::DolphinMainWindow() : KXmlGuiWindow(0), m_newFileMenu(0), - m_tabBar(0), + m_tabWidget(0), m_activeViewContainer(0), - m_centralWidgetLayout(0), - m_tabIndex(-1), - m_viewTab(), m_actionHandler(0), m_remoteEncoding(0), m_settingsDialog(), @@ -135,6 +132,15 @@ DolphinMainWindow::DolphinMainWindow() : setAcceptDrops(true); + m_tabWidget = new DolphinTabWidget(this); + connect(m_tabWidget, SIGNAL(activeViewChanged(DolphinViewContainer*)), + this, SLOT(activeViewChanged(DolphinViewContainer*))); + connect(m_tabWidget, SIGNAL(tabCountChanged(int)), + this, SLOT(tabCountChanged(int))); + connect(m_tabWidget, SIGNAL(currentUrlChanged(KUrl)), + this, SLOT(setUrlAsCaption(KUrl))); + setCentralWidget(m_tabWidget); + setupActions(); m_actionHandler = new DolphinViewActionHandler(actionCollection(), this); @@ -145,30 +151,6 @@ DolphinMainWindow::DolphinMainWindow() : connect(this, &DolphinMainWindow::urlChanged, m_remoteEncoding, &DolphinRemoteEncoding::slotAboutToOpenUrl); - m_tabBar = new DolphinTabBar(this); - connect(m_tabBar, SIGNAL(currentChanged(int)), - this, SLOT(setActiveTab(int))); - connect(m_tabBar, SIGNAL(tabCloseRequested(int)), - this, SLOT(closeTab(int))); - connect(m_tabBar, SIGNAL(openNewActivatedTab(int)), - this, SLOT(openNewActivatedTab(int))); - connect(m_tabBar, SIGNAL(tabMoved(int,int)), - this, SLOT(slotTabMoved(int,int))); - connect(m_tabBar, SIGNAL(tabDropEvent(int,QDropEvent*)), - this, SLOT(tabDropEvent(int,QDropEvent*))); - connect(m_tabBar, SIGNAL(tabDetachRequested(int)), - this, SLOT(detachTab(int))); - - m_tabBar->blockSignals(true); // signals get unblocked after at least 2 tabs are open - m_tabBar->hide(); - - QWidget* centralWidget = new QWidget(this); - m_centralWidgetLayout = new QVBoxLayout(centralWidget); - m_centralWidgetLayout->setSpacing(0); - m_centralWidgetLayout->setMargin(0); - m_centralWidgetLayout->addWidget(m_tabBar); - - setCentralWidget(centralWidget); setupDockWidgets(); setupGUI(Keys | Save | Create | ToolBar); @@ -201,48 +183,12 @@ DolphinMainWindow::~DolphinMainWindow() void DolphinMainWindow::openDirectories(const QList& dirs) { - const bool hasSplitView = GeneralSettings::splitView(); - - // Open each directory inside a new tab. If the "split view" option has been enabled, - // always show two directories within one tab. - QList::const_iterator it = dirs.constBegin(); - while (it != dirs.constEnd()) { - const KUrl& primaryUrl = *(it++); - if (hasSplitView && (it != dirs.constEnd())) { - const KUrl& secondaryUrl = *(it++); - openNewTab(primaryUrl, secondaryUrl); - } else { - openNewTab(primaryUrl); - } - } + m_tabWidget->openDirectories(dirs); } void DolphinMainWindow::openFiles(const QList& files) { - if (files.isEmpty()) { - return; - } - - // 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 dirs; - foreach (const KUrl& url, files) { - const KUrl dir(url.directory()); - if (!dirs.contains(dir)) { - dirs.append(dir); - } - } - - openDirectories(dirs); - - // 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. - foreach (DolphinTabPage* tabPage, m_viewTab) { - tabPage->markUrlsAsSelected(files); - tabPage->markUrlAsCurrent(files.first()); - } + m_tabWidget->openFiles(files); } void DolphinMainWindow::showCommand(CommandType command) @@ -288,21 +234,13 @@ void DolphinMainWindow::changeUrl(const KUrl& url) return; } - DolphinViewContainer* view = activeViewContainer(); - if (view) { - view->setUrl(url); - updateEditActions(); - updatePasteAction(); - updateViewActions(); - updateGoActions(); - setUrlAsCaption(url); + m_activeViewContainer->setUrl(url); + updateEditActions(); + updatePasteAction(); + updateViewActions(); + updateGoActions(); - const QString iconName = KIO::iconNameForUrl(url); - m_tabBar->setTabIcon(m_tabIndex, QIcon::fromTheme(iconName)); - m_tabBar->setTabText(m_tabIndex, squeezedText(tabName(view->url()))); - - emit urlChanged(url); - } + emit urlChanged(url); } void DolphinMainWindow::slotTerminalDirectoryChanged(const KUrl& url) @@ -323,7 +261,7 @@ void DolphinMainWindow::slotSelectionChanged(const KFileItemList& selection) { updateEditActions(); - const int selectedUrlsCount = m_viewTab.at(m_tabIndex)->selectedItemsCount(); + const int selectedUrlsCount = m_tabWidget->currentTabPage()->selectedItemsCount(); QAction* compareFilesAction = actionCollection()->action("compare_files"); if (selectedUrlsCount == 2) { @@ -369,99 +307,19 @@ void DolphinMainWindow::openNewMainWindow() KRun::run("dolphin %u", KUrl::List(), this); } -void DolphinMainWindow::openNewTab() +void DolphinMainWindow::openNewActivatedTab() { - const bool isUrlEditable = m_activeViewContainer->urlNavigator()->isUrlEditable(); - - openNewTab(m_activeViewContainer->url()); - m_tabBar->setCurrentIndex(m_viewTab.count() - 1); - - // The URL navigator of the new tab should have the same editable state - // as the current tab - KUrlNavigator* navigator = m_activeViewContainer->urlNavigator(); - navigator->setUrlEditable(isUrlEditable); - - if (isUrlEditable) { - // If a new tab is opened and the URL is editable, assure that - // the user can edit the URL without manually setting the focus - navigator->setFocus(); - } + m_tabWidget->openNewActivatedTab(); } void DolphinMainWindow::openNewTab(const KUrl& primaryUrl, const KUrl& secondaryUrl) { - QWidget* focusWidget = QApplication::focusWidget(); - - DolphinTabPage* tabPage = new DolphinTabPage(primaryUrl, secondaryUrl, this); - m_viewTab.append(tabPage); - - connect(tabPage, SIGNAL(activeViewChanged()), - this, SLOT(activeViewChanged())); - - // The places-selector from the URL navigator should only be shown - // if the places dock is invisible - QDockWidget* placesDock = findChild("placesDock"); - const bool placesSelectorVisible = !placesDock || !placesDock->isVisible(); - tabPage->setPlacesSelectorVisible(placesSelectorVisible); - - DolphinViewContainer* primaryContainer = tabPage->primaryViewContainer(); - connectViewSignals(primaryContainer); - - if (tabPage->splitViewEnabled()) { - DolphinViewContainer* secondaryContainer = tabPage->secondaryViewContainer(); - connectViewSignals(secondaryContainer); - } - - tabPage->hide(); - - m_tabBar->addTab(QIcon::fromTheme(KIO::iconNameForUrl(primaryUrl)), - squeezedText(tabName(primaryUrl))); - - if (m_viewTab.count() > 1) { - actionCollection()->action("close_tab")->setEnabled(true); - actionCollection()->action("activate_prev_tab")->setEnabled(true); - actionCollection()->action("activate_next_tab")->setEnabled(true); - m_tabBar->show(); - m_tabBar->blockSignals(false); - } - - if (focusWidget) { - // The DolphinViewContainer grabbed the keyboard focus. As the tab is opened - // in background, assure that the previous focused widget gets the focus back. - focusWidget->setFocus(); - } + m_tabWidget->openNewTab(primaryUrl, secondaryUrl); } void DolphinMainWindow::openNewActivatedTab(const KUrl& primaryUrl, const KUrl& secondaryUrl) { - openNewTab(primaryUrl, secondaryUrl); - setActiveTab(m_viewTab.count() - 1); -} - -void DolphinMainWindow::openNewActivatedTab(int index) -{ - Q_ASSERT(index >= 0); - const DolphinTabPage* tabPage = m_viewTab.at(index); - openNewActivatedTab(tabPage->activeViewContainer()->url()); -} - -void DolphinMainWindow::activateNextTab() -{ - if (m_viewTab.count() >= 2) { - const int tabIndex = (m_tabBar->currentIndex() + 1) % m_tabBar->count(); - setActiveTab(tabIndex); - } -} - -void DolphinMainWindow::activatePrevTab() -{ - if (m_viewTab.count() >= 2) { - int tabIndex = m_tabBar->currentIndex() - 1; - if (tabIndex == -1) { - tabIndex = m_tabBar->count() - 1; - } - setActiveTab(tabIndex); - } + m_tabWidget->openNewActivatedTab(primaryUrl, secondaryUrl); } void DolphinMainWindow::openInNewTab() @@ -500,12 +358,6 @@ void DolphinMainWindow::showEvent(QShowEvent* event) { KXmlGuiWindow::showEvent(event); - if (!m_activeViewContainer && m_viewTab.count() > 0) { - // If we have no active view container yet, we set the primary view container - // of the first tab as active view container. - setActiveTab(0); - } - if (!event->spontaneous()) { m_activeViewContainer->view()->setFocus(); } @@ -521,7 +373,7 @@ void DolphinMainWindow::closeEvent(QCloseEvent* event) closedByUser = false; } - if (m_viewTab.count() > 1 && GeneralSettings::confirmClosingMultipleTabs() && closedByUser) { + if (m_tabWidget->count() > 1 && GeneralSettings::confirmClosingMultipleTabs() && closedByUser) { // Ask the user if he really wants to quit and close all tabs. // Open a confirmation dialog with 3 buttons: // KDialog::Yes -> Quit @@ -557,7 +409,7 @@ void DolphinMainWindow::closeEvent(QCloseEvent* event) break; case QDialogButtonBox::No: // Close only the current tab - closeTab(); + m_tabWidget->closeTab(); default: event->ignore(); return; @@ -572,32 +424,12 @@ void DolphinMainWindow::closeEvent(QCloseEvent* event) void DolphinMainWindow::saveProperties(KConfigGroup& group) { - const int tabCount = m_viewTab.count(); - group.writeEntry("Tab Count", tabCount); - group.writeEntry("Active Tab Index", m_tabBar->currentIndex()); - - for (int i = 0; i < tabCount; ++i) { - const DolphinTabPage* tabPage = m_viewTab.at(i); - group.writeEntry("Tab " % QString::number(i), tabPage->saveState()); - } + m_tabWidget->saveProperties(group); } void DolphinMainWindow::readProperties(const KConfigGroup& group) { - const int tabCount = group.readEntry("Tab Count", 1); - for (int i = 0; i < tabCount; ++i) { - const QByteArray state = group.readEntry("Tab " % QString::number(i), QByteArray()); - DolphinTabPage* tabPage = m_viewTab.at(i); - tabPage->restoreState(state); - - // openNewTab() needs to be called only tabCount - 1 times - if (i != tabCount - 1) { - openNewTab(); - } - } - - const int index = group.readEntry("Active Tab Index", 0); - m_tabBar->setCurrentIndex(index); + m_tabWidget->readProperties(group); } void DolphinMainWindow::updateNewMenu() @@ -701,13 +533,9 @@ void DolphinMainWindow::invertSelection() void DolphinMainWindow::toggleSplitView() { - DolphinTabPage* tabPage = m_viewTab.at(m_tabIndex); + DolphinTabPage* tabPage = m_tabWidget->currentTabPage(); tabPage->setSplitViewEnabled(!tabPage->splitViewEnabled()); - if (tabPage->splitViewEnabled()) { - connectViewSignals(tabPage->secondaryViewContainer()); - } - updateViewActions(); } @@ -770,14 +598,6 @@ void DolphinMainWindow::togglePanelLockState() GeneralSettings::setLockPanels(newLockState); } -void DolphinMainWindow::slotPlacesPanelVisibilityChanged(bool visible) -{ - foreach (DolphinTabPage* tabPage, m_viewTab) { - // The Places selector in the location bar should be shown if and only if the Places panel is hidden. - tabPage->setPlacesSelectorVisible(!visible); - } -} - void DolphinMainWindow::goBack() { KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); @@ -843,7 +663,7 @@ void DolphinMainWindow::goHome(Qt::MouseButtons buttons) void DolphinMainWindow::compareFiles() { - const KFileItemList items = m_viewTab.at(m_tabIndex)->selectedItems(); + const KFileItemList items = m_tabWidget->currentTabPage()->selectedItems(); if (items.count() != 2) { // The action is disabled in this case, but it could have been triggered // via D-Bus, see https://bugs.kde.org/show_bug.cgi?id=325517 @@ -905,111 +725,6 @@ void DolphinMainWindow::editSettings() } } -void DolphinMainWindow::setActiveTab(int index) -{ - Q_ASSERT(index >= 0); - Q_ASSERT(index < m_viewTab.count()); - if (index == m_tabIndex) { - return; - } - - m_tabBar->setCurrentIndex(index); - - // hide current tab content - if (m_tabIndex >= 0) { - DolphinTabPage* hiddenTabPage = m_viewTab.at(m_tabIndex); - hiddenTabPage->hide(); - m_centralWidgetLayout->removeWidget(hiddenTabPage); - } - - // show active tab content - m_tabIndex = index; - - DolphinTabPage* tabPage = m_viewTab.at(index); - m_centralWidgetLayout->addWidget(tabPage, 1); - tabPage->show(); - - setActiveViewContainer(tabPage->activeViewContainer()); -} - -void DolphinMainWindow::closeTab() -{ - closeTab(m_tabBar->currentIndex()); -} - -void DolphinMainWindow::closeTab(int index) -{ - Q_ASSERT(index >= 0); - Q_ASSERT(index < m_viewTab.count()); - if (m_viewTab.count() == 1) { - // the last tab may never get closed - return; - } - - if (index == m_tabIndex) { - // The tab that should be closed is the active tab. Activate the - // previous tab before closing the tab. - m_tabBar->setCurrentIndex((index > 0) ? index - 1 : 1); - } - - DolphinTabPage* tabPage = m_viewTab.at(index); - - if (tabPage->splitViewEnabled()) { - emit rememberClosedTab(tabPage->primaryViewContainer()->url(), - tabPage->secondaryViewContainer()->url()); - } else { - emit rememberClosedTab(tabPage->primaryViewContainer()->url(), KUrl()); - } - - // delete tab - m_viewTab.removeAt(index); - tabPage->deleteLater(); - - m_tabBar->blockSignals(true); - m_tabBar->removeTab(index); - - if (m_tabIndex > index) { - m_tabIndex--; - Q_ASSERT(m_tabIndex >= 0); - } - - // if only one tab is left, also remove the tab entry so that - // closing the last tab is not possible - if (m_viewTab.count() < 2) { - actionCollection()->action("close_tab")->setEnabled(false); - actionCollection()->action("activate_prev_tab")->setEnabled(false); - actionCollection()->action("activate_next_tab")->setEnabled(false); - m_tabBar->hide(); - } else { - m_tabBar->blockSignals(false); - } -} - -void DolphinMainWindow::detachTab(int index) -{ - Q_ASSERT(index >= 0); - - const QString separator(QLatin1Char(' ')); - QString command = QLatin1String("dolphin"); - - const DolphinTabPage* tabPage = m_viewTab.at(index); - command += separator + tabPage->primaryViewContainer()->url().url(); - if (tabPage->splitViewEnabled()) { - command += separator + tabPage->secondaryViewContainer()->url().url(); - command += separator + QLatin1String("-split"); - } - - KRun::runCommand(command, this); - - closeTab(index); -} - -void DolphinMainWindow::slotTabMoved(int from, int to) -{ - m_viewTab.move(from, to); - m_tabIndex = m_tabBar->currentIndex(); -} - void DolphinMainWindow::handleUrl(const KUrl& url) { delete m_lastHandleUrlStatJob; @@ -1043,20 +758,6 @@ void DolphinMainWindow::slotHandleUrlStatFinished(KJob* job) } } -void DolphinMainWindow::tabDropEvent(int tab, QDropEvent* event) -{ - const KUrl::List urls = KUrl::List::fromMimeData(event->mimeData()); - if (!urls.isEmpty() && tab != -1) { - const DolphinView* view = m_viewTab.at(tab)->activeViewContainer()->view(); - - QString error; - DragAndDropHelper::dropUrls(view->rootItem(), view->url(), event, error); - if (!error.isEmpty()) { - activeViewContainer()->showMessage(error, DolphinViewContainer::Error); - } - } -} - void DolphinMainWindow::slotWriteStateChanged(bool isFolderWritable) { newFileMenu()->setEnabled(isFolderWritable); @@ -1217,27 +918,28 @@ void DolphinMainWindow::slotPlaceActivated(const KUrl& url) } } -void DolphinMainWindow::activeViewChanged() -{ - const DolphinTabPage* tabPage = m_viewTab.at(m_tabIndex); - setActiveViewContainer(tabPage->activeViewContainer()); -} - void DolphinMainWindow::closedTabsCountChanged(unsigned int count) { actionCollection()->action("undo_close_tab")->setEnabled(count > 0); } -void DolphinMainWindow::setActiveViewContainer(DolphinViewContainer* viewContainer) +void DolphinMainWindow::activeViewChanged(DolphinViewContainer* viewContainer) { + DolphinViewContainer* oldViewContainer = m_activeViewContainer; Q_ASSERT(viewContainer); - Q_ASSERT((viewContainer == m_viewTab.at(m_tabIndex)->primaryViewContainer()) || - (viewContainer == m_viewTab.at(m_tabIndex)->secondaryViewContainer())); - if (m_activeViewContainer == viewContainer) { - return; - } m_activeViewContainer = viewContainer; + + if (oldViewContainer) { + // Disconnect all signals between the old view container (container, + // view and url navigator) and main window. + oldViewContainer->disconnect(this); + oldViewContainer->view()->disconnect(this); + oldViewContainer->urlNavigator()->disconnect(this); + } + + connectViewSignals(viewContainer); + m_actionHandler->setCurrentView(viewContainer->view()); updateHistory(); @@ -1246,14 +948,34 @@ void DolphinMainWindow::setActiveViewContainer(DolphinViewContainer* viewContain updateViewActions(); updateGoActions(); - const KUrl url = m_activeViewContainer->url(); - setUrlAsCaption(url); - m_tabBar->setTabText(m_tabIndex, squeezedText(tabName(url))); - m_tabBar->setTabIcon(m_tabIndex, QIcon::fromTheme(KIO::iconNameForUrl(url))); - + const KUrl url = viewContainer->url(); emit urlChanged(url); } +void DolphinMainWindow::tabCountChanged(int count) +{ + const bool enableTabActions = (count > 1); + actionCollection()->action("close_tab")->setEnabled(enableTabActions); + actionCollection()->action("activate_next_tab")->setEnabled(enableTabActions); + actionCollection()->action("activate_prev_tab")->setEnabled(enableTabActions); +} + +void DolphinMainWindow::setUrlAsCaption(const KUrl& url) +{ + QString caption; + if (!url.isLocalFile()) { + caption.append(url.protocol() + " - "); + if (url.hasHost()) { + caption.append(url.host() + " - "); + } + } + + const QString fileName = url.fileName().isEmpty() ? "/" : url.fileName(); + caption.append(fileName); + + setCaption(caption); +} + void DolphinMainWindow::setupActions() { // setup 'File' menu @@ -1275,14 +997,14 @@ void DolphinMainWindow::setupActions() newTab->setIcon(QIcon::fromTheme("tab-new")); newTab->setText(i18nc("@action:inmenu File", "New Tab")); newTab->setShortcuts(QList() << QKeySequence(Qt::CTRL | Qt::Key_T) << QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_N)); - connect(newTab, &QAction::triggered, this, static_cast(&DolphinMainWindow::openNewTab)); + connect(newTab, &QAction::triggered, this, static_cast(&DolphinMainWindow::openNewActivatedTab)); QAction* closeTab = actionCollection()->addAction("close_tab"); closeTab->setIcon(QIcon::fromTheme("tab-close")); closeTab->setText(i18nc("@action:inmenu File", "Close Tab")); closeTab->setShortcut(Qt::CTRL | Qt::Key_W); closeTab->setEnabled(false); - connect(closeTab, &QAction::triggered, this, static_cast(&DolphinMainWindow::closeTab)); + connect(closeTab, &QAction::triggered, m_tabWidget, static_cast(&DolphinTabWidget::closeTab)); KStandardAction::quit(this, SLOT(quit()), actionCollection()); @@ -1353,10 +1075,10 @@ void DolphinMainWindow::setupActions() DolphinRecentTabsMenu* recentTabsMenu = new DolphinRecentTabsMenu(this); actionCollection()->addAction("closed_tabs", recentTabsMenu); - connect(this, SIGNAL(rememberClosedTab(KUrl,KUrl)), - recentTabsMenu, SLOT(rememberClosedTab(KUrl,KUrl))); - connect(recentTabsMenu, SIGNAL(restoreClosedTab(KUrl,KUrl)), - this, SLOT(openNewActivatedTab(KUrl,KUrl))); + connect(m_tabWidget, SIGNAL(rememberClosedTab(KUrl,QByteArray)), + recentTabsMenu, SLOT(rememberClosedTab(KUrl,QByteArray))); + connect(recentTabsMenu, SIGNAL(restoreClosedTab(QByteArray)), + m_tabWidget, SLOT(restoreClosedTab(QByteArray))); connect(recentTabsMenu, SIGNAL(closedTabsCountChanged(uint)), this, SLOT(closedTabsCountChanged(uint))); @@ -1409,14 +1131,14 @@ void DolphinMainWindow::setupActions() activateNextTab->setIconText(i18nc("@action:inmenu", "Next Tab")); activateNextTab->setText(i18nc("@action:inmenu", "Activate Next Tab")); activateNextTab->setEnabled(false); - connect(activateNextTab, &QAction::triggered, this, &DolphinMainWindow::activateNextTab); + connect(activateNextTab, &QAction::triggered, m_tabWidget, &DolphinTabWidget::activateNextTab); activateNextTab->setShortcuts(QApplication::isRightToLeft() ? prevTabKeys : nextTabKeys); QAction* activatePrevTab = actionCollection()->addAction("activate_prev_tab"); activatePrevTab->setIconText(i18nc("@action:inmenu", "Previous Tab")); activatePrevTab->setText(i18nc("@action:inmenu", "Activate Previous Tab")); activatePrevTab->setEnabled(false); - connect(activatePrevTab, &QAction::triggered, this, &DolphinMainWindow::activatePrevTab); + connect(activatePrevTab, &QAction::triggered, m_tabWidget, &DolphinTabWidget::activatePrevTab); activatePrevTab->setShortcuts(QApplication::isRightToLeft() ? nextTabKeys : prevTabKeys); // for context menu @@ -1545,10 +1267,12 @@ void DolphinMainWindow::setupDockWidgets() connect(this, &DolphinMainWindow::urlChanged, placesPanel, &PlacesPanel::setUrl); connect(placesDock, &DolphinDockWidget::visibilityChanged, - this, &DolphinMainWindow::slotPlacesPanelVisibilityChanged); + m_tabWidget, &DolphinTabWidget::slotPlacesPanelVisibilityChanged); connect(this, &DolphinMainWindow::settingsChanged, placesPanel, &PlacesPanel::readSettings); + m_tabWidget->slotPlacesPanelVisibilityChanged(placesPanel->isVisible()); + // Add actions into the "Panels" menu KActionMenu* panelsMenu = new KActionMenu(i18nc("@action:inmenu View", "Panels"), this); actionCollection()->addAction("panels", panelsMenu); @@ -1671,15 +1395,13 @@ bool DolphinMainWindow::addActionToMenu(QAction* action, KMenu* menu) void DolphinMainWindow::refreshViews() { - foreach (DolphinTabPage* tabPage, m_viewTab) { - tabPage->refreshViews(); - } + m_tabWidget->refreshViews(); if (GeneralSettings::modifiedStartupSettings()) { // The startup settings have been changed by the user (see bug #254947). // Synchronize the split-view setting with the active view: const bool splitView = GeneralSettings::splitView(); - m_viewTab.at(m_tabIndex)->setSplitViewEnabled(splitView); + m_tabWidget->currentTabPage()->setSplitViewEnabled(splitView); updateSplitAction(); } @@ -1730,7 +1452,7 @@ void DolphinMainWindow::connectViewSignals(DolphinViewContainer* container) void DolphinMainWindow::updateSplitAction() { QAction* splitAction = actionCollection()->action("split_view"); - const DolphinTabPage* tabPage = m_viewTab.at(m_tabIndex); + const DolphinTabPage* tabPage = m_tabWidget->currentTabPage(); if (tabPage->splitViewEnabled()) { if (tabPage->primaryViewActive()) { splitAction->setText(i18nc("@action:intoolbar Close left view", "Close")); @@ -1748,24 +1470,6 @@ void DolphinMainWindow::updateSplitAction() } } -QString DolphinMainWindow::tabName(const KUrl& url) const -{ - QString name; - if (url.equals(KUrl("file:///"))) { - name = '/'; - } else { - name = url.fileName(); - if (name.isEmpty()) { - name = url.protocol(); - } else { - // Make sure that a '&' inside the directory name is displayed correctly - // and not misinterpreted as a keyboard shortcut in QTabBar::setTabText() - name.replace('&', "&&"); - } - } - return name; -} - bool DolphinMainWindow::isKompareInstalled() const { static bool initialized = false; @@ -1779,28 +1483,6 @@ bool DolphinMainWindow::isKompareInstalled() const return installed; } -void DolphinMainWindow::setUrlAsCaption(const KUrl& url) -{ - QString caption; - if (!url.isLocalFile()) { - caption.append(url.protocol() + " - "); - if (url.hasHost()) { - caption.append(url.host() + " - "); - } - } - - const QString fileName = url.fileName().isEmpty() ? "/" : url.fileName(); - caption.append(fileName); - - setCaption(caption); -} - -QString DolphinMainWindow::squeezedText(const QString& text) const -{ - const QFontMetrics fm = fontMetrics(); - return fm.elidedText(text, Qt::ElideMiddle, fm.maxWidth() * 10); -} - void DolphinMainWindow::createPanelAction(const QIcon& icon, const QKeySequence& shortcut, QAction* dockAction, diff --git a/src/dolphinmainwindow.h b/src/dolphinmainwindow.h index 50c8f8923f..fd231cf0b7 100644 --- a/src/dolphinmainwindow.h +++ b/src/dolphinmainwindow.h @@ -39,17 +39,14 @@ typedef KIO::FileUndoManager::CommandType CommandType; class DolphinViewActionHandler; class DolphinApplication; class DolphinSettingsDialog; -class DolphinTabBar; class DolphinViewContainer; class DolphinRemoteEncoding; -class DolphinTabPage; +class DolphinTabWidget; class KFileItem; class KFileItemList; class KJob; class KNewFileMenu; -class QSplitter; class QToolButton; -class QVBoxLayout; class QIcon; /** @@ -149,11 +146,6 @@ signals: */ void settingsChanged(); - /** - * Is emitted when a tab has been closed. - */ - void rememberClosedTab(const KUrl& primaryUrl, const KUrl& secondaryUrl); - protected: /** @see QWidget::showEvent() */ virtual void showEvent(QShowEvent* event); @@ -262,13 +254,6 @@ private slots: */ void togglePanelLockState(); - /** - * Is invoked if the Places panel got visible/invisible and takes care - * that the places-selector of all views is only shown if the Places panel - * is invisible. - */ - void slotPlacesPanelVisibilityChanged(bool visible); - /** Goes back one step of the URL history. */ void goBack(); @@ -343,8 +328,11 @@ private slots: /** Open a new main window. */ void openNewMainWindow(); - /** Opens a new view with the current URL that is part of a tab. */ - void openNewTab(); + /** + * Opens a new view with the current URL that is part of a tab and + * activates it. + */ + void openNewActivatedTab(); /** * Opens a new tab in the background showing the URL \a primaryUrl and the @@ -358,16 +346,6 @@ private slots: */ void openNewActivatedTab(const KUrl& primaryUrl, const KUrl& secondaryUrl = KUrl()); - /** - * Opens a new tab showing the url from tab at the given \a index and - * activates the tab. - */ - void openNewActivatedTab(int index); - - void activateNextTab(); - - void activatePrevTab(); - /** * Opens the selected folder in a new tab. */ @@ -384,33 +362,6 @@ private slots: */ void showCommand(CommandType command); - /** - * Activates the tab with the index \a index, which means that the current view - * is replaced by the view of the given tab. - */ - void setActiveTab(int index); - - /** Closes the currently active tab. */ - void closeTab(); - - /** - * Closes the tab with the index \a index and activates the tab with index - 1. - */ - void closeTab(int index); - - /** - * Opens the tab with the index \a index in a new Dolphin instance and closes - * this tab. - */ - void detachTab(int index); - - /** - * Is connected to the QTabBar signal tabMoved(int from, int to). - * Reorders the list of tabs after a tab was moved in the tab bar - * and sets m_tabIndex to the new index of the current tab. - */ - void slotTabMoved(int from, int to); - /** * If the URL can be listed, open it in the current view, otherwise * run it through KRun. @@ -423,12 +374,6 @@ private slots: */ void slotHandleUrlStatFinished(KJob* job); - /** - * Is connected to the KTabBar signal receivedDropEvent. - * Allows dragging and dropping files onto tabs. - */ - void tabDropEvent(int tab, QDropEvent* event); - /** * Is invoked when the write state of a folder has been changed and * enables/disables the "Create New..." menu entry. @@ -466,19 +411,31 @@ private slots: */ void slotPlaceActivated(const KUrl& url); - void activeViewChanged(); + /** + * Is called if the another view has been activated by changing the current + * tab or activating another view in split-view mode. + * + * Activates the given view, which means that all menu actions are applied + * to this view. When having a split view setup, the nonactive view is + * usually shown in darker colors. + */ + void activeViewChanged(DolphinViewContainer* viewContainer); void closedTabsCountChanged(unsigned int count); -private: /** - * Activates the given view, which means that - * all menu actions are applied to this view. When - * having a split view setup, the nonactive view - * is usually shown in darker colors. + * Is called if a new tab has been opened or a tab has been closed to + * enable/disable the tab actions. */ - void setActiveViewContainer(DolphinViewContainer* view); + void tabCountChanged(int count); + /** + * Sets the window caption to url.fileName() if this is non-empty, + * "/" if the URL is "file:///", and url.protocol() otherwise. + */ + void setUrlAsCaption(const KUrl& url); + +private: void setupActions(); void setupDockWidgets(); void updateEditActions(); @@ -511,20 +468,8 @@ private: */ void updateSplitAction(); - /** Returns the name of the tab for the URL \a url. */ - QString tabName(const KUrl& url) const; - - bool isKompareInstalled() const; - /** - * Sets the window caption to url.fileName() if this is non-empty, - * "/" if the URL is "file:///", and url.protocol() otherwise. - */ - void setUrlAsCaption(const KUrl& url); - - QString squeezedText(const QString& text) const; - /** * Creates an action for showing/hiding a panel, that is accessible * in "Configure toolbars..." and "Configure shortcuts...". This is necessary @@ -551,14 +496,10 @@ private: }; KNewFileMenu* m_newFileMenu; - DolphinTabBar* m_tabBar; + DolphinTabWidget* m_tabWidget; DolphinViewContainer* m_activeViewContainer; - QVBoxLayout* m_centralWidgetLayout; int m_id; - int m_tabIndex; - QList m_viewTab; - DolphinViewActionHandler* m_actionHandler; DolphinRemoteEncoding* m_remoteEncoding; QWeakPointer m_settingsDialog; diff --git a/src/dolphinrecenttabsmenu.cpp b/src/dolphinrecenttabsmenu.cpp index 0f90e634b3..5440a1b378 100644 --- a/src/dolphinrecenttabsmenu.cpp +++ b/src/dolphinrecenttabsmenu.cpp @@ -21,7 +21,7 @@ #include #include -#include +#include #include DolphinRecentTabsMenu::DolphinRecentTabsMenu(QObject* parent) : @@ -40,19 +40,14 @@ DolphinRecentTabsMenu::DolphinRecentTabsMenu(QObject* parent) : this, SLOT(handleAction(QAction*))); } -void DolphinRecentTabsMenu::rememberClosedTab(const KUrl& primaryUrl, const KUrl& secondaryUrl) +void DolphinRecentTabsMenu::rememberClosedTab(const KUrl& url, const QByteArray& state) { QAction* action = new QAction(menu()); - action->setText(primaryUrl.path()); - - const QString iconName = KMimeType::iconNameForUrl(primaryUrl); + action->setText(url.path()); + action->setData(state); + const QString iconName = KIO::iconNameForUrl(url); action->setIcon(QIcon::fromTheme(iconName)); - KUrl::List urls; - urls << primaryUrl; - urls << secondaryUrl; - action->setData(QVariant::fromValue(urls)); - // Add the closed tab menu entry after the separator and // "Empty Recently Closed Tabs" entry if (menu()->actions().size() == 2) { @@ -88,13 +83,11 @@ void DolphinRecentTabsMenu::handleAction(QAction* action) } emit closedTabsCountChanged(0); } else { - const KUrl::List urls = action->data().value(); + const QByteArray state = action->data().value(); removeAction(action); delete action; action = 0; - if (urls.count() == 2) { - emit restoreClosedTab(urls.first(), urls.last()); - } + emit restoreClosedTab(state); emit closedTabsCountChanged(menu()->actions().size() - 2); } diff --git a/src/dolphinrecenttabsmenu.h b/src/dolphinrecenttabsmenu.h index b5acc735e5..910e564a19 100644 --- a/src/dolphinrecenttabsmenu.h +++ b/src/dolphinrecenttabsmenu.h @@ -34,11 +34,11 @@ public: explicit DolphinRecentTabsMenu(QObject* parent); public slots: - void rememberClosedTab(const KUrl& primaryUrl, const KUrl& secondaryUrl); + void rememberClosedTab(const KUrl& url, const QByteArray& state); void undoCloseTab(); signals: - void restoreClosedTab(const KUrl& primaryUrl, const KUrl& secondaryUrl); + void restoreClosedTab(const QByteArray& state); void closedTabsCountChanged(unsigned int count); private slots: diff --git a/src/dolphintabpage.cpp b/src/dolphintabpage.cpp index 82be6d59c1..f7000ea668 100644 --- a/src/dolphintabpage.cpp +++ b/src/dolphintabpage.cpp @@ -41,6 +41,8 @@ DolphinTabPage::DolphinTabPage(const KUrl& primaryUrl, const KUrl& secondaryUrl, m_primaryViewContainer = createViewContainer(primaryUrl); connect(m_primaryViewContainer->view(), SIGNAL(urlChanged(KUrl)), this, SIGNAL(activeViewUrlChanged(KUrl))); + connect(m_primaryViewContainer->view(), SIGNAL(redirection(KUrl,KUrl)), + this, SLOT(slotViewUrlRedirection(KUrl,KUrl))); m_splitter->addWidget(m_primaryViewContainer); m_primaryViewContainer->show(); @@ -169,14 +171,18 @@ QByteArray DolphinTabPage::saveState() const QByteArray state; QDataStream stream(&state, QIODevice::WriteOnly); + stream << quint32(2); // Tab state version + stream << m_splitViewEnabled; stream << m_primaryViewContainer->url(); stream << m_primaryViewContainer->urlNavigator()->isUrlEditable(); + m_primaryViewContainer->view()->saveState(stream); if (m_splitViewEnabled) { stream << m_secondaryViewContainer->url(); stream << m_secondaryViewContainer->urlNavigator()->isUrlEditable(); + m_secondaryViewContainer->view()->saveState(stream); } stream << m_primaryViewActive; @@ -187,6 +193,62 @@ QByteArray DolphinTabPage::saveState() const void DolphinTabPage::restoreState(const QByteArray& state) { + if (state.isEmpty()) { + return; + } + + QByteArray sd = state; + QDataStream stream(&sd, QIODevice::ReadOnly); + + // Read the version number of the tab state and check if the version is supported. + quint32 version = 0; + stream >> version; + if (version != 2) { + // The version of the tab state isn't supported, we can't restore it. + return; + } + + bool isSplitViewEnabled = false; + stream >> isSplitViewEnabled; + setSplitViewEnabled(isSplitViewEnabled); + + KUrl primaryUrl; + stream >> primaryUrl; + m_primaryViewContainer->setUrl(primaryUrl); + bool primaryUrlEditable; + stream >> primaryUrlEditable; + m_primaryViewContainer->urlNavigator()->setUrlEditable(primaryUrlEditable); + m_primaryViewContainer->view()->restoreState(stream); + + if (isSplitViewEnabled) { + KUrl secondaryUrl; + stream >> secondaryUrl; + m_secondaryViewContainer->setUrl(secondaryUrl); + bool secondaryUrlEditable; + stream >> secondaryUrlEditable; + m_secondaryViewContainer->urlNavigator()->setUrlEditable(secondaryUrlEditable); + m_secondaryViewContainer->view()->restoreState(stream); + } + + stream >> m_primaryViewActive; + if (m_primaryViewActive) { + m_primaryViewContainer->setActive(true); + } else { + Q_ASSERT(m_splitViewEnabled); + m_secondaryViewContainer->setActive(true); + } + + QByteArray splitterState; + stream >> splitterState; + m_splitter->restoreState(splitterState); +} + +void DolphinTabPage::restoreStateV1(const QByteArray& state) +{ + if (state.isEmpty()) { + return; + } + QByteArray sd = state; QDataStream stream(&sd, QIODevice::ReadOnly); @@ -241,12 +303,23 @@ void DolphinTabPage::slotViewActivated() if (newActiveView != oldActiveView) { disconnect(oldActiveView, SIGNAL(urlChanged(KUrl)), this, SIGNAL(activeViewUrlChanged(KUrl))); + disconnect(oldActiveView, SIGNAL(redirection(KUrl,KUrl)), + this, SLOT(slotViewUrlRedirection(KUrl,KUrl))); connect(newActiveView, SIGNAL(urlChanged(KUrl)), this, SIGNAL(activeViewUrlChanged(KUrl))); + connect(newActiveView, SIGNAL(redirection(KUrl,KUrl)), + this, SLOT(slotViewUrlRedirection(KUrl,KUrl))); } emit activeViewUrlChanged(activeViewContainer()->url()); - emit activeViewChanged(); + emit activeViewChanged(activeViewContainer()); +} + +void DolphinTabPage::slotViewUrlRedirection(const KUrl& oldUrl, const KUrl& newUrl) +{ + Q_UNUSED(oldUrl); + + emit activeViewUrlChanged(newUrl); } DolphinViewContainer* DolphinTabPage::createViewContainer(const KUrl& url) const diff --git a/src/dolphintabpage.h b/src/dolphintabpage.h index 95c02ed0a1..2a406f4a9a 100644 --- a/src/dolphintabpage.h +++ b/src/dolphintabpage.h @@ -120,8 +120,17 @@ public: */ void restoreState(const QByteArray& state); + /** + * Restores all tab related properties (urls, splitter layout, ...) from + * the given \a state. + * + * @deprecated The first tab state version has no version number, we keep + * this method to restore old states (<= Dolphin 4.14.x). + */ + void restoreStateV1(const QByteArray& state); + signals: - void activeViewChanged(); + void activeViewChanged(DolphinViewContainer* viewContainer); void activeViewUrlChanged(const KUrl& url); private slots: @@ -133,6 +142,13 @@ private slots: */ void slotViewActivated(); + /** + * Handles the view url redirection event. + * + * It emits the activeViewUrlChanged signal with the url \a newUrl. + */ + void slotViewUrlRedirection(const KUrl& oldUrl, const KUrl& newUrl); + private: /** * Creates a new view container and does the default initialization. diff --git a/src/dolphintabwidget.cpp b/src/dolphintabwidget.cpp new file mode 100644 index 0000000000..4dd7df7426 --- /dev/null +++ b/src/dolphintabwidget.cpp @@ -0,0 +1,360 @@ +/*************************************************************************** + * Copyright (C) 2014 by Emmanuel Pescosta * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "dolphintabwidget.h" + +#include "dolphintabbar.h" +#include "dolphintabpage.h" +#include "dolphinviewcontainer.h" +#include "dolphin_generalsettings.h" +#include "views/draganddrophelper.h" + +#include +#include +#include +#include + +DolphinTabWidget::DolphinTabWidget(QWidget* parent) : + QTabWidget(parent), + m_placesSelectorVisible(true) +{ + connect(this, SIGNAL(tabCloseRequested(int)), + this, SLOT(closeTab(int))); + connect(this, SIGNAL(currentChanged(int)), + this, SLOT(currentTabChanged(int))); + + DolphinTabBar* tabBar = new DolphinTabBar(this); + connect(tabBar, SIGNAL(openNewActivatedTab(int)), + this, SLOT(openNewActivatedTab(int))); + connect(tabBar, SIGNAL(tabDropEvent(int,QDropEvent*)), + this, SLOT(tabDropEvent(int,QDropEvent*))); + connect(tabBar, SIGNAL(tabDetachRequested(int)), + this, SLOT(detachTab(int))); + tabBar->hide(); + + setTabBar(tabBar); + setDocumentMode(true); + setElideMode(Qt::ElideRight); + setUsesScrollButtons(true); +} + +DolphinTabPage* DolphinTabWidget::currentTabPage() const +{ + return tabPageAt(currentIndex()); +} + +DolphinTabPage* DolphinTabWidget::tabPageAt(const int index) const +{ + return static_cast(widget(index)); +} + +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); + group.writeEntry("Tab Data " % QString::number(i), tabPage->saveState()); + } +} + +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 int index = group.readEntry("Active Tab Index", 0); + setCurrentIndex(index); +} + +void DolphinTabWidget::refreshViews() +{ + const int tabCount = count(); + for (int i = 0; i < tabCount; ++i) { + tabPageAt(i)->refreshViews(); + } +} + +void DolphinTabWidget::openNewActivatedTab() +{ + const DolphinViewContainer* oldActiveViewContainer = currentTabPage()->activeViewContainer(); + Q_ASSERT(oldActiveViewContainer); + + const bool isUrlEditable = oldActiveViewContainer->urlNavigator()->isUrlEditable(); + + openNewActivatedTab(oldActiveViewContainer->url()); + + DolphinViewContainer* newActiveViewContainer = currentTabPage()->activeViewContainer(); + Q_ASSERT(newActiveViewContainer); + + // The URL navigator of the new tab should have the same editable state + // as the current tab + KUrlNavigator* navigator = newActiveViewContainer->urlNavigator(); + navigator->setUrlEditable(isUrlEditable); + + if (isUrlEditable) { + // If a new tab is opened and the URL is editable, assure that + // the user can edit the URL without manually setting the focus + navigator->setFocus(); + } +} + +void DolphinTabWidget::openNewActivatedTab(const KUrl& primaryUrl, const KUrl& secondaryUrl) +{ + openNewTab(primaryUrl, secondaryUrl); + setCurrentIndex(count() - 1); +} + +void DolphinTabWidget::openNewTab(const KUrl& primaryUrl, const KUrl& secondaryUrl) +{ + QWidget* focusWidget = QApplication::focusWidget(); + + DolphinTabPage* tabPage = new DolphinTabPage(primaryUrl, secondaryUrl, this); + tabPage->setPlacesSelectorVisible(m_placesSelectorVisible); + connect(tabPage, SIGNAL(activeViewChanged(DolphinViewContainer*)), + this, SIGNAL(activeViewChanged(DolphinViewContainer*))); + connect(tabPage, SIGNAL(activeViewUrlChanged(KUrl)), + this, SLOT(tabUrlChanged(KUrl))); + addTab(tabPage, QIcon::fromTheme(KIO::iconNameForUrl(primaryUrl)), tabName(primaryUrl)); + + if (focusWidget) { + // The DolphinViewContainer grabbed the keyboard focus. As the tab is opened + // in background, assure that the previous focused widget gets the focus back. + focusWidget->setFocus(); + } +} + +void DolphinTabWidget::openDirectories(const QList& dirs) +{ + const bool hasSplitView = GeneralSettings::splitView(); + + // Open each directory inside a new tab. If the "split view" option has been enabled, + // always show two directories within one tab. + QList::const_iterator it = dirs.constBegin(); + while (it != dirs.constEnd()) { + const KUrl& primaryUrl = *(it++); + if (hasSplitView && (it != dirs.constEnd())) { + const KUrl& secondaryUrl = *(it++); + openNewTab(primaryUrl, secondaryUrl); + } else { + openNewTab(primaryUrl); + } + } +} + +void DolphinTabWidget::openFiles(const QList& files) +{ + if (files.isEmpty()) { + return; + } + + // 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 dirs; + foreach (const KUrl& url, files) { + const KUrl dir(url.directory()); + if (!dirs.contains(dir)) { + dirs.append(dir); + } + } + + const int oldTabCount = count(); + openDirectories(dirs); + 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 = oldTabCount; i < tabCount; ++i) { + DolphinTabPage* tabPage = tabPageAt(i); + tabPage->markUrlsAsSelected(files); + tabPage->markUrlAsCurrent(files.first()); + } +} + +void DolphinTabWidget::closeTab() +{ + closeTab(currentIndex()); +} + +void DolphinTabWidget::closeTab(const int index) +{ + Q_ASSERT(index >= 0); + Q_ASSERT(index < count()); + + if (count() < 2) { + // Never close the last tab. + return; + } + + DolphinTabPage* tabPage = tabPageAt(index); + emit rememberClosedTab(tabPage->activeViewContainer()->url(), tabPage->saveState()); + + removeTab(index); + tabPage->deleteLater(); +} + +void DolphinTabWidget::activateNextTab() +{ + const int index = currentIndex() + 1; + setCurrentIndex(index < count() ? index : 0); +} + +void DolphinTabWidget::activatePrevTab() +{ + const int index = currentIndex() - 1; + 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) +{ + openNewActivatedTab(); + currentTabPage()->restoreState(state); +} + +void DolphinTabWidget::detachTab(int index) +{ + Q_ASSERT(index >= 0); + + const QString separator(QLatin1Char(' ')); + QString command = QLatin1String("dolphin"); + + const DolphinTabPage* tabPage = tabPageAt(index); + command += separator + tabPage->primaryViewContainer()->url().url(); + if (tabPage->splitViewEnabled()) { + command += separator + tabPage->secondaryViewContainer()->url().url(); + command += separator + QLatin1String("-split"); + } + + KRun::runCommand(command, this); + + closeTab(index); +} + +void DolphinTabWidget::openNewActivatedTab(int index) +{ + Q_ASSERT(index >= 0); + const DolphinTabPage* tabPage = tabPageAt(index); + openNewActivatedTab(tabPage->activeViewContainer()->url()); +} + +void DolphinTabWidget::tabDropEvent(int index, QDropEvent* event) +{ + if (index >= 0) { + const DolphinView* view = tabPageAt(index)->activeViewContainer()->view(); + + QString error; + DragAndDropHelper::dropUrls(view->rootItem(), view->url(), event, error); + if (!error.isEmpty()) { + currentTabPage()->activeViewContainer()->showMessage(error, DolphinViewContainer::Error); + } + } +} + +void DolphinTabWidget::tabUrlChanged(const KUrl& url) +{ + const int index = indexOf(qobject_cast(sender())); + if (index >= 0) { + tabBar()->setTabText(index, tabName(url)); + tabBar()->setTabIcon(index, QIcon::fromTheme(KIO::iconNameForUrl(url))); + + // Emit the currentUrlChanged signal if the url of the current tab has been changed. + if (index == currentIndex()) { + emit currentUrlChanged(url); + } + } +} + +void DolphinTabWidget::currentTabChanged(int index) +{ + DolphinViewContainer* viewContainer = tabPageAt(index)->activeViewContainer(); + emit activeViewChanged(viewContainer); + emit currentUrlChanged(viewContainer->url()); +} + +void DolphinTabWidget::tabInserted(int index) +{ + QTabWidget::tabInserted(index); + + if (count() > 1) { + tabBar()->show(); + } + + 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()); +} + +QString DolphinTabWidget::tabName(const KUrl& url) const +{ + QString name; + if (url.equals(KUrl("file:///"))) { + name = '/'; + } else { + name = url.fileName(); + if (name.isEmpty()) { + name = url.protocol(); + } else { + // Make sure that a '&' inside the directory name is displayed correctly + // and not misinterpreted as a keyboard shortcut in QTabBar::setTabText() + name.replace('&', "&&"); + } + } + return name; +} diff --git a/src/dolphintabwidget.h b/src/dolphintabwidget.h new file mode 100644 index 0000000000..98bcd985ab --- /dev/null +++ b/src/dolphintabwidget.h @@ -0,0 +1,190 @@ +/*************************************************************************** + * Copyright (C) 2014 by Emmanuel Pescosta * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef DOLPHIN_TAB_WIDGET_H +#define DOLPHIN_TAB_WIDGET_H + +#include +#include + +class DolphinViewContainer; +class DolphinTabPage; +class KConfigGroup; + +class DolphinTabWidget : public QTabWidget +{ + Q_OBJECT + +public: + explicit DolphinTabWidget(QWidget* parent); + + /** + * @return Tab page at the current index (can be 0 if tabs count is smaller than 1) + */ + DolphinTabPage* currentTabPage() const; + + /** + * @return Tab page at the given \a index (can be 0 if the index is out-of-range) + */ + DolphinTabPage* tabPageAt(const int index) const; + + void saveProperties(KConfigGroup& group) const; + void readProperties(const KConfigGroup& group); + + /** + * Refreshes the views of the main window by recreating them according to + * the given Dolphin settings. + */ + void refreshViews(); + +signals: + /** + * Is emitted when the active view has been changed, by changing the current + * tab or by activating another view when split view is enabled in the current + * tab. + */ + void activeViewChanged(DolphinViewContainer* viewContainer); + + /** + * Is emitted when the number of open tabs has changed (e.g. by opening or + * closing a tab) + */ + void tabCountChanged(int count); + + /** + * Is emitted when a tab has been closed. + */ + void rememberClosedTab(const KUrl& url, const QByteArray& state); + + /** + * Is emitted when the url of the current tab has been changed. This signal + * is also emitted when the active view has been changed. + */ + void currentUrlChanged(const KUrl& url); + +public slots: + /** + * Opens a new view with the current URL that is part of a tab and activates + * the tab. + */ + void openNewActivatedTab(); + + /** + * Opens a new tab showing the URL \a primaryUrl and the optional URL + * \a secondaryUrl and activates the tab. + */ + void openNewActivatedTab(const KUrl& primaryUrl, const KUrl& secondaryUrl = KUrl()); + + /** + * Opens a new tab in the background showing the URL \a primaryUrl and the + * optional URL \a secondaryUrl. + */ + void openNewTab(const KUrl& primaryUrl, const KUrl& secondaryUrl = KUrl()); + + /** + * Opens each directory in \p dirs in a separate tab. If the "split view" + * option is enabled, 2 directories are collected within one tab. + */ + void openDirectories(const QList& dirs); + + /** + * Opens the directory which contains the files \p files + * and selects all files (implements the --select option + * of Dolphin). + */ + void openFiles(const QList& files); + + /** + * Closes the currently active tab. + */ + void closeTab(); + + /** + * Closes the tab with the index \a index and activates the tab with index - 1. + */ + void closeTab(const int index); + + /** + * Activates the next tab in the tab bar. + * If the current active tab is the last tab, it activates the first tab. + */ + void activateNextTab(); + + /** + * Activates the previous tab in the tab bar. + * If the current active tab is the first tab, it activates the last tab. + */ + void activatePrevTab(); + + /** + * Is invoked if the Places panel got visible/invisible and takes care + * that the places-selector of all views is only shown if the Places panel + * is invisible. + */ + void slotPlacesPanelVisibilityChanged(bool visible); + + /** + * Is called when the user wants to reopen a previously closed tab from + * the recent tabs menu. + */ + void restoreClosedTab(const QByteArray& state); + +private slots: + /** + * Opens the tab with the index \a index in a new Dolphin instance and closes + * this tab. + */ + void detachTab(int index); + + /** + * Opens a new tab showing the url from tab at the given \a index and + * activates the tab. + */ + void openNewActivatedTab(int index); + + /** + * Is connected to the KTabBar signal receivedDropEvent. + * Allows dragging and dropping files onto tabs. + */ + void tabDropEvent(int tab, QDropEvent* event); + + /** + * The active view url of a tab has been changed so update the text and the + * icon of the corresponding tab. + */ + void tabUrlChanged(const KUrl& url); + + void currentTabChanged(int index); + +protected: + virtual void tabInserted(int index); + virtual void tabRemoved(int index); + +private: + /** + * Returns the name of the tab for the URL \a url. + */ + QString tabName(const KUrl& url) const; + +private: + /** Caches the (negated) places panel visibility */ + bool m_placesSelectorVisible; +}; + +#endif \ No newline at end of file diff --git a/src/dolphinviewcontainer.cpp b/src/dolphinviewcontainer.cpp index 46225f9315..b18e436311 100644 --- a/src/dolphinviewcontainer.cpp +++ b/src/dolphinviewcontainer.cpp @@ -112,6 +112,8 @@ DolphinViewContainer::DolphinViewContainer(const KUrl& url, QWidget* parent) : m_urlNavigator, &KUrlNavigator::setUrl); connect(m_view, &DolphinView::urlChanged, m_messageWidget, &KMessageWidget::hide); + connect(m_view, &DolphinView::directoryLoadingCompleted, + m_messageWidget, &KMessageWidget::hide); connect(m_view, &DolphinView::writeStateChanged, this, &DolphinViewContainer::writeStateChanged); connect(m_view, &DolphinView::requestItemInfo, @@ -301,6 +303,9 @@ void DolphinViewContainer::showMessage(const QString& msg, MessageType type) const int unwrappedWidth = m_messageWidget->sizeHint().width(); m_messageWidget->setWordWrap(unwrappedWidth > size().width()); + if (m_messageWidget->isVisible()) { + m_messageWidget->hide(); + } m_messageWidget->animatedShow(); } diff --git a/src/kitemviews/kstandarditemlistwidget.cpp b/src/kitemviews/kstandarditemlistwidget.cpp index 88286120a8..8633a25710 100644 --- a/src/kitemviews/kstandarditemlistwidget.cpp +++ b/src/kitemviews/kstandarditemlistwidget.cpp @@ -1136,14 +1136,24 @@ void KStandardItemListWidget::updateIconsLayoutTextCache() const int textLength = line.textStart() + line.textLength(); if (textLength < nameText.length()) { // Elide the last line of the text - QString lastTextLine = nameText.mid(line.textStart()); - lastTextLine = m_customizedFontMetrics.elidedText(lastTextLine, - Qt::ElideRight, - maxWidth); - const QString elidedText = nameText.left(line.textStart()) + lastTextLine; - nameTextInfo->staticText.setText(elidedText); + qreal elidingWidth = maxWidth; + qreal lastLineWidth; + do { + QString lastTextLine = nameText.mid(line.textStart()); + lastTextLine = m_customizedFontMetrics.elidedText(lastTextLine, + Qt::ElideRight, + elidingWidth); + const QString elidedText = nameText.left(line.textStart()) + lastTextLine; + nameTextInfo->staticText.setText(elidedText); + + lastLineWidth = m_customizedFontMetrics.boundingRect(lastTextLine).width(); + + // We do the text eliding in a loop with decreasing width (1 px / iteration) + // to avoid problems related to different width calculation code paths + // within Qt. (see bug 337104) + elidingWidth -= 1.0; + } while (lastLineWidth > maxWidth); - const qreal lastLineWidth = m_customizedFontMetrics.boundingRect(lastTextLine).width(); nameWidth = qMax(nameWidth, lastLineWidth); } break; diff --git a/src/statusbar/dolphinstatusbar.cpp b/src/statusbar/dolphinstatusbar.cpp index 21f657ada3..ea94e68b4c 100644 --- a/src/statusbar/dolphinstatusbar.cpp +++ b/src/statusbar/dolphinstatusbar.cpp @@ -109,18 +109,19 @@ DolphinStatusBar::DolphinStatusBar(QWidget* parent) : const int zoomSliderHeight = m_zoomSlider->minimumSizeHint().height(); const int contentHeight = qMax(fontHeight, zoomSliderHeight); + QFontMetrics fontMetrics(m_label->font()); + m_label->setFixedHeight(contentHeight); m_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - m_zoomSlider->setFixedHeight(contentHeight); - m_zoomSlider->setMaximumWidth(150); + m_zoomSlider->setMaximumWidth(fontMetrics.averageCharWidth() * 15); m_spaceInfo->setFixedHeight(contentHeight); - m_spaceInfo->setMaximumWidth(150); + m_spaceInfo->setMaximumWidth(fontMetrics.averageCharWidth() * 15); m_spaceInfo->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); m_progressBar->setFixedHeight(contentHeight); - m_progressBar->setMaximumWidth(150); + m_progressBar->setMaximumWidth(fontMetrics.averageCharWidth() * 15); QHBoxLayout* topLayout = new QHBoxLayout(this); topLayout->setMargin(0); diff --git a/src/views/dolphinview.cpp b/src/views/dolphinview.cpp index e57881c10e..e7034984e9 100644 --- a/src/views/dolphinview.cpp +++ b/src/views/dolphinview.cpp @@ -1176,6 +1176,14 @@ bool DolphinView::itemsExpandable() const void DolphinView::restoreState(QDataStream& stream) { + // Read the version number of the view state and check if the version is supported. + quint32 version = 0; + stream >> version; + if (version != 1) { + // The version of the view state isn't supported, we can't restore it. + return; + } + // Restore the current item that had the keyboard focus stream >> m_currentItemUrl; @@ -1190,6 +1198,8 @@ void DolphinView::restoreState(QDataStream& stream) void DolphinView::saveState(QDataStream& stream) { + stream << quint32(1); // View state version + // Save the current item that has the keyboard focus const int currentIndex = m_container->controller()->selectionManager()->currentItem(); if (currentIndex != -1) { @@ -1298,11 +1308,11 @@ void DolphinView::updateViewState() m_view->scrollToItem(currentIndex); m_scrollToCurrentItem = false; } - - m_currentItemUrl = KUrl(); } else { selectionManager->setCurrentItem(0); } + + m_currentItemUrl = KUrl(); } if (!m_restoredContentsPosition.isNull()) {