diff options
74 files changed, 756 insertions, 669 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index f103107f8..0ab79e643 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,14 +1,14 @@ cmake_minimum_required(VERSION 3.0) # KDE Application Version, managed by release script -set (KDE_APPLICATIONS_VERSION_MAJOR "19") -set (KDE_APPLICATIONS_VERSION_MINOR "12") -set (KDE_APPLICATIONS_VERSION_MICRO "1") -set (KDE_APPLICATIONS_VERSION "${KDE_APPLICATIONS_VERSION_MAJOR}.${KDE_APPLICATIONS_VERSION_MINOR}.${KDE_APPLICATIONS_VERSION_MICRO}") -project(Dolphin VERSION ${KDE_APPLICATIONS_VERSION}) +set (RELEASE_SERVICE_VERSION_MAJOR "20") +set (RELEASE_SERVICE_VERSION_MINOR "03") +set (RELEASE_SERVICE_VERSION_MICRO "70") +set (RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}") +project(Dolphin VERSION ${RELEASE_SERVICE_VERSION}) set(QT_MIN_VERSION "5.11.0") -set(KF5_MIN_VERSION "5.63.0") +set(KF5_MIN_VERSION "5.67.0") # ECM setup find_package(ECM ${KF5_MIN_VERSION} CONFIG REQUIRED) @@ -24,7 +24,7 @@ include(KDECMakeSettings) include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) include(ECMQtDeclareLoggingCategory) -ecm_setup_version(${KDE_APPLICATIONS_VERSION} VARIABLE_PREFIX DOLPHIN +ecm_setup_version(${RELEASE_SERVICE_VERSION} VARIABLE_PREFIX DOLPHIN VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/src/dolphin_version.h" ) @@ -70,7 +70,7 @@ find_package(KF5 ${KF5_MIN_VERSION} OPTIONAL_COMPONENTS Activities ) set_package_properties(KF5Activities PROPERTIES DESCRIPTION "KActivities libraries" - URL "http://www.kde.org" + URL "https://www.kde.org" TYPE OPTIONAL PURPOSE "For tracking which folders are frequently accessed on a Plasma desktop" ) @@ -79,14 +79,14 @@ find_package(Phonon4Qt5 CONFIG REQUIRED) find_package(KF5Baloo ${KF5_MIN_VERSION}) set_package_properties(KF5Baloo PROPERTIES DESCRIPTION "Baloo Core libraries" - URL "http://www.kde.org" + URL "https://www.kde.org" TYPE OPTIONAL PURPOSE "For adding desktop-wide search and tagging support to dolphin" ) find_package(KF5BalooWidgets 19.07.70) set_package_properties(KF5BalooWidgets PROPERTIES DESCRIPTION "Baloos Widgets" - URL "http://www.kde.org" + URL "https://www.kde.org" TYPE OPTIONAL ) @@ -148,7 +148,7 @@ install(FILES configure_file(org.kde.dolphin.FileManager1.service.in ${CMAKE_CURRENT_BINARY_DIR}/org.kde.dolphin.FileManager1.service) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.kde.dolphin.FileManager1.service - DESTINATION ${DBUS_SERVICES_INSTALL_DIR}) + DESTINATION ${KDE_INSTALL_DBUSSERVICEDIR}) install(FILES dolphin.categories DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR}) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/cmake/DbusInterfaceMacros.cmake b/cmake/DbusInterfaceMacros.cmake index 1083dfd44..b5f6a80b0 100644 --- a/cmake/DbusInterfaceMacros.cmake +++ b/cmake/DbusInterfaceMacros.cmake @@ -9,7 +9,7 @@ macro (generate_and_install_dbus_interface main_project_target header_file outpu ) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${output_xml_file} - DESTINATION ${DBUS_INTERFACES_INSTALL_DIR} + DESTINATION ${KDE_INSTALL_DBUSINTERFACEDIR} ) add_dependencies( ${main_project_target} diff --git a/doc/index.docbook b/doc/index.docbook index d2c06c6a0..0d1a35f58 100644 --- a/doc/index.docbook +++ b/doc/index.docbook @@ -312,7 +312,7 @@ The buttons in the toolbar which control the appearance of the view. All the settings discussed below and other options concerning, ⪚ the sorting of the files in the current folder, can also be modified in the <guimenu>View</guimenu> menu and in the -<link linkend="view-properties-dialog">View Properties dialog</link>. By +<link linkend="view-properties-dialog">View Display Style dialog</link>. By default, these settings are remembered for each folder separately. This behavior can be changed in the <link linkend="preferences-dialog-general"><quote>General</quote></link> section of the settings. @@ -951,11 +951,11 @@ using the <link linkend="preferences-dialog">Preferences Dialog</link>. <listitem><para> Settings which determine how the contents of a folder are displayed in &dolphin;. -These settings are called <link linkend="view-properties">View Properties</link> +These settings are called <link linkend="view-properties">View Display Styles</link> and can be controlled with toolbar buttons, via the <guimenu>View</guimenu> menu, -and with the <link linkend="view-properties-dialog">View Properties Dialog</link>. -In the default configuration, the view properties are remembered for each folder, -but &dolphin; can also be configured to use common view properties for all folders +and with the <link linkend="view-properties-dialog">View Display Style dialog</link>. +In the default configuration, all folders use the same display style, +but &dolphin; can also be configured to remember each folder's display style separately in the <link linkend="preferences-dialog-general-behavior"><quote>General</quote> section of the settings</link>. </para></listitem> @@ -1008,9 +1008,9 @@ tab bar at the top. <itemizedlist> <listitem><para> -In the <guilabel>View</guilabel> section, you can configure whether the -<link linkend="view-properties"> view properties</link> are stored for each -folder or if common view properties are to be used for all folders. +In the <guilabel>View</guilabel> section, you can configure whether the same +<link linkend="view-properties">view display style</link> is shared among all folders +or folders remember their own individual view display styles. </para></listitem> <listitem><para> @@ -1412,7 +1412,7 @@ largest files can be deleted automatically. </sect1> <!-- begin copy to konqueror filemanager.docbook --> <sect1 id="view-properties"> -<title>Folder View Properties</title> +<title>Folder View Display Style</title> <para> The following settings control how the contents of a folder are displayed in the @@ -1454,31 +1454,31 @@ What additional information (besides the name) is shown in the Icons or Details </itemizedlist> <para> -The view properties can be configured in the +The view display style can be configured in the <menuchoice><guimenu>View</guimenu></menuchoice> menu, some (such as the view mode) can also be changed using toolbar buttons. </para> <sect2 id="view-properties-dialog"> -<title>The View Properties Dialog</title> +<title>The View Display Style dialog</title> <para> <screenshot> -<screeninfo>Screenshot of the View Properties dialog</screeninfo> +<screeninfo>Screenshot of the View Display Style dialog</screeninfo> <mediaobject> <imageobject> <imagedata fileref="viewproperties-dialog.png" format="PNG"/> </imageobject> <textobject> -<phrase>The View Properties dialog.</phrase> +<phrase>The View Display Style dialog.</phrase> </textobject> -<caption><para>The View Properties Dialog.</para></caption> +<caption><para>The View Display Style Dialog.</para></caption> </mediaobject> </screenshot> -The <guilabel>View Properties</guilabel> dialog can be used to quickly modify -several view properties at once. This is done for the current folder, for the +The <guilabel>View Display Style</guilabel> dialog can be used to quickly modify +the view display styles for many folders at once. This is done for the current folder, for the current folder including all subfolders, or even for all folders, depending on the choice made in the <guilabel>Apply to</guilabel> section. </para> @@ -1917,9 +1917,9 @@ if necessary, and selects the location such that it can be replaced quickly. <varlistentry> <term><menuchoice> <guimenu>View</guimenu> -<guimenuitem>Adjust View Properties...</guimenuitem> +<guimenuitem>Adjust View Display Style...</guimenuitem> </menuchoice></term> -<listitem><para><action>Opens the <link linkend="view-properties-dialog">View Properties +<listitem><para><action>Opens the <link linkend="view-properties-dialog">View Display Style Dialog</link>.</action></para></listitem> </varlistentry> </variablelist> @@ -2020,6 +2020,17 @@ for this action.</para></listitem> <varlistentry> <term><menuchoice> +<shortcut> +<keycombo action="simul">&Ctrl;&Shift;<keycap>F</keycap></keycombo> +</shortcut> +<guimenu>Tools</guimenu> +<guimenuitem>Open Preferred Search Tool</guimenuitem> +</menuchoice></term> +<listitem><para><action>Opens preferred search tool in the current folder.</action></para></listitem> +</varlistentry> + +<varlistentry> +<term><menuchoice> <guimenu>Tools</guimenu> <guimenuitem>Compare Files</guimenuitem> </menuchoice></term> @@ -2043,8 +2054,8 @@ connection manually.</action></para></listitem> <title>The Settings and Help Menu</title> <para> &dolphin; has the common &kde; <guimenu>Settings</guimenu> and <guimenu>Help</guimenu> -menu items, for more information read the sections about the <ulink url="help:/fundamentals/ui.html#menus-settings" ->Settings Menu</ulink> and <ulink url="help:/fundamentals/ui.html#menus-help">Help Menu</ulink> +menu items, for more information read the sections about the <ulink url="help:/fundamentals/menus.html#menus-settings" +>Settings Menu</ulink> and <ulink url="help:/fundamentals/menus.html#menus-help">Help Menu</ulink> of the &kde; Fundamentals. </para> </sect2> diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 617d59480..e8d623d2f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -99,7 +99,6 @@ set(dolphinprivate_LIB_SRCS views/dolphinview.cpp views/dolphinviewactionhandler.cpp views/draganddrophelper.cpp - views/renamedialog.cpp views/versioncontrol/updateitemstatesthread.cpp views/versioncontrol/versioncontrolobserver.cpp views/viewmodecontroller.cpp @@ -301,9 +300,10 @@ ecm_add_app_icon(dolphin_SRCS ICONS ${ICONS_SRCS}) kf5_add_kdeinit_executable(dolphin ${dolphin_SRCS}) -target_link_libraries(kdeinit_dolphin PRIVATE - dolphinstatic +target_link_libraries(kdeinit_dolphin PUBLIC dolphinprivate + PRIVATE + dolphinstatic KF5::Crash ) diff --git a/src/dolphinbookmarkhandler.cpp b/src/dolphinbookmarkhandler.cpp index bb8f641ec..ded83d6bb 100644 --- a/src/dolphinbookmarkhandler.cpp +++ b/src/dolphinbookmarkhandler.cpp @@ -54,11 +54,6 @@ DolphinBookmarkHandler::~DolphinBookmarkHandler() { } -void DolphinBookmarkHandler::fillControlMenu(QMenu* menu, KActionCollection* collection) -{ - m_bookmarkControlMenu.reset(new KBookmarkMenu(m_bookmarkManager, this, menu, collection)); -} - QString DolphinBookmarkHandler::currentTitle() const { return title(m_mainWindow->activeViewContainer()); diff --git a/src/dolphinbookmarkhandler.h b/src/dolphinbookmarkhandler.h index 6fd511d80..bafef41f8 100644 --- a/src/dolphinbookmarkhandler.h +++ b/src/dolphinbookmarkhandler.h @@ -36,7 +36,7 @@ class DolphinBookmarkHandler : public QObject, public KBookmarkOwner public: DolphinBookmarkHandler(DolphinMainWindow *mainWindow, KActionCollection *collection, QMenu *menu, QObject *parent); ~DolphinBookmarkHandler() override; - void fillControlMenu(QMenu *menu, KActionCollection *collection); + private: QString currentTitle() const override; QUrl currentUrl() const override; @@ -55,7 +55,6 @@ private: DolphinMainWindow* m_mainWindow; KBookmarkManager *m_bookmarkManager; QScopedPointer<KBookmarkMenu> m_bookmarkMenu; - QScopedPointer<KBookmarkMenu> m_bookmarkControlMenu; }; #endif // DOLPHINBOOKMARKHANDLER_H diff --git a/src/dolphincontextmenu.cpp b/src/dolphincontextmenu.cpp index c8e9629c3..ca0f5e32f 100644 --- a/src/dolphincontextmenu.cpp +++ b/src/dolphincontextmenu.cpp @@ -32,7 +32,6 @@ #include "views/dolphinview.h" #include "views/viewmodecontroller.h" -#include <KAbstractFileItemActionPlugin> #include <KActionCollection> #include <KFileItemActions> #include <KFileItemListProperties> @@ -95,8 +94,13 @@ void DolphinContextMenu::setCustomActions(const QList<QAction*>& actions) DolphinContextMenu::Command DolphinContextMenu::open() { // get the context information - if (m_baseUrl.scheme() == QLatin1String("trash")) { + const auto scheme = m_baseUrl.scheme(); + if (scheme == QLatin1String("trash")) { m_context |= TrashContext; + } else if (scheme.contains(QLatin1String("search"))) { + m_context |= SearchContext; + } else if (scheme.contains(QLatin1String("timeline"))) { + m_context |= TimelineContext; } if (!m_fileInfo.isNull() && !m_selectedItems.isEmpty()) { @@ -184,6 +188,36 @@ void DolphinContextMenu::openTrashItemContextMenu() } } +void DolphinContextMenu::addDirectoryItemContextMenu(KFileItemActions &fileItemActions) +{ + // insert 'Open in new window' and 'Open in new tab' entries + + const KFileItemListProperties& selectedItemsProps = selectedItemsProperties(); + + addAction(m_mainWindow->actionCollection()->action(QStringLiteral("open_in_new_window"))); + addAction(m_mainWindow->actionCollection()->action(QStringLiteral("open_in_new_tab"))); + + // Insert 'Open With' entries + addOpenWithActions(fileItemActions); + + // set up '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(QList<QUrl>() << 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(); +} + void DolphinContextMenu::openItemContextMenu() { Q_ASSERT(!m_fileInfo.isNull()); @@ -198,31 +232,10 @@ void DolphinContextMenu::openItemContextMenu() fileItemActions.setItemListProperties(selectedItemsProps); if (m_selectedItems.count() == 1) { + // single files if (m_fileInfo.isDir()) { - // 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 'Open With' entries - addOpenWithActions(fileItemActions); - - // set up '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(); - } else if (m_baseUrl.scheme().contains(QLatin1String("search")) || m_baseUrl.scheme().contains(QLatin1String("timeline"))) { + addDirectoryItemContextMenu(fileItemActions); + } else if (m_context & TimelineContext || m_context & SearchContext) { addOpenWithActions(fileItemActions); openParentAction = new QAction(QIcon::fromTheme(QStringLiteral("document-open-folder")), @@ -253,8 +266,9 @@ void DolphinContextMenu::openItemContextMenu() addSeparator(); } } else { + // multiple files bool selectionHasOnlyDirs = true; - foreach (const KFileItem& item, m_selectedItems) { + for (const auto &item : qAsConst(m_selectedItems)) { const QUrl& url = DolphinView::openItemAsFolderUrl(item); if (url.isEmpty()) { selectionHasOnlyDirs = false; @@ -338,7 +352,7 @@ void DolphinContextMenu::openViewportContextMenu() KNewFileMenu* newFileMenu = m_mainWindow->newFileMenu(); newFileMenu->setViewShowsHiddenFiles(view->hiddenFilesShown()); newFileMenu->checkUpToDate(); - newFileMenu->setPopupFiles(m_baseUrl); + newFileMenu->setPopupFiles(QList<QUrl>() << m_baseUrl); addMenu(newFileMenu->menu()); QAction* pasteAction = createPasteAction(); diff --git a/src/dolphincontextmenu.h b/src/dolphincontextmenu.h index bf0cf2c97..3f36895ec 100644 --- a/src/dolphincontextmenu.h +++ b/src/dolphincontextmenu.h @@ -146,7 +146,9 @@ private: { NoContext = 0, ItemContext = 1, - TrashContext = 2 + TrashContext = 2, + TimelineContext = 4, + SearchContext = 8, }; QPoint m_pos; @@ -167,6 +169,8 @@ private: Command m_command; DolphinRemoveAction* m_removeAction; // Action that represents either 'Move To Trash' or 'Delete' + void addDirectoryItemContextMenu(KFileItemActions &fileItemActions); + }; #endif diff --git a/src/dolphindockwidget.h b/src/dolphindockwidget.h index 0b745f083..fd3882612 100644 --- a/src/dolphindockwidget.h +++ b/src/dolphindockwidget.h @@ -30,7 +30,7 @@ class DolphinDockWidget : public QDockWidget Q_OBJECT public: - explicit DolphinDockWidget(const QString& title = QString(), QWidget* parent = nullptr, Qt::WindowFlags flags = nullptr); + explicit DolphinDockWidget(const QString& title = QString(), QWidget* parent = nullptr, Qt::WindowFlags flags = {}); ~DolphinDockWidget() override; /** diff --git a/src/dolphinmainwindow.cpp b/src/dolphinmainwindow.cpp index 56ea93e10..642c24e60 100644 --- a/src/dolphinmainwindow.cpp +++ b/src/dolphinmainwindow.cpp @@ -57,6 +57,7 @@ #include <KJobWidgets> #include <KLocalizedString> #include <KMessageBox> +#include <KNS3/KMoreToolsMenuFactory> #include <KProtocolInfo> #include <KProtocolManager> #include <KRun> @@ -197,6 +198,8 @@ DolphinMainWindow::DolphinMainWindow() : toolBar()->installEventFilter(middleClickEventFilter); setupWhatsThis(); + + QTimer::singleShot(0, this, &DolphinMainWindow::setupUpdateOpenPreferredSearchToolAction); } DolphinMainWindow::~DolphinMainWindow() @@ -592,13 +595,13 @@ void DolphinMainWindow::updateNewMenu() { m_newFileMenu->setViewShowsHiddenFiles(activeViewContainer()->view()->hiddenFilesShown()); m_newFileMenu->checkUpToDate(); - m_newFileMenu->setPopupFiles(activeViewContainer()->url()); + m_newFileMenu->setPopupFiles(QList<QUrl>() << activeViewContainer()->url()); } void DolphinMainWindow::createDirectory() { m_newFileMenu->setViewShowsHiddenFiles(activeViewContainer()->view()->hiddenFilesShown()); - m_newFileMenu->setPopupFiles(activeViewContainer()->url()); + m_newFileMenu->setPopupFiles(QList<QUrl>() << activeViewContainer()->url()); m_newFileMenu->createDirectory(); } @@ -933,23 +936,88 @@ void DolphinMainWindow::toggleShowMenuBar() } } -void DolphinMainWindow::openTerminal() +QString DolphinMainWindow::activeContainerLocalPath() { - QString dir(QDir::homePath()); - - // If the given directory is not local, it can still be the URL of an - // ioslave using UDS_LOCAL_PATH which to be converted first. KIO::StatJob* statJob = KIO::mostLocalUrl(m_activeViewContainer->url()); KJobWidgets::setWindow(statJob, this); statJob->exec(); QUrl url = statJob->mostLocalUrl(); - - //If the URL is local after the above conversion, set the directory. if (url.isLocalFile()) { - dir = url.toLocalFile(); + return url.toLocalFile(); + } + return QDir::homePath(); +} + +QPointer<QAction> DolphinMainWindow::preferredSearchTool() +{ + m_searchTools.clear(); + KMoreToolsMenuFactory("dolphin/search-tools").fillMenuFromGroupingNames( + &m_searchTools, { "files-find" }, QUrl::fromLocalFile(activeContainerLocalPath()) + ); + QList<QAction*> actions = m_searchTools.actions(); + if (actions.isEmpty()) { + return nullptr; + } + QAction* action = actions.first(); + if (action->isSeparator()) { + return nullptr; + } + return action; +} + +void DolphinMainWindow::setupUpdateOpenPreferredSearchToolAction() +{ + QAction* openPreferredSearchTool = actionCollection()->action(QStringLiteral("open_preferred_search_tool")); + const QList<QWidget*> widgets = openPreferredSearchTool->associatedWidgets(); + for (QWidget* widget : widgets) { + QMenu* menu = qobject_cast<QMenu*>(widget); + if (menu) { + connect(menu, &QMenu::aboutToShow, this, &DolphinMainWindow::updateOpenPreferredSearchToolAction); + } } - KToolInvocation::invokeTerminal(QString(), dir); + // Update the open_preferred_search_tool action *before* the Configure Shortcuts window is shown, + // since this action is then listed in that window and it should be up-to-date when it is displayed. + // This update is instantaneous if user made no changes to the search tools in the meantime. + // Maybe all KStandardActions should defer calls to their slots, so that we could simply connect() to trigger()? + connect( + actionCollection()->action(KStandardAction::name(KStandardAction::KeyBindings)), &QAction::hovered, + this, &DolphinMainWindow::updateOpenPreferredSearchToolAction + ); + + updateOpenPreferredSearchToolAction(); +} + +void DolphinMainWindow::updateOpenPreferredSearchToolAction() +{ + QAction* openPreferredSearchTool = actionCollection()->action(QStringLiteral("open_preferred_search_tool")); + if (!openPreferredSearchTool) { + return; + } + QPointer<QAction> tool = preferredSearchTool(); + if (tool) { + openPreferredSearchTool->setVisible(true); + openPreferredSearchTool->setText(i18nc("@action:inmenu Tools", "Open %1", tool->text())); + openPreferredSearchTool->setIcon(tool->icon()); + } else { + openPreferredSearchTool->setVisible(false); + // still visible in Shortcuts configuration window + openPreferredSearchTool->setText(i18nc("@action:inmenu Tools", "Open Preferred Search Tool")); + openPreferredSearchTool->setIcon(QIcon::fromTheme(QStringLiteral("search"))); + } +} + +void DolphinMainWindow::openPreferredSearchTool() +{ + QPointer<QAction> tool = preferredSearchTool(); + if (tool) { + tool->trigger(); + } +} + +void DolphinMainWindow::openTerminal() +{ + KToolInvocation::invokeTerminal(QString(), activeContainerLocalPath()); } void DolphinMainWindow::editSettings() @@ -1091,7 +1159,9 @@ void DolphinMainWindow::updateControlMenu() // Add a curated assortment of items from the "Tools" menu addActionToMenu(ac->action(QStringLiteral("show_filter_bar")), menu); + addActionToMenu(ac->action(QStringLiteral("open_preferred_search_tool")), menu); addActionToMenu(ac->action(QStringLiteral("open_terminal")), menu); + connect(menu, &QMenu::aboutToShow, this, &DolphinMainWindow::updateOpenPreferredSearchToolAction); menu->addSeparator(); @@ -1247,6 +1317,7 @@ void DolphinMainWindow::setupActions() QAction* addToPlaces = actionCollection()->addAction(QStringLiteral("add_to_places")); addToPlaces->setIcon(QIcon::fromTheme(QStringLiteral("bookmark-new"))); + addToPlaces->setText(i18nc("@action:inmenu Add current folder to places", "Add to Places")); addToPlaces->setWhatsThis(xi18nc("@info:whatsthis", "This adds the selected folder " "to the Places panel.")); connect(addToPlaces, &QAction::triggered, this, &DolphinMainWindow::addToPlaces); @@ -1465,6 +1536,15 @@ void DolphinMainWindow::setupActions() compareFiles->setEnabled(false); connect(compareFiles, &QAction::triggered, this, &DolphinMainWindow::compareFiles); + QAction* openPreferredSearchTool = actionCollection()->addAction(QStringLiteral("open_preferred_search_tool")); + openPreferredSearchTool->setText(i18nc("@action:inmenu Tools", "Open Preferred Search Tool")); + openPreferredSearchTool->setWhatsThis(xi18nc("@info:whatsthis", + "<para>This opens a preferred search tool for the viewed location.</para>" + "<para>Use <emphasis>More Search Tools</emphasis> menu to configure it.</para>")); + openPreferredSearchTool->setIcon(QIcon::fromTheme(QStringLiteral("search"))); + actionCollection()->setDefaultShortcut(openPreferredSearchTool, Qt::CTRL + Qt::SHIFT + Qt::Key_F); + connect(openPreferredSearchTool, &QAction::triggered, this, &DolphinMainWindow::openPreferredSearchTool); + #ifdef HAVE_TERMINAL if (KAuthorized::authorize(QStringLiteral("shell_access"))) { QAction* openTerminal = actionCollection()->addAction(QStringLiteral("open_terminal")); @@ -1475,6 +1555,12 @@ void DolphinMainWindow::setupActions() openTerminal->setIcon(QIcon::fromTheme(QStringLiteral("dialog-scripts"))); actionCollection()->setDefaultShortcut(openTerminal, Qt::SHIFT + Qt::Key_F4); connect(openTerminal, &QAction::triggered, this, &DolphinMainWindow::openTerminal); + + QAction* focusTerminalPanel = actionCollection()->addAction(QStringLiteral("focus_terminal_panel")); + focusTerminalPanel->setText(i18nc("@action:inmenu Tools", "Focus Terminal Panel")); + focusTerminalPanel->setIcon(QIcon::fromTheme(QStringLiteral("swap-panels"))); + actionCollection()->setDefaultShortcut(focusTerminalPanel, Qt::CTRL + Qt::SHIFT + Qt::Key_F4); + connect(focusTerminalPanel, &QAction::triggered, this, &DolphinMainWindow::focusTerminalPanel); } #endif @@ -1810,7 +1896,6 @@ void DolphinMainWindow::updateFileAndEditActions() stateChanged(QStringLiteral("has_no_selection")); addToPlacesAction->setEnabled(true); - addToPlacesAction->setText(i18nc("@action:inmenu Add current folder to places", "Add '%1' to Places", m_activeViewContainer->placesText())); } else { stateChanged(QStringLiteral("has_selection")); @@ -1823,10 +1908,8 @@ void DolphinMainWindow::updateFileAndEditActions() if (list.length() == 1 && list.first().isDir()) { addToPlacesAction->setEnabled(true); - addToPlacesAction->setText(i18nc("@action:inmenu Add current folder to places", "Add '%1' to Places", list.first().name())); } else { addToPlacesAction->setEnabled(false); - addToPlacesAction->setText(i18nc("@action:inmenu Add current folder to places", "Add to Places")); } KFileItemListProperties capabilities(list); @@ -2208,6 +2291,8 @@ bool DolphinMainWindow::event(QEvent *event) QWhatsThisClickedEvent* whatsThisEvent = dynamic_cast<QWhatsThisClickedEvent*>(event); QDesktopServices::openUrl(QUrl(whatsThisEvent->href())); return true; + } else if (event->type() == QEvent::WindowActivate) { + updateOpenPreferredSearchToolAction(); } return KXmlGuiWindow::event(event); } @@ -2224,6 +2309,22 @@ bool DolphinMainWindow::eventFilter(QObject* obj, QEvent* event) return false; } +void DolphinMainWindow::focusTerminalPanel() +{ + if (m_terminalPanel->isVisible()) { + if (m_terminalPanel->terminalHasFocus()) { + m_activeViewContainer->view()->setFocus(Qt::FocusReason::ShortcutFocusReason); + actionCollection()->action(QStringLiteral("focus_terminal_panel"))->setText(i18nc("@action:inmenu Tools", "Focus Terminal Panel")); + } else { + m_terminalPanel->setFocus(Qt::FocusReason::ShortcutFocusReason); + actionCollection()->action(QStringLiteral("focus_terminal_panel"))->setText(i18nc("@action:inmenu Tools", "Defocus Terminal Panel")); + } + } else { + actionCollection()->action(QStringLiteral("show_terminal_panel"))->trigger(); + actionCollection()->action(QStringLiteral("focus_terminal_panel"))->setText(i18nc("@action:inmenu Tools", "Defocus Terminal Panel")); + } +} + DolphinMainWindow::UndoUiInterface::UndoUiInterface() : KIO::FileUndoManager::UiInterface() { diff --git a/src/dolphinmainwindow.h b/src/dolphinmainwindow.h index 3d86340d6..940a03d83 100644 --- a/src/dolphinmainwindow.h +++ b/src/dolphinmainwindow.h @@ -30,6 +30,7 @@ #include <QIcon> #include <QList> +#include <QMenu> #include <QPointer> #include <QUrl> #include <QVector> @@ -352,9 +353,21 @@ private slots: */ void toggleShowMenuBar(); + /** Sets up updates for "Open Preferred Search Tool" action. */ + void setupUpdateOpenPreferredSearchToolAction(); + + /** Updates "Open Preferred Search Tool" action. */ + void updateOpenPreferredSearchToolAction(); + + /** Opens preferred search tool for the current location. */ + void openPreferredSearchTool(); + /** Opens a terminal window for the current location. */ void openTerminal(); + /** Focus a Terminal Panel. */ + void focusTerminalPanel(); + /** Opens the settings dialog for Dolphin. */ void editSettings(); @@ -597,6 +610,15 @@ private: /** Adds "What's This?" texts to many widgets and StandardActions. */ void setupWhatsThis(); + /** + * Returns the KIO::StatJob::mostLocalUrl() for the active container URL + * if it's a local file. Otherwise returns the user's home path. + */ + QString activeContainerLocalPath(); + + /** Returns preferred search tool as configured in "More Search Tools" menu. */ + QPointer<QAction> preferredSearchTool(); + private: /** * Implements a custom error handling for the undo manager. This @@ -633,6 +655,9 @@ private: KToolBarPopupAction* m_backAction; KToolBarPopupAction* m_forwardAction; + + QMenu m_searchTools; + }; inline DolphinViewContainer* DolphinMainWindow::activeViewContainer() const diff --git a/src/dolphinpart.cpp b/src/dolphinpart.cpp index 607917f9a..7e7425121 100644 --- a/src/dolphinpart.cpp +++ b/src/dolphinpart.cpp @@ -40,6 +40,7 @@ #include <KLocalizedString> #include <KMessageBox> #include <KMimeTypeEditor> +#include <KNS3/KMoreToolsMenuFactory> #include <KPluginFactory> #include <KRun> #include <KSharedConfig> @@ -532,28 +533,21 @@ void DolphinPart::setNameFilter(const QString& nameFilter) void DolphinPart::slotOpenTerminal() { - QString dir(QDir::homePath()); - - QUrl u(url()); - - // If the given directory is not local, it can still be the URL of an - // ioslave using UDS_LOCAL_PATH which to be converted first. - KIO::StatJob* statJob = KIO::mostLocalUrl(u); - KJobWidgets::setWindow(statJob, widget()); - statJob->exec(); - u = statJob->mostLocalUrl(); - - //If the URL is local after the above conversion, set the directory. - if (u.isLocalFile()) { - dir = u.toLocalFile(); - } - - KToolInvocation::invokeTerminal(QString(), dir); + KToolInvocation::invokeTerminal(QString(), KParts::ReadOnlyPart::localFilePath()); } void DolphinPart::slotFindFile() { - KRun::run(QStringLiteral("kfind"), {url()}, widget()); + QMenu searchTools; + KMoreToolsMenuFactory("dolphin/search-tools").fillMenuFromGroupingNames( + &searchTools, { "files-find" }, QUrl::fromLocalFile(KParts::ReadOnlyPart::localFilePath()) + ); + QList<QAction*> actions = searchTools.actions(); + if (!(actions.isEmpty())) { + actions.first()->trigger(); + } else { + KRun::run(QStringLiteral("kfind"), {url()}, widget()); + } } void DolphinPart::updateNewMenu() @@ -562,7 +556,7 @@ void DolphinPart::updateNewMenu() m_newFileMenu->checkUpToDate(); m_newFileMenu->setViewShowsHiddenFiles(m_view->hiddenFilesShown()); // And set the files that the menu apply on : - m_newFileMenu->setPopupFiles(url()); + m_newFileMenu->setPopupFiles(QList<QUrl>() << url()); } void DolphinPart::updateStatusBar() @@ -579,7 +573,7 @@ void DolphinPart::updateProgress(int percent) void DolphinPart::createDirectory() { m_newFileMenu->setViewShowsHiddenFiles(m_view->hiddenFilesShown()); - m_newFileMenu->setPopupFiles(url()); + m_newFileMenu->setPopupFiles(QList<QUrl>() << url()); m_newFileMenu->createDirectory(); } diff --git a/src/dolphinpart.h b/src/dolphinpart.h index 864c08344..fe8f2d14e 100644 --- a/src/dolphinpart.h +++ b/src/dolphinpart.h @@ -194,7 +194,7 @@ private Q_SLOTS: void slotOpenTerminal(); /** - * Open KFind with the current path. + * Open preferred search tool in the current directory to find files. */ void slotFindFile(); diff --git a/src/dolphinpart.rc b/src/dolphinpart.rc index afd3838e3..df152fb20 100644 --- a/src/dolphinpart.rc +++ b/src/dolphinpart.rc @@ -1,5 +1,5 @@ <!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> -<kpartgui name="dolphinpart" version="14" translationDomain="dolphin"> +<kpartgui name="dolphinpart" version="15" translationDomain="dolphin"> <MenuBar> <Menu name="edit"><text>&Edit</text> <Action name="new_menu"/> @@ -39,6 +39,7 @@ </Menu> <Menu name="tools"><text context="@title:menu">Tools</text> <Action name="open_terminal"/> + <Action name="focus_terminal_panel"/> <Action name="find_file" /> <Action name="show_filter_bar" /> <Action name="compare_files" /> diff --git a/src/dolphinui.rc b/src/dolphinui.rc index dcacc56c4..e1bb9ee58 100644 --- a/src/dolphinui.rc +++ b/src/dolphinui.rc @@ -1,5 +1,5 @@ <!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> -<kpartgui name="dolphin" version="27"> +<kpartgui name="dolphin" version="29"> <MenuBar> <Menu name="file"> <Action name="new_menu" /> @@ -54,7 +54,9 @@ </Menu> <Menu name="tools"> <Action name="show_filter_bar" /> + <Action name="open_preferred_search_tool" /> <Action name="open_terminal" /> + <Action name="focus_terminal_panel"/> <Action name="compare_files" /> <Action name="change_remote_encoding" /> </Menu> diff --git a/src/dolphinviewcontainer.cpp b/src/dolphinviewcontainer.cpp index 9ed7654fa..bd3c8ed20 100644 --- a/src/dolphinviewcontainer.cpp +++ b/src/dolphinviewcontainer.cpp @@ -50,6 +50,7 @@ #include <QTimer> #include <QUrl> #include <QVBoxLayout> +#include <QDesktopServices> DolphinViewContainer::DolphinViewContainer(const QUrl& url, QWidget* parent) : QWidget(parent), @@ -691,30 +692,18 @@ void DolphinViewContainer::slotUrlNavigatorLocationChanged(const QUrl& url) 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.remove(0, 1); - } - } } else { showMessage(i18nc("@info:status", - "Protocol not supported by Dolphin, Konqueror has been launched"), + "Protocol not supported by Dolphin, default application has been launched"), Information); } - const QString secureUrl = KShell::quoteArg(url.toDisplayString(QUrl::PreferLocalFile)); - const QString command = app + ' ' + secureUrl; - KRun::runCommand(command, app, app, this); + QDesktopServices::openUrl(url); + redirect(QUrl(), m_urlNavigator->locationUrl(1)); } else { showMessage(i18nc("@info:status", "Invalid protocol"), Error); } diff --git a/src/global.cpp b/src/global.cpp index 3b81c536a..9aff25b26 100644 --- a/src/global.cpp +++ b/src/global.cpp @@ -77,39 +77,39 @@ bool Dolphin::attachToExistingInstance(const QList<QUrl>& inputUrls, bool openFi return false; } - QVector<QPair<QSharedPointer<QDBusInterface>, QStringList>> dolphinServices; + QVector<QPair<QSharedPointer<QDBusInterface>, QStringList>> dolphinInterfaces; if (!preferredService.isEmpty()) { - QSharedPointer<QDBusInterface> preferred( + QSharedPointer<QDBusInterface> preferredInterface( new QDBusInterface(preferredService, QStringLiteral("/dolphin/Dolphin_1"), QString()) // #414402: use empty interface name to prevent QtDBus from caching the interface. ); - if (preferred->isValid() && !preferred->lastError().isValid()) { - dolphinServices.append(qMakePair(preferred, QStringList())); + if (preferredInterface->isValid() && !preferredInterface->lastError().isValid()) { + dolphinInterfaces.append(qMakePair(preferredInterface, QStringList())); } } - // find all dolphin instances - const QStringList services = QDBusConnection::sessionBus().interface()->registeredServiceNames().value(); + // Look for dolphin instances among all available dbus services. + const QStringList dbusServices = QDBusConnection::sessionBus().interface()->registeredServiceNames().value(); // Don't match the service without trailing "-" (unique instance) const QString pattern = QStringLiteral("org.kde.dolphin-"); // Don't match the pid without leading "-" const QString myPid = QLatin1Char('-') + QString::number(QCoreApplication::applicationPid()); - for (const QString& service : services) { + for (const QString& service : dbusServices) { if (service.startsWith(pattern) && !service.endsWith(myPid)) { // Check if instance can handle our URLs - QSharedPointer<QDBusInterface> instance( + QSharedPointer<QDBusInterface> interface( new QDBusInterface(service, QStringLiteral("/dolphin/Dolphin_1"), QStringLiteral("org.kde.dolphin.MainWindow")) ); - if (instance->isValid() && !instance->lastError().isValid()) { - dolphinServices.append(qMakePair(instance, QStringList())); + if (interface->isValid() && !interface->lastError().isValid()) { + dolphinInterfaces.append(qMakePair(interface, QStringList())); } } } - if (dolphinServices.isEmpty()) { + if (dolphinInterfaces.isEmpty()) { return false; } @@ -119,10 +119,10 @@ bool Dolphin::attachToExistingInstance(const QList<QUrl>& inputUrls, bool openFi const auto urls = QUrl::toStringList(inputUrls); for (const QString& url : urls) { bool urlFound = false; - for (auto& service: dolphinServices) { - QDBusReply<bool> isUrlOpen = service.first->call(QStringLiteral("isUrlOpen"), url); - if (isUrlOpen.isValid() && isUrlOpen.value()) { - service.second.append(url); + for (auto& interface: dolphinInterfaces) { + QDBusReply<bool> isUrlOpenReply = interface.first->call(QStringLiteral("isUrlOpen"), url); + if (isUrlOpenReply.isValid() && isUrlOpenReply.value()) { + interface.second.append(url); urlFound = true; break; } @@ -131,12 +131,12 @@ bool Dolphin::attachToExistingInstance(const QList<QUrl>& inputUrls, bool openFi newUrls.append(url); } } - dolphinServices.front().second << newUrls; + dolphinInterfaces.front().second << newUrls; - for (const auto& service: dolphinServices) { - if (!service.second.isEmpty()) { - service.first->call(openFiles ? QStringLiteral("openFiles") : QStringLiteral("openDirectories"), service.second, splitView); - service.first->call(QStringLiteral("activateWindow")); + for (const auto& interface: dolphinInterfaces) { + if (!interface.second.isEmpty()) { + interface.first->call(openFiles ? QStringLiteral("openFiles") : QStringLiteral("openDirectories"), interface.second, splitView); + interface.first->call(QStringLiteral("activateWindow")); } } return true; diff --git a/src/kitemviews/kfileitemmodel.cpp b/src/kitemviews/kfileitemmodel.cpp index 51ffb2140..ad996a59e 100644 --- a/src/kitemviews/kfileitemmodel.cpp +++ b/src/kitemviews/kfileitemmodel.cpp @@ -267,7 +267,7 @@ QMimeData* KFileItemModel::createMimeData(const KItemSet& indexes) const urls << item.url(); bool isLocal; - mostLocalUrls << item.mostLocalUrl(isLocal); + mostLocalUrls << item.mostLocalUrl(&isLocal); } } @@ -2309,38 +2309,38 @@ const KFileItemModel::RoleInfoMap* KFileItemModel::rolesInfoMap(int& count) static const RoleInfoMap rolesInfoMap[] = { // | role | roleType | role translation | group translation | requires Baloo | requires indexer { nullptr, NoRole, nullptr, nullptr, nullptr, nullptr, false, false }, - { "text", NameRole, I18N_NOOP2_NOSTRIP("@label", "Name"), nullptr, nullptr, false, false }, - { "size", SizeRole, I18N_NOOP2_NOSTRIP("@label", "Size"), nullptr, nullptr, false, false }, - { "modificationtime", ModificationTimeRole, I18N_NOOP2_NOSTRIP("@label", "Modified"), nullptr, nullptr, false, false }, - { "creationtime", CreationTimeRole, I18N_NOOP2_NOSTRIP("@label", "Created"), nullptr, nullptr, false, false }, - { "accesstime", AccessTimeRole, I18N_NOOP2_NOSTRIP("@label", "Accessed"), nullptr, nullptr, false, false }, - { "type", TypeRole, I18N_NOOP2_NOSTRIP("@label", "Type"), nullptr, nullptr, false, false }, - { "rating", RatingRole, I18N_NOOP2_NOSTRIP("@label", "Rating"), nullptr, nullptr, true, false }, - { "tags", TagsRole, I18N_NOOP2_NOSTRIP("@label", "Tags"), nullptr, nullptr, true, false }, - { "comment", CommentRole, I18N_NOOP2_NOSTRIP("@label", "Comment"), nullptr, nullptr, true, false }, - { "title", TitleRole, I18N_NOOP2_NOSTRIP("@label", "Title"), I18N_NOOP2_NOSTRIP("@label", "Document"), true, true }, - { "wordCount", WordCountRole, I18N_NOOP2_NOSTRIP("@label", "Word Count"), I18N_NOOP2_NOSTRIP("@label", "Document"), true, true }, - { "lineCount", LineCountRole, I18N_NOOP2_NOSTRIP("@label", "Line Count"), I18N_NOOP2_NOSTRIP("@label", "Document"), true, true }, - { "imageDateTime", ImageDateTimeRole, I18N_NOOP2_NOSTRIP("@label", "Date Photographed"), I18N_NOOP2_NOSTRIP("@label", "Image"), true, true }, - { "width", WidthRole, I18N_NOOP2_NOSTRIP("@label", "Width"), I18N_NOOP2_NOSTRIP("@label", "Image"), true, true }, - { "height", HeightRole, I18N_NOOP2_NOSTRIP("@label", "Height"), I18N_NOOP2_NOSTRIP("@label", "Image"), true, true }, - { "orientation", OrientationRole, I18N_NOOP2_NOSTRIP("@label", "Orientation"), I18N_NOOP2_NOSTRIP("@label", "Image"), true, true }, - { "artist", ArtistRole, I18N_NOOP2_NOSTRIP("@label", "Artist"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true }, - { "genre", GenreRole, I18N_NOOP2_NOSTRIP("@label", "Genre"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true }, - { "album", AlbumRole, I18N_NOOP2_NOSTRIP("@label", "Album"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true }, - { "duration", DurationRole, I18N_NOOP2_NOSTRIP("@label", "Duration"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true }, - { "bitrate", BitrateRole, I18N_NOOP2_NOSTRIP("@label", "Bitrate"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true }, - { "track", TrackRole, I18N_NOOP2_NOSTRIP("@label", "Track"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true }, - { "releaseYear", ReleaseYearRole, I18N_NOOP2_NOSTRIP("@label", "Release Year"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true }, - { "aspectRatio", AspectRatioRole, I18N_NOOP2_NOSTRIP("@label", "Aspect Ratio"), I18N_NOOP2_NOSTRIP("@label", "Video"), true, true }, - { "frameRate", FrameRateRole, I18N_NOOP2_NOSTRIP("@label", "Frame Rate"), I18N_NOOP2_NOSTRIP("@label", "Video"), true, true }, - { "path", PathRole, I18N_NOOP2_NOSTRIP("@label", "Path"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false }, - { "deletiontime", DeletionTimeRole, I18N_NOOP2_NOSTRIP("@label", "Deletion Time"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false }, - { "destination", DestinationRole, I18N_NOOP2_NOSTRIP("@label", "Link Destination"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false }, - { "originUrl", OriginUrlRole, I18N_NOOP2_NOSTRIP("@label", "Downloaded From"), I18N_NOOP2_NOSTRIP("@label", "Other"), true, false }, - { "permissions", PermissionsRole, I18N_NOOP2_NOSTRIP("@label", "Permissions"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false }, - { "owner", OwnerRole, I18N_NOOP2_NOSTRIP("@label", "Owner"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false }, - { "group", GroupRole, I18N_NOOP2_NOSTRIP("@label", "User Group"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false }, + { "text", NameRole, I18NC_NOOP("@label", "Name"), nullptr, nullptr, false, false }, + { "size", SizeRole, I18NC_NOOP("@label", "Size"), nullptr, nullptr, false, false }, + { "modificationtime", ModificationTimeRole, I18NC_NOOP("@label", "Modified"), nullptr, nullptr, false, false }, + { "creationtime", CreationTimeRole, I18NC_NOOP("@label", "Created"), nullptr, nullptr, false, false }, + { "accesstime", AccessTimeRole, I18NC_NOOP("@label", "Accessed"), nullptr, nullptr, false, false }, + { "type", TypeRole, I18NC_NOOP("@label", "Type"), nullptr, nullptr, false, false }, + { "rating", RatingRole, I18NC_NOOP("@label", "Rating"), nullptr, nullptr, true, false }, + { "tags", TagsRole, I18NC_NOOP("@label", "Tags"), nullptr, nullptr, true, false }, + { "comment", CommentRole, I18NC_NOOP("@label", "Comment"), nullptr, nullptr, true, false }, + { "title", TitleRole, I18NC_NOOP("@label", "Title"), I18NC_NOOP("@label", "Document"), true, true }, + { "wordCount", WordCountRole, I18NC_NOOP("@label", "Word Count"), I18NC_NOOP("@label", "Document"), true, true }, + { "lineCount", LineCountRole, I18NC_NOOP("@label", "Line Count"), I18NC_NOOP("@label", "Document"), true, true }, + { "imageDateTime", ImageDateTimeRole, I18NC_NOOP("@label", "Date Photographed"), I18NC_NOOP("@label", "Image"), true, true }, + { "width", WidthRole, I18NC_NOOP("@label", "Width"), I18NC_NOOP("@label", "Image"), true, true }, + { "height", HeightRole, I18NC_NOOP("@label", "Height"), I18NC_NOOP("@label", "Image"), true, true }, + { "orientation", OrientationRole, I18NC_NOOP("@label", "Orientation"), I18NC_NOOP("@label", "Image"), true, true }, + { "artist", ArtistRole, I18NC_NOOP("@label", "Artist"), I18NC_NOOP("@label", "Audio"), true, true }, + { "genre", GenreRole, I18NC_NOOP("@label", "Genre"), I18NC_NOOP("@label", "Audio"), true, true }, + { "album", AlbumRole, I18NC_NOOP("@label", "Album"), I18NC_NOOP("@label", "Audio"), true, true }, + { "duration", DurationRole, I18NC_NOOP("@label", "Duration"), I18NC_NOOP("@label", "Audio"), true, true }, + { "bitrate", BitrateRole, I18NC_NOOP("@label", "Bitrate"), I18NC_NOOP("@label", "Audio"), true, true }, + { "track", TrackRole, I18NC_NOOP("@label", "Track"), I18NC_NOOP("@label", "Audio"), true, true }, + { "releaseYear", ReleaseYearRole, I18NC_NOOP("@label", "Release Year"), I18NC_NOOP("@label", "Audio"), true, true }, + { "aspectRatio", AspectRatioRole, I18NC_NOOP("@label", "Aspect Ratio"), I18NC_NOOP("@label", "Video"), true, true }, + { "frameRate", FrameRateRole, I18NC_NOOP("@label", "Frame Rate"), I18NC_NOOP("@label", "Video"), true, true }, + { "path", PathRole, I18NC_NOOP("@label", "Path"), I18NC_NOOP("@label", "Other"), false, false }, + { "deletiontime", DeletionTimeRole, I18NC_NOOP("@label", "Deletion Time"), I18NC_NOOP("@label", "Other"), false, false }, + { "destination", DestinationRole, I18NC_NOOP("@label", "Link Destination"), I18NC_NOOP("@label", "Other"), false, false }, + { "originUrl", OriginUrlRole, I18NC_NOOP("@label", "Downloaded From"), I18NC_NOOP("@label", "Other"), true, false }, + { "permissions", PermissionsRole, I18NC_NOOP("@label", "Permissions"), I18NC_NOOP("@label", "Other"), false, false }, + { "owner", OwnerRole, I18NC_NOOP("@label", "Owner"), I18NC_NOOP("@label", "Other"), false, false }, + { "group", GroupRole, I18NC_NOOP("@label", "User Group"), I18NC_NOOP("@label", "Other"), false, false }, }; count = sizeof(rolesInfoMap) / sizeof(RoleInfoMap); diff --git a/src/kitemviews/kitemlistcontainer.cpp b/src/kitemviews/kitemlistcontainer.cpp index b274e75e2..6279b15a9 100644 --- a/src/kitemviews/kitemlistcontainer.cpp +++ b/src/kitemviews/kitemlistcontainer.cpp @@ -1,8 +1,7 @@ /*************************************************************************** * Copyright (C) 2011 by Peter Penz <[email protected]> * * * - * Based on the Itemviews NG project from Trolltech Labs: * - * http://qt.gitorious.org/qt-labs/itemviews-ng * + * Based on the Itemviews NG project from Trolltech Labs * * * * 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 * diff --git a/src/kitemviews/kitemlistcontainer.h b/src/kitemviews/kitemlistcontainer.h index c5ef85563..9fcb88969 100644 --- a/src/kitemviews/kitemlistcontainer.h +++ b/src/kitemviews/kitemlistcontainer.h @@ -1,8 +1,7 @@ /*************************************************************************** * Copyright (C) 2011 by Peter Penz <[email protected]> * * * - * Based on the Itemviews NG project from Trolltech Labs: * - * http://qt.gitorious.org/qt-labs/itemviews-ng * + * Based on the Itemviews NG project from Trolltech Labs * * * * 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 * diff --git a/src/kitemviews/kitemlistcontroller.cpp b/src/kitemviews/kitemlistcontroller.cpp index a80869dfc..65c42b547 100644 --- a/src/kitemviews/kitemlistcontroller.cpp +++ b/src/kitemviews/kitemlistcontroller.cpp @@ -2,8 +2,7 @@ * Copyright (C) 2011 by Peter Penz <[email protected]> * * Copyright (C) 2012 by Frank Reininghaus <[email protected]> * * * - * Based on the Itemviews NG project from Trolltech Labs: * - * http://qt.gitorious.org/qt-labs/itemviews-ng * + * Based on the Itemviews NG project from Trolltech Labs * * * * 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 * diff --git a/src/kitemviews/kitemlistcontroller.h b/src/kitemviews/kitemlistcontroller.h index b21e50735..6d9b2ac6a 100644 --- a/src/kitemviews/kitemlistcontroller.h +++ b/src/kitemviews/kitemlistcontroller.h @@ -1,8 +1,7 @@ /*************************************************************************** * Copyright (C) 2011 by Peter Penz <[email protected]> * * * - * Based on the Itemviews NG project from Trolltech Labs: * - * http://qt.gitorious.org/qt-labs/itemviews-ng * + * Based on the Itemviews NG project from Trolltech Labs * * * * 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 * diff --git a/src/kitemviews/kitemlistgroupheader.cpp b/src/kitemviews/kitemlistgroupheader.cpp index 9689180d1..0c495693c 100644 --- a/src/kitemviews/kitemlistgroupheader.cpp +++ b/src/kitemviews/kitemlistgroupheader.cpp @@ -1,8 +1,7 @@ /*************************************************************************** * Copyright (C) 2011 by Peter Penz <[email protected]> * * * - * Based on the Itemviews NG project from Trolltech Labs: * - * http://qt.gitorious.org/qt-labs/itemviews-ng * + * Based on the Itemviews NG project from Trolltech Labs * * * * 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 * @@ -29,7 +28,7 @@ #include <QStyleOptionGraphicsItem> KItemListGroupHeader::KItemListGroupHeader(QGraphicsWidget* parent) : - QGraphicsWidget(parent, nullptr), + QGraphicsWidget(parent), m_dirtyCache(true), m_role(), m_data(), diff --git a/src/kitemviews/kitemlistselectionmanager.cpp b/src/kitemviews/kitemlistselectionmanager.cpp index 1b4f7db45..6bee19414 100644 --- a/src/kitemviews/kitemlistselectionmanager.cpp +++ b/src/kitemviews/kitemlistselectionmanager.cpp @@ -2,8 +2,7 @@ * Copyright (C) 2011 by Peter Penz <[email protected]> * * Copyright (C) 2011 by Frank Reininghaus <[email protected]> * * * - * Based on the Itemviews NG project from Trolltech Labs: * - * http://qt.gitorious.org/qt-labs/itemviews-ng * + * Based on the Itemviews NG project from Trolltech Labs * * * * 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 * diff --git a/src/kitemviews/kitemlistselectionmanager.h b/src/kitemviews/kitemlistselectionmanager.h index 6f5710006..6b0435139 100644 --- a/src/kitemviews/kitemlistselectionmanager.h +++ b/src/kitemviews/kitemlistselectionmanager.h @@ -1,8 +1,7 @@ /*************************************************************************** * Copyright (C) 2011 by Peter Penz <[email protected]> * * * - * Based on the Itemviews NG project from Trolltech Labs: * - * http://qt.gitorious.org/qt-labs/itemviews-ng * + * Based on the Itemviews NG project from Trolltech Labs * * * * 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 * diff --git a/src/kitemviews/kitemliststyleoption.cpp b/src/kitemviews/kitemliststyleoption.cpp index bcfb86064..84054aaac 100644 --- a/src/kitemviews/kitemliststyleoption.cpp +++ b/src/kitemviews/kitemliststyleoption.cpp @@ -35,21 +35,6 @@ KItemListStyleOption::KItemListStyleOption() : { } -KItemListStyleOption::KItemListStyleOption(const KItemListStyleOption& other) : - rect(other.rect), - font(other.font), - fontMetrics(other.fontMetrics), - palette(other.palette), - padding(other.padding), - horizontalMargin(other.horizontalMargin), - verticalMargin(other.verticalMargin), - iconSize(other.iconSize), - extendedSelectionRegion(other.extendedSelectionRegion), - maxTextLines(other.maxTextLines), - maxTextWidth(other.maxTextWidth) -{ -} - KItemListStyleOption::~KItemListStyleOption() { } diff --git a/src/kitemviews/kitemliststyleoption.h b/src/kitemviews/kitemliststyleoption.h index 93aafac1f..381f4bbe5 100644 --- a/src/kitemviews/kitemliststyleoption.h +++ b/src/kitemviews/kitemliststyleoption.h @@ -31,7 +31,6 @@ class DOLPHIN_EXPORT KItemListStyleOption { public: KItemListStyleOption(); - KItemListStyleOption(const KItemListStyleOption& other); virtual ~KItemListStyleOption(); QRect rect; @@ -48,8 +47,6 @@ public: bool operator==(const KItemListStyleOption& other) const; bool operator!=(const KItemListStyleOption& other) const; - - }; #endif diff --git a/src/kitemviews/kitemlistview.cpp b/src/kitemviews/kitemlistview.cpp index 21cb2ae98..f00805242 100644 --- a/src/kitemviews/kitemlistview.cpp +++ b/src/kitemviews/kitemlistview.cpp @@ -1,8 +1,7 @@ /*************************************************************************** * Copyright (C) 2011 by Peter Penz <[email protected]> * * * - * Based on the Itemviews NG project from Trolltech Labs: * - * http://qt.gitorious.org/qt-labs/itemviews-ng * + * Based on the Itemviews NG project from Trolltech Labs * * * * 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 * diff --git a/src/kitemviews/kitemlistview.h b/src/kitemviews/kitemlistview.h index 3dff35a63..2a3b7ada2 100644 --- a/src/kitemviews/kitemlistview.h +++ b/src/kitemviews/kitemlistview.h @@ -1,8 +1,7 @@ /*************************************************************************** * Copyright (C) 2011 by Peter Penz <[email protected]> * * * - * Based on the Itemviews NG project from Trolltech Labs: * - * http://qt.gitorious.org/qt-labs/itemviews-ng * + * Based on the Itemviews NG project from Trolltech Labs * * * * 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 * diff --git a/src/kitemviews/kitemlistwidget.cpp b/src/kitemviews/kitemlistwidget.cpp index c33335ae6..49a13f68f 100644 --- a/src/kitemviews/kitemlistwidget.cpp +++ b/src/kitemviews/kitemlistwidget.cpp @@ -1,8 +1,7 @@ /*************************************************************************** * Copyright (C) 2011 by Peter Penz <[email protected]> * * * - * Based on the Itemviews NG project from Trolltech Labs: * - * http://qt.gitorious.org/qt-labs/itemviews-ng * + * Based on the Itemviews NG project from Trolltech Labs * * * * 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 * @@ -39,7 +38,7 @@ KItemListWidgetInformant::~KItemListWidgetInformant() } KItemListWidget::KItemListWidget(KItemListWidgetInformant* informant, QGraphicsItem* parent) : - QGraphicsWidget(parent, nullptr), + QGraphicsWidget(parent), m_informant(informant), m_index(-1), m_selected(false), diff --git a/src/kitemviews/kitemlistwidget.h b/src/kitemviews/kitemlistwidget.h index 433048aa0..66b2da069 100644 --- a/src/kitemviews/kitemlistwidget.h +++ b/src/kitemviews/kitemlistwidget.h @@ -1,8 +1,7 @@ /*************************************************************************** * Copyright (C) 2011 by Peter Penz <[email protected]> * * * - * Based on the Itemviews NG project from Trolltech Labs: * - * http://qt.gitorious.org/qt-labs/itemviews-ng * + * Based on the Itemviews NG project from Trolltech Labs * * * * 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 * diff --git a/src/kitemviews/kitemmodelbase.cpp b/src/kitemviews/kitemmodelbase.cpp index 9cbcbf3c0..faabdbe1d 100644 --- a/src/kitemviews/kitemmodelbase.cpp +++ b/src/kitemviews/kitemmodelbase.cpp @@ -1,8 +1,7 @@ /*************************************************************************** * Copyright (C) 2011 by Peter Penz <[email protected]> * * * - * Based on the Itemviews NG project from Trolltech Labs: * - * http://qt.gitorious.org/qt-labs/itemviews-ng * + * Based on the Itemviews NG project from Trolltech Labs * * * * 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 * diff --git a/src/kitemviews/kitemmodelbase.h b/src/kitemviews/kitemmodelbase.h index f1945de06..a6ba3f7c4 100644 --- a/src/kitemviews/kitemmodelbase.h +++ b/src/kitemviews/kitemmodelbase.h @@ -1,8 +1,7 @@ /*************************************************************************** * Copyright (C) 2011 by Peter Penz <[email protected]> * * * - * Based on the Itemviews NG project from Trolltech Labs: * - * http://qt.gitorious.org/qt-labs/itemviews-ng * + * Based on the Itemviews NG project from Trolltech Labs * * * * 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 * diff --git a/src/kitemviews/kitemrange.h b/src/kitemviews/kitemrange.h index 6453f2154..f62305a39 100644 --- a/src/kitemviews/kitemrange.h +++ b/src/kitemviews/kitemrange.h @@ -2,8 +2,7 @@ * Copyright (C) 2011 by Peter Penz <[email protected]> * * Copyright (C) 2013 by Frank Reininghaus <[email protected]> * * * - * Based on the Itemviews NG project from Trolltech Labs: * - * http://qt.gitorious.org/qt-labs/itemviews-ng * + * Based on the Itemviews NG project from Trolltech Labs * * * * 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 * diff --git a/src/kitemviews/kstandarditemlistgroupheader.cpp b/src/kitemviews/kstandarditemlistgroupheader.cpp index 015362e3f..062b20e90 100644 --- a/src/kitemviews/kstandarditemlistgroupheader.cpp +++ b/src/kitemviews/kstandarditemlistgroupheader.cpp @@ -1,8 +1,7 @@ /*************************************************************************** * Copyright (C) 2011 by Peter Penz <[email protected]> * * * - * Based on the Itemviews NG project from Trolltech Labs: * - * http://qt.gitorious.org/qt-labs/itemviews-ng * + * Based on the Itemviews NG project from Trolltech Labs * * * * 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 * diff --git a/src/kitemviews/private/kbaloorolesprovider.cpp b/src/kitemviews/private/kbaloorolesprovider.cpp index 8a2a64b31..0f7100faa 100644 --- a/src/kitemviews/private/kbaloorolesprovider.cpp +++ b/src/kitemviews/private/kbaloorolesprovider.cpp @@ -81,7 +81,12 @@ QHash<QByteArray, QVariant> KBalooRolesProvider::roleValues(const Baloo::File& f std::for_each(rangeBegin, rangeEnd, [&list](const entry& s) { list.append(s.second); }); values.insert(role, propertyInfo.formatAsDisplayString(list)); } else { - values.insert(role, propertyInfo.formatAsDisplayString((*rangeBegin).second)); + if (propertyInfo.valueType() == QVariant::DateTime) { + // Let dolphin format later Dates + values.insert(role, (*rangeBegin).second); + } else { + values.insert(role, propertyInfo.formatAsDisplayString((*rangeBegin).second)); + } } rangeBegin = rangeEnd; } diff --git a/src/kitemviews/private/kdirectorycontentscounter.cpp b/src/kitemviews/private/kdirectorycontentscounter.cpp index 977a83d5d..bd204fe8e 100644 --- a/src/kitemviews/private/kdirectorycontentscounter.cpp +++ b/src/kitemviews/private/kdirectorycontentscounter.cpp @@ -23,6 +23,7 @@ #include <KDirWatch> +#include <QFileInfo> #include <QThread> KDirectoryContentsCounter::KDirectoryContentsCounter(KFileItemModel* model, QObject* parent) : @@ -85,9 +86,11 @@ void KDirectoryContentsCounter::addDirectory(const QString& path) int KDirectoryContentsCounter::countDirectoryContentsSynchronously(const QString& path) { - if (!m_dirWatcher->contains(path)) { - m_dirWatcher->addDir(path); - m_watchedDirs.insert(path); + const QString resolvedPath = QFileInfo(path).canonicalFilePath(); + + if (!m_dirWatcher->contains(resolvedPath)) { + m_dirWatcher->addDir(resolvedPath); + m_watchedDirs.insert(resolvedPath); } KDirectoryContentsCounterWorker::Options options; @@ -107,9 +110,11 @@ void KDirectoryContentsCounter::slotResult(const QString& path, int count) { m_workerIsBusy = false; - if (!m_dirWatcher->contains(path)) { - m_dirWatcher->addDir(path); - m_watchedDirs.insert(path); + const QString resolvedPath = QFileInfo(path).canonicalFilePath(); + + if (!m_dirWatcher->contains(resolvedPath)) { + m_dirWatcher->addDir(resolvedPath); + m_watchedDirs.insert(resolvedPath); } if (!m_queue.isEmpty()) { diff --git a/src/kitemviews/private/kfileitemclipboard.cpp b/src/kitemviews/private/kfileitemclipboard.cpp index 2bc96a0fa..d6cc87e1d 100644 --- a/src/kitemviews/private/kfileitemclipboard.cpp +++ b/src/kitemviews/private/kfileitemclipboard.cpp @@ -46,7 +46,7 @@ bool KFileItemClipboard::isCut(const QUrl& url) const QList<QUrl> KFileItemClipboard::cutItems() const { - return m_cutItems.toList(); + return m_cutItems.values(); } KFileItemClipboard::~KFileItemClipboard() diff --git a/src/kitemviews/private/kitemlistkeyboardsearchmanager.cpp b/src/kitemviews/private/kitemlistkeyboardsearchmanager.cpp index 5256f69ca..1fa6f7a7b 100644 --- a/src/kitemviews/private/kitemlistkeyboardsearchmanager.cpp +++ b/src/kitemviews/private/kitemlistkeyboardsearchmanager.cpp @@ -1,8 +1,7 @@ /*************************************************************************** * Copyright (C) 2011 by Tirtha Chatterjee <[email protected]> * * * - * Based on the Itemviews NG project from Trolltech Labs: * - * http://qt.gitorious.org/qt-labs/itemviews-ng * + * Based on the Itemviews NG project from Trolltech Labs * * * * 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 * diff --git a/src/kitemviews/private/kitemlistkeyboardsearchmanager.h b/src/kitemviews/private/kitemlistkeyboardsearchmanager.h index 9995c16b0..d0161f927 100644 --- a/src/kitemviews/private/kitemlistkeyboardsearchmanager.h +++ b/src/kitemviews/private/kitemlistkeyboardsearchmanager.h @@ -1,8 +1,7 @@ /*************************************************************************** * Copyright (C) 2011 by Tirtha Chatterjee <[email protected]> * * * - * Based on the Itemviews NG project from Trolltech Labs: * - * http://qt.gitorious.org/qt-labs/itemviews-ng * + * Based on the Itemviews NG project from Trolltech Labs * * * * 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 * @@ -76,7 +75,7 @@ signals: * current item. */ // TODO: Think about getting rid of the bool parameter - // (see http://doc.qt.nokia.com/qq/qq13-apis.html#thebooleanparametertrap) + // (see https://doc.qt.io/archives/qq/qq13-apis.html#thebooleanparametertrap) void changeCurrentItem(const QString& string, bool searchFromNextItem); private: diff --git a/src/kitemviews/private/kitemlistselectiontoggle.cpp b/src/kitemviews/private/kitemlistselectiontoggle.cpp index 6bdd18da5..48c6854aa 100644 --- a/src/kitemviews/private/kitemlistselectiontoggle.cpp +++ b/src/kitemviews/private/kitemlistselectiontoggle.cpp @@ -25,7 +25,7 @@ #include <QPainter> KItemListSelectionToggle::KItemListSelectionToggle(QGraphicsItem* parent) : - QGraphicsWidget(parent, nullptr), + QGraphicsWidget(parent), m_checked(false), m_hovered(false) { diff --git a/src/org.kde.dolphin.appdata.xml b/src/org.kde.dolphin.appdata.xml index 6884ffb6f..faa90b29c 100644 --- a/src/org.kde.dolphin.appdata.xml +++ b/src/org.kde.dolphin.appdata.xml @@ -26,6 +26,7 @@ <name xml:lang="it">Dolphin</name> <name xml:lang="ko">Dolphin</name> <name xml:lang="lt">Dolphin</name> + <name xml:lang="ml">ഡോൾഫിൻ</name> <name xml:lang="nb">Dolphin</name> <name xml:lang="nds">Dolphin</name> <name xml:lang="nl">Dolphin</name> @@ -72,6 +73,7 @@ <summary xml:lang="it">Gestore file</summary> <summary xml:lang="ko">파일 관리자</summary> <summary xml:lang="lt">Failų tvarkytuvė</summary> + <summary xml:lang="ml">ഫയല് കാര്യസ്ഥന്</summary> <summary xml:lang="nb">Filbehandler</summary> <summary xml:lang="nds">Dateipleger</summary> <summary xml:lang="nl">Bestandsbeheerder</summary> @@ -117,6 +119,7 @@ <p xml:lang="id">Dolphin adalah pengelola file yang ringan. Ini telah dirancang dengan kemudahan penggunaan dan kesederhanaan dalam hal, sementara masih memungkinkan fleksibilitas dan kustomisasi. Ini berarti kamu bisa melakukan pengelolaan file-mu persis seperti yang kamu inginkan.</p> <p xml:lang="it">Dolphin è un gestore file leggero. È stato progettato per essere facile da utilizzare e pensando alla semplicità, garantendo al contempo flessibilità e personalizzazione. Ciò significa che puoi gestire i tuoi file come meglio desideri.</p> <p xml:lang="ko">Dolphin은 가벼운 파일 관리자입니다. 사용 편의성과 단순함을 염두에 두고 설계되었으며, 유연성과 사용자 지정을 여전히 허용합니다. 즉, 파일 관리를 원하는 방식으로 정확하게 수행할 수 있습니다.</p> + <p xml:lang="ml">ഡോൾഫിൻ ഒരു ഭാരം കുറഞ്ഞ ഫയൽ മാനേജറാണ്. ഇത് നിർമ്മിച്ചിരിക്കുന്നത് ഉപയോഗിക്കുന്നതിനുള്ള എളുപ്പത്തിനും , മാനസികമായിയുള്ള എളുപ്പത്തിനുമാണ്, ഒപ്പം തന്നെ അഭിരുചിക്കനുസൃതമാക്കുന്നതിനും വഴക്കം നൽകുന്നതിനും അനുവദിക്കുന്നു. അതായത് നിങ്ങൾക്ക് നിങ്ങളുടെ ഫയലുകൾ എങ്ങനെയൊക്കെ കൈകാര്യം ചെയ്യണോ അങ്ങനെ കൈകാര്യം ചെയ്യാവുന്നതാണ്.</p> <p xml:lang="nb">Dolphin er en lettvekts filbehandler. Den er laget for å være enkel og lett å bruke, samtidig som den er fleksibel og kan tilpasses. Det betyr at du kan utføre dine filbehandlingsoppgaver akkurat slik du vil gjøre det.</p> <p xml:lang="nds">Dolphin is en slank Dateipleger. Dat wöör buut mit de Idee vun't eenfache Bedenen vör Ogen, bides dat liekers flexibel un topassbor wesen schull. Du kannst Dien Dateien also jüst so plegen, as Du dat wullt.</p> <p xml:lang="nl">Dolphin is een lichtgewicht bestandsbeheerder. Het is ontworpen met gebruiksgemak en eenvoud in gedachte en staat toch flexibiliteit en aan te passen toe. Dit betekent dat u uw bestandsbeheer kunt doen precies op de manier zoals u dat wilt.</p> @@ -161,6 +164,7 @@ <p xml:lang="it">Funzionalità:</p> <p xml:lang="ko">기능:</p> <p xml:lang="lt">Galimybės</p> + <p xml:lang="ml">വിശേഷതകൾ:</p> <p xml:lang="nb">Egenskaper:</p> <p xml:lang="nds">Markmalen:</p> <p xml:lang="nl">Mogelijkheden:</p> @@ -203,6 +207,7 @@ <li xml:lang="id">Bilah navigasi (atau breadcrumb) untuk URL-URL, memungkinkan kamu secara cepat bernavigasi melalui hirerarki file-file dan folder-folder.</li> <li xml:lang="it">La barra di navigazione per gli URL, che ti consente di navigare rapidamente attraverso la struttura di file e cartelle.</li> <li xml:lang="ko">URL에 대한 탐색(또는 이동 경로) 표시줄을 사용하면 파일 및 폴더의 계층 구조를 빠르게 탐색할 수 있습니다.</li> + <li xml:lang="ml">ഫയലുകളുടെയും അറകളുടെയും ശ്രേണി കണ്ടുപിടിക്കുന്നതിന് URL കൾക്ക് നാവിഗേഷൻ (അല്ലെങ്കിൽ വഴി കാട്ടുന്നതിനുള്ള) സ്ഥലം.</li> <li xml:lang="nb">Navigasjonslinje (brødsmulelinje) for URL-er slik at du raskt kan navigere gjennom hierarkiet av filer og mapper.</li> <li xml:lang="nds">Steed- (oder Krömelspoor-)Balken för URLs, mit de Du Di fix dör de Hierarchie ut Dateien un Ornern bewegen kannst</li> <li xml:lang="nl">Navigatie- (of broodkruimel)balk voor URL's, waarmee u snel kunt navigeren door de hiërarchie van bestanden en mappen.</li> @@ -244,6 +249,7 @@ <li xml:lang="id">Mendukung beberapa jenis gaya tampilan dan properti yang berbeda dan memungkinkanmu untuk mengonfigurasi tampilan persis seperti yang kamu inginkan.</li> <li xml:lang="it">Supporta diversi stili di visualizzazione e proprietà e ti consente di configurare la vista come desideri.</li> <li xml:lang="ko">여러 종류의 보기 스타일 및 속성을 지원하며 원하는 방식으로 보기를 구성할 수 있습니다.</li> + <li xml:lang="ml">നിങ്ങൾക്ക് ഇഷ്ടമുള്ള രീതിയിൽ കാണുന്നതിന് ആവശ്യമായിട്ടുള്ള വിവിധ അലങ്കാര രീതികള്ക്ക് പിന്തുണ നൽകുന്നു.</li> <li xml:lang="nb">Støtter flere forskjellige visningsstiler og kan sette opp visningen akkurat slik du vil ha den.</li> <li xml:lang="nds">Ünnerstütt en Reeg verscheden Ansichtstilen un -egenschappen un lett Di de Ansicht jüst so topassen, as Du dat bruukst.</li> <li xml:lang="nl">Ondersteunt een aantal verschillende soorten stijlen van weergave en eigenschappen en biedt u de mogelijkheid de weergave in te stellen precies zoals u dat wilt.</li> @@ -285,6 +291,7 @@ <li xml:lang="id">Tampilan belah, memungkinkanmu untuk menyalin atau memindah file antar lokasi dengan mudah.</li> <li xml:lang="it">La vista divisa, che ti consente di copiare o spostare i file tra le diverse posizioni in maniera semplice.</li> <li xml:lang="ko">분할 보기를 통해 위치간에 파일을 쉽게 복사하거나 이동할 수 있습니다.</li> + <li xml:lang="ml">എളുപ്പത്തിൽ ഫയലുകൾ പകർത്തുന്നതിനും നീക്കുന്നതിനും സഹായിക്കുന്ന വേർതിരിച്ച കാഴ്ച</li> <li xml:lang="nb">Delt visning, så du lett kan kopiere eller flytte filer mellom steder.</li> <li xml:lang="nds">Deelt Ansicht, mit De Du Dateien eenfach twischen Steden koperen oder bewegen kannst.</li> <li xml:lang="nl">Gesplitst beeld, waarmee u gemakkelijk bestanden kunt kopiëren of verplaatsen tussen locaties.</li> @@ -326,6 +333,7 @@ <li xml:lang="id">Informasi tambahan dan pintasan tersedia sebagai panel yang bisa di-dock, memungkinkanmu untuk memindahkannya secara bebas dan menampilkan apa yang kamu inginkan.</li> <li xml:lang="it">Informazioni aggiuntive e scorciatoie sono disponibili come pannelli agganciabili, che possono essere spostati liberamente e visualizzare esattamente ciò che desideri.</li> <li xml:lang="ko">추가 정보 및 바로 가기는 도킹 가능한 패널로 제공되므로 자유롭게 이동할 수 있고 원하는 대로 정확히 표시할 수 있습니다.</li> + <li xml:lang="ml">നിങ്ങൾക്ക് ആവശ്യമായിട്ടുള്ള കുറുക്കുവഴികളും വിവരങ്ങളും എളുപ്പത്തിൽ തുറക്കാവുന്ന ഭാഗങ്ങളായി ലഭ്യമാണ്.</li> <li xml:lang="nb">Mer informasjon og snarveier er tilgjengelige som dokkbare ruter, som du kan flytte fritt rundt og bruke til å vise akkurat hva du vil.</li> <li xml:lang="nds">Bito-Infos un Leestekens laat sik as Paneels andocken, Du kannst ehr verschuven un se jüst dat wiesen laten, wat Du weten wullt.</li> <li xml:lang="nl">Extra informatie en sneltoetsen zijn beschikbaar als vast te zetten panelen, die u vrij kunt verplaatsen en precies kunt tonen wat u wilt.</li> @@ -370,6 +378,7 @@ <li xml:lang="it">Supporto di schede multiple</li> <li xml:lang="ko">다중 탭 지원</li> <li xml:lang="lt">Daugelio kortelių palaikymas</li> + <li xml:lang="ml">അനേക ടാബുകളുടെ പിൻതുണ</li> <li xml:lang="nb">Støtte for flere faner</li> <li xml:lang="nds">Ünnerstütten för Paneels</li> <li xml:lang="nl">Ondersteuning voor meerdere tabbladen</li> @@ -413,6 +422,7 @@ <li xml:lang="id">Dialog informasi ditampilkan dengan cara yang tidak mengganggu.</li> <li xml:lang="it">Le finestre informative sono visualizzate in modo molto discreto.</li> <li xml:lang="ko">정보 대화 상자는 눈에 거슬리지 않는 방식으로 표시됩니다.</li> + <li xml:lang="ml">വിവരങ്ങൾ അച്ചടക്കമുള്ള രീതിയിൽ ദൃശ്യമാകുന്നു.</li> <li xml:lang="nb">Informasjonsdialoger vises på en lite påtrengende måte.</li> <li xml:lang="nds">Informatschoondialogen kaamt Di nich in'n Weg.</li> <li xml:lang="nl">Informatiedialogen worden op een prettige manier getoond.</li> @@ -456,6 +466,7 @@ <li xml:lang="id">Dukungan urungkan/lanjurkan</li> <li xml:lang="it">Supporto di annulla/rifai</li> <li xml:lang="ko">실행 취소/다시 실행 지원</li> + <li xml:lang="ml">നിഷ്ക്രിയമാക്കുക/വീണ്ടും ചെയ്യുക പിന്തുണയ്ക്കുന്നു</li> <li xml:lang="nb">Støtte for angring/omgjøring</li> <li xml:lang="nds">Ünnerstütten för Torüchnehmen un Wedderherstellen</li> <li xml:lang="nl">Ondersteuning ongedaan maken/opnieuw</li> @@ -500,6 +511,7 @@ <li xml:lang="id">Akses jaringan transparan melalui sistem KIO.</li> <li xml:lang="it">Accesso trasparente alla rete tramite il sistema KIO.</li> <li xml:lang="ko">KIO 시스템을 통한 투명한 네트워크 액세스.</li> + <li xml:lang="ml">KIO സിസ്റ്റത്തിലൂടെ ശൃംഖലയെ സമീപിക്കുന്നു.</li> <li xml:lang="nb">Gjennomsiktig nettverkstilgang via KIO-systemet.</li> <li xml:lang="nds">Direkt Nettwarktogriep över dat KDE-In-/Utgaav-(KIO-)Moduulsysteem</li> <li xml:lang="nl">Transparante toegang tot het netwerk via het KIO systeem.</li> @@ -547,6 +559,7 @@ <caption xml:lang="id">Pengelolaan file di Dolphin</caption> <caption xml:lang="it">Gestione dei file in Dolphin</caption> <caption xml:lang="ko">Dolphin의 파일 관리</caption> + <caption xml:lang="ml">ഡോൾഫിനിലെ ഫയൽ കൈകാര്യം ചെയ്യൽ</caption> <caption xml:lang="nl">Bestandsbeheer in Dolphin</caption> <caption xml:lang="nn">Filhandsaming i Dolphin</caption> <caption xml:lang="pl">Zarządzanie plikami w Dolphinie</caption> diff --git a/src/panels/folders/folderspanel.cpp b/src/panels/folders/folderspanel.cpp index 01f338461..2e7e4dc28 100644 --- a/src/panels/folders/folderspanel.cpp +++ b/src/panels/folders/folderspanel.cpp @@ -30,13 +30,13 @@ #include "kitemviews/kitemlistselectionmanager.h" #include "treeviewcontextmenu.h" #include "views/draganddrophelper.h" -#include "views/renamedialog.h" #include <KJobWidgets> #include <KJobUiDelegate> #include <KIO/CopyJob> #include <KIO/DropJob> #include <KIO/FileUndoManager> +#include <KIO/RenameFileDialog> #include <QApplication> #include <QBoxLayout> @@ -104,7 +104,7 @@ void FoldersPanel::rename(const KFileItem& item) const int index = m_model->index(item); m_controller->view()->editRole(index, "text"); } else { - RenameDialog* dialog = new RenameDialog(this, KFileItemList() << item); + KIO::RenameFileDialog* dialog = new KIO::RenameFileDialog(KFileItemList({item}), this); dialog->open(); } } diff --git a/src/panels/folders/treeviewcontextmenu.cpp b/src/panels/folders/treeviewcontextmenu.cpp index df9b9d62e..6c4adf883 100644 --- a/src/panels/folders/treeviewcontextmenu.cpp +++ b/src/panels/folders/treeviewcontextmenu.cpp @@ -173,7 +173,7 @@ void TreeViewContextMenu::populateMimeData(QMimeData* mimeData, bool cut) kdeUrls.append(m_fileItem.url()); QList<QUrl> mostLocalUrls; bool dummy; - mostLocalUrls.append(m_fileItem.mostLocalUrl(dummy)); + mostLocalUrls.append(m_fileItem.mostLocalUrl(&dummy)); KIO::setClipboardDataCut(mimeData, cut); KUrlMimeData::setUrls(kdeUrls, mostLocalUrls, mimeData); } diff --git a/src/panels/information/informationpanel.cpp b/src/panels/information/informationpanel.cpp index e6c3bf32a..23e7f1922 100644 --- a/src/panels/information/informationpanel.cpp +++ b/src/panels/information/informationpanel.cpp @@ -290,8 +290,8 @@ void InformationPanel::reset() void InformationPanel::slotFileRenamed(const QString& source, const QString& dest) { - if (m_shownUrl == QUrl::fromLocalFile(source)) { - m_shownUrl = QUrl::fromLocalFile(dest); + if (m_shownUrl == QUrl::fromUserInput(source)) { + m_shownUrl = QUrl::fromUserInput(dest); m_fileItem = KFileItem(m_shownUrl); if ((m_selection.count() == 1) && (m_selection[0].url() == QUrl::fromLocalFile(source))) { @@ -308,10 +308,10 @@ void InformationPanel::slotFileRenamed(const QString& source, const QString& des void InformationPanel::slotFilesAdded(const QString& directory) { - if (m_shownUrl == QUrl::fromLocalFile(directory)) { + if (m_shownUrl == QUrl::fromUserInput(directory)) { // If the 'trash' icon changes because the trash has been emptied or got filled, // the signal filesAdded("trash:/") will be emitted. - KFileItem item(QUrl::fromLocalFile(directory)); + KFileItem item(QUrl::fromUserInput(directory)); requestDelayedItemInfo(item); } } @@ -319,7 +319,7 @@ void InformationPanel::slotFilesAdded(const QString& directory) void InformationPanel::slotFilesChanged(const QStringList& files) { for (const QString& fileName : files) { - if (m_shownUrl == QUrl::fromLocalFile(fileName)) { + if (m_shownUrl == QUrl::fromUserInput(fileName)) { showItemInfo(); break; } @@ -329,7 +329,7 @@ void InformationPanel::slotFilesChanged(const QStringList& files) void InformationPanel::slotFilesRemoved(const QStringList& files) { for (const QString& fileName : files) { - if (m_shownUrl == QUrl::fromLocalFile(fileName)) { + if (m_shownUrl == QUrl::fromUserInput(fileName)) { // the currently shown item has been removed, show // the parent directory as fallback markUrlAsInvalid(); @@ -340,15 +340,15 @@ void InformationPanel::slotFilesRemoved(const QStringList& files) void InformationPanel::slotEnteredDirectory(const QString& directory) { - if (m_shownUrl == QUrl::fromLocalFile(directory)) { - KFileItem item(QUrl::fromLocalFile(directory)); + if (m_shownUrl == QUrl::fromUserInput(directory)) { + KFileItem item(QUrl::fromUserInput(directory)); requestDelayedItemInfo(item); } } void InformationPanel::slotLeftDirectory(const QString& directory) { - if (m_shownUrl == QUrl::fromLocalFile(directory)) { + if (m_shownUrl == QUrl::fromUserInput(directory)) { // The signal 'leftDirectory' is also emitted when a media // has been unmounted. In this case no directory change will be // done in Dolphin, but the Information Panel must be updated to diff --git a/src/panels/information/informationpanelcontent.cpp b/src/panels/information/informationpanelcontent.cpp index 417ca709c..5c1b7ae22 100644 --- a/src/panels/information/informationpanelcontent.cpp +++ b/src/panels/information/informationpanelcontent.cpp @@ -166,12 +166,13 @@ InformationPanelContent::~InformationPanelContent() void InformationPanelContent::showItem(const KFileItem& item) { - if (item != m_item) { + // compares item entries, comparing items only compares urls + if (m_item.entry() != item.entry()) { m_item = item; - m_preview->stopAnimatedImage(); refreshMetaData(); } + refreshPreview(); } diff --git a/src/panels/information/phononwidget.cpp b/src/panels/information/phononwidget.cpp index 6911f79bd..f815b4bd0 100644 --- a/src/panels/information/phononwidget.cpp +++ b/src/panels/information/phononwidget.cpp @@ -20,7 +20,6 @@ #include "phononwidget.h" -#include <KIconLoader> #include <KLocalizedString> #include <Phonon/AudioOutput> #include <Phonon/MediaObject> @@ -28,6 +27,7 @@ #include <Phonon/VideoWidget> #include <QShowEvent> +#include <QStyle> #include <QToolButton> #include <QVBoxLayout> @@ -161,7 +161,7 @@ void PhononWidget::showEvent(QShowEvent *event) m_topLayout->addLayout(controlsLayout); - const int smallIconSize = IconSize(KIconLoader::Small); + const int smallIconSize = style()->pixelMetric(QStyle::PM_SmallIconSize); const QSize buttonSize(smallIconSize, smallIconSize); m_playButton->setToolTip(i18n("play")); diff --git a/src/panels/places/placesitemlistgroupheader.cpp b/src/panels/places/placesitemlistgroupheader.cpp index 884859d5b..dbd055f36 100644 --- a/src/panels/places/placesitemlistgroupheader.cpp +++ b/src/panels/places/placesitemlistgroupheader.cpp @@ -1,8 +1,7 @@ /*************************************************************************** * Copyright (C) 2012 by Peter Penz <[email protected]> * * * - * Based on the Itemviews NG project from Trolltech Labs: * - * http://qt.gitorious.org/qt-labs/itemviews-ng * + * Based on the Itemviews NG project from Trolltech Labs * * * * 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 * diff --git a/src/panels/places/placespanel.cpp b/src/panels/places/placespanel.cpp index 8469399a7..34361a5a7 100644 --- a/src/panels/places/placespanel.cpp +++ b/src/panels/places/placespanel.cpp @@ -309,10 +309,10 @@ void PlacesPanel::slotViewContextMenuRequested(const QPointF& pos) const int iconSizeCount = 4; static const IconSizeInfo iconSizes[iconSizeCount] = { - {KIconLoader::SizeSmall, I18N_NOOP2_NOSTRIP("Small icon size", "Small (%1x%2)")}, - {KIconLoader::SizeSmallMedium, I18N_NOOP2_NOSTRIP("Medium icon size", "Medium (%1x%2)")}, - {KIconLoader::SizeMedium, I18N_NOOP2_NOSTRIP("Large icon size", "Large (%1x%2)")}, - {KIconLoader::SizeLarge, I18N_NOOP2_NOSTRIP("Huge icon size", "Huge (%1x%2)")} + {KIconLoader::SizeSmall, I18NC_NOOP("Small icon size", "Small (%1x%2)")}, + {KIconLoader::SizeSmallMedium, I18NC_NOOP("Medium icon size", "Medium (%1x%2)")}, + {KIconLoader::SizeMedium, I18NC_NOOP("Large icon size", "Large (%1x%2)")}, + {KIconLoader::SizeLarge, I18NC_NOOP("Huge icon size", "Huge (%1x%2)")} }; QHash<QAction*, int> iconSizeActionMap; diff --git a/src/panels/terminal/terminalpanel.cpp b/src/panels/terminal/terminalpanel.cpp index 86974d200..861afebee 100644 --- a/src/panels/terminal/terminalpanel.cpp +++ b/src/panels/terminal/terminalpanel.cpp @@ -147,6 +147,7 @@ void TerminalPanel::showEvent(QShowEvent* event) if (m_konsolePart) { connect(m_konsolePart, &KParts::ReadOnlyPart::destroyed, this, &TerminalPanel::terminalExited); m_terminalWidget = m_konsolePart->widget(); + setFocusProxy(m_terminalWidget); m_layout->addWidget(m_terminalWidget); if (m_konsolePartMissingMessage) { m_layout->removeWidget(m_konsolePartMissingMessage); @@ -263,3 +264,8 @@ void TerminalPanel::slotKonsolePartCurrentDirectoryChanged(const QString& dir) const QUrl url(QUrl::fromLocalFile(dir)); emit changeUrl(url); } + +bool TerminalPanel::terminalHasFocus() const +{ + return m_terminalWidget->hasFocus(); +} diff --git a/src/panels/terminal/terminalpanel.h b/src/panels/terminal/terminalpanel.h index f5d66e548..6ab205fe3 100644 --- a/src/panels/terminal/terminalpanel.h +++ b/src/panels/terminal/terminalpanel.h @@ -56,6 +56,7 @@ public: void goHome(); QString currentWorkingDirectory(); bool isHiddenInVisibleWindow() const; + bool terminalHasFocus() const; bool hasProgramRunning() const; QString runningProgramName() const; diff --git a/src/search/dolphinfacetswidget.cpp b/src/search/dolphinfacetswidget.cpp index ae05509e7..d9943abcd 100644 --- a/src/search/dolphinfacetswidget.cpp +++ b/src/search/dolphinfacetswidget.cpp @@ -27,12 +27,15 @@ #include <QEvent> #include <QHBoxLayout> #include <QIcon> +#include <QMenu> +#include <QToolButton> DolphinFacetsWidget::DolphinFacetsWidget(QWidget* parent) : QWidget(parent), m_typeSelector(nullptr), m_dateSelector(nullptr), - m_ratingSelector(nullptr) + m_ratingSelector(nullptr), + m_tagsSelector(nullptr) { m_typeSelector = new QComboBox(this); m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("none")), i18nc("@item:inlistbox", "Any Type"), QString()); @@ -63,13 +66,27 @@ DolphinFacetsWidget::DolphinFacetsWidget(QWidget* parent) : m_ratingSelector->addItem(QIcon::fromTheme(QStringLiteral("starred-symbolic")), i18nc("@item:inlistbox", "Highest Rating"), 5); initComboBox(m_ratingSelector); + m_tagsSelector = new QToolButton(this); + m_tagsSelector->setIcon(QIcon::fromTheme(QStringLiteral("tag"))); + m_tagsSelector->setMenu(new QMenu(this)); + m_tagsSelector->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + m_tagsSelector->setPopupMode(QToolButton::MenuButtonPopup); + m_tagsSelector->setAutoRaise(true); + updateTagsSelector(); + + connect(m_tagsSelector, &QToolButton::clicked, m_tagsSelector, &QToolButton::showMenu); + connect(m_tagsSelector->menu(), &QMenu::aboutToShow, this, &DolphinFacetsWidget::updateTagsMenu); + connect(&m_tagsLister, &KCoreDirLister::itemsAdded, this, &DolphinFacetsWidget::updateTagsMenuItems); + updateTagsMenu(); + QHBoxLayout* topLayout = new QHBoxLayout(this); topLayout->setContentsMargins(0, 0, 0, 0); topLayout->addWidget(m_typeSelector); topLayout->addWidget(m_dateSelector); topLayout->addWidget(m_ratingSelector); + topLayout->addWidget(m_tagsSelector); - resetOptions(); + resetSearchTerms(); } DolphinFacetsWidget::~DolphinFacetsWidget() @@ -78,19 +95,27 @@ DolphinFacetsWidget::~DolphinFacetsWidget() void DolphinFacetsWidget::changeEvent(QEvent *event) { - if (event->type() == QEvent::EnabledChange && !isEnabled()) { - resetOptions(); + if (event->type() == QEvent::EnabledChange) { + if (isEnabled()) { + updateTagsSelector(); + } else { + resetSearchTerms(); + } } } -void DolphinFacetsWidget::resetOptions() +void DolphinFacetsWidget::resetSearchTerms() { m_typeSelector->setCurrentIndex(0); m_dateSelector->setCurrentIndex(0); m_ratingSelector->setCurrentIndex(0); + + m_searchTags = QStringList(); + updateTagsSelector(); + updateTagsMenu(); } -QString DolphinFacetsWidget::ratingTerm() const +QStringList DolphinFacetsWidget::searchTerms() const { QStringList terms; @@ -104,7 +129,17 @@ QString DolphinFacetsWidget::ratingTerm() const terms << QStringLiteral("modified>=%1").arg(date.toString(Qt::ISODate)); } - return terms.join(QLatin1String(" AND ")); + if (!m_searchTags.isEmpty()) { + for (auto const &tag : m_searchTags) { + if (tag.contains(QLatin1Char(' '))) { + terms << QStringLiteral("tag:\"%1\"").arg(tag); + } else { + terms << QStringLiteral("tag:%1").arg(tag); + } + } + } + + return terms; } QString DolphinFacetsWidget::facetType() const @@ -112,42 +147,36 @@ QString DolphinFacetsWidget::facetType() const return m_typeSelector->currentData().toString(); } -bool DolphinFacetsWidget::isRatingTerm(const QString& term) const +bool DolphinFacetsWidget::isSearchTerm(const QString& term) const { - const QStringList subTerms = term.split(' ', QString::SkipEmptyParts); - - // If term has sub terms, then sone of the sub terms are always "rating" and "modified" terms. - bool containsRating = false; - bool containsModified = false; + static const QLatin1String searchTokens[] { + QLatin1String("modified>="), + QLatin1String("rating>="), + QLatin1String("tag:"), QLatin1String("tag=") + }; - foreach (const QString& subTerm, subTerms) { - if (subTerm.startsWith(QLatin1String("rating>="))) { - containsRating = true; - } else if (subTerm.startsWith(QLatin1String("modified>="))) { - containsModified = true; + for (const auto &searchToken : searchTokens) { + if (term.startsWith(searchToken)) { + return true; } } - - return containsModified || containsRating; + return false; } -void DolphinFacetsWidget::setRatingTerm(const QString& term) +void DolphinFacetsWidget::setSearchTerm(const QString& term) { - // If term has sub terms, then the sub terms are always "rating" and "modified" terms. - // If term has no sub terms, then the term itself is either a "rating" term or a "modified" - // term. To avoid code duplication we add term to subTerms list, if the list is empty. - QStringList subTerms = term.split(' ', QString::SkipEmptyParts); - - foreach (const QString& subTerm, subTerms) { - if (subTerm.startsWith(QLatin1String("modified>="))) { - const QString value = subTerm.mid(10); - const QDate date = QDate::fromString(value, Qt::ISODate); - setTimespan(date); - } else if (subTerm.startsWith(QLatin1String("rating>="))) { - const QString value = subTerm.mid(8); - const int stars = value.toInt() / 2; - setRating(stars); - } + if (term.startsWith(QLatin1String("modified>="))) { + const QString value = term.mid(10); + const QDate date = QDate::fromString(value, Qt::ISODate); + setTimespan(date); + } else if (term.startsWith(QLatin1String("rating>="))) { + const QString value = term.mid(8); + const int stars = value.toInt() / 2; + setRating(stars); + } else if (term.startsWith(QLatin1String("tag:")) || + term.startsWith(QLatin1String("tag="))) { + const QString value = term.mid(4); + addSearchTag(value); } } @@ -183,6 +212,25 @@ void DolphinFacetsWidget::setTimespan(const QDate& date) } } +void DolphinFacetsWidget::addSearchTag(const QString& tag) +{ + if (tag.isEmpty() || m_searchTags.contains(tag)) { + return; + } + m_searchTags.append(tag); + m_searchTags.sort(); + updateTagsSelector(); +} + +void DolphinFacetsWidget::removeSearchTag(const QString& tag) +{ + if (tag.isEmpty() || !m_searchTags.contains(tag)) { + return; + } + m_searchTags.removeAll(tag); + updateTagsSelector(); +} + void DolphinFacetsWidget::initComboBox(QComboBox* combo) { combo->setFrame(false); @@ -191,3 +239,55 @@ void DolphinFacetsWidget::initComboBox(QComboBox* combo) connect(combo, QOverload<int>::of(&QComboBox::activated), this, &DolphinFacetsWidget::facetChanged); } +void DolphinFacetsWidget::updateTagsSelector() +{ + const bool hasListedTags = !m_tagsSelector->menu()->isEmpty(); + const bool hasSelectedTags = !m_searchTags.isEmpty(); + + if (hasSelectedTags) { + const QString tagsText = m_searchTags.join(i18nc("String list separator", ", ")); + m_tagsSelector->setText(i18ncp("@action:button %2 is a list of tags", + "Tag: %2", "Tags: %2",m_searchTags.count(), tagsText)); + } else { + m_tagsSelector->setText(i18nc("@action:button", "Add Tags")); + } + + m_tagsSelector->setEnabled(isEnabled() && (hasListedTags || hasSelectedTags)); +} + +void DolphinFacetsWidget::updateTagsMenu() +{ + updateTagsMenuItems({}, {}); + m_tagsLister.openUrl(QUrl(QStringLiteral("tags:/")), KCoreDirLister::OpenUrlFlag::Reload); +} + +void DolphinFacetsWidget::updateTagsMenuItems(const QUrl&, const KFileItemList& items) +{ + m_tagsSelector->menu()->clear(); + + QStringList allTags = QStringList(m_searchTags); + for (const KFileItem &item: items) { + allTags.append(item.name()); + } + allTags.sort(Qt::CaseInsensitive); + allTags.removeDuplicates(); + + for (const QString& tagName : qAsConst(allTags)) { + QAction* action = m_tagsSelector->menu()->addAction(QIcon::fromTheme(QStringLiteral("tag")), tagName); + action->setCheckable(true); + action->setChecked(m_searchTags.contains(tagName)); + + connect(action, &QAction::triggered, this, [this, tagName](bool isChecked) { + if (isChecked) { + addSearchTag(tagName); + } else { + removeSearchTag(tagName); + } + emit facetChanged(); + + m_tagsSelector->menu()->show(); + }); + } + + updateTagsSelector(); +} diff --git a/src/search/dolphinfacetswidget.h b/src/search/dolphinfacetswidget.h index 0a8a5161f..2e91bcc96 100644 --- a/src/search/dolphinfacetswidget.h +++ b/src/search/dolphinfacetswidget.h @@ -21,10 +21,12 @@ #define DOLPHINFACETSWIDGET_H #include <QWidget> +#include <KCoreDirLister> class QComboBox; class QDate; class QEvent; +class QToolButton; /** * @brief Allows to filter search-queries by facets. @@ -50,13 +52,12 @@ public: explicit DolphinFacetsWidget(QWidget* parent = nullptr); ~DolphinFacetsWidget() override; - void resetOptions(); - - QString ratingTerm() const; + QStringList searchTerms() const; QString facetType() const; - bool isRatingTerm(const QString& term) const; - void setRatingTerm(const QString& term); + bool isSearchTerm(const QString& term) const; + void setSearchTerm(const QString& term); + void resetSearchTerms(); void setFacetType(const QString& type); @@ -66,15 +67,27 @@ signals: protected: void changeEvent(QEvent* event) override; +private slots: + void updateTagsMenu(); + void updateTagsMenuItems(const QUrl&, const KFileItemList& items); + private: void setRating(const int stars); void setTimespan(const QDate& date); + void addSearchTag(const QString& tag); + void removeSearchTag(const QString& tag); + void initComboBox(QComboBox* combo); + void updateTagsSelector(); private: QComboBox* m_typeSelector; QComboBox* m_dateSelector; QComboBox* m_ratingSelector; + QToolButton* m_tagsSelector; + + QStringList m_searchTags; + KCoreDirLister m_tagsLister; }; #endif diff --git a/src/search/dolphinquery.cpp b/src/search/dolphinquery.cpp index 8f8cb09ec..ab107f43f 100644 --- a/src/search/dolphinquery.cpp +++ b/src/search/dolphinquery.cpp @@ -19,28 +19,53 @@ #include "dolphinquery.h" +#include <QRegularExpression> + #include <config-baloo.h> #ifdef HAVE_BALOO #include <Baloo/Query> #endif namespace { - /** Checks if a given term in the Baloo::Query::searchString() is a special search term. - * This is a copy of `DolphinFacetsWidget::isRatingTerm()` method. + /** Checks if a given term in the Baloo::Query::searchString() is a special search term + * @return: the specific search token of the term, or an empty QString() if none is found */ - bool isSearchTerm(const QString& term) + QString searchTermToken(const QString& term) { static const QLatin1String searchTokens[] { + QLatin1String("filename:"), QLatin1String("modified>="), - QLatin1String("rating>=") + QLatin1String("rating>="), + QLatin1String("tag:"), QLatin1String("tag=") }; for (const auto &searchToken : searchTokens) { if (term.startsWith(searchToken)) { - return true; + return searchToken; } } - return false; + return QString(); + } + + QString stripQuotes(const QString& text) + { + if (text.length() >= 2 && text.at(0) == QLatin1Char('"') + && text.back() == QLatin1Char('"')) { + return text.mid(1, text.size() - 2); + } + return text; + } + + QStringList splitOutsideQuotes(const QString& text) + { + const QRegularExpression subTermsRegExp("(\\S*?\"[^\"]*?\"|(?<=\\s|^)\\S+(?=\\s|$))"); + auto subTermsMatchIterator = subTermsRegExp.globalMatch(text); + + QStringList textParts; + while (subTermsMatchIterator.hasNext()) { + textParts << subTermsMatchIterator.next().captured(0); + } + return textParts; } } @@ -58,29 +83,35 @@ DolphinQuery DolphinQuery::fromBalooSearchUrl(const QUrl& searchUrl) model.m_fileType = types.isEmpty() ? QString() : types.first(); QStringList textParts; + QString fileName; - const QStringList subTerms = query.searchString().split(' ', QString::SkipEmptyParts); + const QStringList subTerms = splitOutsideQuotes(query.searchString()); foreach (const QString& subTerm, subTerms) { - QString value; - if (subTerm.startsWith(QLatin1String("filename:"))) { - value = subTerm.mid(9); - } else if (isSearchTerm(subTerm)) { - model.m_searchTerms << subTerm; + const QString token = searchTermToken(subTerm); + const QString value = stripQuotes(subTerm.mid(token.length())); + + if (token == QLatin1String("filename:")) { + if (!value.isEmpty()) { + fileName = value; + model.m_hasFileName = true; + } + continue; + } else if (!token.isEmpty()) { + model.m_searchTerms << token + value; continue; } else if (subTerm == QLatin1String("AND") && subTerm != subTerms.at(0) && subTerm != subTerms.back()) { continue; - } else { - value = subTerm; + } else if (!value.isEmpty()) { + textParts << value; + model.m_hasContentSearch = true; } + } - if (!value.isEmpty() && value.at(0) == QLatin1Char('"')) { - value = value.mid(1); - } - if (!value.isEmpty() && value.back() == QLatin1Char('"')) { - value = value.mid(0, value.size() - 1); - } - if (!value.isEmpty()) { - textParts << value; + if (model.m_hasFileName) { + if (model.m_hasContentSearch) { + textParts << QStringLiteral("filename:\"%1\"").arg(fileName); + } else { + textParts << fileName; } } @@ -114,3 +145,13 @@ QString DolphinQuery::includeFolder() const { return m_includeFolder; } + +bool DolphinQuery::hasContentSearch() const +{ + return m_hasContentSearch; +} + +bool DolphinQuery::hasFileName() const +{ + return m_hasFileName; +} diff --git a/src/search/dolphinquery.h b/src/search/dolphinquery.h index e60008e3b..544f246bc 100644 --- a/src/search/dolphinquery.h +++ b/src/search/dolphinquery.h @@ -48,6 +48,10 @@ public: /** @return Baloo::Query::includeFolder(), that is, the initial directory * for the query or an empty string if its a global search" */ QString includeFolder() const; + /** @return whether the query includes search in file content */ + bool hasContentSearch() const; + /** @return whether the query includes a filter by fileName */ + bool hasFileName() const; private: QUrl m_searchUrl; @@ -55,6 +59,8 @@ private: QString m_fileType; QStringList m_searchTerms; QString m_includeFolder; + bool m_hasContentSearch = false; + bool m_hasFileName = false; }; #endif //DOLPHINQUERY_H diff --git a/src/search/dolphinsearchbox.cpp b/src/search/dolphinsearchbox.cpp index 16f17bbcd..23f520de1 100644 --- a/src/search/dolphinsearchbox.cpp +++ b/src/search/dolphinsearchbox.cpp @@ -477,11 +477,7 @@ QUrl DolphinSearchBox::balooUrlForSearching() const Baloo::Query query; query.addType(m_facetsWidget->facetType()); - QStringList queryStrings; - QString ratingQuery = m_facetsWidget->ratingTerm(); - if (!ratingQuery.isEmpty()) { - queryStrings << ratingQuery; - } + QStringList queryStrings = m_facetsWidget->searchTerms(); if (m_contentButton->isChecked()) { queryStrings << text; @@ -517,11 +513,17 @@ void DolphinSearchBox::updateFromQuery(const DolphinQuery& query) setText(query.text()); - m_facetsWidget->resetOptions(); + 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->setRatingTerm(searchTerm); + m_facetsWidget->setSearchTerm(searchTerm); } m_startSearchTimer->stop(); diff --git a/src/settings/dolphin_generalsettings.kcfg b/src/settings/dolphin_generalsettings.kcfg index 5a7bb1a01..fca70656d 100644 --- a/src/settings/dolphin_generalsettings.kcfg +++ b/src/settings/dolphin_generalsettings.kcfg @@ -16,7 +16,7 @@ </entry> <entry name="UrlCompletionMode" type="Enum"> <label>Text completion mode of the URL Navigator</label> - <default>KCompletion::CompletionPopup</default> + <default>KCompletion::CompletionPopupAuto</default> </entry> <entry name="ShowFullPath" type="Bool"> <label>Should the full path be shown inside the location bar</label> diff --git a/src/settings/general/behaviorsettingspage.cpp b/src/settings/general/behaviorsettingspage.cpp index df7ea2113..30883c5da 100644 --- a/src/settings/general/behaviorsettingspage.cpp +++ b/src/settings/general/behaviorsettingspage.cpp @@ -48,8 +48,8 @@ BehaviorSettingsPage::BehaviorSettingsPage(const QUrl& url, QWidget* parent) : // View properties - m_globalViewProps = new QRadioButton(i18nc("@option:radio", "Use common properties for all folders")); - m_localViewProps = new QRadioButton(i18nc("@option:radio", "Remember properties for each folder")); + m_globalViewProps = new QRadioButton(i18nc("@option:radio", "Use common display style for all folders")); + m_localViewProps = new QRadioButton(i18nc("@option:radio", "Remember display style for each folder")); m_localViewProps->setToolTip(i18nc("@info", "Dolphin will create a hidden .directory file in each folder you change view properties for.")); QButtonGroup* viewGroup = new QButtonGroup(this); diff --git a/src/settings/general/configurepreviewplugindialog.cpp b/src/settings/general/configurepreviewplugindialog.cpp index 60e814c13..bc3cca706 100644 --- a/src/settings/general/configurepreviewplugindialog.cpp +++ b/src/settings/general/configurepreviewplugindialog.cpp @@ -66,7 +66,7 @@ ConfigurePreviewPluginDialog::ConfigurePreviewPluginDialog(const QString& plugin // delete the whole thumbnails directory. previewPlugin->writeConfiguration(configurationWidget); - // http://specifications.freedesktop.org/thumbnail-spec/thumbnail-spec-latest.html#DIRECTORY + // https://specifications.freedesktop.org/thumbnail-spec/thumbnail-spec-latest.html#DIRECTORY const QString thumbnailsPath = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QLatin1String("/thumbnails/"); KIO::del(QUrl::fromLocalFile(thumbnailsPath), KIO::HideProgressInfo); }); diff --git a/src/settings/general/generalsettingspage.cpp b/src/settings/general/generalsettingspage.cpp index daa74aff1..6e0a1d474 100644 --- a/src/settings/general/generalsettingspage.cpp +++ b/src/settings/general/generalsettingspage.cpp @@ -64,7 +64,7 @@ GeneralSettingsPage::GeneralSettingsPage(const QUrl& url, QWidget* parent) : m_pages.append(confirmationsPage); m_pages.append(statusBarPage); - topLayout->addWidget(tabWidget, 0, nullptr); + topLayout->addWidget(tabWidget, 0, {}); } GeneralSettingsPage::~GeneralSettingsPage() diff --git a/src/settings/kcm/kcmdolphingeneral.cpp b/src/settings/kcm/kcmdolphingeneral.cpp index ccc32a8a6..a82cb3858 100644 --- a/src/settings/kcm/kcmdolphingeneral.cpp +++ b/src/settings/kcm/kcmdolphingeneral.cpp @@ -26,6 +26,7 @@ #include <KLocalizedString> #include <KPluginFactory> #include <KPluginLoader> +#include <kconfigwidgets_version.h> #include <QTabWidget> #include <QVBoxLayout> @@ -48,23 +49,34 @@ DolphinGeneralConfigModule::DolphinGeneralConfigModule(QWidget* parent, const QV // initialize 'Behavior' tab BehaviorSettingsPage* behaviorPage = new BehaviorSettingsPage(QUrl::fromLocalFile(QDir::homePath()), tabWidget); tabWidget->addTab(behaviorPage, i18nc("@title:tab Behavior settings", "Behavior")); +#if KCONFIGWIDGETS_VERSION < QT_VERSION_CHECK(5, 64, 0) connect(behaviorPage, &BehaviorSettingsPage::changed, this, QOverload<>::of(&DolphinGeneralConfigModule::changed)); +#else + connect(behaviorPage, &BehaviorSettingsPage::changed, this, &DolphinGeneralConfigModule::markAsChanged); +#endif // initialize 'Previews' tab PreviewsSettingsPage* previewsPage = new PreviewsSettingsPage(tabWidget); tabWidget->addTab(previewsPage, i18nc("@title:tab Previews settings", "Previews")); +#if KCONFIGWIDGETS_VERSION < QT_VERSION_CHECK(5, 64, 0) connect(previewsPage, &PreviewsSettingsPage::changed, this, QOverload<>::of(&DolphinGeneralConfigModule::changed)); +#else + connect(previewsPage, &PreviewsSettingsPage::changed, this, &DolphinGeneralConfigModule::markAsChanged); +#endif // initialize 'Confirmations' tab ConfirmationsSettingsPage* confirmationsPage = new ConfirmationsSettingsPage(tabWidget); tabWidget->addTab(confirmationsPage, i18nc("@title:tab Confirmations settings", "Confirmations")); +#if KCONFIGWIDGETS_VERSION < QT_VERSION_CHECK(5, 64, 0) connect(confirmationsPage, &ConfirmationsSettingsPage::changed, this, QOverload<>::of(&DolphinGeneralConfigModule::changed)); - +#else + connect(confirmationsPage, &ConfirmationsSettingsPage::changed, this, &DolphinGeneralConfigModule::markAsChanged); +#endif m_pages.append(behaviorPage); m_pages.append(previewsPage); m_pages.append(confirmationsPage); - topLayout->addWidget(tabWidget, 0, nullptr); + topLayout->addWidget(tabWidget, 0, {}); } DolphinGeneralConfigModule::~DolphinGeneralConfigModule() diff --git a/src/settings/kcm/kcmdolphinnavigation.cpp b/src/settings/kcm/kcmdolphinnavigation.cpp index 6b88dadac..2cdabdeee 100644 --- a/src/settings/kcm/kcmdolphinnavigation.cpp +++ b/src/settings/kcm/kcmdolphinnavigation.cpp @@ -20,6 +20,7 @@ #include "kcmdolphinnavigation.h" #include "settings/navigation/navigationsettingspage.h" +#include <kconfigwidgets_version.h> #include <KPluginFactory> #include <KPluginLoader> @@ -40,8 +41,12 @@ DolphinNavigationConfigModule::DolphinNavigationConfigModule(QWidget* parent, co topLayout->setContentsMargins(0, 0, 0, 0); m_navigation = new NavigationSettingsPage(this); +#if KCONFIGWIDGETS_VERSION < QT_VERSION_CHECK(5, 64, 0) connect(m_navigation, &NavigationSettingsPage::changed, this, QOverload<>::of(&DolphinNavigationConfigModule::changed)); - topLayout->addWidget(m_navigation, 0, nullptr); +#else + connect(m_navigation, &NavigationSettingsPage::changed, this, &DolphinNavigationConfigModule::markAsChanged); +#endif + topLayout->addWidget(m_navigation, 0, {}); } DolphinNavigationConfigModule::~DolphinNavigationConfigModule() diff --git a/src/settings/kcm/kcmdolphinservices.cpp b/src/settings/kcm/kcmdolphinservices.cpp index d5457f1fd..e6a8867d7 100644 --- a/src/settings/kcm/kcmdolphinservices.cpp +++ b/src/settings/kcm/kcmdolphinservices.cpp @@ -21,6 +21,7 @@ #include "settings/services/servicessettingspage.h" +#include <kconfigwidgets_version.h> #include <KPluginFactory> #include <KPluginLoader> @@ -40,8 +41,12 @@ DolphinServicesConfigModule::DolphinServicesConfigModule(QWidget* parent, const topLayout->setContentsMargins(0, 0, 0, 0); m_services = new ServicesSettingsPage(this); +#if KCONFIGWIDGETS_VERSION < QT_VERSION_CHECK(5, 64, 0) connect(m_services, &ServicesSettingsPage::changed, this, QOverload<>::of(&DolphinServicesConfigModule::changed)); - topLayout->addWidget(m_services, 0, nullptr); +#else + connect(m_services, &ServicesSettingsPage::changed, this, &DolphinServicesConfigModule::markAsChanged); +#endif + topLayout->addWidget(m_services, 0, {}); } DolphinServicesConfigModule::~DolphinServicesConfigModule() diff --git a/src/settings/kcm/kcmdolphinviewmodes.cpp b/src/settings/kcm/kcmdolphinviewmodes.cpp index 12a2056c1..4fac11600 100644 --- a/src/settings/kcm/kcmdolphinviewmodes.cpp +++ b/src/settings/kcm/kcmdolphinviewmodes.cpp @@ -65,7 +65,7 @@ DolphinViewModesConfigModule::DolphinViewModesConfigModule(QWidget* parent, cons m_tabs.append(compactTab); m_tabs.append(detailsTab); - topLayout->addWidget(tabWidget, 0, nullptr); + topLayout->addWidget(tabWidget, 0, {}); } DolphinViewModesConfigModule::~DolphinViewModesConfigModule() diff --git a/src/settings/kcm/kcmdolphinviewmodes.desktop b/src/settings/kcm/kcmdolphinviewmodes.desktop index 996e84109..76e561d9d 100644 --- a/src/settings/kcm/kcmdolphinviewmodes.desktop +++ b/src/settings/kcm/kcmdolphinviewmodes.desktop @@ -61,7 +61,7 @@ Comment[fi]=Tällä palvelulla voi muokata Dolphinin katselutilojen asetuksia. Comment[fr]=Ce service permet de configurer les modes d'affichage de Dolphin. Comment[gl]=Este servizo permite configurar os modos de vista de Dolphin. Comment[hu]=Ez a szolgáltatás lehetővé teszi a Dolphin nézetmódjainak beállítását. -Comment[ia]=Iste servicio permitte configuration de le modos de vista de Dolphin +Comment[ia]=Iste servicio permitte configuration del modos de vista de Dolphin Comment[id]=Layanan ini memungkinkan konfigurasi mode tampilan Dolphin. Comment[it]=Questo servizio permette di configurare le viste di Dolphin. Comment[ja]=Dolphin の表示モードを設定します diff --git a/src/settings/viewmodes/viewsettingspage.cpp b/src/settings/viewmodes/viewsettingspage.cpp index be2226db6..d2e291a3b 100644 --- a/src/settings/viewmodes/viewsettingspage.cpp +++ b/src/settings/viewmodes/viewsettingspage.cpp @@ -56,7 +56,7 @@ ViewSettingsPage::ViewSettingsPage(QWidget* parent) : m_tabs.append(compactTab); m_tabs.append(detailsTab); - topLayout->addWidget(tabWidget, 0, nullptr); + topLayout->addWidget(tabWidget, 0); } ViewSettingsPage::~ViewSettingsPage() diff --git a/src/settings/viewpropertiesdialog.cpp b/src/settings/viewpropertiesdialog.cpp index a1f9718fe..c3078d5df 100644 --- a/src/settings/viewpropertiesdialog.cpp +++ b/src/settings/viewpropertiesdialog.cpp @@ -68,7 +68,7 @@ ViewPropertiesDialog::ViewPropertiesDialog(DolphinView* dolphinView) : Q_ASSERT(dolphinView); const bool useGlobalViewProps = GeneralSettings::globalViewProps(); - setWindowTitle(i18nc("@title:window", "View Properties")); + setWindowTitle(i18nc("@title:window", "View Display Style")); const QUrl& url = dolphinView->url(); m_viewProps = new ViewProperties(url); diff --git a/src/tests/dolphinquerytest.cpp b/src/tests/dolphinquerytest.cpp index 1c6b39e26..b65ee037f 100644 --- a/src/tests/dolphinquerytest.cpp +++ b/src/tests/dolphinquerytest.cpp @@ -41,38 +41,80 @@ private slots: */ void DolphinSearchBoxTest::testBalooSearchParsing_data() { - const QString text = QStringLiteral("xyz"); - const QString filename = QStringLiteral("filename:\"xyz\""); + const QString text = QStringLiteral("abc"); + const QString textS = QStringLiteral("abc xyz"); + const QString filename = QStringLiteral("filename:\"%1\"").arg(text); + const QString filenameS = QStringLiteral("filename:\"%1\"").arg(textS); + const QString rating = QStringLiteral("rating>=2"); - const QString modified = QString("modified>=2019-08-07"); + const QString modified = QStringLiteral("modified>=2019-08-07"); + + const QString tag = QStringLiteral("tag:tagA"); + const QString tagS = QStringLiteral("tag:\"tagB with spaces\""); // in search url + const QString tagR = QStringLiteral("tag:tagB with spaces"); // in result term QTest::addColumn<QString>("searchString"); QTest::addColumn<QString>("expectedText"); QTest::addColumn<QStringList>("expectedTerms"); + QTest::addColumn<bool>("hasContent"); + QTest::addColumn<bool>("hasFileName"); // Test for "Content" - QTest::newRow("content") << text << text << QStringList(); - QTest::newRow("content/empty") << "" << "" << QStringList(); - QTest::newRow("content/singleQuote") << "\"" << "" << QStringList(); - QTest::newRow("content/doubleQuote") << "\"\"" << "" << QStringList(); - // Test for empty `filename` - QTest::newRow("filename") << filename << text << QStringList(); - QTest::newRow("filename/empty") << "filename:" << "" << QStringList(); - QTest::newRow("filename/singleQuote") << "filename:\"" << "" << QStringList(); - QTest::newRow("filename/doubleQuote") << "filename:\"\"" << "" << QStringList(); + QTest::newRow("content") << text << text << QStringList() << true << false; + QTest::newRow("content/space") << textS << textS << QStringList() << true << false; + QTest::newRow("content/empty") << "" << "" << QStringList() << false << false; + QTest::newRow("content/single_quote") << "\"" << "\"" << QStringList() << true << false; + QTest::newRow("content/double_quote") << "\"\"" << "" << QStringList() << false << false; + + // Test for "FileName" + QTest::newRow("filename") << filename << text << QStringList() << false << true; + QTest::newRow("filename/space") << filenameS << textS << QStringList() << false << true; + QTest::newRow("filename/empty") << "filename:" << "" << QStringList() << false << false; + QTest::newRow("filename/single_quote") << "filename:\"" << "\"" << QStringList() << false << true; + QTest::newRow("filename/double_quote") << "filename:\"\"" << "" << QStringList() << false << false; + + // Combined content and filename search + QTest::newRow("content+filename") + << text + " " + filename + << text + " " + filename << QStringList() << true << true; // Test for rating - QTest::newRow("rating") << rating << "" << QStringList({rating}); - QTest::newRow("rating+content") << rating + " " + text << text << QStringList({rating}); - QTest::newRow("rating+filename") << rating + " " + filename << text << QStringList({rating}); + QTest::newRow("rating") << rating << "" << QStringList({rating}) << false << false; + QTest::newRow("rating+content") << rating + " " + text << text << QStringList({rating}) << true << false; + QTest::newRow("rating+filename") << rating + " " + filename << text << QStringList({rating}) << false << true; + // Test for modified date - QTest::newRow("modified") << modified << "" << QStringList({modified}); - QTest::newRow("modified+content") << modified + " " + text << text << QStringList({modified}); - QTest::newRow("modified+filename") << modified + " " + filename << text << QStringList({modified}); - // Combined tests - QTest::newRow("rating+modified") << rating + " AND " + modified << "" << QStringList({modified, rating}); - QTest::newRow("rating+modified+content") << rating + " AND " + modified + " " + text << text << QStringList({modified, rating}); - QTest::newRow("rating+modified+filename") << rating + " AND " + modified + " " + filename << text << QStringList({modified, rating}); + QTest::newRow("modified") << modified << "" << QStringList({modified}) << false << false; + QTest::newRow("modified+content") << modified + " " + text << text << QStringList({modified}) << true << false; + QTest::newRow("modified+filename") << modified + " " + filename << text << QStringList({modified}) << false << true; + + // Test for tags + QTest::newRow("tag") << tag << "" << QStringList({tag}) << false << false; + QTest::newRow("tag/space" ) << tagS << "" << QStringList({tagR}) << false << false; + QTest::newRow("tag/double") << tag + " " + tagS << "" << QStringList({tag, tagR}) << false << false; + QTest::newRow("tag+content") << tag + " " + text << text << QStringList({tag}) << true << false; + QTest::newRow("tag+filename") << tag + " " + filename << text << QStringList({tag}) << false << true; + + // Combined search terms + QTest::newRow("searchTerms") + << rating + " AND " + modified + " AND " + tag + " AND " + tagS + << "" << QStringList({modified, rating, tag, tagR}) << false << false; + + QTest::newRow("searchTerms+content") + << rating + " AND " + modified + " " + text + " " + tag + " AND " + tagS + << text << QStringList({modified, rating, tag, tagR}) << true << false; + + QTest::newRow("searchTerms+filename") + << rating + " AND " + modified + " " + filename + " " + tag + " AND " + tagS + << text << QStringList({modified, rating, tag, tagR}) << false << true; + + QTest::newRow("allTerms") + << text + " " + filename + " " + rating + " AND " + modified + " AND " + tag + << text + " " + filename << QStringList({modified, rating, tag}) << true << true; + + QTest::newRow("allTerms/space") + << textS + " " + filenameS + " " + rating + " AND " + modified + " AND " + tagS + << textS + " " + filenameS << QStringList({modified, rating, tagR}) << true << true; } /** @@ -107,6 +149,8 @@ void DolphinSearchBoxTest::testBalooSearchParsing() QFETCH(QString, searchString); QFETCH(QString, expectedText); QFETCH(QStringList, expectedTerms); + QFETCH(bool, hasContent); + QFETCH(bool, hasFileName); const QUrl testUrl = composeQueryUrl(searchString); const DolphinQuery query = DolphinQuery::fromBalooSearchUrl(testUrl); @@ -122,6 +166,10 @@ void DolphinSearchBoxTest::testBalooSearchParsing() for (int i = 0; i < expectedTerms.count(); i++) { QCOMPARE(searchTerms.at(i), expectedTerms.at(i)); } + + // Check for filename and content detection + QCOMPARE(query.hasContentSearch(), hasContent); + QCOMPARE(query.hasFileName(), hasFileName); } QTEST_MAIN(DolphinSearchBoxTest) diff --git a/src/views/dolphinview.cpp b/src/views/dolphinview.cpp index 3437db7a7..cfece0fe6 100644 --- a/src/views/dolphinview.cpp +++ b/src/views/dolphinview.cpp @@ -31,7 +31,6 @@ #include "kitemviews/kitemlistcontroller.h" #include "kitemviews/kitemlistheader.h" #include "kitemviews/kitemlistselectionmanager.h" -#include "renamedialog.h" #include "versioncontrol/versioncontrolobserver.h" #include "viewproperties.h" #include "views/tooltips/tooltipmanager.h" @@ -52,6 +51,7 @@ #include <KIO/Paste> #include <KIO/PasteJob> #include <KIO/PreviewJob> +#include <KIO/RenameFileDialog> #include <KJobWidgets> #include <KLocalizedString> #include <KMessageBox> @@ -637,8 +637,9 @@ void DolphinView::renameSelectedItems() connect(m_view, &DolphinItemListView::roleEditingFinished, this, &DolphinView::slotRoleEditingFinished); } else { - RenameDialog* dialog = new RenameDialog(this, items); - connect(dialog, &RenameDialog::renamingFinished, this, &DolphinView::slotRenameDialogRenamingFinished); + KIO::RenameFileDialog* dialog = new KIO::RenameFileDialog(items, this); + connect(dialog, &KIO::RenameFileDialog::renamingFinished, + this, &DolphinView::slotRenameDialogRenamingFinished); dialog->open(); } diff --git a/src/views/dolphinviewactionhandler.cpp b/src/views/dolphinviewactionhandler.cpp index 0d9f9f81c..ef9f317ee 100644 --- a/src/views/dolphinviewactionhandler.cpp +++ b/src/views/dolphinviewactionhandler.cpp @@ -280,7 +280,7 @@ void DolphinViewActionHandler::createActions() connect(showHiddenFiles, &KToggleAction::triggered, this, &DolphinViewActionHandler::toggleShowHiddenFiles); QAction* adjustViewProps = m_actionCollection->addAction(QStringLiteral("view_properties")); - adjustViewProps->setText(i18nc("@action:inmenu View", "Adjust View Properties...")); + adjustViewProps->setText(i18nc("@action:inmenu View", "Adjust View Display Style...")); adjustViewProps->setIcon(QIcon::fromTheme(QStringLiteral("view-choose"))); adjustViewProps->setWhatsThis(i18nc("@info:whatsthis", "This opens a window " "in which all folder view properties can be adjusted.")); diff --git a/src/views/renamedialog.cpp b/src/views/renamedialog.cpp deleted file mode 100644 index 96068564d..000000000 --- a/src/views/renamedialog.cpp +++ /dev/null @@ -1,225 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2006-2010 by Peter Penz ([email protected]) * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - ***************************************************************************/ - -#include "renamedialog.h" - -#include <KGuiItem> -#include <KIO/BatchRenameJob> -#include <KIO/CopyJob> -#include <KIO/FileUndoManager> -#include <KJobUiDelegate> -#include <KJobWidgets> -#include <KLocalizedString> - -#include <QDialogButtonBox> -#include <QHBoxLayout> -#include <QLabel> -#include <QLineEdit> -#include <QMimeDatabase> -#include <QPushButton> -#include <QSpinBox> - -RenameDialog::RenameDialog(QWidget *parent, const KFileItemList& items) : - QDialog(parent), - m_renameOneItem(false), - m_newName(), - m_lineEdit(nullptr), - m_items(items), - m_allExtensionsDifferent(true), - m_spinBox(nullptr) -{ - const QSize minSize = minimumSize(); - setMinimumSize(QSize(320, minSize.height())); - - const int itemCount = items.count(); - Q_ASSERT(itemCount >= 1); - m_renameOneItem = (itemCount == 1); - - setWindowTitle(m_renameOneItem ? - i18nc("@title:window", "Rename Item") : - i18nc("@title:window", "Rename Items")); - QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); - QVBoxLayout *mainLayout = new QVBoxLayout; - setLayout(mainLayout); - m_okButton = buttonBox->button(QDialogButtonBox::Ok); - m_okButton->setDefault(true); - m_okButton->setShortcut(Qt::CTRL + Qt::Key_Return); - connect(buttonBox, &QDialogButtonBox::accepted, this, &RenameDialog::slotAccepted); - connect(buttonBox, &QDialogButtonBox::rejected, this, &RenameDialog::reject); - connect(buttonBox, &QDialogButtonBox::rejected, this, &QObject::deleteLater); - m_okButton->setDefault(true); - - KGuiItem::assign(m_okButton, KGuiItem(i18nc("@action:button", "&Rename"), QStringLiteral("dialog-ok-apply"))); - - QWidget* page = new QWidget(this); - mainLayout->addWidget(page); - mainLayout->addWidget(buttonBox); - - QVBoxLayout* topLayout = new QVBoxLayout(page); - - QLabel* editLabel = nullptr; - if (m_renameOneItem) { - m_newName = items.first().name(); - editLabel = new QLabel(xi18nc("@label:textbox", "Rename the item <filename>%1</filename> to:", m_newName), - page); - editLabel->setTextFormat(Qt::PlainText); - } else { - m_newName = i18nc("@info:status", "New name #"); - editLabel = new QLabel(i18ncp("@label:textbox", - "Rename the %1 selected item to:", - "Rename the %1 selected items to:", itemCount), - page); - } - - m_lineEdit = new QLineEdit(page); - mainLayout->addWidget(m_lineEdit); - connect(m_lineEdit, &QLineEdit::textChanged, this, &RenameDialog::slotTextChanged); - - int selectionLength = m_newName.length(); - if (m_renameOneItem) { - const QString fileName = items.first().url().toDisplayString(); - QMimeDatabase db; - const QString extension = db.suffixForFileName(fileName.toLower()); - - // If the current item is a directory, select the whole file name. - if ((extension.length() > 0) && !items.first().isDir()) { - // Don't select the extension - selectionLength -= extension.length() + 1; - } - } else { - // Don't select the # character - --selectionLength; - } - - m_lineEdit->setText(m_newName); - m_lineEdit->setSelection(0, selectionLength); - - topLayout->addWidget(editLabel); - topLayout->addWidget(m_lineEdit); - - if (!m_renameOneItem) { - QSet<QString> extensions; - foreach (const KFileItem& item, m_items) { - QMimeDatabase db; - const QString extension = db.suffixForFileName(item.url().toDisplayString().toLower()); - - if (extensions.contains(extension)) { - m_allExtensionsDifferent = false; - break; - } - - extensions.insert(extension); - } - - QLabel* infoLabel = new QLabel(i18nc("@info", "# will be replaced by ascending numbers starting with:"), page); - mainLayout->addWidget(infoLabel); - m_spinBox = new QSpinBox(page); - m_spinBox->setMaximum(10000); - m_spinBox->setMinimum(0); - m_spinBox->setSingleStep(1); - m_spinBox->setValue(1); - m_spinBox->setDisplayIntegerBase(10); - - QHBoxLayout* horizontalLayout = new QHBoxLayout(page); - horizontalLayout->setContentsMargins(0, 0, 0, 0); - horizontalLayout->addWidget(infoLabel); - horizontalLayout->addWidget(m_spinBox); - - topLayout->addLayout(horizontalLayout); - } -} - -RenameDialog::~RenameDialog() -{ -} - -void RenameDialog::slotAccepted() -{ - QWidget* widget = parentWidget(); - if (!widget) { - widget = this; - } - - const QList<QUrl> srcList = m_items.urlList(); - const QString newName = m_lineEdit->text(); - KIO::FileUndoManager::CommandType cmdType; - KIO::Job *job = nullptr; - if (m_renameOneItem) { - Q_ASSERT(m_items.count() == 1); - cmdType = KIO::FileUndoManager::Rename; - const QUrl oldUrl = m_items.constFirst().url(); - QUrl newUrl = oldUrl.adjusted(QUrl::RemoveFilename); - newUrl.setPath(newUrl.path() + KIO::encodeFileName(newName)); - m_renamedItems << newUrl; - job = KIO::moveAs(oldUrl, newUrl, KIO::HideProgressInfo); - } else { - cmdType = KIO::FileUndoManager::BatchRename; - job = KIO::batchRename(srcList, newName, m_spinBox->value(), QLatin1Char('#')); - connect(qobject_cast<KIO::BatchRenameJob*>(job), &KIO::BatchRenameJob::fileRenamed, this, &RenameDialog::slotFileRenamed); - } - - KJobWidgets::setWindow(job, widget); - const QUrl parentUrl = srcList.first().adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash); - KIO::FileUndoManager::self()->recordJob(cmdType, srcList, parentUrl, job); - - connect(job, &KJob::result, this, &RenameDialog::slotResult); - connect(job, &KJob::result, this, &QObject::deleteLater); - - job->uiDelegate()->setAutoErrorHandlingEnabled(true); - - accept(); -} - -void RenameDialog::slotTextChanged(const QString& newName) -{ - bool enable = !newName.isEmpty() && (newName != QLatin1String("..")) && (newName != QLatin1Char('.')); - if (enable && !m_renameOneItem) { - const int count = newName.count(QLatin1Char('#')); - if (count == 0) { - // Renaming multiple files without '#' will only work if all extensions are different. - enable = m_allExtensionsDifferent; - } else { - // Assure that the new name contains exactly one # (or a connected sequence of #'s) - const int first = newName.indexOf(QLatin1Char('#')); - const int last = newName.lastIndexOf(QLatin1Char('#')); - enable = (last - first + 1 == count); - } - } - m_okButton->setEnabled(enable); -} - -void RenameDialog::slotFileRenamed(const QUrl &oldUrl, const QUrl &newUrl) -{ - Q_UNUSED(oldUrl) - m_renamedItems << newUrl; -} - -void RenameDialog::slotResult(KJob *job) -{ - if (!job->error()) { - emit renamingFinished(m_renamedItems); - } -} - -void RenameDialog::showEvent(QShowEvent* event) -{ - m_lineEdit->setFocus(); - - QDialog::showEvent(event); -} diff --git a/src/views/renamedialog.h b/src/views/renamedialog.h deleted file mode 100644 index 08571cd9d..000000000 --- a/src/views/renamedialog.h +++ /dev/null @@ -1,70 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2006-2010 by Peter Penz ([email protected]) * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - ***************************************************************************/ - -#ifndef RENAMEDIALOG_H -#define RENAMEDIALOG_H - -#include "dolphin_export.h" - -#include <KFileItem> - -#include <QDialog> -#include <QString> - -class QLineEdit; -class QSpinBox; -class QPushButton; -class KJob; -/** - * @brief Dialog for renaming a variable number of files. - * - * The dialog deletes itself when accepted or rejected. - */ -class DOLPHIN_EXPORT RenameDialog : public QDialog -{ - Q_OBJECT - -public: - explicit RenameDialog(QWidget* parent, const KFileItemList& items); - ~RenameDialog() override; - -signals: - void renamingFinished(const QList<QUrl>& urls); - -private slots: - void slotAccepted(); - void slotTextChanged(const QString& newName); - void slotFileRenamed(const QUrl& oldUrl, const QUrl& newUrl); - void slotResult(KJob* job); - -protected: - void showEvent(QShowEvent* event) override; - -private: - bool m_renameOneItem; - QList<QUrl> m_renamedItems; - QString m_newName; - QLineEdit* m_lineEdit; - KFileItemList m_items; - bool m_allExtensionsDifferent; - QSpinBox* m_spinBox; - QPushButton* m_okButton; -}; - -#endif diff --git a/src/views/versioncontrol/versioncontrolobserver.cpp b/src/views/versioncontrol/versioncontrolobserver.cpp index fc74390a9..65c13ef7d 100644 --- a/src/views/versioncontrol/versioncontrolobserver.cpp +++ b/src/views/versioncontrol/versioncontrolobserver.cpp @@ -69,7 +69,7 @@ void VersionControlObserver::setModel(KFileItemModel* model) disconnect(m_model, &KFileItemModel::itemsInserted, this, &VersionControlObserver::delayedDirectoryVerification); disconnect(m_model, &KFileItemModel::itemsChanged, - this, &VersionControlObserver::delayedDirectoryVerification); + this, &VersionControlObserver::slotItemsChanged); } m_model = model; @@ -78,7 +78,7 @@ void VersionControlObserver::setModel(KFileItemModel* model) connect(m_model, &KFileItemModel::itemsInserted, this, &VersionControlObserver::delayedDirectoryVerification); connect(m_model, &KFileItemModel::itemsChanged, - this, &VersionControlObserver::delayedDirectoryVerification); + this, &VersionControlObserver::slotItemsChanged); } } @@ -137,6 +137,18 @@ void VersionControlObserver::silentDirectoryVerification() m_dirVerificationTimer->start(); } +void VersionControlObserver::slotItemsChanged(const KItemRangeList& itemRanges, const QSet<QByteArray>& roles) +{ + Q_UNUSED(itemRanges) + + // Because "version" role is emitted by VCS plugin (ourselfs) we don't need to + // analyze it and update directory item states information. So lets check if + // there is only "version". + if ( !(roles.count() == 1 && roles.contains("version")) ) { + delayedDirectoryVerification(); + } +} + void VersionControlObserver::verifyDirectory() { if (!m_model) { diff --git a/src/views/versioncontrol/versioncontrolobserver.h b/src/views/versioncontrol/versioncontrolobserver.h index 08279dc86..7b269abec 100644 --- a/src/views/versioncontrol/versioncontrolobserver.h +++ b/src/views/versioncontrol/versioncontrolobserver.h @@ -33,6 +33,7 @@ class KFileItemList; class KFileItemModel; +class KItemRangeList; class QAction; class QTimer; class UpdateItemStatesThread; @@ -97,6 +98,12 @@ private slots: */ void silentDirectoryVerification(); + /** + * Invokes delayedDirectoryVerification() only if the itemsChanged() signal has not + * been triggered by the VCS plugin itself. + */ + void slotItemsChanged(const KItemRangeList& itemRanges, const QSet<QByteArray>& roles); + void verifyDirectory(); /** |
