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