97556bcdd9b760af1a6ec6b83735362bec61552b
[watersofshiloah] / src / windows.py
1 import ConfigParser
2 import datetime
3 import logging
4
5 import gobject
6 import gtk
7
8 import constants
9 import hildonize
10 import util.misc as misc_utils
11
12 import banners
13 import playcontrol
14 import presenter
15
16
17 _moduleLogger = logging.getLogger(__name__)
18
19
20 class BasicWindow(gobject.GObject):
21
22         __gsignals__ = {
23                 'quit' : (
24                         gobject.SIGNAL_RUN_LAST,
25                         gobject.TYPE_NONE,
26                         (),
27                 ),
28                 'fullscreen' : (
29                         gobject.SIGNAL_RUN_LAST,
30                         gobject.TYPE_NONE,
31                         (gobject.TYPE_PYOBJECT, ),
32                 ),
33         }
34
35         def __init__(self, player, store, index):
36                 gobject.GObject.__init__(self)
37
38                 self._player = player
39                 self._store = store
40                 self._index = index
41
42                 self._clipboard = gtk.clipboard_get()
43                 self._windowInFullscreen = False
44
45                 self._errorBanner = banners.StackingBanner()
46
47                 self._layout = gtk.VBox()
48                 self._layout.pack_start(self._errorBanner.toplevel, False, True)
49
50                 self._window = gtk.Window()
51                 self._window.add(self._layout)
52                 self._window = hildonize.hildonize_window(self, self._window)
53
54                 self._window.set_icon(self._store.get_pixbuf_from_store(self._store.STORE_LOOKUP["icon"]))
55                 self._window.connect("key-press-event", self._on_key_press)
56                 self._window.connect("window-state-event", self._on_window_state_change)
57
58         @property
59         def window(self):
60                 return self._window
61
62         def save_settings(self, config, sectionName):
63                 config.add_section(sectionName)
64                 config.set(sectionName, "fullscreen", str(self._windowInFullscreen))
65
66         def load_settings(self, config, sectionName):
67                 try:
68                         self._windowInFullscreen = config.getboolean(sectionName, "fullscreen")
69                 except ConfigParser.NoSectionError, e:
70                         _moduleLogger.info(
71                                 "Settings file %s is missing section %s" % (
72                                         constants._user_settings_,
73                                         e.section,
74                                 )
75                         )
76
77                 if self._windowInFullscreen:
78                         self._window.fullscreen()
79                 else:
80                         self._window.unfullscreen()
81
82         @misc_utils.log_exception(_moduleLogger)
83         def _on_window_state_change(self, widget, event, *args):
84                 if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
85                         self._windowInFullscreen = True
86                 else:
87                         self._windowInFullscreen = False
88                 self.emit("fullscreen", self._windowInFullscreen)
89
90         @misc_utils.log_exception(_moduleLogger)
91         def _on_key_press(self, widget, event, *args):
92                 RETURN_TYPES = (gtk.keysyms.Return, gtk.keysyms.ISO_Enter, gtk.keysyms.KP_Enter)
93                 isCtrl = bool(event.get_state() & gtk.gdk.CONTROL_MASK)
94                 if (
95                         event.keyval == gtk.keysyms.F6 or
96                         event.keyval in RETURN_TYPES and isCtrl
97                 ):
98                         # The "Full screen" hardware key has been pressed
99                         if self._windowInFullscreen:
100                                 self._window.unfullscreen ()
101                         else:
102                                 self._window.fullscreen ()
103                         return True
104                 elif (
105                         event.keyval in (gtk.keysyms.w, ) and
106                         event.get_state() & gtk.gdk.CONTROL_MASK
107                 ):
108                         self._window.destroy()
109                 elif (
110                         event.keyval in (gtk.keysyms.q, ) and
111                         event.get_state() & gtk.gdk.CONTROL_MASK
112                 ):
113                         self.emit("quit")
114                 elif event.keyval == gtk.keysyms.l and event.get_state() & gtk.gdk.CONTROL_MASK:
115                         with open(constants._user_logpath_, "r") as f:
116                                 logLines = f.xreadlines()
117                                 log = "".join(logLines)
118                                 self._clipboard.set_text(str(log))
119                         return True
120
121
122 class SourceSelector(BasicWindow):
123
124         def __init__(self, player, store, index):
125                 BasicWindow.__init__(self, player, store, index)
126
127                 self._radioButton = self._create_button("radio", "Radio")
128                 self._radioButton.connect("clicked", self._on_radio_selected)
129                 self._radioWrapper = gtk.VBox()
130                 self._radioWrapper.pack_start(self._radioButton, False, True)
131
132                 self._conferenceButton = self._create_button("conferences", "Conferences")
133                 #self._conferenceButton.connect("clicked", self._on_conference_selected)
134                 self._conferenceWrapper = gtk.VBox()
135                 self._conferenceWrapper.pack_start(self._conferenceButton, False, True)
136
137                 self._magazineButton = self._create_button("magazines", "Magazines")
138                 #self._magazineButton.connect("clicked", self._on_magazine_selected)
139                 self._magazineWrapper = gtk.VBox()
140                 self._magazineWrapper.pack_start(self._magazineButton, False, True)
141
142                 self._scriptureButton = self._create_button("scriptures", "Scriptures")
143                 #self._scriptureButton.connect("clicked", self._on_scripture_selected)
144                 self._scriptureWrapper = gtk.VBox()
145                 self._scriptureWrapper.pack_start(self._scriptureButton, False, True)
146
147                 self._buttonLayout = gtk.VBox(True, 5)
148                 self._buttonLayout.set_property("border-width", 5)
149                 self._buttonLayout.pack_start(self._radioWrapper, True, True)
150                 self._buttonLayout.pack_start(self._conferenceWrapper, True, True)
151                 self._buttonLayout.pack_start(self._magazineWrapper, True, True)
152                 self._buttonLayout.pack_start(self._scriptureWrapper, True, True)
153
154                 self._playcontrol = playcontrol.PlayControl(player, store)
155
156                 self._layout.pack_start(self._buttonLayout, True, True)
157                 self._layout.pack_start(self._playcontrol.toplevel, False, True)
158
159                 self._window.set_title(constants.__pretty_app_name__)
160                 self._window.show_all()
161                 self._errorBanner.toplevel.hide()
162                 self._playcontrol.toplevel.hide()
163
164         def _create_button(self, icon, message):
165                 image = self._store.get_image_from_store(self._store.STORE_LOOKUP[icon])
166
167                 label = gtk.Label()
168                 label.set_text(message)
169
170                 buttonLayout = gtk.HBox(False, 5)
171                 buttonLayout.pack_start(image, False, False)
172                 buttonLayout.pack_start(label, False, True)
173                 button = gtk.Button()
174                 button.add(buttonLayout)
175
176                 return button
177
178         @misc_utils.log_exception(_moduleLogger)
179         def _on_radio_selected(self, *args):
180                 radioView = RadioView(self._player, self._store, self._index)
181                 radioView.window.set_modal(True)
182                 radioView.window.set_transient_for(self._window)
183                 radioView.window.set_default_size(*self._window.get_size())
184
185
186 class RadioView(BasicWindow):
187
188         def __init__(self, player, store, index):
189                 BasicWindow.__init__(self, player, store, index)
190
191                 self._loadingBanner = banners.GenericBanner()
192
193                 headerPath = self._store.STORE_LOOKUP["radio_header"]
194                 self._header = self._store.get_image_from_store(headerPath)
195
196                 self._programmingModel = gtk.ListStore(
197                         gobject.TYPE_STRING,
198                         gobject.TYPE_STRING,
199                 )
200
201                 textrenderer = gtk.CellRendererText()
202                 timeColumn = gtk.TreeViewColumn("Time")
203                 timeColumn.pack_start(textrenderer, expand=True)
204                 timeColumn.add_attribute(textrenderer, "text", 0)
205
206                 textrenderer = gtk.CellRendererText()
207                 titleColumn = gtk.TreeViewColumn("Program")
208                 titleColumn.pack_start(textrenderer, expand=True)
209                 titleColumn.add_attribute(textrenderer, "text", 1)
210
211                 self._treeView = gtk.TreeView()
212                 self._treeView.set_headers_visible(False)
213                 self._treeView.set_model(self._programmingModel)
214                 self._treeView.append_column(timeColumn)
215                 self._treeView.append_column(titleColumn)
216
217                 self._treeScroller = gtk.ScrolledWindow()
218                 self._treeScroller.add(self._treeView)
219                 self._treeScroller.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
220
221                 self._presenter = presenter.StreamMiniPresenter(self._player, self._store)
222
223                 self._radioLayout = gtk.VBox(False)
224                 self._radioLayout.pack_start(self._header, False, False)
225                 self._radioLayout.pack_start(self._treeScroller, True, True)
226                 self._radioLayout.pack_start(self._presenter.toplevel, False, True)
227
228                 self._layout.pack_start(self._loadingBanner.toplevel, False, False)
229                 self._layout.pack_start(self._radioLayout, True, True)
230
231                 self._window.set_title("Radio")
232                 self._window.show_all()
233                 self._errorBanner.toplevel.hide()
234                 self._loadingBanner.toplevel.hide()
235
236                 self._show_loading()
237                 self._index.download_radio(self._on_channels, self._on_load_error)
238
239         def _show_loading(self):
240                 animationPath = self._store.STORE_LOOKUP["loading"]
241                 animation = self._store.get_pixbuf_animation_from_store(animationPath)
242                 self._loadingBanner.show(animation, "Loading...")
243
244         def _hide_loading(self):
245                 self._loadingBanner.hide()
246
247         @misc_utils.log_exception(_moduleLogger)
248         def _on_channels(self, channels):
249                 channels = list(channels)
250                 if 1 < len(channels):
251                         _moduleLogger.warning("More channels now available!")
252                 channel = channels[0]
253                 self._index.download_radio(self._on_channel, self._on_load_error, channel["id"])
254
255         @misc_utils.log_exception(_moduleLogger)
256         def _on_channel(self, programs):
257                 self._hide_loading()
258                 for program in programs:
259                         row = program["time"], program["title"]
260                         self._programmingModel.append(row)
261
262                 path = (self._get_current_row(), )
263                 self._treeView.scroll_to_cell(path)
264                 self._treeView.get_selection().select_path(path)
265
266         def _get_current_row(self):
267                 now = datetime.datetime.now()
268                 nowTime = now.strftime("%H:%M:%S")
269                 print nowTime
270                 for i, row in enumerate(self._programmingModel):
271                         if nowTime < row[0]:
272                                 return i - 1
273                 else:
274                         return i
275
276         @misc_utils.log_exception(_moduleLogger)
277         def _on_load_error(self, exception):
278                 self._hide_loading()
279                 self._errorBanner.push_message(exception)