]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/search/dolphinsearchbox.cpp
Add symmetric padding on right side of details view
[dolphin.git] / src / search / dolphinsearchbox.cpp
index e2fd81a6f7aec004591062f3289ce8e2c368ce27..860d9f6cd5d31b935e9073b5be77cc329b0217e0 100644 (file)
-/***************************************************************************
- *   Copyright (C) 2009 by Peter Penz <peter.penz@gmx.at>                  *
- *   Copyright (C) 2009 by Matthias Fuchs <mat69@gmx.net>                  *
- *                                                                         *
- *   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"
+/*
+ * SPDX-FileCopyrightText: 2010 Peter Penz <peter.penz19@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
 
-#include <config-nepomuk.h>
+#include "global.h"
+#include "dolphinsearchbox.h"
 
-#include <KConfigGroup>
-#include <KDesktopFile>
-#include <kglobalsettings.h>
-#include <klineedit.h>
-#include <klocale.h>
-#include <kiconloader.h>
-#include <KStandardDirs>
+#include "dolphin_searchsettings.h"
+#include "dolphinfacetswidget.h"
+#include "dolphinplacesmodelsingleton.h"
+#include "dolphinquery.h"
+
+#include <KLocalizedString>
+#include <KNS3/KMoreToolsMenuFactory>
+#include <KSeparator>
+#include <config-baloo.h>
+#ifdef HAVE_BALOO
+#include <Baloo/Query>
+#include <Baloo/IndexerConfig>
+#endif
 
-#include <QEvent>
-#include <QKeyEvent>
+#include <QButtonGroup>
+#include <QDir>
+#include <QFontDatabase>
 #include <QHBoxLayout>
-#include <QStandardItemModel>
-#include <QtGui/QCompleter>
-#include <QtGui/QTreeView>
+#include <QIcon>
+#include <QKeyEvent>
+#include <QLabel>
+#include <QLineEdit>
+#include <QScrollArea>
+#include <QShowEvent>
+#include <QTimer>
 #include <QToolButton>
+#include <QUrlQuery>
 
-#ifdef HAVE_NEPOMUK
-#include <Nepomuk/ResourceManager>
-#include <Nepomuk/Tag>
-#endif
+DolphinSearchBox::DolphinSearchBox(QWidget* parent) :
+    QWidget(parent),
+    m_startedSearching(false),
+    m_active(true),
+    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(nullptr)
+{
+}
 
-DolphinSearchCompleter::DolphinSearchCompleter(KLineEdit* linedit) :
-    QObject(0),
-    q(linedit),
-    m_completer(0),
-    m_completionModel(0),
-    m_wordStart(-1),
-    m_wordEnd(-1)
-{
-    m_completionModel = new QStandardItemModel(this);
-
-#ifdef HAVE_NEPOMUK
-    if (!Nepomuk::ResourceManager::instance()->init()) {
-        //read all currently set tags
-        //NOTE if the user changes tags elsewhere they won't get updated here
-        QList<Nepomuk::Tag> tags = Nepomuk::Tag::allTags();
-        foreach (const Nepomuk::Tag& tag, tags) {
-            const QString tagText = tag.label();
-            addCompletionItem(tagText,
-                              "tag:\"" + tagText + '\"',
-                              i18nc("Tag as in Nepomuk::Tag", "Tag"),
-                              QString(),
-                              KIcon("mail-tagged"));
-        }
+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)
+{
+    if (url == m_searchPath) {
+        return;
     }
-#endif //HAVE_NEPOMUK
-
-    // load the completions stored in the desktop file
-    KDesktopFile file(KStandardDirs::locate("data", "dolphin/dolphinsearchcommands.desktop"));
-    foreach (const QString &group, file.groupList()) {
-        KConfigGroup cg(&file, group);
-        const QString displayed = cg.readEntry("Name", QString());
-        const QString usedForCompletition = cg.readEntry("Completion", QString());
-        const QString description = cg.readEntry("Comment", QString());
-        const QString toolTip = cg.readEntry("GenericName", QString());
-        const QString icon = cg.readEntry("Icon", QString());
-
-        if (icon.isEmpty()) {
-            addCompletionItem(displayed, usedForCompletition, description, toolTip);
-        } else {
-            addCompletionItem(displayed, usedForCompletition, description, toolTip, KIcon(icon));
+
+    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_completionModel->sort(0, Qt::AscendingOrder);
+    m_searchPath = url;
 
-    m_completer = new QCompleter(m_completionModel, this);
-    m_completer->setWidget(q);
-    m_completer->setCaseSensitivity(Qt::CaseInsensitive);
-    QTreeView *view = new QTreeView;
-    m_completer->setPopup(view);
-    view->setRootIsDecorated(false);
-    view->setHeaderHidden(true);
+    QFontMetrics metrics(m_fromHereButton->font());
+    const int maxWidth = metrics.height() * 8;
 
-    connect(q, SIGNAL(textEdited(QString)), this, SLOT(slotTextEdited(QString)));
-    connect(m_completer, SIGNAL(highlighted(QModelIndex)), this, SLOT(highlighted(QModelIndex)));
+    QString location = cleanedUrl.fileName();
+    if (location.isEmpty()) {
+        location = cleanedUrl.toString(QUrl::PreferLocalFile);
+    }
+    const QString elidedLocation = metrics.elidedText(location, Qt::ElideMiddle, maxWidth);
+    m_fromHereButton->setText(i18nc("action:button", "From Here (%1)", elidedLocation));
+    m_fromHereButton->setToolTip(i18nc("action:button", "Limit search to '%1' and its subfolders", cleanedUrl.toString(QUrl::PreferLocalFile)));
 }
 
-void DolphinSearchCompleter::addCompletionItem(const QString& displayed, const QString& usedForCompletition, const QString& description, const QString& toolTip, const KIcon& icon)
+QUrl DolphinSearchBox::searchPath() const
 {
-    if (displayed.isEmpty() || usedForCompletition.isEmpty()) {
-        return;
+    return m_everywhereButton->isChecked() ? QUrl::fromLocalFile(QDir::homePath()) : m_searchPath;
+}
+
+QUrl DolphinSearchBox::urlForSearching() const
+{
+    QUrl url;
+
+    if (isIndexingEnabled()) {
+        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"));
+        }
+
+        query.addQueryItem(QStringLiteral("url"), searchPath().url());
+        query.addQueryItem(QStringLiteral("title"), queryTitle(m_searchInput->text()));
+
+        url.setQuery(query);
     }
 
-    QList<QStandardItem*> items;
-    QStandardItem *item = new QStandardItem();
-    item->setData(QVariant(displayed), Qt::DisplayRole);
-    item->setData(QVariant(usedForCompletition), Qt::UserRole);
-    item->setData(QVariant(toolTip), Qt::ToolTipRole);
-    items << item;
+    return url;
+}
 
-    item = new QStandardItem(description);
-    if (!icon.isNull()) {
-        item->setIcon(icon);
+void DolphinSearchBox::fromSearchUrl(const QUrl& url)
+{
+    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(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);
     }
-    item->setData(QVariant(toolTip), Qt::ToolTipRole);
-    items << item;
 
-    m_completionModel->insertRow(m_completionModel->rowCount(), items);
+    updateFacetsVisible();
 }
 
-void DolphinSearchCompleter::findText(int* wordStart, int* wordEnd, QString* newWord, int cursorPos, const QString &input)
+void DolphinSearchBox::selectAll()
 {
-    --cursorPos;//decrease to get a useful position (not the end of the word e.g.)
+    m_searchInput->selectAll();
+}
 
-    if (!wordStart || !wordEnd) {
-        return;
-    }
+void DolphinSearchBox::setActive(bool active)
+{
+    if (active != m_active) {
+        m_active = active;
 
-    *wordStart = -1;
-    *wordEnd = -1;
-
-    // the word might contain "" and thus maybe spaces
-    if (input.contains('\"')) {
-        int tempStart = -1;
-        int tempEnd = -1;
-
-        do {
-            tempStart = input.indexOf('\"', tempEnd + 1);
-            tempEnd = input.indexOf('\"', tempStart + 1);
-            if ((cursorPos >= tempStart) && (cursorPos <= tempEnd)) {
-                *wordStart = tempStart;
-                *wordEnd = tempEnd;
-                break;
-            } else if ((tempEnd == -1) && (cursorPos >= tempStart)) {
-                //one " found, so probably the beginning of the new word
-                *wordStart = tempStart;
-                break;
-            }
-        } while ((tempStart != -1) && (tempEnd != -1));
+        if (active) {
+            Q_EMIT activated();
+        }
     }
+}
 
-    if (*wordEnd > -1) {
-        *wordEnd = input.indexOf(' ', *wordEnd) - 1;
-    } else {
-        *wordEnd = input.indexOf(' ', cursorPos) - 1;
+bool DolphinSearchBox::isActive() const
+{
+    return m_active;
+}
+
+bool DolphinSearchBox::event(QEvent* event)
+{
+    if (event->type() == QEvent::Polish) {
+        init();
     }
-    if (*wordEnd < 0) {
-        *wordEnd = input.length() - 1;
+    return QWidget::event(event);
+}
+
+void DolphinSearchBox::showEvent(QShowEvent* event)
+{
+    if (!event->spontaneous()) {
+        m_searchInput->setFocus();
+        m_startedSearching = false;
     }
+}
 
-    if (*wordStart > -1) {
-        *wordStart = input.lastIndexOf(' ', *wordStart + 1) + 1;
-    } else {
-        *wordStart = input.lastIndexOf(' ', cursorPos) + 1;
+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()) {
+            Q_EMIT closeRequest();
+        } else {
+            m_searchInput->clear();
+        }
     }
-    if (*wordStart < 0) {
-        *wordStart = 0;
+    else if (event->key() == Qt::Key_Down) {
+        Q_EMIT focusViewRequest();
     }
+}
 
+bool DolphinSearchBox::eventFilter(QObject* obj, QEvent* event)
+{
+    switch (event->type()) {
+    case QEvent::FocusIn:
+        // #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;
 
-    QString word = input.mid(*wordStart, *wordEnd - *wordStart + 1);
-
-    //remove opening braces or negations ('-' = not) at the beginning
-    while (word.count() && ((word[0] == '(') || (word[0] == '-'))) {
-        word.remove(0, 1);
-        ++(*wordStart);
+    default:
+        break;
     }
 
-    //remove ending braces at the end
-    while (word.count() && (word[word.count() - 1] == ')')) {
-        word.remove(word.count() - 1, 1);
-        --(*wordEnd);
-    }
+    return QObject::eventFilter(obj, event);
+}
 
-    if (newWord) {
-        *newWord = word;
+void DolphinSearchBox::emitSearchRequest()
+{
+    m_startSearchTimer->stop();
+    m_startedSearching = true;
+    m_saveSearchAction->setEnabled(true);
+    Q_EMIT searchRequest();
+}
+
+void DolphinSearchBox::emitCloseRequest()
+{
+    m_startSearchTimer->stop();
+    m_startedSearching = false;
+    m_saveSearchAction->setEnabled(false);
+    Q_EMIT closeRequest();
+}
+
+void DolphinSearchBox::slotConfigurationChanged()
+{
+    saveSettings();
+    if (m_startedSearching) {
+        emitSearchRequest();
     }
 }
 
-void DolphinSearchCompleter::slotTextEdited(const QString& text)
+void DolphinSearchBox::slotSearchTextChanged(const QString& text)
 {
-    findText(&m_wordStart, &m_wordEnd, &m_userText, q->cursorPosition(), text);
 
-    if (!m_userText.isEmpty()) {
-        const int role = m_completer->completionRole();
+    if (text.isEmpty()) {
+        m_startSearchTimer->stop();
+    } else {
+        m_startSearchTimer->start();
+    }
+    Q_EMIT searchTextChanged(text);
+}
 
-        //change the role used for comparison depending on what the user entered
-        if (m_userText.contains(':') || m_userText.contains('\"')) {
-            //assume that m_userText contains searchinformation like 'tag:"..."'
-            if (role != Qt::UserRole) {
-                m_completer->setCompletionRole(Qt::UserRole);
-            }
-        } else if (role != Qt::EditRole) {
-            m_completer->setCompletionRole(Qt::EditRole);
-        }
+void DolphinSearchBox::slotReturnPressed()
+{
+    emitSearchRequest();
+    Q_EMIT focusViewRequest();
+}
+
+void DolphinSearchBox::slotFacetChanged()
+{
+    m_startedSearching = true;
+    m_startSearchTimer->stop();
+    Q_EMIT searchRequest();
+}
 
-        m_completer->setCompletionPrefix(m_userText);
-        m_completer->complete();
+void DolphinSearchBox::slotSearchSaved()
+{
+    const QUrl searchURL = urlForSearching();
+    if (searchURL.isValid()) {
+        const QString label = i18n("Search for %1 in %2", text(), searchPath().fileName());
+        DolphinPlacesModelSingleton::instance().placesModel()->addPlace(label, searchURL, QStringLiteral("folder-saved-search-symbolic"));
     }
 }
 
-void DolphinSearchCompleter::highlighted(const QModelIndex& index)
+void DolphinSearchBox::initButton(QToolButton* button)
 {
-    QString text = q->text();
-    int wordStart;
-    int wordEnd;
+    button->installEventFilter(this);
+    button->setAutoExclusive(true);
+    button->setAutoRaise(true);
+    button->setCheckable(true);
+    connect(button, &QToolButton::clicked, this, &DolphinSearchBox::slotConfigurationChanged);
+}
 
-    findText(&wordStart, &wordEnd, 0, q->cursorPosition(), text);
+void DolphinSearchBox::loadSettings()
+{
+    if (SearchSettings::location() == QLatin1String("Everywhere")) {
+        m_everywhereButton->setChecked(true);
+    } else {
+        m_fromHereButton->setChecked(true);
+    }
 
-    QString replace = index.sibling(index.row(), 0).data(Qt::UserRole).toString();
-    //show the originally entered text
-    if (replace.isEmpty()) {
-        replace = m_userText;
+    if (SearchSettings::what() == QLatin1String("Content")) {
+        m_contentButton->setChecked(true);
+    } else {
+        m_fileNameButton->setChecked(true);
     }
 
-    text.replace(wordStart, wordEnd - wordStart + 1, replace);
-    q->setText(text);
-    q->setCursorPosition(wordStart + replace.length());
+    updateFacetsVisible();
 }
 
-DolphinSearchBox::DolphinSearchBox(QWidget* parent) :
-    QWidget(parent),
-    m_searchInput(0),
-    m_searchButton(0),
-    m_completer(0)
-{
-    QHBoxLayout* hLayout = new QHBoxLayout(this);
-    hLayout->setMargin(0);
-    hLayout->setSpacing(0);
-
-    m_searchInput = new KLineEdit(this);
-    m_searchInput->setClearButtonShown(true);
-    m_searchInput->setMinimumWidth(150);
-    m_searchInput->setClickMessage(i18nc("@label:textbox", "Search..."));
-    m_searchInput->installEventFilter(this);
-    hLayout->addWidget(m_searchInput);
-    connect(m_searchInput, SIGNAL(textChanged(const QString&)),
-            this, SIGNAL(textChanged(const QString&)));
-    connect(m_searchInput, SIGNAL(returnPressed()),
-            this, SLOT(emitSearchSignal()));
+void DolphinSearchBox::saveSettings()
+{
+    SearchSettings::setLocation(m_fromHereButton->isChecked() ? QStringLiteral("FromHere") : QStringLiteral("Everywhere"));
+    SearchSettings::setWhat(m_fileNameButton->isChecked() ? QStringLiteral("FileName") : QStringLiteral("Content"));
+    SearchSettings::self()->save();
+}
 
-    m_searchButton = new QToolButton(this);
-    m_searchButton->setAutoRaise(true);
-    m_searchButton->setIcon(KIcon("edit-find"));
-    m_searchButton->setToolTip(i18nc("@info:tooltip", "Click to begin the search"));
-    hLayout->addWidget(m_searchButton);
-    connect(m_searchButton, SIGNAL(clicked()),
-            this, SLOT(emitSearchSignal()));
+void DolphinSearchBox::init()
+{
+    // 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));
+    connect(m_searchInput, &QLineEdit::returnPressed,
+            this, &DolphinSearchBox::slotReturnPressed);
+    connect(m_searchInput, &QLineEdit::textChanged,
+            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->setContentsMargins(0, 0, 0, 0);
+    searchInputLayout->addWidget(m_searchInput);
+    searchInputLayout->addWidget(closeButton);
+
+    // 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 "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", "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);
+
+    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);
+
+    // 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);
+
+    // Apply layout for the options
+    QHBoxLayout* optionsLayout = new QHBoxLayout(optionsContainer);
+    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);
+
+    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->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);
+
+    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);
 }
 
-DolphinSearchBox::~DolphinSearchBox()
+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);
 }
 
-bool DolphinSearchBox::event(QEvent* event)
+QUrl DolphinSearchBox::balooUrlForSearching() const
 {
-    if (event->type() == QEvent::Polish) {
-        m_searchInput->setFont(KGlobalSettings::generalFont());
-    } else if (event->type() == QEvent::KeyPress) {
-        if (static_cast<QKeyEvent *>(event)->key() == Qt::Key_Escape) {
-            m_searchInput->clear();
-        }
+#ifdef HAVE_BALOO
+    const QString text = m_searchInput->text();
+
+    Baloo::Query query;
+    query.addType(m_facetsWidget->facetType());
+
+    QStringList queryStrings = m_facetsWidget->searchTerms();
+
+    if (m_contentButton->isChecked()) {
+        queryStrings << text;
+    } else if (!text.isEmpty()) {
+        queryStrings << QStringLiteral("filename:\"%1\"").arg(text);
     }
-    return QWidget::event(event);
+
+    if (m_fromHereButton->isChecked()) {
+        query.setIncludeFolder(m_searchPath.toLocalFile());
+    }
+
+    query.setSearchString(queryStrings.join(QLatin1Char(' ')));
+
+    return query.toSearchUrl(queryTitle(text));
+#else
+    return QUrl();
+#endif
 }
 
-bool DolphinSearchBox::eventFilter(QObject* watched, QEvent* event)
+void DolphinSearchBox::updateFromQuery(const DolphinQuery& query)
 {
-    if ((watched == m_searchInput) && (event->type() == QEvent::FocusIn)) {
-        // Postpone the creation of the search completer until
-        // the search box is used. This decreases the startup time
-        // of Dolphin.
-        if (m_completer == 0) {
-            m_completer = new DolphinSearchCompleter(m_searchInput);
-        }
-        emit requestSearchOptions();
+    // 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()));
     }
 
-    return QWidget::eventFilter(watched, event);
-}
+    // If the input box has focus, do not update to avoid messing with user typing
+    if (!m_searchInput->hasFocus()) {
+        setText(query.text());
+    }
 
+    if (query.hasContentSearch()) {
+        m_contentButton->setChecked(true);
+    } else if (query.hasFileName())  {
+        m_fileNameButton->setChecked(true);
+    }
+
+    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);
+}
 
-void DolphinSearchBox::emitSearchSignal()
+void DolphinSearchBox::updateFacetsVisible()
 {
-    emit search(KUrl("nepomuksearch:/" + m_searchInput->text()));
+    const bool indexingEnabled = isIndexingEnabled();
+    m_facetsWidget->setEnabled(indexingEnabled);
+    m_facetsWidget->setVisible(indexingEnabled);
 }
 
-#include "dolphinsearchbox.moc"
+bool DolphinSearchBox::isIndexingEnabled() const
+{
+#ifdef HAVE_BALOO
+    const Baloo::IndexerConfig searchInfo;
+    return searchInfo.fileIndexingEnabled() && !searchPath().isEmpty() && searchInfo.shouldBeIndexed(searchPath().toLocalFile());
+#else
+    return false;
+#endif
+}