• 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
43inline const char *dcopTypeName( const KonqCommand & ) { return "KonqCommand"; }
44inline const char *dcopTypeName( const KonqCommand::Stack & ) { return "KonqCommand::Stack"; }
45
66class KonqUndoJob : public TDEIO::Job
67{
68public:
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
75class KonqCommandRecorder::KonqCommandRecorderPrivate
76{
77public:
78 KonqCommandRecorderPrivate()
79 {
80 }
81 ~KonqCommandRecorderPrivate()
82 {
83 }
84
85 KonqCommand m_cmd;
86};
87
88KonqCommandRecorder::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
109KonqCommandRecorder::~KonqCommandRecorder()
110{
111 KonqUndoManager::decRef();
112 delete d;
113}
114
115void KonqCommandRecorder::slotResult( TDEIO::Job *job )
116{
117 if ( job->error() )
118 return;
119
120 KonqUndoManager::self()->addCommand( d->m_cmd );
121}
122
123void 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
148void 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
161KonqUndoManager *KonqUndoManager::s_self = 0;
162unsigned long KonqUndoManager::s_refCnt = 0;
163
164class KonqUndoManager::KonqUndoManagerPrivate
165{
166public:
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
197KonqUndoManager::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
209KonqUndoManager::~KonqUndoManager()
210{
211 delete d;
212}
213
214void KonqUndoManager::incRef()
215{
216 s_refCnt++;
217}
218
219void 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
229KonqUndoManager *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
240void KonqUndoManager::addCommand( const KonqCommand &cmd )
241{
242 broadcastPush( cmd );
243}
244
245bool KonqUndoManager::undoAvailable() const
246{
247 return ( d->m_commands.count() > 0 ) && !d->m_lock;
248}
249
250TQString 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
272void 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
355void 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
372void 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
391void 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
397void 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
418void 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
430void 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
481void 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
504void 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
535void KonqUndoManager::push( const KonqCommand &cmd )
536{
537 d->m_commands.push( cmd );
538 emit undoAvailable( true );
539 emit undoTextChanged( undoText() );
540}
541
542void KonqUndoManager::pop()
543{
544 d->m_commands.pop();
545 emit undoAvailable( undoAvailable() );
546 emit undoTextChanged( undoText() );
547}
548
549void KonqUndoManager::lock()
550{
551// assert( !d->m_lock );
552 d->m_lock = true;
553 emit undoAvailable( undoAvailable() );
554}
555
556void KonqUndoManager::unlock()
557{
558// assert( d->m_lock );
559 d->m_lock = false;
560 emit undoAvailable( undoAvailable() );
561}
562
563KonqCommand::Stack KonqUndoManager::get() const
564{
565 return d->m_commands;
566}
567
568void 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
580void 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
591void 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
604void 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
617bool 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
640TQDataStream &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}
646TQDataStream &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
653TQDataStream &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
659TQDataStream &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

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.9.4
This website is maintained by Timothy Pearson.