diff --git a/krusader/Panel/krpopupmenu.cpp b/krusader/Panel/krpopupmenu.cpp --- a/krusader/Panel/krpopupmenu.cpp +++ b/krusader/Panel/krpopupmenu.cpp @@ -221,9 +221,8 @@ addAction(i18n("Rename"))->setData(QVariant(RENAME_ID)); // -------- MOVE TO TRASH - KConfigGroup saver(krConfig, "General"); - bool trash = saver.readEntry("Move To Trash", _MoveToTrash); - if (trash && !inTrash) + if (KConfigGroup(krConfig, "General").readEntry("Move To Trash", _MoveToTrash) + && panel->func->files()->canMoveToTrash(fileNames)) addAction(i18n("Move to Trash"))->setData(QVariant(TRASH_ID)); // -------- DELETE addAction(i18n("Delete"))->setData(QVariant(DELETE_ID)); diff --git a/krusader/Panel/panelfunc.h b/krusader/Panel/panelfunc.h --- a/krusader/Panel/panelfunc.h +++ b/krusader/Panel/panelfunc.h @@ -86,6 +86,8 @@ */ void mkdir(); void deleteFiles(bool reallyDelete = false); + // delete virtual files or directories in virtual filesystem + void removeVirtualFiles(); void rename(); void krlink(bool sym = true); void createChecksum(); diff --git a/krusader/Panel/panelfunc.cpp b/krusader/Panel/panelfunc.cpp --- a/krusader/Panel/panelfunc.cpp +++ b/krusader/Panel/panelfunc.cpp @@ -706,33 +706,34 @@ } // for } -// TODO it is not possible to move virtual local files to trash void ListPanelFunc::deleteFiles(bool reallyDelete) { + if (files()->type() == vfs::VFS_VIRT && files()->isRoot()) { + // only virtual deletion possible + removeVirtualFiles(); + return; + } + // first get the selected file names list QStringList fileNames = panel->getSelectedNames(); if (fileNames.isEmpty()) - return ; + return; - KConfigGroup gg(krConfig, "General"); - bool trash = gg.readEntry("Move To Trash", _MoveToTrash); - // now ask the user if he want to delete: - KConfigGroup group(krConfig, "Advanced"); - if (group.readEntry("Confirm Delete", _ConfirmDelete)) { - QString s; - KGuiItem b; + const KConfigGroup generalGroup(krConfig, "General"); + bool moveToTrash = !reallyDelete && generalGroup.readEntry("Move To Trash", _MoveToTrash); + // make sure this is possible + moveToTrash = moveToTrash && files()->canMoveToTrash(fileNames); - if (!reallyDelete && trash && files()->isLocal()) { + // now ask the user if he/she is sure: + const KConfigGroup advancedGroup(krConfig, "Advanced"); + if (advancedGroup.readEntry("Confirm Delete", _ConfirmDelete)) { + QString s; // text + KGuiItem b; // continue button + + if (moveToTrash) { s = i18np("Do you really want to move this item to the trash?", "Do you really want to move these %1 items to the trash?", fileNames.count()); b = KGuiItem(i18n("&Trash")); - } else if (files()->type() == vfs::VFS_VIRT && files()->isRoot()) { - s = i18np( - "Do you really want to delete this virtual item (physical files stay untouched)?", - "Do you really want to delete these %1 virtual items (physical files stay " - "untouched)?", - fileNames.count()); - b = KStandardGuiItem::del(); } else if (files()->type() == vfs::VFS_VIRT) { s = i18np("Do you really want to delete this item physically (not just " "removing it from the virtual items)?", @@ -748,49 +749,75 @@ // show message // note: i'm using continue and not yes/no because the yes/no has cancel as default button - if (KMessageBox::warningContinueCancelList(krMainWindow, s, fileNames, - i18n("Warning"), b) != KMessageBox::Continue) - return ; + if (KMessageBox::warningContinueCancelList(krMainWindow, s, fileNames, i18n("Warning"), + b) != KMessageBox::Continue) + return; } + // we want to warn the user about non empty dir - // and files he don't have permission to delete - bool emptyDirVerify = group.readEntry("Confirm Unempty Dir", _ConfirmUnemptyDir); - emptyDirVerify = (emptyDirVerify && files()->isLocal()); - - QDir dir; - for (QStringList::Iterator name = fileNames.begin(); name != fileNames.end();) { - vfile * vf = files()->getVfile(*name); - - // verify non-empty dirs delete... (only for local vfs) - if (vf && emptyDirVerify && vf->vfile_isDir() && !vf->vfile_isSymLink()) { - dir.setPath(panel->virtualPath().path() + '/' + (*name)); - if (dir.entryList(QDir::TypeMask | QDir::System | QDir::Hidden).count() > 2) { - switch (KMessageBox::warningYesNoCancel(krMainWindow, - i18n("

Folder %1 is not empty.

Skip this one or delete all?

", *name), - QString(), KGuiItem(i18n("&Skip")), KGuiItem(i18n("&Delete All")))) { - case KMessageBox::No : - emptyDirVerify = false; - break; - case KMessageBox::Yes : - name = fileNames.erase(name); - continue; - default : - return ; + bool emptyDirVerify = advancedGroup.readEntry("Confirm Unempty Dir", _ConfirmUnemptyDir); + // TODO only local fs supported + emptyDirVerify &= files()->isLocal(); + + if (emptyDirVerify) { + for (const QString fileName: fileNames) { + vfile *vfile = files()->getVfile(fileName); + if (vfile && !vfile->vfile_isSymLink() && vfile->vfile_isDir()) { + // read local dir... + const QDir dir(vfile->vfile_getUrl().path()); + if (dir.entryList(QDir::TypeMask | QDir::System | QDir::Hidden).count() > 2) { + // ...is not empty, ask user + const KMessageBox::ButtonCode result = KMessageBox::warningYesNoCancel( + krMainWindow, + i18n("

Folder %1 is not empty.

Skip this one " + "or delete all?

", + fileName), + QString(), KGuiItem(i18n("&Skip")), KGuiItem(i18n("&Delete All"))); + if (result == KMessageBox::Yes) { + fileNames.removeAll(fileName); // skip + } else if (result == KMessageBox::No) { + break; // accept all remaining + } else { + return; // cancel + } } } } - ++name; } if (fileNames.count() == 0) - return ; // nothing to delete + return; // nothing to delete // after the delete return the cursor to the first unmarked // file above the current item; panel->prepareToDelete(); // let the vfs do the job... - files()->deleteFiles(fileNames, reallyDelete); + files()->deleteFiles(fileNames, moveToTrash); +} + +void ListPanelFunc::removeVirtualFiles() +{ + if (files()->type() != vfs::VFS_VIRT) { + krOut << "filesystem not virtual"; + return; + } + + const QStringList fileNames = panel->getSelectedNames(); + if (fileNames.isEmpty()) + return; + + const QString text = + i18np("Do you really want to delete this virtual item (physical files stay untouched)?", + "Do you really want to delete these %1 virtual items (physical files stay " + "untouched)?", + fileNames.count()); + if (KMessageBox::warningContinueCancelList(krMainWindow, text, fileNames, i18n("Warning"), + KStandardGuiItem::remove()) != KMessageBox::Continue) + return; + + virt_vfs *vfs = static_cast(files()); + vfs->remove(fileNames); } void ListPanelFunc::goInside(const QString& name) diff --git a/krusader/VFS/default_vfs.h b/krusader/VFS/default_vfs.h --- a/krusader/VFS/default_vfs.h +++ b/krusader/VFS/default_vfs.h @@ -63,12 +63,11 @@ void addFiles(const QList &fileUrls, KIO::CopyJob::CopyMode mode, QString dir = "") Q_DECL_OVERRIDE; - void deleteFiles(const QStringList &fileNames, - bool forceDeletion = false) Q_DECL_OVERRIDE; void mkDir(const QString &name) Q_DECL_OVERRIDE; void rename(const QString &fileName, const QString &newName) Q_DECL_OVERRIDE; /// Return URL for file name - even if file does not exist. QUrl getUrl(const QString &name) Q_DECL_OVERRIDE; + bool canMoveToTrash(const QStringList &) Q_DECL_OVERRIDE { return isLocal(); } QString mountPoint() { return _mountPoint; } bool hasAutoUpdate() Q_DECL_OVERRIDE { return !_watcher.isNull(); } @@ -91,7 +90,6 @@ private: void connectSourceVFS(KJob *job, const QList urls); - void connectJob(KJob *job, const QUrl &destination); bool refreshLocal(const QUrl &directory); // NOTE: this is very fast vfile *createLocalVFile(const QString &name); diff --git a/krusader/VFS/default_vfs.cpp b/krusader/VFS/default_vfs.cpp --- a/krusader/VFS/default_vfs.cpp +++ b/krusader/VFS/default_vfs.cpp @@ -117,28 +117,6 @@ copyFiles(fileUrls, destination, mode); } -void default_vfs::deleteFiles(const QStringList &fileNames, bool forceDeletion) -{ - // get absolute URLs for file names - const QList fileUrls = getUrls(fileNames); - - // delete or move to trash? - const KConfigGroup group(krConfig, "General"); - const bool moveToTrash = !forceDeletion && isLocal() - && group.readEntry("Move To Trash", _MoveToTrash); - KrJob *krJob = KrJob::createDeleteJob(fileUrls, moveToTrash); - connect(krJob, &KrJob::started, [=](KIO::Job *job) { connectJob(job, currentDirectory()); }); - if (moveToTrash) { - // update destination: the trash bin (in case a panel/tab is showing it) - connect(krJob, &KrJob::started, [=](KIO::Job *job) { - // Note: the "trash" protocal should always have only one "/" after the "scheme:" part - connect(job, &KIO::Job::result, [=]() { emit filesystemChanged(QUrl("trash:/")); }); - }); - } - - krJobMan->manageJob(krJob); -} - void default_vfs::mkDir(const QString &name) { KIO::SimpleJob* job = KIO::mkdir(getUrl(name)); @@ -155,14 +133,6 @@ KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Rename, {oldUrl}, newUrl, job); } -void default_vfs::connectJob(KJob *job, const QUrl &destination) -{ - // (additional) direct refresh if on local fs because watcher is too slow - const bool refresh = cleanUrl(destination) == _currentDirectory && isLocal(); - connect(job, &KIO::Job::result, this, [=](KJob* job) { slotJobResult(job, refresh); }); - connect(job, &KIO::Job::result, [=]() { emit filesystemChanged(destination); }); -} - QUrl default_vfs::getUrl(const QString& name) { // NOTE: on non-local fs file URL does not have to be path + name! diff --git a/krusader/VFS/vfs.h b/krusader/VFS/vfs.h --- a/krusader/VFS/vfs.h +++ b/krusader/VFS/vfs.h @@ -95,8 +95,6 @@ /// directory name relative to the current dir. May implemented async. virtual void addFiles(const QList &fileUrls, KIO::CopyJob::CopyMode mode, QString dir = "") = 0; - /// Delete or move a file in the current directory to trash. May implemented async. - virtual void deleteFiles(const QStringList &fileNames, bool reallyDelete = false) = 0; /// Create a new directory in the current directory. May implemented async. virtual void mkDir(const QString &name) = 0; /// Rename file/directory in the current directory. May implemented async. @@ -107,6 +105,8 @@ virtual QUrl getUrl(const QString &name) = 0; /// Return a list of URLs for multiple files/directories in the current directory. QList getUrls(const QStringList &names); + /// Return true if all files can be moved to trash, else false. + virtual bool canMoveToTrash(const QStringList &fileNames) = 0; /// Return the filesystem mount point of the current directory. Empty string by default. virtual QString mountPoint() { return QString(); } @@ -137,6 +137,8 @@ } /// Returns true if this VFS is currently refreshing the current directory. inline bool isRefreshing() { return _isRefreshing; } + /// Delete or trash files in the current directory. Implemented async. + void deleteFiles(const QStringList &fileNames, bool moveToTrash = true); /// Calculate the amount of space occupied by a file or directory in the current directory /// (recursive). @@ -182,6 +184,8 @@ /// Fill the vfs dictionary with vfiles, must be implemented for each VFS. virtual bool refreshInternal(const QUrl &origin, bool showHidden) = 0; + /// Connect the result signal of a file operation job. + void connectJob(KJob *job, const QUrl &destination); /// Returns true if showing hidden files is set in config. bool showHiddenFiles(); /// Add a new vfile to the internal dictionary (while refreshing). diff --git a/krusader/VFS/vfs.cpp b/krusader/VFS/vfs.cpp --- a/krusader/VFS/vfs.cpp +++ b/krusader/VFS/vfs.cpp @@ -48,9 +48,10 @@ #include "../defaults.h" #include "../krglobal.h" +#include "../JobMan/jobman.h" +#include "../JobMan/krjob.h" #include "krpermhandler.h" - vfs::vfs() : VfileContainer(0), _isRefreshing(false) {} vfs::~vfs() @@ -177,8 +178,34 @@ return true; } +void vfs::deleteFiles(const QStringList &fileNames, bool moveToTrash) +{ + // get absolute URLs for file names + const QList fileUrls = getUrls(fileNames); + + KrJob *krJob = KrJob::createDeleteJob(fileUrls, moveToTrash); + connect(krJob, &KrJob::started, [=](KIO::Job *job) { connectJob(job, currentDirectory()); }); + if (moveToTrash) { + // update destination: the trash bin (in case a panel/tab is showing it) + connect(krJob, &KrJob::started, [=](KIO::Job *job) { + // Note: the "trash" protocal should always have only one "/" after the "scheme:" part + connect(job, &KIO::Job::result, [=]() { emit filesystemChanged(QUrl("trash:/")); }); + }); + } + + krJobMan->manageJob(krJob); +} + // ==== protected ==== +void vfs::connectJob(KJob *job, const QUrl &destination) +{ + // (additional) direct refresh if on local fs because watcher is too slow + const bool refresh = cleanUrl(destination) == _currentDirectory && isLocal(); + connect(job, &KIO::Job::result, this, [=](KJob* job) { slotJobResult(job, refresh); }); + connect(job, &KIO::Job::result, [=]() { emit filesystemChanged(destination); }); +} + bool vfs::showHiddenFiles() { const KConfigGroup gl(krConfig, "Look&Feel"); diff --git a/krusader/VFS/virt_vfs.h b/krusader/VFS/virt_vfs.h --- a/krusader/VFS/virt_vfs.h +++ b/krusader/VFS/virt_vfs.h @@ -54,20 +54,20 @@ /// Add virtual files to the current directory. void addFiles(const QList &fileUrls, KIO::CopyJob::CopyMode mode = KIO::CopyJob::Copy, QString dir = "") Q_DECL_OVERRIDE; - /// Delete files from the current directory (real files, not virtual). - void deleteFiles(const QStringList &fileNames, bool reallyDelete = true) Q_DECL_OVERRIDE; - /// Remove files from the collection (only virtual, not the real file). - void vfs_removeFiles(QStringList *fileNames); /// Create a virtual directory. Only possible in the root directory. void mkDir(const QString &name) Q_DECL_OVERRIDE; /// Rename a (real) file in the current directory. void rename(const QString &fileName, const QString &newName) Q_DECL_OVERRIDE; void calcSpace(const QString &name, KIO::filesize_t *totalSize, unsigned long *totalFiles, unsigned long *totalDirs, bool *stop) Q_DECL_OVERRIDE; /// Returns the URL of the real file or an empty URL if file with name does not exist. QUrl getUrl(const QString& name) Q_DECL_OVERRIDE; + bool canMoveToTrash(const QStringList &fileNames) Q_DECL_OVERRIDE; - void setMetaInformation(QString info); + /// Remove virtual files or directories. Real files stay untouched. + void remove(const QStringList &fileNames); + /// Set meta information to be displayed in UI for the current directory + void setMetaInformation(const QString &info); protected: bool refreshInternal(const QUrl &origin, bool showHidden) Q_DECL_OVERRIDE; diff --git a/krusader/VFS/virt_vfs.cpp b/krusader/VFS/virt_vfs.cpp --- a/krusader/VFS/virt_vfs.cpp +++ b/krusader/VFS/virt_vfs.cpp @@ -97,46 +97,23 @@ copyFiles(fileUrls, destination); } -void virt_vfs::deleteFiles(const QStringList &fileNames, bool reallyDelete) +void virt_vfs::remove(const QStringList &fileNames) { - if (currentDir() == "/") { // remove virtual directory + const QString parentDir = currentDir(); + if (parentDir == "/") { // remove virtual directory for (const QString &filename : fileNames) { _virtVfsDict["/"]->removeAll(QUrl(QStringLiteral("virt:/") + filename)); delete _virtVfsDict[filename]; _virtVfsDict.remove(filename); _metaInfoDict.remove(filename); } - emit filesystemChanged(currentDirectory()); // will call refresh() - return; - } - - // names -> urls - QList filesUrls = getUrls(fileNames); - - // delete or move to trash? - KIO::Job *job; - KConfigGroup group(krConfig, "General"); - if (!reallyDelete && group.readEntry("Move To Trash", _MoveToTrash)) { - job = KIO::trash(filesUrls); } else { - job = KIO::del(filesUrls); - } - - connect(job, &KIO::Job::result, this, [=](KJob* job) { slotJobResult(job, false); }); - // refresh will remove the deleted files from the vfs dict... - connect(job, &KIO::Job::result, [=]() { emit filesystemChanged(currentDirectory()); }); -} - -void virt_vfs::vfs_removeFiles(QStringList *fileNames) -{ - if (currentDir() == "/") - return; - - // removing the URLs from the collection - for (int i = 0; i < fileNames->count(); ++i) { - if (_virtVfsDict.find(currentDir()) != _virtVfsDict.end()) { - QList *urlList = _virtVfsDict[currentDir()]; - urlList->removeAll(getUrl((*fileNames)[i])); + // remove the URLs from the collection + for (const QString name : fileNames) { + if (_virtVfsDict.find(parentDir) != _virtVfsDict.end()) { + QList *urlList = _virtVfsDict[parentDir]; + urlList->removeAll(getUrl(name)); + } } } @@ -214,7 +191,20 @@ vfs::calcSpace(name, totalSize, totalFiles, totalDirs, stop); } -void virt_vfs::setMetaInformation(QString info) +bool virt_vfs::canMoveToTrash(const QStringList &fileNames) +{ + if (isRoot()) + return false; + + for (const QString fileName : fileNames) { + if (!getUrl(fileName).isLocalFile()) { + return false; + } + } + return true; +} + +void virt_vfs::setMetaInformation(const QString &info) { _metaInfoDict[currentDir()] = info; }