1'''status.py - Statusbar for main window.'''
2
3from gi.repository import Gdk, Gtk
4
5from mcomix import i18n
6from mcomix import constants
7from mcomix.preferences import prefs
8
9class Statusbar(Gtk.EventBox):
10
11    SPACING = 5
12
13    def __init__(self):
14        super(Statusbar, self).__init__()
15
16        self._loading = True
17
18        # Status text, page number, file number, resolution, path, filename, filesize
19        self.status = Gtk.Statusbar()
20        self.add(self.status)
21
22        # Create popup menu for enabling/disabling status boxes.
23        self.ui_manager = Gtk.UIManager()
24        self.tooltipstatus = TooltipStatusHelper(self.ui_manager, self.status)
25        ui_description = '''
26        <ui>
27            <popup name="Statusbar">
28                <menuitem action="pagenumber" />
29                <menuitem action="filenumber" />
30                <menuitem action="resolution" />
31                <menuitem action="rootpath" />
32                <menuitem action="filename" />
33                <menuitem action="filesize" />
34            </popup>
35        </ui>
36        '''
37        self.ui_manager.add_ui_from_string(ui_description)
38
39        actiongroup = Gtk.ActionGroup(name='mcomix-statusbar')
40        actiongroup.add_toggle_actions([
41            ('pagenumber', None, _('Show page numbers'), None, None,
42             self.toggle_status_visibility),
43            ('filenumber', None, _('Show file numbers'), None, None,
44             self.toggle_status_visibility),
45            ('resolution', None, _('Show resolution'), None, None,
46             self.toggle_status_visibility),
47            ('rootpath', None, _('Show path'), None, None,
48             self.toggle_status_visibility),
49            ('filename', None, _('Show filename'), None, None,
50             self.toggle_status_visibility),
51            ('filesize', None, _('Show filesize'), None, None,
52             self.toggle_status_visibility)])
53        self.ui_manager.insert_action_group(actiongroup, 0)
54
55        # Hook mouse release event
56        self.connect('button-release-event', self._button_released)
57        self.set_events(Gdk.EventMask.BUTTON_PRESS_MASK|Gdk.EventMask.BUTTON_RELEASE_MASK)
58
59        # Default status information
60        self._page_info = ''
61        self._file_info = ''
62        self._resolution = ''
63        self._root = ''
64        self._filename = ''
65        self._filesize = ''
66        self._update_sensitivity()
67        self.show_all()
68
69        self._loading = False
70
71    def set_message(self, message):
72        '''Set a specific message (such as an error message) on the statusbar,
73        replacing whatever was there earlier.
74        '''
75        self.status.pop(0)
76        self.status.push(0, ' ' * Statusbar.SPACING + message)
77
78    def set_page_number(self, page, total, this_screen):
79        '''Update the page number.'''
80        p = ','.join(str(page+i) for i in range(this_screen))
81        self._page_info = '{} / {}'.format(p,total)
82
83    def get_page_number(self):
84        '''Returns the bar's page information.'''
85        return self._page_info
86
87    def set_file_number(self, fileno, total):
88        '''Updates the file number (i.e. number of current file/total
89        files loaded).'''
90        if total > 0:
91            self._file_info = '(%d / %d)' % (fileno, total)
92        else:
93            self._file_info = ''
94
95    def get_file_number(self):
96        ''' Returns the bar's file information.'''
97        return self._file_info
98
99    def set_resolution(self, dimensions): # 2D only
100        '''Update the resolution data.
101
102        Takes an iterable of tuples, (x, y, scale), describing the original
103        resolution of an image as well as the currently displayed scale.
104        '''
105        self._resolution = ', '.join('{}x{} ({:.1%})'.format(*d) for d in dimensions)
106
107    def set_root(self, root):
108        '''Set the name of the root (directory or archive).'''
109        self._root = i18n.to_unicode(root)
110
111    def set_filename(self, filename):
112        '''Update the filename.'''
113        self._filename = i18n.to_unicode(filename)
114
115    def set_filesize(self, size):
116        '''Update the filesize.'''
117        if size is None:
118            size = ''
119        self._filesize = size
120
121    def update(self):
122        '''Set the statusbar to display the current state.'''
123
124        s = '{0:^{1}}'.format('|',Statusbar.SPACING*2+1)
125        text = s.join(self._get_status_text())
126        self.status.pop(0)
127        self.status.push(0, '{1:>{2}}{0}'.format(text,'',Statusbar.SPACING))
128
129    def push(self, context_id, message):
130        ''' Compatibility with Gtk.Statusbar. '''
131        assert context_id >= 0
132        self.status.push(context_id + 1, message)
133
134    def pop(self, context_id):
135        ''' Compatibility with Gtk.Statusbar. '''
136        assert context_id >= 0
137        self.status.pop(context_id + 1)
138
139    def _get_status_text(self):
140        ''' Returns an array of text fields that should be displayed. '''
141        fields = [
142            (constants.STATUS_PAGE,       self._page_info ),
143            (constants.STATUS_FILENUMBER, self._file_info ),
144            (constants.STATUS_RESOLUTION, self._resolution),
145            (constants.STATUS_PATH,       self._root      ),
146            (constants.STATUS_FILENAME,   self._filename  ),
147            (constants.STATUS_FILESIZE,   self._filesize  ),
148        ]
149        p = prefs['statusbar fields']
150
151        return [s for c,s in filter(lambda f:f[0]&p,fields)]
152
153    def toggle_status_visibility(self, action, *args):
154        ''' Called when status entries visibility is to be changed. '''
155
156        # Ignore events as long as control is still loading.
157        if self._loading:
158            return
159
160        names = {
161            'pagenumber': constants.STATUS_PAGE,
162            'resolution': constants.STATUS_RESOLUTION,
163            'rootpath':   constants.STATUS_PATH,
164            'filename':   constants.STATUS_FILENAME,
165            'filenumber': constants.STATUS_FILENUMBER,
166            'filesize':   constants.STATUS_FILESIZE,
167        }
168
169        bit = names[action.get_name()]
170
171        if action.get_active():
172            prefs['statusbar fields'] |= bit
173        else:
174            prefs['statusbar fields'] &= ~bit
175
176        self.update()
177        self._update_sensitivity()
178
179    def _button_released(self, widget, event, *args):
180        ''' Triggered when a mouse button is released to open the context
181        menu. '''
182        if event.button == 3:
183            self.ui_manager.get_widget('/Statusbar').popup(None, None, None, None,
184                                                           event.button, event.time)
185
186    def _update_sensitivity(self):
187        ''' Updates the action menu's sensitivity based on user preferences. '''
188
189        p = prefs['statusbar fields']
190        names = {
191            'pagenumber': p & constants.STATUS_PAGE,
192            'filenumber': p & constants.STATUS_FILENUMBER,
193            'resolution': p & constants.STATUS_RESOLUTION,
194            'rootpath':   p & constants.STATUS_PATH,
195            'filename':   p & constants.STATUS_FILENAME,
196            'filesize':   p & constants.STATUS_FILESIZE,
197        }
198
199        for n,v in names.items():
200            action = self.ui_manager.get_action('/Statusbar/' + n)
201            action.set_active(v)
202
203
204class TooltipStatusHelper(object):
205    ''' Attaches to a L{Gtk.UIManager} to provide statusbar tooltips when
206    selecting menu items. '''
207
208    def __init__(self, uimanager, statusbar):
209        self._statusbar = statusbar
210
211        uimanager.connect('connect-proxy', self._on_connect_proxy)
212        uimanager.connect('disconnect-proxy', self._on_disconnect_proxy)
213
214    def _on_connect_proxy(self, uimgr, action, widget):
215        ''' Connects the widget's selection handlers to the status bar update.
216        '''
217        tooltip = action.get_property('tooltip')
218        if isinstance(widget, Gtk.MenuItem) and tooltip:
219            cid = widget.connect('select', self._on_item_select, tooltip)
220            cid2 = widget.connect('deselect', self._on_item_deselect)
221            setattr(widget, 'app::connect-ids', (cid, cid2))
222
223    def _on_disconnect_proxy(self, uimgr, action, widget):
224        ''' Disconnects the widget's selection handlers. '''
225        cids = getattr(widget, 'app::connect-ids', ())
226        for cid in cids:
227            widget.disconnect(cid)
228
229    def _on_item_select(self, menuitem, tooltip):
230        self._statusbar.push(0, ' ' * Statusbar.SPACING + tooltip)
231
232    def _on_item_deselect(self, menuitem):
233        self._statusbar.pop(0)
234
235# vim: expandtab:sw=4:ts=4
236