3
# (c) 2007 Canonical Ltd.
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.
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.
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.
19
'''GTK user interface implementation.'''
23
import gtk, gtk.glade, gobject, pynotify
25
from jockey.ui import AbstractUI
26
from jockey.oslib import OSLib
28
class GtkUI(AbstractUI):
29
'''GTK user interface implementation.'''
32
# Implementation of required AbstractUI methods
35
def convert_keybindings(self, str):
36
'''Keyboard accelerator aware gettext() wrapper.
38
This optionally converts keyboard accelerators to the appropriate
39
format for the frontend.
41
A double underscore ('__') is converted to a real '_'.
43
# nothing to do for GTK
51
self.glade = gtk.glade.XML('/usr/share/jockey/main.glade')
53
self.glade = gtk.glade.XML(os.path.join(os.path.dirname(__file__), 'main.glade'))
55
self.glade.signal_autoconnect(self)
57
self.treeview = self.w('treeview_drivers')
59
self.w('dialog_manager').set_title(self.main_window_title())
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)
66
col1.pack_start(text_renderer, True)
67
col1.set_attributes(text_renderer, text=3)
68
self.treeview.append_column(col1)
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)
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)
91
self.treeview.grab_focus()
93
self.update_tree_model()
96
self.w('dialog_manager').show()
98
def ui_main_loop(self):
99
'''Main loop for the user interface.
101
This should return if the user wants to quit the program, and return
106
def error_message(self, title, text):
107
'''Present an error message box.'''
109
md = gtk.MessageDialog(type=gtk.MESSAGE_ERROR,
110
buttons=gtk.BUTTONS_CLOSE, message_format=text)
116
def confirm_action(self, title, text, subtext=None, action=None):
117
'''Present a confirmation dialog.
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.
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))
128
md = gtk.MessageDialog(type=gtk.MESSAGE_QUESTION,
129
message_format=text, buttons=gtk.BUTTONS_OK_CANCEL)
131
md.format_secondary_text(subtext)
136
return ret == gtk.RESPONSE_OK
138
def ui_notification(self, title, text):
139
'''Present a notification popup.
141
This should preferably create a tray icon. Clicking on the tray icon or
142
notification should run the GUI.
144
pynotify.init('jockey')
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)
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))
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)
167
def open_app(self, widget):
168
OSLib.inst.open_app(self)
171
'''Process pending UI events and return.
173
This is called while waiting for external processes such as package
176
while gtk.events_pending():
177
gtk.main_iteration(False)
179
def ui_download_start(self, url, total_size):
180
'''Create a progress dialog for a download of given URL.
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).
186
raise NotImplementedError, 'TODO'
188
def ui_download_progress(self, current_size, total_size):
189
'''Update download progress of current download.
191
This should return True to cancel the current download, and False
194
raise NotImplementedError, 'TODO'
196
def ui_download_finish(self):
197
'''Close the current download progress dialog.'''
199
raise NotImplementedError, 'TODO'
206
'''Shortcut for getting a glade widget.'''
208
return self.glade.get_widget(widget)
210
def update_tree_model(self):
211
'''Update treeview to current set of handlers and their states.'''
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
220
theme = gtk.icon_theme_get_default()
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()
228
if category not in parents:
229
parents[category] = self.model.append(None, [None, None, None,
230
category, None, None, None, False])
232
if (is_enabled != is_used) and handler.changed():
233
status, icon = self.string_restart, gtk.STOCK_REFRESH
235
status, icon = self.string_in_use, gtk.STOCK_YES
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,
243
self.treeview.set_model(self.model)
244
self.treeview.expand_all()
246
self.w('label_heading').set_label('<span weight="bold">%s</span>\n\n%s' %
247
self.main_window_text())
253
def on_quit(self, *args):
256
def on_button_help_clicked(self, widget):
257
OSLib.inst.ui_help(self)
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)
265
def on_treeview_drivers_cursor_changed(self, widget):
266
tip = self.get_handler_tooltip(self.model[widget.get_cursor()[0]][0])
268
self.treeview.set_tooltip_text(tip)
269
self.treeview.set_property('has-tooltip', True)
271
self.treeview.set_property('has-tooltip', False)
273
if __name__ == '__main__':