1
1
#!/usr/bin/env python
2
2
# -*- coding: UTF8 -*-
5
appAuthors = ['Zack Cerza <zcerza@redhat.com>', 'David Malcolm <dmalcolm@redhat.com']
7
from dogtail.utils import checkForA11yInteractively
8
checkForA11yInteractively()
10
from dogtail import tree
11
from dogtail import utils
15
program = gnome.program_init(appName, '0.1')
19
if os.path.exists('sniff.glade'):
20
x = gtk.glade.XML('sniff.glade')
23
exec_root = sys.argv[0].split("/bin/")[0]
24
if exec_root[0] is not '/':
26
x = gtk.glade.XML(exec_root + '/share/dogtail/glade/sniff.glade')
28
sniff = x.get_widget(appName)
31
sniff.set_icon_from_file('../icons/dogtail-head.svg')
33
sniff.set_icon_from_file('/usr/share/icons/hicolor/scalable/apps/dogtail-head.svg')
35
view = x.get_widget('treeTreeView')
37
nameTextLabel = x.get_widget('nameTextLabel')
38
roleNameTextLabel = x.get_widget('roleNameTextLabel')
39
descTextLabel = x.get_widget('descTextLabel')
40
actionsTextLabel = x.get_widget('actionsTextLabel')
41
textLabel = x.get_widget('textLabel')
42
textTextView = x.get_widget('textTextView')
44
# Indices of the various fields of the tree model:
47
MODEL_FIELD_ROLENAME = 2
48
MODEL_FIELD_DESCRIPTION = 3
49
MODEL_FIELD_PIXBUF = 4
54
def expandAll(widget, *args):
56
if args[0] == True: view.expand_all()
57
elif args[0] == False: view.collapse_all()
60
about = gtk.AboutDialog()
61
about.set_name(appName)
62
about.set_authors(appAuthors)
63
about.set_comments('Explore your desktop with Dogtail')
64
about.set_website('http://people.redhat.com/zcerza/dogtail/')
68
sniff.connect('delete_event', quit)
70
quit1 = x.get_widget('quit1')
71
quit1.connect('activate', quit)
73
expand_all1 = x.get_widget('expand_all1')
74
expand_all1.connect('activate', expandAll, True)
75
collapse_all1 = x.get_widget('collapse_all1')
76
collapse_all1.connect('activate', expandAll, False)
78
about1 = x.get_widget('about1')
79
about1.connect('activate', showAbout)
81
refreshMenuItem = x.get_widget('refresh1')
82
refreshMenuItem.connect('activate', refreshAll)
84
view.connect('button-press-event', buttonPress)
85
view.connect('row-expanded', rowExpanded)
86
view.connect('row-collapsed', rowCollapsed)
87
treeSelection = view.get_selection()
88
treeSelection.connect('changed', selectionChanged)
90
def buttonPress(widget, event, *userParams):
92
try: path, treeViewCol, relX, relY = \
93
view.get_path_at_pos(int(event.x), int(event.y))
94
except TypeError: return
102
for action in node.actions.values():
103
menuItem = gtk.MenuItem(action.name.capitalize())
104
menuItem.connect('activate', menuItemActivate, action.do)
106
menu.append(menuItem)
107
if not menuItem: return
109
menu.popup(None, None, None, event.button, event.time)
111
# I hate globals too. I think we need one here, though.
112
# Make it a non-integer, so we can know if it has been set yet (later).
113
textTextViewBufferChangedLastHandlerID = 3.141592654
115
def setUpTable(node):
116
"""Generic code for setting up the table under the TreeView"""
117
nameTextLabel.set_text(node.name)
118
roleNameTextLabel.set_text(node.roleName)
119
descTextLabel.set_text(node.description)
4
http://en.wikipedia.org/wiki/Model-view-controller
6
The SniffApp class sets up all of sniff's widgets.
8
Data storage is handled by the SniffModel class.
9
There is no SniffView class; we just use a GtkTreeView.
10
Data display is handled by the SniffController class.
13
gi.require_version('Gtk', '3.0')
14
gi.require_version('Gdk', '3.0')
16
from dogtail.config import config
18
if config.checkForA11y:
19
from dogtail.utils import checkForA11yInteractively
20
checkForA11yInteractively()
22
config.logDebugToFile = False
23
config.childrenLimit = 100000
27
from gi.repository import Gtk
28
from gi.repository import Gdk
29
from gi.repository import Gio
30
from gi.repository import GdkPixbuf
31
from gi.repository import GObject
33
builder = Gtk.Builder()
35
class SniffApp(object):
37
appAuthors = ['Zack Cerza <zcerza@redhat.com>', \
38
'David Malcolm <dmalcolm@redhat.com']
41
self.builder = builder
43
if os.path.exists('sniff.ui'):
44
self.builder.add_from_file('sniff.ui')
47
exec_root = sys.argv[0].split("/bin/")[0]
48
if exec_root[0] is not '/':
50
self.builder.add_from_file(exec_root + \
51
'/share/dogtail/glade/sniff.ui')
52
self.app = self.builder.get_object(self.appName)
54
self.app.set_icon_from_file('../icons/dogtail-head.svg')
57
path = os.path.abspath (os.path.join(__file__, os.path.pardir, os.path.pardir))
58
self.app.set_icon_from_file( os.path.join( path, \
59
'share/icons/hicolor/scalable/apps/dogtail-head.svg'))
65
def setUpWidgets(self):
66
self.quit1 = self.builder.get_object('quit1')
67
self.expand_all1 = self.builder.get_object('expand_all1')
68
self.collapse_all1 = self.builder.get_object('collapse_all1')
69
self.about1 = self.builder.get_object('about1')
70
self.refreshMenuItem = self.builder.get_object('refresh1')
71
self.autoRefreshMenuItem = self.builder.get_object('autorefresh')
72
self.setRootMenuItem = self.builder.get_object('setRootMenuItem')
73
self.unsetRootMenuItem = self.builder.get_object('unsetRootMenuItem')
76
self.tree = SniffController()
78
def connectSignals(self):
79
self.app.connect('delete_event', self.quit, self)
80
self.quit1.connect('activate', self.quit, self)
81
self.expand_all1.connect('activate', self.tree.expandAll, True)
82
self.collapse_all1.connect('activate', self.tree.expandAll, False)
83
self.about1.connect('activate', self.showAbout, self)
84
self.refreshMenuItem.connect('activate', self.tree.refresh)
85
self.autoRefreshMenuItem.connect('toggled', self.tree.toggleAutoRefresh)
86
self.setRootMenuItem.connect('activate', self.tree.changeRoot, True)
87
self.unsetRootMenuItem.connect('activate', self.tree.changeRoot, False)
89
self.setStartupAutoRefresh()
91
def setStartupAutoRefresh(self):
93
if not os.path.exists('/tmp/sniff_refresh.lock'):
94
self.autoRefreshMenuItem.set_active(True)
96
def showAbout(self, *args):
98
self.about = Gtk.AboutDialog()
99
self.about.set_name(self.appName)
100
self.about.set_authors(self.appAuthors)
101
self.about.set_comments('Explore your desktop with Dogtail')
102
self.about.set_website('http://people.redhat.com/zcerza/dogtail/')
103
self.about.connect("response", self.hideAbout)
104
self.about.show_all()
106
def hideAbout(self, window, response):
107
if response == Gtk.ResponseType.CANCEL: window.hide()
109
def quit(self, *args):
113
class SniffController(object):
114
invalidBufferCallbackID = None
117
self.builder = builder
118
self.nameTextLabel = self.builder.get_object('nameTextLabel')
119
self.nameTextLabel.set_text('')
120
self.roleNameTextLabel = self.builder.get_object('roleNameTextLabel')
121
self.roleNameTextLabel.set_text('')
122
self.descTextLabel = self.builder.get_object('descTextLabel')
123
self.descTextLabel.set_text('')
124
self.actionsTextLabel = self.builder.get_object('actionsTextLabel')
125
self.actionsTextLabel.set_text('')
126
self.textTextView = self.builder.get_object('textTextView')
127
self.textTextViewBufferCallbackID = self.invalidBufferCallbackID
128
self.textTextView.set_sensitive(False)
129
self.textTextView.get_buffer().set_text('')
130
self.labelerButton = self.builder.get_object('labelerButton')
131
self.labelerButton.set_sensitive(False)
132
self.labeleeButton = self.builder.get_object('labeleeButton')
133
self.labeleeButton.set_sensitive(False)
134
self.stateView = self.builder.get_object('stateTreeView')
135
self.highlight1 = self.builder.get_object('highlight1')
136
self.autorefresh = self.builder.get_object('autorefresh')
137
self.stateModel = StateModel()
138
self.setUpStateView()
139
self.treeView = self.builder.get_object('treeTreeView')
140
self.treeSelection = self.treeView.get_selection()
141
self.treeModel = SniffModel()
143
self.connectSignals()
146
def setUpStateView(self):
147
self.stateView.set_model(self.stateModel)
148
cellRenderer = Gtk.CellRendererText()
149
col = Gtk.TreeViewColumn('Present States', cellRenderer, \
150
text=self.stateModel.stateColumn)
151
col.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
152
self.stateView.insert_column(col, -1)
154
def setUpTreeView(self):
155
self.treeView.set_enable_tree_lines(True)
157
self.treeView.set_model(self.treeModel)
159
col = Gtk.TreeViewColumn()
160
cellRenderer = Gtk.CellRendererPixbuf()
161
col.pack_start(cellRenderer, expand = False)
162
col.add_attribute(cellRenderer, 'pixbuf', self.treeModel.pixbufColumn)
164
cellRenderer = Gtk.CellRendererText()
165
col.pack_end(cellRenderer, expand = False)
166
col.add_attribute(cellRenderer, 'text', self.treeModel.nameColumn)
168
col.set_title('Name')
170
self.treeView.insert_column(col, -1)
172
for column in self.treeView.get_columns():
173
column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
174
column.set_resizable(True)
177
self.treeView.expand_all()
178
#self.rowExpanded(self.treeView, self.treeModel.get_iter(path), path)
180
def changeRoot(self, menuItem, toSelected = True, *args):
181
if toSelected: node = self.getSelectedNode()
182
if toSelected and node: self.treeModel.changeRoot(node)
183
elif not toSelected: self.treeModel.reset()
185
self.refresh(refreshModel = False)
187
def refresh(self, menuItem = None, refreshModel = True, *args):
188
if refreshModel: self.treeModel.refresh()
189
rootPath = self.treeModel.get_path(self.treeModel.get_iter_first())
190
self.treeView.expand_all()
191
self.treeView.expand_row(rootPath, False)
193
def toggleAutoRefresh(self, *args):
194
if self.autorefresh.get_active() is True:
195
pyatspi.Registry.registerEventListener(self.treeModel.nodeChanged, \
196
'object:children-changed')
197
pyatspi.Registry.registerEventListener(self.treeModel.nodeChanged, \
198
'object:property-change:accessible-name')
199
pyatspi.Registry.registerEventListener(self.treeModel.nodeChanged, \
200
'object:property-change:accessible-state')
201
pyatspi.Registry.registerEventListener(self.treeModel.nodeChanged, \
202
'object:state-changed')
205
pyatspi.Registry.deregisterEventListener(self.treeModel.nodeChanged, \
206
'object:children-changed')
207
pyatspi.Registry.deregisterEventListener(self.treeModel.nodeChanged, \
208
'object:property-change:accessible-name')
209
pyatspi.Registry.deregisterEventListener(self.treeModel.nodeChanged, \
210
'object:property-change:accessible-state')
211
pyatspi.Registry.deregisterEventListener(self.treeModel.nodeChanged, \
212
'object:state-changed')
214
def connectSignals(self):
215
self.labelerButton.connect('clicked', self.showRelationTarget, \
217
self.labeleeButton.connect('clicked', self.showRelationTarget, \
219
self.treeView.connect('button-press-event', self.buttonPress)
220
self.treeView.connect('key-press-event', self.keyPress)
221
self.treeView.connect('row-expanded', self.rowExpanded, self.treeModel)
222
self.treeView.connect('row-collapsed', self.rowCollapsed)
223
self.treeSelection.connect('changed', self.selectionChanged)
226
def selectionChanged(self, treeSelection):
227
node = self.getSelectedNode()
229
self.setUpBottomPane(node)
230
if self.highlight1.get_active() is True:
233
def getSelectedNode(self):
234
(store, iter) = self.treeView.get_selection().get_selected()
235
if not iter: node = None
236
else: node = self.treeModel.getNode(iter)
239
def expandAll(self, widget, *args):
240
if args[0] == True: self.treeView.expand_all()
241
elif args[0] == False: self.treeView.collapse_all()
243
def rowExpanded(self, treeview, iter, path, *userParams):
244
row = self.treeModel[path]
245
childRows = row.iterchildren()
248
childRow = childRows.next()
249
self.treeModel.populateChildren(childRow.iter)
250
except StopIteration: break
252
def rowCollapsed(self, treeview, iter, path, *userParams):
253
row = self.treeModel[path]
254
childRows = row.iterchildren()
257
childRow = childRows.next()
258
grandChildRows = childRow.iterchildren()
261
grandChildRow = grandChildRows.next()
262
self.treeModel.remove(grandChildRow.iter)
263
except StopIteration: pass
264
except StopIteration: pass
266
def menuItemActivate(self, menuItem, *userParams):
267
if len(userParams) < 2: return
268
method = userParams[0]
272
def keyPress(self, widget, event, *userParams):
273
if event.keyval == Gdk.KEY_Return:
274
path = self.treeSelection.get_selected_rows()[1][0]
275
if self.treeView.row_expanded(path):
276
self.treeView.collapse_row(path)
278
self.treeView.expand_row(path, False)
281
def buttonPress(self, widget, event, *userParams):
282
try: path, treeViewCol, relX, relY = \
283
self.treeView.get_path_at_pos(int(event.x), \
285
except TypeError: return
286
node = self.treeModel.getNode(self.treeModel.get_iter(path))
287
if node == None: return
289
if event.button == 3:
290
self.menu = Gtk.Menu()
293
for action in node.actions.keys():
294
menuItem = Gtk.MenuItem(action.capitalize())
295
menuItem.connect('activate', self.menuItemActivate, node.doActionNamed, action)
297
self.menu.append(menuItem)
298
if not menuItem: return
300
self.menu.popup(None, None, None, None, event.button, event.time)
302
def showRelationTarget(self, button, relation, *args):
303
target = getattr(self.getSelectedNode(), relation)
304
if not target: return
308
traceback.print_exc()
310
def setUpBottomPane(self, node):
311
"""Generic code for setting up the table under the TreeView"""
312
if node == None: return
313
self.nameTextLabel.set_text(node.name)
314
self.roleNameTextLabel.set_text(node.roleName)
315
self.descTextLabel.set_text(node.description)
122
for action in node.actions.values(): str = ' '.join([str, action.name]).strip()
123
actionsTextLabel.set_text(str)
125
global textTextViewBufferChangedLastHandlerID
126
# Have we connected this signal yet? If so, disconnect it before proceeding.
127
if int(textTextViewBufferChangedLastHandlerID) == \
128
textTextViewBufferChangedLastHandlerID:
129
textTextView.get_buffer().disconnect(textTextViewBufferChangedLastHandlerID)
131
if node.text is not None:
132
buffer = textTextView.get_buffer()
133
buffer.set_text(node.text)
134
# Eeew! I'm touching tree's privates.
135
if node._Node__editableText:
136
# Remember the handler ID of this connection.
137
textTextView.set_sensitive(True)
138
textTextViewBufferChangedLastHandlerID = \
139
buffer.connect('changed', changeText, node)
141
textTextView.set_sensitive(False)
143
textTextView.get_buffer().set_text('')
144
textTextView.set_sensitive(False)
146
def changeText(textBuffer, node):
147
node.text = textBuffer.get_text(textBuffer.get_start_iter(), \
148
textBuffer.get_end_iter())
150
def rowExpanded(treeview, iter, path, *userParams):
153
childRows = row.iterchildren()
155
childNodes = node.children
156
for childNode in childNodes:
157
childRow = childRows.next()
158
addChildren(childNode, childRow.iter)
160
def rowCollapsed(treeview, iter, path, *userParams):
163
childRows = row.iterchildren()
166
childRow = childRows.next()
167
grandChildRows = childRow.iterchildren()
170
grandChildRow = grandChildRows.next()
171
model.remove(grandChildRow.iter)
172
except StopIteration: pass
173
except StopIteration: pass
175
def selectionChanged(treeSelection):
176
# print "selection changed"
177
node = getSelectedNode()
178
if node: node.blink()
180
def getSelectedNode():
183
(store, iter) = view.get_selection().get_selected()
184
if not iter: node = None
185
else: node = model[iter][0]
188
def menuItemActivate(menuItem, *userParams):
189
method = userParams[0]
192
def refreshAll(menuItem, *userParams):
196
def addNodeToModel(node, parentInTreeView = None):
198
iter = model.insert_before(parentInTreeView, None)
199
model.set_value(iter, MODEL_FIELD_NODE, node)
202
def addChildren(node, parentInTreeView = None):
204
for child in node.children:
205
iter = addNodeToModel(child, parentInTreeView)
207
def showNode(node, parentInTreeView = None):
209
iter = addNodeToModel(node, parentInTreeView)
210
addChildren(node, iter)
216
rootPath = model.get_path(model.get_iter_root())
217
view.expand_row(rootPath, False)
317
if node.actions: str = ' '.join(node.actions.keys())
318
self.actionsTextLabel.set_text(str)
320
# Have we connected this signal yet?
321
# If so, disconnect it before proceeding.
322
if self.textTextViewBufferCallbackID != self.invalidBufferCallbackID:
323
self.textTextView.get_buffer().disconnect( \
324
self.textTextViewBufferCallbackID)
325
self.textTextViewBufferCallbackID = self.invalidBufferCallbackID
327
if node.text is not None:
328
buffer = self.textTextView.get_buffer()
329
buffer.set_text(node.text)
331
node.queryEditableText()
332
# Remember the handler ID of this connection.
333
self.textTextView.set_sensitive(True)
334
self.textTextViewBufferCallbackID = \
335
buffer.connect('changed', self.changeText, node)
336
except NotImplementedError:
337
self.textTextView.set_sensitive(False)
339
self.textTextView.get_buffer().set_text('')
340
self.textTextView.set_sensitive(False)
342
if node.labeler and not node.labeler.dead:
343
self.labelerButton.set_sensitive(True)
344
self.labelerButton.show()
345
#elif node.labeler and node.labeler.dead:
346
# print "labeler is dead", node.labeler
348
self.labelerButton.set_sensitive(False)
349
self.labelerButton.hide()
350
if node.labelee and not node.labelee.dead:
351
self.labeleeButton.set_sensitive(True)
352
self.labeleeButton.show()
353
#elif node.labelee and node.labelee.dead:
354
# print "labelee is dead", node.labelee
356
self.labeleeButton.set_sensitive(False)
357
self.labeleeButton.hide()
359
self.stateModel.setNode(node)
361
def changeText(self, textBuffer, node):
362
if node == None: return
363
node.text = textBuffer.get_text(textBuffer.get_start_iter(), \
364
textBuffer.get_end_iter())
366
class SniffModel(Gtk.TreeStore):
374
self.builder = builder
375
#self.autorefresh = self.builder.get_object('autorefresh')
376
Gtk.TreeStore.__init__(self, GObject.TYPE_PYOBJECT, \
377
GObject.TYPE_STRING, GdkPixbuf.Pixbuf)
378
root = pyatspi.Registry.getDesktop(0)
379
self.rootNode = self.initialRootNode = root
380
self.appendAndPopulate(None, self.rootNode)
382
def __contains__(self, item):
383
from dogtail.tree import Node
384
if isinstance(item, Node):
385
if item in self.cache:
386
row = self.cache[item]
387
# If row is None, we need to call getPath() to be sure
389
path = self.getPath(item)
390
return path is not None
391
elif row in self: return True
393
elif isinstance(item, Gtk.TreeIter):
394
return self.iter_is_valid(item)
395
elif isinstance(item, list) or isinstance(item, tuple):
396
try: iter = self.get_iter(item)
397
except ValueError: return False
399
elif isinstance(item, Gtk.TreeRowReference):
400
return item.valid() and item.get_path() in self
404
def changeRoot(self, node):
409
self.rootNode = self.initialRootNode
415
self.appendAndPopulate(None, self.rootNode)
417
def append(self, parentIter, node):
418
if node: self.cache[node] = None
419
pb = self.getPixbufForNode(node)
420
return Gtk.TreeStore.append(self, parentIter, (node, node.name, pb))
422
def remove(self, iter):
423
node = self.getNode(iter)
424
try: del self.cache[node]
425
finally: return Gtk.TreeStore.remove(self, iter)
427
def populateChildren(self, iter):
431
node = self.getNode(iter)
432
for child in node.children:
433
if child in self: continue
434
result = result and self.append(iter, child)
437
def appendAndPopulate(self, iter, node):
438
childIter = self.append(iter, node)
439
return self.populateChildren(childIter)
441
def getNode(self, iter):
442
if not iter in self: return None
443
return self.get_value(iter, self.nodeColumn)
445
def getPath(self, node):
446
if not node: raise ValueError
447
try: indexInParent = node.indexInParent
448
except LookupError: return None
449
root = pyatspi.Registry.getDesktop(0)
450
row = self.cache.get(node, None)
454
if row in self: path = row.get_path()
455
else: del self.cache[node]
456
elif node == self.rootNode:
459
elif node.role == pyatspi.ROLE_APPLICATION or node.roleName == \
462
indexInParent = list(root.children).index(node)
464
elif not node.parent: return None
465
elif (0 <= indexInParent <= (len(node.parent) - 1)) and \
466
node.parent[indexInParent] != node:
468
siblings = node.parent.children
469
sibIndex = siblings.index(node)
471
if siblings[sibIndex] != node: return None
472
else: indexInParent = sibIndex
473
except ValueError: return None
474
if type(path) == list:
476
parentPath = self.getPath(node.parent)
477
if parentPath is None: return None
478
else: path = list(parentPath)
479
path.append(indexInParent)
483
nodeByPath = self.getNode(self.get_iter(path))
484
if node != nodeByPath:
485
#print "%s is not %s!" % (node, nodeByPath)
488
#print "I smell a bug in %s..." % node.getApplication()
491
#self.cache[node] = Gtk.TreeRowReference(self, path)
494
def processEvents(self):
495
if not len(self.eventQueue): return
496
queueCopy = self.eventQueue[:]
497
for event in queueCopy:
498
self.processChangedNode(event)
499
self.eventQueue.remove(event)
502
def nodeChanged(self, event):
503
#if self.autorefresh.get_active() is False: return
505
if not node or not node in self: return
506
app = event.host_application
507
if app and app.name == 'sniff': return
508
self.eventQueue.append(event)
509
GObject.idle_add(self.processEvents)
511
def processChangedNode(self, event):
513
if not node or not node in self: return
514
path = self.getPath(node)
516
iter = self.get_iter(path)
517
except (ValueError, TypeError):
519
if event.type.major == "property-change":
520
if event.type.minor == "accessible-name":
521
node = self.getNode(iter)
522
self.set_value(iter, self.nameColumn, node.name)
523
elif event.type.minor == "accessible-state":
525
elif event.type.major == "state-changed":
527
elif event.type.major == "children-changed":
528
if event.type.minor == 'add':
529
for child in node.children:
530
if not child in self:
532
self.appendAndPopulate(iter, child)
534
# If it has no children now, give it a sec
535
# to come up with some.
536
GObject.timeout_add(1000, \
537
self.__addNodeCB, iter, child)
538
elif event.type.minor == 'remove':
539
self.__removeNodeCB(iter, node, path)
541
def __addNodeCB(self, iter, parent):
542
self.appendAndPopulate(iter, parent)
545
def __removeNodeCB(self, iter, parent, path):
546
childRow = self.iter_children(iter)
547
while childRow is not None:
548
node = self.getNode(childRow)
549
if node is None: break
550
if node and self.getNode(childRow) not in parent:
551
self.remove(childRow)
552
else: childRow = self.iter_next(childRow)
554
def __populateCB(self, iter):
555
self.populateChildren(iter)
558
def getPixbufForNode(self, node):
559
theme = Gtk.IconTheme().get_default()
561
if node.role == pyatspi.ROLE_APPLICATION:
562
# FIXME: Use the pixbuf from libwcnk (if available):
563
# wnckApp = Application(node).getWnckApplication()
566
return theme.load_icon(node.name, 24, \
567
Gtk.IconLookupFlags.USE_BUILTIN)
568
except GObject.GError:
570
return theme.load_icon(node.name.lower(), 24, \
571
Gtk.IconLookupFlags.USE_BUILTIN)
572
except GObject.GError:
575
return iconForRole[node.role]
577
return theme.load_icon("gnome-fs-desktop", 24, \
578
Gtk.IconLookupFlags.USE_BUILTIN)
580
return theme.load_icon("gtk-dialog-error", 24, \
581
Gtk.IconLookupFlags.USE_BUILTIN)
583
class StateModel(Gtk.ListStore):
585
statesSupported = ['checked', 'focusable', 'focused', 'sensitive', \
589
Gtk.ListStore.__init__(self, GObject.TYPE_STRING)
591
def setNode(self, node):
593
for stateName in self.statesSupported:
594
if getattr(node, stateName) is True:
595
self.append((stateName.capitalize(),))
219
598
def loadIcon(iconName):
221
pixbuf = gtk.gdk.pixbuf_new_from_file('icons/' + iconName)
222
except gobject.GError:
223
iconName = '/usr/share/dogtail/icons/' + iconName
224
pixbuf = gtk.gdk.pixbuf_new_from_file(iconName)
600
pixbuf = GdkPixbuf.Pixbuf.new_from_file('icons/' + iconName)
601
except GObject.GError:
603
path = os.path.abspath (os.path.join(__file__, os.path.pardir, os.path.pardir))
604
iconName = os.path.join(path, 'share/dogtail/icons/', iconName)
605
pixbuf = GdkPixbuf.Pixbuf.new_from_file(iconName)
227
608
button_xpm = loadIcon("button.xpm")
250
631
window_xpm = loadIcon("window.xpm")
252
633
iconForRole = { \
253
atspi.SPI_ROLE_INVALID : None, \
254
atspi.SPI_ROLE_ACCEL_LABEL : label_xpm, \
255
atspi.SPI_ROLE_ALERT : None, \
256
atspi.SPI_ROLE_ANIMATION : None, \
257
atspi.SPI_ROLE_ARROW : None, \
258
atspi.SPI_ROLE_CALENDAR : None, \
259
atspi.SPI_ROLE_CANVAS : None, \
260
atspi.SPI_ROLE_CHECK_BOX : checkbutton_xpm, \
261
atspi.SPI_ROLE_CHECK_MENU_ITEM : checkmenuitem_xpm, \
262
atspi.SPI_ROLE_COLOR_CHOOSER : colorselection_xpm, \
263
atspi.SPI_ROLE_COLUMN_HEADER : None, \
264
atspi.SPI_ROLE_COMBO_BOX : combo_xpm, \
265
atspi.SPI_ROLE_DATE_EDITOR : None, \
266
atspi.SPI_ROLE_DESKTOP_ICON : None, \
267
atspi.SPI_ROLE_DESKTOP_FRAME : None, \
268
atspi.SPI_ROLE_DIAL : None, \
269
atspi.SPI_ROLE_DIALOG : dialog_xpm, \
270
atspi.SPI_ROLE_DIRECTORY_PANE : None, \
271
atspi.SPI_ROLE_DRAWING_AREA : None, \
272
atspi.SPI_ROLE_FILE_CHOOSER : None, \
273
atspi.SPI_ROLE_FILLER : None, \
274
atspi.SPI_ROLE_FONT_CHOOSER : None, \
275
atspi.SPI_ROLE_FRAME : window_xpm, \
276
atspi.SPI_ROLE_GLASS_PANE : None, \
277
atspi.SPI_ROLE_HTML_CONTAINER : None, \
278
atspi.SPI_ROLE_ICON : image_xpm, \
279
atspi.SPI_ROLE_IMAGE : image_xpm, \
280
atspi.SPI_ROLE_INTERNAL_FRAME : None, \
281
atspi.SPI_ROLE_LABEL : label_xpm, \
282
atspi.SPI_ROLE_LAYERED_PANE : viewport_xpm, \
283
atspi.SPI_ROLE_LIST : None, \
284
atspi.SPI_ROLE_LIST_ITEM : None, \
285
atspi.SPI_ROLE_MENU : menuitem_xpm, \
286
atspi.SPI_ROLE_MENU_BAR : menubar_xpm, \
287
atspi.SPI_ROLE_MENU_ITEM : menuitem_xpm, \
288
atspi.SPI_ROLE_OPTION_PANE : None, \
289
atspi.SPI_ROLE_PAGE_TAB : notebook_xpm, \
290
atspi.SPI_ROLE_PAGE_TAB_LIST : notebook_xpm, \
291
atspi.SPI_ROLE_PANEL : viewport_xpm, \
292
atspi.SPI_ROLE_PASSWORD_TEXT : None, \
293
atspi.SPI_ROLE_POPUP_MENU : None, \
294
atspi.SPI_ROLE_PROGRESS_BAR : None, \
295
atspi.SPI_ROLE_PUSH_BUTTON : button_xpm, \
296
atspi.SPI_ROLE_RADIO_BUTTON : None, \
297
atspi.SPI_ROLE_RADIO_MENU_ITEM : None, \
298
atspi.SPI_ROLE_ROOT_PANE : viewport_xpm, \
299
atspi.SPI_ROLE_ROW_HEADER : None, \
300
atspi.SPI_ROLE_SCROLL_BAR : vscrollbar_xpm, \
301
atspi.SPI_ROLE_SCROLL_PANE : scrolledwindow_xpm, \
302
atspi.SPI_ROLE_SEPARATOR : vseparator_xpm, \
303
atspi.SPI_ROLE_SLIDER : None, \
304
atspi.SPI_ROLE_SPIN_BUTTON : spinbutton_xpm, \
305
atspi.SPI_ROLE_SPLIT_PANE : None, \
306
atspi.SPI_ROLE_STATUS_BAR : statusbar_xpm, \
307
atspi.SPI_ROLE_TABLE : table_xpm, \
308
atspi.SPI_ROLE_TABLE_CELL : treeitem_xpm, \
309
atspi.SPI_ROLE_TABLE_COLUMN_HEADER : None, \
310
atspi.SPI_ROLE_TABLE_ROW_HEADER : None, \
311
atspi.SPI_ROLE_TEAROFF_MENU_ITEM : None, \
312
atspi.SPI_ROLE_TERMINAL : None, \
313
atspi.SPI_ROLE_TEXT : text_xpm, \
314
atspi.SPI_ROLE_TOGGLE_BUTTON : None, \
315
atspi.SPI_ROLE_TOOL_BAR : toolbar_xpm, \
316
atspi.SPI_ROLE_TOOL_TIP : None, \
317
atspi.SPI_ROLE_TREE : tree_xpm, \
318
atspi.SPI_ROLE_TREE_TABLE : tree_xpm, \
319
atspi.SPI_ROLE_UNKNOWN : unknown_xpm, \
320
atspi.SPI_ROLE_VIEWPORT : viewport_xpm, \
321
atspi.SPI_ROLE_WINDOW : window_xpm, \
322
atspi.SPI_ROLE_EXTENDED : None, \
323
atspi.SPI_ROLE_HEADER : None, \
324
atspi.SPI_ROLE_FOOTER : None, \
325
atspi.SPI_ROLE_PARAGRAPH : None, \
326
atspi.SPI_ROLE_RULER : None, \
327
atspi.SPI_ROLE_APPLICATION : None, \
328
atspi.SPI_ROLE_AUTOCOMPLETE : None, \
329
atspi.SPI_ROLE_EDITBAR : None, \
330
atspi.SPI_ROLE_EMBEDDED : None, \
331
atspi.SPI_ROLE_LAST_DEFINED: None }
333
def getPixbufForNode(node):
334
theme = gtk.icon_theme_get_default()
336
if node.role==atspi.SPI_ROLE_APPLICATION:
337
# FIXME: Use the pixbuf from libwcnk (if available):
338
# wnckApp = Application(node).getWnckApplication()
340
try: return theme.load_icon(node.name, 24, gtk.ICON_LOOKUP_USE_BUILTIN)
341
except gobject.GError:
342
try: return theme.load_icon(node.name.lower(), 24, gtk.ICON_LOOKUP_USE_BUILTIN)
343
except gobject.GError: return None
345
return iconForRole[node.role]
347
return theme.load_icon("gnome-fs-desktop", 24, gtk.ICON_LOOKUP_USE_BUILTIN)
348
except atspi.SpiException:
349
return theme.load_icon("gtk-dialog-error", 24, gtk.ICON_LOOKUP_USE_BUILTIN)
351
def getNodeAttr(column, cell, model, iter, attr):
352
node = model.get_value(iter, MODEL_FIELD_NODE)
354
text = getattr(node, attr)
355
except atspi.SpiException:
356
text = "(broken node)"
357
cell.set_property('text', text)
359
def getCellPixbufForNode(column, cell, model, iter):
360
node = model.get_value(iter, MODEL_FIELD_NODE)
361
pixbuf = getPixbufForNode(node)
362
cell.set_property('pixbuf', pixbuf)
634
pyatspi.ROLE_INVALID : None, \
635
# pyatspi doesn't have the following... not even sure if it exists
637
#atspi.SPI_ROLE_ACCEL_LABEL : label_xpm, \
638
pyatspi.ROLE_ALERT : None, \
639
pyatspi.ROLE_ANIMATION : None, \
640
pyatspi.ROLE_ARROW : None, \
641
pyatspi.ROLE_CALENDAR : None, \
642
pyatspi.ROLE_CANVAS : None, \
643
pyatspi.ROLE_CHECK_BOX : checkbutton_xpm, \
644
pyatspi.ROLE_CHECK_MENU_ITEM : checkmenuitem_xpm, \
645
pyatspi.ROLE_COLOR_CHOOSER : colorselection_xpm, \
646
pyatspi.ROLE_COLUMN_HEADER : None, \
647
pyatspi.ROLE_COMBO_BOX : combo_xpm, \
648
pyatspi.ROLE_DATE_EDITOR : None, \
649
pyatspi.ROLE_DESKTOP_ICON : None, \
650
pyatspi.ROLE_DESKTOP_FRAME : None, \
651
pyatspi.ROLE_DIAL : None, \
652
pyatspi.ROLE_DIALOG : dialog_xpm, \
653
pyatspi.ROLE_DIRECTORY_PANE : None, \
654
pyatspi.ROLE_DRAWING_AREA : None, \
655
pyatspi.ROLE_FILE_CHOOSER : None, \
656
pyatspi.ROLE_FILLER : None, \
657
pyatspi.ROLE_FONT_CHOOSER : None, \
658
pyatspi.ROLE_FRAME : window_xpm, \
659
pyatspi.ROLE_GLASS_PANE : None, \
660
pyatspi.ROLE_HTML_CONTAINER : None, \
661
pyatspi.ROLE_ICON : image_xpm, \
662
pyatspi.ROLE_IMAGE : image_xpm, \
663
pyatspi.ROLE_INTERNAL_FRAME : None, \
664
pyatspi.ROLE_LABEL : label_xpm, \
665
pyatspi.ROLE_LAYERED_PANE : viewport_xpm, \
666
pyatspi.ROLE_LIST : None, \
667
pyatspi.ROLE_LIST_ITEM : None, \
668
pyatspi.ROLE_MENU : menuitem_xpm, \
669
pyatspi.ROLE_MENU_BAR : menubar_xpm, \
670
pyatspi.ROLE_MENU_ITEM : menuitem_xpm, \
671
pyatspi.ROLE_OPTION_PANE : None, \
672
pyatspi.ROLE_PAGE_TAB : notebook_xpm, \
673
pyatspi.ROLE_PAGE_TAB_LIST : notebook_xpm, \
674
pyatspi.ROLE_PANEL : viewport_xpm, \
675
pyatspi.ROLE_PASSWORD_TEXT : None, \
676
pyatspi.ROLE_POPUP_MENU : None, \
677
pyatspi.ROLE_PROGRESS_BAR : None, \
678
pyatspi.ROLE_PUSH_BUTTON : button_xpm, \
679
pyatspi.ROLE_RADIO_BUTTON : None, \
680
pyatspi.ROLE_RADIO_MENU_ITEM : None, \
681
pyatspi.ROLE_ROOT_PANE : viewport_xpm, \
682
pyatspi.ROLE_ROW_HEADER : None, \
683
pyatspi.ROLE_SCROLL_BAR : vscrollbar_xpm, \
684
pyatspi.ROLE_SCROLL_PANE : scrolledwindow_xpm, \
685
pyatspi.ROLE_SEPARATOR : vseparator_xpm, \
686
pyatspi.ROLE_SLIDER : None, \
687
pyatspi.ROLE_SPIN_BUTTON : spinbutton_xpm, \
688
pyatspi.ROLE_SPLIT_PANE : None, \
689
pyatspi.ROLE_STATUS_BAR : statusbar_xpm, \
690
pyatspi.ROLE_TABLE : table_xpm, \
691
pyatspi.ROLE_TABLE_CELL : treeitem_xpm, \
692
pyatspi.ROLE_TABLE_COLUMN_HEADER : None, \
693
pyatspi.ROLE_TABLE_ROW_HEADER : None, \
694
pyatspi.ROLE_TEAROFF_MENU_ITEM : None, \
695
pyatspi.ROLE_TERMINAL : None, \
696
pyatspi.ROLE_TEXT : text_xpm, \
697
pyatspi.ROLE_TOGGLE_BUTTON : None, \
698
pyatspi.ROLE_TOOL_BAR : toolbar_xpm, \
699
pyatspi.ROLE_TOOL_TIP : None, \
700
pyatspi.ROLE_TREE : tree_xpm, \
701
pyatspi.ROLE_TREE_TABLE : tree_xpm, \
702
pyatspi.ROLE_UNKNOWN : unknown_xpm, \
703
pyatspi.ROLE_VIEWPORT : viewport_xpm, \
704
pyatspi.ROLE_WINDOW : window_xpm, \
705
pyatspi.ROLE_EXTENDED : None, \
706
pyatspi.ROLE_HEADER : None, \
707
pyatspi.ROLE_FOOTER : None, \
708
pyatspi.ROLE_PARAGRAPH : None, \
709
pyatspi.ROLE_RULER : None, \
710
pyatspi.ROLE_APPLICATION : None, \
711
pyatspi.ROLE_AUTOCOMPLETE : None, \
712
pyatspi.ROLE_EDITBAR : None, \
713
pyatspi.ROLE_EMBEDDED : None, \
714
pyatspi.ROLE_LAST_DEFINED: None }
367
nameTextLabel.set_text('')
368
roleNameTextLabel.set_text('')
369
descTextLabel.set_text('')
370
actionsTextLabel.set_text('')
371
textTextView.set_sensitive(False)
372
textTextView.get_buffer().set_text('')
374
global sniff, view, model
376
col = gtk.TreeViewColumn()
377
cellRenderer = gtk.CellRendererPixbuf()
378
col.pack_start(cellRenderer, expand = False)
379
col.set_cell_data_func(cellRenderer, getCellPixbufForNode)
381
cellRenderer = gtk.CellRendererText()
382
col.pack_end(cellRenderer, expand = False)
383
col.set_cell_data_func(cellRenderer, getNodeAttr, 'name')
385
col.set_title('Name')
387
view.insert_column(col, -1)
388
#view.insert_column_with_data_func(-1, "Role Name", \
389
# gtk.CellRendererText(), getNodeAttr, 'roleName')
390
#view.insert_column_with_data_func(-1, "Description", \
391
# gtk.CellRendererText(), getNodeAttr, 'description')
392
for column in view.get_columns():
393
column.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
394
column.set_resizable(True)
398
model = gtk.TreeStore(gobject.TYPE_PYOBJECT, gobject.TYPE_STRING, \
399
gobject.TYPE_STRING, gobject.TYPE_STRING)
400
view.set_model(model)
406
if __name__ == '__main__': main()
718
from dogtail.utils import Lock
719
#We need this to prohibit sniff making(and removing on exit) sniff_refresh lock when importing Node
720
sniff_running = Lock(lockname='sniff_running.lock',randomize=False)
727
if __name__ == '__main__':