summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile37
-rw-r--r--cfmradio.c132
-rw-r--r--debian/changelog6
-rw-r--r--po/cfmradio.pot45
-rw-r--r--po/es.po47
-rw-r--r--preset_list.c29
-rw-r--r--preset_renderer.c190
-rw-r--r--preset_renderer.h38
-rw-r--r--presets.c1
-rw-r--r--radio.c24
-rw-r--r--radio.h4
11 files changed, 538 insertions, 15 deletions
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 <libosso.h>
#include <hildon/hildon.h>
+#include <glib/gi18n.h>
#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("<span font=\"31\">%s</span>",
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 <maemo@javispedro.com> 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 <EMAIL@ADDRESS>, 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 <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\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 <javispedro@gmail.com>, 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 <maemo@javispedro.com>\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 <gtk/gtk.h>
#include <hildon/hildon.h>
+#include <glib/gi18n.h>
#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 <gtk/gtk.h>
+
+#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 <gtk/gtk.h>
+
+#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__ */