Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/GUIEventTaskComparisonRule.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/GUIEventTaskComparisonRule.java	(revision 1113)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/GUIEventTaskComparisonRule.java	(revision 1125)
@@ -17,7 +17,15 @@
 import de.ugoe.cs.autoquest.eventcore.IEventTarget;
 import de.ugoe.cs.autoquest.eventcore.gui.IInteraction;
+import de.ugoe.cs.autoquest.eventcore.gui.KeyInteraction;
+import de.ugoe.cs.autoquest.eventcore.gui.KeyPressed;
+import de.ugoe.cs.autoquest.eventcore.gui.KeyReleased;
+import de.ugoe.cs.autoquest.eventcore.gui.KeyTyped;
+import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonDown;
+import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonInteraction;
+import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonUp;
 import de.ugoe.cs.autoquest.eventcore.gui.MouseClick;
 import de.ugoe.cs.autoquest.eventcore.gui.MouseDoubleClick;
 import de.ugoe.cs.autoquest.eventcore.gui.MouseDragAndDrop;
+import de.ugoe.cs.autoquest.eventcore.gui.Scroll;
 import de.ugoe.cs.autoquest.eventcore.gui.TextInput;
 import de.ugoe.cs.autoquest.eventcore.gui.ValueSelection;
@@ -27,4 +35,5 @@
 import de.ugoe.cs.autoquest.eventcore.guimodel.IImage;
 import de.ugoe.cs.autoquest.eventcore.guimodel.IListBox;
+import de.ugoe.cs.autoquest.eventcore.guimodel.IMenu;
 import de.ugoe.cs.autoquest.eventcore.guimodel.IMenuButton;
 import de.ugoe.cs.autoquest.eventcore.guimodel.IRadioButton;
@@ -51,28 +60,60 @@
 public class GUIEventTaskComparisonRule implements NodeComparisonRule {
     
-    /*
-     * (non-Javadoc)
-     * 
-     * @see de.ugoe.cs.tasktree.nodeequality.NodeEqualityRule#apply(TaskTreeNode, TaskTreeNode)
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#isApplicable(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public boolean isApplicable(ITaskTreeNode node1, ITaskTreeNode node2) {
+        return
+            ((node1 instanceof IEventTask) && (node2 instanceof IEventTask) &&
+            (((IEventTask) node1).getEventType() instanceof IInteraction) &&
+            (((IEventTask) node2).getEventType() instanceof IInteraction));
+    }
+
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#areLexicallyEqual(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public boolean areLexicallyEqual(ITaskTreeNode node1, ITaskTreeNode node2) {
+        NodeEquality equality = getEquality(node1, node2, NodeEquality.LEXICALLY_EQUAL);
+        return (equality != null) && (equality.isAtLeast(NodeEquality.LEXICALLY_EQUAL));
+    }
+
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#areSyntacticallyEqual(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public boolean areSyntacticallyEqual(ITaskTreeNode node1, ITaskTreeNode node2) {
+        NodeEquality equality = getEquality(node1, node2, NodeEquality.SYNTACTICALLY_EQUAL);
+        return (equality != null) && (equality.isAtLeast(NodeEquality.SYNTACTICALLY_EQUAL));
+    }
+
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#areSemanticallyEqual(ITaskTreeNode, ITaskTreeNode)
+     */
+    @Override
+    public boolean areSemanticallyEqual(ITaskTreeNode node1, ITaskTreeNode node2) {
+        NodeEquality equality = getEquality(node1, node2, NodeEquality.SEMANTICALLY_EQUAL);
+        return (equality != null) && (equality.isAtLeast(NodeEquality.SEMANTICALLY_EQUAL));
+    }
+
+    /* (non-Javadoc)
+     * @see NodeComparisonRule#compare(ITaskTreeNode, ITaskTreeNode)
      */
     @Override
     public NodeEquality compare(ITaskTreeNode node1, ITaskTreeNode node2) {
-        if ((!(node1 instanceof IEventTask)) || (!(node2 instanceof IEventTask))) {
-            return null;
-        }
-        
+        return getEquality(node1, node2, null);
+    }
+
+    /**
+     * 
+     */
+    private NodeEquality getEquality(ITaskTreeNode node1,
+                                     ITaskTreeNode node2,
+                                     NodeEquality  requiredEqualityLevel)
+    {
         IEventTask task1 = (IEventTask) node1;
         IEventTask task2 = (IEventTask) node2;
         
-        if ((!(task1.getEventType() instanceof IInteraction)) ||
-            (!(task2.getEventType() instanceof IInteraction)))
-        {
-            return null;
-        }
-
-        if (node1 == node2) {
-            return NodeEquality.IDENTICAL;
-        }
-
         if (!task1.getEventTarget().equals(task2.getEventTarget())) {
             return NodeEquality.UNEQUAL;
@@ -82,5 +123,6 @@
         IInteraction interaction2 = (IInteraction) task2.getEventType();
         
-        return compareInteractions(interaction1, interaction2, task1.getEventTarget());
+        return compareInteractions
+            (interaction1, interaction2, task1.getEventTarget(), requiredEqualityLevel);
     }
 
@@ -105,11 +147,35 @@
     private NodeEquality compareInteractions(IInteraction interaction1,
                                              IInteraction interaction2,
-                                             IEventTarget eventTarget)
-    {
+                                             IEventTarget eventTarget,
+                                             NodeEquality equalityLevel)
+    {
+        NodeEquality level = equalityLevel;
+        
+        if (level == null) {
+            level = NodeEquality.LEXICALLY_EQUAL;
+        }
+        
         if (interaction1 == interaction2) {
             return NodeEquality.LEXICALLY_EQUAL;
         }
+        else if ((interaction1 instanceof KeyInteraction) &&
+                 (interaction2 instanceof KeyInteraction))
+        {
+            return compareKeyInteractions
+                ((KeyInteraction) interaction1, (KeyInteraction) interaction2, level);
+        }
+        else if ((interaction1 instanceof MouseButtonInteraction) &&
+                 (interaction2 instanceof MouseButtonInteraction))
+        {
+            return compareMouseButtonInteractions
+                ((MouseButtonInteraction) interaction1, (MouseButtonInteraction) interaction2,
+                 eventTarget, level);
+        }
+        else if ((interaction1 instanceof Scroll) && (interaction2 instanceof Scroll)) {
+            return compareScrolls((Scroll) interaction1, (Scroll) interaction2, level);
+        }
         else if ((interaction1 instanceof TextInput) && (interaction2 instanceof TextInput)) {
-            return compareTextInputs((TextInput) interaction1, (TextInput) interaction2);
+            return compareTextInputs
+                ((TextInput) interaction1, (TextInput) interaction2, level);
         }
         else if ((interaction1 instanceof ValueSelection) &&
@@ -117,23 +183,5 @@
         {
             return compareValueSelections
-                ((ValueSelection<?>) interaction1, (ValueSelection<?>) interaction2);
-        }
-        else if ((interaction1 instanceof MouseClick) &&
-                 (interaction2 instanceof MouseClick))
-        {
-            return compareMouseClicks
-                ((MouseClick) interaction1, (MouseClick) interaction2, eventTarget);
-        }
-        else if ((interaction1 instanceof MouseDoubleClick) &&
-                 (interaction2 instanceof MouseDoubleClick))
-        {
-            return compareMouseDoubleClicks
-                ((MouseDoubleClick) interaction1, (MouseDoubleClick) interaction2, eventTarget);
-        }
-        else if ((interaction1 instanceof MouseDragAndDrop) &&
-                 (interaction2 instanceof MouseDragAndDrop))
-        {
-            return compareMouseDragAndDrops
-                ((MouseDragAndDrop) interaction1, (MouseDragAndDrop) interaction2);
+                ((ValueSelection<?>) interaction1, (ValueSelection<?>) interaction2, level);
         }
         else if (interaction1.equals(interaction2)) {
@@ -143,4 +191,175 @@
             return NodeEquality.UNEQUAL;
         }
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param interaction1
+     * @param interaction2
+     * @param eventTarget
+     * @param level
+     * @return
+     */
+    private NodeEquality compareKeyInteractions(KeyInteraction interaction1,
+                                                KeyInteraction interaction2,
+                                                NodeEquality   equalityLevel)
+    {
+        if (((interaction1 instanceof KeyPressed) && (interaction2 instanceof KeyPressed)) ||
+            ((interaction1 instanceof KeyReleased) && (interaction2 instanceof KeyReleased)) ||
+            ((interaction1 instanceof KeyTyped) && (interaction2 instanceof KeyTyped)))
+        {
+            if ((equalityLevel.isAtLeast(NodeEquality.SYNTACTICALLY_EQUAL)) &&
+                (interaction1.getKey() == interaction2.getKey()))
+            {
+                return NodeEquality.LEXICALLY_EQUAL;
+            }
+            else {
+                return NodeEquality.SEMANTICALLY_EQUAL;
+            }
+        }
+        
+        return NodeEquality.UNEQUAL;
+    }
+    
+    /**
+     * <p>
+     * compares two mouse drag and drops. If both drag and drops have the same start and end
+     * coordinates, they are lexically equal. Otherwise, they are semantically equal.
+     * </p>
+     *
+     * @param interaction1 the first mouse drag and drop to compare
+     * @param interaction2 the second mouse drag and drop to compare
+     * 
+     * @return as described
+     */
+    private NodeEquality compareMouseDragAndDrops(MouseDragAndDrop interaction1,
+                                                  MouseDragAndDrop interaction2,
+                                                  NodeEquality     equalityLevel)
+    {
+        if (interaction1.getButton() != interaction2.getButton()) {
+            return NodeEquality.UNEQUAL;
+        }
+        
+        if (equalityLevel.isAtLeast(NodeEquality.SYNTACTICALLY_EQUAL)) {
+            int x1 = interaction1.getX();
+            int x1Start = interaction1.getXStart();
+            int x2 = interaction2.getX();
+            int x2Start = interaction2.getXStart();
+            int y1 = interaction1.getY();
+            int y1Start = interaction1.getYStart();
+            int y2 = interaction2.getY();
+            int y2Start = interaction2.getYStart();
+        
+            if ((x1Start == x2Start) && (x1 == x2) && (y1Start == y2Start) && (y1 == y2)) {
+                return NodeEquality.LEXICALLY_EQUAL;
+            }
+        }
+        
+        return NodeEquality.SEMANTICALLY_EQUAL;
+    }
+
+    /**
+     * <p>
+     * compares two mouse button interactions such as clicks, mouse button down, or double clicks.
+     * If both interactions have the same coordinates, they are lexically equal. Otherwise, they
+     * are semantically equal. Mouse clicks for which the coordinates make no lexical difference
+     * (see {@link #clickCoordinatesMakeLexicalDifference(IEventTarget)}) are treated as
+     * lexically equal.
+     * </p>
+     *
+     * @param interaction1 the first mouse button interaction to compare
+     * @param interaction2 the second mouse button interaction to compare
+     * @param eventTarget  the event target on which the interactions happened (used within
+     *                     special comparisons like mouse clicks on buttons, where the coordinates
+     *                     can be ignored)
+     * 
+     * @return as described
+     */
+    private NodeEquality compareMouseButtonInteractions(MouseButtonInteraction interaction1,
+                                                        MouseButtonInteraction interaction2,
+                                                        IEventTarget           eventTarget,
+                                                        NodeEquality           equalityLevel)
+    {
+        boolean coordinatesMatch = true;
+        
+        if ((interaction1 instanceof MouseDragAndDrop) &&
+            (interaction2 instanceof MouseDragAndDrop))
+        {
+            return compareMouseDragAndDrops
+                ((MouseDragAndDrop) interaction1, (MouseDragAndDrop) interaction2, equalityLevel);
+        }
+        else if (interaction1.getButton() != interaction2.getButton()) {
+            return NodeEquality.UNEQUAL;
+        }
+        else if (equalityLevel.isAtLeast(NodeEquality.SYNTACTICALLY_EQUAL) &&
+                 clickCoordinatesMakeLexicalDifference(eventTarget))
+        {
+            int x1 = interaction1.getX();
+            int x2 = interaction2.getX();
+            int y1 = interaction1.getY();
+            int y2 = interaction2.getY();
+
+            if ((x1 != x2) || (y1 != y2)) {
+                coordinatesMatch = false;
+            }
+        }
+        
+        // up to now, they can be equal. Now check the types. Do it as last action as these
+        // checks take the most time and should, therefore, only be done latest
+        if (((interaction1 instanceof MouseClick) && (interaction2 instanceof MouseClick)) ||
+            ((interaction1 instanceof MouseDoubleClick) &&
+             (interaction2 instanceof MouseDoubleClick)) ||
+            ((interaction1 instanceof MouseButtonDown) &&
+             (interaction2 instanceof MouseButtonDown)) ||
+            ((interaction1 instanceof MouseButtonUp) &&
+             (interaction2 instanceof MouseButtonUp)))
+        {
+            if (coordinatesMatch) {
+                return NodeEquality.LEXICALLY_EQUAL;
+            }
+            else {
+                return NodeEquality.SEMANTICALLY_EQUAL;
+            }
+        }
+        
+        return NodeEquality.UNEQUAL;
+    }
+
+    /**
+     * <p>
+     * compares two mouse button interactions such as clicks, mouse button down, or double clicks.
+     * If both interactions have the same coordinates, they are lexically equal. Otherwise, they
+     * are semantically equal. Mouse clicks for which the coordinates make no lexical difference
+     * (see {@link #clickCoordinatesMakeLexicalDifference(IEventTarget)}) are treated as
+     * lexically equal.
+     * </p>
+     *
+     * @param interaction1 the first mouse button interaction to compare
+     * @param interaction2 the second mouse button interaction to compare
+     * @param eventTarget  the event target on which the interactions happened (used within
+     *                     special comparisons like mouse clicks on buttons, where the coordinates
+     *                     can be ignored)
+     * 
+     * @return as described
+     */
+    private NodeEquality compareScrolls(Scroll       interaction1,
+                                        Scroll       interaction2,
+                                        NodeEquality equalityLevel)
+    {
+        if (equalityLevel.isAtLeast(NodeEquality.SYNTACTICALLY_EQUAL)) {
+            int x1 = interaction1.getXPosition();
+            int x2 = interaction2.getXPosition();
+            int y1 = interaction1.getYPosition();
+            int y2 = interaction2.getYPosition();
+        
+            if ((x1 == x2) && (y1 == y2)) {
+                return NodeEquality.LEXICALLY_EQUAL;
+            }
+        }
+        
+        return NodeEquality.SEMANTICALLY_EQUAL;
     }
 
@@ -158,15 +377,23 @@
      * @return as described
      */
-    private NodeEquality compareTextInputs(TextInput interaction1, TextInput interaction2) {
-        if (interaction1.getEnteredText().equals(interaction2.getEnteredText())) {
-            if (interaction1.getTextInputEvents().equals(interaction2.getTextInputEvents())) {
-                return NodeEquality.LEXICALLY_EQUAL;
-            }
-            else {
-                return NodeEquality.SYNTACTICALLY_EQUAL;
-            }
-        }
-        else {
-            return NodeEquality.SEMANTICALLY_EQUAL;
+    private NodeEquality compareTextInputs(TextInput    interaction1,
+                                           TextInput    interaction2,
+                                           NodeEquality equalityLevel)
+    {
+        switch (equalityLevel) {
+            case LEXICALLY_EQUAL:
+                if (interaction1.getTextInputEvents().equals(interaction2.getTextInputEvents())) {
+                    return NodeEquality.LEXICALLY_EQUAL;
+                }
+                // fall through
+            case SYNTACTICALLY_EQUAL:
+                if (interaction1.getEnteredText().equals(interaction2.getEnteredText())) {
+                    return NodeEquality.SYNTACTICALLY_EQUAL;
+                }
+                // fall through
+            case SEMANTICALLY_EQUAL:
+                return NodeEquality.SEMANTICALLY_EQUAL;
+            default:
+                return NodeEquality.UNEQUAL;
         }
     }
@@ -185,133 +412,17 @@
      */
     private NodeEquality compareValueSelections(ValueSelection<?> interaction1,
-                                                ValueSelection<?> interaction2)
-    {
-        Object value1 = interaction1.getSelectedValue();
-        Object value2 = interaction2.getSelectedValue();
-        
-        if ((value1 == value2) || ((value1 != null) && (value1.equals(value2)))) {
-            return NodeEquality.LEXICALLY_EQUAL;
-        }
-        else {
-            return NodeEquality.SEMANTICALLY_EQUAL;
-        }
-    }
-
-    /**
-     * <p>
-     * compares two mouse clicks. If both clicks have the same coordinates, they are lexically
-     * equal. Otherwise, they are semantically equal. Mouse clicks for which the coordinates make
-     * no lexical difference (see {@link #clickCoordinatesMakeLexicalDifference(IEventTarget)})
-     * are treated as lexically equal.
-     * </p>
-     *
-     * @param interaction1 the first mouse click to compare
-     * @param interaction2 the second mouse click to compare
-     * @param eventTarget  the event target on which the interactions happened (used within
-     *                     special comparisons like mouse clicks on buttons, where the coordinates
-     *                     can be ignored)
-     * 
-     * @return as described
-     */
-    private NodeEquality compareMouseClicks(MouseClick   interaction1,
-                                            MouseClick   interaction2,
-                                            IEventTarget eventTarget)
-    {
-        if (interaction1.getButton() != interaction2.getButton()) {
-            return NodeEquality.UNEQUAL;
-        }
-        
-        if (!clickCoordinatesMakeLexicalDifference(eventTarget)) {
-            return NodeEquality.LEXICALLY_EQUAL;
-        }
-        
-        int x1 = interaction1.getX();
-        int x2 = interaction2.getX();
-        int y1 = interaction1.getY();
-        int y2 = interaction2.getY();
-        
-        if ((x1 == x2) && (y1 == y2)) {
-            return NodeEquality.LEXICALLY_EQUAL;
-        }
-        else {
-            return NodeEquality.SEMANTICALLY_EQUAL;
-        }
-    }
-
-    /**
-     * <p>
-     * compares two mouse double clicks. If both double clicks have the same coordinates, they are
-     * lexically equal. Otherwise, they are semantically equal. Double clicks for which the
-     * coordinates make no lexical difference
-     * (see {@link #clickCoordinatesMakeLexicalDifference(IEventTarget)}) are treated as lexically
-     * equal.
-     * </p>
-     *
-     * @param interaction1 the first mouse double click to compare
-     * @param interaction2 the second mouse double click to compare
-     * @param eventTarget  the event target on which the interactions happened (used within
-     *                     special comparisons like mouse clicks on buttons, where the coordinates
-     *                     can be ignored)
-     * 
-     * @return as described
-     */
-    private NodeEquality compareMouseDoubleClicks(MouseDoubleClick interaction1,
-                                                  MouseDoubleClick interaction2,
-                                                  IEventTarget     eventTarget)
-    {
-        if (interaction1.getButton() != interaction2.getButton()) {
-            return NodeEquality.UNEQUAL;
-        }
-        
-        if (!clickCoordinatesMakeLexicalDifference(eventTarget)) {
-            return NodeEquality.LEXICALLY_EQUAL;
-        }
-        
-        int x1 = interaction1.getX();
-        int x2 = interaction2.getX();
-        int y1 = interaction1.getY();
-        int y2 = interaction2.getY();
-        
-        if ((x1 == x2) && (y1 == y2)) {
-            return NodeEquality.LEXICALLY_EQUAL;
-        }
-        else {
-            return NodeEquality.SEMANTICALLY_EQUAL;
-        }
-    }
-
-    /**
-     * <p>
-     * compares two mouse drag and drops. If both drag and drops have the same start and end
-     * coordinates, they are lexically equal. Otherwise, they are semantically equal.
-     * </p>
-     *
-     * @param interaction1 the first mouse drag and drop to compare
-     * @param interaction2 the second mouse drag and drop to compare
-     * 
-     * @return as described
-     */
-    private NodeEquality compareMouseDragAndDrops(MouseDragAndDrop interaction1,
-                                                  MouseDragAndDrop interaction2)
-    {
-        if (interaction1.getButton() != interaction2.getButton()) {
-            return NodeEquality.UNEQUAL;
-        }
-        
-        int x1 = interaction1.getX();
-        int x1Start = interaction1.getXStart();
-        int x2 = interaction2.getX();
-        int x2Start = interaction2.getXStart();
-        int y1 = interaction1.getY();
-        int y1Start = interaction1.getYStart();
-        int y2 = interaction2.getY();
-        int y2Start = interaction2.getYStart();
-        
-        if ((x1Start == x2Start) && (x1 == x2) && (y1Start == y2Start) && (y1 == y2)) {
-            return NodeEquality.LEXICALLY_EQUAL;
-        }
-        else {
-            return NodeEquality.SEMANTICALLY_EQUAL;
-        }
+                                                ValueSelection<?> interaction2,
+                                                NodeEquality      equalityLevel)
+    {
+        if (equalityLevel.isAtLeast(NodeEquality.SYNTACTICALLY_EQUAL)) {
+            Object value1 = interaction1.getSelectedValue();
+            Object value2 = interaction2.getSelectedValue();
+        
+            if ((value1 == value2) || ((value1 != null) && (value1.equals(value2)))) {
+                return NodeEquality.LEXICALLY_EQUAL;
+            }
+        }
+        
+        return NodeEquality.SEMANTICALLY_EQUAL;
     }
 
@@ -336,4 +447,5 @@
             (eventTarget instanceof IImage) ||
             (eventTarget instanceof IListBox) ||
+            (eventTarget instanceof IMenu) ||
             (eventTarget instanceof IMenuButton) ||
             (eventTarget instanceof IRadioButton) ||
