diff --git a/src/dolphincontextmenu.cpp b/src/dolphincontextmenu.cpp index b4d249d76..881ef34b4 100644 --- a/src/dolphincontextmenu.cpp +++ b/src/dolphincontextmenu.cpp @@ -1,500 +1,505 @@ /*************************************************************************** * Copyright (C) 2006 by Peter Penz (peter.penz@gmx.at) and * * Cvetoslav Ludmiloff * * * * 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 "dolphincontextmenu.h" #include "dolphinmainwindow.h" #include "dolphinnewfilemenu.h" #include "dolphinviewcontainer.h" #include "dolphin_generalsettings.h" #include "dolphinremoveaction.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "views/dolphinview.h" #include "views/viewmodecontroller.h" DolphinContextMenu::DolphinContextMenu(DolphinMainWindow* parent, const QPoint& pos, const KFileItem& fileInfo, const QUrl& baseUrl) : QMenu(parent), m_pos(pos), m_mainWindow(parent), m_fileInfo(fileInfo), m_baseUrl(baseUrl), m_baseFileItem(0), m_selectedItems(), m_selectedItemsProperties(0), m_context(NoContext), m_copyToMenu(parent), m_customActions(), m_command(None), m_removeAction(0) { // The context menu either accesses the URLs of the selected items // or the items itself. To increase the performance both lists are cached. const DolphinView* view = m_mainWindow->activeViewContainer()->view(); m_selectedItems = view->selectedItems(); } DolphinContextMenu::~DolphinContextMenu() { delete m_selectedItemsProperties; m_selectedItemsProperties = 0; } void DolphinContextMenu::setCustomActions(const QList& actions) { m_customActions = actions; } DolphinContextMenu::Command DolphinContextMenu::open() { // get the context information if (m_baseUrl.scheme() == QLatin1String("trash")) { m_context |= TrashContext; } if (!m_fileInfo.isNull() && !m_selectedItems.isEmpty()) { m_context |= ItemContext; // TODO: handle other use cases like devices + desktop files } // open the corresponding popup for the context if (m_context & TrashContext) { if (m_context & ItemContext) { openTrashItemContextMenu(); } else { openTrashContextMenu(); } } else if (m_context & ItemContext) { openItemContextMenu(); } else { Q_ASSERT(m_context == NoContext); openViewportContextMenu(); } return m_command; } void DolphinContextMenu::keyPressEvent(QKeyEvent *ev) { if (m_removeAction && ev->key() == Qt::Key_Shift) { m_removeAction->update(DolphinRemoveAction::ShiftState::Pressed); } QMenu::keyPressEvent(ev); } void DolphinContextMenu::keyReleaseEvent(QKeyEvent *ev) { if (m_removeAction && ev->key() == Qt::Key_Shift) { m_removeAction->update(DolphinRemoveAction::ShiftState::Released); } QMenu::keyReleaseEvent(ev); } void DolphinContextMenu::openTrashContextMenu() { Q_ASSERT(m_context & TrashContext); QAction* emptyTrashAction = new QAction(QIcon::fromTheme(QStringLiteral("trash-empty")), i18nc("@action:inmenu", "Empty Trash"), this); KConfig trashConfig(QStringLiteral("trashrc"), KConfig::SimpleConfig); emptyTrashAction->setEnabled(!trashConfig.group("Status").readEntry("Empty", true)); addAction(emptyTrashAction); addCustomActions(); QAction* propertiesAction = m_mainWindow->actionCollection()->action(QStringLiteral("properties")); addAction(propertiesAction); addShowMenuBarAction(); if (exec(m_pos) == emptyTrashAction) { KIO::JobUiDelegate uiDelegate; uiDelegate.setWindow(m_mainWindow); if (uiDelegate.askDeleteConfirmation(QList(), KIO::JobUiDelegate::EmptyTrash, KIO::JobUiDelegate::DefaultConfirmation)) { KIO::Job* job = KIO::emptyTrash(); KJobWidgets::setWindow(job, m_mainWindow); job->uiDelegate()->setAutoErrorHandlingEnabled(true); } } } void DolphinContextMenu::openTrashItemContextMenu() { Q_ASSERT(m_context & TrashContext); Q_ASSERT(m_context & ItemContext); QAction* restoreAction = new QAction(i18nc("@action:inmenu", "Restore"), m_mainWindow); addAction(restoreAction); QAction* deleteAction = m_mainWindow->actionCollection()->action(KStandardAction::name(KStandardAction::DeleteFile)); addAction(deleteAction); QAction* propertiesAction = m_mainWindow->actionCollection()->action(QStringLiteral("properties")); addAction(propertiesAction); if (exec(m_pos) == restoreAction) { QList selectedUrls; selectedUrls.reserve(m_selectedItems.count()); foreach (const KFileItem &item, m_selectedItems) { selectedUrls.append(item.url()); } KIO::RestoreJob *job = KIO::restoreFromTrash(selectedUrls); KJobWidgets::setWindow(job, m_mainWindow); job->uiDelegate()->setAutoErrorHandlingEnabled(true); } } void DolphinContextMenu::openItemContextMenu() { Q_ASSERT(!m_fileInfo.isNull()); QAction* openParentAction = 0; QAction* openParentInNewWindowAction = 0; QAction* openParentInNewTabAction = 0; QAction* addToPlacesAction = 0; const KFileItemListProperties& selectedItemsProps = selectedItemsProperties(); if (m_selectedItems.count() == 1) { if (m_fileInfo.isDir()) { // setup 'Create New' menu DolphinNewFileMenu* newFileMenu = new DolphinNewFileMenu(m_mainWindow->actionCollection(), m_mainWindow); const DolphinView* view = m_mainWindow->activeViewContainer()->view(); newFileMenu->setViewShowsHiddenFiles(view->hiddenFilesShown()); newFileMenu->checkUpToDate(); newFileMenu->setPopupFiles(m_fileInfo.url()); newFileMenu->setEnabled(selectedItemsProps.supportsWriting()); connect(newFileMenu, &DolphinNewFileMenu::fileCreated, newFileMenu, &DolphinNewFileMenu::deleteLater); connect(newFileMenu, &DolphinNewFileMenu::directoryCreated, newFileMenu, &DolphinNewFileMenu::deleteLater); QMenu* menu = newFileMenu->menu(); menu->setTitle(i18nc("@title:menu Create new folder, file, link, etc.", "Create New")); menu->setIcon(QIcon::fromTheme(QStringLiteral("document-new"))); addMenu(menu); addSeparator(); // insert 'Open in new window' and 'Open in new tab' entries addAction(m_mainWindow->actionCollection()->action(QStringLiteral("open_in_new_window"))); addAction(m_mainWindow->actionCollection()->action(QStringLiteral("open_in_new_tab"))); // insert 'Add to Places' entry if (!placeExists(m_fileInfo.url())) { addToPlacesAction = addAction(QIcon::fromTheme(QStringLiteral("bookmark-new")), i18nc("@action:inmenu Add selected folder to places", "Add to Places")); } addSeparator(); } else if (m_baseUrl.scheme().contains(QStringLiteral("search")) || m_baseUrl.scheme().contains(QStringLiteral("timeline"))) { openParentAction = new QAction(QIcon::fromTheme(QStringLiteral("document-open-folder")), i18nc("@action:inmenu", "Open Path"), this); addAction(openParentAction); openParentInNewWindowAction = new QAction(QIcon::fromTheme(QStringLiteral("window-new")), i18nc("@action:inmenu", "Open Path in New Window"), this); addAction(openParentInNewWindowAction); openParentInNewTabAction = new QAction(QIcon::fromTheme(QStringLiteral("tab-new")), i18nc("@action:inmenu", "Open Path in New Tab"), this); addAction(openParentInNewTabAction); addSeparator(); } else if (!DolphinView::openItemAsFolderUrl(m_fileInfo).isEmpty()) { // insert 'Open in new window' and 'Open in new tab' entries addAction(m_mainWindow->actionCollection()->action(QStringLiteral("open_in_new_window"))); addAction(m_mainWindow->actionCollection()->action(QStringLiteral("open_in_new_tab"))); addSeparator(); } } else { bool selectionHasOnlyDirs = true; foreach (const KFileItem& item, m_selectedItems) { const QUrl& url = DolphinView::openItemAsFolderUrl(item); if (url.isEmpty()) { selectionHasOnlyDirs = false; break; } } if (selectionHasOnlyDirs) { // insert 'Open in new tab' entry addAction(m_mainWindow->actionCollection()->action(QStringLiteral("open_in_new_tabs"))); addSeparator(); } } insertDefaultItemActions(selectedItemsProps); addSeparator(); KFileItemActions fileItemActions; fileItemActions.setItemListProperties(selectedItemsProps); addServiceActions(fileItemActions); fileItemActions.addPluginActionsTo(this); addVersionControlPluginActions(); // insert 'Copy To' and 'Move To' sub menus if (GeneralSettings::showCopyMoveMenu()) { m_copyToMenu.setUrls(m_selectedItems.urlList()); m_copyToMenu.setReadOnly(!selectedItemsProps.supportsWriting()); m_copyToMenu.setAutoErrorHandlingEnabled(true); m_copyToMenu.addActionsTo(this); } // insert 'Properties...' entry QAction* propertiesAction = m_mainWindow->actionCollection()->action(QStringLiteral("properties")); addAction(propertiesAction); QAction* activatedAction = exec(m_pos); if (activatedAction) { if (activatedAction == addToPlacesAction) { const QUrl selectedUrl(m_fileInfo.url()); if (selectedUrl.isValid()) { PlacesItemModel model; const QString text = selectedUrl.fileName(); PlacesItem* item = model.createPlacesItem(text, selectedUrl, KIO::iconNameForUrl(selectedUrl)); model.appendItemToGroup(item); model.saveBookmarks(); } } else if (activatedAction == openParentAction) { m_command = OpenParentFolder; } else if (activatedAction == openParentInNewWindowAction) { m_command = OpenParentFolderInNewWindow; } else if (activatedAction == openParentInNewTabAction) { m_command = OpenParentFolderInNewTab; } } } void DolphinContextMenu::openViewportContextMenu() { // setup 'Create New' menu KNewFileMenu* newFileMenu = m_mainWindow->newFileMenu(); const DolphinView* view = m_mainWindow->activeViewContainer()->view(); newFileMenu->setViewShowsHiddenFiles(view->hiddenFilesShown()); newFileMenu->checkUpToDate(); newFileMenu->setPopupFiles(m_baseUrl); addMenu(newFileMenu->menu()); addSeparator(); // Insert 'New Window' and 'New Tab' entries. Don't use "open_in_new_window" and // "open_in_new_tab" here, as the current selection should get ignored. addAction(m_mainWindow->actionCollection()->action(QStringLiteral("new_window"))); addAction(m_mainWindow->actionCollection()->action(QStringLiteral("new_tab"))); // Insert 'Add to Places' entry if exactly one item is selected QAction* addToPlacesAction = 0; if (!placeExists(m_mainWindow->activeViewContainer()->url())) { addToPlacesAction = addAction(QIcon::fromTheme(QStringLiteral("bookmark-new")), i18nc("@action:inmenu Add current folder to places", "Add to Places")); } addSeparator(); QAction* pasteAction = createPasteAction(); addAction(pasteAction); addSeparator(); // Insert service actions const KFileItemListProperties baseUrlProperties(KFileItemList() << baseFileItem()); KFileItemActions fileItemActions; fileItemActions.setItemListProperties(baseUrlProperties); addServiceActions(fileItemActions); fileItemActions.addPluginActionsTo(this); addVersionControlPluginActions(); addCustomActions(); QAction* propertiesAction = m_mainWindow->actionCollection()->action(QStringLiteral("properties")); addAction(propertiesAction); addShowMenuBarAction(); QAction* action = exec(m_pos); if (addToPlacesAction && (action == addToPlacesAction)) { const DolphinViewContainer* container = m_mainWindow->activeViewContainer(); - if (container->url().isValid()) { + const QUrl url = container->url(); + if (url.isValid()) { PlacesItemModel model; - PlacesItem* item = model.createPlacesItem(container->placesText(), - container->url(), - KIO::iconNameForUrl(container->url())); + QString icon; + if (container->isSearchModeEnabled()) { + icon = QStringLiteral("folder-saved-search-symbolic"); + } else { + icon = KIO::iconNameForUrl(url); + } + PlacesItem* item = model.createPlacesItem(container->placesText(), url, icon); model.appendItemToGroup(item); model.saveBookmarks(); } } } void DolphinContextMenu::insertDefaultItemActions(const KFileItemListProperties& properties) { const KActionCollection* collection = m_mainWindow->actionCollection(); // Insert 'Cut', 'Copy' and 'Paste' addAction(collection->action(KStandardAction::name(KStandardAction::Cut))); addAction(collection->action(KStandardAction::name(KStandardAction::Copy))); addAction(createPasteAction()); addSeparator(); // Insert 'Rename' addAction(collection->action(KStandardAction::name(KStandardAction::RenameFile))); // Insert 'Move to Trash' and/or 'Delete' if (properties.supportsDeleting()) { const bool showDeleteAction = (KSharedConfig::openConfig()->group("KDE").readEntry("ShowDeleteCommand", false) || !properties.isLocal()); const bool showMoveToTrashAction = (properties.isLocal() && properties.supportsMoving()); if (showDeleteAction && showMoveToTrashAction) { delete m_removeAction; m_removeAction = 0; addAction(m_mainWindow->actionCollection()->action(KStandardAction::name(KStandardAction::MoveToTrash))); addAction(m_mainWindow->actionCollection()->action(KStandardAction::name(KStandardAction::DeleteFile))); } else if (showDeleteAction && !showMoveToTrashAction) { addAction(m_mainWindow->actionCollection()->action(KStandardAction::name(KStandardAction::DeleteFile))); } else { if (!m_removeAction) { m_removeAction = new DolphinRemoveAction(this, m_mainWindow->actionCollection()); } addAction(m_removeAction); m_removeAction->update(); } } } void DolphinContextMenu::addShowMenuBarAction() { const KActionCollection* ac = m_mainWindow->actionCollection(); QAction* showMenuBar = ac->action(KStandardAction::name(KStandardAction::ShowMenubar)); if (!m_mainWindow->menuBar()->isVisible() && !m_mainWindow->toolBar()->isVisible()) { addSeparator(); addAction(showMenuBar); } } bool DolphinContextMenu::placeExists(const QUrl& url) const { // Creating up a PlacesItemModel to find out if 'url' is one of the Places // can be expensive because the model asks Solid for the devices which are // available, which can take some time. // TODO: Consider restoring this check if the handling of Places and devices // will be decoupled in the future. return false; } QAction* DolphinContextMenu::createPasteAction() { QAction* action = 0; const bool isDir = !m_fileInfo.isNull() && m_fileInfo.isDir(); if (isDir && (m_selectedItems.count() == 1)) { const QMimeData *mimeData = QApplication::clipboard()->mimeData(); bool canPaste; const QString text = KIO::pasteActionText(mimeData, &canPaste, m_fileInfo); action = new QAction(QIcon::fromTheme(QStringLiteral("edit-paste")), text, this); action->setEnabled(canPaste); connect(action, &QAction::triggered, m_mainWindow, &DolphinMainWindow::pasteIntoFolder); } else { action = m_mainWindow->actionCollection()->action(KStandardAction::name(KStandardAction::Paste)); } return action; } KFileItemListProperties& DolphinContextMenu::selectedItemsProperties() const { if (!m_selectedItemsProperties) { m_selectedItemsProperties = new KFileItemListProperties(m_selectedItems); } return *m_selectedItemsProperties; } KFileItem DolphinContextMenu::baseFileItem() { if (!m_baseFileItem) { m_baseFileItem = new KFileItem(m_baseUrl); } return *m_baseFileItem; } void DolphinContextMenu::addServiceActions(KFileItemActions& fileItemActions) { fileItemActions.setParentWidget(m_mainWindow); // insert 'Open With...' action or sub menu fileItemActions.addOpenWithActionsTo(this, QStringLiteral("DesktopEntryName != 'dolphin'")); // insert 'Actions' sub menu fileItemActions.addServiceActionsTo(this); } void DolphinContextMenu::addVersionControlPluginActions() { const DolphinView* view = m_mainWindow->activeViewContainer()->view(); const QList versionControlActions = view->versionControlActions(m_selectedItems); if (!versionControlActions.isEmpty()) { addActions(versionControlActions); addSeparator(); } } void DolphinContextMenu::addCustomActions() { addActions(m_customActions); } diff --git a/src/dolphinviewcontainer.cpp b/src/dolphinviewcontainer.cpp index d83016610..e92ec6022 100644 --- a/src/dolphinviewcontainer.cpp +++ b/src/dolphinviewcontainer.cpp @@ -1,697 +1,697 @@ /*************************************************************************** * Copyright (C) 2007 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 "dolphinviewcontainer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef KActivities_FOUND #endif #include "global.h" #include "dolphindebug.h" #include "dolphin_generalsettings.h" #include "filterbar/filterbar.h" #include "search/dolphinsearchbox.h" #include "statusbar/dolphinstatusbar.h" #include "views/viewmodecontroller.h" #include "views/viewproperties.h" DolphinViewContainer::DolphinViewContainer(const QUrl& url, QWidget* parent) : QWidget(parent), m_topLayout(0), m_urlNavigator(0), m_searchBox(0), m_messageWidget(0), m_view(0), m_filterBar(0), m_statusBar(0), m_statusBarTimer(0), m_statusBarTimestamp(), m_autoGrabFocus(true) #ifdef KActivities_FOUND , m_activityResourceInstance(0) #endif { hide(); m_topLayout = new QVBoxLayout(this); m_topLayout->setSpacing(0); m_topLayout->setMargin(0); m_urlNavigator = new KUrlNavigator(new KFilePlacesModel(this), url, this); connect(m_urlNavigator, &KUrlNavigator::activated, this, &DolphinViewContainer::activate); connect(m_urlNavigator->editor(), &KUrlComboBox::completionModeChanged, this, &DolphinViewContainer::saveUrlCompletionMode); const GeneralSettings* settings = GeneralSettings::self(); m_urlNavigator->setUrlEditable(settings->editableUrl()); m_urlNavigator->setShowFullPath(settings->showFullPath()); m_urlNavigator->setHomeUrl(Dolphin::homeUrl()); KUrlComboBox* editor = m_urlNavigator->editor(); editor->setCompletionMode(KCompletion::CompletionMode(settings->urlCompletionMode())); m_searchBox = new DolphinSearchBox(this); m_searchBox->hide(); connect(m_searchBox, &DolphinSearchBox::activated, this, &DolphinViewContainer::activate); connect(m_searchBox, &DolphinSearchBox::closeRequest, this, &DolphinViewContainer::closeSearchBox); connect(m_searchBox, &DolphinSearchBox::searchRequest, this, &DolphinViewContainer::startSearching); connect(m_searchBox, &DolphinSearchBox::returnPressed, this, &DolphinViewContainer::requestFocus); m_messageWidget = new KMessageWidget(this); m_messageWidget->setCloseButtonVisible(true); m_messageWidget->hide(); m_view = new DolphinView(url, this); connect(m_view, &DolphinView::urlChanged, m_urlNavigator, &KUrlNavigator::setLocationUrl); connect(m_view, &DolphinView::urlChanged, m_messageWidget, &KMessageWidget::hide); connect(m_view, &DolphinView::writeStateChanged, this, &DolphinViewContainer::writeStateChanged); connect(m_view, &DolphinView::requestItemInfo, this, &DolphinViewContainer::showItemInfo); connect(m_view, &DolphinView::itemActivated, this, &DolphinViewContainer::slotItemActivated); connect(m_view, &DolphinView::itemsActivated, this, &DolphinViewContainer::slotItemsActivated); connect(m_view, &DolphinView::redirection, this, &DolphinViewContainer::redirect); connect(m_view, &DolphinView::directoryLoadingStarted, this, &DolphinViewContainer::slotDirectoryLoadingStarted); connect(m_view, &DolphinView::directoryLoadingCompleted, this, &DolphinViewContainer::slotDirectoryLoadingCompleted); connect(m_view, &DolphinView::directoryLoadingCanceled, this, &DolphinViewContainer::slotDirectoryLoadingCanceled); connect(m_view, &DolphinView::itemCountChanged, this, &DolphinViewContainer::delayedStatusBarUpdate); connect(m_view, &DolphinView::directoryLoadingProgress, this, &DolphinViewContainer::updateDirectoryLoadingProgress); connect(m_view, &DolphinView::directorySortingProgress, this, &DolphinViewContainer::updateDirectorySortingProgress); connect(m_view, &DolphinView::selectionChanged, this, &DolphinViewContainer::delayedStatusBarUpdate); connect(m_view, &DolphinView::errorMessage, this, &DolphinViewContainer::showErrorMessage); connect(m_view, &DolphinView::urlIsFileError, this, &DolphinViewContainer::slotUrlIsFileError); connect(m_view, &DolphinView::activated, this, &DolphinViewContainer::activate); connect(m_urlNavigator, &KUrlNavigator::urlAboutToBeChanged, this, &DolphinViewContainer::slotUrlNavigatorLocationAboutToBeChanged); connect(m_urlNavigator, &KUrlNavigator::urlChanged, this, &DolphinViewContainer::slotUrlNavigatorLocationChanged); connect(m_urlNavigator, &KUrlNavigator::urlSelectionRequested, this, &DolphinViewContainer::slotUrlSelectionRequested); connect(m_urlNavigator, &KUrlNavigator::returnPressed, this, &DolphinViewContainer::slotReturnPressed); connect(m_urlNavigator, &KUrlNavigator::urlsDropped, this, [=](const QUrl &destination, QDropEvent *event) { #if KIO_VERSION >= QT_VERSION_CHECK(5, 37, 0) m_view->dropUrls(destination, event, m_urlNavigator->dropWidget()); #else // TODO: remove as soon as we can hard-depend of KF5 >= 5.37 m_view->dropUrls(destination, event, m_view); #endif }); // Initialize status bar m_statusBar = new DolphinStatusBar(this); m_statusBar->setUrl(m_view->url()); m_statusBar->setZoomLevel(m_view->zoomLevel()); connect(m_view, &DolphinView::urlChanged, m_statusBar, &DolphinStatusBar::setUrl); connect(m_view, &DolphinView::zoomLevelChanged, m_statusBar, &DolphinStatusBar::setZoomLevel); connect(m_view, &DolphinView::infoMessage, m_statusBar, &DolphinStatusBar::setText); connect(m_view, &DolphinView::operationCompletedMessage, m_statusBar, &DolphinStatusBar::setText); connect(m_statusBar, &DolphinStatusBar::stopPressed, this, &DolphinViewContainer::stopDirectoryLoading); connect(m_statusBar, &DolphinStatusBar::zoomLevelChanged, this, &DolphinViewContainer::slotStatusBarZoomLevelChanged); m_statusBarTimer = new QTimer(this); m_statusBarTimer->setSingleShot(true); m_statusBarTimer->setInterval(300); connect(m_statusBarTimer, &QTimer::timeout, this, &DolphinViewContainer::updateStatusBar); KIO::FileUndoManager* undoManager = KIO::FileUndoManager::self(); connect(undoManager, &KIO::FileUndoManager::jobRecordingFinished, this, &DolphinViewContainer::delayedStatusBarUpdate); // Initialize filter bar m_filterBar = new FilterBar(this); m_filterBar->setVisible(settings->filterBar()); connect(m_filterBar, &FilterBar::filterChanged, this, &DolphinViewContainer::setNameFilter); connect(m_filterBar, &FilterBar::closeRequest, this, &DolphinViewContainer::closeFilterBar); connect(m_filterBar, &FilterBar::focusViewRequest, this, &DolphinViewContainer::requestFocus); connect(m_view, &DolphinView::urlChanged, m_filterBar, &FilterBar::slotUrlChanged); m_topLayout->addWidget(m_urlNavigator); m_topLayout->addWidget(m_searchBox); m_topLayout->addWidget(m_messageWidget); m_topLayout->addWidget(m_view); m_topLayout->addWidget(m_filterBar); m_topLayout->addWidget(m_statusBar); setSearchModeEnabled(isSearchUrl(url)); // Initialize kactivities resource instance #ifdef KActivities_FOUND m_activityResourceInstance = new KActivities::ResourceInstance( window()->winId(), url); m_activityResourceInstance->setParent(this); #endif } DolphinViewContainer::~DolphinViewContainer() { } QUrl DolphinViewContainer::url() const { return m_view->url(); } void DolphinViewContainer::setActive(bool active) { m_searchBox->setActive(active); m_urlNavigator->setActive(active); m_view->setActive(active); #ifdef KActivities_FOUND if (active) { m_activityResourceInstance->notifyFocusedIn(); } else { m_activityResourceInstance->notifyFocusedOut(); } #endif } bool DolphinViewContainer::isActive() const { Q_ASSERT(m_view->isActive() == m_urlNavigator->isActive()); return m_view->isActive(); } void DolphinViewContainer::setAutoGrabFocus(bool grab) { m_autoGrabFocus = grab; } bool DolphinViewContainer::autoGrabFocus() const { return m_autoGrabFocus; } const DolphinStatusBar* DolphinViewContainer::statusBar() const { return m_statusBar; } DolphinStatusBar* DolphinViewContainer::statusBar() { return m_statusBar; } const KUrlNavigator* DolphinViewContainer::urlNavigator() const { return m_urlNavigator; } KUrlNavigator* DolphinViewContainer::urlNavigator() { return m_urlNavigator; } const DolphinView* DolphinViewContainer::view() const { return m_view; } DolphinView* DolphinViewContainer::view() { return m_view; } void DolphinViewContainer::showMessage(const QString& msg, MessageType type) { if (msg.isEmpty()) { return; } m_messageWidget->setText(msg); switch (type) { case Information: m_messageWidget->setMessageType(KMessageWidget::Information); break; case Warning: m_messageWidget->setMessageType(KMessageWidget::Warning); break; case Error: m_messageWidget->setMessageType(KMessageWidget::Error); break; default: Q_ASSERT(false); break; } m_messageWidget->setWordWrap(false); const int unwrappedWidth = m_messageWidget->sizeHint().width(); m_messageWidget->setWordWrap(unwrappedWidth > size().width()); if (m_messageWidget->isVisible()) { m_messageWidget->hide(); } m_messageWidget->animatedShow(); } void DolphinViewContainer::readSettings() { if (GeneralSettings::modifiedStartupSettings()) { // The startup settings should only get applied if they have been // modified by the user. Otherwise keep the (possibly) different current // settings of the URL navigator and the filterbar. m_urlNavigator->setUrlEditable(GeneralSettings::editableUrl()); m_urlNavigator->setShowFullPath(GeneralSettings::showFullPath()); m_urlNavigator->setHomeUrl(Dolphin::homeUrl()); setFilterBarVisible(GeneralSettings::filterBar()); } m_view->readSettings(); m_statusBar->readSettings(); } bool DolphinViewContainer::isFilterBarVisible() const { return m_filterBar->isVisible(); } void DolphinViewContainer::setSearchModeEnabled(bool enabled) { if (enabled == isSearchModeEnabled()) { if (enabled && !m_searchBox->hasFocus()) { m_searchBox->setFocus(); m_searchBox->selectAll(); } return; } m_searchBox->setVisible(enabled); m_urlNavigator->setVisible(!enabled); if (enabled) { const QUrl& locationUrl = m_urlNavigator->locationUrl(); m_searchBox->fromSearchUrl(locationUrl); } else { m_view->setViewPropertiesContext(QString()); // Restore the URL for the URL navigator. If Dolphin has been // started with a search-URL, the home URL is used as fallback. QUrl url = m_searchBox->searchPath(); if (url.isEmpty() || !url.isValid() || isSearchUrl(url)) { url = Dolphin::homeUrl(); } m_urlNavigator->setLocationUrl(url); } } bool DolphinViewContainer::isSearchModeEnabled() const { return m_searchBox->isVisible(); } QString DolphinViewContainer::placesText() const { QString text; if (isSearchModeEnabled()) { - text = m_searchBox->searchPath().fileName() + QLatin1String(": ") + m_searchBox->text(); + text = i18n("Search for %1 in %2", m_searchBox->text(), m_searchBox->searchPath().fileName()); } else { text = url().fileName(); if (text.isEmpty()) { text = url().host(); } if (text.isEmpty()) { text = url().scheme(); } } return text; } void DolphinViewContainer::reload() { view()->reload(); m_messageWidget->hide(); } void DolphinViewContainer::setUrl(const QUrl& newUrl) { if (newUrl != m_urlNavigator->locationUrl()) { m_urlNavigator->setLocationUrl(newUrl); } #ifdef KActivities_FOUND m_activityResourceInstance->setUri(newUrl); #endif } void DolphinViewContainer::setFilterBarVisible(bool visible) { Q_ASSERT(m_filterBar); if (visible) { m_filterBar->show(); m_filterBar->setFocus(); m_filterBar->selectAll(); } else { closeFilterBar(); } } void DolphinViewContainer::delayedStatusBarUpdate() { if (m_statusBarTimer->isActive() && (m_statusBarTimestamp.elapsed() > 2000)) { // No update of the statusbar has been done during the last 2 seconds, // although an update has been requested. Trigger an immediate update. m_statusBarTimer->stop(); updateStatusBar(); } else { // Invoke updateStatusBar() with a small delay. This assures that // when a lot of delayedStatusBarUpdates() are done in a short time, // no bottleneck is given. m_statusBarTimer->start(); } } void DolphinViewContainer::updateStatusBar() { m_statusBarTimestamp.start(); const QString text = m_view->statusBarText(); m_statusBar->setDefaultText(text); m_statusBar->resetToDefaultText(); } void DolphinViewContainer::updateDirectoryLoadingProgress(int percent) { if (m_statusBar->progressText().isEmpty()) { m_statusBar->setProgressText(i18nc("@info:progress", "Loading folder...")); } m_statusBar->setProgress(percent); } void DolphinViewContainer::updateDirectorySortingProgress(int percent) { if (m_statusBar->progressText().isEmpty()) { m_statusBar->setProgressText(i18nc("@info:progress", "Sorting...")); } m_statusBar->setProgress(percent); } void DolphinViewContainer::slotDirectoryLoadingStarted() { if (isSearchUrl(url())) { // Search KIO-slaves usually don't provide any progress information. Give // a hint to the user that a searching is done: updateStatusBar(); m_statusBar->setProgressText(i18nc("@info", "Searching...")); m_statusBar->setProgress(-1); } else { // Trigger an undetermined progress indication. The progress // information in percent will be triggered by the percent() signal // of the directory lister later. updateDirectoryLoadingProgress(-1); } } void DolphinViewContainer::slotDirectoryLoadingCompleted() { if (!m_statusBar->progressText().isEmpty()) { m_statusBar->setProgressText(QString()); m_statusBar->setProgress(100); } if (isSearchUrl(url()) && m_view->itemsCount() == 0) { // The dir lister has been completed on a Baloo-URI and no items have been found. Instead // of showing the default status bar information ("0 items") a more helpful information is given: m_statusBar->setText(i18nc("@info:status", "No items found.")); } else { updateStatusBar(); } } void DolphinViewContainer::slotDirectoryLoadingCanceled() { if (!m_statusBar->progressText().isEmpty()) { m_statusBar->setProgressText(QString()); m_statusBar->setProgress(100); } m_statusBar->setText(QString()); } void DolphinViewContainer::slotUrlIsFileError(const QUrl& url) { const KFileItem item(url); // Find out if the file can be opened in the view (for example, this is the // case if the file is an archive). The mime type must be known for that. item.determineMimeType(); const QUrl& folderUrl = DolphinView::openItemAsFolderUrl(item, true); if (!folderUrl.isEmpty()) { setUrl(folderUrl); } else { slotItemActivated(item); } } void DolphinViewContainer::slotItemActivated(const KFileItem& item) { // It is possible to activate items on inactive views by // drag & drop operations. Assure that activating an item always // results in an active view. m_view->setActive(true); const QUrl& url = DolphinView::openItemAsFolderUrl(item, GeneralSettings::browseThroughArchives()); if (!url.isEmpty()) { setUrl(url); return; } KRun *run = new KRun(item.targetUrl(), this); run->setShowScriptExecutionPrompt(true); } void DolphinViewContainer::slotItemsActivated(const KFileItemList& items) { Q_ASSERT(items.count() >= 2); KFileItemActions fileItemActions(this); fileItemActions.runPreferredApplications(items, QString()); } void DolphinViewContainer::showItemInfo(const KFileItem& item) { if (item.isNull()) { m_statusBar->resetToDefaultText(); } else { m_statusBar->setText(item.getStatusBarInfo()); } } void DolphinViewContainer::closeFilterBar() { m_filterBar->closeFilterBar(); m_view->setFocus(); emit showFilterBarChanged(false); } void DolphinViewContainer::setNameFilter(const QString& nameFilter) { m_view->setNameFilter(nameFilter); delayedStatusBarUpdate(); } void DolphinViewContainer::activate() { setActive(true); } void DolphinViewContainer::slotUrlNavigatorLocationAboutToBeChanged(const QUrl&) { saveViewState(); } void DolphinViewContainer::slotUrlNavigatorLocationChanged(const QUrl& url) { slotReturnPressed(); if (KProtocolManager::supportsListing(url)) { setSearchModeEnabled(isSearchUrl(url)); m_view->setUrl(url); tryRestoreViewState(); if (m_autoGrabFocus && isActive() && !isSearchUrl(url)) { // When an URL has been entered, the view should get the focus. // The focus must be requested asynchronously, as changing the URL might create // a new view widget. QTimer::singleShot(0, this, &DolphinViewContainer::requestFocus); } } else if (KProtocolManager::isSourceProtocol(url)) { QString app = QStringLiteral("konqueror"); if (url.scheme().startsWith(QLatin1String("http"))) { showMessage(i18nc("@info:status", // krazy:exclude=qmethods "Dolphin does not support web pages, the web browser has been launched"), Information); const KConfigGroup config(KSharedConfig::openConfig(QStringLiteral("kdeglobals")), "General"); const QString browser = config.readEntry("BrowserApplication"); if (!browser.isEmpty()) { app = browser; if (app.startsWith('!')) { // a literal command has been configured, remove the '!' prefix app = app.mid(1); } } } else { showMessage(i18nc("@info:status", "Protocol not supported by Dolphin, Konqueror has been launched"), Information); } const QString secureUrl = KShell::quoteArg(url.toDisplayString(QUrl::PreferLocalFile)); const QString command = app + ' ' + secureUrl; KRun::runCommand(command, app, app, this); } else { showMessage(i18nc("@info:status", "Invalid protocol"), Error); } } void DolphinViewContainer::slotUrlSelectionRequested(const QUrl& url) { qCDebug(DolphinDebug) << "slotUrlSelectionRequested: " << url; m_view->markUrlsAsSelected({url}); m_view->markUrlAsCurrent(url); // makes the item scroll into view } void DolphinViewContainer::redirect(const QUrl& oldUrl, const QUrl& newUrl) { Q_UNUSED(oldUrl); const bool block = m_urlNavigator->signalsBlocked(); m_urlNavigator->blockSignals(true); // Assure that the location state is reset for redirection URLs. This // allows to skip redirection URLs when going back or forward in the // URL history. m_urlNavigator->saveLocationState(QByteArray()); m_urlNavigator->setLocationUrl(newUrl); setSearchModeEnabled(isSearchUrl(newUrl)); m_urlNavigator->blockSignals(block); } void DolphinViewContainer::requestFocus() { m_view->setFocus(); } void DolphinViewContainer::saveUrlCompletionMode(KCompletion::CompletionMode completion) { GeneralSettings::setUrlCompletionMode(completion); } void DolphinViewContainer::slotReturnPressed() { if (!GeneralSettings::editableUrl()) { m_urlNavigator->setUrlEditable(false); } } void DolphinViewContainer::startSearching() { const QUrl url = m_searchBox->urlForSearching(); if (url.isValid() && !url.isEmpty()) { m_view->setViewPropertiesContext(QStringLiteral("search")); m_urlNavigator->setLocationUrl(url); } } void DolphinViewContainer::closeSearchBox() { setSearchModeEnabled(false); } void DolphinViewContainer::stopDirectoryLoading() { m_view->stopLoading(); m_statusBar->setProgress(100); } void DolphinViewContainer::slotStatusBarZoomLevelChanged(int zoomLevel) { m_view->setZoomLevel(zoomLevel); } void DolphinViewContainer::showErrorMessage(const QString& msg) { showMessage(msg, Error); } bool DolphinViewContainer::isSearchUrl(const QUrl& url) const { return url.scheme().contains(QStringLiteral("search")); } void DolphinViewContainer::saveViewState() { QByteArray locationState; QDataStream stream(&locationState, QIODevice::WriteOnly); m_view->saveState(stream); m_urlNavigator->saveLocationState(locationState); } void DolphinViewContainer::tryRestoreViewState() { QByteArray locationState = m_urlNavigator->locationState(); if (!locationState.isEmpty()) { QDataStream stream(&locationState, QIODevice::ReadOnly); m_view->restoreState(stream); } } diff --git a/src/search/dolphinsearchbox.cpp b/src/search/dolphinsearchbox.cpp index c6943c608..345692008 100644 --- a/src/search/dolphinsearchbox.cpp +++ b/src/search/dolphinsearchbox.cpp @@ -1,544 +1,571 @@ /*************************************************************************** * 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 #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_saveSearchAction(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: // #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; + m_saveSearchAction->setEnabled(true); emit searchRequest(); } void DolphinSearchBox::emitCloseRequest() { m_startSearchTimer->stop(); m_startedSearching = false; + m_saveSearchAction->setEnabled(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::slotSearchSaved() +{ + const QUrl searchURL = urlForSearching(); + if (searchURL.isValid()) { + PlacesItemModel model; + const QString label = i18n("Search for %1 in %2", text(), searchPath().fileName()); + PlacesItem* item = model.createPlacesItem(label, + searchURL, + QStringLiteral("folder-saved-search-symbolic")); + model.appendItemToGroup(item); + model.saveBookmarks(); + } +} + 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); + // 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); + // 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")); } diff --git a/src/search/dolphinsearchbox.h b/src/search/dolphinsearchbox.h index 5063c2bf5..f1ea49091 100644 --- a/src/search/dolphinsearchbox.h +++ b/src/search/dolphinsearchbox.h @@ -1,185 +1,187 @@ /*************************************************************************** * 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 * ***************************************************************************/ #ifndef DOLPHINSEARCHBOX_H #define DOLPHINSEARCHBOX_H #include #include class DolphinFacetsWidget; class QLineEdit; class KSeparator; class QToolButton; class QScrollArea; class QLabel; class QVBoxLayout; class KMoreToolsMenuFactory; /** * @brief Input box for searching files with or without Baloo. * * The widget allows to specify: * - Where to search: Everywhere or below the current directory * - What to search: Filenames or content * * If Baloo is available and the current folder is indexed, further * options are offered. */ class DolphinSearchBox : public QWidget { Q_OBJECT public: explicit DolphinSearchBox(QWidget* parent = 0); virtual ~DolphinSearchBox(); /** * Sets the text that should be used as input for * searching. */ void setText(const QString& text); /** * Returns the text that should be used as input * for searching. */ QString text() const; /** * Sets the current path that is used as root for * searching files, if "From Here" has been selected. */ void setSearchPath(const QUrl& url); QUrl searchPath() const; /** @return URL that will start the searching of files. */ QUrl urlForSearching() const; /** * Extracts information from the given search \a url to * initialize the search box properly. */ void fromSearchUrl(const QUrl& url); /** * Selects the whole text of the search box. */ void selectAll(); /** * Set the search box to the active mode, if \a active * is true. The active mode is default. The inactive mode only differs * visually from the active mode, no change of the behavior is given. * * Using the search box in the inactive mode is useful when having split views, * where the inactive view is indicated by an search box visually. */ void setActive(bool active); /** * @return True, if the search box is in the active mode. * @see DolphinSearchBox::setActive() */ bool isActive() const; protected: virtual bool event(QEvent* event) Q_DECL_OVERRIDE; virtual void showEvent(QShowEvent* event) Q_DECL_OVERRIDE; virtual void hideEvent(QHideEvent* event) Q_DECL_OVERRIDE; virtual void keyReleaseEvent(QKeyEvent* event) Q_DECL_OVERRIDE; virtual bool eventFilter(QObject* obj, QEvent* event) Q_DECL_OVERRIDE; signals: /** * Is emitted when a searching should be triggered. */ void searchRequest(); /** * Is emitted when the user has changed a character of * the text that should be used as input for searching. */ void searchTextChanged(const QString& text); void returnPressed(); /** * Emitted as soon as the search box should get closed. */ void closeRequest(); /** * Is emitted, if the searchbox has been activated by * an user interaction * @see DolphinSearchBox::setActive() */ void activated(); private slots: void emitSearchRequest(); void emitCloseRequest(); void slotConfigurationChanged(); void slotSearchTextChanged(const QString& text); void slotReturnPressed(); void slotFacetsButtonToggled(); void slotFacetChanged(); + void slotSearchSaved(); private: void initButton(QToolButton* button); void loadSettings(); void saveSettings(); void init(); /** * @return URL that represents the Baloo query for starting the search. */ QUrl balooUrlForSearching() const; /** * Extracts information from the given Baloo search \a url to * initialize the search box properly. */ void fromBalooSearchUrl(const QUrl& url); void updateFacetsToggleButton(); private: bool m_startedSearching; bool m_active; QVBoxLayout* m_topLayout; QLabel* m_searchLabel; QLineEdit* m_searchInput; + QAction* m_saveSearchAction; QScrollArea* m_optionsScrollArea; QToolButton* m_fileNameButton; QToolButton* m_contentButton; KSeparator* m_separator; QToolButton* m_fromHereButton; QToolButton* m_everywhereButton; QToolButton* m_facetsToggleButton; DolphinFacetsWidget* m_facetsWidget; QUrl m_searchPath; QScopedPointer m_menuFactory; QTimer* m_startSearchTimer; }; #endif