diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/dolphinmainwindow.cpp | 480 | ||||
| -rw-r--r-- | src/dolphinmainwindow.h | 101 | ||||
| -rw-r--r-- | src/dolphinrecenttabsmenu.cpp | 16 | ||||
| -rw-r--r-- | src/dolphinrecenttabsmenu.h | 2 | ||||
| -rw-r--r-- | src/dolphintabbar.cpp | 174 | ||||
| -rw-r--r-- | src/dolphintabbar.h | 65 | ||||
| -rw-r--r-- | src/dolphintabpage.cpp | 2 | ||||
| -rw-r--r-- | src/dolphintabpage.h | 2 | ||||
| -rw-r--r-- | src/dolphintabwidget.cpp | 345 | ||||
| -rw-r--r-- | src/dolphintabwidget.h | 178 | ||||
| -rw-r--r-- | src/dolphinui.rc | 1 | ||||
| -rw-r--r-- | src/panels/places/placesitemmodel.cpp | 10 |
13 files changed, 883 insertions, 495 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 518c8b7d2..6f256a2f4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -171,8 +171,10 @@ set(dolphin_SRCS dolphinmainwindow.cpp dolphinviewcontainer.cpp dolphincontextmenu.cpp + 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 db4ad0765..0882bac71 100644 --- a/src/dolphinmainwindow.cpp +++ b/src/dolphinmainwindow.cpp @@ -26,6 +26,7 @@ #include "dolphincontextmenu.h" #include "dolphinnewfilemenu.h" #include "dolphinrecenttabsmenu.h" +#include "dolphintabwidget.h" #include "dolphinviewcontainer.h" #include "dolphintabpage.h" #include "panels/folders/folderspanel.h" @@ -75,7 +76,6 @@ #include <KStandardDirs> #include <kstatusbar.h> #include <KStandardAction> -#include <ktabbar.h> #include <KToggleAction> #include <KUrlNavigator> #include <KUrl> @@ -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,36 +148,6 @@ DolphinMainWindow::DolphinMainWindow() : connect(this, SIGNAL(urlChanged(KUrl)), m_remoteEncoding, SLOT(slotAboutToOpenUrl())); - m_tabBar = new KTabBar(this); - m_tabBar->setMovable(true); - m_tabBar->setTabsClosable(true); - connect(m_tabBar, SIGNAL(currentChanged(int)), - this, SLOT(setActiveTab(int))); - connect(m_tabBar, SIGNAL(tabCloseRequested(int)), - this, SLOT(closeTab(int))); - connect(m_tabBar, SIGNAL(contextMenu(int,QPoint)), - this, SLOT(openTabContextMenu(int,QPoint))); - connect(m_tabBar, SIGNAL(newTabRequest()), - this, SLOT(openNewTab())); - connect(m_tabBar, SIGNAL(testCanDecode(const QDragMoveEvent*,bool&)), - this, SLOT(slotTestCanDecode(const QDragMoveEvent*,bool&))); - connect(m_tabBar, SIGNAL(mouseMiddleClick(int)), - this, SLOT(closeTab(int))); - connect(m_tabBar, SIGNAL(tabMoved(int,int)), - this, SLOT(slotTabMoved(int,int))); - connect(m_tabBar, SIGNAL(receivedDropEvent(int,QDropEvent*)), - this, SLOT(tabDropEvent(int,QDropEvent*))); - - 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); @@ -206,48 +180,12 @@ DolphinMainWindow::~DolphinMainWindow() void DolphinMainWindow::openDirectories(const QList<KUrl>& 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<KUrl>::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<KUrl>& 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<KUrl> 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) @@ -293,21 +231,14 @@ void DolphinMainWindow::changeUrl(const KUrl& url) return; } - DolphinViewContainer* view = activeViewContainer(); - if (view) { - view->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()))); + m_activeViewContainer->setUrl(url); + updateEditActions(); + updatePasteAction(); + updateViewActions(); + updateGoActions(); + setUrlAsCaption(url); - emit urlChanged(url); - } + emit urlChanged(url); } void DolphinMainWindow::slotTerminalDirectoryChanged(const KUrl& url) @@ -328,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) { @@ -374,92 +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<QDockWidget*>("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::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() @@ -498,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(); } @@ -519,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 @@ -554,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; @@ -569,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() @@ -698,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(); } @@ -767,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(); @@ -840,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 @@ -902,143 +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::openTabContextMenu(int index, const QPoint& pos) -{ - KMenu menu(this); - - QAction* newTabAction = menu.addAction(KIcon("tab-new"), i18nc("@action:inmenu", "New Tab")); - newTabAction->setShortcut(actionCollection()->action("new_tab")->shortcut()); - - QAction* detachTabAction = menu.addAction(KIcon("tab-detach"), i18nc("@action:inmenu", "Detach Tab")); - - QAction* closeOtherTabsAction = menu.addAction(KIcon("tab-close-other"), i18nc("@action:inmenu", "Close Other Tabs")); - - QAction* closeTabAction = menu.addAction(KIcon("tab-close"), i18nc("@action:inmenu", "Close Tab")); - closeTabAction->setShortcut(actionCollection()->action("close_tab")->shortcut()); - QAction* selectedAction = menu.exec(pos); - if (selectedAction == newTabAction) { - const KUrl url = m_viewTab.at(index)->activeViewContainer()->url(); - openNewTab(url); - m_tabBar->setCurrentIndex(m_viewTab.count() - 1); - } else if (selectedAction == detachTabAction) { - 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); - } else if (selectedAction == closeOtherTabsAction) { - const int count = m_tabBar->count(); - for (int i = 0; i < index; ++i) { - closeTab(0); - } - for (int i = index + 1; i < count; ++i) { - closeTab(1); - } - } else if (selectedAction == closeTabAction) { - closeTab(index); - } -} - -void DolphinMainWindow::slotTabMoved(int from, int to) -{ - m_viewTab.move(from, to); - m_tabIndex = m_tabBar->currentIndex(); -} - -void DolphinMainWindow::slotTestCanDecode(const QDragMoveEvent* event, bool& canDecode) -{ - canDecode = KUrl::List::canDecode(event->mimeData()); -} - void DolphinMainWindow::handleUrl(const KUrl& url) { delete m_lastHandleUrlStatJob; @@ -1072,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); @@ -1245,22 +914,28 @@ void DolphinMainWindow::slotPlaceActivated(const KUrl& url) } } -void DolphinMainWindow::activeViewChanged() +void DolphinMainWindow::closedTabsCountChanged(unsigned int count) { - const DolphinTabPage* tabPage = m_viewTab.at(m_tabIndex); - setActiveViewContainer(tabPage->activeViewContainer()); + 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(); @@ -1269,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 @@ -1298,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()); @@ -1377,10 +1058,19 @@ 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))); + connect(recentTabsMenu, SIGNAL(closedTabsCountChanged(uint)), + this, SLOT(closedTabsCountChanged(uint))); + + KAction* undoCloseTab = actionCollection()->addAction("undo_close_tab"); + undoCloseTab->setText(i18nc("@action:inmenu File", "Undo close tab")); + undoCloseTab->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_T); + undoCloseTab->setIcon(KIcon("edit-undo")); + undoCloseTab->setEnabled(false); + connect(undoCloseTab, SIGNAL(triggered()), recentTabsMenu, SLOT(undoCloseTab())); KAction* forwardAction = KStandardAction::forward(this, SLOT(goForward()), actionCollection()); connect(forwardAction, SIGNAL(triggered(Qt::MouseButtons,Qt::KeyboardModifiers)), this, SLOT(goForward(Qt::MouseButtons))); @@ -1429,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 @@ -1565,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); @@ -1691,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(); } @@ -1750,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")); @@ -1768,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; @@ -1815,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 7c3bff47f..7bce7f13e 100644 --- a/src/dolphinmainwindow.h +++ b/src/dolphinmainwindow.h @@ -40,17 +40,14 @@ class DolphinApplication; class DolphinSettingsDialog; class DolphinViewContainer; class DolphinRemoteEncoding; -class DolphinTabPage; +class DolphinTabWidget; class KAction; class KFileItem; class KFileItemList; class KJob; class KNewFileMenu; -class KTabBar; 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,10 +346,6 @@ private slots: */ void openNewActivatedTab(const KUrl& primaryUrl, const KUrl& secondaryUrl = KUrl()); - void activateNextTab(); - - void activatePrevTab(); - /** * Opens the selected folder in a new tab. */ @@ -379,39 +363,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 a context menu for the tab with the index \a index - * on the position \a pos. - */ - void openTabContextMenu(int index, const QPoint& pos); - - /** - * 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); - - /** - * Is connected to the KTabBar signal testCanDecode() and adjusts - * the output parameter \a accept. - */ - void slotTestCanDecode(const QDragMoveEvent* event, bool& accept); - - /** * If the URL can be listed, open it in the current view, otherwise * run it through KRun. */ @@ -424,12 +375,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,17 +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(); @@ -509,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; /** @@ -521,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 @@ -549,14 +496,10 @@ private: }; KNewFileMenu* m_newFileMenu; - KTabBar* m_tabBar; + DolphinTabWidget* m_tabWidget; DolphinViewContainer* m_activeViewContainer; - QVBoxLayout* m_centralWidgetLayout; int m_id; - int m_tabIndex; - QList<DolphinTabPage*> m_viewTab; - DolphinViewActionHandler* m_actionHandler; DolphinRemoteEncoding* m_remoteEncoding; QWeakPointer<DolphinSettingsDialog> m_settingsDialog; diff --git a/src/dolphinrecenttabsmenu.cpp b/src/dolphinrecenttabsmenu.cpp index a39f9945b..2335f1bf4 100644 --- a/src/dolphinrecenttabsmenu.cpp +++ b/src/dolphinrecenttabsmenu.cpp @@ -60,7 +60,7 @@ void DolphinRecentTabsMenu::rememberClosedTab(const KUrl& primaryUrl, const KUrl } else { insertAction(menu()->actions().at(2), action); } - + emit closedTabsCountChanged(menu()->actions().size() - 2); // Assure that only up to 6 closed tabs are shown in the menu. // 8 because of clear action + separator + 6 closed tabs if (menu()->actions().size() > 8) { @@ -70,6 +70,12 @@ void DolphinRecentTabsMenu::rememberClosedTab(const KUrl& primaryUrl, const KUrl KAcceleratorManager::manage(menu()); } +void DolphinRecentTabsMenu::undoCloseTab() +{ + Q_ASSERT(menu()->actions().size() > 2); + handleAction(menu()->actions().at(2)); +} + void DolphinRecentTabsMenu::handleAction(QAction* action) { if (action == m_clearListAction) { @@ -80,14 +86,16 @@ void DolphinRecentTabsMenu::handleAction(QAction* action) for (int i = 2; i < count; ++i) { removeAction(actions.at(i)); } + emit closedTabsCountChanged(0); } else { const KUrl::List urls = action->data().value<KUrl::List>(); - if (urls.count() == 2) { - emit restoreClosedTab(urls.first(), urls.last()); - } removeAction(action); delete action; action = 0; + if (urls.count() == 2) { + emit restoreClosedTab(urls.first(), urls.last()); + } + emit closedTabsCountChanged(menu()->actions().size() - 2); } if (menu()->actions().count() <= 2) { diff --git a/src/dolphinrecenttabsmenu.h b/src/dolphinrecenttabsmenu.h index 34d41530b..b5acc735e 100644 --- a/src/dolphinrecenttabsmenu.h +++ b/src/dolphinrecenttabsmenu.h @@ -35,9 +35,11 @@ public: public slots: void rememberClosedTab(const KUrl& primaryUrl, const KUrl& secondaryUrl); + void undoCloseTab(); signals: void restoreClosedTab(const KUrl& primaryUrl, const KUrl& secondaryUrl); + void closedTabsCountChanged(unsigned int count); private slots: void handleAction(QAction* action); diff --git a/src/dolphintabbar.cpp b/src/dolphintabbar.cpp new file mode 100644 index 000000000..78bd5edcb --- /dev/null +++ b/src/dolphintabbar.cpp @@ -0,0 +1,174 @@ +/*************************************************************************** + * Copyright (C) 2014 by Emmanuel Pescosta <[email protected]> * + * * + * 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 "dolphintabbar.h" + +#include <QTimer> +#include <QDragEnterEvent> +#include <KLocalizedString> +#include <KMenu> +#include <KIcon> +#include <KUrl> + +DolphinTabBar::DolphinTabBar(QWidget* parent) : + QTabBar(parent), + m_autoActivationIndex(-1) +{ + setAcceptDrops(true); + setSelectionBehaviorOnRemove(QTabBar::SelectPreviousTab); + setMovable(true); + setTabsClosable(true); + + m_autoActivationTimer = new QTimer(this); + m_autoActivationTimer->setSingleShot(true); + m_autoActivationTimer->setInterval(800); + connect(m_autoActivationTimer, SIGNAL(timeout()), + this, SLOT(slotAutoActivationTimeout())); +} + +void DolphinTabBar::dragEnterEvent(QDragEnterEvent* event) +{ + const QMimeData* mimeData = event->mimeData(); + const int index = tabAt(event->pos()); + + if (KUrl::List::canDecode(mimeData)) { + event->acceptProposedAction(); + updateAutoActivationTimer(index); + } + + QTabBar::dragEnterEvent(event); +} + +void DolphinTabBar::dragLeaveEvent(QDragLeaveEvent* event) +{ + updateAutoActivationTimer(-1); + + QTabBar::dragLeaveEvent(event); +} + +void DolphinTabBar::dragMoveEvent(QDragMoveEvent* event) +{ + const QMimeData* mimeData = event->mimeData(); + const int index = tabAt(event->pos()); + + if (KUrl::List::canDecode(mimeData)) { + updateAutoActivationTimer(index); + } + + QTabBar::dragMoveEvent(event); +} + +void DolphinTabBar::dropEvent(QDropEvent* event) +{ + // Disable the auto activation timer + updateAutoActivationTimer(-1); + + const QMimeData* mimeData = event->mimeData(); + const int index = tabAt(event->pos()); + + if (index >= 0 && KUrl::List::canDecode(mimeData)) { + emit tabDropEvent(index, event); + } + + QTabBar::dropEvent(event); +} + +void DolphinTabBar::mousePressEvent(QMouseEvent* event) +{ + const int index = tabAt(event->pos()); + + if (index >= 0 && event->button() == Qt::MiddleButton) { + // Mouse middle click on a tab closes this tab. + emit tabCloseRequested(index); + return; + } + + QTabBar::mousePressEvent(event); +} + +void DolphinTabBar::mouseDoubleClickEvent(QMouseEvent* event) +{ + const int index = tabAt(event->pos()); + + if (index < 0) { + // Double click on the empty tabbar area opens a new activated tab + // with the url from the current tab. + emit openNewActivatedTab(currentIndex()); + return; + } + + QTabBar::mouseDoubleClickEvent(event); +} + +void DolphinTabBar::contextMenuEvent(QContextMenuEvent* event) +{ + const int index = tabAt(event->pos()); + + if (index >= 0) { + // Tab context menu + KMenu menu(this); + + QAction* newTabAction = menu.addAction(KIcon("tab-new"), i18nc("@action:inmenu", "New Tab")); + QAction* detachTabAction = menu.addAction(KIcon("tab-detach"), i18nc("@action:inmenu", "Detach Tab")); + QAction* closeOtherTabsAction = menu.addAction(KIcon("tab-close-other"), i18nc("@action:inmenu", "Close Other Tabs")); + QAction* closeTabAction = menu.addAction(KIcon("tab-close"), i18nc("@action:inmenu", "Close Tab")); + + QAction* selectedAction = menu.exec(event->globalPos()); + if (selectedAction == newTabAction) { + emit openNewActivatedTab(index); + } else if (selectedAction == detachTabAction) { + emit tabDetachRequested(index); + } else if (selectedAction == closeOtherTabsAction) { + const int tabCount = count(); + for (int i = 0; i < index; i++) { + emit tabCloseRequested(0); + } + for (int i = index + 1; i < tabCount; i++) { + emit tabCloseRequested(1); + } + } else if (selectedAction == closeTabAction) { + emit tabCloseRequested(index); + } + + return; + } + + QTabBar::contextMenuEvent(event); +} + +void DolphinTabBar::slotAutoActivationTimeout() +{ + if (m_autoActivationIndex >= 0) { + setCurrentIndex(m_autoActivationIndex); + updateAutoActivationTimer(-1); + } +} + +void DolphinTabBar::updateAutoActivationTimer(const int index) +{ + if (m_autoActivationIndex != index) { + m_autoActivationIndex = index; + + if (m_autoActivationIndex < 0) { + m_autoActivationTimer->stop(); + } else { + m_autoActivationTimer->start(); + } + } +} diff --git a/src/dolphintabbar.h b/src/dolphintabbar.h new file mode 100644 index 000000000..d2b2e9e57 --- /dev/null +++ b/src/dolphintabbar.h @@ -0,0 +1,65 @@ +/*************************************************************************** + * Copyright (C) 2014 by Emmanuel Pescosta <[email protected]> * + * * + * 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_BAR_H +#define DOLPHIN_TAB_BAR_H + +#include <QTabBar> + +class DolphinTabBar : public QTabBar +{ + Q_OBJECT + +public: + explicit DolphinTabBar(QWidget* parent); + +signals: + void openNewActivatedTab(int index); + void tabDropEvent(int index, QDropEvent* event); + void tabDetachRequested(int index); + +protected: + virtual void dragEnterEvent(QDragEnterEvent* event); + virtual void dragLeaveEvent(QDragLeaveEvent* event); + virtual void dragMoveEvent(QDragMoveEvent* event); + virtual void dropEvent(QDropEvent* event); + virtual void mousePressEvent(QMouseEvent* event); + virtual void mouseDoubleClickEvent(QMouseEvent* event); + + /** + * Opens a context menu for the tab on the \a event position. + */ + virtual void contextMenuEvent(QContextMenuEvent* event); + +private slots: + void slotAutoActivationTimeout(); + +private: + /** + * If \a index is a valid index (>= 0), store the index and start the timer + * (if the interval >= 0 ms). If the index is not valid (< 0), stop the timer. + */ + void updateAutoActivationTimer(const int index); + +private: + QTimer* m_autoActivationTimer; + int m_autoActivationIndex; +}; + +#endif // DOLPHIN_TAB_BAR_H diff --git a/src/dolphintabpage.cpp b/src/dolphintabpage.cpp index 4c49869f7..23e33c958 100644 --- a/src/dolphintabpage.cpp +++ b/src/dolphintabpage.cpp @@ -250,7 +250,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 95c02ed0a..278524792 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 000000000..4bb70b4ea --- /dev/null +++ b/src/dolphintabwidget.cpp @@ -0,0 +1,345 @@ +/*************************************************************************** + * Copyright (C) 2014 by Emmanuel Pescosta <[email protected]> * + * * + * 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 <QApplication> +#include <KConfigGroup> +#include <KIcon> +#include <KRun> + +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<DolphinTabPage*>(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<KUrl>& 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<KUrl>::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<KUrl>& 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<KUrl> 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<QWidget*>(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 000000000..a9bef11be --- /dev/null +++ b/src/dolphintabwidget.h @@ -0,0 +1,178 @@ +/*************************************************************************** + * Copyright (C) 2014 by Emmanuel Pescosta <[email protected]> * + * * + * 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 <QTabWidget> +#include <KUrl> + +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<KUrl>& dirs); + + /** + * Opens the directory which contains the files \p files + * and selects all files (implements the --select option + * of Dolphin). + */ + void openFiles(const QList<KUrl>& 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 diff --git a/src/dolphinui.rc b/src/dolphinui.rc index 52826bb43..f197af402 100644 --- a/src/dolphinui.rc +++ b/src/dolphinui.rc @@ -6,6 +6,7 @@ <Action name="new_window" /> <Action name="new_tab" /> <Action name="close_tab" /> + <Action name="undo_close_tab" /> <Separator/> <Action name="rename" /> <Action name="move_to_trash" /> diff --git a/src/panels/places/placesitemmodel.cpp b/src/panels/places/placesitemmodel.cpp index 1f05e07f9..dc2b95c36 100644 --- a/src/panels/places/placesitemmodel.cpp +++ b/src/panels/places/placesitemmodel.cpp @@ -313,7 +313,8 @@ void PlacesItemModel::requestEject(int index) Solid::OpticalDrive* drive = item->device().parent().as<Solid::OpticalDrive>(); if (drive) { connect(drive, SIGNAL(ejectDone(Solid::ErrorType,QVariant,QString)), - this, SLOT(slotStorageTeardownDone(Solid::ErrorType,QVariant))); + this, SLOT(slotStorageTeardownDone(Solid::ErrorType,QVariant)), + Qt::UniqueConnection); drive->eject(); } else { const QString label = item->text(); @@ -330,7 +331,8 @@ void PlacesItemModel::requestTeardown(int index) Solid::StorageAccess* access = item->device().as<Solid::StorageAccess>(); if (access) { connect(access, SIGNAL(teardownDone(Solid::ErrorType,QVariant,QString)), - this, SLOT(slotStorageTeardownDone(Solid::ErrorType,QVariant))); + this, SLOT(slotStorageTeardownDone(Solid::ErrorType,QVariant)), + Qt::UniqueConnection); access->teardown(); } } @@ -359,7 +361,8 @@ void PlacesItemModel::requestStorageSetup(int index) m_storageSetupInProgress[access] = index; connect(access, SIGNAL(setupDone(Solid::ErrorType,QVariant,QString)), - this, SLOT(slotStorageSetupDone(Solid::ErrorType,QVariant,QString))); + this, SLOT(slotStorageSetupDone(Solid::ErrorType,QVariant,QString)), + Qt::UniqueConnection); access->setup(); } @@ -598,6 +601,7 @@ void PlacesItemModel::slotStorageSetupDone(Solid::ErrorType error, { Q_UNUSED(udi); + Q_ASSERT(!m_storageSetupInProgress.isEmpty()); const int index = m_storageSetupInProgress.take(sender()); const PlacesItem* item = placesItem(index); if (!item) { |
