From adeda41eb895e75bb3884c14e123509d792635fc Mon Sep 17 00:00:00 2001 From: "Javier S. Pedro" Date: Fri, 14 Jan 2011 21:02:31 +0100 Subject: implementing presets --- Makefile | 37 +++++++++-- cfmradio.c | 132 +++++++++++++++++++++++++++++++++++-- debian/changelog | 6 ++ po/cfmradio.pot | 45 +++++++++++++ po/es.po | 47 ++++++++++++++ preset_list.c | 29 +++++++-- preset_renderer.c | 190 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ preset_renderer.h | 38 +++++++++++ presets.c | 1 - radio.c | 24 +++++++ radio.h | 4 +- 11 files changed, 538 insertions(+), 15 deletions(-) create mode 100644 po/cfmradio.pot create mode 100644 po/es.po create mode 100644 preset_renderer.c create mode 100644 preset_renderer.h diff --git a/Makefile b/Makefile index a47812f..f60915e 100644 --- a/Makefile +++ b/Makefile @@ -1,16 +1,26 @@ CFLAGS?=-Os -g -Wall LDFLAGS?=-Wl,--as-needed -MISC_CFLAGS:=-std=gnu99 -DG_LOG_DOMAIN=\"CFmRadio\" +LOCALEDIR?=/usr/share/locale + +GETTEXT_PACKAGE:=cfmradio +GETTEXT_CFLAGS:=-DGETTEXT_PACKAGE=\"$(GETTEXT_PACKAGE)\" -DLOCALEDIR=\"$(LOCALEDIR)\" PKGCONFIG_PKGS:=libosso hildon-1 alsa libpulse-mainloop-glib dbus-glib-1 gconf-2.0 PKGCONFIG_CFLAGS:=$(shell pkg-config $(PKGCONFIG_PKGS) --cflags) PKGCONFIG_LIBS:=$(shell pkg-config $(PKGCONFIG_PKGS) --libs) LAUNCHER_CFLAGS:=$(shell pkg-config maemo-launcher-app --cflags) LAUNCHER_LDFLAGS:=$(shell pkg-config maemo-launcher-app --libs) +MISC_CFLAGS:=-std=gnu99 -DG_LOG_DOMAIN=\"CFmRadio\" -OBJS=cfmradio.o radio.o types.o tuner.o rds.o presets.o preset_list.o +SRCS:=cfmradio.c radio.c types.c tuner.c rds.c \ + presets.c preset_list.c preset_renderer.c +OBJS:=$(SRCS:.c=.o) +POT:=po/$(GETTEXT_PACKAGE).pot +PO_FILES:=$(wildcard po/*.po) +MO_FILES:=$(PO_FILES:.po=.mo) +LANGS:=$(basename $(notdir $(PO_FILES))) -all: cfmradio.launch +all: cfmradio.launch $(MO_FILES) cfmradio.launch: $(OBJS) $(CC) $(LAUNCHER_LDFLAGS) $(LDFLAGS) -o $@ $^ $(PKGCONFIG_LIBS) $(LIBS) @@ -19,13 +29,25 @@ cfmradio: $(OBJS) $(CC) $(LDFLAGS) -o $@ $^ $(PKGCONFIG_LIBS) $(LIBS) $(OBJS): %.o: %.c - $(CC) $(MISC_CFLAGS) $(PKGCONFIG_CFLAGS) $(LAUNCHER_CFLAGS) $(CFLAGS) -o $@ -c $< + $(CC) $(GETTEXT_CFLAGS) $(MISC_CFLAGS) $(PKGCONFIG_CFLAGS) $(LAUNCHER_CFLAGS) $(CFLAGS) -o $@ -c $< radio.c: radio.h types.h n900-fmrx-enabler.h n900-fmrx-enabler.h: n900-fmrx-enabler.xml dbus-binding-tool --mode=glib-client --output=$@ --prefix=fmrx_enabler $< +$(POT): $(SRCS) + xgettext --default-domain=$(GETTEXT_PACKAGE) --from-code=UTF-8 --language=C \ + --msgid-bugs-address=maemo@javispedro.com --package-name=CFmRadio \ + --keyword=_ -o$@ $^ + +$(MO_FILES): %.mo: %.po + msgfmt -c -o $@ $< + +$(PO_FILES): %: $(POT) + msgmerge -U $@ $(POT) + @touch $@ + install: cfmradio.launch install -m 0755 $(IFLAGS) cfmradio.launch $(DESTDIR)/usr/bin/ ln -sf /usr/bin/maemo-invoker $(DESTDIR)/usr/bin/cfmradio @@ -35,9 +57,14 @@ install: cfmradio.launch $(DESTDIR)/usr/share/dbus-1/services/ install -m 0644 $(IFLAGS) data/cfmradio.desktop \ $(DESTDIR)/usr/share/applications/hildon/ + for lang in $(LANGS); do \ + install -d $(DESTDIR)$(LOCALEDIR)/$$lang/LC_MESSAGES ; \ + install -m 0644 po/$$lang.mo \ + $(DESTDIR)$(LOCALEDIR)/$$lang/LC_MESSAGES/$(GETTEXT_PACKAGE).mo ; \ + done clean: - rm -f cfmradio cfmradio.launch *.o + rm -f cfmradio cfmradio.launch *.o $(MO_FILES) .PHONY: all clean diff --git a/cfmradio.c b/cfmradio.c index a269b53..5431e6f 100644 --- a/cfmradio.c +++ b/cfmradio.c @@ -1,5 +1,6 @@ #include #include +#include #include "radio.h" #include "presets.h" @@ -7,6 +8,9 @@ #include "tuner.h" #include "types.h" +#define SCAN_LOCK_TIME 1 +#define SCAN_INCREMENT 100000 + static osso_context_t *osso_context; static HildonProgram *program; static HildonWindow *main_window; @@ -19,10 +23,17 @@ static GtkLabel *ps_label, *rt_label; static CFmTuner *tuner; //static GtkToolbar *toolbar; +static GtkWidget *start_scan_button, *stop_scan_button; + static CFmPresetList *preset_list; static guint rds_timer; +static guint scan_timer; +static gulong scan_prev_freq, scan_max; + +static void cancel_scan(); + static void print_freq(gulong freq) { static gchar markup[256]; @@ -36,10 +47,13 @@ static void print_freq(gulong freq) static void print_rds() { + gulong freq; gchar *rds_ps, *rds_rt; gchar *markup; + const gchar *preset; - g_object_get(G_OBJECT(radio), "rds-ps", &rds_ps, "rds-rt", &rds_rt, NULL); + g_object_get(G_OBJECT(radio), "frequency", &freq, "rds-ps", &rds_ps, + "rds-rt", &rds_rt, NULL); markup = g_markup_printf_escaped("%s", g_strstrip(rds_ps)); @@ -49,6 +63,12 @@ static void print_rds() gtk_label_set_text(rt_label, g_strstrip(rds_rt)); + preset = cfm_presets_get_preset(presets, freq); + if (preset && preset[0] == '\0') { + /* If the preset exists but has no name, give it one. */ + cfm_presets_set_preset(presets, freq, rds_ps); + } + g_free(rds_ps); g_free(rds_rt); } @@ -98,6 +118,7 @@ static gboolean key_press_cb(GObject *object, GdkEventKey *event, gpointer user_ static gboolean tuner_changed_cb(GObject *object, gpointer user_data) { gulong freq; + cancel_scan(); g_object_get(G_OBJECT(tuner), "frequency", &freq, NULL); print_freq(freq); return TRUE; @@ -106,6 +127,7 @@ static gboolean tuner_changed_cb(GObject *object, gpointer user_data) static gboolean tuner_tuned_cb(GObject *object, gpointer user_data) { gulong freq; + cancel_scan(); g_object_get(G_OBJECT(tuner), "frequency", &freq, NULL); g_object_set(G_OBJECT(radio), "frequency", freq, NULL); print_freq(freq); @@ -126,6 +148,7 @@ static void presets_clicked(GtkButton *button, gpointer user_data) static void preset_frequency_cb(GObject *object, GParamSpec *psec, gpointer user_data) { gulong freq; + cancel_scan(); g_object_get(G_OBJECT(preset_list), "frequency", &freq, NULL); g_object_set(G_OBJECT(radio), "frequency", freq, NULL); gtk_widget_hide(GTK_WIDGET(preset_list)); @@ -143,27 +166,123 @@ static void add_preset_clicked(GtkButton *button, gpointer user_data) g_free(rds_ps); } +static void remove_preset_clicked(GtkButton *button, gpointer user_data) +{ + gulong freq; + + g_object_get(G_OBJECT(radio), "frequency", &freq, NULL); + cfm_presets_remove_preset(presets, freq); +} + +static void end_scan() +{ + scan_timer = 0; + + g_object_set(G_OBJECT(radio), "output", CFM_RADIO_OUTPUT_SYSTEM, NULL); + + hildon_gtk_window_set_progress_indicator(GTK_WINDOW(main_window), 0); + gtk_widget_show(start_scan_button); + gtk_widget_hide(stop_scan_button); +} + +static gboolean scan_step(gpointer data) +{ + gulong freq; + guint signal; + g_object_get(G_OBJECT(radio), "frequency", &freq, "signal", &signal, NULL); + + g_print("Autoscan %f Mhz: %f %%\n", freq / 1000000.0f, signal / 655.36f), + g_object_set(G_OBJECT(tuner), "frequency", freq, NULL); + print_freq(freq); + + if (signal > (65536 / 3)) { + /* Signal > 33% : Create / Update preset */ + /* TODO: Rounding */ + if (!cfm_presets_is_preset(presets, freq)) { + cfm_presets_set_preset(presets, freq, ""); + } + } + + if (freq >= scan_max) { + /* Scan ended succesfully */ + g_object_set(G_OBJECT(radio), "frequency", scan_prev_freq, NULL); + g_object_set(G_OBJECT(tuner), "frequency", scan_prev_freq, NULL); + print_freq(scan_prev_freq); + end_scan(); + return FALSE; + } + + g_object_set(G_OBJECT(radio), "frequency", freq + SCAN_INCREMENT, NULL); + cfm_radio_seek_up(radio); + + return TRUE; +} + +static void start_scan(void) +{ + gulong range_low, range_high; + if (scan_timer) { + return; /* We are already scanning */ + } + g_object_set(G_OBJECT(radio), "output", CFM_RADIO_OUTPUT_MUTE, NULL); + + g_object_get(G_OBJECT(radio), "frequency", &scan_prev_freq, + "range-low", &range_low, + "range-high", &range_high, NULL); + + scan_max = range_high; + g_object_set(G_OBJECT(radio), "frequency", range_low, NULL); + + cfm_radio_seek_up(radio); + scan_timer = g_idle_add(scan_step, NULL); + + hildon_gtk_window_set_progress_indicator(GTK_WINDOW(main_window), 1); + gtk_widget_show(stop_scan_button); + gtk_widget_hide(start_scan_button); +} + +static void cancel_scan(void) +{ + if (!scan_timer) return; + g_source_remove(scan_timer); + end_scan(); +} + static void build_main_window() { GtkBox *box; main_window = HILDON_WINDOW(hildon_stackable_window_new()); - gtk_window_set_title(GTK_WINDOW(main_window), "Radio"); + gtk_window_set_title(GTK_WINDOW(main_window), _("Radio")); hildon_gtk_window_set_portrait_flags(GTK_WINDOW(main_window), HILDON_PORTRAIT_MODE_SUPPORT); HildonAppMenu *menu = HILDON_APP_MENU(hildon_app_menu_new()); GtkWidget *menu_button; - menu_button = gtk_button_new_with_label("Presets..."); + menu_button = gtk_button_new_with_label(_("Presets…")); g_signal_connect_after(G_OBJECT(menu_button), "clicked", G_CALLBACK(presets_clicked), NULL); hildon_app_menu_append(menu, GTK_BUTTON(menu_button)); - menu_button = gtk_button_new_with_label("Add preset"); + start_scan_button = gtk_button_new_with_label(_("Scan for presets")); + g_signal_connect_after(G_OBJECT(start_scan_button), "clicked", + G_CALLBACK(start_scan), NULL); + hildon_app_menu_append(menu, GTK_BUTTON(start_scan_button)); + stop_scan_button = gtk_button_new_with_label(_("Stop scanning")); + g_signal_connect_after(G_OBJECT(stop_scan_button), "clicked", + G_CALLBACK(cancel_scan), NULL); + hildon_app_menu_append(menu, GTK_BUTTON(stop_scan_button)); + menu_button = gtk_button_new_with_label(_("Set preset")); g_signal_connect_after(G_OBJECT(menu_button), "clicked", G_CALLBACK(add_preset_clicked), NULL); hildon_app_menu_append(menu, GTK_BUTTON(menu_button)); + menu_button = gtk_button_new_with_label(_("Remove preset")); + g_signal_connect_after(G_OBJECT(menu_button), "clicked", + G_CALLBACK(remove_preset_clicked), NULL); + hildon_app_menu_append(menu, GTK_BUTTON(menu_button)); + gtk_widget_show_all(GTK_WIDGET(menu)); + gtk_widget_hide(GTK_WIDGET(stop_scan_button)); box = GTK_BOX(gtk_vbox_new(FALSE, 0)); @@ -223,6 +342,10 @@ int main(int argc, char *argv[]) { hildon_gtk_init(&argc, &argv); + bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); + bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); + textdomain(GETTEXT_PACKAGE); + osso_context = osso_initialize("com.javispedro.cfmradio", "0.1", TRUE, NULL); g_warn_if_fail(osso_context != NULL); @@ -240,6 +363,7 @@ int main(int argc, char *argv[]) presets = cfm_presets_get_default(); rds_timer = g_timeout_add_seconds(1, rds_timer_cb, NULL); + scan_timer = 0; build_main_window(); diff --git a/debian/changelog b/debian/changelog index d3af7d2..17d9c0e 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +cfmradio (0.2) unstable; urgency=low + + * Implementing presets. + + -- Javier S. Pedro Fri, 14 Jan 2011 21:02:04 +0100 + cfmradio (0.1) unstable; urgency=low * Initial Release. diff --git a/po/cfmradio.pot b/po/cfmradio.pot new file mode 100644 index 0000000..45ee09e --- /dev/null +++ b/po/cfmradio.pot @@ -0,0 +1,45 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: CFmRadio\n" +"Report-Msgid-Bugs-To: maemo@javispedro.com\n" +"POT-Creation-Date: 2011-01-14 20:52+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: cfmradio.c:248 +msgid "Radio" +msgstr "" + +#: cfmradio.c:255 +msgid "Presets…" +msgstr "" + +#: cfmradio.c:259 +msgid "Scan for presets" +msgstr "" + +#: cfmradio.c:263 +msgid "Stop scanning" +msgstr "" + +#: cfmradio.c:267 +msgid "Set preset" +msgstr "" + +#: cfmradio.c:271 +msgid "Remove preset" +msgstr "" + +#: preset_list.c:112 +msgid "Presets" +msgstr "" diff --git a/po/es.po b/po/es.po new file mode 100644 index 0000000..40d59c8 --- /dev/null +++ b/po/es.po @@ -0,0 +1,47 @@ +# Spanish translations for CFmRadio package +# Traducciones al español para el paquete CFmRadio. +# Copyright (C) 2011 THE CFmRadio'S COPYRIGHT HOLDER +# This file is distributed under the same license as the CFmRadio package. +# Javier , 2011. +# +msgid "" +msgstr "" +"Project-Id-Version: CFmRadio\n" +"Report-Msgid-Bugs-To: maemo@javispedro.com\n" +"POT-Creation-Date: 2011-01-14 20:52+0100\n" +"PO-Revision-Date: 2011-01-14 20:40+0100\n" +"Last-Translator: Javier \n" +"Language-Team: Spanish\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: es\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: cfmradio.c:248 +msgid "Radio" +msgstr "Radio" + +#: cfmradio.c:255 +msgid "Presets…" +msgstr "Emisoras…" + +#: cfmradio.c:259 +msgid "Scan for presets" +msgstr "Buscar emisoras" + +#: cfmradio.c:263 +msgid "Stop scanning" +msgstr "Detener búsqueda" + +#: cfmradio.c:267 +msgid "Set preset" +msgstr "Añadir emisora" + +#: cfmradio.c:271 +msgid "Remove preset" +msgstr "Quitar emisora" + +#: preset_list.c:112 +msgid "Presets" +msgstr "Emisoras" diff --git a/preset_list.c b/preset_list.c index 0fef49b..5621f9f 100644 --- a/preset_list.c +++ b/preset_list.c @@ -2,10 +2,11 @@ * GPL 2 */ -#include #include +#include #include "presets.h" +#include "preset_renderer.h" #include "preset_list.h" G_DEFINE_TYPE(CFmPresetList, cfm_preset_list, HILDON_TYPE_STACKABLE_WINDOW); @@ -18,6 +19,7 @@ G_DEFINE_TYPE(CFmPresetList, cfm_preset_list, HILDON_TYPE_STACKABLE_WINDOW); struct _CFmPresetListPrivate { HildonTouchSelector *sel; HildonTouchSelectorColumn *col; + GtkCellRenderer *renderer; }; enum { @@ -83,6 +85,21 @@ static void cfm_preset_list_get_property(GObject *object, guint property_id, } } +static void cfm_preset_list_dispose(GObject *object) +{ + CFmPresetList *self = CFM_PRESET_LIST(object); + CFmPresetListPrivate *priv = self->priv; + + if (priv->sel) g_object_unref(priv->sel); + priv->sel = NULL; + /* Col is owned by sel */ + priv->col = NULL; + if (priv->renderer) g_object_unref(priv->renderer); + priv->renderer = NULL; + + G_OBJECT_CLASS(cfm_preset_list_parent_class)->dispose(object); +} + static void cfm_preset_list_init(CFmPresetList *self) { CFmPresetListPrivate *priv; @@ -92,13 +109,16 @@ static void cfm_preset_list_init(CFmPresetList *self) /* Empty model for now */ GtkTreeModel *model = GTK_TREE_MODEL(gtk_list_store_new(2, G_TYPE_FLOAT, G_TYPE_STRING)); - gtk_window_set_title(GTK_WINDOW(self), "Presets"); + gtk_window_set_title(GTK_WINDOW(self), _("Presets")); hildon_gtk_window_set_portrait_flags(GTK_WINDOW(self), HILDON_PORTRAIT_MODE_SUPPORT); priv->sel = HILDON_TOUCH_SELECTOR(hildon_touch_selector_new()); - priv->col = hildon_touch_selector_append_text_column(priv->sel, model, FALSE); - hildon_touch_selector_column_set_text_column(priv->col, 1); + priv->renderer = cfm_preset_renderer_new(); + g_object_set(G_OBJECT(priv->renderer), "xpad", HILDON_MARGIN_DEFAULT, NULL); + priv->col = hildon_touch_selector_append_column(priv->sel, model, + priv->renderer, "frequency", 0, "name", 1); + hildon_touch_selector_column_set_text_column(priv->col, 0); gtk_container_add(GTK_CONTAINER(self), GTK_WIDGET(priv->sel)); @@ -115,6 +135,7 @@ static void cfm_preset_list_class_init(CFmPresetListClass *klass) gobject_class->set_property = cfm_preset_list_set_property; gobject_class->get_property = cfm_preset_list_get_property; + gobject_class->dispose = cfm_preset_list_dispose; g_type_class_add_private(klass, sizeof(CFmPresetListPrivate)); diff --git a/preset_renderer.c b/preset_renderer.c new file mode 100644 index 0000000..7a219d1 --- /dev/null +++ b/preset_renderer.c @@ -0,0 +1,190 @@ +/* + * GPL 2 + */ + +#include + +#include "preset_renderer.h" + +G_DEFINE_TYPE (CFmPresetRenderer, cfm_preset_renderer, GTK_TYPE_CELL_RENDERER); + +#define ABS_RANGE_LOW 60000000 +#define ABS_RANGE_HIGH 140000000 + +enum { + PROP_0, + PROP_FREQUENCY, + PROP_NAME, + PROP_LAST +}; + +static GParamSpec *properties[PROP_LAST]; + +static void cfm_preset_renderer_get_size_internal(GtkCellRenderer *cell, + GtkWidget *widget, GdkRectangle *cell_area, PangoLayout *layout, + gint *x_offset, gint *y_offset, gint *width, gint *height) +{ + CFmPresetRenderer *self = CFM_PRESET_RENDERER(cell); + PangoRectangle rect; + gint min_width, min_height; + + if (layout) { + g_object_ref(layout); + } else { + layout = gtk_widget_create_pango_layout(widget, NULL); + } + + pango_layout_set_text(layout, self->freq_text, -1); + pango_layout_get_pixel_extents(layout, NULL, &rect); + min_height = cell->ypad * 2 + rect.height; + min_width = cell->xpad * 2 + rect.width; + + if (self->name && self->name[0] != '\0') { + pango_layout_set_text(layout, self->name, -1); + pango_layout_get_pixel_extents(layout, NULL, &rect); + min_height = MAX(min_height, cell->ypad * 2 + rect.height); + min_width += rect.width; + } + + if (cell_area) { + if (x_offset) *x_offset = 0; /* This renderer always expands horiz. */ + if (y_offset) { + *y_offset = cell->yalign * (cell_area->height - min_height); + } + } else { + if (x_offset) *x_offset = 0; + if (y_offset) *y_offset = 0; + } + + if (width) *width = min_width; + if (height) *height = min_height; + + g_object_unref(layout); +} + +static void cfm_preset_renderer_get_size(GtkCellRenderer *cell, + GtkWidget *widget, GdkRectangle *cell_area, gint *x_offset, gint *y_offset, + gint *width, gint *height) +{ + PangoLayout *layout = gtk_widget_create_pango_layout(widget, NULL); + + cfm_preset_renderer_get_size_internal(cell, widget, cell_area, layout, + x_offset, y_offset, width, height); + + g_object_unref(layout); +} + +static void cfm_preset_renderer_render(GtkCellRenderer *cell, GdkWindow *window, + GtkWidget *widget, GdkRectangle *background_area, GdkRectangle *cell_area, + GdkRectangle *expose_area, GtkCellRendererState flags) +{ + CFmPresetRenderer *self = CFM_PRESET_RENDERER(cell); + PangoLayout *layout = gtk_widget_create_pango_layout(widget, NULL); + gint avail_width = cell_area->width - (2*cell->xpad); + gint x_offset, y_offset, x, y; + + cfm_preset_renderer_get_size_internal(cell, widget, cell_area, layout, + &x_offset, &y_offset, NULL, NULL); + + x = cell_area->x + x_offset + cell->xpad; + y = cell_area->y + y_offset + cell->ypad; + + pango_layout_set_text(layout, self->freq_text, -1); + gtk_paint_layout(widget->style, window, GTK_STATE_NORMAL, TRUE, expose_area, + widget, "cellrenderertext", x, y, layout); + + if (self->name && self->name[0] != '\0') { + pango_layout_set_text(layout, self->name, -1); + x += avail_width / 2; + gtk_paint_layout(widget->style, window, GTK_STATE_NORMAL, TRUE, + expose_area, widget, "cellrenderertext", x, y, layout); + } + + g_object_unref(layout); +} + +static void cfm_preset_renderer_init(CFmPresetRenderer *self) +{ + +} + +static void cfm_preset_renderer_set_property(GObject *object, guint property_id, + const GValue *value, GParamSpec *pspec) +{ + CFmPresetRenderer *self = CFM_PRESET_RENDERER(object); + switch (property_id) { + case PROP_NAME: + g_free(self->name); + self->name = g_value_dup_string(value); + break; + case PROP_FREQUENCY: + g_free(self->freq_text); + self->frequency = g_value_get_ulong(value); + self->freq_text = g_strdup_printf("%.1f MHz", + self->frequency / 1000000.0f); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + break; + } +} + +static void cfm_preset_renderer_get_property(GObject *object, guint property_id, + GValue *value, GParamSpec *pspec) +{ + CFmPresetRenderer *self = CFM_PRESET_RENDERER(object); + switch (property_id) { + case PROP_NAME: + g_value_set_string(value, self->name); + break; + case PROP_FREQUENCY: + g_value_set_ulong(value, self->frequency); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + break; + } +} + +static void cfm_preset_renderer_finalize(GObject *object) +{ + CFmPresetRenderer *self = CFM_PRESET_RENDERER(object); + g_free(self->name); + g_free(self->freq_text); + + G_OBJECT_CLASS(cfm_preset_renderer_parent_class)->finalize(object); +} + +static void cfm_preset_renderer_class_init (CFmPresetRendererClass *klass) +{ + GObjectClass* gobject_class = G_OBJECT_CLASS (klass); + GtkCellRendererClass* parent_class = GTK_CELL_RENDERER_CLASS (klass); + GParamSpec *param_spec; + + parent_class->get_size = cfm_preset_renderer_get_size; + parent_class->render = cfm_preset_renderer_render; + gobject_class->set_property = cfm_preset_renderer_set_property; + gobject_class->get_property = cfm_preset_renderer_get_property; + gobject_class->finalize = cfm_preset_renderer_finalize; + + param_spec = g_param_spec_ulong("frequency", + "Frequency", + "This is the frequency of this preset, in Hz", + ABS_RANGE_LOW, ABS_RANGE_HIGH, ABS_RANGE_LOW, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + properties[PROP_FREQUENCY] = param_spec; + g_object_class_install_property(gobject_class, PROP_FREQUENCY, param_spec); + param_spec = g_param_spec_string("name", + "Name", + "Name for this preset", + "", + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + properties[PROP_NAME] = param_spec; + g_object_class_install_property(gobject_class, PROP_NAME, param_spec); +} + +GtkCellRenderer* cfm_preset_renderer_new() +{ + return g_object_new(CFM_TYPE_PRESET_RENDERER, NULL); +} + diff --git a/preset_renderer.h b/preset_renderer.h new file mode 100644 index 0000000..9857bfb --- /dev/null +++ b/preset_renderer.h @@ -0,0 +1,38 @@ +/* + * GPL 2 + */ + +#ifndef _CFM_PRESET_RENDERER_H_ +#define _CFM_PRESET_RENDERER_H_ + +#include + +#define CFM_TYPE_PRESET_RENDERER (cfm_preset_renderer_get_type ()) +#define CFM_PRESET_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CFM_TYPE_PRESET_RENDERER, CFmPresetRenderer)) +#define CFM_PRESET_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CFM_TYPE_PRESET_RENDERER, CFmPresetRendererClass)) +#define CFM_IS_PRESET_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CFM_TYPE_PRESET_RENDERER)) +#define CFM_IS_PRESET_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CFM_TYPE_PRESET_RENDERER)) +#define CFM_PRESET_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CFM_TYPE_PRESET_RENDERER, CFmPresetRendererClass)) + +typedef struct _CFmPresetRendererClass CFmPresetRendererClass; +typedef struct _CFmPresetRenderer CFmPresetRenderer; + +struct _CFmPresetRendererClass +{ + GtkCellRendererClass parent_class; +}; + +struct _CFmPresetRenderer +{ + GtkCellRenderer parent_instance; + gchar *name; + gchar *freq_text; + float frequency; +}; + +GType cfm_preset_renderer_get_type(void) G_GNUC_CONST; +GtkCellRenderer* cfm_preset_renderer_new(void); + +G_END_DECLS + +#endif /* _CFM_PRESET_RENDERER_H_ */ diff --git a/presets.c b/presets.c index f35901a..2f1f37b 100644 --- a/presets.c +++ b/presets.c @@ -149,7 +149,6 @@ static void cfm_presets_get_property(GObject *object, guint property_id, CFmPresets *self = CFM_PRESETS(object); switch (property_id) { case PROP_NAME: - g_debug("Prop name is now set\n"); g_value_set_string(value, self->priv->name); break; default: diff --git a/radio.c b/radio.c index 614702f..6b8048f 100644 --- a/radio.c +++ b/radio.c @@ -94,6 +94,18 @@ static void cfm_radio_tuner_power(CFmRadio *self, gboolean enable) } } +static void cfm_radio_tuner_hw_seek(CFmRadio *self, gboolean upward) +{ + CFmRadioPrivate *priv = self->priv; + struct v4l2_hw_freq_seek t_freq_seek = { 0 }; + g_return_if_fail(priv->fd != -1); + t_freq_seek.tuner = 0; + t_freq_seek.type = V4L2_TUNER_RADIO; + t_freq_seek.seek_upward = upward; + int res = ioctl(priv->fd, VIDIOC_S_HW_FREQ_SEEK, &t_freq_seek); + g_warn_if_fail(res == 0); +} + static void cfm_radio_mixer_set_enum_value(CFmRadio *self, const char * name, const char * value) { CFmRadioPrivate *priv = self->priv; @@ -303,6 +315,8 @@ static void cfm_radio_si_request(pa_stream *p, size_t nbytes, void *userdata) CFmRadio *self = CFM_RADIO(userdata); CFmRadioPrivate *priv = self->priv; + g_return_if_fail(priv->si || priv->so); + if (nbytes < MIN_BUFFER_SIZE) return; @@ -665,3 +679,13 @@ CFmRadio* cfm_radio_new() return g_object_new(CFM_TYPE_RADIO, NULL); } +void cfm_radio_seek_up(CFmRadio* radio) +{ + cfm_radio_tuner_hw_seek(radio, TRUE); +} + +void cfm_radio_seek_down(CFmRadio* radio) +{ + cfm_radio_tuner_hw_seek(radio, FALSE); +} + diff --git a/radio.h b/radio.h index b5d0b82..a20477d 100644 --- a/radio.h +++ b/radio.h @@ -31,7 +31,9 @@ struct _CFmRadioClass GType cfm_radio_get_type (void); CFmRadio* cfm_radio_new(); -void cfm_radio_free(CFmRadio* radio); + +void cfm_radio_seek_up(CFmRadio* radio); +void cfm_radio_seek_down(CFmRadio* radio); #endif /* __CFM_RADIO_H__ */ -- cgit v1.2.3