libkonq Library API Documentation

konq_undo.cc

00001 /* This file is part of the KDE project
00002    Copyright (C) 2000 Simon Hausmann <hausmann@kde.org>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00017    Boston, MA 02111-1307, USA.
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++; // someone forgot to call incRef
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   /* NOTREACHED */
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 ); // this should not happen!
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 //  assert( !d->m_lock );
00466   d->m_lock = true;
00467   emit undoAvailable( undoAvailable() );
00468 }
00469 
00470 void KonqUndoManager::unlock()
00471 {
00472 //  assert( d->m_lock );
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 //  assert( !d->m_lock );
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 //  assert( d->m_lock );
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   // ### workaround for dcop problem and upcoming 2.1 release:
00539   // in case of huge io operations the amount of data sent over
00540   // dcop (containing undo information broadcasted for global undo
00541   // to all konqueror instances) can easily exceed the 64kb limit
00542   // of dcop. In order not to run into trouble we disable global
00543   // undo for now! (Simon)
00544   // ### FIXME: post 2.1
00545   return false;
00546 
00547   DCOPClient *client = kapp->dcopClient();
00548 
00549   if ( client->appId() == "kdesktop" ) // we are master :)
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"
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Thu Jan 29 23:03:28 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001