// Copyright 2012 Georg-August-Universität Göttingen, Germany // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package de.ugoe.cs.autoquest.eventcore.gui; import java.util.LinkedList; import java.util.List; import de.ugoe.cs.autoquest.eventcore.Event; import de.ugoe.cs.autoquest.eventcore.IEventTarget; /** *

* This class condenses mouse clicks, i.e. it reduces a sequence of mouse button down, mouse button * up and possibly a subsequent mouse click with the same button on the same event target and the * same coordinates to a single mouse click with that button on that target at the coordinates. * The mouse button down and mouse button up events are discarded. For this, it iterates the * provided sequence and identifies any match of the named event sequence pattern. This match is * condensed to the mouse click event. *

* * @version 1.0 * @author Patrick Harms */ public class MouseClickCondenser { /** *

* This method performs the work described in the description of the class. A new list is * instantiated and returned. This list is filled with the events provided by the sequence being * the parameter of the method except for mouse button down and mouse button up events which are * followed by a mouse click event with the same button on the same target. *

* * @param sequence * the event sequence to condense the mouse clicks in * * @return the resulting sequence, in which mouse clicks are condensed to single mouse click * events */ public List condenseMouseClicks(List sequence) { List resultingSequence = new LinkedList(); int index = 0; boolean mouseClickHandled; while (index < sequence.size()) { mouseClickHandled = false; if ((index + 1) < sequence.size()) { Event mbDown = sequence.get(index); Event mbUp = sequence.get(index + 1); if (((index + 2) < sequence.size()) && mouseClickSequenceFound(mbDown, mbUp, sequence.get(index + 2))) { // skip the mouse button down and mouse button up event and add the mouse click index += 2; resultingSequence.add(sequence.get(index)); index++; mouseClickHandled = true; } else if (mouseClickSequenceFound(mbDown, mbUp)) { // replace the mouse button down and mouse button up event with a generated // mouse click index += 2; resultingSequence.add(createClick(mbDown, mbUp)); mouseClickHandled = true; } else if (mouseDragAndDropSequenceFound(mbDown, mbUp)) { // replace the mouse button down and mouse button up event with a generated // mouse drag and drop index += 2; resultingSequence.add(createDragAndDrop(mbDown, mbUp)); mouseClickHandled = true; } else if (mouseDoubleClickSequenceFound(mbDown, mbUp)) { // replace the two mouse click events with a generated mouse double click index += 2; resultingSequence.add(createDoubleClick(mbDown, mbUp)); mouseClickHandled = true; } } if (!mouseClickHandled) { resultingSequence.add(sequence.get(index)); index++; } if (resultingSequence.size() > 1) { // check for double clicks int resultingSequenceIndex = resultingSequence.size() - 1; Event click1 = resultingSequence.get(resultingSequenceIndex - 1); Event click2 = resultingSequence.get(resultingSequenceIndex); if (mouseDoubleClickSequenceFound(click1, click2)) { resultingSequence.remove(resultingSequenceIndex); resultingSequence.remove(resultingSequenceIndex - 1); resultingSequence.add(createDoubleClick(click1, click2)); } } } return resultingSequence; } /** * */ private boolean mouseClickSequenceFound(Event mouseButtonDown, Event mouseButtonUp, Event mouseClick) { if (!mouseClickSequenceFound(mouseButtonDown, mouseButtonUp)) { return false; } // check the third node for validity if (!(mouseClick.getType() instanceof MouseClick)) { return false; } if (!targetsEqual(mouseButtonDown, mouseClick)) { return false; } if (!buttonsEqual(mouseButtonDown, mouseClick)) { return false; } if (!coordinatesEqual(mouseButtonDown, mouseClick)) { return false; } return true; } /** * */ private boolean mouseClickSequenceFound(Event mouseButtonDown, Event mouseButtonUp) { // check the first in a row of three for validity if (!(mouseButtonDown.getType() instanceof MouseButtonDown)) { return false; } // check the second node for validity if (!(mouseButtonUp.getType() instanceof MouseButtonUp)) { return false; } if (!targetsEqual(mouseButtonDown, mouseButtonUp)) { return false; } if (!buttonsEqual(mouseButtonDown, mouseButtonUp)) { return false; } if (!coordinatesEqual(mouseButtonDown, mouseButtonUp)) { return false; } return true; } /** * */ private boolean mouseDragAndDropSequenceFound(Event mouseButtonDown, Event mouseButtonUp) { // check the first in a row of three for validity if (!(mouseButtonDown.getType() instanceof MouseButtonDown)) { return false; } // check the second node for validity if (!(mouseButtonUp.getType() instanceof MouseButtonUp)) { return false; } if (!targetsEqual(mouseButtonDown, mouseButtonUp)) { return false; } MouseButtonInteraction.Button button = ((MouseButtonDown) mouseButtonDown.getType()).getButton(); if (MouseButtonInteraction.Button.LEFT != button) { return false; } if (!buttonsEqual(mouseButtonDown, mouseButtonUp)) { return false; } if (coordinatesEqual(mouseButtonDown, mouseButtonUp)) { return false; } return true; } /** * */ private boolean mouseDoubleClickSequenceFound(Event click1, Event click2) { // check the first in a row of three for validity if (!(click1.getType() instanceof MouseClick)) { return false; } if (((MouseClick) click1.getType()).getButton() != MouseButtonInteraction.Button.LEFT) { return false; } // check the second node for validity if (!(click2.getType() instanceof MouseClick)) { return false; } // use 500 milliseconds as timestamp difference as this is more or less similar to default // values in Microsoft Windows if (!timestampDifferenceSmallerThan(click1, click2, 500)) { return false; } if (!targetsEqual(click1, click2)) { return false; } if (!buttonsEqual(click1, click2)) { return false; } if (!coordinatesEqual(click1, click2)) { return false; } return true; } /** * */ private Event createClick(Event mouseButtonDown, Event mouseButtonUp) { MouseButtonInteraction.Button button = ((MouseButtonDown) mouseButtonDown.getType()).getButton(); int x = ((MouseButtonDown) mouseButtonDown.getType()).getX(); int y = ((MouseButtonDown) mouseButtonDown.getType()).getY(); Event click = new Event(new MouseClick(button, x, y), mouseButtonDown.getTarget()); click.setTimestamp(mouseButtonDown.getTimestamp()); return click; } /** * */ private Event createDoubleClick(Event click1, Event click2) { MouseButtonInteraction.Button button = ((MouseClick) click1.getType()).getButton(); int x = ((MouseClick) click1.getType()).getX(); int y = ((MouseClick) click1.getType()).getY(); Event doubleClick = new Event(new MouseDoubleClick(button, x, y), click1.getTarget()); doubleClick.setTimestamp(click1.getTimestamp()); return doubleClick; } /** * */ private Event createDragAndDrop(Event mouseButtonDown, Event mouseButtonUp) { int xStart = ((MouseButtonDown) mouseButtonDown.getType()).getX(); int yStart = ((MouseButtonDown) mouseButtonDown.getType()).getY(); int xEnd = ((MouseButtonUp) mouseButtonUp.getType()).getX(); int yEnd = ((MouseButtonUp) mouseButtonUp.getType()).getY(); Event dragAndDrop = new Event (new MouseDragAndDrop(xStart, yStart, xEnd, yEnd), mouseButtonDown.getTarget()); dragAndDrop.setTimestamp(mouseButtonDown.getTimestamp()); return dragAndDrop; } /** * */ private boolean targetsEqual(Event event1, Event event2) { IEventTarget target1 = event1.getTarget(); IEventTarget target2 = event2.getTarget(); return target1 == null ? target2 == null : target1.equals(target2); } /** * */ private boolean buttonsEqual(Event event1, Event event2) { MouseButtonInteraction.Button button1 = (event1.getType() instanceof MouseButtonInteraction) ? ((MouseButtonInteraction) event1.getType()).getButton() : null; MouseButtonInteraction.Button button2 = (event2.getType() instanceof MouseButtonInteraction) ? ((MouseButtonInteraction) event2.getType()).getButton() : null; return button1 == null ? button2 == null : button1.equals(button2); } /** * */ private boolean timestampDifferenceSmallerThan(Event event1, Event event2, long difference) { long timestamp1 = event1.getTimestamp(); if (timestamp1 < 0) { return false; } long timestamp2 = event2.getTimestamp(); if (timestamp2 < 0) { return false; } return (Math.abs((timestamp2 - timestamp1))) < difference; } /** * */ private boolean coordinatesEqual(Event event1, Event event2) { int x1 = (event1.getType() instanceof MouseButtonInteraction) ? ((MouseButtonInteraction) event1.getType()).getX() : -1; int x2 = (event2.getType() instanceof MouseButtonInteraction) ? ((MouseButtonInteraction) event2.getType()).getX() : -1; // allow a deviation of one pixel to identify it as click anyway if ((x1 == -1) || (x2 == -1) || (x2 < (x1 - 1)) || ((x1 + 1) < x2)) { return false; } int y1 = (event1.getType() instanceof MouseButtonInteraction) ? ((MouseButtonInteraction) event1.getType()).getY() : -1; int y2 = (event2.getType() instanceof MouseButtonInteraction) ? ((MouseButtonInteraction) event2.getType()).getY() : -1; return (y1 != -1) && (y2 != -1) && ((y1 - 1) < y2) && (y2 < (y1 + 1)); } }