~alexeftimie/jockey/fix-gobject

« back to all changes in this revision

Viewing changes to gtk/jockey-gtk

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2008-01-17 15:02:40 UTC
  • Revision ID: james.westby@ubuntu.com-20080117150240-djmsi8giu255vzzn
Tags: 0.1~r118
* Initial release, result of completely rewriting restricted-manager to be
  maintainable, robust, and suitable for other distributions. Some features
  and the KDE UI still need to be ported.
* See restricted-manager-rewrite specification for details.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
 
 
3
# (c) 2007 Canonical Ltd.
 
4
#
 
5
# This program is free software; you can redistribute it and/or modify
 
6
# it under the terms of the GNU General Public License as published by
 
7
# the Free Software Foundation; either version 2 of the License, or
 
8
# (at your option) any later version.
 
9
#
 
10
# This program is distributed in the hope that it will be useful,
 
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
# GNU General Public License for more details.
 
14
#
 
15
# You should have received a copy of the GNU General Public License along
 
16
# with this program; if not, write to the Free Software Foundation, Inc.,
 
17
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
18
 
 
19
'''GTK user interface implementation.'''
 
20
 
 
21
import sys, os.path
 
22
 
 
23
import gtk, gtk.glade, gobject, pynotify
 
24
 
 
25
from jockey.ui import AbstractUI
 
26
from jockey.oslib import OSLib
 
27
 
 
28
class GtkUI(AbstractUI):
 
29
    '''GTK user interface implementation.'''
 
30
 
 
31
    #
 
32
    # Implementation of required AbstractUI methods
 
33
    # 
 
34
 
 
35
    def convert_keybindings(self, str):
 
36
        '''Keyboard accelerator aware gettext() wrapper.
 
37
        
 
38
        This optionally converts keyboard accelerators to the appropriate
 
39
        format for the frontend.
 
40
 
 
41
        A double underscore ('__') is converted to a real '_'.
 
42
        '''
 
43
        # nothing to do for GTK
 
44
        return str
 
45
 
 
46
    def ui_init(self):
 
47
        '''Initialize UI.'''
 
48
 
 
49
        # load glade
 
50
        try:
 
51
            self.glade = gtk.glade.XML('/usr/share/jockey/main.glade')
 
52
        except RuntimeError:
 
53
            self.glade = gtk.glade.XML(os.path.join(os.path.dirname(__file__), 'main.glade'))
 
54
 
 
55
        self.glade.signal_autoconnect(self)
 
56
 
 
57
        self.treeview = self.w('treeview_drivers')
 
58
 
 
59
        self.w('dialog_manager').set_title(self.main_window_title())
 
60
 
 
61
        # initialize handler treeview
 
62
        text_renderer = gtk.CellRendererText()
 
63
        col1 = gtk.TreeViewColumn(self.string_handler)
 
64
        col1.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
 
65
        col1.set_expand(True)
 
66
        col1.pack_start(text_renderer, True)
 
67
        col1.set_attributes(text_renderer, text=3)
 
68
        self.treeview.append_column(col1)
 
69
 
 
70
        toggle_renderer = gtk.CellRendererToggle()
 
71
        toggle_renderer.set_property('activatable', True)
 
72
        toggle_renderer.connect('toggled', self.on_handler_changed)
 
73
        col2 = gtk.TreeViewColumn(self.string_enabled)
 
74
        col2.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
 
75
        col2.set_expand(False)
 
76
        col2.pack_start(toggle_renderer, True)
 
77
        col2.set_attributes(toggle_renderer, active=4, visible=7)
 
78
        self.treeview.append_column(col2)
 
79
 
 
80
        pixbuf_renderer = gtk.CellRendererPixbuf()
 
81
        text_renderer = gtk.CellRendererText()
 
82
        col3 = gtk.TreeViewColumn(self.string_status)
 
83
        col3.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
 
84
        col3.set_expand(False)
 
85
        col3.pack_start(pixbuf_renderer, False)
 
86
        col3.pack_end(text_renderer, True)
 
87
        col3.set_attributes(pixbuf_renderer, pixbuf=5)
 
88
        col3.set_attributes(text_renderer, text=6)
 
89
        self.treeview.append_column(col3)
 
90
 
 
91
        self.treeview.grab_focus()
 
92
 
 
93
        self.update_tree_model()
 
94
 
 
95
        # lift the curtain
 
96
        self.w('dialog_manager').show()
 
97
 
 
98
    def ui_main_loop(self):
 
99
        '''Main loop for the user interface.
 
100
        
 
101
        This should return if the user wants to quit the program, and return
 
102
        the exit code.
 
103
        '''
 
104
        gtk.main()
 
105
 
 
106
    def error_message(self, title, text):
 
107
        '''Present an error message box.'''
 
108
 
 
109
        md = gtk.MessageDialog(type=gtk.MESSAGE_ERROR,
 
110
            buttons=gtk.BUTTONS_CLOSE, message_format=text)
 
111
        if title:
 
112
            md.set_title(title)
 
113
        md.run()
 
114
        md.hide()
 
115
 
 
116
    def confirm_action(self, title, text, subtext=None, action=None):
 
117
        '''Present a confirmation dialog.
 
118
 
 
119
        If action is given, it is used as button label instead of the default
 
120
        'OK'.  Return True if the user confirms, False otherwise.
 
121
        '''
 
122
        if action:
 
123
            md = gtk.MessageDialog(type=gtk.MESSAGE_QUESTION, message_format=text)
 
124
            md.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
 
125
            md.add_button(action, gtk.RESPONSE_OK).set_image(
 
126
                gtk.image_new_from_stock(gtk.STOCK_APPLY, gtk.ICON_SIZE_BUTTON))
 
127
        else:
 
128
            md = gtk.MessageDialog(type=gtk.MESSAGE_QUESTION,
 
129
                message_format=text, buttons=gtk.BUTTONS_OK_CANCEL)
 
130
        if subtext:
 
131
            md.format_secondary_text(subtext)
 
132
        if title:
 
133
            md.set_title(title)
 
134
        ret = md.run()
 
135
        md.hide()
 
136
        return ret == gtk.RESPONSE_OK
 
137
 
 
138
    def ui_notification(self, title, text):
 
139
        '''Present a notification popup.
 
140
 
 
141
        This should preferably create a tray icon. Clicking on the tray icon or
 
142
        notification should run the GUI.
 
143
        '''
 
144
        pynotify.init('jockey')
 
145
 
 
146
        trayicon = gtk.status_icon_new_from_icon_name('jockey')
 
147
        trayicon.connect('activate', self.open_app)
 
148
        trayicon.set_visible(False)
 
149
        trayicon.set_tooltip(title)
 
150
        trayicon.set_visible(True)
 
151
 
 
152
        self.ui_idle()
 
153
 
 
154
        # creating the notification immediately causes the bubble to point into
 
155
        # the void; icon needs to settle first
 
156
        gobject.timeout_add (500, self.show_notification, 
 
157
            (title, text, trayicon))
 
158
 
 
159
    def show_notification(self, data):
 
160
        (title, text, trayicon) = data
 
161
        notify = pynotify.Notification(title, text, 'jockey')
 
162
        notify.set_urgency(pynotify.URGENCY_NORMAL)
 
163
        notify.attach_to_status_icon(trayicon)
 
164
        notify.set_timeout(10000)
 
165
        notify.show()
 
166
 
 
167
    def open_app(self, widget):
 
168
        OSLib.inst.open_app(self)
 
169
 
 
170
    def ui_idle(self):
 
171
        '''Process pending UI events and return.
 
172
 
 
173
        This is called while waiting for external processes such as package
 
174
        installers.
 
175
        '''
 
176
        while gtk.events_pending():
 
177
            gtk.main_iteration(False)
 
178
 
 
179
    def ui_download_start(self, url, total_size):
 
180
        '''Create a progress dialog for a download of given URL.
 
181
 
 
182
        total_size specifes the number of bytes to download, or -1 if it cannot
 
183
        be determined. In this case the dialog should display an indeterminated
 
184
        progress bar (bouncing back and forth).
 
185
        '''
 
186
        raise NotImplementedError, 'TODO'
 
187
 
 
188
    def ui_download_progress(self, current_size, total_size):
 
189
        '''Update download progress of current download.
 
190
        
 
191
        This should return True to cancel the current download, and False
 
192
        otherwise.
 
193
        '''
 
194
        raise NotImplementedError, 'TODO'
 
195
 
 
196
    def ui_download_finish(self):
 
197
        '''Close the current download progress dialog.'''
 
198
 
 
199
        raise NotImplementedError, 'TODO'
 
200
 
 
201
    #
 
202
    # helper functions
 
203
    #
 
204
 
 
205
    def w(self, widget):
 
206
        '''Shortcut for getting a glade widget.'''
 
207
 
 
208
        return self.glade.get_widget(widget)
 
209
 
 
210
    def update_tree_model(self):
 
211
        '''Update treeview to current set of handlers and their states.'''
 
212
 
 
213
        self.model = gtk.TreeStore(gobject.TYPE_PYOBJECT,
 
214
                                gobject.TYPE_PYOBJECT,
 
215
                                gobject.TYPE_PYOBJECT,
 
216
                                str, bool, gtk.gdk.Pixbuf, str,
 
217
                                bool # toggle visible or not
 
218
                                )
 
219
 
 
220
        theme = gtk.icon_theme_get_default()
 
221
 
 
222
        parents = {}
 
223
        for handler in sorted(self.handlers, key=lambda x: (x.ui_category(), x.name())):
 
224
            is_enabled = handler.enabled()
 
225
            is_used = handler.used()
 
226
            category = handler.ui_category()
 
227
 
 
228
            if category not in parents:
 
229
                parents[category] = self.model.append(None, [None, None, None,
 
230
                    category, None, None, None, False])
 
231
 
 
232
            if (is_enabled != is_used) and handler.changed():
 
233
                status, icon = self.string_restart, gtk.STOCK_REFRESH
 
234
            elif is_used:
 
235
                status, icon = self.string_in_use, gtk.STOCK_YES
 
236
            else:
 
237
                status, icon = self.string_not_in_use, gtk.STOCK_NO
 
238
            pixbuf = theme.load_icon(icon, 16, gtk.ICON_LOOKUP_USE_BUILTIN)
 
239
            self.model.append(parents[category], [ handler, is_enabled, is_used,
 
240
                                handler.name(), is_enabled, pixbuf,
 
241
                                status, True ])
 
242
 
 
243
        self.treeview.set_model(self.model)
 
244
        self.treeview.expand_all()
 
245
 
 
246
        self.w('label_heading').set_label('<span weight="bold">%s</span>\n\n%s' %
 
247
            self.main_window_text())
 
248
 
 
249
    #
 
250
    # event callbacks
 
251
    #
 
252
 
 
253
    def on_quit(self, *args):
 
254
        gtk.main_quit()
 
255
 
 
256
    def on_button_help_clicked(self, widget):
 
257
        OSLib.inst.ui_help(self)
 
258
 
 
259
    def on_handler_changed(self, widget, path):
 
260
        self.treeview.set_sensitive(False)
 
261
        if self.toggle_handler(self.model[path][0]):
 
262
            self.update_tree_model()
 
263
        self.treeview.set_sensitive(True)
 
264
 
 
265
    def on_treeview_drivers_cursor_changed(self, widget):
 
266
        tip = self.get_handler_tooltip(self.model[widget.get_cursor()[0]][0])
 
267
        if tip:
 
268
            self.treeview.set_tooltip_text(tip)
 
269
            self.treeview.set_property('has-tooltip', True)
 
270
        else:
 
271
            self.treeview.set_property('has-tooltip', False)
 
272
 
 
273
if __name__ == '__main__':
 
274
    OSLib.inst = OSLib()
 
275
    u = GtkUI()
 
276
    sys.exit(u.run())