source: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/commands/CMDgenerateJacaretoReplay.java @ 1809

Last change on this file since 1809 was 1809, checked in by dmay, 10 years ago

more click fixes

  • Property svn:mime-type set to text/plain
File size: 23.4 KB
RevLine 
[1671]1//   Copyright 2012 Georg-August-Universität Göttingen, Germany
2//
3//   Licensed under the Apache License, Version 2.0 (the "License");
4//   you may not use this file except in compliance with the License.
5//   You may obtain a copy of the License at
6//
7//       http://www.apache.org/licenses/LICENSE-2.0
8//
9//   Unless required by applicable law or agreed to in writing, software
10//   distributed under the License is distributed on an "AS IS" BASIS,
11//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//   See the License for the specific language governing permissions and
13//   limitations under the License.
14
[1673]15package de.ugoe.cs.autoquest.plugin.jfc.commands;
[1671]16
17import java.io.BufferedWriter;
[1673]18import java.io.File;
19import java.io.FileOutputStream;
[1671]20import java.io.IOException;
[1673]21import java.io.OutputStreamWriter;
[1671]22import java.util.ArrayList;
[1715]23import java.util.Calendar;
[1671]24import java.util.Collection;
[1704]25import java.util.HashMap;
[1671]26import java.util.Iterator;
27import java.util.List;
[1678]28import java.util.UUID;
[1673]29import java.util.logging.Level;
[1671]30
31import de.ugoe.cs.autoquest.CommandHelpers;
32import de.ugoe.cs.autoquest.SequenceInstanceOf;
[1673]33import de.ugoe.cs.util.console.Command;
[1671]34import de.ugoe.cs.autoquest.eventcore.Event;
[1809]35import de.ugoe.cs.autoquest.eventcore.IEventTarget;
[1685]36import de.ugoe.cs.autoquest.eventcore.gui.*;
[1691]37import de.ugoe.cs.autoquest.keyboardmaps.VirtualKey;
[1673]38import de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCGUIElement;
[1671]39import de.ugoe.cs.util.console.Console;
40import de.ugoe.cs.util.console.GlobalDataContainer;
41
[1688]42// helper class for the tree like structure part within a Jacareto file
43class StructureNode {
[1713]44    public static int nextRef = 0;
[1689]45
[1688]46    public String content;
47    public ArrayList<StructureNode> children;
48
[1689]49    public StructureNode(String type) {
[1714]50        setContent(type);
[1689]51        children = new ArrayList<StructureNode>();
[1688]52    }
53
[1689]54    public StructureNode() {
55        content = "<Recordable ref=\"" + (nextRef++) + "\" />";
56        children = new ArrayList<StructureNode>();
57    }
58
[1714]59    public void setContent(String type) {
60        content = "<StructureElement class=\"jacareto.struct." + type + "\">";
61    }
62
[1689]63    public StructureNode add(String type) {
64        StructureNode node = new StructureNode(type);
[1688]65        children.add(node);
66        return node;
67    }
68
[1689]69    public void addRecordable() {
70        children.add(new StructureNode());
71    }
72
[1688]73    @Override
74    public String toString() {
75        String separator = System.getProperty("line.separator");
76        String result = content + separator;
77
78        for (StructureNode child : children) {
79            result += child.toString();
80        }
81
[1689]82        if (content.endsWith("/>")) {
[1688]83            return result;
84        }
85        return result + "</StructureElement>" + separator;
86    }
87}
88
[1671]89/**
90 * <p>
91 * Command to create a Jacareto xml replay file from stored sessions.
92 * </p>
93 *
94 * @author Daniel May
95 * @version 1.0
96 */
[1673]97public class CMDgenerateJacaretoReplay implements Command {
[1719]98    private static final int EVENT_DURATION = 150;
[1808]99    private static final int DOUBLE_CLICK_DURATION = 50;
100    private static final int STARTUP_DELAY = 10000;
[1719]101
[1686]102    private JFCGUIElement currentFocus;
[1688]103    private StructureNode structure;
[1704]104
[1701]105    private StructureNode lastKeySequenceEvent;
106    private StructureNode lastKeyTypedEvent;
[1704]107    private int currentKeyModifiers;
108
109    private HashMap<VirtualKey, Integer> modifiers;
[1712]110
[1688]111    private StructureNode lastMouseClickEvent;
112    private StructureNode lastFocusChangeEvent;
113    private StructureNode lastItemActionEvent;
[1809]114    private IEventTarget lastMouseDownTarget;
[1688]115
[1671]116    /*
117     * (non-Javadoc)
118     *
119     * @see de.ugoe.cs.util.console.Command#help()
120     */
121    @Override
122    public String help() {
[1712]123        return "generateJacaretoReplay <filename> <sequences> <class> <basepath> <classpathext> {<initclass>}";
[1671]124    }
125
126    /*
127     * (non-Javadoc)
128     *
129     * @see de.ugoe.cs.util.console.Command#run(java.util.List)
130     */
131    @SuppressWarnings("unchecked")
132    @Override
133    public void run(List<Object> parameters) {
134        String filename;
135        String sequencesName;
[1712]136        String classpath;
137        String basepath;
138        String classpathext;
139        String initclass = "";
[1671]140        try {
141            filename = (String) parameters.get(0);
142            sequencesName = (String) parameters.get(1);
[1712]143            classpath = (String) parameters.get(2);
144            basepath = (String) parameters.get(3);
145            classpathext = (String) parameters.get(4);
[1671]146        }
147        catch (Exception e) {
148            throw new IllegalArgumentException();
149        }
150
[1712]151        if (parameters.size() > 5) {
152            initclass = (String) parameters.get(5);
153        }
154
[1671]155        Collection<List<Event>> sequences = null;
156        Object dataObject = GlobalDataContainer.getInstance().getData(sequencesName);
157        if (dataObject == null) {
158            CommandHelpers.objectNotFoundMessage(sequencesName);
159            return;
160        }
161        if (!SequenceInstanceOf.isCollectionOfSequences(dataObject)) {
162            CommandHelpers.objectNotType(sequencesName, "Collection<List<Event<?>>>");
163            return;
164        }
165
166        sequences = (Collection<List<Event>>) dataObject;
167
[1704]168        // map which maps VirtualKeys back to awt key modifier codes
169        modifiers = new HashMap<>();
170        modifiers.put(VirtualKey.SHIFT, 1);
171        modifiers.put(VirtualKey.CONTROL, 2);
172        modifiers.put(VirtualKey.ALT, 8);
173        modifiers.put(VirtualKey.ALT_GRAPH, 32);
174        currentKeyModifiers = 0;
[1712]175
[1713]176        StructureNode.nextRef = 0;
177
[1712]178        writeJacaretoXML(sequences, filename, classpath, initclass, basepath, classpathext);
[1671]179    }
180
181    private void writeLine(BufferedWriter writer, String line) throws IOException {
182        writer.write(line);
183        writer.newLine();
184    }
185
[1712]186    private void writeJacaretoHead(BufferedWriter writer,
187                                   String classname,
188                                   String initclass,
189                                   String basepath,
190                                   String classpathext) throws IOException
191    {
[1715]192        Calendar now = Calendar.getInstance();
193
[1671]194        writeLine(writer, "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>");
195        writeLine(writer, "<JacaretoStructure>");
196        writeLine(writer, "<Record>");
197
[1715]198        //@formatter:off
[1716]199        writeLine(writer, "<Calendar "
200            + "procTime=\"0\" "
[1715]201            + "duration=\"0\" "
202            + "year=\"" + now.get(Calendar.YEAR) + "\" "
[1716]203            + "month=\"" + (now.get(Calendar.MONTH) + 1) + "\" "
[1715]204            + "date=\"" + now.get(Calendar.DAY_OF_MONTH) + "\" "
205            + "hour=\"" + now.get(Calendar.HOUR_OF_DAY) + "\" "
206            + "min=\"" + now.get(Calendar.MINUTE) + "\" "
207            + "sec=\"" + now.get(Calendar.SECOND) + "\" "
208            + "uuid=\"" + UUID.randomUUID() + "\" />"
209        );
[1671]210        writeLine(writer,
211                  "<SystemInfo procTime=\"0\" duration=\"0\" screenWidth=\"2646\" screenHeight=\"1024\" javaVersion=\"1.7.0_65\" lookAndFeel=\"javax.swing.plaf.metal.MetalLookAndFeel\" uuid=\"720f430f-52cf-4d8b-9fbe-58434f766efe\" />");
212        writeLine(writer,
213                  "<KeyboardState procTime=\"0\" duration=\"0\" isNumLockOn=\"false\" isScrollLockOn=\"false\" isCapsLockOn=\"false\" applyIsNumLockOn=\"true\" applyIsScrollLockOn=\"true\" applyIsCapsLockOn=\"true\" uuid=\"28146f79-9fc7-49f9-b4a8-5866a7625683\" />");
[1678]214        writeLine(writer, "<ComponentMode numberPopupMenues=\"true\" />");
[1712]215        writeLine(writer, "<ApplicationStarter "
[1808]216            + "procTime=\"" + STARTUP_DELAY + "\" "
217            + "duration=\"" + STARTUP_DELAY + "\" "
[1712]218            + "name=\"Autoquest Replay\" "
219            + "class=\"" + classname + "\" "
220            + "initclass=\"" + initclass + "\" "
221            + "basepath=\"" + basepath + "\" "
222            + "classpathext=\"" + classpathext + "\" "
223            + "detectDuration=\"false\" "
224            + "captureparams=\"\" "
225            + "replayparams=\"\" "
[1715]226            + "uuid=\"" + UUID.randomUUID() + "\" />"
[1712]227        );
228        //@formatter:on
[1671]229    }
230
[1688]231    private void writeJacaretoEvents(BufferedWriter writer, Collection<List<Event>> sequences)
[1671]232        throws IOException
233    {
[1689]234        structure = new StructureNode("RootElement");
[1671]235        // reference the elements that we included in the header
[1689]236        structure.addRecordable(); // Calendar
237        structure.addRecordable(); // SystemInfo
238        structure.addRecordable(); // KeyboardState
239        structure.addRecordable(); // ComponentMode
240        structure.addRecordable(); // ApplicationStarter
[1671]241
242        for (List<Event> sequence : sequences) {
243            for (Iterator<Event> eventIter = sequence.iterator(); eventIter.hasNext();) {
244                Event event = eventIter.next();
245
[1685]246                if (event.getType() instanceof MouseButtonDown) {
[1808]247                    commitFocusEvent();
[1701]248                    lastKeySequenceEvent = null;
249
[1689]250                    lastMouseClickEvent = new StructureNode("MouseClick");
[1809]251                    lastMouseDownTarget = event.getTarget();
[1808]252                    writeMouseClickEvent(writer, event, EVENT_DURATION, 501);
[1684]253                }
[1685]254                else if (event.getType() instanceof MouseButtonUp) {
[1701]255                    lastKeySequenceEvent = null;
256
[1808]257                    writeMouseClickEvent(writer, event, EVENT_DURATION, 502);
[1685]258                }
[1808]259                else if (event.getType() instanceof MouseDoubleClick) {
260                    StructureNode multiClick = structure.add("MultipleMouseClick");
261
262                    // first click
263                    lastMouseClickEvent = multiClick.add("MouseClick");
264                    writeMouseClickEvent(writer, event, DOUBLE_CLICK_DURATION, 501);
265                    writeMouseClickEvent(writer, event, DOUBLE_CLICK_DURATION, 502);
266                    writeMouseClickEvent(writer, event, DOUBLE_CLICK_DURATION, 500);
267                    // second click
268                    lastMouseClickEvent = multiClick.add("MouseClick");
269                    writeMouseClickEvent(writer, event, DOUBLE_CLICK_DURATION, 501);
270                    writeMouseClickEvent(writer, event, DOUBLE_CLICK_DURATION, 502);
271                    writeMouseClickEvent(writer, event, DOUBLE_CLICK_DURATION, 500);
272
273                    lastMouseClickEvent = null;
274                }
[1685]275                else if (event.getType() instanceof MouseClick) {
[1809]276                    lastKeySequenceEvent = null;
277
[1808]278                    if (lastMouseClickEvent != null) {
[1809]279                        if (lastMouseDownTarget == event.getTarget()) {
280                            // this is the standard case:
281                            // mouseDown, mouseUp and mouseClick sequence
282                            // was triggered on this target
[1701]283
[1809]284                            writeMouseClickEvent(writer, event, EVENT_DURATION, 500);
285                            writeItemActionEvent(writer, event);
286
287                            if (lastFocusChangeEvent == null) {
288                                // write structure sequentially
289                                structure.children.add(lastMouseClickEvent);
290                                structure.children.add(lastItemActionEvent);
291                            }
292                            else {
293                                // with nested structure
294                                structure.children.add(lastItemActionEvent);
295                                lastItemActionEvent.children.add(0, lastFocusChangeEvent);
296                                lastFocusChangeEvent.children.add(0, lastMouseClickEvent);
297
298                                lastFocusChangeEvent = null;
299                                lastMouseClickEvent = null;
300                            }
[1808]301                        }
302                        else {
[1809]303                            // target of mouseDown and mouseClick are different
304                            // -> this is, for example, a click on a menu item
305                            // within a condensed sequence
306                            commitFocusEvent();
[1688]307
[1809]308                            // finish the last click on the old target
309                            writeMouseClickEvent(writer, event, EVENT_DURATION, 500);
310                            structure.children.add(lastMouseClickEvent);
311
312                            // and generate a new one
313                            generateFullClick(writer, event);
[1808]314                        }
[1688]315                    }
[1809]316                    else {
317                        // a target was clicked repeatedly:
318                        // the condensed sequence contains no mouseDowns or
319                        // mouseUps anymore
320                        // -> just generate another full click
321                        generateFullClick(writer, event);
322                    }
[1685]323                }
[1686]324                else if (event.getType() instanceof KeyboardFocusChange) {
[1701]325                    lastKeySequenceEvent = null;
326
[1688]327                    writeFocusChangeEvent(writer, event);
[1686]328                }
[1691]329                else if (event.getType() instanceof KeyPressed) {
[1808]330                    commitFocusEvent();
[1714]331
[1701]332                    if (lastKeySequenceEvent == null) {
333                        lastKeySequenceEvent = structure.add("KeySequence");
334                    }
335                    lastKeyTypedEvent = lastKeySequenceEvent.add("KeyTyped");
336                    writeKeyEvent(writer, event, 401);
[1691]337                }
338                else if (event.getType() instanceof KeyReleased) {
[1808]339                    commitFocusEvent();
[1714]340
[1701]341                    writeKeyEvent(writer, event, 402);
[1691]342                }
[1671]343            }
344        }
345    }
346
[1808]347    private void commitFocusEvent() {
348        if (lastFocusChangeEvent != null) {
349            structure.children.add(lastFocusChangeEvent);
350            lastFocusChangeEvent = null;
351        }
352    }
353
[1809]354    private void generateFullClick(BufferedWriter writer, Event event) throws IOException {
355        lastMouseClickEvent = new StructureNode("MouseClick");
356        lastMouseDownTarget = event.getTarget();
357
358        writeMouseClickEvent(writer, event, EVENT_DURATION, 501);
359        writeMouseClickEvent(writer, event, EVENT_DURATION, 502);
360        writeMouseClickEvent(writer, event, EVENT_DURATION, 500);
361        writeItemActionEvent(writer, event);
362
363        structure.children.add(lastMouseClickEvent);
364        structure.children.add(lastItemActionEvent);
365
366        lastMouseDownTarget = null;
367
[1714]368    }
369
[1688]370    private void writeJacaretoTail(BufferedWriter writer) throws IOException {
[1671]371        writeLine(writer, "</Record>");
372
373        // write the recording's structure
374        writeLine(writer, "<Structure>");
[1688]375        writer.write(structure.toString());
[1678]376        // close root element
[1671]377        writeLine(writer, "</Structure>");
378    }
379
[1712]380    private void writeJacaretoXML(Collection<List<Event>> sequences,
381                                  String filename,
382                                  String classpath,
383                                  String initclass,
384                                  String basepath,
385                                  String classpathext)
386    {
[1671]387        BufferedWriter writer = new BufferedWriter(openReplayFile(filename + ".xml"));
388
389        try {
[1712]390            writeJacaretoHead(writer, classpath, initclass, basepath, classpathext);
[1688]391            writeJacaretoEvents(writer, sequences);
392            writeJacaretoTail(writer);
[1678]393            writeLine(writer, "</JacaretoStructure>");
[1671]394
395            writer.flush();
396            writer.close();
397        }
398        catch (IOException e) {
399            Console.printerrln("Unable to write Jacareto replay file " + filename);
400        }
401    }
402
[1673]403    /**
404     * <p>
405     * Helper function that opens the replay file for writing.
406     * </p>
407     *
408     * @param filename
409     *            name and path of the replay file
410     * @param encoding
411     *            file encoding, empty string for platform default
412     * @return {@link OutputStreamWriter} that writes to the replay file
413     */
414    private OutputStreamWriter openReplayFile(String filename) {
415        File file = new File(filename);
416        boolean fileCreated;
417        try {
418            fileCreated = file.createNewFile();
419            if (!fileCreated) {
420                Console.traceln(Level.INFO, "Created logfile " + filename);
421            }
422            else {
423                Console.traceln(Level.INFO, "Overwrote existing logfile " + filename);
424            }
425        }
426        catch (IOException e) {
427            Console.printerrln("Unable to create file " + filename);
428            Console.logException(e);
429        }
430        OutputStreamWriter writer = null;
431        try {
432            writer = new OutputStreamWriter(new FileOutputStream(file));
433        }
434        catch (IOException e) {
435            Console.printerrln("Unable to open file for writing (read-only file):" + filename);
436            Console.logException(e);
437        }
438        return writer;
439    }
[1683]440
[1688]441    private void writeItemActionEvent(BufferedWriter writer, Event event) throws IOException {
[1685]442        JFCGUIElement target = (JFCGUIElement) event.getTarget();
443        MouseButtonInteraction info = (MouseButtonInteraction) event.getType();
444
[1684]445        //@formatter:off
446        writeLine(writer,
447            "<ItemEvent "
448            + "procTime=\"0\" "
449            + "duration=\"0\" "
450            + "source=\"" + target.getJacaretoHierarchy() + "\" "
451            + "class=\"" + target.getSpecification().getType() + "\" "
452            + "uuid=\"" + UUID.randomUUID() + "\" "
453            + "ID=\"701\" "
454            + "item=\"\" "
455            + "stateChange=\"1\" />"
456        );
457        writeLine(writer,
458            "<ActionEvent "
459            + "procTime=\"0\" "
460            + "duration=\"0\" "
461            + "source=\"" + target.getJacaretoHierarchy() + "\" "
462            + "class=\"" + target.getSpecification().getType() + "\" "
463            + "uuid=\"" + UUID.randomUUID() + "\" "
464            + "ID=\"1001\" "
465            + "command=" + target.getName() + " "
[1685]466            + "modifiers=\"" + getButtonModifier(info) + "\" />"
[1684]467        );
468        //@formatter:on
[1689]469        lastItemActionEvent = new StructureNode("ItemStateChange");
470        lastItemActionEvent.addRecordable();
471        lastItemActionEvent.addRecordable();
[1684]472    }
473
[1688]474    private void writeFocusChangeEvent(BufferedWriter writer, Event event) throws IOException {
[1686]475        KeyboardFocusChange info = (KeyboardFocusChange) event.getType();
476        JFCGUIElement target = (JFCGUIElement) event.getTarget();
477
478        if (currentFocus != null) {
[1689]479            lastFocusChangeEvent = new StructureNode("FocusChange");
[1688]480
[1686]481            // focus lost on old target
[1688]482            writeFocusEvent(writer, info, currentFocus, 1005);
[1686]483            // focus gained on new target
[1688]484            writeFocusEvent(writer, info, target, 1004);
[1686]485        }
486        else {
487            // TODO: it seems like Jacareto wants a window activation before
488            // the first focus event but that is not the case in autoquest,
489            // skip for now
490        }
491
492        currentFocus = target;
493    }
494
495    private void writeFocusEvent(BufferedWriter writer,
496                                 KeyboardFocusChange info,
497                                 JFCGUIElement target,
498                                 int jacId) throws IOException
499    {
500        //@formatter:off
501        writeLine(writer,
502            "<FocusEvent "
503            + "procTime=\"0\" "
504            + "duration=\"0\" "
505            + "source=\"" + target.getJacaretoHierarchy() + "\" "
506            + "class=\"" + target.getSpecification().getType() + "\" "
507            + "uuid=\"" + UUID.randomUUID() + "\" "
508            + "ID=\"" + jacId + "\" "
509            + "component=\"null\" "
510            + "root=\"" + target.getJacaretoRoot() + "\" "
511            + "xPos=\"0\" "
512            + "yPos=\"0\" "
513            + "width=\"0\" "
514            + "height=\"0\" "
515            + "isTemporary=\"false\" />"
516        );
517        //@formatter:on
[1689]518        lastFocusChangeEvent.addRecordable();
[1686]519    }
520
[1808]521    private void writeMouseClickEvent(BufferedWriter writer, Event event, int duration, int jacId)
[1688]522        throws IOException
[1683]523    {
[1685]524        MouseButtonInteraction info = (MouseButtonInteraction) event.getType();
525        JFCGUIElement target = (JFCGUIElement) event.getTarget();
526        int clickCount = event.getType() instanceof MouseDoubleClick ? 2 : 1;
527
[1683]528        // TODO: change procTime and duration to adequate values
529        //@formatter:off
530        writeLine(writer,
531            "<MouseEvent "
532            + "procTime=\"0\" "
[1808]533            + "duration=\"" + duration + "\" "
[1683]534            + "source=\"" + target.getJacaretoHierarchy() + "\" "
535            + "class=\"" + target.getSpecification().getType() + "\" "
536            + "uuid=\"" + UUID.randomUUID() + "\" "
537            + "ID=\"" + jacId + "\" "
538            + "component=\"null\" "
539            + "root=\"" + target.getJacaretoRoot() + "\" "
540            + "xPos=\"0\" "
541            + "yPos=\"0\" "
542            + "width=\"0\" "
543            + "height=\"0\" "
[1685]544            + "when=\"" + event.getTimestamp() + "\" "
[1683]545            + "isConsumed=\"false\">"
546        );
547        writeLine(writer,
548            "<MouseInfo "
[1686]549            + "xPosition=\"" + info.getX() + "\" "
550            + "yPosition=\"" + info.getY() + "\" "
[1683]551            + "rootX=\"0\" "
552            + "rootY=\"0\" "
[1685]553            + "clickCount=\"" + clickCount + "\" "
554            + "modifiers=\"" + getButtonModifier(info) + "\" "
[1683]555            + "isPopupTrigger=\"false\" />"
556        );
557        writeLine(writer, "</MouseEvent>");
558        //@formatter:on
559
[1689]560        lastMouseClickEvent.addRecordable();
[1683]561    }
[1685]562
563    private int getButtonModifier(MouseButtonInteraction info) {
564        switch (info.getButton())
565        {
566            case LEFT:
567                return 16;
568            case MIDDLE:
569                return 8;
570            case RIGHT:
571                return 4;
572            default:
573                // TODO: handle unknown mouse button
574                return -1;
575        }
576    }
[1691]577
578    private void writeKeyEvent(BufferedWriter writer, Event event, int jacId) throws IOException {
579        KeyInteraction info = (KeyInteraction) event.getType();
580        JFCGUIElement target = (JFCGUIElement) event.getTarget();
[1704]581        int keyCode = info.getKey().getVirtualKeyCode();
[1691]582
[1704]583        applyKeyModifier(info.getKey(), jacId == 401);
[1712]584
[1691]585        //@formatter:off
586        writeLine(writer,
587            "<KeyEvent "
588            + "procTime=\"0\" "
[1719]589            + "duration=\"" + EVENT_DURATION + "\" "
[1691]590            + "source=\"" + target.getJacaretoHierarchy() + "\" "
591            + "class=\"" + target.getSpecification().getType() + "\" "
592            + "uuid=\"" + UUID.randomUUID() + "\" "
593            + "ID=\"" + jacId + "\" "
594            + "component=\"null\" "
595            + "root=\"" + target.getJacaretoRoot() + "\" "
596            + "xPos=\"0\" "
597            + "yPos=\"0\" "
598            + "width=\"0\" "
599            + "height=\"0\" "
600            + "when=\"" + event.getTimestamp() + "\" "
601            + "isConsumed=\"false\">"
602        );
603        writeLine(writer,
604            "<KeyInfo "
[1704]605            + "keyCode=\"" + keyCode + "\" "
606            + "keyChar=\"" + getKeyChar(keyCode) + "\" "
607            + "modifiers=\"" + currentKeyModifiers + "\" />"
[1691]608        );
609       
610        writeLine(writer, "</KeyEvent>");
611       
[1701]612        lastKeyTypedEvent.addRecordable();
[1691]613    }
614   
[1701]615    private String getKeyChar (int keyCode) {
616        if (keyCode >= 32 && keyCode < 127) {
617            return String.valueOf((char)keyCode);
618        }
[1712]619        return "_NO_LEGAL_XML_CHAR";
[1691]620    }
[1704]621   
622    private void applyKeyModifier (VirtualKey key, boolean set) {
623        Integer modifier = modifiers.get(key);
624        if (modifier != null) {
625            if (set) {
626                currentKeyModifiers |= modifier;
627            }
628            else {
629                currentKeyModifiers &= ~modifier;
630            }
631        }
632    }
[1671]633}
Note: See TracBrowser for help on using the repository browser.