pre3 updates
[dia.git] / lib / persistence.c
blob25468da4120ebb03cb9e8311eef4fcf2168cec71
1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 2003 Lars Clausen
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
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
12 * GNU General Public License for more details.
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 /* persistence.c -- functions that handle persistent stores, such as
20 * window positions and sizes, font menu, document history etc.
23 #include "config.h"
25 #include <stdio.h>
26 #include <string.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #ifdef HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif
33 #include "persistence.h"
34 #include "dia_dirs.h"
35 #include "dia_xml_libxml.h"
36 #include "dia_xml.h"
38 #include <gtk/gtk.h>
39 #include <libxml/tree.h>
41 /* Hash table from window role (string) to PersistentWindow structure.
43 static GHashTable *persistent_windows, *persistent_entrystrings, *persistent_lists;
44 static GHashTable *persistent_integers, *persistent_reals;
45 static GHashTable *persistent_booleans, *persistent_strings;
46 static GHashTable *persistent_colors;
48 /* *********************** LOADING FUNCTIONS *********************** */
50 typedef void (*PersistenceLoadFunc)(gchar *role, xmlNodePtr node);
52 static void
53 persistence_load_window(gchar *role, xmlNodePtr node)
55 AttributeNode attr;
56 PersistentWindow *wininfo = g_new0(PersistentWindow, 1);
58 attr = composite_find_attribute(node, "xpos");
59 if (attr != NULL)
60 wininfo->x = data_int(attribute_first_data(attr));
61 attr = composite_find_attribute(node, "ypos");
62 if (attr != NULL)
63 wininfo->y = data_int(attribute_first_data(attr));
64 attr = composite_find_attribute(node, "width");
65 if (attr != NULL)
66 wininfo->width = data_int(attribute_first_data(attr));
67 attr = composite_find_attribute(node, "height");
68 if (attr != NULL)
69 wininfo->height = data_int(attribute_first_data(attr));
70 attr = composite_find_attribute(node, "isopen");
71 if (attr != NULL)
72 wininfo->isopen = data_boolean(attribute_first_data(attr));
74 g_hash_table_insert(persistent_windows, role, wininfo);
77 /** Load a persistent string into the strings hashtable */
78 static void
79 persistence_load_entrystring(gchar *role, xmlNodePtr node)
81 AttributeNode attr;
82 gchar *string = NULL;
84 /* Find the contents? */
85 attr = composite_find_attribute(node, "stringvalue");
86 if (attr != NULL)
87 string = data_string(attribute_first_data(attr));
88 else
89 return;
91 if (string != NULL)
92 g_hash_table_insert(persistent_entrystrings, role, string);
95 static void
96 persistence_load_list(gchar *role, xmlNodePtr node)
98 AttributeNode attr;
99 gchar *string = NULL;
101 /* Find the contents? */
102 attr = composite_find_attribute(node, "listvalue");
103 if (attr != NULL)
104 string = data_string(attribute_first_data(attr));
105 else
106 return;
108 if (string != NULL) {
109 gchar **strings = g_strsplit(string, "\n", -1);
110 PersistentList *plist;
111 GList *list = NULL;
112 int i;
113 for (i = 0; strings[i] != NULL; i++) {
114 list = g_list_append(list, g_strdup(strings[i]));
116 /* This frees the strings, too? */
117 g_strfreev(strings);
118 /* yes but not the other one --hb */
119 g_free (string);
120 plist = g_new(PersistentList, 1);
121 plist->glist = list;
122 plist->role = role;
123 plist->sorted = FALSE;
124 plist->max_members = G_MAXINT;
125 g_hash_table_insert(persistent_lists, role, plist);
129 static void
130 persistence_load_integer(gchar *role, xmlNodePtr node)
132 AttributeNode attr;
133 gint *integer;
135 /* Find the contents? */
136 attr = composite_find_attribute(node, "intvalue");
137 if (attr != NULL) {
138 integer = g_new(gint, 1);
139 *integer = data_int(attribute_first_data(attr));
140 } else
141 return;
143 if (g_hash_table_lookup(persistent_integers, role) == NULL)
144 g_hash_table_insert(persistent_integers, role, integer);
145 else
146 printf("Int %s registered before loading persistence!\n", role);
149 static void
150 persistence_load_real(gchar *role, xmlNodePtr node)
152 AttributeNode attr;
153 real *realval;
155 /* Find the contents? */
156 attr = composite_find_attribute(node, "realvalue");
157 if (attr != NULL) {
158 realval = g_new(real, 1);
159 *realval = data_real(attribute_first_data(attr));
160 } else
161 return;
163 if (g_hash_table_lookup(persistent_reals, role) == NULL)
164 g_hash_table_insert(persistent_reals, role, realval);
165 else
166 printf("Real %s registered before loading persistence!\n", role);
169 static void
170 persistence_load_boolean(gchar *role, xmlNodePtr node)
172 AttributeNode attr;
173 gboolean *booleanval;
175 /* Find the contents? */
176 attr = composite_find_attribute(node, "booleanvalue");
177 if (attr != NULL) {
178 booleanval = g_new(gboolean, 1);
179 *booleanval = data_boolean(attribute_first_data(attr));
180 } else
181 return;
183 if (g_hash_table_lookup(persistent_booleans, role) == NULL)
184 g_hash_table_insert(persistent_booleans, role, booleanval);
185 else
186 printf("Boolean %s registered before loading persistence!\n", role);
189 static void
190 persistence_load_string(gchar *role, xmlNodePtr node)
192 AttributeNode attr;
193 gchar *stringval;
195 /* Find the contents? */
196 attr = composite_find_attribute(node, "stringvalue");
197 if (attr != NULL) {
198 stringval = data_string(attribute_first_data(attr));
199 } else
200 return;
202 if (g_hash_table_lookup(persistent_strings, role) == NULL)
203 g_hash_table_insert(persistent_strings, role, stringval);
204 else
205 printf("String %s registered before loading persistence!\n", role);
208 static void
209 persistence_load_color(gchar *role, xmlNodePtr node)
211 AttributeNode attr;
212 Color *colorval;
214 /* Find the contents? */
215 attr = composite_find_attribute(node, "colorvalue");
216 if (attr != NULL) {
217 colorval = g_new(Color, 1);
218 data_color(attribute_first_data(attr), colorval);
219 } else
220 return;
222 if (g_hash_table_lookup(persistent_colors, role) == NULL)
223 g_hash_table_insert(persistent_colors, role, colorval);
224 else
225 printf("Color %s registered before loading persistence!\n", role);
228 static xmlNodePtr
229 find_node_named (xmlNodePtr p, const char *name)
231 while (p && 0 != strcmp(p->name, name))
232 p = p->next;
233 return p;
236 static GHashTable *type_handlers;
238 /** Load the named type of entries using the given function.
239 * func is a void (*func)(gchar *role, xmlNodePtr *node)
241 static void
242 persistence_load_type(xmlNodePtr node)
244 const gchar *typename = node->name;
245 gchar *name;
247 PersistenceLoadFunc func =
248 (PersistenceLoadFunc)g_hash_table_lookup(type_handlers, typename);
249 if (func == NULL) {
250 return;
253 name = xmlGetProp(node, "role");
254 if (name == NULL) {
255 return;
258 (*func)(name, node);
259 node = node->next;
262 static void
263 persistence_set_type_handler(gchar *name, PersistenceLoadFunc func)
265 if (type_handlers == NULL)
266 type_handlers = g_hash_table_new(g_str_hash, g_str_equal);
268 g_hash_table_insert(type_handlers, name, (gpointer)func);
271 static void
272 persistence_init()
274 persistence_set_type_handler("window", persistence_load_window);
275 persistence_set_type_handler("entrystring", persistence_load_entrystring);
276 persistence_set_type_handler("list", persistence_load_list);
277 persistence_set_type_handler("integer", persistence_load_integer);
278 persistence_set_type_handler("real", persistence_load_real);
279 persistence_set_type_handler("boolean", persistence_load_boolean);
280 persistence_set_type_handler("string", persistence_load_string);
281 persistence_set_type_handler("color", persistence_load_color);
283 if (persistent_windows == NULL) {
284 persistent_windows = g_hash_table_new(g_str_hash, g_str_equal);
286 if (persistent_entrystrings == NULL) {
287 persistent_entrystrings = g_hash_table_new(g_str_hash, g_str_equal);
289 if (persistent_lists == NULL) {
290 persistent_lists = g_hash_table_new(g_str_hash, g_str_equal);
292 if (persistent_integers == NULL) {
293 persistent_integers = g_hash_table_new(g_str_hash, g_str_equal);
295 if (persistent_reals == NULL) {
296 persistent_reals = g_hash_table_new(g_str_hash, g_str_equal);
298 if (persistent_booleans == NULL) {
299 persistent_booleans = g_hash_table_new(g_str_hash, g_str_equal);
301 if (persistent_strings == NULL) {
302 persistent_strings = g_hash_table_new(g_str_hash, g_str_equal);
304 if (persistent_colors == NULL) {
305 persistent_colors = g_hash_table_new(g_str_hash, g_str_equal);
309 /* Load all persistent data. */
310 void
311 persistence_load()
313 xmlDocPtr doc;
314 gchar *filename = dia_config_filename("persistence");
316 persistence_init();
318 if (!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) {
319 g_free (filename);
320 return;
322 doc = xmlDiaParseFile(filename);
323 if (doc != NULL) {
324 if (doc->xmlRootNode != NULL) {
325 xmlNsPtr namespace = xmlSearchNs(doc, doc->xmlRootNode, "dia");
326 if (!strcmp (doc->xmlRootNode->name, "persistence") &&
327 namespace != NULL) {
328 xmlNodePtr child_node = doc->xmlRootNode->children;
329 for (; child_node != NULL; child_node = child_node->next) {
330 persistence_load_type(child_node);
334 xmlFreeDoc(doc);
336 g_free(filename);
339 /* *********************** SAVING FUNCTIONS *********************** */
341 /* Save the position of a window */
342 static void
343 persistence_save_window(gpointer key, gpointer value, gpointer data)
345 xmlNodePtr tree = (xmlNodePtr)data;
346 PersistentWindow *window_pos = (PersistentWindow *)value;
347 ObjectNode window;
349 window = (ObjectNode)xmlNewChild(tree, NULL, "window", NULL);
351 xmlSetProp(window, "role", (char *)key);
352 data_add_int(new_attribute(window, "xpos"), window_pos->x);
353 data_add_int(new_attribute(window, "ypos"), window_pos->y);
354 data_add_int(new_attribute(window, "width"), window_pos->width);
355 data_add_int(new_attribute(window, "height"), window_pos->height);
356 data_add_boolean(new_attribute(window, "isopen"), window_pos->isopen);
360 /* Save the contents of a string */
361 static void
362 persistence_save_list(gpointer key, gpointer value, gpointer data)
364 xmlNodePtr tree = (xmlNodePtr)data;
365 ObjectNode listnode;
366 GString *buf;
367 GList *items;
369 listnode = (ObjectNode)xmlNewChild(tree, NULL, "list", NULL);
371 xmlSetProp(listnode, "role", (char *)key);
372 /* Make a string out of the list */
373 buf = g_string_new("");
374 for (items = ((PersistentList*)value)->glist; items != NULL;
375 items = g_list_next(items)) {
376 g_string_append(buf, (gchar *)items->data);
377 if (g_list_next(items) != NULL) g_string_append(buf, "\n");
380 data_add_string(new_attribute(listnode, "listvalue"), buf->str);
381 g_string_free(buf, TRUE);
384 static void
385 persistence_save_entrystring(gpointer key, gpointer value, gpointer data)
387 xmlNodePtr tree = (xmlNodePtr)data;
388 ObjectNode stringnode;
390 stringnode = (ObjectNode)xmlNewChild(tree, NULL, "entrystring", NULL);
392 xmlSetProp(stringnode, "role", (char *)key);
393 data_add_string(new_attribute(stringnode, "stringvalue"), (char *)value);
396 static void
397 persistence_save_integer(gpointer key, gpointer value, gpointer data)
399 xmlNodePtr tree = (xmlNodePtr)data;
400 ObjectNode integernode;
402 integernode = (ObjectNode)xmlNewChild(tree, NULL, "integer", NULL);
404 xmlSetProp(integernode, "role", (char *)key);
405 data_add_int(new_attribute(integernode, "intvalue"), *(gint *)value);
408 static void
409 persistence_save_real(gpointer key, gpointer value, gpointer data)
411 xmlNodePtr tree = (xmlNodePtr)data;
412 ObjectNode realnode;
414 realnode = (ObjectNode)xmlNewChild(tree, NULL, "real", NULL);
416 xmlSetProp(realnode, "role", (char *)key);
417 data_add_real(new_attribute(realnode, "realvalue"), *(real *)value);
420 static void
421 persistence_save_boolean(gpointer key, gpointer value, gpointer data)
423 xmlNodePtr tree = (xmlNodePtr)data;
424 ObjectNode booleannode;
426 booleannode = (ObjectNode)xmlNewChild(tree, NULL, "boolean", NULL);
428 xmlSetProp(booleannode, "role", (char *)key);
429 data_add_boolean(new_attribute(booleannode, "booleanvalue"), *(gboolean *)value);
432 static void
433 persistence_save_string(gpointer key, gpointer value, gpointer data)
435 xmlNodePtr tree = (xmlNodePtr)data;
436 ObjectNode stringnode;
438 stringnode = (ObjectNode)xmlNewChild(tree, NULL, "string", NULL);
440 xmlSetProp(stringnode, "role", (char *)key);
441 data_add_string(new_attribute(stringnode, "stringvalue"), (gchar *)value);
444 static void
445 persistence_save_color(gpointer key, gpointer value, gpointer data)
447 xmlNodePtr tree = (xmlNodePtr)data;
448 ObjectNode colornode;
450 colornode = (ObjectNode)xmlNewChild(tree, NULL, "color", NULL);
452 xmlSetProp(colornode, "role", (char *)key);
453 data_add_color(new_attribute(colornode, "colorvalue"), (Color *)value);
457 static void
458 persistence_save_type(xmlDocPtr doc, GHashTable *entries, GHFunc func)
460 if (entries != NULL && g_hash_table_size(entries) != 0) {
461 g_hash_table_foreach(entries, func, doc->xmlRootNode);
465 /* Save all persistent data. */
466 void
467 persistence_save()
469 xmlDocPtr doc;
470 xmlNs *name_space;
471 gchar *filename = dia_config_filename("persistence");
473 doc = xmlNewDoc("1.0");
474 doc->encoding = xmlStrdup("UTF-8");
475 doc->xmlRootNode = xmlNewDocNode(doc, NULL, "persistence", NULL);
477 name_space = xmlNewNs(doc->xmlRootNode,
478 DIA_XML_NAME_SPACE_BASE,
479 "dia");
480 xmlSetNs(doc->xmlRootNode, name_space);
482 persistence_save_type(doc, persistent_windows, persistence_save_window);
483 persistence_save_type(doc, persistent_entrystrings, persistence_save_string);
484 persistence_save_type(doc, persistent_lists, persistence_save_list);
485 persistence_save_type(doc, persistent_integers, persistence_save_integer);
486 persistence_save_type(doc, persistent_reals, persistence_save_real);
487 persistence_save_type(doc, persistent_booleans, persistence_save_boolean);
488 persistence_save_type(doc, persistent_strings, persistence_save_string);
489 persistence_save_type(doc, persistent_colors, persistence_save_color);
491 xmlDiaSaveFile(filename, doc);
492 g_free(filename);
493 xmlFreeDoc(doc);
496 /* *********************** USAGE FUNCTIONS *********************** */
498 /* ********* WINDOWS ********* */
500 /* Returns the name used for a window in persistence.
502 static const gchar *
503 persistence_get_window_name(GtkWindow *window)
505 const gchar *name = gtk_window_get_role(window);
506 if (name == NULL) {
507 printf("Internal: Window %s has no role.\n", gtk_window_get_title(window));
508 return NULL;
510 return name;
513 static void
514 persistence_store_window_info(GtkWindow *window, PersistentWindow *wininfo,
515 gboolean isclosed)
517 /* Drawable means visible & mapped, what we usually think of as open. */
518 if (!isclosed) {
519 gtk_window_get_position(window, &wininfo->x, &wininfo->y);
520 gtk_window_get_size(window, &wininfo->width, &wininfo->height);
521 wininfo->isopen = TRUE;
522 } else {
523 wininfo->isopen = FALSE;
527 static gboolean
528 persistence_update_window(GtkWindow *window, GdkEvent *event, gpointer data)
530 const gchar *name = persistence_get_window_name(window);
531 PersistentWindow *wininfo;
532 gboolean isclosed;
534 if (name == NULL) return FALSE;
535 if (persistent_windows == NULL) {
536 persistent_windows = g_hash_table_new(g_str_hash, g_str_equal);
538 wininfo = (PersistentWindow *)g_hash_table_lookup(persistent_windows, name);
540 /* Can't tell the window state from the window itself yet. */
541 isclosed = (event->type == GDK_UNMAP);
542 if (wininfo != NULL) {
543 persistence_store_window_info(window, wininfo, isclosed);
544 } else {
545 wininfo = g_new0(PersistentWindow, 1);
546 persistence_store_window_info(window, wininfo, isclosed);
547 g_hash_table_insert(persistent_windows, name, wininfo);
549 if (wininfo->window != NULL && wininfo->window != window) {
550 g_object_unref(wininfo->window);
551 wininfo->window = NULL;
553 if (wininfo->window == NULL) {
554 wininfo->window = window;
555 g_object_ref(window);
557 return FALSE;
560 /* Call this function after the window has a role assigned to use any
561 * persistence information about the window.
563 void
564 persistence_register_window(GtkWindow *window)
566 const gchar *name = persistence_get_window_name(window);
567 PersistentWindow *wininfo;
569 if (name == NULL) return;
570 if (persistent_windows == NULL) {
571 persistent_windows = g_hash_table_new(g_str_hash, g_str_equal);
573 wininfo = (PersistentWindow *)g_hash_table_lookup(persistent_windows, name);
574 if (wininfo != NULL) {
575 gtk_window_move(window, wininfo->x, wininfo->y);
576 gtk_window_resize(window, wininfo->width, wininfo->height);
577 if (wininfo->isopen) gtk_widget_show(GTK_WIDGET(window));
578 } else {
579 wininfo = g_new0(PersistentWindow, 1);
580 gtk_window_get_position(window, &wininfo->x, &wininfo->y);
581 gtk_window_get_size(window, &wininfo->width, &wininfo->height);
582 /* Drawable means visible & mapped, what we usually think of as open. */
583 wininfo->isopen = GTK_WIDGET_DRAWABLE(GTK_WIDGET(window));
584 g_hash_table_insert(persistent_windows, name, wininfo);
586 if (wininfo->window != NULL && wininfo->window != window) {
587 g_object_unref(wininfo->window);
588 wininfo->window = NULL;
590 if (wininfo->window == NULL) {
591 wininfo->window = window;
592 g_object_ref(window);
595 g_signal_connect(GTK_OBJECT(window), "configure-event",
596 G_CALLBACK(persistence_update_window), NULL);
598 g_signal_connect(GTK_OBJECT(window), "unmap-event",
599 G_CALLBACK(persistence_update_window), NULL);
602 /** Call this function at start-up to have a window creation function
603 * called if the window should be opened at startup.
604 * If no persistence information is available for the given role,
605 * nothing happens.
606 * @arg role The role of the window, as will be set by gtk_window_set_role()
607 * @arg createfunc A 0-argument function that creates the window. This
608 * function will be called if the persistence information indicates that the
609 * window should be open. The function should create and show the window.
611 void
612 persistence_register_window_create(gchar *role, NullaryFunc *func)
614 PersistentWindow *wininfo;
616 if (role == NULL) return;
617 if (persistent_windows == NULL) return;
618 wininfo = (PersistentWindow *)g_hash_table_lookup(persistent_windows, role);
619 if (wininfo != NULL) {
620 (*func)();
625 /* ********* STRING ENTRIES ********** */
627 static gboolean
628 persistence_update_string_entry(GtkWidget *widget, GdkEvent *event,
629 gpointer userdata)
631 gchar *role = (gchar*)userdata;
633 if (event->type == GDK_FOCUS_CHANGE) {
634 gchar *string = (gchar *)g_hash_table_lookup(persistent_entrystrings, role);
635 const gchar *entrystring = gtk_entry_get_text(GTK_ENTRY(widget));
636 if (string == NULL || strcmp(string, entrystring)) {
637 g_hash_table_insert(persistent_entrystrings, role, g_strdup(entrystring));
638 if (string != NULL) g_free(string);
642 return FALSE;
645 /** Change the contents of the persistently stored string entry.
646 * If widget is non-null, it is updated to reflect the change.
647 * This can be used e.g. for when a dialog is cancelled and the old
648 * contents should be restored.
650 gboolean
651 persistence_change_string_entry(gchar *role, gchar *string,
652 GtkWidget *widget)
654 gchar *old_string = (gchar*)g_hash_table_lookup(persistent_entrystrings, role);
655 if (old_string != NULL) {
656 if (widget != NULL) {
657 gtk_entry_set_text(GTK_ENTRY(widget), string);
659 g_hash_table_insert(persistent_entrystrings, role, g_strdup(string));
660 g_free(old_string);
663 return FALSE;
666 /** Register a string in a GtkEntry for persistence.
667 * This should include not only a unique name, but some way to update
668 * whereever the string is used.
670 void
671 persistence_register_string_entry(gchar *role, GtkWidget *entry)
673 gchar *string;
674 if (role == NULL) return;
675 if (persistent_entrystrings == NULL) {
676 persistent_entrystrings = g_hash_table_new(g_str_hash, g_str_equal);
678 string = (gchar *)g_hash_table_lookup(persistent_entrystrings, role);
679 if (string != NULL) {
680 gtk_entry_set_text(GTK_ENTRY(entry), string);
681 } else {
682 string = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
683 g_hash_table_insert(persistent_entrystrings, role, string);
685 g_signal_connect(G_OBJECT(entry), "event",
686 G_CALLBACK(persistence_update_string_entry), role);
689 /* ********* LISTS ********** */
691 /* Lists are used for e.g. recent files, selected fonts, etc.
692 * Anywhere where the user occasionally picks from a long list and
693 * is likely to reuse the items.
696 PersistentList *
697 persistence_register_list(const gchar *role)
699 PersistentList *list;
700 if (role == NULL) return NULL;
701 if (persistent_lists == NULL) {
702 persistent_lists = g_hash_table_new(g_str_hash, g_str_equal);
703 } else {
704 list = (PersistentList *)g_hash_table_lookup(persistent_lists, role);
705 if (list != NULL) {
706 return list;
709 list = g_new(PersistentList, 1);
710 list->role = role;
711 list->glist = NULL;
712 list->sorted = FALSE;
713 list->max_members = G_MAXINT;
714 g_hash_table_insert(persistent_lists, role, list);
715 return list;
718 PersistentList *
719 persistent_list_get(const gchar *role)
721 PersistentList *list;
722 if (role == NULL) return NULL;
723 if (persistent_lists != NULL) {
724 list = (PersistentList *)g_hash_table_lookup(persistent_lists, role);
725 if (list != NULL) {
726 return list;
729 /* Not registered! */
730 return NULL;
733 GList *
734 persistent_list_get_glist(const gchar *role)
736 PersistentList *plist = persistent_list_get(role);
737 if (plist == NULL) return NULL;
738 return plist->glist;
741 static GList *
742 persistent_list_cut_length(GList *list, gint length)
744 while (g_list_length(list) > length) {
745 GList *last = g_list_last(list);
746 /* Leaking data? See not in persistent_list_add */
747 list = g_list_remove_link(list, last);
748 g_list_free(last);
750 return list;
753 /** Add a new entry to this persistent list.
754 * @param role The name of a persistent list.
755 * @param item An entry to add.
756 * @returns FALSE if the entry already existed in the list, TRUE otherwise.
758 gboolean
759 persistent_list_add(const gchar *role, const gchar *item)
761 PersistentList *plist = persistent_list_get(role);
762 if(plist == NULL) {
763 printf("Can't find list for %s when adding %s\n",
764 role, item);
765 return TRUE;
767 if (plist->sorted) {
768 /* Sorting not implemented yet. */
769 return TRUE;
770 } else {
771 gboolean existed = FALSE;
772 GList *tmplist = plist->glist;
773 GList *old_elem = g_list_find_custom(tmplist, item, g_strcasecmp);
774 while (old_elem != NULL) {
775 tmplist = g_list_remove_link(tmplist, old_elem);
776 /* Don't free this, as it makes recent_files go boom after
777 * selecting a file there several times. Yes, it should be strdup'd,
778 * but it isn't.
780 /*g_free(old_elem->data);*/
781 g_list_free_1(old_elem);
782 old_elem = g_list_find_custom(tmplist, item, g_strcasecmp);
783 existed = TRUE;
785 tmplist = g_list_prepend(tmplist, g_strdup(item));
786 tmplist = persistent_list_cut_length(tmplist, plist->max_members);
787 plist->glist = tmplist;
788 return existed;
792 void
793 persistent_list_set_max_length(const gchar *role, gint max)
795 PersistentList *plist = persistent_list_get(role);
796 plist->max_members = max;
797 plist->glist = persistent_list_cut_length(plist->glist, max);
800 /** Remove an item from the persistent list.
801 * @param role The name of the persistent list.
802 * @param role The entry to remove.
803 * @returns TRUE if the item existed in the list, FALSE otherwise.
805 gboolean
806 persistent_list_remove(const gchar *role, const gchar *item)
808 PersistentList *plist = persistent_list_get(role);
809 /* Leaking data? See not in persistent_list_add */
810 GList *entry = g_list_find_custom(plist->glist, item, g_strcasecmp);
811 if (entry != NULL) {
812 plist->glist = g_list_remove_link(plist->glist, entry);
813 g_free(entry->data);
814 return TRUE;
815 } else return FALSE;
818 void
819 persistent_list_remove_all(const gchar *role)
821 PersistentList *plist = persistent_list_get(role);
822 persistent_list_cut_length(plist->glist, 0);
823 plist->glist = NULL;
826 typedef struct {
827 PersistenceCallback func;
828 GObject *watch;
829 gpointer userdata;
830 } ListenerData;
832 /** Add a listener to updates on the list, so that if another
833 * instance changes the list, menus and such can be updated.
834 * @param role The name of the persistent list to watch.
835 * @param func A function to call when the list is updated, takes
836 * the given userdata.
837 * @param userdata Data passed back into the callback function.
839 void
840 persistent_list_add_listener(const gchar *role, PersistenceCallback func,
841 GObject *watch, gpointer userdata)
843 PersistentList *plist = persistent_list_get(role);
844 ListenerData *listener;
846 if (plist != NULL) {
847 listener = g_new(ListenerData, 1);
848 listener->func = func;
849 listener->watch = watch;
850 g_object_add_weak_pointer(watch, &listener->watch);
851 listener->userdata = userdata;
852 plist->listeners = g_list_append(plist->listeners, listener);
856 /** When changing the list, call the listeners.
858 static void
859 persistent_list_invoke_listeners(gchar *role)
861 GList *tmp;
862 PersistentList *plist = persistent_list_get(role);
863 if (plist != NULL) {
864 for (tmp = plist->listeners; tmp != NULL; tmp = g_list_next(tmp)) {
865 ListenerData *listener = (ListenerData*)tmp->data;
866 if (listener->watch == NULL) {
867 /* Listener died */
868 plist->listeners = g_list_remove_link(plist->listeners, listener);
869 g_free(listener);
870 } else {
871 /* Still listening */
872 (listener->func)(listener->watch, listener->userdata);
878 /* ********* INTEGERS ********** */
879 gint
880 persistence_register_integer(gchar *role, int defaultvalue)
882 gint *integer;
883 if (role == NULL) return 0;
884 if (persistent_integers == NULL) {
885 persistent_integers = g_hash_table_new(g_str_hash, g_str_equal);
887 integer = (gint *)g_hash_table_lookup(persistent_integers, role);
888 if (integer == NULL) {
889 integer = g_new(gint, 1);
890 *integer = defaultvalue;
891 g_hash_table_insert(persistent_integers, role, integer);
893 return *integer;
896 gint
897 persistence_get_integer(gchar *role)
899 gint *integer;
900 if (persistent_integers == NULL) {
901 printf("No persistent integers to get for %s!\n", role);
902 return 0;
904 integer = (gint *)g_hash_table_lookup(persistent_integers, role);
905 if (integer != NULL) return *integer;
906 printf("No integer to get for %s\n", role);
907 return 0;
910 void
911 persistence_set_integer(gchar *role, gint newvalue)
913 gint *integer;
914 if (persistent_integers == NULL) {
915 printf("No persistent integers yet for %s!\n", role);
916 return;
918 integer = (gint *)g_hash_table_lookup(persistent_integers, role);
919 if (integer != NULL) *integer = newvalue;
920 else printf("No integer to set for %s\n", role);
923 /* ********* REALS ********** */
924 real
925 persistence_register_real(gchar *role, real defaultvalue)
927 real *realval;
928 if (role == NULL) return 0;
929 if (persistent_reals == NULL) {
930 persistent_reals = g_hash_table_new(g_str_hash, g_str_equal);
932 realval = (real *)g_hash_table_lookup(persistent_reals, role);
933 if (realval == NULL) {
934 realval = g_new(real, 1);
935 *realval = defaultvalue;
936 g_hash_table_insert(persistent_reals, role, realval);
938 return *realval;
941 real
942 persistence_get_real(gchar *role)
944 real *realval;
945 if (persistent_reals == NULL) {
946 printf("No persistent reals to get for %s!\n", role);
947 return 0;
949 realval = (real *)g_hash_table_lookup(persistent_reals, role);
950 if (realval != NULL) return *realval;
951 printf("No real to get for %s\n", role);
952 return 0.0;
955 void
956 persistence_set_real(gchar *role, real newvalue)
958 real *realval;
959 if (persistent_reals == NULL) {
960 printf("No persistent reals yet for %s!\n", role);
961 return;
963 realval = (real *)g_hash_table_lookup(persistent_reals, role);
964 if (realval != NULL) *realval = newvalue;
965 else printf("No real to set for %s\n", role);
969 /* ********* BOOLEANS ********** */
970 gboolean
971 persistence_register_boolean(gchar *role, gboolean defaultvalue)
973 gboolean *booleanval;
974 if (role == NULL) return 0;
975 if (persistent_booleans == NULL) {
976 persistent_booleans = g_hash_table_new(g_str_hash, g_str_equal);
978 booleanval = (gboolean *)g_hash_table_lookup(persistent_booleans, role);
979 if (booleanval == NULL) {
980 booleanval = g_new(gboolean, 1);
981 *booleanval = defaultvalue;
982 g_hash_table_insert(persistent_booleans, role, booleanval);
984 return *booleanval;
987 gboolean
988 persistence_get_boolean(gchar *role)
990 gboolean *booleanval;
991 if (persistent_booleans == NULL) {
992 printf("No persistent booleans to get for %s!\n", role);
993 return FALSE;
995 booleanval = (gboolean *)g_hash_table_lookup(persistent_booleans, role);
996 if (booleanval != NULL) return *booleanval;
997 printf("No boolean to get for %s\n", role);
998 return FALSE;
1001 void
1002 persistence_set_boolean(gchar *role, gboolean newvalue)
1004 gboolean *booleanval;
1005 if (persistent_booleans == NULL) {
1006 printf("No persistent booleans yet for %s!\n", role);
1007 return;
1009 booleanval = (gboolean *)g_hash_table_lookup(persistent_booleans, role);
1010 if (booleanval != NULL) *booleanval = newvalue;
1011 else printf("No boolean to set for %s\n", role);
1014 /* ********* STRINGS ********** */
1015 gchar *
1016 persistence_register_string(gchar *role, gchar *defaultvalue)
1018 gchar *stringval;
1019 if (role == NULL) return 0;
1020 if (persistent_strings == NULL) {
1021 persistent_strings = g_hash_table_new(g_str_hash, g_str_equal);
1023 stringval = (gchar *)g_hash_table_lookup(persistent_strings, role);
1024 if (stringval == NULL) {
1025 stringval = g_strdup(defaultvalue);
1026 g_hash_table_insert(persistent_strings, role, stringval);
1028 return stringval;
1031 gchar *
1032 persistence_get_string(gchar *role)
1034 gchar *stringval;
1035 if (persistent_strings == NULL) {
1036 printf("No persistent strings to get for %s!\n", role);
1037 return NULL;
1039 stringval = (gchar *)g_hash_table_lookup(persistent_strings, role);
1040 if (stringval != NULL) return g_strdup(stringval);
1041 printf("No string to get for %s\n", role);
1042 return NULL;
1045 void
1046 persistence_set_string(gchar *role, gchar *newvalue)
1048 gchar *stringval;
1049 if (persistent_strings == NULL) {
1050 printf("No persistent strings yet for %s!\n", role);
1051 return;
1053 stringval = (gchar *)g_hash_table_lookup(persistent_strings, role);
1054 if (stringval != NULL) {
1055 g_hash_table_insert(persistent_strings, role, g_strdup(newvalue));
1056 g_free(stringval);
1058 else printf("No string to set for %s\n", role);
1061 /* ********* COLORS ********** */
1062 /* Remember that colors returned are private, not to be deallocated.
1063 * They will be smashed in some undefined way by persistence_set_color */
1064 Color *
1065 persistence_register_color(gchar *role, Color *defaultvalue)
1067 Color *colorval;
1068 if (role == NULL) return 0;
1069 if (persistent_colors == NULL) {
1070 persistent_colors = g_hash_table_new(g_str_hash, g_str_equal);
1072 colorval = (Color *)g_hash_table_lookup(persistent_colors, role);
1073 if (colorval == NULL) {
1074 colorval = g_new(Color, 1);
1075 *colorval = *defaultvalue;
1076 g_hash_table_insert(persistent_colors, role, colorval);
1078 return colorval;
1081 Color *
1082 persistence_get_color(gchar *role)
1084 Color *colorval;
1085 if (persistent_colors == NULL) {
1086 printf("No persistent colors to get for %s!\n", role);
1087 return 0;
1089 colorval = (Color *)g_hash_table_lookup(persistent_colors, role);
1090 if (colorval != NULL) return colorval;
1091 printf("No color to get for %s\n", role);
1092 return 0;
1095 void
1096 persistence_set_color(gchar *role, Color *newvalue)
1098 Color *colorval;
1099 if (persistent_colors == NULL) {
1100 printf("No persistent colors yet for %s!\n", role);
1101 return;
1103 colorval = (Color *)g_hash_table_lookup(persistent_colors, role);
1104 if (colorval != NULL) *colorval = *newvalue;
1105 else printf("No color to set for %s\n", role);