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

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