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.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Sep 16 15:59:26 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003