home *** CD-ROM | disk | FTP | other *** search
- #!/usr/bin/python
-
- # (c) 2007 Canonical Ltd.
- #
- # This program is free software; you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation; either version 2 of the License, or
- # (at your option) any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License along
- # with this program; if not, write to the Free Software Foundation, Inc.,
- # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
- '''GTK user interface implementation.'''
-
- import sys, os.path, os
-
- import glib, gtk, gobject, pynotify
-
- import jockey.ui
- from jockey.oslib import OSLib
-
- class GtkUI(jockey.ui.AbstractUI):
- '''GTK user interface implementation.'''
-
- #
- # Implementation of required AbstractUI methods
- #
-
- def convert_keybindings(self, str):
- '''Keyboard accelerator aware gettext() wrapper.
-
- This optionally converts keyboard accelerators to the appropriate
- format for the frontend.
-
- A double underscore ('__') is converted to a real '_'.
- '''
- # nothing to do for GTK
- return str
-
- def ui_init(self):
- '''Initialize UI.'''
-
- # load UI
- self.widgets = gtk.Builder()
- ui_path = '/usr/share/jockey/jockey-gtk.ui'
- if not os.path.exists(ui_path):
- ui_path = os.path.join(os.path.dirname(__file__), 'jockey-gtk.ui')
- self.widgets.add_from_file(ui_path)
- self.widgets.connect_signals(self)
-
- self.w('label_license_label').set_label(self.string_license_label)
- self.w('linkbutton_licensetext').set_label('(%s)' % self.string_details)
- self.w('dialog_licensetext').set_title(self.string_license_dialog_title)
-
- def ui_show_main(self):
- '''Show main window.'''
-
- # check $DISPLAY validity, etc.
- try:
- gtk.init_check()
- except RuntimeError:
- self.error_msg('Could not initialize GTK: invalid $DISPLAY?')
- sys.exit(1)
-
- self.treeview = self.w('treeview_drivers')
-
- self.w('dialog_manager').set_title(self.main_window_title())
-
- # initialize handler treeview (but only do it once)
- if not self.treeview.get_columns():
- self.treeview.set_headers_visible(False)
-
- # icon
- pixbuf_renderer = gtk.CellRendererPixbuf()
- col_icon = gtk.TreeViewColumn()
- col_icon.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
- col_icon.set_expand(False)
- col_icon.pack_start(pixbuf_renderer, False)
- col_icon.set_attributes(pixbuf_renderer, pixbuf=1)
- self.treeview.append_column(col_icon)
-
- # name
- text_renderer = gtk.CellRendererText()
- col_name = gtk.TreeViewColumn()
- col_name.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
- col_name.set_expand(True)
- col_name.pack_start(text_renderer, True)
- col_name.set_attributes(text_renderer, text=2)
- self.treeview.append_column(col_name)
-
- self.treeview.grab_focus()
- self.update_driver_info_ui(None)
- self.update_tree_model()
-
- # show help button?
- if not OSLib.inst.ui_help_available(self):
- self.w('button_help').hide()
- # EDGE is the default, but that centers if there is just one button
- self.w('button_box_main').set_layout(gtk.BUTTONBOX_END)
-
- # default height of details scrollwindow is too small
- self.w('dialog_manager').set_default_size(-1, 550)
-
- # default vpane size
- if len(self.get_displayed_handlers()) < 2:
- self.w('vpaned1').set_position(36)
- elif len(self.get_displayed_handlers()) < 4:
- self.w('vpaned1').set_position(72)
- else:
- self.w('vpaned1').set_position(100)
-
- # lift the curtain
- self.w('dialog_manager').show()
-
- def ui_main_loop(self):
- '''Main loop for the user interface.
-
- This should return if the user wants to quit the program, and return
- the exit code.
- '''
- gtk.main()
- self.w('dialog_manager').hide()
-
- def error_message(self, title, text):
- '''Present an error message box.'''
-
- self.msgbox = gtk.MessageDialog(type=gtk.MESSAGE_ERROR,
- buttons=gtk.BUTTONS_CLOSE, message_format=text)
- if title:
- self.msgbox.set_title(title)
- self.msgbox.run()
- self.msgbox.destroy()
- self.msgbox = None
-
- def confirm_action(self, title, text, subtext=None, action=None):
- '''Present a confirmation dialog.
-
- If action is given, it is used as button label instead of the default
- 'OK'. Return True if the user confirms, False otherwise.
- '''
- if action:
- self.msgbox = gtk.MessageDialog(type=gtk.MESSAGE_QUESTION,
- message_format=text)
- self.msgbox.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
- self.msgbox.add_button(action, gtk.RESPONSE_OK).set_image(
- gtk.image_new_from_stock(gtk.STOCK_APPLY,
- gtk.ICON_SIZE_BUTTON))
- else:
- self.msgbox = gtk.MessageDialog(type=gtk.MESSAGE_QUESTION,
- message_format=text, buttons=gtk.BUTTONS_OK_CANCEL)
- if subtext:
- self.msgbox.format_secondary_text(subtext)
- if title:
- self.msgbox.set_title(title)
- ret = self.msgbox.run()
- self.msgbox.destroy()
- self.msgbox = None
- return ret == gtk.RESPONSE_OK
-
- def ui_notification(self, title, text):
- '''Present a notification popup.
-
- This should preferably create a tray icon. Clicking on the tray icon or
- notification should run the GUI.
- '''
- pynotify.init('jockey')
-
- trayicon = gtk.status_icon_new_from_icon_name('jockey')
- trayicon.connect('activate', lambda widget: self.ui_show_main())
- trayicon.set_visible(False)
- trayicon.set_tooltip(title)
- trayicon.set_visible(True)
-
- self.ui_idle()
-
- # creating the notification immediately causes the bubble to point into
- # the void; icon needs to settle first
- gobject.timeout_add (500, self.show_notification,
- (title, text, trayicon))
-
- def show_notification(self, data):
- (title, text, trayicon) = data
- notify = pynotify.Notification(title, text, 'jockey')
- notify.set_urgency(pynotify.URGENCY_NORMAL)
- notify.attach_to_status_icon(trayicon)
- notify.set_timeout(10000)
- notify.show()
-
- def ui_idle(self):
- '''Process pending UI events and return.
-
- This is called while waiting for external processes such as package
- installers.
- '''
- while gtk.events_pending():
- gtk.main_iteration(False)
-
- def ui_progress_start(self, title, description, total):
- '''Create a progress dialog.'''
-
- self.w('progress').set_fraction(0)
- self.w('label_description').set_label(description)
- self.w('window_progress').set_title(title)
- self.w('window_progress').set_transient_for(self.w('dialog_manager'))
- self.w('window_progress').show()
- self.cancel_progress = False
-
- def ui_progress_update(self, current, total):
- '''Update status of current progress dialog.
-
- current/total specify the number of steps done and total steps to
- do, or -1 if it cannot be determined. In this case the dialog should
- display an indeterminated progress bar (bouncing back and forth).
-
- This should return True to cancel, and False otherwise.
- '''
- if current < 0 or total < 0:
- self.w('progress').set_pulse_step(0.1)
- self.w('progress').pulse()
- else:
- self.w('progress').set_fraction(float(current)/total)
- return self.cancel_progress
-
- def ui_progress_finish(self):
- '''Close the current progress dialog.'''
-
- self.w('window_progress').hide()
-
- #
- # helper functions
- #
-
- def w(self, widget):
- '''Shortcut for getting an UI widget from GtkBuilder.'''
-
- return self.widgets.get_object(widget)
-
- def update_tree_model(self):
- '''Update treeview to current set of handlers and their states.'''
-
- # handler ID, icon, name
- self.model = gtk.ListStore(gobject.TYPE_STRING, gtk.gdk.Pixbuf, gobject.TYPE_STRING)
-
- theme = gtk.icon_theme_get_default()
-
- cur_path = self.treeview.get_cursor()[0]
-
- for h_id in self.get_displayed_handlers():
- info = self.get_ui_driver_info(h_id)
- if info['needs_reboot']:
- pixbuf = theme.load_icon(gtk.STOCK_REFRESH, 16, gtk.ICON_LOOKUP_USE_BUILTIN)
- elif info['enabled']:
- try:
- pixbuf = theme.load_icon('jockey-enabled', 16, 0)
- except glib.GError:
- pixbuf = theme.load_icon(gtk.STOCK_YES, 16, gtk.ICON_LOOKUP_USE_BUILTIN)
- else:
- try:
- pixbuf = theme.load_icon('jockey-disabled', 16, 0)
- except glib.GError:
- pixbuf = theme.load_icon(gtk.STOCK_NO, 16, gtk.ICON_LOOKUP_USE_BUILTIN)
- iter = self.model.append([h_id, pixbuf, info['name']])
- if not cur_path:
- cur_path = self.model.get_path(iter)
-
- self.treeview.set_model(self.model)
- if cur_path:
- self.treeview.set_cursor(cur_path)
- self.treeview.expand_all()
-
- self.w('label_heading').set_label('<span weight="bold">%s</span>\n\n%s' %
- self.main_window_text())
-
- def update_driver_info_ui(self, handler_id):
- '''Update UI elements which show the driver details.
-
- If handler_id is None, then no driver is selected, no information
- shown, and the appropriate controls are disabled.
- '''
- info = self.get_ui_driver_info(handler_id)
-
- self.w('label_drivername').set_label('<b>%s</b>' % info['name'])
- self.current_driver_name = info['name']
- self.w('textview_description').get_buffer().set_text(info['description'])
- if info['certified'] == None:
- self.w('image_certification').hide()
- elif info['certified']:
- self.w('image_certification').show()
- self.w('image_certification').set_from_icon_name('jockey-certified', gtk.ICON_SIZE_BUTTON)
- else:
- self.w('image_certification').show()
- self.w('image_certification').set_from_stock(gtk.STOCK_DIALOG_WARNING, gtk.ICON_SIZE_BUTTON)
- self.w('label_certification').set_label(info['certification_label'])
- if info['free'] == None:
- self.w('image_license').hide()
- self.w('label_license_label').hide()
- elif info['free']:
- self.w('image_license').show()
- self.w('label_license_label').show()
- self.w('image_license').set_from_icon_name('jockey-free', gtk.ICON_SIZE_BUTTON)
- else:
- self.w('image_license').show()
- self.w('label_license_label').show()
- self.w('image_license').set_from_icon_name('jockey-proprietary',
- gtk.ICON_SIZE_BUTTON)
- self.w('label_license').set_label(info['license_label'])
- if info['license_text']:
- self.w('linkbutton_licensetext').show()
- self.current_license_text = info['license_text']
- else:
- self.w('linkbutton_licensetext').hide()
- self.current_license_text = None
- if info['enabled'] == None:
- self.w('image_enabled').hide()
- elif info['needs_reboot']:
- self.w('image_enabled').show()
- self.w('image_enabled').set_from_stock(gtk.STOCK_REFRESH, gtk.ICON_SIZE_BUTTON)
- elif info['enabled']:
- self.w('image_enabled').show()
- self.w('image_enabled').set_from_icon_name('jockey-enabled', gtk.ICON_SIZE_BUTTON)
- else:
- self.w('image_enabled').show()
- self.w('image_enabled').set_from_icon_name('jockey-disabled', gtk.ICON_SIZE_BUTTON)
- self.w('label_status').set_label(info['status_label'])
-
- if not info['button_toggle_label']:
- self.w('button_toggle').set_label(self.string_button_enable)
- self.w('button_toggle').set_sensitive(False)
- else:
- self.w('button_toggle').set_sensitive(True)
- self.w('button_toggle').set_label(info['button_toggle_label'])
- if info['enabled']:
- self.w('button_toggle').set_image(
- gtk.image_new_from_icon_name('jockey-disabled', gtk.ICON_SIZE_BUTTON))
- else:
- self.w('button_toggle').set_image(
- gtk.image_new_from_icon_name('jockey-enabled', gtk.ICON_SIZE_BUTTON))
-
- #
- # event callbacks
- #
-
- def on_quit(self, *args):
- gtk.main_quit()
- return True
-
- def on_button_help_clicked(self, widget):
- OSLib.inst.ui_help(self)
- return True
-
- def on_button_toggle_clicked(self, widget):
- self.treeview.set_sensitive(False)
- if self.set_handler_enable(self.model[self.treeview.get_cursor()[0]][0],
- 'toggle', False):
- self.update_tree_model()
- self.treeview.set_sensitive(True)
- return True
-
- def on_treeview_drivers_cursor_changed(self, widget):
- path = widget.get_cursor()[0]
- if path:
- h_id = self.model[path][0]
- else:
- h_id = None
- self.update_driver_info_ui(h_id)
- return True
-
- def on_progress_cancel(self, *args):
- self.cancel_progress = True
- return True
-
- def on_linkbutton_licensetext_clicked(self, widget):
- self.w('label_license_drivername').set_label('<b>%s</b>' %
- self.current_driver_name)
- self.w('textview_license_text').get_buffer().set_text(self.current_license_text)
- self.w('dialog_licensetext').set_default_size(600, 480)
- self.w('dialog_licensetext').run()
- self.w('dialog_licensetext').hide()
- return True
-
- if __name__ == '__main__':
- if not os.environ.get('DISPLAY'):
- print >> sys.stderr, 'This program needs a running X session. Please use jockey-text for a command line version of Jockey.'
- sys.exit(1)
-
- OSLib.inst = OSLib(client_only=True)
- u = GtkUI()
- sys.exit(u.run())
-