View Javadoc

1   /**********************************************
2    * Copyright (C) 2009 Lukas Laag
3    * This file is part of Vectomatic.
4    * 
5    * Vectomatic 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   * Vectomatic 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 Vectomatic.  If not, see http://www.gnu.org/licenses/
17   **********************************************/
18  package org.vectomatic.client.rep.controller;
19  
20  import java.util.ArrayList;
21  import java.util.Iterator;
22  import java.util.List;
23  import java.util.NoSuchElementException;
24  
25  import org.vectomatic.client.rep.events.IShapeSelectionListener;
26  import org.vectomatic.common.events.IDrawingModelListener;
27  import org.vectomatic.common.model.Attribute;
28  import org.vectomatic.common.model.CloneShapeVisitor;
29  import org.vectomatic.common.model.DrawingModel;
30  import org.vectomatic.common.model.FloatAttributeValue;
31  import org.vectomatic.common.model.IAttributeValue;
32  import org.vectomatic.common.model.Shape;
33  import org.vectomatic.common.model.geometry.ShapeGroup;
34  
35  
36  /**
37   * Class to represent the currently selected shapes.
38   * Selected shapes are cloned and clones are accessed with getRootShape().
39   * @author Lukas Laag
40   */
41  public class ShapeSelection implements IDrawingModelListener {
42  	private Shape _rootShape;
43  	private List<Shape> _selectedShapes;
44  	private DrawingModel _model;
45  	private CloneShapeVisitor _cloner;
46  	private List<IShapeSelectionListener> _shapeSelectionListeners;
47  	private int _current;
48  
49  	public ShapeSelection(DrawingModel model) {
50  		_selectedShapes = new ArrayList<Shape>();
51  		_model = model;
52  		_model.addDrawingModelListener(this);
53  		_cloner = new CloneShapeVisitor();
54  	}
55  	
56  	public void addShapeSelectionListener(IShapeSelectionListener listener) {
57  		if (_shapeSelectionListeners == null) {
58  			_shapeSelectionListeners = new ArrayList<IShapeSelectionListener>();
59  		}
60  		_shapeSelectionListeners.add(listener);
61  	}
62  	
63  	public void removeShapeSelectionListener(IShapeSelectionListener listener) {
64  		if (_shapeSelectionListeners != null) {
65  			_shapeSelectionListeners.remove(listener);
66  		}
67  	}
68  	
69  	public void fireSelectionHasChanged() {
70  		if (_shapeSelectionListeners != null) {
71  			for (int i = 0, size = _shapeSelectionListeners.size(); i < size; i++) {
72  				IShapeSelectionListener listener = _shapeSelectionListeners.get(i);
73  				listener.selectionChanged(this);
74  			}
75  		}
76  	}
77  	
78  	/**
79  	 * Returns the clone of the currently selected shapes. 
80  	 * For mono selection, this is simply the selected shape clone.
81  	 * For multi selection, it is the temporary shape group which 
82  	 * contains the clones of the selected shapes.
83  	 */
84  	public Shape getRootShape() {
85  		return _rootShape;
86  	}
87  	
88  	/**
89  	 * Returns a list which contains all the selected shapes.
90  	 * The list is immutable
91  	 */
92  	public List<Shape> getSelectedShapes() {
93  		return _selectedShapes;
94  	}
95  	
96  	/**
97  	 * @param rootShape
98  	 */
99  	public Shape select(List<Shape> shapes) {
100 		_rootShape = null;
101 		_selectedShapes.clear();
102 
103 		// Select the specified shapes
104 		if (shapes.size() > 1) {
105 			// Multi-selection
106 			List<Shape> shapeClones = new ArrayList<Shape>();
107 			for (int i = 0, size = shapes.size(); i < size; i++) {
108 				Shape shape = shapes.get(i);
109 				_selectedShapes.add(shape);
110 				shape.acceptVisitor(_cloner);
111 				shapeClones.add(_cloner.getClone());
112 			}
113 			_rootShape = new ShapeGroup(shapeClones);
114 		} else if (shapes.size() > 0) {
115 			// Mono-selection
116 			Shape shape = shapes.get(0);
117 			_selectedShapes.add(shape);
118 			shape.acceptVisitor(_cloner);
119 			_rootShape = _cloner.getClone();
120 		}
121 		if (_rootShape != null) {
122 			_rootShape.setAttribute(Attribute.FILL_OPACITY, new FloatAttributeValue(0.2f));
123 		}
124 		fireSelectionHasChanged();
125 		return _rootShape;
126 	}
127 
128 	/**
129 	 * Listens to model change events. Removal of a shape from
130 	 * the model causes the selection to be recomputed.
131 	 */
132 	public void modelHasChanged(DrawingModel model) {
133 		List<Shape> newSelectedShapes = new ArrayList<Shape>();
134 		for (int i = 0, size = _selectedShapes.size(); i < size; i++) {
135 			Shape shape = _selectedShapes.get(i);
136 			if (_model.contains(shape)) {
137 				newSelectedShapes.add(shape);
138 			}
139 		}
140 		if (!newSelectedShapes.equals(_selectedShapes)) {
141 			select(newSelectedShapes);
142 		} else {
143 			// The model change may be have been caused by of the shapes
144 			// changing its attributes. Refresh the cloned attributes
145 			if (_selectedShapes.size() > 1) {
146 				List<Shape> shapes = ((ShapeGroup)_rootShape).getShapes();
147 				for (int i = 0, size = shapes.size(); i < size; i++) {
148 					shapes.get(i).copyAttributes(_selectedShapes.get(i));
149 				}
150 				_rootShape.setAttribute(Attribute.FILL_OPACITY, new FloatAttributeValue(0.2f));
151 			} else if (_selectedShapes.size() == 1) {
152 				_rootShape.copyAttributes(_selectedShapes.get(0));
153 				_rootShape.setAttribute(Attribute.FILL_OPACITY, new FloatAttributeValue(0.2f));
154 			}
155 		}
156 	}
157 	
158 	public Iterator<Shape> iterator() {
159 		_current = 0;
160 		return new Iterator<Shape>() {
161 
162 			public boolean hasNext() {
163 				return _current < _selectedShapes.size();
164 			}
165 
166 			public Shape next() {
167 				if (_current >= _selectedShapes.size()) {
168 					throw new NoSuchElementException();
169 				}
170 				return _selectedShapes.get(_current++);
171 			}
172 
173 			public void remove() {
174 				throw new UnsupportedOperationException();
175 			}
176 		};
177 	}
178 	
179 	public boolean hasAttributeChanged(Attribute attribute, IAttributeValue value) {
180 		boolean changed = false;
181 		for (int i = 0, size = _selectedShapes.size(); i < size; i++) {
182 			Shape shape = _selectedShapes.get(i);
183 			IAttributeValue attributeValue = shape.getAttribute(attribute);
184 			if (!value.equals(attributeValue)) {
185 				changed = true;
186 				break;
187 			}
188 		}
189 		return changed;
190 	}
191 }