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

Last change on this file since 687 was 687, checked in by pharms, 12 years ago
  • moved text input detection from task tree generation to dedicated command to be run on sequences
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    {
51        List<Event> resultingSequence = new LinkedList<Event>();
52       
53        int textEntryStartIndex = -1;
54        IEventTarget lastEventTarget = null;
55
56        int index = 0;
57        Event currentEvent = null;
58        Event textInputEvent = null;
59        while (index < sequence.size()) {
60            currentEvent = sequence.get(index);
61            textInputEvent = null;
62           
63            if (isKeyInteraction(currentEvent) && isDataInputEventTarget(currentEvent.getTarget()))
64            {
65                if (textEntryStartIndex < 0) {
66                    textEntryStartIndex = index;
67                    lastEventTarget = currentEvent.getTarget();
68                }
69                else if (!lastEventTarget.equals(currentEvent.getTarget())) {
70                    textInputEvent = handleTextEntrySequence
71                        (sequence, textEntryStartIndex, index - 1, lastEventTarget);
72                   
73                    textEntryStartIndex = index;
74                    lastEventTarget = currentEvent.getTarget();
75                }
76                currentEvent = null;
77            }
78            else {
79                if (textEntryStartIndex >= 0) {
80                    textInputEvent = handleTextEntrySequence
81                        (sequence, textEntryStartIndex, index - 1, lastEventTarget);
82                   
83                    textEntryStartIndex = -1;
84                    lastEventTarget = null;
85                }
86               
87            }
88
89            if (textInputEvent != null) {
90                resultingSequence.add(textInputEvent);
91            }
92           
93            if (currentEvent != null) {
94                resultingSequence.add(currentEvent);
95            }
96           
97            index++;
98        }
99
100        if (textEntryStartIndex >= 0) {
101            textInputEvent = handleTextEntrySequence
102                (sequence, textEntryStartIndex, sequence.size() - 1, lastEventTarget);
103           
104            if (textInputEvent != null) {
105                resultingSequence.add(textInputEvent);
106            }
107        }
108
109        return resultingSequence;
110    }
111
112    /**
113     * <p>
114     * returns true if the provide event is a key interaction; false else
115     * </p>
116     *
117     * @param event the even to check
118     *
119     * @return as described
120     */
121    private boolean isKeyInteraction(Event event) {
122        return (event.getType() instanceof KeyInteraction);
123    }
124
125    /**
126     * <p>
127     * creates a single text input event as replacement for key interactions being part of the
128     * subsequence of events denoted by the start and end index in the provide event sequence. If
129     * no text was entered, because the subsequence only contained key released events, then no
130     * text input event is generated (the method returns null).
131     * </p>
132     *
133     * @param sequence
134     *            the event sequence of which the subsequence is analyzed
135     * @param startIndex
136     *            the start index in the event sequence from which the analysis should start
137     *            (inclusive)
138     * @param endIndex
139     *            the end index in the event sequence where the analysis should end (inclusive)
140     * @param eventTarget
141     *            the event target to be used for the new event
142     *
143     * @return a text input event representing the text input resulting from the events of
144     *         the provided subsequence
145     *
146     * @throws IllegalArgumentException
147     *             if the denoted subsequence contains other events than key interactions
148     */
149    private Event handleTextEntrySequence(List<Event>  sequence,
150                                          int          startIndex,
151                                          int          endIndex,
152                                          IEventTarget eventTarget)
153    {
154        List<Event> textInputEvents = new ArrayList<Event>();
155
156        String enteredText = determineEnteredText(sequence, startIndex, endIndex, textInputEvents);
157       
158        if ((enteredText != null) && (!"".equals(enteredText))) {
159            TextInput textInput = new TextInput(enteredText, textInputEvents);
160            return new Event(textInput, eventTarget);
161        }
162        else {
163            return null;
164        }
165    }
166
167    /**
168     * <p>
169     * check if an event target is a data input field, i.e. a text field or a text area
170     * </p>
171     *
172     * @param eventTarget the event target to check
173     *
174     * @return true, if it is a text field or a text area ; false else
175     */
176    private boolean isDataInputEventTarget(IEventTarget eventTarget) {
177        return ((eventTarget instanceof ITextField) || (eventTarget instanceof ITextArea));
178    }
179
180    /**
181     * <p>
182     * determines the text entered in the event subsequence denoted by the start and end index of
183     * the provided event sequence. The method records any pressed and released key interaction as
184     * well as combinations of pressed key interactions (such as shift + letter) and determines the
185     * respective character. All identified characters are then combined to the entered text. The
186     * method also identifies the usage of the back space. The enter key is ignored for text fields
187     * in which pressing the enter key results in finishing the text entry. All analyzed key
188     * interaction events are stored in the provided text input events result list. If the
189     * subsequence only contains key released events, no text was entered and the returned text is
190     * null.
191     * </p>
192     *
193     * @param sequence
194     *            the event sequence of which the subsequence is analyzed
195     * @param startIndex
196     *            the start index in the event sequence from which the analysis should start
197     *            (inclusive)
198     * @param endIndex
199     *            the end index in the event sequence where the analysis should end (inclusive)
200     * @param textInputEvents
201     *            a buffer to contain any key interaction event analyzed (in out)
202     *
203     * @return the text entered through the interaction events of the denoted subsequence
204     *
205     * @throws IllegalArgumentException
206     *             if the denoted sequence contains other events than key interactions
207     */
208    private String determineEnteredText(List<Event> sequence,
209                                        int         startIndex,
210                                        int         endIndex,
211                                        List<Event> textInputEvents)
212        throws IllegalArgumentException
213    {
214        Event event;
215        StringBuffer enteredText = new StringBuffer();
216       
217        for (int i = startIndex; i <= endIndex; i++) {
218            event = sequence.get(i);
219           
220            if (event.getType() instanceof KeyPressed) {
221                VirtualKey key = ((KeyPressed) event.getType()).getKey();
222
223                pressedKeys.add(key);
224
225                if (key == VirtualKey.BACK_SPACE) {
226                    if (enteredText.length() > 0) {
227                        enteredText.deleteCharAt(enteredText.length() - 1);
228                    }
229                }
230                else if (key == VirtualKey.ENTER) {
231                    // text fields only contain one line of code. Therefore the return is ignored.
232                    if (!(event.getTarget() instanceof ITextField)) {
233                        enteredText.append(getCharacter(key, pressedKeys));
234                    }
235                }
236                else {
237                    char theChar = getCharacter(key, pressedKeys);
238                    if (theChar != Character.UNASSIGNED) {
239                        enteredText.append(theChar);
240                    }
241                }
242            }
243            else if (event.getType() instanceof KeyReleased) {
244                pressedKeys.remove(((KeyReleased) event.getType()).getKey());
245            }
246            else {
247                throw new IllegalArgumentException
248                    ("the subsequence denoted by the indexes contains other interactions than " +
249                     "just key strokes");
250            }
251           
252            textInputEvents.add(event);
253        }
254       
255        if (enteredText.length() > 0) {
256            return enteredText.toString();
257        }
258        else {
259            return null;
260        }
261    }
262
263    /**
264     * <p>
265     * determines the character matching the pressed key depending on other keys pressed in parallel
266     * such as the shift key.
267     * </p>
268     *
269     * @param key         the key for which the character shall be determined
270     * @param pressedKeys the list of other keys pressed in parallel
271     *
272     * @return the character resulting from the combination of pressed keys
273     */
274    private char getCharacter(VirtualKey key, List<VirtualKey> pressedKeys) {
275        boolean numlock = false;
276        boolean shift = false;
277        boolean altgr = false;
278
279        for (VirtualKey pressedKey : pressedKeys) {
280            if (pressedKey.isShiftKey()) {
281                shift = !shift;
282            }
283            else if (pressedKey == VirtualKey.ALT_GRAPH) {
284                altgr = !altgr;
285            }
286            else if (pressedKey == VirtualKey.NUM_LOCK) {
287                numlock = !numlock;
288            }
289        }
290
291        return keyboardMap.getCharacterFor(key, numlock, shift, altgr, false);
292    }
293
294}
Note: See TracBrowser for help on using the repository browser.