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