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.Map;
21  
22  import org.vectomatic.dom.svg.OMSVGSVGElement;
23  import org.vectomatic.dom.svg.ui.SVGButtonBase.SVGFaceName;
24  import org.vectomatic.dom.svg.utils.DOMHelper;
25  
26  import com.google.gwt.dom.client.Element;
27  import com.google.gwt.dom.client.EventTarget;
28  import com.google.gwt.dom.client.NativeEvent;
29  import com.google.gwt.event.dom.client.LoseCaptureEvent;
30  import com.google.gwt.event.dom.client.LoseCaptureHandler;
31  import com.google.gwt.event.dom.client.MouseDownEvent;
32  import com.google.gwt.event.dom.client.MouseEvent;
33  import com.google.gwt.event.dom.client.MouseOutEvent;
34  import com.google.gwt.event.dom.client.MouseOverEvent;
35  import com.google.gwt.event.dom.client.MouseUpEvent;
36  import com.google.gwt.uibinder.client.ElementParserToUse;
37  import com.google.gwt.user.client.Timer;
38  
39  /**
40   * SVG push button class.
41   * <p>An SVG push button typically defines at least the following two faces:</p>
42   * <ul>
43   * <li>UP</li>
44   * <li>DOWN_HOVERING</li>
45   * </ul>
46   * <p>You can define an SVG push button using UiBinder templates. Use
47   * the <em>svgui:upFace</em>, <em>svgui:upDisabledFace</em>, <em>svgui:upHoveringFace</em>
48   * <em>svgui:downFace</em>, <em>svgui:downDisabledFace</em>, <em>svgui:downHoveringFace</em>
49   * tags to defined faces.</p>
50   * <p>Depending on your needs, you can either define the SVG inline with
51   * the <em>svgui:element</em> tag. This can be convenient if you want to 
52   * localize the button label, or use styles defined in the template. 
53   * Or you can use an {@link org.vectomatic.dom.svg.ui.SVGResource SVGResource} with the <em>resource</em> attribute,
54   * if your SVG is large or if you want to keep your template more readable.</p>
55   * <p>Each face contains one or more <em>svgui:styleChange</em> tags.
56   * The <em>classNames</em> attribute specifies the name of the
57   * CSS class selectors to be applied to the SVG element when the face
58   * is activated.</p>
59   * <p>The following section shows a sample UiBinder template:</p>
60   * <pre>
61   * &lt;svgui:SVGPushButton ui:field="clickMeButton"&gt;
62   *   &lt;svgui:element&gt;
63   *     &lt;svg viewBox="0 0 200 60" width="200" height="60" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"&gt;
64   *       &lt;rect class="btn-shape" x="3" y="3" width="194" height="54" rx="10" ry="10" /&gt;
65   *       &lt;text class="btn-text" x="16" y="43"&gt;Click me !&lt;/text&gt;
66   *      &lt;/svg&gt;
67   *   &lt;/svgui:element&gt;
68   *   &lt;svgui:upFace&gt;&lt;svgui:styleChange classNames="{style.btn1} {style.btn1-up}"/&gt;&lt;/svgui:upFace&gt;
69   *   &lt;svgui:downHoveringFace&gt;&lt;svgui:styleChange classNames="{style.btn1} {style.btn1-down-hovering}"/&gt;&lt;/svgui:downHoveringFace&gt;
70   * &lt;/svgui:SVGPushButton&gt;
71   * </pre>
72   * Note that by default the inline SVG in SVGPushButtons is validated against the SVG 1.1 XSD schema.
73   * You can opt out of validation by setting the <code>validated="false"</code>
74   * attribute on the <em>svgui:element</em> tag.
75   * @author laaglu
76   */
77  @ElementParserToUse(className = "org.vectomatic.dev.svg.impl.gen.SVGButtonBaseParser")
78  public class SVGPushButton extends SVGButtonBase {
79  	private int repeatDelayMillis;
80  	private CustomerTimer timer;
81  	private class CustomerTimer extends Timer {
82  		private EventClone mouseDownEvent;
83  		public CustomerTimer(EventClone mouseDownEvent) {
84  			this.mouseDownEvent = mouseDownEvent;
85  		}
86  		@Override
87  		public void run() {
88  			mouseDownEvent.fireEvent(svgElement.getElement());
89  		}
90  	}
91  	private static class EventClone {
92  		private String type;
93  		private int canBubbleArg;
94  		private int cancelableArg;
95  		private int screenX;
96  		private int screenY;
97  		private int clientX;
98  		private int clientY;
99  		private boolean ctrlKey;
100 		private boolean altKey;
101 		private boolean shiftKey;
102 		private boolean metaKey;
103 		private int button;
104 		private EventTarget relatedEventTarget;
105 		public EventClone(MouseEvent<?> mouseEvent) {
106 			NativeEvent evt = mouseEvent.getNativeEvent();
107 			type = evt.getType();
108 			screenX = evt.getScreenX();
109 			screenY = evt.getScreenY();
110 			clientX = evt.getClientX();
111 			clientY = evt.getClientY();
112 			ctrlKey = evt.getCtrlKey();
113 			altKey = evt.getAltKey();
114 			shiftKey = evt.getShiftKey();
115 			metaKey = evt.getMetaKey();
116 			button = evt.getButton();
117 			relatedEventTarget = evt.getRelatedEventTarget();
118 		}
119 
120 		public final native void fireEvent(Element element) /*-{
121 		   var evt = element.ownerDocument.createEvent("MouseEvents");
122 		   evt.initMouseEvent(
123 				  this.@org.vectomatic.dom.svg.ui.SVGPushButton.EventClone::type, 
124 				  true, 
125 				  true, 
126 				  $wnd,
127 				  0, 
128 				  this.@org.vectomatic.dom.svg.ui.SVGPushButton.EventClone::screenX, 
129 				  this.@org.vectomatic.dom.svg.ui.SVGPushButton.EventClone::screenY, 
130 				  this.@org.vectomatic.dom.svg.ui.SVGPushButton.EventClone::clientX, 
131 				  this.@org.vectomatic.dom.svg.ui.SVGPushButton.EventClone::clientY, 
132 				  this.@org.vectomatic.dom.svg.ui.SVGPushButton.EventClone::ctrlKey, 
133 				  this.@org.vectomatic.dom.svg.ui.SVGPushButton.EventClone::altKey, 
134 				  this.@org.vectomatic.dom.svg.ui.SVGPushButton.EventClone::shiftKey,
135 				  this.@org.vectomatic.dom.svg.ui.SVGPushButton.EventClone::metaKey,
136 				  this.@org.vectomatic.dom.svg.ui.SVGPushButton.EventClone::button, 
137 				  this.@org.vectomatic.dom.svg.ui.SVGPushButton.EventClone::relatedEventTarget);
138 			element.dispatchEvent(evt);
139 		}-*/;
140 	}
141 	public SVGPushButton() {
142 	}
143 	public SVGPushButton(OMSVGSVGElement svgElement, Map<SVGFaceName, SVGFace> faces) {
144 		super(svgElement, faces);
145 		showFace(SVGFaceName.UP);
146 	}
147 	public SVGPushButton(SVGResource resource, Map<SVGFaceName, SVGFace> faces) {
148 		this(resource.getSvg(), faces);
149 	}
150 	
151 	@Override
152 	public void setEnabled(boolean enabled) {
153 		if (enabled != isEnabled()) {
154 			if (enabled) {
155 				showFace(SVGFaceName.UP);
156 			} else {
157 				cancelTimer();
158 				showFace(SVGFaceName.UP_DISABLED);
159 			}
160 		}
161 	}
162 	public void onMouseDown(MouseDownEvent event) {
163 //		GWT.log("onMouseDown");
164 		if (isEnabled()) {
165 			DOMHelper.setCaptureElement(svgElement, new LoseCaptureHandler() {
166 				@Override
167 				public void onLoseCapture(LoseCaptureEvent event) {
168 					cancelTimer();
169 				}
170 			});
171 			if (repeatDelayMillis > 0 && timer == null) {
172 				timer = new CustomerTimer(new EventClone(event));
173 				timer.scheduleRepeating(repeatDelayMillis);
174 			}
175 			showFace(SVGFaceName.DOWN_HOVERING);
176 		}
177 		event.stopPropagation();
178 		event.preventDefault();
179 	}
180 	public void onMouseUp(MouseUpEvent event) {
181 //		GWT.log("onMouseUp");
182 		cancelTimer();
183 		DOMHelper.releaseCaptureElement();
184 		if (isEnabled()) {
185 			switch(currentFaceName) {
186 				case DOWN:
187 					showFace(SVGFaceName.UP);
188 					break;
189 				case DOWN_HOVERING:
190 					showFace(SVGFaceName.UP_HOVERING);
191 					break;
192 			}
193 		}
194 		event.stopPropagation();
195 		event.preventDefault();
196 	}
197 	public void onMouseOver(MouseOverEvent event) {
198 //		GWT.log("onMouseOver");
199 		if (isEnabled()) {
200 			switch(currentFaceName) {
201 				case UP:
202 					showFace(SVGFaceName.UP_HOVERING);
203 					break;
204 				case DOWN:
205 					showFace(SVGFaceName.DOWN_HOVERING);
206 					break;
207 			}
208 		}
209 		event.stopPropagation();
210 		event.preventDefault();
211 	}
212 	public void onMouseOut(MouseOutEvent event) {
213 //		GWT.log("onMouseOut");
214 		if (isEnabled()) {
215 			switch(currentFaceName) {
216 				case UP_HOVERING:
217 					showFace(SVGFaceName.UP);
218 					break;
219 				case DOWN_HOVERING:
220 					showFace(SVGFaceName.DOWN);
221 					break;
222 			}
223 		}
224 		event.stopPropagation();
225 		event.preventDefault();
226 	}
227 	
228 	@Override
229 	public SVGFace getFace(SVGFaceName faceName) {
230 		if (!faces.containsKey(faceName)) {
231 			switch (faceName) {
232 				case UP_HOVERING:
233 				case UP_DISABLED:
234 				case DOWN:
235 					faceName = SVGFaceName.UP;
236 					break;
237 				case DOWN_HOVERING:
238 				case DOWN_DISABLED:
239 					faceName = SVGFaceName.DOWN;
240 					break;
241 			}
242 		}
243 		return super.getFace(faceName);
244 	}
245 	public void setRepeatDelay(int repeatDelayMillis) {
246 		this.repeatDelayMillis = repeatDelayMillis;
247 	}
248 	public int getRepeatDelay() {
249 		return repeatDelayMillis;
250 	}
251 	private void cancelTimer() {
252 		if (timer != null) {
253 			timer.cancel();
254 			timer = null;
255 		}
256 	}
257 }