00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "konq_undo.h"
00021
00022 #undef Always
00023
00024 #include <kio/uiserver_stub.h>
00025
00026 #include <assert.h>
00027
00028 #include <dcopclient.h>
00029 #include <dcopref.h>
00030
00031 #include <kapplication.h>
00032 #include <kdatastream.h>
00033 #include <kdebug.h>
00034 #include <klocale.h>
00035 #include <kglobalsettings.h>
00036 #include <kconfig.h>
00037 #include <kipc.h>
00038
00039 #include <kio/job.h>
00040 #include <kdirnotify_stub.h>
00041
00042 inline const char *dcopTypeName( const KonqCommand & ) { return "KonqCommand"; }
00043 inline const char *dcopTypeName( const KonqCommand::Stack & ) { return "KonqCommand::Stack"; }
00044
00065 class KonqUndoJob : public KIO::Job
00066 {
00067 public:
00068 KonqUndoJob() : KIO::Job( true ) { KonqUndoManager::incRef(); };
00069 virtual ~KonqUndoJob() { KonqUndoManager::decRef(); }
00070
00071 virtual void kill( bool q) { KonqUndoManager::self()->stopUndo( true ); KIO::Job::kill( q ); }
00072 };
00073
00074 class KonqCommandRecorder::KonqCommandRecorderPrivate
00075 {
00076 public:
00077 KonqCommandRecorderPrivate()
00078 {
00079 }
00080 ~KonqCommandRecorderPrivate()
00081 {
00082 }
00083
00084 KonqCommand m_cmd;
00085 };
00086
00087 KonqCommandRecorder::KonqCommandRecorder( KonqCommand::Type op, const KURL::List &src, const KURL &dst, KIO::Job *job )
00088 : QObject( job, "konqcmdrecorder" )
00089 {
00090 d = new KonqCommandRecorderPrivate;
00091 d->m_cmd.m_type = op;
00092 d->m_cmd.m_valid = true;
00093 d->m_cmd.m_src = src;
00094 d->m_cmd.m_dst = dst;
00095 connect( job, SIGNAL( result( KIO::Job * ) ),
00096 this, SLOT( slotResult( KIO::Job * ) ) );
00097
00098 if ( op != KonqCommand::MKDIR ) {
00099 connect( job, SIGNAL( copyingDone( KIO::Job *, const KURL &, const KURL &, bool, bool ) ),
00100 this, SLOT( slotCopyingDone( KIO::Job *, const KURL &, const KURL &, bool, bool ) ) );
00101 connect( job, SIGNAL( copyingLinkDone( KIO::Job *, const KURL &, const QString &, const KURL & ) ),
00102 this, SLOT( slotCopyingLinkDone( KIO::Job *, const KURL &, const QString &, const KURL & ) ) );
00103 }
00104
00105 KonqUndoManager::incRef();
00106 }
00107
00108 KonqCommandRecorder::~KonqCommandRecorder()
00109 {
00110 KonqUndoManager::decRef();
00111 delete d;
00112 }
00113
00114 void KonqCommandRecorder::slotResult( KIO::Job *job )
00115 {
00116 if ( job->error() )
00117 return;
00118
00119 KonqUndoManager::self()->addCommand( d->m_cmd );
00120 }
00121
00122 void KonqCommandRecorder::slotCopyingDone( KIO::Job *job, const KURL &from, const KURL &to, bool directory, bool renamed )
00123 {
00124 KonqBasicOperation op;
00125 op.m_valid = true;
00126 op.m_directory = directory;
00127 op.m_renamed = renamed;
00128 op.m_src = from;
00129 op.m_dst = to;
00130 op.m_link = false;
00131
00132 if ( d->m_cmd.m_type == KonqCommand::TRASH )
00133 {
00134 Q_ASSERT( from.isLocalFile() );
00135 Q_ASSERT( to.protocol() == "trash" );
00136 QMap<QString, QString> metaData = job->metaData();
00137 QMap<QString, QString>::ConstIterator it = metaData.find( "trashURL-" + from.path() );
00138 if ( it != metaData.end() ) {
00139
00140 op.m_dst = it.data();
00141 }
00142 }
00143
00144 d->m_cmd.m_opStack.prepend( op );
00145 }
00146
00147 void KonqCommandRecorder::slotCopyingLinkDone( KIO::Job *, const KURL &from, const QString &target, const KURL &to )
00148 {
00149 KonqBasicOperation op;
00150 op.m_valid = true;
00151 op.m_directory = false;
00152 op.m_renamed = false;
00153 op.m_src = from;
00154 op.m_target = target;
00155 op.m_dst = to;
00156 op.m_link = true;
00157 d->m_cmd.m_opStack.prepend( op );
00158 }
00159
00160 KonqUndoManager *KonqUndoManager::s_self = 0;
00161 unsigned long KonqUndoManager::s_refCnt = 0;
00162
00163 class KonqUndoManager::KonqUndoManagerPrivate
00164 {
00165 public:
00166 KonqUndoManagerPrivate()
00167 {
00168 m_uiserver = new UIServer_stub( "kio_uiserver", "UIServer" );
00169 m_undoJob = 0;
00170 }
00171 ~KonqUndoManagerPrivate()
00172 {
00173 delete m_uiserver;
00174 }
00175
00176 bool m_syncronized;
00177
00178 KonqCommand::Stack m_commands;
00179
00180 KonqCommand m_current;
00181 KIO::Job *m_currentJob;
00182 UndoState m_undoState;
00183 QValueStack<KURL> m_dirStack;
00184 QValueStack<KURL> m_dirCleanupStack;
00185 QValueStack<KURL> m_fileCleanupStack;
00186 QValueList<KURL> m_dirsToUpdate;
00187
00188 bool m_lock;
00189
00190 UIServer_stub *m_uiserver;
00191 int m_uiserverJobId;
00192
00193 KonqUndoJob *m_undoJob;
00194 };
00195
00196 KonqUndoManager::KonqUndoManager()
00197 : DCOPObject( "KonqUndoManager" )
00198 {
00199 if ( !kapp->dcopClient()->isAttached() )
00200 kapp->dcopClient()->attach();
00201
00202 d = new KonqUndoManagerPrivate;
00203 d->m_syncronized = initializeFromKDesky();
00204 d->m_lock = false;
00205 d->m_currentJob = 0;
00206 }
00207
00208 KonqUndoManager::~KonqUndoManager()
00209 {
00210 delete d;
00211 }
00212
00213 void KonqUndoManager::incRef()
00214 {
00215 s_refCnt++;
00216 }
00217
00218 void KonqUndoManager::decRef()
00219 {
00220 s_refCnt--;
00221 if ( s_refCnt == 0 && s_self )
00222 {
00223 delete s_self;
00224 s_self = 0;
00225 }
00226 }
00227
00228 KonqUndoManager *KonqUndoManager::self()
00229 {
00230 if ( !s_self )
00231 {
00232 if ( s_refCnt == 0 )
00233 s_refCnt++;
00234 s_self = new KonqUndoManager;
00235 }
00236 return s_self;
00237 }
00238
00239 void KonqUndoManager::addCommand( const KonqCommand &cmd )
00240 {
00241 broadcastPush( cmd );
00242 }
00243
00244 bool KonqUndoManager::undoAvailable() const
00245 {
00246 return ( d->m_commands.count() > 0 ) && !d->m_lock;
00247 }
00248
00249 QString KonqUndoManager::undoText() const
00250 {
00251 if ( d->m_commands.count() == 0 )
00252 return i18n( "Und&o" );
00253
00254 KonqCommand::Type t = d->m_commands.top().m_type;
00255 if ( t == KonqCommand::COPY )
00256 return i18n( "Und&o: Copy" );
00257 else if ( t == KonqCommand::LINK )
00258 return i18n( "Und&o: Link" );
00259 else if ( t == KonqCommand::MOVE )
00260 return i18n( "Und&o: Move" );
00261 else if ( t == KonqCommand::TRASH )
00262 return i18n( "Und&o: Trash" );
00263 else if ( t == KonqCommand::MKDIR )
00264 return i18n( "Und&o: Create Folder" );
00265 else
00266 assert( false );
00267
00268 return QString::null;
00269 }
00270
00271 void KonqUndoManager::undo()
00272 {
00273 KonqCommand cmd = d->m_commands.top();
00274 broadcastPop();
00275 broadcastLock();
00276
00277 assert( cmd.m_valid );
00278
00279 d->m_current = cmd;
00280 d->m_dirCleanupStack.clear();
00281 d->m_dirStack.clear();
00282 d->m_dirsToUpdate.clear();
00283
00284 d->m_undoState = MOVINGFILES;
00285 kdDebug(1203) << "KonqUndoManager::undo MOVINGFILES" << endl;
00286
00287 QValueList<KonqBasicOperation>::Iterator it = d->m_current.m_opStack.begin();
00288 QValueList<KonqBasicOperation>::Iterator end = d->m_current.m_opStack.end();
00289 while ( it != end )
00290 {
00291 if ( (*it).m_directory && !(*it).m_renamed )
00292 {
00293 d->m_dirStack.push( (*it).m_src );
00294 d->m_dirCleanupStack.prepend( (*it).m_dst );
00295 it = d->m_current.m_opStack.remove( it );
00296 d->m_undoState = MAKINGDIRS;
00297 kdDebug(1203) << "KonqUndoManager::undo MAKINGDIRS" << endl;
00298 }
00299 else if ( (*it).m_link )
00300 {
00301 if ( !d->m_fileCleanupStack.contains( (*it).m_dst ) )
00302 d->m_fileCleanupStack.prepend( (*it).m_dst );
00303
00304 if ( d->m_current.m_type != KonqCommand::MOVE )
00305 it = d->m_current.m_opStack.remove( it );
00306 else
00307 ++it;
00308 }
00309 else
00310 ++it;
00311 }
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326
00327
00328 if ( d->m_current.m_type != KonqCommand::MOVE )
00329 d->m_dirStack.clear();
00330
00331 d->m_undoJob = new KonqUndoJob;
00332 d->m_uiserverJobId = d->m_undoJob->progressId();
00333 undoStep();
00334 }
00335
00336 void KonqUndoManager::stopUndo( bool step )
00337 {
00338 d->m_current.m_opStack.clear();
00339 d->m_dirCleanupStack.clear();
00340 d->m_fileCleanupStack.clear();
00341 d->m_undoState = REMOVINGDIRS;
00342 d->m_undoJob = 0;
00343
00344 if ( d->m_currentJob )
00345 d->m_currentJob->kill( true );
00346
00347 d->m_currentJob = 0;
00348
00349 if ( step )
00350 undoStep();
00351 }
00352
00353 void KonqUndoManager::slotResult( KIO::Job *job )
00354 {
00355 d->m_uiserver->jobFinished( d->m_uiserverJobId );
00356 if ( job->error() )
00357 {
00358 job->showErrorDialog( 0L );
00359 d->m_currentJob = 0;
00360 stopUndo( false );
00361 if ( d->m_undoJob )
00362 {
00363 delete d->m_undoJob;
00364 d->m_undoJob = 0;
00365 }
00366 }
00367
00368 undoStep();
00369 }
00370
00371
00372 void KonqUndoManager::addDirToUpdate( const KURL& url )
00373 {
00374 if ( d->m_dirsToUpdate.find( url ) == d->m_dirsToUpdate.end() )
00375 d->m_dirsToUpdate.prepend( url );
00376 }
00377
00378 void KonqUndoManager::undoStep()
00379 {
00380 d->m_currentJob = 0;
00381
00382 if ( d->m_undoState == MAKINGDIRS )
00383 undoMakingDirectories();
00384
00385 if ( d->m_undoState == MOVINGFILES )
00386 undoMovingFiles();
00387
00388 if ( d->m_undoState == REMOVINGFILES )
00389 undoRemovingFiles();
00390
00391 if ( d->m_undoState == REMOVINGDIRS )
00392 undoRemovingDirectories();
00393
00394 if ( d->m_currentJob )
00395 connect( d->m_currentJob, SIGNAL( result( KIO::Job * ) ),
00396 this, SLOT( slotResult( KIO::Job * ) ) );
00397 }
00398
00399 void KonqUndoManager::undoMakingDirectories()
00400 {
00401 if ( !d->m_dirStack.isEmpty() ) {
00402 KURL dir = d->m_dirStack.pop();
00403 kdDebug(1203) << "KonqUndoManager::undoStep creatingDir " << dir.prettyURL() << endl;
00404 d->m_currentJob = KIO::mkdir( dir );
00405 d->m_uiserver->creatingDir( d->m_uiserverJobId, dir );
00406 }
00407 else
00408 d->m_undoState = MOVINGFILES;
00409 }
00410
00411 void KonqUndoManager::undoMovingFiles()
00412 {
00413 if ( !d->m_current.m_opStack.isEmpty() )
00414 {
00415 KonqBasicOperation op = d->m_current.m_opStack.pop();
00416
00417 assert( op.m_valid );
00418 if ( op.m_directory )
00419 {
00420 if ( op.m_renamed )
00421 {
00422 kdDebug(1203) << "KonqUndoManager::undoStep rename " << op.m_dst.prettyURL() << " " << op.m_src.prettyURL() << endl;
00423 d->m_currentJob = KIO::rename( op.m_dst, op.m_src, false );
00424 d->m_uiserver->moving( d->m_uiserverJobId, op.m_dst, op.m_src );
00425 }
00426 else
00427 assert( 0 );
00428 }
00429 else if ( op.m_link )
00430 {
00431 kdDebug(1203) << "KonqUndoManager::undoStep symlink " << op.m_target << " " << op.m_src.prettyURL() << endl;
00432 d->m_currentJob = KIO::symlink( op.m_target, op.m_src, true, false );
00433 }
00434 else if ( d->m_current.m_type == KonqCommand::COPY )
00435 {
00436 kdDebug(1203) << "KonqUndoManager::undoStep file_delete " << op.m_dst.prettyURL() << endl;
00437 d->m_currentJob = KIO::file_delete( op.m_dst );
00438 d->m_uiserver->deleting( d->m_uiserverJobId, op.m_dst );
00439 }
00440 else if ( d->m_current.m_type == KonqCommand::MOVE
00441 || d->m_current.m_type == KonqCommand::TRASH )
00442 {
00443 kdDebug(1203) << "KonqUndoManager::undoStep file_move " << op.m_dst.prettyURL() << " " << op.m_src.prettyURL() << endl;
00444 d->m_currentJob = KIO::file_move( op.m_dst, op.m_src, -1, true );
00445 d->m_uiserver->moving( d->m_uiserverJobId, op.m_dst, op.m_src );
00446 }
00447
00448
00449
00450 KURL url( op.m_dst );
00451 url.setPath( url.directory() );
00452 addDirToUpdate( url );
00453
00454 url = op.m_src;
00455 url.setPath( url.directory() );
00456 addDirToUpdate( url );
00457 }
00458 else
00459 d->m_undoState = REMOVINGFILES;
00460 }
00461
00462 void KonqUndoManager::undoRemovingFiles()
00463 {
00464 kdDebug(1203) << "KonqUndoManager::undoStep REMOVINGFILES" << endl;
00465 if ( !d->m_fileCleanupStack.isEmpty() )
00466 {
00467 KURL file = d->m_fileCleanupStack.pop();
00468 kdDebug(1203) << "KonqUndoManager::undoStep file_delete " << file.prettyURL() << endl;
00469 d->m_currentJob = KIO::file_delete( file );
00470 d->m_uiserver->deleting( d->m_uiserverJobId, file );
00471
00472 KURL url( file );
00473 url.setPath( url.directory() );
00474 addDirToUpdate( url );
00475 }
00476 else
00477 {
00478 d->m_undoState = REMOVINGDIRS;
00479
00480 if ( d->m_dirCleanupStack.isEmpty() && d->m_current.m_type == KonqCommand::MKDIR )
00481 d->m_dirCleanupStack << d->m_current.m_dst;
00482 }
00483 }
00484
00485 void KonqUndoManager::undoRemovingDirectories()
00486 {
00487 if ( !d->m_dirCleanupStack.isEmpty() )
00488 {
00489 KURL dir = d->m_dirCleanupStack.pop();
00490 kdDebug(1203) << "KonqUndoManager::undoStep rmdir " << dir.prettyURL() << endl;
00491 d->m_currentJob = KIO::rmdir( dir );
00492 d->m_uiserver->deleting( d->m_uiserverJobId, dir );
00493 addDirToUpdate( dir );
00494 }
00495 else
00496 {
00497 d->m_current.m_valid = false;
00498 d->m_currentJob = 0;
00499 if ( d->m_undoJob )
00500 {
00501 kdDebug(1203) << "KonqUndoManager::undoStep deleting undojob" << endl;
00502 d->m_uiserver->jobFinished( d->m_uiserverJobId );
00503 delete d->m_undoJob;
00504 d->m_undoJob = 0;
00505 }
00506 KDirNotify_stub allDirNotify( "*", "KDirNotify*" );
00507 QValueList<KURL>::ConstIterator it = d->m_dirsToUpdate.begin();
00508 for( ; it != d->m_dirsToUpdate.end(); ++it ) {
00509 kdDebug() << "Notifying FilesAdded for " << *it << endl;
00510 allDirNotify.FilesAdded( *it );
00511 }
00512 broadcastUnlock();
00513 }
00514 }
00515
00516 void KonqUndoManager::push( const KonqCommand &cmd )
00517 {
00518 d->m_commands.push( cmd );
00519 emit undoAvailable( true );
00520 emit undoTextChanged( undoText() );
00521 }
00522
00523 void KonqUndoManager::pop()
00524 {
00525 d->m_commands.pop();
00526 emit undoAvailable( undoAvailable() );
00527 emit undoTextChanged( undoText() );
00528 }
00529
00530 void KonqUndoManager::lock()
00531 {
00532
00533 d->m_lock = true;
00534 emit undoAvailable( undoAvailable() );
00535 }
00536
00537 void KonqUndoManager::unlock()
00538 {
00539
00540 d->m_lock = false;
00541 emit undoAvailable( undoAvailable() );
00542 }
00543
00544 KonqCommand::Stack KonqUndoManager::get() const
00545 {
00546 return d->m_commands;
00547 }
00548
00549 void KonqUndoManager::broadcastPush( const KonqCommand &cmd )
00550 {
00551 if ( !d->m_syncronized )
00552 {
00553 push( cmd );
00554 return;
00555 }
00556
00557 DCOPRef( "kdesktop", "KonqUndoManager" ).send( "push", cmd );
00558 DCOPRef( "konqueror*", "KonqUndoManager" ).send( "push", cmd );
00559 }
00560
00561 void KonqUndoManager::broadcastPop()
00562 {
00563 if ( !d->m_syncronized )
00564 {
00565 pop();
00566 return;
00567 }
00568 DCOPRef( "kdesktop", "KonqUndoManager" ).send( "pop" );
00569 DCOPRef( "konqueror*", "KonqUndoManager" ).send( "pop" );
00570 }
00571
00572 void KonqUndoManager::broadcastLock()
00573 {
00574
00575
00576 if ( !d->m_syncronized )
00577 {
00578 lock();
00579 return;
00580 }
00581 DCOPRef( "kdesktop", "KonqUndoManager" ).send( "lock" );
00582 DCOPRef( "konqueror*", "KonqUndoManager" ).send( "lock" );
00583 }
00584
00585 void KonqUndoManager::broadcastUnlock()
00586 {
00587
00588
00589 if ( !d->m_syncronized )
00590 {
00591 unlock();
00592 return;
00593 }
00594 DCOPRef( "kdesktop", "KonqUndoManager" ).send( "unlock" );
00595 DCOPRef( "konqueror*", "KonqUndoManager" ).send( "unlock" );
00596 }
00597
00598 bool KonqUndoManager::initializeFromKDesky()
00599 {
00600
00601
00602
00603
00604
00605
00606
00607 return false;
00608
00609 DCOPClient *client = kapp->dcopClient();
00610
00611 if ( client->appId() == "kdesktop" )
00612 return true;
00613
00614 if ( !client->isApplicationRegistered( "kdesktop" ) )
00615 return false;
00616
00617 d->m_commands = DCOPRef( "kdesktop", "KonqUndoManager" ).call( "get" );
00618 return true;
00619 }
00620
00621 QDataStream &operator<<( QDataStream &stream, const KonqBasicOperation &op )
00622 {
00623 stream << op.m_valid << op.m_directory << op.m_renamed << op.m_link
00624 << op.m_src << op.m_dst << op.m_target;
00625 return stream;
00626 }
00627 QDataStream &operator>>( QDataStream &stream, KonqBasicOperation &op )
00628 {
00629 stream >> op.m_valid >> op.m_directory >> op.m_renamed >> op.m_link
00630 >> op.m_src >> op.m_dst >> op.m_target;
00631 return stream;
00632 }
00633
00634 QDataStream &operator<<( QDataStream &stream, const KonqCommand &cmd )
00635 {
00636 stream << cmd.m_valid << (Q_INT8)cmd.m_type << cmd.m_opStack << cmd.m_src << cmd.m_dst;
00637 return stream;
00638 }
00639
00640 QDataStream &operator>>( QDataStream &stream, KonqCommand &cmd )
00641 {
00642 Q_INT8 type;
00643 stream >> cmd.m_valid >> type >> cmd.m_opStack >> cmd.m_src >> cmd.m_dst;
00644 cmd.m_type = static_cast<KonqCommand::Type>( type );
00645 return stream;
00646 }
00647
00648 #include "konq_undo.moc"