1'''
2Defines a base class having tools common to the core and the plugins.
3
4@author: Eitan Isaacson
5@organization: IBM Corporation
6@copyright: Copyright (c) 2006, 2007 IBM Corporation
7@license: BSD
8
9All rights reserved. This program and the accompanying materials are made
10available under the terms of the BSD which accompanies this distribution, and
11is available at U{http://www.opensource.org/licenses/bsd-license.php}
12'''
13import os
14import pickle
15import weakref
16
17import traceback
18import functools
19
20class Tools(object):
21  '''
22  A class with some common methods that more than a few classes will need.
23
24  @cvar SETTINGS_PATH: Directory in which the L{SETTINGS_FILE} resides.
25  @type SETTINGS_PATH: string
26  @cvar SETTINGS_FILE: The file that contains information we with to persist
27  across session.
28  @type SETTINGS_FILE: string
29  @ivar my_app_id: Unique L{Accessibility.Application} ID of current
30  instance.
31  @type my_app_id: integer
32  '''
33
34  def isMyApp(self, acc):
35    '''
36    Checks if a given L{Accessibility.Accessible} belongs to this current
37    app instance. This is useful for avoiding recursion and such.
38
39    @param acc: The given L{Accessibility.Accessible} to check.
40    @type acc: L{Accessibility.Accessible}
41    @return: True if L{acc} is a member of current app instance.
42    @rtype: boolean
43    '''
44    if not acc:
45      return False
46    try:
47      app = acc.getApplication()
48    except Exception as e:
49      return False
50    try:
51      app_id = app.id
52    except:
53      return False
54    if hasattr(self, 'my_app_id'):
55      if self.my_app_id == app_id:
56        return True
57    else:
58      if app.description == str(os.getpid()):
59        self.my_app_id = app_id
60        return True
61    return False
62
63
64class Proxy(object):
65  '''
66  Our own proxy object which enables weak references to bound and unbound
67  methods and arbitrary callables. Pulls information about the function,
68  class, and instance out of a bound method. Stores a weak reference to the
69  instance to support garbage collection.
70  '''
71  def __init__(self, cb):
72    try:
73      try:
74        self.inst = weakref.ref(cb.__self__)
75      except TypeError:
76        self.inst = None
77      self.func = cb.__func__
78      self.klass = cb.__self__.__class__
79    except AttributeError:
80      self.inst = None
81      self.func = cb.__func__
82      self.klass = None
83
84  def __call__(self, *args, **kwargs):
85    '''
86    Proxy for a call to the weak referenced object. Take arbitrary params to
87    pass to the callable.
88
89    @raise ReferenceError: When the weak reference refers to a dead object
90    '''
91    if self.inst is not None and self.inst() is None:
92      return
93    elif self.inst is not None:
94      # build a new instance method with a strong reference to the instance
95      mtd = self.func.__get__(self.inst(), self.klass)
96    else:
97      # not a bound method, just return the func
98      mtd = self.func
99    # invoke the callable and return the result
100    return mtd(*args, **kwargs)
101
102  def __eq__(self, other):
103    '''
104    Compare the held function and instance with that held by another proxy.
105
106    @param other: Another proxy object
107    @type other: L{Proxy}
108    @return: Whether this func/inst pair is equal to the one in the other proxy
109      object or not
110    @rtype: boolean
111    '''
112    try:
113      return self.func == other.func and self.inst() == other.inst()
114    except Exception:
115      return False
116
117  def __ne__(self, other):
118    '''
119    Inverse of __eq__.
120    '''
121    return not self.__eq__(other)
122
123def parseColorString(color_string):
124  '''
125  Parse a string representation of a 24-bit color, and a 8 bit alpha mask.
126
127  @param color_string: String in the format: #rrbbggaa.
128  @type color_string: string
129
130  @return: A color string in the format of #rrggbb, and an opacity value
131  of 0.0 to 1.0
132  @rtype: tuple of string and float.
133  '''
134  return color_string[:-2], int(color_string[-2:], 16)/255.0
135
136def getTreePathBoundingBox(treeview, path, col):
137  '''
138  Get bounding box of given tree path.
139  '''
140  gdkwindow = treeview.window
141  x, y = treeview.allocation.x, treeview.allocation.y
142  while gdkwindow:
143    window_x, window_y = gdkwindow.get_position()
144    x += window_x
145    y += window_y
146    gdkwindow = gdkwindow.get_parent()
147  rect = treeview.get_cell_area(path, col)
148  rect.x, rect.y = treeview.tree_to_widget_coords(rect.x, rect.y)
149  rect.x += x
150  rect.y += y
151  return rect
152
153def logException(func):
154  '''
155  Handle (and log) the exceptions that are coming from plugins
156  '''
157  @functools.wraps(func)
158  def newfunc(*args, **kwargs):
159    # use Exception otherwise KeyboardInterrupt won't get through
160    try:
161      return func(*args, **kwargs)
162    except Exception:
163      traceback.print_exc()
164  return newfunc
165
166class ToolsAccessor(Tools):
167  '''
168  By following the recommendation on
169  https://bugzilla.gnome.org/show_bug.cgi?id=723081#c4, this Accessor allows us
170  to wrap every plugin's method and in order to catch all possible exceptions
171  and print them appropiately.
172  '''
173  def __init__(self, plugin):
174    self.plugin = plugin
175
176    @logException
177    def method(self):
178      return self.plugin.method()
179