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

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

handle text input events by splitting them into separate key events again

  • Property svn:mime-type set to text/plain
File size: 28.2 KB
Line 
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
15package de.ugoe.cs.autoquest.plugin.jfc.commands;
16
17import java.io.BufferedWriter;
18import java.io.File;
19import java.io.FileOutputStream;
20import java.io.IOException;
21import java.io.OutputStreamWriter;
22import java.nio.charset.Charset;
23import java.nio.file.Files;
24import java.nio.file.Paths;
25import java.util.ArrayList;
26import java.util.Calendar;
27import java.util.Collection;
28import java.util.HashMap;
29import java.util.Iterator;
30import java.util.List;
31import java.util.Stack;
32import java.util.UUID;
33import java.util.logging.Level;
34
35import javax.swing.UIManager;
36
37import de.ugoe.cs.autoquest.CommandHelpers;
38import de.ugoe.cs.autoquest.SequenceInstanceOf;
39import de.ugoe.cs.util.console.Command;
40import de.ugoe.cs.autoquest.eventcore.Event;
41import de.ugoe.cs.autoquest.eventcore.IEventTarget;
42import de.ugoe.cs.autoquest.eventcore.gui.*;
43import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModel;
44import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
45import de.ugoe.cs.autoquest.keyboardmaps.VirtualKey;
46import de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCGUIElement;
47import de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCMenu;
48import de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCMenuButton;
49import de.ugoe.cs.util.console.Console;
50import de.ugoe.cs.util.console.GlobalDataContainer;
51
52// helper class for the tree like structure part within a Jacareto file
53class StructureNode {
54    public static int nextRef = 0;
55
56    public String content;
57    public ArrayList<StructureNode> children;
58
59    public StructureNode(String type) {
60        setContent(type);
61        children = new ArrayList<StructureNode>();
62    }
63
64    public StructureNode() {
65        content = "<Recordable ref=\"" + (nextRef++) + "\" />";
66        children = new ArrayList<StructureNode>();
67    }
68
69    public void setContent(String type) {
70        content = "<StructureElement class=\"jacareto.struct." + type + "\">";
71    }
72
73    public StructureNode add(String type) {
74        StructureNode node = new StructureNode(type);
75        children.add(node);
76        return node;
77    }
78
79    public void addRecordable() {
80        children.add(new StructureNode());
81    }
82
83    @Override
84    public String toString() {
85        String separator = System.getProperty("line.separator");
86        String result = content + separator;
87
88        for (StructureNode child : children) {
89            result += child.toString();
90        }
91
92        if (content.endsWith("/>")) {
93            return result;
94        }
95        return result + "</StructureElement>" + separator;
96    }
97}
98
99/**
100 * <p>
101 * Command to create a Jacareto xml replay file from stored sessions.
102 * </p>
103 *
104 * @author Daniel May
105 * @version 1.0
106 */
107public class CMDgenerateJacaretoReplay implements Command {
108    private static final int EVENT_DURATION = 150;
109    private static final int DOUBLE_CLICK_DURATION = 50;
110    private static final int STARTUP_DELAY = 10000;
111
112    private JFCGUIElement currentFocus;
113    private StructureNode structure;
114
115    private StructureNode lastKeySequenceEvent;
116    private StructureNode lastKeyTypedEvent;
117    private int currentKeyModifiers;
118
119    private HashMap<VirtualKey, Integer> modifiers;
120
121    private StructureNode lastMouseClickEvent;
122    private StructureNode lastFocusChangeEvent;
123    private StructureNode lastItemActionEvent;
124    private IEventTarget lastMouseDownTarget;
125    private HashMap<String, JFCGUIElement> menuElements;
126    private List<String> menuList;
127
128    /*
129     * (non-Javadoc)
130     *
131     * @see de.ugoe.cs.util.console.Command#help()
132     */
133    @Override
134    public String help() {
135        return "generateJacaretoReplay <filename> <sequences> <class> <basepath> <classpathext> {<menufile>}";
136    }
137
138    /*
139     * (non-Javadoc)
140     *
141     * @see de.ugoe.cs.util.console.Command#run(java.util.List)
142     */
143    @SuppressWarnings("unchecked")
144    @Override
145    public void run(List<Object> parameters) {
146        String filename;
147        String sequencesName;
148        String classpath;
149        String basepath;
150        String classpathext;
151        try {
152            filename = (String) parameters.get(0);
153            sequencesName = (String) parameters.get(1);
154            classpath = (String) parameters.get(2);
155            basepath = (String) parameters.get(3);
156            classpathext = (String) parameters.get(4);
157        }
158        catch (Exception e) {
159            throw new IllegalArgumentException();
160        }
161
162        if (parameters.size() > 5) {
163            try {
164                menuList =
165                    Files.readAllLines(Paths.get((String) parameters.get(5)),
166                                       Charset.defaultCharset());
167            }
168            catch (IOException e) {
169                Console.printerrln("Unable to open menu file");
170                Console.logException(e);
171            }
172        }
173
174        Collection<List<Event>> sequences = null;
175        Object dataObject = GlobalDataContainer.getInstance().getData(sequencesName);
176        if (dataObject == null) {
177            CommandHelpers.objectNotFoundMessage(sequencesName);
178            return;
179        }
180        if (!SequenceInstanceOf.isCollectionOfSequences(dataObject)) {
181            CommandHelpers.objectNotType(sequencesName, "Collection<List<Event<?>>>");
182            return;
183        }
184
185        sequences = (Collection<List<Event>>) dataObject;
186
187        menuElements = new HashMap<>();
188
189        // map which maps VirtualKeys back to awt key modifier codes
190        modifiers = new HashMap<>();
191        modifiers.put(VirtualKey.SHIFT, 1);
192        modifiers.put(VirtualKey.CONTROL, 2);
193        modifiers.put(VirtualKey.ALT, 8);
194        modifiers.put(VirtualKey.ALT_GRAPH, 32);
195        currentKeyModifiers = 0;
196
197        StructureNode.nextRef = 0;
198
199        writeJacaretoXML(sequences, filename, classpath, basepath, classpathext);
200    }
201
202    private void writeLine(BufferedWriter writer, String line) throws IOException {
203        writer.write(line);
204        writer.newLine();
205    }
206
207    private void writeJacaretoHead(BufferedWriter writer,
208                                   String classname,
209                                   String basepath,
210                                   String classpathext) throws IOException
211    {
212        Calendar now = Calendar.getInstance();
213
214        writeLine(writer, "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>");
215        writeLine(writer, "<JacaretoStructure>");
216        writeLine(writer, "<Record>");
217
218        //@formatter:off
219        writeLine(writer, "<Calendar "
220            + "procTime=\"0\" "
221            + "duration=\"0\" "
222            + "year=\"" + now.get(Calendar.YEAR) + "\" "
223            + "month=\"" + (now.get(Calendar.MONTH) + 1) + "\" "
224            + "date=\"" + now.get(Calendar.DAY_OF_MONTH) + "\" "
225            + "hour=\"" + now.get(Calendar.HOUR_OF_DAY) + "\" "
226            + "min=\"" + now.get(Calendar.MINUTE) + "\" "
227            + "sec=\"" + now.get(Calendar.SECOND) + "\" "
228            + "uuid=\"" + UUID.randomUUID() + "\" />"
229        );
230        writeLine(writer,
231                  "<SystemInfo procTime=\"0\" duration=\"0\" screenWidth=\"2646\" screenHeight=\"1024\" javaVersion=\"1.7.0_65\" "
232                  + "lookAndFeel=\"" + UIManager.getLookAndFeel().getClass().getName() + "\" "
233                  + "uuid=\"720f430f-52cf-4d8b-9fbe-58434f766efe\" />");
234        writeLine(writer,
235                  "<KeyboardState procTime=\"0\" duration=\"0\" isNumLockOn=\"false\" isScrollLockOn=\"false\" isCapsLockOn=\"false\" applyIsNumLockOn=\"true\" applyIsScrollLockOn=\"true\" applyIsCapsLockOn=\"true\" uuid=\"28146f79-9fc7-49f9-b4a8-5866a7625683\" />");
236        writeLine(writer, "<ComponentMode numberPopupMenues=\"true\" />");
237        writeLine(writer, "<ApplicationStarter "
238            + "procTime=\"" + STARTUP_DELAY + "\" "
239            + "duration=\"" + STARTUP_DELAY + "\" "
240            + "name=\"Autoquest Replay\" "
241            + "class=\"" + classname + "\" "
242            + "initclass=\"\" "
243            + "basepath=\"" + basepath + "\" "
244            + "classpathext=\"" + classpathext + "\" "
245            + "detectDuration=\"false\" "
246            + "captureparams=\"\" "
247            + "replayparams=\"\" "
248            + "uuid=\"" + UUID.randomUUID() + "\" />"
249        );
250        //@formatter:on
251    }
252
253    private void writeJacaretoEvents(BufferedWriter writer, Collection<List<Event>> sequences)
254        throws IOException
255    {
256        structure = new StructureNode("RootElement");
257        // reference the elements that we included in the header
258        structure.addRecordable(); // Calendar
259        structure.addRecordable(); // SystemInfo
260        structure.addRecordable(); // KeyboardState
261        structure.addRecordable(); // ComponentMode
262        structure.addRecordable(); // ApplicationStarter
263
264        for (List<Event> sequence : sequences) {
265            for (Iterator<Event> eventIter = sequence.iterator(); eventIter.hasNext();) {
266                Event event = eventIter.next();
267
268                if (event.getType() instanceof MouseButtonDown) {
269                    commitFocusEvent();
270                    lastKeySequenceEvent = null;
271
272                    lastMouseClickEvent = new StructureNode("MouseClick");
273                    lastMouseDownTarget = event.getTarget();
274                    writeMouseClickEvent(writer, event, (JFCGUIElement) event.getTarget(),
275                                         EVENT_DURATION, 501);
276                }
277                else if (event.getType() instanceof MouseButtonUp) {
278                    lastKeySequenceEvent = null;
279
280                    writeMouseClickEvent(writer, event, (JFCGUIElement) event.getTarget(),
281                                         EVENT_DURATION, 502);
282                }
283                else if (event.getType() instanceof MouseDoubleClick) {
284                    StructureNode multiClick = structure.add("MultipleMouseClick");
285
286                    // first click
287                    lastMouseClickEvent = multiClick.add("MouseClick");
288                    writeMouseClickEvent(writer, event, (JFCGUIElement) event.getTarget(),
289                                         DOUBLE_CLICK_DURATION, 501);
290                    writeMouseClickEvent(writer, event, (JFCGUIElement) event.getTarget(),
291                                         DOUBLE_CLICK_DURATION, 502);
292                    writeMouseClickEvent(writer, event, (JFCGUIElement) event.getTarget(),
293                                         DOUBLE_CLICK_DURATION, 500);
294                    // second click
295                    lastMouseClickEvent = multiClick.add("MouseClick");
296                    writeMouseClickEvent(writer, event, (JFCGUIElement) event.getTarget(),
297                                         DOUBLE_CLICK_DURATION, 501);
298                    writeMouseClickEvent(writer, event, (JFCGUIElement) event.getTarget(),
299                                         DOUBLE_CLICK_DURATION, 502);
300                    writeMouseClickEvent(writer, event, (JFCGUIElement) event.getTarget(),
301                                         DOUBLE_CLICK_DURATION, 500);
302
303                    lastMouseClickEvent = null;
304                }
305                else if (event.getType() instanceof MouseClick) {
306                    if (event.getTarget() instanceof JFCMenuButton) {
307                        // if a menu file was provided, use the improved event
308                        // generation
309                        if (menuList != null) {
310                            if (menuElements.isEmpty()) {
311                                // parse the menu structure
312                                GUIModel model = ((IGUIElement) event.getTarget()).getGUIModel();
313                                getMenuElements(model.getRootElements(), model);
314                            }
315
316                            Stack<JFCGUIElement> hierarchy =
317                                findMenuItemHierarchy((JFCGUIElement) event.getTarget());
318
319                            while (!hierarchy.empty()) {
320                                generateFullClick(writer, event, hierarchy.pop());
321                            }
322                            continue;
323                        }
324                    }
325
326                    lastKeySequenceEvent = null;
327
328                    if (lastMouseClickEvent != null) {
329                        if (lastMouseDownTarget == event.getTarget()) {
330                            // this is the standard case:
331                            // mouseDown, mouseUp and mouseClick sequence
332                            // was triggered on this target
333
334                            writeMouseClickEvent(writer, event, (JFCGUIElement) event.getTarget(),
335                                                 EVENT_DURATION, 500);
336                            writeItemActionEvent(writer, event);
337
338                            if (lastFocusChangeEvent == null) {
339                                // write structure sequentially
340                                structure.children.add(lastMouseClickEvent);
341                                structure.children.add(lastItemActionEvent);
342                            }
343                            else {
344                                // with nested structure
345                                structure.children.add(lastItemActionEvent);
346                                lastItemActionEvent.children.add(0, lastFocusChangeEvent);
347                                lastFocusChangeEvent.children.add(0, lastMouseClickEvent);
348
349                                lastFocusChangeEvent = null;
350                                lastMouseClickEvent = null;
351                            }
352                        }
353                        else {
354                            // target of mouseDown and mouseClick are different
355                            // -> this is, for example, a click on a menu item
356                            // within a condensed sequence
357                            commitFocusEvent();
358
359                            // finish the last click on the old target
360                            writeMouseClickEvent(writer, event, (JFCGUIElement) event.getTarget(),
361                                                 EVENT_DURATION, 500);
362                            structure.children.add(lastMouseClickEvent);
363
364                            // and generate a new one
365                            generateFullClick(writer, event, (JFCGUIElement) event.getTarget());
366                        }
367                    }
368                    else {
369                        // a target was clicked repeatedly:
370                        // the condensed sequence contains no mouseDowns or
371                        // mouseUps anymore
372                        // -> just generate another full click
373                        generateFullClick(writer, event, (JFCGUIElement) event.getTarget());
374                    }
375                }
376                else if (event.getType() instanceof KeyboardFocusChange) {
377                    lastKeySequenceEvent = null;
378
379                    writeFocusChangeEvent(writer, event);
380                }
381                else if (event.getType() instanceof KeyPressed) {
382                    handleKeyPressed(writer, event);
383                }
384                else if (event.getType() instanceof KeyReleased) {
385                    handleKeyReleased(writer, event);
386                }
387                else if (event.getType() instanceof TextInput) {
388                    handleTextInput(writer, event);
389                }
390            }
391        }
392    }
393
394    private void handleKeyPressed(BufferedWriter writer, Event event) throws IOException {
395        commitFocusEvent();
396
397        if (lastKeySequenceEvent == null) {
398            lastKeySequenceEvent = structure.add("KeySequence");
399        }
400        lastKeyTypedEvent = lastKeySequenceEvent.add("KeyTyped");
401
402        writeKeyEvent(writer, event, 401);
403    }
404
405    private void handleKeyReleased(BufferedWriter writer, Event event) throws IOException {
406        commitFocusEvent();
407
408        writeKeyEvent(writer, event, 402);
409    }
410
411    private void handleTextInput(BufferedWriter writer, Event event) throws IOException {
412        List<Event> textEvents = ((TextInput) event.getType()).getTextInputEvents();
413
414        // just split the text event into its key events again
415        for (Event textEvent : textEvents) {
416            if (textEvent.getType() instanceof KeyPressed) {
417                handleKeyPressed(writer, textEvent);
418            }
419            else if (textEvent.getType() instanceof KeyReleased) {
420                handleKeyReleased(writer, textEvent);
421            }
422        }
423    }
424
425    private void getMenuElements(List<IGUIElement> elements, GUIModel model) {
426        for (IGUIElement child : elements) {
427            if (child instanceof JFCMenuButton || child instanceof JFCMenu) {
428                menuElements.put(((JFCGUIElement) child).getName().replaceAll("^\"|\"$", ""),
429                                 (JFCGUIElement) child);
430            }
431            getMenuElements(model.getChildren(child), model);
432        }
433    }
434
435    private Stack<JFCGUIElement> findMenuItemHierarchy(JFCGUIElement item) {
436        Stack<JFCGUIElement> elements = new Stack<>();
437
438        // find line that contains this menu item name
439        int lineOfItem = -1;
440        for (int i = 0; i < menuList.size(); i++) {
441            String name = "\"" + menuList.get(i).trim().toLowerCase() + "\"";
442            if (name.equals(item.getName().trim().toLowerCase())) {
443                lineOfItem = i;
444            }
445        }
446
447        // now go backwards until the toplevel menu is found
448        int oldIndent = Integer.MAX_VALUE;
449        for (int j = lineOfItem; j >= 0; j--) {
450            String stripped = menuList.get(j).replaceFirst("^ *", "");
451            int indent = menuList.get(j).length() - stripped.length();
452
453            if (indent < oldIndent) {
454                // this is a parent submenu
455                elements.push(menuElements.get(stripped));
456                oldIndent = indent;
457            }
458        }
459
460        return elements;
461    }
462
463    private void commitFocusEvent() {
464        if (lastFocusChangeEvent != null) {
465            structure.children.add(lastFocusChangeEvent);
466            lastFocusChangeEvent = null;
467        }
468    }
469
470    private void generateFullClick(BufferedWriter writer, Event event, JFCGUIElement target)
471        throws IOException
472    {
473        lastMouseClickEvent = new StructureNode("MouseClick");
474        lastMouseDownTarget = event.getTarget();
475
476        writeMouseClickEvent(writer, event, target, EVENT_DURATION, 501);
477        writeMouseClickEvent(writer, event, target, EVENT_DURATION, 502);
478        writeMouseClickEvent(writer, event, target, EVENT_DURATION, 500);
479        writeItemActionEvent(writer, event);
480
481        structure.children.add(lastMouseClickEvent);
482        structure.children.add(lastItemActionEvent);
483
484        lastMouseDownTarget = null;
485    }
486
487    private void writeJacaretoTail(BufferedWriter writer) throws IOException {
488        writeLine(writer, "</Record>");
489
490        // write the recording's structure
491        writeLine(writer, "<Structure>");
492        writer.write(structure.toString());
493        // close root element
494        writeLine(writer, "</Structure>");
495    }
496
497    private void writeJacaretoXML(Collection<List<Event>> sequences,
498                                  String filename,
499                                  String classpath,
500                                  String basepath,
501                                  String classpathext)
502    {
503        BufferedWriter writer = new BufferedWriter(openReplayFile(filename + ".xml"));
504
505        try {
506            writeJacaretoHead(writer, classpath, basepath, classpathext);
507            writeJacaretoEvents(writer, sequences);
508            writeJacaretoTail(writer);
509            writeLine(writer, "</JacaretoStructure>");
510
511            writer.flush();
512            writer.close();
513        }
514        catch (IOException e) {
515            Console.printerrln("Unable to write Jacareto replay file " + filename);
516        }
517    }
518
519    /**
520     * <p>
521     * Helper function that opens the replay file for writing.
522     * </p>
523     *
524     * @param filename
525     *            name and path of the replay file
526     * @param encoding
527     *            file encoding, empty string for platform default
528     * @return {@link OutputStreamWriter} that writes to the replay file
529     */
530    private OutputStreamWriter openReplayFile(String filename) {
531        File file = new File(filename);
532        boolean fileCreated;
533        try {
534            fileCreated = file.createNewFile();
535            if (!fileCreated) {
536                Console.traceln(Level.INFO, "Created logfile " + filename);
537            }
538            else {
539                Console.traceln(Level.INFO, "Overwrote existing logfile " + filename);
540            }
541        }
542        catch (IOException e) {
543            Console.printerrln("Unable to create file " + filename);
544            Console.logException(e);
545        }
546        OutputStreamWriter writer = null;
547        try {
548            writer = new OutputStreamWriter(new FileOutputStream(file));
549        }
550        catch (IOException e) {
551            Console.printerrln("Unable to open file for writing (read-only file):" + filename);
552            Console.logException(e);
553        }
554        return writer;
555    }
556
557    private void writeItemActionEvent(BufferedWriter writer, Event event) throws IOException {
558        JFCGUIElement target = (JFCGUIElement) event.getTarget();
559        MouseButtonInteraction info = (MouseButtonInteraction) event.getType();
560
561        //@formatter:off
562        writeLine(writer,
563            "<ItemEvent "
564            + "procTime=\"0\" "
565            + "duration=\"0\" "
566            + "source=\"" + target.getJacaretoHierarchy() + "\" "
567            + "class=\"" + target.getSpecification().getType() + "\" "
568            + "uuid=\"" + UUID.randomUUID() + "\" "
569            + "ID=\"701\" "
570            + "item=\"\" "
571            + "stateChange=\"1\" />"
572        );
573        writeLine(writer,
574            "<ActionEvent "
575            + "procTime=\"0\" "
576            + "duration=\"0\" "
577            + "source=\"" + target.getJacaretoHierarchy() + "\" "
578            + "class=\"" + target.getSpecification().getType() + "\" "
579            + "uuid=\"" + UUID.randomUUID() + "\" "
580            + "ID=\"1001\" "
581            + "command=" + target.getName() + " "
582            + "modifiers=\"" + getButtonModifier(info) + "\" />"
583        );
584        //@formatter:on
585        lastItemActionEvent = new StructureNode("ItemStateChange");
586        lastItemActionEvent.addRecordable();
587        lastItemActionEvent.addRecordable();
588    }
589
590    private void writeFocusChangeEvent(BufferedWriter writer, Event event) throws IOException {
591        KeyboardFocusChange info = (KeyboardFocusChange) event.getType();
592        JFCGUIElement target = (JFCGUIElement) event.getTarget();
593
594        if (currentFocus != null) {
595            lastFocusChangeEvent = new StructureNode("FocusChange");
596
597            // focus lost on old target
598            writeFocusEvent(writer, info, currentFocus, 1005);
599            // focus gained on new target
600            writeFocusEvent(writer, info, target, 1004);
601        }
602        else {
603            // TODO: it seems like Jacareto wants a window activation before
604            // the first focus event but that is not the case in autoquest,
605            // skip for now
606        }
607
608        currentFocus = target;
609    }
610
611    private void writeFocusEvent(BufferedWriter writer,
612                                 KeyboardFocusChange info,
613                                 JFCGUIElement target,
614                                 int jacId) throws IOException
615    {
616        //@formatter:off
617        writeLine(writer,
618            "<FocusEvent "
619            + "procTime=\"0\" "
620            + "duration=\"0\" "
621            + "source=\"" + target.getJacaretoHierarchy() + "\" "
622            + "class=\"" + target.getSpecification().getType() + "\" "
623            + "uuid=\"" + UUID.randomUUID() + "\" "
624            + "ID=\"" + jacId + "\" "
625            + "component=\"null\" "
626            + "root=\"" + target.getJacaretoRoot() + "\" "
627            + "xPos=\"0\" "
628            + "yPos=\"0\" "
629            + "width=\"0\" "
630            + "height=\"0\" "
631            + "isTemporary=\"false\" />"
632        );
633        //@formatter:on
634        lastFocusChangeEvent.addRecordable();
635    }
636
637    private void writeMouseClickEvent(BufferedWriter writer,
638                                      Event event,
639                                      JFCGUIElement target,
640                                      int duration,
641                                      int jacId) throws IOException
642    {
643        MouseButtonInteraction info = (MouseButtonInteraction) event.getType();
644        int clickCount = event.getType() instanceof MouseDoubleClick ? 2 : 1;
645
646        //@formatter:off
647        writeLine(writer,
648            "<MouseEvent "
649            + "procTime=\"0\" "
650            + "duration=\"" + duration + "\" "
651            + "source=\"" + target.getJacaretoHierarchy() + "\" "
652            + "class=\"" + target.getSpecification().getType() + "\" "
653            + "uuid=\"" + UUID.randomUUID() + "\" "
654            + "ID=\"" + jacId + "\" "
655            + "component=\"null\" "
656            + "root=\"" + target.getJacaretoRoot() + "\" "
657            + "xPos=\"0\" "
658            + "yPos=\"0\" "
659            + "width=\"0\" "
660            + "height=\"0\" "
661            + "when=\"" + event.getTimestamp() + "\" "
662            + "isConsumed=\"false\">"
663        );
664        writeLine(writer,
665            "<MouseInfo "
666            + "xPosition=\"" + info.getX() + "\" "
667            + "yPosition=\"" + info.getY() + "\" "
668            + "rootX=\"0\" "
669            + "rootY=\"0\" "
670            + "clickCount=\"" + clickCount + "\" "
671            + "modifiers=\"" + getButtonModifier(info) + "\" "
672            + "isPopupTrigger=\"false\" />"
673        );
674        writeLine(writer, "</MouseEvent>");
675        //@formatter:on
676
677        lastMouseClickEvent.addRecordable();
678    }
679
680    private int getButtonModifier(MouseButtonInteraction info) {
681        switch (info.getButton())
682        {
683            case LEFT:
684                return 16;
685            case MIDDLE:
686                return 8;
687            case RIGHT:
688                return 4;
689            default:
690                // TODO: handle unknown mouse button
691                return -1;
692        }
693    }
694
695    private void writeKeyEvent(BufferedWriter writer, Event event, int jacId) throws IOException {
696        KeyInteraction info = (KeyInteraction) event.getType();
697        JFCGUIElement target = (JFCGUIElement) event.getTarget();
698        int keyCode = info.getKey().getVirtualKeyCode();
699
700        applyKeyModifier(info.getKey(), jacId == 401);
701
702        //@formatter:off
703        writeLine(writer,
704            "<KeyEvent "
705            + "procTime=\"0\" "
706            + "duration=\"" + EVENT_DURATION + "\" "
707            + "source=\"" + target.getJacaretoHierarchy() + "\" "
708            + "class=\"" + target.getSpecification().getType() + "\" "
709            + "uuid=\"" + UUID.randomUUID() + "\" "
710            + "ID=\"" + jacId + "\" "
711            + "component=\"null\" "
712            + "root=\"" + target.getJacaretoRoot() + "\" "
713            + "xPos=\"0\" "
714            + "yPos=\"0\" "
715            + "width=\"0\" "
716            + "height=\"0\" "
717            + "when=\"" + event.getTimestamp() + "\" "
718            + "isConsumed=\"false\">"
719        );
720        writeLine(writer,
721            "<KeyInfo "
722            + "keyCode=\"" + keyCode + "\" "
723            + "keyChar=\"" + getKeyChar(keyCode) + "\" "
724            + "modifiers=\"" + currentKeyModifiers + "\" />"
725        );
726       
727        writeLine(writer, "</KeyEvent>");
728       
729        lastKeyTypedEvent.addRecordable();
730    }
731   
732    private String getKeyChar (int keyCode) {
733        if (keyCode >= 32 && keyCode < 127) {
734            return String.valueOf((char)keyCode);
735        }
736        return "_NO_LEGAL_XML_CHAR";
737    }
738   
739    private void applyKeyModifier (VirtualKey key, boolean set) {
740        Integer modifier = modifiers.get(key);
741        if (modifier != null) {
742            if (set) {
743                currentKeyModifiers |= modifier;
744            }
745            else {
746                currentKeyModifiers &= ~modifier;
747            }
748        }
749    }
750}
Note: See TracBrowser for help on using the repository browser.