001 /*
002 * Copyright 2006 Mat Gessel <mat.gessel@gmail.com>
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005 * use this file except in compliance with the License. You may obtain a copy of
006 * the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013 * License for the specific language governing permissions and limitations under
014 * the License.
015 */
016 package asquare.gwt.debug.client;
017
018 import com.google.gwt.user.client.DOM;
019 import com.google.gwt.user.client.Event;
020
021 /**
022 * A UI event monitor. Event descriptions are output via
023 * {@link asquare.gwt.debug.client.Debug#println(String)}. Tracing is
024 * enabled/disabled by pressing the enabler key twice sequentially. You can
025 * specify which kinds of events you are interested in when you create the
026 * monitor.
027 * <p>
028 * Popups and dialogs will block the listener if they are shown after it is
029 * installed. Likewise, the listener will prevent with event filtering by popups
030 * if initialized after the popup is shown. This is due to the design of
031 * {@link com.google.gwt.user.client.EventPreview EventPreview}.
032 * </p>
033 *
034 * @see #DEFAULT_ENABLE_KEY
035 * @see #DEFAULT_MASK
036 * @see com.google.gwt.user.client.EventPreview
037 */
038 public class DebugEventListener
039 {
040 /**
041 * A mask matching all events
042 */
043 public static final int MASK_ALL = ~0;
044
045 /**
046 * A mask matching all but onmousemove, onmouseover, onmouseout and onscroll
047 */
048 public static final int DEFAULT_MASK = MASK_ALL & ~Event.ONMOUSEMOVE & ~Event.ONSCROLL &
049 ~Event.ONMOUSEOVER & ~Event.ONMOUSEOUT;
050
051 /**
052 * The default enabler key, which is <code>'e'</code>
053 */
054 public static final char DEFAULT_ENABLE_KEY = 'e';
055
056 private final String m_name;
057
058 private int m_eventMask;
059
060 private char m_enableKey;
061
062 private boolean m_enabled = false;
063
064 private char m_lastKeyDown = 0;
065
066 /**
067 * Create a new instance with the default key code and event mask.
068 */
069 public DebugEventListener()
070 {
071 this((char) 0, -1, null);
072 }
073
074 /**
075 * Create a new instance with the specified enable key and the default event mask.
076 *
077 * @param enableKey a key code, or -1 for the default (<code>'e'</code>)
078 */
079 public DebugEventListener(char enableKey)
080 {
081 this(enableKey, -1, null);
082 }
083
084 /**
085 * Create a new instance with the specified event mask and the default enable key.
086 *
087 * @param eventMask an event mask or -1 for the default
088 * @see Event
089 */
090 public DebugEventListener(int eventMask)
091 {
092 this((char) 0, eventMask, null);
093 }
094
095 /**
096 * Create a new instance with the specified enable key and event mask.
097 *
098 * @param enableKey a key code, or 0 for the default (<code>'e'</code>)
099 * @param eventMask an event mask or -1 for the default
100 * @param name a name to identify this event listener instance in debug statements
101 * @see Event
102 */
103 public DebugEventListener(char enableKey, int eventMask, String name)
104 {
105 m_enableKey = (enableKey > 0) ? enableKey : DEFAULT_ENABLE_KEY;
106 m_enableKey = (Character.toUpperCase(m_enableKey));
107 m_eventMask = (eventMask != -1) ? eventMask : DEFAULT_MASK; // negative values are possible
108 m_name = (name != null) ? name : "Event monitor";
109 }
110
111 /**
112 * Installs this listener so that it listens to dispatched events. It will
113 * not process events or produce output until it is enabled by pressing the
114 * enable key twice or via {@link #enable(boolean)}.
115 */
116 public void install()
117 {
118 EventPreviewDispatcher.addListener(this);
119 }
120
121 public void uninstall()
122 {
123 EventPreviewDispatcher.removeListener(this);
124 }
125
126 /**
127 * Get the key used to enable processing of events.
128 *
129 * @return a unicode character code corresponding the key
130 */
131 public char getEnableKey()
132 {
133 return m_enableKey;
134 }
135
136 /**
137 * Set the key used to enable processing of events.
138 *
139 * @param keyCode a unicode character code corresponding the key
140 */
141 public void setEnableKey(char keyCode)
142 {
143 m_enableKey = keyCode;
144 }
145
146 /**
147 * Get the bitmask representing the events which this listener will process.
148 *
149 * @return the bitmask
150 * @see Event
151 */
152 public int getEventMask()
153 {
154 return m_eventMask;
155 }
156
157 /**
158 * Specifies the events which the listener will process when it is enabled.
159 * Pass <code>0</code> to ignore all events.
160 * <p>
161 * Example:
162 * <pre>setEventMask(Event.ONMOUSEDOWN | Event.ONCLICK)</pre>
163 *
164 * @param mask a bitmask of event types
165 * @see Event
166 */
167 public void setEventMask(int mask)
168 {
169 m_eventMask = mask;
170 }
171
172 /**
173 * Enables/disables this listener. When enabled it will process events
174 * via {@link #doEvent(Event)}
175 *
176 * @param enable
177 */
178 public void enable(boolean enable)
179 {
180 if (m_enabled != enable)
181 {
182 m_enabled = enable;
183 if (m_enabled)
184 {
185 doEnabled();
186 }
187 else
188 {
189 doDisabled();
190 }
191 }
192 }
193
194 /**
195 * Sets this listener as enabled/disabled but does not perform the
196 * {@link #doEnabled()}/{@link #doDisabled()} action.
197 *
198 * @param enabled
199 */
200 public void setEnabled(boolean enabled)
201 {
202 m_enabled = enabled;
203 }
204
205 /**
206 * Called by {@link EventPreviewDispatcher} when an event is previewed.
207 *
208 * @param event
209 */
210 public void eventDispatched(Event event)
211 {
212 /*
213 * onKeyDown works with invisible characters (e.g. caps lock)
214 * onKeyPress works with visible characters (i.e. [a-zA-Z0-9~!@#$%^&*()_+])
215 */
216 int eventType = DOM.eventGetType(event);
217 if (eventType == Event.ONKEYDOWN)
218 {
219 char keyCode = (char) DOM.eventGetKeyCode(event);
220 if (isSameKey(keyCode, m_lastKeyDown) && isSameKey(keyCode, m_enableKey))
221 {
222 m_lastKeyDown = 0;
223 enable(! m_enabled);
224 }
225 else
226 {
227 m_lastKeyDown = keyCode;
228 }
229 }
230
231 if (m_enabled && (m_eventMask & eventType) != 0)
232 {
233 doEvent(event);
234 }
235 }
236
237 /**
238 * Compares two character codes, accounting for case.
239 * Calling <code>isSameKey('a', 'A')</code> would return true.
240 *
241 * @param a
242 * @param b
243 * @return true if both keycodes are generated from the same key
244 */
245 private boolean isSameKey(char a, char b)
246 {
247 if (a == b)
248 return true;
249
250 if (Character.isLetter(a) && Character.isLetter(b))
251 {
252 return (Character.toLowerCase(a) == Character.toLowerCase(b));
253 }
254
255 return false;
256 }
257
258 /**
259 * Override to perform a custom action whenever this listener is disabled.
260 */
261 protected void doEnabled()
262 {
263 Debug.println("(" + m_enableKey + ") " + m_name + " enabled for " + DebugUtil.prettyPrintEventMask(m_eventMask));
264 }
265
266 /**
267 * Override to perform a custom action whenever this listener is enabled.
268 */
269 protected void doDisabled()
270 {
271 Debug.println("(" + m_enableKey + ") " + m_name + " disabled");
272 }
273
274 /**
275 * Override to perform a custom action for each event.
276 *
277 * @param event
278 */
279 protected void doEvent(Event event)
280 {
281 Debug.println(DebugUtil.prettyPrintEvent(event));
282 }
283 }