• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • libkonq
 

libkonq

  • libkonq
konq_undo.cpp
1 /* This file is part of the KDE project
2  Copyright (C) 2000 Simon Hausmann <hausmann@kde.org>
3 
4  This library is free software; you can redistribute it and/or
5  modify it under the terms of the GNU Library General Public
6  License as published by the Free Software Foundation; either
7  version 2 of the License, or (at your option) any later version.
8 
9  This library is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  Library General Public License for more details.
13 
14  You should have received a copy of the GNU Library General Public License
15  along with this library; see the file COPYING.LIB. If not, write to
16  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  Boston, MA 02110-1301, USA.
18 */
19 
20 #include "konq_undo.h"
21 
22 #undef Always
23 
24 #include <tdeio/uiserver_stub.h>
25 #include "konq_operations.h"
26 
27 #include <assert.h>
28 
29 #include <dcopclient.h>
30 #include <dcopref.h>
31 
32 #include <tdeapplication.h>
33 #include <kdatastream.h>
34 #include <kdebug.h>
35 #include <tdelocale.h>
36 #include <tdeglobalsettings.h>
37 #include <tdeconfig.h>
38 #include <kipc.h>
39 
40 #include <tdeio/job.h>
41 #include <kdirnotify_stub.h>
42 
43 inline const char *dcopTypeName( const KonqCommand & ) { return "KonqCommand"; }
44 inline const char *dcopTypeName( const KonqCommand::Stack & ) { return "KonqCommand::Stack"; }
45 
66 class KonqUndoJob : public TDEIO::Job
67 {
68 public:
69  KonqUndoJob() : TDEIO::Job( true ) { KonqUndoManager::incRef(); };
70  virtual ~KonqUndoJob() { KonqUndoManager::decRef(); }
71 
72  virtual void kill( bool q) { KonqUndoManager::self()->stopUndo( true ); TDEIO::Job::kill( q ); }
73 };
74 
75 class KonqCommandRecorder::KonqCommandRecorderPrivate
76 {
77 public:
78  KonqCommandRecorderPrivate()
79  {
80  }
81  ~KonqCommandRecorderPrivate()
82  {
83  }
84 
85  KonqCommand m_cmd;
86 };
87 
88 KonqCommandRecorder::KonqCommandRecorder( KonqCommand::Type op, const KURL::List &src, const KURL &dst, TDEIO::Job *job )
89  : TQObject( job, "konqcmdrecorder" )
90 {
91  d = new KonqCommandRecorderPrivate;
92  d->m_cmd.m_type = op;
93  d->m_cmd.m_valid = true;
94  d->m_cmd.m_src = src;
95  d->m_cmd.m_dst = dst;
96  connect( job, TQ_SIGNAL( result( TDEIO::Job * ) ),
97  this, TQ_SLOT( slotResult( TDEIO::Job * ) ) );
98 
99  if ( op != KonqCommand::MKDIR ) {
100  connect( job, TQ_SIGNAL( copyingDone( TDEIO::Job *, const KURL &, const KURL &, bool, bool ) ),
101  this, TQ_SLOT( slotCopyingDone( TDEIO::Job *, const KURL &, const KURL &, bool, bool ) ) );
102  connect( job, TQ_SIGNAL( copyingLinkDone( TDEIO::Job *, const KURL &, const TQString &, const KURL & ) ),
103  this, TQ_SLOT( slotCopyingLinkDone( TDEIO::Job *, const KURL &, const TQString &, const KURL & ) ) );
104  }
105 
106  KonqUndoManager::incRef();
107 }
108 
109 KonqCommandRecorder::~KonqCommandRecorder()
110 {
111  KonqUndoManager::decRef();
112  delete d;
113 }
114 
115 void KonqCommandRecorder::slotResult( TDEIO::Job *job )
116 {
117  if ( job->error() )
118  return;
119 
120  KonqUndoManager::self()->addCommand( d->m_cmd );
121 }
122 
123 void KonqCommandRecorder::slotCopyingDone( TDEIO::Job *job, const KURL &from, const KURL &to, bool directory, bool renamed )
124 {
125  KonqBasicOperation op;
126  op.m_valid = true;
127  op.m_directory = directory;
128  op.m_renamed = renamed;
129  op.m_src = from;
130  op.m_dst = to;
131  op.m_link = false;
132 
133  if ( d->m_cmd.m_type == KonqCommand::TRASH )
134  {
135  Q_ASSERT( from.isLocalFile() );
136  Q_ASSERT( to.protocol() == "trash" );
137  TQMap<TQString, TQString> metaData = job->metaData();
138  TQMap<TQString, TQString>::ConstIterator it = metaData.find( "trashURL-" + from.path() );
139  if ( it != metaData.end() ) {
140  // Update URL
141  op.m_dst = it.data();
142  }
143  }
144 
145  d->m_cmd.m_opStack.prepend( op );
146 }
147 
148 void KonqCommandRecorder::slotCopyingLinkDone( TDEIO::Job *, const KURL &from, const TQString &target, const KURL &to )
149 {
150  KonqBasicOperation op;
151  op.m_valid = true;
152  op.m_directory = false;
153  op.m_renamed = false;
154  op.m_src = from;
155  op.m_target = target;
156  op.m_dst = to;
157  op.m_link = true;
158  d->m_cmd.m_opStack.prepend( op );
159 }
160 
161 KonqUndoManager *KonqUndoManager::s_self = 0;
162 unsigned long KonqUndoManager::s_refCnt = 0;
163 
164 class KonqUndoManager::KonqUndoManagerPrivate
165 {
166 public:
167  KonqUndoManagerPrivate()
168  {
169  m_uiserver = new UIServer_stub( "tdeio_uiserver", "UIServer" );
170  m_undoJob = 0;
171  }
172  ~KonqUndoManagerPrivate()
173  {
174  delete m_uiserver;
175  }
176 
177  bool m_syncronized;
178 
179  KonqCommand::Stack m_commands;
180 
181  KonqCommand m_current;
182  TDEIO::Job *m_currentJob;
183  UndoState m_undoState;
184  TQValueStack<KURL> m_dirStack;
185  TQValueStack<KURL> m_dirCleanupStack;
186  TQValueStack<KURL> m_fileCleanupStack;
187  TQValueList<KURL> m_dirsToUpdate;
188 
189  bool m_lock;
190 
191  UIServer_stub *m_uiserver;
192  int m_uiserverJobId;
193 
194  KonqUndoJob *m_undoJob;
195 };
196 
197 KonqUndoManager::KonqUndoManager()
198 : DCOPObject( "KonqUndoManager" )
199 {
200  if ( !tdeApp->dcopClient()->isAttached() )
201  tdeApp->dcopClient()->attach();
202 
203  d = new KonqUndoManagerPrivate;
204  d->m_syncronized = initializeFromKDesky();
205  d->m_lock = false;
206  d->m_currentJob = 0;
207 }
208 
209 KonqUndoManager::~KonqUndoManager()
210 {
211  delete d;
212 }
213 
214 void KonqUndoManager::incRef()
215 {
216  s_refCnt++;
217 }
218 
219 void KonqUndoManager::decRef()
220 {
221  s_refCnt--;
222  if ( s_refCnt == 0 && s_self )
223  {
224  delete s_self;
225  s_self = 0;
226  }
227 }
228 
229 KonqUndoManager *KonqUndoManager::self()
230 {
231  if ( !s_self )
232  {
233  if ( s_refCnt == 0 )
234  s_refCnt++; // someone forgot to call incRef
235  s_self = new KonqUndoManager;
236  }
237  return s_self;
238 }
239 
240 void KonqUndoManager::addCommand( const KonqCommand &cmd )
241 {
242  broadcastPush( cmd );
243 }
244 
245 bool KonqUndoManager::undoAvailable() const
246 {
247  return ( d->m_commands.count() > 0 ) && !d->m_lock;
248 }
249 
250 TQString KonqUndoManager::undoText() const
251 {
252  if ( d->m_commands.count() == 0 )
253  return i18n( "Und&o" );
254 
255  KonqCommand::Type t = d->m_commands.top().m_type;
256  if ( t == KonqCommand::COPY )
257  return i18n( "Und&o: Copy" );
258  else if ( t == KonqCommand::LINK )
259  return i18n( "Und&o: Link" );
260  else if ( t == KonqCommand::MOVE )
261  return i18n( "Und&o: Move" );
262  else if ( t == KonqCommand::TRASH )
263  return i18n( "Und&o: Trash" );
264  else if ( t == KonqCommand::MKDIR )
265  return i18n( "Und&o: Create Folder" );
266  else
267  assert( false );
268  /* NOTREACHED */
269  return TQString::null;
270 }
271 
272 void KonqUndoManager::undo()
273 {
274  KonqCommand cmd = d->m_commands.top();
275  assert( cmd.m_valid );
276 
277  d->m_current = cmd;
278 
279  TQValueList<KonqBasicOperation>& opStack = d->m_current.m_opStack;
280 
281  // Let's first ask for confirmation if we need to delete any file (#99898)
282  KURL::List fileCleanupStack;
283  TQValueList<KonqBasicOperation>::Iterator it = opStack.begin();
284  for ( ; it != opStack.end() ; ++it ) {
285  if ( !(*it).m_directory && !(*it).m_link && d->m_current.m_type == KonqCommand::COPY ) {
286  fileCleanupStack.append( (*it).m_dst );
287  }
288  }
289  if ( !fileCleanupStack.isEmpty() ) {
290  // Because undo can happen with an accidental Ctrl-Z, we want to always confirm.
291  if ( !KonqOperations::askDeleteConfirmation( fileCleanupStack, KonqOperations::DEL,
292  KonqOperations::FORCE_CONFIRMATION,
293  0 /* TODO parent */ ) )
294  return;
295  }
296 
297  d->m_dirCleanupStack.clear();
298  d->m_dirStack.clear();
299  d->m_dirsToUpdate.clear();
300 
301  d->m_undoState = MOVINGFILES;
302 
303  broadcastPop();
304  broadcastLock();
305 
306  it = opStack.begin();
307  TQValueList<KonqBasicOperation>::Iterator end = opStack.end();
308  while ( it != end )
309  {
310  if ( (*it).m_directory && !(*it).m_renamed )
311  {
312  d->m_dirStack.push( (*it).m_src );
313  d->m_dirCleanupStack.prepend( (*it).m_dst );
314  it = d->m_current.m_opStack.remove( it );
315  d->m_undoState = MAKINGDIRS;
316  kdDebug(1203) << "KonqUndoManager::undo MAKINGDIRS" << endl;
317  }
318  else if ( (*it).m_link )
319  {
320  if ( !d->m_fileCleanupStack.contains( (*it).m_dst ) )
321  d->m_fileCleanupStack.prepend( (*it).m_dst );
322 
323  if ( d->m_current.m_type != KonqCommand::MOVE )
324  it = d->m_current.m_opStack.remove( it );
325  else
326  ++it;
327  }
328  else
329  ++it;
330  }
331 
332  /* this shouldn't be necessary at all:
333  * 1) the source list may contain files, we don't want to
334  * create those as... directories
335  * 2) all directories that need creation should already be in the
336  * directory stack
337  if ( d->m_undoState == MAKINGDIRS )
338  {
339  KURL::List::ConstIterator it = d->m_current.m_src.begin();
340  KURL::List::ConstIterator end = d->m_current.m_src.end();
341  for (; it != end; ++it )
342  if ( !d->m_dirStack.contains( *it) )
343  d->m_dirStack.push( *it );
344  }
345  */
346 
347  if ( d->m_current.m_type != KonqCommand::MOVE )
348  d->m_dirStack.clear();
349 
350  d->m_undoJob = new KonqUndoJob;
351  d->m_uiserverJobId = d->m_undoJob->progressId();
352  undoStep();
353 }
354 
355 void KonqUndoManager::stopUndo( bool step )
356 {
357  d->m_current.m_opStack.clear();
358  d->m_dirCleanupStack.clear();
359  d->m_fileCleanupStack.clear();
360  d->m_undoState = REMOVINGDIRS;
361  d->m_undoJob = 0;
362 
363  if ( d->m_currentJob )
364  d->m_currentJob->kill( true );
365 
366  d->m_currentJob = 0;
367 
368  if ( step )
369  undoStep();
370 }
371 
372 void KonqUndoManager::slotResult( TDEIO::Job *job )
373 {
374  d->m_uiserver->jobFinished( d->m_uiserverJobId );
375  if ( job->error() )
376  {
377  job->showErrorDialog( 0L );
378  d->m_currentJob = 0;
379  stopUndo( false );
380  if ( d->m_undoJob )
381  {
382  delete d->m_undoJob;
383  d->m_undoJob = 0;
384  }
385  }
386 
387  undoStep();
388 }
389 
390 
391 void KonqUndoManager::addDirToUpdate( const KURL& url )
392 {
393  if ( d->m_dirsToUpdate.find( url ) == d->m_dirsToUpdate.end() )
394  d->m_dirsToUpdate.prepend( url );
395 }
396 
397 void KonqUndoManager::undoStep()
398 {
399  d->m_currentJob = 0;
400 
401  if ( d->m_undoState == MAKINGDIRS )
402  undoMakingDirectories();
403 
404  if ( d->m_undoState == MOVINGFILES )
405  undoMovingFiles();
406 
407  if ( d->m_undoState == REMOVINGFILES )
408  undoRemovingFiles();
409 
410  if ( d->m_undoState == REMOVINGDIRS )
411  undoRemovingDirectories();
412 
413  if ( d->m_currentJob )
414  connect( d->m_currentJob, TQ_SIGNAL( result( TDEIO::Job * ) ),
415  this, TQ_SLOT( slotResult( TDEIO::Job * ) ) );
416 }
417 
418 void KonqUndoManager::undoMakingDirectories()
419 {
420  if ( !d->m_dirStack.isEmpty() ) {
421  KURL dir = d->m_dirStack.pop();
422  kdDebug(1203) << "KonqUndoManager::undoStep creatingDir " << dir.prettyURL() << endl;
423  d->m_currentJob = TDEIO::mkdir( dir );
424  d->m_uiserver->creatingDir( d->m_uiserverJobId, dir );
425  }
426  else
427  d->m_undoState = MOVINGFILES;
428 }
429 
430 void KonqUndoManager::undoMovingFiles()
431 {
432  if ( !d->m_current.m_opStack.isEmpty() )
433  {
434  KonqBasicOperation op = d->m_current.m_opStack.pop();
435 
436  assert( op.m_valid );
437  if ( op.m_directory )
438  {
439  if ( op.m_renamed )
440  {
441  kdDebug(1203) << "KonqUndoManager::undoStep rename " << op.m_dst.prettyURL() << " " << op.m_src.prettyURL() << endl;
442  d->m_currentJob = TDEIO::rename( op.m_dst, op.m_src, false );
443  d->m_uiserver->moving( d->m_uiserverJobId, op.m_dst, op.m_src );
444  }
445  else
446  assert( 0 ); // this should not happen!
447  }
448  else if ( op.m_link )
449  {
450  kdDebug(1203) << "KonqUndoManager::undoStep symlink " << op.m_target << " " << op.m_src.prettyURL() << endl;
451  d->m_currentJob = TDEIO::symlink( op.m_target, op.m_src, true, false );
452  }
453  else if ( d->m_current.m_type == KonqCommand::COPY )
454  {
455  kdDebug(1203) << "KonqUndoManager::undoStep file_delete " << op.m_dst.prettyURL() << endl;
456  d->m_currentJob = TDEIO::file_delete( op.m_dst );
457  d->m_uiserver->deleting( d->m_uiserverJobId, op.m_dst );
458  }
459  else if ( d->m_current.m_type == KonqCommand::MOVE
460  || d->m_current.m_type == KonqCommand::TRASH )
461  {
462  kdDebug(1203) << "KonqUndoManager::undoStep file_move " << op.m_dst.prettyURL() << " " << op.m_src.prettyURL() << endl;
463  d->m_currentJob = TDEIO::file_move( op.m_dst, op.m_src, -1, true );
464  d->m_uiserver->moving( d->m_uiserverJobId, op.m_dst, op.m_src );
465  }
466 
467  // The above TDEIO jobs are lowlevel, they don't trigger KDirNotify notification
468  // So we need to do it ourselves (but schedule it to the end of the undo, to compress them)
469  KURL url( op.m_dst );
470  url.setPath( url.directory() );
471  addDirToUpdate( url );
472 
473  url = op.m_src;
474  url.setPath( url.directory() );
475  addDirToUpdate( url );
476  }
477  else
478  d->m_undoState = REMOVINGFILES;
479 }
480 
481 void KonqUndoManager::undoRemovingFiles()
482 {
483  kdDebug(1203) << "KonqUndoManager::undoStep REMOVINGFILES" << endl;
484  if ( !d->m_fileCleanupStack.isEmpty() )
485  {
486  KURL file = d->m_fileCleanupStack.pop();
487  kdDebug(1203) << "KonqUndoManager::undoStep file_delete " << file.prettyURL() << endl;
488  d->m_currentJob = TDEIO::file_delete( file );
489  d->m_uiserver->deleting( d->m_uiserverJobId, file );
490 
491  KURL url( file );
492  url.setPath( url.directory() );
493  addDirToUpdate( url );
494  }
495  else
496  {
497  d->m_undoState = REMOVINGDIRS;
498 
499  if ( d->m_dirCleanupStack.isEmpty() && d->m_current.m_type == KonqCommand::MKDIR )
500  d->m_dirCleanupStack << d->m_current.m_dst;
501  }
502 }
503 
504 void KonqUndoManager::undoRemovingDirectories()
505 {
506  if ( !d->m_dirCleanupStack.isEmpty() )
507  {
508  KURL dir = d->m_dirCleanupStack.pop();
509  kdDebug(1203) << "KonqUndoManager::undoStep rmdir " << dir.prettyURL() << endl;
510  d->m_currentJob = TDEIO::rmdir( dir );
511  d->m_uiserver->deleting( d->m_uiserverJobId, dir );
512  addDirToUpdate( dir );
513  }
514  else
515  {
516  d->m_current.m_valid = false;
517  d->m_currentJob = 0;
518  if ( d->m_undoJob )
519  {
520  kdDebug(1203) << "KonqUndoManager::undoStep deleting undojob" << endl;
521  d->m_uiserver->jobFinished( d->m_uiserverJobId );
522  delete d->m_undoJob;
523  d->m_undoJob = 0;
524  }
525  KDirNotify_stub allDirNotify( "*", "KDirNotify*" );
526  TQValueList<KURL>::ConstIterator it = d->m_dirsToUpdate.begin();
527  for( ; it != d->m_dirsToUpdate.end(); ++it ) {
528  kdDebug() << "Notifying FilesAdded for " << *it << endl;
529  allDirNotify.FilesAdded( *it );
530  }
531  broadcastUnlock();
532  }
533 }
534 
535 void KonqUndoManager::push( const KonqCommand &cmd )
536 {
537  d->m_commands.push( cmd );
538  emit undoAvailable( true );
539  emit undoTextChanged( undoText() );
540 }
541 
542 void KonqUndoManager::pop()
543 {
544  d->m_commands.pop();
545  emit undoAvailable( undoAvailable() );
546  emit undoTextChanged( undoText() );
547 }
548 
549 void KonqUndoManager::lock()
550 {
551 // assert( !d->m_lock );
552  d->m_lock = true;
553  emit undoAvailable( undoAvailable() );
554 }
555 
556 void KonqUndoManager::unlock()
557 {
558 // assert( d->m_lock );
559  d->m_lock = false;
560  emit undoAvailable( undoAvailable() );
561 }
562 
563 KonqCommand::Stack KonqUndoManager::get() const
564 {
565  return d->m_commands;
566 }
567 
568 void KonqUndoManager::broadcastPush( const KonqCommand &cmd )
569 {
570  if ( !d->m_syncronized )
571  {
572  push( cmd );
573  return;
574  }
575 
576  DCOPRef( "kdesktop", "KonqUndoManager" ).send( "push", cmd );
577  DCOPRef( "konqueror*", "KonqUndoManager" ).send( "push", cmd );
578 }
579 
580 void KonqUndoManager::broadcastPop()
581 {
582  if ( !d->m_syncronized )
583  {
584  pop();
585  return;
586  }
587  DCOPRef( "kdesktop", "KonqUndoManager" ).send( "pop" );
588  DCOPRef( "konqueror*", "KonqUndoManager" ).send( "pop" );
589 }
590 
591 void KonqUndoManager::broadcastLock()
592 {
593 // assert( !d->m_lock );
594 
595  if ( !d->m_syncronized )
596  {
597  lock();
598  return;
599  }
600  DCOPRef( "kdesktop", "KonqUndoManager" ).send( "lock" );
601  DCOPRef( "konqueror*", "KonqUndoManager" ).send( "lock" );
602 }
603 
604 void KonqUndoManager::broadcastUnlock()
605 {
606 // assert( d->m_lock );
607 
608  if ( !d->m_syncronized )
609  {
610  unlock();
611  return;
612  }
613  DCOPRef( "kdesktop", "KonqUndoManager" ).send( "unlock" );
614  DCOPRef( "konqueror*", "KonqUndoManager" ).send( "unlock" );
615 }
616 
617 bool KonqUndoManager::initializeFromKDesky()
618 {
619  // ### workaround for dcop problem and upcoming 2.1 release:
620  // in case of huge io operations the amount of data sent over
621  // dcop (containing undo information broadcasted for global undo
622  // to all konqueror instances) can easily exceed the 64kb limit
623  // of dcop. In order not to run into trouble we disable global
624  // undo for now! (Simon)
625  // ### FIXME: post 2.1
626  return false;
627 
628  DCOPClient *client = tdeApp->dcopClient();
629 
630  if ( client->appId() == "kdesktop" ) // we are master :)
631  return true;
632 
633  if ( !client->isApplicationRegistered( "kdesktop" ) )
634  return false;
635 
636  d->m_commands = DCOPRef( "kdesktop", "KonqUndoManager" ).call( "get" );
637  return true;
638 }
639 
640 TQDataStream &operator<<( TQDataStream &stream, const KonqBasicOperation &op )
641 {
642  stream << op.m_valid << op.m_directory << op.m_renamed << op.m_link
643  << op.m_src << op.m_dst << op.m_target;
644  return stream;
645 }
646 TQDataStream &operator>>( TQDataStream &stream, KonqBasicOperation &op )
647 {
648  stream >> op.m_valid >> op.m_directory >> op.m_renamed >> op.m_link
649  >> op.m_src >> op.m_dst >> op.m_target;
650  return stream;
651 }
652 
653 TQDataStream &operator<<( TQDataStream &stream, const KonqCommand &cmd )
654 {
655  stream << cmd.m_valid << (TQ_INT8)cmd.m_type << cmd.m_opStack << cmd.m_src << cmd.m_dst;
656  return stream;
657 }
658 
659 TQDataStream &operator>>( TQDataStream &stream, KonqCommand &cmd )
660 {
661  TQ_INT8 type;
662  stream >> cmd.m_valid >> type >> cmd.m_opStack >> cmd.m_src >> cmd.m_dst;
663  cmd.m_type = static_cast<KonqCommand::Type>( type );
664  return stream;
665 }
666 
667 #include "konq_undo.moc"
KonqOperations::askDeleteConfirmation
static bool askDeleteConfirmation(const KURL::List &selectedURLs, int method, ConfirmationType confirmation, TQWidget *widget)
Ask for confirmation before deleting/trashing selectedURLs.
Definition: konq_operations.cpp:238
TDEIO
Definition: favicons.h:25

libkonq

Skip menu "libkonq"
  • Main Page
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members
  • Related Pages

libkonq

Skip menu "libkonq"
  • kate
  • libkonq
  • twin
  •   lib
Generated for libkonq by doxygen 1.8.13
This website is maintained by Timothy Pearson.