X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/9aee5d22513f0367febab54b38b3a7dc58d120bb..f57ee4b64d924fca85c6ce0be659cd6235f959a9:/src/search/dolphinsearchbox.cpp diff --git a/src/search/dolphinsearchbox.cpp b/src/search/dolphinsearchbox.cpp index 524c4462f..239280280 100644 --- a/src/search/dolphinsearchbox.cpp +++ b/src/search/dolphinsearchbox.cpp @@ -17,53 +17,53 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * **************************************************************************/ +#include "global.h" #include "dolphinsearchbox.h" #include "dolphin_searchsettings.h" #include "dolphinfacetswidget.h" +#include "dolphinquery.h" +#include "panels/places/placesitemmodel.h" -#include -#include #include +#include #include +#include +#ifdef HAVE_BALOO +#include +#include +#endif #include #include -#include +#include #include +#include #include #include +#include #include +#include #include #include -#include #include -#include -#ifdef HAVE_BALOO - #include - #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_topLayout(nullptr), + m_searchInput(nullptr), + m_saveSearchAction(nullptr), + m_optionsScrollArea(nullptr), + m_fileNameButton(nullptr), + m_contentButton(nullptr), + m_separator(nullptr), + m_fromHereButton(nullptr), + m_everywhereButton(nullptr), + m_facetsWidget(nullptr), m_searchPath(), - m_startSearchTimer(0) + m_startSearchTimer(nullptr) { } @@ -84,70 +84,59 @@ QString DolphinSearchBox::text() const void DolphinSearchBox::setSearchPath(const QUrl& url) { + if (url == m_searchPath) { + return; + } + + const QUrl cleanedUrl = url.adjusted(QUrl::RemoveUserInfo | QUrl::StripTrailingSlash); + + if (cleanedUrl.path() == QDir::homePath()) { + m_fromHereButton->setChecked(false); + m_everywhereButton->setChecked(true); + if (!m_searchPath.isEmpty()) { + return; + } + } else { + m_everywhereButton->setChecked(false); + m_fromHereButton->setChecked(true); + } + m_searchPath = url; QFontMetrics metrics(m_fromHereButton->font()); const int maxWidth = metrics.height() * 8; - QString location = url.fileName(); + QString location = cleanedUrl.fileName(); if (location.isEmpty()) { - if (url.isLocalFile()) { - location = QLatin1String("/"); - } else { - location = url.scheme() + QLatin1String(" - ") + url.host(); - } + location = cleanedUrl.toString(QUrl::PreferLocalFile); } - 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); + m_fromHereButton->setToolTip(i18nc("action:button", "Limit search to '%1' and its subfolders", cleanedUrl.toString(QUrl::PreferLocalFile))); } QUrl DolphinSearchBox::searchPath() const { - return m_searchPath; + return m_everywhereButton->isChecked() ? QUrl::fromLocalFile(QDir::homePath()) : 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) { + + if (isIndexingEnabled()) { url = balooUrlForSearching(); } else { - url.setScheme("filenamesearch"); + url.setScheme(QStringLiteral("filenamesearch")); QUrlQuery query; - query.addQueryItem("search", m_searchInput->text()); + query.addQueryItem(QStringLiteral("search"), m_searchInput->text()); if (m_contentButton->isChecked()) { - query.addQueryItem("checkContent", "yes"); + 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("url", encodedUrl); + query.addQueryItem(QStringLiteral("url"), searchPath().url()); + query.addQueryItem(QStringLiteral("title"), queryTitle(m_searchInput->text())); url.setQuery(query); } @@ -157,17 +146,24 @@ QUrl DolphinSearchBox::urlForSearching() const void DolphinSearchBox::fromSearchUrl(const QUrl& url) { - if (url.scheme() == "baloosearch") { - fromBalooSearchUrl(url); - } else if (url.scheme() == "filenamesearch") { + if (DolphinQuery::supportsScheme(url.scheme())) { + const DolphinQuery query = DolphinQuery::fromSearchUrl(url); + updateFromQuery(query); + } else if (url.scheme() == QLatin1String("filenamesearch")) { const QUrlQuery query(url); - setText(query.queryItemValue("search")); - setSearchPath(QUrl::fromUserInput(query.queryItemValue("url"), QString(), QUrl::AssumeLocalFile)); - m_contentButton->setChecked(query.queryItemValue("checkContent") == "yes"); + setText(query.queryItemValue(QStringLiteral("search"))); + if (m_searchPath.scheme() != url.scheme()) { + m_searchPath = QUrl(); + } + setSearchPath(QUrl::fromUserInput(query.queryItemValue(QStringLiteral("url")), QString(), QUrl::AssumeLocalFile)); + m_contentButton->setChecked(query.queryItemValue(QStringLiteral("checkContent")) == QLatin1String("yes")); } else { setText(QString()); + m_searchPath = QUrl(); setSearchPath(url); } + + updateFacetsVisible(); } void DolphinSearchBox::selectAll() @@ -207,6 +203,13 @@ void DolphinSearchBox::showEvent(QShowEvent* event) } } +void DolphinSearchBox::hideEvent(QHideEvent* event) +{ + Q_UNUSED(event) + m_startedSearching = false; + m_startSearchTimer->stop(); +} + void DolphinSearchBox::keyReleaseEvent(QKeyEvent* event) { QWidget::keyReleaseEvent(event); @@ -217,14 +220,26 @@ void DolphinSearchBox::keyReleaseEvent(QKeyEvent* event) m_searchInput->clear(); } } + else if (event->key() == Qt::Key_Down) { + emit focusViewRequest(); + } } 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. + // We also don't want to schedule the activation process if we are already active, + // otherwise we can enter in a loop of FocusIn/FocusOut events with the searchbox of another tab. + if (!isActive()) { + QTimer::singleShot(0, this, [this] { + setActive(true); + setFocus(); + }); + } break; default: @@ -238,6 +253,7 @@ void DolphinSearchBox::emitSearchRequest() { m_startSearchTimer->stop(); m_startedSearching = true; + m_saveSearchAction->setEnabled(true); emit searchRequest(); } @@ -245,6 +261,7 @@ void DolphinSearchBox::emitCloseRequest() { m_startSearchTimer->stop(); m_startedSearching = false; + m_saveSearchAction->setEnabled(false); emit closeRequest(); } @@ -270,14 +287,7 @@ void DolphinSearchBox::slotSearchTextChanged(const QString& text) void DolphinSearchBox::slotReturnPressed() { emitSearchRequest(); - emit returnPressed(); -} - -void DolphinSearchBox::slotFacetsButtonToggled() -{ - const bool facetsIsVisible = !m_facetsWidget->isVisible(); - m_facetsWidget->setVisible(facetsIsVisible); - updateFacetsToggleButton(); + emit focusViewRequest(); } void DolphinSearchBox::slotFacetChanged() @@ -287,6 +297,18 @@ void DolphinSearchBox::slotFacetChanged() emit searchRequest(); } +void DolphinSearchBox::slotSearchSaved() +{ + const QUrl searchURL = urlForSearching(); + if (searchURL.isValid()) { + PlacesItemModel model; + const QString label = i18n("Search for %1 in %2", text(), searchPath().fileName()); + model.createPlacesItem(label, + searchURL, + QStringLiteral("folder-saved-search-symbolic")); + } +} + void DolphinSearchBox::initButton(QToolButton* button) { button->installEventFilter(this); @@ -310,31 +332,21 @@ void DolphinSearchBox::loadSettings() m_fileNameButton->setChecked(true); } - m_facetsWidget->setVisible(SearchSettings::showFacetsWidget()); + updateFacetsVisible(); } void DolphinSearchBox::saveSettings() { - SearchSettings::setLocation(m_fromHereButton->isChecked() ? "FromHere" : "Everywhere"); - SearchSettings::setWhat(m_fileNameButton->isChecked() ? "FileName" : "Content"); - SearchSettings::setShowFacetsWidget(m_facetsToggleButton->isChecked()); + SearchSettings::setLocation(m_fromHereButton->isChecked() ? QStringLiteral("FromHere") : QStringLiteral("Everywhere")); + SearchSettings::setWhat(m_fileNameButton->isChecked() ? QStringLiteral("FileName") : QStringLiteral("Content")); SearchSettings::self()->save(); } void DolphinSearchBox::init() { - // Create close button - QToolButton* closeButton = new QToolButton(this); - closeButton->setAutoRaise(true); - closeButton->setIcon(QIcon::fromTheme("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->setPlaceholderText(i18n("Search...")); m_searchInput->installEventFilter(this); m_searchInput->setClearButtonEnabled(true); m_searchInput->setFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont)); @@ -344,12 +356,26 @@ void DolphinSearchBox::init() this, &DolphinSearchBox::slotSearchTextChanged); setFocusProxy(m_searchInput); + // Add "Save search" button inside search box + m_saveSearchAction = new QAction(this); + m_saveSearchAction->setIcon (QIcon::fromTheme(QStringLiteral("document-save-symbolic"))); + m_saveSearchAction->setText(i18nc("action:button", "Save this search to quickly access it again in the future")); + m_saveSearchAction->setEnabled(false); + m_searchInput->addAction(m_saveSearchAction, QLineEdit::TrailingPosition); + connect(m_saveSearchAction, &QAction::triggered, this, &DolphinSearchBox::slotSearchSaved); + + // 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); + // Apply layout for the search input QHBoxLayout* searchInputLayout = new QHBoxLayout(); - searchInputLayout->setMargin(0); - searchInputLayout->addWidget(closeButton); - searchInputLayout->addWidget(m_searchLabel); + searchInputLayout->setContentsMargins(0, 0, 0, 0); searchInputLayout->addWidget(m_searchInput); + searchInputLayout->addWidget(closeButton); // Create "Filename" and "Content" button m_fileNameButton = new QToolButton(this); @@ -366,40 +392,55 @@ void DolphinSearchBox::init() m_separator = new KSeparator(Qt::Vertical, this); - // Create "From Here" and "Everywhere"button + // Create "From Here" and "Your files" buttons 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")); + m_everywhereButton->setText(i18nc("action:button", "Your files")); + m_everywhereButton->setToolTip(i18nc("action:button", "Search in your home directory")); + m_everywhereButton->setIcon(QIcon::fromTheme(QStringLiteral("user-home"))); + m_everywhereButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); initButton(m_everywhereButton); QButtonGroup* searchLocationGroup = new QButtonGroup(this); searchLocationGroup->addButton(m_fromHereButton); searchLocationGroup->addButton(m_everywhereButton); - // Create "Facets" widgets - m_facetsToggleButton = new QToolButton(this); - m_facetsToggleButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - initButton(m_facetsToggleButton); - connect(m_facetsToggleButton, &QToolButton::clicked, this, &DolphinSearchBox::slotFacetsButtonToggled); - + 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" widget m_facetsWidget = new DolphinFacetsWidget(this); m_facetsWidget->installEventFilter(this); m_facetsWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); + m_facetsWidget->layout()->setSpacing(Dolphin::LAYOUT_SPACING_SMALL); connect(m_facetsWidget, &DolphinFacetsWidget::facetChanged, this, &DolphinSearchBox::slotFacetChanged); // Apply layout for the options QHBoxLayout* optionsLayout = new QHBoxLayout(); - optionsLayout->setMargin(0); + optionsLayout->setContentsMargins(0, 0, 0, 0); + optionsLayout->setSpacing(Dolphin::LAYOUT_SPACING_SMALL); 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(moreSearchToolsButton); optionsLayout->addStretch(1); - optionsLayout->addWidget(m_facetsToggleButton); // Put the options into a QScrollArea. This prevents increasing the view width // in case that not enough width for the options is available. @@ -416,7 +457,8 @@ void DolphinSearchBox::init() m_optionsScrollArea->setWidgetResizable(true); m_topLayout = new QVBoxLayout(this); - m_topLayout->setMargin(0); + m_topLayout->setContentsMargins(0, 0, 0, 0); + m_topLayout->setSpacing(Dolphin::LAYOUT_SPACING_SMALL); m_topLayout->addLayout(searchInputLayout); m_topLayout->addWidget(m_optionsScrollArea); m_topLayout->addWidget(m_facetsWidget); @@ -429,8 +471,12 @@ void DolphinSearchBox::init() m_startSearchTimer->setSingleShot(true); m_startSearchTimer->setInterval(1000); connect(m_startSearchTimer, &QTimer::timeout, this, &DolphinSearchBox::emitSearchRequest); +} - updateFacetsToggleButton(); +QString DolphinSearchBox::queryTitle(const QString& text) const +{ + return i18nc("@title UDS_DISPLAY_NAME for a KIO directory listing. %1 is the query the user entered.", + "Query Results from '%1'", text); } QUrl DolphinSearchBox::balooUrlForSearching() const @@ -439,41 +485,30 @@ QUrl DolphinSearchBox::balooUrlForSearching() const const QString text = m_searchInput->text(); Baloo::Query query; - query.addType("File"); query.addType(m_facetsWidget->facetType()); - Baloo::Term term(Baloo::Term::And); - - Baloo::Term ratingTerm = m_facetsWidget->ratingTerm(); - if (ratingTerm.isValid()) { - term.addSubTerm(ratingTerm); - } + QStringList queryStrings = m_facetsWidget->searchTerms(); if (m_contentButton->isChecked()) { - query.setSearchString(text); + queryStrings << text; } else if (!text.isEmpty()) { - term.addSubTerm(Baloo::Term(QLatin1String("filename"), text)); + queryStrings << QStringLiteral("filename:\"%1\"").arg(text); } if (m_fromHereButton->isChecked()) { query.setIncludeFolder(m_searchPath.toLocalFile()); } - query.setTerm(term); + query.setSearchString(queryStrings.join(QLatin1Char(' '))); - 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)); + return query.toSearchUrl(queryTitle(text)); #else return QUrl(); #endif } -void DolphinSearchBox::fromBalooSearchUrl(const QUrl& url) +void DolphinSearchBox::updateFromQuery(const DolphinQuery& query) { -#ifdef HAVE_BALOO - const Baloo::Query query = Baloo::Query::fromSearchUrl(url); - const Baloo::Term term = query.term(); - // Block all signals to avoid unnecessary "searchRequest" signals // while we adjust the search text and the facet widget. blockSignals(true); @@ -482,39 +517,41 @@ void DolphinSearchBox::fromBalooSearchUrl(const QUrl& url) if (!customDir.isEmpty()) { setSearchPath(QUrl::fromLocalFile(customDir)); } else { - setSearchPath(QDir::homePath()); + setSearchPath(QUrl::fromLocalFile(QDir::homePath())); } - setText(query.searchString()); + setText(query.text()); - QStringList types = query.types(); - types.removeOne("File"); // We are only interested in facet widget types - if (!types.isEmpty()) { - m_facetsWidget->setFacetType(types.first()); + if (query.hasContentSearch()) { + m_contentButton->setChecked(true); + } else if (query.hasFileName()) { + m_fileNameButton->setChecked(true); } - foreach (const Baloo::Term& subTerm, term.subTerms()) { - const QString property = subTerm.property(); - - if (property == QLatin1String("filename")) { - setText(subTerm.value().toString()); - } else if (m_facetsWidget->isRatingTerm(subTerm)) { - m_facetsWidget->setRatingTerm(subTerm); - } + m_facetsWidget->resetSearchTerms(); + m_facetsWidget->setFacetType(query.type()); + const QStringList searchTerms = query.searchTerms(); + for (const QString& searchTerm : searchTerms) { + m_facetsWidget->setSearchTerm(searchTerm); } m_startSearchTimer->stop(); blockSignals(false); -#else - Q_UNUSED(url); -#endif } -void DolphinSearchBox::updateFacetsToggleButton() +void DolphinSearchBox::updateFacetsVisible() { - const bool facetsIsVisible = SearchSettings::showFacetsWidget(); - m_facetsToggleButton->setChecked(facetsIsVisible ? true : false); - m_facetsToggleButton->setIcon(QIcon::fromTheme(facetsIsVisible ? "arrow-up-double" : "arrow-down-double")); - m_facetsToggleButton->setText(facetsIsVisible ? i18nc("action:button", "Fewer Options") : i18nc("action:button", "More Options")); + const bool indexingEnabled = isIndexingEnabled(); + m_facetsWidget->setEnabled(indexingEnabled); + m_facetsWidget->setVisible(indexingEnabled); } +bool DolphinSearchBox::isIndexingEnabled() const +{ +#ifdef HAVE_BALOO + const Baloo::IndexerConfig searchInfo; + return searchInfo.fileIndexingEnabled() && !searchPath().isEmpty() && searchInfo.shouldBeIndexed(searchPath().toLocalFile()); +#else + return false; +#endif +}