]> www.fi.muni.cz Git - evince.git/blob - cut-n-paste/toolbar-editor/egg-toolbars-model.c
fa19ec2a0d7077dcea869adc98d5e37223488381
[evince.git] / cut-n-paste / toolbar-editor / egg-toolbars-model.c
1 /*
2  *  Copyright (C) 2002-2004 Marco Pesenti Gritti
3  *  Copyright (C) 2004 Christian Persch
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, or (at your option)
8  *  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  *
19  *  $Id$
20  */
21
22 #include "config.h"
23
24 #include "egg-toolbars-model.h"
25 #include "eggtypebuiltins.h"
26 #include "eggmarshalers.h"
27
28 #include <unistd.h>
29 #include <string.h>
30 #include <libxml/tree.h>
31 #include <gdk/gdk.h>
32
33 static void egg_toolbars_model_finalize   (GObject               *object);
34
35 enum
36 {
37   ITEM_ADDED,
38   ITEM_REMOVED,
39   TOOLBAR_ADDED,
40   TOOLBAR_CHANGED,
41   TOOLBAR_REMOVED,
42   LAST_SIGNAL
43 };
44
45 typedef struct
46 {
47   char *name;
48   EggTbModelFlags flags;
49 } EggToolbarsToolbar;
50
51 typedef struct
52 {
53   char *name;
54 } EggToolbarsItem;
55
56 static guint signals[LAST_SIGNAL] = { 0 };
57
58 #define EGG_TOOLBARS_MODEL_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EGG_TYPE_TOOLBARS_MODEL, EggToolbarsModelPrivate))
59
60 struct EggToolbarsModelPrivate
61 {
62   GNode *toolbars;
63   GList *types;
64   GHashTable *flags;
65 };
66
67 G_DEFINE_TYPE (EggToolbarsModel, egg_toolbars_model, G_TYPE_OBJECT)
68
69 static xmlDocPtr
70 egg_toolbars_model_to_xml (EggToolbarsModel *model)
71 {
72   GNode *l1, *l2, *tl;
73   GList *l3;
74   xmlDocPtr doc;
75
76   g_return_val_if_fail (EGG_IS_TOOLBARS_MODEL (model), NULL);
77
78   tl = model->priv->toolbars;
79
80   xmlIndentTreeOutput = TRUE;
81   doc = xmlNewDoc ((const xmlChar*) "1.0");
82   doc->children = xmlNewDocNode (doc, NULL, (const xmlChar*) "toolbars", NULL);
83
84   for (l1 = tl->children; l1 != NULL; l1 = l1->next)
85     {
86       xmlNodePtr tnode;
87       EggToolbarsToolbar *toolbar = l1->data;
88
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");
95
96       for (l2 = l1->children; l2 != NULL; l2 = l2->next)
97         {
98           xmlNodePtr node;
99           EggToolbarsItem *item = l2->data;
100
101           if (strcmp (item->name, "_separator") == 0)
102             {
103               node = xmlNewChild (tnode, NULL, (const xmlChar*) "separator", NULL);
104               continue;
105             }
106           
107           node = xmlNewChild (tnode, NULL, (const xmlChar*) "toolitem", NULL);
108           xmlSetProp (node, (const xmlChar*) "name", (const xmlChar*) item->name);
109
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)
113             {
114               EggToolbarsItemType *type = l3->data;
115               if (type->get_name != NULL && type->get_data != NULL)
116                 {
117                   xmlNodePtr dnode;
118                   char *tmp;
119                   
120                   tmp = type->get_data (type, item->name);
121                   if (tmp != NULL)
122                     {
123                       dnode = xmlNewTextChild (node, NULL, (const xmlChar*) "data", (const xmlChar*) tmp);
124                       g_free (tmp);
125                       
126                       tmp = gdk_atom_name (type->type);
127                       xmlSetProp (dnode, (const xmlChar*) "type", (const xmlChar*) tmp);
128                       g_free (tmp);
129                     }
130                 }
131             }
132         }
133     }
134
135   return doc;
136 }
137
138 static gboolean
139 safe_save_xml (const char *xml_file, xmlDocPtr doc)
140 {
141         char *tmp_file;
142         char *old_file;
143         gboolean old_exist;
144         gboolean retval = TRUE;
145
146         tmp_file = g_strconcat (xml_file, ".tmp", NULL);
147         old_file = g_strconcat (xml_file, ".old", NULL);
148
149         if (xmlSaveFormatFile (tmp_file, doc, 1) <= 0)
150         {
151                 g_warning ("Failed to write XML data to %s", tmp_file);
152                 goto failed;
153         }
154
155         old_exist = g_file_test (xml_file, G_FILE_TEST_EXISTS);
156
157         if (old_exist)
158         {
159                 if (rename (xml_file, old_file) < 0)
160                 {
161                         g_warning ("Failed to rename %s to %s", xml_file, old_file);
162                         retval = FALSE;
163                         goto failed;
164                 }
165         }
166
167         if (rename (tmp_file, xml_file) < 0)
168         {
169                 g_warning ("Failed to rename %s to %s", tmp_file, xml_file);
170
171                 if (rename (old_file, xml_file) < 0)
172                 {
173                         g_warning ("Failed to restore %s from %s", xml_file, tmp_file);
174                 }
175                 retval = FALSE;
176                 goto failed;
177         }
178
179         if (old_exist)
180         {
181                 if (unlink (old_file) < 0)
182                 {
183                         g_warning ("Failed to delete old file %s", old_file);
184                 }
185         }
186
187         failed:
188         g_free (old_file);
189         g_free (tmp_file);
190
191         return retval;
192 }
193
194 void
195 egg_toolbars_model_save_toolbars (EggToolbarsModel *model,
196                                   const char *xml_file,
197                                   const char *version)
198 {
199   xmlDocPtr doc;
200   xmlNodePtr root;
201
202   g_return_if_fail (EGG_IS_TOOLBARS_MODEL (model));
203
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);
208   xmlFreeDoc (doc);
209 }
210
211 static gboolean
212 is_unique (EggToolbarsModel *model,
213            EggToolbarsItem *idata)
214 {
215   EggToolbarsItem *idata2;
216   GNode *toolbar, *item;
217   
218    
219   for(toolbar = g_node_first_child (model->priv->toolbars);
220       toolbar != NULL; toolbar = g_node_next_sibling (toolbar))
221     {
222       for(item = g_node_first_child (toolbar);
223           item != NULL; item = g_node_next_sibling (item))
224         {
225           idata2 = item->data;
226           
227           if (idata != idata2 && strcmp (idata->name, idata2->name) == 0)
228             {
229               return FALSE;
230             }
231         }
232     }
233   
234   return TRUE;
235 }
236
237 static GNode *
238 toolbar_node_new (const char *name)
239 {
240   EggToolbarsToolbar *toolbar;
241
242   toolbar = g_new (EggToolbarsToolbar, 1);
243   toolbar->name = g_strdup (name);
244   toolbar->flags = 0;
245
246   return g_node_new (toolbar);
247 }
248
249 static GNode *
250 item_node_new (const char *name, EggToolbarsModel *model)
251 {
252   EggToolbarsItem *item;
253   int flags;
254
255   g_return_val_if_fail (name != NULL, NULL);
256
257   item = g_new (EggToolbarsItem, 1);
258   item->name = g_strdup (name);
259
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));
265
266   return g_node_new (item);
267 }
268
269 static void
270 item_node_free (GNode *item_node, EggToolbarsModel *model)
271 {
272   EggToolbarsItem *item = item_node->data;
273   int flags;
274
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));
280
281   g_free (item->name);
282   g_free (item);
283
284   g_node_destroy (item_node);
285 }
286
287 static void
288 toolbar_node_free (GNode *toolbar_node, EggToolbarsModel *model)
289 {
290   EggToolbarsToolbar *toolbar = toolbar_node->data;
291
292   g_node_children_foreach (toolbar_node, G_TRAVERSE_ALL,
293                            (GNodeForeachFunc) item_node_free, model);
294     
295   g_free (toolbar->name);
296   g_free (toolbar);
297
298   g_node_destroy (toolbar_node);
299 }
300
301 EggTbModelFlags
302 egg_toolbars_model_get_flags (EggToolbarsModel *model,
303                               int               toolbar_position)
304 {
305   GNode *toolbar_node;
306   EggToolbarsToolbar *toolbar;
307
308   toolbar_node = g_node_nth_child (model->priv->toolbars, toolbar_position);
309   g_return_val_if_fail (toolbar_node != NULL, 0);
310
311   toolbar = toolbar_node->data;
312
313   return toolbar->flags;
314 }
315
316 void
317 egg_toolbars_model_set_flags (EggToolbarsModel *model,
318                               int               toolbar_position,
319                               EggTbModelFlags   flags)
320 {
321   GNode *toolbar_node;
322   EggToolbarsToolbar *toolbar;
323
324   toolbar_node = g_node_nth_child (model->priv->toolbars, toolbar_position);
325   g_return_if_fail (toolbar_node != NULL);
326
327   toolbar = toolbar_node->data;
328
329   toolbar->flags = flags;
330
331   g_signal_emit (G_OBJECT (model), signals[TOOLBAR_CHANGED],
332                  0, toolbar_position);
333 }
334
335
336 char *
337 egg_toolbars_model_get_data (EggToolbarsModel *model,
338                              GdkAtom           type,
339                              const char       *name)
340 {
341   EggToolbarsItemType *t;
342   char *data = NULL;
343   GList *l;
344   
345   if (type == GDK_NONE || type == gdk_atom_intern (EGG_TOOLBAR_ITEM_TYPE, FALSE))
346     {
347       g_return_val_if_fail (name != NULL, NULL);
348       g_return_val_if_fail (*name != 0,   NULL);
349       return strdup (name);
350     }
351   
352   for (l = model->priv->types; l != NULL; l = l->next)
353     {
354       t = l->data;
355       if (t->type == type && t->get_data != NULL)
356         {
357           data = t->get_data (t, name);
358           if (data != NULL) break;
359         }
360     }
361   
362   return data;
363 }
364
365 char *
366 egg_toolbars_model_get_name (EggToolbarsModel *model,
367                              GdkAtom           type,
368                              const char       *data,
369                              gboolean          create)
370 {
371   EggToolbarsItemType *t;
372   char *name = NULL;
373   GList *l;
374   
375   if (type == GDK_NONE || type == gdk_atom_intern (EGG_TOOLBAR_ITEM_TYPE, FALSE))
376     {
377       g_return_val_if_fail (data, NULL);
378       g_return_val_if_fail (*data, NULL);
379       return strdup (data);
380     }
381   
382   if (create)
383     {
384       for (l = model->priv->types; name == NULL && l != NULL; l = l->next)
385         {
386           t = l->data;
387           if (t->type == type && t->new_name != NULL)
388             name = t->new_name (t, data);
389         }
390       
391       return name;
392     }
393   else
394     {
395       for (l = model->priv->types; name == NULL && l != NULL; l = l->next)
396         {
397           t = l->data;
398           if (t->type == type && t->get_name != NULL)
399             name = t->get_name (t, data);
400         }
401       
402       return name;
403     }  
404 }
405
406 static gboolean
407 impl_add_item (EggToolbarsModel    *model,
408                int                  toolbar_position,
409                int                  position,
410                const char          *name)
411 {
412   GNode *parent_node;
413   GNode *child_node;
414   int real_position;
415
416   g_return_val_if_fail (EGG_IS_TOOLBARS_MODEL (model), FALSE);
417   g_return_val_if_fail (name != NULL, FALSE);
418
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);
422
423   real_position = g_node_child_position (parent_node, child_node);
424
425   g_signal_emit (G_OBJECT (model), signals[ITEM_ADDED], 0,
426                  toolbar_position, real_position);
427
428   return TRUE;
429 }
430
431 gboolean
432 egg_toolbars_model_add_item (EggToolbarsModel *model,
433                              int               toolbar_position,
434                              int               position,
435                              const char       *name)
436 {
437   EggToolbarsModelClass *klass = EGG_TOOLBARS_MODEL_GET_CLASS (model);
438   return klass->add_item (model, toolbar_position, position, name);
439 }
440
441 int
442 egg_toolbars_model_add_toolbar (EggToolbarsModel *model,
443                                 int               position,
444                                 const char       *name)
445 {
446   GNode *node;
447   int real_position;
448
449   g_return_val_if_fail (EGG_IS_TOOLBARS_MODEL (model), -1);
450
451   node = toolbar_node_new (name);
452   g_node_insert (model->priv->toolbars, position, node);
453
454   real_position = g_node_child_position (model->priv->toolbars, node);
455
456   g_signal_emit (G_OBJECT (model), signals[TOOLBAR_ADDED],
457                  0, real_position);
458
459   return g_node_child_position (model->priv->toolbars, node);
460 }
461
462 static char *
463 parse_data_list (EggToolbarsModel *model,
464                  xmlNodePtr        child,
465                  gboolean          create)
466 {
467   char *name = NULL;
468   while (child && name == NULL)
469     {
470       if (xmlStrEqual (child->name, (const xmlChar*) "data"))
471         {
472           xmlChar *type = xmlGetProp (child, (const xmlChar*) "type");
473           xmlChar *data = xmlNodeGetContent (child);
474   
475           if (type != NULL)
476             {
477               GdkAtom atom = gdk_atom_intern ((const char*) type, TRUE);
478               name = egg_toolbars_model_get_name (model, atom, (const char*) data, create);
479             }
480           
481           xmlFree (type);
482           xmlFree (data);
483         }
484       
485       child = child->next;
486     }
487   
488   return name;
489 }
490
491 static void
492 parse_item_list (EggToolbarsModel *model,
493                  xmlNodePtr        child,
494                  int               position)
495 {
496   while (child)
497     {
498       if (xmlStrEqual (child->name, (const xmlChar*) "toolitem"))
499         {
500           char *name;
501
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);
505           if (name == NULL)
506             {
507               name = parse_data_list (model, child->children, TRUE);
508             }
509           
510           /* If that fails, try to use the name. */
511           if (name == NULL)
512             {
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;
516               
517               /* If an old format, try to use it. */
518               name = egg_toolbars_model_get_name (model, atom, (const char*) data, FALSE);
519               if (name == NULL)
520                 {
521                   name = egg_toolbars_model_get_name (model, atom, (const char*) data, TRUE);
522                 }
523               
524               xmlFree (type);
525               xmlFree (data);
526             }
527           
528           if (name != NULL)
529             {
530               egg_toolbars_model_add_item (model, position, -1, name);
531               g_free (name);
532             }
533         }
534       else if (xmlStrEqual (child->name, (const xmlChar*) "separator"))
535         {
536           egg_toolbars_model_add_item (model, position, -1, "_separator");
537         }
538
539       child = child->next;
540     }
541 }
542
543 static void
544 parse_toolbars (EggToolbarsModel *model,
545                 xmlNodePtr        child)
546 {
547   while (child)
548     {
549       if (xmlStrEqual (child->name, (const xmlChar*) "toolbar"))
550         {
551           xmlChar *string;
552           int position;
553           EggTbModelFlags flags;
554
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);
558           xmlFree (string);
559
560           string = xmlGetProp (child, (const xmlChar*) "editable");
561           if (string && xmlStrEqual (string, (const xmlChar*) "false"))
562             flags |= EGG_TB_MODEL_NOT_EDITABLE;
563           xmlFree (string);
564
565           string = xmlGetProp (child, (const xmlChar*) "hidden");
566           if (string && xmlStrEqual (string, (const xmlChar*) "true"))
567             flags |= EGG_TB_MODEL_HIDDEN;
568           xmlFree (string);
569
570           string = xmlGetProp (child, (const xmlChar*) "style");
571           if (string && xmlStrEqual (string, (const xmlChar*) "icons-only"))
572             flags |= EGG_TB_MODEL_ICONS;
573           xmlFree (string);
574
575           egg_toolbars_model_set_flags (model, position, flags);
576           
577           parse_item_list (model, child->children, position);
578         }
579
580       child = child->next;
581     }
582 }
583
584 gboolean
585 egg_toolbars_model_load_toolbars (EggToolbarsModel *model,
586                                   const char *xml_file)
587 {
588   xmlDocPtr doc;
589   xmlNodePtr root;
590
591   g_return_val_if_fail (EGG_IS_TOOLBARS_MODEL (model), FALSE);
592
593   if (!xml_file || !g_file_test (xml_file, G_FILE_TEST_EXISTS)) return FALSE;
594
595   doc = xmlParseFile (xml_file);
596   if (doc == NULL)
597   {
598     g_warning ("Failed to load XML data from %s", xml_file);
599     return FALSE;
600   }
601   root = xmlDocGetRootElement (doc);
602
603   parse_toolbars (model, root->children);
604
605   xmlFreeDoc (doc);
606
607   return TRUE;
608 }
609
610 static void
611 parse_available_list (EggToolbarsModel *model,
612                       xmlNodePtr        child)
613 {
614   gint flags;
615   
616   while (child)
617     {
618       if (xmlStrEqual (child->name, (const xmlChar*) "toolitem"))
619         {
620           xmlChar *name;
621
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);
627           xmlFree (name);
628         }
629       child = child->next;
630     }
631 }
632
633 static void
634 parse_names (EggToolbarsModel *model,
635              xmlNodePtr        child)
636 {
637   while (child)
638     {
639       if (xmlStrEqual (child->name, (const xmlChar*) "available"))
640         {
641           parse_available_list (model, child->children);
642         }
643
644       child = child->next;
645     }
646 }
647
648 gboolean
649 egg_toolbars_model_load_names (EggToolbarsModel *model,
650                                const char *xml_file)
651 {
652   xmlDocPtr doc;
653   xmlNodePtr root;
654
655   g_return_val_if_fail (EGG_IS_TOOLBARS_MODEL (model), FALSE);
656
657   if (!xml_file || !g_file_test (xml_file, G_FILE_TEST_EXISTS)) return FALSE;
658
659   doc = xmlParseFile (xml_file);
660   if (doc == NULL)
661   {
662     g_warning ("Failed to load XML data from %s", xml_file);
663     return FALSE;
664   }
665   root = xmlDocGetRootElement (doc);
666
667   parse_names (model, root->children);
668
669   xmlFreeDoc (doc);
670
671   return TRUE;
672 }
673
674 static void
675 egg_toolbars_model_class_init (EggToolbarsModelClass *klass)
676 {
677   GObjectClass *object_class = G_OBJECT_CLASS (klass);
678   volatile GType flags_type; /* work around gcc's optimiser */
679
680   /* make sure the flags type is known */
681   flags_type = EGG_TYPE_TB_MODEL_FLAGS;
682
683   object_class->finalize = egg_toolbars_model_finalize;
684
685   klass->add_item = impl_add_item;
686
687   signals[ITEM_ADDED] =
688     g_signal_new ("item_added",
689                   G_OBJECT_CLASS_TYPE (object_class),
690                   G_SIGNAL_RUN_LAST,
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),
697                   G_SIGNAL_RUN_LAST,
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),
704                   G_SIGNAL_RUN_LAST,
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),
711                   G_SIGNAL_RUN_LAST,
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),
718                   G_SIGNAL_RUN_LAST,
719                   G_STRUCT_OFFSET (EggToolbarsModelClass, toolbar_changed),
720                   NULL, NULL, g_cclosure_marshal_VOID__INT,
721                   G_TYPE_NONE, 1, G_TYPE_INT);
722
723   g_type_class_add_private (object_class, sizeof (EggToolbarsModelPrivate));
724 }
725
726 static void
727 egg_toolbars_model_init (EggToolbarsModel *model)
728 {
729   model->priv =EGG_TOOLBARS_MODEL_GET_PRIVATE (model);
730
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);
736 }
737
738 static void
739 egg_toolbars_model_finalize (GObject *object)
740 {
741   EggToolbarsModel *model = EGG_TOOLBARS_MODEL (object);
742
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);
747
748   G_OBJECT_CLASS (egg_toolbars_model_parent_class)->finalize (object);
749 }
750
751 EggToolbarsModel *
752 egg_toolbars_model_new (void)
753 {
754   return EGG_TOOLBARS_MODEL (g_object_new (EGG_TYPE_TOOLBARS_MODEL, NULL));
755 }
756
757 void
758 egg_toolbars_model_remove_toolbar (EggToolbarsModel   *model,
759                                    int                 position)
760 {
761   GNode *node;
762   EggTbModelFlags flags;
763
764   g_return_if_fail (EGG_IS_TOOLBARS_MODEL (model));
765
766   flags = egg_toolbars_model_get_flags (model, position);
767
768   if (!(flags & EGG_TB_MODEL_NOT_REMOVABLE))
769     {
770       node = g_node_nth_child (model->priv->toolbars, position);
771       g_return_if_fail (node != NULL);
772
773       toolbar_node_free (node, model);
774
775       g_signal_emit (G_OBJECT (model), signals[TOOLBAR_REMOVED],
776                      0, position);
777     }
778 }
779
780 void
781 egg_toolbars_model_remove_item (EggToolbarsModel *model,
782                                 int               toolbar_position,
783                                 int               position)
784 {
785   GNode *node, *toolbar;
786
787   g_return_if_fail (EGG_IS_TOOLBARS_MODEL (model));
788
789   toolbar = g_node_nth_child (model->priv->toolbars, toolbar_position);
790   g_return_if_fail (toolbar != NULL);
791
792   node = g_node_nth_child (toolbar, position);
793   g_return_if_fail (node != NULL);
794
795   item_node_free (node, model);
796
797   g_signal_emit (G_OBJECT (model), signals[ITEM_REMOVED], 0,
798                  toolbar_position, position);
799 }
800
801 void
802 egg_toolbars_model_move_item (EggToolbarsModel *model,
803                               int               toolbar_position,
804                               int               position,
805                               int               new_toolbar_position,
806                               int               new_position)
807 {
808   GNode *node, *toolbar, *new_toolbar;
809
810   g_return_if_fail (EGG_IS_TOOLBARS_MODEL (model));
811
812   toolbar = g_node_nth_child (model->priv->toolbars, toolbar_position);
813   g_return_if_fail (toolbar != NULL);
814
815   new_toolbar = g_node_nth_child (model->priv->toolbars, new_toolbar_position);
816   g_return_if_fail (new_toolbar != NULL);
817
818   node = g_node_nth_child (toolbar, position);
819   g_return_if_fail (node != NULL);
820
821   g_node_unlink (node);
822
823   g_signal_emit (G_OBJECT (model), signals[ITEM_REMOVED], 0,
824                  toolbar_position, position);
825
826   g_node_insert (new_toolbar, new_position, node);
827
828   g_signal_emit (G_OBJECT (model), signals[ITEM_ADDED], 0,
829                  new_toolbar_position, new_position);
830 }
831
832 void
833 egg_toolbars_model_delete_item (EggToolbarsModel *model,
834                                 const char       *name)
835 {
836   EggToolbarsItem *idata;
837   EggToolbarsToolbar *tdata;
838   GNode *toolbar, *item, *next;
839   int tpos, ipos;
840
841   g_return_if_fail (EGG_IS_TOOLBARS_MODEL (model));
842   
843   toolbar = g_node_first_child (model->priv->toolbars);
844   tpos = 0;
845   
846   while (toolbar != NULL)
847     {
848       item = g_node_first_child (toolbar);
849       ipos = 0;
850       
851       /* Don't delete toolbars that were already empty */
852       if (item == NULL)
853         {
854           toolbar = g_node_next_sibling (toolbar);
855           continue;
856         }
857       
858       while (item != NULL)
859         {
860           next = g_node_next_sibling (item);
861           idata = item->data;
862           if (strcmp (idata->name, name) == 0)
863             {
864               item_node_free (item, model);
865               g_signal_emit (G_OBJECT (model),
866                              signals[ITEM_REMOVED],
867                              0, tpos, ipos);
868             }
869           else
870             {
871               ipos++;
872             }
873           
874           item = next;
875         }
876       
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)
881         {
882           toolbar_node_free (toolbar, model);
883
884           g_signal_emit (G_OBJECT (model),
885                          signals[TOOLBAR_REMOVED],
886                          0, tpos);
887         }
888       else
889         {
890           tpos++;
891         }
892       
893       toolbar = next;
894     }
895 }
896
897 int
898 egg_toolbars_model_n_items (EggToolbarsModel *model,
899                             int               toolbar_position)
900 {
901   GNode *toolbar;
902
903   toolbar = g_node_nth_child (model->priv->toolbars, toolbar_position);
904   g_return_val_if_fail (toolbar != NULL, -1);
905
906   return g_node_n_children (toolbar);
907 }
908
909 const char *
910 egg_toolbars_model_item_nth (EggToolbarsModel *model,
911                              int               toolbar_position,
912                              int               position)
913 {
914   GNode *toolbar;
915   GNode *item;
916   EggToolbarsItem *idata;
917
918   toolbar = g_node_nth_child (model->priv->toolbars, toolbar_position);
919   g_return_val_if_fail (toolbar != NULL, NULL);
920
921   item = g_node_nth_child (toolbar, position);
922   g_return_val_if_fail (item != NULL, NULL);
923
924   idata = item->data;
925   return idata->name;
926 }
927
928 int
929 egg_toolbars_model_n_toolbars (EggToolbarsModel *model)
930 {
931   return g_node_n_children (model->priv->toolbars);
932 }
933
934 const char *
935 egg_toolbars_model_toolbar_nth (EggToolbarsModel *model,
936                                 int               position)
937 {
938   GNode *toolbar;
939   EggToolbarsToolbar *tdata;
940
941   toolbar = g_node_nth_child (model->priv->toolbars, position);
942   g_return_val_if_fail (toolbar != NULL, NULL);
943
944   tdata = toolbar->data;
945
946   return tdata->name;
947 }
948
949 GList *
950 egg_toolbars_model_get_types (EggToolbarsModel *model)
951 {
952   return model->priv->types;
953 }
954
955 void
956 egg_toolbars_model_set_types (EggToolbarsModel *model, GList *types)
957 {
958   model->priv->types = types;
959 }
960
961 static void
962 fill_avail_array (gpointer key, gpointer value, GPtrArray *array)
963 {
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);
967 }
968
969 GPtrArray *
970 egg_toolbars_model_get_name_avail (EggToolbarsModel *model)
971 {
972   GPtrArray *array = g_ptr_array_new ();
973   g_hash_table_foreach (model->priv->flags, (GHFunc) fill_avail_array, array);
974   return array;
975 }
976
977 gint
978 egg_toolbars_model_get_name_flags (EggToolbarsModel *model, const char *name)
979 {
980   return GPOINTER_TO_INT (g_hash_table_lookup (model->priv->flags, name));
981 }
982
983 void
984 egg_toolbars_model_set_name_flags (EggToolbarsModel *model, const char *name, gint flags)
985 {
986   g_hash_table_insert (model->priv->flags, g_strdup (name), GINT_TO_POINTER (flags));
987 }