/* * Copyright 2006-2007, Haiku. * Distributed under the terms of the MIT License. * * Authors: * Stephan Aßmus */ #include "CommandStack.h" #include #include #include #include #include "Command.h" // constructor CommandStack::CommandStack() : BLocker("history"), Observable(), fSavedCommand(NULL) { } // destructor CommandStack::~CommandStack() { Clear(); } // Perform status_t CommandStack::Perform(Command* command) { if (!Lock()) return B_ERROR; status_t ret = command ? B_OK : B_BAD_VALUE; if (ret == B_OK) ret = command->InitCheck(); if (ret == B_OK) ret = command->Perform(); if (ret == B_OK) ret = _AddCommand(command); if (ret != B_OK) { // no one else feels responsible... delete command; } Unlock(); return ret; } // Undo status_t CommandStack::Undo() { if (!Lock()) return B_ERROR; status_t status = B_ERROR; if (!fUndoHistory.empty()) { Command* command = fUndoHistory.top(); fUndoHistory.pop(); status = command->Undo(); if (status == B_OK) fRedoHistory.push(command); else fUndoHistory.push(command); } Unlock(); Notify(); return status; } // Redo status_t CommandStack::Redo() { if (!Lock()) return B_ERROR; status_t status = B_ERROR; if (!fRedoHistory.empty()) { Command* command = fRedoHistory.top(); fRedoHistory.pop(); status = command->Redo(); if (status == B_OK) fUndoHistory.push(command); else fRedoHistory.push(command); } Unlock(); Notify(); return status; } // UndoName bool CommandStack::GetUndoName(BString& name) { bool success = false; if (Lock()) { if (!fUndoHistory.empty()) { name << " "; fUndoHistory.top()->GetName(name); success = true; } Unlock(); } return success; } // RedoName bool CommandStack::GetRedoName(BString& name) { bool success = false; if (Lock()) { if (!fRedoHistory.empty()) { name << " "; fRedoHistory.top()->GetName(name); success = true; } Unlock(); } return success; } // Clear void CommandStack::Clear() { if (Lock()) { while (!fUndoHistory.empty()) { delete fUndoHistory.top(); fUndoHistory.pop(); } while (!fRedoHistory.empty()) { delete fRedoHistory.top(); fRedoHistory.pop(); } Unlock(); } Notify(); } // Save void CommandStack::Save() { if (Lock()) { if (!fUndoHistory.empty()) fSavedCommand = fUndoHistory.top(); Unlock(); } Notify(); } // IsSaved bool CommandStack::IsSaved() { bool saved = false; if (Lock()) { saved = fUndoHistory.empty(); if (fSavedCommand && !saved) { if (fSavedCommand == fUndoHistory.top()) saved = true; } Unlock(); } return saved; } // #pragma mark - // _AddCommand status_t CommandStack::_AddCommand(Command* command) { status_t status = B_OK; bool add = true; if (!fUndoHistory.empty()) { // try to collapse commands to a single command // or remove this and the previous command if // they reverse each other if (Command* top = fUndoHistory.top()) { if (command->UndoesPrevious(top)) { add = false; fUndoHistory.pop(); delete top; delete command; } else if (top->CombineWithNext(command)) { add = false; delete command; // after collapsing, the command might // have changed it's mind about InitCheck() // (the commands reversed each other) if (top->InitCheck() < B_OK) { fUndoHistory.pop(); delete top; } } else if (command->CombineWithPrevious(top)) { fUndoHistory.pop(); delete top; // after collapsing, the command might // have changed it's mind about InitCheck() // (the commands reversed each other) if (command->InitCheck() < B_OK) { delete command; add = false; } } } } if (add) { try { fUndoHistory.push(command); } catch (...) { status = B_ERROR; } } if (status == B_OK) { // the redo stack needs to be empty // as soon as a command was added (also in case of collapsing) while (!fRedoHistory.empty()) { delete fRedoHistory.top(); fRedoHistory.pop(); } } Notify(); return status; }