2 * Copyright (C) 2002-2004 Marco Pesenti Gritti
3 * Copyright (C) 2004 Christian Persch
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, or (at your option)
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.
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 #include "egg-toolbars-model.h"
25 #include "eggtypebuiltins.h"
26 #include "eggmarshalers.h"
30 #include <libxml/tree.h>
31 #include <gdk/gdkproperty.h>
33 static void egg_toolbars_model_finalize (GObject *object);
48 EggTbModelFlags flags;
56 static guint signals[LAST_SIGNAL] = { 0 };
58 #define EGG_TOOLBARS_MODEL_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EGG_TYPE_TOOLBARS_MODEL, EggToolbarsModelPrivate))
60 struct EggToolbarsModelPrivate
67 G_DEFINE_TYPE (EggToolbarsModel, egg_toolbars_model, G_TYPE_OBJECT)
70 egg_toolbars_model_to_xml (EggToolbarsModel *model)
76 g_return_val_if_fail (EGG_IS_TOOLBARS_MODEL (model), NULL);
78 tl = model->priv->toolbars;
80 xmlIndentTreeOutput = TRUE;
81 doc = xmlNewDoc ((const xmlChar*) "1.0");
82 doc->children = xmlNewDocNode (doc, NULL, (const xmlChar*) "toolbars", NULL);
84 for (l1 = tl->children; l1 != NULL; l1 = l1->next)
87 EggToolbarsToolbar *toolbar = l1->data;
89 tnode = xmlNewChild (doc->children, NULL, (const xmlChar*) "toolbar", NULL);
90 xmlSetProp (tnode, (const xmlChar*) "name", (const xmlChar*) toolbar->name);
91 xmlSetProp (tnode, (const xmlChar*) "hidden",
92 (toolbar->flags&EGG_TB_MODEL_HIDDEN) ? (const xmlChar*) "true" : (const xmlChar*) "false");
93 xmlSetProp (tnode, (const xmlChar*) "editable",
94 (toolbar->flags&EGG_TB_MODEL_NOT_EDITABLE) ? (const xmlChar*) "false" : (const xmlChar*) "true");
96 for (l2 = l1->children; l2 != NULL; l2 = l2->next)
99 EggToolbarsItem *item = l2->data;
101 if (strcmp (item->name, "_separator") == 0)
103 node = xmlNewChild (tnode, NULL, (const xmlChar*) "separator", NULL);
107 node = xmlNewChild (tnode, NULL, (const xmlChar*) "toolitem", NULL);
108 xmlSetProp (node, (const xmlChar*) "name", (const xmlChar*) item->name);
110 /* Add 'data' nodes for each data type which can be written out for this
111 * item. Only write types which can be used to restore the data. */
112 for (l3 = model->priv->types; l3 != NULL; l3 = l3->next)
114 EggToolbarsItemType *type = l3->data;
115 if (type->get_name != NULL && type->get_data != NULL)
120 tmp = type->get_data (type, item->name);
123 dnode = xmlNewTextChild (node, NULL, (const xmlChar*) "data", (const xmlChar*) tmp);
126 tmp = gdk_atom_name (type->type);
127 xmlSetProp (dnode, (const xmlChar*) "type", (const xmlChar*) tmp);
139 safe_save_xml (const char *xml_file, xmlDocPtr doc)
144 gboolean retval = TRUE;
146 tmp_file = g_strconcat (xml_file, ".tmp", NULL);
147 old_file = g_strconcat (xml_file, ".old", NULL);
149 if (xmlSaveFormatFile (tmp_file, doc, 1) <= 0)
151 g_warning ("Failed to write XML data to %s", tmp_file);
155 old_exist = g_file_test (xml_file, G_FILE_TEST_EXISTS);
159 if (rename (xml_file, old_file) < 0)
161 g_warning ("Failed to rename %s to %s", xml_file, old_file);
167 if (rename (tmp_file, xml_file) < 0)
169 g_warning ("Failed to rename %s to %s", tmp_file, xml_file);
171 if (rename (old_file, xml_file) < 0)
173 g_warning ("Failed to restore %s from %s", xml_file, tmp_file);
181 if (unlink (old_file) < 0)
183 g_warning ("Failed to delete old file %s", old_file);
195 egg_toolbars_model_save_toolbars (EggToolbarsModel *model,
196 const char *xml_file,
202 g_return_if_fail (EGG_IS_TOOLBARS_MODEL (model));
204 doc = egg_toolbars_model_to_xml (model);
205 root = xmlDocGetRootElement (doc);
206 xmlSetProp (root, (const xmlChar*) "version", (const xmlChar*) version);
207 safe_save_xml (xml_file, doc);
212 is_unique (EggToolbarsModel *model,
213 EggToolbarsItem *idata)
215 EggToolbarsItem *idata2;
216 GNode *toolbar, *item;
219 for(toolbar = g_node_first_child (model->priv->toolbars);
220 toolbar != NULL; toolbar = g_node_next_sibling (toolbar))
222 for(item = g_node_first_child (toolbar);
223 item != NULL; item = g_node_next_sibling (item))
227 if (idata != idata2 && strcmp (idata->name, idata2->name) == 0)
238 toolbar_node_new (const char *name)
240 EggToolbarsToolbar *toolbar;
242 toolbar = g_new (EggToolbarsToolbar, 1);
243 toolbar->name = g_strdup (name);
246 return g_node_new (toolbar);
250 item_node_new (const char *name, EggToolbarsModel *model)
252 EggToolbarsItem *item;
255 g_return_val_if_fail (name != NULL, NULL);
257 item = g_new (EggToolbarsItem, 1);
258 item->name = g_strdup (name);
260 flags = GPOINTER_TO_INT (g_hash_table_lookup (model->priv->flags, item->name));
261 if ((flags & EGG_TB_MODEL_NAME_INFINITE) == 0)
262 g_hash_table_insert (model->priv->flags,
263 g_strdup (item->name),
264 GINT_TO_POINTER (flags | EGG_TB_MODEL_NAME_USED));
266 return g_node_new (item);
270 item_node_free (GNode *item_node, EggToolbarsModel *model)
272 EggToolbarsItem *item = item_node->data;
275 flags = GPOINTER_TO_INT (g_hash_table_lookup (model->priv->flags, item->name));
276 if ((flags & EGG_TB_MODEL_NAME_INFINITE) == 0 && is_unique (model, item))
277 g_hash_table_insert (model->priv->flags,
278 g_strdup (item->name),
279 GINT_TO_POINTER (flags & ~EGG_TB_MODEL_NAME_USED));
284 g_node_destroy (item_node);
288 toolbar_node_free (GNode *toolbar_node, EggToolbarsModel *model)
290 EggToolbarsToolbar *toolbar = toolbar_node->data;
292 g_node_children_foreach (toolbar_node, G_TRAVERSE_ALL,
293 (GNodeForeachFunc) item_node_free, model);
295 g_free (toolbar->name);
298 g_node_destroy (toolbar_node);
302 egg_toolbars_model_get_flags (EggToolbarsModel *model,
303 int toolbar_position)
306 EggToolbarsToolbar *toolbar;
308 toolbar_node = g_node_nth_child (model->priv->toolbars, toolbar_position);
309 g_return_val_if_fail (toolbar_node != NULL, 0);
311 toolbar = toolbar_node->data;
313 return toolbar->flags;
317 egg_toolbars_model_set_flags (EggToolbarsModel *model,
318 int toolbar_position,
319 EggTbModelFlags flags)
322 EggToolbarsToolbar *toolbar;
324 toolbar_node = g_node_nth_child (model->priv->toolbars, toolbar_position);
325 g_return_if_fail (toolbar_node != NULL);
327 toolbar = toolbar_node->data;
329 toolbar->flags = flags;
331 g_signal_emit (G_OBJECT (model), signals[TOOLBAR_CHANGED],
332 0, toolbar_position);
337 egg_toolbars_model_get_data (EggToolbarsModel *model,
341 EggToolbarsItemType *t;
345 if (type == GDK_NONE || type == gdk_atom_intern (EGG_TOOLBAR_ITEM_TYPE, FALSE))
347 g_return_val_if_fail (name != NULL, NULL);
348 g_return_val_if_fail (*name != 0, NULL);
349 return strdup (name);
352 for (l = model->priv->types; l != NULL; l = l->next)
355 if (t->type == type && t->get_data != NULL)
357 data = t->get_data (t, name);
358 if (data != NULL) break;
366 egg_toolbars_model_get_name (EggToolbarsModel *model,
371 EggToolbarsItemType *t;
375 if (type == GDK_NONE || type == gdk_atom_intern (EGG_TOOLBAR_ITEM_TYPE, FALSE))
377 g_return_val_if_fail (data, NULL);
378 g_return_val_if_fail (*data, NULL);
379 return strdup (data);
384 for (l = model->priv->types; name == NULL && l != NULL; l = l->next)
387 if (t->type == type && t->new_name != NULL)
388 name = t->new_name (t, data);
395 for (l = model->priv->types; name == NULL && l != NULL; l = l->next)
398 if (t->type == type && t->get_name != NULL)
399 name = t->get_name (t, data);
407 impl_add_item (EggToolbarsModel *model,
408 int toolbar_position,
416 g_return_val_if_fail (EGG_IS_TOOLBARS_MODEL (model), FALSE);
417 g_return_val_if_fail (name != NULL, FALSE);
419 parent_node = g_node_nth_child (model->priv->toolbars, toolbar_position);
420 child_node = item_node_new (name, model);
421 g_node_insert (parent_node, position, child_node);
423 real_position = g_node_child_position (parent_node, child_node);
425 g_signal_emit (G_OBJECT (model), signals[ITEM_ADDED], 0,
426 toolbar_position, real_position);
432 egg_toolbars_model_add_item (EggToolbarsModel *model,
433 int toolbar_position,
437 EggToolbarsModelClass *klass = EGG_TOOLBARS_MODEL_GET_CLASS (model);
438 return klass->add_item (model, toolbar_position, position, name);
442 egg_toolbars_model_add_toolbar (EggToolbarsModel *model,
449 g_return_val_if_fail (EGG_IS_TOOLBARS_MODEL (model), -1);
451 node = toolbar_node_new (name);
452 g_node_insert (model->priv->toolbars, position, node);
454 real_position = g_node_child_position (model->priv->toolbars, node);
456 g_signal_emit (G_OBJECT (model), signals[TOOLBAR_ADDED],
459 return g_node_child_position (model->priv->toolbars, node);
463 parse_data_list (EggToolbarsModel *model,
468 while (child && name == NULL)
470 if (xmlStrEqual (child->name, (const xmlChar*) "data"))
472 xmlChar *type = xmlGetProp (child, (const xmlChar*) "type");
473 xmlChar *data = xmlNodeGetContent (child);
477 GdkAtom atom = gdk_atom_intern ((const char*) type, TRUE);
478 name = egg_toolbars_model_get_name (model, atom, (const char*) data, create);
492 parse_item_list (EggToolbarsModel *model,
498 if (xmlStrEqual (child->name, (const xmlChar*) "toolitem"))
502 /* Try to get the name using the data elements first,
503 as they are more 'portable' or 'persistent'. */
504 name = parse_data_list (model, child->children, FALSE);
507 name = parse_data_list (model, child->children, TRUE);
510 /* If that fails, try to use the name. */
513 xmlChar *type = xmlGetProp (child, (const xmlChar*) "type");
514 xmlChar *data = xmlGetProp (child, (const xmlChar*) "name");
515 GdkAtom atom = type ? gdk_atom_intern ((const char*) type, TRUE) : GDK_NONE;
517 /* If an old format, try to use it. */
518 name = egg_toolbars_model_get_name (model, atom, (const char*) data, FALSE);
521 name = egg_toolbars_model_get_name (model, atom, (const char*) data, TRUE);
530 egg_toolbars_model_add_item (model, position, -1, name);
534 else if (xmlStrEqual (child->name, (const xmlChar*) "separator"))
536 egg_toolbars_model_add_item (model, position, -1, "_separator");
544 parse_toolbars (EggToolbarsModel *model,
549 if (xmlStrEqual (child->name, (const xmlChar*) "toolbar"))
553 EggTbModelFlags flags;
555 string = xmlGetProp (child, (const xmlChar*) "name");
556 position = egg_toolbars_model_add_toolbar (model, -1, (const char*) string);
557 flags = egg_toolbars_model_get_flags (model, position);
560 string = xmlGetProp (child, (const xmlChar*) "editable");
561 if (string && xmlStrEqual (string, (const xmlChar*) "false"))
562 flags |= EGG_TB_MODEL_NOT_EDITABLE;
565 string = xmlGetProp (child, (const xmlChar*) "hidden");
566 if (string && xmlStrEqual (string, (const xmlChar*) "true"))
567 flags |= EGG_TB_MODEL_HIDDEN;
570 string = xmlGetProp (child, (const xmlChar*) "style");
571 if (string && xmlStrEqual (string, (const xmlChar*) "icons-only"))
572 flags |= EGG_TB_MODEL_ICONS;
575 egg_toolbars_model_set_flags (model, position, flags);
577 parse_item_list (model, child->children, position);
585 egg_toolbars_model_load_toolbars (EggToolbarsModel *model,
586 const char *xml_file)
591 g_return_val_if_fail (EGG_IS_TOOLBARS_MODEL (model), FALSE);
593 if (!xml_file || !g_file_test (xml_file, G_FILE_TEST_EXISTS)) return FALSE;
595 doc = xmlParseFile (xml_file);
598 g_warning ("Failed to load XML data from %s", xml_file);
601 root = xmlDocGetRootElement (doc);
603 parse_toolbars (model, root->children);
611 parse_available_list (EggToolbarsModel *model,
618 if (xmlStrEqual (child->name, (const xmlChar*) "toolitem"))
622 name = xmlGetProp (child, (const xmlChar*) "name");
623 flags = egg_toolbars_model_get_name_flags
624 (model, (const char*)name);
625 egg_toolbars_model_set_name_flags
626 (model, (const char*)name, flags | EGG_TB_MODEL_NAME_KNOWN);
634 parse_names (EggToolbarsModel *model,
639 if (xmlStrEqual (child->name, (const xmlChar*) "available"))
641 parse_available_list (model, child->children);
649 egg_toolbars_model_load_names (EggToolbarsModel *model,
650 const char *xml_file)
655 g_return_val_if_fail (EGG_IS_TOOLBARS_MODEL (model), FALSE);
657 if (!xml_file || !g_file_test (xml_file, G_FILE_TEST_EXISTS)) return FALSE;
659 doc = xmlParseFile (xml_file);
662 g_warning ("Failed to load XML data from %s", xml_file);
665 root = xmlDocGetRootElement (doc);
667 parse_names (model, root->children);
675 egg_toolbars_model_class_init (EggToolbarsModelClass *klass)
677 GObjectClass *object_class = G_OBJECT_CLASS (klass);
678 volatile GType flags_type; /* work around gcc's optimiser */
680 /* make sure the flags type is known */
681 flags_type = EGG_TYPE_TB_MODEL_FLAGS;
683 object_class->finalize = egg_toolbars_model_finalize;
685 klass->add_item = impl_add_item;
687 signals[ITEM_ADDED] =
688 g_signal_new ("item_added",
689 G_OBJECT_CLASS_TYPE (object_class),
691 G_STRUCT_OFFSET (EggToolbarsModelClass, item_added),
692 NULL, NULL, _egg_marshal_VOID__INT_INT,
693 G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
694 signals[TOOLBAR_ADDED] =
695 g_signal_new ("toolbar_added",
696 G_OBJECT_CLASS_TYPE (object_class),
698 G_STRUCT_OFFSET (EggToolbarsModelClass, toolbar_added),
699 NULL, NULL, g_cclosure_marshal_VOID__INT,
700 G_TYPE_NONE, 1, G_TYPE_INT);
701 signals[ITEM_REMOVED] =
702 g_signal_new ("item_removed",
703 G_OBJECT_CLASS_TYPE (object_class),
705 G_STRUCT_OFFSET (EggToolbarsModelClass, item_removed),
706 NULL, NULL, _egg_marshal_VOID__INT_INT,
707 G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
708 signals[TOOLBAR_REMOVED] =
709 g_signal_new ("toolbar_removed",
710 G_OBJECT_CLASS_TYPE (object_class),
712 G_STRUCT_OFFSET (EggToolbarsModelClass, toolbar_removed),
713 NULL, NULL, g_cclosure_marshal_VOID__INT,
714 G_TYPE_NONE, 1, G_TYPE_INT);
715 signals[TOOLBAR_CHANGED] =
716 g_signal_new ("toolbar_changed",
717 G_OBJECT_CLASS_TYPE (object_class),
719 G_STRUCT_OFFSET (EggToolbarsModelClass, toolbar_changed),
720 NULL, NULL, g_cclosure_marshal_VOID__INT,
721 G_TYPE_NONE, 1, G_TYPE_INT);
723 g_type_class_add_private (object_class, sizeof (EggToolbarsModelPrivate));
727 egg_toolbars_model_init (EggToolbarsModel *model)
729 model->priv =EGG_TOOLBARS_MODEL_GET_PRIVATE (model);
731 model->priv->toolbars = g_node_new (NULL);
732 model->priv->flags = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
733 egg_toolbars_model_set_name_flags (model, "_separator",
734 EGG_TB_MODEL_NAME_KNOWN |
735 EGG_TB_MODEL_NAME_INFINITE);
739 egg_toolbars_model_finalize (GObject *object)
741 EggToolbarsModel *model = EGG_TOOLBARS_MODEL (object);
743 g_node_children_foreach (model->priv->toolbars, G_TRAVERSE_ALL,
744 (GNodeForeachFunc) toolbar_node_free, model);
745 g_node_destroy (model->priv->toolbars);
746 g_hash_table_destroy (model->priv->flags);
748 G_OBJECT_CLASS (egg_toolbars_model_parent_class)->finalize (object);
752 egg_toolbars_model_new (void)
754 return EGG_TOOLBARS_MODEL (g_object_new (EGG_TYPE_TOOLBARS_MODEL, NULL));
758 egg_toolbars_model_remove_toolbar (EggToolbarsModel *model,
762 EggTbModelFlags flags;
764 g_return_if_fail (EGG_IS_TOOLBARS_MODEL (model));
766 flags = egg_toolbars_model_get_flags (model, position);
768 if (!(flags & EGG_TB_MODEL_NOT_REMOVABLE))
770 node = g_node_nth_child (model->priv->toolbars, position);
771 g_return_if_fail (node != NULL);
773 toolbar_node_free (node, model);
775 g_signal_emit (G_OBJECT (model), signals[TOOLBAR_REMOVED],
781 egg_toolbars_model_remove_item (EggToolbarsModel *model,
782 int toolbar_position,
785 GNode *node, *toolbar;
787 g_return_if_fail (EGG_IS_TOOLBARS_MODEL (model));
789 toolbar = g_node_nth_child (model->priv->toolbars, toolbar_position);
790 g_return_if_fail (toolbar != NULL);
792 node = g_node_nth_child (toolbar, position);
793 g_return_if_fail (node != NULL);
795 item_node_free (node, model);
797 g_signal_emit (G_OBJECT (model), signals[ITEM_REMOVED], 0,
798 toolbar_position, position);
802 egg_toolbars_model_move_item (EggToolbarsModel *model,
803 int toolbar_position,
805 int new_toolbar_position,
808 GNode *node, *toolbar, *new_toolbar;
810 g_return_if_fail (EGG_IS_TOOLBARS_MODEL (model));
812 toolbar = g_node_nth_child (model->priv->toolbars, toolbar_position);
813 g_return_if_fail (toolbar != NULL);
815 new_toolbar = g_node_nth_child (model->priv->toolbars, new_toolbar_position);
816 g_return_if_fail (new_toolbar != NULL);
818 node = g_node_nth_child (toolbar, position);
819 g_return_if_fail (node != NULL);
821 g_node_unlink (node);
823 g_signal_emit (G_OBJECT (model), signals[ITEM_REMOVED], 0,
824 toolbar_position, position);
826 g_node_insert (new_toolbar, new_position, node);
828 g_signal_emit (G_OBJECT (model), signals[ITEM_ADDED], 0,
829 new_toolbar_position, new_position);
833 egg_toolbars_model_delete_item (EggToolbarsModel *model,
836 EggToolbarsItem *idata;
837 EggToolbarsToolbar *tdata;
838 GNode *toolbar, *item, *next;
841 g_return_if_fail (EGG_IS_TOOLBARS_MODEL (model));
843 toolbar = g_node_first_child (model->priv->toolbars);
846 while (toolbar != NULL)
848 item = g_node_first_child (toolbar);
851 /* Don't delete toolbars that were already empty */
854 toolbar = g_node_next_sibling (toolbar);
860 next = g_node_next_sibling (item);
862 if (strcmp (idata->name, name) == 0)
864 item_node_free (item, model);
865 g_signal_emit (G_OBJECT (model),
866 signals[ITEM_REMOVED],
877 next = g_node_next_sibling (toolbar);
878 tdata = toolbar->data;
879 if (!(tdata->flags & EGG_TB_MODEL_NOT_REMOVABLE) &&
880 g_node_first_child (toolbar) == NULL)
882 toolbar_node_free (toolbar, model);
884 g_signal_emit (G_OBJECT (model),
885 signals[TOOLBAR_REMOVED],
898 egg_toolbars_model_n_items (EggToolbarsModel *model,
899 int toolbar_position)
903 toolbar = g_node_nth_child (model->priv->toolbars, toolbar_position);
904 g_return_val_if_fail (toolbar != NULL, -1);
906 return g_node_n_children (toolbar);
910 egg_toolbars_model_item_nth (EggToolbarsModel *model,
911 int toolbar_position,
916 EggToolbarsItem *idata;
918 toolbar = g_node_nth_child (model->priv->toolbars, toolbar_position);
919 g_return_val_if_fail (toolbar != NULL, NULL);
921 item = g_node_nth_child (toolbar, position);
922 g_return_val_if_fail (item != NULL, NULL);
929 egg_toolbars_model_n_toolbars (EggToolbarsModel *model)
931 return g_node_n_children (model->priv->toolbars);
935 egg_toolbars_model_toolbar_nth (EggToolbarsModel *model,
939 EggToolbarsToolbar *tdata;
941 toolbar = g_node_nth_child (model->priv->toolbars, position);
942 g_return_val_if_fail (toolbar != NULL, NULL);
944 tdata = toolbar->data;
950 egg_toolbars_model_get_types (EggToolbarsModel *model)
952 return model->priv->types;
956 egg_toolbars_model_set_types (EggToolbarsModel *model, GList *types)
958 model->priv->types = types;
962 fill_avail_array (gpointer key, gpointer value, GPtrArray *array)
964 int flags = GPOINTER_TO_INT (value);
965 if ((flags & EGG_TB_MODEL_NAME_KNOWN) && !(flags & EGG_TB_MODEL_NAME_USED))
966 g_ptr_array_add (array, key);
970 egg_toolbars_model_get_name_avail (EggToolbarsModel *model)
972 GPtrArray *array = g_ptr_array_new ();
973 g_hash_table_foreach (model->priv->flags, (GHFunc) fill_avail_array, array);
978 egg_toolbars_model_get_name_flags (EggToolbarsModel *model, const char *name)
980 return GPOINTER_TO_INT (g_hash_table_lookup (model->priv->flags, name));
984 egg_toolbars_model_set_name_flags (EggToolbarsModel *model, const char *name, gint flags)
986 g_hash_table_insert (model->priv->flags, g_strdup (name), GINT_TO_POINTER (flags));