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

Last change on this file since 738 was 738, checked in by pharms, 12 years ago
  • handle invalid log in which there is a key down missing for a matched key up
  • Property svn:executable set to *
File size: 7.8 KB
Line 
1package de.ugoe.cs.quest.eventcore.gui;
2
3import java.io.Serializable;
4import java.util.ArrayList;
5import java.util.Collection;
6import java.util.List;
7
8import de.ugoe.cs.quest.eventcore.Event;
9
10/**
11 * TODO comment
12 *
13 * @version $Revision: $ $Date: $
14 * @author 2011, last modified by $Author: $
15 */
16public class SortedInteractionEventList extends ArrayList<Event> implements List<Event> {
17   
18    /**  */
19    private static final long serialVersionUID = 1L;
20   
21    /** stores the events to be handled later in the order, they should be processed */
22    private List<KeyEventPair> eventPairs = new ArrayList<KeyEventPair>();
23
24    /**
25     * this method sorts interaction events for pressed keys. The reason is, that sometimes the
26     * release of one key comes after the pressing of the succeeding key. This only makes sense for
27     * shift, ctrl or alt keys, but not for usual characters. So the events are sorted.<br/>
28     * Furthermore, this methods creates key released events in the specific case, where a key is
29     * pressed for a long time. Here, many subsequent key pressed events for the same key can be
30     * observed, for which there are no key released events.
31     */
32    @Override
33    public boolean add(Event event) {
34        if (event.getType() instanceof KeyInteraction) {
35            for (int i = 0; i < eventPairs.size(); i++) {
36                KeyEventPair eventPair = eventPairs.get(i);
37
38                if (eventPair.process(event)) {
39                    // handle all leading and completed event pairs, if any
40                    while ((eventPairs.size() > 0) && (eventPairs.get(0).isComplete())) {
41                        addEventPair(eventPairs.get(0));
42                        eventPairs.remove(0);
43                    }
44                    return true;
45                }
46            }
47
48            // in the case the log has an error because it has a key up for which there is no
49            // key down, log a warning and add the key up event without sorting it
50            if (event.getType() instanceof KeyPressed) {
51                eventPairs.add(new KeyEventPair(event));
52                return true;
53            }
54            else {
55                return super.add(event);
56            }
57        }
58        else {
59            // any other event is simply added
60            return super.add(event);
61        }
62    }
63
64    /*
65     * (non-Javadoc)
66     *
67     * @see java.util.ArrayList#add(int, java.lang.Object)
68     */
69    @Override
70    public void add(int index, Event event) {
71        List<Event> remaining = new ArrayList<Event>();
72
73        while (index < super.size()) {
74            remaining.add(super.remove(index));
75        }
76
77        add(event);
78        addAll(remaining);
79    }
80
81    /*
82     * (non-Javadoc)
83     *
84     * @see java.util.ArrayList#addAll(java.util.Collection)
85     */
86    @Override
87    public boolean addAll(Collection<? extends Event> events) {
88        int initialSize = super.size();
89
90        for (Event event : events) {
91            add(event);
92        }
93
94        return initialSize != super.size();
95    }
96
97    /*
98     * (non-Javadoc)
99     *
100     * @see java.util.ArrayList#addAll(int, java.util.Collection)
101     */
102    @Override
103    public boolean addAll(int index, Collection<? extends Event> events) {
104        int initialSize = super.size();
105
106        List<Event> remaining = new ArrayList<Event>();
107
108        while (index < super.size()) {
109            remaining.add(super.remove(index));
110        }
111
112        addAll(events);
113        addAll(remaining);
114
115        return initialSize != super.size();
116    }
117
118    /**
119     *
120     */
121    private void addEventPair(KeyEventPair eventPair) {
122        super.add(eventPair.first);
123
124        for (KeyEventPair child : eventPair.children) {
125            addEventPair(child);
126        }
127
128        super.add(eventPair.second);
129    }
130
131    /**
132     *
133     */
134    private static class KeyEventPair implements Serializable {
135       
136        /**  */
137        private static final long serialVersionUID = 1L;
138
139        /** the first key interaction event */
140        private Event first;
141
142        /** the second key interaction event */
143        private Event second;
144
145        /** the children, this event pair may have */
146        private List<KeyEventPair> children = new ArrayList<KeyEventPair>();
147
148        /**
149         *
150         */
151        private KeyEventPair(Event first) {
152            if (!(first.getType() instanceof KeyPressed)) {
153                throw new IllegalArgumentException
154                  ("can only handle key pressed interaction events as first element of an" +
155                   "event pair");
156            }
157            this.first = first;
158        }
159
160        /**
161         * sorts the event as the second of the pair or as the closing of this. Returns true, if
162         * this matched, false else
163         */
164        private boolean process(Event event) {
165            if (!(event.getType() instanceof KeyInteraction)) {
166                throw new IllegalArgumentException("can only handle key interaction events");
167            }
168
169            if (this.isComplete()) {
170                // do not process events, if there are no more events expected
171                return false;
172            }
173
174            if ((event.getType() instanceof KeyReleased) &&
175                (((KeyInteraction) first.getType()).getKey() ==
176                 ((KeyInteraction) event.getType()).getKey()) &&
177                (second == null))
178            {
179                // this is the release of the represented key. Store it and notify the processing.
180                second = event;
181                return true;
182            }
183            else if ((event.getType() instanceof KeyPressed) &&
184                     (((KeyInteraction) first.getType()).getKey() ==
185                      ((KeyInteraction) event.getType()).getKey()) &&
186                     (second == null))
187            {
188                // the key was pressed before it was released again. This happens, if the key
189                // stays pressed for a long time. Generate a key released event but do not mark
190                // the new event as processed
191                second = new Event
192                    (new KeyReleased(((KeyInteraction) event.getType()).getKey()),
193                     first.getTarget());
194               
195                return false;
196            }
197            else if (((KeyInteraction) first.getType()).getKey().isCombinationKey() &&
198                     (((KeyInteraction) first.getType()).getKey() !=
199                      ((KeyInteraction) event.getType()).getKey()))
200            {
201                // this pair may have children. Let the event be processed by the children. If this
202                // doesn't work, add the event as a new child pair, if it is a new key pressed
203
204                for (KeyEventPair child : children) {
205                    if (child.process(event)) {
206                        return true;
207                    }
208                }
209
210                if (event.getType() instanceof KeyPressed) {
211                    children.add(new KeyEventPair(event));
212                    return true;
213                }
214                else {
215                    return false;
216                }
217            }
218            else {
219                // this pair may not have children
220                return false;
221            }
222        }
223
224        /**
225         * @return
226         */
227        private boolean isComplete() {
228            if ((first != null) && (second != null)) {
229                for (KeyEventPair child : children) {
230                    if (!child.isComplete()) {
231                        return false;
232                    }
233                }
234
235                return true;
236            }
237            else {
238                return false;
239            }
240        }
241    }
242}
Note: See TracBrowser for help on using the repository browser.