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 9f2925c3f024d7511aad21ba547bf0be25bb22e2
Author: politebot <[email protected]>
AuthorDate: Sat Oct 11 04:31:15 2025 -0500

    Add pagination for search results
---
 src/appdata.h      |   2 +
 src/eradio.edc     | 147 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/http.c         |  40 +++++++++++----
 src/http.h         |   2 +-
 src/station_list.c |   7 +--
 src/station_list.h |   2 +-
 src/ui.c           |  34 +++++++++++--
 src/ui.h           |   1 +
 8 files changed, 217 insertions(+), 18 deletions(-)

diff --git a/src/appdata.h b/src/appdata.h
index 37d2219..374c78e 100644
--- a/src/appdata.h
+++ b/src/appdata.h
@@ -29,6 +29,7 @@ typedef struct _AppData
    Evas_Object *play_pause_btn;
    Evas_Object *stop_btn;
    Evas_Object *search_btn;
+   Evas_Object *load_more_btn;
    Evas_Object *search_bar;
    Eina_List *stations;
    Eina_List *api_servers;    // list of strings (hostnames)
@@ -37,4 +38,5 @@ typedef struct _AppData
    Eina_Hash *favorites;
    Eina_List *favorites_stations;
    ViewMode view_mode;
+   int search_offset;
 } AppData;
diff --git a/src/eradio.edc b/src/eradio.edc
new file mode 100644
index 0000000..518668a
--- /dev/null
+++ b/src/eradio.edc
@@ -0,0 +1,147 @@
+collections {
+   group {
+      name: "main";
+      parts {
+         part {
+            name: "background";
+            type: RECT;
+            description {
+               state: "default" 0.0;
+               color: 255 255 255 255;
+            }
+         }
+         part {
+            name: "main_box";
+            type: BOX;
+            description {
+               state: "default" 0.0;
+               align: 1.0 1.0;
+               rel1 {
+                  relative: 0.0 0.0;
+               }
+               rel2 {
+                  relative: 1.0 1.0;
+               }
+               padding: 10 10;
+            }
+            box {
+               layout: "vertical";
+               padding: 10 10;
+               align: 0.5 0.0;
+            }
+         }
+         part {
+            name: "search_box";
+            type: BOX;
+            description {
+               state: "default" 0.0;
+               align: 0.5 0.0;
+               min: 0 30;
+            }
+            box {
+               layout: "horizontal";
+               padding: 10 0;
+               align: 0.5 0.0;
+            }
+         }
+         part {
+            name: "search_entry";
+            type: SWALLOW;
+            description {
+               state: "default" 0.0;
+               rel1.to: "search_box";
+               rel2.to: "search_box";
+               align: 0.0 0.5;
+            }
+         }
+         part {
+            name: "search_hoversel";
+            type: SWALLOW;
+            description {
+               state: "default" 0.0;
+               rel1.to: "search_entry";
+               rel1.relative: 1.0 0.0;
+               rel2.to: "search_entry";
+               rel2.relative: 1.0 1.0;
+               align: 0.0 0.5;
+               min: 100 30;
+            }
+         }
+         part {
+            name: "search_btn";
+            type: SWALLOW;
+            description {
+               state: "default" 0.0;
+               rel1.to: "search_hoversel";
+               rel1.relative: 1.0 0.0;
+               rel2.to: "search_hoversel";
+               rel2.relative: 1.0 1.0;
+               align: 0.0 0.5;
+               min: 80 30;
+            }
+         }
+         part {
+            name: "station_list";
+            type: SWALLOW;
+            description {
+               state: "default" 0.0;
+               align: 0.5 1.0;
+            }
+         }
+         part {
+            name: "controls_box";
+            type: BOX;
+            description {
+               state: "default" 0.0;
+               align: 0.5 1.0;
+               min: 0 50;
+            }
+            box {
+               layout: "horizontal";
+               padding: 10 0;
+               align: 0.5 1.0;
+            }
+         }
+         part {
+            name: "play_pause_btn";
+            type: SWALLOW;
+            description {
+               state: "default" 0.0;
+               rel1.to: "controls_box";
+               rel2.to: "controls_box";
+               align: 0.5 0.5;
+            }
+         }
+         part {
+            name: "stop_btn";
+            type: SWALLOW;
+            description {
+               state: "default" 0.0;
+               rel1.to: "play_pause_btn";
+               rel1.relative: 1.0 0.0;
+               rel2.to: "play_pause_btn";
+               rel2.relative: 1.0 1.0;
+               align: 0.5 0.5;
+            }
+         }
+      }
+      programs {
+         program {
+            name: "load";
+            signal: "load";
+            source: "";
+            action: STATE_SET "default" 0.0;
+            target: "background";
+            target: "main_box";
+            target: "search_box";
+            target: "search_entry";
+            target: "search_hoversel";
+            target: "search_btn";
+            target: "station_list";
+            target: "controls_box";
+            target: "play_pause_btn";
+            target: "stop_btn";
+         }
+      }
+   }
+}
diff --git a/src/http.c b/src/http.c
index dbea4f7..c3a68fb 100644
--- a/src/http.c
+++ b/src/http.c
@@ -10,6 +10,7 @@
 #include "http.h"
 #include "station_list.h"
 #include "favorites.h"
+#include "ui.h" // Include ui.h for ui_set_load_more_button_visibility
 
 typedef enum _Download_Type
 {
@@ -32,6 +33,9 @@ typedef struct _Station_Download_Context
    Eina_List *current;     // current server node
    char search_type[64];
    char search_term[512];
+   int offset;
+   int limit;
+   Eina_Bool new_search;
 } Station_Download_Context;
 
 typedef struct _Icon_Download_Context
@@ -81,7 +85,7 @@ http_shutdown(void)
 }
 
 void
-http_search_stations(AppData *ad, const char *search_term, const char *search_type)
+http_search_stations(AppData *ad, const char *search_term, const char *search_type, int offset, int limit, Eina_Bool new_search)
 {
    Ecore_Con_Url *url;
    Station_Download_Context *d_ctx;
@@ -91,6 +95,9 @@ http_search_stations(AppData *ad, const char *search_term, const char *search_ty
    d_ctx = calloc(1, sizeof(Station_Download_Context));
    d_ctx->base.type = DOWNLOAD_TYPE_STATIONS;
    d_ctx->base.ad = ad;
+   d_ctx->offset = offset;
+   d_ctx->limit = limit;
+   d_ctx->new_search = new_search;
    _populate_station_request(d_ctx, ad, search_type, search_term);
    _issue_station_request(&url, d_ctx);
    ecore_con_url_additional_header_add(url, "User-Agent", "eradio/1.0");
@@ -128,16 +135,17 @@ http_download_icon(AppData *ad, Elm_Object_Item *list_item, const char *url_str)
 }
 
 void
-_search_btn_clicked_cb(void *data, Evas_Object *obj, void *event_info)
+_search_btn_clicked_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
 {
    AppData *ad = data;
    const char *search_term = elm_object_text_get(ad->search_entry);
    const char *search_type = elm_object_text_get(ad->search_hoversel);
-   http_search_stations(ad, search_term, search_type);
+   ad->search_offset = 0; // Reset offset for a new search
+   http_search_stations(ad, search_term, search_type, ad->search_offset, 100, EINA_TRUE);
 }
 
 void
-_search_entry_activated_cb(void *data, Evas_Object *obj, void *event_info)
+_search_entry_activated_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
 {
    _search_btn_clicked_cb(data, obj, event_info);
 }
@@ -224,7 +232,6 @@ _handle_station_list_complete(Ecore_Con_Event_Url_Complete *ev)
     xmlParseChunk(d_ctx->ctxt, "", 0, 1);
     doc = d_ctx->ctxt->myDoc;
     xmlFreeParserCtxt(d_ctx->ctxt);
-    free(d_ctx);
 
     if (doc == NULL)
     {
@@ -238,6 +245,7 @@ _handle_station_list_complete(Ecore_Con_Event_Url_Complete *ev)
     {
         printf("Error: could not create new XPath context\n");
         xmlFreeDoc(doc);
+        free(d_ctx);
         return;
     }
 
@@ -247,11 +255,15 @@ _handle_station_list_complete(Ecore_Con_Event_Url_Complete *ev)
         printf("Error: could not evaluate xpath _expression_\n");
         xmlXPathFreeContext(xpathCtx);
         xmlFreeDoc(doc);
+        free(d_ctx);
         return;
     }
 
-    eina_list_free(ad->stations);
-    ad->stations = NULL;
+    if (d_ctx->new_search)
+    {
+        eina_list_free(ad->stations);
+        ad->stations = NULL;
+    }
 
     for (int i = 0; i < xpathObj->nodesetval->nodeNr; i++)
     {
@@ -289,9 +301,17 @@ _handle_station_list_complete(Ecore_Con_Event_Url_Complete *ev)
         ad->stations = eina_list_append(ad->stations, st);
     }
 
+    free(d_ctx);
+
     favorites_apply_to_stations(ad);
     if (ad->view_mode == VIEW_SEARCH)
-      station_list_populate(ad, ad->stations);
+      station_list_populate(ad, ad->stations, d_ctx->new_search);
+
+    // Determine if "Load More" button should be visible
+    if (xpathObj->nodesetval->nodeNr == d_ctx->limit)
+      ui_set_load_more_button_visibility(ad, EINA_TRUE);
+    else
+      ui_set_load_more_button_visibility(ad, EINA_FALSE);
 
     xmlXPathFreeObject(xpathObj);
     xmlXPathFreeContext(xpathCtx);
@@ -464,9 +484,9 @@ static void _issue_station_request(Ecore_Con_Url **url_out, Station_Download_Con
    const char *server = d_ctx->current ? (const char *)d_ctx->current->data : NULL;
    char url_str[1024];
    if (server)
-      snprintf(url_str, sizeof(url_str), "http://%s/xml/stations/search?%s=%s", server, d_ctx->search_type, d_ctx->search_term);
+      snprintf(url_str, sizeof(url_str), "http://%s/xml/stations/search?%s=%s&offset=%d&limit=%d", server, d_ctx->search_type, d_ctx->search_term, d_ctx->offset, d_ctx->limit);
    else
-      snprintf(url_str, sizeof(url_str), "http://de2.api.radio-browser.info/xml/stations/search?%s=%s", d_ctx->search_type, d_ctx->search_term);
+      snprintf(url_str, sizeof(url_str), "http://de2.api.radio-browser.info/xml/stations/search?%s=%s&offset=%d&limit=%d", d_ctx->search_type, d_ctx->search_term, d_ctx->offset, d_ctx->limit);
    *url_out = ecore_con_url_new(url_str);
 }
 
diff --git a/src/http.h b/src/http.h
index 24e27d8..78bdfd3 100644
--- a/src/http.h
+++ b/src/http.h
@@ -4,7 +4,7 @@
 
 void http_init(AppData *ad);
 void http_shutdown(void);
-void http_search_stations(AppData *ad, const char *search_term, const char *search_type);
+void http_search_stations(AppData *ad, const char *search_term, const char *search_type, int offset, int limit, Eina_Bool new_search);
 void http_download_icon(AppData *ad, Elm_Object_Item *list_item, const char *url);
 void _search_btn_clicked_cb(void *data, Evas_Object *obj, void *event_info);
 void _search_entry_activated_cb(void *data, Evas_Object *obj, void *event_info);
diff --git a/src/station_list.c b/src/station_list.c
index 77de71b..4319b56 100644
--- a/src/station_list.c
+++ b/src/station_list.c
@@ -33,12 +33,13 @@ station_list_clear(AppData *ad)
 }
 
 void
-station_list_populate(AppData *ad, Eina_List *stations)
+station_list_populate(AppData *ad, Eina_List *stations, Eina_Bool new_search)
 {
     Eina_List *l;
     Station *st;
 
-    station_list_clear(ad);
+    if (new_search)
+      station_list_clear(ad);
 
     EINA_LIST_FOREACH(stations, l, st)
     {
@@ -139,7 +140,7 @@ _favorite_remove_btn_clicked_cb(void *data, Evas_Object *obj EINA_UNUSED, void *
 
     // Rebuild favorites list to reflect removal
     favorites_rebuild_station_list(ctx->ad);
-    station_list_populate(ctx->ad, ctx->ad->favorites_stations);
+    station_list_populate(ctx->ad, ctx->ad->favorites_stations, EINA_TRUE);
 
     free(ctx);
 }
diff --git a/src/station_list.h b/src/station_list.h
index d544bcf..00ae1a9 100644
--- a/src/station_list.h
+++ b/src/station_list.h
@@ -2,6 +2,6 @@
 
 #include "appdata.h"
 
-void station_list_populate(AppData *ad, Eina_List *stations);
+void station_list_populate(AppData *ad, Eina_List *stations, Eina_Bool new_search);
 void station_list_clear(AppData *ad);
 void _list_item_selected_cb(void *data, Evas_Object *obj, void *event_info);
diff --git a/src/ui.c b/src/ui.c
index 111f8af..97e20a6 100644
--- a/src/ui.c
+++ b/src/ui.c
@@ -2,6 +2,7 @@
 #include "appdata.h"
 #include "favorites.h"
 #include "station_list.h"
+#include "http.h" // Include http.h for http_search_stations
 
 static void _win_del_cb(void *data, Evas_Object *obj, void *event_info);
 static void _app_exit_cb(void *data, Evas_Object *obj, void *event_info);
@@ -17,6 +18,7 @@ void _search_btn_clicked_cb(void *data, Evas_Object *obj, void *event_info);
 void _search_entry_activated_cb(void *data, Evas_Object *obj, void *event_info);
 void _list_item_selected_cb(void *data, Evas_Object *obj, void *event_info);
 static void _favorites_btn_clicked_cb(void *data, Evas_Object *obj, void *event_info);
+static void _load_more_btn_clicked_cb(void *data, Evas_Object *obj, void *event_info);
 
 
 static void
@@ -153,18 +155,26 @@ ui_create(AppData *ad)
    elm_box_pack_end(controls_hbox, ad->stop_btn);
    evas_object_show(ad->stop_btn);
 
+   ad->load_more_btn = elm_button_add(ad->win);
+   elm_object_text_set(ad->load_more_btn, "Load More");
+   elm_box_pack_end(controls_hbox, ad->load_more_btn);
+   evas_object_show(ad->load_more_btn);
+
    evas_object_smart_callback_add(ad->play_pause_btn, "clicked", _play_pause_btn_clicked_cb, ad);
    evas_object_smart_callback_add(ad->stop_btn, "clicked", _stop_btn_clicked_cb, ad);
    evas_object_smart_callback_add(ad->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_smart_callback_add(ad->load_more_btn, "clicked", _load_more_btn_clicked_cb, ad);
    /* Favorites view button removed; toolbar callbacks handle switching */
 
    /* Default to Search view on startup */
    ad->view_mode = VIEW_SEARCH;
+   ad->search_offset = 0; // Initialize offset
+   ui_set_load_more_button_visibility(ad, EINA_FALSE); // Initially hide load more button
    evas_object_show(ad->search_bar);
    if (ad->stations)
-     station_list_populate(ad, ad->stations);
+     station_list_populate(ad, ad->stations, EINA_TRUE);
    else
      station_list_clear(ad);
 
@@ -205,7 +215,7 @@ _tb_favorites_clicked_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_i
    ad->view_mode = VIEW_FAVORITES;
    evas_object_hide(ad->search_bar);
    favorites_rebuild_station_list(ad);
-   station_list_populate(ad, ad->favorites_stations);
+   station_list_populate(ad, ad->favorites_stations, EINA_TRUE);
 }
 
 static void
@@ -215,7 +225,25 @@ _tb_search_clicked_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info
    ad->view_mode = VIEW_SEARCH;
    evas_object_show(ad->search_bar);
    if (ad->stations)
-     station_list_populate(ad, ad->stations);
+     station_list_populate(ad, ad->stations, EINA_TRUE);
    else
      station_list_clear(ad);
 }
+
+static void
+_load_more_btn_clicked_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+   AppData *ad = data;
+   const char *search_term = elm_object_text_get(ad->search_entry);
+   const char *search_type = elm_object_text_get(ad->search_hoversel);
+   ad->search_offset += 100; // Increment offset for next page
+   http_search_stations(ad, search_term, search_type, ad->search_offset, 100, EINA_FALSE);
+}
+
+void ui_set_load_more_button_visibility(AppData *ad, Eina_Bool visible)
+{
+    if (visible)
+        evas_object_show(ad->load_more_btn);
+    else
+        evas_object_hide(ad->load_more_btn);
+}
diff --git a/src/ui.h b/src/ui.h
index 84e40d1..c1765c0 100644
--- a/src/ui.h
+++ b/src/ui.h
@@ -6,3 +6,4 @@ typedef struct _AppData AppData;
 
 void ui_create(AppData *ad);
 void ui_update_server_list(AppData *ad);
+void ui_set_load_more_button_visibility(AppData *ad, Eina_Bool visible);

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.

Reply via email to