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