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

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

reintroduce mouse dragging (done right, this time)

  • Property svn:mime-type set to text/plain
File size: 30.0 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                    handleMouseDown(writer, event, "MouseClick");
270                }
271                else if (event.getType() instanceof MouseButtonUp) {
272                    handleMouseUp(writer, event);
273                }
274                else if (event.getType() instanceof MouseDoubleClick) {
275                    StructureNode multiClick = structure.add("MultipleMouseClick");
276
277                    // first click
278                    lastMouseClickEvent = multiClick.add("MouseClick");
279                    writeMouseClickEvent(writer, event, (JFCGUIElement) event.getTarget(),
280                                         DOUBLE_CLICK_DURATION, 501);
281                    writeMouseClickEvent(writer, event, (JFCGUIElement) event.getTarget(),
282                                         DOUBLE_CLICK_DURATION, 502);
283                    writeMouseClickEvent(writer, event, (JFCGUIElement) event.getTarget(),
284                                         DOUBLE_CLICK_DURATION, 500);
285                    // second click
286                    lastMouseClickEvent = multiClick.add("MouseClick");
287                    writeMouseClickEvent(writer, event, (JFCGUIElement) event.getTarget(),
288                                         DOUBLE_CLICK_DURATION, 501);
289                    writeMouseClickEvent(writer, event, (JFCGUIElement) event.getTarget(),
290                                         DOUBLE_CLICK_DURATION, 502);
291                    writeMouseClickEvent(writer, event, (JFCGUIElement) event.getTarget(),
292                                         DOUBLE_CLICK_DURATION, 500);
293
294                    lastMouseClickEvent = null;
295                }
296                else if (event.getType() instanceof MouseClick) {
297                    if (event.getTarget() instanceof JFCMenuButton) {
298                        // if a menu file was provided, use the improved event
299                        // generation
300                        if (menuList != null) {
301                            if (menuElements.isEmpty()) {
302                                // parse the menu structure
303                                GUIModel model = ((IGUIElement) event.getTarget()).getGUIModel();
304                                getMenuElements(model.getRootElements(), model);
305                            }
306
307                            Stack<JFCGUIElement> hierarchy =
308                                findMenuItemHierarchy((JFCGUIElement) event.getTarget());
309
310                            while (!hierarchy.empty()) {
311                                generateFullClick(writer, event, hierarchy.pop());
312                            }
313                            continue;
314                        }
315                    }
316
317                    lastKeySequenceEvent = null;
318
319                    if (lastMouseClickEvent != null) {
320                        if (lastMouseDownTarget == event.getTarget()) {
321                            // this is the standard case:
322                            // mouseDown, mouseUp and mouseClick sequence
323                            // was triggered on this target
324
325                            writeMouseClickEvent(writer, event, (JFCGUIElement) event.getTarget(),
326                                                 EVENT_DURATION, 500);
327                            writeItemActionEvent(writer, event);
328
329                            if (lastFocusChangeEvent == null) {
330                                // write structure sequentially
331                                structure.children.add(lastMouseClickEvent);
332                                structure.children.add(lastItemActionEvent);
333                            }
334                            else {
335                                // with nested structure
336                                structure.children.add(lastItemActionEvent);
337                                lastItemActionEvent.children.add(0, lastFocusChangeEvent);
338                                lastFocusChangeEvent.children.add(0, lastMouseClickEvent);
339
340                                lastFocusChangeEvent = null;
341                                lastMouseClickEvent = null;
342                            }
343                        }
344                        else {
345                            // target of mouseDown and mouseClick are different
346                            // -> this is, for example, a click on a menu item
347                            // within a condensed sequence
348                            commitFocusEvent();
349
350                            // finish the last click on the old target
351                            writeMouseClickEvent(writer, event, (JFCGUIElement) event.getTarget(),
352                                                 EVENT_DURATION, 500);
353                            structure.children.add(lastMouseClickEvent);
354
355                            // and generate a new one
356                            generateFullClick(writer, event, (JFCGUIElement) event.getTarget());
357                        }
358                    }
359                    else {
360                        // a target was clicked repeatedly:
361                        // the condensed sequence contains no mouseDowns or
362                        // mouseUps anymore
363                        // -> just generate another full click
364                        generateFullClick(writer, event, (JFCGUIElement) event.getTarget());
365                    }
366                }
367                else if (event.getType() instanceof KeyboardFocusChange) {
368                    lastKeySequenceEvent = null;
369
370                    writeFocusChangeEvent(writer, event);
371                }
372                else if (event.getType() instanceof MouseDragAndDrop) {
373                    handleMouseDragAndDrop(writer, event);
374                }
375                else if (event.getType() instanceof KeyPressed) {
376                    handleKeyPressed(writer, event);
377                }
378                else if (event.getType() instanceof KeyReleased) {
379                    handleKeyReleased(writer, event);
380                }
381                else if (event.getType() instanceof TextInput) {
382                    handleTextInput(writer, event);
383                }
384                else {
385                    Console.traceln(Level.WARNING, "No handler for event \"" + event +
386                        "\". Skipped.");
387                }
388            }
389        }
390    }
391
392    private void handleMouseDown(BufferedWriter writer, Event event, String structureName)
393        throws IOException
394    {
395        commitFocusEvent();
396        lastKeySequenceEvent = null;
397
398        lastMouseClickEvent = new StructureNode(structureName);
399        lastMouseDownTarget = event.getTarget();
400        writeMouseClickEvent(writer, event, (JFCGUIElement) event.getTarget(), EVENT_DURATION, 501);
401    }
402
403    private void handleMouseUp(BufferedWriter writer, Event event) throws IOException {
404        lastKeySequenceEvent = null;
405
406        writeMouseClickEvent(writer, event, (JFCGUIElement) event.getTarget(), EVENT_DURATION, 502);
407    }
408
409    private void handleMouseDragAndDrop(BufferedWriter writer, Event event) throws IOException {
410        commitFocusEvent();
411
412        MouseDragAndDrop dragEvent = (MouseDragAndDrop) event.getType();
413        lastMouseClickEvent = new StructureNode("MouseDrag");
414        lastMouseDownTarget = null;
415
416        writeMouseClickEvent(writer, event, (JFCGUIElement) event.getTarget(), EVENT_DURATION,
417                             dragEvent.getXStart(), dragEvent.getYStart(), 501);
418        writeMouseClickEvent(writer, event, (JFCGUIElement) event.getTarget(), EVENT_DURATION,
419                             dragEvent.getX(), dragEvent.getY(), 506);
420
421        structure.children.add(lastMouseClickEvent);
422    }
423
424    private void handleKeyPressed(BufferedWriter writer, Event event) throws IOException {
425        commitFocusEvent();
426
427        if (lastKeySequenceEvent == null) {
428            lastKeySequenceEvent = structure.add("KeySequence");
429        }
430        lastKeyTypedEvent = lastKeySequenceEvent.add("KeyTyped");
431
432        writeKeyEvent(writer, event, 401);
433    }
434
435    private void handleKeyReleased(BufferedWriter writer, Event event) throws IOException {
436        commitFocusEvent();
437
438        writeKeyEvent(writer, event, 402);
439    }
440
441    private void handleTextInput(BufferedWriter writer, Event event) throws IOException {
442        List<Event> textEvents = ((TextInput) event.getType()).getTextInputEvents();
443
444        // just split the text event into its key events again
445        for (Event textEvent : textEvents) {
446            if (textEvent.getType() instanceof KeyPressed) {
447                handleKeyPressed(writer, textEvent);
448            }
449            else if (textEvent.getType() instanceof KeyReleased) {
450                handleKeyReleased(writer, textEvent);
451            }
452        }
453    }
454
455    private void getMenuElements(List<IGUIElement> elements, GUIModel model) {
456        for (IGUIElement child : elements) {
457            if (child instanceof JFCMenuButton || child instanceof JFCMenu) {
458                menuElements.put(((JFCGUIElement) child).getName().replaceAll("^\"|\"$", ""),
459                                 (JFCGUIElement) child);
460            }
461            getMenuElements(model.getChildren(child), model);
462        }
463    }
464
465    private Stack<JFCGUIElement> findMenuItemHierarchy(JFCGUIElement item) {
466        Stack<JFCGUIElement> elements = new Stack<>();
467
468        // find line that contains this menu item name
469        int lineOfItem = -1;
470        for (int i = 0; i < menuList.size(); i++) {
471            String name = "\"" + menuList.get(i).trim().toLowerCase() + "\"";
472            if (name.equals(item.getName().trim().toLowerCase())) {
473                lineOfItem = i;
474            }
475        }
476
477        // now go backwards until the toplevel menu is found
478        int oldIndent = Integer.MAX_VALUE;
479        for (int j = lineOfItem; j >= 0; j--) {
480            String stripped = menuList.get(j).replaceFirst("^ *", "");
481            int indent = menuList.get(j).length() - stripped.length();
482
483            if (indent < oldIndent) {
484                // this is a parent submenu
485                elements.push(menuElements.get(stripped));
486                oldIndent = indent;
487            }
488        }
489
490        return elements;
491    }
492
493    private void commitFocusEvent() {
494        if (lastFocusChangeEvent != null) {
495            structure.children.add(lastFocusChangeEvent);
496            lastFocusChangeEvent = null;
497        }
498    }
499
500    private void generateFullClick(BufferedWriter writer, Event event, JFCGUIElement target)
501        throws IOException
502    {
503        lastMouseClickEvent = new StructureNode("MouseClick");
504        lastMouseDownTarget = event.getTarget();
505
506        writeMouseClickEvent(writer, event, target, EVENT_DURATION, 501);
507        writeMouseClickEvent(writer, event, target, EVENT_DURATION, 502);
508        writeMouseClickEvent(writer, event, target, EVENT_DURATION, 500);
509        writeItemActionEvent(writer, event);
510
511        structure.children.add(lastMouseClickEvent);
512        structure.children.add(lastItemActionEvent);
513
514        lastMouseDownTarget = null;
515    }
516
517    private void writeJacaretoTail(BufferedWriter writer) throws IOException {
518        writeLine(writer, "</Record>");
519
520        // write the recording's structure
521        writeLine(writer, "<Structure>");
522        writer.write(structure.toString());
523        // close root element
524        writeLine(writer, "</Structure>");
525    }
526
527    private void writeJacaretoXML(Collection<List<Event>> sequences,
528                                  String filename,
529                                  String classpath,
530                                  String basepath,
531                                  String classpathext)
532    {
533        BufferedWriter writer = new BufferedWriter(openReplayFile(filename + ".xml"));
534
535        try {
536            writeJacaretoHead(writer, classpath, basepath, classpathext);
537            writeJacaretoEvents(writer, sequences);
538            writeJacaretoTail(writer);
539            writeLine(writer, "</JacaretoStructure>");
540
541            writer.flush();
542            writer.close();
543        }
544        catch (IOException e) {
545            Console.printerrln("Unable to write Jacareto replay file " + filename);
546        }
547    }
548
549    /**
550     * <p>
551     * Helper function that opens the replay file for writing.
552     * </p>
553     *
554     * @param filename
555     *            name and path of the replay file
556     * @param encoding
557     *            file encoding, empty string for platform default
558     * @return {@link OutputStreamWriter} that writes to the replay file
559     */
560    private OutputStreamWriter openReplayFile(String filename) {
561        File file = new File(filename);
562        boolean fileCreated;
563        try {
564            fileCreated = file.createNewFile();
565            if (!fileCreated) {
566                Console.traceln(Level.INFO, "Created logfile " + filename);
567            }
568            else {
569                Console.traceln(Level.INFO, "Overwrote existing logfile " + filename);
570            }
571        }
572        catch (IOException e) {
573            Console.printerrln("Unable to create file " + filename);
574            Console.logException(e);
575        }
576        OutputStreamWriter writer = null;
577        try {
578            writer = new OutputStreamWriter(new FileOutputStream(file));
579        }
580        catch (IOException e) {
581            Console.printerrln("Unable to open file for writing (read-only file):" + filename);
582            Console.logException(e);
583        }
584        return writer;
585    }
586
587    private void writeItemActionEvent(BufferedWriter writer, Event event) throws IOException {
588        JFCGUIElement target = (JFCGUIElement) event.getTarget();
589        MouseButtonInteraction info = (MouseButtonInteraction) event.getType();
590
591        //@formatter:off
592        writeLine(writer,
593            "<ItemEvent "
594            + "procTime=\"0\" "
595            + "duration=\"0\" "
596            + "source=\"" + target.getJacaretoHierarchy() + "\" "
597            + "class=\"" + target.getSpecification().getType() + "\" "
598            + "uuid=\"" + UUID.randomUUID() + "\" "
599            + "ID=\"701\" "
600            + "item=\"\" "
601            + "stateChange=\"1\" />"
602        );
603        writeLine(writer,
604            "<ActionEvent "
605            + "procTime=\"0\" "
606            + "duration=\"0\" "
607            + "source=\"" + target.getJacaretoHierarchy() + "\" "
608            + "class=\"" + target.getSpecification().getType() + "\" "
609            + "uuid=\"" + UUID.randomUUID() + "\" "
610            + "ID=\"1001\" "
611            + "command=" + target.getName() + " "
612            + "modifiers=\"" + getButtonModifier(info) + "\" />"
613        );
614        //@formatter:on
615        lastItemActionEvent = new StructureNode("ItemStateChange");
616        lastItemActionEvent.addRecordable();
617        lastItemActionEvent.addRecordable();
618    }
619
620    private void writeFocusChangeEvent(BufferedWriter writer, Event event) throws IOException {
621        KeyboardFocusChange info = (KeyboardFocusChange) event.getType();
622        JFCGUIElement target = (JFCGUIElement) event.getTarget();
623
624        if (currentFocus != null) {
625            lastFocusChangeEvent = new StructureNode("FocusChange");
626
627            // focus lost on old target
628            writeFocusEvent(writer, info, currentFocus, 1005);
629            // focus gained on new target
630            writeFocusEvent(writer, info, target, 1004);
631        }
632        else {
633            // TODO: it seems like Jacareto wants a window activation before
634            // the first focus event but that is not the case in autoquest,
635            // skip for now
636        }
637
638        currentFocus = target;
639    }
640
641    private void writeFocusEvent(BufferedWriter writer,
642                                 KeyboardFocusChange info,
643                                 JFCGUIElement target,
644                                 int jacId) throws IOException
645    {
646        //@formatter:off
647        writeLine(writer,
648            "<FocusEvent "
649            + "procTime=\"0\" "
650            + "duration=\"0\" "
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            + "isTemporary=\"false\" />"
662        );
663        //@formatter:on
664        lastFocusChangeEvent.addRecordable();
665    }
666
667    private void writeMouseClickEvent(BufferedWriter writer,
668                                      Event event,
669                                      JFCGUIElement target,
670                                      int duration,
671                                      int jacId) throws IOException
672    {
673        MouseButtonInteraction info = (MouseButtonInteraction) event.getType();
674        writeMouseClickEvent(writer, event, target, duration, info.getX(), info.getY(), jacId);
675    }
676
677    private void writeMouseClickEvent(BufferedWriter writer,
678                                      Event event,
679                                      JFCGUIElement target,
680                                      int duration,
681                                      int x,
682                                      int y,
683                                      int jacId) throws IOException
684    {
685        MouseButtonInteraction info = (MouseButtonInteraction) event.getType();
686        int clickCount = event.getType() instanceof MouseDoubleClick ? 2 : 1;
687
688        //@formatter:off
689        writeLine(writer,
690            "<MouseEvent "
691            + "procTime=\"0\" "
692            + "duration=\"" + duration + "\" "
693            + "source=\"" + target.getJacaretoHierarchy() + "\" "
694            + "class=\"" + target.getSpecification().getType() + "\" "
695            + "uuid=\"" + UUID.randomUUID() + "\" "
696            + "ID=\"" + jacId + "\" "
697            + "component=\"null\" "
698            + "root=\"" + target.getJacaretoRoot() + "\" "
699            + "xPos=\"0\" "
700            + "yPos=\"0\" "
701            + "width=\"0\" "
702            + "height=\"0\" "
703            + "when=\"" + event.getTimestamp() + "\" "
704            + "isConsumed=\"false\">"
705        );
706        writeLine(writer,
707            "<MouseInfo "
708            + "xPosition=\"" + x + "\" "
709            + "yPosition=\"" + y + "\" "
710            + "rootX=\"0\" "
711            + "rootY=\"0\" "
712            + "clickCount=\"" + clickCount + "\" "
713            + "modifiers=\"" + getButtonModifier(info) + "\" "
714            + "isPopupTrigger=\"false\" />"
715        );
716        writeLine(writer, "</MouseEvent>");
717        //@formatter:on
718
719        lastMouseClickEvent.addRecordable();
720    }
721
722    private int getButtonModifier(MouseButtonInteraction info) {
723        switch (info.getButton())
724        {
725            case LEFT:
726                return 16;
727            case MIDDLE:
728                return 8;
729            case RIGHT:
730                return 4;
731            default:
732                // TODO: handle unknown mouse button
733                return -1;
734        }
735    }
736
737    private void writeKeyEvent(BufferedWriter writer, Event event, int jacId) throws IOException {
738        KeyInteraction info = (KeyInteraction) event.getType();
739        JFCGUIElement target = (JFCGUIElement) event.getTarget();
740        int keyCode = info.getKey().getVirtualKeyCode();
741
742        applyKeyModifier(info.getKey(), jacId == 401);
743
744        //@formatter:off
745        writeLine(writer,
746            "<KeyEvent "
747            + "procTime=\"0\" "
748            + "duration=\"" + EVENT_DURATION + "\" "
749            + "source=\"" + target.getJacaretoHierarchy() + "\" "
750            + "class=\"" + target.getSpecification().getType() + "\" "
751            + "uuid=\"" + UUID.randomUUID() + "\" "
752            + "ID=\"" + jacId + "\" "
753            + "component=\"null\" "
754            + "root=\"" + target.getJacaretoRoot() + "\" "
755            + "xPos=\"0\" "
756            + "yPos=\"0\" "
757            + "width=\"0\" "
758            + "height=\"0\" "
759            + "when=\"" + event.getTimestamp() + "\" "
760            + "isConsumed=\"false\">"
761        );
762        writeLine(writer,
763            "<KeyInfo "
764            + "keyCode=\"" + keyCode + "\" "
765            + "keyChar=\"" + getKeyChar(keyCode) + "\" "
766            + "modifiers=\"" + currentKeyModifiers + "\" />"
767        );
768       
769        writeLine(writer, "</KeyEvent>");
770       
771        lastKeyTypedEvent.addRecordable();
772    }
773   
774    private String getKeyChar (int keyCode) {
775        if (keyCode >= 32 && keyCode < 127) {
776            return String.valueOf((char)keyCode);
777        }
778        return "_NO_LEGAL_XML_CHAR";
779    }
780   
781    private void applyKeyModifier (VirtualKey key, boolean set) {
782        Integer modifier = modifiers.get(key);
783        if (modifier != null) {
784            if (set) {
785                currentKeyModifiers |= modifier;
786            }
787            else {
788                currentKeyModifiers &= ~modifier;
789            }
790        }
791    }
792}
Note: See TracBrowser for help on using the repository browser.