1 '''
2 Defines a gtk dialog L{AEChooser} for configuring SUE scripts, devices,
3 profiles, and system settings. Uses a glade XML file to define the shell of the
4 dialog. Dynamically generates the contents of the script, device, and system
5 panes internal panes based on the available L{AEState} settings. Generates the
6 contents of the profile views using information from the
7 L{AccessEngine.AERegistrar}.
8
9 @author: Peter Parente
10 @organization: IBM Corporation
11 @copyright: Copyright (c) 2005, 2007 IBM Corporation
12 @license: The BSD License
13
14 @author: Frank Zenker
15 @organization: IT Science Center Ruegen gGmbH, Germany
16 @copyright: Copyright (c) 2007, 2008 ITSC Ruegen
17 @license: The BSD License
18
19 All rights reserved. This program and the accompanying materials are made
20 available under the terms of the BSD license which accompanies
21 this distribution, and is available at
22 U{http://www.opensource.org/licenses/bsd-license.php}
23 '''
24 import pygtk
25 pygtk.require('2.0')
26 import gtk, gobject, atk
27 from Tools import Atk
28 import gtk.glade
29 from AccessEngine import AEChooser
30 from AccessEngine import AEState
31 from Tools.i18n import _, DOMAIN
32 from GTKUIEView import *
33 import weakref, logging
34
35 __uie__ = dict(kind='chooser')
36
37 log = logging.getLogger('SettingsChooser')
38
224
226 '''
227 SUE settings dialog for configuring scripts, devices, profiles, and other
228 system settings.
229
230 @ivar factory: Factory used to create gtk widget for settings
231 @type factory: L{WidgetFactory}
232 @ivar dialog: Dialog widget for configuring SUE settings
233 @type dialog: gtk.Dialog
234 @ivar system: SUE system wide settings
235 @type system: L{AEState}
236 @ivar scripts: L{AccessEngine.AEScript} settings keyed by human readable
237 script name
238 @type scripts: dictionary of string : L{AEState}
239 @ivar devices: Per L{AEOutput} device settings keyed by human readable device
240 name
241 @type devices: dictionary of string : L{AEState}
242 @ivar installed: All UIE class names, human readable names, and
243 descriptions installed keyed by their type
244 @type installed: dictionary of string : 3-tuple of string
245 @ivar associated: All UIE class names associated with this profile
246 @type associated: list of string
247 @ivar section_nb: Main tabbed panel
248 @type section_nb: gtk.Notebook
249 @ivar script_tv: List of configurable L{AccessEngine.AEScript}s
250 @type script_tv: gtk.TreeView
251 @ivar device_tv: List of configurable L{AEInput} and L{AEOutput} devices
252 @type device_tv: gtk.TreeView
253 @ivar system_vp: Panel for system settings
254 @type system_vp: gtk.Viewport
255 @ivar profile_scripts: Aids selection of L{AccessEngine.AEScript}s to
256 associate with the profile
257 @type profile_scripts: L{GTKUIEView.UIEView}
258 @ivar profile_monitors: Aids selection of L{AEMonitor}s to associate with the
259 profile
260 @type profile_monitors: L{GTKUIEView.UIEView}
261 @ivar profile_choosers: View of L{AEChooser}s installed
262 @type profile_choosers: L{GTKUIEView.UIEView}
263 @ivar profile_devices: Aids selection of preferred L{AEOutput} and L{AEInput}
264 devices associated with this profile
265 @type profile_devices: L{GTKUIEView.UIEView}
266 @cvar FRAME_MARKUP: Pango markup for frame panels to make text larger
267 @type FRAME_MARKUP: string
268 @cvar SCRIPT: Tab number for configuring scripts
269 @type SCRIPT: integer
270 @cvar DEVICE: Tab number for configuring devices
271 @type DEVICE: integer
272 @cvar SYSTEM: Tab number for configuring system settings
273 @type SYSTEM: integer
274 @cvar PROFILE: Tab number for configuring the profile
275 @type PROFILE: integer
276 @cvar RAISE: Raise priority of a L{AEUserInterface} for handling events or getting
277 loaded
278 @type RAISE: integer
279 @cvar LOWER: Lower priority of a L{AEUserInterface} for handling events or getting
280 loaded
281 @type LOWER: integer
282 @cvar TO_LOAD: Indicates a L{AEUserInterface} is now set to be loaded
283 @type TO_LOAD: integer
284 @cvar TO_UNLOAD: Indicates a L{AEUserInterface} is now set to be unloaded
285 @type TO_UNLOAD: integer
286 '''
287 GLOBAL_SINGLETON = True
288 SCRIPT = 0
289 DEVICE = 1
290 SYSTEM = 2
291 PROFILE = 3
292 FRAME_MARKUP = '<span size="xx-large" weight="bold">%s</span>'
293
294
295 RAISE = RAISE
296 LOWER = LOWER
297 TO_LOAD = TO_LOAD
298 TO_UNLOAD = TO_UNLOAD
299
300 - def init(self, system, scripts, installed, associated, profile, devices,
301 timestamp, **kwargs):
302 '''
303 Creates and shows the settings dialog and its components
304
305 @param system: SUE system wide settings
306 @type system: L{AEState}
307 @param scripts: L{AccessEngine.AEScript} settings keyed by script name
308 @type scripts: dictionary of string: L{AEState}
309 @param installed: All UIE class names, human readable names, and
310 descriptions installed keyed by their type
311 @type installed: dictionary of string: list of 3-tuple of string
312 @param associated: All UIE class names, human readable names, and
313 descriptions associated with this profile keyed by their type
314 @type associated: dictionary of string: list of 3-tuple of string
315 @param profile: Name of the profile
316 @type profile: string
317 @param devices: L{AEOutput}/L{AEInput} settings keyed by device name
318 @type devices: dictionary of string : L{AEState}
319 @param timestamp: Time at which input was given indicating the start of
320 this chooser
321 @type timestamp: float
322 '''
323
324 self.factory = WidgetFactory()
325
326
327 self.system = system
328 self.scripts = scripts
329 self.devices = devices
330
331
332 gtk.glade.set_custom_handler(self._createCustomWidget)
333 source = gtk.glade.XML(self._getResource('sue_settings.glade'),
334 'main dialog', DOMAIN)
335
336
337 self.dialog = source.get_widget('main dialog')
338
339
340
341 self.tips = gtk.Tooltips()
342 self.section_nb = source.get_widget('section notebook')
343 self.script_tv = source.get_widget('script treeview')
344 script_label = source.get_widget('script label')
345 script_vp = source.get_widget('script viewport')
346 self.device_tv = source.get_widget('device treeview')
347 device_label = source.get_widget('device label')
348 device_vp = source.get_widget('device viewport')
349 self.system_vp = source.get_widget('system viewport')
350
351 pl = source.get_widget('profile label')
352 pl.set_text(pl.get_text() % profile.title())
353
354
355 self.script_tv.set_model(gtk.ListStore(gobject.TYPE_STRING))
356 self.script_tv.set_data('subsection label', script_label)
357 self.script_tv.set_data('subsection viewport', script_vp)
358
359 self.device_tv.set_model(gtk.ListStore(gobject.TYPE_STRING))
360 self.device_tv.set_data('subsection label', device_label)
361 self.device_tv.set_data('subsection viewport', device_vp)
362
363
364 source.signal_autoconnect(self)
365
366
367 self._createScriptSettingsView()
368 self._createDeviceSettingsView()
369 self._createProfileView(associated, installed)
370 self._createSystemSettingsView()
371
372 self.activate(timestamp, present=False)
373
374 - def activate(self, timestamp, present=True, **kwargs):
375 '''
376 Try to bring the window to the foreground.
377 '''
378 try:
379
380 self.dialog.window.set_user_time(timestamp)
381 except AttributeError:
382 pass
383 if present:
384 try:
385 self.dialog.present()
386 except gtk.Warning:
387
388 pass
389
404
415
426
438
449
451 '''
452 Populates the system settings profile panel.
453 '''
454 layout = self._populateSection(self.system.getGroups(), self.system_vp)
455 self.system_vp.add(layout)
456 self.system_vp.show_all()
457
459 '''
460 Populates all of the profile view tab panels.
461 '''
462 order = (('scripts', self.profile_scripts),
463 ('devices', self.profile_devices),
464 ('monitors', self.profile_monitors),
465 ('choosers', self.profile_choosers))
466 for name, view in order:
467 loaded = associated[name]
468 unloaded = set(installed[name])-set(loaded)
469 view.setData(loaded, unloaded)
470
472 '''
473 Populates the list of configurable L{AccessEngine.AEScript}s and selects the
474 first to generate its settings panel.
475 '''
476
477 model = self.script_tv.get_model()
478 model.clear()
479 cols = self.script_tv.get_columns()
480 map(self.script_tv.remove_column, cols)
481
482
483 crt = gtk.CellRendererText()
484 tvc = gtk.TreeViewColumn('', crt, text=0)
485 self.script_tv.append_column(tvc)
486
487
488 if len(self.scripts) > 0:
489 keys = self.scripts.keys()
490 keys.sort()
491 for name in keys:
492 try:
493 self.scripts[name].getGroups()
494 except NotImplementedError:
495
496 continue
497 model.append([name])
498
499 self.script_tv.set_cursor((0,))
500
502 '''
503 Populates the list of configurable L{AEInput} and L{AEOutput} devices and
504 selects the first to generate its settings panel.
505 '''
506
507 model = self.device_tv.get_model()
508 model.clear()
509 cols = self.device_tv.get_columns()
510 map(self.device_tv.remove_column, cols)
511
512
513 crt = gtk.CellRendererText()
514 tvc = gtk.TreeViewColumn('', crt, text=0)
515 self.device_tv.append_column(tvc)
516
517
518 if len(self.devices) > 0:
519 keys = self.devices.keys()
520 keys.sort()
521 for name in keys:
522 try:
523 self.devices[name].getGroups()
524 except NotImplementedError:
525
526 continue
527 model.append([name])
528
529 self.device_tv.set_cursor((0,))
530
551
553 '''
554 Prepares a settings panel for viewing when the name of a
555 L{AccessEngine.AEScript} or L{AEInput}/L{AEOutput} device is selected from
556 the list of configurable elements.
557
558 @param tv: Treeview in which a configurable item is selected
559 @type tv: gtk.TreeView
560 '''
561
562 model = tv.get_model()
563 label = tv.get_data('subsection label')
564 viewport = tv.get_data('subsection viewport')
565
566 path, col = tv.get_cursor()
567
568
569
570
571
572
573 name = unicode(model[path][0])
574
575
576 child = viewport.get_child()
577 if child:
578 viewport.remove(child)
579
580
581 label.set_markup(self.FRAME_MARKUP % name)
582
583 if self.script_tv == tv:
584 group = self.scripts[name].getGroups()
585 try:
586 layout = self._populateSection(group, viewport)
587 viewport.add(layout)
588 except AssertionError:
589 log.warn('empty script settings group')
590 elif self.device_tv == tv:
591 group = self.devices[name].getGroups()
592 try:
593 layout = self._populateSection(group, viewport)
594 viewport.add(layout)
595 except AssertionError:
596 log.warn('empty device settings group')
597 viewport.show_all()
598
600 '''
601 Populate a section pane with widgets representing the L{AEState.Setting}s
602 in the group. The container is the top level widget in which other widgets
603 should be made. If recursive groups exist, their settings will be properly
604 represented by widgets in a widget sub-container.
605
606 @param group: Group organizing a subset of settings in the state
607 @type group: L{AEState.Setting.Group}
608 @param viewport: Reference to the viewport to populate with widgets
609 @type viewport: gtk.Viewport
610 @raise AssertionError: When there are no settings in the group
611 '''
612 n = len(group)
613 if n < 1:
614
615 raise AssertionError
616
617 table = gtk.Table(n, 2)
618 table.set_row_spacings(5)
619 table.set_col_spacings(5)
620
621 for row, setting in group.iterSettings(reverse=True):
622 widget, label = self.factory.create(setting)
623 if isinstance(widget, gtk.Frame):
624
625 layout = self._populateSection(setting, viewport)
626 layout.set_border_width(5)
627
628 widget.add(layout)
629
630
631
632 table.attach(widget, 0, 2, n-row-1, n-row, yoptions=gtk.FILL,
633 xpadding=5)
634 else:
635 if isinstance(widget, gtk.CheckButton):
636
637 table.attach(widget, 0, 2, n-row-1, n-row, yoptions=gtk.FILL)
638 else:
639
640 Atk.setRelation(widget, atk.RELATION_LABELLED_BY, label)
641 Atk.setRelation(label, atk.RELATION_LABEL_FOR, widget)
642
643
644
645 table.attach(widget, 1, 2, n-row-1, n-row, yoptions=gtk.FILL)
646 table.attach(label, 0, 1, n-row-1, n-row, gtk.FILL, gtk.FILL, 5, 0)
647
648 if setting.description:
649
650
651 self.tips.set_tip(widget, setting.description)
652
653 Atk.setNameDesc(widget, description=setting.description)
654
655 widget.connect('focus', self._onScrollToFocus, viewport)
656 return table
657
659 '''
660 Gets lists of L{AEUserInterface} class names currently set to be associated
661 with the active profile.
662
663 @return: Dictionary of associated UIEs keyed by their kind
664 @rtype: dictionary of string : list of string
665 '''
666 d = {}
667 if self.profile_scripts.isDirty():
668 d['scripts'] = self.profile_scripts.getCurrentUIEs()[0]
669 if self.profile_monitors.isDirty():
670 d['monitors'] = self.profile_monitors.getCurrentUIEs()[0]
671 if self.profile_devices.isDirty():
672 d['devices'] = self.profile_devices.getCurrentUIEs()[0]
673 return d
674
675 - def _onOK(self, widget):
676 '''
677 Closes the dialog and sends a signal indicating the OK action.
678
679 @param widget: Source of GUI event
680 @type widget: gtk.Widget
681 '''
682 self._signal(self.OK, system=self.system, scripts=self.scripts,
683 devices=self.devices, associated=self._getAssociated())
684 self.close()
685
687 '''
688 Closes the dialog and sends a signal indicating the Cancel action.
689
690 @param widget: Source of GUI event
691 @type widget: gtk.Widget
692 '''
693 self._signal(self.CANCEL, system=self.system, scripts=self.scripts,
694 devices=self.devices)
695 self.close()
696
698 '''
699 Sends a signal indicating the Apply action.
700
701 @param widget: Source of GUI event
702 @type widget: gtk.Widget
703 '''
704
705 self._signal(self.APPLY, system=self.system, scripts=self.scripts,
706 devices=self.devices, associated=self._getAssociated())
707
709 '''
710 Closes the chooser, preventing further chooser interaction with the user.
711 '''
712 self.tips = None
713 self.factory = None
714 gtk.glade.set_custom_handler(lambda x: None)
715 self.dialog.destroy()
716
718 '''
719 Gets the name of the chooser.
720
721 @return: Human readable name of the chooser
722 @rtype: string
723 '''
724 return _('SUE Settings')
725
727 '''
728 Updates the list of devices according to those that are now loaded.
729
730 @param devices: Per L{AEOutput}/L{AEInput} settings keyed by device name
731 @type devices: dictionary of string : L{AEState}
732 '''
733 self.devices = devices
734 self._createDeviceSettingsView()
735