10 import util.misc as misc_utils
17 _moduleLogger = logging.getLogger(__name__)
20 class BasicWindow(gobject.GObject):
24 gobject.SIGNAL_RUN_LAST,
29 gobject.SIGNAL_RUN_LAST,
31 (gobject.TYPE_PYOBJECT, ),
35 def __init__(self, player, store, index):
36 gobject.GObject.__init__(self)
37 self._isDestroyed = False
43 self._clipboard = gtk.clipboard_get()
44 self._windowInFullscreen = False
46 self._errorBanner = banners.StackingBanner()
48 self._layout = gtk.VBox()
49 self._layout.pack_start(self._errorBanner.toplevel, False, True)
51 self._window = gtk.Window()
52 self._window.add(self._layout)
53 self._window = hildonize.hildonize_window(self, self._window)
55 self._window.set_icon(self._store.get_pixbuf_from_store(self._store.STORE_LOOKUP["icon"]))
56 self._window.connect("key-press-event", self._on_key_press)
57 self._window.connect("window-state-event", self._on_window_state_change)
58 self._window.connect("destroy", self._on_destroy)
64 def save_settings(self, config, sectionName):
65 config.add_section(sectionName)
66 config.set(sectionName, "fullscreen", str(self._windowInFullscreen))
68 def load_settings(self, config, sectionName):
70 self._windowInFullscreen = config.getboolean(sectionName, "fullscreen")
71 except ConfigParser.NoSectionError, e:
73 "Settings file %s is missing section %s" % (
74 constants._user_settings_,
79 if self._windowInFullscreen:
80 self._window.fullscreen()
82 self._window.unfullscreen()
84 @misc_utils.log_exception(_moduleLogger)
85 def _on_destroy(self, *args):
86 self._isDestroyed = True
88 @misc_utils.log_exception(_moduleLogger)
89 def _on_window_state_change(self, widget, event, *args):
90 if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
91 self._windowInFullscreen = True
93 self._windowInFullscreen = False
94 self.emit("fullscreen", self._windowInFullscreen)
96 @misc_utils.log_exception(_moduleLogger)
97 def _on_key_press(self, widget, event, *args):
98 RETURN_TYPES = (gtk.keysyms.Return, gtk.keysyms.ISO_Enter, gtk.keysyms.KP_Enter)
99 isCtrl = bool(event.get_state() & gtk.gdk.CONTROL_MASK)
101 event.keyval == gtk.keysyms.F6 or
102 event.keyval in RETURN_TYPES and isCtrl
104 # The "Full screen" hardware key has been pressed
105 if self._windowInFullscreen:
106 self._window.unfullscreen ()
108 self._window.fullscreen ()
111 event.keyval in (gtk.keysyms.w, ) and
112 event.get_state() & gtk.gdk.CONTROL_MASK
114 self._window.destroy()
116 event.keyval in (gtk.keysyms.q, ) and
117 event.get_state() & gtk.gdk.CONTROL_MASK
120 elif event.keyval == gtk.keysyms.l and event.get_state() & gtk.gdk.CONTROL_MASK:
121 with open(constants._user_logpath_, "r") as f:
122 logLines = f.xreadlines()
123 log = "".join(logLines)
124 self._clipboard.set_text(str(log))
128 class SourceSelector(BasicWindow):
130 def __init__(self, player, store, index):
133 BasicWindow.__init__(self, player, store, index)
135 self._loadingBanner = banners.GenericBanner()
137 self._radioButton = self._create_button("radio", "Radio")
138 self._radioButton.connect("clicked", self._on_source_selected, RadioWindow)
139 self._radioWrapper = gtk.VBox()
140 self._radioWrapper.pack_start(self._radioButton, False, True)
142 self._conferenceButton = self._create_button("conferences", "Conferences")
143 self._conferenceButton.connect("clicked", self._on_source_selected, ConferencesWindow)
144 self._conferenceWrapper = gtk.VBox()
145 self._conferenceWrapper.pack_start(self._conferenceButton, False, True)
147 self._magazineButton = self._create_button("magazines", "Magazines")
148 #self._magazineButton.connect("clicked", self._on_source_selected)
149 self._magazineWrapper = gtk.VBox()
150 self._magazineWrapper.pack_start(self._magazineButton, False, True)
152 self._scriptureButton = self._create_button("scriptures", "Scriptures")
153 #self._scriptureButton.connect("clicked", self._on_source_selected)
154 self._scriptureWrapper = gtk.VBox()
155 self._scriptureWrapper.pack_start(self._scriptureButton, False, True)
157 self._buttonLayout = gtk.VBox(True, 5)
158 self._buttonLayout.set_property("border-width", 5)
159 self._buttonLayout.pack_start(self._radioWrapper, True, True)
160 self._buttonLayout.pack_start(self._conferenceWrapper, True, True)
161 self._buttonLayout.pack_start(self._magazineWrapper, True, True)
162 self._buttonLayout.pack_start(self._scriptureWrapper, True, True)
164 self._playcontrol = playcontrol.PlayControl(player, store)
166 self._layout.pack_start(self._loadingBanner.toplevel, False, False)
167 self._layout.pack_start(self._buttonLayout, True, True)
168 self._layout.pack_start(self._playcontrol.toplevel, False, True)
170 self._window.set_title(constants.__pretty_app_name__)
171 self._window.show_all()
172 self._errorBanner.toplevel.hide()
173 self._playcontrol.toplevel.hide()
177 def _show_loading(self):
178 animationPath = self._store.STORE_LOOKUP["loading"]
179 animation = self._store.get_pixbuf_animation_from_store(animationPath)
180 self._loadingBanner.show(animation, "Loading...")
181 self._buttonLayout.set_sensitive(False)
183 def _hide_loading(self):
184 self._loadingBanner.hide()
185 self._buttonLayout.set_sensitive(True)
189 self._index.download(
195 def _create_button(self, icon, message):
196 image = self._store.get_image_from_store(self._store.STORE_LOOKUP[icon])
199 label.set_text(message)
201 buttonLayout = gtk.HBox(False, 5)
202 buttonLayout.pack_start(image, False, False)
203 buttonLayout.pack_start(label, False, True)
204 button = gtk.Button()
205 button.add(buttonLayout)
209 @misc_utils.log_exception(_moduleLogger)
210 def _on_languages(self, languages):
212 self._languages = list(languages)
214 @misc_utils.log_exception(_moduleLogger)
215 def _on_error(self, exception):
217 self._errorBanner.push_message(exception)
219 @misc_utils.log_exception(_moduleLogger)
220 def _on_source_selected(self, widget, Source):
221 sourceWindow = Source(self._player, self._store, self._index, self._languages[0]["id"])
222 sourceWindow.window.set_modal(True)
223 sourceWindow.window.set_transient_for(self._window)
224 sourceWindow.window.set_default_size(*self._window.get_size())
227 gobject.type_register(SourceSelector)
230 class RadioWindow(BasicWindow):
232 def __init__(self, player, store, index, languageId):
233 BasicWindow.__init__(self, player, store, index)
235 self._loadingBanner = banners.GenericBanner()
237 headerPath = self._store.STORE_LOOKUP["radio_header"]
238 self._header = self._store.get_image_from_store(headerPath)
239 self._headerNavigation = presenter.NavigationBox()
240 self._headerNavigation.toplevel.add(self._header)
241 self._headerNavigation.connect("action", self._on_nav_action)
244 self._programmingModel = gtk.ListStore(
249 textrenderer = gtk.CellRendererText()
250 timeColumn = gtk.TreeViewColumn("Time")
251 timeColumn.pack_start(textrenderer, expand=True)
252 timeColumn.add_attribute(textrenderer, "text", 0)
254 textrenderer = gtk.CellRendererText()
255 titleColumn = gtk.TreeViewColumn("Program")
256 titleColumn.pack_start(textrenderer, expand=True)
257 titleColumn.add_attribute(textrenderer, "text", 1)
259 self._treeView = gtk.TreeView()
260 self._treeView.set_headers_visible(False)
261 self._treeView.set_model(self._programmingModel)
262 self._treeView.append_column(timeColumn)
263 self._treeView.append_column(titleColumn)
264 self._treeView.get_selection().connect("changed", self._on_row_changed)
266 self._treeScroller = gtk.ScrolledWindow()
267 self._treeScroller.add(self._treeView)
268 self._treeScroller.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
270 self._presenter = presenter.StreamMiniPresenter(self._player, self._store)
271 self._presenterNavigation = presenter.NavigationBox()
272 self._presenterNavigation.toplevel.add(self._presenter.toplevel)
273 self._presenterNavigation.connect("action", self._on_nav_action)
275 self._radioLayout = gtk.VBox(False)
276 self._radioLayout.pack_start(self._headerNavigation.toplevel, False, False)
277 self._radioLayout.pack_start(self._treeScroller, True, True)
278 self._radioLayout.pack_start(self._presenterNavigation.toplevel, False, True)
280 self._layout.pack_start(self._loadingBanner.toplevel, False, False)
281 self._layout.pack_start(self._radioLayout, True, True)
283 self._window.set_title("Radio")
284 self._window.show_all()
285 self._errorBanner.toplevel.hide()
286 self._loadingBanner.toplevel.hide()
288 self._dateShown = datetime.datetime.now()
291 def _show_loading(self):
292 animationPath = self._store.STORE_LOOKUP["loading"]
293 animation = self._store.get_pixbuf_animation_from_store(animationPath)
294 self._loadingBanner.show(animation, "Loading...")
296 def _hide_loading(self):
297 self._loadingBanner.hide()
301 self._programmingModel.clear()
302 self._index.download(
303 "get_radio_channels",
308 def _get_current_row(self):
309 nowTime = self._dateShown.strftime("%H:%M:%S")
311 for i, row in enumerate(self._programmingModel):
320 @misc_utils.log_exception(_moduleLogger)
321 def _on_nav_action(self, widget, navState):
322 if navState == "clicking":
324 elif navState == "down":
325 self.window.destroy()
326 elif navState == "up":
328 elif navState == "left":
329 self._dateShown += datetime.timedelta(days=1)
331 elif navState == "right":
332 self._dateShown -= datetime.timedelta(days=1)
335 @misc_utils.log_exception(_moduleLogger)
336 def _on_channels(self, channels):
337 if self._isDestroyed:
338 _moduleLogger.info("Download complete but window destroyed")
341 channels = list(channels)
342 if 1 < len(channels):
343 _moduleLogger.warning("More channels now available!")
344 channel = channels[0]
345 self._index.download(
346 "get_radio_channel_programming",
353 @misc_utils.log_exception(_moduleLogger)
354 def _on_channel(self, programs):
355 if self._isDestroyed:
356 _moduleLogger.info("Download complete but window destroyed")
360 for program in programs:
361 row = program["time"], program["title"]
362 self._programmingModel.append(row)
364 path = (self._get_current_row(), )
365 self._treeView.scroll_to_cell(path)
366 self._treeView.get_selection().select_path(path)
368 @misc_utils.log_exception(_moduleLogger)
369 def _on_load_error(self, exception):
371 self._errorBanner.push_message(exception)
373 @misc_utils.log_exception(_moduleLogger)
374 def _on_row_changed(self, selection):
375 if len(self._programmingModel) == 0:
378 rowIndex = self._get_current_row()
380 if not selection.path_is_selected(path):
381 # Undo the user's changing of the selection
382 selection.select_path(path)
385 gobject.type_register(RadioWindow)
388 class ListWindow(BasicWindow):
390 def __init__(self, player, store, index):
391 BasicWindow.__init__(self, player, store, index)
393 self._loadingBanner = banners.GenericBanner()
395 modelTypes, columns = zip(*self._get_columns())
397 self._model = gtk.ListStore(*modelTypes)
399 self._treeView = gtk.TreeView()
400 self._treeView.connect("row-activated", self._on_row_activated)
401 self._treeView.set_headers_visible(False)
402 self._treeView.set_model(self._model)
403 for column in columns:
404 if column is not None:
405 self._treeView.append_column(column)
407 self._treeScroller = gtk.ScrolledWindow()
408 self._treeScroller.add(self._treeView)
409 self._treeScroller.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
411 self._playcontrol = playcontrol.PlayControl(self._player, self._store)
413 self._contentLayout = gtk.VBox(False)
414 self._contentLayout.pack_start(self._treeScroller, True, True)
415 self._contentLayout.pack_start(self._playcontrol.toplevel, False, True)
417 self._layout.pack_start(self._loadingBanner.toplevel, False, False)
418 self._layout.pack_start(self._contentLayout, True, True)
420 self._window.show_all()
421 self._errorBanner.toplevel.hide()
422 self._loadingBanner.toplevel.hide()
425 self._playcontrol.refresh()
428 def _get_columns(cls):
429 raise NotImplementedError("")
431 def _get_current_row(self):
432 raise NotImplementedError("")
434 @misc_utils.log_exception(_moduleLogger)
435 def _on_row_activated(self, view, path, column):
436 raise NotImplementedError("")
438 def _show_loading(self):
439 animationPath = self._store.STORE_LOOKUP["loading"]
440 animation = self._store.get_pixbuf_animation_from_store(animationPath)
441 self._loadingBanner.show(animation, "Loading...")
443 def _hide_loading(self):
444 self._loadingBanner.hide()
450 def _select_row(self):
451 path = (self._get_current_row(), )
452 self._treeView.scroll_to_cell(path)
453 self._treeView.get_selection().select_path(path)
456 class ConferencesWindow(ListWindow):
458 def __init__(self, player, store, index, languageId):
459 self._languageId = languageId
461 ListWindow.__init__(self, player, store, index)
462 self._window.set_title("Conferences")
465 def _get_columns(cls):
466 yield gobject.TYPE_STRING, None
468 textrenderer = gtk.CellRendererText()
469 column = gtk.TreeViewColumn("Date")
470 column.pack_start(textrenderer, expand=True)
471 column.add_attribute(textrenderer, "text", 1)
472 yield gobject.TYPE_STRING, column
474 textrenderer = gtk.CellRendererText()
475 column = gtk.TreeViewColumn("Conference")
476 column.pack_start(textrenderer, expand=True)
477 column.add_attribute(textrenderer, "text", 2)
478 yield gobject.TYPE_STRING, column
480 def _get_current_row(self):
481 # @todo Not implemented yet
485 ListWindow._refresh(self)
486 self._index.download(
488 self._on_conferences,
493 @misc_utils.log_exception(_moduleLogger)
494 def _on_conferences(self, programs):
495 if self._isDestroyed:
496 _moduleLogger.info("Download complete but window destroyed")
500 for program in programs:
501 row = program["id"], program["title"], program["full_title"]
502 self._model.append(row)
504 path = (self._get_current_row(), )
505 self._treeView.scroll_to_cell(path)
506 self._treeView.get_selection().select_path(path)
508 @misc_utils.log_exception(_moduleLogger)
509 def _on_error(self, exception):
511 self._errorBanner.push_message(exception)
513 @misc_utils.log_exception(_moduleLogger)
514 def _on_row_activated(self, view, path, column):
515 itr = self._model.get_iter(path)
516 conferenceId = self._model.get_value(itr, 0)
518 sessionsWindow = ConferenceSessionsWindow(self._player, self._store, self._index, conferenceId)
519 sessionsWindow.window.set_modal(True)
520 sessionsWindow.window.set_transient_for(self._window)
521 sessionsWindow.window.set_default_size(*self._window.get_size())
524 gobject.type_register(ConferencesWindow)
527 class ConferenceSessionsWindow(ListWindow):
529 def __init__(self, player, store, index, conferenceId):
530 self._conferenceId = conferenceId
532 ListWindow.__init__(self, player, store, index)
533 self._window.set_title("Sessions")
536 def _get_columns(cls):
537 yield gobject.TYPE_STRING, None
539 textrenderer = gtk.CellRendererText()
540 column = gtk.TreeViewColumn("Session")
541 column.pack_start(textrenderer, expand=True)
542 column.add_attribute(textrenderer, "text", 1)
543 yield gobject.TYPE_STRING, column
545 def _get_current_row(self):
546 # @todo Not implemented yet
550 ListWindow._refresh(self)
551 self._index.download(
552 "get_conference_sessions",
553 self._on_conference_sessions,
558 @misc_utils.log_exception(_moduleLogger)
559 def _on_conference_sessions(self, programs):
560 if self._isDestroyed:
561 _moduleLogger.info("Download complete but window destroyed")
565 for program in programs:
566 row = program["id"], program["title"]
567 self._model.append(row)
569 path = (self._get_current_row(), )
570 self._treeView.scroll_to_cell(path)
571 self._treeView.get_selection().select_path(path)
573 @misc_utils.log_exception(_moduleLogger)
574 def _on_error(self, exception):
576 self._errorBanner.push_message(exception)
578 @misc_utils.log_exception(_moduleLogger)
579 def _on_row_activated(self, view, path, column):
580 itr = self._model.get_iter(path)
581 sessionId = self._model.get_value(itr, 0)
583 sessionsWindow = ConferenceTalksWindow(self._player, self._store, self._index, sessionId)
584 sessionsWindow.window.set_modal(True)
585 sessionsWindow.window.set_transient_for(self._window)
586 sessionsWindow.window.set_default_size(*self._window.get_size())
589 gobject.type_register(ConferenceSessionsWindow)
592 class ConferenceTalksWindow(ListWindow):
594 def __init__(self, player, store, index, sessionId):
595 self._sessionId = sessionId
597 ListWindow.__init__(self, player, store, index)
598 self._window.set_title("Talks")
601 def _get_columns(cls):
602 yield gobject.TYPE_STRING, None
604 textrenderer = gtk.CellRendererText()
605 column = gtk.TreeViewColumn("Talk")
606 column.pack_start(textrenderer, expand=True)
607 column.add_attribute(textrenderer, "text", 1)
608 yield gobject.TYPE_STRING, column
610 def _get_current_row(self):
611 # @todo Not implemented yet
615 ListWindow._refresh(self)
616 self._index.download(
617 "get_conference_talks",
618 self._on_conference_talks,
623 @misc_utils.log_exception(_moduleLogger)
624 def _on_conference_talks(self, programs):
625 if self._isDestroyed:
626 _moduleLogger.info("Download complete but window destroyed")
630 for program in programs:
631 row = program["id"], "%s\n%s" % (program["title"], program["speaker"])
632 self._model.append(row)
634 path = (self._get_current_row(), )
635 self._treeView.scroll_to_cell(path)
636 self._treeView.get_selection().select_path(path)
638 @misc_utils.log_exception(_moduleLogger)
639 def _on_error(self, exception):
641 self._errorBanner.push_message(exception)
643 @misc_utils.log_exception(_moduleLogger)
644 def _on_row_activated(self, view, path, column):
645 raise NotImplementedError("")
648 gobject.type_register(ConferenceTalksWindow)