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