View Javadoc

1   /**********************************************
2    * Copyright (C) 2011 Lukas Laag
3    * This file is part of svgreal.
4    * 
5    * svgreal is free software: you can redistribute it and/or modify
6    * it under the terms of the GNU General Public License as published by
7    * the Free Software Foundation, either version 3 of the License, or
8    * (at your option) any later version.
9    * 
10   * svgreal is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU General Public License for more details.
14   * 
15   * You should have received a copy of the GNU General Public License
16   * along with svgreal.  If not, see http://www.gnu.org/licenses/
17   **********************************************/
18  package org.vectomatic.svg.edit.client.command;
19  
20  import java.util.ArrayList;
21  import java.util.Collections;
22  import java.util.List;
23  
24  import org.vectomatic.svg.edit.client.SvgrealApp;
25  
26  import com.extjs.gxt.ui.client.data.BeanModel;
27  import com.extjs.gxt.ui.client.store.ListStore;
28  import com.extjs.gxt.ui.client.store.StoreEvent;
29  import com.google.gwt.core.client.GWT;
30  
31  /**
32   * Class to represent the stack of commands which have
33   * been applied to an SVG model to edit it.
34   */
35  public class CommandStore extends ListStore<BeanModel> {
36  	/**
37  	 * The current command index
38  	 * Can vary between 0 and N where N is the number of commands
39  	 * in the stack. 0 means before the least recent command (redo
40  	 * is possible, undo is impossible). N means after the most recent
41  	 * command (redo is impossible, undo is possible)
42  	 */
43  	private int current;
44  	
45  	public CommandStore() {
46  		super();
47  		applyFilters(null);
48  	}
49  	
50  	/**
51  	 * Returns true if the command stack contains a command which can be 
52  	 * undone false otherwise.
53  	 * @return true if the command stack contains a command which can be 
54  	 * undone false otherwise.
55  	 */
56  	public boolean canUndo() {
57  		return (current > 0);
58  	}
59  	
60  	/**
61  	 * Returns true if the command stack contains a command which can be 
62  	 * redone false otherwise.
63  	 * @return true if the command stack contains a command which can be 
64  	 * redone false otherwise.
65  	 */
66  	public boolean canRedo() {
67  		return (current < snapshot.size());
68  	}
69  	
70  	/**
71  	 * Undoes the current command
72  	 */
73  	public void undo() {
74  		if (!canUndo()) {
75  			throw new IllegalStateException("Invalid undo");
76  		}
77  		SvgrealApp.getApp().getCommandFactorySelector().suspendAll();
78  		((ICommand)getUndoCommand().getBean()).rollback();
79  		current--;
80  		applyFilters(null);
81  		SvgrealApp.getApp().getCommandFactorySelector().resumeAll();
82  	}
83  	
84  	/**
85  	 * Undoes all the commands up to the specified command
86  	 */
87  	public void undo(BeanModel command) {
88  		if (!canUndo()) {
89  			throw new IllegalStateException("Invalid undo");
90  		}
91  		SvgrealApp.getApp().getCommandFactorySelector().suspendAll();
92  		boolean found = false;
93  		for (int i = 0; i < current; i++) {
94  			if (all.get(i) == command) {
95  				found = true;
96  				break;
97  			}
98  		}
99  		if (!found) {
100 			throw new IllegalStateException("Invalid undo");
101 		}
102 		
103 		BeanModel currentCommand;
104 		do {
105 			currentCommand = getUndoCommand();
106 			((ICommand)currentCommand.getBean()).rollback();
107 			current--;
108 		} while(currentCommand != command);
109 		
110 		applyFilters(null);
111 		SvgrealApp.getApp().getCommandFactorySelector().resumeAll();
112 	}
113 
114 	
115 	/**
116 	 * Redoes the previously undone command
117 	 */
118 	public void redo() {
119 		if (!canRedo()) {
120 			throw new IllegalStateException("Invalid redo");
121 		}
122 		SvgrealApp.getApp().getCommandFactorySelector().suspendAll();
123 		((ICommand)getRedoCommand().getBean()).commit();
124 		current++;
125 		applyFilters(null);
126 		SvgrealApp.getApp().getCommandFactorySelector().resumeAll();
127 	}
128 
129 	/**
130 	 * Redoes the previously undone command up to the specified command
131 	 */
132 	public void redo(BeanModel command) {
133 		if (!canRedo()) {
134 			throw new IllegalStateException("Invalid redo");
135 		}
136 		SvgrealApp.getApp().getCommandFactorySelector().suspendAll();
137 		boolean found = false;
138 		for (int i = current, size = snapshot.size(); i < size; i++) {
139 			if (snapshot.get(i) == command) {
140 				found = true;
141 				break;
142 			}
143 		}
144 		if (!found) {
145 			throw new IllegalStateException("Invalid redo");
146 		}
147 		BeanModel currentCommand;
148 		do {
149 			currentCommand = getRedoCommand();
150 			((ICommand)currentCommand.getBean()).commit();
151 			current++;
152 		} while(currentCommand != command);
153 		applyFilters(null);
154 		SvgrealApp.getApp().getCommandFactorySelector().resumeAll();
155 	}
156 
157 	/**
158 	 * Returns the command which will be undone if undo is invoked
159 	 * @return
160 	 */
161 	public BeanModel getUndoCommand() {
162 		if (!canUndo()) {
163 			return null;
164 		}
165 		return snapshot.get(current - 1);
166 	}
167 	
168 	/**
169 	 * Returns the command which will be redone if undo is invoked
170 	 * @return
171 	 */
172 	public BeanModel getRedoCommand() {
173 		if (!canRedo()) {
174 			return null;
175 		}
176 		return snapshot.get(current);
177 	}
178 	
179 	/**
180 	 * Gets all the commands currently in the stack
181 	 * @return
182 	 */
183 	public List<BeanModel> getCommands() {
184 		return all;
185 	}
186 	
187 	/**
188 	 * Gets all the commands which can be undone
189 	 * @return
190 	 */
191 	public List<BeanModel> getUndoCommands() {
192 		if (!canUndo()) {
193 			return null;
194 		}
195 		return snapshot.subList(0, current);
196 	}
197 
198 	/**
199 	 * Gets all the commands which can be redone
200 	 * @return
201 	 */
202 	public List<BeanModel> getRedoCommands() {
203 		if (!canRedo()) {
204 			return null;
205 		}
206 		return snapshot.subList(current, snapshot.size());
207 	}
208 
209 	/**
210 	 * Adds a new command to the stack
211 	 * @param command the command to add
212 	 */
213 	public void addCommand(ICommand command) {
214 		GWT.log("CommandStore.addCommand: " + command.getDescription());
215 		if (current < snapshot.size()) {
216 			for (int i = snapshot.size() - 1; i >= current; i--) {
217 				snapshot.remove(i);
218 			}
219 		}
220         StoreEvent<BeanModel> evt = createStoreEvent();
221         BeanModel commandModel = command.asModel();
222         List<BeanModel> commandList = Collections.<BeanModel>singletonList(commandModel);
223         evt.setModels(commandList);
224         if (!fireEvent(BeforeAdd, evt)) {
225           return;
226         }
227         snapshot.add(commandModel);
228         all.add(commandModel);
229 		current = snapshot.size();
230         evt = createStoreEvent();
231         evt.setModels(commandList);
232         evt.setIndex(current - 1);
233         fireEvent(Add, evt);
234 	}	
235 
236 	@Override
237 	public void applyFilters(String property) {
238 	    filterProperty = property;
239 	    if (!filtersEnabled) {
240 	    	snapshot = all;
241 		    filtersEnabled = true;
242 	    }
243 		all = filtered = new ArrayList<BeanModel>(snapshot.subList(0, current));
244 		fireEvent(Filter, createStoreEvent());
245 	}
246 	
247 	@Override
248 	public void clearFilters() {
249 		// Filters cannot be cleared as they are part of the
250 		// way this class work.
251 	}
252 }