1 /* -*- Mode: C; indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2004 by Carlos Garc�a Campos
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 
24 #include <gtk/gtk.h>
25 
26 #include "gn-combo-history.h"
27 
28 #define PARENT_TYPE G_TYPE_OBJECT
29 
30 enum {
31 	PROP_0,
32 	PROP_COMBO,
33 	PROP_ID,
34 	PROP_MAX_HISTORY
35 };
36 
37 struct _GnComboHistoryPrivate {
38 	GtkComboBox *combo;
39 	gchar       *id;
40 	guint        max_history;
41 
42 	GSList       *items;
43 
44 	GSettings   *settings;
45 };
46 
47 static void     gn_combo_history_init         (GnComboHistory      *history);
48 static void     gn_combo_history_class_init   (GnComboHistoryClass *klass);
49 static void     gn_combo_history_finalize     (GObject             *object);
50 
51 static void     gn_combo_history_set_property (GObject             *object,
52 					       guint                prop_id,
53 					       const GValue        *value,
54 					       GParamSpec          *pspec);
55 static void     gn_combo_history_get_property (GObject             *object,
56 					       guint                prop_id,
57 					       GValue              *value,
58 					       GParamSpec          *pspec);
59 
60 static void     gn_combo_history_settings_register_id   (GnComboHistory *history);
61 static void     gn_combo_history_settings_load       (GnComboHistory *history);
62 static void     gn_combo_history_settings_save       (GnComboHistory *history);
63 static void     gn_combo_history_set_popdown_strings (GnComboHistory *history);
64 
65 static GObjectClass *parent_class = NULL;
66 
67 GType
gn_combo_history_get_type(void)68 gn_combo_history_get_type (void)
69 {
70 	static GType type = 0;
71 
72 	if (!type) {
73 		static const GTypeInfo info = {
74 			sizeof (GnComboHistoryClass),
75 			(GBaseInitFunc) NULL,
76 			(GBaseFinalizeFunc) NULL,
77 			(GClassInitFunc) gn_combo_history_class_init,
78 			NULL,
79 			NULL,
80 			sizeof (GnComboHistory),
81 			0,
82 			(GInstanceInitFunc) gn_combo_history_init
83 		};
84 		type = g_type_register_static (PARENT_TYPE, "GnComboHistory",
85 					       &info, 0);
86 	}
87 	return type;
88 }
89 
90 static void
gn_combo_history_init(GnComboHistory * history)91 gn_combo_history_init (GnComboHistory *history)
92 {
93 	g_return_if_fail (GN_IS_COMBO_HISTORY (history));
94 
95 	history->priv = g_new0 (GnComboHistoryPrivate, 1);
96 	history->priv->combo = NULL;
97 	history->priv->id = NULL;
98 	history->priv->max_history = 10;
99 	history->priv->items = NULL;
100 	history->priv->settings = g_settings_new ("org.gnome.gnome-nettool");
101 }
102 
103 static void
gn_combo_history_class_init(GnComboHistoryClass * klass)104 gn_combo_history_class_init (GnComboHistoryClass *klass)
105 {
106 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
107 
108 	parent_class = g_type_class_peek_parent (klass);
109 
110 	object_class->set_property = gn_combo_history_set_property;
111 	object_class->get_property = gn_combo_history_get_property;
112 
113 	g_object_class_install_property (object_class, PROP_COMBO,
114 					 g_param_spec_pointer ("combo", NULL, NULL,
115 							       G_PARAM_READWRITE));
116 	g_object_class_install_property (object_class, PROP_ID,
117 					 g_param_spec_string ("id", NULL, NULL,
118 							      NULL, G_PARAM_READWRITE));
119 	g_object_class_install_property (object_class, PROP_MAX_HISTORY,
120 					 g_param_spec_uint ("max_history", NULL, NULL,
121 							    0, G_MAXUINT, 10,
122 							    G_PARAM_READWRITE));
123 
124 	object_class->finalize = gn_combo_history_finalize;
125 }
126 
127 static void
gn_combo_free_items(GnComboHistory * history)128 gn_combo_free_items (GnComboHistory *history)
129 {
130 	if (history->priv->items) {
131 		g_slist_foreach (history->priv->items, (GFunc) g_free, NULL);
132 		g_slist_free (history->priv->items);
133 		history->priv->items = NULL;
134 	}
135 }
136 
137 static void
gn_combo_history_finalize(GObject * object)138 gn_combo_history_finalize (GObject *object)
139 {
140 	GnComboHistory *history = GN_COMBO_HISTORY (object);
141 
142 	if (history->priv) {
143 		if (history->priv->combo) {
144 			g_object_unref (G_OBJECT (history->priv->combo));
145 			history->priv->combo = NULL;
146 		}
147 
148 		if (history->priv->id) {
149 			g_free (history->priv->id);
150 			history->priv->id = NULL;
151 		}
152 
153 		gn_combo_free_items (history);
154 
155 		g_object_unref (G_OBJECT (history->priv->settings));
156 		history->priv->settings = NULL;
157 	}
158 
159 	if (G_OBJECT_CLASS (parent_class)->finalize)
160 		(* G_OBJECT_CLASS (parent_class)->finalize) (object);
161 }
162 
163 static void
gn_combo_history_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * spec)164 gn_combo_history_set_property (GObject  *object, guint prop_id, const GValue *value,
165 			       GParamSpec *spec)
166 {
167 	GnComboHistory *history;
168 
169 	g_return_if_fail (GN_IS_COMBO_HISTORY (object));
170 
171 	history = GN_COMBO_HISTORY (object);
172 
173 	switch (prop_id) {
174 	case PROP_COMBO:
175 		history->priv->combo = g_value_get_pointer (value);
176 		break;
177 	case PROP_ID:
178 		if (history->priv->id) g_free (history->priv->id);
179 		history->priv->id = g_value_dup_string (value);
180 		break;
181 	case PROP_MAX_HISTORY:
182 		history->priv->max_history = g_value_get_uint (value);
183 		break;
184 	default:
185 		break;
186 	}
187 }
188 
189 static void
gn_combo_history_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * spec)190 gn_combo_history_get_property (GObject  *object, guint prop_id, GValue *value,
191 			       GParamSpec *spec)
192 {
193 	GnComboHistory *history;
194 
195 	g_return_if_fail (GN_IS_COMBO_HISTORY (object));
196 
197 	history = GN_COMBO_HISTORY (object);
198 
199 	switch (prop_id) {
200 	case PROP_COMBO:
201 		g_value_set_pointer (value, history->priv->combo);
202 		break;
203 	case PROP_ID:
204 		g_value_set_string (value, history->priv->id);
205 		break;
206 	case PROP_MAX_HISTORY:
207 		g_value_set_uint (value, history->priv->max_history);
208 		break;
209 	default:
210 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
211 	}
212 }
213 
214 GnComboHistory *
gn_combo_history_new(void)215 gn_combo_history_new (void)
216 {
217 	GnComboHistory *history;
218 
219 	history = g_object_new (GN_TYPE_COMBO_HISTORY, NULL);
220 
221 	return (history);
222 }
223 
224 static void
gn_combo_history_settings_load(GnComboHistory * history)225 gn_combo_history_settings_load (GnComboHistory *history)
226 {
227 	gchar **items;
228 	guint i;
229 
230 	g_return_if_fail (GN_IS_COMBO_HISTORY (history));
231 	g_return_if_fail (history->priv->id != NULL);
232 
233 	gn_combo_free_items (history);
234 	history->priv->items = NULL;
235 
236 	items = g_settings_get_strv (history->priv->settings, history->priv->id);
237 
238 	for (i = 0; items[i] && i < history->priv->max_history; i++)
239 		history->priv->items = g_slist_append (history->priv->items, g_strdup (items[i]));
240 	g_strfreev (items);
241 }
242 
243 static void
gn_combo_history_settings_save(GnComboHistory * history)244 gn_combo_history_settings_save (GnComboHistory *history)
245 {
246 	const gchar **items;
247 	GSList *item;
248 	gint i;
249 
250 	g_return_if_fail (GN_IS_COMBO_HISTORY (history));
251 	g_return_if_fail (history->priv->id != NULL);
252 
253 	items = g_malloc (sizeof (gchar *) * (g_slist_length (history->priv->items) + 1));
254 	for (item = history->priv->items, i = 0; item; item = item->next, i++)
255 		items[i] = item->data;
256 	items[i] = NULL;
257 	g_settings_set_strv (history->priv->settings, history->priv->id, items);
258 	g_free (items);
259 }
260 
261 static void
gn_combo_history_set_popdown_strings(GnComboHistory * history)262 gn_combo_history_set_popdown_strings (GnComboHistory *history)
263 {
264 	GtkTreeModel *model;
265 	GtkTreeIter   iter;
266 	GSList *items;
267 	gchar  *text;
268 	gint    text_column, i;
269 
270 	g_return_if_fail (GN_IS_COMBO_HISTORY (history));
271 	g_return_if_fail (GTK_IS_COMBO_BOX (history->priv->combo));
272 
273 	model = gtk_combo_box_get_model (history->priv->combo);
274 
275 	if (!model)
276 		return;
277 
278 	text_column = gtk_combo_box_get_entry_text_column (
279 							   GTK_COMBO_BOX (history->priv->combo));
280 
281 	gtk_list_store_clear (GTK_LIST_STORE (model));
282 
283 	if (! history->priv->items) {
284 
285 		gtk_combo_box_set_active (GTK_COMBO_BOX (history->priv->combo), -1);
286 
287 		return;
288 	}
289 
290 	i = 0;
291 	for (items = history->priv->items; items; items = items->next) {
292 		text = items->data;
293 
294 		gtk_list_store_insert (GTK_LIST_STORE (model), &iter, i);
295 		gtk_list_store_set (GTK_LIST_STORE (model), &iter,
296 				    text_column, text,
297 				    -1);
298 
299 		i ++;
300 	}
301 
302 	/* At this point the current selection always be at the first place in the model */
303 	gtk_combo_box_set_active (GTK_COMBO_BOX (history->priv->combo), 0);
304 }
305 
306 void
gn_combo_history_set_combo(GnComboHistory * history,GtkComboBox * combo)307 gn_combo_history_set_combo (GnComboHistory *history, GtkComboBox *combo)
308 {
309 	g_return_if_fail (GN_IS_COMBO_HISTORY (history));
310 	g_return_if_fail (GTK_IS_COMBO_BOX (combo));
311 
312 	if (history->priv->combo)
313 		g_object_unref (G_OBJECT (history->priv->combo));
314 
315 	history->priv->combo = combo;
316 
317 	gn_combo_history_settings_load (history);
318 
319 	gn_combo_history_set_popdown_strings (history);
320 
321 	gtk_combo_box_set_active (GTK_COMBO_BOX (history->priv->combo),
322 				  -1);
323 
324 	gtk_entry_set_text (
325 			    GTK_ENTRY (gtk_bin_get_child (
326 							  GTK_BIN (history->priv->combo))), "");
327 
328 	g_object_ref (G_OBJECT (history->priv->combo));
329 }
330 
331 GtkComboBox *
gn_combo_history_get_combo(GnComboHistory * history)332 gn_combo_history_get_combo (GnComboHistory *history)
333 {
334 	g_return_val_if_fail (GN_IS_COMBO_HISTORY (history), NULL);
335 
336 	return history->priv->combo;
337 }
338 
339 static void
gn_on_settings_history_changed(GSettings * settings,gchar * key,gpointer gdata)340 gn_on_settings_history_changed (GSettings *settings, gchar *key, gpointer gdata)
341 {
342 	GnComboHistory *history;
343 
344 	history = GN_COMBO_HISTORY (gdata);
345 
346 	if (!g_str_equal (key, history->priv->id))
347 		return;
348 
349 	gn_combo_history_settings_load (history);
350 	gn_combo_history_set_popdown_strings (history);
351 }
352 
353 static void
gn_combo_history_settings_register_id(GnComboHistory * history)354 gn_combo_history_settings_register_id (GnComboHistory *history)
355 {
356 	g_return_if_fail (GN_IS_COMBO_HISTORY (history));
357 	g_signal_connect (history->priv->settings, "changed", G_CALLBACK (gn_on_settings_history_changed), history);
358 }
359 
360 void
gn_combo_history_set_id(GnComboHistory * history,const gchar * history_id)361 gn_combo_history_set_id (GnComboHistory *history, const gchar *history_id)
362 {
363 	g_return_if_fail (GN_IS_COMBO_HISTORY (history));
364 	g_return_if_fail (history_id != NULL);
365 
366 	if (history->priv->id)
367 		g_free (history->priv->id);
368 
369 	history->priv->id = g_strdup (history_id);
370 
371 	gn_combo_history_settings_register_id (history);
372 }
373 
374 const gchar *
gn_combo_history_get_id(GnComboHistory * history)375 gn_combo_history_get_id (GnComboHistory *history)
376 {
377 	g_return_val_if_fail (GN_IS_COMBO_HISTORY (history), NULL);
378 
379 	return history->priv->id;
380 }
381 
382 static gint
compare(gconstpointer a,gconstpointer b)383 compare (gconstpointer a, gconstpointer b)
384 {
385 	return (g_ascii_strcasecmp (a, b));
386 }
387 
388 void
gn_combo_history_add(GnComboHistory * history,const gchar * text)389 gn_combo_history_add (GnComboHistory *history, const gchar *text)
390 {
391 	GSList *item;
392 
393 	g_return_if_fail (GN_IS_COMBO_HISTORY (history));
394 	g_return_if_fail (text != NULL);
395 
396 	if ((item = g_slist_find_custom (history->priv->items, (gpointer) text, compare))) {
397 		/* item is already in list, remove them */
398 		history->priv->items = g_slist_remove (history->priv->items, item->data);
399 	}
400 
401 	if (g_slist_length (history->priv->items) >= history->priv->max_history) {
402 		item = g_slist_last (history->priv->items);
403 		history->priv->items = g_slist_remove (history->priv->items, item->data);
404 	}
405 
406 	history->priv->items = g_slist_prepend (history->priv->items,
407 						g_strdup (text));
408 
409 	gn_combo_history_set_popdown_strings (history);
410 
411 	gn_combo_history_settings_save (history);
412 }
413 
414 void
gn_combo_history_clear(GnComboHistory * history)415 gn_combo_history_clear (GnComboHistory *history)
416 {
417 	g_return_if_fail (GN_IS_COMBO_HISTORY (history));
418 
419 	if (history->priv->items) {
420 		gn_combo_free_items (history);
421 		gn_combo_history_settings_save (history);
422 	}
423 }
424 
425 guint
gn_combo_history_get_max_history(GnComboHistory * history)426 gn_combo_history_get_max_history (GnComboHistory *history)
427 {
428 	g_return_val_if_fail (GN_IS_COMBO_HISTORY (history), 0);
429 
430 	return history->priv->max_history;
431 }
432 
433 void
gn_combo_history_set_max_history(GnComboHistory * history,guint max_history)434 gn_combo_history_set_max_history (GnComboHistory *history,
435 				  guint max_history)
436 {
437 	g_return_if_fail (GN_IS_COMBO_HISTORY (history));
438 
439 	history->priv->max_history = max_history;
440 }
441