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