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);
}
|