1#include "Commands.h"
2#include "FSUndoRedo.h"
3#include "FSUtils.h"
4
5#include <Autolock.h>
6#include <Volume.h>
7#include <Node.h>
8#include <Path.h>
9
10
11static const int32 kUndoRedoListMaxCount = 20;
12
13
14namespace BPrivate {
15
16class UndoItem {
17	public:
18		virtual ~UndoItem() {}
19
20		virtual status_t Undo() = 0;
21		virtual status_t Redo() = 0;
22
23		virtual void UpdateEntry(BEntry* /*entry*/, const char* /*name*/) {}
24			// updates the name of the target from the source entry "entry"
25};
26
27static BObjectList<UndoItem> sUndoList, sRedoList;
28static BLocker sLock("undo");
29
30class UndoItemCopy : public UndoItem {
31	public:
32		UndoItemCopy(BObjectList<entry_ref>* sourceList, BDirectory &target,
33			BList* pointList, uint32 moveMode);
34		virtual ~UndoItemCopy();
35
36		virtual status_t Undo();
37		virtual status_t Redo();
38		virtual void UpdateEntry(BEntry* entry, const char* name);
39
40	private:
41		BObjectList<entry_ref> fSourceList;
42		BObjectList<entry_ref> fTargetList;
43		entry_ref	fSourceRef, fTargetRef;
44		uint32		fMoveMode;
45};
46
47
48class UndoItemMove : public UndoItem {
49	public:
50		/** source - list of file(s) that were moved.  Assumes ownership.
51		 *	origfolder - location it was moved from
52		 */
53		UndoItemMove(BObjectList<entry_ref>* sourceList, BDirectory &target,
54			BList* pointList);
55		virtual ~UndoItemMove();
56
57		virtual status_t Undo();
58		virtual status_t Redo();
59
60	private:
61		BObjectList<entry_ref> fSourceList;
62		entry_ref	fSourceRef, fTargetRef;
63};
64
65
66class UndoItemFolder : public UndoItem {
67	public:
68		UndoItemFolder(const entry_ref &ref);
69			// ref - entry_ref indicating the folder created
70		virtual ~UndoItemFolder();
71
72		virtual status_t Undo();
73		virtual status_t Redo();
74
75	private:
76		// this ref has two different meanings in the different states of
77		// this object:
78		//  - Undo() - fRef indicates the folder that was created via
79		//    FSCreateNewFolderIn(...)
80		//  - Redo() - fRef indicates the folder in which
81		//    FSCreateNewFolderIn() should be performed
82		entry_ref	fRef;
83};
84
85
86class UndoItemRename : public UndoItem {
87	public:
88		UndoItemRename(const entry_ref &origRef, const entry_ref &ref);
89		UndoItemRename(const BEntry &entry, const char* newName);
90		virtual ~UndoItemRename();
91
92		virtual status_t Undo();
93		virtual status_t Redo();
94
95	private:
96		entry_ref	fRef, fOrigRef;
97};
98
99
100class UndoItemRenameVolume : public UndoItem {
101	public:
102		UndoItemRenameVolume(BVolume &volume, const char* newName);
103		virtual ~UndoItemRenameVolume();
104
105		virtual status_t Undo();
106		virtual status_t Redo();
107
108	private:
109		BVolume	fVolume;
110		BString	fOldName, fNewName;
111};
112
113
114//--------------------------
115
116
117static status_t
118ChangeListSource(BObjectList<entry_ref> &list, BEntry &entry)
119{
120	node_ref source;
121	if (entry.GetNodeRef(&source) != B_OK)
122		return B_ERROR;
123
124	for (int32 index = 0; index < list.CountItems(); index++) {
125		entry_ref* ref = list.ItemAt(index);
126
127		ref->device = source.device;
128		ref->directory = source.node;
129	}
130
131	return B_OK;
132}
133
134
135static void
136AddUndoItem(UndoItem* item)
137{
138	BAutolock locker(sLock);
139
140	// we have a restricted number of possible undos
141	if (sUndoList.CountItems() == kUndoRedoListMaxCount)
142		sUndoList.RemoveItem(sUndoList.LastItem());
143
144	sUndoList.AddItem(item, 0);
145	sRedoList.MakeEmpty();
146}
147
148
149//	#pragma mark -
150
151
152Undo::~Undo()
153{
154	if (fUndo != NULL)
155		AddUndoItem(fUndo);
156}
157
158
159void
160Undo::UpdateEntry(BEntry* entry, const char* destName)
161{
162	if (fUndo != NULL)
163		fUndo->UpdateEntry(entry, destName);
164}
165
166
167void
168Undo::Remove()
169{
170	delete fUndo;
171	fUndo = NULL;
172}
173
174
175MoveCopyUndo::MoveCopyUndo(BObjectList<entry_ref>* sourceList,
176	BDirectory &dest, BList* pointList, uint32 moveMode)
177{
178	if (moveMode == kMoveSelectionTo)
179		fUndo = new UndoItemMove(sourceList, dest, pointList);
180	else
181		fUndo = new UndoItemCopy(sourceList, dest, pointList, moveMode);
182}
183
184
185NewFolderUndo::NewFolderUndo(const entry_ref &ref)
186{
187	fUndo = new UndoItemFolder(ref);
188}
189
190
191RenameUndo::RenameUndo(BEntry &entry, const char* newName)
192{
193	fUndo = new UndoItemRename(entry, newName);
194}
195
196
197RenameVolumeUndo::RenameVolumeUndo(BVolume &volume, const char* newName)
198{
199	fUndo = new UndoItemRenameVolume(volume, newName);
200}
201
202
203//	#pragma mark -
204
205
206UndoItemCopy::UndoItemCopy(BObjectList<entry_ref>* sourceList,
207	BDirectory &target, BList* /*pointList*/, uint32 moveMode)
208	:
209	fSourceList(*sourceList),
210	fTargetList(*sourceList),
211	fMoveMode(moveMode)
212{
213	BEntry entry(sourceList->ItemAt(0));
214
215	BEntry sourceEntry;
216	entry.GetParent(&sourceEntry);
217	sourceEntry.GetRef(&fSourceRef);
218
219	BEntry targetEntry;
220	target.GetEntry(&targetEntry);
221	targetEntry.GetRef(&fTargetRef);
222	ChangeListSource(fTargetList, targetEntry);
223}
224
225
226UndoItemCopy::~UndoItemCopy()
227{
228}
229
230
231status_t
232UndoItemCopy::Undo()
233{
234	FSDeleteRefList(new BObjectList<entry_ref>(fTargetList), true, false);
235	return B_OK;
236}
237
238
239status_t
240UndoItemCopy::Redo()
241{
242	FSMoveToFolder(new BObjectList<entry_ref>(fSourceList),
243		new BEntry(&fTargetRef), FSUndoMoveMode(fMoveMode), NULL);
244
245	return B_OK;
246}
247
248
249void
250UndoItemCopy::UpdateEntry(BEntry* entry, const char* name)
251{
252	entry_ref changedRef;
253	if (entry->GetRef(&changedRef) != B_OK)
254		return;
255
256	for (int32 index = 0; index < fSourceList.CountItems(); index++) {
257		entry_ref* ref = fSourceList.ItemAt(index);
258		if (changedRef != *ref)
259			continue;
260
261		ref = fTargetList.ItemAt(index);
262		ref->set_name(name);
263	}
264}
265
266
267//	#pragma mark -
268
269
270UndoItemMove::UndoItemMove(BObjectList<entry_ref>* sourceList,
271	BDirectory &target, BList* /*pointList*/)
272	:
273	fSourceList(*sourceList)
274{
275	BEntry entry(sourceList->ItemAt(0));
276	BEntry source;
277	entry.GetParent(&source);
278	source.GetRef(&fSourceRef);
279
280	BEntry targetEntry;
281	target.GetEntry(&targetEntry);
282	targetEntry.GetRef(&fTargetRef);
283}
284
285
286UndoItemMove::~UndoItemMove()
287{
288}
289
290
291status_t
292UndoItemMove::Undo()
293{
294	BObjectList<entry_ref>* list = new BObjectList<entry_ref>(fSourceList);
295	BEntry entry(&fTargetRef);
296	ChangeListSource(*list, entry);
297
298	// FSMoveToFolder() owns its arguments
299	FSMoveToFolder(list, new BEntry(&fSourceRef),
300		FSUndoMoveMode(kMoveSelectionTo), NULL);
301
302	return B_OK;
303}
304
305
306status_t
307UndoItemMove::Redo()
308{
309	// FSMoveToFolder() owns its arguments
310	FSMoveToFolder(new BObjectList<entry_ref>(fSourceList),
311		new BEntry(&fTargetRef), FSUndoMoveMode(kMoveSelectionTo), NULL);
312
313	return B_OK;
314}
315
316
317//	#pragma mark -
318
319
320UndoItemFolder::UndoItemFolder(const entry_ref &ref)
321	:
322	fRef(ref)
323{
324}
325
326
327UndoItemFolder::~UndoItemFolder()
328{
329}
330
331
332status_t
333UndoItemFolder::Undo()
334{
335	FSDelete(new entry_ref(fRef), false, false);
336	return B_OK;
337}
338
339
340status_t
341UndoItemFolder::Redo()
342{
343	return FSCreateNewFolder(&fRef);
344}
345
346
347//	#pragma mark -
348
349
350UndoItemRename::UndoItemRename(const entry_ref &origRef, const entry_ref &ref)
351	:
352	fRef(ref),
353	fOrigRef(origRef)
354{
355}
356
357
358UndoItemRename::UndoItemRename(const BEntry &entry, const char* newName)
359{
360	entry.GetRef(&fOrigRef);
361
362	fRef = fOrigRef;
363	fRef.set_name(newName);
364}
365
366
367UndoItemRename::~UndoItemRename()
368{
369}
370
371
372status_t
373UndoItemRename::Undo()
374{
375	BEntry entry(&fRef, false);
376	return entry.Rename(fOrigRef.name);
377}
378
379
380status_t
381UndoItemRename::Redo()
382{
383	BEntry entry(&fOrigRef, false);
384	return entry.Rename(fRef.name);
385}
386
387
388//	#pragma mark -
389
390
391UndoItemRenameVolume::UndoItemRenameVolume(BVolume &volume,
392		const char* newName)
393	:
394	fVolume(volume),
395	fNewName(newName)
396{
397	char* buffer = fOldName.LockBuffer(B_FILE_NAME_LENGTH);
398	if (buffer != NULL) {
399		fVolume.GetName(buffer);
400		fOldName.UnlockBuffer();
401	}
402}
403
404
405UndoItemRenameVolume::~UndoItemRenameVolume()
406{
407}
408
409
410status_t
411UndoItemRenameVolume::Undo()
412{
413	return fVolume.SetName(fOldName.String());
414}
415
416
417status_t
418UndoItemRenameVolume::Redo()
419{
420	return fVolume.SetName(fNewName.String());
421}
422
423
424//	#pragma mark -
425
426
427void
428FSUndo()
429{
430	BAutolock locker(sLock);
431
432	UndoItem* undoItem = sUndoList.FirstItem();
433	if (undoItem == NULL)
434		return;
435
436	undoItem->Undo();
437		// ToDo: evaluate return code
438
439	sUndoList.RemoveItem(undoItem);
440
441	if (sRedoList.CountItems() == kUndoRedoListMaxCount)
442		sRedoList.RemoveItem(sRedoList.LastItem());
443
444	sRedoList.AddItem(undoItem, 0);
445}
446
447
448void
449FSRedo()
450{
451	BAutolock locker(sLock);
452
453	UndoItem* undoItem = sRedoList.FirstItem();
454	if (undoItem == NULL)
455		return;
456
457	undoItem->Redo();
458		// ToDo: evaluate return code
459
460	sRedoList.RemoveItem(undoItem);
461
462	if (sUndoList.CountItems() == kUndoRedoListMaxCount)
463		sUndoList.RemoveItem(sUndoList.LastItem());
464
465	sUndoList.AddItem(undoItem, 0);
466}
467
468}	// namespace BPrivate
469