diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7b0210aef9..6f256a2f43 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -174,6 +174,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/dolphinmainwindow.cpp b/src/dolphinmainwindow.cpp index d321d3b4a7..0882bac717 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" @@ -97,11 +97,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(), @@ -134,6 +131,13 @@ 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))); + setCentralWidget(m_tabWidget); + setupActions(); m_actionHandler = new DolphinViewActionHandler(actionCollection(), this); @@ -144,30 +148,6 @@ DolphinMainWindow::DolphinMainWindow() : connect(this, SIGNAL(urlChanged(KUrl)), m_remoteEncoding, SLOT(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); @@ -200,48 +180,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) @@ -287,21 +231,14 @@ 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(); + setUrlAsCaption(url); - const QString iconName = KMimeType::iconNameForUrl(url); - m_tabBar->setTabIcon(m_tabIndex, KIcon(iconName)); - m_tabBar->setTabText(m_tabIndex, squeezedText(tabName(view->url()))); - - emit urlChanged(url); - } + emit urlChanged(url); } void DolphinMainWindow::slotTerminalDirectoryChanged(const KUrl& url) @@ -322,7 +259,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) { @@ -368,99 +305,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(KIcon(KMimeType::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() @@ -499,12 +356,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(); } @@ -520,7 +371,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 @@ -555,7 +406,7 @@ void DolphinMainWindow::closeEvent(QCloseEvent* event) break; case KDialog::No: // Close only the current tab - closeTab(); + m_tabWidget->closeTab(); default: event->ignore(); return; @@ -570,32 +421,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() @@ -699,13 +530,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(); } @@ -768,14 +595,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(); @@ -841,7 +660,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 @@ -903,111 +722,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; @@ -1041,20 +755,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); @@ -1214,27 +914,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(); @@ -1243,14 +944,20 @@ void DolphinMainWindow::setActiveViewContainer(DolphinViewContainer* viewContain updateViewActions(); updateGoActions(); - const KUrl url = m_activeViewContainer->url(); + const KUrl url = viewContainer->url(); setUrlAsCaption(url); - m_tabBar->setTabText(m_tabIndex, squeezedText(tabName(url))); - m_tabBar->setTabIcon(m_tabIndex, KIcon(KMimeType::iconNameForUrl(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::setupActions() { // setup 'File' menu @@ -1272,14 +979,14 @@ void DolphinMainWindow::setupActions() newTab->setIcon(KIcon("tab-new")); newTab->setText(i18nc("@action:inmenu File", "New Tab")); newTab->setShortcut(KShortcut(Qt::CTRL | Qt::Key_T, Qt::CTRL | Qt::SHIFT | Qt::Key_N)); - connect(newTab, SIGNAL(triggered()), this, SLOT(openNewTab())); + connect(newTab, SIGNAL(triggered()), this, SLOT(openNewActivatedTab())); KAction* closeTab = actionCollection()->addAction("close_tab"); closeTab->setIcon(KIcon("tab-close")); closeTab->setText(i18nc("@action:inmenu File", "Close Tab")); closeTab->setShortcut(Qt::CTRL | Qt::Key_W); closeTab->setEnabled(false); - connect(closeTab, SIGNAL(triggered()), this, SLOT(closeTab())); + connect(closeTab, SIGNAL(triggered()), m_tabWidget, SLOT(closeTab())); KStandardAction::quit(this, SLOT(quit()), actionCollection()); @@ -1351,7 +1058,7 @@ void DolphinMainWindow::setupActions() DolphinRecentTabsMenu* recentTabsMenu = new DolphinRecentTabsMenu(this); actionCollection()->addAction("closed_tabs", recentTabsMenu); - connect(this, SIGNAL(rememberClosedTab(KUrl,KUrl)), + connect(m_tabWidget, SIGNAL(rememberClosedTab(KUrl,KUrl)), recentTabsMenu, SLOT(rememberClosedTab(KUrl,KUrl))); connect(recentTabsMenu, SIGNAL(restoreClosedTab(KUrl,KUrl)), this, SLOT(openNewActivatedTab(KUrl,KUrl))); @@ -1412,14 +1119,14 @@ void DolphinMainWindow::setupActions() activateNextTab->setIconText(i18nc("@action:inmenu", "Next Tab")); activateNextTab->setText(i18nc("@action:inmenu", "Activate Next Tab")); activateNextTab->setEnabled(false); - connect(activateNextTab, SIGNAL(triggered()), SLOT(activateNextTab())); + connect(activateNextTab, SIGNAL(triggered()), m_tabWidget, SLOT(activateNextTab())); activateNextTab->setShortcuts(QApplication::isRightToLeft() ? prevTabKeys : nextTabKeys); KAction* 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, SIGNAL(triggered()), SLOT(activatePrevTab())); + connect(activatePrevTab, SIGNAL(triggered()), m_tabWidget, SLOT(activatePrevTab())); activatePrevTab->setShortcuts(QApplication::isRightToLeft() ? nextTabKeys : prevTabKeys); // for context menu @@ -1548,10 +1255,12 @@ void DolphinMainWindow::setupDockWidgets() connect(this, SIGNAL(urlChanged(KUrl)), placesPanel, SLOT(setUrl(KUrl))); connect(placesDock, SIGNAL(visibilityChanged(bool)), - this, SLOT(slotPlacesPanelVisibilityChanged(bool))); + m_tabWidget, SLOT(slotPlacesPanelVisibilityChanged(bool))); connect(this, SIGNAL(settingsChanged()), placesPanel, SLOT(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); @@ -1674,15 +1383,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(); } @@ -1733,7 +1440,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")); @@ -1751,24 +1458,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; @@ -1798,12 +1487,6 @@ void DolphinMainWindow::setUrlAsCaption(const KUrl& url) 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 KIcon& icon, const QKeySequence& shortcut, QAction* dockAction, diff --git a/src/dolphinmainwindow.h b/src/dolphinmainwindow.h index 6f37f42ee6..7bce7f13e9 100644 --- a/src/dolphinmainwindow.h +++ b/src/dolphinmainwindow.h @@ -38,19 +38,16 @@ typedef KIO::FileUndoManager::CommandType CommandType; class DolphinViewActionHandler; class DolphinApplication; class DolphinSettingsDialog; -class DolphinTabBar; class DolphinViewContainer; class DolphinRemoteEncoding; -class DolphinTabPage; +class DolphinTabWidget; class KAction; class KFileItem; class KFileItemList; class KJob; class KNewFileMenu; class KUrl; -class QSplitter; class QToolButton; -class QVBoxLayout; /** * @short Main window for Dolphin. @@ -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,25 @@ 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); +private: void setupActions(); void setupDockWidgets(); void updateEditActions(); @@ -511,10 +462,6 @@ private: */ void updateSplitAction(); - /** Returns the name of the tab for the URL \a url. */ - QString tabName(const KUrl& url) const; - - bool isKompareInstalled() const; /** @@ -523,8 +470,6 @@ private: */ 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/dolphintabpage.cpp b/src/dolphintabpage.cpp index 82be6d59c1..d1184a9f2c 100644 --- a/src/dolphintabpage.cpp +++ b/src/dolphintabpage.cpp @@ -246,7 +246,7 @@ void DolphinTabPage::slotViewActivated() } emit activeViewUrlChanged(activeViewContainer()->url()); - emit activeViewChanged(); + emit activeViewChanged(activeViewContainer()); } DolphinViewContainer* DolphinTabPage::createViewContainer(const KUrl& url) const diff --git a/src/dolphintabpage.h b/src/dolphintabpage.h index 95c02ed0a1..278524792c 100644 --- a/src/dolphintabpage.h +++ b/src/dolphintabpage.h @@ -121,7 +121,7 @@ public: void restoreState(const QByteArray& state); signals: - void activeViewChanged(); + void activeViewChanged(DolphinViewContainer* viewContainer); void activeViewUrlChanged(const KUrl& url); private slots: diff --git a/src/dolphintabwidget.cpp b/src/dolphintabwidget.cpp new file mode 100644 index 0000000000..4bb70b4eab --- /dev/null +++ b/src/dolphintabwidget.cpp @@ -0,0 +1,345 @@ +/*************************************************************************** + * 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 " % 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(); + } + const QByteArray state = group.readEntry("Tab " % QString::number(i), QByteArray()); + tabPageAt(i)->restoreState(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, KIcon(KMimeType::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); + if (tabPage->splitViewEnabled()) { + emit rememberClosedTab(tabPage->primaryViewContainer()->url(), + tabPage->secondaryViewContainer()->url()); + } else { + emit rememberClosedTab(tabPage->primaryViewContainer()->url(), KUrl()); + } + + 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::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, KIcon(KMimeType::iconNameForUrl(url))); + } +} + +void DolphinTabWidget::currentTabChanged(int index) +{ + emit activeViewChanged(tabPageAt(index)->activeViewContainer()); +} + +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..a9bef11be1 --- /dev/null +++ b/src/dolphintabwidget.h @@ -0,0 +1,178 @@ +/*************************************************************************** + * 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& primaryUrl, const KUrl& secondaryUrl); + +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); + +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