]> cloud.milkyroute.net Git - dolphin.git/blob - src/undomanager.cpp
commited initial version of Dolphin
[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
42 DolphinCommand::DolphinCommand(Type type,
43 const KUrl::List& source,
44 const KUrl& dest) :
45 m_type(type),
46 m_macroIndex(-1),
47 m_source(source),
48 m_dest(dest)
49 {
50 }
51
52 DolphinCommand::~DolphinCommand()
53 {
54 }
55
56 DolphinCommand& DolphinCommand::operator = (const DolphinCommand& command)
57 {
58 m_type = command.m_type;
59 m_source = command.m_source;
60 m_dest = command.m_dest;
61 return *this;
62 }
63
64 UndoManager& UndoManager::instance()
65 {
66 static UndoManager* instance = 0;
67 if (instance == 0) {
68 instance = new UndoManager();
69 }
70 return *instance;
71 }
72
73 void UndoManager::addCommand(const DolphinCommand& command)
74 {
75 ++m_historyIndex;
76
77 if (m_recordMacro) {
78 DolphinCommand macroCommand = command;
79 macroCommand.m_macroIndex = m_macroCounter;
80 m_history.insert(m_history.at(m_historyIndex), macroCommand);
81 }
82 else {
83 m_history.insert(m_history.at(m_historyIndex), command);
84 }
85
86 emit undoAvailable(true);
87 emit undoTextChanged(i18n("Undo: %1").arg(commandText(command)));
88
89 // prevent an endless growing of the Undo history
90 if (m_historyIndex > 10000) {
91 m_history.erase(m_history.begin());
92 --m_historyIndex;
93 }
94 }
95
96 void UndoManager::beginMacro()
97 {
98 assert(!m_recordMacro);
99 m_recordMacro = true;
100 ++m_macroCounter;
101 }
102
103 void UndoManager::endMacro()
104 {
105 assert(m_recordMacro);
106 m_recordMacro = false;
107 }
108
109 void UndoManager::undo()
110 {
111 if (m_recordMacro) {
112 endMacro();
113 }
114
115 if (m_historyIndex < 0) {
116 return;
117 }
118
119 int progressCount = 0;
120 int macroCount = 1;
121 calcStepsCount(macroCount, progressCount);
122
123 m_progressIndicator = new ProgressIndicator(i18n("Executing undo operation..."),
124 i18n("Executed undo operation."),
125 progressCount);
126
127 for (int i = 0; i < macroCount; ++i) {
128 const DolphinCommand command = m_history[m_historyIndex];
129 --m_historyIndex;
130 if (m_historyIndex < 0) {
131 emit undoAvailable(false);
132 emit undoTextChanged(i18n("Undo"));
133 }
134 else {
135 emit undoTextChanged(i18n("Undo: %1").arg(commandText(m_history[m_historyIndex])));
136 }
137
138 if (m_historyIndex < static_cast<int>(m_history.count()) - 1) {
139 emit redoAvailable(true);
140 emit redoTextChanged(i18n("Redo: %1").arg(commandText(command)));
141 }
142 else {
143 emit redoAvailable(false);
144 emit redoTextChanged(i18n("Redo"));
145 }
146
147 KUrl::List sourceURLs = command.source();
148 KUrl::List::Iterator it = sourceURLs.begin();
149 const KUrl::List::Iterator end = sourceURLs.end();
150 const QString destURL(command.destination().prettyURL(+1));
151
152 KIO::Job* job = 0;
153 switch (command.type()) {
154 case DolphinCommand::Link:
155 case DolphinCommand::Copy: {
156 KUrl::List list;
157 while (it != end) {
158 const KUrl deleteURL(destURL + (*it).filename());
159 list.append(deleteURL);
160 ++it;
161 }
162 job = KIO::del(list, false, false);
163 break;
164 }
165
166 case DolphinCommand::Move: {
167 KUrl::List list;
168 const KUrl newDestURL((*it).directory());
169 while (it != end) {
170 const KUrl newSourceURL(destURL + (*it).filename());
171 list.append(newSourceURL);
172 ++it;
173 }
174 job = KIO::move(list, newDestURL, false);
175 break;
176 }
177
178 case DolphinCommand::Rename: {
179 assert(sourceURLs.count() == 1);
180 KIO::NetAccess::move(command.destination(), (*it));
181 break;
182 }
183
184 case DolphinCommand::Trash: {
185 while (it != end) {
186 // TODO: use KIO::special for accessing the trash protocol. See
187 // also Dolphin::slotJobResult() for further details.
188 const QString originalFileName((*it).filename().section('-', 1));
189 KUrl newDestURL(destURL + originalFileName);
190 KIO::NetAccess::move(*it, newDestURL);
191 ++it;
192
193 m_progressIndicator->execOperation();
194 }
195 break;
196 }
197
198 case DolphinCommand::CreateFolder:
199 case DolphinCommand::CreateFile: {
200 KIO::NetAccess::del(command.destination(), &Dolphin::mainWin());
201 break;
202 }
203 }
204
205 if (job != 0) {
206 // Execute the jobs in a synchronous manner and forward the progress
207 // information to the Dolphin statusbar.
208 connect(job, SIGNAL(percent(KIO::Job*, unsigned long)),
209 this, SLOT(slotPercent(KIO::Job*, unsigned long)));
210 KIO::NetAccess::synchronousRun(job, &Dolphin::mainWin());
211 }
212
213 m_progressIndicator->execOperation();
214 }
215
216 delete m_progressIndicator;
217 m_progressIndicator = 0;
218 }
219
220 void UndoManager::redo()
221 {
222 if (m_recordMacro) {
223 endMacro();
224 }
225
226 const int maxHistoryIndex = m_history.count() - 1;
227 if (m_historyIndex >= maxHistoryIndex) {
228 return;
229 }
230 ++m_historyIndex;
231
232 int progressCount = 0;
233 int macroCount = 1;
234 calcStepsCount(macroCount, progressCount);
235
236 m_progressIndicator = new ProgressIndicator(i18n("Executing redo operation..."),
237 i18n("Executed redo operation."),
238 progressCount);
239
240 for (int i = 0; i < macroCount; ++i) {
241 const DolphinCommand command = m_history[m_historyIndex];
242 if (m_historyIndex >= maxHistoryIndex) {
243 emit redoAvailable(false);
244 emit redoTextChanged(i18n("Redo"));
245 }
246 else {
247 emit redoTextChanged(i18n("Redo: %1").arg(commandText(m_history[m_historyIndex + 1])));
248 }
249
250 emit undoAvailable(true);
251 emit undoTextChanged(i18n("Undo: %1").arg(commandText(command)));
252
253 Dolphin& dolphin = Dolphin::mainWin();
254
255 KUrl::List sourceURLs = command.source();
256 KUrl::List::Iterator it = sourceURLs.begin();
257 const KUrl::List::Iterator end = sourceURLs.end();
258
259 KIO::Job* job = 0;
260 switch (command.type()) {
261 case DolphinCommand::Link: {
262 job = KIO::link(sourceURLs, command.destination(), false);
263 break;
264 }
265
266 case DolphinCommand::Copy: {
267 job = KIO::copy(sourceURLs, command.destination(), false);
268 break;
269 }
270
271 case DolphinCommand::Rename:
272 case DolphinCommand::Move: {
273 job = KIO::move(sourceURLs, command.destination(), false);
274 break;
275 }
276
277 case DolphinCommand::Trash: {
278 const QString destURL(command.destination().prettyURL());
279 while (it != end) {
280 // TODO: use KIO::special for accessing the trash protocol. See
281 // also Dolphin::slotJobResult() for further details.
282 const QString originalFileName((*it).filename().section('-', 1));
283 KUrl originalSourceURL(destURL + "/" + originalFileName);
284 KIO::Job* moveToTrashJob = KIO::trash(originalSourceURL);
285 KIO::NetAccess::synchronousRun(moveToTrashJob, &dolphin);
286 ++it;
287
288 m_progressIndicator->execOperation();
289 }
290 break;
291 }
292
293 case DolphinCommand::CreateFolder: {
294 KIO::NetAccess::mkdir(command.destination(), &dolphin);
295 break;
296 }
297
298 case DolphinCommand::CreateFile: {
299 m_progressIndicator->execOperation();
300 KUrl::List::Iterator it = sourceURLs.begin();
301 assert(sourceURLs.count() == 1);
302 KIO::CopyJob* copyJob = KIO::copyAs(*it, command.destination(), false);
303 copyJob->setDefaultPermissions(true);
304 job = copyJob;
305 break;
306 }
307 }
308
309 if (job != 0) {
310 // Execute the jobs in a synchronous manner and forward the progress
311 // information to the Dolphin statusbar.
312 connect(job, SIGNAL(percent(KIO::Job*, unsigned long)),
313 this, SLOT(slotPercent(KIO::Job*, unsigned long)));
314 KIO::NetAccess::synchronousRun(job, &dolphin);
315 }
316
317 ++m_historyIndex;
318 m_progressIndicator->execOperation();
319 }
320
321 --m_historyIndex;
322
323 delete m_progressIndicator;
324 m_progressIndicator = 0;
325 }
326
327 UndoManager::UndoManager() :
328 m_recordMacro(false),
329 m_historyIndex(-1),
330 m_macroCounter(0),
331 m_progressIndicator(0)
332 {
333 }
334
335 UndoManager::~UndoManager()
336 {
337 delete m_progressIndicator;
338 m_progressIndicator = 0;
339 }
340
341 QString UndoManager::commandText(const DolphinCommand& command) const
342 {
343 QString text;
344 switch (command.type()) {
345 case DolphinCommand::Copy: text = i18n("Copy"); break;
346 case DolphinCommand::Move: text = i18n("Move"); break;
347 case DolphinCommand::Link: text = i18n("Link"); break;
348 case DolphinCommand::Rename: text = i18n("Rename"); break;
349 case DolphinCommand::Trash: text = i18n("Move to Trash"); break;
350 case DolphinCommand::CreateFolder: text = i18n("Create New Folder"); break;
351 case DolphinCommand::CreateFile: text = i18n("Create New File"); break;
352 default: break;
353 }
354 return text;
355 }
356
357 void UndoManager::slotPercent(KIO::Job* /* job */, unsigned long /* percent */)
358 {
359 // It is not allowed to update the progress indicator in the context
360 // of this slot, hence do an asynchronous triggering.
361 QTimer::singleShot(0, this, SLOT(updateProgress()));
362 }
363
364 void UndoManager::updateProgress()
365 {
366 m_progressIndicator->execOperation();
367 }
368
369 void UndoManager::calcStepsCount(int& macroCount, int& progressCount)
370 {
371 progressCount = 0;
372 macroCount = 0;
373
374 const int macroIndex = m_history[m_historyIndex].m_macroIndex;
375 if (macroIndex < 0) {
376 // default use case: no macro has been recorded
377 macroCount = 1;
378 progressCount = m_history[m_historyIndex].source().count();
379 return;
380 }
381
382 // iterate backward for undo...
383 int i = m_historyIndex;
384 while ((i >= 0) && (m_history[i].m_macroIndex == macroIndex)) {
385 ++macroCount;
386 progressCount += m_history[i].source().count();
387 --i;
388 }
389
390 // iterate forward for redo...
391 const int max = m_history.count() - 1;
392 i = m_historyIndex + 1;
393 while ((i <= max) && (m_history[i].m_macroIndex == macroIndex)) {
394 ++macroCount;
395 progressCount += m_history[i].source().count();
396 ++i;
397 }
398 }
399
400 #include "undomanager.moc"
401
402