source: trunk/quest-core-events/src/main/java/de/ugoe/cs/quest/eventcore/gui/TextInputDetector.java @ 709

Last change on this file since 709 was 709, checked in by pharms, 12 years ago

improved debugging support

File size: 11.1 KB
Line 
1package de.ugoe.cs.quest.eventcore.gui;
2
3import java.util.ArrayList;
4import java.util.LinkedList;
5import java.util.List;
6import java.util.Locale;
7
8import de.ugoe.cs.quest.eventcore.Event;
9import de.ugoe.cs.quest.eventcore.IEventTarget;
10import de.ugoe.cs.quest.eventcore.gui.KeyInteraction;
11import de.ugoe.cs.quest.eventcore.gui.KeyPressed;
12import de.ugoe.cs.quest.eventcore.gui.KeyReleased;
13import de.ugoe.cs.quest.eventcore.guimodel.ITextArea;
14import de.ugoe.cs.quest.eventcore.guimodel.ITextField;
15import de.ugoe.cs.tasktree.keyboardmaps.KeyboardMap;
16import de.ugoe.cs.tasktree.keyboardmaps.KeyboardMapFactory;
17import de.ugoe.cs.tasktree.keyboardmaps.VirtualKey;
18
19/**
20 * <p>
21 * The text input detector iterates a list of events and searches for subsequent key events.
22 * Those are replaced by a single text input event representing the text entered through the key
23 * events. The replacement is only done, if the key events have a text field or text area as target
24 * </p>
25 *
26 * @version $Revision: $ $Date: 18.03.2012$
27 * @author 2012, last modified by $Author: patrick$
28 */
29public class TextInputDetector {
30   
31    /** the keyboard map to use for character recognition*/
32    private KeyboardMap keyboardMap = KeyboardMapFactory.createKeyboardMap(Locale.GERMAN);
33   
34    /** the keys pressed in parallel */
35    List<VirtualKey> pressedKeys = new ArrayList<VirtualKey>();
36
37    /**
38     * <p>
39     * in the provided list of events, this method detects any event sequences that consists of
40     * key interactions and replaces them with a single text input interaction. This contains
41     * the entered text as well as the replaced key interaction events
42     * </p>
43     *
44     * @param sequence the event sequence to search for text input events
45     *
46     * @return the resulting sequence, in which key interactions on text fields and areas are
47     *         reduced to text input interactions
48     */
49    public List<Event> detectTextInputs(List<Event> sequence) {
50        List<Event> resultingSequence = new LinkedList<Event>();
51       
52        int textEntryStartIndex = -1;
53        IEventTarget lastEventTarget = null;
54
55        int index = 0;
56        Event currentEvent = null;
57        Event textInputEvent = null;
58        while (index < sequence.size()) {
59            currentEvent = sequence.get(index);
60            textInputEvent = null;
61           
62            if (isKeyInteraction(currentEvent) && isDataInputEventTarget(currentEvent.getTarget()))
63            {
64                if (textEntryStartIndex < 0) {
65                    textEntryStartIndex = index;
66                    lastEventTarget = currentEvent.getTarget();
67                }
68                else if (!lastEventTarget.equals(currentEvent.getTarget())) {
69                    textInputEvent = handleTextEntrySequence
70                        (sequence, textEntryStartIndex, index - 1, lastEventTarget);
71                   
72                    textEntryStartIndex = index;
73                    lastEventTarget = currentEvent.getTarget();
74                }
75                currentEvent = null;
76            }
77            else {
78                if (textEntryStartIndex >= 0) {
79                    textInputEvent = handleTextEntrySequence
80                        (sequence, textEntryStartIndex, index - 1, lastEventTarget);
81                   
82                    textEntryStartIndex = -1;
83                    lastEventTarget = null;
84                }
85               
86            }
87
88            if (textInputEvent != null) {
89                resultingSequence.add(textInputEvent);
90            }
91           
92            if (currentEvent != null) {
93                resultingSequence.add(currentEvent);
94            }
95           
96            index++;
97        }
98
99        if (textEntryStartIndex >= 0) {
100            textInputEvent = handleTextEntrySequence
101                (sequence, textEntryStartIndex, sequence.size() - 1, lastEventTarget);
102           
103            if (textInputEvent != null) {
104                resultingSequence.add(textInputEvent);
105            }
106        }
107
108        return resultingSequence;
109    }
110
111    /**
112     * <p>
113     * returns true if the provide event is a key interaction; false else
114     * </p>
115     *
116     * @param event the even to check
117     *
118     * @return as described
119     */
120    private boolean isKeyInteraction(Event event) {
121        return (event.getType() instanceof KeyInteraction);
122    }
123
124    /**
125     * <p>
126     * creates a single text input event as replacement for key interactions being part of the
127     * subsequence of events denoted by the start and end index in the provide event sequence. If
128     * no text was entered, because the subsequence only contained key released events, then no
129     * text input event is generated (the method returns null).
130     * </p>
131     *
132     * @param sequence
133     *            the event sequence of which the subsequence is analyzed
134     * @param startIndex
135     *            the start index in the event sequence from which the analysis should start
136     *            (inclusive)
137     * @param endIndex
138     *            the end index in the event sequence where the analysis should end (inclusive)
139     * @param eventTarget
140     *            the event target to be used for the new event
141     *
142     * @return a text input event representing the text input resulting from the events of
143     *         the provided subsequence
144     *
145     * @throws IllegalArgumentException
146     *             if the denoted subsequence contains other events than key interactions
147     */
148    private Event handleTextEntrySequence(List<Event>  sequence,
149                                          int          startIndex,
150                                          int          endIndex,
151                                          IEventTarget eventTarget)
152    {
153        List<Event> textInputEvents = new ArrayList<Event>();
154
155        String enteredText = determineEnteredText(sequence, startIndex, endIndex, textInputEvents);
156       
157        if ((enteredText != null) && (!"".equals(enteredText))) {
158            TextInput textInput = new TextInput(enteredText, textInputEvents);
159            return new Event(textInput, eventTarget);
160        }
161        else {
162            return null;
163        }
164    }
165
166    /**
167     * <p>
168     * check if an event target is a data input field, i.e. a text field or a text area
169     * </p>
170     *
171     * @param eventTarget the event target to check
172     *
173     * @return true, if it is a text field or a text area ; false else
174     */
175    private boolean isDataInputEventTarget(IEventTarget eventTarget) {
176        return ((eventTarget instanceof ITextField) || (eventTarget instanceof ITextArea));
177    }
178
179    /**
180     * <p>
181     * determines the text entered in the event subsequence denoted by the start and end index of
182     * the provided event sequence. The method records any pressed and released key interaction as
183     * well as combinations of pressed key interactions (such as shift + letter) and determines the
184     * respective character. All identified characters are then combined to the entered text. The
185     * method also identifies the usage of the back space. The enter key is ignored for text fields
186     * in which pressing the enter key results in finishing the text entry. All analyzed key
187     * interaction events are stored in the provided text input events result list. If the
188     * subsequence only contains key released events, no text was entered and the returned text is
189     * null.
190     * </p>
191     *
192     * @param sequence
193     *            the event sequence of which the subsequence is analyzed
194     * @param startIndex
195     *            the start index in the event sequence from which the analysis should start
196     *            (inclusive)
197     * @param endIndex
198     *            the end index in the event sequence where the analysis should end (inclusive)
199     * @param textInputEvents
200     *            a buffer to contain any key interaction event analyzed (in out)
201     *
202     * @return the text entered through the interaction events of the denoted subsequence
203     *
204     * @throws IllegalArgumentException
205     *             if the denoted sequence contains other events than key interactions
206     */
207    private String determineEnteredText(List<Event> sequence,
208                                        int         startIndex,
209                                        int         endIndex,
210                                        List<Event> textInputEvents)
211        throws IllegalArgumentException
212    {
213        Event event;
214        StringBuffer enteredText = new StringBuffer();
215       
216        for (int i = startIndex; i <= endIndex; i++) {
217            event = sequence.get(i);
218           
219            if (event.getType() instanceof KeyPressed) {
220                VirtualKey key = ((KeyPressed) event.getType()).getKey();
221
222                pressedKeys.add(key);
223
224                if (key == VirtualKey.BACK_SPACE) {
225                    if (enteredText.length() > 0) {
226                        enteredText.deleteCharAt(enteredText.length() - 1);
227                    }
228                }
229                else if (key == VirtualKey.ENTER) {
230                    // text fields only contain one line of code. Therefore the return is ignored.
231                    if (!(event.getTarget() instanceof ITextField)) {
232                        enteredText.append(getCharacter(key, pressedKeys));
233                    }
234                }
235                else {
236                    char theChar = getCharacter(key, pressedKeys);
237                    if (theChar != Character.UNASSIGNED) {
238                        enteredText.append(theChar);
239                    }
240                }
241            }
242            else if (event.getType() instanceof KeyReleased) {
243                pressedKeys.remove(((KeyReleased) event.getType()).getKey());
244            }
245            else {
246                throw new IllegalArgumentException
247                    ("the subsequence denoted by the indexes contains other interactions than " +
248                     "just key strokes");
249            }
250           
251            textInputEvents.add(event);
252        }
253       
254        if (enteredText.length() > 0) {
255            return enteredText.toString();
256        }
257        else {
258            return null;
259        }
260    }
261
262    /**
263     * <p>
264     * determines the character matching the pressed key depending on other keys pressed in parallel
265     * such as the shift key.
266     * </p>
267     *
268     * @param key         the key for which the character shall be determined
269     * @param pressedKeys the list of other keys pressed in parallel
270     *
271     * @return the character resulting from the combination of pressed keys
272     */
273    private char getCharacter(VirtualKey key, List<VirtualKey> pressedKeys) {
274        boolean numlock = false;
275        boolean shift = false;
276        boolean altgr = false;
277
278        for (VirtualKey pressedKey : pressedKeys) {
279            if (pressedKey.isShiftKey()) {
280                shift = !shift;
281            }
282            else if (pressedKey == VirtualKey.ALT_GRAPH) {
283                altgr = !altgr;
284            }
285            else if (pressedKey == VirtualKey.NUM_LOCK) {
286                numlock = !numlock;
287            }
288        }
289
290        return keyboardMap.getCharacterFor(key, numlock, shift, altgr, false);
291    }
292
293}
Note: See TracBrowser for help on using the repository browser.