This is an automated email from the git hooks/post-receive script.
git pushed a commit to branch main
in repository eradio.
View the commit online.
commit 7c7b608b02ce95854a05dd4fcdca45e9100b0b70
Author: politebot <[email protected]>
AuthorDate: Fri Oct 10 14:39:56 2025 -0500
Thumbnails
---
src/main.c | 250 ++++++++++++++++++++++++++++++++++++++++++-------------------
1 file changed, 171 insertions(+), 79 deletions(-)
diff --git a/src/main.c b/src/main.c
index c7d829e..6ff0b53 100644
--- a/src/main.c
+++ b/src/main.c
@@ -2,7 +2,6 @@
#include <Ecore_Evas.h>
#include <Emotion.h>
#include <Ecore_Con.h>
-#include <Ethumb_Client.h>
#include <libxml/parser.h>
#include <libxml/xpath.h>
@@ -21,16 +20,47 @@ typedef struct _AppData
Evas_Object *emotion;
Evas_Object *search_entry;
Evas_Object *search_hoversel;
+ Evas_Object *play_pause_btn;
Eina_List *stations;
+ Eina_Bool playing;
} AppData;
+typedef enum _Download_Type
+{
+ DOWNLOAD_TYPE_STATIONS,
+ DOWNLOAD_TYPE_ICON
+} Download_Type;
+
+typedef struct _Download_Context
+{
+ Download_Type type;
+ AppData *ad;
+} Download_Context;
+
+typedef struct _Station_Download_Context
+{
+ Download_Context base;
+ xmlParserCtxtPtr ctxt;
+} Station_Download_Context;
+
+typedef struct _Icon_Download_Context
+{
+ Download_Context base;
+ Elm_Object_Item *list_item;
+ Eina_Binbuf *image_data;
+} Icon_Download_Context;
+
+
static void _win_del_cb(void *data, Evas_Object *obj, void *event_info);
static void _list_item_selected_cb(void *data, Evas_Object *obj, void *event_info);
-static void _play_btn_clicked_cb(void *data, Evas_Object *obj, void *event_info);
-static void _pause_btn_clicked_cb(void *data, Evas_Object *obj, void *event_info);
+static void _play_pause_btn_clicked_cb(void *data, Evas_Object *obj, void *event_info);
static void _stop_btn_clicked_cb(void *data, Evas_Object *obj, void *event_info);
static void _hoversel_item_selected_cb(void *data, Evas_Object *obj, void *event_info);
static void _search_btn_clicked_cb(void *data, Evas_Object *obj, void *event_info);
+static void _search_entry_activated_cb(void *data, Evas_Object *obj, void *event_info);
+static Eina_Bool _url_data_cb(void *data, int type, void *event_info);
+static Eina_Bool _url_complete_cb(void *data, int type, void *event_info);
+
static void
_win_del_cb(void *data, Evas_Object *obj, void *event_info)
@@ -68,6 +98,7 @@ _list_item_selected_cb(void *data, Evas_Object *obj, void *event_info)
AppData *ad = data;
Elm_Object_Item *it = event_info;
Station *st = elm_object_item_data_get(it);
+ Evas_Object *ic;
if (!st) return;
@@ -77,34 +108,28 @@ _list_item_selected_cb(void *data, Evas_Object *obj, void *event_info)
{
emotion_object_file_set(ad->emotion, st->url);
emotion_object_play_set(ad->emotion, EINA_TRUE);
+ ad->playing = EINA_TRUE;
+ ic = elm_object_part_content_get(ad->play_pause_btn, "icon");
+ elm_icon_standard_set(ic, "media-playback-pause");
}
}
-
-
static void
-_play_btn_clicked_cb(void *data, Evas_Object *obj, void *event_info)
+_play_pause_btn_clicked_cb(void *data, Evas_Object *obj, void *event_info)
{
AppData *ad = data;
- emotion_object_play_set(ad->emotion, EINA_TRUE);
+ Evas_Object *ic;
+
+ ad->playing = !ad->playing;
+ emotion_object_play_set(ad->emotion, ad->playing);
+
+ ic = elm_object_part_content_get(ad->play_pause_btn, "icon");
+ if (ad->playing)
+ elm_icon_standard_set(ic, "media-playback-pause");
+ else
+ elm_icon_standard_set(ic, "media-playback-start");
}
-static void
-_pause_btn_clicked_cb(void *data, Evas_Object *obj, void *event_info)
-{
- AppData *ad = data;
- emotion_object_play_set(ad->emotion, EINA_FALSE);
-}
-
-static Eina_Bool _url_progress_cb(void *data, int type, void *event_info);
-static Eina_Bool _url_complete_cb(void *data, int type, void *event_info);
-
-typedef struct _Download_Context
-{
- AppData *ad;
- xmlParserCtxtPtr ctxt;
-} Download_Context;
-
static void
_search_btn_clicked_cb(void *data, Evas_Object *obj, void *event_info)
{
@@ -113,12 +138,13 @@ _search_btn_clicked_cb(void *data, Evas_Object *obj, void *event_info)
const char *search_type = elm_object_text_get(ad->search_hoversel);
char url_str[1024];
Ecore_Con_Url *url;
- Download_Context *d_ctx;
+ Station_Download_Context *d_ctx;
if (!search_term || !search_term[0]) return;
- d_ctx = calloc(1, sizeof(Download_Context));
- d_ctx->ad = ad;
+ d_ctx = calloc(1, sizeof(Station_Download_Context));
+ d_ctx->base.type = DOWNLOAD_TYPE_STATIONS;
+ d_ctx->base.ad = ad;
snprintf(url_str, sizeof(url_str), "http://de2.api.radio-browser.info/xml/stations/search?%s=%s",
search_type, search_term);
@@ -129,19 +155,18 @@ _search_btn_clicked_cb(void *data, Evas_Object *obj, void *event_info)
ecore_con_url_get(url);
}
-static Eina_Bool
-_url_progress_cb(void *data, int type, void *event_info)
+static void
+_search_entry_activated_cb(void *data, Evas_Object *obj, void *event_info)
{
- return ECORE_CALLBACK_PASS_ON;
+ _search_btn_clicked_cb(data, obj, event_info);
}
-static Eina_Bool
-_url_data_cb(void *data, int type, void *event_info)
+static void
+_handle_station_list_data(Ecore_Con_Event_Url_Data *url_data)
{
- Ecore_Con_Event_Url_Data *url_data = event_info;
- Download_Context *d_ctx = ecore_con_url_data_get(url_data->url_con);
+ Station_Download_Context *d_ctx = ecore_con_url_data_get(url_data->url_con);
- if (!d_ctx) return ECORE_CALLBACK_PASS_ON;
+ if (!d_ctx) return;
if (!d_ctx->ctxt)
{
@@ -151,40 +176,60 @@ _url_data_cb(void *data, int type, void *event_info)
}
else
xmlParseChunk(d_ctx->ctxt, (const char *)url_data->data, url_data->size, 0);
-
- return ECORE_CALLBACK_PASS_ON;
}
static void
-_thumb_load_error_cb(void *data, Evas_Object *obj, void *event_info)
+_handle_icon_data(Ecore_Con_Event_Url_Data *url_data)
{
- fprintf(stderr, "ERROR: could not load thumb\n");
+ Icon_Download_Context *icon_ctx = ecore_con_url_data_get(url_data->url_con);
+
+ if (!icon_ctx) return;
+
+ if (!icon_ctx->image_data)
+ icon_ctx->image_data = eina_binbuf_new();
+
+ eina_binbuf_append_length(icon_ctx->image_data, (const unsigned char *)url_data->data, url_data->size);
}
-static Eina_Bool
-_url_complete_cb(void *data, int type, void *event_info)
+static void
+_handle_icon_complete(Ecore_Con_Event_Url_Complete *ev)
{
- Ecore_Con_Event_Url_Complete *ev = event_info;
- Download_Context *d_ctx = ecore_con_url_data_get(ev->url_con);
+ Icon_Download_Context *icon_ctx = ecore_con_url_data_get(ev->url_con);
+
+ if (icon_ctx && icon_ctx->image_data)
+ {
+ Elm_Object_Item *it = icon_ctx->list_item;
+ Evas_Object *icon = elm_object_item_part_content_get(it, "start");
+ const char *ext = strrchr(ecore_con_url_url_get(ev->url_con), '.');
+ if (ext) ext++;
+
+ elm_image_memfile_set(icon, eina_binbuf_string_get(icon_ctx->image_data), eina_binbuf_length_get(icon_ctx->image_data), (char *)ext, NULL);
+ }
+
+ if (icon_ctx)
+ {
+ if (icon_ctx->image_data) eina_binbuf_free(icon_ctx->image_data);
+ free(icon_ctx);
+ }
+}
+
+static void
+_handle_station_list_complete(Ecore_Con_Event_Url_Complete *ev)
+{
+ Station_Download_Context *d_ctx = ecore_con_url_data_get(ev->url_con);
AppData *ad;
xmlDocPtr doc;
xmlXPathContextPtr xpathCtx;
xmlXPathObjectPtr xpathObj;
- if (!d_ctx)
- {
- ecore_con_url_free(ev->url_con);
- return ECORE_CALLBACK_PASS_ON;
- }
+ if (!d_ctx) return;
- ad = d_ctx->ad;
+ ad = d_ctx->base.ad;
if (!d_ctx->ctxt)
{
- // In case of empty reply, the data callback is not called
free(d_ctx);
- ecore_con_url_free(ev->url_con);
- return ECORE_CALLBACK_PASS_ON;
+ return;
}
xmlParseChunk(d_ctx->ctxt, "", 0, 1);
@@ -195,18 +240,15 @@ _url_complete_cb(void *data, int type, void *event_info)
if (doc == NULL)
{
printf("Error: could not parse XML\n");
- ecore_con_url_free(ev->url_con);
- return ECORE_CALLBACK_PASS_ON;
+ return;
}
-
xpathCtx = xmlXPathNewContext(doc);
if (xpathCtx == NULL)
{
printf("Error: could not create new XPath context\n");
xmlFreeDoc(doc);
- ecore_con_url_free(ev->url_con);
- return ECORE_CALLBACK_PASS_ON;
+ return;
}
xpathObj = xmlXPathEvalExpression((xmlChar *)"//station", xpathCtx);
@@ -215,8 +257,7 @@ _url_complete_cb(void *data, int type, void *event_info)
printf("Error: could not evaluate xpath _expression_\n");
xmlXPathFreeContext(xpathCtx);
xmlFreeDoc(doc);
- ecore_con_url_free(ev->url_con);
- return ECORE_CALLBACK_PASS_ON;
+ return;
}
elm_list_clear(ad->list);
@@ -256,29 +297,77 @@ _url_complete_cb(void *data, int type, void *event_info)
}
ad->stations = eina_list_append(ad->stations, st);
- Evas_Object *thumb = elm_thumb_add(ad->win);
- evas_object_smart_callback_add(thumb, "load,error", _thumb_load_error_cb, NULL);
+ Evas_Object *icon = elm_icon_add(ad->win);
+ elm_icon_standard_set(icon, "radio");
+ Elm_Object_Item *li = elm_list_item_append(ad->list, st->name, icon, NULL, _list_item_selected_cb, ad);
+ elm_object_item_data_set(li, st);
+
if (st->favicon && st->favicon[0])
- elm_thumb_file_set(thumb, st->favicon, NULL);
- elm_list_item_append(ad->list, st->name, thumb, NULL, NULL, st);
+ {
+ Ecore_Con_Url *url = ""
+ Icon_Download_Context *icon_ctx = calloc(1, sizeof(Icon_Download_Context));
+ icon_ctx->base.type = DOWNLOAD_TYPE_ICON;
+ icon_ctx->base.ad = ad;
+ icon_ctx->list_item = li;
+ ecore_con_url_data_set(url, icon_ctx);
+ ecore_con_url_get(url);
+ }
}
elm_list_go(ad->list);
xmlXPathFreeObject(xpathObj);
xmlXPathFreeContext(xpathCtx);
xmlFreeDoc(doc);
- ecore_con_url_free(ev->url_con);
+}
+static Eina_Bool
+_url_data_cb(void *data, int type, void *event_info)
+{
+ Ecore_Con_Event_Url_Data *url_data = event_info;
+ Download_Context *ctx = ecore_con_url_data_get(url_data->url_con);
+
+ if (!ctx) return ECORE_CALLBACK_PASS_ON;
+
+ if (ctx->type == DOWNLOAD_TYPE_STATIONS)
+ _handle_station_list_data(url_data);
+ else if (ctx->type == DOWNLOAD_TYPE_ICON)
+ _handle_icon_data(url_data);
+
+ return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_url_complete_cb(void *data, int type, void *event_info)
+{
+ Ecore_Con_Event_Url_Complete *ev = event_info;
+ Download_Context *ctx = ecore_con_url_data_get(ev->url_con);
+
+ if (!ctx)
+ {
+ ecore_con_url_free(ev->url_con);
+ return ECORE_CALLBACK_PASS_ON;
+ }
+
+ if (ctx->type == DOWNLOAD_TYPE_STATIONS)
+ _handle_station_list_complete(ev);
+ else if (ctx->type == DOWNLOAD_TYPE_ICON)
+ _handle_icon_complete(ev);
+
+ ecore_con_url_free(ev->url_con);
return ECORE_CALLBACK_PASS_ON;
}
static void
_stop_btn_clicked_cb(void *data, Evas_Object *obj, void *event_info)
-
{
AppData *ad = data;
+ Evas_Object *ic;
+
emotion_object_play_set(ad->emotion, EINA_FALSE);
emotion_object_position_set(ad->emotion, 0.0);
+ ad->playing = EINA_FALSE;
+ ic = elm_object_part_content_get(ad->play_pause_btn, "icon");
+ elm_icon_standard_set(ic, "media-playback-start");
}
static void
@@ -300,11 +389,11 @@ elm_main(int argc, char **argv)
int dpi = 0;
ecore_evas_screen_dpi_get(ee, NULL, &dpi);
if (dpi >= 192)
- elm_config_scale_set(2.0);
+ elm_config_scale_set(4.0);
}
+
ecore_con_init();
- ethumb_client_init();
elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED);
@@ -339,6 +428,7 @@ elm_main(int argc, char **argv)
elm_object_part_text_set(ad.search_entry, "guide", "Search for stations...");
elm_box_pack_end(search_hbox, ad.search_entry);
evas_object_show(ad.search_entry);
+ elm_object_focus_set(ad.search_entry, EINA_TRUE);
ad.search_hoversel = elm_hoversel_add(ad.win);
elm_hoversel_hover_parent_set(ad.search_hoversel, ad.win);
@@ -372,32 +462,34 @@ elm_main(int argc, char **argv)
elm_box_pack_end(box, controls_hbox);
evas_object_show(controls_hbox);
- Evas_Object *play_btn = elm_button_add(ad.win);
- elm_object_text_set(play_btn, "▶");
- elm_box_pack_end(controls_hbox, play_btn);
- evas_object_show(play_btn);
+ Evas_Object *ic;
- Evas_Object *pause_btn = elm_button_add(ad.win);
- elm_object_text_set(pause_btn, "⏸");
- elm_box_pack_end(controls_hbox, pause_btn);
- evas_object_show(pause_btn);
+ ad.play_pause_btn = elm_button_add(ad.win);
+ ic = elm_icon_add(ad.play_pause_btn);
+ elm_icon_standard_set(ic, "media-playback-start");
+ evas_object_size_hint_min_set(ic, 40, 40);
+ elm_object_part_content_set(ad.play_pause_btn, "icon", ic);
+ elm_box_pack_end(controls_hbox, ad.play_pause_btn);
+ evas_object_show(ad.play_pause_btn);
Evas_Object *stop_btn = elm_button_add(ad.win);
- elm_object_text_set(stop_btn, "⏹");
+ ic = elm_icon_add(stop_btn);
+ elm_icon_standard_set(ic, "media-playback-stop");
+ evas_object_size_hint_min_set(ic, 40, 40);
+ elm_object_part_content_set(stop_btn, "icon", ic);
elm_box_pack_end(controls_hbox, stop_btn);
evas_object_show(stop_btn);
- evas_object_smart_callback_add(ad.list, "selected", _list_item_selected_cb, &ad);
- evas_object_smart_callback_add(play_btn, "clicked", _play_btn_clicked_cb, &ad);
- evas_object_smart_callback_add(pause_btn, "clicked", _pause_btn_clicked_cb, &ad);
+ evas_object_smart_callback_add(ad.play_pause_btn, "clicked", _play_pause_btn_clicked_cb, &ad);
evas_object_smart_callback_add(stop_btn, "clicked", _stop_btn_clicked_cb, &ad);
evas_object_smart_callback_add(search_btn, "clicked", _search_btn_clicked_cb, &ad);
+ evas_object_smart_callback_add(ad.search_entry, "activated", _search_entry_activated_cb, &ad);
+ evas_object_smart_callback_add(ad.list, "selected", _list_item_selected_cb, &ad);
evas_object_resize(ad.win, 400, 600);
evas_object_show(ad.win);
elm_run();
- ethumb_client_shutdown();
ecore_con_shutdown();
return 0;
}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.