]> cloud.milkyroute.net Git - dolphin.git/blob - src/undomanager.cpp
Initial step for moving to KDirModel. Large code parts have been deleted, as a step...
[dolphin.git] / src / undomanager.cpp
1 /***************************************************************************
2 * Copyright (C) 2006 by Peter Penz *
3 * peter.penz@gmx.at *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
20
21 #include "undomanager.h"
22 #include <klocale.h>
23 #include <kio/netaccess.h>
24 #include <qtimer.h>
25 #include <assert.h>
26
27 #include "dolphin.h"
28 #include "dolphinstatusbar.h"
29 #include "progressindicator.h"
30
31 DolphinCommand::DolphinCommand() :
32 m_type(Copy),
33 m_macroIndex(-1)
34 {
35 // Implementation note: DolphinCommands are stored in a QValueList, whereas
36 // QValueList requires a default constructor of the added class.
37 // Instead of expressing this implementation detail to the interface by adding a
38 // Type::Undefined just Type::Copy is used to assure that all members have
39 // a defined state.
40 //
41 // KDE4TODO: QList doesn't require a default constructor iirc - so remove this
42 }
43
44 DolphinCommand::DolphinCommand(Type type,
45 const KUrl::List& source,
46 const KUrl& dest) :
47 m_type(type),
48 m_macroIndex(-1),
49 m_source(source),
50 m_dest(dest)
51 {
52 }
53
54 DolphinCommand::~DolphinCommand()
55 {
56 }
57
58 DolphinCommand& DolphinCommand::operator = (const DolphinCommand& command)
59 {
60 m_type = command.m_type;
61 m_source = command.m_source;
62 m_dest = command.m_dest;
63 return *this;
64 }
65
66 UndoManager& UndoManager::instance()
67 {
68 static UndoManager* instance = 0;
69 if (instance == 0) {
70 instance = new UndoManager();
71 }
72 return *instance;
73 }
74
75 void UndoManager::addCommand(const DolphinCommand& command)
76 {
77 ++m_historyIndex;
78
79 if (m_recordMacro) {
80 DolphinCommand macroCommand = command;
81 macroCommand.m_macroIndex = m_macroCounter;
82 m_history.insert(m_history.at(m_historyIndex), macroCommand);
83 }
84 else {
85 m_history.insert(m_history.at(m_historyIndex), command);
86 }
87
88 emit undoAvailable(true);
89 emit undoTextChanged(i18n("Undo: %1",commandText(command)));
90
91 // prevent an endless growing of the Undo history
92 if (m_historyIndex > 10000) {
93 m_history.erase(m_history.begin());
94 --m_historyIndex;
95 }
96 }
97
98 void UndoManager::beginMacro()
99 {
100 assert(!m_recordMacro);
101 m_recordMacro = true;
102 ++m_macroCounter;
103 }
104
105 void UndoManager::endMacro()
106 {
107 assert(m_recordMacro);
108 m_recordMacro = false;
109 }
110
111 // KDE4 TODO: consider switching to KCommandHistory (kdeui) for the command history, and to
112 // KonqCommandRecorder etc. from libkonq/konq_undo.*
113 void UndoManager::undo()
114 {
115 if (m_recordMacro) {
116 endMacro();
117 }
118
119 if (m_historyIndex < 0) {
120 return;
121 }
122
123 int progressCount = 0;
124 int macroCount = 1;
125 calcStepsCount(macroCount, progressCount);
126
127 m_progressIndicator = new ProgressIndicator(i18n("Executing undo operation..."),
128 i18n("Executed undo operation."),
129 progressCount);
130
131 for (int i = 0; i < macroCount; ++i) {
132 const DolphinCommand command = m_history[m_historyIndex];
133 --m_historyIndex;
134 if (m_historyIndex < 0) {
135 emit undoAvailable(false);
136 emit undoTextChanged(i18n("Undo"));
137 }
138 else {
139 emit undoTextChanged(i18n("Undo: %1",commandText(m_history[m_historyIndex])));
140 }
141
142 if (m_historyIndex < static_cast<int>(m_history.count()) - 1) {
143 emit redoAvailable(true);
144 emit redoTextChanged(i18n("Redo: %1",commandText(command)));
145 }
146 else {
147 emit redoAvailable(false);
148 emit redoTextChanged(i18n("Redo"));
149 }
150
151 KUrl::List sourceUrls = command.source();
152 KUrl::List::Iterator it = sourceUrls.begin();
153 const KUrl::List::Iterator end = sourceUrls.end();
154 const QString destUrl(command.destination().prettyUrl(KUrl::AddTrailingSlash));
155
156 KIO::Job* job = 0;
157 switch (command.type()) {
158 case DolphinCommand::Link:
159 case DolphinCommand::Copy: {
160 KUrl::List list;
161 while (it != end) {
162 const KUrl deleteUrl(destUrl + (*it).fileName());
163 list.append(deleteUrl);
164 ++it;
165 }
166 job = KIO::del(list, false, false);
167 break;
168 }
169
170 case DolphinCommand::Move: {
171 KUrl::List list;
172 const KUrl newDestUrl((*it).directory());
173 while (it != end) {
174 const KUrl newSourceUrl(destUrl + (*it).fileName());
175 list.append(newSourceUrl);
176 ++it;
177 }
178 job = KIO::move(list, newDestUrl, false);
179 break;
180 }
181
182 case DolphinCommand::Rename: {
183 assert(sourceUrls.count() == 1);
184 KIO::NetAccess::move(command.destination(), (*it));
185 break;
186 }
187
188 case DolphinCommand::Trash: {
189 while (it != end) {
190 // TODO: use KIO::special for accessing the trash protocol. See
191 // also Dolphin::slotJobResult() for further details.
192 const QString originalFileName((*it).fileName().section('-', 1));
193 KUrl newDestUrl(destUrl + originalFileName);
194 KIO::NetAccess::move(*it, newDestUrl);
195 ++it;
196
197 m_progressIndicator->execOperation();
198 }
199 break;
200 }
201
202 case DolphinCommand::CreateFolder:
203 case DolphinCommand::CreateFile: {
204 KIO::NetAccess::del(command.destination(), &Dolphin::mainWin());
205 break;
206 }
207 }
208
209 if (job != 0) {
210 // Execute the jobs in a synchronous manner and forward the progress
211 // information to the Dolphin statusbar.
212 connect(job, SIGNAL(percent(KIO::Job*, unsigned long)),
213 this, SLOT(slotPercent(KIO::Job*, unsigned long)));
214 KIO::NetAccess::synchronousRun(job, &Dolphin::mainWin());
215 }
216
217 m_progressIndicator->execOperation();
218 }
219
220 delete m_progressIndicator;
221 m_progressIndicator = 0;
222 }
223
224 void UndoManager::redo()
225 {
226 if (m_recordMacro) {
227 endMacro();
228 }
229
230 const int maxHistoryIndex = m_history.count() - 1;
231 if (m_historyIndex >= maxHistoryIndex) {
232 return;
233 }
234 ++m_historyIndex;
235
236 int progressCount = 0;
237 int macroCount = 1;
238 calcStepsCount(macroCount, progressCount);
239
240 m_progressIndicator = new ProgressIndicator(i18n("Executing redo operation..."),
241 i18n("Executed redo operation."),
242 progressCount);
243
244 for (int i = 0; i < macroCount; ++i) {
245 const DolphinCommand command = m_history[m_historyIndex];
246 if (m_historyIndex >= maxHistoryIndex) {
247 emit redoAvailable(false);
248 emit redoTextChanged(i18n("Redo"));
249 }
250 else {
251 emit redoTextChanged(i18n("Redo: %1",commandText(m_history[m_historyIndex + 1])));
252 }
253
254 emit undoAvailable(true);
255 emit undoTextChanged(i18n("Undo: %1",commandText(command)));
256
257 Dolphin& dolphin = Dolphin::mainWin();
258
259 KUrl::List sourceUrls = command.source();
260 KUrl::List::Iterator it = sourceUrls.begin();
261 const KUrl::List::Iterator end = sourceUrls.end();
262
263 KIO::Job* job = 0;
264 switch (command.type()) {
265 case DolphinCommand::Link: {
266 job = KIO::link(sourceUrls, command.destination(), false);
267 break;
268 }
269
270 case DolphinCommand::Copy: {
271 job = KIO::copy(sourceUrls, command.destination(), false);
272 break;
273 }
274
275 case DolphinCommand::Rename:
276 case DolphinCommand::Move: {
277 job = KIO::move(sourceUrls, command.destination(), false);
278 break;
279 }
280
281 case DolphinCommand::Trash: {
282 const QString destUrl(command.destination().prettyUrl());
283 while (it != end) {
284 // TODO: use KIO::special for accessing the trash protocol. See
285 // also Dolphin::slotJobResult() for further details.
286 const QString originalFileName((*it).fileName().section('-', 1));
287 KUrl originalSourceUrl(destUrl + "/" + originalFileName);
288 KIO::Job* moveToTrashJob = KIO::trash(originalSourceUrl);
289 KIO::NetAccess::synchronousRun(moveToTrashJob, &dolphin);
290 ++it;
291
292 m_progressIndicator->execOperation();
293 }
294 break;
295 }
296
297 case DolphinCommand::CreateFolder: {
298 KIO::NetAccess::mkdir(command.destination(), &dolphin);
299 break;
300 }
301
302 case DolphinCommand::CreateFile: {
303 m_progressIndicator->execOperation();
304 KUrl::List::Iterator it = sourceUrls.begin();
305 assert(sourceUrls.count() == 1);
306 KIO::CopyJob* copyJob = KIO::copyAs(*it, command.destination(), false);
307 copyJob->setDefaultPermissions(true);
308 job = copyJob;
309 break;
310 }
311 }
312
313 if (job != 0) {
314 // Execute the jobs in a synchronous manner and forward the progress
315 // information to the Dolphin statusbar.
316 connect(job, SIGNAL(percent(KIO::Job*, unsigned long)),
317 this, SLOT(slotPercent(KIO::Job*, unsigned long)));
318 KIO::NetAccess::synchronousRun(job, &dolphin);
319 }
320
321 ++m_historyIndex;
322 m_progressIndicator->execOperation();
323 }
324
325 --m_historyIndex;
326
327 delete m_progressIndicator;
328 m_progressIndicator = 0;
329 }
330
331 UndoManager::UndoManager() :
332 m_recordMacro(false),
333 m_historyIndex(-1),
334 m_macroCounter(0),
335 m_progressIndicator(0)
336 {
337 }
338
339 UndoManager::~UndoManager()
340 {
341 delete m_progressIndicator;
342 m_progressIndicator = 0;
343 }
344
345 QString UndoManager::commandText(const DolphinCommand& command) const
346 {
347 QString text;
348 switch (command.type()) {
349 case DolphinCommand::Copy: text = i18n("Copy"); break;
350 case DolphinCommand::Move: text = i18n("Move"); break;
351 case DolphinCommand::Link: text = i18n("Link"); break;
352 case DolphinCommand::Rename: text = i18n("Rename"); break;
353 case DolphinCommand::Trash: text = i18n("Move to Trash"); break;
354 case DolphinCommand::CreateFolder: text = i18n("Create New Folder"); break;
355 case DolphinCommand::CreateFile: text = i18n("Create New File"); break;
356 default: break;
357 }
358 return text;
359 }
360
361 void UndoManager::slotPercent(KIO::Job* /* job */, unsigned long /* percent */)
362 {
363 // It is not allowed to update the progress indicator in the context
364 // of this slot, hence do an asynchronous triggering.
365 QTimer::singleShot(0, this, SLOT(updateProgress()));
366 }
367
368 void UndoManager::updateProgress()
369 {
370 m_progressIndicator->execOperation();
371 }
372
373 void UndoManager::calcStepsCount(int& macroCount, int& progressCount)
374 {
375 progressCount = 0;
376 macroCount = 0;
377
378 const int macroIndex = m_history[m_historyIndex].m_macroIndex;
379 if (macroIndex < 0) {
380 // default use case: no macro has been recorded
381 macroCount = 1;
382 progressCount = m_history[m_historyIndex].source().count();
383 return;
384 }
385
386 // iterate backward for undo...
387 int i = m_historyIndex;
388 while ((i >= 0) && (m_history[i].m_macroIndex == macroIndex)) {
389 ++macroCount;
390 progressCount += m_history[i].source().count();
391 --i;
392 }
393
394 // iterate forward for redo...
395 const int max = m_history.count() - 1;
396 i = m_historyIndex + 1;
397 while ((i <= max) && (m_history[i].m_macroIndex == macroIndex)) {
398 ++macroCount;
399 progressCount += m_history[i].source().count();
400 ++i;
401 }
402 }
403
404 #include "undomanager.moc"
405
406