View Javadoc

1   /**********************************************
2    * Copyright (C) 2010 Lukas Laag
3    * This file is part of lib-gwt-svg.
4    * 
5    * libgwtsvg is free software: you can redistribute it and/or modify
6    * it under the terms of the GNU Lesser 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   * libgwtsvg 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 Lesser General Public License for more details.
14   * 
15   * You should have received a copy of the GNU Lesser General Public License
16   * along with libgwtsvg.  If not, see http://www.gnu.org/licenses/
17   **********************************************/
18  package org.vectomatic.dom.svg.ui;
19  
20  import java.util.HashMap;
21  import java.util.Map;
22  
23  import org.vectomatic.dom.svg.OMSVGAnimatedString;
24  import org.vectomatic.dom.svg.OMSVGSVGElement;
25  import org.vectomatic.dom.svg.OMSVGStyle;
26  import org.vectomatic.dom.svg.itf.ISVGStylable;
27  
28  import com.google.gwt.event.dom.client.ClickEvent;
29  import com.google.gwt.event.dom.client.ClickHandler;
30  import com.google.gwt.event.dom.client.HasAllMouseHandlers;
31  import com.google.gwt.event.dom.client.HasClickHandlers;
32  import com.google.gwt.event.dom.client.MouseDownEvent;
33  import com.google.gwt.event.dom.client.MouseDownHandler;
34  import com.google.gwt.event.dom.client.MouseMoveEvent;
35  import com.google.gwt.event.dom.client.MouseMoveHandler;
36  import com.google.gwt.event.dom.client.MouseOutEvent;
37  import com.google.gwt.event.dom.client.MouseOutHandler;
38  import com.google.gwt.event.dom.client.MouseOverEvent;
39  import com.google.gwt.event.dom.client.MouseOverHandler;
40  import com.google.gwt.event.dom.client.MouseUpEvent;
41  import com.google.gwt.event.dom.client.MouseUpHandler;
42  import com.google.gwt.event.dom.client.MouseWheelEvent;
43  import com.google.gwt.event.dom.client.MouseWheelHandler;
44  import com.google.gwt.event.shared.HandlerRegistration;
45  
46  /**
47   * Abstract base class for SVG buttons. 
48   * <p>SVG buttons consists
49   * in an SVG element and a collection of six predefined faces<p>
50   * <ul>
51   * <li>UP</li>
52   * <li>UP_DISABLED</li>
53   * <li>UP_HOVERING</li>
54   * <li>DOWN</li>
55   * <li>DOWN_DISABLED</li>
56   * <li>DOWN_HOVERING</li>
57   * </ul>
58   * <p>Each face consists in a list of changes which are applied to the
59   * main SVG element (currently, CSS style changes but other changes
60   * are possible). You do not need to specify all faces for a button. In
61   * case a face is missing, the widget will attempt to use another face for
62   * the button.</p>
63   * @author laaglu
64   */
65  public abstract class SVGButtonBase extends SVGWidget implements HasClickHandlers, HasAllMouseHandlers, MouseDownHandler, MouseUpHandler, MouseOverHandler, MouseOutHandler, ClickHandler, ISVGStylable {
66  	/**
67  	 * Enum to represent the possible states of an SVG button
68  	 * @author laaglu
69  	 */
70  	public enum SVGFaceName {
71  		/**
72  		 * face shown when button is up
73  		 */
74  		UP,
75  		/**
76  		 * face shown when button is up and disabled
77  		 */
78  		UP_DISABLED,
79  		/**
80  		 * face shown when the cursor is hovering over an up enabled button
81  		 */
82  		UP_HOVERING,
83  		/**
84  		 * face shown when button is down
85  		 */
86  		DOWN,
87  		/**
88  		 * face shown when button is down and disabled
89  		 */
90  		DOWN_DISABLED,
91  		/**
92  		 * face shown when the cursor is hovering over a down enabled button
93  		 */
94  		DOWN_HOVERING
95  	}
96  	/**
97  	 * Class to represent an SVG button face. A
98  	 * button face consists in an array of changes
99  	 * to the button SVG which occur when the face is displayed
100 	 */
101 	public static class SVGFace {
102 		private SVGFaceChange[] changes;
103 		/**
104 		 * Constructor
105 		 * @param changes an array of changes to the
106 		 *  button SVG which occur when the face is displayed
107 		 */
108 		public SVGFace(SVGFaceChange[] changes) {
109 			this.changes = changes;
110 		}
111 		/**
112 		 * Returns the array of changes to the
113 		 * button SVG which occur when the face is displayed
114 		 * @return the array of changes to the
115 		 * button SVG which occur when the face is displayed
116 		 */
117 		public SVGFaceChange[] getChanges() {
118 			return changes;
119 		}
120 	}
121 	
122 	/**
123 	 * Base class to represent the changes occurring
124 	 * to an SVG button when it enters a new state.
125 	 * @author laaglu
126 	 */
127 	public static abstract class SVGFaceChange {
128 		void install(SVGButtonBase button) {	
129 		}
130 		void uninstall(SVGButtonBase button) {
131 		}
132 	}
133 	
134 	/**
135 	 * Class to represent a style change as an SVG
136 	 * button enters a new state.
137 	 * @author laaglu
138 	 */
139 	public static class SVGStyleChange extends SVGFaceChange {
140 		private String[] classNames;
141 		public SVGStyleChange(String[] classNames) {
142 			this.classNames = classNames;
143 		}
144 		@Override
145 		void uninstall(SVGButtonBase button) {
146 			if (button.svgElement != null) {
147 				for (String className : classNames) {
148 					button.svgElement.removeClassNameBaseVal(className);
149 				}
150 			}
151 		}
152 		@Override
153 		void install(SVGButtonBase button) {
154 			if (button.svgElement != null) {
155 				for (String className : classNames) {
156 					button.svgElement.addClassNameBaseVal(className);
157 				}
158 			}
159 		}
160 		public String[] getClassNames() {
161 			return classNames;
162 		}
163 		public void getClassNames(String[] classNames) {
164 			this.classNames = classNames;
165 		}
166 	}
167 	
168 	/**
169 	 * The {@link org.vectomatic.dom.svg.OMSVGSVGElement} representing
170 	 * this button
171 	 */
172 	protected OMSVGSVGElement svgElement;
173 	/**
174 	 * The name of the face currently displayed by this button
175 	 */
176 	protected SVGFaceName currentFaceName;
177 	/**
178 	 * A map associating button face names to button faces
179 	 */
180 	protected Map<SVGFaceName, SVGFace> faces;
181 	
182 	/**
183 	 * No-arg constructor.
184 	 * You must call {@link SVGButtonBase#setResource(SVGResource)} or {@link SVGButtonBase#setSvgElement(OMSVGSVGElement)}
185 	 * before using the widget.
186 	 */
187 	protected SVGButtonBase() {
188 		faces = new HashMap<SVGFaceName, SVGFace>();
189 	}
190 	/**
191 	 * Constructor
192 	 * @param svgElement
193 	 * The SVG element defining the button
194 	 * @param faces
195 	 * A collection of faces
196 	 */
197 	protected SVGButtonBase(OMSVGSVGElement svgElement, Map<SVGFaceName, SVGFace> faces) {
198 		this();
199 		if (faces != null) {
200 			this.faces.putAll(faces);
201 		}
202 		setSvgElement(svgElement);
203 	}
204 	/**
205 	 * Constructor
206 	 * @param resource
207 	 * An SVG resource to use for the SVG element defining the button
208 	 * @param faces
209 	 * The SVG element defining the button
210 	 */
211 	protected SVGButtonBase(SVGResource resource, Map<SVGFaceName, SVGFace> faces) {
212 		this(resource.getSvg(), faces);
213 	}
214 	
215 	/**
216 	 * Adds a new faces to the button
217 	 * @param faceName
218 	 * The face name
219 	 * @param face
220 	 * The face
221 	 * @return
222 	 * The face previously associated with this face name if there was one,
223 	 * null otherwise
224 	 */
225 	public SVGFace addFace(SVGFaceName faceName, SVGFace face) {
226 		return faces.put(faceName, face);
227 	}
228 	/**
229 	 * Returns the face associated with a face name
230 	 * @param faceName
231 	 * The face name
232 	 * @return
233 	 * The face associated with this face name if there is one,
234 	 * null otherwise
235 	 */
236 	public SVGFace getFace(SVGFaceName faceName) {
237 		return faces.get(faceName);
238 	}
239 	/**
240 	 * Sets the SVG resource defining the button
241 	 * @param resource
242 	 * An SVG resource
243 	 */
244 	public void setResource(SVGResource resource) {
245 		setSvgElement(resource.getSvg());
246 	}
247 	/**
248 	 * Returns the SVG element defining the button
249 	 * @return
250 	 * the SVG element defining the button
251 	 */
252 	public OMSVGSVGElement getSvgElement() {
253 		return svgElement;
254 	}
255 	/**
256 	 * Sets the SVG element defining the button
257 	 * @param svgElement
258 	 * the SVG element defining the button
259 	 */
260 	public void setSvgElement(OMSVGSVGElement svgElement) {
261 		this.svgElement = svgElement;
262 		setElement(this.svgElement.getElement());
263 		if (this.svgElement != null) {
264 			this.svgElement.addDomHandler(this, MouseOutEvent.getType());
265 			this.svgElement.addDomHandler(this, MouseOverEvent.getType());
266 			this.svgElement.addDomHandler(this, MouseUpEvent.getType());
267 			this.svgElement.addDomHandler(this, MouseDownEvent.getType());
268 			this.svgElement.addDomHandler(this, ClickEvent.getType());
269 		}
270 	}
271 	/**
272 	 * Returns the name of the face currently displayed by this button
273 	 * @return
274 	 * the name of the face currently displayed by this button
275 	 */
276 	public SVGFaceName getCurrentFaceName() {
277 		return currentFaceName;
278 	}
279 	/**
280 	 * Returns true if this button is enabled, false otherwise
281 	 * @return
282 	 * true if this button is enabled, false otherwise
283 	 */
284 	public boolean isEnabled() {
285 		return currentFaceName != SVGFaceName.UP_DISABLED && currentFaceName != SVGFaceName.DOWN_DISABLED;
286 	}
287 	/**
288 	 * Sets whether this button is enabled
289 	 * @param enabled
290 	 * true to enable the button, false to disable it
291 	 */
292 	public void setEnabled(boolean enabled) {
293 		if (enabled) {
294 			switch(currentFaceName) {
295 				case UP_DISABLED:
296 				case UP_HOVERING:
297 					showFace(SVGFaceName.UP);
298 					break;
299 				case DOWN_DISABLED:
300 				case DOWN_HOVERING:
301 					showFace(SVGFaceName.DOWN);
302 					break;
303 			}
304 		} else {
305 			switch(currentFaceName) {
306 				case UP:
307 				case UP_HOVERING:
308 					showFace(SVGFaceName.UP_DISABLED);
309 					break;
310 				case DOWN:
311 				case DOWN_HOVERING:
312 					showFace(SVGFaceName.DOWN_DISABLED);
313 					break;
314 			}
315 		}
316 	}
317 
318 	/**
319 	 * Forces the button to display the specified face
320 	 * @param faceName
321 	 * The name of the face to display
322 	 */
323 	public void showFace(SVGFaceName faceName) {
324 //		GWT.log((currentFaceName != null ? currentFaceName.name() : "null") + " -> " + faceName.name());
325 		SVGFace currentFace = currentFaceName != null ? getFace(currentFaceName) : null;
326 		SVGFace face = faceName != null ? getFace(faceName) : null;
327 		if (face != currentFace) {
328 			if (currentFace != null) {
329 				for (SVGFaceChange change : currentFace.getChanges()) {
330 					change.uninstall(this);
331 				}
332 			}
333 			if (face != null) {
334 				for (SVGFaceChange change : face.getChanges()) {
335 					change.install(this);
336 				}
337 			}
338 		}
339 		currentFaceName = faceName;
340 	}
341 	
342 	@Override
343 	public HandlerRegistration addClickHandler(ClickHandler handler) {
344 		return svgElement.addDomHandler(handler, ClickEvent.getType());
345 	}
346 	@Override
347 	public HandlerRegistration addMouseDownHandler(MouseDownHandler handler) {
348 		return svgElement.addDomHandler(handler, MouseDownEvent.getType());
349 	}
350 	@Override
351 	public HandlerRegistration addMouseUpHandler(MouseUpHandler handler) {
352 		return svgElement.addDomHandler(handler, MouseUpEvent.getType());
353 	}
354 	@Override
355 	public HandlerRegistration addMouseOutHandler(MouseOutHandler handler) {
356 		return svgElement.addDomHandler(handler, MouseOutEvent.getType());
357 	}
358 	@Override
359 	public HandlerRegistration addMouseOverHandler(MouseOverHandler handler) {
360 		return svgElement.addDomHandler(handler, MouseOverEvent.getType());
361 	}
362 	@Override
363 	public HandlerRegistration addMouseMoveHandler(MouseMoveHandler handler) {
364 		return svgElement.addDomHandler(handler, MouseMoveEvent.getType());
365 	}
366 	@Override
367 	public HandlerRegistration addMouseWheelHandler(MouseWheelHandler handler) {
368 		return svgElement.addDomHandler(handler, MouseWheelEvent.getType());
369 	}
370 
371 	
372 	// Implementation of the svg::Stylable W3C IDL interface
373 	public OMSVGStyle getStyle() {
374 		return svgElement.getStyle();
375 	}
376 
377 	public final OMSVGAnimatedString getClassName() {
378 		return svgElement.getClassName();
379 	}
380 
381 	public final void addClassNameBaseVal(String className) {
382 		svgElement.addClassNameBaseVal(className);
383 	}
384 
385 	public final void removeClassNameBaseVal(String className) {
386 		svgElement.removeClassNameBaseVal(className);
387 	}
388 
389 	public final void replaceClassNameBaseVal(String oldClassName, String newClassName) {
390 		svgElement.replaceClassNameBaseVal(oldClassName, newClassName);
391 	}
392 
393 	public final void setClassNameBaseVal(String className) {
394 		svgElement.setClassNameBaseVal(className);
395 	}
396 	@Override
397 	public void onClick(ClickEvent event) {
398 		// Silently block the event. Otherwise it will propagate
399 		// to parent elements in Webkit and trigger text selection
400 		event.stopPropagation();
401 		event.preventDefault();
402 	}
403 }