Module GTKUIEView
[hide private]
[frames] | no frames]

Source Code for Module GTKUIEView

  1  ''' 
  2  Defines reusable gtk widgets for ordering L{AEUserInterface}s and setting them to 
  3  load or not load at runtime. 
  4   
  5  @var RAISE: Raise priority of a L{AEUserInterface} for handling events or getting  
  6    loaded 
  7  @type RAISE: integer 
  8  @var LOWER: Lower priority of a L{AEUserInterface} for handling events or getting  
  9    loaded 
 10  @type LOWER: integer 
 11  @var TO_LOAD: Indicates a L{AEUserInterface} is now set to be loaded 
 12  @type TO_LOAD: integer 
 13  @var TO_UNLOAD: Indicates a L{AEUserInterface} is now set to be unloaded 
 14  @type TO_UNLOAD: integer 
 15   
 16  @author: Peter Parente 
 17  @organization: IBM Corporation 
 18  @copyright: Copyright (c) 2005, 2007 IBM Corporation 
 19  @license: The BSD License 
 20   
 21  All rights reserved. This program and the accompanying materials are made  
 22  available under the terms of the BSD license which accompanies 
 23  this distribution, and is available at 
 24  U{http://www.opensource.org/licenses/bsd-license.php} 
 25  ''' 
 26  import pygtk 
 27  pygtk.require('2.0') 
 28  import gtk, gobject 
 29  import gtk.glade 
 30  import weakref 
 31  from Tools.i18n import _, DOMAIN 
 32   
 33  # signal constants 
 34  RAISE = 0 
 35  LOWER = 1 
 36  TO_LOAD = 2 
 37  TO_UNLOAD = 3 
 38   
39 -class UIEView(object):
40 ''' 41 Reusable viewer for L{AEUserInterface}s. Shows the name and description of all 42 elements. If constructed with the ordered flag set, allows reordering of 43 elements using raise and lower buttons. If constructed with the activatable 44 flag set, allows for selecting UIEs to load or not load. 45 46 @ivar chooser: Chooser that is showing this view 47 @type chooser: weakref.proxy for L{AccessEngine.AEChooser} 48 @ivar loaded: Class names, names, and descriptions of all loaded 49 L{AccessEngine.AEUserInterface}s 50 @type loaded: list of 3-tuple of string 51 @ivar unloaded: Class names, names, and descriptions of all L{AEUserInterface}s 52 @type unloaded: list of 3-tuple of string 53 @ivar uie_tv: List view of all L{AccessEngine.AEUserInterface}s 54 @type uie_tv: gtk.TreeView 55 @ivar lower_button: button to lower priority of L{AccessEngine.AEScript}s 56 @type lower_button: gtk.Button 57 @ivar raise_button: button to raise priority of L{AccessEngine.AEScript}s 58 @type raise_button: gtk.Button 59 @ivar changed: Has the view been maniuplated in any way? 60 @type changed: boolean 61 '''
62 - def __init__(self, chooser, ordered=True, activatable=True, wrap=450):
63 ''' 64 Initializes the view by constructing the list view and populating it with 65 data. 66 67 @param chooser: Chooser that is showing this view 68 @type chooser: L{AccessEngine.AEChooser} 69 @param ordered: Can loaded items be reordered? 70 @type ordered: boolean 71 @param activatable: Can items be set to load/unload? 72 @type activatable: boolean 73 @param wrap: Width in pixels at which to wrap description text 74 @type wrap: integer 75 ''' 76 self.changed = False 77 # store a reference to the chooser for signaling 78 self.chooser = weakref.proxy(chooser) 79 80 # determine which root widget to use 81 if ordered: 82 name = 'ordered vbox' 83 else: 84 name = 'unordered scrollwindow' 85 86 # load the glade file for this widget 87 source = gtk.glade.XML(chooser._getResource('uie_widget.glade'), 88 name, DOMAIN) 89 90 # get top level widgets 91 self.uie_tv = source.get_widget('uie treeview') 92 self.root = source.get_widget(name) 93 94 # configure the list view 95 model = gtk.ListStore(str, str, str, bool) 96 self.uie_tv.set_model(model) 97 98 # name column 99 col = gtk.TreeViewColumn(_('Name')) 100 rend = gtk.CellRendererText() 101 if activatable: 102 rend2 = gtk.CellRendererToggle() 103 rend2.set_property('activatable', True) 104 col.pack_end(rend2, False) 105 col.set_attributes(rend2, active=3) 106 # watch for toggle events 107 rend2.connect('toggled', self._onToggleLoad, model, 3) 108 col.pack_start(rend, True) 109 col.set_attributes(rend, text=1) 110 self.uie_tv.append_column(col) 111 # description column 112 rend = gtk.CellRendererText() 113 rend.set_property('wrap-width', wrap) 114 col = gtk.TreeViewColumn(_('Description'), rend, text=2) 115 self.uie_tv.append_column(col) 116 117 # get buttons for raising/lowering UIEs 118 self.raise_button = source.get_widget('raise button') 119 self.lower_button = source.get_widget('lower button') 120 121 # connect signal handlers 122 source.signal_autoconnect(self)
123
124 - def isDirty(self):
125 ''' 126 Gets if the order or load/unload status has been changed by the user. 127 128 @return: Has the view been manipulated in any way? 129 @rtype: boolean 130 ''' 131 return self.changed
132
133 - def setData(self, loaded, unloaded):
134 ''' 135 Sets the L{AEUserInterface} data to be shown by the list view. Puts the 136 cursor on the first item if it exists. 137 138 @param loaded: Class names, names, and descriptions of all loaded 139 L{AEUserInterface}s 140 @type loaded: list of 3-tuple of string 141 @param unloaded: Class names, names, and descriptions of all 142 L{AEUserInterface}s 143 @type unloaded: list of 3-tuple of string 144 ''' 145 model = self.uie_tv.get_model() 146 # store loaded and unloaded scripts with an extra boolean indicating state 147 self.loaded = [metadata+(True,) for metadata in loaded] 148 self.unloaded = [metadata+(False,) for metadata in unloaded] 149 150 # populate the data model 151 map(model.append, self.loaded) 152 map(model.append, self.unloaded) 153 154 # select the first item in the loaded list if it is not empty 155 first = model.get_iter_first() 156 if first is not None: 157 self.uie_tv.set_cursor_on_cell(model.get_path(first))
158
159 - def _getLoadedCount(self):
160 ''' 161 @return: Count of the number of L{AccessEngine.AEScript}s set to load 162 @rtype: integer 163 ''' 164 scripts = self.uie_tv.get_model() 165 return len([row[0] for row in scripts if row[3]])
166
167 - def getWidget(self):
168 ''' 169 Gets the root widget for the view, either the box when the view supports 170 UIE ordering or the tree view when it does not. 171 172 @return: Root widget of the view for integration in another UI 173 @rtype: gtk.Widget 174 ''' 175 return self.root
176
177 - def getAllUIEs(self):
178 ''' 179 Returns the list of all L{AEUserInterface}s. 180 181 @return: All L{AEUserInterface} class names 182 @rtype: list of string 183 ''' 184 return [row[0] for row in model]
185
186 - def getCurrentUIEs(self):
187 ''' 188 Returns the new loaded/unloaded L{AEUserInterface} configuration. 189 190 @return: 2-tuple of loaded, unloaded L{AEUserInterface} class names 191 @rtype: 2-tuple of list, list 192 ''' 193 model = self.uie_tv.get_model() 194 loaded = [row[0] for row in model if row[3]] 195 unloaded = [row[0] for row in model if not row[3]] 196 return loaded, unloaded
197
198 - def _onToggleLoad(self, cell, path, model, column):
199 ''' 200 Sets a L{AEUserInterface} to load or unload. Moves the UIE to the top of the 201 load order if it is now set to load or to the bottom of the entire list if 202 it is set to unload. 203 204 @param cell: Cell toggled 205 @type cell: gtk.TreeViewCell 206 @param path: Path to the row toggled 207 @type path: tuple 208 @param model: Model for the gtk.TreeView 209 @type model: gtk.ListStore 210 @param column: Column in the model that the toggle should affect 211 @type column: integer 212 ''' 213 self.changed = True 214 val = not model[path][column] 215 # toggle model data 216 model[path][column] = val 217 if self.raise_button is None: 218 # don't do auto reordering if ordering is not supported 219 return 220 if val == True: 221 # move to start of list 222 iter = model.get_iter(path) 223 model.move_after(iter, None) 224 self.uie_tv.scroll_to_cell((0,)) 225 self.chooser._signal(TO_LOAD, name=model[0][1]) 226 self.raise_button.set_sensitive(False) 227 self.lower_button.set_sensitive(True) 228 else: 229 # move to end of list 230 iter = model.get_iter(path) 231 last = reduce(lambda x, s: s + int(x), [r[3] for r in model], 0) 232 new_path = (last,) 233 new_iter = model.get_iter(new_path) 234 model.move_after(iter, new_iter) 235 self.uie_tv.scroll_to_cell(new_path) 236 self.chooser._signal(TO_UNLOAD, name=model[last][1]) 237 self.raise_button.set_sensitive(False) 238 self.lower_button.set_sensitive(False)
239
240 - def _onRaise(self, widget):
241 ''' 242 Handles 'raise' button 'clicked' events. Determines the current selected 243 item in 'loaded' list, raises its index (loaded priority) so it displays at 244 a higher position. 245 246 @param widget: Source of GUI event 247 @type widget: gtk.Widget 248 ''' 249 model = self.uie_tv.get_model() 250 path, col = self.uie_tv.get_cursor() 251 if path is not None: 252 self.changed = True 253 row = path[0] 254 new_path = (row-1,) 255 # swap selected row with row immediately preceding it 256 iter1 = model.get_iter(path) 257 iter2 = model.get_iter(new_path) 258 model.swap(iter1, iter2) 259 # notify the script so it can say something intelligent 260 if row < self._getLoadedCount(): 261 below = model[row][1] 262 else: 263 below = None 264 if row-2 >= 0: 265 above = model[row-2][1] 266 else: 267 above = None 268 self.chooser._signal(RAISE, above=above, below=below, 269 name=model[row-1][1]) 270 self.uie_tv.scroll_to_cell(new_path) 271 self._onCursorChanged(self.uie_tv)
272
273 - def _onLower(self, widget):
274 ''' 275 Handles 'lower' button 'clicked' events. Determines the current selected 276 item in 'loaded' list, lower its index (loaded priority) so it displays at 277 a lower position. 278 279 @param widget: Source of GUI event 280 @type widget: gtk.Widget 281 ''' 282 model = self.uie_tv.get_model() 283 path, col = self.uie_tv.get_cursor() 284 if path is not None: 285 self.changed = True 286 row = path[0] 287 new_path = (row+1,) 288 # swap selected row with row immediately preceding it 289 iter1 = model.get_iter(path) 290 iter2 = model.get_iter(new_path) 291 model.swap(iter1, iter2) 292 # notify the script so it can say something intelligent 293 if row+2 < self._getLoadedCount(): 294 below = model[row+2][1] 295 else: 296 below = None 297 if row >= 0: 298 above = model[row][1] 299 else: 300 above = None 301 self.chooser._signal(LOWER, above=above, below=below, 302 name=model[row+1][1]) 303 # scroll the cell into view 304 self.uie_tv.scroll_to_cell(new_path) 305 # announce the new selection 306 self._onCursorChanged(self.uie_tv)
307
308 - def _onCursorChanged(self, tv):
309 ''' 310 Determines whether or not raise/lower button should be enabled (in Gtk 311 terms, whether or not the 'sensitive' flag is set to True). If current 312 selection is at top, disable 'raise' button; if selection is at bottom, 313 disable 'lower' button. This only affects usability, not functionality; 314 that is, gtk doesn't dim the buttons automatically, the onRaise/onLower 315 methods discard requests to move a table item beyond its proper scope. 316 317 Does nothing if the widget is not ordered. 318 319 @param tv: View of the loaded or unloaded L{AEUserInterface}s 320 @type tv: gtk.TreeView 321 ''' 322 if self.lower_button is None: 323 # not ordered, so bail 324 return 325 model = tv.get_model() 326 path, col = tv.get_cursor() 327 can_up = can_down = True 328 n = self._getLoadedCount() 329 if path[0] == 0: 330 # top of the list 331 can_up = False 332 can_down = True 333 elif path[0] == n-1: 334 # last loaded item 335 can_up = True 336 can_down = False 337 elif path is None or n == 0 or path[0] >= n: 338 # unloaded item or empty list 339 can_up = can_down = False 340 # enable/disable raise/lower appropriately 341 self.lower_button.set_sensitive(can_down) 342 self.raise_button.set_sensitive(can_up)
343