8b2bde924c1a20836442ddc4df2c3192c1cd01ee
[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 class AccessibleCache(object):
47         """
48         There is one accessible cache per application.
49         For each application the accessible cache stores
50         data on every accessible object within the app.
51
52         It also acts as the factory for creating client
53         side proxies for these accessible objects.
54
55         connection - DBus connection.
56         busName    - Name of DBus connection where cache interface resides.
57         """
58
59         _PATH = '/org/freedesktop/atspi/tree'
60         _INTERFACE = 'org.freedesktop.atspi.Tree'
61         _GET_METHOD = 'getTree'
62         _UPDATE_SIGNAL = 'updateTree'
63
64         def __init__(self, registry, connection, bus_name):
65                 """
66                 Creates a cache.
67
68                 connection - DBus connection.
69                 busName    - Name of DBus connection where cache interface resides.
70                 """
71                 self._registry = registry
72                 self._connection = connection
73                 self._bus_name = bus_name
74
75                 obj = connection.get_object(bus_name, self._PATH, introspect=False)
76                 itf = _dbus.Interface(obj, self._INTERFACE)
77
78                 self._objects = {}
79
80                 get_method = itf.get_dbus_method(self._GET_METHOD)
81                 self._update_objects(get_method())
82
83                 self._signalMatch = itf.connect_to_signal(self._UPDATE_SIGNAL, self._update_handler)
84
85                 obj = connection.get_object(self._bus_name, self._PATH, introspect=False)
86                 itf = _dbus.Interface(obj, self._INTERFACE)
87
88                 self._root = itf.getRoot()
89
90         def __getitem__(self, key):
91                 return self._objects[key]
92
93         def __contains__(self, key):
94                 return key in self._objects
95
96         def _update_cache_dispatch_events(self, cachedata, data):
97                 (path,
98                  parent,
99                  children,
100                  interfaces,
101                  name,
102                  role,
103                  description) = data
104
105                 # TODO The 'self._registry._cache' statement makes me think
106                 # I have serious modularization FAIL here. 
107
108                 if name != cachedata.name:
109                         event = _Event(self._registry._cache,
110                                        path,
111                                        self._bus_name,
112                                        "org.freedesktop.atspi.Event.Object",
113                                        "property-change",
114                                        ("name", 0, 0, name))
115                         self._registry._notifyNameChange(event)
116
117                 if description != cachedata.description:
118                         event = _Event(self._registry._cache,
119                                        path,
120                                        self._bus_name,
121                                        "org.freedesktop.atspi.Event.Object",
122                                        "property-change",
123                                        ("description", 0, 0, description))
124                         self._registry._notifyDescriptionChange(event)
125
126                 if parent != cachedata.parent:
127                         event = _Event(self._registry._cache,
128                                        path,
129                                        self._bus_name,
130                                        "org.freedesktop.atspi.Event.Object",
131                                        "property-change",
132                                        ("parent", 0, 0, ""))
133                         self._registry._notifyParentChange(event)
134
135                 if children != cachedata.children:
136                         event = _Event(self._registry._cache,
137                                        path,
138                                        self._bus_name,
139                                        "org.freedesktop.atspi.Event.Object",
140                                        "children-changed",
141                                        ("", 0, 0, ""))
142                         self._registry._notifyChildrenChange(event)
143
144                 cachedata._update(data)
145
146         def _update_handler(self, update, remove):
147                 self._remove_objects(remove)
148                 self._update_objects(update)
149
150         def _update_objects(self, objects):
151                 for data in objects:
152                         #First element is the object path.
153                         path = data[0]
154                         if path in self._objects:
155                                 cachedata = self._objects[path]
156                                 self._update_cache_dispatch_events(cachedata, data)
157                         else:
158                                 self._objects[path] = _CacheData(data)
159
160         def _remove_objects(self, paths):
161                 for path in paths:
162                         # TODO I'm squashing a possible error here
163                         # I've seen things appear to be deleted twice
164                         # which needs investigation
165                         try:
166                                 del(self._objects[path])
167                         except KeyError:
168                                 pass
169
170         def _get_root(self):
171                 return self._root
172
173         root = property(fget=_get_root)
174
175 #END---------------------------------------------------------------------------