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