5729f7100c223040bb9f748e892f3e947cb19be9
[platform/core/uifw/at-spi2-atk.git] / pyatspi / accessiblecache.py
1 #Copyright (C) 2008 Codethink Ltd
2
3 #This library is free software; you can redistribute it and/or
4 #modify it under the terms of the GNU Lesser General Public
5 #License version 2 as published by the Free Software Foundation.
6
7 #This program is distributed in the hope that it will be useful,
8 #but WITHOUT ANY WARRANTY; without even the implied warranty of
9 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 #GNU General Public License for more details.
11 #You should have received a copy of the GNU Lesser General Public License
12 #along with this program; if not, write to the Free Software
13 #Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
14
15 import dbus as _dbus
16
17 from event import Event as _Event
18
19 #------------------------------------------------------------------------------
20
21 class _CacheData(object):
22         __slots__ = [
23                         'parent',
24                         'interfaces',
25                         'children',
26                         'role',
27                         'name',
28                         'description',
29                     ]
30
31         def __init__(self, data):
32                 self._update(data)
33
34         def _update(self, data):
35                 #Don't cache the path here, used as lookup in cache object dict.
36                 (path,
37                 self.parent,
38                 self.children,
39                 self.interfaces,
40                 self.name,
41                 self.role,
42                 self.description) = data
43
44 #------------------------------------------------------------------------------
45
46 def _list_items_added_removed (l1, l2):
47         """
48         Returns a tuple (boolean, boolean).
49         The first value indicates if, when
50         moving from l1 to l2, any items have been added.
51         The second value indicates whether any items have
52         been removed.
53         """
54         l1notl2 = [item for item in l1 if item not in l2]
55         l2notl1 = [item for item in l2 if item not in l1]
56         return ((len(l1notl2) > 0), (len(l2notl1) > 0))
57
58 #------------------------------------------------------------------------------
59
60 class AccessibleCache(object):
61         """
62         There is one accessible cache per application.
63         For each application the accessible cache stores
64         data on every accessible object within the app.
65
66         It also acts as the factory for creating client
67         side proxies for these accessible objects.
68
69         connection - DBus connection.
70         busName    - Name of DBus connection where cache interface resides.
71         """
72
73         _PATH = '/org/freedesktop/atspi/tree'
74         _INTERFACE = 'org.freedesktop.atspi.Tree'
75         _GET_METHOD = 'getTree'
76         _UPDATE_SIGNAL = 'updateTree'
77
78         def __init__(self, registry, connection, bus_name):
79                 """
80                 Creates a cache.
81
82                 connection - DBus connection.
83                 busName    - Name of DBus connection where cache interface resides.
84                 """
85                 self._registry = registry
86                 self._connection = connection
87                 self._bus_name = bus_name
88
89                 obj = connection.get_object(bus_name, self._PATH, introspect=False)
90                 itf = _dbus.Interface(obj, self._INTERFACE)
91
92                 self._objects = {}
93
94                 get_method = itf.get_dbus_method(self._GET_METHOD)
95                 self._update_objects(get_method())
96
97                 self._signalMatch = itf.connect_to_signal(self._UPDATE_SIGNAL, self._update_handler)
98
99                 obj = connection.get_object(self._bus_name, self._PATH, introspect=False)
100                 itf = _dbus.Interface(obj, self._INTERFACE)
101
102                 self._root = itf.getRoot()
103
104         def __getitem__(self, key):
105                 return self._objects[key]
106
107         def __contains__(self, key):
108                 return key in self._objects
109
110         def _dispatch_event(self, olddata, newdata):
111                 if olddata.name != newdata.name:
112                         event = _Event(self._registry._cache,
113                                        path,
114                                        self._bus_name,
115                                        "org.freedesktop.atspi.Event.Object",
116                                        "property-change",
117                                        ("name", 0, 0, newdata.name))
118                         self._registry._notifyNameChange(event)
119
120                 if olddata.description != newdata.description:
121                         event = _Event(self._registry._cache,
122                                        path,
123                                        self._bus_name,
124                                        "org.freedesktop.atspi.Event.Object",
125                                        "property-change",
126                                        ("description", 0, 0, description))
127                         self._registry._notifyDescriptionChange(event)
128
129                 if olddata.parent != newdata.parent:
130                         event = _Event(self._registry._cache,
131                                        path,
132                                        self._bus_name,
133                                        "org.freedesktop.atspi.Event.Object",
134                                        "property-change",
135                                        ("parent", 0, 0, ""))
136                         self._registry._notifyParentChange(event)
137
138                 added, removed = _list_items_added_removed (olddata.children, newdata.children):
139
140                 if added:
141                         event = _Event(self._registry._cache,
142                                        path,
143                                        self._bus_name,
144                                        "org.freedesktop.atspi.Event.Object",
145                                        "children-changed",
146                                        ("add", 0, 0, ""))
147                         self._registry._notifyChildrenChange(event)
148
149                 if removed:
150                         event = _Event(self._registry._cache,
151                                        path,
152                                        self._bus_name,
153                                        "org.freedesktop.atspi.Event.Object",
154                                        "children-changed",
155                                        ("remove", 0, 0, ""))
156                         self._registry._notifyChildrenChange(event)
157
158         def _update_handler(self, update, remove):
159                 self._remove_objects(remove)
160                 self._update_objects(update)
161
162         def _update_objects(self, objects):
163                 cache_update_objects = []
164                 for data in objects:
165                         #First element is the object path.
166                         path = data[0]
167                         if path in self._objects:
168                                 olddata = self._objects[path]
169                                 newdata = _CacheData(data)
170                                 cache_update_objects.append((olddata, newdata))
171                                 self._objects[path] = newdata
172                         else:
173                                 self._objects[path] = _CacheData(data)
174                 for old, new in cache_update_objects:
175                         self._dispatch_event(old, new)
176
177         def _remove_objects(self, paths):
178                 for path in paths:
179                         # TODO I'm squashing a possible error here
180                         # I've seen things appear to be deleted twice
181                         # which needs investigation
182                         try:
183                                 del(self._objects[path])
184                         except KeyError:
185                                 pass
186
187         def _get_root(self):
188                 return self._root
189
190         root = property(fget=_get_root)
191
192 #END---------------------------------------------------------------------------