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