mirror of
https://bitbucket.org/smil3y/kdelibs.git
synced 2025-02-23 18:32:49 +00:00
683 lines
20 KiB
C++
683 lines
20 KiB
C++
/* This file is part of the KDE project
|
|
Copyright (C) 2000 Simon Hausmann <hausmann@kde.org>
|
|
Copyright (C) 2006, 2008 David Faure <faure@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) 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 "fileundomanager.h"
|
|
#include "fileundomanager_p.h"
|
|
#include "clipboardupdater_p.h"
|
|
|
|
#include <kdebug.h>
|
|
#include <kdirnotify.h>
|
|
#include <kglobal.h>
|
|
#include <kio/copyjob.h>
|
|
#include <kio/job.h>
|
|
#include <kio/jobuidelegate.h>
|
|
#include <klocale.h>
|
|
#include <kmessagebox.h>
|
|
#include <kjobtrackerinterface.h>
|
|
|
|
#include <assert.h>
|
|
|
|
using namespace KIO;
|
|
|
|
static const char* undoStateToString(UndoState state)
|
|
{
|
|
static const char* const s_undoStateToString[] = {
|
|
"MAKINGDIRS", "MOVINGFILES", "STATINGFILE", "REMOVINGDIRS", "REMOVINGLINKS"
|
|
};
|
|
return s_undoStateToString[state];
|
|
}
|
|
|
|
/**
|
|
* checklist:
|
|
* copy dir -> overwrite -> works
|
|
* move dir -> overwrite -> works
|
|
* copy dir -> rename -> works
|
|
* move dir -> rename -> works
|
|
*
|
|
* copy dir -> works
|
|
* move dir -> works
|
|
*
|
|
* copy files -> works
|
|
* move files -> works (TODO: optimize (change FileCopyJob to use the renamed arg for copyingDone)
|
|
*
|
|
* copy files -> overwrite -> works (sorry for your overwritten file...)
|
|
* move files -> overwrite -> works (sorry for your overwritten file...)
|
|
*
|
|
* copy files -> rename -> works
|
|
* move files -> rename -> works
|
|
*
|
|
* -> see also fileundomanagertest, which tests some of the above (but not renaming).
|
|
*
|
|
*/
|
|
|
|
class KIO::UndoJob : public KIO::Job
|
|
{
|
|
public:
|
|
UndoJob(bool showProgressInfo) : KIO::Job() {
|
|
if (showProgressInfo)
|
|
KIO::getJobTracker()->registerJob(this);
|
|
}
|
|
virtual ~UndoJob() {}
|
|
|
|
virtual void kill(bool) {
|
|
FileUndoManager::self()->d->stopUndo(true);
|
|
KIO::Job::doKill();
|
|
}
|
|
|
|
void emitCreatingDir(const KUrl &dir)
|
|
{ emit description(this, i18n("Creating directory"),
|
|
qMakePair(i18n("Directory"), dir.prettyUrl())); }
|
|
void emitMoving(const KUrl &src, const KUrl &dest)
|
|
{ emit description(this, i18n("Moving"),
|
|
qMakePair(i18nc("The source of a file operation", "Source"), src.prettyUrl()),
|
|
qMakePair(i18nc("The destination of a file operation", "Destination"), dest.prettyUrl())); }
|
|
void emitDeleting(const KUrl &url)
|
|
{ emit description(this, i18n("Deleting"),
|
|
qMakePair(i18n("File"), url.prettyUrl())); }
|
|
void emitResult() { KIO::Job::emitResult(); }
|
|
};
|
|
|
|
CommandRecorder::CommandRecorder(FileUndoManager::CommandType op, const KUrl::List &src, const KUrl &dst, KIO::Job *job)
|
|
: QObject(job)
|
|
{
|
|
m_cmd.m_type = op;
|
|
m_cmd.m_valid = true;
|
|
m_cmd.m_serialNumber = FileUndoManager::self()->newCommandSerialNumber();
|
|
m_cmd.m_src = src;
|
|
m_cmd.m_dst = dst;
|
|
connect(job, SIGNAL(result(KJob*)),
|
|
this, SLOT(slotResult(KJob*)));
|
|
|
|
// TODO whitelist, instead
|
|
if (op != FileUndoManager::Mkdir && op != FileUndoManager::Put) {
|
|
connect(job, SIGNAL(copyingDone(KIO::Job*,KUrl,KUrl,time_t,bool,bool)),
|
|
this, SLOT(slotCopyingDone(KIO::Job*,KUrl,KUrl,time_t,bool,bool)));
|
|
connect(job, SIGNAL(copyingLinkDone(KIO::Job*,KUrl,QString,KUrl)),
|
|
this, SLOT(slotCopyingLinkDone(KIO::Job*,KUrl,QString,KUrl)));
|
|
}
|
|
}
|
|
|
|
CommandRecorder::~CommandRecorder()
|
|
{
|
|
}
|
|
|
|
void CommandRecorder::slotResult(KJob *job)
|
|
{
|
|
if (job->error())
|
|
return;
|
|
|
|
FileUndoManager::self()->d->addCommand(m_cmd);
|
|
}
|
|
|
|
void CommandRecorder::slotCopyingDone(KIO::Job *job, const KUrl &from, const KUrl &to, time_t mtime, bool directory, bool renamed)
|
|
{
|
|
BasicOperation op;
|
|
op.m_valid = true;
|
|
op.m_type = directory ? BasicOperation::Directory : BasicOperation::File;
|
|
op.m_renamed = renamed;
|
|
op.m_src = from;
|
|
op.m_dst = to;
|
|
op.m_mtime = mtime;
|
|
|
|
if (m_cmd.m_type == FileUndoManager::Trash)
|
|
{
|
|
Q_ASSERT(to.protocol() == "trash");
|
|
const KIO::MetaData metaData = job->metaData();
|
|
KIO::MetaData::ConstIterator it = metaData.find("trashURL-" + from.path());
|
|
if (it != metaData.constEnd()) {
|
|
// Update URL
|
|
op.m_dst = it.value();
|
|
}
|
|
}
|
|
|
|
m_cmd.m_opStack.prepend(op);
|
|
}
|
|
|
|
// TODO merge the signals?
|
|
void CommandRecorder::slotCopyingLinkDone(KIO::Job *, const KUrl &from, const QString &target, const KUrl &to)
|
|
{
|
|
BasicOperation op;
|
|
op.m_valid = true;
|
|
op.m_type = BasicOperation::Link;
|
|
op.m_renamed = false;
|
|
op.m_src = from;
|
|
op.m_target = target;
|
|
op.m_dst = to;
|
|
op.m_mtime = -1;
|
|
m_cmd.m_opStack.prepend(op);
|
|
}
|
|
|
|
////
|
|
|
|
class KIO::FileUndoManagerSingleton
|
|
{
|
|
public:
|
|
FileUndoManager self;
|
|
};
|
|
K_GLOBAL_STATIC(KIO::FileUndoManagerSingleton, globalFileUndoManager)
|
|
|
|
FileUndoManager *FileUndoManager::self()
|
|
{
|
|
return &globalFileUndoManager->self;
|
|
}
|
|
|
|
|
|
// m_nextCommandIndex is initialized to a high number so that konqueror can
|
|
// assign low numbers to closed items loaded "on-demand" from a config file
|
|
// in KonqClosedWindowsManager::readConfig and thus maintaining the real
|
|
// order of the undo items.
|
|
FileUndoManagerPrivate::FileUndoManagerPrivate(FileUndoManager* qq)
|
|
: m_uiInterface(new FileUndoManager::UiInterface()),
|
|
m_undoJob(0), m_nextCommandIndex(1000), q(qq)
|
|
{
|
|
}
|
|
|
|
FileUndoManager::FileUndoManager()
|
|
{
|
|
d = new FileUndoManagerPrivate(this);
|
|
d->m_lock = false;
|
|
d->m_currentJob = 0;
|
|
}
|
|
|
|
FileUndoManager::~FileUndoManager()
|
|
{
|
|
delete d;
|
|
}
|
|
|
|
void FileUndoManager::recordJob(CommandType op, const KUrl::List &src, const KUrl &dst, KIO::Job *job)
|
|
{
|
|
// This records what the job does and calls addCommand when done
|
|
(void) new CommandRecorder(op, src, dst, job);
|
|
emit jobRecordingStarted(op);
|
|
}
|
|
|
|
void FileUndoManager::recordCopyJob(KIO::CopyJob* copyJob)
|
|
{
|
|
CommandType commandType;
|
|
switch (copyJob->operationMode()) {
|
|
case CopyJob::Copy:
|
|
commandType = Copy;
|
|
break;
|
|
case CopyJob::Move:
|
|
commandType = Move;
|
|
break;
|
|
case CopyJob::Link:
|
|
default: // prevent "wrong" compiler warning because of possibly uninitialized variable
|
|
commandType = Link;
|
|
break;
|
|
}
|
|
recordJob(commandType, copyJob->srcUrls(), copyJob->destUrl(), copyJob);
|
|
}
|
|
|
|
void FileUndoManagerPrivate::addCommand(const UndoCommand &cmd)
|
|
{
|
|
push(cmd);
|
|
emit q->jobRecordingFinished(cmd.m_type);
|
|
}
|
|
|
|
bool FileUndoManager::undoAvailable() const
|
|
{
|
|
return (d->m_commands.count() > 0) && !d->m_lock;
|
|
}
|
|
|
|
QString FileUndoManager::undoText() const
|
|
{
|
|
if (d->m_commands.isEmpty())
|
|
return i18n("Und&o");
|
|
|
|
FileUndoManager::CommandType t = d->m_commands.last().m_type;
|
|
switch(t) {
|
|
case FileUndoManager::Copy:
|
|
return i18n("Und&o: Copy");
|
|
case FileUndoManager::Link:
|
|
return i18n("Und&o: Link");
|
|
case FileUndoManager::Move:
|
|
return i18n("Und&o: Move");
|
|
case FileUndoManager::Rename:
|
|
return i18n("Und&o: Rename");
|
|
case FileUndoManager::Trash:
|
|
return i18n("Und&o: Trash");
|
|
case FileUndoManager::Mkdir:
|
|
return i18n("Und&o: Create Folder");
|
|
case FileUndoManager::Put:
|
|
return i18n("Und&o: Create File");
|
|
}
|
|
/* NOTREACHED */
|
|
return QString();
|
|
}
|
|
|
|
quint64 FileUndoManager::newCommandSerialNumber()
|
|
{
|
|
return ++(d->m_nextCommandIndex);
|
|
}
|
|
|
|
quint64 FileUndoManager::currentCommandSerialNumber() const
|
|
{
|
|
if(!d->m_commands.isEmpty())
|
|
{
|
|
const UndoCommand& cmd = d->m_commands.last();
|
|
assert(cmd.m_valid);
|
|
return cmd.m_serialNumber;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void FileUndoManager::undo()
|
|
{
|
|
if (d->m_commands.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
// Make a copy of the command to undo before slotPop() pops it.
|
|
UndoCommand cmd = d->m_commands.last();
|
|
assert(cmd.m_valid);
|
|
d->m_current = cmd;
|
|
|
|
BasicOperation::Stack& opStack = d->m_current.m_opStack;
|
|
// Note that opStack is empty for simple operations like Mkdir.
|
|
|
|
// Let's first ask for confirmation if we need to delete any file (#99898)
|
|
KUrl::List fileCleanupStack;
|
|
BasicOperation::Stack::Iterator it = opStack.begin();
|
|
for (; it != opStack.end() ; ++it) {
|
|
BasicOperation::Type type = (*it).m_type;
|
|
if (type == BasicOperation::File && d->m_current.m_type == FileUndoManager::Copy) {
|
|
fileCleanupStack.append((*it).m_dst);
|
|
}
|
|
}
|
|
if (d->m_current.m_type == FileUndoManager::Mkdir || d->m_current.m_type == FileUndoManager::Put) {
|
|
fileCleanupStack.append(d->m_current.m_dst);
|
|
}
|
|
if (!fileCleanupStack.isEmpty()) {
|
|
if (!d->m_uiInterface->confirmDeletion(fileCleanupStack)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
d->pop();
|
|
d->lock();
|
|
|
|
d->m_dirCleanupStack.clear();
|
|
d->m_dirStack.clear();
|
|
d->m_dirsToUpdate.clear();
|
|
|
|
d->m_undoState = MOVINGFILES;
|
|
|
|
// Let's have a look at the basic operations we need to undo.
|
|
// While we're at it, collect all links that should be deleted.
|
|
|
|
it = opStack.begin();
|
|
while (it != opStack.end()) // don't cache end() here, erase modifies it
|
|
{
|
|
bool removeBasicOperation = false;
|
|
BasicOperation::Type type = (*it).m_type;
|
|
if (type == BasicOperation::Directory && !(*it).m_renamed)
|
|
{
|
|
// If any directory has to be created/deleted, we'll start with that
|
|
d->m_undoState = MAKINGDIRS;
|
|
// Collect all the dirs that have to be created in case of a move undo.
|
|
if (d->m_current.isMoveCommand())
|
|
d->m_dirStack.push((*it).m_src);
|
|
// Collect all dirs that have to be deleted
|
|
// from the destination in both cases (copy and move).
|
|
d->m_dirCleanupStack.prepend((*it).m_dst);
|
|
removeBasicOperation = true;
|
|
}
|
|
else if (type == BasicOperation::Link)
|
|
{
|
|
d->m_fileCleanupStack.prepend((*it).m_dst);
|
|
|
|
removeBasicOperation = !d->m_current.isMoveCommand();
|
|
}
|
|
|
|
if (removeBasicOperation)
|
|
it = opStack.erase(it);
|
|
else
|
|
++it;
|
|
}
|
|
|
|
if (d->m_current.m_type == FileUndoManager::Put) {
|
|
d->m_fileCleanupStack.append(d->m_current.m_dst);
|
|
}
|
|
|
|
kDebug() << "starting with" << undoStateToString(d->m_undoState);
|
|
d->m_undoJob = new UndoJob(d->m_uiInterface->showProgressInfo());
|
|
d->undoStep();
|
|
}
|
|
|
|
void FileUndoManagerPrivate::stopUndo(bool step)
|
|
{
|
|
m_current.m_opStack.clear();
|
|
m_dirCleanupStack.clear();
|
|
m_fileCleanupStack.clear();
|
|
m_undoState = REMOVINGDIRS;
|
|
m_undoJob = 0;
|
|
|
|
if (m_currentJob)
|
|
m_currentJob->kill();
|
|
|
|
m_currentJob = 0;
|
|
|
|
if (step)
|
|
undoStep();
|
|
}
|
|
|
|
void FileUndoManagerPrivate::slotResult(KJob *job)
|
|
{
|
|
m_currentJob = 0;
|
|
if (job->error())
|
|
{
|
|
m_uiInterface->jobError(static_cast<KIO::Job*>(job));
|
|
delete m_undoJob;
|
|
stopUndo(false);
|
|
}
|
|
else if (m_undoState == STATINGFILE)
|
|
{
|
|
BasicOperation op = m_current.m_opStack.last();
|
|
// kDebug() << "stat result for " << op.m_dst;
|
|
KIO::StatJob* statJob = static_cast<KIO::StatJob*>(job);
|
|
time_t mtime = statJob->statResult().numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME, -1);
|
|
if (mtime != op.m_mtime) {
|
|
kDebug() << op.m_dst << "was modified after being copied!";
|
|
QDateTime srcTime; srcTime.setTime_t(op.m_mtime); srcTime = srcTime.toLocalTime();
|
|
QDateTime destTime; destTime.setTime_t(mtime); destTime = destTime.toLocalTime();
|
|
if (!m_uiInterface->copiedFileWasModified(op.m_src, op.m_dst, srcTime, destTime)) {
|
|
stopUndo(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
undoStep();
|
|
}
|
|
|
|
|
|
void FileUndoManagerPrivate::addDirToUpdate(const KUrl& url)
|
|
{
|
|
if (!m_dirsToUpdate.contains(url))
|
|
m_dirsToUpdate.prepend(url);
|
|
}
|
|
|
|
void FileUndoManagerPrivate::undoStep()
|
|
{
|
|
m_currentJob = 0;
|
|
|
|
if (m_undoState == MAKINGDIRS)
|
|
stepMakingDirectories();
|
|
|
|
if (m_undoState == MOVINGFILES || m_undoState == STATINGFILE)
|
|
stepMovingFiles();
|
|
|
|
if (m_undoState == REMOVINGLINKS)
|
|
stepRemovingLinks();
|
|
|
|
if (m_undoState == REMOVINGDIRS)
|
|
stepRemovingDirectories();
|
|
|
|
if (m_currentJob) {
|
|
if (m_uiInterface)
|
|
m_currentJob->ui()->setWindow(m_uiInterface->parentWidget());
|
|
QObject::connect(m_currentJob, SIGNAL(result(KJob*)),
|
|
this, SLOT(slotResult(KJob*)));
|
|
}
|
|
}
|
|
|
|
void FileUndoManagerPrivate::stepMakingDirectories()
|
|
{
|
|
if (!m_dirStack.isEmpty()) {
|
|
KUrl dir = m_dirStack.pop();
|
|
kDebug() << "creatingDir" << dir;
|
|
m_currentJob = KIO::mkdir(dir);
|
|
m_undoJob->emitCreatingDir(dir);
|
|
}
|
|
else
|
|
m_undoState = MOVINGFILES;
|
|
}
|
|
|
|
// Misnamed method: It moves files back, but it also
|
|
// renames directories back, recreates symlinks,
|
|
// deletes copied files, and restores trashed files.
|
|
void FileUndoManagerPrivate::stepMovingFiles()
|
|
{
|
|
if (!m_current.m_opStack.isEmpty())
|
|
{
|
|
BasicOperation op = m_current.m_opStack.last();
|
|
BasicOperation::Type type = op.m_type;
|
|
|
|
assert(op.m_valid);
|
|
if (type == BasicOperation::Directory)
|
|
{
|
|
if (op.m_renamed)
|
|
{
|
|
kDebug() << "rename" << op.m_dst << op.m_src;
|
|
m_currentJob = KIO::rename(op.m_dst, op.m_src, KIO::HideProgressInfo);
|
|
m_undoJob->emitMoving(op.m_dst, op.m_src);
|
|
}
|
|
else
|
|
assert(0); // this should not happen!
|
|
}
|
|
else if (type == BasicOperation::Link)
|
|
{
|
|
kDebug() << "symlink" << op.m_target << op.m_src;
|
|
m_currentJob = KIO::symlink(op.m_target, op.m_src, KIO::Overwrite | KIO::HideProgressInfo);
|
|
}
|
|
else if (m_current.m_type == FileUndoManager::Copy)
|
|
{
|
|
if (m_undoState == MOVINGFILES) // dest not stat'ed yet
|
|
{
|
|
// Before we delete op.m_dst, let's check if it was modified (#20532)
|
|
kDebug() << "stat" << op.m_dst;
|
|
m_currentJob = KIO::stat(op.m_dst, KIO::HideProgressInfo);
|
|
m_undoState = STATINGFILE; // temporarily
|
|
return; // no pop() yet, we'll finish the work in slotResult
|
|
}
|
|
else // dest was stat'ed, and the deletion was approved in slotResult
|
|
{
|
|
m_currentJob = KIO::file_delete(op.m_dst, KIO::HideProgressInfo);
|
|
m_undoJob->emitDeleting(op.m_dst);
|
|
m_undoState = MOVINGFILES;
|
|
}
|
|
}
|
|
else if (m_current.isMoveCommand()
|
|
|| m_current.m_type == FileUndoManager::Trash)
|
|
{
|
|
kDebug() << "file_move" << op.m_dst << op.m_src;
|
|
m_currentJob = KIO::file_move(op.m_dst, op.m_src, -1, KIO::Overwrite | KIO::HideProgressInfo);
|
|
KIO::ClipboardUpdater::create(m_currentJob, KIO::ClipboardUpdater::UpdateContent);
|
|
m_undoJob->emitMoving(op.m_dst, op.m_src);
|
|
}
|
|
|
|
m_current.m_opStack.removeLast();
|
|
// The above KIO jobs are lowlevel, they don't trigger KDirNotify notification
|
|
// So we need to do it ourselves (but schedule it to the end of the undo, to compress them)
|
|
KUrl url(op.m_dst);
|
|
url.setPath(url.directory());
|
|
addDirToUpdate(url);
|
|
|
|
url = op.m_src;
|
|
url.setPath(url.directory());
|
|
addDirToUpdate(url);
|
|
}
|
|
else
|
|
m_undoState = REMOVINGLINKS;
|
|
}
|
|
|
|
void FileUndoManagerPrivate::stepRemovingLinks()
|
|
{
|
|
kDebug() << "REMOVINGLINKS";
|
|
if (!m_fileCleanupStack.isEmpty())
|
|
{
|
|
KUrl file = m_fileCleanupStack.pop();
|
|
kDebug() << "file_delete" << file;
|
|
m_currentJob = KIO::file_delete(file, KIO::HideProgressInfo);
|
|
m_undoJob->emitDeleting(file);
|
|
|
|
KUrl url(file);
|
|
url.setPath(url.directory());
|
|
addDirToUpdate(url);
|
|
}
|
|
else
|
|
{
|
|
m_undoState = REMOVINGDIRS;
|
|
|
|
if (m_dirCleanupStack.isEmpty() && m_current.m_type == FileUndoManager::Mkdir)
|
|
m_dirCleanupStack << m_current.m_dst;
|
|
}
|
|
}
|
|
|
|
void FileUndoManagerPrivate::stepRemovingDirectories()
|
|
{
|
|
if (!m_dirCleanupStack.isEmpty())
|
|
{
|
|
KUrl dir = m_dirCleanupStack.pop();
|
|
kDebug() << "rmdir" << dir;
|
|
m_currentJob = KIO::rmdir(dir);
|
|
m_undoJob->emitDeleting(dir);
|
|
addDirToUpdate(dir);
|
|
} else {
|
|
m_current.m_valid = false;
|
|
m_currentJob = 0;
|
|
if (m_undoJob)
|
|
{
|
|
kDebug() << "deleting undojob";
|
|
m_undoJob->emitResult();
|
|
m_undoJob = 0;
|
|
}
|
|
foreach (const KUrl it, m_dirsToUpdate) {
|
|
kDebug() << "Notifying FilesAdded for " << it;
|
|
org::kde::KDirNotify::emitFilesAdded(it.url());
|
|
}
|
|
emit q->undoJobFinished();
|
|
unlock();
|
|
}
|
|
}
|
|
|
|
void FileUndoManagerPrivate::push(const UndoCommand &cmd)
|
|
{
|
|
m_commands.append(cmd);
|
|
emit q->undoAvailable(true);
|
|
emit q->undoTextChanged(q->undoText());
|
|
}
|
|
|
|
void FileUndoManagerPrivate::pop()
|
|
{
|
|
m_commands.removeLast();
|
|
emit q->undoAvailable(q->undoAvailable());
|
|
emit q->undoTextChanged(q->undoText());
|
|
}
|
|
|
|
void FileUndoManagerPrivate::lock()
|
|
{
|
|
// assert(!m_lock);
|
|
m_lock = true;
|
|
emit q->undoAvailable(q->undoAvailable());
|
|
}
|
|
|
|
void FileUndoManagerPrivate::unlock()
|
|
{
|
|
// assert(m_lock);
|
|
m_lock = false;
|
|
emit q->undoAvailable(q->undoAvailable());
|
|
}
|
|
|
|
void FileUndoManager::setUiInterface(UiInterface* ui)
|
|
{
|
|
delete d->m_uiInterface;
|
|
d->m_uiInterface = ui;
|
|
}
|
|
|
|
FileUndoManager::UiInterface* FileUndoManager::uiInterface() const
|
|
{
|
|
return d->m_uiInterface;
|
|
}
|
|
|
|
////
|
|
|
|
class FileUndoManager::UiInterface::UiInterfacePrivate
|
|
{
|
|
public:
|
|
UiInterfacePrivate()
|
|
: m_parentWidget(0), m_showProgressInfo(true)
|
|
{}
|
|
QWidget* m_parentWidget;
|
|
bool m_showProgressInfo;
|
|
};
|
|
|
|
FileUndoManager::UiInterface::UiInterface()
|
|
: d(new UiInterfacePrivate)
|
|
{
|
|
}
|
|
|
|
FileUndoManager::UiInterface::~UiInterface()
|
|
{
|
|
delete d;
|
|
}
|
|
|
|
void FileUndoManager::UiInterface::jobError(KIO::Job* job)
|
|
{
|
|
job->ui()->showErrorMessage();
|
|
}
|
|
|
|
bool FileUndoManager::UiInterface::copiedFileWasModified(const KUrl& src, const KUrl& dest, const QDateTime& srcTime, const QDateTime& destTime)
|
|
{
|
|
Q_UNUSED(srcTime); // not sure it should appear in the msgbox
|
|
// Possible improvement: only show the time if date is today
|
|
const QString timeStr = KGlobal::locale()->formatDateTime(destTime, QLocale::ShortFormat);
|
|
return KMessageBox::warningContinueCancel(
|
|
d->m_parentWidget,
|
|
i18n("The file %1 was copied from %2, but since then it has apparently been modified at %3.\n"
|
|
"Undoing the copy will delete the file, and all modifications will be lost.\n"
|
|
"Are you sure you want to delete %4?", dest.pathOrUrl(), src.pathOrUrl(), timeStr, dest.pathOrUrl()),
|
|
i18n("Undo File Copy Confirmation"),
|
|
KStandardGuiItem::cont(),
|
|
KStandardGuiItem::cancel(),
|
|
QString(),
|
|
KMessageBox::Notify | KMessageBox::Dangerous) == KMessageBox::Continue;
|
|
}
|
|
|
|
bool FileUndoManager::UiInterface::confirmDeletion(const KUrl::List& files)
|
|
{
|
|
KIO::JobUiDelegate uiDelegate;
|
|
uiDelegate.setWindow(d->m_parentWidget);
|
|
// Because undo can happen with an accidental Ctrl-Z, we want to always confirm.
|
|
return uiDelegate.askDeleteConfirmation(files, KIO::JobUiDelegate::Delete, KIO::JobUiDelegate::ForceConfirmation);
|
|
}
|
|
|
|
QWidget* FileUndoManager::UiInterface::parentWidget() const
|
|
{
|
|
return d->m_parentWidget;
|
|
}
|
|
|
|
void FileUndoManager::UiInterface::setParentWidget(QWidget* parentWidget)
|
|
{
|
|
d->m_parentWidget = parentWidget;
|
|
}
|
|
|
|
void FileUndoManager::UiInterface::setShowProgressInfo(bool b)
|
|
{
|
|
d->m_showProgressInfo = b;
|
|
}
|
|
|
|
bool FileUndoManager::UiInterface::showProgressInfo() const
|
|
{
|
|
return d->m_showProgressInfo;
|
|
}
|
|
|
|
#include "moc_fileundomanager_p.cpp"
|
|
#include "moc_fileundomanager.cpp"
|