Change in kio[master]: KIO: new class KFileCopyToMenu. Adds Copy To / Move To to po...
David Faure (Code Review)
noreply at kde.org
Sun Jan 4 15:57:14 UTC 2015
David Faure has uploaded a new change for review.
https://gerrit.vesnicky.cesnet.cz/r/305
Change subject: KIO: new class KFileCopyToMenu. Adds Copy To / Move To to popupmenus.
......................................................................
KIO: new class KFileCopyToMenu. Adds Copy To / Move To to popupmenus.
Mostly for filemanager-like applications, which is why it's going into
KIOFileWidgets.
Moved from libkonq, added error signal for dolphin to avoid messageboxes, added unittest.
Change-Id: I64baa301016bdd3a31b7590c10d1218d76a7cc3a
---
M autotests/CMakeLists.txt
M src/filewidgets/CMakeLists.txt
A src/filewidgets/kfilecopytomenu.cpp
A src/filewidgets/kfilecopytomenu.h
A src/filewidgets/kfilecopytomenu_p.h
5 files changed, 445 insertions(+), 0 deletions(-)
git pull ssh://gerrit.vesnicky.cesnet.cz:29418/kio refs/changes/05/305/1
diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt
index c667bb9..f203df3 100644
--- a/autotests/CMakeLists.txt
+++ b/autotests/CMakeLists.txt
@@ -66,6 +66,7 @@
kurlnavigatortest.cpp
kdiroperatortest.cpp
knewfilemenutest.cpp
+ kfilecopytomenutest.cpp
kfileplacesmodeltest.cpp
NAME_PREFIX "kiofilewidgets-"
LINK_LIBRARIES KF5::KIOFileWidgets KF5::KIOWidgets Qt5::Test
diff --git a/src/filewidgets/CMakeLists.txt b/src/filewidgets/CMakeLists.txt
index dd2b995..c2ad483 100644
--- a/src/filewidgets/CMakeLists.txt
+++ b/src/filewidgets/CMakeLists.txt
@@ -15,6 +15,7 @@
kdirsortfilterproxymodel.cpp #used in combination with kdirmodel.cpp
kencodingfiledialog.cpp
kfilebookmarkhandler.cpp
+ kfilecopytomenu.cpp
kfilefiltercombo.cpp
kfilewidget.cpp
kfileplacesitem.cpp
@@ -68,6 +69,7 @@
KStatusBarOfflineIndicator
KDirOperator
KDirSortFilterProxyModel
+ KFileCopyToMenu
KFileFilterCombo
KFilePlacesModel
KFilePlacesView
diff --git a/src/filewidgets/kfilecopytomenu.cpp b/src/filewidgets/kfilecopytomenu.cpp
new file mode 100644
index 0000000..c22da2b
--- /dev/null
+++ b/src/filewidgets/kfilecopytomenu.cpp
@@ -0,0 +1,268 @@
+/* Copyright 2008, 2009 David Faure <faure at kde.org>
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published
+ by the Free Software Foundation; either version 2 of the License or
+ ( at your option ) version 3 or, at the discretion of KDE e.V.
+ ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kfilecopytomenu.h"
+#include "kfilecopytomenu_p.h"
+#include <QAction>
+#include <QDebug>
+#include <QDir>
+#include <QIcon>
+#include <QFileDialog>
+#include <QMimeDatabase>
+#include <QMimeType>
+
+#include <KIO/FileUndoManager>
+#include <KIO/CopyJob>
+#include <KIO/JobUiDelegate>
+#include <KLocalizedString>
+#include <KSharedConfig>
+#include <KStringHandler>
+#include <KJobWidgets>
+
+#ifdef Q_OS_WIN
+#include "Windows.h"
+#endif
+
+KFileCopyToMenuPrivate::KFileCopyToMenuPrivate(KFileCopyToMenu *qq, QWidget *parentWidget)
+ : q(qq),
+ m_urls(),
+ m_parentWidget(parentWidget),
+ m_readOnly(false),
+ m_autoErrorHandling(false)
+{
+}
+
+////
+
+KFileCopyToMenu::KFileCopyToMenu(QWidget *parentWidget)
+ : QObject(parentWidget), d(new KFileCopyToMenuPrivate(this, parentWidget))
+{
+}
+
+KFileCopyToMenu::~KFileCopyToMenu()
+{
+ delete d;
+}
+
+void KFileCopyToMenu::setUrls(const QList<QUrl> &urls)
+{
+ d->m_urls = urls;
+}
+
+void KFileCopyToMenu::setReadOnly(bool ro)
+{
+ d->m_readOnly = ro;
+}
+
+void KFileCopyToMenu::setAutoErrorHandlingEnabled(bool b)
+{
+ d->m_autoErrorHandling = b;
+}
+
+void KFileCopyToMenu::addActionsTo(QMenu *menu)
+{
+ QMenu *mainCopyMenu = new KFileCopyToMainMenu(menu, d, Copy);
+ mainCopyMenu->setTitle(i18nc("@title:menu", "Copy To"));
+ mainCopyMenu->menuAction()->setObjectName(QLatin1String("copyTo_submenu")); // for the unittest
+ menu->addMenu(mainCopyMenu);
+
+ if (!d->m_readOnly) {
+ QMenu *mainMoveMenu = new KFileCopyToMainMenu(menu, d, Move);
+ mainMoveMenu->setTitle(i18nc("@title:menu", "Move To"));
+ mainMoveMenu->menuAction()->setObjectName(QLatin1String("moveTo_submenu")); // for the unittest
+ menu->addMenu(mainMoveMenu);
+ }
+}
+
+////
+
+KFileCopyToMainMenu::KFileCopyToMainMenu(QMenu *parent, KFileCopyToMenuPrivate *_d, MenuType menuType)
+ : QMenu(parent), m_menuType(menuType),
+ m_actionGroup(static_cast<QWidget *>(0)),
+ d(_d),
+ m_recentDirsGroup(KSharedConfig::openConfig(), m_menuType == Copy ? "kuick-copy" : "kuick-move")
+{
+ connect(this, &KFileCopyToMainMenu::aboutToShow, this, &KFileCopyToMainMenu::slotAboutToShow);
+ connect(&m_actionGroup, &QActionGroup::triggered, this, &KFileCopyToMainMenu::slotTriggered);
+}
+
+void KFileCopyToMainMenu::slotAboutToShow()
+{
+ clear();
+ KFileCopyToDirectoryMenu *subMenu;
+ // Home Folder
+ subMenu = new KFileCopyToDirectoryMenu(this, this, QDir::homePath());
+ subMenu->setTitle(i18nc("@title:menu", "Home Folder"));
+ subMenu->setIcon(QIcon::fromTheme("go-home"));
+ QAction *act = addMenu(subMenu);
+ act->setObjectName("home");
+
+ // Root Folder
+#ifndef Q_OS_WIN
+ subMenu = new KFileCopyToDirectoryMenu(this, this, QDir::rootPath());
+ subMenu->setTitle(i18nc("@title:menu", "Root Folder"));
+ subMenu->setIcon(QIcon::fromTheme("folder-red"));
+ act = addMenu(subMenu);
+ act->setObjectName("root");
+#else
+ foreach (const QFileInfo &info, QDir::drives()) {
+ QString driveIcon = "drive-harddisk";
+ const uint type = GetDriveTypeW((wchar_t *)info.absoluteFilePath().utf16());
+ switch (type) {
+ case DRIVE_REMOVABLE:
+ driveIcon = "drive-removable-media";
+ break;
+ case DRIVE_FIXED:
+ driveIcon = "drive-harddisk";
+ break;
+ case DRIVE_REMOTE:
+ driveIcon = "network-server";
+ break;
+ case DRIVE_CDROM:
+ driveIcon = "drive-optical";
+ break;
+ case DRIVE_RAMDISK:
+ case DRIVE_UNKNOWN:
+ case DRIVE_NO_ROOT_DIR:
+ default:
+ driveIcon = "drive-harddisk";
+ }
+ subMenu = new KFileCopyToDirectoryMenu(this, this, info.absoluteFilePath());
+ subMenu->setTitle(info.absoluteFilePath());
+ subMenu->setIcon(QIcon::fromTheme(driveIcon));
+ addMenu(subMenu);
+ }
+#endif
+
+ // Browse... action, shows a file dialog
+ QAction *browseAction = new QAction(i18nc("@title:menu in Copy To or Move To submenu", "Browse..."), this);
+ browseAction->setObjectName("browse");
+ connect(browseAction, &QAction::triggered, this, &KFileCopyToMainMenu::slotBrowse);
+ addAction(browseAction);
+
+ addSeparator(); // Qt handles removing it automatically if it's last in the menu, nice.
+
+ // Recent Destinations
+ const QStringList recentDirs = m_recentDirsGroup.readPathEntry("Paths", QStringList());
+ Q_FOREACH (const QString &recentDir, recentDirs) {
+ const QUrl url = QUrl::fromLocalFile(recentDir);
+ const QString text = KStringHandler::csqueeze(url.toDisplayString(), 60); // shorten very long paths (#61386)
+ QAction *act = new QAction(text, this);
+ act->setObjectName(recentDir);
+ act->setData(url);
+ m_actionGroup.addAction(act);
+ addAction(act);
+ }
+}
+
+void KFileCopyToMainMenu::slotBrowse()
+{
+ const QUrl dest = QFileDialog::getExistingDirectoryUrl(d->m_parentWidget ? d->m_parentWidget : this);
+ if (!dest.isEmpty()) {
+ copyOrMoveTo(dest);
+ }
+}
+
+void KFileCopyToMainMenu::slotTriggered(QAction *action)
+{
+ const QUrl url = action->data().value<QUrl>();
+ Q_ASSERT(!url.isEmpty());
+ copyOrMoveTo(url);
+}
+
+void KFileCopyToMainMenu::copyOrMoveTo(const QUrl &dest)
+{
+ // Insert into the recent destinations list
+ QStringList recentDirs = m_recentDirsGroup.readPathEntry("Paths", QStringList());
+ const QString niceDest = dest.toDisplayString();
+ if (!recentDirs.contains(niceDest)) { // don't change position if already there, moving stuff is bad usability
+ recentDirs.prepend(niceDest);
+ while (recentDirs.size() > 10) { // hardcoded max size
+ recentDirs.removeLast();
+ }
+ m_recentDirsGroup.writePathEntry("Paths", recentDirs);
+ }
+
+ // #199549: add a trailing slash to avoid unexpected results when the
+ // dest doesn't exist anymore: it was creating a file with the name of
+ // the now non-existing dest.
+ QUrl dirDest = dest;
+ if (!dirDest.path().endsWith('/')) {
+ dirDest.setPath(dirDest.path() + '/');
+ }
+
+ // And now let's do the copy or move -- with undo/redo support.
+ KIO::CopyJob *job = m_menuType == Copy ? KIO::copy(d->m_urls, dirDest) : KIO::move(d->m_urls, dirDest);
+ KIO::FileUndoManager::self()->recordCopyJob(job);
+ KJobWidgets::setWindow(job, d->m_parentWidget ? d->m_parentWidget : this);
+ if (d->m_autoErrorHandling)
+ job->ui()->setAutoErrorHandlingEnabled(true);
+ connect(job, &KIO::CopyJob::result, this, [this](KJob *job) { emit d->q->error(job->error(), job->errorString()); });
+}
+
+////
+
+KFileCopyToDirectoryMenu::KFileCopyToDirectoryMenu(QMenu *parent, KFileCopyToMainMenu *mainMenu, const QString &path)
+ : QMenu(parent), m_mainMenu(mainMenu), m_path(path)
+{
+ connect(this, &KFileCopyToDirectoryMenu::aboutToShow, this, &KFileCopyToDirectoryMenu::slotAboutToShow);
+}
+
+void KFileCopyToDirectoryMenu::slotAboutToShow()
+{
+ clear();
+ QAction *act = new QAction(m_mainMenu->menuType() == Copy
+ ? i18nc("@title:menu", "Copy Here")
+ : i18nc("@title:menu", "Move Here"), this);
+ act->setData(QUrl::fromLocalFile(m_path));
+ act->setEnabled(QFileInfo(m_path).isWritable());
+ m_mainMenu->actionGroup().addAction(act);
+ addAction(act);
+
+ addSeparator(); // Qt handles removing it automatically if it's last in the menu, nice.
+
+ // List directory
+ // All we need is sub folder names, their permissions, their icon.
+ // KDirLister or KIO::listDir would fetch much more info, and would be async,
+ // and we only care about local directories so we use QDir directly.
+ QDir dir(m_path);
+ const QStringList entries = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::LocaleAware);
+ const QMimeDatabase db;
+ const QMimeType dirMime = db.mimeTypeForName("inode/directory");
+ Q_FOREACH (const QString &subDir, entries) {
+ QString subPath = m_path;
+ if (!subPath.endsWith('/')) {
+ subPath.append('/');
+ }
+ subPath += subDir;
+ KFileCopyToDirectoryMenu *subMenu = new KFileCopyToDirectoryMenu(this, m_mainMenu, subPath);
+ QString menuTitle(subDir);
+ // Replace '&' by "&&" to make sure that '&' inside the directory name is displayed
+ // correctly and not misinterpreted as an indicator for a keyboard shortcut
+ subMenu->setTitle(menuTitle.replace('&', "&&"));
+ const QString iconName = dirMime.iconName();
+ subMenu->setIcon(QIcon::fromTheme(iconName));
+ if (QFileInfo(subPath).isSymLink()) {
+ QFont font = subMenu->menuAction()->font();
+ font.setItalic(true);
+ subMenu->menuAction()->setFont(font);
+ }
+ addMenu(subMenu);
+ }
+}
diff --git a/src/filewidgets/kfilecopytomenu.h b/src/filewidgets/kfilecopytomenu.h
new file mode 100644
index 0000000..e6b0087
--- /dev/null
+++ b/src/filewidgets/kfilecopytomenu.h
@@ -0,0 +1,89 @@
+/* Copyright 2008 David Faure <faure at kde.org>
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published
+ by the Free Software Foundation; either version 2 of the License or
+ ( at your option ) version 3 or, at the discretion of KDE e.V.
+ ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KFILECOPYTOMENU_H
+#define KFILECOPYTOMENU_H
+
+#include <QUrl>
+#include <QObject>
+
+#include <kiofilewidgets_export.h>
+
+class QMenu;
+class KFileCopyToMenuPrivate;
+
+/**
+ * This class adds "Copy To" and "Move To" submenus to a popupmenu.
+ */
+class KIOFILEWIDGETS_EXPORT KFileCopyToMenu : public QObject
+{
+ Q_OBJECT
+public:
+ /**
+ * Creates a KFileCopyToMenu instance
+ * Note that this instance (and the widget) must stay alive for at least as
+ * long as the popupmenu; it has the slots for the actions created by addActionsTo.
+ *
+ * @param parentWidget parent widget for the file dialog and message boxes.
+ * The parentWidget also serves as a parent for this object.
+ */
+ KFileCopyToMenu(QWidget *parentWidget);
+
+ /**
+ * Destructor
+ */
+ ~KFileCopyToMenu();
+
+ /**
+ * Sets the URLs which the actions apply to.
+ */
+ void setUrls(const QList<QUrl> &urls);
+
+ /**
+ * If setReadOnly(true) is called, the "Move To" submenu will not appear.
+ */
+ void setReadOnly(bool ro);
+
+ /**
+ * Generate the actions and submenus, and adds them to the @p menu.
+ * All actions are created as children of the menu.
+ */
+ void addActionsTo(QMenu *menu);
+
+ /**
+ * Enables or disables automatic error handling with message boxes.
+ * When called with true, a messagebox is shown in case of an error during a copy or move.
+ * When called with false, the application should connect to the error signal instead.
+ * Auto error handling is disabled by default.
+ */
+ void setAutoErrorHandlingEnabled(bool b);
+
+Q_SIGNALS:
+ /**
+ * Emitted when the copy or move job fails.
+ * @param errorCode the KIO job error code
+ * @param message the error message to show the user
+ */
+ void error(int errorCode, const QString &message);
+
+private:
+ KFileCopyToMenuPrivate *const d;
+};
+
+#endif
diff --git a/src/filewidgets/kfilecopytomenu_p.h b/src/filewidgets/kfilecopytomenu_p.h
new file mode 100644
index 0000000..3536810
--- /dev/null
+++ b/src/filewidgets/kfilecopytomenu_p.h
@@ -0,0 +1,85 @@
+/* Copyright 2008 David Faure <faure at kde.org>
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published
+ by the Free Software Foundation; either version 2 of the License or
+ ( at your option ) version 3 or, at the discretion of KDE e.V.
+ ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <kconfiggroup.h>
+#include <QMenu>
+#include <QActionGroup>
+#include <QObject>
+#include <QUrl>
+
+class KFileCopyToMenuPrivate
+{
+public:
+ KFileCopyToMenuPrivate(KFileCopyToMenu *qq, QWidget *parentWidget);
+
+ KFileCopyToMenu *q;
+ QList<QUrl> m_urls;
+ QWidget *m_parentWidget;
+ bool m_readOnly;
+ bool m_autoErrorHandling;
+};
+
+enum MenuType { Copy, Move };
+
+// The main menu, shown when opening "Copy To" or "Move To"
+// It contains Home Folder, Root Folder, Browse, and recent destinations
+class KFileCopyToMainMenu : public QMenu
+{
+ Q_OBJECT
+public:
+ KFileCopyToMainMenu(QMenu *parent, KFileCopyToMenuPrivate *d, MenuType menuType);
+
+ QActionGroup &actionGroup()
+ {
+ return m_actionGroup; // used by submenus
+ }
+ MenuType menuType() const
+ {
+ return m_menuType; // used by submenus
+ }
+
+private Q_SLOTS:
+ void slotAboutToShow();
+ void slotBrowse();
+ void slotTriggered(QAction *action);
+
+private:
+ void copyOrMoveTo(const QUrl &dest);
+
+private:
+ MenuType m_menuType;
+ QActionGroup m_actionGroup;
+ KFileCopyToMenuPrivate *d; // this isn't our own d pointer, it's the one for the public class
+ KConfigGroup m_recentDirsGroup;
+};
+
+// The menu that lists a directory
+class KFileCopyToDirectoryMenu : public QMenu
+{
+ Q_OBJECT
+public:
+ KFileCopyToDirectoryMenu(QMenu *parent, KFileCopyToMainMenu *mainMenu, const QString &path);
+
+private Q_SLOTS:
+ void slotAboutToShow();
+
+private:
+ KFileCopyToMainMenu *m_mainMenu;
+ QString m_path;
+};
--
To view, visit https://gerrit.vesnicky.cesnet.cz/r/305
To unsubscribe, visit https://gerrit.vesnicky.cesnet.cz/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I64baa301016bdd3a31b7590c10d1218d76a7cc3a
Gerrit-PatchSet: 1
Gerrit-Project: kio
Gerrit-Branch: master
Gerrit-Owner: David Faure <faure at kde.org>
More information about the Kde-frameworks-devel
mailing list