# 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)
TextWidgets
Notifications
Crash
+ WindowSystem
)
find_package(KF5 ${KF5_MIN_VERSION} OPTIONAL_COMPONENTS
Activities
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
--- /dev/null
+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.
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}
--- /dev/null
+#=============================================================================
+# 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()
--- /dev/null
+#=============================================================================
+# 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
+)
<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>
</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>,
<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>
KF5::ConfigCore
KF5::NewStuff
KF5::Parts
+ KF5::WindowSystem
)
if(HAVE_BALOO)
##########################################
set(dolphinstatic_SRCS
+ dolphinbookmarkhandler.cpp
dolphindockwidget.cpp
dolphinmainwindow.cpp
dolphinviewcontainer.cpp
if(HAVE_BALOO)
set(dolphinstatic_SRCS
${dolphinstatic_SRCS}
- panels/information/filemetadataconfigurationdialog.cpp
panels/information/informationpanel.cpp
panels/information/informationpanelcontent.cpp
panels/information/pixmapviewer.cpp
########### 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
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} )
#include "dbusinterface.h"
#include "global.h"
+#include "dolphin_generalsettings.h"
#include <KPropertiesDialog>
+#include <QApplication>
#include <QDBusConnection>
+#include <QDBusInterface>
#include <QDBusConnectionInterface>
DBusInterface::DBusInterface() :
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)
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)
--- /dev/null
+/***************************************************************************
+ * 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());
+}
--- /dev/null
+/***************************************************************************
+ * 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
#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>
m_actionHandler(nullptr),
m_remoteEncoding(nullptr),
m_settingsDialog(),
+ m_bookmarkHandler(nullptr),
m_controlButton(nullptr),
m_updateToolBarTimer(nullptr),
m_lastHandleUrlStatJob(nullptr),
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);
{
}
+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();
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);
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
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());
// 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());
}
#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
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);
}
}
+bool DolphinMainWindow::isUrlOpen(const QString& url)
+{
+ if (m_tabWidget->getIndexByUrl(QUrl::fromUserInput((url))) >= 0) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
#include <QList>
#include <QPointer>
#include <QUrl>
+#include <QVector>
typedef KIO::FileUndoManager::CommandType CommandType;
+class DolphinBookmarkHandler;
class DolphinViewActionHandler;
class DolphinSettingsDialog;
class DolphinViewContainer;
*/
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.
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,
/** 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
*/
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.
*/
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;
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);
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,
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
Name[ja]=アイコン
Name[ko]=아이콘
Name[lt]=Ženkliukai
+Name[ml]=സൂചനാചിത്രങ്ങള്
Name[nb]=Ikoner
Name[nl]=Pictogrammen
Name[nn]=Ikon
Name[ja]=コンパクト
Name[ko]=축소됨
Name[lt]=Kompaktiškas
+Name[ml]=തിങ്ങിയതായി
Name[nb]=Kompakt
Name[nl]=Compact
Name[nn]=Kompakt
Name[ja]=詳細
Name[ko]=자세히
Name[lt]=Informacija
+Name[ml]=വിശദമായി
Name[nb]=Detaljer
Name[nl]=Details
Name[nn]=Detaljar
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,
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);
}
}
}
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);
// 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;
+}
*/
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
<!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" />
<Action name="view_properties" />
</Menu>
<Menu name="go">
+ <Action name="bookmarks" />
<Action name="closed_tabs" />
</Menu>
<Menu name="tools">
}
#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,
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);
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);
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()
#include "dolphindebug.h"
#include <KRun>
+#include <KWindowSystem>
#include <QApplication>
#include <QIcon>
+#include <QDBusInterface>
+#include <QDBusConnectionInterface>
QList<QUrl> Dolphin::validateUris(const QStringList& uriList)
{
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"));
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;
}
*/
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
*/
}
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
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));
}
}
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);
// 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:
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();
}
}
- 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);
}
break;
}
+ }
- m_view->scrollToItem(index);
+ if (navigationPressed) {
+ if (index < m_view->firstVisibleIndex() || index > m_view->lastVisibleIndex()) {
+ m_view->scrollToItem(index);
+ }
}
return true;
}
#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()
{
}
KStandardItem::KStandardItem(const QString& icon, const QString& text, KStandardItem* parent) :
- m_parent(parent),
- m_children(),
+ QObject(parent),
m_model(nullptr),
m_data()
{
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()
{
}
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;
return m_data;
}
-QList<KStandardItem*> KStandardItem::children() const
-{
- return m_children;
-}
-
void KStandardItem::onDataValueChanged(const QByteArray& role,
const QVariant& current,
const QVariant& previous)
#include <QByteArray>
#include <QHash>
-#include <QList>
+#include <QObject>
#include <QVariant>
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();
/**
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,
const QHash<QByteArray, QVariant>& previous);
private:
- KStandardItem* m_parent;
- QList<KStandardItem*> m_children;
KStandardItemModel* m_model;
QHash<QByteArray, QVariant> m_data;
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();
}
}
};
/**
- * @brief Itemlist widget implementation for KStandardItemView and KStandardItemModel.
+ * @brief Itemlist widget implementation for KStandardItemListView and KStandardItemModel.
*/
class DOLPHIN_EXPORT KStandardItemListWidget : public KItemListWidget
{
onItemRemoved(index, item);
- delete item;
+ item->deleteLater();
item = nullptr;
emit itemsRemoved(KItemRangeList() << KItemRange(index, 1));
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.
{
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());
{ "album", "album" },
{ "duration", "duration" },
{ "bitRate", "bitrate" },
+ { "aspectRatio", "aspectRatio" },
+ { "frameRate", "frameRate" },
{ "releaseYear", "releaseYear" },
{ "trackNumber", "track" },
{ "originUrl", "originUrl" }
}
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;
}
#include <QApplication>
#include <QCommandLineParser>
+#include <QDBusConnection>
+#include <QDBusInterface>
+#include <QDBusAbstractInterface>
+#include <QDBusConnectionInterface>
#ifndef Q_OS_WIN
#include <unistd.h>
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);
Name[ja]=Dolphin
Name[ko]=Dolphin
Name[lt]=Dolphin
+Name[ml]=ഡോള്ഫിന്
Name[nb]=Dolphin
Name[nl]=Dolphin
Name[nn]=Dolphin
GenericName[ja]=ファイルマネージャ
GenericName[ko]=파일 관리자
GenericName[lt]=Failų tvarkyklė
+GenericName[ml]=ഫയല് മാനേജര്
GenericName[nb]=Filbehandler
GenericName[nl]=Bestandsbeheerder
GenericName[nn]=Filhandsamar
MimeType=inode/directory;
InitialPreference=10
X-DBUS-ServiceName=org.kde.dolphin
+X-KDE-Shortcuts=Meta+E
StartupWMClass=dolphin
+++ /dev/null
-/***************************************************************************
- * 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();
-}
-
+++ /dev/null
-/***************************************************************************
- * 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
#include <QMenu>
#include "dolphin_informationpanelsettings.h"
-#include "filemetadataconfigurationdialog.h"
InformationPanel::InformationPanel(QWidget* parent) :
Panel(parent),
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"));
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")));
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);
}
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);
void InformationPanel::slotFilesChanged(const QStringList& files)
{
- foreach (const QString& fileName, files) {
+ for (const QString& fileName : files) {
if (m_shownUrl == QUrl::fromLocalFile(fileName)) {
showItemInfo();
break;
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
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);
KIO::Job* m_folderStatJob;
InformationPanelContent* m_content;
+ bool m_inConfigurationMode = false;
};
#endif // INFORMATIONPANEL_H
#include <Phonon/MediaObject>
#include <QLabel>
+#include <QDialogButtonBox>
#include <QScrollArea>
#include <QTextLayout>
#include <QTimer>
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);
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);
}
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) {
}
}
-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)
);
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();
m_preview->setPixmap(disabledPixmap);
}
-KFileItemList InformationPanelContent::items() {
+KFileItemList InformationPanelContent::items()
+{
return m_metaDataWidget->items();
}
// 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));
class PixmapViewer;
class PlacesItemModel;
class QPixmap;
+class QDialogButtonBox;
class QString;
class QLabel;
class QScrollArea;
class PreviewJob;
}
-#ifndef HAVE_BALOO
-class KFileMetaDataWidget;
-#else
namespace Baloo {
class FileMetaDataWidget;
}
-#endif
/**
* @brief Manages the widgets that display the meta information
*/
void refreshPreview();
+ /**
+ * Switch the metadatawidget into configuration mode
+ */
+ void configureShownProperties();
+
signals:
void urlActivated( const QUrl& url );
+ void configurationFinished();
public slots:
/**
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;
};
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);
}
}
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);
}
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);
}
}
m_startedSearching(false),
m_active(true),
m_topLayout(nullptr),
- m_searchLabel(nullptr),
m_searchInput(nullptr),
m_saveSearchAction(nullptr),
m_optionsScrollArea(nullptr),
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
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();
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));
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
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);
QVBoxLayout* m_topLayout;
- QLabel* m_searchLabel;
QLineEdit* m_searchInput;
QAction* m_saveSearchAction;
QScrollArea* m_optionsScrollArea;
#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();
void DolphinSettingsDialog::enableApply()
{
buttonBox()->button(QDialogButtonBox::Apply)->setEnabled(true);
+ m_unsavedChanges = true;
}
void DolphinSettingsDialog::applySettings()
settings->save();
}
buttonBox()->button(QDialogButtonBox::Apply)->setEnabled(false);
+ m_unsavedChanges = false;
}
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"))) {
void applySettings();
void restoreDefaults();
+protected:
+ void closeEvent(QCloseEvent* event) override;
+
private:
static SettingsPageBase *createTrashSettingsPage(QWidget *parent);
QList<SettingsPageBase*> m_pages;
+ bool m_unsavedChanges;
};
#endif
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()
// 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);
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
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.
Name[ja]=全般
Name[ko]=일반
Name[lt]=Bendra
+Name[ml]=പൊതുവായതു്
Name[nb]=Generelt
Name[nl]=Algemeen
Name[nn]=Generelt
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
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
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);
}
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
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.
Name[ja]=ナビゲーション
Name[ko]=탐색
Name[lt]=Navigacija
+Name[ml]=നാവിഗേഷന്
Name[nb]=Navigasjon
Name[nl]=Navigatie
Name[nn]=Navigasjon
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
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
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);
}
Name[ja]=Dolphin サービス
Name[ko]=Dolphin 서비스
Name[lt]=Dolphin tarnybos
+Name[ml]=ഡോള്ഫിന് സേവനങ്ങള്
Name[nb]=Dolphin-tjenester
Name[nl]=Dolphin-services
Name[nn]=Dolphin-tenester
Name[ja]=サービス
Name[ko]=서비스
Name[lt]=Paslaugos
+Name[ml]=സേവനങ്ങള്
Name[nb]=Tjenester
Name[nl]=Services
Name[nn]=Tenester
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
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
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
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.
Name[ja]=表示モード
Name[ko]=보기 모드
Name[lt]=Rodymo būdai
+Name[ml]=അവതരണ ദശകള്
Name[nb]=Visningsmåter
Name[nl]=Weergavemodi
Name[nn]=Visingsmodusar
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
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
#!/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)
#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+# 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'
--- /dev/null
+#!/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__))
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()
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);
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);
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);
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);
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)
TEST_NAME placesitemmodeltest
LINK_LIBRARIES dolphinprivate dolphinstatic Qt5::Test)
+add_test(NAME servicemenutest
+ COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/../settings/services/test/test_run.rb)
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));
}
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);
urls << QDir::homePath() + QStringLiteral("/Desktop");
}
+ if (m_hasDocumentsFolder) {
+ urls << QDir::homePath() + QStringLiteral("/Documents");
+ }
+
if (m_hasDownloadsFolder) {
urls << QDir::homePath() + QStringLiteral("/Downloads");
}
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++;
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);
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"));
}
if (m_hasDesktopFolder) {
tempDirIndex++;
}
+ if (m_hasDocumentsFolder) {
+ tempDirIndex++;
+ }
if (m_hasDownloadsFolder) {
tempDirIndex++;
}
if (m_hasDesktopFolder) {
tempDirIndex++;
}
+ if (m_hasDocumentsFolder) {
+ tempDirIndex++;
+ }
if (m_hasDownloadsFolder) {
tempDirIndex++;
}
if (m_hasDesktopFolder) {
tempDirIndex++;
}
+ if (m_hasDocumentsFolder) {
+ tempDirIndex++;
+ }
if (m_hasDownloadsFolder) {
tempDirIndex++;
}
if (m_hasDesktopFolder) {
tempDirIndex++;
}
+ if (m_hasDocumentsFolder) {
+ tempDirIndex++;
+ }
if (m_hasDownloadsFolder) {
tempDirIndex++;
}
if (m_hasDesktopFolder) {
tempDirIndex++;
}
+ if (m_hasDocumentsFolder) {
+ tempDirIndex++;
+ }
if (m_hasDownloadsFolder) {
tempDirIndex++;
}
if (m_hasDesktopFolder) {
tempDirIndex++;
}
+ if (m_hasDocumentsFolder) {
+ tempDirIndex++;
+ }
if (m_hasDownloadsFolder) {
tempDirIndex++;
}
if (m_hasDesktopFolder) {
lastIndex++;
}
+ if (m_hasDocumentsFolder) {
+ lastIndex++;
+ }
if (m_hasDownloadsFolder) {
lastIndex++;
}
if (m_hasDesktopFolder) {
tempDirIndex++;
}
+ if (m_hasDocumentsFolder) {
+ tempDirIndex++;
+ }
if (m_hasDownloadsFolder) {
tempDirIndex++;
}
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:/")));
}
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,
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);
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