KIO
fileundomanager.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE project 00002 Copyright (C) 2000 Simon Hausmann <hausmann@kde.org> 00003 Copyright (C) 2006, 2008 David Faure <faure@kde.org> 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Library General Public 00007 License as published by the Free Software Foundation; either 00008 version 2 of the License, or (at your option) any later version. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Library General Public License for more details. 00014 00015 You should have received a copy of the GNU Library General Public License 00016 along with this library; see the file COPYING.LIB. If not, write to 00017 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00018 Boston, MA 02110-1301, USA. 00019 */ 00020 00021 #include "fileundomanager.h" 00022 #include "fileundomanager_p.h" 00023 #include "fileundomanager_adaptor.h" 00024 00025 #include <kdatetime.h> 00026 #include <kdebug.h> 00027 #include <kdirnotify.h> 00028 #include <kglobal.h> 00029 #include <kio/copyjob.h> 00030 #include <kio/job.h> 00031 #include <kio/jobuidelegate.h> 00032 #include <klocale.h> 00033 #include <kmessagebox.h> 00034 #include <kjobtrackerinterface.h> 00035 00036 #include <QtDBus/QtDBus> 00037 00038 #include <assert.h> 00039 00040 using namespace KIO; 00041 00042 static const char* undoStateToString(UndoState state) { 00043 static const char* const s_undoStateToString[] = { "MAKINGDIRS", "MOVINGFILES", "STATINGFILE", "REMOVINGDIRS", "REMOVINGLINKS" }; 00044 return s_undoStateToString[state]; 00045 } 00046 00047 static QDataStream &operator<<(QDataStream &stream, const KIO::BasicOperation &op) 00048 { 00049 stream << op.m_valid << (qint8)op.m_type << op.m_renamed 00050 << op.m_src << op.m_dst << op.m_target << (qint64)op.m_mtime; 00051 return stream; 00052 } 00053 static QDataStream &operator>>(QDataStream &stream, BasicOperation &op) 00054 { 00055 qint8 type; 00056 qint64 mtime; 00057 stream >> op.m_valid >> type >> op.m_renamed 00058 >> op.m_src >> op.m_dst >> op.m_target >> mtime; 00059 op.m_type = static_cast<BasicOperation::Type>(type); 00060 op.m_mtime = mtime; 00061 return stream; 00062 } 00063 00064 static QDataStream &operator<<(QDataStream &stream, const UndoCommand &cmd) 00065 { 00066 stream << cmd.m_valid << (qint8)cmd.m_type << cmd.m_opStack << cmd.m_src << cmd.m_dst; 00067 return stream; 00068 } 00069 00070 static QDataStream &operator>>(QDataStream &stream, UndoCommand &cmd) 00071 { 00072 qint8 type; 00073 stream >> cmd.m_valid >> type >> cmd.m_opStack >> cmd.m_src >> cmd.m_dst; 00074 cmd.m_type = static_cast<FileUndoManager::CommandType>(type); 00075 return stream; 00076 } 00077 00101 class KIO::UndoJob : public KIO::Job 00102 { 00103 public: 00104 UndoJob(bool showProgressInfo) : KIO::Job() { 00105 if (showProgressInfo) 00106 KIO::getJobTracker()->registerJob(this); 00107 } 00108 virtual ~UndoJob() {} 00109 00110 virtual void kill(bool) { 00111 FileUndoManager::self()->d->stopUndo(true); 00112 KIO::Job::doKill(); 00113 } 00114 00115 void emitCreatingDir(const KUrl &dir) 00116 { emit description(this, i18n("Creating directory"), 00117 qMakePair(i18n("Directory"), dir.prettyUrl())); } 00118 void emitMoving(const KUrl &src, const KUrl &dest) 00119 { emit description(this, i18n("Moving"), 00120 qMakePair(i18nc("The source of a file operation", "Source"), src.prettyUrl()), 00121 qMakePair(i18nc("The destination of a file operation", "Destination"), dest.prettyUrl())); } 00122 void emitDeleting(const KUrl &url) 00123 { emit description(this, i18n("Deleting"), 00124 qMakePair(i18n("File"), url.prettyUrl())); } 00125 void emitResult() { KIO::Job::emitResult(); } 00126 }; 00127 00128 CommandRecorder::CommandRecorder(FileUndoManager::CommandType op, const KUrl::List &src, const KUrl &dst, KIO::Job *job) 00129 : QObject(job) 00130 { 00131 m_cmd.m_type = op; 00132 m_cmd.m_valid = true; 00133 m_cmd.m_serialNumber = FileUndoManager::self()->newCommandSerialNumber(); 00134 m_cmd.m_src = src; 00135 m_cmd.m_dst = dst; 00136 connect(job, SIGNAL(result(KJob*)), 00137 this, SLOT(slotResult(KJob*))); 00138 00139 // TODO whitelist, instead 00140 if (op != FileUndoManager::Mkdir && op != FileUndoManager::Put) { 00141 connect(job, SIGNAL(copyingDone(KIO::Job*,KUrl,KUrl,time_t,bool,bool)), 00142 this, SLOT(slotCopyingDone(KIO::Job*,KUrl,KUrl,time_t,bool,bool))); 00143 connect(job, SIGNAL(copyingLinkDone(KIO::Job *,KUrl,QString,KUrl)), 00144 this, SLOT(slotCopyingLinkDone(KIO::Job *,KUrl,QString,KUrl))); 00145 } 00146 } 00147 00148 CommandRecorder::~CommandRecorder() 00149 { 00150 } 00151 00152 void CommandRecorder::slotResult(KJob *job) 00153 { 00154 if (job->error()) 00155 return; 00156 00157 FileUndoManager::self()->d->addCommand(m_cmd); 00158 } 00159 00160 void CommandRecorder::slotCopyingDone(KIO::Job *job, const KUrl &from, const KUrl &to, time_t mtime, bool directory, bool renamed) 00161 { 00162 BasicOperation op; 00163 op.m_valid = true; 00164 op.m_type = directory ? BasicOperation::Directory : BasicOperation::File; 00165 op.m_renamed = renamed; 00166 op.m_src = from; 00167 op.m_dst = to; 00168 op.m_mtime = mtime; 00169 00170 if (m_cmd.m_type == FileUndoManager::Trash) 00171 { 00172 Q_ASSERT(to.protocol() == "trash"); 00173 const QMap<QString, QString> metaData = job->metaData(); 00174 QMap<QString, QString>::ConstIterator it = metaData.find("trashURL-" + from.path()); 00175 if (it != metaData.constEnd()) { 00176 // Update URL 00177 op.m_dst = it.value(); 00178 } 00179 } 00180 00181 m_cmd.m_opStack.prepend(op); 00182 } 00183 00184 // TODO merge the signals? 00185 void CommandRecorder::slotCopyingLinkDone(KIO::Job *, const KUrl &from, const QString &target, const KUrl &to) 00186 { 00187 BasicOperation op; 00188 op.m_valid = true; 00189 op.m_type = BasicOperation::Link; 00190 op.m_renamed = false; 00191 op.m_src = from; 00192 op.m_target = target; 00193 op.m_dst = to; 00194 op.m_mtime = -1; 00195 m_cmd.m_opStack.prepend(op); 00196 } 00197 00199 00200 class KIO::FileUndoManagerSingleton 00201 { 00202 public: 00203 FileUndoManager self; 00204 }; 00205 K_GLOBAL_STATIC(KIO::FileUndoManagerSingleton, globalFileUndoManager) 00206 00207 FileUndoManager *FileUndoManager::self() 00208 { 00209 return &globalFileUndoManager->self; 00210 } 00211 00212 00213 // m_nextCommandIndex is initialized to a high number so that konqueror can 00214 // assign low numbers to closed items loaded "on-demand" from a config file 00215 // in KonqClosedWindowsManager::readConfig and thus maintaining the real 00216 // order of the undo items. 00217 FileUndoManagerPrivate::FileUndoManagerPrivate(FileUndoManager* qq) 00218 : m_uiInterface(new FileUndoManager::UiInterface()), 00219 m_undoJob(0), m_nextCommandIndex(1000), q(qq) 00220 { 00221 m_syncronized = initializeFromKDesky(); 00222 (void) new KIOFileUndoManagerAdaptor(this); 00223 const QString dbusPath = "/FileUndoManager"; 00224 const QString dbusInterface = "org.kde.kio.FileUndoManager"; 00225 00226 QDBusConnection dbus = QDBusConnection::sessionBus(); 00227 dbus.registerObject(dbusPath, this); 00228 dbus.connect(QString(), dbusPath, dbusInterface, "lock", this, SLOT(slotLock())); 00229 dbus.connect(QString(), dbusPath, dbusInterface, "pop", this, SLOT(slotPop())); 00230 dbus.connect(QString(), dbusPath, dbusInterface, "push", this, SLOT(slotPush(QByteArray))); 00231 dbus.connect(QString(), dbusPath, dbusInterface, "unlock", this, SLOT(slotUnlock())); 00232 } 00233 00234 FileUndoManager::FileUndoManager() 00235 { 00236 d = new FileUndoManagerPrivate(this); 00237 d->m_lock = false; 00238 d->m_currentJob = 0; 00239 } 00240 00241 FileUndoManager::~FileUndoManager() 00242 { 00243 delete d; 00244 } 00245 00246 void FileUndoManager::recordJob(CommandType op, const KUrl::List &src, const KUrl &dst, KIO::Job *job) 00247 { 00248 // This records what the job does and calls addCommand when done 00249 (void) new CommandRecorder(op, src, dst, job); 00250 emit jobRecordingStarted(op); 00251 } 00252 00253 void FileUndoManager::recordCopyJob(KIO::CopyJob* copyJob) 00254 { 00255 CommandType commandType; 00256 switch (copyJob->operationMode()) { 00257 case CopyJob::Copy: 00258 commandType = Copy; 00259 break; 00260 case CopyJob::Move: 00261 commandType = Move; 00262 break; 00263 case CopyJob::Link: 00264 default: // prevent "wrong" compiler warning because of possibly uninitialized variable 00265 commandType = Link; 00266 break; 00267 } 00268 recordJob(commandType, copyJob->srcUrls(), copyJob->destUrl(), copyJob); 00269 } 00270 00271 void FileUndoManagerPrivate::addCommand(const UndoCommand &cmd) 00272 { 00273 broadcastPush(cmd); 00274 emit q->jobRecordingFinished(cmd.m_type); 00275 } 00276 00277 bool FileUndoManager::undoAvailable() const 00278 { 00279 return (d->m_commands.count() > 0) && !d->m_lock; 00280 } 00281 00282 QString FileUndoManager::undoText() const 00283 { 00284 if (d->m_commands.isEmpty()) 00285 return i18n("Und&o"); 00286 00287 FileUndoManager::CommandType t = d->m_commands.last().m_type; 00288 switch(t) { 00289 case FileUndoManager::Copy: 00290 return i18n("Und&o: Copy"); 00291 case FileUndoManager::Link: 00292 return i18n("Und&o: Link"); 00293 case FileUndoManager::Move: 00294 return i18n("Und&o: Move"); 00295 case FileUndoManager::Rename: 00296 return i18n("Und&o: Rename"); 00297 case FileUndoManager::Trash: 00298 return i18n("Und&o: Trash"); 00299 case FileUndoManager::Mkdir: 00300 return i18n("Und&o: Create Folder"); 00301 case FileUndoManager::Put: 00302 return i18n("Und&o: Create File"); 00303 } 00304 /* NOTREACHED */ 00305 return QString(); 00306 } 00307 00308 quint64 FileUndoManager::newCommandSerialNumber() 00309 { 00310 return ++(d->m_nextCommandIndex); 00311 } 00312 00313 quint64 FileUndoManager::currentCommandSerialNumber() const 00314 { 00315 if(!d->m_commands.isEmpty()) 00316 { 00317 const UndoCommand& cmd = d->m_commands.last(); 00318 assert(cmd.m_valid); 00319 return cmd.m_serialNumber; 00320 } else 00321 return 0; 00322 } 00323 00324 void FileUndoManager::undo() 00325 { 00326 // Make a copy of the command to undo before broadcastPop() pops it. 00327 UndoCommand cmd = d->m_commands.last(); 00328 assert(cmd.m_valid); 00329 d->m_current = cmd; 00330 00331 BasicOperation::Stack& opStack = d->m_current.m_opStack; 00332 // Note that opStack is empty for simple operations like Mkdir. 00333 00334 // Let's first ask for confirmation if we need to delete any file (#99898) 00335 KUrl::List fileCleanupStack; 00336 BasicOperation::Stack::Iterator it = opStack.begin(); 00337 for (; it != opStack.end() ; ++it) { 00338 BasicOperation::Type type = (*it).m_type; 00339 if (type == BasicOperation::File && d->m_current.m_type == FileUndoManager::Copy) { 00340 fileCleanupStack.append((*it).m_dst); 00341 } 00342 } 00343 if (d->m_current.m_type == FileUndoManager::Mkdir || d->m_current.m_type == FileUndoManager::Put) { 00344 fileCleanupStack.append(d->m_current.m_dst); 00345 } 00346 if (!fileCleanupStack.isEmpty()) { 00347 if (!d->m_uiInterface->confirmDeletion(fileCleanupStack)) { 00348 return; 00349 } 00350 } 00351 00352 d->broadcastPop(); 00353 d->broadcastLock(); 00354 00355 d->m_dirCleanupStack.clear(); 00356 d->m_dirStack.clear(); 00357 d->m_dirsToUpdate.clear(); 00358 00359 d->m_undoState = MOVINGFILES; 00360 00361 // Let's have a look at the basic operations we need to undo. 00362 // While we're at it, collect all links that should be deleted. 00363 00364 it = opStack.begin(); 00365 while (it != opStack.end()) // don't cache end() here, erase modifies it 00366 { 00367 bool removeBasicOperation = false; 00368 BasicOperation::Type type = (*it).m_type; 00369 if (type == BasicOperation::Directory && !(*it).m_renamed) 00370 { 00371 // If any directory has to be created/deleted, we'll start with that 00372 d->m_undoState = MAKINGDIRS; 00373 // Collect all the dirs that have to be created in case of a move undo. 00374 if (d->m_current.isMoveCommand()) 00375 d->m_dirStack.push((*it).m_src); 00376 // Collect all dirs that have to be deleted 00377 // from the destination in both cases (copy and move). 00378 d->m_dirCleanupStack.prepend((*it).m_dst); 00379 removeBasicOperation = true; 00380 } 00381 else if (type == BasicOperation::Link) 00382 { 00383 d->m_fileCleanupStack.prepend((*it).m_dst); 00384 00385 removeBasicOperation = !d->m_current.isMoveCommand(); 00386 } 00387 00388 if (removeBasicOperation) 00389 it = opStack.erase(it); 00390 else 00391 ++it; 00392 } 00393 00394 if (d->m_current.m_type == FileUndoManager::Put) { 00395 d->m_fileCleanupStack.append(d->m_current.m_dst); 00396 } 00397 00398 kDebug(1203) << "starting with" << undoStateToString(d->m_undoState); 00399 d->m_undoJob = new UndoJob(d->m_uiInterface->showProgressInfo()); 00400 d->undoStep(); 00401 } 00402 00403 void FileUndoManagerPrivate::stopUndo(bool step) 00404 { 00405 m_current.m_opStack.clear(); 00406 m_dirCleanupStack.clear(); 00407 m_fileCleanupStack.clear(); 00408 m_undoState = REMOVINGDIRS; 00409 m_undoJob = 0; 00410 00411 if (m_currentJob) 00412 m_currentJob->kill(); 00413 00414 m_currentJob = 0; 00415 00416 if (step) 00417 undoStep(); 00418 } 00419 00420 void FileUndoManagerPrivate::slotResult(KJob *job) 00421 { 00422 m_currentJob = 0; 00423 if (job->error()) 00424 { 00425 m_uiInterface->jobError(static_cast<KIO::Job*>(job)); 00426 delete m_undoJob; 00427 stopUndo(false); 00428 } 00429 else if (m_undoState == STATINGFILE) 00430 { 00431 BasicOperation op = m_current.m_opStack.last(); 00432 //kDebug(1203) << "stat result for " << op.m_dst; 00433 KIO::StatJob* statJob = static_cast<KIO::StatJob*>(job); 00434 time_t mtime = statJob->statResult().numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME, -1); 00435 if (mtime != op.m_mtime) { 00436 kDebug(1203) << op.m_dst << " was modified after being copied!"; 00437 KDateTime srcTime; srcTime.setTime_t(op.m_mtime); srcTime = srcTime.toLocalZone(); 00438 KDateTime destTime; destTime.setTime_t(mtime); destTime = destTime.toLocalZone(); 00439 if (!m_uiInterface->copiedFileWasModified(op.m_src, op.m_dst, srcTime, destTime)) { 00440 stopUndo(false); 00441 } 00442 } 00443 } 00444 00445 undoStep(); 00446 } 00447 00448 00449 void FileUndoManagerPrivate::addDirToUpdate(const KUrl& url) 00450 { 00451 if (!m_dirsToUpdate.contains(url)) 00452 m_dirsToUpdate.prepend(url); 00453 } 00454 00455 void FileUndoManagerPrivate::undoStep() 00456 { 00457 m_currentJob = 0; 00458 00459 if (m_undoState == MAKINGDIRS) 00460 stepMakingDirectories(); 00461 00462 if (m_undoState == MOVINGFILES || m_undoState == STATINGFILE) 00463 stepMovingFiles(); 00464 00465 if (m_undoState == REMOVINGLINKS) 00466 stepRemovingLinks(); 00467 00468 if (m_undoState == REMOVINGDIRS) 00469 stepRemovingDirectories(); 00470 00471 if (m_currentJob) { 00472 if (m_uiInterface) 00473 m_currentJob->ui()->setWindow(m_uiInterface->parentWidget()); 00474 QObject::connect(m_currentJob, SIGNAL(result(KJob*)), 00475 this, SLOT(slotResult(KJob*))); 00476 } 00477 } 00478 00479 void FileUndoManagerPrivate::stepMakingDirectories() 00480 { 00481 if (!m_dirStack.isEmpty()) { 00482 KUrl dir = m_dirStack.pop(); 00483 kDebug(1203) << "creatingDir" << dir; 00484 m_currentJob = KIO::mkdir(dir); 00485 m_undoJob->emitCreatingDir(dir); 00486 } 00487 else 00488 m_undoState = MOVINGFILES; 00489 } 00490 00491 // Misnamed method: It moves files back, but it also 00492 // renames directories back, recreates symlinks, 00493 // deletes copied files, and restores trashed files. 00494 void FileUndoManagerPrivate::stepMovingFiles() 00495 { 00496 if (!m_current.m_opStack.isEmpty()) 00497 { 00498 BasicOperation op = m_current.m_opStack.last(); 00499 BasicOperation::Type type = op.m_type; 00500 00501 assert(op.m_valid); 00502 if (type == BasicOperation::Directory) 00503 { 00504 if (op.m_renamed) 00505 { 00506 kDebug(1203) << "rename" << op.m_dst << op.m_src; 00507 m_currentJob = KIO::rename(op.m_dst, op.m_src, KIO::HideProgressInfo); 00508 m_undoJob->emitMoving(op.m_dst, op.m_src); 00509 } 00510 else 00511 assert(0); // this should not happen! 00512 } 00513 else if (type == BasicOperation::Link) 00514 { 00515 kDebug(1203) << "symlink" << op.m_target << op.m_src; 00516 m_currentJob = KIO::symlink(op.m_target, op.m_src, KIO::Overwrite | KIO::HideProgressInfo); 00517 } 00518 else if (m_current.m_type == FileUndoManager::Copy) 00519 { 00520 if (m_undoState == MOVINGFILES) // dest not stat'ed yet 00521 { 00522 // Before we delete op.m_dst, let's check if it was modified (#20532) 00523 kDebug(1203) << "stat" << op.m_dst; 00524 m_currentJob = KIO::stat(op.m_dst, KIO::HideProgressInfo); 00525 m_undoState = STATINGFILE; // temporarily 00526 return; // no pop() yet, we'll finish the work in slotResult 00527 } 00528 else // dest was stat'ed, and the deletion was approved in slotResult 00529 { 00530 m_currentJob = KIO::file_delete(op.m_dst, KIO::HideProgressInfo); 00531 m_undoJob->emitDeleting(op.m_dst); 00532 m_undoState = MOVINGFILES; 00533 } 00534 } 00535 else if (m_current.isMoveCommand() 00536 || m_current.m_type == FileUndoManager::Trash) 00537 { 00538 kDebug(1203) << "file_move" << op.m_dst << op.m_src; 00539 m_currentJob = KIO::file_move(op.m_dst, op.m_src, -1, KIO::Overwrite | KIO::HideProgressInfo); 00540 m_undoJob->emitMoving(op.m_dst, op.m_src); 00541 } 00542 00543 m_current.m_opStack.removeLast(); 00544 // The above KIO jobs are lowlevel, they don't trigger KDirNotify notification 00545 // So we need to do it ourselves (but schedule it to the end of the undo, to compress them) 00546 KUrl url(op.m_dst); 00547 url.setPath(url.directory()); 00548 addDirToUpdate(url); 00549 00550 url = op.m_src; 00551 url.setPath(url.directory()); 00552 addDirToUpdate(url); 00553 } 00554 else 00555 m_undoState = REMOVINGLINKS; 00556 } 00557 00558 void FileUndoManagerPrivate::stepRemovingLinks() 00559 { 00560 kDebug(1203) << "REMOVINGLINKS"; 00561 if (!m_fileCleanupStack.isEmpty()) 00562 { 00563 KUrl file = m_fileCleanupStack.pop(); 00564 kDebug(1203) << "file_delete" << file; 00565 m_currentJob = KIO::file_delete(file, KIO::HideProgressInfo); 00566 m_undoJob->emitDeleting(file); 00567 00568 KUrl url(file); 00569 url.setPath(url.directory()); 00570 addDirToUpdate(url); 00571 } 00572 else 00573 { 00574 m_undoState = REMOVINGDIRS; 00575 00576 if (m_dirCleanupStack.isEmpty() && m_current.m_type == FileUndoManager::Mkdir) 00577 m_dirCleanupStack << m_current.m_dst; 00578 } 00579 } 00580 00581 void FileUndoManagerPrivate::stepRemovingDirectories() 00582 { 00583 if (!m_dirCleanupStack.isEmpty()) 00584 { 00585 KUrl dir = m_dirCleanupStack.pop(); 00586 kDebug(1203) << "rmdir" << dir; 00587 m_currentJob = KIO::rmdir(dir); 00588 m_undoJob->emitDeleting(dir); 00589 addDirToUpdate(dir); 00590 } 00591 else 00592 { 00593 m_current.m_valid = false; 00594 m_currentJob = 0; 00595 if (m_undoJob) 00596 { 00597 kDebug(1203) << "deleting undojob"; 00598 m_undoJob->emitResult(); 00599 m_undoJob = 0; 00600 } 00601 QList<KUrl>::ConstIterator it = m_dirsToUpdate.constBegin(); 00602 for(; it != m_dirsToUpdate.constEnd(); ++it) { 00603 kDebug() << "Notifying FilesAdded for " << *it; 00604 org::kde::KDirNotify::emitFilesAdded((*it).url()); 00605 } 00606 emit q->undoJobFinished(); 00607 broadcastUnlock(); 00608 } 00609 } 00610 00611 // const ref doesn't work due to QDataStream 00612 void FileUndoManagerPrivate::slotPush(QByteArray data) 00613 { 00614 QDataStream strm(&data, QIODevice::ReadOnly); 00615 UndoCommand cmd; 00616 strm >> cmd; 00617 pushCommand(cmd); 00618 } 00619 00620 void FileUndoManagerPrivate::pushCommand(const UndoCommand& cmd) 00621 { 00622 m_commands.append(cmd); 00623 emit q->undoAvailable(true); 00624 emit q->undoTextChanged(q->undoText()); 00625 } 00626 00627 void FileUndoManagerPrivate::slotPop() 00628 { 00629 m_commands.removeLast(); 00630 emit q->undoAvailable(q->undoAvailable()); 00631 emit q->undoTextChanged(q->undoText()); 00632 } 00633 00634 void FileUndoManagerPrivate::slotLock() 00635 { 00636 // assert(!m_lock); 00637 m_lock = true; 00638 emit q->undoAvailable(q->undoAvailable()); 00639 } 00640 00641 void FileUndoManagerPrivate::slotUnlock() 00642 { 00643 // assert(m_lock); 00644 m_lock = false; 00645 emit q->undoAvailable(q->undoAvailable()); 00646 } 00647 00648 QByteArray FileUndoManagerPrivate::get() const 00649 { 00650 QByteArray data; 00651 QDataStream stream(&data, QIODevice::WriteOnly); 00652 stream << m_commands; 00653 return data; 00654 } 00655 00656 void FileUndoManagerPrivate::broadcastPush(const UndoCommand &cmd) 00657 { 00658 if (!m_syncronized) { 00659 pushCommand(cmd); 00660 return; 00661 } 00662 00663 QByteArray data; 00664 QDataStream stream(&data, QIODevice::WriteOnly); 00665 stream << cmd; 00666 emit push(data); // DBUS signal 00667 } 00668 00669 void FileUndoManagerPrivate::broadcastPop() 00670 { 00671 if (!m_syncronized) { 00672 slotPop(); 00673 return; 00674 } 00675 00676 emit pop(); // DBUS signal 00677 } 00678 00679 void FileUndoManagerPrivate::broadcastLock() 00680 { 00681 // assert(!m_lock); 00682 00683 if (!m_syncronized) { 00684 slotLock(); 00685 return; 00686 } 00687 emit lock(); // DBUS signal 00688 } 00689 00690 void FileUndoManagerPrivate::broadcastUnlock() 00691 { 00692 // assert(m_lock); 00693 00694 if (!m_syncronized) { 00695 slotUnlock(); 00696 return; 00697 } 00698 emit unlock(); // DBUS signal 00699 } 00700 00701 bool FileUndoManagerPrivate::initializeFromKDesky() 00702 { 00703 // ### workaround for dcop problem and upcoming 2.1 release: 00704 // in case of huge io operations the amount of data sent over 00705 // dcop (containing undo information broadcasted for global undo 00706 // to all konqueror instances) can easily exceed the 64kb limit 00707 // of dcop. In order not to run into trouble we disable global 00708 // undo for now! (Simon) 00709 // ### FIXME: post 2.1 00710 // TODO KDE4: port to DBUS and test 00711 return false; 00712 #if 0 00713 DCOPClient *client = kapp->dcopClient(); 00714 00715 if (client->appId() == "kdesktop") // we are master :) 00716 return true; 00717 00718 if (!client->isApplicationRegistered("kdesktop")) 00719 return false; 00720 00721 d->m_commands = DCOPRef("kdesktop", "FileUndoManager").call("get"); 00722 return true; 00723 #endif 00724 } 00725 00726 void FileUndoManager::setUiInterface(UiInterface* ui) 00727 { 00728 delete d->m_uiInterface; 00729 d->m_uiInterface = ui; 00730 } 00731 00732 FileUndoManager::UiInterface* FileUndoManager::uiInterface() const 00733 { 00734 return d->m_uiInterface; 00735 } 00736 00738 00739 class FileUndoManager::UiInterface::UiInterfacePrivate 00740 { 00741 public: 00742 UiInterfacePrivate() 00743 : m_parentWidget(0), m_showProgressInfo(true) 00744 {} 00745 QWidget* m_parentWidget; 00746 bool m_showProgressInfo; 00747 }; 00748 00749 FileUndoManager::UiInterface::UiInterface() 00750 : d(new UiInterfacePrivate) 00751 { 00752 } 00753 00754 FileUndoManager::UiInterface::~UiInterface() 00755 { 00756 delete d; 00757 } 00758 00759 void FileUndoManager::UiInterface::jobError(KIO::Job* job) 00760 { 00761 job->ui()->showErrorMessage(); 00762 } 00763 00764 bool FileUndoManager::UiInterface::copiedFileWasModified(const KUrl& src, const KUrl& dest, const KDateTime& srcTime, const KDateTime& destTime) 00765 { 00766 Q_UNUSED(srcTime); // not sure it should appear in the msgbox 00767 // Possible improvement: only show the time if date is today 00768 const QString timeStr = KGlobal::locale()->formatDateTime(destTime, KLocale::ShortDate); 00769 return KMessageBox::warningContinueCancel( 00770 d->m_parentWidget, 00771 i18n("The file %1 was copied from %2, but since then it has apparently been modified at %3.\n" 00772 "Undoing the copy will delete the file, and all modifications will be lost.\n" 00773 "Are you sure you want to delete %4?", dest.pathOrUrl(), src.pathOrUrl(), timeStr, dest.pathOrUrl()), 00774 i18n("Undo File Copy Confirmation"), 00775 KStandardGuiItem::cont(), 00776 KStandardGuiItem::cancel(), 00777 QString(), 00778 KMessageBox::Notify | KMessageBox::Dangerous) == KMessageBox::Continue; 00779 } 00780 00781 bool FileUndoManager::UiInterface::confirmDeletion(const KUrl::List& files) 00782 { 00783 KIO::JobUiDelegate uiDelegate; 00784 uiDelegate.setWindow(d->m_parentWidget); 00785 // Because undo can happen with an accidental Ctrl-Z, we want to always confirm. 00786 return uiDelegate.askDeleteConfirmation(files, KIO::JobUiDelegate::Delete, KIO::JobUiDelegate::ForceConfirmation); 00787 } 00788 00789 QWidget* FileUndoManager::UiInterface::parentWidget() const 00790 { 00791 return d->m_parentWidget; 00792 } 00793 00794 void FileUndoManager::UiInterface::setParentWidget(QWidget* parentWidget) 00795 { 00796 d->m_parentWidget = parentWidget; 00797 } 00798 00799 void FileUndoManager::UiInterface::setShowProgressInfo(bool b) 00800 { 00801 d->m_showProgressInfo = b; 00802 } 00803 00804 bool FileUndoManager::UiInterface::showProgressInfo() const 00805 { 00806 return d->m_showProgressInfo; 00807 } 00808 00809 void FileUndoManager::UiInterface::virtual_hook(int, void*) 00810 { 00811 } 00812 00813 #include "fileundomanager_p.moc" 00814 #include "fileundomanager.moc"
KDE 4.7 API Reference