diff options
Diffstat (limited to 'src/focus_ring.c')
| -rw-r--r-- | src/focus_ring.c | 105 |
1 files changed, 105 insertions, 0 deletions
diff --git a/src/focus_ring.c b/src/focus_ring.c new file mode 100644 index 0000000..45f64b2 --- /dev/null +++ b/src/focus_ring.c @@ -0,0 +1,105 @@ +/* Functions related to a ring of focusable items */ + +#include "focus_ring.h" + +static gint find_by_value(gconstpointer data, gconstpointer user_data); + + +/* Initialize the FocusRing with the given options, value getter, & label + * + * Returns NULL if the passed GList is NULL. + * Throws an error if cannot allocate memory for the FocusRing. + */ +FocusRing *initialize_focus_ring(const GList *options, gchar *(*getter_function)(gconstpointer), const gchar *const label) +{ + if (options == NULL) { + return NULL; + } + + FocusRing *ring = malloc(sizeof(FocusRing)); + if (ring == NULL) { + g_error("Could not allocate memory for FocusRing: %s", label); + } + + ring->selected = options; + ring->beginning = options; + ring->end = g_list_last((GList *) options); + + ring->getter_function = getter_function; + + return ring; +} + +/* Free only the FocusRing & not the underlying GList */ +void destroy_focus_ring(FocusRing *ring) +{ + free(ring); +} + +/* Focus the next item in the ring, or loop back to the first item. */ +gchar *focus_ring_next(FocusRing *ring) +{ + if (ring->selected->next == NULL) { + ring->selected = ring->beginning; + } else { + ring->selected = ring->selected->next; + } + return focus_ring_get_value(ring); +} + +/* Focus the previous item in the ring, or loop back to the last item. */ +gchar *focus_ring_prev(FocusRing *ring) +{ + if (ring->selected->prev == NULL) { + ring->selected = ring->end; + } else { + ring->selected = ring->selected->prev; + } + return focus_ring_get_value(ring); +} + +/* Get the currently selected data. */ +gconstpointer focus_ring_get_selected(FocusRing *ring) +{ + return ring->selected->data; +} + +/* Get the inner value of the currently selected item. */ +gchar *focus_ring_get_value(FocusRing *ring) +{ + return ring->getter_function(focus_ring_get_selected(ring)); +} + + +/* Internal data used by the find_by_value & focus_ring_scroll_to_value + * functions. + */ +struct FindByValueData { + FocusRing *ring; + const gchar *target_value; +}; + +/* Attempt to scroll the ring to the item with the matching inner value. + * + * Does nothing if the item cannot be found. + */ +gchar *focus_ring_scroll_to_value(FocusRing *ring, const gchar *target_value) +{ + struct FindByValueData user_data = { .ring = ring, .target_value = target_value }; + GList *find_result = g_list_find_custom((GList *) ring->beginning, (void *) &user_data, &find_by_value); + if (find_result != NULL) { + ring->selected = find_result; + } + return focus_ring_get_value(ring); +} + +/* Determine if the given data's inner value matches what we're looking for. + * + * Used as callback to the g_list_find_custom function. + */ +static gint find_by_value(gconstpointer data, gconstpointer user_data) +{ + struct FindByValueData *local_user_data = (struct FindByValueData *) user_data; + + return g_strcmp0(local_user_data->ring->getter_function(data), local_user_data->target_value); +} |
