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