Tizen 2.0 Release
[framework/base/gconf-dbus.git] / gconf / gconf-changeset.c
1 /* GConf
2  * Copyright (C) 1999, 2000 Red Hat Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library 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  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "gconf-changeset.h"
21 #include "gconf-internals.h"
22
23 typedef enum {
24   CHANGE_INVALID,
25   CHANGE_SET,
26   CHANGE_UNSET
27 } ChangeType;
28
29 typedef struct _Change Change;
30
31 struct _Change {
32   gchar* key;
33   ChangeType type;
34   GConfValue* value;
35 };
36
37 static Change* change_new    (const gchar* key);
38 static void    change_set    (Change* c, GConfValue* value);
39 static void    change_unset  (Change* c);
40 static void    change_destroy(Change* c);
41
42 struct _GConfChangeSet {
43   guint refcount;
44   GHashTable* hash;
45   gint in_foreach;
46   gpointer user_data;
47   GDestroyNotify dnotify;
48 };
49
50 GType
51 gconf_change_set_get_type (void)
52 {
53   static GType our_type = 0;
54
55   if (our_type == 0)
56     our_type = g_boxed_type_register_static ("GConfChangeSet",
57                                              (GBoxedCopyFunc) gconf_change_set_ref,
58                                              (GBoxedFreeFunc) gconf_change_set_unref);
59
60   return our_type;
61 }
62
63 GConfChangeSet*
64 gconf_change_set_new      (void)
65 {
66   GConfChangeSet* cs;
67
68   cs = g_new(GConfChangeSet, 1);
69
70   cs->refcount = 1;
71   cs->hash = g_hash_table_new(g_str_hash, g_str_equal);
72   cs->in_foreach = 0;
73   cs->user_data = NULL;
74   cs->dnotify = NULL;
75   
76   return cs;
77 }
78
79 void
80 gconf_change_set_ref      (GConfChangeSet* cs)
81 {
82   g_return_if_fail(cs != NULL);
83   
84   cs->refcount += 1;
85 }
86
87 void
88 gconf_change_set_unref    (GConfChangeSet* cs)
89 {
90   g_return_if_fail(cs != NULL);
91   g_return_if_fail(cs->refcount > 0);
92
93   cs->refcount -= 1;
94
95   if (cs->refcount == 0)
96     {
97       if (cs->in_foreach > 0)
98         g_warning("GConfChangeSet refcount reduced to 0 during a foreach");
99       
100       gconf_change_set_clear(cs);
101
102       g_hash_table_destroy(cs->hash);
103       
104       g_free(cs);
105     }
106 }
107
108 void
109 gconf_change_set_set_user_data (GConfChangeSet *cs,
110                                 gpointer        data,
111                                 GDestroyNotify  dnotify)
112 {
113   if (cs->dnotify)
114     (* cs->dnotify) (cs->user_data);
115
116   cs->user_data = data;
117   cs->dnotify = dnotify;
118 }
119
120 gpointer
121 gconf_change_set_get_user_data (GConfChangeSet *cs)
122 {
123   return cs->user_data;
124 }
125
126 static Change*
127 get_change_unconditional (GConfChangeSet* cs,
128                           const gchar* key)
129 {
130   Change* c;
131
132   c = g_hash_table_lookup(cs->hash, key);
133
134   if (c == NULL)
135     {
136       c = change_new(key);
137
138       g_hash_table_insert(cs->hash, c->key, c);
139     }
140
141   return c;
142 }
143
144 static gboolean
145 destroy_foreach (gpointer key, gpointer value, gpointer user_data)
146 {
147   Change* c = value;
148
149   g_assert(c != NULL);
150
151   change_destroy(c);
152
153   return TRUE; /* remove from hash */
154 }
155
156 void
157 gconf_change_set_clear    (GConfChangeSet* cs)
158 {
159   g_return_if_fail(cs != NULL);
160
161   g_hash_table_foreach_remove (cs->hash, destroy_foreach, NULL);
162 }
163
164 guint
165 gconf_change_set_size     (GConfChangeSet* cs)
166 {
167   g_return_val_if_fail(cs != NULL, 0);
168   
169   return g_hash_table_size(cs->hash);
170 }
171
172 void
173 gconf_change_set_remove   (GConfChangeSet* cs,
174                            const gchar* key)
175 {
176   Change* c;
177   
178   g_return_if_fail(cs != NULL);
179   g_return_if_fail(cs->in_foreach == 0);
180   
181   c = g_hash_table_lookup(cs->hash, key);
182
183   if (c != NULL)
184     {
185       g_hash_table_remove(cs->hash, c->key);
186       change_destroy(c);
187     }
188 }
189
190
191 struct ForeachData {
192   GConfChangeSet* cs;
193   GConfChangeSetForeachFunc func;
194   gpointer user_data;
195 };
196
197 static void
198 foreach(gpointer key, gpointer value, gpointer user_data)
199 {
200   Change* c;
201   struct ForeachData* fd = user_data;
202   
203   c = value;
204
205   /* assumes that an UNSET change has a NULL value */
206   (* fd->func) (fd->cs, c->key, c->value, fd->user_data);
207 }
208
209 void
210 gconf_change_set_foreach  (GConfChangeSet* cs,
211                            GConfChangeSetForeachFunc func,
212                            gpointer user_data)
213 {
214   struct ForeachData fd;
215   
216   g_return_if_fail(cs != NULL);
217   g_return_if_fail(func != NULL);
218   
219   fd.cs = cs;
220   fd.func = func;
221   fd.user_data = user_data;
222
223   gconf_change_set_ref(cs);
224
225   cs->in_foreach += 1;
226   
227   g_hash_table_foreach(cs->hash, foreach, &fd);
228
229   cs->in_foreach -= 1;
230   
231   gconf_change_set_unref(cs);
232 }
233
234 gboolean
235 gconf_change_set_check_value   (GConfChangeSet* cs, const gchar* key,
236                                 GConfValue** value_retloc)
237 {
238   Change* c;
239   
240   g_return_val_if_fail(cs != NULL, FALSE);
241
242   c = g_hash_table_lookup(cs->hash, key);
243
244   if (c == NULL)
245     return FALSE;
246   else
247     {
248       if (value_retloc != NULL)
249         *value_retloc = c->value;
250
251       return TRUE;
252     }
253 }
254
255 void
256 gconf_change_set_set_nocopy  (GConfChangeSet* cs, const gchar* key,
257                               GConfValue* value)
258 {
259   Change* c;
260   
261   g_return_if_fail(cs != NULL);
262   g_return_if_fail(value != NULL);
263
264   c = get_change_unconditional(cs, key);
265
266   change_set(c, value);
267 }
268
269 void
270 gconf_change_set_set (GConfChangeSet* cs, const gchar* key,
271                       GConfValue* value)
272 {
273   g_return_if_fail(value != NULL);
274   
275   gconf_change_set_set_nocopy(cs, key, gconf_value_copy(value));
276 }
277
278 void
279 gconf_change_set_unset      (GConfChangeSet* cs, const gchar* key)
280 {
281   Change* c;
282   
283   g_return_if_fail(cs != NULL);
284
285   c = get_change_unconditional(cs, key);
286
287   change_unset(c);
288 }
289
290 void
291 gconf_change_set_set_float   (GConfChangeSet* cs, const gchar* key,
292                               gdouble val)
293 {
294   GConfValue* value;
295   
296   g_return_if_fail(cs != NULL);
297
298   value = gconf_value_new(GCONF_VALUE_FLOAT);
299   gconf_value_set_float(value, val);
300   
301   gconf_change_set_set_nocopy(cs, key, value);
302 }
303
304 void
305 gconf_change_set_set_int     (GConfChangeSet* cs, const gchar* key,
306                               gint val)
307 {
308   GConfValue* value;
309   
310   g_return_if_fail(cs != NULL);
311
312   value = gconf_value_new(GCONF_VALUE_INT);
313   gconf_value_set_int(value, val);
314   
315   gconf_change_set_set_nocopy(cs, key, value);
316 }
317
318 void
319 gconf_change_set_set_string  (GConfChangeSet* cs, const gchar* key,
320                               const gchar* val)
321 {
322   GConfValue* value;
323   
324   g_return_if_fail(cs != NULL);
325   g_return_if_fail(key != NULL);
326   g_return_if_fail(val != NULL);
327   
328   value = gconf_value_new(GCONF_VALUE_STRING);
329   gconf_value_set_string(value, val);
330   
331   gconf_change_set_set_nocopy(cs, key, value);
332 }
333
334 void
335 gconf_change_set_set_bool    (GConfChangeSet* cs, const gchar* key,
336                               gboolean val)
337 {
338   GConfValue* value;
339   
340   g_return_if_fail(cs != NULL);
341
342   value = gconf_value_new(GCONF_VALUE_BOOL);
343   gconf_value_set_bool(value, val);
344   
345   gconf_change_set_set_nocopy(cs, key, value);
346 }
347
348 void
349 gconf_change_set_set_schema  (GConfChangeSet* cs, const gchar* key,
350                               GConfSchema* val)
351 {
352   GConfValue* value;
353   
354   g_return_if_fail(cs != NULL);
355
356   value = gconf_value_new(GCONF_VALUE_SCHEMA);
357   gconf_value_set_schema(value, val);
358   
359   gconf_change_set_set_nocopy(cs, key, value);
360 }
361
362 void
363 gconf_change_set_set_list    (GConfChangeSet* cs, const gchar* key,
364                               GConfValueType list_type,
365                               GSList* list)
366 {
367   GConfValue* value_list;
368   
369   g_return_if_fail(cs != NULL);
370   g_return_if_fail(key != NULL);
371   g_return_if_fail(list_type != GCONF_VALUE_INVALID);
372   g_return_if_fail(list_type != GCONF_VALUE_LIST);
373   g_return_if_fail(list_type != GCONF_VALUE_PAIR);
374   
375   value_list = gconf_value_list_from_primitive_list (list_type, list, NULL);
376   
377   gconf_change_set_set_nocopy(cs, key, value_list);
378 }
379
380
381 void
382 gconf_change_set_set_pair    (GConfChangeSet* cs, const gchar* key,
383                               GConfValueType car_type, GConfValueType cdr_type,
384                               gconstpointer address_of_car,
385                               gconstpointer address_of_cdr)
386 {
387   GConfValue* pair;
388   
389   g_return_if_fail(cs != NULL);
390   g_return_if_fail(key != NULL);
391   g_return_if_fail(car_type != GCONF_VALUE_INVALID);
392   g_return_if_fail(car_type != GCONF_VALUE_LIST);
393   g_return_if_fail(car_type != GCONF_VALUE_PAIR);
394   g_return_if_fail(cdr_type != GCONF_VALUE_INVALID);
395   g_return_if_fail(cdr_type != GCONF_VALUE_LIST);
396   g_return_if_fail(cdr_type != GCONF_VALUE_PAIR);
397   g_return_if_fail(address_of_car != NULL);
398   g_return_if_fail(address_of_cdr != NULL);
399
400   pair = gconf_value_pair_from_primitive_pair (car_type, cdr_type,
401                                                address_of_car, address_of_cdr,
402                                                NULL);
403   
404   gconf_change_set_set_nocopy(cs, key, pair);
405 }
406
407
408 /*
409  * Change
410  */
411
412 Change*
413 change_new    (const gchar* key)
414 {
415   Change* c;
416
417   c = g_new(Change, 1);
418
419   c->key  = g_strdup(key);
420   c->type = CHANGE_INVALID;
421   c->value = NULL;
422
423   return c;
424 }
425
426 void
427 change_destroy(Change* c)
428 {
429   g_return_if_fail(c != NULL);
430   
431   g_free(c->key);
432
433   if (c->value)
434     gconf_value_free(c->value);
435
436   g_free(c);
437 }
438
439 void
440 change_set    (Change* c, GConfValue* value)
441 {
442   g_return_if_fail(value == NULL ||
443                    GCONF_VALUE_TYPE_VALID(value->type));
444   
445   c->type = CHANGE_SET;
446
447   if (value == c->value)
448     return;
449   
450   if (c->value)
451     gconf_value_free(c->value);
452
453   c->value = value;
454 }
455
456 void
457 change_unset  (Change* c)
458 {
459   c->type = CHANGE_UNSET;
460
461   if (c->value)
462     gconf_value_free(c->value);
463
464   c->value = NULL;
465 }
466
467 /*
468  * Actually send it upstream
469  */
470
471 struct CommitData {
472   GConfEngine* conf;
473   GError* error;
474   GSList* remove_list;
475   gboolean remove_committed;
476 };
477
478 static void
479 commit_foreach (GConfChangeSet* cs,
480                 const gchar* key,
481                 GConfValue* value,
482                 gpointer user_data)
483 {
484   struct CommitData* cd = user_data;
485
486   g_assert(cd != NULL);
487
488   if (cd->error != NULL)
489     return;
490   
491   if (value)
492     gconf_engine_set   (cd->conf, key, value, &cd->error);
493   else
494     gconf_engine_unset (cd->conf, key, &cd->error);
495
496   if (cd->error == NULL && cd->remove_committed)
497     {
498       /* Bad bad bad; we keep the key reference, knowing that it's
499          valid until we modify the change set, to avoid string copies.  */
500       cd->remove_list = g_slist_prepend(cd->remove_list, (gchar*)key);
501     }
502 }
503
504 gboolean
505 gconf_engine_commit_change_set   (GConfEngine* conf,
506                            GConfChangeSet* cs,
507                            gboolean remove_committed,
508                            GError** err)
509 {
510   struct CommitData cd;
511   GSList* tmp;
512
513   g_return_val_if_fail(conf != NULL, FALSE);
514   g_return_val_if_fail(cs != NULL, FALSE);
515   g_return_val_if_fail(err == NULL || *err == NULL, FALSE);
516   
517   cd.conf = conf;
518   cd.error = NULL;
519   cd.remove_list = NULL;
520   cd.remove_committed = remove_committed;
521
522   /* Because the commit could have lots of side
523      effects, this makes it safer */
524   gconf_change_set_ref(cs);
525   gconf_engine_ref(conf);
526   
527   gconf_change_set_foreach(cs, commit_foreach, &cd);
528
529   tmp = cd.remove_list;
530   while (tmp != NULL)
531     {
532       const gchar* key = tmp->data;
533       
534       gconf_change_set_remove(cs, key);
535
536       /* key is now invalid due to our little evil trick */
537
538       tmp = g_slist_next(tmp);
539     }
540
541   g_slist_free(cd.remove_list);
542   
543   gconf_change_set_unref(cs);
544   gconf_engine_unref(conf);
545
546   if (cd.error != NULL)
547     {
548       if (err != NULL)
549         *err = cd.error;
550       else
551         g_error_free(cd.error);
552
553       return FALSE;
554     }
555   else
556     {
557       return TRUE;
558     }
559 }
560
561 struct RevertData {
562   GConfEngine* conf;
563   GError* error;
564   GConfChangeSet* revert_set;
565 };
566
567 static void
568 revert_foreach (GConfChangeSet* cs,
569                 const gchar* key,
570                 GConfValue* value,
571                 gpointer user_data)
572 {
573   struct RevertData* rd = user_data;
574   GConfValue* old_value;
575   GError* error = NULL;
576   
577   g_assert(rd != NULL);
578
579   if (rd->error != NULL)
580     return;
581
582   old_value = gconf_engine_get_without_default(rd->conf, key, &error);
583
584   if (error != NULL)
585     {
586       /* FIXME */
587       g_warning("error creating revert set: %s", error->message);
588       g_error_free(error);
589       error = NULL;
590     }
591   
592   if (old_value == NULL &&
593       value == NULL)
594     return; /* this commit will have no effect. */
595
596   if (old_value == NULL)
597     gconf_change_set_unset(rd->revert_set, key);
598   else
599     gconf_change_set_set_nocopy(rd->revert_set, key, old_value);
600 }
601
602
603 GConfChangeSet*
604 gconf_engine_reverse_change_set  (GConfEngine* conf,
605                                   GConfChangeSet* cs,
606                                   GError** err)
607 {
608   struct RevertData rd;
609
610   g_return_val_if_fail(err == NULL || *err == NULL, NULL);
611   
612   rd.error = NULL;
613   rd.conf = conf;
614   rd.revert_set = gconf_change_set_new();
615
616   gconf_change_set_foreach(cs, revert_foreach, &rd);
617
618   if (rd.error != NULL)
619     {
620       if (err != NULL)
621         *err = rd.error;
622       else
623         g_error_free(rd.error);
624     }
625   
626   return rd.revert_set;
627 }
628
629 GConfChangeSet*
630 gconf_engine_change_set_from_currentv (GConfEngine* conf,
631                                        const gchar** keys,
632                                        GError** err)
633 {
634   GConfValue* old_value;
635   GConfChangeSet* new_set;
636   const gchar** keyp;
637   
638   g_return_val_if_fail(err == NULL || *err == NULL, NULL);
639
640   new_set = gconf_change_set_new();
641   
642   keyp = keys;
643
644   while (*keyp != NULL)
645     {
646       GError* error = NULL;
647       const gchar* key = *keyp;
648       
649       old_value = gconf_engine_get_without_default(conf, key, &error);
650
651       if (error != NULL)
652         {
653           /* FIXME */
654           g_warning("error creating change set from current keys: %s", error->message);
655           g_error_free(error);
656           error = NULL;
657         }
658       
659       if (old_value == NULL)
660         gconf_change_set_unset(new_set, key);
661       else
662         gconf_change_set_set_nocopy(new_set, key, old_value);
663
664       ++keyp;
665     }
666
667   return new_set;
668 }
669
670 GConfChangeSet*
671 gconf_engine_change_set_from_current (GConfEngine* conf,
672                                       GError** err,
673                                       const gchar* first_key,
674                                       ...)
675 {
676   GSList* keys = NULL;
677   va_list args;
678   const gchar* arg;
679   const gchar** vec;
680   GConfChangeSet* retval;
681   GSList* tmp;
682   guint i;
683   
684   g_return_val_if_fail(err == NULL || *err == NULL, NULL);
685
686   va_start (args, first_key);
687
688   arg = first_key;
689
690   while (arg != NULL)
691     {
692       keys = g_slist_prepend(keys, (/*not-const*/gchar*)arg);
693
694       arg = va_arg (args, const gchar*);
695     }
696   
697   va_end (args);
698
699   vec = g_new0(const gchar*, g_slist_length(keys) + 1);
700
701   i = 0;
702   tmp = keys;
703
704   while (tmp != NULL)
705     {
706       vec[i] = tmp->data;
707       
708       ++i;
709       tmp = g_slist_next(tmp);
710     }
711
712   g_slist_free(keys);
713   
714   retval = gconf_engine_change_set_from_currentv(conf, vec, err);
715   
716   g_free(vec);
717
718   return retval;
719 }