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