]> cloud.milkyroute.net Git - dolphin.git/commitdiff
Merge branch 'Applications/19.04'
authorKai Uwe Broulik <kde@privat.broulik.de>
Tue, 4 Jun 2019 06:59:02 +0000 (08:59 +0200)
committerKai Uwe Broulik <kde@privat.broulik.de>
Tue, 4 Jun 2019 06:59:02 +0000 (08:59 +0200)
72 files changed:
CMakeLists.txt
HACKING.md [new file with mode: 0644]
cmake/DbusInterfaceMacros.cmake
cmake/FindGem.cmake [new file with mode: 0644]
cmake/FindGem.cmake.in [new file with mode: 0644]
doc/index.docbook
src/CMakeLists.txt
src/dbusinterface.cpp
src/dolphinbookmarkhandler.cpp [new file with mode: 0644]
src/dolphinbookmarkhandler.h [new file with mode: 0644]
src/dolphinmainwindow.cpp
src/dolphinmainwindow.h
src/dolphinpart.cpp
src/dolphinpart.desktop
src/dolphintabwidget.cpp
src/dolphintabwidget.h
src/dolphinui.rc
src/dolphinviewcontainer.cpp
src/filterbar/filterbar.cpp
src/global.cpp
src/global.h
src/kitemviews/kfileitemmodel.cpp
src/kitemviews/kfileitemmodel.h
src/kitemviews/kitemlistcontroller.cpp
src/kitemviews/kstandarditem.cpp
src/kitemviews/kstandarditem.h
src/kitemviews/kstandarditemlistwidget.cpp
src/kitemviews/kstandarditemlistwidget.h
src/kitemviews/kstandarditemmodel.cpp
src/kitemviews/kstandarditemmodel.h
src/kitemviews/private/kbaloorolesprovider.cpp
src/kitemviews/private/kitemlistroleeditor.cpp
src/main.cpp
src/org.kde.dolphin.desktop
src/panels/information/filemetadataconfigurationdialog.cpp [deleted file]
src/panels/information/filemetadataconfigurationdialog.h [deleted file]
src/panels/information/informationpanel.cpp
src/panels/information/informationpanel.h
src/panels/information/informationpanelcontent.cpp
src/panels/information/informationpanelcontent.h
src/panels/information/pixmapviewer.cpp
src/panels/places/placespanel.cpp
src/search/dolphinsearchbox.cpp
src/search/dolphinsearchbox.h
src/settings/dolphinsettingsdialog.cpp
src/settings/dolphinsettingsdialog.h
src/settings/general/previewssettingspage.cpp
src/settings/kcm/kcmdolphingeneral.cpp
src/settings/kcm/kcmdolphingeneral.desktop
src/settings/kcm/kcmdolphinnavigation.cpp
src/settings/kcm/kcmdolphinnavigation.desktop
src/settings/kcm/kcmdolphinservices.cpp
src/settings/kcm/kcmdolphinservices.desktop
src/settings/kcm/kcmdolphinviewmodes.desktop
src/settings/services/servicemenudeinstallation
src/settings/services/servicemenuinstallation
src/settings/services/test/service_menu_deinstallation_test.rb [new file with mode: 0644]
src/settings/services/test/service_menu_installation_test.rb [new file with mode: 0644]
src/settings/services/test/test_helper.rb [new file with mode: 0644]
src/settings/services/test/test_run.rb [new file with mode: 0755]
src/settings/trash/trashsettingspage.cpp
src/settings/viewmodes/dolphinfontrequester.cpp
src/settings/viewmodes/viewsettingstab.cpp
src/settings/viewpropertiesdialog.cpp
src/settings/viewpropsprogressinfo.cpp
src/tests/CMakeLists.txt
src/tests/kfileitemmodeltest.cpp
src/tests/placesitemmodeltest.cpp
src/trash/dolphintrash.cpp
src/views/dolphinviewactionhandler.cpp
src/views/tooltips/tooltipmanager.cpp
src/views/versioncontrol/fileviewversioncontrolplugin.desktop

index 56e00749efb31c07a1228e03d90dee250b822726..2feef8e28f856104bbca3e1d9c1093acdf0d5c7d 100644 (file)
@@ -2,13 +2,13 @@ cmake_minimum_required(VERSION 3.0)
 
 # KDE Application Version, managed by release script
 set (KDE_APPLICATIONS_VERSION_MAJOR "19")
-set (KDE_APPLICATIONS_VERSION_MINOR "04")
-set (KDE_APPLICATIONS_VERSION_MICRO "2")
+set (KDE_APPLICATIONS_VERSION_MINOR "07")
+set (KDE_APPLICATIONS_VERSION_MICRO "70")
 set (KDE_APPLICATIONS_VERSION "${KDE_APPLICATIONS_VERSION_MAJOR}.${KDE_APPLICATIONS_VERSION_MINOR}.${KDE_APPLICATIONS_VERSION_MICRO}")
 project(Dolphin VERSION ${KDE_APPLICATIONS_VERSION})
 
 set(QT_MIN_VERSION "5.8.0")
-set(KF5_MIN_VERSION "5.56.0")
+set(KF5_MIN_VERSION "5.58.0")
 
 # ECM setup
 find_package(ECM ${KF5_MIN_VERSION} CONFIG REQUIRED)
@@ -64,6 +64,7 @@ find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS
     TextWidgets
     Notifications
     Crash
+    WindowSystem
 )
 find_package(KF5 ${KF5_MIN_VERSION} OPTIONAL_COMPONENTS
     Activities
@@ -83,7 +84,7 @@ set_package_properties(KF5Baloo PROPERTIES DESCRIPTION "Baloo Core libraries"
                        PURPOSE "For adding desktop-wide search and tagging support to dolphin"
                       )
 
-find_package(KF5BalooWidgets 18.08.0)
+find_package(KF5BalooWidgets 19.07.70)
 set_package_properties(KF5BalooWidgets PROPERTIES DESCRIPTION "Baloos Widgets"
                        URL "http://www.kde.org"
                        TYPE OPTIONAL
diff --git a/HACKING.md b/HACKING.md
new file mode 100644 (file)
index 0000000..34acf95
--- /dev/null
@@ -0,0 +1,33 @@
+Philosophy
+==========
+
+Dolphin is a file manager focusing on usability. When reading the term Usability people often assume that the focus is on newbies and only basic features are offered. This assumption is wrong.
+
+Target User Group
+-----------------
+
+Focusing on usability means that features are discoverable and efficient to use. The feature set is defined indirectly by the target user group of Dolphin:
+
+- **Lisa**: Lisa is familiar with computers since 10 years. Due to her job she has experience with Word, Excel and Outlook. At home she mainly uses the computer for browsing the web and writing e-mails. She requires a file manager for managing photos from the camera, documents she gets per e-mail or PDF-documents she downloads with a browser. Lisa knows concepts like folders and a file hierarchy, but she is not familiar with the file hierarchy of Linux.
+
+- **Simon**: Simon has been a developer at a software company for 8 years. At home he uses a file manager to maintain his large collection of photos and music. Additionally he owns a small homepage and needs to transfer updated files on the FTP server. Moving and copying files are regular tasks in Simon's workflow.
+
+Not part of the target user group of Dolphin are Fred and Jeff:
+
+- **Fred**: Fred is 75 years old and is able to write e-mails and browsing the web. He is not familiar with file hierarchies and stores all his documents on the desktop.
+
+- **Jeff**: Jeff is Linux-freak since the age of 16 a few years ago. He is developer and in his spare time he acts as administrator for a small company. Jeff has two monitors to keep the overview about his huge number of opened applications.
+
+This does not mean that Fred or Jeff cannot work with Dolphin. But there might be features and concepts of Dolphin that overburden Fred. Also Jeff might miss some features which are a must-have for his daily work.
+
+Non-Intrusive Features
+-----------------------
+
+Before a feature is added in Dolphin, it is checked whether the feature is mandatory for the target user group. If this is not the case, then this does not mean that the feature cannot be added; first it must be clarified whether the feature might be non-intrusive, so that it adds value for users outside the primary target user group of Dolphin. Non-intrusive is mainly related to the user interface. A feature that adds a lot of clutter to the main menu, context menus or toolbar might harm the target user group. In this case the feature will not be added.
+
+A good example of a feature that is non-intrusive is the embedded terminal in Dolphin. It only requires one entry inside a sub menu, but adds great value for Jeff, who is not part of the target user group.
+
+Options
+-------
+
+Options are mandatory as the user "average Joe" does not exist. Still it is not the goal of Dolphin offering options for all kind of things. Again the focus is on the possible needs of the target user group. Each additional option makes it harder finding other options, so the same rules for features are applied to options too.
index 71ad9067d992d1cb41bcdc0ea0f8dc3328729794..1083dfd443aa93090b478728ab09535d100b10ac 100644 (file)
@@ -6,7 +6,7 @@ macro (generate_and_install_dbus_interface main_project_target header_file outpu
     add_custom_target(
         ${output_xml_file}_target
         SOURCES ${CMAKE_CURRENT_BINARY_DIR}/${output_xml_file}
-       )
+    )
     install(
         FILES ${CMAKE_CURRENT_BINARY_DIR}/${output_xml_file}
         DESTINATION ${DBUS_INTERFACES_INSTALL_DIR}
diff --git a/cmake/FindGem.cmake b/cmake/FindGem.cmake
new file mode 100644 (file)
index 0000000..11c9c67
--- /dev/null
@@ -0,0 +1,39 @@
+#=============================================================================
+# Copyright (c) 2019 Harald Sitter <sitter@kde.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+# 3. The name of the author may not be used to endorse or promote products
+#    derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#=============================================================================
+
+# In this scope it's the dir we are in, in the function scope it will be the
+# caller's dir. So, keep our dir in a var.
+set(FINDGEM_MODULES_DIR ${CMAKE_CURRENT_LIST_DIR})
+
+function(find_gem GEM_NAME)
+    set(GEM_PACKAGE "Gem:${GEM_NAME}")
+
+    configure_file(${FINDGEM_MODULES_DIR}/FindGem.cmake.in Find${GEM_PACKAGE}.cmake @ONLY)
+
+    set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_BINARY_DIR}" ${CMAKE_MODULE_PATH})
+    find_package(${GEM_PACKAGE} ${ARGN})
+endfunction()
diff --git a/cmake/FindGem.cmake.in b/cmake/FindGem.cmake.in
new file mode 100644 (file)
index 0000000..0dcc677
--- /dev/null
@@ -0,0 +1,53 @@
+#=============================================================================
+# Copyright (c) 2019 Harald Sitter <sitter@kde.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+# 3. The name of the author may not be used to endorse or promote products
+#    derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#=============================================================================
+
+find_program(RUBY_EXE ruby)
+if(NOT RUBY_EXE)
+    message(WARNING "Could not find ruby program")
+    return()
+endif()
+
+execute_process(
+    COMMAND ${RUBY_EXE} -e "require '@GEM_NAME@'"
+    ERROR_VARIABLE ERROR_VAR
+    RESULT_VARIABLE RESULT_VAR
+)
+
+if(RESULT_VAR EQUAL 0)
+    set(@GEM_PACKAGE@_FOUND TRUE)
+else()
+    message(WARNING ${ERROR_VAR})
+    return()
+endif()
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(@GEM_PACKAGE@
+    FOUND_VAR
+        @GEM_PACKAGE@_FOUND
+    REQUIRED_VARS
+        @GEM_PACKAGE@_FOUND
+)
index 4a38a157aaac5e83db40335e44f42819ccd58f9e..3495ee9bcfe711d218a2f8a04ef972756d60a0b4 100644 (file)
@@ -782,7 +782,7 @@ current folder, &RMB; click in the work space and click
 <para>
 &dolphin; is capable of searching for files and for content in files. If <keycombo action="simul">
 &Ctrl;<keycap>F</keycap></keycombo> is pressed or <menuchoice> <guimenu>Edit</guimenu> 
-<guimenuitem>Find...</guimenuitem> </menuchoice> is used, the <guilabel>Find</guilabel> 
+<guimenuitem>Search...</guimenuitem> </menuchoice> is used, the <guilabel>Search</guilabel>
 bar will open already set up to search for files within the current folder and any sub-folders.
 Start to type into the find input box and the search starts immediately.
 <screenshot>
@@ -821,7 +821,7 @@ starts the search from the user's <replaceable>Home</replaceable> folder.</para>
 </screenshot>
 
 <para>
-Use the <guilabel>More Options</guilabel> button to extend the <guilabel>Find</guilabel> 
+Use the <guilabel>More Options</guilabel> button to extend the <guilabel>Search</guilabel>
 bar. This provides a very comfortable way for 
 the user to shrink the number of search results.</para>
 <para>To start a search select one or more file types (<guilabel>Documents</guilabel>, 
@@ -1693,7 +1693,7 @@ The name of this file has to be entered in a dialog.
 <keycombo action="simul">&Ctrl;<keycap>F</keycap></keycombo>
 </shortcut>
 <guimenu>Edit</guimenu>
-<guimenuitem>Find...</guimenuitem>
+<guimenuitem>Search...</guimenuitem>
 </menuchoice></term>
 <listitem><para><action>Opens the find bar. Enter a search term into the edit box and select to search for filename
 or in contents of files starting from the current folder or everywhere.</action></para></listitem>
index e0dd576793b54b22f1192a14fe1dd68074ad487b..8a025e584369bbcc12b24e00414bc16ebf56f92c 100644 (file)
@@ -148,6 +148,7 @@ target_link_libraries(
     KF5::ConfigCore
     KF5::NewStuff
     KF5::Parts
+    KF5::WindowSystem
 )
 
 if(HAVE_BALOO)
@@ -190,6 +191,7 @@ install(FILES dolphinpart.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR})
 ##########################################
 
 set(dolphinstatic_SRCS
+    dolphinbookmarkhandler.cpp
     dolphindockwidget.cpp
     dolphinmainwindow.cpp
     dolphinviewcontainer.cpp
@@ -246,7 +248,6 @@ set(dolphinstatic_SRCS
 if(HAVE_BALOO)
     set(dolphinstatic_SRCS
         ${dolphinstatic_SRCS}
-        panels/information/filemetadataconfigurationdialog.cpp
         panels/information/informationpanel.cpp
         panels/information/informationpanelcontent.cpp
         panels/information/pixmapviewer.cpp
@@ -384,6 +385,13 @@ install(TARGETS kcm_dolphingeneral DESTINATION ${KDE_INSTALL_PLUGINDIR} )
 ########### install files ###############
 
 install( PROGRAMS org.kde.dolphin.desktop DESTINATION ${KDE_INSTALL_APPDIR} )
+
+install( DIRECTORY DESTINATION "${KDE_INSTALL_FULL_DATAROOTDIR}/kglobalaccel" )
+
+install(
+    CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" -E create_symlink \"${KDE_INSTALL_FULL_APPDIR}/org.kde.dolphin.desktop\" \"\$ENV{DESTDIR}${KDE_INSTALL_FULL_DATAROOTDIR}/kglobalaccel/org.kde.dolphin.desktop\")"
+)
+
 install( FILES settings/dolphin_directoryviewpropertysettings.kcfg
                settings/dolphin_generalsettings.kcfg
                settings/dolphin_compactmodesettings.kcfg
@@ -392,8 +400,7 @@ install( FILES settings/dolphin_directoryviewpropertysettings.kcfg
                settings/dolphin_versioncontrolsettings.kcfg
          DESTINATION ${KDE_INSTALL_KCFGDIR} )
 install( FILES org.kde.dolphin.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR} )
-install( FILES settings/kcm/kcmdolphinviewmodes.desktop DESTINATION
-${KDE_INSTALL_KSERVICES5DIR} )
+install( FILES settings/kcm/kcmdolphinviewmodes.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR} )
 install( FILES settings/kcm/kcmdolphinnavigation.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR} )
 install( FILES settings/kcm/kcmdolphinservices.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR} )
 install( FILES settings/kcm/kcmdolphingeneral.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR} )
index c780bc7cdc9cb853f4df70cc9d57a4df72183872..4e24354abefe5578c05ac501bea288417000df1f 100644 (file)
 
 #include "dbusinterface.h"
 #include "global.h"
+#include "dolphin_generalsettings.h"
 
 #include <KPropertiesDialog>
 
+#include <QApplication>
 #include <QDBusConnection>
+#include <QDBusInterface>
 #include <QDBusConnectionInterface>
 
 DBusInterface::DBusInterface() :
@@ -41,7 +44,10 @@ void DBusInterface::ShowFolders(const QStringList& uriList, const QString& start
     if (urls.isEmpty()) {
         return;
     }
-    Dolphin::openNewWindow(urls);
+    const auto serviceName = QStringLiteral("org.kde.dolphin-%1").arg(QCoreApplication::applicationPid());
+    if(!Dolphin::attachToExistingInstance(urls, false, GeneralSettings::splitView(), serviceName)) {
+        Dolphin::openNewWindow(urls);
+    }
 }
 
 void DBusInterface::ShowItems(const QStringList& uriList, const QString& startUpId)
@@ -51,7 +57,10 @@ void DBusInterface::ShowItems(const QStringList& uriList, const QString& startUp
     if (urls.isEmpty()) {
         return;
     }
-    Dolphin::openNewWindow(urls, nullptr, Dolphin::OpenNewWindowFlag::Select);
+    const auto serviceName = QStringLiteral("org.kde.dolphin-%1").arg(QCoreApplication::applicationPid());
+    if(!Dolphin::attachToExistingInstance(urls, true, GeneralSettings::splitView(), serviceName)) {
+        Dolphin::openNewWindow(urls);
+    };
 }
 
 void DBusInterface::ShowItemProperties(const QStringList& uriList, const QString& startUpId)
diff --git a/src/dolphinbookmarkhandler.cpp b/src/dolphinbookmarkhandler.cpp
new file mode 100644 (file)
index 0000000..bb8f641
--- /dev/null
@@ -0,0 +1,135 @@
+/***************************************************************************
+ *   Copyright (C) 2019 by David Hallas <david@davidhallas.dk>             *
+ *                                                                         *
+ *   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 "dolphinbookmarkhandler.h"
+#include "dolphinmainwindow.h"
+#include "dolphinviewcontainer.h"
+#include "global.h"
+#include <KBookmarkMenu>
+#include <KIO/Global>
+#include <QDebug>
+#include <QDir>
+#include <QStandardPaths>
+
+DolphinBookmarkHandler::DolphinBookmarkHandler(DolphinMainWindow *mainWindow,
+                                               KActionCollection* collection,
+                                               QMenu* menu,
+                                               QObject* parent) :
+    QObject(parent),
+    m_mainWindow(mainWindow)
+{
+    QString bookmarksFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation,
+                                                   QStringLiteral("kfile/bookmarks.xml"));
+    if (bookmarksFile.isEmpty()) {
+        QString genericDataLocation = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
+        if (genericDataLocation.isEmpty()) {
+            qWarning() << "GenericDataLocation is empty! Bookmarks will not be saved correctly.";
+        }
+        bookmarksFile = QStringLiteral("%1/dolphin").arg(genericDataLocation);
+        QDir().mkpath(bookmarksFile);
+        bookmarksFile += QLatin1String("/bookmarks.xml");
+    }
+    m_bookmarkManager = KBookmarkManager::managerForFile(bookmarksFile, QStringLiteral("dolphin"));
+    m_bookmarkManager->setUpdate(true);
+    m_bookmarkMenu.reset(new KBookmarkMenu(m_bookmarkManager, this, menu, collection));
+}
+
+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());
+}
+
+QUrl DolphinBookmarkHandler::currentUrl() const
+{
+    return url(m_mainWindow->activeViewContainer());
+}
+
+QString DolphinBookmarkHandler::currentIcon() const
+{
+    return icon(m_mainWindow->activeViewContainer());
+}
+
+bool DolphinBookmarkHandler::supportsTabs() const
+{
+    return true;
+}
+
+QList<KBookmarkOwner::FutureBookmark> DolphinBookmarkHandler::currentBookmarkList() const
+{
+    const auto viewContainers = m_mainWindow->viewContainers();
+    QList<FutureBookmark> bookmarks;
+    bookmarks.reserve(viewContainers.size());
+    for (const auto viewContainer : viewContainers) {
+        bookmarks << FutureBookmark(title(viewContainer), url(viewContainer), icon(viewContainer));
+    }
+    return bookmarks;
+}
+
+bool DolphinBookmarkHandler::enableOption(KBookmarkOwner::BookmarkOption option) const
+{
+    switch (option) {
+    case BookmarkOption::ShowAddBookmark: return true;
+    case BookmarkOption::ShowEditBookmark: return true;
+    }
+    return false;
+}
+
+void DolphinBookmarkHandler::openBookmark(const KBookmark& bookmark, Qt::MouseButtons, Qt::KeyboardModifiers)
+{
+    m_mainWindow->changeUrl(bookmark.url());
+}
+
+void DolphinBookmarkHandler::openFolderinTabs(const KBookmarkGroup& bookmarkGroup)
+{
+    m_mainWindow->openDirectories(bookmarkGroup.groupUrlList(), false);
+}
+
+void DolphinBookmarkHandler::openInNewTab(const KBookmark& bookmark)
+{
+    m_mainWindow->openNewTabAfterCurrentTab(bookmark.url());
+}
+
+void DolphinBookmarkHandler::openInNewWindow(const KBookmark& bookmark)
+{
+    Dolphin::openNewWindow({bookmark.url()}, m_mainWindow);
+}
+
+QString DolphinBookmarkHandler::title(DolphinViewContainer* viewContainer)
+{
+    return viewContainer->caption();
+}
+
+QUrl DolphinBookmarkHandler::url(DolphinViewContainer* viewContainer)
+{
+    return viewContainer->url();
+}
+
+QString DolphinBookmarkHandler::icon(DolphinViewContainer* viewContainer)
+{
+    return KIO::iconNameForUrl(viewContainer->url());
+}
diff --git a/src/dolphinbookmarkhandler.h b/src/dolphinbookmarkhandler.h
new file mode 100644 (file)
index 0000000..6fd511d
--- /dev/null
@@ -0,0 +1,61 @@
+/***************************************************************************
+ *   Copyright (C) 2019 by David Hallas <david@davidhallas.dk>             *
+ *                                                                         *
+ *   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 DOLPHINBOOKMARKHANDLER_H
+#define DOLPHINBOOKMARKHANDLER_H
+
+#include <KBookmarkManager>
+#include <QObject>
+
+class DolphinMainWindow;
+class DolphinViewContainer;
+class KActionCollection;
+class KBookmarkManager;
+class KBookmarkMenu;
+class QMenu;
+
+class DolphinBookmarkHandler : public QObject, public KBookmarkOwner
+{
+    Q_OBJECT
+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;
+    QString currentIcon() const override;
+    bool supportsTabs() const override;
+    QList<FutureBookmark> currentBookmarkList() const override;
+    bool enableOption(BookmarkOption option) const override;
+    void openBookmark(const KBookmark &bookmark, Qt::MouseButtons, Qt::KeyboardModifiers) override;
+    void openFolderinTabs(const KBookmarkGroup &bookmarkGroup) override;
+    void openInNewTab(const KBookmark &bookmark) override;
+    void openInNewWindow(const KBookmark &bookmark) override;
+    static QString title(DolphinViewContainer* viewContainer);
+    static QUrl url(DolphinViewContainer* viewContainer);
+    static QString icon(DolphinViewContainer* viewContainer);
+private:
+    DolphinMainWindow* m_mainWindow;
+    KBookmarkManager *m_bookmarkManager;
+    QScopedPointer<KBookmarkMenu> m_bookmarkMenu;
+    QScopedPointer<KBookmarkMenu> m_bookmarkControlMenu;
+};
+
+#endif // DOLPHINBOOKMARKHANDLER_H
index 7224c8ea940c166db524c654b0ef78291fbd53f4..385b61a2a7fb8ca9b033854c9ee8bf239471f78f 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "config-terminal.h"
 #include "global.h"
+#include "dolphinbookmarkhandler.h"
 #include "dolphindockwidget.h"
 #include "dolphincontextmenu.h"
 #include "dolphinnewfilemenu.h"
 #include <KRun>
 #include <KShell>
 #include <KStandardAction>
+#include <KStartupInfo>
 #include <KToggleAction>
 #include <KToolBar>
 #include <KToolInvocation>
 #include <KUrlComboBox>
 #include <KUrlNavigator>
+#include <KWindowSystem>
 
 #include <QApplication>
 #include <QClipboard>
@@ -94,6 +97,7 @@ DolphinMainWindow::DolphinMainWindow() :
     m_actionHandler(nullptr),
     m_remoteEncoding(nullptr),
     m_settingsDialog(),
+    m_bookmarkHandler(nullptr),
     m_controlButton(nullptr),
     m_updateToolBarTimer(nullptr),
     m_lastHandleUrlStatJob(nullptr),
@@ -111,7 +115,7 @@ DolphinMainWindow::DolphinMainWindow() :
     KIO::FileUndoManager* undoManager = KIO::FileUndoManager::self();
     undoManager->setUiInterface(new UndoUiInterface());
 
-    connect(undoManager, static_cast<void(KIO::FileUndoManager::*)(bool)>(&KIO::FileUndoManager::undoAvailable),
+    connect(undoManager, QOverload<bool>::of(&KIO::FileUndoManager::undoAvailable),
             this, &DolphinMainWindow::slotUndoAvailable);
     connect(undoManager, &KIO::FileUndoManager::undoTextChanged,
             this, &DolphinMainWindow::slotUndoTextChanged);
@@ -183,16 +187,42 @@ DolphinMainWindow::~DolphinMainWindow()
 {
 }
 
+QVector<DolphinViewContainer*> DolphinMainWindow::viewContainers() const
+{
+    QVector<DolphinViewContainer*> viewContainers;
+    viewContainers.reserve(m_tabWidget->count());
+    for (int i = 0; i < m_tabWidget->count(); ++i) {
+        viewContainers << m_tabWidget->tabPageAt(i)->activeViewContainer();
+    }
+    return viewContainers;
+}
+
 void DolphinMainWindow::openDirectories(const QList<QUrl>& dirs, bool splitView)
 {
     m_tabWidget->openDirectories(dirs, splitView);
 }
 
+void DolphinMainWindow::openDirectories(const QStringList& dirs, bool splitView)
+{
+    openDirectories(QUrl::fromStringList(dirs), splitView);
+}
+
 void DolphinMainWindow::openFiles(const QList<QUrl>& files, bool splitView)
 {
     m_tabWidget->openFiles(files, splitView);
 }
 
+void DolphinMainWindow::openFiles(const QStringList& files, bool splitView)
+{
+    openFiles(QUrl::fromStringList(files), splitView);
+}
+
+void DolphinMainWindow::activateWindow()
+{
+    KStartupInfo::setNewStartupId(window(), KStartupInfo::startupId());
+    KWindowSystem::activateWindow(window()->effectiveWinId());
+}
+
 void DolphinMainWindow::showCommand(CommandType command)
 {
     DolphinStatusBar* statusBar = m_activeViewContainer->statusBar();
@@ -928,9 +958,13 @@ void DolphinMainWindow::updateControlMenu()
 
     menu->addSeparator();
 
+    // Overwrite Find action to Search action
+    QAction *searchAction = ac->action(KStandardAction::name(KStandardAction::Find));
+    searchAction->setText(i18n("Search..."));
+
     // Add "Edit" actions
     bool added = addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Undo)), menu) |
-                 addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Find)), menu) |
+                 addActionToMenu(searchAction, menu) |
                  addActionToMenu(ac->action(KStandardAction::name(KStandardAction::SelectAll)), menu) |
                  addActionToMenu(ac->action(QStringLiteral("invert_selection")), menu);
 
@@ -978,6 +1012,9 @@ void DolphinMainWindow::updateControlMenu()
     goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Up)));
     goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Home)));
     goMenu->addAction(ac->action(QStringLiteral("closed_tabs")));
+    KActionMenu *bookmarkMenu = new KActionMenu(i18nc("@title:menu", "&Bookmarks"), goMenu);
+    m_bookmarkHandler->fillControlMenu(bookmarkMenu->menu(), ac);
+    goMenu->addAction(bookmarkMenu);
     menu->addMenu(goMenu);
 
     // Add "Tool" menu
@@ -1118,10 +1155,9 @@ void DolphinMainWindow::setupActions()
     newTab->setIcon(QIcon::fromTheme(QStringLiteral("tab-new")));
     newTab->setText(i18nc("@action:inmenu File", "New Tab"));
     actionCollection()->setDefaultShortcuts(newTab, {Qt::CTRL + Qt::Key_T, Qt::CTRL + Qt::SHIFT + Qt::Key_N});
-    connect(newTab, &QAction::triggered, this, static_cast<void(DolphinMainWindow::*)()>(&DolphinMainWindow::openNewActivatedTab));
+    connect(newTab, &QAction::triggered, this, &DolphinMainWindow::openNewActivatedTab);
 
-    QAction* closeTab = KStandardAction::close(
-            m_tabWidget, static_cast<void(DolphinTabWidget::*)()>(&DolphinTabWidget::closeTab), actionCollection());
+    QAction* closeTab = KStandardAction::close(m_tabWidget, QOverload<>::of(&DolphinTabWidget::closeTab), actionCollection());
     closeTab->setText(i18nc("@action:inmenu File", "Close Tab"));
 
     KStandardAction::quit(this, &DolphinMainWindow::quit, actionCollection());
@@ -1140,7 +1176,8 @@ void DolphinMainWindow::setupActions()
     // due to the long text, the text "Paste" is used:
     paste->setIconText(i18nc("@action:inmenu Edit", "Paste"));
 
-    KStandardAction::find(this, &DolphinMainWindow::find, actionCollection());
+    QAction *searchAction = KStandardAction::find(this, &DolphinMainWindow::find, actionCollection());
+    searchAction->setText(i18n("Search..."));
 
     KStandardAction::selectAll(this, &DolphinMainWindow::selectAll, actionCollection());
 
@@ -1236,6 +1273,11 @@ void DolphinMainWindow::setupActions()
     }
 #endif
 
+    // setup 'Bookmarks' menu
+    KActionMenu *bookmarkMenu = new KActionMenu(i18nc("@title:menu", "&Bookmarks"), this);
+    m_bookmarkHandler = new DolphinBookmarkHandler(this, actionCollection(), bookmarkMenu->menu(), this);
+    actionCollection()->addAction(QStringLiteral("bookmarks"), bookmarkMenu);
+
     // setup 'Settings' menu
     KToggleAction* showMenuBar = KStandardAction::showMenubar(nullptr, nullptr, actionCollection());
     connect(showMenuBar, &KToggleAction::triggered,                   // Fixes #286822
@@ -1596,9 +1638,9 @@ void DolphinMainWindow::connectViewSignals(DolphinViewContainer* container)
     connect(view, &DolphinView::directoryLoadingCompleted,
             this, &DolphinMainWindow::slotDirectoryLoadingCompleted);
     connect(view, &DolphinView::goBackRequested,
-            this, static_cast<void(DolphinMainWindow::*)()>(&DolphinMainWindow::goBack));
+            this, &DolphinMainWindow::goBack);
     connect(view, &DolphinView::goForwardRequested,
-            this, static_cast<void(DolphinMainWindow::*)()>(&DolphinMainWindow::goForward));
+            this, &DolphinMainWindow::goForward);
     connect(view, &DolphinView::urlActivated,
             this, &DolphinMainWindow::handleUrl);
 
@@ -1683,3 +1725,12 @@ void DolphinMainWindow::UndoUiInterface::jobError(KIO::Job* job)
     }
 }
 
+bool DolphinMainWindow::isUrlOpen(const QString& url)
+{
+    if (m_tabWidget->getIndexByUrl(QUrl::fromUserInput((url))) >= 0) {
+        return true;
+    } else {
+        return false;
+    }
+}
+
index 1734d4ad4c1f1c99e90227fb99b844415148f300..4037c14689fc82d2f429501a35de694c0d70358c 100644 (file)
 #include <QList>
 #include <QPointer>
 #include <QUrl>
+#include <QVector>
 
 typedef KIO::FileUndoManager::CommandType CommandType;
 
+class DolphinBookmarkHandler;
 class DolphinViewActionHandler;
 class DolphinSettingsDialog;
 class DolphinViewContainer;
@@ -71,6 +73,11 @@ public:
      */
     DolphinViewContainer* activeViewContainer() const;
 
+    /**
+     * Returns view container for all tabs
+     */
+    QVector<DolphinViewContainer*> viewContainers() const;
+
     /**
      * Opens each directory in \p dirs in a separate tab. If \a splitView is set,
      * 2 directories are collected within one tab.
@@ -94,6 +101,39 @@ public:
     void setTabsToHomeIfMountPathOpen(const QString& mountPath);
 
 public slots:
+    /**
+     * Opens each directory in \p dirs in a separate tab. If \a splitView is set,
+     * 2 directories are collected within one tab.
+     * \pre \a dirs must contain at least one url.
+     *
+     * @note this function is overloaded so that it is callable via DBus.
+     */
+    void openDirectories(const QStringList &dirs, bool splitView);
+
+    /**
+     * Opens the directories which contain the files \p files and selects all files.
+     * If \a splitView is set, 2 directories are collected within one tab.
+     * \pre \a files must contain at least one url.
+     *
+     * @note this is overloaded so that this function is callable via DBus.
+     */
+    void openFiles(const QStringList &files, bool splitView);
+
+    /**
+     * Tries to raise/activate the Dolphin window.
+     */
+    void activateWindow();
+
+    /**
+     * Determines if a URL is open in any tab.
+     * @note Use of QString instead of QUrl is required to be callable via DBus.
+     *
+     * @param url URL to look for
+     * @returns true if url is currently open in a tab, false otherwise.
+     */
+    bool isUrlOpen(const QString &url);
+
+
     /**
      * Pastes the clipboard data into the currently selected folder
      * of the active view. If not exactly one folder is selected,
@@ -118,6 +158,16 @@ public slots:
     /** Stores all settings and quits Dolphin. */
     void quit();
 
+    /**
+     * Opens a new tab and places it after the current tab
+     */
+    void openNewTabAfterCurrentTab(const QUrl& url);
+
+    /**
+     * Opens a new tab and places it as the last tab
+     */
+    void openNewTabAfterLastTab(const QUrl& url);
+
 signals:
     /**
      * Is sent if the selection of the currently active view has
@@ -330,16 +380,6 @@ private slots:
      */
     void openNewTab(const QUrl& url, DolphinTabWidget::TabPlacement tabPlacement);
 
-    /**
-     * Opens a new tab and places it after the current tab
-     */
-    void openNewTabAfterCurrentTab(const QUrl& url);
-
-    /**
-     * Opens a new tab and places it as the last tab
-     */
-    void openNewTabAfterLastTab(const QUrl& url);
-
     /**
      * Opens the selected folder in a new tab.
      */
@@ -522,6 +562,7 @@ private:
     DolphinViewActionHandler* m_actionHandler;
     DolphinRemoteEncoding* m_remoteEncoding;
     QPointer<DolphinSettingsDialog> m_settingsDialog;
+    DolphinBookmarkHandler* m_bookmarkHandler;
 
     // Members for the toolbar menu that is shown when the menubar is hidden:
     QToolButton* m_controlButton;
index f5d4f95b64e95ed21713147ac8a3346c422bb5cc..a4d7fdf78abcf1806099ff6901e5366c84bcc30a 100644 (file)
@@ -76,7 +76,7 @@ DolphinPart::DolphinPart(QWidget* parentWidget, QObject* parent, const QVariantL
     connect(&DolphinNewFileMenuObserver::instance(), &DolphinNewFileMenuObserver::errorMessage,
             this, &DolphinPart::slotErrorMessage);
 
-    connect(m_view, &DolphinView::directoryLoadingCompleted, this, static_cast<void(DolphinPart::*)()>(&DolphinPart::completed));
+    connect(m_view, &DolphinView::directoryLoadingCompleted, this, QOverload<>::of(&KParts::ReadOnlyPart::completed));
     connect(m_view, &DolphinView::directoryLoadingCompleted, this, &DolphinPart::updatePasteAction);
     connect(m_view, &DolphinView::directoryLoadingProgress, this, &DolphinPart::updateProgress);
     connect(m_view, &DolphinView::errorMessage, this, &DolphinPart::slotErrorMessage);
@@ -98,7 +98,7 @@ DolphinPart::DolphinPart(QWidget* parentWidget, QObject* parent, const QVariantL
     connect(m_view, &DolphinView::requestContextMenu,
             this, &DolphinPart::slotOpenContextMenu);
     connect(m_view, &DolphinView::selectionChanged,
-            m_extension, static_cast<void(DolphinPartBrowserExtension::*)(const KFileItemList&)>(&DolphinPartBrowserExtension::selectionInfo));
+            m_extension, QOverload<const KFileItemList&>::of(&KParts::BrowserExtension::selectionInfo));
     connect(m_view, &DolphinView::selectionChanged,
             this, &DolphinPart::slotSelectionChanged);
     connect(m_view, &DolphinView::requestItemInfo,
index 033a27e98757b15997bb9caac4d849f4b1f61aa0..5f2d093457b385632f7ed3433372d11f2c634128 100644 (file)
@@ -23,6 +23,7 @@ Name[it]=Vista di Dolphin
 Name[ja]=Dolphin ビュー
 Name[ko]=Dolphin 보기
 Name[lt]=Dolphin žiūryklė
+Name[ml]=ഡോള്‍ഫിന്‍ അവതരണരീതി
 Name[nb]=Dolphin visning
 Name[nl]=Dolphin-weergave
 Name[nn]=Dolphin-vising
@@ -82,6 +83,7 @@ Name[it]=Icone
 Name[ja]=アイコン
 Name[ko]=아이콘
 Name[lt]=Ženkliukai
+Name[ml]=സൂചനാചിത്രങ്ങള്‍
 Name[nb]=Ikoner
 Name[nl]=Pictogrammen
 Name[nn]=Ikon
@@ -132,6 +134,7 @@ Name[it]=Compatta
 Name[ja]=コンパクト
 Name[ko]=축소됨
 Name[lt]=Kompaktiškas
+Name[ml]=തിങ്ങിയതായി
 Name[nb]=Kompakt
 Name[nl]=Compact
 Name[nn]=Kompakt
@@ -181,6 +184,7 @@ Name[it]=Dettagli
 Name[ja]=詳細
 Name[ko]=자세히
 Name[lt]=Informacija
+Name[ml]=വിശദമായി
 Name[nb]=Detaljer
 Name[nl]=Details
 Name[nn]=Detaljar
index c677054d8907e274388fb184946b671a34a3f45a..defd089c16075cf48e1ca31e0a3779eb0aba7faf 100644 (file)
@@ -38,13 +38,13 @@ DolphinTabWidget::DolphinTabWidget(QWidget* parent) :
     m_lastViewedTab(0)
 {
     connect(this, &DolphinTabWidget::tabCloseRequested,
-            this, static_cast<void (DolphinTabWidget::*)(int)>(&DolphinTabWidget::closeTab));
+            this, QOverload<int>::of(&DolphinTabWidget::closeTab));
     connect(this, &DolphinTabWidget::currentChanged,
             this, &DolphinTabWidget::currentTabChanged);
 
     DolphinTabBar* tabBar = new DolphinTabBar(this);
     connect(tabBar, &DolphinTabBar::openNewActivatedTab,
-            this,  static_cast<void (DolphinTabWidget::*)(int)>(&DolphinTabWidget::openNewActivatedTab));
+            this, QOverload<int>::of(&DolphinTabWidget::openNewActivatedTab));
     connect(tabBar, &DolphinTabBar::tabDropEvent,
             this, &DolphinTabWidget::tabDropEvent);
     connect(tabBar, &DolphinTabBar::tabDetachRequested,
@@ -186,11 +186,16 @@ void DolphinTabWidget::openDirectories(const QList<QUrl>& dirs, bool splitView)
     QList<QUrl>::const_iterator it = dirs.constBegin();
     while (it != dirs.constEnd()) {
         const QUrl& primaryUrl = *(it++);
+        const int index = getIndexByUrl(primaryUrl);
+        if (index >= 0) {
+            setCurrentIndex(index);
+            continue;
+        }
         if (splitView && (it != dirs.constEnd())) {
             const QUrl& secondaryUrl = *(it++);
-            openNewTab(primaryUrl, secondaryUrl);
+            openNewActivatedTab(primaryUrl, secondaryUrl);
         } else {
-            openNewTab(primaryUrl);
+            openNewActivatedTab(primaryUrl);
         }
     }
 }
@@ -290,6 +295,7 @@ void DolphinTabWidget::detachTab(int index)
         args << tabPage->secondaryViewContainer()->url().url();
         args << QStringLiteral("--split");
     }
+    args << QStringLiteral("--new-window");
 
     const QString command = QStringLiteral("dolphin %1").arg(KShell::joinArgs(args));
     KRun::runCommand(command, this);
@@ -374,3 +380,17 @@ QString DolphinTabWidget::tabName(DolphinTabPage* tabPage) const
     // and not misinterpreted as a keyboard shortcut in QTabBar::setTabText()
     return name.replace('&', QLatin1String("&&"));
 }
+
+int DolphinTabWidget::getIndexByUrl(const QUrl& url) const
+{
+    for (int i = 0; i < count(); i++) {
+        // Conversion to display string is necessary to deal with the '~' alias.
+        // i.e. to acknowledge that ~/ is equivalent to /home/user/
+        const QUrl tabUrl = tabPageAt(i)->activeViewContainer()->url();
+        if (url == tabUrl ||
+            url.toDisplayString(QUrl::StripTrailingSlash) == tabUrl.toDisplayString(QUrl::StripTrailingSlash)) {
+            return i;
+        }
+    }
+    return -1;
+}
index 7e3b30c0dff5285cc534d44fa21674070f503804..3e8301725c71e83c69fca4ab62d570aae87d5f51 100644 (file)
@@ -78,6 +78,12 @@ public:
      */
     void refreshViews();
 
+    /**
+     * @param url The URL that we would like
+     * @return index of the tab with the desired URL. returns -1 if not found
+     */
+    int getIndexByUrl(const QUrl& url) const;
+
 signals:
     /**
      * Is emitted when the active view has been changed, by changing the current
index 282ea63e5f2a0f74dbec5ced091cdbf66d949e97..b90321d0592e19d6e1beb3d818b8e4fc180336d9 100644 (file)
@@ -1,12 +1,12 @@
 <!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
-<kpartgui name="dolphin" version="21">
+<kpartgui name="dolphin" version="22">
     <MenuBar>
         <Menu name="file">
             <Action name="new_menu" />
             <Action name="file_new" />
             <Action name="new_tab" />
             <Action name="file_close" />
-           <Action name="undo_close_tab" />
+            <Action name="undo_close_tab" />
             <Separator/>
             <Action name="renamefile" />
             <Action name="movetotrash" />
@@ -43,6 +43,7 @@
             <Action name="view_properties" />
         </Menu>
         <Menu name="go">
+            <Action name="bookmarks" />
             <Action name="closed_tabs" />
         </Menu>
         <Menu name="tools">
index e21262977a66039886a5c4e8c6ce103e0bf1f12b..1e5d0f7d18c21fdc549bf0fff7bc893097ce3a7f 100644 (file)
@@ -120,7 +120,21 @@ DolphinViewContainer::DolphinViewContainer(const QUrl& url, QWidget* parent) :
     }
 #endif
 
+    // Initialize filter bar
+    m_filterBar = new FilterBar(this);
+    m_filterBar->setVisible(settings->filterBar());
+
+    connect(m_filterBar, &FilterBar::filterChanged,
+            this, &DolphinViewContainer::setNameFilter);
+    connect(m_filterBar, &FilterBar::closeRequest,
+            this, &DolphinViewContainer::closeFilterBar);
+    connect(m_filterBar, &FilterBar::focusViewRequest,
+            this, &DolphinViewContainer::requestFocus);
+
+    // Initialize the main view
     m_view = new DolphinView(url, this);
+    connect(m_view, &DolphinView::urlChanged,
+            m_filterBar, &FilterBar::slotUrlChanged);
     connect(m_view, &DolphinView::urlChanged,
             m_urlNavigator, &KUrlNavigator::setLocationUrl);
     connect(m_view, &DolphinView::urlChanged,
@@ -198,18 +212,6 @@ DolphinViewContainer::DolphinViewContainer(const QUrl& url, QWidget* parent) :
     connect(undoManager, &KIO::FileUndoManager::jobRecordingFinished,
             this, &DolphinViewContainer::delayedStatusBarUpdate);
 
-    // Initialize filter bar
-    m_filterBar = new FilterBar(this);
-    m_filterBar->setVisible(settings->filterBar());
-    connect(m_filterBar, &FilterBar::filterChanged,
-            this, &DolphinViewContainer::setNameFilter);
-    connect(m_filterBar, &FilterBar::closeRequest,
-            this, &DolphinViewContainer::closeFilterBar);
-    connect(m_filterBar, &FilterBar::focusViewRequest,
-            this, &DolphinViewContainer::requestFocus);
-    connect(m_view, &DolphinView::urlChanged,
-            m_filterBar, &FilterBar::slotUrlChanged);
-
     navigatorLayout->addWidget(m_urlNavigator);
     navigatorLayout->addWidget(m_emptyTrashButton);
 
index b4fef22a817cb7983add4ceb3feb544a98220c85..50af2c6c7bd6e46b627afd67544576360fc6e895 100644 (file)
@@ -47,13 +47,12 @@ FilterBar::FilterBar(QWidget* parent) :
     m_lockButton->setToolTip(i18nc("@info:tooltip", "Keep Filter When Changing Folders"));
     connect(m_lockButton, &QToolButton::toggled, this, &FilterBar::slotToggleLockButton);
 
-    // Create label
-    QLabel* filterLabel = new QLabel(i18nc("@label:textbox", "Filter:"), this);
 
     // Create filter editor
     m_filterInput = new QLineEdit(this);
     m_filterInput->setLayoutDirection(Qt::LeftToRight);
     m_filterInput->setClearButtonEnabled(true);
+    m_filterInput->setPlaceholderText(i18n("Filter..."));
     connect(m_filterInput, &QLineEdit::textChanged,
             this, &FilterBar::filterChanged);
     setFocusProxy(m_filterInput);
@@ -62,11 +61,8 @@ FilterBar::FilterBar(QWidget* parent) :
     QHBoxLayout* hLayout = new QHBoxLayout(this);
     hLayout->setContentsMargins(0, 0, 0, 0);
     hLayout->addWidget(closeButton);
-    hLayout->addWidget(filterLabel);
-    hLayout->addWidget(m_filterInput);
     hLayout->addWidget(m_lockButton);
-
-    filterLabel->setBuddy(m_filterInput);
+    hLayout->addWidget(m_filterInput);
 }
 
 FilterBar::~FilterBar()
index aaeb5812060e18d7f2a38d494d9afa3afc4eedcd..97d743438440efdcf33ab71f2ba9913889d27bdc 100644 (file)
 #include "dolphindebug.h"
 
 #include <KRun>
+#include <KWindowSystem>
 
 #include <QApplication>
 #include <QIcon>
+#include <QDBusInterface>
+#include <QDBusConnectionInterface>
 
 QList<QUrl> Dolphin::validateUris(const QStringList& uriList)
 {
@@ -49,7 +52,7 @@ QUrl Dolphin::homeUrl()
 
 void Dolphin::openNewWindow(const QList<QUrl> &urls, QWidget *window, const OpenNewWindowFlags &flags)
 {
-    QString command = QStringLiteral("dolphin");
+    QString command = QStringLiteral("dolphin --new-window");
 
     if (flags.testFlag(OpenNewWindowFlag::Select)) {
         command.append(QLatin1String(" --select"));
@@ -58,6 +61,84 @@ void Dolphin::openNewWindow(const QList<QUrl> &urls, QWidget *window, const Open
     if (!urls.isEmpty()) {
         command.append(QLatin1String(" %U"));
     }
+    KRun::run(
+        command,
+        urls,
+        window,
+        QApplication::applicationDisplayName(),
+        QApplication::windowIcon().name()
+    );
+}
+
+bool Dolphin::attachToExistingInstance(const QList<QUrl>& urls, bool openFiles, bool splitView, const QString& preferredService)
+{
+    if (KWindowSystem::isPlatformWayland()) {
+        // TODO: once Wayland clients can raise or activate themselves remove this conditional
+        return false;
+    }
+
+    const QStringList services = 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 = QStringLiteral("-") + QString::number(QCoreApplication::applicationPid());
+    QVector<QPair<QSharedPointer<QDBusInterface>, QStringList>> dolphinServices;
+    if (!preferredService.isEmpty()) {
+        QSharedPointer<QDBusInterface> preferred(
+            new QDBusInterface(preferredService,
+            QStringLiteral("/dolphin/Dolphin_1"),
+            QStringLiteral("org.kde.dolphin.MainWindow"))
+        );
+        if (preferred->isValid()) {
+            dolphinServices.append(qMakePair(preferred, QStringList() ));
+        }
+    }
+
+    // find all dolphin instances
+    for (const QString& service : services) {
+        if (service.startsWith(pattern) && !service.endsWith(myPid)) {
+            // Check if instance can handle our URLs
+            QSharedPointer<QDBusInterface> instance(
+                new QDBusInterface(service,
+                QStringLiteral("/dolphin/Dolphin_1"),
+                QStringLiteral("org.kde.dolphin.MainWindow"))
+            );
+            if (!instance->isValid()) {
+                continue;
+            }
+            dolphinServices.append(qMakePair(instance, QStringList()));
+        }
+    }
+
+    if (dolphinServices.isEmpty()) {
+        return false;
+    }
+
+    QStringList newUrls;
+
+    // check to see if any instances already have any of the given URLs open
+    for (const QString& url : QUrl::toStringList(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);
+                    urlFound = true;
+                    break;
+            }
+        }
+        if (!urlFound) {
+            newUrls.append(url);
+        }
+    }
+    dolphinServices.front().second << newUrls;
 
-    KRun::run(command, urls, window, qApp->applicationDisplayName(), qApp->windowIcon().name());
+    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"));
+        }
+    }
+    return true;
 }
index e3c4a1cca10ee508ef4d3fb05eed65f16250cb81..16305eafebbaec9a63b64b94ab21fbac155f4098 100644 (file)
@@ -42,6 +42,12 @@ namespace Dolphin {
      */
     void openNewWindow(const QList<QUrl> &urls = {}, QWidget *window = nullptr, const OpenNewWindowFlags &flags = OpenNewWindowFlag::None);
 
+    /**
+     * Attaches URLs to an existing Dolphin instance if possible.
+     * Returns true if URLs were successfully attached
+     */
+    bool attachToExistingInstance(const QList<QUrl>& urls, bool openFiles, bool splitView, const QString& preferredService = QString());
+
     /**
      * TODO: Move this somewhere global to all KDE apps, not just Dolphin
      */
index fc14c79c1c270ac30bfca4422597b4147cd078b3..8145a00f11ba1f2fc22bd9f7aca0679c3ddfdfdb 100644 (file)
@@ -68,16 +68,16 @@ KFileItemModel::KFileItemModel(QObject* parent) :
     }
 
     connect(m_dirLister, &KFileItemModelDirLister::started, this, &KFileItemModel::directoryLoadingStarted);
-    connect(m_dirLister, static_cast<void(KFileItemModelDirLister::*)()>(&KFileItemModelDirLister::canceled), this, &KFileItemModel::slotCanceled);
-    connect(m_dirLister, static_cast<void(KFileItemModelDirLister::*)(const QUrl&)>(&KFileItemModelDirLister::completed), this, &KFileItemModel::slotCompleted);
+    connect(m_dirLister, QOverload<>::of(&KCoreDirLister::canceled), this, &KFileItemModel::slotCanceled);
+    connect(m_dirLister, QOverload<const QUrl&>::of(&KCoreDirLister::completed), this, &KFileItemModel::slotCompleted);
     connect(m_dirLister, &KFileItemModelDirLister::itemsAdded, this, &KFileItemModel::slotItemsAdded);
     connect(m_dirLister, &KFileItemModelDirLister::itemsDeleted, this, &KFileItemModel::slotItemsDeleted);
     connect(m_dirLister, &KFileItemModelDirLister::refreshItems, this, &KFileItemModel::slotRefreshItems);
-    connect(m_dirLister, static_cast<void(KFileItemModelDirLister::*)()>(&KFileItemModelDirLister::clear), this, &KFileItemModel::slotClear);
+    connect(m_dirLister, QOverload<>::of(&KCoreDirLister::clear), this, &KFileItemModel::slotClear);
     connect(m_dirLister, &KFileItemModelDirLister::infoMessage, this, &KFileItemModel::infoMessage);
     connect(m_dirLister, &KFileItemModelDirLister::errorMessage, this, &KFileItemModel::errorMessage);
     connect(m_dirLister, &KFileItemModelDirLister::percent, this, &KFileItemModel::directoryLoadingProgress);
-    connect(m_dirLister, static_cast<void(KFileItemModelDirLister::*)(const QUrl&, const QUrl&)>(&KFileItemModelDirLister::redirection), this, &KFileItemModel::directoryRedirection);
+    connect(m_dirLister, QOverload<const QUrl&, const QUrl&>::of(&KCoreDirLister::redirection), this, &KFileItemModel::directoryRedirection);
     connect(m_dirLister, &KFileItemModelDirLister::urlIsFileError, this, &KFileItemModel::urlIsFileError);
 
     // Apply default roles that should be determined
@@ -304,9 +304,9 @@ QString KFileItemModel::roleDescription(const QByteArray& role) const
         int count = 0;
         const RoleInfoMap* map = rolesInfoMap(count);
         for (int i = 0; i < count; ++i) {
-           if (!map[i].roleTranslation) {
-                   continue;
-           }
+            if (!map[i].roleTranslation) {
+                continue;
+            }
             description.insert(map[i].role, i18nc(map[i].roleTranslationContext, map[i].roleTranslation));
         }
     }
@@ -2297,38 +2297,40 @@ void KFileItemModel::emitSortProgress(int resolvedCount)
 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  },
-        { "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 },
+    //  |         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 },
     };
 
     count = sizeof(rolesInfoMap) / sizeof(RoleInfoMap);
index d15cfebc1b8cf14231eedd2e010d84e4f7a818f7..0f7926aae1b432f5238999a03cc7247b0015d996 100644 (file)
@@ -288,7 +288,7 @@ private:
         // User visible roles available with Baloo:
         CommentRole, TagsRole, RatingRole, WidthRole, HeightRole, ImageDateTimeRole, OrientationRole,
         WordCountRole, TitleRole, LineCountRole, ArtistRole, GenreRole, AlbumRole, DurationRole, TrackRole, ReleaseYearRole,
-        BitrateRole, OriginUrlRole,
+        BitrateRole, OriginUrlRole, AspectRatioRole, FrameRateRole,
         // Non-visible roles:
         IsDirRole, IsLinkRole, IsHiddenRole, IsExpandedRole, IsExpandableRole, ExpandedParentsCountRole,
         // Mandatory last entry:
index d3dbeb35ccdd369b36abd3488728c64f8df006c3..6fb6a5132964de116cfb64cff1a025ffb6946e0c 100644 (file)
@@ -231,6 +231,9 @@ bool KItemListController::keyPressEvent(QKeyEvent* event)
     const bool shiftPressed = event->modifiers() & Qt::ShiftModifier;
     const bool controlPressed = event->modifiers() & Qt::ControlModifier;
     const bool shiftOrControlPressed = shiftPressed || controlPressed;
+    const bool navigationPressed = key == Qt::Key_Home || key == Qt::Key_End  ||
+                                   key == Qt::Key_Up   || key == Qt::Key_Down ||
+                                   key == Qt::Key_Left || key == Qt::Key_Right;
 
     const int itemCount = m_model->count();
 
@@ -246,11 +249,8 @@ bool KItemListController::keyPressEvent(QKeyEvent* event)
         }
     }
 
-    const bool selectSingleItem = m_selectionBehavior != NoSelection &&
-                                  itemCount == 1 &&
-                                  (key == Qt::Key_Home || key == Qt::Key_End  ||
-                                   key == Qt::Key_Up   || key == Qt::Key_Down ||
-                                   key == Qt::Key_Left || key == Qt::Key_Right);
+    const bool selectSingleItem = m_selectionBehavior != NoSelection && itemCount == 1 && navigationPressed;
+
     if (selectSingleItem) {
         const int current = m_selectionManager->currentItem();
         m_selectionManager->setSelected(current);
@@ -458,8 +458,12 @@ bool KItemListController::keyPressEvent(QKeyEvent* event)
             }
             break;
         }
+    }
 
-        m_view->scrollToItem(index);
+    if (navigationPressed) {
+        if (index < m_view->firstVisibleIndex() || index > m_view->lastVisibleIndex()) {
+            m_view->scrollToItem(index);
+        }
     }
     return true;
 }
index 4fb3f8f9834143e61fdf4806199a5f8f11e24b93..b3bbcaa41123cb7b1fcaa0996b5876d9296148d1 100644 (file)
 #include "kstandarditemmodel.h"
 
 KStandardItem::KStandardItem(KStandardItem* parent) :
-    m_parent(parent),
-    m_children(),
+    QObject(parent),
     m_model(nullptr),
     m_data()
 {
 }
 
 KStandardItem::KStandardItem(const QString& text, KStandardItem* parent) :
-    m_parent(parent),
-    m_children(),
+    QObject(parent),
     m_model(nullptr),
     m_data()
 {
@@ -38,8 +36,7 @@ KStandardItem::KStandardItem(const QString& text, KStandardItem* parent) :
 }
 
 KStandardItem::KStandardItem(const QString& icon, const QString& text, KStandardItem* parent) :
-    m_parent(parent),
-    m_children(),
+    QObject(parent),
     m_model(nullptr),
     m_data()
 {
@@ -47,14 +44,6 @@ KStandardItem::KStandardItem(const QString& icon, const QString& text, KStandard
     setText(text);
 }
 
-KStandardItem::KStandardItem(const KStandardItem& item) :
-    m_parent(item.m_parent),
-    m_children(item.m_children),
-    m_model(item.m_model),
-    m_data(item.m_data)
-{
-}
-
 KStandardItem::~KStandardItem()
 {
 }
@@ -123,17 +112,6 @@ QVariant KStandardItem::dataValue(const QByteArray& role) const
     return m_data[role];
 }
 
-void KStandardItem::setParent(KStandardItem* parent)
-{
-    // TODO: not implemented yet
-    m_parent = parent;
-}
-
-KStandardItem* KStandardItem::parent() const
-{
-    return m_parent;
-}
-
 void KStandardItem::setData(const QHash<QByteArray, QVariant>& values)
 {
     const QHash<QByteArray, QVariant> previous = m_data;
@@ -146,11 +124,6 @@ QHash<QByteArray, QVariant> KStandardItem::data() const
     return m_data;
 }
 
-QList<KStandardItem*> KStandardItem::children() const
-{
-    return m_children;
-}
-
 void KStandardItem::onDataValueChanged(const QByteArray& role,
                                        const QVariant& current,
                                        const QVariant& previous)
index fec197c06ff34dbadbbf5a8b9d6f7da9ae33ed38..ad3452d774ba36ac3f565324f740d0084916a09e 100644 (file)
@@ -24,7 +24,7 @@
 
 #include <QByteArray>
 #include <QHash>
-#include <QList>
+#include <QObject>
 #include <QVariant>
 
 class KStandardItemModel;
@@ -36,14 +36,13 @@ class KStandardItemModel;
  * used roles. It is possible to assign values for custom
  * roles by using setDataValue().
  */
-class DOLPHIN_EXPORT KStandardItem
+class DOLPHIN_EXPORT KStandardItem : public QObject
 {
-
+    Q_OBJECT
 public:
     explicit KStandardItem(KStandardItem* parent = nullptr);
     explicit KStandardItem(const QString& text, KStandardItem* parent = nullptr);
     KStandardItem(const QString& icon, const QString& text, KStandardItem* parent = nullptr);
-    KStandardItem(const KStandardItem& item);
     virtual ~KStandardItem();
 
     /**
@@ -70,14 +69,9 @@ public:
     void setDataValue(const QByteArray& role, const QVariant& value);
     QVariant dataValue(const QByteArray& role) const;
 
-    void setParent(KStandardItem* parent);
-    KStandardItem* parent() const;
-
     void setData(const QHash<QByteArray, QVariant>& values);
     QHash<QByteArray, QVariant> data() const;
 
-    QList<KStandardItem*> children() const;
-
 protected:
     virtual void onDataValueChanged(const QByteArray& role,
                                     const QVariant& current,
@@ -87,8 +81,6 @@ protected:
                                const QHash<QByteArray, QVariant>& previous);
 
 private:
-    KStandardItem* m_parent;
-    QList<KStandardItem*> m_children;
     KStandardItemModel* m_model;
 
     QHash<QByteArray, QVariant> m_data;
index f56f68ac54007b826d458ab53e603a5c688c4e91..15c01726fa8b972775d71880bee0ca4cc7298f2d 100644 (file)
@@ -1225,7 +1225,7 @@ void KStandardItemListWidget::updateIconsLayoutTextCache()
                 textInfo->staticText.setText(elidedText);
                 requiredWidth = m_customizedFontMetrics.width(elidedText);
             } else if (role == "rating") {
-               // Use the width of the rating pixmap, because the rating text is empty.
+                // Use the width of the rating pixmap, because the rating text is empty.
                 requiredWidth = m_rating.width();
             }
         }
index 220c0ebc3329071c3dae677305b45ebf0cbedfbd..c8102e42114d74d828dabee9c305311067ebd16a 100644 (file)
@@ -80,7 +80,7 @@ protected:
 };
 
 /**
- * @brief Itemlist widget implementation for KStandardItemView and KStandardItemModel.
+ * @brief Itemlist widget implementation for KStandardItemListView and KStandardItemModel.
  */
 class DOLPHIN_EXPORT KStandardItemListWidget : public KItemListWidget
 {
index a4d42b5715a73162b25a090ae35125ca73644c55..d7307c0bdd40d56030fa0adc1ba047739818b7dd 100644 (file)
@@ -112,7 +112,7 @@ void KStandardItemModel::removeItem(int index)
 
         onItemRemoved(index, item);
 
-        delete item;
+        item->deleteLater();
         item = nullptr;
 
         emit itemsRemoved(KItemRangeList() << KItemRange(index, 1));
index 6685a2038fa0366fc822d1432d8ac1b45167d2dd..8643d2e9e0ae34ddc88873862780caeb49d99cfa 100644 (file)
@@ -29,7 +29,7 @@
 class KStandardItem;
 
 /**
- * @brief Model counterpart for KStandardItemView.
+ * @brief Model counterpart for KStandardItemListView.
  *
  * Allows to add items to the model in an easy way by the
  * class KStandardItem.
index 0eedf180656515769b83796d98c9b38ec378e0f2..f3671540dd89c4e6d49fbfdfe18404d978d44c4b 100644 (file)
@@ -56,18 +56,34 @@ QHash<QByteArray, QVariant> KBalooRolesProvider::roleValues(const Baloo::File& f
 {
     QHash<QByteArray, QVariant> values;
 
-    QMapIterator<KFileMetaData::Property::Property, QVariant> it(file.properties());
-    while (it.hasNext()) {
-        it.next();
+    using entry = std::pair<const KFileMetaData::Property::Property&, const QVariant&>;
+
+    const auto& propMap = file.properties();
+    auto rangeBegin = propMap.constKeyValueBegin();
+
+    while (rangeBegin != propMap.constKeyValueEnd()) {
+        auto key = (*rangeBegin).first;
+        const KFileMetaData::PropertyInfo propertyInfo(key);
+        const QByteArray role = roleForProperty(propertyInfo.name());
+
+        auto rangeEnd = std::find_if(rangeBegin, propMap.constKeyValueEnd(),
+            [key](const entry& e) { return e.first != key; });
 
-        const KFileMetaData::PropertyInfo pi(it.key());
-        const QString property = pi.name();
-        const QByteArray role = roleForProperty(property);
         if (role.isEmpty() || !roles.contains(role)) {
+            rangeBegin = rangeEnd;
             continue;
         }
 
-        values.insert(role, pi.formatAsDisplayString(it.value()));
+        auto distance = std::distance(rangeBegin, rangeEnd);
+        if (distance > 1) {
+            QVariantList list;
+            list.reserve(static_cast<int>(distance));
+            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));
+        }
+        rangeBegin = rangeEnd;
     }
 
     KFileMetaData::UserMetaData md(file.path());
@@ -120,6 +136,8 @@ KBalooRolesProvider::KBalooRolesProvider() :
         { "album",    "album" },
         { "duration",      "duration" },
         { "bitRate", "bitrate" },
+        { "aspectRatio", "aspectRatio" },
+        { "frameRate", "frameRate" },
         { "releaseYear",    "releaseYear" },
         { "trackNumber",   "track" },
         { "originUrl", "originUrl" }
index e79a9f9d144483a2b023499490fffd0f9be7e78b..eb6f1de767c48a44f964dcd8bb8704ed9a85bf34 100644 (file)
@@ -107,6 +107,23 @@ void KItemListRoleEditor::keyPressEvent(QKeyEvent* event)
         }
         break;
     }
+    case Qt::Key_Home:
+    case Qt::Key_End: {
+        if (event->modifiers() == Qt::NoModifier || event->modifiers() == Qt::ShiftModifier) {
+            const QTextCursor::MoveOperation op = event->key() == Qt::Key_Home
+                                                ? QTextCursor::Start
+                                                : QTextCursor::End;
+            const QTextCursor::MoveMode mode = event->modifiers() == Qt::NoModifier
+                                             ? QTextCursor::MoveAnchor
+                                             : QTextCursor::KeepAnchor;
+            QTextCursor cursor = textCursor();
+            cursor.movePosition(op, mode);
+            setTextCursor(cursor);
+            event->accept();
+            return;
+        }
+        break;
+    }
     default:
         break;
     }
index 08405d0073b963d6ea187bd8e1aaf705e4c51903..639dc32efa6694a8fdf00142935552200c0512e5 100644 (file)
 
 #include <QApplication>
 #include <QCommandLineParser>
+#include <QDBusConnection>
+#include <QDBusInterface>
+#include <QDBusAbstractInterface>
+#include <QDBusConnectionInterface>
 
 #ifndef Q_OS_WIN
 #include <unistd.h>
@@ -122,33 +126,42 @@ extern "C" Q_DECL_EXPORT int kdemain(int argc, char **argv)
     parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("select"), i18nc("@info:shell", "The files and folders passed as arguments "
                                                                                         "will be selected.")));
     parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("split"), i18nc("@info:shell", "Dolphin will get started with a split view.")));
+    parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("new-window"), i18nc("@info:shell", "Dolphin will explicitly open in a new window.")));
     parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("daemon"), i18nc("@info:shell", "Start Dolphin Daemon (only required for DBus Interface)")));
     parser.addPositionalArgument(QStringLiteral("+[Url]"), i18nc("@info:shell", "Document to open"));
 
     parser.process(app);
     aboutData.processCommandLine(&parser);
 
+    const bool splitView = parser.isSet(QStringLiteral("split")) || GeneralSettings::splitView();
+    const bool openFiles = parser.isSet(QStringLiteral("select"));
+    const QStringList args = parser.positionalArguments();
+    QList<QUrl> urls = Dolphin::validateUris(args);
+
     if (parser.isSet(QStringLiteral("daemon"))) {
         return app.exec();
     }
 
-    const QStringList args = parser.positionalArguments();
-    QList<QUrl> urls = Dolphin::validateUris(args);
-
     if (urls.isEmpty()) {
         // We need at least one URL to open Dolphin
         urls.append(Dolphin::homeUrl());
     }
 
-    const bool splitView = parser.isSet(QStringLiteral("split")) || GeneralSettings::splitView();
     if (splitView && urls.size() < 2) {
         // Split view does only make sense if we have at least 2 URLs
         urls.append(urls.last());
     }
 
+    if (!parser.isSet(QStringLiteral("new-window"))) {
+        if (Dolphin::attachToExistingInstance(urls, openFiles, splitView)) {
+            // Successfully attached to existing instance of Dolphin
+            return 0;
+        }
+    }
+
     DolphinMainWindow* mainWindow = new DolphinMainWindow();
 
-    if (parser.isSet(QStringLiteral("select"))) {
+    if (openFiles) {
         mainWindow->openFiles(urls, splitView);
     } else {
         mainWindow->openDirectories(urls, splitView);
index e5bfed010af841fac0c9d8b7aab7401626c5f7c6..e75eeab997fefa306ce3e6e66625c3bb1f0bb8f0 100755 (executable)
@@ -22,6 +22,7 @@ Name[it]=Dolphin
 Name[ja]=Dolphin
 Name[ko]=Dolphin
 Name[lt]=Dolphin
+Name[ml]=ഡോള്‍ഫിന്‍
 Name[nb]=Dolphin
 Name[nl]=Dolphin
 Name[nn]=Dolphin
@@ -72,6 +73,7 @@ GenericName[it]=Gestore dei file
 GenericName[ja]=ファイルマネージャ
 GenericName[ko]=파일 관리자
 GenericName[lt]=Failų tvarkyklė
+GenericName[ml]=ഫയല്‍ മാനേജര്‍
 GenericName[nb]=Filbehandler
 GenericName[nl]=Bestandsbeheerder
 GenericName[nn]=Filhandsamar
@@ -97,4 +99,5 @@ Terminal=false
 MimeType=inode/directory;
 InitialPreference=10
 X-DBUS-ServiceName=org.kde.dolphin
+X-KDE-Shortcuts=Meta+E
 StartupWMClass=dolphin
diff --git a/src/panels/information/filemetadataconfigurationdialog.cpp b/src/panels/information/filemetadataconfigurationdialog.cpp
deleted file mode 100644 (file)
index f3ca819..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-/***************************************************************************
- *   Copyright (C) 2010 by Peter Penz <peter.penz19@gmail.com>             *
- *                                                                         *
- *   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 "filemetadataconfigurationdialog.h"
-
-#include <Baloo/FileMetaDataConfigWidget>
-#include <KConfigGroup>
-#include <KLocalizedString>
-#include <KSharedConfig>
-#include <KWindowConfig>
-
-#include <QDialogButtonBox>
-#include <QLabel>
-#include <QPushButton>
-#include <QVBoxLayout>
-
-FileMetaDataConfigurationDialog::FileMetaDataConfigurationDialog(QWidget* parent) :
-    QDialog(parent),
-    m_descriptionLabel(nullptr),
-    m_configWidget(nullptr)
-
-{
-    setWindowTitle(i18nc("@title:window", "Configure Shown Data"));
-    QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel);
-    QVBoxLayout *mainLayout = new QVBoxLayout;
-    setLayout(mainLayout);
-    QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok);
-    okButton->setDefault(true);
-    okButton->setShortcut(Qt::CTRL + Qt::Key_Return);
-    connect(buttonBox, &QDialogButtonBox::accepted, this, &FileMetaDataConfigurationDialog::slotAccepted);
-    connect(buttonBox, &QDialogButtonBox::rejected, this, &FileMetaDataConfigurationDialog::reject);
-    buttonBox->button(QDialogButtonBox::Ok)->setDefault(true);
-
-    m_descriptionLabel = new QLabel(i18nc("@label::textbox",
-                                          "Select which data should "
-                                          "be shown:"), this);
-    m_descriptionLabel->setWordWrap(true);
-
-    m_configWidget = new Baloo::FileMetaDataConfigWidget(this);
-
-    QWidget* mainWidget = new QWidget(this);
-    QVBoxLayout* topLayout = new QVBoxLayout(mainWidget);
-    topLayout->addWidget(m_descriptionLabel);
-    topLayout->addWidget(m_configWidget);
-    mainLayout->addWidget(mainWidget);
-    mainLayout->addWidget(buttonBox);
-
-
-    const KConfigGroup dialogConfig(KSharedConfig::openConfig(QStringLiteral("dolphinrc")),
-                                    "FileMetaDataConfigurationDialog");
-    KWindowConfig::restoreWindowSize(windowHandle(), dialogConfig);
-}
-
-FileMetaDataConfigurationDialog::~FileMetaDataConfigurationDialog()
-{
-    KConfigGroup dialogConfig(KSharedConfig::openConfig(QStringLiteral("dolphinrc")),
-                              "FileMetaDataConfigurationDialog");
-    KWindowConfig::saveWindowSize(windowHandle(), dialogConfig);
-}
-
-void FileMetaDataConfigurationDialog::setItems(const KFileItemList& items)
-{
-    m_configWidget->setItems(items);
-}
-
-KFileItemList FileMetaDataConfigurationDialog::items() const
-{
-    return m_configWidget->items();
-}
-
-void FileMetaDataConfigurationDialog::slotAccepted()
-{
-    m_configWidget->save();
-    accept();
-}
-
-void FileMetaDataConfigurationDialog::setDescription(const QString& description)
-{
-    m_descriptionLabel->setText(description);
-}
-
-QString FileMetaDataConfigurationDialog::description() const
-{
-    return m_descriptionLabel->text();
-}
-
diff --git a/src/panels/information/filemetadataconfigurationdialog.h b/src/panels/information/filemetadataconfigurationdialog.h
deleted file mode 100644 (file)
index 0435778..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/***************************************************************************
- *   Copyright (C) 2010 by Peter Penz <peter.penz19@gmail.com>             *
- *                                                                         *
- *   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 FILEMETADATACONFIGURATIONDIALOG_H
-#define FILEMETADATACONFIGURATIONDIALOG_H
-
-#include <QDialog>
-
-#include <KFileItem>
-#include <config-baloo.h>
-#ifndef HAVE_BALOO
-class KFileMetaDataConfigurationWidget;
-#else
-namespace Baloo {
-    class FileMetaDataConfigWidget;
-}
-#endif
-
-class QLabel;
-
-/**
- * @brief Dialog which allows to configure which meta data should be shown
- *        in the KFileMetaDataWidget.
- */
-class FileMetaDataConfigurationDialog : public QDialog
-{
-    Q_OBJECT
-
-public:
-    explicit FileMetaDataConfigurationDialog(QWidget* parent = nullptr);
-    ~FileMetaDataConfigurationDialog() override;
-
-    /**
-     * Sets the items, for which the visibility of the meta data should
-     * be configured. Note that the visibility of the meta data is not
-     * bound to the items itself, the items are only used to determine
-     * which meta data should be configurable. For example when a JPEG image
-     * is set as item, it will be configurable which EXIF data should be
-     * shown. If an audio file is set as item, it will be configurable
-     * whether the artist, album name, ... should be shown.
-     */
-    void setItems(const KFileItemList& items);
-    KFileItemList items() const;
-
-    /**
-     * Sets the description that is shown above the list
-     * of meta data. Per default the translated text for
-     * "Select which data should be shown." is set.
-     */
-    void setDescription(const QString& description);
-    QString description() const;
-
-protected slots:
-    void slotAccepted();
-private:
-    QLabel* m_descriptionLabel;
-    Baloo::FileMetaDataConfigWidget* m_configWidget;
-};
-
-#endif
index cd8b6b38d354629a2f60b3db9747720c16427087..9a0358df07f255fc59659b9d90690ff1f12b6100 100644 (file)
@@ -36,7 +36,6 @@
 #include <QMenu>
 
 #include "dolphin_informationpanelsettings.h"
-#include "filemetadataconfigurationdialog.h"
 
 InformationPanel::InformationPanel(QWidget* parent) :
     Panel(parent),
@@ -168,7 +167,8 @@ void InformationPanel::contextMenuEvent(QContextMenuEvent* event)
     Panel::contextMenuEvent(event);
 }
 
-void InformationPanel::showContextMenu(const QPoint &pos) {
+void InformationPanel::showContextMenu(const QPoint &pos)
+{
     QMenu popup(this);
 
     QAction* previewAction = popup.addAction(i18nc("@action:inmenu", "Preview"));
@@ -178,6 +178,9 @@ void InformationPanel::showContextMenu(const QPoint &pos) {
 
     QAction* configureAction = popup.addAction(i18nc("@action:inmenu", "Configure..."));
     configureAction->setIcon(QIcon::fromTheme(QStringLiteral("configure")));
+    if (m_inConfigurationMode) {
+        configureAction->setEnabled(false);
+    }
 
     QAction* dateformatAction = popup.addAction(i18nc("@action:inmenu", "Condensed Date"));
     dateformatAction->setIcon(QIcon::fromTheme(QStringLiteral("change-date-symbolic")));
@@ -185,7 +188,8 @@ void InformationPanel::showContextMenu(const QPoint &pos) {
     dateformatAction->setChecked(InformationPanelSettings::dateFormat() == static_cast<int>(Baloo::DateFormats::ShortFormat));
 
     popup.addSeparator();
-    foreach (QAction* action, customContextMenuActions()) {
+    const auto actions = customContextMenuActions();
+    for (QAction *action : actions) {
         popup.addAction(action);
     }
 
@@ -201,13 +205,8 @@ void InformationPanel::showContextMenu(const QPoint &pos) {
         InformationPanelSettings::setPreviewsShown(isChecked);
         m_content->refreshPreview();
     } else if (action == configureAction) {
-        FileMetaDataConfigurationDialog* dialog = new FileMetaDataConfigurationDialog(this);
-        dialog->setDescription(i18nc("@label::textbox",
-                                     "Select which data should be shown in the information panel:"));
-        dialog->setItems(m_content->items());
-        dialog->setAttribute(Qt::WA_DeleteOnClose);
-        dialog->show();
-        connect(dialog, &FileMetaDataConfigurationDialog::destroyed, m_content, &InformationPanelContent::refreshMetaData);
+        m_inConfigurationMode = true;
+        m_content->configureShownProperties();
     }
     if (action == dateformatAction) {
         int dateFormat = static_cast<int>(isChecked ? Baloo::DateFormats::ShortFormat : Baloo::DateFormats::LongFormat);
@@ -311,7 +310,7 @@ void InformationPanel::slotFilesAdded(const QString& directory)
 
 void InformationPanel::slotFilesChanged(const QStringList& files)
 {
-    foreach (const QString& fileName, files) {
+    for (const QString& fileName : files) {
         if (m_shownUrl == QUrl::fromLocalFile(fileName)) {
             showItemInfo();
             break;
@@ -321,7 +320,7 @@ void InformationPanel::slotFilesChanged(const QStringList& files)
 
 void InformationPanel::slotFilesRemoved(const QStringList& files)
 {
-    foreach (const QString& fileName, files) {
+    for (const QString& fileName : files) {
         if (m_shownUrl == QUrl::fromLocalFile(fileName)) {
             // the currently shown item has been removed, show
             // the parent directory as fallback
@@ -410,6 +409,7 @@ void InformationPanel::init()
 
     m_content = new InformationPanelContent(this);
     connect(m_content, &InformationPanelContent::urlActivated, this, &InformationPanel::urlActivated);
+    connect(m_content, &InformationPanelContent::configurationFinished, this, [this]() { m_inConfigurationMode = false; });
 
     QVBoxLayout* layout = new QVBoxLayout(this);
     layout->setContentsMargins(0, 0, 0, 0);
index f63af1e53ae70a57d9386c36b4436481de9efde7..321827c5bb09551186157273ae94d6ba1475d3eb 100644 (file)
@@ -161,6 +161,7 @@ private:
     KIO::Job* m_folderStatJob;
 
     InformationPanelContent* m_content;
+    bool m_inConfigurationMode = false;
 };
 
 #endif // INFORMATIONPANEL_H
index 0eaa125f11906ce015180fd89be0aee882ba5177..5b7dbbfe90464cb0c2de03fa05c68e57560bb192 100644 (file)
@@ -40,6 +40,7 @@
 #include <Phonon/MediaObject>
 
 #include <QLabel>
+#include <QDialogButtonBox>
 #include <QScrollArea>
 #include <QTextLayout>
 #include <QTimer>
@@ -107,18 +108,31 @@ InformationPanelContent::InformationPanelContent(QWidget* parent) :
     m_metaDataWidget->setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
     m_metaDataWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
 
-    // Encapsulate the MetaDataWidget inside a container that has a dummy widget
-    // at the bottom. This prevents that the meta data widget gets vertically stretched
-    // in the case where the height of m_metaDataArea > m_metaDataWidget.
-    QWidget* metaDataWidgetContainer = new QWidget(parent);
-    QVBoxLayout* containerLayout = new QVBoxLayout(metaDataWidgetContainer);
-    containerLayout->setContentsMargins(0, 0, 0, 0);
-    containerLayout->setSpacing(0);
-    containerLayout->addWidget(m_metaDataWidget);
-    containerLayout->addStretch();
+    // Configuration
+    m_configureLabel = new QLabel(i18nc("@label::textbox",
+                                        "Select which data should be shown:"), this);
+    m_configureLabel->setWordWrap(true);
+    m_configureLabel->setVisible(false);
+
+    m_configureButtons = new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Cancel);
+    m_configureButtons->setVisible(false);
+    connect(m_configureButtons, &QDialogButtonBox::accepted, this, [this]() {
+                m_metaDataWidget->setConfigurationMode(Baloo::ConfigurationMode::Accept);
+                m_configureButtons->setVisible(false);
+                m_configureLabel->setVisible(false);
+                emit configurationFinished();
+            }
+    );
+    connect(m_configureButtons, &QDialogButtonBox::rejected, this, [this]() {
+                m_metaDataWidget->setConfigurationMode(Baloo::ConfigurationMode::Cancel);
+                m_configureButtons->setVisible(false);
+                m_configureLabel->setVisible(false);
+                emit configurationFinished();
+            }
+    );
 
     m_metaDataArea = new QScrollArea(parent);
-    m_metaDataArea->setWidget(metaDataWidgetContainer);
+    m_metaDataArea->setWidget(m_metaDataWidget);
     m_metaDataArea->setWidgetResizable(true);
     m_metaDataArea->setFrameShape(QFrame::NoFrame);
 
@@ -129,7 +143,9 @@ InformationPanelContent::InformationPanelContent(QWidget* parent) :
     layout->addWidget(m_phononWidget);
     layout->addWidget(m_nameLabel);
     layout->addWidget(new KSeparator());
+    layout->addWidget(m_configureLabel);
     layout->addWidget(m_metaDataArea);
+    layout->addWidget(m_configureButtons);
 
     m_placesItemModel = new PlacesItemModel(this);
 }
@@ -147,7 +163,8 @@ void InformationPanelContent::showItem(const KFileItem& item)
     refreshMetaData();
 }
 
-void InformationPanelContent::refreshPreview() {
+void InformationPanelContent::refreshPreview()
+{
     // If there is a preview job, kill it to prevent that we have jobs for
     // multiple items running, and thus a race condition (bug 250787).
     if (m_previewJob) {
@@ -209,12 +226,18 @@ void InformationPanelContent::refreshPreview() {
     }
 }
 
-void InformationPanelContent::refreshMetaData() {
-    if (m_metaDataWidget) {
-        m_metaDataWidget->setDateFormat(static_cast<Baloo::DateFormats>(InformationPanelSettings::dateFormat()));
-        m_metaDataWidget->show();
-        m_metaDataWidget->setItems(KFileItemList() << m_item);
-    }
+void InformationPanelContent::configureShownProperties()
+{
+    m_configureLabel->setVisible(true);
+    m_configureButtons->setVisible(true);
+    m_metaDataWidget->setConfigurationMode(Baloo::ConfigurationMode::ReStart);
+}
+
+void InformationPanelContent::refreshMetaData()
+{
+    m_metaDataWidget->setDateFormat(static_cast<Baloo::DateFormats>(InformationPanelSettings::dateFormat()));
+    m_metaDataWidget->show();
+    m_metaDataWidget->setItems(KFileItemList() << m_item);
 }
 
 void InformationPanelContent::showItems(const KFileItemList& items)
@@ -230,9 +253,7 @@ void InformationPanelContent::showItems(const KFileItemList& items)
     );
     setNameLabelText(i18ncp("@label", "%1 item selected", "%1 items selected", items.count()));
 
-    if (m_metaDataWidget) {
-        m_metaDataWidget->setItems(items);
-    }
+    m_metaDataWidget->setItems(items);
 
     m_phononWidget->hide();
 
@@ -297,7 +318,8 @@ void InformationPanelContent::markOutdatedPreview()
     m_preview->setPixmap(disabledPixmap);
 }
 
-KFileItemList InformationPanelContent::items() {
+KFileItemList InformationPanelContent::items()
+{
     return m_metaDataWidget->items();
 }
 
@@ -349,9 +371,7 @@ void InformationPanelContent::adjustWidgetSizes(int width)
 
     // The metadata widget also contains a text widget which may return
     // a large preferred width.
-    if (m_metaDataWidget) {
-        m_metaDataWidget->setMaximumWidth(maxWidth);
-    }
+    m_metaDataWidget->setMaximumWidth(maxWidth);
 
     // try to increase the preview as large as possible
     m_preview->setSizeHint(QSize(maxWidth, maxWidth));
index 83fb3d80babad03faa86de1f36112d2685eab8e5..43410ddfae9690cff3e5be39d9712eed09c56509 100644 (file)
@@ -32,6 +32,7 @@ class PhononWidget;
 class PixmapViewer;
 class PlacesItemModel;
 class QPixmap;
+class QDialogButtonBox;
 class QString;
 class QLabel;
 class QScrollArea;
@@ -40,13 +41,9 @@ namespace KIO {
   class PreviewJob;
 }
 
-#ifndef HAVE_BALOO
-class KFileMetaDataWidget;
-#else
 namespace Baloo {
     class FileMetaDataWidget;
 }
-#endif
 
 /**
  * @brief Manages the widgets that display the meta information
@@ -79,8 +76,14 @@ public:
      */
     void refreshPreview();
 
+    /**
+     * Switch the metadatawidget into configuration mode
+     */
+    void configureShownProperties();
+
 signals:
     void urlActivated( const QUrl& url );
+    void configurationFinished();
 
 public slots:
     /**
@@ -138,12 +141,10 @@ private:
     PixmapViewer* m_preview;
     PhononWidget* m_phononWidget;
     QLabel* m_nameLabel;
-#ifndef HAVE_BALOO
-    KFileMetaDataWidget* m_metaDataWidget;
-#else
     Baloo::FileMetaDataWidget* m_metaDataWidget;
-#endif
     QScrollArea* m_metaDataArea;
+    QLabel* m_configureLabel;
+    QDialogButtonBox* m_configureButtons;
 
     PlacesItemModel* m_placesItemModel;
 };
index 5828a37ddb44261145e600dba8f14f9757d7394c..311995ec2f81250e2b99f7947cb3b06fceda1b85 100644 (file)
@@ -37,7 +37,7 @@ PixmapViewer::PixmapViewer(QWidget* parent, Transition transition) :
     m_animation.setCurveShape(QTimeLine::LinearCurve);
 
     if (m_transition != NoTransition) {
-        connect(&m_animation, &QTimeLine::valueChanged, this, static_cast<void(PixmapViewer::*)()>(&PixmapViewer::update));
+        connect(&m_animation, &QTimeLine::valueChanged, this, QOverload<>::of(&PixmapViewer::update));
         connect(&m_animation, &QTimeLine::finished, this, &PixmapViewer::checkPendingPixmaps);
     }
 }
@@ -97,14 +97,14 @@ void PixmapViewer::paintEvent(QPaintEvent* event)
         const bool useOldPixmap = (m_transition == SizeTransition) &&
                                   (m_oldPixmap.width() > m_pixmap.width());
         const QPixmap& largePixmap = useOldPixmap ? m_oldPixmap : m_pixmap;
-       if (!largePixmap.isNull()) {
+        if (!largePixmap.isNull()) {
             const QPixmap scaledPixmap = largePixmap.scaled(scaledWidth,
                                                             scaledHeight,
                                                             Qt::IgnoreAspectRatio,
                                                             Qt::FastTransformation);
 
             style()->drawItemPixmap(&painter, rect(), Qt::AlignCenter, scaledPixmap);
-       }
+        }
     } else {
         style()->drawItemPixmap(&painter, rect(), Qt::AlignCenter, m_pixmap);
     }
index 4e99daf7a711b599269a772cba5406a30951d5b9..a2f0e06556769d122afad8176843dffefc2d28af 100644 (file)
@@ -93,8 +93,8 @@ bool PlacesPanel::urlChanged()
 void PlacesPanel::readSettings()
 {
     if (m_controller) {
-       const int delay = GeneralSettings::autoExpandFolders() ? 750 : -1;
-       m_controller->setAutoActivationDelay(delay);
+        const int delay = GeneralSettings::autoExpandFolders() ? 750 : -1;
+        m_controller->setAutoActivationDelay(delay);
     }
 }
 
index 9c41db9c585e5cf8a8bd441ee403ce9c0666254f..d846e5b6c44a7ef8487b26aa62ee6d54dc19f671 100644 (file)
@@ -49,7 +49,6 @@ DolphinSearchBox::DolphinSearchBox(QWidget* parent) :
     m_startedSearching(false),
     m_active(true),
     m_topLayout(nullptr),
-    m_searchLabel(nullptr),
     m_searchInput(nullptr),
     m_saveSearchAction(nullptr),
     m_optionsScrollArea(nullptr),
@@ -87,22 +86,22 @@ void DolphinSearchBox::setSearchPath(const QUrl& url)
     QFontMetrics metrics(m_fromHereButton->font());
     const int maxWidth = metrics.height() * 8;
 
-    QString location = url.fileName();
+    const QUrl cleanedUrl = url.adjusted(QUrl::RemoveUserInfo | QUrl::StripTrailingSlash);
+    QString location = cleanedUrl.fileName();
     if (location.isEmpty()) {
-        if (url.isLocalFile()) {
-            location = QStringLiteral("/");
-        } else {
-            location = url.scheme() + QLatin1String(" - ") + url.host();
-        }
+        location = cleanedUrl.toString(QUrl::PreferLocalFile);
+    }
+    if (m_fromHereButton->isChecked() && cleanedUrl.path() == QDir::homePath()) {
+        m_fromHereButton->setChecked(false);
+        m_everywhereButton->setChecked(true);
+    } else {
+        m_fromHereButton->setChecked(true);
+        m_everywhereButton->setChecked(false);
     }
 
     const QString elidedLocation = metrics.elidedText(location, Qt::ElideMiddle, maxWidth);
     m_fromHereButton->setText(i18nc("action:button", "From Here (%1)", elidedLocation));
-
-    const bool showSearchFromButtons = url.isLocalFile();
-    m_separator->setVisible(showSearchFromButtons);
-    m_fromHereButton->setVisible(showSearchFromButtons);
-    m_everywhereButton->setVisible(showSearchFromButtons);
+    m_fromHereButton->setToolTip(i18nc("action:button", "Limit search to '%1' and its subfolders", cleanedUrl.toString(QUrl::PreferLocalFile)));
 
     bool hasFacetsSupport = false;
 #ifdef HAVE_BALOO
@@ -138,9 +137,6 @@ QUrl DolphinSearchBox::urlForSearching() const
 
         QString encodedUrl;
         if (m_everywhereButton->isChecked()) {
-            // It is very unlikely, that the majority of Dolphins target users
-            // mean "the whole harddisk" instead of "my home folder" when
-            // selecting the "Everywhere" button.
             encodedUrl = QDir::homePath();
         } else {
             encodedUrl = m_searchPath.url();
@@ -358,11 +354,9 @@ void DolphinSearchBox::init()
     closeButton->setToolTip(i18nc("@info:tooltip", "Quit searching"));
     connect(closeButton, &QToolButton::clicked, this, &DolphinSearchBox::emitCloseRequest);
 
-    // Create search label
-    m_searchLabel = new QLabel(this);
-
     // Create search box
     m_searchInput = new QLineEdit(this);
+    m_searchInput->setPlaceholderText(i18n("Search..."));
     m_searchInput->installEventFilter(this);
     m_searchInput->setClearButtonEnabled(true);
     m_searchInput->setFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont));
@@ -384,7 +378,6 @@ void DolphinSearchBox::init()
     QHBoxLayout* searchInputLayout = new QHBoxLayout();
     searchInputLayout->setContentsMargins(0, 0, 0, 0);
     searchInputLayout->addWidget(closeButton);
-    searchInputLayout->addWidget(m_searchLabel);
     searchInputLayout->addWidget(m_searchInput);
 
     // Create "Filename" and "Content" button
@@ -402,13 +395,16 @@ void DolphinSearchBox::init()
 
     m_separator = new KSeparator(Qt::Vertical, this);
 
-    // Create "From Here" and "Everywhere"button
+    // Create "From Here" and "Your files" buttons
     m_fromHereButton = new QToolButton(this);
     m_fromHereButton->setText(i18nc("action:button", "From Here"));
     initButton(m_fromHereButton);
 
     m_everywhereButton = new QToolButton(this);
-    m_everywhereButton->setText(i18nc("action:button", "Everywhere"));
+    m_everywhereButton->setText(i18nc("action:button", "Your files"));
+    m_everywhereButton->setToolTip(i18nc("action:button", "Search in your home directory"));
+    m_everywhereButton->setIcon(QIcon::fromTheme(QStringLiteral("user-home")));
+    m_everywhereButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
     initButton(m_everywhereButton);
 
     QButtonGroup* searchLocationGroup = new QButtonGroup(this);
index c138cfe7f3fb35776923a033b02755151a0b3391..bb71049c746a6999aa92e10a56ea386a6943f057 100644 (file)
@@ -166,7 +166,6 @@ private:
 
     QVBoxLayout* m_topLayout;
 
-    QLabel* m_searchLabel;
     QLineEdit* m_searchInput;
     QAction* m_saveSearchAction;
     QScrollArea* m_optionsScrollArea;
index 6bddb861f03d80802c7926521f77af6fd789b791..f4da53c9df65095151eb2a73e06abb2724479c91 100644 (file)
 #include <KAuthorized>
 #include <KLocalizedString>
 #include <KWindowConfig>
+#include <KMessageBox>
 
 #include <QPushButton>
 
 DolphinSettingsDialog::DolphinSettingsDialog(const QUrl& url, QWidget* parent) :
     KPageDialog(parent),
-    m_pages()
+    m_pages(),
+    m_unsavedChanges(false)
 
 {
     const QSize minSize = minimumSize();
@@ -121,6 +123,7 @@ DolphinSettingsDialog::~DolphinSettingsDialog()
 void DolphinSettingsDialog::enableApply()
 {
     buttonBox()->button(QDialogButtonBox::Apply)->setEnabled(true);
+    m_unsavedChanges = true;
 }
 
 void DolphinSettingsDialog::applySettings()
@@ -139,6 +142,7 @@ void DolphinSettingsDialog::applySettings()
         settings->save();
     }
     buttonBox()->button(QDialogButtonBox::Apply)->setEnabled(false);
+    m_unsavedChanges = false;
 }
 
 void DolphinSettingsDialog::restoreDefaults()
@@ -148,6 +152,35 @@ void DolphinSettingsDialog::restoreDefaults()
     }
 }
 
+void DolphinSettingsDialog::closeEvent(QCloseEvent* event)
+{
+    if (!m_unsavedChanges) {
+        event->accept();
+        return;
+    }
+
+    const auto response = KMessageBox::warningYesNoCancel(this,
+                                        i18n("You have unsaved changes. Do you want to apply the changes or discard them?"),
+                                        i18n("Warning"),
+                                        KStandardGuiItem::save(),
+                                        KStandardGuiItem::discard(),
+                                        KStandardGuiItem::cancel());
+    switch (response) {
+        case KMessageBox::Yes:
+            applySettings();
+            Q_FALLTHROUGH();
+        case KMessageBox::No:
+            event->accept();
+            break;
+        case KMessageBox::Cancel:
+            event->ignore();
+            break;
+        default:
+            break;
+    }
+}
+
+
 SettingsPageBase *DolphinSettingsDialog::createTrashSettingsPage(QWidget *parent)
 {
     if (!KAuthorized::authorizeControlModule(QStringLiteral("kcmtrash.desktop"))) {
index 4c8768fde5ca473b0e67f643fc2b64805b12f77d..85871b12dafc5a53fcfd478bd6f52e015b0e1eb1 100644 (file)
@@ -48,10 +48,14 @@ private slots:
     void applySettings();
     void restoreDefaults();
 
+protected:
+    void closeEvent(QCloseEvent* event) override;
+
 private:
     static SettingsPageBase *createTrashSettingsPage(QWidget *parent);
 
     QList<SettingsPageBase*> m_pages;
+    bool m_unsavedChanges;
 };
 
 #endif
index 187442ef96edba6ea217701eba2743a126916794..30d71ff5456c52777710fc9f545817cbcb045da4 100644 (file)
@@ -85,7 +85,7 @@ PreviewsSettingsPage::PreviewsSettingsPage(QWidget* parent) :
     loadSettings();
 
     connect(m_listView, &QListView::clicked, this, &PreviewsSettingsPage::changed);
-    connect(m_remoteFileSizeBox, static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &PreviewsSettingsPage::changed);
+    connect(m_remoteFileSizeBox, QOverload<int>::of(&QSpinBox::valueChanged), this, &PreviewsSettingsPage::changed);
 }
 
 PreviewsSettingsPage::~PreviewsSettingsPage()
index 0df806e41a2611b06b345556e2e6b550098fe6ba..032c2df4dd3cdfd6af6cca0bfe51ddf643345dd6 100644 (file)
@@ -48,17 +48,17 @@ 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"));
-    connect(behaviorPage, &BehaviorSettingsPage::changed, this, static_cast<void(DolphinGeneralConfigModule::*)()>(&DolphinGeneralConfigModule::changed));
+    connect(behaviorPage, &BehaviorSettingsPage::changed, this, QOverload<>::of(&DolphinGeneralConfigModule::changed));
 
     // initialize 'Previews' tab
     PreviewsSettingsPage* previewsPage = new PreviewsSettingsPage(tabWidget);
     tabWidget->addTab(previewsPage, i18nc("@title:tab Previews settings", "Previews"));
-    connect(previewsPage, &PreviewsSettingsPage::changed, this, static_cast<void(DolphinGeneralConfigModule::*)()>(&DolphinGeneralConfigModule::changed));
+    connect(previewsPage, &PreviewsSettingsPage::changed, this, QOverload<>::of(&DolphinGeneralConfigModule::changed));
 
     // initialize 'Confirmations' tab
     ConfirmationsSettingsPage* confirmationsPage = new ConfirmationsSettingsPage(tabWidget);
     tabWidget->addTab(confirmationsPage,  i18nc("@title:tab Confirmations settings", "Confirmations"));
-    connect(confirmationsPage, &ConfirmationsSettingsPage::changed, this, static_cast<void(DolphinGeneralConfigModule::*)()>(&DolphinGeneralConfigModule::changed));
+    connect(confirmationsPage, &ConfirmationsSettingsPage::changed, this, QOverload<>::of(&DolphinGeneralConfigModule::changed));
 
     m_pages.append(behaviorPage);
     m_pages.append(previewsPage);
index 64c00713b1a7f2c441717396f5566c266c0a63cb..01163842bf40fb61ab082631fe555894358d14ce 100644 (file)
@@ -21,6 +21,7 @@ Name[it]=Impostazioni generali di Dolphin
 Name[ja]=Dolphin 全般
 Name[ko]=Dolphin 일반
 Name[lt]=Dolphin bendrieji
+Name[ml]=പൊതു സജ്ജീകരണങ്ങള്‍
 Name[nb]=Dolphin generelt
 Name[nl]=Dolphin algemeen
 Name[nn]=Generelt for Dolphin
@@ -65,6 +66,7 @@ Comment[it]=Questo servizio permette di configurare le impostazioni generali di
 Comment[ja]=Dolphin の全般的な設定を行います
 Comment[ko]=이 서비스는 일반 Dolphin 설정을 담당합니다.
 Comment[lt]=Ši tarnyba leidžia konfigūruoti Dolphin bendrąsias nuostatas.
+Comment[ml]=പൊതുവായ ഡോള്‍ഫിന്‍ സജ്ജീകരണങ്ങള്‍ ക്രമീകരിയ്ക്കാന്‍ ഈ സേവനം അനുവദിക്കുന്നു.
 Comment[nb]=Med denne tjenesten kan du sette opp generelle innstillinger for Dolphin.
 Comment[nl]=Met deze dienst kunt u algemene Dolphin-instellingen configureren.
 Comment[nn]=Denne tenesta lèt deg setja opp generelle innstillingar for Dolphin.
@@ -121,6 +123,7 @@ Name[it]=Generale
 Name[ja]=全般
 Name[ko]=일반
 Name[lt]=Bendra
+Name[ml]=പൊതുവായതു്
 Name[nb]=Generelt
 Name[nl]=Algemeen
 Name[nn]=Generelt
@@ -164,6 +167,7 @@ Comment[it]=Configura le impostazioni generali del gestore dei file
 Comment[ja]=ファイルマネージャの全般的な設定を行います
 Comment[ko]=일반 파일 관리자 설정
 Comment[lt]=Bendrųjų failų tvarkyklės nuostatų konfigūravimas
+Comment[ml]=ഫയൽ മാനേജറിന്റെ പൊതുവായ സജ്ജീകരണങ്ങള്‍ ക്രമീകരിയ്ക്കുക
 Comment[nb]=Sett opp generelle innstillinger for filbehandleren
 Comment[nl]=Algemene bestandsbeheerderinstellingen configureren
 Comment[nn]=Set opp generelle innstillingar for filhandsamaren
@@ -208,6 +212,7 @@ X-KDE-Keywords[it]=gestore dei file
 X-KDE-Keywords[ja]=ファイルマネージャ
 X-KDE-Keywords[ko]=파일 관리자
 X-KDE-Keywords[lt]=Failų tvarkyklė
+X-KDE-Keywords[ml]=ഫയൽ മാനേജർ 
 X-KDE-Keywords[nb]=filbehandler
 X-KDE-Keywords[nl]=bestandsbeheerder
 X-KDE-Keywords[nn]=filhandsamar
index cc073247a20ffae0c6547a8dcd02e383b6ca65ba..2cd9ff30ce97e7387b184e9c41f54062aa0af02e 100644 (file)
@@ -40,7 +40,7 @@ DolphinNavigationConfigModule::DolphinNavigationConfigModule(QWidget* parent, co
     topLayout->setContentsMargins(0, 0, 0, 0);
 
     m_navigation = new NavigationSettingsPage(this);
-    connect(m_navigation, &NavigationSettingsPage::changed, this, static_cast<void(DolphinNavigationConfigModule::*)()>(&DolphinNavigationConfigModule::changed));
+    connect(m_navigation, &NavigationSettingsPage::changed, this, QOverload<>::of(&DolphinNavigationConfigModule::changed));
     topLayout->addWidget(m_navigation, 0, nullptr);
 }
 
index ecc2e46d9f0e9abdf1dc77c277428bac93da641f..f6686fbc8c2eb7d109bcadcf01da1f20a88f8694 100644 (file)
@@ -21,6 +21,7 @@ Name[it]=Navigazione di Dolphin
 Name[ja]=Dolphin ナビゲーション
 Name[ko]=Dolphin 탐색
 Name[lt]=Dolphin navigacija
+Name[ml]=ഡോള്‍ഫിന്‍ നാവിഗേഷന്‍
 Name[nb]=Navigasjon i Dolphin
 Name[nl]=Dolphin-navigatie
 Name[nn]=Navigasjon i Dolphin
@@ -65,6 +66,7 @@ Comment[it]=Questo servizio permette di configurare la navigazione con Dolphin.
 Comment[ja]=Dolphin でのナビゲーションを設定します
 Comment[ko]=이 서비스는 Dolphin 탐색을 설정합니다.
 Comment[lt]=Ši tarnyba leidžia konfigūruoti Dolphin navigaciją.
+Comment[ml]=ഡോള്‍ഫിന്‍ നാവിഗേഷൻ ക്രമീകരിയ്ക്കാന്‍ ഈ സേവനം അനുവദിയ്ക്കുന്നു.
 Comment[nb]=Med denne tjenesten kan du sette opp navigasjon for Dolphin.
 Comment[nl]=Met deze dienst kunt u Dolphin-navigatie configureren.
 Comment[nn]=Denne tenesta lèt deg setja opp navigasjonen for Dolphin.
@@ -120,6 +122,7 @@ Name[it]=Navigazione
 Name[ja]=ナビゲーション
 Name[ko]=탐색
 Name[lt]=Navigacija
+Name[ml]=നാവിഗേഷന്‍
 Name[nb]=Navigasjon
 Name[nl]=Navigatie
 Name[nn]=Navigasjon
@@ -164,6 +167,7 @@ Comment[it]=Configura la navigazione col gestore dei file
 Comment[ja]=ファイルマネージャでのナビゲーションを設定します
 Comment[ko]=파일 관리자 탐색 설정
 Comment[lt]=Konfigūruokite failų tvarkyklės navigaciją
+Comment[ml]=ഫയല്‍ മാനേജർ നാവിഗേഷൻ ക്രമീകരിയ്ക്കുക
 Comment[nb]=Sett opp navigasjon i filbehandleren
 Comment[nl]=Bestandsbeheerdernavigatie configureren
 Comment[nn]=Set opp navigasjonen i filhandsamaren
@@ -209,6 +213,7 @@ X-KDE-Keywords[it]=gestore dei file
 X-KDE-Keywords[ja]=ファイルマネージャ
 X-KDE-Keywords[ko]=파일 관리자
 X-KDE-Keywords[lt]=Failų tvarkyklė
+X-KDE-Keywords[ml]=ഫയൽ മാനേജർ 
 X-KDE-Keywords[nb]=filbehandler
 X-KDE-Keywords[nl]=bestandsbeheerder
 X-KDE-Keywords[nn]=filhandsamar
index 91f4fd121b24abac1cc8cf9d4f31197da8278db3..94494c184b38291118015a161e4182db1e1faefd 100644 (file)
@@ -40,7 +40,7 @@ DolphinServicesConfigModule::DolphinServicesConfigModule(QWidget* parent, const
     topLayout->setContentsMargins(0, 0, 0, 0);
 
     m_services = new ServicesSettingsPage(this);
-    connect(m_services, &ServicesSettingsPage::changed, this, static_cast<void(DolphinServicesConfigModule::*)()>(&DolphinServicesConfigModule::changed));
+    connect(m_services, &ServicesSettingsPage::changed, this, QOverload<>::of(&DolphinServicesConfigModule::changed));
     topLayout->addWidget(m_services, 0, nullptr);
 }
 
index 409a3aa2188a5ce41fa5a35db5d7bd864edbdcfd..07ac89c526fbac71edecf404804876ffc9de0001 100644 (file)
@@ -21,6 +21,7 @@ Name[it]=Servizi di Dolphin
 Name[ja]=Dolphin サービス
 Name[ko]=Dolphin 서비스
 Name[lt]=Dolphin tarnybos
+Name[ml]=ഡോള്‍ഫിന്‍ സേവനങ്ങള്‍
 Name[nb]=Dolphin-tjenester
 Name[nl]=Dolphin-services
 Name[nn]=Dolphin-tenester
@@ -76,6 +77,7 @@ Name[it]=Servizi
 Name[ja]=サービス
 Name[ko]=서비스
 Name[lt]=Paslaugos
+Name[ml]=സേവനങ്ങള്‍
 Name[nb]=Tjenester
 Name[nl]=Services
 Name[nn]=Tenester
@@ -119,6 +121,7 @@ Comment[it]=Configura i servizi del gestore dei file
 Comment[ja]=ファイルマネージャのサービスを設定します
 Comment[ko]=파일 관리자 서비스 설정
 Comment[lt]=Konfigūruokite failų tvarkyklės tarnybas
+Comment[ml]=ഫയല്‍ മാനേജർ സേവനങ്ങള്‍ ക്രമീകരിയ്ക്കുക
 Comment[nb]=Sett opp tjenester i filbehandleren
 Comment[nl]=Bestandsbeheerderservices configureren
 Comment[nn]=Set opp tenester i filhandsamaren
@@ -163,6 +166,7 @@ X-KDE-Keywords[it]=gestore dei file
 X-KDE-Keywords[ja]=ファイルマネージャ
 X-KDE-Keywords[ko]=파일 관리자
 X-KDE-Keywords[lt]=Failų tvarkyklė
+X-KDE-Keywords[ml]=ഫയൽ മാനേജർ 
 X-KDE-Keywords[nb]=filbehandler
 X-KDE-Keywords[nl]=bestandsbeheerder
 X-KDE-Keywords[nn]=filhandsamar
index 9c29e403a609f96006a4be9039512a8295ba0f22..c50fa3800ab879bf4470591abea1f4d7aa795fc9 100644 (file)
@@ -21,6 +21,7 @@ Name[it]=Viste di Dolphin
 Name[ja]=Dolphin 表示モード
 Name[ko]=Dolphin 보기 모드
 Name[lt]=Dolphin rodymo būdai
+Name[ml]=ഡോള്‍ഫിന്‍ അവതരണദശകള്‍
 Name[nb]=Dolphin visningsmåter
 Name[nl]=Dolphin-weergavemodussen
 Name[nn]=Dolphin-visingar
@@ -65,6 +66,7 @@ Comment[it]=Questo servizio permette di configurare le viste di Dolphin.
 Comment[ja]=Dolphin の表示モードを設定します
 Comment[ko]=이 서비스는 Dolphin 보기 모드를 설정합니다.
 Comment[lt]=Ši tarnyba leidžia konfigūruoti Dolphin rodymo būdus.
+Comment[ml]=ഡോള്‍ഫിന്‍ അവതരണദശകള്‍ ക്രമീകരിയ്ക്കാന്‍ ഈ സേവനം അനുവദിയ്ക്കുന്നു.
 Comment[nb]=Med denne tjenesten kan du sette opp Dolphins visningsmåter.
 Comment[nl]=Met deze dienst kunt u Dolphin-weergavemodussen configureren.
 Comment[nn]=Denne tenesta lèt deg setja opp Dolphin-visingsmodusar.
@@ -119,6 +121,7 @@ Name[it]=Viste
 Name[ja]=表示モード
 Name[ko]=보기 모드
 Name[lt]=Rodymo būdai
+Name[ml]=അവതരണ ദശകള്‍
 Name[nb]=Visningsmåter
 Name[nl]=Weergavemodi
 Name[nn]=Visingsmodusar
@@ -163,6 +166,7 @@ Comment[it]=Configura le viste del gestore dei file
 Comment[ja]=ファイルマネージャの表示モードを設定します
 Comment[ko]=파일 관리자 보기 모드 설정
 Comment[lt]=Failų tvarkyklės rodymo būdų konfigūravimas
+Comment[ml]=ഫയല്‍ മാനേജറിന്റെ അവതരണ ദശകള്‍ ക്രമീകരിയ്ക്കുക
 Comment[nb]=Tilpass filbehandlerens visningsmåter
 Comment[nl]=Bestandsbeheerderweergavemodussen configureren
 Comment[nn]=Set opp visingsmodusane i filhandsamaren
@@ -208,6 +212,7 @@ X-KDE-Keywords[it]=gestore dei file
 X-KDE-Keywords[ja]=ファイルマネージャ
 X-KDE-Keywords[ko]=파일 관리자
 X-KDE-Keywords[lt]=Failų tvarkyklė
+X-KDE-Keywords[ml]=ഫയൽ മാനേജർ 
 X-KDE-Keywords[nb]=filbehandler
 X-KDE-Keywords[nl]=bestandsbeheerder
 X-KDE-Keywords[nn]=filhandsamar
index 5e4234262cc2e88346c33e09f77ac030e383a296..9e090e2cda57716bc906c2cef2e5b03e25149c00 100755 (executable)
@@ -1,37 +1,72 @@
 #!/usr/bin/env ruby
+
+# Copyright (C) 2009 Jonathan Schmidt-Dominé <devel@the-user.org>
+# Copyright (C) 2019 Harald Sitter <sitter@kde.org>
+#
+# 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
+
 require 'fileutils'
-archive = ARGV[0]
-if archive[(archive.length - 8)..(archive.length)] == ".desktop"
-    FileUtils.rm(`qtpaths --writable-path GenericDataLocation`.strip!  + "/kservices5/ServiceMenus/" + File.basename(archive))
-    exit(0)
+
+ARCHIVE = ARGV[0]
+
+# @param log_msg [String] error that gets logged to CLI
+def fail(log_msg: nil)
+  # FIXME: this is not translated...
+  msg = 'Dolphin service menu installation failed'
+  warn log_msg if log_msg
+  system('kdialog', '--passivepopup', msg, '15')
+  abort
 end
-dir = archive + "-dir"
-# try: deinstall.sh
-# try: deinstall
-# try: installKDE4.sh
-# try: installKDE4
-# try: install.sh
-# try: install
-while true
-    dd = Dir.new(dir)
-    break if dd.count != 3
-    odir = dir
-    for entry in dd
-        dir += "/" + entry if entry != "." && entry != ".."
-    end
-    if !File.directory? dir
-        dir = odir
-        break
-    end
+
+if ARCHIVE.end_with?('.desktop')
+  data_location = `qtpaths --writable-path GenericDataLocation`.strip
+  unless $?.success?
+    fail(log_msg: "Could not get GenericDataLocation #{data_location}")
+  end
+  FileUtils.rm("#{data_location}/kservices5/ServiceMenus/#{File.basename(ARCHIVE)}")
+  exit(0)
 end
-Dir.chdir(dir)
-def fail()
-    system("kdialog --passivepopup \"Deinstallation failed\" 15")
-    exit(-1)
+dir = "#{ARCHIVE}-dir"
+
+deinstaller = nil
+%w[deinstall.sh deinstall].find do |script|
+  deinstaller = Dir.glob("#{dir}/**/#{script}")[0]
 end
-if !((File.exist?(file = "./deinstall.sh") || File.exist?(file = "./deinstall")) && system(file))
-    fail() if !File.exist?(file = "./installKDE4.sh") && !File.exist?(file = "./installKDE4") && !File.exist?(file = "./install.sh") && !File.exist?(file = "./install")
-File.new(file).chmod(0700)
-    fail() if !system(file + " --remove") && !system(file + " --delete") && !system(file + " --uninstall") && !system(file + " --deinstall")
+
+installer = nil
+%w[install-it.sh install-it installKDE4.sh installKDE4 install.sh install].find do |script|
+  installer = Dir.glob("#{dir}/**/#{script}")[0]
+end
+
+Dir.chdir(dir) do
+  deinstalled = false
+
+  [deinstaller, installer].uniq.compact.each { |f| File.chmod(0o700, f) }
+
+  if deinstaller
+    puts "[servicemenudeinstallation]: Trying to run deinstaller #{deinstaller}"
+    deinstalled = system(deinstaller)
+  elsif installer
+    puts "[servicemenudeinstallation]: Trying to run installer #{installer}"
+    %w[--remove --delete --uninstall --deinstall].any? do |arg|
+      deinstalled = system(installer, arg)
+    end
+  end
+
+  fail unless deinstalled
 end
+
 FileUtils.rm_r(dir)
index 60b699bb6041b3cce0cd4a28f48e6a4c5c188d1d..e2d42bfbf4f0f92f48d3085a58db888df2e7133d 100755 (executable)
 #!/usr/bin/env ruby
-require 'pathname'
+
+# Copyright (C) 2009 Jonathan Schmidt-Dominé <devel@the-user.org>
+# Copyright (C) 2019 Harald Sitter <sitter@kde.org>
+#
+# 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
+
 require 'fileutils'
-archive = ARGV[0]
-$servicedir = `qtpaths --writable-path GenericDataLocation`.strip!  + "/kservices5/ServiceMenus/"
-FileUtils.mkdir_p($servicedir) if !File.exist?($servicedir)
-if archive[(archive.length - 8)..(archive.length - 1)] == ".desktop"
-    puts "Single-File Service-Menu"
-    puts archive
-    puts $servicedir
-    FileUtils.cp(archive, $servicedir);
-    exit(0)
+
+ARCHIVE_UNCOMPRESSORS = {
+  'application/x-tar' => :"tar -xf %s -C %s",
+  'application/tar' => :"tar -xf %s -C %s",
+  'application/x-gzip' => :"tar -zxf %s -C %s",
+  'application/gzip' => :"tar -zxf %s -C %s",
+  'application/x-gzip-compressed-tar' => :"tar -zxf %s -C %s",
+  'application/gzip-compressed-tar' => :"tar -zxf %s -C %s",
+  'application/x-gzip-compressed' => :"tar -zxf %s -C %s",
+  'application/gzip-compressed' => :"tar -zxf %s -C %s",
+  'application/bzip' => :"tar -jxf %s -C %s",
+  'application/bzip2' => :"tar -jxf %s -C %s",
+  'application/x-bzip' => :"tar -jxf %s -C %s",
+  'application/x-bzip2' => :"tar -jxf %s -C %s",
+  'application/bzip-compressed' => :"tar -jxf %s -C %s",
+  'application/bzip2-compressed' => :"tar -jxf %s -C %s",
+  'application/x-bzip-compressed' => :"tar -jxf %s -C %s",
+  'application/x-bzip2-compressed' => :"tar -jxf %s -C %s",
+  'application/bzip-compressed-tar' => :"tar -jxf %s -C %s",
+  'application/bzip2-compressed-tar' => :"tar -jxf %s -C %s",
+  'application/x-bzip-compressed-tar' => :"tar -jxf %s -C %s",
+  'application/x-bzip2-compressed-tar' => :"tar -jxf %s -C %s",
+  'application/zip' => :"unzip %s -d %s",
+  'application/x-zip' => :"unzip %s -d %s",
+  'application/x-zip-compressed' => :"unzip %s -d %s",
+  'multipart/x-zip' => :"unzip %s -d %s",
+  'application/tgz' => :"tar -zxf %s -C %s",
+  'application/x-compressed-gtar' => :"tar -zxf %s -C %s",
+  'file/tgz' => :"tar -zxf %s -C %s",
+  'multipart/x-tar-gz' => :"tar -zxf %s -C %s",
+  'application/x-gunzip' => :"tar -zxf %s -C %s",
+  'application/gzipped' => :"tar -zxf %s -C %s",
+  'gzip/document' => :"tar -zxf %s -C %s",
+  'application/x-bz2 ' => :"tar -jxf %s -C %s",
+  'application/x-gtar' => :"tar -xf %s -C %s",
+  'multipart/x-tar' => :"tar -xf %s -C %s"
+}
+
+ARCHIVE = ARGV[0]
+
+# @param log_msg [String] error that gets logged to CLI
+def fail(log_msg: nil)
+  # FIXME: this is not translated...
+  msg = 'Dolphin service menu installation failed'
+  warn log_msg if log_msg
+  system('kdialog', '--passivepopup', msg, '15')
+  abort
 end
-def mimeType(filename)
-    IO.popen("file --mime-type -b " + filename).gets().strip!()
+
+def mime_type(filename)
+  ret = `xdg-mime query filetype #{filename}`.strip
+  return ret if $?.success?
+
+  warn 'Failed to xdg-mime'
+  fail(log_msg: "Failed to xdg-mime #{filename}: #{ret}")
 end
-$archivetypes = { "application/x-tar" => :"tar -xf %s -C %s",
-        "application/tar" => :"tar -xf %s -C %s",
-        "application/x-gzip" => :"tar -zxf %s -C %s",
-        "application/gzip" => :"tar -zxf %s -C %s",
-        "application/x-gzip-compressed-tar" => :"tar -zxf %s -C %s",
-        "application/gzip-compressed-tar" => :"tar -zxf %s -C %s",
-        "application/x-gzip-compressed" => :"tar -zxf %s -C %s",
-        "application/gzip-compressed" => :"tar -zxf %s -C %s",
-        "application/bzip" => :"tar -jxf %s -C %s",
-        "application/bzip2" => :"tar -jxf %s -C %s",
-        "application/x-bzip" => :"tar -jxf %s -C %s",
-        "application/x-bzip2" => :"tar -jxf %s -C %s",
-        "application/bzip-compressed" => :"tar -jxf %s -C %s",
-        "application/bzip2-compressed" => :"tar -jxf %s -C %s",
-        "application/x-bzip-compressed" => :"tar -jxf %s -C %s",
-        "application/x-bzip2-compressed" => :"tar -jxf %s -C %s",
-        "application/bzip-compressed-tar" => :"tar -jxf %s -C %s",
-        "application/bzip2-compressed-tar" => :"tar -jxf %s -C %s",
-        "application/x-bzip-compressed-tar" => :"tar -jxf %s -C %s",
-        "application/x-bzip2-compressed-tar" => :"tar -jxf %s -C %s",
-        "application/zip" => :"unzip %s -d %s",
-        "application/x-zip" => :"unzip %s -d %s",
-        "application/x-zip-compressed" => :"unzip %s -d %s",
-        "multipart/x-zip" => :"unzip %s -d %s",
-        "application/tgz" => :"tar -zxf %s -C %s",
-        "application/x-compressed-gtar" => :"tar -zxf %s -C %s",
-        "file/tgz" => :"tar -zxf %s -C %s",
-        "multipart/x-tar-gz" => :"tar -zxf %s -C %s",
-        "application/x-gunzip" => :"tar -zxf %s -C %s",
-        "application/gzipped" => :"tar -zxf %s -C %s",
-        "gzip/document" => :"tar -zxf %s -C %s",
-        "application/x-bz2 " => :"tar -jxf %s -C %s",
-        "application/x-gtar" => :"tar -xf %s -C %s",
-        "multipart/x-tar" => :"tar -xf %s -C %s"
-}
+
 def uncompress(filename, output)
-    system(sprintf($archivetypes[mimeType(filename)].to_s, filename, output))
+  uncompressor = ARCHIVE_UNCOMPRESSORS.fetch(mime_type(filename)).to_s
+  system(format(uncompressor, filename, output))
+rescue KeyError => e
+  # If a mimetype doesn't have an uncompressor mapped we'll get a keyerror.
+  # we'll log the error but visually report the failure.
+  fail(log_msg: "Unmapped compression format #{filename}; #{e.message}")
 end
-dir = archive + "-dir"
-if File.exist?(dir)
-    FileUtils.rm_r(dir)
+
+data_location = `qtpaths --writable-path GenericDataLocation`.strip
+unless $?.success?
+  fail(log_msg: "Could not get GenericDataLocation #{data_location}")
 end
+servicedir = "#{data_location}/kservices5/ServiceMenus/"
+
+FileUtils.mkdir_p(servicedir) unless File.exist?(servicedir)
+if ARCHIVE.end_with?('.desktop')
+  puts 'Single-File Service-Menu'
+  puts ARCHIVE
+  puts servicedir
+  FileUtils.cp(ARCHIVE, servicedir)
+  exit
+end
+
+dir = "#{ARCHIVE}-dir"
+
+FileUtils.rm_r(dir) if File.exist?(dir)
 FileUtils.mkdir(dir)
-exit(-1) if !uncompress(archive, dir)
-# try: install-it.sh
-# try: install-it
-# try: installKDE4.sh
-# try: installKDE4
-# try: install.sh
-# try: install
-while true
-    dd = Dir.new(dir)
-    break if dd.count != 3
-    odir = dir
-    for entry in dd
-        dir += "/" + entry if entry != "." && entry != ".."
-    end
-    if !File.directory? dir
-        dir = odir
-        break
-    end
+
+fail(log_msg: 'uncompress failed') unless uncompress(ARCHIVE, dir)
+
+install_it = nil
+%w[install-it.sh install-it].find do |script|
+  install_it = Dir.glob("#{dir}/**/#{script}")[0]
+end
+
+installer = nil
+%w[installKDE4.sh installKDE4 install.sh install].find do |script|
+  installer = Dir.glob("#{dir}/**/#{script}")[0]
 end
-Dir.chdir(dir)
-def fail()
-    system("kdialog --passivepopup \"Installation failed\" 15")
-    exit(-1)
+
+Dir.chdir(dir) do
+  installed = false
+
+  [install_it, installer].uniq.compact.each { |f| File.chmod(0o700, f) }
+
+  if install_it
+    puts "[servicemenuinstallation]: Trying to run install_it #{install_it}"
+    installed = system(install_it)
+  elsif installer
+    puts "[servicemenuinstallation]: Trying to run installer #{installer}"
+    %w[--local --local-install --install].any? do |arg|
+      installed = system(installer, arg)
+    end
+  end
+
+  fail unless installed
 end
-if !((File.exist?(file = "./install-it.sh") || File.exist?(file = "./install-it")) && system(file))
-    fail() if !File.exist?(file = "./installKDE4.sh") && !File.exist?(file = "./installKDE4") && !File.exist?(file = "./install.sh") && !File.exist?(file = "./install")
-    File.new(file).chmod(0700)
-    fail() if !system(file + " --local") && !system(file + "--local-install") && !system(file + " --install")
-end    
diff --git a/src/settings/services/test/service_menu_deinstallation_test.rb b/src/settings/services/test/service_menu_deinstallation_test.rb
new file mode 100644 (file)
index 0000000..2b3514a
--- /dev/null
@@ -0,0 +1,121 @@
+#!/usr/bin/env ruby
+
+# Copyright (C) 2019 Harald Sitter <sitter@kde.org>
+#
+# 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
+
+require_relative 'test_helper'
+
+require 'tmpdir'
+
+class ServiceMenuDeinstallationTest < Test::Unit::TestCase
+  def setup
+    @tmpdir = Dir.mktmpdir("dolphintest-#{self.class.to_s.tr(':', '_')}")
+    @pwdir = Dir.pwd
+    Dir.chdir(@tmpdir)
+
+    ENV['XDG_DATA_HOME'] = File.join(@tmpdir, 'data')
+  end
+
+  def teardown
+    Dir.chdir(@pwdir)
+    FileUtils.rm_rf(@tmpdir)
+
+    ENV.delete('XDG_DATA_HOME')
+  end
+
+  def test_run_deinstall
+    service_dir = File.join(Dir.pwd, 'share/servicemenu-download')
+    archive_base = "#{service_dir}/foo.zip"
+    archive_dir = "#{archive_base}-dir/foo-1.1/"
+    FileUtils.mkpath(archive_dir)
+    File.write("#{archive_dir}/deinstall.sh", <<-DEINSTALL_SH)
+#!/bin/sh
+touch #{@tmpdir}/deinstall.sh-run
+    DEINSTALL_SH
+    File.write("#{archive_dir}/install.sh", <<-INSTALL_SH)
+#!/bin/sh
+touch #{@tmpdir}/install.sh-run
+    INSTALL_SH
+
+    assert(covered_system('servicemenudeinstallation', archive_base))
+
+    # deinstaller should be run
+    # installer should not be run
+    # archive_dir should have been correctly removed
+
+    assert_path_exist('deinstall.sh-run')
+    assert_path_not_exist('install.sh-run')
+    assert_path_not_exist(archive_dir)
+  end
+
+  def test_run_install_with_arg
+    service_dir = File.join(Dir.pwd, 'share/servicemenu-download')
+    archive_base = "#{service_dir}/foo.zip"
+    archive_dir = "#{archive_base}-dir/foo-1.1/"
+    FileUtils.mkpath(archive_dir)
+
+    File.write("#{archive_dir}/install.sh", <<-INSTALL_SH)
+#!/bin/sh
+if [ "$@" = "--uninstall" ]; then
+  touch #{@tmpdir}/install.sh-run
+  exit 0
+fi
+exit 1
+    INSTALL_SH
+
+    assert(covered_system('servicemenudeinstallation', archive_base))
+
+    assert_path_not_exist('deinstall.sh-run')
+    assert_path_exist('install.sh-run')
+    assert_path_not_exist(archive_dir)
+  end
+
+  # no scripts in sight
+  def test_run_fail
+    service_dir = File.join(Dir.pwd, 'share/servicemenu-download')
+    archive_base = "#{service_dir}/foo.zip"
+    archive_dir = "#{archive_base}-dir/foo-1.1/"
+    FileUtils.mkpath(archive_dir)
+
+    refute(covered_system('servicemenudeinstallation', archive_base))
+
+    # I am unsure if deinstallation really should keep the files around. But
+    # that's how it behaved originally so it's supposedly intentional
+    #   - sitter, 2019
+    assert_path_exist(archive_dir)
+  end
+
+  # For desktop files things are a bit special. There is one in .local/share/servicemenu-download
+  # and another in the actual ServiceMenus dir. The latter gets removed by the
+  # script, the former by KNS.
+  def test_run_desktop
+    service_dir = File.join(Dir.pwd, 'share/servicemenu-download')
+    downloaded_file = "#{service_dir}/foo.desktop"
+    FileUtils.mkpath(service_dir)
+    FileUtils.touch(downloaded_file)
+
+    menu_dir = "#{ENV['XDG_DATA_HOME']}/kservices5/ServiceMenus/"
+    installed_file = "#{menu_dir}/foo.desktop"
+    FileUtils.mkpath(menu_dir)
+    FileUtils.touch(installed_file)
+
+    assert(covered_system('servicemenudeinstallation', downloaded_file))
+
+    assert_path_exist(downloaded_file)
+    assert_path_not_exist(installed_file)
+  end
+end
diff --git a/src/settings/services/test/service_menu_installation_test.rb b/src/settings/services/test/service_menu_installation_test.rb
new file mode 100644 (file)
index 0000000..43f5949
--- /dev/null
@@ -0,0 +1,119 @@
+#!/usr/bin/env ruby
+
+# Copyright (C) 2019 Harald Sitter <sitter@kde.org>
+#
+# 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
+
+require_relative 'test_helper'
+
+require 'tmpdir'
+
+class ServiceMenuInstallationTest < Test::Unit::TestCase
+  def setup
+    @tmpdir = Dir.mktmpdir("dolphintest-#{self.class.to_s.tr(':', '_')}")
+    @pwdir = Dir.pwd
+    Dir.chdir(@tmpdir)
+
+    ENV['XDG_DATA_HOME'] = File.join(@tmpdir, 'data')
+  end
+
+  def teardown
+    Dir.chdir(@pwdir)
+    FileUtils.rm_rf(@tmpdir)
+
+    ENV.delete('XDG_DATA_HOME')
+  end
+
+  def test_run_install
+    service_dir = File.join(Dir.pwd, 'share/servicemenu-download')
+    FileUtils.mkpath(service_dir)
+    archive = "#{service_dir}/foo.tar"
+
+    archive_dir = 'foo' # relative so tar cf is relative without fuzz
+    FileUtils.mkpath(archive_dir)
+    File.write("#{archive_dir}/install-it.sh", <<-INSTALL_IT_SH)
+#!/bin/sh
+touch #{@tmpdir}/install-it.sh-run
+INSTALL_IT_SH
+    File.write("#{archive_dir}/install.sh", <<-INSTALL_SH)
+#!/bin/sh
+touch #{@tmpdir}/install.sh-run
+    INSTALL_SH
+    assert(system('tar', '-cf', archive, archive_dir))
+
+    assert(covered_system('servicemenuinstallation', archive))
+
+    tar_dir = "#{service_dir}/foo.tar-dir"
+    tar_extract_dir = "#{service_dir}/foo.tar-dir/foo"
+    assert_path_exist(tar_dir)
+    assert_path_exist(tar_extract_dir)
+    assert_path_exist("#{tar_extract_dir}/install-it.sh")
+    assert_path_exist("#{tar_extract_dir}/install.sh")
+  end
+
+  def test_run_install_with_arg
+    service_dir = File.join(Dir.pwd, 'share/servicemenu-download')
+    FileUtils.mkpath(service_dir)
+    archive = "#{service_dir}/foo.tar"
+
+    archive_dir = 'foo' # relative so tar cf is relative without fuzz
+    FileUtils.mkpath(archive_dir)
+    File.write("#{archive_dir}/install.sh", <<-INSTALL_SH)
+#!/bin/sh
+if [ "$@" = "--install" ]; then
+  touch #{@tmpdir}/install.sh-run
+  exit 0
+fi
+exit 1
+    INSTALL_SH
+    assert(system('tar', '-cf', archive, archive_dir))
+
+    assert(covered_system('servicemenuinstallation', archive))
+
+    tar_dir = "#{service_dir}/foo.tar-dir"
+    tar_extract_dir = "#{service_dir}/foo.tar-dir/foo"
+    assert_path_exist(tar_dir)
+    assert_path_exist(tar_extract_dir)
+    assert_path_not_exist("#{tar_extract_dir}/install-it.sh")
+    assert_path_exist("#{tar_extract_dir}/install.sh")
+  end
+
+  def test_run_fail
+    service_dir = File.join(Dir.pwd, 'share/servicemenu-download')
+    FileUtils.mkpath(service_dir)
+    archive = "#{service_dir}/foo.tar"
+
+    archive_dir = 'foo' # relative so tar cf is relative without fuzz
+    FileUtils.mkpath(archive_dir)
+    assert(system('tar', '-cf', archive, archive_dir))
+
+    refute(covered_system('servicemenuinstallation', archive))
+  end
+
+  def test_run_desktop
+    service_dir = File.join(Dir.pwd, 'share/servicemenu-download')
+    downloaded_file = "#{service_dir}/foo.desktop"
+    FileUtils.mkpath(service_dir)
+    FileUtils.touch(downloaded_file)
+
+    installed_file = "#{ENV['XDG_DATA_HOME']}/kservices5/ServiceMenus/foo.desktop"
+
+    assert(covered_system('servicemenuinstallation', downloaded_file))
+
+    assert_path_exist(downloaded_file)
+    assert_path_exist(installed_file)
+  end
+end
diff --git a/src/settings/services/test/test_helper.rb b/src/settings/services/test/test_helper.rb
new file mode 100644 (file)
index 0000000..9da5cf3
--- /dev/null
@@ -0,0 +1,100 @@
+# Copyright (C) 2019 Harald Sitter <sitter@kde.org>
+#
+# 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
+
+GLOBAL_COVERAGE_ROOT = File.dirname(__dir__) # ../
+
+# Simplecov is a bit meh and expects src and coverage to be under the
+# same root. Since we get run through cmake that assumption absolutely
+# doesn't hold true, so we'll need to figure out the coverage_dir relative
+# to the root and the root must always be the source :/
+# The relativity only works because internally the path gets expanded, this
+# isn't fully reliable, but oh well...
+# https://github.com/colszowka/simplecov/issues/716
+GLOBAL_COVERAGE_DIR = begin
+  require 'pathname'
+  src_path = Pathname.new(GLOBAL_COVERAGE_ROOT)
+  coverage_path = Pathname.new(File.join(Dir.pwd, 'coverage'))
+  coverage_path.relative_path_from(src_path).to_s
+end
+
+begin
+  require 'simplecov'
+
+  SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new(
+    [
+      SimpleCov::Formatter::HTMLFormatter
+    ]
+  )
+
+  SimpleCov.start do
+    root GLOBAL_COVERAGE_ROOT
+    coverage_dir GLOBAL_COVERAGE_DIR
+  end
+rescue LoadError
+  warn 'SimpleCov not loaded'
+end
+
+# FIXME: add coverage report for jenkins?
+
+$LOAD_PATH.unshift(File.absolute_path('../', __dir__)) # ../
+
+def __test_method_name__
+  return @method_name if defined?(:@method_name)
+  index = 0
+  caller = ''
+  until caller.start_with?('test_')
+    caller = caller_locations(index, 1)[0].label
+    index += 1
+  end
+  caller
+end
+
+# system() variant which sets up merge-coverage. simplecov supports merging
+# of multiple coverage sets. we use this to get coverage metrics on the
+# binaries without having to refactor the script into runnable classes.
+def covered_system(cmd, *argv)
+  pid = fork do
+    Kernel.module_exec do
+      alias_method(:real_system, :system)
+      define_method(:system) do |*args|
+        return true if args.include?('kdialog') # disable kdialog call
+        real_system(*args)
+      end
+    end
+
+    begin
+      require 'simplecov'
+      SimpleCov.start do
+        root GLOBAL_COVERAGE_ROOT
+        coverage_dir GLOBAL_COVERAGE_DIR
+        command_name "#{cmd}_#{__test_method_name__}"
+      end
+    rescue LoadError
+      warn 'SimpleCov not loaded'
+    end
+
+    ARGV.replace(argv)
+    load "#{__dir__}/../#{cmd}"
+    puts 'all good, fork ending!'
+    exit 0
+  end
+  waitedpid, status = Process.waitpid2(pid)
+  assert_equal(pid, waitedpid)
+  status.success? # behave like system and return the success only
+end
+
+require 'test/unit'
diff --git a/src/settings/services/test/test_run.rb b/src/settings/services/test/test_run.rb
new file mode 100755 (executable)
index 0000000..b2149bb
--- /dev/null
@@ -0,0 +1,24 @@
+#!/usr/bin/env ruby
+
+# Copyright (C) 2019 Harald Sitter <sitter@kde.org>
+#
+# 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
+# This is a fancy wrapper around test_helper to prevent the collector from
+# loading the helper twice as it would occur if we ran the helper directly.
+
+require_relative 'test_helper'
+
+Test::Unit::AutoRunner.run(true, File.absolute_path(__dir__))
index a9b8c734c7a0516571ee1987e05b40438ca2b30a..dd6038fb6ce7845a30f82bd8c1884f63e02ad772 100644 (file)
@@ -33,7 +33,7 @@ TrashSettingsPage::TrashSettingsPage(QWidget* parent) :
 
     loadSettings();
 
-    connect(m_proxy, static_cast<void(KCModuleProxy::*)(bool)>(&KCModuleProxy::changed), this, &TrashSettingsPage::changed);
+    connect(m_proxy, QOverload<bool>::of(&KCModuleProxy::changed), this, &TrashSettingsPage::changed);
 }
 
 TrashSettingsPage::~TrashSettingsPage()
index e7b4589e376211e6ddbf8816384102b67c727b5f..09aacc3dfeeb1d600ff582a6fdecc1011f37b16b 100644 (file)
@@ -40,7 +40,7 @@ DolphinFontRequester::DolphinFontRequester(QWidget* parent) :
     m_modeCombo = new QComboBox(this);
     m_modeCombo->addItem(i18nc("@item:inlistbox Font", "System Font"));
     m_modeCombo->addItem(i18nc("@item:inlistbox Font", "Custom Font"));
-    connect(m_modeCombo, static_cast<void(QComboBox::*)(int)>(&QComboBox::activated),
+    connect(m_modeCombo, QOverload<int>::of(&QComboBox::activated),
             this, &DolphinFontRequester::changeMode);
 
     m_chooseFontButton = new QPushButton(i18nc("@action:button Choose font", "Choose..."), this);
index 925a982d901ae803ff2236ec2d578ad1dd19f61a..06b0b8cf5ca191d80b5e2faef4560eb1374de559 100644 (file)
@@ -120,11 +120,11 @@ ViewSettingsTab::ViewSettingsTab(Mode mode, QWidget* parent) :
 
     switch (m_mode) {
     case IconsMode:
-        connect(m_widthBox, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ViewSettingsTab::changed);
-        connect(m_maxLinesBox, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ViewSettingsTab::changed);
+        connect(m_widthBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ViewSettingsTab::changed);
+        connect(m_maxLinesBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ViewSettingsTab::changed);
         break;
     case CompactMode:
-        connect(m_widthBox, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ViewSettingsTab::changed);
+        connect(m_widthBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ViewSettingsTab::changed);
         break;
     case DetailsMode:
         connect(m_expandableFolders, &QCheckBox::toggled, this, &ViewSettingsTab::changed);
index 3ccf9a342c8841919071ecc2f2ed325e56b96799..a1f9718feaceccf70faee9e6784457a7d1cea332 100644 (file)
@@ -163,11 +163,11 @@ ViewPropertiesDialog::ViewPropertiesDialog(DolphinView* dolphinView) :
     layout->addRow(QString(), m_showInGroups);
     layout->addRow(QString(), m_showHiddenFiles);
 
-    connect(m_viewMode, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+    connect(m_viewMode, QOverload<int>::of(&QComboBox::currentIndexChanged),
             this, &ViewPropertiesDialog::slotViewModeChanged);
-    connect(m_sorting, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+    connect(m_sorting, QOverload<int>::of(&QComboBox::currentIndexChanged),
             this, &ViewPropertiesDialog::slotSortingChanged);
-    connect(m_sortOrder, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+    connect(m_sortOrder, QOverload<int>::of(&QComboBox::currentIndexChanged),
             this, &ViewPropertiesDialog::slotSortOrderChanged);
     connect(m_sortFoldersFirst, &QCheckBox::clicked,
             this, &ViewPropertiesDialog::slotSortFoldersFirstChanged);
index a6c0c6d58475182dd2c81c205a82dba9fcfb644f..44b7a4c8cedfb55d9068eebe26926b45018f2aee 100644 (file)
@@ -53,7 +53,7 @@ ViewPropsProgressInfo::ViewPropsProgressInfo(QWidget* parent,
     m_viewProps = new ViewProperties(dir);
     m_viewProps->setDirProperties(viewProps);
 
-    // the view properties are stored by the ViewPropsApplierJob, so prevent
+    // the view properties are stored by the ApplyViewPropsJob, so prevent
     // that the view properties are saved twice:
     m_viewProps->setAutoSaveEnabled(false);
 
index 07e4257a0becf6f9fb2a4b51619fe56dbebd1e6f..8ef20cb83b7bfc04f95164d6daa0cf1468de21a6 100644 (file)
@@ -3,6 +3,18 @@ set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} )
 find_package(Qt5Test CONFIG REQUIRED)
 include(ECMAddTests)
 
+include(FindGem)
+
+find_gem(test-unit REQUIRED)
+set_package_properties(Gem:test-unit PROPERTIES
+    DESCRIPTION "Ruby gem 'test-unit' required for testing of servicemenu helpers.")
+
+if(BUILD_COVERAGE)
+    find_gem(simplecov)
+    set_package_properties(Gem:simplecov PROPERTIES
+        DESCRIPTION "Ruby gem 'simplecov' used for coverage statistics.")
+endif()
+
 # KItemSetTest
 ecm_add_test(kitemsettest.cpp LINK_LIBRARIES dolphinprivate Qt5::Test)
 
@@ -69,3 +81,5 @@ ecm_add_test(placesitemmodeltest.cpp
 TEST_NAME placesitemmodeltest
 LINK_LIBRARIES dolphinprivate dolphinstatic Qt5::Test)
 
+add_test(NAME servicemenutest
+ COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/../settings/services/test/test_run.rb)
index 2f258d17d94310405f9419d4900aeda350146a2b..c539799707ae1b8870f5f19c15f436399b858d49 100644 (file)
@@ -1553,22 +1553,22 @@ void KFileItemModelTest::testChangeSortRoleWhileFiltering()
 
     KIO::UDSEntry entry[3];
 
-    entry[0].insert(KIO::UDSEntry::UDS_NAME, "a.txt");
-    entry[0].insert(KIO::UDSEntry::UDS_USER, "user-b");
+    entry[0].fastInsert(KIO::UDSEntry::UDS_NAME, "a.txt");
+    entry[0].fastInsert(KIO::UDSEntry::UDS_USER, "user-b");
 
-    entry[1].insert(KIO::UDSEntry::UDS_NAME, "b.txt");
-    entry[1].insert(KIO::UDSEntry::UDS_USER, "user-c");
+    entry[1].fastInsert(KIO::UDSEntry::UDS_NAME, "b.txt");
+    entry[1].fastInsert(KIO::UDSEntry::UDS_USER, "user-c");
 
-    entry[2].insert(KIO::UDSEntry::UDS_NAME, "c.txt");
-    entry[2].insert(KIO::UDSEntry::UDS_USER, "user-a");
+    entry[2].fastInsert(KIO::UDSEntry::UDS_NAME, "c.txt");
+    entry[2].fastInsert(KIO::UDSEntry::UDS_USER, "user-a");
 
     for (int i = 0; i < 3; ++i) {
-        entry[i].insert(KIO::UDSEntry::UDS_FILE_TYPE, 0100000);    // S_IFREG might not be defined on non-Unix platforms.
-        entry[i].insert(KIO::UDSEntry::UDS_ACCESS, 07777);
-        entry[i].insert(KIO::UDSEntry::UDS_SIZE, 0);
-        entry[i].insert(KIO::UDSEntry::UDS_MODIFICATION_TIME, 0);
-        entry[i].insert(KIO::UDSEntry::UDS_GROUP, "group");
-        entry[i].insert(KIO::UDSEntry::UDS_ACCESS_TIME, 0);
+        entry[i].fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, 0100000);    // S_IFREG might not be defined on non-Unix platforms.
+        entry[i].fastInsert(KIO::UDSEntry::UDS_ACCESS, 07777);
+        entry[i].fastInsert(KIO::UDSEntry::UDS_SIZE, 0);
+        entry[i].fastInsert(KIO::UDSEntry::UDS_MODIFICATION_TIME, 0);
+        entry[i].fastInsert(KIO::UDSEntry::UDS_GROUP, "group");
+        entry[i].fastInsert(KIO::UDSEntry::UDS_ACCESS_TIME, 0);
         items.append(KFileItem(entry[i], m_testDir->url(), false, true));
     }
 
index 7f0d498a7a0d8f93ff6eaae646fc99262471c8ef..af466908cd8cba0c121c6b007740e4d477b80427 100644 (file)
@@ -88,6 +88,7 @@ private:
     QMap<QString, QDBusInterface *> m_interfacesMap;
     int m_expectedModelCount = 15;
     bool m_hasDesktopFolder = false;
+    bool m_hasDocumentsFolder = false;
     bool m_hasDownloadsFolder = false;
 
     void setBalooEnabled(bool enabled);
@@ -171,6 +172,10 @@ QStringList PlacesItemModelTest::initialUrls() const
             urls << QDir::homePath() + QStringLiteral("/Desktop");
         }
 
+        if (m_hasDocumentsFolder) {
+            urls << QDir::homePath() + QStringLiteral("/Documents");
+        }
+
         if (m_hasDownloadsFolder) {
             urls << QDir::homePath() + QStringLiteral("/Downloads");
         }
@@ -269,6 +274,11 @@ void PlacesItemModelTest::initTestCase()
         m_expectedModelCount++;
     }
 
+    if (QDir(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)).exists()) {
+        m_hasDocumentsFolder = true;
+        m_expectedModelCount++;
+    }
+
     if (QDir(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)).exists()) {
         m_hasDownloadsFolder = true;
         m_expectedModelCount++;
@@ -292,12 +302,15 @@ void PlacesItemModelTest::testModelSort()
 void PlacesItemModelTest::testGroups()
 {
     const auto groups = m_model->groups();
-    int expectedGroupSize = 3;
+    int expectedRemoteIndex = 3;
     if (m_hasDesktopFolder) {
-        expectedGroupSize++;
+        expectedRemoteIndex++;
+    }
+    if (m_hasDocumentsFolder) {
+        expectedRemoteIndex++;
     }
     if (m_hasDownloadsFolder) {
-        expectedGroupSize++;
+        expectedRemoteIndex++;
     }
 
     QCOMPARE(groups.size(), 6);
@@ -305,19 +318,19 @@ void PlacesItemModelTest::testGroups()
     QCOMPARE(groups.at(0).first, 0);
     QCOMPARE(groups.at(0).second.toString(), QStringLiteral("Places"));
 
-    QCOMPARE(groups.at(1).first, expectedGroupSize);
+    QCOMPARE(groups.at(1).first, expectedRemoteIndex);
     QCOMPARE(groups.at(1).second.toString(), QStringLiteral("Remote"));
 
-    QCOMPARE(groups.at(2).first, expectedGroupSize + 2);
+    QCOMPARE(groups.at(2).first, expectedRemoteIndex + 2);
     QCOMPARE(groups.at(2).second.toString(), QStringLiteral("Recently Saved"));
 
-    QCOMPARE(groups.at(3).first, expectedGroupSize + 4);
+    QCOMPARE(groups.at(3).first, expectedRemoteIndex + 4);
     QCOMPARE(groups.at(3).second.toString(), QStringLiteral("Search For"));
 
-    QCOMPARE(groups.at(4).first, expectedGroupSize + 8);
+    QCOMPARE(groups.at(4).first, expectedRemoteIndex + 8);
     QCOMPARE(groups.at(4).second.toString(), QStringLiteral("Devices"));
 
-    QCOMPARE(groups.at(5).first, expectedGroupSize + 9);
+    QCOMPARE(groups.at(5).first, expectedRemoteIndex + 9);
     QCOMPARE(groups.at(5).second.toString(), QStringLiteral("Removable Devices"));
 }
 
@@ -372,6 +385,9 @@ void PlacesItemModelTest::testDeletePlace()
     if (m_hasDesktopFolder) {
         tempDirIndex++;
     }
+    if (m_hasDocumentsFolder) {
+        tempDirIndex++;
+    }
     if (m_hasDownloadsFolder) {
         tempDirIndex++;
     }
@@ -545,6 +561,9 @@ void PlacesItemModelTest::testSystemItems()
     if (m_hasDesktopFolder) {
         tempDirIndex++;
     }
+    if (m_hasDocumentsFolder) {
+        tempDirIndex++;
+    }
     if (m_hasDownloadsFolder) {
         tempDirIndex++;
     }
@@ -593,6 +612,9 @@ void PlacesItemModelTest::testEditBookmark()
     if (m_hasDesktopFolder) {
         tempDirIndex++;
     }
+    if (m_hasDocumentsFolder) {
+        tempDirIndex++;
+    }
     if (m_hasDownloadsFolder) {
         tempDirIndex++;
     }
@@ -631,6 +653,9 @@ void PlacesItemModelTest::testEditAfterCreation()
     if (m_hasDesktopFolder) {
         tempDirIndex++;
     }
+    if (m_hasDocumentsFolder) {
+        tempDirIndex++;
+    }
     if (m_hasDownloadsFolder) {
         tempDirIndex++;
     }
@@ -668,6 +693,9 @@ void PlacesItemModelTest::testEditMetadata()
     if (m_hasDesktopFolder) {
         tempDirIndex++;
     }
+    if (m_hasDocumentsFolder) {
+        tempDirIndex++;
+    }
     if (m_hasDownloadsFolder) {
         tempDirIndex++;
     }
@@ -707,6 +735,9 @@ void PlacesItemModelTest::testRefresh()
     if (m_hasDesktopFolder) {
         tempDirIndex++;
     }
+    if (m_hasDocumentsFolder) {
+        tempDirIndex++;
+    }
     if (m_hasDownloadsFolder) {
         tempDirIndex++;
     }
@@ -778,6 +809,9 @@ void PlacesItemModelTest::testDragAndDrop()
     if (m_hasDesktopFolder) {
         lastIndex++;
     }
+    if (m_hasDocumentsFolder) {
+        lastIndex++;
+    }
     if (m_hasDownloadsFolder) {
         lastIndex++;
     }
@@ -897,6 +931,9 @@ void PlacesItemModelTest::renameAfterCreation()
     if (m_hasDesktopFolder) {
         tempDirIndex++;
     }
+    if (m_hasDocumentsFolder) {
+        tempDirIndex++;
+    }
     if (m_hasDownloadsFolder) {
         tempDirIndex++;
     }
index e4a2d947c1a7a2ffe7086e429d69e5d0c8596e74..957091e548ceef298d821abcbb3513748b572492 100644 (file)
@@ -40,7 +40,7 @@ Trash::Trash()
         bool isTrashEmpty = m_trashDirLister->items().isEmpty();
         emit emptinessChanged(isTrashEmpty);
     };
-    connect(m_trashDirLister, static_cast<void(KDirLister::*)()>(&KDirLister::completed), this, trashDirContentChanged);
+    connect(m_trashDirLister, QOverload<>::of(&KCoreDirLister::completed), this, trashDirContentChanged);
     connect(m_trashDirLister, &KDirLister::itemsDeleted, this, trashDirContentChanged);
     m_trashDirLister->openUrl(QUrl(QStringLiteral("trash:/")));
 }
index f4fbddf60cc34f5abbee3fcdcc7450ff9e56c9e7..6e5468cd59b43149100677258af31a38dc28aa35 100644 (file)
@@ -144,7 +144,7 @@ void DolphinViewActionHandler::createActions()
     viewModeActions->addAction(compactAction);
     viewModeActions->addAction(detailsAction);
     viewModeActions->setToolBarMode(KSelectAction::MenuMode);
-    connect(viewModeActions, static_cast<void(KSelectAction::*)(QAction*)>(&KSelectAction::triggered), this, &DolphinViewActionHandler::slotViewModeActionTriggered);
+    connect(viewModeActions, QOverload<QAction*>::of(&KSelectAction::triggered), this, &DolphinViewActionHandler::slotViewModeActionTriggered);
 
     KStandardAction::zoomIn(this,
                             &DolphinViewActionHandler::zoomIn,
index 2990f424931ac30be59e3a70fb45b064d6c19697..aae97458cfd6bbadfb606840366fec6b7765da7e 100644 (file)
@@ -64,7 +64,7 @@ ToolTipManager::ToolTipManager(QWidget* parent) :
     m_showToolTipTimer = new QTimer(this);
     m_showToolTipTimer->setSingleShot(true);
     m_showToolTipTimer->setInterval(500);
-    connect(m_showToolTipTimer, &QTimer::timeout, this, static_cast<void(ToolTipManager::*)()>(&ToolTipManager::showToolTip));
+    connect(m_showToolTipTimer, &QTimer::timeout, this, QOverload<>::of(&ToolTipManager::showToolTip));
 
     m_contentRetrievalTimer = new QTimer(this);
     m_contentRetrievalTimer->setSingleShot(true);
index 71d1603e648dceb8edfafb819b17f7e4f5feb082..72c02b73583e9c6d92522922dbd1b22ebbbefed4 100644 (file)
@@ -24,6 +24,7 @@ Comment[it]=Estensione di controllo delle versioni per le viste dei file
 Comment[ja]=ファイルビューのためのバージョン管理プラグイン
 Comment[ko]=파일 보기를 위한 버전 관리 플러그인
 Comment[lt]=Versijų kontrolės papildinys failų tvarkyklėms
+Comment[ml]=ഫയല്‍ അവതരണദിശകൾക്കുള്ള പതിപ്പ് നിയന്ത്രണ സംയോജകം
 Comment[nb]=Versjonskontrollmodul for filvisninger
 Comment[nl]=Plugin voor versiecontrole op bestandoverzichten
 Comment[nn]=Versjonskontroll-tillegg for filvisingar