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