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