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