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):
131 BasicWindow.__init__(self, player, store, index)
133 self._radioButton = self._create_button("radio", "Radio")
134 self._radioButton.connect("clicked", self._on_radio_selected)
135 self._radioWrapper = gtk.VBox()
136 self._radioWrapper.pack_start(self._radioButton, False, True)
138 self._conferenceButton = self._create_button("conferences", "Conferences")
139 #self._conferenceButton.connect("clicked", self._on_conference_selected)
140 self._conferenceWrapper = gtk.VBox()
141 self._conferenceWrapper.pack_start(self._conferenceButton, False, True)
143 self._magazineButton = self._create_button("magazines", "Magazines")
144 #self._magazineButton.connect("clicked", self._on_magazine_selected)
145 self._magazineWrapper = gtk.VBox()
146 self._magazineWrapper.pack_start(self._magazineButton, False, True)
148 self._scriptureButton = self._create_button("scriptures", "Scriptures")
149 #self._scriptureButton.connect("clicked", self._on_scripture_selected)
150 self._scriptureWrapper = gtk.VBox()
151 self._scriptureWrapper.pack_start(self._scriptureButton, False, True)
153 self._buttonLayout = gtk.VBox(True, 5)
154 self._buttonLayout.set_property("border-width", 5)
155 self._buttonLayout.pack_start(self._radioWrapper, True, True)
156 self._buttonLayout.pack_start(self._conferenceWrapper, True, True)
157 self._buttonLayout.pack_start(self._magazineWrapper, True, True)
158 self._buttonLayout.pack_start(self._scriptureWrapper, True, True)
160 self._playcontrol = playcontrol.PlayControl(player, store)
162 self._layout.pack_start(self._buttonLayout, True, True)
163 self._layout.pack_start(self._playcontrol.toplevel, False, True)
165 self._window.set_title(constants.__pretty_app_name__)
166 self._window.show_all()
167 self._errorBanner.toplevel.hide()
168 self._playcontrol.toplevel.hide()
170 def _create_button(self, icon, message):
171 image = self._store.get_image_from_store(self._store.STORE_LOOKUP[icon])
174 label.set_text(message)
176 buttonLayout = gtk.HBox(False, 5)
177 buttonLayout.pack_start(image, False, False)
178 buttonLayout.pack_start(label, False, True)
179 button = gtk.Button()
180 button.add(buttonLayout)
184 @misc_utils.log_exception(_moduleLogger)
185 def _on_radio_selected(self, *args):
186 radioView = RadioView(self._player, self._store, self._index)
187 radioView.window.set_modal(True)
188 radioView.window.set_transient_for(self._window)
189 radioView.window.set_default_size(*self._window.get_size())
192 class RadioView(BasicWindow):
194 def __init__(self, player, store, index):
195 BasicWindow.__init__(self, player, store, index)
197 self._loadingBanner = banners.GenericBanner()
199 headerPath = self._store.STORE_LOOKUP["radio_header"]
200 self._header = self._store.get_image_from_store(headerPath)
202 self._programmingModel = gtk.ListStore(
207 textrenderer = gtk.CellRendererText()
208 timeColumn = gtk.TreeViewColumn("Time")
209 timeColumn.pack_start(textrenderer, expand=True)
210 timeColumn.add_attribute(textrenderer, "text", 0)
212 textrenderer = gtk.CellRendererText()
213 titleColumn = gtk.TreeViewColumn("Program")
214 titleColumn.pack_start(textrenderer, expand=True)
215 titleColumn.add_attribute(textrenderer, "text", 1)
217 self._treeView = gtk.TreeView()
218 self._treeView.set_headers_visible(False)
219 self._treeView.set_model(self._programmingModel)
220 self._treeView.append_column(timeColumn)
221 self._treeView.append_column(titleColumn)
222 self._treeView.get_selection().connect("changed", self._on_row_changed)
224 self._treeScroller = gtk.ScrolledWindow()
225 self._treeScroller.add(self._treeView)
226 self._treeScroller.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
228 self._presenter = presenter.StreamMiniPresenter(self._player, self._store)
230 self._radioLayout = gtk.VBox(False)
231 self._radioLayout.pack_start(self._header, False, False)
232 self._radioLayout.pack_start(self._treeScroller, True, True)
233 self._radioLayout.pack_start(self._presenter.toplevel, False, True)
235 self._programNavigation = presenter.NavigationBox()
236 self._programNavigation.toplevel.add(self._radioLayout)
237 self._programNavigation.connect("action", self._on_nav_action)
239 self._layout.pack_start(self._loadingBanner.toplevel, False, False)
240 self._layout.pack_start(self._programNavigation.toplevel, True, True)
242 self._window.set_title("Radio")
243 self._window.show_all()
244 self._errorBanner.toplevel.hide()
245 self._loadingBanner.toplevel.hide()
247 self._dateShown = datetime.datetime.now()
250 def _show_loading(self):
251 animationPath = self._store.STORE_LOOKUP["loading"]
252 animation = self._store.get_pixbuf_animation_from_store(animationPath)
253 self._loadingBanner.show(animation, "Loading...")
255 def _hide_loading(self):
256 self._loadingBanner.hide()
260 self._programmingModel.clear()
261 self._index.download_radio(self._on_channels, self._on_load_error)
263 def _get_current_row(self):
264 nowTime = self._dateShown.strftime("%H:%M:%S")
265 for i, row in enumerate(self._programmingModel):
271 @misc_utils.log_exception(_moduleLogger)
272 def _on_nav_action(self, widget, navState):
273 _moduleLogger.info(navState)
274 if navState == "clicking":
276 elif navState == "down":
277 self.window.destroy()
278 elif navState == "up":
280 elif navState == "left":
281 self._dateShown += datetime.timedelta(days=1)
283 elif navState == "right":
284 self._dateShown -= datetime.timedelta(days=1)
287 @misc_utils.log_exception(_moduleLogger)
288 def _on_channels(self, channels):
289 if self._isDestroyed:
290 _moduleLogger.info("Download complete but window destroyed")
293 channels = list(channels)
294 if 1 < len(channels):
295 _moduleLogger.warning("More channels now available!")
296 channel = channels[0]
297 self._index.download_radio(self._on_channel, self._on_load_error, channel["id"])
299 @misc_utils.log_exception(_moduleLogger)
300 def _on_channel(self, programs):
301 if self._isDestroyed:
302 _moduleLogger.info("Download complete but window destroyed")
306 for program in programs:
307 row = program["time"], program["title"]
308 self._programmingModel.append(row)
310 path = (self._get_current_row(), )
311 self._treeView.scroll_to_cell(path)
312 self._treeView.get_selection().select_path(path)
314 @misc_utils.log_exception(_moduleLogger)
315 def _on_load_error(self, exception):
317 self._errorBanner.push_message(exception)
319 @misc_utils.log_exception(_moduleLogger)
320 def _on_row_changed(self, selection):
321 path = (self._get_current_row(), )
322 if not selection.path_is_selected(path):
323 # Undo the user's changing of the selection
324 selection.select_path(path)