]> cloud.milkyroute.net Git - dolphin.git/blob - src/undomanager.cpp
Use a KIO Job for applying the view properties recursively to sub directories.
[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 "dolphinmainwindow.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(DolphinMainWindow* mainWindow)
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 /*
128 * KDE4, ### TODO Only here to avoid possible crash
129 */
130 ProgressIndicator progressIndicator(mainWindow, i18n("Executing undo operation..."),
131 i18n("Executed undo operation."),
132 progressCount);
133
134 for (int i = 0; i < macroCount; ++i) {
135 const DolphinCommand command = m_history[m_historyIndex];
136
137 --m_historyIndex;
138 if (m_historyIndex < 0) {
139 emit undoAvailable(false);
140 emit undoTextChanged(i18n("Undo"));
141 }
142 else {
143 emit undoTextChanged(i18n("Undo: %1",commandText(m_history[m_historyIndex])));
144 }
145
146 if (m_historyIndex < static_cast<int>(m_history.count()) - 1) {
147 emit redoAvailable(true);
148 emit redoTextChanged(i18n("Redo: %1",commandText(command)));
149 }
150 else {
151 emit redoAvailable(false);
152 emit redoTextChanged(i18n("Redo"));
153 }
154
155 KUrl::List sourceUrls = command.source();
156 KUrl::List::Iterator it = sourceUrls.begin();
157 const KUrl::List::Iterator end = sourceUrls.end();
158 const QString destUrl(command.destination().prettyUrl(KUrl::AddTrailingSlash));
159
160 KIO::Job* job = 0;
161 switch (command.type()) {
162 case DolphinCommand::Link:
163 case DolphinCommand::Copy: {
164 KUrl::List list;
165 while (it != end) {
166 const KUrl deleteUrl(destUrl + (*it).fileName());
167 list.append(deleteUrl);
168 ++it;
169 }
170 job = KIO::del(list, false, false);
171 break;
172 }
173
174 case DolphinCommand::Move: {
175 KUrl::List list;
176 const KUrl newDestUrl((*it).directory());
177 while (it != end) {
178 const KUrl newSourceUrl(destUrl + (*it).fileName());
179 list.append(newSourceUrl);
180 ++it;
181 }
182 job = KIO::move(list, newDestUrl, false);
183 break;
184 }
185
186 case DolphinCommand::Rename: {
187 assert(sourceUrls.count() == 1);
188 KIO::NetAccess::move(command.destination(), (*it));
189 break;
190 }
191
192 case DolphinCommand::Trash: {
193 while (it != end) {
194 // TODO: use KIO::special for accessing the trash protocol. See
195 // also Dolphin::slotJobResult() for further details.
196 const QString originalFileName((*it).fileName().section('-', 1));
197 KUrl newDestUrl(destUrl + originalFileName);
198 KIO::NetAccess::move(*it, newDestUrl);
199 ++it;
200
201 progressIndicator.execOperation();
202 }
203 break;
204 }
205
206 case DolphinCommand::CreateFolder:
207 case DolphinCommand::CreateFile: {
208 KIO::NetAccess::del(command.destination(), mainWindow);
209 break;
210 }
211 }
212
213 if (job != 0) {
214 // Execute the jobs in a synchronous manner and forward the progress
215 // information to the Dolphin statusbar.
216 connect(job, SIGNAL(percent(KIO::Job*, unsigned long)),
217 this, SLOT(slotPercent(KIO::Job*, unsigned long)));
218 KIO::NetAccess::synchronousRun(job, mainWindow);
219 }
220
221 progressIndicator.execOperation();
222 }
223 }
224
225 void UndoManager::redo(DolphinMainWindow *mainWindow)
226 {
227 if (m_recordMacro) {
228 endMacro();
229 }
230
231 const int maxHistoryIndex = m_history.count() - 1;
232 if (m_historyIndex >= maxHistoryIndex) {
233 return;
234 }
235 ++m_historyIndex;
236
237 int progressCount = 0;
238 int macroCount = 1;
239 calcStepsCount(macroCount, progressCount);
240
241 ProgressIndicator progressIndicator(mainWindow, i18n("Executing redo operation..."),
242 i18n("Executed redo operation."),
243 progressCount);
244
245 for (int i = 0; i < macroCount; ++i) {
246 const DolphinCommand command = m_history[m_historyIndex];
247 if (m_historyIndex >= maxHistoryIndex) {
248 emit redoAvailable(false);
249 emit redoTextChanged(i18n("Redo"));
250 }
251 else {
252 emit redoTextChanged(i18n("Redo: %1",commandText(m_history[m_historyIndex + 1])));
253 }
254
255 emit undoAvailable(true);
256 emit undoTextChanged(i18n("Undo: %1",commandText(command)));
257
258 KUrl::List sourceUrls = command.source();
259 KUrl::List::Iterator it = sourceUrls.begin();
260 const KUrl::List::Iterator end = sourceUrls.end();
261
262 KIO::Job* job = 0;
263 switch (command.type()) {
264 case DolphinCommand::Link: {
265 job = KIO::link(sourceUrls, command.destination(), false);
266 break;
267 }
268
269 case DolphinCommand::Copy: {
270 job = KIO::copy(sourceUrls, command.destination(), false);
271 break;
272 }
273
274 case DolphinCommand::Rename:
275 case DolphinCommand::Move: {
276 job = KIO::move(sourceUrls, command.destination(), false);
277 break;
278 }
279
280 case DolphinCommand::Trash: {
281 const QString destUrl(command.destination().prettyUrl());
282 while (it != end) {
283 // TODO: use KIO::special for accessing the trash protocol. See
284 // also Dolphin::slotJobResult() for further details.
285 const QString originalFileName((*it).fileName().section('-', 1));
286 KUrl originalSourceUrl(destUrl + "/" + originalFileName);
287 KIO::Job* moveToTrashJob = KIO::trash(originalSourceUrl);
288 KIO::NetAccess::synchronousRun(moveToTrashJob, mainWindow);
289 ++it;
290
291 progressIndicator.execOperation();
292 }
293 break;
294 }
295
296 case DolphinCommand::CreateFolder: {
297 KIO::NetAccess::mkdir(command.destination(), mainWindow);
298 break;
299 }
300
301 case DolphinCommand::CreateFile: {
302 progressIndicator.execOperation();
303 KUrl::List::Iterator it = sourceUrls.begin();
304 assert(sourceUrls.count() == 1);
305 KIO::CopyJob* copyJob = KIO::copyAs(*it, command.destination(), false);
306 copyJob->setDefaultPermissions(true);
307 job = copyJob;
308 break;
309 }
310 }
311
312 if (job != 0) {
313 // Execute the jobs in a synchronous manner and forward the progress
314 // information to the Dolphin statusbar.
315 connect(job, SIGNAL(percent(KJob*, unsigned long)),
316 this, SLOT(slotPercent(KJob*, unsigned long)));
317 KIO::NetAccess::synchronousRun(job, mainWindow);
318 }
319
320 ++m_historyIndex;
321 progressIndicator.execOperation();
322 }
323
324 --m_historyIndex;
325
326 }
327
328 UndoManager::UndoManager() :
329 m_recordMacro(false),
330 m_historyIndex(-1),
331 m_macroCounter(0)
332 {
333 }
334
335 UndoManager::~UndoManager()
336 {
337 }
338
339 QString UndoManager::commandText(const DolphinCommand& command) const
340 {
341 QString text;
342 switch (command.type()) {
343 case DolphinCommand::Copy: text = i18n("Copy"); break;
344 case DolphinCommand::Move: text = i18n("Move"); break;
345 case DolphinCommand::Link: text = i18n("Link"); break;
346 case DolphinCommand::Rename: text = i18n("Rename"); break;
347 case DolphinCommand::Trash: text = i18n("Move to Trash"); break;
348 case DolphinCommand::CreateFolder: text = i18n("Create New Folder"); break;
349 case DolphinCommand::CreateFile: text = i18n("Create New File"); break;
350 default: break;
351 }
352 return text;
353 }
354
355 void UndoManager::slotPercent(KJob* /* job */, unsigned long /* percent */)
356 {
357 // It is not allowed to update the progress indicator in the context
358 // of this slot, hence do an asynchronous triggering.
359 QTimer::singleShot(0, this, SLOT(updateProgress()));
360 }
361
362 void UndoManager::updateProgress()
363 {
364 /*
365 * ### XXX, TODO, KDE4 make this work when switchting to KonqUndoManager
366 */
367 //m_progressIndicator->execOperation();
368 }
369
370 void UndoManager::calcStepsCount(int& macroCount, int& progressCount)
371 {
372 progressCount = 0;
373 macroCount = 0;
374
375 const int macroIndex = m_history[m_historyIndex].m_macroIndex;
376 if (macroIndex < 0) {
377 // default use case: no macro has been recorded
378 macroCount = 1;
379 progressCount = m_history[m_historyIndex].source().count();
380 return;
381 }
382
383 // iterate backward for undo...
384 int i = m_historyIndex;
385 while ((i >= 0) && (m_history[i].m_macroIndex == macroIndex)) {
386 ++macroCount;
387 progressCount += m_history[i].source().count();
388 --i;
389 }
390
391 // iterate forward for redo...
392 const int max = m_history.count() - 1;
393 i = m_historyIndex + 1;
394 while ((i <= max) && (m_history[i].m_macroIndex == macroIndex)) {
395 ++macroCount;
396 progressCount += m_history[i].source().count();
397 ++i;
398 }
399 }
400
401 #include "undomanager.moc"
402
403