diff --git a/src/dolphintabpage.cpp b/src/dolphintabpage.cpp index 5c42d41ec..1d3a6f08c 100644 --- a/src/dolphintabpage.cpp +++ b/src/dolphintabpage.cpp @@ -1,351 +1,366 @@ /*************************************************************************** * 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 "dolphintabpage.h" #include "dolphinviewcontainer.h" #include "dolphin_generalsettings.h" #include #include DolphinTabPage::DolphinTabPage(const QUrl &primaryUrl, const QUrl &secondaryUrl, QWidget* parent) : QWidget(parent), m_primaryViewActive(true), - m_splitViewEnabled(false) + m_splitViewEnabled(false), + m_active(true) { QVBoxLayout* layout = new QVBoxLayout(this); layout->setSpacing(0); layout->setMargin(0); m_splitter = new QSplitter(Qt::Horizontal, this); m_splitter->setChildrenCollapsible(false); layout->addWidget(m_splitter); // Create a new primary view m_primaryViewContainer = createViewContainer(primaryUrl); connect(m_primaryViewContainer->view(), &DolphinView::urlChanged, this, &DolphinTabPage::activeViewUrlChanged); connect(m_primaryViewContainer->view(), &DolphinView::redirection, this, &DolphinTabPage::slotViewUrlRedirection); m_splitter->addWidget(m_primaryViewContainer); m_primaryViewContainer->show(); if (secondaryUrl.isValid() || GeneralSettings::splitView()) { // Provide a secondary view, if the given secondary url is valid or if the // startup settings are set this way (use the url of the primary view). m_splitViewEnabled = true; const QUrl& url = secondaryUrl.isValid() ? secondaryUrl : primaryUrl; m_secondaryViewContainer = createViewContainer(url); m_splitter->addWidget(m_secondaryViewContainer); m_secondaryViewContainer->show(); } m_primaryViewContainer->setActive(true); } bool DolphinTabPage::primaryViewActive() const { return m_primaryViewActive; } bool DolphinTabPage::splitViewEnabled() const { return m_splitViewEnabled; } void DolphinTabPage::setSplitViewEnabled(bool enabled, const QUrl &secondaryUrl) { if (m_splitViewEnabled != enabled) { m_splitViewEnabled = enabled; if (enabled) { const QUrl& url = (secondaryUrl.isEmpty()) ? m_primaryViewContainer->url() : secondaryUrl; m_secondaryViewContainer = createViewContainer(url); const bool placesSelectorVisible = m_primaryViewContainer->urlNavigator()->isPlacesSelectorVisible(); m_secondaryViewContainer->urlNavigator()->setPlacesSelectorVisible(placesSelectorVisible); m_splitter->addWidget(m_secondaryViewContainer); m_secondaryViewContainer->show(); m_secondaryViewContainer->setActive(true); } else { // Close the view which is active. DolphinViewContainer* view = activeViewContainer(); if (m_primaryViewActive) { // If the primary view is active, we have to swap the pointers // because the secondary view will be the new primary view. qSwap(m_primaryViewContainer, m_secondaryViewContainer); } m_primaryViewContainer->setActive(true); view->close(); view->deleteLater(); } } } DolphinViewContainer* DolphinTabPage::primaryViewContainer() const { return m_primaryViewContainer; } DolphinViewContainer* DolphinTabPage::secondaryViewContainer() const { return m_secondaryViewContainer; } DolphinViewContainer* DolphinTabPage::activeViewContainer() const { return m_primaryViewActive ? m_primaryViewContainer : m_secondaryViewContainer; } KFileItemList DolphinTabPage::selectedItems() const { KFileItemList items = m_primaryViewContainer->view()->selectedItems(); if (m_splitViewEnabled) { items += m_secondaryViewContainer->view()->selectedItems(); } return items; } int DolphinTabPage::selectedItemsCount() const { int selectedItemsCount = m_primaryViewContainer->view()->selectedItemsCount(); if (m_splitViewEnabled) { selectedItemsCount += m_secondaryViewContainer->view()->selectedItemsCount(); } return selectedItemsCount; } void DolphinTabPage::markUrlsAsSelected(const QList& urls) { m_primaryViewContainer->view()->markUrlsAsSelected(urls); if (m_splitViewEnabled) { m_secondaryViewContainer->view()->markUrlsAsSelected(urls); } } void DolphinTabPage::markUrlAsCurrent(const QUrl& url) { m_primaryViewContainer->view()->markUrlAsCurrent(url); if (m_splitViewEnabled) { m_secondaryViewContainer->view()->markUrlAsCurrent(url); } } void DolphinTabPage::setPlacesSelectorVisible(bool visible) { m_primaryViewContainer->urlNavigator()->setPlacesSelectorVisible(visible); if (m_splitViewEnabled) { m_secondaryViewContainer->urlNavigator()->setPlacesSelectorVisible(visible); } } void DolphinTabPage::refreshViews() { m_primaryViewContainer->readSettings(); if (m_splitViewEnabled) { m_secondaryViewContainer->readSettings(); } } QByteArray DolphinTabPage::saveState() const { QByteArray state; QDataStream stream(&state, QIODevice::WriteOnly); stream << quint32(2); // Tab state version stream << m_splitViewEnabled; stream << m_primaryViewContainer->url(); stream << m_primaryViewContainer->urlNavigator()->isUrlEditable(); m_primaryViewContainer->view()->saveState(stream); if (m_splitViewEnabled) { stream << m_secondaryViewContainer->url(); stream << m_secondaryViewContainer->urlNavigator()->isUrlEditable(); m_secondaryViewContainer->view()->saveState(stream); } stream << m_primaryViewActive; stream << m_splitter->saveState(); return state; } void DolphinTabPage::restoreState(const QByteArray& state) { if (state.isEmpty()) { return; } QByteArray sd = state; QDataStream stream(&sd, QIODevice::ReadOnly); // Read the version number of the tab state and check if the version is supported. quint32 version = 0; stream >> version; if (version != 2) { // The version of the tab state isn't supported, we can't restore it. return; } bool isSplitViewEnabled = false; stream >> isSplitViewEnabled; setSplitViewEnabled(isSplitViewEnabled); QUrl primaryUrl; stream >> primaryUrl; m_primaryViewContainer->setUrl(primaryUrl); bool primaryUrlEditable; stream >> primaryUrlEditable; m_primaryViewContainer->urlNavigator()->setUrlEditable(primaryUrlEditable); m_primaryViewContainer->view()->restoreState(stream); if (isSplitViewEnabled) { QUrl secondaryUrl; stream >> secondaryUrl; m_secondaryViewContainer->setUrl(secondaryUrl); bool secondaryUrlEditable; stream >> secondaryUrlEditable; m_secondaryViewContainer->urlNavigator()->setUrlEditable(secondaryUrlEditable); m_secondaryViewContainer->view()->restoreState(stream); } stream >> m_primaryViewActive; if (m_primaryViewActive) { m_primaryViewContainer->setActive(true); } else { Q_ASSERT(m_splitViewEnabled); m_secondaryViewContainer->setActive(true); } QByteArray splitterState; stream >> splitterState; m_splitter->restoreState(splitterState); } void DolphinTabPage::restoreStateV1(const QByteArray& state) { if (state.isEmpty()) { return; } QByteArray sd = state; QDataStream stream(&sd, QIODevice::ReadOnly); bool isSplitViewEnabled = false; stream >> isSplitViewEnabled; setSplitViewEnabled(isSplitViewEnabled); QUrl primaryUrl; stream >> primaryUrl; m_primaryViewContainer->setUrl(primaryUrl); bool primaryUrlEditable; stream >> primaryUrlEditable; m_primaryViewContainer->urlNavigator()->setUrlEditable(primaryUrlEditable); if (isSplitViewEnabled) { QUrl secondaryUrl; stream >> secondaryUrl; m_secondaryViewContainer->setUrl(secondaryUrl); bool secondaryUrlEditable; stream >> secondaryUrlEditable; m_secondaryViewContainer->urlNavigator()->setUrlEditable(secondaryUrlEditable); } stream >> m_primaryViewActive; if (m_primaryViewActive) { m_primaryViewContainer->setActive(true); } else { Q_ASSERT(m_splitViewEnabled); m_secondaryViewContainer->setActive(true); } QByteArray splitterState; stream >> splitterState; m_splitter->restoreState(splitterState); } +void DolphinTabPage::setActive(bool active) +{ + if (active) { + m_active = active; + } else { + // we should bypass changing active view in split mode + m_active = !m_splitViewEnabled; + } + // we want view to fire activated when goes from false to true + activeViewContainer()->setActive(active); +} + void DolphinTabPage::slotViewActivated() { const DolphinView* oldActiveView = activeViewContainer()->view(); // Set the view, which was active before, to inactive - // and update the active view type. - if (m_splitViewEnabled) { - activeViewContainer()->setActive(false); - m_primaryViewActive = !m_primaryViewActive; - } else { - m_primaryViewActive = true; + // and update the active view type, if tab is active + if (m_active) { + if (m_splitViewEnabled) { + activeViewContainer()->setActive(false); + m_primaryViewActive = !m_primaryViewActive; + } else { + m_primaryViewActive = true; + } } const DolphinView* newActiveView = activeViewContainer()->view(); if (newActiveView != oldActiveView) { disconnect(oldActiveView, &DolphinView::urlChanged, this, &DolphinTabPage::activeViewUrlChanged); disconnect(oldActiveView, &DolphinView::redirection, this, &DolphinTabPage::slotViewUrlRedirection); connect(newActiveView, &DolphinView::urlChanged, this, &DolphinTabPage::activeViewUrlChanged); connect(newActiveView, &DolphinView::redirection, this, &DolphinTabPage::slotViewUrlRedirection); } emit activeViewUrlChanged(activeViewContainer()->url()); emit activeViewChanged(activeViewContainer()); } void DolphinTabPage::slotViewUrlRedirection(const QUrl& oldUrl, const QUrl& newUrl) { Q_UNUSED(oldUrl); emit activeViewUrlChanged(newUrl); } void DolphinTabPage::switchActiveView() { if (!m_splitViewEnabled) { return; } if (m_primaryViewActive) { m_secondaryViewContainer->setActive(true); } else { m_primaryViewContainer->setActive(true); } } DolphinViewContainer* DolphinTabPage::createViewContainer(const QUrl& url) const { DolphinViewContainer* container = new DolphinViewContainer(url, m_splitter); container->setActive(false); const DolphinView* view = container->view(); connect(view, &DolphinView::activated, this, &DolphinTabPage::slotViewActivated); connect(view, &DolphinView::toggleActiveViewRequested, this, &DolphinTabPage::switchActiveView); return container; } diff --git a/src/dolphintabpage.h b/src/dolphintabpage.h index 85295bf92..b8772602d 100644 --- a/src/dolphintabpage.h +++ b/src/dolphintabpage.h @@ -1,170 +1,177 @@ /*************************************************************************** * 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_PAGE_H #define DOLPHIN_TAB_PAGE_H #include #include #include class QSplitter; class DolphinViewContainer; class KFileItemList; class DolphinTabPage : public QWidget { Q_OBJECT public: explicit DolphinTabPage(const QUrl& primaryUrl, const QUrl& secondaryUrl = QUrl(), QWidget* parent = 0); /** * @return True if primary view is the active view in this tab. */ bool primaryViewActive() const; /** * @return True if split view is enabled. */ bool splitViewEnabled() const; /** * Enables or disables the split view mode. * * If \a enabled is true, it creates a secondary view with the url of the primary view. */ void setSplitViewEnabled(bool enabled, const QUrl &secondaryUrl = QUrl()); /** * @return The primary view containter. */ DolphinViewContainer* primaryViewContainer() const; /** * @return The secondary view containter, can be 0 if split view is disabled. */ DolphinViewContainer* secondaryViewContainer() const; /** * @return DolphinViewContainer of the active view */ DolphinViewContainer* activeViewContainer() const; /** * Returns the selected items. The list is empty if no item has been * selected. */ KFileItemList selectedItems() const; /** * Returns the number of selected items (this is faster than * invoking selectedItems().count()). */ int selectedItemsCount() const; /** * Marks the items indicated by \p urls to get selected after the * directory DolphinView::url() has been loaded. Note that nothing * gets selected if no loading of a directory has been triggered * by DolphinView::setUrl() or DolphinView::reload(). */ void markUrlsAsSelected(const QList &urls); /** * Marks the item indicated by \p url to be scrolled to and as the * current item after directory DolphinView::url() has been loaded. */ void markUrlAsCurrent(const QUrl& url); /** * Sets the places selector visible, if \a visible is true. * The places selector allows to select the places provided * by the places model passed in the constructor. Per default * the places selector is visible. */ void setPlacesSelectorVisible(bool visible); /** * Refreshes the views of the main window by recreating them according to * the given Dolphin settings. */ void refreshViews(); /** * Saves all tab related properties (urls, splitter layout, ...). * * @return A byte-array which contains all properties. */ QByteArray saveState() const; /** * Restores all tab related properties (urls, splitter layout, ...) from * the given \a state. */ void restoreState(const QByteArray& state); /** * Restores all tab related properties (urls, splitter layout, ...) from * the given \a state. * * @deprecated The first tab state version has no version number, we keep * this method to restore old states (<= Dolphin 4.14.x). */ Q_DECL_DEPRECATED void restoreStateV1(const QByteArray& state); + /** + * Set whether the tab page is active + * + */ + void setActive(bool active); + signals: void activeViewChanged(DolphinViewContainer* viewContainer); void activeViewUrlChanged(const QUrl& url); private slots: /** * Handles the view activated event. * * It sets the previous active view to inactive, updates the current * active view type and triggers the activeViewChanged event. */ void slotViewActivated(); /** * Handles the view url redirection event. * * It emits the activeViewUrlChanged signal with the url \a newUrl. */ void slotViewUrlRedirection(const QUrl& oldUrl, const QUrl& newUrl); void switchActiveView(); private: /** * Creates a new view container and does the default initialization. */ DolphinViewContainer* createViewContainer(const QUrl& url) const; private: QSplitter* m_splitter; QPointer m_primaryViewContainer; QPointer m_secondaryViewContainer; bool m_primaryViewActive; bool m_splitViewEnabled; + bool m_active; }; #endif // DOLPHIN_TAB_PAGE_H diff --git a/src/dolphintabwidget.cpp b/src/dolphintabwidget.cpp index bcd4a49fd..94b7a0144 100644 --- a/src/dolphintabwidget.cpp +++ b/src/dolphintabwidget.cpp @@ -1,359 +1,359 @@ /*************************************************************************** * 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 #include #include #include #include #include DolphinTabWidget::DolphinTabWidget(QWidget* parent) : QTabWidget(parent), m_placesSelectorVisible(true), - m_previousTab(-1) + m_previousTab(0) { connect(this, &DolphinTabWidget::tabCloseRequested, this, static_cast(&DolphinTabWidget::closeTab)); connect(this, &DolphinTabWidget::currentChanged, this, &DolphinTabWidget::currentTabChanged); DolphinTabBar* tabBar = new DolphinTabBar(this); connect(tabBar, &DolphinTabBar::openNewActivatedTab, this, static_cast(&DolphinTabWidget::openNewActivatedTab)); connect(tabBar, &DolphinTabBar::tabDropEvent, this, &DolphinTabWidget::tabDropEvent); connect(tabBar, &DolphinTabBar::tabDetachRequested, this, &DolphinTabWidget::detachTab); tabBar->hide(); setTabBar(tabBar); setDocumentMode(true); setElideMode(Qt::ElideRight); setUsesScrollButtons(true); } DolphinTabPage* DolphinTabWidget::currentTabPage() const { return tabPageAt(currentIndex()); } DolphinTabPage* DolphinTabWidget::tabPageAt(const int index) const { return static_cast(widget(index)); } void DolphinTabWidget::saveProperties(KConfigGroup& group) const { const int tabCount = count(); group.writeEntry("Tab Count", tabCount); group.writeEntry("Active Tab Index", currentIndex()); for (int i = 0; i < tabCount; ++i) { const DolphinTabPage* tabPage = tabPageAt(i); group.writeEntry("Tab Data " % QString::number(i), tabPage->saveState()); } } void DolphinTabWidget::readProperties(const KConfigGroup& group) { const int tabCount = group.readEntry("Tab Count", 0); for (int i = 0; i < tabCount; ++i) { if (i >= count()) { openNewActivatedTab(); } if (group.hasKey("Tab Data " % QString::number(i))) { // Tab state created with Dolphin > 4.14.x const QByteArray state = group.readEntry("Tab Data " % QString::number(i), QByteArray()); tabPageAt(i)->restoreState(state); } else { // Tab state created with Dolphin <= 4.14.x const QByteArray state = group.readEntry("Tab " % QString::number(i), QByteArray()); tabPageAt(i)->restoreStateV1(state); } } const int index = group.readEntry("Active Tab Index", 0); setCurrentIndex(index); } void DolphinTabWidget::refreshViews() { const int tabCount = count(); for (int i = 0; i < tabCount; ++i) { tabPageAt(i)->refreshViews(); } } void DolphinTabWidget::openNewActivatedTab() { const DolphinViewContainer* oldActiveViewContainer = currentTabPage()->activeViewContainer(); Q_ASSERT(oldActiveViewContainer); const bool isUrlEditable = oldActiveViewContainer->urlNavigator()->isUrlEditable(); openNewActivatedTab(oldActiveViewContainer->url()); DolphinViewContainer* newActiveViewContainer = currentTabPage()->activeViewContainer(); Q_ASSERT(newActiveViewContainer); // The URL navigator of the new tab should have the same editable state // as the current tab KUrlNavigator* navigator = newActiveViewContainer->urlNavigator(); navigator->setUrlEditable(isUrlEditable); if (isUrlEditable) { // If a new tab is opened and the URL is editable, assure that // the user can edit the URL without manually setting the focus navigator->setFocus(); } } void DolphinTabWidget::openNewActivatedTab(const QUrl& primaryUrl, const QUrl& secondaryUrl) { openNewTab(primaryUrl, secondaryUrl); setCurrentIndex(count() - 1); } void DolphinTabWidget::openNewTab(const QUrl& primaryUrl, const QUrl& secondaryUrl) { QWidget* focusWidget = QApplication::focusWidget(); DolphinTabPage* tabPage = new DolphinTabPage(primaryUrl, secondaryUrl, this); tabPage->setPlacesSelectorVisible(m_placesSelectorVisible); connect(tabPage, &DolphinTabPage::activeViewChanged, this, &DolphinTabWidget::activeViewChanged); connect(tabPage, &DolphinTabPage::activeViewUrlChanged, this, &DolphinTabWidget::tabUrlChanged); addTab(tabPage, QIcon::fromTheme(KIO::iconNameForUrl(primaryUrl)), tabName(primaryUrl)); if (focusWidget) { // The DolphinViewContainer grabbed the keyboard focus. As the tab is opened // in background, assure that the previous focused widget gets the focus back. focusWidget->setFocus(); } } void DolphinTabWidget::openDirectories(const QList& dirs, bool splitView) { Q_ASSERT(dirs.size() > 0); QList::const_iterator it = dirs.constBegin(); while (it != dirs.constEnd()) { const QUrl& primaryUrl = *(it++); if (splitView && (it != dirs.constEnd())) { const QUrl& secondaryUrl = *(it++); openNewTab(primaryUrl, secondaryUrl); } else { openNewTab(primaryUrl); } } } void DolphinTabWidget::openFiles(const QList& files, bool splitView) { Q_ASSERT(files.size() > 0); // Get all distinct directories from 'files' and open a tab // for each directory. If the "split view" option is enabled, two // directories are shown inside one tab (see openDirectories()). QList dirs; foreach (const QUrl& url, files) { const QUrl dir(url.adjusted(QUrl::RemoveFilename)); if (!dirs.contains(dir)) { dirs.append(dir); } } const int oldTabCount = count(); openDirectories(dirs, splitView); const int tabCount = count(); // Select the files. Although the files can be split between several // tabs, there is no need to split 'files' accordingly, as // the DolphinView will just ignore invalid selections. for (int i = oldTabCount; i < tabCount; ++i) { DolphinTabPage* tabPage = tabPageAt(i); tabPage->markUrlsAsSelected(files); tabPage->markUrlAsCurrent(files.first()); } } void DolphinTabWidget::closeTab() { closeTab(currentIndex()); } void DolphinTabWidget::closeTab(const int index) { Q_ASSERT(index >= 0); Q_ASSERT(index < count()); if (count() < 2) { // Never close the last tab. return; } DolphinTabPage* tabPage = tabPageAt(index); emit rememberClosedTab(tabPage->activeViewContainer()->url(), tabPage->saveState()); removeTab(index); tabPage->deleteLater(); } void DolphinTabWidget::activateNextTab() { const int index = currentIndex() + 1; setCurrentIndex(index < count() ? index : 0); } void DolphinTabWidget::activatePrevTab() { const int index = currentIndex() - 1; setCurrentIndex(index >= 0 ? index : (count() - 1)); } void DolphinTabWidget::slotPlacesPanelVisibilityChanged(bool visible) { // The places-selector from the URL navigator should only be shown // if the places dock is invisible m_placesSelectorVisible = !visible; const int tabCount = count(); for (int i = 0; i < tabCount; ++i) { DolphinTabPage* tabPage = tabPageAt(i); tabPage->setPlacesSelectorVisible(m_placesSelectorVisible); } } void DolphinTabWidget::restoreClosedTab(const QByteArray& state) { openNewActivatedTab(); currentTabPage()->restoreState(state); } void DolphinTabWidget::detachTab(int index) { Q_ASSERT(index >= 0); QStringList args; const DolphinTabPage* tabPage = tabPageAt(index); args << tabPage->primaryViewContainer()->url().url(); if (tabPage->splitViewEnabled()) { args << tabPage->secondaryViewContainer()->url().url(); args << QStringLiteral("--split"); } const QString command = QStringLiteral("dolphin %1").arg(KShell::joinArgs(args)); 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) { DolphinView* view = tabPageAt(index)->activeViewContainer()->view(); view->dropUrls(view->url(), event); } } void DolphinTabWidget::tabUrlChanged(const QUrl& url) { const int index = indexOf(qobject_cast(sender())); if (index >= 0) { tabBar()->setTabText(index, tabName(url)); tabBar()->setTabIcon(index, QIcon::fromTheme(KIO::iconNameForUrl(url))); // Emit the currentUrlChanged signal if the url of the current tab has been changed. if (index == currentIndex()) { emit currentUrlChanged(url); } } } void DolphinTabWidget::currentTabChanged(int index) { - DolphinViewContainer* viewContainer = tabPageAt(index)->activeViewContainer(); - viewContainer->setActive(true); + // previous tab deactivation + if (DolphinTabPage* tabPage = tabPageAt(m_previousTab)) { + tabPage->setActive(false); + } + DolphinTabPage* tabPage = tabPageAt(index); + DolphinViewContainer* viewContainer = tabPage->activeViewContainer(); emit activeViewChanged(viewContainer); emit currentUrlChanged(viewContainer->url()); - viewContainer->view()->setFocus(); - - if (tabPageAt(m_previousTab)) { - tabPageAt(m_previousTab)->activeViewContainer()->setActive(false); - } + tabPage->setActive(true); m_previousTab = index; } 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 QUrl& url) const { QString name; if (url == QUrl(QStringLiteral("file:///"))) { name = '/'; } else { name = url.adjusted(QUrl::StripTrailingSlash).fileName(); if (name.isEmpty()) { name = url.scheme(); } else { // Make sure that a '&' inside the directory name is displayed correctly // and not misinterpreted as a keyboard shortcut in QTabBar::setTabText() name.replace('&', QLatin1String("&&")); } } return name; } diff --git a/src/search/dolphinsearchbox.cpp b/src/search/dolphinsearchbox.cpp index 5d5906b78..c6943c608 100644 --- a/src/search/dolphinsearchbox.cpp +++ b/src/search/dolphinsearchbox.cpp @@ -1,539 +1,544 @@ /*************************************************************************** * Copyright (C) 2010 by Peter Penz * * * * 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 "dolphinsearchbox.h" #include "dolphin_searchsettings.h" #include "dolphinfacetswidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_BALOO #include #include #endif #include DolphinSearchBox::DolphinSearchBox(QWidget* parent) : QWidget(parent), m_startedSearching(false), m_active(true), m_topLayout(0), m_searchLabel(0), m_searchInput(0), m_optionsScrollArea(0), m_fileNameButton(0), m_contentButton(0), m_separator(0), m_fromHereButton(0), m_everywhereButton(0), m_facetsToggleButton(0), m_facetsWidget(0), m_searchPath(), m_startSearchTimer(0) { } DolphinSearchBox::~DolphinSearchBox() { saveSettings(); } void DolphinSearchBox::setText(const QString& text) { m_searchInput->setText(text); } QString DolphinSearchBox::text() const { return m_searchInput->text(); } void DolphinSearchBox::setSearchPath(const QUrl& url) { m_searchPath = url; QFontMetrics metrics(m_fromHereButton->font()); const int maxWidth = metrics.height() * 8; QString location = url.fileName(); if (location.isEmpty()) { if (url.isLocalFile()) { location = QStringLiteral("/"); } else { location = url.scheme() + QLatin1String(" - ") + url.host(); } } const QString elidedLocation = metrics.elidedText(location, Qt::ElideMiddle, maxWidth); m_fromHereButton->setText(i18nc("action:button", "From Here (%1)", elidedLocation)); const bool showSearchFromButtons = url.isLocalFile(); m_separator->setVisible(showSearchFromButtons); m_fromHereButton->setVisible(showSearchFromButtons); m_everywhereButton->setVisible(showSearchFromButtons); bool hasFacetsSupport = false; #ifdef HAVE_BALOO const Baloo::IndexerConfig searchInfo; hasFacetsSupport = searchInfo.fileIndexingEnabled() && searchInfo.shouldBeIndexed(m_searchPath.toLocalFile()); #endif m_facetsWidget->setEnabled(hasFacetsSupport); } QUrl DolphinSearchBox::searchPath() const { return m_searchPath; } QUrl DolphinSearchBox::urlForSearching() const { QUrl url; bool useBalooSearch = false; #ifdef HAVE_BALOO const Baloo::IndexerConfig searchInfo; useBalooSearch = searchInfo.fileIndexingEnabled() && searchInfo.shouldBeIndexed(m_searchPath.toLocalFile()); #endif if (useBalooSearch) { url = balooUrlForSearching(); } else { url.setScheme(QStringLiteral("filenamesearch")); QUrlQuery query; query.addQueryItem(QStringLiteral("search"), m_searchInput->text()); if (m_contentButton->isChecked()) { query.addQueryItem(QStringLiteral("checkContent"), QStringLiteral("yes")); } QString encodedUrl; if (m_everywhereButton->isChecked()) { // It is very unlikely, that the majority of Dolphins target users // mean "the whole harddisk" instead of "my home folder" when // selecting the "Everywhere" button. encodedUrl = QDir::homePath(); } else { encodedUrl = m_searchPath.url(); } query.addQueryItem(QStringLiteral("url"), encodedUrl); url.setQuery(query); } return url; } void DolphinSearchBox::fromSearchUrl(const QUrl& url) { if (url.scheme() == QLatin1String("baloosearch")) { fromBalooSearchUrl(url); } else if (url.scheme() == QLatin1String("filenamesearch")) { const QUrlQuery query(url); setText(query.queryItemValue(QStringLiteral("search"))); setSearchPath(QUrl::fromUserInput(query.queryItemValue(QStringLiteral("url")), QString(), QUrl::AssumeLocalFile)); m_contentButton->setChecked(query.queryItemValue(QStringLiteral("checkContent")) == QLatin1String("yes")); } else { setText(QString()); setSearchPath(url); } } void DolphinSearchBox::selectAll() { m_searchInput->selectAll(); } void DolphinSearchBox::setActive(bool active) { if (active != m_active) { m_active = active; if (active) { emit activated(); } } } bool DolphinSearchBox::isActive() const { return m_active; } bool DolphinSearchBox::event(QEvent* event) { if (event->type() == QEvent::Polish) { init(); } return QWidget::event(event); } void DolphinSearchBox::showEvent(QShowEvent* event) { if (!event->spontaneous()) { m_searchInput->setFocus(); m_startedSearching = false; } } void DolphinSearchBox::hideEvent(QHideEvent* event) { Q_UNUSED(event); m_startedSearching = false; m_startSearchTimer->stop(); } void DolphinSearchBox::keyReleaseEvent(QKeyEvent* event) { QWidget::keyReleaseEvent(event); if (event->key() == Qt::Key_Escape) { if (m_searchInput->text().isEmpty()) { emit closeRequest(); } else { m_searchInput->clear(); } } } bool DolphinSearchBox::eventFilter(QObject* obj, QEvent* event) { switch (event->type()) { case QEvent::FocusIn: - setActive(true); - setFocus(); + // #379135: we get the FocusIn event when we close a tab but we don't want to emit + // the activated() signal before the removeTab() call in DolphinTabWidget::closeTab() returns. + // To avoid this issue, we delay the activation of the search box. + QTimer::singleShot(0, this, [this] { + setActive(true); + setFocus(); + }); break; default: break; } return QObject::eventFilter(obj, event); } void DolphinSearchBox::emitSearchRequest() { m_startSearchTimer->stop(); m_startedSearching = true; emit searchRequest(); } void DolphinSearchBox::emitCloseRequest() { m_startSearchTimer->stop(); m_startedSearching = false; emit closeRequest(); } void DolphinSearchBox::slotConfigurationChanged() { saveSettings(); if (m_startedSearching) { emitSearchRequest(); } } void DolphinSearchBox::slotSearchTextChanged(const QString& text) { if (text.isEmpty()) { m_startSearchTimer->stop(); } else { m_startSearchTimer->start(); } emit searchTextChanged(text); } void DolphinSearchBox::slotReturnPressed() { emitSearchRequest(); emit returnPressed(); } void DolphinSearchBox::slotFacetsButtonToggled() { const bool facetsIsVisible = !m_facetsWidget->isVisible(); m_facetsWidget->setVisible(facetsIsVisible); updateFacetsToggleButton(); } void DolphinSearchBox::slotFacetChanged() { m_startedSearching = true; m_startSearchTimer->stop(); emit searchRequest(); } void DolphinSearchBox::initButton(QToolButton* button) { button->installEventFilter(this); button->setAutoExclusive(true); button->setAutoRaise(true); button->setCheckable(true); connect(button, &QToolButton::clicked, this, &DolphinSearchBox::slotConfigurationChanged); } void DolphinSearchBox::loadSettings() { if (SearchSettings::location() == QLatin1String("Everywhere")) { m_everywhereButton->setChecked(true); } else { m_fromHereButton->setChecked(true); } if (SearchSettings::what() == QLatin1String("Content")) { m_contentButton->setChecked(true); } else { m_fileNameButton->setChecked(true); } m_facetsWidget->setVisible(SearchSettings::showFacetsWidget()); } void DolphinSearchBox::saveSettings() { SearchSettings::setLocation(m_fromHereButton->isChecked() ? QStringLiteral("FromHere") : QStringLiteral("Everywhere")); SearchSettings::setWhat(m_fileNameButton->isChecked() ? QStringLiteral("FileName") : QStringLiteral("Content")); SearchSettings::setShowFacetsWidget(m_facetsToggleButton->isChecked()); SearchSettings::self()->save(); } void DolphinSearchBox::init() { // Create close button QToolButton* closeButton = new QToolButton(this); closeButton->setAutoRaise(true); closeButton->setIcon(QIcon::fromTheme(QStringLiteral("dialog-close"))); closeButton->setToolTip(i18nc("@info:tooltip", "Quit searching")); connect(closeButton, &QToolButton::clicked, this, &DolphinSearchBox::emitCloseRequest); // Create search label m_searchLabel = new QLabel(this); // Create search box m_searchInput = new QLineEdit(this); m_searchInput->installEventFilter(this); m_searchInput->setClearButtonEnabled(true); m_searchInput->setFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont)); connect(m_searchInput, &QLineEdit::returnPressed, this, &DolphinSearchBox::slotReturnPressed); connect(m_searchInput, &QLineEdit::textChanged, this, &DolphinSearchBox::slotSearchTextChanged); setFocusProxy(m_searchInput); // Apply layout for the search input QHBoxLayout* searchInputLayout = new QHBoxLayout(); searchInputLayout->setMargin(0); searchInputLayout->addWidget(closeButton); searchInputLayout->addWidget(m_searchLabel); searchInputLayout->addWidget(m_searchInput); // Create "Filename" and "Content" button m_fileNameButton = new QToolButton(this); m_fileNameButton->setText(i18nc("action:button", "Filename")); initButton(m_fileNameButton); m_contentButton = new QToolButton(); m_contentButton->setText(i18nc("action:button", "Content")); initButton(m_contentButton); QButtonGroup* searchWhatGroup = new QButtonGroup(this); searchWhatGroup->addButton(m_fileNameButton); searchWhatGroup->addButton(m_contentButton); m_separator = new KSeparator(Qt::Vertical, this); // Create "From Here" and "Everywhere"button m_fromHereButton = new QToolButton(this); m_fromHereButton->setText(i18nc("action:button", "From Here")); initButton(m_fromHereButton); m_everywhereButton = new QToolButton(this); m_everywhereButton->setText(i18nc("action:button", "Everywhere")); initButton(m_everywhereButton); QButtonGroup* searchLocationGroup = new QButtonGroup(this); searchLocationGroup->addButton(m_fromHereButton); searchLocationGroup->addButton(m_everywhereButton); auto moreSearchToolsButton = new QToolButton(this); moreSearchToolsButton->setAutoRaise(true); moreSearchToolsButton->setPopupMode(QToolButton::InstantPopup); moreSearchToolsButton->setIcon(QIcon::fromTheme("arrow-down-double")); moreSearchToolsButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); moreSearchToolsButton->setText(i18n("More Search Tools")); moreSearchToolsButton->setMenu(new QMenu(this)); connect(moreSearchToolsButton->menu(), &QMenu::aboutToShow, moreSearchToolsButton->menu(), [this, moreSearchToolsButton]() { m_menuFactory.reset(new KMoreToolsMenuFactory("dolphin/search-tools")); moreSearchToolsButton->menu()->clear(); m_menuFactory->fillMenuFromGroupingNames(moreSearchToolsButton->menu(), { "files-find" }, this->m_searchPath); } ); // Create "Facets" widgets m_facetsToggleButton = new QToolButton(this); m_facetsToggleButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); initButton(m_facetsToggleButton); connect(m_facetsToggleButton, &QToolButton::clicked, this, &DolphinSearchBox::slotFacetsButtonToggled); m_facetsWidget = new DolphinFacetsWidget(this); m_facetsWidget->installEventFilter(this); m_facetsWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); connect(m_facetsWidget, &DolphinFacetsWidget::facetChanged, this, &DolphinSearchBox::slotFacetChanged); // Apply layout for the options QHBoxLayout* optionsLayout = new QHBoxLayout(); optionsLayout->setMargin(0); optionsLayout->addWidget(m_fileNameButton); optionsLayout->addWidget(m_contentButton); optionsLayout->addWidget(m_separator); optionsLayout->addWidget(m_fromHereButton); optionsLayout->addWidget(m_everywhereButton); optionsLayout->addWidget(new KSeparator(Qt::Vertical, this)); optionsLayout->addWidget(m_facetsToggleButton); optionsLayout->addWidget(moreSearchToolsButton); optionsLayout->addStretch(1); // Put the options into a QScrollArea. This prevents increasing the view width // in case that not enough width for the options is available. QWidget* optionsContainer = new QWidget(this); optionsContainer->setLayout(optionsLayout); m_optionsScrollArea = new QScrollArea(this); m_optionsScrollArea->setFrameShape(QFrame::NoFrame); m_optionsScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_optionsScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_optionsScrollArea->setMaximumHeight(optionsContainer->sizeHint().height()); m_optionsScrollArea->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); m_optionsScrollArea->setWidget(optionsContainer); m_optionsScrollArea->setWidgetResizable(true); m_topLayout = new QVBoxLayout(this); m_topLayout->setMargin(0); m_topLayout->addLayout(searchInputLayout); m_topLayout->addWidget(m_optionsScrollArea); m_topLayout->addWidget(m_facetsWidget); loadSettings(); // The searching should be started automatically after the user did not change // the text within one second m_startSearchTimer = new QTimer(this); m_startSearchTimer->setSingleShot(true); m_startSearchTimer->setInterval(1000); connect(m_startSearchTimer, &QTimer::timeout, this, &DolphinSearchBox::emitSearchRequest); updateFacetsToggleButton(); } QUrl DolphinSearchBox::balooUrlForSearching() const { #ifdef HAVE_BALOO const QString text = m_searchInput->text(); Baloo::Query query; query.addType(m_facetsWidget->facetType()); QStringList queryStrings; QString ratingQuery = m_facetsWidget->ratingTerm(); if (!ratingQuery.isEmpty()) { queryStrings << ratingQuery; } if (m_contentButton->isChecked()) { queryStrings << text; } else if (!text.isEmpty()) { queryStrings << QStringLiteral("filename:\"%1\"").arg(text); } if (m_fromHereButton->isChecked()) { query.setIncludeFolder(m_searchPath.toLocalFile()); } query.setSearchString(queryStrings.join(QStringLiteral(" "))); return query.toSearchUrl(i18nc("@title UDS_DISPLAY_NAME for a KIO directory listing. %1 is the query the user entered.", "Query Results from '%1'", text)); #else return QUrl(); #endif } void DolphinSearchBox::fromBalooSearchUrl(const QUrl& url) { #ifdef HAVE_BALOO const Baloo::Query query = Baloo::Query::fromSearchUrl(url); // Block all signals to avoid unnecessary "searchRequest" signals // while we adjust the search text and the facet widget. blockSignals(true); const QString customDir = query.includeFolder(); if (!customDir.isEmpty()) { setSearchPath(QUrl::fromLocalFile(customDir)); } else { setSearchPath(QUrl::fromLocalFile(QDir::homePath())); } setText(query.searchString()); QStringList types = query.types(); if (!types.isEmpty()) { m_facetsWidget->setFacetType(types.first()); } const QStringList subTerms = query.searchString().split(' ', QString::SkipEmptyParts); foreach (const QString& subTerm, subTerms) { if (subTerm.startsWith(QLatin1String("filename:"))) { const QString value = subTerm.mid(9); setText(value); } else if (m_facetsWidget->isRatingTerm(subTerm)) { m_facetsWidget->setRatingTerm(subTerm); } } m_startSearchTimer->stop(); blockSignals(false); #else Q_UNUSED(url); #endif } void DolphinSearchBox::updateFacetsToggleButton() { const bool facetsIsVisible = SearchSettings::showFacetsWidget(); m_facetsToggleButton->setChecked(facetsIsVisible ? true : false); m_facetsToggleButton->setIcon(QIcon::fromTheme(facetsIsVisible ? QStringLiteral("arrow-up-double") : QStringLiteral("arrow-down-double"))); m_facetsToggleButton->setText(facetsIsVisible ? i18nc("action:button", "Fewer Options") : i18nc("action:button", "More Options")); }