-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
-
29
-
30
-
31
-
32
-
33
-
34
-
35
-
36
-
37
-
38
-
39
-
40
-
41
-
42
-
43
-
44
-
45
-
46
-
47
-
48
-
49
-
50
-
51
-
52
-
53
-
54
-
55
-
56
-
57
-
58
-
59
-
60
-
61
-
62
-
63
-
64
-
65
-
66
-
67
-
68
-
69
-
70
-
71
-
72
-
73
-
74
-
75
-
76
-
77
-
78
-
79
-
80
-
81
-
82
-
83
-
84
-
85
-
86
-
87
-
88
-
89
-
90
-
91
-
92
-
93
-
94
-
95
-
96
-
97
-
98
-
99
-
100
-
101
-
102
-
103
-
104
-
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);
}