1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * Copyright (C) 2013 Intel Corporation
4 *
5 * This library is free software: you can redistribute it and/or modify it
6 * under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation.
8 *
9 * This library is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this library. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Authors: Tristan Van Berkom <tristanvb@openismus.com>
18 */
19
20 #include "cursor-navigator.h"
21
22 /* GObjectClass */
23 static void cursor_navigator_constructed (GObject *object);
24 static void cursor_navigator_finalize (GObject *object);
25
26 /* GtkScaleClass */
27 static gchar *cursor_navigator_format_value (GtkScale *scale,
28 gdouble value);
29
30 static void cursor_navigator_changed (GtkAdjustment *adj,
31 GParamSpec *pspec,
32 CursorNavigator *navigator);
33
34 struct _CursorNavigatorPrivate {
35 gchar **alphabet;
36 gint letters;
37 gint index;
38 };
39
40 enum {
41 INDEX_CHANGED,
42 LAST_SIGNAL
43 };
44
45 static guint signals[LAST_SIGNAL];
46
47 enum {
48 PROP_0,
49 PROP_INDEX,
50 };
51
52 G_DEFINE_TYPE_WITH_PRIVATE (CursorNavigator, cursor_navigator, GTK_TYPE_SCALE);
53
54 /************************************************************************
55 * GObjectClass *
56 ************************************************************************/
57 static void
cursor_navigator_class_init(CursorNavigatorClass * klass)58 cursor_navigator_class_init (CursorNavigatorClass *klass)
59 {
60 GObjectClass *object_class;
61 GtkScaleClass *scale_class;
62
63 object_class = G_OBJECT_CLASS (klass);
64 object_class->constructed = cursor_navigator_constructed;
65 object_class->finalize = cursor_navigator_finalize;
66
67 scale_class = GTK_SCALE_CLASS (klass);
68 scale_class->format_value = cursor_navigator_format_value;
69
70 signals[INDEX_CHANGED] = g_signal_new (
71 "index-changed",
72 G_OBJECT_CLASS_TYPE (object_class),
73 G_SIGNAL_RUN_LAST,
74 0, NULL, NULL, NULL,
75 G_TYPE_NONE, 0);
76 }
77
78 static void
cursor_navigator_init(CursorNavigator * navigator)79 cursor_navigator_init (CursorNavigator *navigator)
80 {
81 CursorNavigatorPrivate *priv;
82
83 navigator->priv = priv = cursor_navigator_get_instance_private (navigator);
84
85 priv->letters = -1;
86 }
87
88 static void
cursor_navigator_constructed(GObject * object)89 cursor_navigator_constructed (GObject *object)
90 {
91 CursorNavigator *navigator = CURSOR_NAVIGATOR (object);
92 GtkAdjustment *adj = NULL;
93
94 G_OBJECT_CLASS (cursor_navigator_parent_class)->constructed (object);
95
96 adj = gtk_adjustment_new (0.0F, 0.0F, 1.0F, 1.0F, 1.0F, 0.0F);
97 gtk_range_set_adjustment (GTK_RANGE (navigator), adj);
98
99 g_signal_connect (
100 adj, "notify::value",
101 G_CALLBACK (cursor_navigator_changed), navigator);
102 }
103
104 static void
cursor_navigator_finalize(GObject * object)105 cursor_navigator_finalize (GObject *object)
106 {
107 CursorNavigator *navigator = CURSOR_NAVIGATOR (object);
108 CursorNavigatorPrivate *priv = navigator->priv;
109
110 g_strfreev (priv->alphabet);
111
112 G_OBJECT_CLASS (cursor_navigator_parent_class)->finalize (object);
113 }
114
115 /************************************************************************
116 * GtkScaleClass *
117 ************************************************************************/
118 static gchar *
cursor_navigator_format_value(GtkScale * scale,gdouble value)119 cursor_navigator_format_value (GtkScale *scale,
120 gdouble value)
121 {
122 CursorNavigator *navigator = CURSOR_NAVIGATOR (scale);
123 CursorNavigatorPrivate *priv = navigator->priv;
124 gint index;
125
126 if (priv->letters < 0)
127 return NULL;
128
129 index = CLAMP ((gint) value, 0, priv->letters - 1);
130
131 /* Return the letter for the gvoidiven value
132 */
133 return g_strdup (priv->alphabet[index]);
134 }
135
136 static void
cursor_navigator_changed(GtkAdjustment * adj,GParamSpec * pspec,CursorNavigator * navigator)137 cursor_navigator_changed (GtkAdjustment *adj,
138 GParamSpec *pspec,
139 CursorNavigator *navigator)
140 {
141 gint index = gtk_adjustment_get_value (adj);
142
143 cursor_navigator_set_index (navigator, index);
144 }
145
146 /************************************************************************
147 * API *
148 ************************************************************************/
149 CursorNavigator *
cursor_navigator_new(void)150 cursor_navigator_new (void)
151 {
152 return g_object_new (CURSOR_TYPE_NAVIGATOR, NULL);
153 }
154
155 static void
cursor_navigator_update_parameters(CursorNavigator * navigator)156 cursor_navigator_update_parameters (CursorNavigator *navigator)
157 {
158 CursorNavigatorPrivate *priv = navigator->priv;
159 GtkScale *scale = GTK_SCALE (navigator);
160 GtkAdjustment *adj;
161 gint i;
162
163 gtk_scale_clear_marks (scale);
164 for (i = 0; i < priv->letters; i++) {
165 gchar *letter;
166
167 letter = g_strdup_printf ("<span size=\"x-small\">%s</span>", priv->alphabet[i]);
168
169 gtk_scale_add_mark (scale, i, GTK_POS_LEFT, letter);
170 g_free (letter);
171 }
172
173 adj = gtk_range_get_adjustment (GTK_RANGE (navigator));
174
175 gtk_adjustment_set_upper (adj, priv->letters - 1);
176 }
177
178 void
cursor_navigator_set_alphabet(CursorNavigator * navigator,const gchar * const * alphabet)179 cursor_navigator_set_alphabet (CursorNavigator *navigator,
180 const gchar * const *alphabet)
181 {
182 CursorNavigatorPrivate *priv;
183
184 g_return_if_fail (CURSOR_IS_NAVIGATOR (navigator));
185 g_return_if_fail (alphabet == NULL ||
186 g_strv_length ((gchar **) alphabet) > 0);
187
188 priv = navigator->priv;
189
190 g_free (priv->alphabet);
191 if (alphabet) {
192 priv->alphabet = g_strdupv ((gchar **) alphabet);
193 priv->letters = g_strv_length ((gchar **) alphabet);
194 } else {
195 priv->alphabet = NULL;
196 priv->letters = -1;
197 }
198
199 cursor_navigator_update_parameters (navigator);
200 cursor_navigator_set_index (navigator, 0);
201 }
202
203 const gchar * const *
cursor_navigator_get_alphabet(CursorNavigator * navigator)204 cursor_navigator_get_alphabet (CursorNavigator *navigator)
205 {
206 CursorNavigatorPrivate *priv;
207
208 g_return_val_if_fail (CURSOR_IS_NAVIGATOR (navigator), NULL);
209
210 priv = navigator->priv;
211
212 return (const gchar * const *) priv->alphabet;
213 }
214
215 void
cursor_navigator_set_index(CursorNavigator * navigator,gint index)216 cursor_navigator_set_index (CursorNavigator *navigator,
217 gint index)
218 {
219 CursorNavigatorPrivate *priv;
220 GtkAdjustment *adj;
221
222 g_return_if_fail (CURSOR_IS_NAVIGATOR (navigator));
223
224 priv = navigator->priv;
225 adj = gtk_range_get_adjustment (GTK_RANGE (navigator));
226
227 index = CLAMP (index, 0, priv->letters);
228
229 if (priv->index != index) {
230
231 priv->index = index;
232
233 g_signal_emit (navigator, signals[INDEX_CHANGED], 0);
234
235 g_signal_handlers_block_by_func (adj, cursor_navigator_changed, navigator);
236 gtk_adjustment_set_value (adj, priv->index);
237 g_signal_handlers_unblock_by_func (adj, cursor_navigator_changed, navigator);
238 }
239 }
240
241 gint
cursor_navigator_get_index(CursorNavigator * navigator)242 cursor_navigator_get_index (CursorNavigator *navigator)
243 {
244 CursorNavigatorPrivate *priv;
245
246 g_return_val_if_fail (CURSOR_IS_NAVIGATOR (navigator), 0);
247
248 priv = navigator->priv;
249
250 return priv->index;
251 }
252