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