/********************************************** * Copyright (C) 2011 Lukas Laag * This file is part of svgreal. * * svgreal is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * svgreal is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with svgreal. If not, see http://www.gnu.org/licenses/ **********************************************/ package org.vectomatic.svg.edit.client.command; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.vectomatic.svg.edit.client.SvgrealApp; import com.extjs.gxt.ui.client.data.BeanModel; import com.extjs.gxt.ui.client.store.ListStore; import com.extjs.gxt.ui.client.store.StoreEvent; import com.google.gwt.core.client.GWT; /** * Class to represent the stack of commands which have * been applied to an SVG model to edit it. */ public class CommandStore extends ListStore<BeanModel> { /** * The current command index * Can vary between 0 and N where N is the number of commands * in the stack. 0 means before the least recent command (redo * is possible, undo is impossible). N means after the most recent * command (redo is impossible, undo is possible) */ private int current; public CommandStore() { super(); applyFilters(null); } /** * Returns true if the command stack contains a command which can be * undone false otherwise. * @return true if the command stack contains a command which can be * undone false otherwise. */ public boolean canUndo() { return (current > 0); } /** * Returns true if the command stack contains a command which can be * redone false otherwise. * @return true if the command stack contains a command which can be * redone false otherwise. */ public boolean canRedo() { return (current < snapshot.size()); } /** * Undoes the current command */ public void undo() { if (!canUndo()) { throw new IllegalStateException("Invalid undo"); } SvgrealApp.getApp().getCommandFactorySelector().suspendAll(); ((ICommand)getUndoCommand().getBean()).rollback(); current--; applyFilters(null); SvgrealApp.getApp().getCommandFactorySelector().resumeAll(); } /** * Undoes all the commands up to the specified command */ public void undo(BeanModel command) { if (!canUndo()) { throw new IllegalStateException("Invalid undo"); } SvgrealApp.getApp().getCommandFactorySelector().suspendAll(); boolean found = false; for (int i = 0; i < current; i++) { if (all.get(i) == command) { found = true; break; } } if (!found) { throw new IllegalStateException("Invalid undo"); } BeanModel currentCommand; do { currentCommand = getUndoCommand(); ((ICommand)currentCommand.getBean()).rollback(); current--; } while(currentCommand != command); applyFilters(null); SvgrealApp.getApp().getCommandFactorySelector().resumeAll(); } /** * Redoes the previously undone command */ public void redo() { if (!canRedo()) { throw new IllegalStateException("Invalid redo"); } SvgrealApp.getApp().getCommandFactorySelector().suspendAll(); ((ICommand)getRedoCommand().getBean()).commit(); current++; applyFilters(null); SvgrealApp.getApp().getCommandFactorySelector().resumeAll(); } /** * Redoes the previously undone command up to the specified command */ public void redo(BeanModel command) { if (!canRedo()) { throw new IllegalStateException("Invalid redo"); } SvgrealApp.getApp().getCommandFactorySelector().suspendAll(); boolean found = false; for (int i = current, size = snapshot.size(); i < size; i++) { if (snapshot.get(i) == command) { found = true; break; } } if (!found) { throw new IllegalStateException("Invalid redo"); } BeanModel currentCommand; do { currentCommand = getRedoCommand(); ((ICommand)currentCommand.getBean()).commit(); current++; } while(currentCommand != command); applyFilters(null); SvgrealApp.getApp().getCommandFactorySelector().resumeAll(); } /** * Returns the command which will be undone if undo is invoked * @return */ public BeanModel getUndoCommand() { if (!canUndo()) { return null; } return snapshot.get(current - 1); } /** * Returns the command which will be redone if undo is invoked * @return */ public BeanModel getRedoCommand() { if (!canRedo()) { return null; } return snapshot.get(current); } /** * Gets all the commands currently in the stack * @return */ public List<BeanModel> getCommands() { return all; } /** * Gets all the commands which can be undone * @return */ public List<BeanModel> getUndoCommands() { if (!canUndo()) { return null; } return snapshot.subList(0, current); } /** * Gets all the commands which can be redone * @return */ public List<BeanModel> getRedoCommands() { if (!canRedo()) { return null; } return snapshot.subList(current, snapshot.size()); } /** * Adds a new command to the stack * @param command the command to add */ public void addCommand(ICommand command) { GWT.log("CommandStore.addCommand: " + command.getDescription()); if (current < snapshot.size()) { for (int i = snapshot.size() - 1; i >= current; i--) { snapshot.remove(i); } } StoreEvent<BeanModel> evt = createStoreEvent(); BeanModel commandModel = command.asModel(); List<BeanModel> commandList = Collections.<BeanModel>singletonList(commandModel); evt.setModels(commandList); if (!fireEvent(BeforeAdd, evt)) { return; } snapshot.add(commandModel); all.add(commandModel); current = snapshot.size(); evt = createStoreEvent(); evt.setModels(commandList); evt.setIndex(current - 1); fireEvent(Add, evt); } @Override public void applyFilters(String property) { filterProperty = property; if (!filtersEnabled) { snapshot = all; filtersEnabled = true; } all = filtered = new ArrayList<BeanModel>(snapshot.subList(0, current)); fireEvent(Filter, createStoreEvent()); } @Override public void clearFilters() { // Filters cannot be cleared as they are part of the // way this class work. } }