xref: /haiku/src/apps/haikudepot/edits_generic/EditManager.cpp (revision 9f739dd2e868114ce19ae73afcff07caf2ddb8b6)
1 /*
2  * Copyright 2006-2012, Stephan Aßmus <superstippi@gmx.de>
3  * Distributed under the terms of the MIT License.
4  */
5 
6 #include "EditManager.h"
7 
8 #include <stdio.h>
9 #include <string.h>
10 
11 #include <Locker.h>
12 #include <String.h>
13 
14 
15 EditManager::Listener::~Listener()
16 {
17 }
18 
19 
20 EditManager::EditManager()
21 {
22 }
23 
24 
25 EditManager::~EditManager()
26 {
27 	Clear();
28 }
29 
30 
31 status_t
32 EditManager::Perform(UndoableEdit* edit, EditContext& context)
33 {
34 	if (edit == NULL)
35 		return B_BAD_VALUE;
36 
37 	return Perform(UndoableEditRef(edit, true), context);
38 }
39 
40 
41 status_t
42 EditManager::Perform(const UndoableEditRef& edit, EditContext& context)
43 {
44 	status_t ret = edit.Get() != NULL ? B_OK : B_BAD_VALUE;
45 	if (ret == B_OK)
46 		ret = edit->InitCheck();
47 
48 	if (ret == B_OK)
49 		ret = edit->Perform(context);
50 
51 	if (ret == B_OK) {
52 		ret = _AddEdit(edit);
53 		if (ret != B_OK)
54 			edit->Undo(context);
55 	}
56 
57 	_NotifyListeners();
58 
59 	return ret;
60 }
61 
62 
63 status_t
64 EditManager::Undo(EditContext& context)
65 {
66 	status_t status = B_ERROR;
67 	if (!fUndoHistory.IsEmpty()) {
68 		UndoableEditRef edit(fUndoHistory.Top());
69 		fUndoHistory.Pop();
70 		status = edit->Undo(context);
71 		if (status == B_OK)
72 			fRedoHistory.Push(edit);
73 		else
74 			fUndoHistory.Push(edit);
75 	}
76 
77 	_NotifyListeners();
78 
79 	return status;
80 }
81 
82 
83 status_t
84 EditManager::Redo(EditContext& context)
85 {
86 	status_t status = B_ERROR;
87 	if (!fRedoHistory.IsEmpty()) {
88 		UndoableEditRef edit(fRedoHistory.Top());
89 		fRedoHistory.Pop();
90 		status = edit->Redo(context);
91 		if (status == B_OK)
92 			fUndoHistory.Push(edit);
93 		else
94 			fRedoHistory.Push(edit);
95 	}
96 
97 	_NotifyListeners();
98 
99 	return status;
100 }
101 
102 
103 bool
104 EditManager::GetUndoName(BString& name)
105 {
106 	if (!fUndoHistory.IsEmpty()) {
107 		name << " ";
108 		fUndoHistory.Top()->GetName(name);
109 		return true;
110 	}
111 	return false;
112 }
113 
114 
115 bool
116 EditManager::GetRedoName(BString& name)
117 {
118 	if (!fRedoHistory.IsEmpty()) {
119 		name << " ";
120 		fRedoHistory.Top()->GetName(name);
121 		return true;
122 	}
123 	return false;
124 }
125 
126 
127 void
128 EditManager::Clear()
129 {
130 	while (!fUndoHistory.IsEmpty())
131 		fUndoHistory.Pop();
132 	while (!fRedoHistory.IsEmpty())
133 		fRedoHistory.Pop();
134 
135 	_NotifyListeners();
136 }
137 
138 
139 void
140 EditManager::Save()
141 {
142 	if (!fUndoHistory.IsEmpty())
143 		fEditAtSave = fUndoHistory.Top();
144 
145 	_NotifyListeners();
146 }
147 
148 
149 bool
150 EditManager::IsSaved()
151 {
152 	bool saved = fUndoHistory.IsEmpty();
153 	if (fEditAtSave.Get() != NULL && !saved) {
154 		if (fEditAtSave == fUndoHistory.Top())
155 			saved = true;
156 	}
157 	return saved;
158 }
159 
160 
161 // #pragma mark -
162 
163 
164 bool
165 EditManager::AddListener(Listener* listener)
166 {
167 	return fListeners.Add(listener);
168 }
169 
170 
171 void
172 EditManager::RemoveListener(Listener* listener)
173 {
174 	fListeners.Remove(listener);
175 }
176 
177 
178 // #pragma mark -
179 
180 
181 status_t
182 EditManager::_AddEdit(const UndoableEditRef& edit)
183 {
184 	status_t status = B_OK;
185 
186 	bool add = true;
187 	if (!fUndoHistory.IsEmpty()) {
188 		// Try to collapse edits to a single edit
189 		// or remove this and the previous edit if
190 		// they reverse each other
191 		const UndoableEditRef& top = fUndoHistory.Top();
192 		if (edit->UndoesPrevious(top.Get())) {
193 			add = false;
194 			fUndoHistory.Pop();
195 		} else if (top->CombineWithNext(edit.Get())) {
196 			add = false;
197 			// After collapsing, the edit might
198 			// have changed it's mind about InitCheck()
199 			// (the commands reversed each other)
200 			if (top->InitCheck() != B_OK) {
201 				fUndoHistory.Pop();
202 			}
203 		} else if (edit->CombineWithPrevious(top.Get())) {
204 			fUndoHistory.Pop();
205 			// After collapsing, the edit might
206 			// have changed it's mind about InitCheck()
207 			// (the commands reversed each other)
208 			if (edit->InitCheck() != B_OK) {
209 				add = false;
210 			}
211 		}
212 	}
213 	if (add) {
214 		if (!fUndoHistory.Push(edit))
215 			status = B_NO_MEMORY;
216 	}
217 
218 	if (status == B_OK) {
219 		// The redo stack needs to be empty
220 		// as soon as an edit was added (also in case of collapsing)
221 		while (!fRedoHistory.IsEmpty()) {
222 			fRedoHistory.Pop();
223 		}
224 	}
225 
226 	return status;
227 }
228 
229 
230 void
231 EditManager::_NotifyListeners()
232 {
233 	int32 count = fListeners.CountItems();
234 	if (count == 0)
235 		return;
236 	// Iterate a copy of the list, so we don't crash if listeners
237 	// detach themselves while being notified.
238 	ListenerList listenersCopy(fListeners);
239 	if (listenersCopy.CountItems() != count)
240 		return;
241 	for (int32 i = 0; i < count; i++)
242 		listenersCopy.ItemAtFast(i)->EditManagerChanged(this);
243 }
244 
245