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
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;
[1825]22import java.nio.charset.Charset;
23import java.nio.file.Files;
24import java.nio.file.Paths;
[1671]25import java.util.ArrayList;
[1715]26import java.util.Calendar;
[1671]27import java.util.Collection;
[1704]28import java.util.HashMap;
[1671]29import java.util.Iterator;
30import java.util.List;
[1825]31import java.util.Stack;
[1678]32import java.util.UUID;
[1673]33import java.util.logging.Level;
[1671]34
[1836]35import javax.swing.UIManager;
36
[1671]37import de.ugoe.cs.autoquest.CommandHelpers;
38import de.ugoe.cs.autoquest.SequenceInstanceOf;
[1673]39import de.ugoe.cs.util.console.Command;
[1671]40import de.ugoe.cs.autoquest.eventcore.Event;
[1809]41import de.ugoe.cs.autoquest.eventcore.IEventTarget;
[1685]42import de.ugoe.cs.autoquest.eventcore.gui.*;
[1825]43import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModel;
44import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
[1691]45import de.ugoe.cs.autoquest.keyboardmaps.VirtualKey;
[1673]46import de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCGUIElement;
[1825]47import de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCMenu;
48import de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCMenuButton;
[1671]49import de.ugoe.cs.util.console.Console;
50import de.ugoe.cs.util.console.GlobalDataContainer;
51
[1688]52// helper class for the tree like structure part within a Jacareto file
53class StructureNode {
[1713]54    public static int nextRef = 0;
[1689]55
[1688]56    public String content;
57    public ArrayList<StructureNode> children;
58
[1689]59    public StructureNode(String type) {
[1714]60        setContent(type);
[1689]61        children = new ArrayList<StructureNode>();
[1688]62    }
63
[1689]64    public StructureNode() {
65        content = "<Recordable ref=\"" + (nextRef++) + "\" />";
66        children = new ArrayList<StructureNode>();
67    }
68
[1714]69    public void setContent(String type) {
70        content = "<StructureElement class=\"jacareto.struct." + type + "\">";
71    }
72
[1689]73    public StructureNode add(String type) {
74        StructureNode node = new StructureNode(type);
[1688]75        children.add(node);
76        return node;
77    }
78
[1689]79    public void addRecordable() {
80        children.add(new StructureNode());
81    }
82
[1688]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
[1689]92        if (content.endsWith("/>")) {
[1688]93            return result;
94        }
95        return result + "</StructureElement>" + separator;
96    }
97}
98
[1671]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 */
[1673]107public class CMDgenerateJacaretoReplay implements Command {
[1719]108    private static final int EVENT_DURATION = 150;
[1808]109    private static final int DOUBLE_CLICK_DURATION = 50;
110    private static final int STARTUP_DELAY = 10000;
[1719]111
[1686]112    private JFCGUIElement currentFocus;
[1688]113    private StructureNode structure;
[1704]114
[1701]115    private StructureNode lastKeySequenceEvent;
116    private StructureNode lastKeyTypedEvent;
[1704]117    private int currentKeyModifiers;
118
119    private HashMap<VirtualKey, Integer> modifiers;
[1712]120
[1688]121    private StructureNode lastMouseClickEvent;
122    private StructureNode lastFocusChangeEvent;
123    private StructureNode lastItemActionEvent;
[1809]124    private IEventTarget lastMouseDownTarget;
[1825]125    private HashMap<String, JFCGUIElement> menuElements;
126    private List<String> menuList;
[1688]127
[1671]128    /*
129     * (non-Javadoc)
130     *
131     * @see de.ugoe.cs.util.console.Command#help()
132     */
133    @Override
134    public String help() {
[1825]135        return "generateJacaretoReplay <filename> <sequences> <class> <basepath> <classpathext> {<menufile>}";
[1671]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;
[1712]148        String classpath;
149        String basepath;
150        String classpathext;
[1671]151        try {
152            filename = (String) parameters.get(0);
153            sequencesName = (String) parameters.get(1);
[1712]154            classpath = (String) parameters.get(2);
155            basepath = (String) parameters.get(3);
156            classpathext = (String) parameters.get(4);
[1671]157        }
158        catch (Exception e) {
159            throw new IllegalArgumentException();
160        }
161
[1712]162        if (parameters.size() > 5) {
[1825]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            }
[1712]172        }
173
[1671]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
[1825]187        menuElements = new HashMap<>();
188
[1704]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;
[1712]196
[1713]197        StructureNode.nextRef = 0;
198
[1825]199        writeJacaretoXML(sequences, filename, classpath, basepath, classpathext);
[1671]200    }
201
202    private void writeLine(BufferedWriter writer, String line) throws IOException {
203        writer.write(line);
204        writer.newLine();
205    }
206
[1712]207    private void writeJacaretoHead(BufferedWriter writer,
208                                   String classname,
209                                   String basepath,
210                                   String classpathext) throws IOException
211    {
[1715]212        Calendar now = Calendar.getInstance();
213
[1671]214        writeLine(writer, "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>");
215        writeLine(writer, "<JacaretoStructure>");
216        writeLine(writer, "<Record>");
217
[1715]218        //@formatter:off
[1716]219        writeLine(writer, "<Calendar "
220            + "procTime=\"0\" "
[1715]221            + "duration=\"0\" "
222            + "year=\"" + now.get(Calendar.YEAR) + "\" "
[1716]223            + "month=\"" + (now.get(Calendar.MONTH) + 1) + "\" "
[1715]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        );
[1671]230        writeLine(writer,
[1836]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\" />");
[1671]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\" />");
[1678]236        writeLine(writer, "<ComponentMode numberPopupMenues=\"true\" />");
[1712]237        writeLine(writer, "<ApplicationStarter "
[1808]238            + "procTime=\"" + STARTUP_DELAY + "\" "
239            + "duration=\"" + STARTUP_DELAY + "\" "
[1712]240            + "name=\"Autoquest Replay\" "
241            + "class=\"" + classname + "\" "
[1825]242            + "initclass=\"\" "
[1712]243            + "basepath=\"" + basepath + "\" "
244            + "classpathext=\"" + classpathext + "\" "
245            + "detectDuration=\"false\" "
246            + "captureparams=\"\" "
247            + "replayparams=\"\" "
[1715]248            + "uuid=\"" + UUID.randomUUID() + "\" />"
[1712]249        );
250        //@formatter:on
[1671]251    }
252
[1688]253    private void writeJacaretoEvents(BufferedWriter writer, Collection<List<Event>> sequences)
[1671]254        throws IOException
255    {
[1689]256        structure = new StructureNode("RootElement");
[1671]257        // reference the elements that we included in the header
[1689]258        structure.addRecordable(); // Calendar
259        structure.addRecordable(); // SystemInfo
260        structure.addRecordable(); // KeyboardState
261        structure.addRecordable(); // ComponentMode
262        structure.addRecordable(); // ApplicationStarter
[1671]263
264        for (List<Event> sequence : sequences) {
265            for (Iterator<Event> eventIter = sequence.iterator(); eventIter.hasNext();) {
266                Event event = eventIter.next();
267
[1685]268                if (event.getType() instanceof MouseButtonDown) {
[1808]269                    commitFocusEvent();
[1701]270                    lastKeySequenceEvent = null;
271
[1689]272                    lastMouseClickEvent = new StructureNode("MouseClick");
[1809]273                    lastMouseDownTarget = event.getTarget();
[1825]274                    writeMouseClickEvent(writer, event, (JFCGUIElement) event.getTarget(),
275                                         EVENT_DURATION, 501);
[1684]276                }
[1685]277                else if (event.getType() instanceof MouseButtonUp) {
[1701]278                    lastKeySequenceEvent = null;
279
[1825]280                    writeMouseClickEvent(writer, event, (JFCGUIElement) event.getTarget(),
281                                         EVENT_DURATION, 502);
[1685]282                }
[1808]283                else if (event.getType() instanceof MouseDoubleClick) {
284                    StructureNode multiClick = structure.add("MultipleMouseClick");
285
286                    // first click
287                    lastMouseClickEvent = multiClick.add("MouseClick");
[1825]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);
[1808]294                    // second click
295                    lastMouseClickEvent = multiClick.add("MouseClick");
[1825]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);
[1808]302
303                    lastMouseClickEvent = null;
304                }
[1685]305                else if (event.getType() instanceof MouseClick) {
[1836]306                    if (event.getTarget() instanceof JFCMenuButton) {
307                        // if a menu file was provided, use the improved event
308                        // generation
309                        if (menuList != null) {
[1825]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
[1809]326                    lastKeySequenceEvent = null;
327
[1808]328                    if (lastMouseClickEvent != null) {
[1809]329                        if (lastMouseDownTarget == event.getTarget()) {
330                            // this is the standard case:
331                            // mouseDown, mouseUp and mouseClick sequence
332                            // was triggered on this target
[1701]333
[1825]334                            writeMouseClickEvent(writer, event, (JFCGUIElement) event.getTarget(),
335                                                 EVENT_DURATION, 500);
[1809]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                            }
[1808]352                        }
353                        else {
[1809]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();
[1688]358
[1809]359                            // finish the last click on the old target
[1825]360                            writeMouseClickEvent(writer, event, (JFCGUIElement) event.getTarget(),
361                                                 EVENT_DURATION, 500);
[1809]362                            structure.children.add(lastMouseClickEvent);
363
364                            // and generate a new one
[1825]365                            generateFullClick(writer, event, (JFCGUIElement) event.getTarget());
[1808]366                        }
[1688]367                    }
[1809]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
[1825]373                        generateFullClick(writer, event, (JFCGUIElement) event.getTarget());
[1809]374                    }
[1685]375                }
[1686]376                else if (event.getType() instanceof KeyboardFocusChange) {
[1701]377                    lastKeySequenceEvent = null;
378
[1688]379                    writeFocusChangeEvent(writer, event);
[1686]380                }
[1691]381                else if (event.getType() instanceof KeyPressed) {
[1836]382                    handleKeyPressed(writer, event);
[1691]383                }
384                else if (event.getType() instanceof KeyReleased) {
[1836]385                    handleKeyReleased(writer, event);
[1691]386                }
[1836]387                else if (event.getType() instanceof TextInput) {
388                    handleTextInput(writer, event);
389                }
[1671]390            }
391        }
392    }
393
[1836]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
[1825]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
[1834]455                elements.push(menuElements.get(stripped));
[1825]456                oldIndent = indent;
457            }
458        }
459
460        return elements;
461    }
462
[1808]463    private void commitFocusEvent() {
464        if (lastFocusChangeEvent != null) {
465            structure.children.add(lastFocusChangeEvent);
466            lastFocusChangeEvent = null;
467        }
468    }
469
[1825]470    private void generateFullClick(BufferedWriter writer, Event event, JFCGUIElement target)
471        throws IOException
472    {
[1809]473        lastMouseClickEvent = new StructureNode("MouseClick");
474        lastMouseDownTarget = event.getTarget();
475
[1825]476        writeMouseClickEvent(writer, event, target, EVENT_DURATION, 501);
477        writeMouseClickEvent(writer, event, target, EVENT_DURATION, 502);
478        writeMouseClickEvent(writer, event, target, EVENT_DURATION, 500);
[1809]479        writeItemActionEvent(writer, event);
480
481        structure.children.add(lastMouseClickEvent);
482        structure.children.add(lastItemActionEvent);
483
484        lastMouseDownTarget = null;
[1714]485    }
486
[1688]487    private void writeJacaretoTail(BufferedWriter writer) throws IOException {
[1671]488        writeLine(writer, "</Record>");
489
490        // write the recording's structure
491        writeLine(writer, "<Structure>");
[1688]492        writer.write(structure.toString());
[1678]493        // close root element
[1671]494        writeLine(writer, "</Structure>");
495    }
496
[1712]497    private void writeJacaretoXML(Collection<List<Event>> sequences,
498                                  String filename,
499                                  String classpath,
500                                  String basepath,
501                                  String classpathext)
502    {
[1671]503        BufferedWriter writer = new BufferedWriter(openReplayFile(filename + ".xml"));
504
505        try {
[1825]506            writeJacaretoHead(writer, classpath, basepath, classpathext);
[1688]507            writeJacaretoEvents(writer, sequences);
508            writeJacaretoTail(writer);
[1678]509            writeLine(writer, "</JacaretoStructure>");
[1671]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
[1673]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    }
[1683]556
[1688]557    private void writeItemActionEvent(BufferedWriter writer, Event event) throws IOException {
[1685]558        JFCGUIElement target = (JFCGUIElement) event.getTarget();
559        MouseButtonInteraction info = (MouseButtonInteraction) event.getType();
560
[1684]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() + " "
[1685]582            + "modifiers=\"" + getButtonModifier(info) + "\" />"
[1684]583        );
584        //@formatter:on
[1689]585        lastItemActionEvent = new StructureNode("ItemStateChange");
586        lastItemActionEvent.addRecordable();
587        lastItemActionEvent.addRecordable();
[1684]588    }
589
[1688]590    private void writeFocusChangeEvent(BufferedWriter writer, Event event) throws IOException {
[1686]591        KeyboardFocusChange info = (KeyboardFocusChange) event.getType();
592        JFCGUIElement target = (JFCGUIElement) event.getTarget();
593
594        if (currentFocus != null) {
[1689]595            lastFocusChangeEvent = new StructureNode("FocusChange");
[1688]596
[1686]597            // focus lost on old target
[1688]598            writeFocusEvent(writer, info, currentFocus, 1005);
[1686]599            // focus gained on new target
[1688]600            writeFocusEvent(writer, info, target, 1004);
[1686]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
[1689]634        lastFocusChangeEvent.addRecordable();
[1686]635    }
636
[1825]637    private void writeMouseClickEvent(BufferedWriter writer,
638                                      Event event,
639                                      JFCGUIElement target,
640                                      int duration,
641                                      int jacId) throws IOException
[1683]642    {
[1685]643        MouseButtonInteraction info = (MouseButtonInteraction) event.getType();
644        int clickCount = event.getType() instanceof MouseDoubleClick ? 2 : 1;
645
[1683]646        //@formatter:off
647        writeLine(writer,
648            "<MouseEvent "
649            + "procTime=\"0\" "
[1808]650            + "duration=\"" + duration + "\" "
[1683]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\" "
[1685]661            + "when=\"" + event.getTimestamp() + "\" "
[1683]662            + "isConsumed=\"false\">"
663        );
664        writeLine(writer,
665            "<MouseInfo "
[1686]666            + "xPosition=\"" + info.getX() + "\" "
667            + "yPosition=\"" + info.getY() + "\" "
[1683]668            + "rootX=\"0\" "
669            + "rootY=\"0\" "
[1685]670            + "clickCount=\"" + clickCount + "\" "
671            + "modifiers=\"" + getButtonModifier(info) + "\" "
[1683]672            + "isPopupTrigger=\"false\" />"
673        );
674        writeLine(writer, "</MouseEvent>");
675        //@formatter:on
676
[1689]677        lastMouseClickEvent.addRecordable();
[1683]678    }
[1685]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    }
[1691]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();
[1704]698        int keyCode = info.getKey().getVirtualKeyCode();
[1691]699
[1704]700        applyKeyModifier(info.getKey(), jacId == 401);
[1712]701
[1691]702        //@formatter:off
703        writeLine(writer,
704            "<KeyEvent "
705            + "procTime=\"0\" "
[1719]706            + "duration=\"" + EVENT_DURATION + "\" "
[1691]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 "
[1704]722            + "keyCode=\"" + keyCode + "\" "
723            + "keyChar=\"" + getKeyChar(keyCode) + "\" "
724            + "modifiers=\"" + currentKeyModifiers + "\" />"
[1691]725        );
726       
727        writeLine(writer, "</KeyEvent>");
728       
[1701]729        lastKeyTypedEvent.addRecordable();
[1691]730    }
731   
[1701]732    private String getKeyChar (int keyCode) {
733        if (keyCode >= 32 && keyCode < 127) {
734            return String.valueOf((char)keyCode);
735        }
[1712]736        return "_NO_LEGAL_XML_CHAR";
[1691]737    }
[1704]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    }
[1671]750}
Note: See TracBrowser for help on using the repository browser.