initial commit
[gconf-editor] / src / gconf-list-model.c
1 /*
2  * Copyright (C) 2001, 2002 Anders Carlsson <andersca@gnu.org>
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include <config.h>
20
21 #include "gconf-list-model.h"
22
23 #include <string.h>
24 #include "gconf-util.h"
25
26 #define G_SLIST(x) ((GSList *) x)
27
28 static const char *gconf_value_icon_names[] = {
29   "type-undefined",
30   "type-string",
31   "type-integer",
32   "type-float",
33   "type-boolean",
34   "type-schema",
35   "type-list",
36   "type-pair"
37 };
38
39 static void gconf_list_model_class_init       (GConfListModelClass *klass);
40 static void gconf_list_model_init             (GConfListModel *model);
41 static void gconf_list_model_tree_model_init  (GtkTreeModelIface *iface);
42
43 G_DEFINE_TYPE_WITH_CODE (GConfListModel, gconf_list_model, G_TYPE_OBJECT,
44                          G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, gconf_list_model_tree_model_init));
45
46 static void
47 gconf_list_model_notify_func (GConfClient* client, guint cnxn_id, GConfEntry *entry, gpointer user_data)
48 {
49         GSList *list;
50         const gchar *key;
51         char *path_str;
52         GConfListModel *list_model = user_data;
53         GtkTreeIter iter;
54         GtkTreePath *path;
55
56         key = gconf_entry_get_key (entry);
57
58         path_str = g_path_get_dirname (key);
59
60         if (strcmp (path_str, list_model->root_path) != 0)
61           {
62             g_free (path_str);
63             return;
64           }
65
66         g_free (path_str);
67
68         if (strncmp (key, list_model->root_path, strlen (list_model->root_path)) != 0)
69             return;
70         
71         if (gconf_client_dir_exists (client, key, NULL))
72                 /* this is a directory -- ignore */
73                 return;
74
75         list = g_hash_table_lookup (list_model->key_hash, key);
76
77         if (list == NULL) {
78                 /* Create a new entry */
79                 entry = gconf_entry_new (gconf_entry_get_key (entry),
80                                          gconf_entry_get_value (entry));
81
82                 list = g_slist_append (list, entry);
83                 list_model->values = g_slist_concat (list_model->values, list);
84                 g_hash_table_insert (list_model->key_hash, g_strdup (key), list);
85
86                 list_model->stamp++;
87
88                 iter.stamp = list_model->stamp;
89                 iter.user_data = list;
90
91                 list_model->length++;
92
93                 path = gtk_tree_model_get_path (GTK_TREE_MODEL (list_model), &iter);
94                 gtk_tree_model_row_inserted (GTK_TREE_MODEL (list_model), path, &iter);
95                 gtk_tree_path_free (path);
96         }
97         else {
98                 list_model->stamp++;
99
100                 iter.stamp = list_model->stamp;
101                 iter.user_data = list;
102
103                 path = gtk_tree_model_get_path (GTK_TREE_MODEL (list_model), &iter);
104
105                 gconf_entry_free (list->data);
106
107                 if (gconf_entry_get_value (entry) != NULL) {
108                         list->data = gconf_entry_new (gconf_entry_get_key (entry),
109                                                       gconf_entry_get_value (entry));
110                         gtk_tree_model_row_changed (GTK_TREE_MODEL (list_model), path, &iter);
111                 }
112                 else {
113                         gtk_tree_model_row_deleted (GTK_TREE_MODEL (list_model), path);
114                         list_model->values = g_slist_remove (list_model->values, list->data);
115                         list_model->length--;
116                         g_hash_table_remove (list_model->key_hash, key);
117                 }
118
119                 gtk_tree_path_free (path);
120         }
121 }
122
123 void
124 gconf_list_model_set_root_path (GConfListModel *model, const gchar *root_path)
125 {
126         GSList *list;
127         GSList *values;
128         GtkTreeIter iter;
129         GtkTreePath *path;
130
131         path = gtk_tree_path_new ();
132         gtk_tree_path_append_index (path, 0);
133
134         if (model->root_path != NULL) {
135                 for (list = model->values; list; list = list->next) {
136                         GConfEntry *entry = list->data;
137
138                         g_hash_table_remove (model->key_hash, gconf_entry_get_key (entry));
139                         model->stamp++;
140                         gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
141
142                         gconf_entry_free (entry);
143                 }
144
145                 gconf_client_notify_remove (model->client, model->notify_id);
146
147                 gconf_client_remove_dir  (model->client,
148                                           model->root_path, NULL);
149
150                 g_free (model->root_path);
151                 g_slist_free (model->values);
152                 model->values = NULL;
153         }
154         gtk_tree_path_free (path);
155
156         gconf_client_add_dir (model->client,
157                               root_path,
158                               GCONF_CLIENT_PRELOAD_ONELEVEL,
159                               NULL);
160
161         model->notify_id = gconf_client_notify_add (model->client, root_path,
162                                                     gconf_list_model_notify_func,
163                                                     model, NULL, NULL);
164
165         model->root_path = g_strdup (root_path);
166         values = gconf_client_all_entries (model->client, root_path, NULL);
167         model->length = 0;
168
169         for (list = values; list; list = list->next) {
170                 GConfEntry *entry = list->data;
171
172                 model->values = g_slist_append (model->values, list->data);
173                 model->length++;
174
175                 model->stamp++;
176
177                 iter.stamp = model->stamp;
178                 iter.user_data = g_slist_last (model->values);
179
180                 g_hash_table_insert (model->key_hash, g_strdup (gconf_entry_get_key (entry)), iter.user_data);
181
182                 path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
183                 gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter);
184
185                 gtk_tree_path_free (path);
186         }
187
188         if (values) 
189                 g_slist_free (values);
190 }
191
192 static GtkTreeModelFlags 
193 gconf_list_model_get_flags (GtkTreeModel *tree_model)
194 {
195         return 0;
196 }
197
198 static gint
199 gconf_list_model_get_n_columns (GtkTreeModel *tree_model)
200 {
201         return GCONF_LIST_MODEL_NUM_COLUMNS;
202 }
203
204 static GType
205 gconf_list_model_get_column_type (GtkTreeModel *tree_model, gint index)
206 {
207         switch (index) {
208         case GCONF_LIST_MODEL_ICON_NAME_COLUMN:
209         case GCONF_LIST_MODEL_KEY_PATH_COLUMN:
210         case GCONF_LIST_MODEL_KEY_NAME_COLUMN:
211                 return G_TYPE_STRING;
212         case GCONF_LIST_MODEL_VALUE_COLUMN:
213                 return GCONF_TYPE_VALUE;
214         default:
215                 return G_TYPE_INVALID;
216         }
217 }
218
219 static gboolean
220 gconf_list_model_get_iter (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreePath *path)
221 {
222         GConfListModel *list_model = (GConfListModel *)tree_model;
223         GSList *list;
224         gint i;
225
226         g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE);
227
228         i = gtk_tree_path_get_indices (path)[0];
229
230         if (i >= list_model->length)
231                 return FALSE;
232
233         list = g_slist_nth (list_model->values, i);
234
235         iter->stamp = list_model->stamp;
236         iter->user_data = list;
237
238         return TRUE;
239 }
240
241 static GtkTreePath *
242 gconf_list_model_get_path (GtkTreeModel *tree_model, GtkTreeIter *iter)
243 {
244         GSList *list;
245         GtkTreePath *tree_path;
246         gint i = 0;
247
248         g_return_val_if_fail (iter->stamp == GCONF_LIST_MODEL (tree_model)->stamp, NULL);
249
250         for (list = G_SLIST (GCONF_LIST_MODEL (tree_model)->values); list; list = list->next) {
251                 if (list == G_SLIST (iter->user_data))
252                         break;
253                 i++;
254         }
255         if (list == NULL)
256                 return NULL;
257
258         tree_path = gtk_tree_path_new ();
259         gtk_tree_path_append_index (tree_path, i);
260
261         return tree_path;
262 }
263
264 static void
265 gconf_list_model_get_value (GtkTreeModel *tree_model, GtkTreeIter *iter, gint column, GValue *value)
266 {
267         GConfEntry *entry;
268         
269         g_return_if_fail (iter->stamp == GCONF_LIST_MODEL (tree_model)->stamp);
270
271         entry = G_SLIST (iter->user_data)->data;
272         
273         switch (column) {
274         case GCONF_LIST_MODEL_KEY_PATH_COLUMN:
275                 g_value_init (value, G_TYPE_STRING);
276                 g_value_set_string (value, gconf_entry_get_key (entry));
277                 break;
278                 
279         case GCONF_LIST_MODEL_KEY_NAME_COLUMN:
280                 g_value_init (value, G_TYPE_STRING);
281                 g_value_take_string (value, gconf_get_key_name_from_path (gconf_entry_get_key (entry)));
282                 break;
283                 
284         case GCONF_LIST_MODEL_ICON_NAME_COLUMN: {
285                 GConfValue *gconf_value;
286                 GConfValueType value_type;
287
288                 gconf_value = gconf_entry_get_value (entry);
289                 if (gconf_value) {
290                         value_type = gconf_value->type;
291                 } else {
292                         value_type = GCONF_VALUE_INVALID;
293                 }
294
295                 g_value_init (value, G_TYPE_STRING);
296                 g_value_set_static_string (value, gconf_value_icon_names[value_type]);
297                 
298                 break;
299         }
300                 
301         case GCONF_LIST_MODEL_VALUE_COLUMN:
302                 g_value_init (value, GCONF_TYPE_VALUE);
303                 g_value_set_boxed (value, gconf_entry_get_value (entry));
304                 break;
305
306         default:
307                 break;
308         }
309 }
310
311 static gboolean
312 gconf_list_model_iter_next (GtkTreeModel *tree_model, GtkTreeIter *iter)
313 {
314         g_return_val_if_fail (iter->stamp == GCONF_LIST_MODEL (tree_model)->stamp, FALSE);
315
316         iter->user_data = G_SLIST (iter->user_data)->next;
317         
318         return (iter->user_data != NULL);
319 }
320
321 static gboolean
322 gconf_list_model_iter_has_child (GtkTreeModel *tree_model, GtkTreeIter *iter)
323 {
324         return FALSE;
325 }
326
327 static gint
328 gconf_list_model_iter_n_children (GtkTreeModel *tree_model, GtkTreeIter *iter)
329 {
330         /* it should ask for the root node, because we're a list */
331         if (!iter)
332                 return g_slist_length (GCONF_LIST_MODEL (tree_model)->values);
333
334         return -1;
335 }
336
337 static gboolean
338 gconf_list_model_iter_nth_child (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent, gint n)
339 {
340         GSList *child;
341
342         if (parent)
343                 return FALSE;
344         
345         child = g_slist_nth (GCONF_LIST_MODEL (tree_model)->values, n);
346
347         if (child) {
348                 iter->stamp = GCONF_LIST_MODEL (tree_model)->stamp;
349                 iter->user_data = child;
350                 return TRUE;
351         }
352         else
353                 return FALSE;
354 }
355
356 static void
357 gconf_list_model_tree_model_init (GtkTreeModelIface *iface)
358 {
359         iface->get_flags = gconf_list_model_get_flags;
360         iface->get_n_columns = gconf_list_model_get_n_columns;
361         iface->get_column_type = gconf_list_model_get_column_type;
362         iface->get_iter = gconf_list_model_get_iter;
363         iface->get_path = gconf_list_model_get_path;
364         iface->get_value = gconf_list_model_get_value;
365         iface->iter_next = gconf_list_model_iter_next;
366         iface->iter_has_child = gconf_list_model_iter_has_child;
367         iface->iter_n_children = gconf_list_model_iter_n_children;
368         iface->iter_nth_child = gconf_list_model_iter_nth_child;
369 }
370
371 static void
372 gconf_list_model_finalize (GObject *object)
373 {
374         GConfListModel *list_model;
375
376         list_model = (GConfListModel *)object;
377         
378         g_hash_table_destroy (list_model->key_hash);
379
380         if (list_model->client && list_model->notify_id > 0) {
381                 gconf_client_notify_remove (list_model->client, list_model->notify_id);
382         }
383
384         if (list_model->client && list_model->root_path) {
385                 gconf_client_remove_dir  (list_model->client,
386                                           list_model->root_path, NULL);
387         }
388
389         if (list_model->client) {
390                 g_object_unref (list_model->client);
391                 list_model->client = NULL;
392         }
393
394         if (list_model->values) {
395                 g_slist_foreach (list_model->values, (GFunc) gconf_entry_free, NULL);
396                 g_slist_free (list_model->values);
397                 list_model->values = NULL;
398         }
399
400         g_free (list_model->root_path);
401
402         G_OBJECT_CLASS (gconf_list_model_parent_class)->finalize (object);
403 }
404
405 static void
406 gconf_list_model_class_init (GConfListModelClass *klass)
407 {
408         GObjectClass *object_class = G_OBJECT_CLASS (klass);
409
410         object_class->finalize = gconf_list_model_finalize;
411 }
412
413 static void
414 gconf_list_model_init (GConfListModel *model)
415 {
416         model->stamp = g_random_int ();
417         model->key_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
418                                                  g_free, NULL);
419         model->client = gconf_client_get_default ();
420 }
421
422 void
423 gconf_list_model_set_client (GConfListModel *model, GConfClient *client)
424 {
425         if (model->client != NULL) {
426                 g_object_unref (model->client);
427         }
428
429         model->client = g_object_ref (client);
430 }
431
432 GtkTreeModel *
433 gconf_list_model_new (void)
434 {
435         return GTK_TREE_MODEL (g_object_new (GCONF_TYPE_LIST_MODEL, NULL));
436 }