Adding an about menu as requested by magnuslu
[nqaap] / src / opt / Nqa-Audiobook-player / Gui.py
1 from __future__ import with_statement
2
3 import os
4 import ConfigParser
5 import logging
6
7 import gobject
8 import gtk
9
10 import constants
11 import hildonize
12 import gtk_toolbox
13 import Browser
14 import CallMonitor
15 import settings
16
17 if hildonize.IS_FREMANTLE_SUPPORTED:
18     # I don't normally do this but I want to error as loudly as possibly when an issue arises
19     import hildon
20
21
22 _moduleLogger = logging.getLogger(__name__)
23
24
25 class Gui(object):
26
27     def __init__(self):
28         _moduleLogger.info("Starting GUI")
29         self._clipboard = gtk.clipboard_get()
30         self._callMonitor = CallMonitor.CallMonitor()
31         self.__settingsWindow = None
32         self.__settingsManager = None
33         self._bookSelection = []
34         self._bookSelectionIndex = -1
35         self._chapterSelection = []
36         self._chapterSelectionIndex = -1
37         self._sleepSelection = ["0", "1", "10", "20", "30", "60"]
38         self._sleepSelectionIndex = 0
39
40         self.__window_in_fullscreen = False #The window isn't in full screen mode initially.
41         self.__isPortrait = False
42
43         self.controller = None
44         self.sleep_timer = None
45         self.auto_chapter_selected = False # true if we are in the
46                                            # midle of an automatic
47                                            # chapter change
48
49         self.ignore_next_chapter_change = False
50         # set up gui
51         self.setup()
52         self._callMonitor.connect("call_start", self.__on_call_started)
53         self._callMonitor.start()
54
55     def setup(self):
56         self._app = hildonize.get_app_class()()
57         self.win = gtk.Window()
58         self.win = hildonize.hildonize_window(self._app, self.win)
59         self.win.set_title(constants.__pretty_app_name__)
60
61         # Cover image
62         self.cover = gtk.Image()
63
64         # Controls:
65
66         # Label that hold the title of the book,and maybe the chapter
67         self.title = gtk.Label()
68         self.title.set_justify(gtk.JUSTIFY_CENTER)
69         self._set_display_title("Select a book to start listening")
70
71         self.chapter = gtk.Label()
72         self.chapter.set_justify(gtk.JUSTIFY_CENTER)
73
74         # Seekbar 
75         if hildonize.IS_FREMANTLE_SUPPORTED:
76             self.seek = hildon.Seekbar()
77             self.seek.set_range(0.0, 100)
78             self.seek.set_draw_value(False)
79             self.seek.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
80             self.seek.connect('change-value', self.seek_changed) # event
81             # self.seek.connect('value-changed',self.seek_changed) # event
82         else:
83             adjustment = gtk.Adjustment(0, 0, 101, 1, 5, 1)
84             self.seek = gtk.HScale(adjustment)
85             self.seek.set_draw_value(False)
86             self.seek.connect('change-value', self.seek_changed) # event
87
88         # Pause button
89         if hildonize.IS_FREMANTLE_SUPPORTED:
90             self.backButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
91             image = gtk.image_new_from_stock(gtk.STOCK_MEDIA_PREVIOUS, gtk.HILDON_SIZE_FINGER_HEIGHT)
92             self.backButton.set_image(image)
93
94             self.button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
95
96             self.forwardButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
97             image = gtk.image_new_from_stock(gtk.STOCK_MEDIA_NEXT, gtk.HILDON_SIZE_FINGER_HEIGHT)
98             self.forwardButton.set_image(image)
99         else:
100             self.backButton = gtk.Button(stock=gtk.STOCK_MEDIA_PREVIOUS)
101             self.button = gtk.Button()
102             self.forwardButton = gtk.Button(stock=gtk.STOCK_MEDIA_NEXT)
103         self.set_button_text("Play", "Start playing the audiobook")
104         self.backButton.connect('clicked', self._on_previous_chapter)
105         self.button.connect('clicked', self.play_pressed) # event
106         self.forwardButton.connect('clicked', self._on_next_chapter)
107
108         self._toolbar = gtk.HBox()
109         self._toolbar.pack_start(self.backButton, False, False, 0)
110         self._toolbar.pack_start(self.button, True, True, 0)
111         self._toolbar.pack_start(self.forwardButton, False, False, 0)
112
113         # Box to hold the controls:
114         self._controlLayout = gtk.VBox()
115         self._controlLayout.pack_start(gtk.Label(), True, True, 0)
116         self._controlLayout.pack_start(self.title, False, True, 0)
117         self._controlLayout.pack_start(self.chapter, False, True, 0)
118         self._controlLayout.pack_start(gtk.Label(), True, True, 0)
119         self._controlLayout.pack_start(self.seek, False, True, 0)
120         self._controlLayout.pack_start(self._toolbar, False, True, 0)
121
122         #Box that divides the layout in two: cover on the lefta
123         #and controls on the right
124         self._viewLayout = gtk.HBox()
125         self._viewLayout.pack_start(self.cover, True, True, 0)
126         self._viewLayout.add(self._controlLayout)
127
128         self._menuBar = gtk.MenuBar()
129         self._menuBar.show()
130
131         self._mainLayout = gtk.VBox()
132         self._mainLayout.pack_start(self._menuBar, False, False, 0)
133         self._mainLayout.pack_start(self._viewLayout)
134
135         # Add hbox to the window
136         self.win.add(self._mainLayout)
137
138         #Menu:
139         # Create menu
140         self._populate_menu()
141
142         self.win.connect("delete_event", self.quit) # Add shutdown event
143         self.win.connect("key-press-event", self.on_key_press)
144         self.win.connect("window-state-event", self._on_window_state_change)
145
146         self.win.show_all()
147
148         # Run update timer
149         self.setup_timers()
150
151     def _populate_menu(self):
152         self._menuBar = hildonize.hildonize_menu(
153             self.win,
154             self._menuBar,
155         )
156         if hildonize.IS_FREMANTLE_SUPPORTED:
157             # Create a picker button 
158             self.book_button = hildon.Button(gtk.HILDON_SIZE_AUTO,
159                                               hildon.BUTTON_ARRANGEMENT_VERTICAL)
160             self.book_button.set_title("Audiobook") # Set a title to the button 
161             self.book_button.connect("clicked", self._on_select_audiobook)
162
163             # Create a picker button 
164             self.chapter_button = hildon.Button(gtk.HILDON_SIZE_AUTO,
165                                               hildon.BUTTON_ARRANGEMENT_VERTICAL)
166             self.chapter_button.set_title("Chapter") # Set a title to the button 
167             self.chapter_button.connect("clicked", self._on_select_chapter)
168
169             # Create a picker button 
170             self.sleeptime_button = hildon.Button(gtk.HILDON_SIZE_AUTO,
171                                               hildon.BUTTON_ARRANGEMENT_VERTICAL)
172             self.sleeptime_button.set_title("Sleeptimer") # Set a title to the button 
173             self.sleeptime_button.connect("clicked", self._on_select_sleep)
174
175             settings_button = hildon.Button(gtk.HILDON_SIZE_AUTO, hildon.BUTTON_ARRANGEMENT_VERTICAL)
176             settings_button.set_label("Settings")
177             settings_button.connect("clicked", self._on_settings)
178
179             about_button = hildon.Button(gtk.HILDON_SIZE_AUTO, hildon.BUTTON_ARRANGEMENT_VERTICAL)
180             about_button.set_label("About")
181             about_button.connect("clicked", self._on_about_activate)
182
183             help_button = hildon.Button(gtk.HILDON_SIZE_AUTO, hildon.BUTTON_ARRANGEMENT_VERTICAL)
184             help_button.set_label("Help")
185             help_button.connect("clicked", self.get_help)
186
187             self._menuBar.append(self.book_button)        # Add the button to menu
188             self._menuBar.append(self.chapter_button)        # Add the button to menu
189             self._menuBar.append(self.sleeptime_button)        # Add the button to menu
190             self._menuBar.append(settings_button)
191             self._menuBar.append(help_button)
192             self._menuBar.append(about_button)
193             self._menuBar.show_all()
194         else:
195             self._audiobookMenuItem = gtk.MenuItem("Audiobook: ")
196             self._audiobookMenuItem.connect("activate", self._on_select_audiobook)
197
198             self._chapterMenuItem = gtk.MenuItem("Chapter: ")
199             self._chapterMenuItem.connect("activate", self._on_select_chapter)
200
201             self._sleepMenuItem = gtk.MenuItem("Sleeptimer: 0")
202             self._sleepMenuItem.connect("activate", self._on_select_sleep)
203
204             settingsMenuItem = gtk.MenuItem("Settings")
205             settingsMenuItem.connect("activate", self._on_settings)
206
207             aboutMenuItem = gtk.MenuItem("About")
208             aboutMenuItem.connect("activate", self._on_about_activate)
209
210             helpMenuItem = gtk.MenuItem("Help")
211             helpMenuItem.connect("activate", self.get_help)
212
213             booksMenu = gtk.Menu()
214             booksMenu.append(self._audiobookMenuItem)
215             booksMenu.append(self._chapterMenuItem)
216             booksMenu.append(self._sleepMenuItem)
217             booksMenu.append(settingsMenuItem)
218             booksMenu.append(helpMenuItem)
219             booksMenu.append(aboutMenuItem)
220
221             booksMenuItem = gtk.MenuItem("Books")
222             booksMenuItem.show()
223             booksMenuItem.set_submenu(booksMenu)
224             self._menuBar.append(booksMenuItem)
225             self._menuBar.show_all()
226
227     def setup_timers(self):
228         self.seek_timer = timeout_add_seconds(3, self.update_seek)
229
230     def save_settings(self):
231         config = ConfigParser.SafeConfigParser()
232         self._save_settings(config)
233         with open(constants._user_settings_, "wb") as configFile:
234             config.write(configFile)
235
236     def _save_settings(self, config):
237         config.add_section(constants.__pretty_app_name__)
238         config.set(constants.__pretty_app_name__, "portrait", str(self.__isPortrait))
239         config.set(constants.__pretty_app_name__, "fullscreen", str(self.__window_in_fullscreen))
240         config.set(constants.__pretty_app_name__, "audiopath", self.controller.get_books_path())
241
242     def load_settings(self):
243         config = ConfigParser.SafeConfigParser()
244         config.read(constants._user_settings_)
245         self._load_settings(config)
246
247     def _load_settings(self, config):
248         isPortrait = False
249         window_in_fullscreen = False
250         booksPath = constants._default_book_path_
251         try:
252             isPortrait = config.getboolean(constants.__pretty_app_name__, "portrait")
253             window_in_fullscreen = config.getboolean(constants.__pretty_app_name__, "fullscreen")
254             booksPath = config.get(constants.__pretty_app_name__, "audiopath")
255         except ConfigParser.NoSectionError, e:
256             _moduleLogger.info(
257                 "Settings file %s is missing section %s" % (
258                     constants._user_settings_,
259                     e.section,
260                 )
261             )
262
263         if isPortrait ^ self.__isPortrait:
264             if isPortrait:
265                 orientation = gtk.ORIENTATION_VERTICAL
266             else:
267                 orientation = gtk.ORIENTATION_HORIZONTAL
268             self.set_orientation(orientation)
269
270         self.__window_in_fullscreen = window_in_fullscreen
271         if self.__window_in_fullscreen:
272             self.win.fullscreen()
273         else:
274             self.win.unfullscreen()
275
276         self.controller.load_books_path(booksPath)
277
278     @staticmethod
279     def __format_name(path):
280         if os.path.isfile(path):
281             return os.path.basename(path).rsplit(".", 1)[0]
282         else:
283             return os.path.basename(path)
284
285     @gtk_toolbox.log_exception(_moduleLogger)
286     def _on_select_audiobook(self, *args):
287         if not self._bookSelection:
288             return
289         index = hildonize.touch_selector(
290             self.win,
291             "Audiobook",
292             (self.__format_name(bookPath) for bookPath in self._bookSelection),
293             self._bookSelectionIndex if 0 <= self._bookSelectionIndex else 0,
294         )
295         self._bookSelectionIndex = index
296         bookName = self._bookSelection[index]
297         self.controller.set_book(bookName)
298
299     @gtk_toolbox.log_exception(_moduleLogger)
300     def _on_select_chapter(self, *args):
301         if not self._chapterSelection:
302             return
303         index = hildonize.touch_selector(
304             self.win,
305             "Chapter",
306             (self.__format_name(chapterPath) for chapterPath in self._chapterSelection),
307             self._chapterSelectionIndex if 0 <= self._chapterSelectionIndex else 0,
308         )
309         self._chapterSelectionIndex = index
310         chapterName = self._chapterSelection[index]
311         self.controller.set_chapter(chapterName)
312
313     @gtk_toolbox.log_exception(_moduleLogger)
314     def _on_select_sleep(self, *args):
315         if self.sleep_timer is not None:
316             gobject.source_remove(self.sleep_timer)
317
318         try:
319             index = hildonize.touch_selector(
320                 self.win,
321                 "Sleeptimer",
322                 self._sleepSelection,
323                 self._sleepSelectionIndex if 0 <= self._sleepSelectionIndex else 0,
324             )
325         except RuntimeError:
326             _moduleLogger.exception("Handling as if user cancelled")
327             hildonize.show_information_banner(self.win, "Sleep timer canceled")
328             index = 0
329
330         self._sleepSelectionIndex = index
331         sleepName = self._sleepSelection[index]
332
333         time_out = int(sleepName)
334         if 0 < time_out:
335             timeout_add_seconds(time_out * 60, self.sleep)
336
337         if hildonize.IS_FREMANTLE_SUPPORTED:
338             self.sleeptime_button.set_text("Sleeptimer", sleepName)
339         else:
340             self._sleepMenuItem.get_child().set_text("Sleeptimer: %s" % (sleepName, ))
341
342     @gtk_toolbox.log_exception(_moduleLogger)
343     def __on_call_started(self, callMonitor):
344         self.pause()
345
346     @gtk_toolbox.log_exception(_moduleLogger)
347     def _on_settings(self, *args):
348         if self.__settingsWindow is None:
349             vbox = gtk.VBox()
350             self.__settingsManager = settings.SettingsDialog(vbox)
351
352             self.__settingsWindow = gtk.Window()
353             self.__settingsWindow.add(vbox)
354             self.__settingsWindow = hildonize.hildonize_window(self._app, self.__settingsWindow)
355             self.__settingsManager.window = self.__settingsWindow
356
357             self.__settingsWindow.set_title("Settings")
358             self.__settingsWindow.set_transient_for(self.win)
359             self.__settingsWindow.set_default_size(*self.win.get_size())
360             self.__settingsWindow.connect("delete-event", self._on_settings_delete)
361         self.__settingsManager.set_portrait_state(self.__isPortrait)
362         self.__settingsManager.set_audiobook_path(self.controller.get_books_path())
363         self.__settingsWindow.set_modal(True)
364         self.__settingsWindow.show_all()
365
366     @gtk_toolbox.log_exception(_moduleLogger)
367     def _on_settings_delete(self, *args):
368         self.__settingsWindow.emit_stop_by_name("delete-event")
369         self.__settingsWindow.hide()
370         self.__settingsWindow.set_modal(False)
371
372         isPortrait = self.__settingsManager.is_portrait()
373         if isPortrait ^ self.__isPortrait:
374             if isPortrait:
375                 orientation = gtk.ORIENTATION_VERTICAL
376             else:
377                 orientation = gtk.ORIENTATION_HORIZONTAL
378             self.set_orientation(orientation)
379         if self.__settingsManager.get_audiobook_path() != self.controller.get_books_path():
380             self.controller.load_books_path(self.__settingsManager.get_audiobook_path())
381
382         return True
383
384     @gtk_toolbox.log_exception(_moduleLogger)
385     def update_seek(self):
386         #print self.controller.get_percentage()
387         if self.controller.is_playing():
388             gtk.gdk.threads_enter()
389             self.seek.set_value(self.controller.get_percentage() * 100)
390             gtk.gdk.threads_leave()
391         #self.controller.get_percentage() 
392         return True                     # run again
393
394     @gtk_toolbox.log_exception(_moduleLogger)
395     def sleep(self):
396         _moduleLogger.info("sleep time timeout")
397         hildonize.show_information_banner(self.win, "Sleep timer")
398         self.controller.stop()
399         self.set_button_text("Resume", "Resume playing the audiobook")
400         return False                    # do not repeat
401
402     @gtk_toolbox.log_exception(_moduleLogger)
403     def get_help(self, button):
404         Browser.open("file:///opt/Nqa-Audiobook-player/Help/nqaap.html")
405
406     @gtk_toolbox.log_exception(_moduleLogger)
407     def seek_changed(self, seek, scroll , value):
408         # print "sok", scroll
409         self.controller.seek_percent(seek.get_value() / 100.0)
410
411     @gtk_toolbox.log_exception(_moduleLogger)
412     def _on_next_chapter(self, *args):
413         self.controller.next_chapter()
414
415     @gtk_toolbox.log_exception(_moduleLogger)
416     def _on_previous_chapter(self, *args):
417         self.controller.previous_chapter()
418
419     @gtk_toolbox.log_exception(_moduleLogger)
420     def play_pressed(self, button):
421         if self.controller.is_playing():
422             self.pause()
423         else:
424             self.play()
425
426     @gtk_toolbox.log_exception(_moduleLogger)
427     def on_key_press(self, widget, event, *args):
428         RETURN_TYPES = (gtk.keysyms.Return, gtk.keysyms.ISO_Enter, gtk.keysyms.KP_Enter)
429         isCtrl = bool(event.get_state() & gtk.gdk.CONTROL_MASK)
430         if (
431             event.keyval == gtk.keysyms.F6 or
432             event.keyval in RETURN_TYPES and isCtrl
433         ):
434             # The "Full screen" hardware key has been pressed 
435             if self.__window_in_fullscreen:
436                 self.win.unfullscreen ()
437             else:
438                 self.win.fullscreen ()
439             return True
440         elif event.keyval == gtk.keysyms.o and isCtrl:
441             self._toggle_rotate()
442             return True
443         elif (
444             event.keyval in (gtk.keysyms.w, gtk.keysyms.q) and
445             event.get_state() & gtk.gdk.CONTROL_MASK
446         ):
447             self.quit()
448         elif event.keyval == gtk.keysyms.l and event.get_state() & gtk.gdk.CONTROL_MASK:
449             with open(constants._user_logpath_, "r") as f:
450                 logLines = f.xreadlines()
451                 log = "".join(logLines)
452                 self._clipboard.set_text(str(log))
453             return True
454         elif event.keyval in RETURN_TYPES:
455             if self.controller.is_playing():
456                 self.pause()
457             else:
458                 self.play()
459             return True
460         elif event.keyval == gtk.keysyms.Left:
461             self.controller.previous_chapter()
462             return True
463         elif event.keyval == gtk.keysyms.Right:
464             self.controller.next_chapter()
465             return True
466
467     @gtk_toolbox.log_exception(_moduleLogger)
468     def _on_window_state_change(self, widget, event, *args):
469         if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
470             self.__window_in_fullscreen = True
471         else:
472             self.__window_in_fullscreen = False
473
474     @gtk_toolbox.log_exception(_moduleLogger)
475     def quit(self, *args):             # what are the arguments?
476         _moduleLogger.info("Shutting down")
477         try:
478             self.save_settings()
479             self.controller.stop()          # to save the state
480         finally:
481             gtk.main_quit()
482
483     @gtk_toolbox.log_exception(_moduleLogger)
484     def _on_about_activate(self, *args):
485         dlg = gtk.AboutDialog()
486         dlg.set_name(constants.__pretty_app_name__)
487         dlg.set_version("%s-%d" % (constants.__version__, constants.__build__))
488         dlg.set_copyright("Copyright 2010")
489         dlg.set_comments("")
490         dlg.set_website("http://nqaap.garage.maemo.org/")
491         dlg.set_authors(["Pengman <pengmeister@gmail.com>", "Ed Page <eopage@byu.net>"])
492         dlg.run()
493         dlg.destroy()
494
495     # Actions:  
496
497     def play(self):
498         self.set_button_text("Stop", "Stop playing the audiobook")
499         self.controller.play()
500
501     def pause(self):
502         self.set_button_text("Resume", "Resume playing the audiobook")
503         self.controller.stop()
504
505     def set_orientation(self, orientation):
506         if orientation == gtk.ORIENTATION_VERTICAL:
507             if self.__isPortrait:
508                 return
509             hildonize.window_to_portrait(self.win)
510             self.__isPortrait = True
511
512             self._viewLayout.remove(self._controlLayout)
513             self._mainLayout.add(self._controlLayout)
514         elif orientation == gtk.ORIENTATION_HORIZONTAL:
515             if not self.__isPortrait:
516                 return
517             hildonize.window_to_landscape(self.win)
518             self.__isPortrait = False
519
520             self._mainLayout.remove(self._controlLayout)
521             self._viewLayout.add(self._controlLayout)
522         else:
523             raise NotImplementedError(orientation)
524
525     def get_orientation(self):
526         return gtk.ORIENTATION_VERTICAL if self.__isPortrait else gtk.ORIENTATION_HORIZONTAL
527
528     def _toggle_rotate(self):
529         if self.__isPortrait:
530             self.set_orientation(gtk.ORIENTATION_HORIZONTAL)
531         else:
532             self.set_orientation(gtk.ORIENTATION_VERTICAL)
533
534     def change_chapter(self, chapterName):
535         if chapterName is None:
536             _moduleLogger.debug("chapter selection canceled.")
537             #import pdb; pdb.set_trace()     # start debugger
538             self.ignore_next_chapter_change = True
539             return True                   # this should end the function and indicate it has been handled
540
541         if self.ignore_next_chapter_change:
542             self.ignore_next_chapter_change = False
543             _moduleLogger.debug("followup chapter selection canceled.")
544             #import pdb; pdb.set_trace()     # start debugger
545             return True                   # this should end the function and indicate it has been handled
546
547         if self.auto_chapter_selected:
548             _moduleLogger.debug("chapter changed (by controller) to: %s" % chapterName)
549             self.auto_chapter_selected = False
550             # do nothing
551         else:
552             _moduleLogger.debug("chapter selection sendt to controller: %s" % chapterName)
553             self.controller.set_chapter(chapterName) # signal controller
554             self.set_button_text("Play", "Start playing the audiobook") # reset button
555
556     def set_button_text(self, title, text):
557         if hildonize.IS_FREMANTLE_SUPPORTED:
558             self.button.set_text(title, text)
559         else:
560             self.button.set_label("%s" % (title, ))
561
562     def set_books(self, books):
563         _moduleLogger.debug("new books")
564         del self._bookSelection[:]
565         self._bookSelection.extend(books)
566         if len(books) == 0 and self.controller is not None:
567             hildonize.show_information_banner(self.win, "No audiobooks found. \nPlease place your audiobooks in the directory %s" % self.controller.get_books_path())
568
569     def set_book(self, bookPath, cover):
570         bookName = self.__format_name(bookPath)
571
572         self.set_button_text("Play", "Start playing the audiobook") # reset button
573         self._set_display_title(bookName)
574         if hildonize.IS_FREMANTLE_SUPPORTED:
575             self.book_button.set_text("Audiobook", bookName)
576         else:
577             self._audiobookMenuItem.get_child().set_text("Audiobook: %s" % (bookName, ))
578         if cover != "":
579             self.cover.set_from_file(cover)
580
581     def set_chapter(self, chapterIndex):
582         '''
583         Called from controller whenever a new chapter is started
584
585         chapter parameter is supposed to be the index for the chapter, not the name
586         '''
587         self.auto_chapter_selected = True
588         self._set_display_chapter(str(chapterIndex + 1))
589         if hildonize.IS_FREMANTLE_SUPPORTED:
590             self.chapter_button.set_text("Chapter", str(chapterIndex))
591         else:
592             self._chapterMenuItem.get_child().set_text("Chapter: %s" % (chapterIndex, ))
593
594     def set_chapters(self, chapters):
595         _moduleLogger.debug("setting chapters" )
596         del self._chapterSelection[:]
597         self._chapterSelection.extend(chapters)
598
599     def set_sleep_timer(self, mins):
600         pass
601
602     # Utils
603     def set_selected_value(self, button, value):
604         i = button.get_selector().get_model(0).index[value] # get index of value from list
605         button.set_active(i)                                # set active index to that index
606
607     def _set_display_title(self, title):
608         self.title.set_markup("<b><big>%s</big></b>" % title)
609
610     def _set_display_chapter(self, chapter):
611         self.chapter.set_markup("<b><big>Chapter %s</big></b>" % chapter)
612
613
614 def _old_timeout_add_seconds(timeout, callback):
615     return gobject.timeout_add(timeout * 1000, callback)
616
617
618 def _timeout_add_seconds(timeout, callback):
619     return gobject.timeout_add_seconds(timeout, callback)
620
621
622 try:
623     gobject.timeout_add_seconds
624     timeout_add_seconds = _timeout_add_seconds
625 except AttributeError:
626     timeout_add_seconds = _old_timeout_add_seconds
627
628
629 if __name__ == "__main__":
630     g = Gui(None)