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