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    }