hythmbox-2.98/plugins/fmradio/rb-fm-radio-source.c

No issues found

  1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  2  *
  3  *  Copyright (C) 2007  James Henstridge <james@jamesh.id.au>
  4  *
  5  *  This program is free software; you can redistribute it and/or modify
  6  *  it under the terms of the GNU General Public License as published by
  7  *  the Free Software Foundation; either version 2 of the License, or
  8  *  (at your option) any later version.
  9  *
 10  *  The Rhythmbox authors hereby grant permission for non-GPL compatible
 11  *  GStreamer plugins to be used and distributed together with GStreamer
 12  *  and Rhythmbox. This permission is above and beyond the permissions granted
 13  *  by the GPL license by which Rhythmbox is covered. If you modify this code
 14  *  you may extend this exception to your version of the code, but you are not
 15  *  obligated to do so. If you do not wish to do so, delete this exception
 16  *  statement from your version.
 17  *
 18  *  This program is distributed in the hope that it will be useful,
 19  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 20  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 21  *  GNU General Public License for more details.
 22  *
 23  *  You should have received a copy of the GNU General Public License
 24  *  along with this program; if not, write to the Free Software
 25  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
 26  *
 27  */
 28 
 29 #include <config.h>
 30 
 31 #include <string.h>
 32 
 33 #include <glib/gi18n-lib.h>
 34 #include <gtk/gtk.h>
 35 
 36 #include <lib/rb-debug.h>
 37 #include <lib/rb-util.h>
 38 #include <rhythmdb/rhythmdb-query-model.h>
 39 #include <shell/rb-shell-player.h>
 40 #include <widgets/rb-entry-view.h>
 41 #include <widgets/rb-uri-dialog.h>
 42 #include <widgets/rb-source-toolbar.h>
 43 
 44 #include "rb-fm-radio-source.h"
 45 #include "rb-radio-tuner.h"
 46 
 47 typedef struct _RhythmDBEntryType RBFMRadioEntryType;
 48 typedef struct _RhythmDBEntryTypeClass RBFMRadioEntryTypeClass;
 49 
 50 static void     rb_fm_radio_source_class_init  (RBFMRadioSourceClass *class);
 51 static void     rb_fm_radio_source_init        (RBFMRadioSource *self);
 52 static void	rb_fm_radio_source_constructed (GObject *object);
 53 static void     rb_fm_radio_source_dispose     (GObject *object);
 54 static void     rb_fm_radio_source_do_query    (RBFMRadioSource *self);
 55 static void     rb_fm_radio_source_songs_view_sort_order_changed (GObject *obj,
 56 								  GParamSpec *pspec,
 57 								  RBFMRadioSource *self);
 58 static void      rb_fm_radio_source_songs_view_show_popup (
 59 						RBEntryView *view,
 60 						gboolean over_entry,
 61 						RBFMRadioSource *self);
 62 
 63 static void      rb_fm_radio_source_cmd_new_station (GtkAction *action,
 64 						     RBFMRadioSource *self);
 65 
 66 static void playing_entry_changed (RBShellPlayer *player, RhythmDBEntry *entry,
 67 				   RBFMRadioSource *self);
 68 
 69 static void         impl_delete         (RBSource *source);
 70 static gboolean     impl_show_popup     (RBDisplayPage *page);
 71 static RBEntryView *impl_get_entry_view (RBSource *source);
 72 
 73 static void rb_fm_radio_entry_type_class_init (RBFMRadioEntryTypeClass *klass);
 74 static void rb_fm_radio_entry_type_init (RBFMRadioEntryType *etype);
 75 GType rb_fm_radio_entry_type_get_type (void);
 76 
 77 struct _RBFMRadioSourcePrivate {
 78 	RhythmDB *db;
 79 	RBShellPlayer *player;
 80 	RhythmDBEntryType *entry_type;
 81 	RhythmDBEntry *playing_entry;
 82 
 83 	RBEntryView *stations;
 84 
 85 	RBRadioTuner *tuner;
 86 
 87 	GtkActionGroup *action_group;
 88 };
 89 
 90 static GtkActionEntry rb_fm_radio_source_actions[] = {
 91 	{ "MusicNewFMRadioStation", GTK_STOCK_NEW, N_("New FM R_adio Station"),
 92 	  NULL, N_("Create a new FM Radio station"),
 93 	  G_CALLBACK (rb_fm_radio_source_cmd_new_station) },
 94 };
 95 
 96 G_DEFINE_DYNAMIC_TYPE (RBFMRadioSource, rb_fm_radio_source, RB_TYPE_SOURCE);
 97 
 98 G_DEFINE_DYNAMIC_TYPE (RBFMRadioEntryType, rb_fm_radio_entry_type, RHYTHMDB_TYPE_ENTRY_TYPE);
 99 
100 static char *
101 rb_fm_radio_source_get_playback_uri (RhythmDBEntryType *etype, RhythmDBEntry *entry)
102 {
103 	return g_strdup("xrbsilence:///");
104 }
105 
106 static void
107 rb_fm_radio_entry_type_class_init (RBFMRadioEntryTypeClass *klass)
108 {
109 	RhythmDBEntryTypeClass *etype_class = RHYTHMDB_ENTRY_TYPE_CLASS (klass);
110 	etype_class->can_sync_metadata = (RhythmDBEntryTypeBooleanFunc) rb_true_function;
111 	etype_class->sync_metadata = (RhythmDBEntryTypeSyncFunc) rb_null_function;
112 	etype_class->get_playback_uri = rb_fm_radio_source_get_playback_uri;
113 }
114 
115 static void
116 rb_fm_radio_entry_type_class_finalize (RBFMRadioEntryTypeClass *klass)
117 {
118 }
119 
120 static void
121 rb_fm_radio_entry_type_init (RBFMRadioEntryType *etype)
122 {
123 }
124 
125 static void
126 rb_fm_radio_source_class_init (RBFMRadioSourceClass *class)
127 {
128 	GObjectClass *object_class = G_OBJECT_CLASS (class);
129 	RBDisplayPageClass *page_class = RB_DISPLAY_PAGE_CLASS (class);
130 	RBSourceClass *source_class = RB_SOURCE_CLASS (class);
131 
132 	object_class->constructed = rb_fm_radio_source_constructed;
133 	object_class->dispose = rb_fm_radio_source_dispose;
134 
135 	page_class->show_popup = impl_show_popup;
136 
137 	source_class->impl_can_copy = (RBSourceFeatureFunc) rb_false_function;
138 	source_class->impl_can_delete = (RBSourceFeatureFunc) rb_true_function;
139 	source_class->impl_can_pause = (RBSourceFeatureFunc) rb_false_function;
140 	source_class->impl_delete = impl_delete;
141 	source_class->impl_get_entry_view = impl_get_entry_view;
142 
143 	g_type_class_add_private (class, sizeof (RBFMRadioSourcePrivate));
144 }
145 
146 static void
147 rb_fm_radio_source_class_finalize (RBFMRadioSourceClass *class)
148 {
149 }
150 
151 static void
152 rb_fm_radio_source_init (RBFMRadioSource *self)
153 {
154 	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (
155 		self, RB_TYPE_FM_RADIO_SOURCE, RBFMRadioSourcePrivate);
156 }
157 
158 static void
159 rb_fm_radio_source_constructed (GObject *object)
160 {
161 	RBFMRadioSource *self;
162 	RBShell *shell;
163 	RBSourceToolbar *toolbar;
164 	GtkUIManager *ui_manager;
165 	GtkWidget *grid;
166 
167 	RB_CHAIN_GOBJECT_METHOD (rb_fm_radio_source_parent_class, constructed, object);
168 	self = RB_FM_RADIO_SOURCE (object);
169 
170 	g_object_get (self,
171 		      "shell", &shell,
172 		      "entry-type", &self->priv->entry_type,
173 		      NULL);
174 	g_object_get (shell,
175 		      "db", &self->priv->db,
176 		      "shell-player", &self->priv->player,
177 		      "ui-manager", &ui_manager,
178 		      NULL);
179 	g_object_unref (shell);
180 
181 	self->priv->action_group = _rb_display_page_register_action_group (
182 		RB_DISPLAY_PAGE (self),
183 		"FMRadioActions",
184 		rb_fm_radio_source_actions,
185 		G_N_ELEMENTS (rb_fm_radio_source_actions),
186 		self);
187 
188 	toolbar = rb_source_toolbar_new (RB_DISPLAY_PAGE (self), ui_manager);
189 	g_object_unref (toolbar);
190 
191 	self->priv->stations = rb_entry_view_new (self->priv->db,
192 						  G_OBJECT (self->priv->player),
193 						  FALSE, FALSE);
194 	rb_entry_view_append_column (self->priv->stations,
195 				     RB_ENTRY_VIEW_COL_TITLE, TRUE);
196 	rb_entry_view_append_column (self->priv->stations,
197 				     RB_ENTRY_VIEW_COL_RATING, TRUE);
198 	rb_entry_view_append_column (self->priv->stations,
199 				     RB_ENTRY_VIEW_COL_LAST_PLAYED, TRUE);
200 
201 	g_signal_connect_object (self->priv->stations,
202 				 "notify::sort-order",
203 				 G_CALLBACK (rb_fm_radio_source_songs_view_sort_order_changed),
204 				 self, 0);
205 	/* sort order */
206 	
207 	g_signal_connect_object (self->priv->stations, "show_popup",
208 		G_CALLBACK (rb_fm_radio_source_songs_view_show_popup),
209 		self, 0);
210 
211 	grid = gtk_grid_new ();
212 	gtk_grid_attach (GTK_GRID (grid), GTK_WIDGET (toolbar), 0, 0, 1, 1);
213 	gtk_grid_attach (GTK_GRID (grid), GTK_WIDGET (self->priv->stations), 0, 1, 1, 1);
214 	gtk_container_add (GTK_CONTAINER (self), grid);
215 	gtk_widget_show_all (GTK_WIDGET (self));
216 
217 	rb_fm_radio_source_do_query (self);
218 
219 	g_signal_connect_object (G_OBJECT (self->priv->player),
220 				 "playing-song-changed",
221 				 G_CALLBACK (playing_entry_changed),
222 				 self, 0);
223 }
224 
225 RBSource *
226 rb_fm_radio_source_new (RBShell *shell, RBRadioTuner *tuner)
227 {
228 	RBFMRadioSource *self;
229 	RhythmDBEntryType *entry_type;
230 	RhythmDB *db;
231 
232 	g_object_get (shell, "db", &db, NULL);
233 
234 	entry_type = rhythmdb_entry_type_get_by_name (db, "fmradio-station");
235 	if (entry_type == NULL) {
236 		entry_type = g_object_new (rb_fm_radio_entry_type_get_type (),
237 					   "db", db,
238 					   "name", "fmradio-station",
239 					   "save-to-disk", TRUE,
240 					   NULL);
241 		rhythmdb_register_entry_type (db, entry_type);
242 	}
243 
244 	self = g_object_new (RB_TYPE_FM_RADIO_SOURCE,
245 			     "name", _("FM Radio"),
246 			     "shell", shell,
247 			     "entry-type", entry_type,
248 			     "toolbar-path", "/FMRadioSourceToolBar",
249 			     NULL);
250 	self->priv->tuner = g_object_ref (tuner);
251 	rb_shell_register_entry_type_for_source (shell, RB_SOURCE (self),
252 						 entry_type);
253 	g_object_unref (db);
254 	return RB_SOURCE (self);
255 }
256 
257 static void
258 rb_fm_radio_source_dispose (GObject *object)
259 {
260 	RBFMRadioSource *self = (RBFMRadioSource *)object;
261 
262 	if (self->priv->playing_entry) {
263 		rhythmdb_entry_unref (self->priv->playing_entry);
264 		self->priv->playing_entry = NULL;
265 	}
266 
267 	if (self->priv->db) {
268 		g_object_unref (self->priv->db);
269 		self->priv->db = NULL;
270 	}
271 
272 	if (self->priv->tuner) {
273 		g_object_unref (self->priv->tuner);
274 		self->priv->tuner = NULL;
275 	}
276 
277 	if (self->priv->action_group) {
278 		g_object_unref (self->priv->action_group);
279 		self->priv->action_group = NULL;
280 	}
281 
282 	G_OBJECT_CLASS (rb_fm_radio_source_parent_class)->dispose (object);
283 }
284 
285 static void
286 rb_fm_radio_source_do_query (RBFMRadioSource *self)
287 {
288 	RhythmDBQueryModel *station_query_model;
289 	GPtrArray *query;
290 
291         query = rhythmdb_query_parse (self->priv->db,
292                                       RHYTHMDB_QUERY_PROP_EQUALS,
293                                       RHYTHMDB_PROP_TYPE,
294                                       self->priv->entry_type,
295                                       RHYTHMDB_QUERY_END);
296 	station_query_model = rhythmdb_query_model_new_empty (self->priv->db);
297 	rhythmdb_do_full_query_parsed (
298 		self->priv->db, RHYTHMDB_QUERY_RESULTS (station_query_model),
299 		query);
300 	rhythmdb_query_free (query);
301 	rb_entry_view_set_model (self->priv->stations, station_query_model);
302 	g_object_set (self, "query-model", station_query_model, NULL);
303 	g_object_unref (station_query_model);
304 }
305 
306 static void
307 rb_fm_radio_source_songs_view_sort_order_changed (GObject *object, GParamSpec *pspec, RBFMRadioSource *self)
308 {
309 	rb_entry_view_resort_model (RB_ENTRY_VIEW (object));
310 }
311 
312 static void
313 rb_fm_radio_source_songs_view_show_popup (RBEntryView *view,
314 					  gboolean over_entry,
315 					  RBFMRadioSource *self)
316 {
317 	if (self == NULL)
318 		return;
319 
320 	if (over_entry)
321 		_rb_display_page_show_popup (RB_DISPLAY_PAGE (self), "/FMRadioViewPopup");
322 	else
323 		_rb_display_page_show_popup (RB_DISPLAY_PAGE (self), "/FMRadioSourcePopup");
324 }
325 
326 void
327 rb_fm_radio_source_add_station (RBFMRadioSource *self, const char *frequency,
328 				const char *title)
329 {
330 	RhythmDBEntry *entry;
331 	gchar *uri, *end = NULL;
332 	GValue val = { 0, };
333 	
334 	/* Check that the location is a double */
335 	g_ascii_strtod (frequency, &end);
336 	if (end == NULL || end[0] != '\0') {
337 		rb_debug ("%s is not a frequency", frequency);
338 		return;
339 	}
340 	uri = g_strconcat ("fmradio:", frequency, NULL);
341 
342 	entry = rhythmdb_entry_lookup_by_location (self->priv->db, uri);
343 	if (entry) {
344 		rb_debug ("uri %s already in db", uri);
345 		g_free (uri);
346 		return;
347 	}
348 
349 	entry = rhythmdb_entry_new (self->priv->db, self->priv->entry_type,
350 				    uri);
351 	g_free (uri);
352 	if (!entry)
353 		return;
354 
355 	g_value_init (&val, G_TYPE_STRING);
356 	if (title)
357 		g_value_set_static_string (&val, title);
358 	else
359 		g_value_set_static_string (&val, frequency);
360 	rhythmdb_entry_set (self->priv->db, entry, RHYTHMDB_PROP_TITLE, &val);
361 	g_value_unset (&val);
362 
363 	g_value_init (&val, G_TYPE_DOUBLE);
364 	g_value_set_double (&val, 0.0);
365 	rhythmdb_entry_set (self->priv->db, entry, RHYTHMDB_PROP_RATING, &val);
366 	g_value_unset (&val);
367 
368 	rhythmdb_commit (self->priv->db);
369 }
370 
371 static void
372 new_station_location_added (RBURIDialog *dialog, const char *frequency,
373 			    RBFMRadioSource *self)
374 {
375 	rb_fm_radio_source_add_station (self, frequency, NULL);
376 }
377 
378 static void
379 new_station_response_cb (GtkDialog *dialog, int response, gpointer meh)
380 {
381 	gtk_widget_destroy (GTK_WIDGET (dialog));
382 }
383 
384 static void
385 rb_fm_radio_source_cmd_new_station (GtkAction *action, RBFMRadioSource *self)
386 {
387 	GtkWidget *dialog;
388 
389 	dialog = rb_uri_dialog_new (_("New FM Radio Station"),
390 				    _("Frequency of radio station"));
391 	g_signal_connect_object (dialog, "location-added",
392 				 G_CALLBACK (new_station_location_added),
393 				 self, 0);
394 	g_signal_connect (dialog, "response", G_CALLBACK (new_station_response_cb), NULL);
395 	gtk_widget_show_all (dialog);
396 }
397 
398 static void
399 playing_entry_changed (RBShellPlayer *player, RhythmDBEntry *entry,
400 		       RBFMRadioSource *self)
401 {
402 	const gchar *location;
403 	gdouble frequency;
404 	gboolean was_playing = FALSE;
405 
406 	if (entry == self->priv->playing_entry)
407 		return;
408 
409 	if (self->priv->playing_entry != NULL) {
410 		rb_source_update_play_statistics (RB_SOURCE (self),
411 						  self->priv->db,
412 						  self->priv->playing_entry);
413 		rhythmdb_entry_unref (self->priv->playing_entry);
414 		self->priv->playing_entry = NULL;
415 		was_playing = TRUE;
416 	}
417 
418 	if (entry != NULL &&
419 	    rhythmdb_entry_get_entry_type (entry) == self->priv->entry_type) {
420 		self->priv->playing_entry = rhythmdb_entry_ref (entry);
421 		location = rhythmdb_entry_get_string (entry,
422 						      RHYTHMDB_PROP_LOCATION);
423 		if (g_str_has_prefix (location, "fmradio:")) {
424 			frequency = g_ascii_strtod(
425 				&location[strlen("fmradio:")], NULL);
426 			if (!was_playing)
427 				rb_radio_tuner_set_mute (self->priv->tuner,
428 							 FALSE);
429 			rb_radio_tuner_set_frequency (self->priv->tuner,
430 						      frequency);
431 		}
432 	} else {
433 		if (was_playing)
434 			rb_radio_tuner_set_mute (self->priv->tuner, TRUE);
435 	}
436 	
437 }
438 
439 static void
440 impl_delete (RBSource *source)
441 {
442 	RBFMRadioSource *self = RB_FM_RADIO_SOURCE (source);
443 	GList *selection, *tmp;
444 
445 	selection = rb_entry_view_get_selected_entries (self->priv->stations);
446 	for (tmp = selection; tmp != NULL; tmp = tmp->next) {
447 		RhythmDBEntry *entry = tmp->data;
448 
449 		rhythmdb_entry_delete (self->priv->db, entry);
450 		rhythmdb_commit (self->priv->db);
451 		rhythmdb_entry_unref (entry);
452 	}
453 	g_list_free (selection);
454 }
455 
456 static gboolean
457 impl_show_popup (RBDisplayPage *page)
458 {
459 	_rb_display_page_show_popup (page, "/FMRadioSourcePopup");
460 	return TRUE;
461 }
462 
463 static RBEntryView *
464 impl_get_entry_view (RBSource *source)
465 {
466 	RBFMRadioSource *self = (RBFMRadioSource *)source;
467 
468 	return self->priv->stations;
469 }
470 
471 void
472 _rb_fm_radio_source_register_type (GTypeModule *module)
473 {
474 	rb_fm_radio_source_register_type (module);
475 	rb_fm_radio_entry_type_register_type (module);
476 }