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 <fa...@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 <fa...@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 <fa...@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 <fa...@kde.org> _______________________________________________ Kde-frameworks-devel mailing list Kde-frameworks-devel@kde.org https://mail.kde.org/mailman/listinfo/kde-frameworks-devel