1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.vectomatic.common.model;
19
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.Comparator;
23 import java.util.HashMap;
24 import java.util.HashSet;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.NoSuchElementException;
29 import java.util.Set;
30
31 import org.vectomatic.common.events.IDrawingModelListener;
32
33
34
35
36
37 public class DrawingModel {
38 protected class Node {
39 public Node prev;
40 public Node next;
41 public float z;
42 public Shape shape;
43 }
44
45 protected static class AscendingNodeComparator implements Comparator<Node> {
46 public int compare(Node o1, Node o2) {
47 return Float.compare(o1.z, o2.z);
48 }
49 }
50
51 protected static class DescendingNodeComparator implements Comparator<Node> {
52 public int compare(Node o1, Node o2) {
53 return Float.compare(o1.z, o2.z) * -1;
54 }
55 }
56
57 protected class AscendingIterator implements Iterator<Shape> {
58 Node _current;
59
60 public boolean hasNext() {
61 return (_current.next != _tail);
62 }
63
64 public Shape next() {
65 if (_current.next == _tail) {
66 throw new NoSuchElementException();
67 }
68 _current = _current.next;
69 return _current.shape;
70 }
71
72 public void remove() {
73 if (_current == _head) {
74 throw new IllegalStateException();
75 }
76 Node prev = _current.prev;
77 Node next = _current.next;
78 prev.next = next;
79 next.prev = prev;
80 _current = prev;
81 }
82 }
83
84 protected class DescendingIterator implements Iterator<Shape> {
85 Node _current;
86
87 public boolean hasNext() {
88 return (_current.prev != _head);
89 }
90
91 public Shape next() {
92 if (_current.prev == _head) {
93 throw new NoSuchElementException();
94 }
95 _current = _current.prev;
96 return _current.shape;
97 }
98
99 public void remove() {
100 if (_current == _tail) {
101 throw new IllegalStateException();
102 }
103 Node prev = _current.prev;
104 Node next = _current.next;
105 prev.next = next;
106 next.prev = prev;
107 _current = next;
108 }
109 }
110
111 protected List<IDrawingModelListener> _drawingModelListeners;
112
113
114
115 protected Node _head;
116
117
118
119 protected Node _tail;
120
121
122
123
124 protected Map<Shape,Node> _shapeToNode;
125
126
127
128 protected AscendingIterator _ascIterator;
129
130
131
132 protected DescendingIterator _descIterator;
133
134
135
136 protected static final Comparator<Node> ORDER_ASCENDING = new AscendingNodeComparator();
137
138
139
140 protected static final Comparator<Node> ORDER_DESCENDING = new DescendingNodeComparator();
141
142
143
144 protected Set<Shape> _shapeSet;
145
146 public DrawingModel() {
147 _shapeToNode = new HashMap<Shape,Node>();
148 _head = new Node();
149 _tail = new Node();
150 connect(_head, _tail);
151 _shapeSet = new HashSet<Shape>();
152 _ascIterator = new AscendingIterator();
153 _descIterator = new DescendingIterator();
154 }
155
156 public void addDrawingModelListener(IDrawingModelListener listener) {
157 if (_drawingModelListeners == null) {
158 _drawingModelListeners = new ArrayList<IDrawingModelListener>();
159 }
160 _drawingModelListeners.add(listener);
161 }
162
163 public void removeDrawingModelListener(IDrawingModelListener listener) {
164 if (_drawingModelListeners != null) {
165 _drawingModelListeners.remove(listener);
166 }
167 }
168
169 public void fireModelHasChanged() {
170 if (_drawingModelListeners != null) {
171 for (int i = 0, size = _drawingModelListeners.size(); i < size; i++) {
172 IDrawingModelListener listener = _drawingModelListeners.get(i);
173 listener.modelHasChanged(this);
174 }
175 }
176 }
177
178 public void addShape(Shape shape) {
179 if (shape == null) {
180 throw new NullPointerException();
181 }
182 shape.setModel(this);
183 Node prevNode = _tail.prev;
184 Node node = new Node();
185 node.shape = shape;
186 node.z = 1f + prevNode.z;
187 connect(prevNode, node);
188 connect(node, _tail);
189 _shapeToNode.put(shape, node);
190 }
191
192 public void removeShape(Shape shape) {
193 if (shape == null) {
194 throw new NullPointerException();
195 }
196 Node node = _shapeToNode.get(shape);
197 if (node == null) {
198 throw new IllegalArgumentException();
199 }
200 Node prevNode = node.prev;
201 Node nextNode = node.next;
202 connect(prevNode, nextNode);
203 _shapeToNode.remove(shape);
204 shape.setModel(null);
205 }
206
207 public void clear() {
208 connect(_head, _tail);
209 _shapeToNode.clear();
210 }
211
212 public void reorder(List<Shape> shapes, List<Float> orders) {
213 Node[] nodes = toNodeArray(shapes);
214 for (int i = 0; i < nodes.length; i++) {
215 nodes[i].z = orders.get(i).floatValue();
216 removeShape(nodes[i].shape);
217 }
218 Arrays.sort(nodes, ORDER_ASCENDING);
219 Node node = _head;
220 int index = 0, count = shapes.size();
221 while (index < count) {
222 if ((node == _tail) || (node.z > nodes[index].z)) {
223 Node prevNode = node.prev;
224 connect(prevNode, nodes[index]);
225 connect(nodes[index], node);
226 _shapeToNode.put(nodes[index].shape, nodes[index]);
227 index++;
228 } else {
229 node = node.next;
230 }
231 }
232 }
233
234 public int count() {
235 return _shapeToNode.size();
236 }
237
238 public Iterator<Shape> iterator() {
239 _ascIterator._current = _head;
240 return _ascIterator;
241 }
242
243 public Iterator<Shape> reverseIterator() {
244 _descIterator._current = _tail;
245 return _descIterator;
246 }
247
248 protected Node[] toNodeArray(List<Shape> shapes) {
249 int size = shapes.size();
250 Node[] nodes = new Node[size];
251 for (int i = 0; i < size; i++) {
252 nodes[i] = _shapeToNode.get(shapes.get(i));
253 }
254 return nodes;
255 }
256
257 public boolean canBringToFront(List<Shape> shapes) {
258 boolean canBringToFront = false;
259 _shapeSet.clear();
260 _shapeSet.addAll(shapes);
261 for (int i = 0, size = shapes.size(); i < size; i++) {
262 Node nextNode = _shapeToNode.get(shapes.get(i)).next;
263 if (nextNode != _tail && (!_shapeSet.contains(nextNode.shape))) {
264 canBringToFront = true;
265 break;
266 }
267 }
268 return canBringToFront;
269 }
270
271 public boolean canSendToBack(List<Shape> shapes) {
272 boolean canSendToBack = false;
273 _shapeSet.clear();
274 _shapeSet.addAll(shapes);
275 for (int i = 0, size = shapes.size(); i < size; i++) {
276 Node prevNode = _shapeToNode.get(shapes.get(i)).prev;
277 if (prevNode != _head && (!_shapeSet.contains(prevNode.shape))) {
278 canSendToBack = true;
279 break;
280 }
281 }
282 return canSendToBack;
283 }
284
285 public boolean canBringForward(List<Shape> shapes) {
286 boolean canBringForward = false;
287 _shapeSet.clear();
288 _shapeSet.addAll(shapes);
289 for (int i = 0, size = shapes.size(); i < size; i++) {
290 Node nextNode = _shapeToNode.get(shapes.get(i)).next;
291 if (nextNode != _tail && (!_shapeSet.contains(nextNode.shape))) {
292 canBringForward = true;
293 break;
294 }
295 }
296 return canBringForward;
297 }
298
299 public boolean canSendBackward(List<Shape> shapes) {
300 boolean canSendBackward = false;
301 _shapeSet.clear();
302 _shapeSet.addAll(shapes);
303 for (int i = 0, size = shapes.size(); i < size; i++) {
304 Node prevNode = _shapeToNode.get(shapes.get(i)).prev;
305 if (prevNode != _head && (!_shapeSet.contains(prevNode.shape))) {
306 canSendBackward = true;
307 break;
308 }
309 }
310 return canSendBackward;
311 }
312
313 public List<Float> bringToFront(List<Shape> shapes) {
314 List<Float> orders = new ArrayList<Float>(shapes.size());
315 _shapeSet.clear();
316 _shapeSet.addAll(shapes);
317 Node[] nodes = toNodeArray(shapes);
318 for (int i = 0; i < nodes.length; i++) {
319 orders.add(new Float(nodes[i].z));
320 }
321 Arrays.sort(nodes, ORDER_DESCENDING);
322 Node tailNode = _tail;
323 for (int i = 0; i < nodes.length; i++) {
324 Node node = nodes[i];
325 Node nextNode = node.next;
326 if (nextNode != tailNode) {
327 Node prevNode = node.prev;
328 Node prevTailNode = tailNode.prev;
329 connect(prevNode, nextNode);
330 connect(prevTailNode, node);
331 connect(node, tailNode);
332 float nextz = (tailNode != _tail) ? tailNode.z : prevTailNode.z + 1f;
333 node.z = (prevTailNode.z + nextz) * 0.5f;
334 }
335 tailNode = node;
336 }
337 return orders;
338 }
339
340 public List<Float> sendToBack(List<Shape> shapes) {
341 List<Float> orders = new ArrayList<Float>(shapes.size());
342 _shapeSet.clear();
343 _shapeSet.addAll(shapes);
344 Node[] nodes = toNodeArray(shapes);
345 for (int i = 0; i < nodes.length; i++) {
346 orders.add(new Float(nodes[i].z));
347 }
348 Arrays.sort(nodes, ORDER_ASCENDING);
349 Node headNode = _head;
350 for (int i = 0; i < nodes.length; i++) {
351 Node node = nodes[i];
352 Node prevNode = node.prev;
353 if (prevNode != headNode) {
354 Node nextNode = node.next;
355 Node nextHeadNode = headNode.next;
356 connect(prevNode, nextNode);
357 connect(node, nextHeadNode);
358 connect(headNode, node);
359 float prevz = (headNode != _head) ? headNode.z : nextHeadNode.z - 1f;
360 node.z = (nextHeadNode.z + prevz) * 0.5f;
361 }
362 headNode = node;
363 }
364 return orders;
365 }
366
367 public List<Float> bringForward(List<Shape> shapes) {
368 List<Float> orders = new ArrayList<Float>(shapes.size());
369 _shapeSet.clear();
370 _shapeSet.addAll(shapes);
371 Node[] nodes = toNodeArray(shapes);
372 for (int i = 0; i < nodes.length; i++) {
373 orders.add(new Float(nodes[i].z));
374 }
375 Arrays.sort(nodes, ORDER_DESCENDING);
376 for (int i = 0; i < nodes.length; i++) {
377 Node node = nodes[i];
378 Node nextNode = node.next;
379 if (nextNode != _tail && !_shapeSet.contains(nextNode)) {
380 Node prevNode = node.prev;
381 Node nextNextNode = nextNode.next;
382 connect(prevNode, nextNode);
383 connect(nextNode, node);
384 connect(node, nextNextNode);
385 float nextNextz = (nextNextNode != _tail) ? nextNextNode.z : nextNode.z + 1f;
386 node.z = (nextNode.z + nextNextz) * 0.5f;
387 }
388 }
389 return orders;
390 }
391
392 public List<Float> sendBackward(List<Shape> shapes) {
393 List<Float> orders = new ArrayList<Float>(shapes.size());
394 _shapeSet.clear();
395 _shapeSet.addAll(shapes);
396 Node[] nodes = toNodeArray(shapes);
397 for (int i = 0; i < nodes.length; i++) {
398 orders.add(new Float(nodes[i].z));
399 }
400 Arrays.sort(nodes, ORDER_ASCENDING);
401 for (int i = 0; i < nodes.length; i++) {
402 Node node = nodes[i];
403 Node prevNode = node.prev;
404 if (prevNode != _head && !_shapeSet.contains(prevNode)) {
405 Node nextNode = node.next;
406 Node prevPrevNode = prevNode.prev;
407 connect(prevPrevNode, node);
408 connect(node, prevNode);
409 connect(prevNode, nextNode);
410 float prevPrevz = (prevPrevNode != _head) ? prevPrevNode.z : prevNode.z - 1f;
411 node.z = (prevNode.z + prevPrevz) * 0.5f;
412 }
413 }
414 return orders;
415 }
416
417 private void connect(Node a, Node b) {
418 a.next = b;
419 b.prev = a;
420 }
421
422 public boolean contains(Shape shape) {
423 return _shapeToNode.containsKey(shape);
424 }
425
426 public Shape[] toShapeArray() {
427 Shape[] shapeArray = new Shape[count()];
428 Iterator<Shape> iterator = iterator();
429 int index = 0;
430 while(iterator.hasNext()) {
431 shapeArray[index++] = iterator.next();
432 }
433 return shapeArray;
434 }
435
436 public void fromShapeArray(Shape[] shapeArray) {
437 clear();
438 if (shapeArray != null) {
439 for (int i = 0; i < shapeArray.length; i++) {
440 addShape(shapeArray[i]);
441 }
442 }
443 }
444 }