1from gi.repository import Gtk as gtk
2from gi.repository import Gdk as gdk
3from gi.repository.Gio import Settings as GSettings
4
5from .plugin import PluginView
6from .i18n import _, N_
7from .accessible_treeview import *
8from . import ui_manager
9from .ui_manager import uimanager
10
11GSCHEMA = 'org.a11y.Accerciser'
12
13class AccerciserMainWindow(gtk.Window):
14  '''
15  Main window class.
16
17  @ivar statusbar: Main window's status bar.
18  @type statusbar: gtk.Statusbar
19  @ivar treeview: Main accessible tree view.
20  @type treeview: L{AccessibleTreeView}
21  @ivar pluginview1: Top plugin area.
22  @type pluginview1: L{PluginView}
23  @ivar pluginview2: Bottom plugin area
24  @type pluginview2: L{PluginView}
25  @ivar main_actions: Main action group.
26  @type main_actions: gtk.ActionGroup
27  @ivar _vpaned: Vertical paned.
28  @type _vpaned: gtk.VPaned
29  @ivar _hpaned: Horizontal paned.
30  @type _hpaned: gtk.HPaned
31  '''
32  __gtype_name__ = "AccerciserMainWindow"
33
34  def __init__(self, node):
35    '''
36    Initialize the window.
37
38    @param node: Main application's node.
39    @type node: L{Node}
40    '''
41    gtk.Window.__init__(self)
42    self.set_icon_name('accerciser')
43    self.set_title(_('Accerciser Accessibility Explorer'))
44    self.connect('key-press-event', self._onKeyPress)
45    node.connect('blink-done', self._onBlinkDone)
46    self.gsettings = GSettings.new(GSCHEMA)
47    width = self.gsettings.get_int('window-width') or 640
48    height = self.gsettings.get_int('window-height') or 640
49    self.set_default_size(width, height)
50    self.add_accel_group(ui_manager.uimanager.get_accel_group())
51    # Populate window
52    self._populateUI(node)
53
54    selection = self.treeview.get_selection()
55    selection.connect('changed', self._onSelectionChanged)
56
57  def _populateUI(self, node):
58    '''
59    Populate the top level window widget.
60
61    @param node: Main application's node.
62    @type node: L{Node}
63    '''
64    main_vbox = gtk.VBox()
65    menu_bar = ui_manager.uimanager.get_widget(ui_manager.MAIN_MENU_PATH)
66    main_vbox.pack_start(menu_bar, False, True, 0)
67    self._vpaned = gtk.VPaned()
68    self._vpaned.set_position(350)
69    self._vpaned.set_name('vpaned')
70    main_vbox.pack_start(self._vpaned, True, True, 0)
71    self.statusbar = gtk.Statusbar()
72    main_vbox.pack_start(self.statusbar, False, True, 0)
73    self._hpaned = gtk.HPaned()
74    self._hpaned.set_position(250)
75    self._hpaned.set_name('hpaned')
76    self._vpaned.add1(self._hpaned)
77    self.pluginview1 = PluginView(N_('Top panel'))
78    self.pluginview2 = PluginView(N_('Bottom panel'))
79    self.pluginview2.connect('page_added',
80                              self._onBottomPanelChange, 'added')
81    self.pluginview2.connect('page_removed',
82                              self._onBottomPanelChange, 'removed')
83    self.pluginview2.connect_after('realize', self._onBottomPanelRealize)
84    self._vpaned.add2(self.pluginview2)
85    self._hpaned.add2(self.pluginview1)
86    sw = gtk.ScrolledWindow()
87    sw.set_policy(gtk.PolicyType.AUTOMATIC, gtk.PolicyType.AUTOMATIC)
88    sw.set_shadow_type(gtk.ShadowType.IN)
89    self.treeview = AccessibleTreeView(node)
90    ui_manager.uimanager.insert_action_group(self.treeview.action_group, 0)
91    for action in self.treeview.action_group.list_actions():
92      merge_id = ui_manager.uimanager.new_merge_id()
93      action_name = action.get_name()
94      ui_manager.uimanager.add_ui(merge_id, ui_manager.TREE_ACTIONS_PATH,
95                                  action_name, action_name,
96                                  gtk.UIManagerItemType.MENUITEM, False)
97
98    merge_id = ui_manager.uimanager.new_merge_id()
99    action_name = self.treeview.refresh_current_action.get_name()
100    ui_manager.uimanager.add_ui(merge_id, ui_manager.POPUP_MENU_PATH,
101                                 action_name, action_name,
102                                 gtk.UIManagerItemType.MENUITEM, False)
103
104    sw.add(self.treeview)
105    self._hpaned.add1(sw)
106
107    for paned in (self._vpaned, self._hpaned):
108      if not self.gsettings.get_int(paned.get_name()): continue
109      paned_position = self.gsettings.get_int(paned.get_name())
110      paned.set_position(paned_position)
111      setattr(paned, 'last_position', paned.get_position())
112
113    self.add(main_vbox)
114
115  def _onBottomPanelChange(self, pluginview, page, page_num, action):
116    '''
117    Callback for changes to the bottom L{PluginView}'s children. If there are no
118    tabs, shrink the paned.
119
120    @param pluginview: The L{PluginView} that emitted the signal.
121    @type pluginview: L{PluginView}
122    @param page: The child widget affected.
123    @type page: L{gtk.Widget}
124    @param page_num: the new page number for page.
125    @type page_num: integer
126    @param action: The type of event that accured, either "removed" or "added"
127    @type action: string
128    '''
129    if pluginview.get_n_pages() == 1 and action == 'added':
130      last_pos = getattr(self._vpaned, 'last_position')
131      self._vpaned.set_position(last_pos or 350)
132    elif pluginview.get_n_pages() == 0:
133      setattr(self._vpaned, 'last_position', self._vpaned.get_position())
134      self._vpaned.set_position(self._vpaned.get_allocated_height() - 30)
135
136  def _onBottomPanelRealize(self, pluginview):
137    if pluginview.get_n_pages() == 0:
138      self._vpaned.set_position(self._vpaned.get_allocated_height() - 30)
139
140  def _onKeyPress(self, widget, event):
141    '''
142    Callback for a keypress event in the main window.
143    Used for navigating plugin tabs (<alt>+num).
144
145    @param widget: The widget that emitted the event.
146    @type widget: L{gtk.Widget}
147    @param event: The event that accured.
148    @type event: L{gtk.gdk.Event}
149    '''
150    if event.state & gdk.ModifierType.MOD1_MASK and \
151          event.keyval in range(gdk.keyval_from_name('0'),
152                                 gdk.keyval_from_name('9')):
153      tab_num = event.keyval - gdk.keyval_from_name('0') or 10
154      pages_count1 = self.pluginview1.getNVisiblePages()
155      pages_count2 = self.pluginview2.getNVisiblePages()
156      if pages_count1 + pages_count2 < tab_num:
157        return
158      elif pages_count1 >= tab_num:
159        self.pluginview1.focusTab(tab_num - 1)
160      else:
161        self.pluginview2.focusTab(tab_num - pages_count1 - 1)
162
163  def saveState(self):
164    '''
165    Save the dimensions of the main window, and the position of the panes.
166    '''
167    self.gsettings.set_int('window-width', self.get_allocated_width())
168    self.gsettings.set_int('window-height', self.get_allocated_height())
169    self.gsettings.set_int('hpaned', self._hpaned.get_position())
170    if self.pluginview2.get_n_pages():
171      position = self._vpaned.get_position()
172    else:
173      position = getattr(self._vpaned, 'last_position')
174    if position is not None:
175      self.gsettings.set_int('vpaned', position)
176
177  def _onBlinkDone(self, node):
178    '''
179    Redraw main window after node stops blinking widget. Gets rid of artifacts.
180
181    @param node:
182    @type node:
183    '''
184    self.queue_draw()
185
186  def _onSelectionChanged(self, selection):
187    '''
188    Callback for selection "changed" of the main treeview selection.
189    Updates the status bar with the path to the selected accessible.
190
191    @param selection: The main tree view's selection object.
192    @type node: gtk.TreeSelection
193    '''
194    model, iter = selection.get_selected()
195    context_id = self.statusbar.get_context_id('lineage')
196    if not iter:
197      return
198    tree_path = model.get_path(iter)
199    path_tuple = tuple(tree_path.get_indices())
200
201    path = list(map(str, path_tuple))
202    self.statusbar.pop(context_id)
203    if len(path) > 1:
204      self.statusbar.push(context_id, 'Path: '+' '.join(path[1:]))
205