Index: dogtail/config.py
===================================================================
RCS file: /cvs/gnome/dogtail/dogtail/config.py,v
retrieving revision 1.6
diff -U5 -r1.6 config.py
--- dogtail/config.py	5 Apr 2006 00:11:36 -0000	1.6
+++ dogtail/config.py	18 May 2006 14:50:12 -0000
@@ -55,10 +55,14 @@
     subsystem.
 
     blinkOnActions (boolean):
     Whether we should blink a rectangle around a Node when an action is
     performed on it.
+
+    modalTrack (boolean):
+    Whether dogtail will respect modal states and avoid sending events or
+    actions to not grabbed windows on checking sensitivity.
     """
     __scriptName = staticmethod(_scriptName)
     __encoding = staticmethod(_encoding)
 
     defaults = {
@@ -85,11 +89,12 @@
             'debugSleep' : False,
             'debugSearchPaths' : False,
             'absoluteNodePaths' : False,
             'ensureSensitivity' : False,
             'debugTranslation' : False,
-            'blinkOnActions' : False
+            'blinkOnActions' : False,
+            'modalTrack' : True
     }
 
     options = {}
 
     invalidValue = "__INVALID__"
Index: dogtail/predicate.py
===================================================================
RCS file: /cvs/gnome/dogtail/dogtail/predicate.py,v
retrieving revision 1.8
diff -U5 -r1.8 predicate.py
--- dogtail/predicate.py	5 Apr 2006 00:11:36 -0000	1.8
+++ dogtail/predicate.py	18 May 2006 14:50:12 -0000
@@ -348,10 +348,21 @@
         return "tab(%s%s)"%(self.tabName, makeScriptRecursiveArgument(isRecursive, True))
 
     def makeScriptVariableName(self):
         return makeCamel(self.tabName)+"Tab"
 
+class IsATopLevel(Predicate):
+    """Predicate subclass that checks that a node is a toplevel (child of an app)"""
+    def satisfiedByNode(self, node):
+        if node.parent:
+            return node.parent.roleName=='application'
+        else:
+            return False
+        
+    def describeSearchResult(self):
+        return "toplevel"
+
 class PredicateTests(unittest.TestCase):
     def testCapitalization(self):
         self.assertEquals(makeCamel("gnome-terminal"),"gnomeTerminal")
         self.assertEquals(makeCamel("Evolution - Mail"), "evolutionMail")
         self.assertEquals(makeCamel('self.assertEquals(makeCamel("Evolution - Mail"), "evolutionMail")'), "selfAssertequalsMakecamelEvolutionMailEvolutionmail")
Index: dogtail/tree.py
===================================================================
RCS file: /cvs/gnome/dogtail/dogtail/tree.py,v
retrieving revision 1.27
diff -U5 -r1.27 tree.py
--- dogtail/tree.py	17 Apr 2006 06:18:54 -0000	1.27
+++ dogtail/tree.py	18 May 2006 14:50:13 -0000
@@ -98,20 +98,34 @@
         self.attr = attr
 
     def __str__(self):
         return self.message % self.attr
 
-class NotSensitiveError(Exception):
+class InteractionBlockedError(Exception):
     """
-    The widget is not sensitive.
+    The widget cannot get events.
     """
-    message = "Cannot %s %s. It is not sensitive."
+    message = "Cannot %s %s as events are blocked."
     def __init__(self, action):
         self.action = action
-
     def __str__(self):
         return self.message % (self.action.name, self.action.node.getLogString())
+        
+
+class NotSensitiveError(InteractionBlockedError):
+    """
+    The widget is not sensitive.
+    """
+    message = "Cannot %s %s. It is not sensitive."
+
+class InteractionBlockedByModalError(InteractionBlockedError):
+    """
+    The widget cannot receive events as it is in a window that
+    is not grabbed, and there is another window that is grabbed
+    (modal window).
+    """
+    message = "Cannot %s %s. There is a modal window preventing interaction."
 
 class ActionNotSupported(Exception):
     """
     The widget does not support the requested action.
     """
@@ -164,10 +178,12 @@
         """
         Performs the given tree.Action, with appropriate delays and logging.
         """
         assert isinstance(self, Action)
         logger.log("%s on %s"%(self.name, self.node.getLogString()))
+        if self.node.blockedByModal:
+            raise InteractionBlockedByModalError, self
         if not self.node.sensitive:
             if config.ensureSensitivity:
                 raise NotSensitiveError, self
             else:
                 nSE = NotSensitiveError(self)
@@ -242,10 +258,19 @@
     'sensitive' (read-only boolean):
     Is this node sensitive (i.e. not greyed out). Generated from stateSet
     based on presence of atspi.SPI_STATE_SENSITIVE
     Not all applications/toolkits seem to properly set this up.
 
+    'modal' (read-only boolean):
+    Is this node modal (i.e. a modal dialog). Generated from stateSet
+    based on presence of atspi.SPI_STATE_MODAL
+
+    'blockedByModal' (read-only boolean):
+    Is there any modal toplevel preventing this widget from getting events?.
+    This function returns always False if dogtail.config modalTrack is
+    disabled.
+
     'showing' (read-only boolean):
     Generated from stateSet based on presence of atspi.SPI_STATE_SHOWING
 
     'actions' (read-only list of Action instances):
     Generated from Accessible_getAction and AccessibleAction_getNActions
@@ -484,12 +509,26 @@
                     return apply(Node, targets)
 
         # Attributes synthesized from the Accessible's stateSet:
         elif attr == "sensitive":
             return self.__accessible.getStateSet().contains(atspi.SPI_STATE_SENSITIVE)
+	elif attr == "modal":
+            return self.__accessible.getStateSet() and self.__accessible.getStateSet().contains(atspi.SPI_STATE_MODAL)
         elif attr == "showing":
             return self.__accessible.getStateSet().contains(atspi.SPI_STATE_SHOWING)
+
+        # Derived attributes:
+        elif attr == "blockedByModal":
+            if config.modalTrack:
+                toplevel = self.findAncestor(predicate.IsATopLevel())
+                if toplevel.modal:
+                    return False
+                if toplevel.parent and toplevel.parent.roleName == 'application':
+                    for child in toplevel.parent.children:
+                        if child.modal:
+                            return True
+            return False
 
         # Attributes from the Action object
         elif attr == "actions":
             actions = {}
             if self.__action:
