]> www.fi.muni.cz Git - evince.git/blob - cut-n-paste/toolbar-editor/egg-editable-toolbar.c
Reduce relocations. Bug #360616.
[evince.git] / cut-n-paste / toolbar-editor / egg-editable-toolbar.c
1 /*
2  *  Copyright (C) 2003, 2004  Marco Pesenti Gritti
3  *  Copyright (C) 2003, 2004, 2005  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-editable-toolbar.h"
25 #include "egg-toolbars-model.h"
26 #include "egg-toolbar-editor.h"
27
28 #include <gtk/gtkvseparator.h>
29 #include <gtk/gtkiconfactory.h>
30 #include <gtk/gtkwindow.h>
31 #include <gtk/gtkmain.h>
32 #include <gtk/gtkdnd.h>
33 #include <gtk/gtkhbox.h>
34 #include <gtk/gtkimage.h>
35 #include <gtk/gtktoggleaction.h>
36 #include <gtk/gtkcheckmenuitem.h>
37 #include <gtk/gtkimagemenuitem.h>
38 #include <gtk/gtkseparatormenuitem.h>
39 #include <gtk/gtkmenu.h>
40 #include <gtk/gtkstock.h>
41 #include <gtk/gtklabel.h>
42 #include <gtk/gtkbutton.h>
43 #include <gtk/gtktoolbar.h>
44 #include <gtk/gtktoolitem.h>
45 #include <gtk/gtktoolbutton.h>
46 #include <gtk/gtkseparatortoolitem.h>
47 #include <glib/gi18n.h>
48 #include <string.h>
49
50 static void egg_editable_toolbar_class_init     (EggEditableToolbarClass *klass);
51 static void egg_editable_toolbar_init           (EggEditableToolbar *etoolbar);
52
53 #define MIN_TOOLBAR_HEIGHT 20
54 #define EGG_ITEM_NAME      "egg-item-name"
55 #define STOCK_DRAG_MODE    "stock_drag-mode"
56
57 static const GtkTargetEntry dest_drag_types[] = {
58   {EGG_TOOLBAR_ITEM_TYPE, GTK_TARGET_SAME_APP, 0},
59 };
60
61 enum
62 {
63   PROP_0,
64   PROP_TOOLBARS_MODEL,
65   PROP_UI_MANAGER,
66   PROP_POPUP_PATH,
67   PROP_SELECTED,
68   PROP_EDIT_MODE
69 };
70
71 enum
72 {
73   ACTION_REQUEST,
74   LAST_SIGNAL
75 };
76
77 static guint egg_editable_toolbar_signals[LAST_SIGNAL] = { 0 };
78
79 static GObjectClass *parent_class = NULL;
80
81 #define EGG_EDITABLE_TOOLBAR_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EGG_TYPE_EDITABLE_TOOLBAR, EggEditableToolbarPrivate))
82
83 struct _EggEditableToolbarPrivate
84 {
85   GtkUIManager *manager;
86   EggToolbarsModel *model;
87   guint edit_mode;
88   gboolean save_hidden;
89   GtkWidget *fixed_toolbar;
90   
91   GtkWidget *selected;
92   GtkActionGroup *actions;
93   
94   guint visibility_id;
95   GList *visibility_paths;
96   GPtrArray *visibility_actions;
97
98   char *popup_path;
99
100   guint        dnd_pending;
101   GtkToolbar  *dnd_toolbar;
102   GtkToolItem *dnd_toolitem;
103 };
104
105 GType
106 egg_editable_toolbar_get_type (void)
107 {
108   static GType type = 0;
109
110   if (G_UNLIKELY (type == 0))
111     {
112       const GTypeInfo our_info = {
113         sizeof (EggEditableToolbarClass),
114         NULL,                   /* base_init */
115         NULL,                   /* base_finalize */
116         (GClassInitFunc) egg_editable_toolbar_class_init,
117         NULL,
118         NULL,                   /* class_data */
119         sizeof (EggEditableToolbar),
120         0,                      /* n_preallocs */
121         (GInstanceInitFunc) egg_editable_toolbar_init
122       };
123
124       type = g_type_register_static (GTK_TYPE_VBOX,
125                                      "EggEditableToolbar",
126                                      &our_info, 0);
127     }
128
129   return type;
130 }
131
132 static int
133 get_dock_position (EggEditableToolbar *etoolbar,
134                    GtkWidget *dock)
135 {
136   GList *l;
137   int result;
138
139   l = gtk_container_get_children (GTK_CONTAINER (etoolbar));
140   result = g_list_index (l, dock);
141   g_list_free (l);
142
143   return result;
144 }
145
146 static int
147 get_toolbar_position (EggEditableToolbar *etoolbar, GtkWidget *toolbar)
148 {
149   return get_dock_position (etoolbar, toolbar->parent);
150 }
151
152 static int
153 get_n_toolbars (EggEditableToolbar *etoolbar)
154 {
155   GList *l;
156   int result;
157
158   l = gtk_container_get_children (GTK_CONTAINER (etoolbar));
159   result = g_list_length (l);
160   g_list_free (l);
161
162   return result;
163 }
164
165 static GtkWidget *
166 get_dock_nth (EggEditableToolbar *etoolbar,
167               int                 position)
168 {
169   GList *l;
170   GtkWidget *result;
171
172   l = gtk_container_get_children (GTK_CONTAINER (etoolbar));
173   result = g_list_nth_data (l, position);
174   g_list_free (l);
175
176   return result;
177 }
178
179 static GtkWidget *
180 get_toolbar_nth (EggEditableToolbar *etoolbar,
181                  int                 position)
182 {
183   GList *l;
184   GtkWidget *dock;
185   GtkWidget *result;
186
187   dock = get_dock_nth (etoolbar, position);
188   g_return_val_if_fail (dock != NULL, NULL);
189
190   l = gtk_container_get_children (GTK_CONTAINER (dock));
191   result = GTK_WIDGET (l->data);
192   g_list_free (l);
193
194   return result;
195 }
196
197 static GtkAction *
198 find_action (EggEditableToolbar *etoolbar,
199              const char         *name)
200 {
201   GList *l;
202   GtkAction *action = NULL;
203
204   l = gtk_ui_manager_get_action_groups (etoolbar->priv->manager);
205
206   g_return_val_if_fail (name != NULL, NULL);
207
208   for (; l != NULL; l = l->next)
209     {
210       GtkAction *tmp;
211
212       tmp = gtk_action_group_get_action (GTK_ACTION_GROUP (l->data), name);
213       if (tmp)
214         action = tmp;
215     }
216
217   return action;
218 }
219
220 static void
221 drag_data_delete_cb (GtkWidget          *widget,
222                      GdkDragContext     *context,
223                      EggEditableToolbar *etoolbar)
224 {
225   int pos, toolbar_pos;
226
227   widget = gtk_widget_get_ancestor (widget, GTK_TYPE_TOOL_ITEM);
228   g_return_if_fail (widget != NULL);
229   g_return_if_fail (EGG_IS_EDITABLE_TOOLBAR (etoolbar));
230
231   pos = gtk_toolbar_get_item_index (GTK_TOOLBAR (widget->parent),
232                                     GTK_TOOL_ITEM (widget));
233   toolbar_pos = get_toolbar_position (etoolbar, widget->parent);
234
235   egg_toolbars_model_remove_item (etoolbar->priv->model,
236                                   toolbar_pos, pos);
237 }
238
239 static void
240 drag_begin_cb (GtkWidget          *widget,
241                GdkDragContext     *context,
242                EggEditableToolbar *etoolbar)
243 {
244   GtkAction *action;
245   gint flags;
246   
247   gtk_widget_hide (widget);
248
249   action = g_object_get_data (G_OBJECT (widget), "gtk-action");
250   if (action == NULL) return;
251   
252   flags = egg_toolbars_model_get_name_flags (etoolbar->priv->model,
253                                              gtk_action_get_name (action));
254   if (!(flags & EGG_TB_MODEL_NAME_INFINITE))
255     {
256       flags &= ~EGG_TB_MODEL_NAME_USED;
257       egg_toolbars_model_set_name_flags (etoolbar->priv->model,
258                                          gtk_action_get_name (action),
259                                          flags);
260     }
261 }
262
263 static void
264 drag_end_cb (GtkWidget          *widget,
265              GdkDragContext     *context,
266              EggEditableToolbar *etoolbar)
267 {
268   GtkAction *action;
269   gint flags;
270  
271   if (gtk_widget_get_parent (widget) != NULL)
272     {
273       gtk_widget_show (widget);
274
275       action = g_object_get_data (G_OBJECT (widget), "gtk-action");
276       if (action == NULL) return;
277       
278       flags = egg_toolbars_model_get_name_flags (etoolbar->priv->model,
279                                                  gtk_action_get_name (action));
280       if (!(flags & EGG_TB_MODEL_NAME_INFINITE))
281         {
282           flags |= EGG_TB_MODEL_NAME_USED;
283           egg_toolbars_model_set_name_flags (etoolbar->priv->model,
284                                              gtk_action_get_name (action),
285                                              flags);
286         }
287     }
288 }
289
290 static void
291 drag_data_get_cb (GtkWidget          *widget,
292                   GdkDragContext     *context,
293                   GtkSelectionData   *selection_data,
294                   guint               info,
295                   guint32             time,
296                   EggEditableToolbar *etoolbar)
297 {
298   EggToolbarsModel *model;
299   const char *name;
300   char *data;
301
302   g_return_if_fail (EGG_IS_EDITABLE_TOOLBAR (etoolbar));
303   model = egg_editable_toolbar_get_model (etoolbar);
304   
305   name = g_object_get_data (G_OBJECT (widget), EGG_ITEM_NAME);
306   if (name == NULL)
307     {
308       name = g_object_get_data (G_OBJECT (gtk_widget_get_parent (widget)), EGG_ITEM_NAME);
309       g_return_if_fail (name != NULL);
310     }
311   
312   data = egg_toolbars_model_get_data (model, selection_data->target, name);
313   if (data != NULL)
314     {
315       gtk_selection_data_set (selection_data, selection_data->target, 8, (unsigned char *)data, strlen (data));
316       g_free (data);
317     }
318 }
319
320 static void
321 move_item_cb (GtkAction          *action,
322               EggEditableToolbar *etoolbar)
323 {
324   GtkWidget *toolitem = gtk_widget_get_ancestor (egg_editable_toolbar_get_selected (etoolbar), GTK_TYPE_TOOL_ITEM);
325   GtkTargetList *list = gtk_target_list_new (dest_drag_types, G_N_ELEMENTS (dest_drag_types));
326
327   GdkEvent *realevent = gtk_get_current_event();
328   GdkEventMotion event;
329   event.type = GDK_MOTION_NOTIFY;
330   event.window = realevent->any.window;
331   event.send_event = FALSE;
332   event.axes = NULL;
333   event.time = gdk_event_get_time (realevent);
334   gdk_event_get_state (realevent, &event.state);
335   gdk_event_get_coords (realevent, &event.x, &event.y);
336   gdk_event_get_root_coords (realevent, &event.x_root, &event.y_root);
337     
338   gtk_drag_begin (toolitem, list, GDK_ACTION_MOVE, 1, (GdkEvent *)&event);
339   gtk_target_list_unref (list);
340 }
341
342 static void
343 remove_item_cb (GtkAction          *action,
344                 EggEditableToolbar *etoolbar)
345 {
346   GtkWidget *toolitem = gtk_widget_get_ancestor (egg_editable_toolbar_get_selected (etoolbar), GTK_TYPE_TOOL_ITEM);
347   int pos, toolbar_pos;
348       
349   toolbar_pos = get_toolbar_position (etoolbar, toolitem->parent);
350   pos = gtk_toolbar_get_item_index (GTK_TOOLBAR (toolitem->parent), 
351                                     GTK_TOOL_ITEM (toolitem));
352
353   egg_toolbars_model_remove_item (etoolbar->priv->model,
354                                   toolbar_pos, pos);
355
356   if (egg_toolbars_model_n_items (etoolbar->priv->model, toolbar_pos) == 0)
357     {
358       egg_toolbars_model_remove_toolbar (etoolbar->priv->model, toolbar_pos);
359     }
360 }
361
362 static void
363 remove_toolbar_cb (GtkAction          *action,
364                    EggEditableToolbar *etoolbar)
365 {
366   GtkWidget *selected = egg_editable_toolbar_get_selected (etoolbar);
367   GtkWidget *toolbar = gtk_widget_get_ancestor (selected, GTK_TYPE_TOOLBAR);
368   int toolbar_pos;
369
370   toolbar_pos = get_toolbar_position (etoolbar, toolbar);
371   egg_toolbars_model_remove_toolbar (etoolbar->priv->model, toolbar_pos);
372 }
373
374 static void
375 popup_context_deactivate (GtkMenuShell *menu,
376                           EggEditableToolbar *etoolbar)
377 {
378   egg_editable_toolbar_set_selected (etoolbar, NULL);
379   g_object_notify (G_OBJECT (etoolbar), "selected");
380 }
381
382 static void
383 popup_context_menu_cb (GtkWidget          *toolbar,
384                        gint                x,
385                        gint                y,
386                        gint                button_number,
387                        EggEditableToolbar *etoolbar)
388 {
389   if (etoolbar->priv->popup_path != NULL)
390     {
391       GtkMenu *menu;
392       
393       egg_editable_toolbar_set_selected (etoolbar, toolbar);
394       g_object_notify (G_OBJECT (etoolbar), "selected");
395       
396       menu = GTK_MENU (gtk_ui_manager_get_widget (etoolbar->priv->manager, 
397                                                   etoolbar->priv->popup_path));
398       g_return_if_fail (menu != NULL);
399       gtk_menu_popup (menu, NULL, NULL, NULL, NULL, button_number, gtk_get_current_event_time ());
400       g_signal_connect_object (menu, "selection-done",
401                                G_CALLBACK (popup_context_deactivate),
402                                etoolbar, 0);
403     }
404 }
405
406 static gboolean
407 button_press_event_cb (GtkWidget *widget,
408                        GdkEventButton *event,
409                        EggEditableToolbar *etoolbar)
410 {
411   if (event->button == 3 && etoolbar->priv->popup_path != NULL)
412     {
413       GtkMenu *menu;
414       
415       egg_editable_toolbar_set_selected (etoolbar, widget);
416       g_object_notify (G_OBJECT (etoolbar), "selected");
417         
418       menu = GTK_MENU (gtk_ui_manager_get_widget (etoolbar->priv->manager, 
419                                                   etoolbar->priv->popup_path));
420       g_return_val_if_fail (menu != NULL, FALSE);
421       gtk_menu_popup (menu, NULL, NULL, NULL, NULL, event->button, event->time);
422       g_signal_connect_object (menu, "selection-done",
423                                G_CALLBACK (popup_context_deactivate),
424                                etoolbar, 0);
425       
426       return TRUE;
427     }
428     
429   return FALSE;
430 }
431
432 static void
433 configure_item_sensitivity (GtkToolItem *item, EggEditableToolbar *etoolbar)
434 {
435   GtkAction *action;
436   char *name;
437   
438   name = g_object_get_data (G_OBJECT (item), EGG_ITEM_NAME);
439   action = name ? find_action (etoolbar, name) : NULL;
440   
441   if (action)
442     {
443       g_object_notify (G_OBJECT (action), "sensitive");
444     }
445
446   gtk_tool_item_set_use_drag_window (item,
447                                      (etoolbar->priv->edit_mode > 0) || 
448                                      GTK_IS_SEPARATOR_TOOL_ITEM (item));
449   
450 }
451
452 static void
453 configure_item_cursor (GtkToolItem *item,
454                        EggEditableToolbar *etoolbar)
455 {
456   EggEditableToolbarPrivate *priv = etoolbar->priv;
457   GtkWidget *widget = GTK_WIDGET (item);
458
459   if (widget->window != NULL)
460     {
461       if (priv->edit_mode > 0)
462         {
463           GdkCursor *cursor;
464           
465           cursor = gdk_cursor_new (GDK_HAND2);
466           gdk_window_set_cursor (widget->window, cursor);
467           gdk_cursor_unref (cursor);
468
469           gtk_drag_source_set (widget, GDK_BUTTON1_MASK, dest_drag_types,
470                                G_N_ELEMENTS (dest_drag_types), GDK_ACTION_MOVE);
471         }
472       else
473         {
474           gdk_window_set_cursor (GTK_WIDGET(item)->window, NULL);
475         }
476     }
477 }
478
479
480 static void
481 configure_item_tooltip (GtkToolItem *item)
482 {
483   GtkAction *action = g_object_get_data (G_OBJECT (item),
484                                          "gtk-action");
485   
486   if (action != NULL)
487     {
488       g_object_notify (G_OBJECT (action), "tooltip");
489     }
490 }
491
492
493 static void
494 connect_widget_signals (GtkWidget *proxy, EggEditableToolbar *etoolbar)
495 {
496   if (GTK_IS_CONTAINER (proxy))
497     {
498        gtk_container_forall (GTK_CONTAINER (proxy),
499                              (GtkCallback) connect_widget_signals,
500                              (gpointer) etoolbar);
501     }
502
503   if (GTK_IS_TOOL_ITEM (proxy))
504     {
505       g_signal_connect_object (proxy, "drag_begin",
506                                G_CALLBACK (drag_begin_cb), 
507                                etoolbar, 0);
508       g_signal_connect_object (proxy, "drag_end",
509                                G_CALLBACK (drag_end_cb),
510                                etoolbar, 0);
511       g_signal_connect_object (proxy, "drag_data_get",
512                                G_CALLBACK (drag_data_get_cb), 
513                                etoolbar, 0);
514       g_signal_connect_object (proxy, "drag_data_delete",
515                                G_CALLBACK (drag_data_delete_cb),
516                                etoolbar, 0);
517     }
518     
519   if (GTK_IS_BUTTON (proxy) || GTK_IS_TOOL_ITEM (proxy))
520     {
521       g_signal_connect_object (proxy, "button-press-event",
522                                G_CALLBACK (button_press_event_cb),
523                                etoolbar, 0);
524     }
525 }
526
527 static void
528 action_sensitive_cb (GtkAction   *action, 
529                      GParamSpec  *pspec,
530                      GtkToolItem *item)
531 {
532   EggEditableToolbar *etoolbar = EGG_EDITABLE_TOOLBAR
533     (gtk_widget_get_ancestor (GTK_WIDGET (item), EGG_TYPE_EDITABLE_TOOLBAR));
534
535   if (etoolbar->priv->edit_mode > 0)
536     {
537       gtk_widget_set_sensitive (GTK_WIDGET (item), TRUE);
538     }
539 }
540
541 static GtkToolItem *
542 create_item_from_action (EggEditableToolbar *etoolbar,
543                          const char *name)
544 {
545   GtkToolItem *item;
546
547   g_return_val_if_fail (name != NULL, NULL);
548   
549   if (strcmp (name, "_separator") == 0)
550     {
551       item = gtk_separator_tool_item_new ();
552     }
553   else
554     {
555       GtkAction *action = find_action (etoolbar, name);
556       if (action == NULL) return NULL;
557         
558       item = GTK_TOOL_ITEM (gtk_action_create_tool_item (action));
559
560       /* Normally done on-demand by the GtkUIManager, but no
561        * such demand may have been made yet, so do it ourselves.
562        */
563       gtk_action_set_accel_group
564         (action, gtk_ui_manager_get_accel_group(etoolbar->priv->manager));
565      
566       g_signal_connect_object (action, "notify::sensitive",
567                                G_CALLBACK (action_sensitive_cb), item, 0);
568     }
569
570   gtk_widget_show (GTK_WIDGET (item));
571
572   g_object_set_data_full (G_OBJECT (item), EGG_ITEM_NAME,
573                           g_strdup (name), g_free);  
574   
575   return item;
576 }
577
578 static GtkToolItem *
579 create_item_from_position (EggEditableToolbar *etoolbar,
580                            int                 toolbar_position,
581                            int                 position)
582 {
583   GtkToolItem *item;
584   const char *name;
585
586   name = egg_toolbars_model_item_nth (etoolbar->priv->model, toolbar_position, position);
587   item = create_item_from_action (etoolbar, name);
588
589   return item;
590 }
591
592 static void
593 toolbar_drag_data_received_cb (GtkToolbar         *toolbar,
594                                GdkDragContext     *context,
595                                gint                x,
596                                gint                y,
597                                GtkSelectionData   *selection_data,
598                                guint               info,
599                                guint               time,
600                                EggEditableToolbar *etoolbar)
601 {
602   /* This function can be called for two reasons
603    *
604    *  (1) drag_motion() needs an item to pass to
605    *      gtk_toolbar_set_drop_highlight_item(). We can
606    *      recognize this case by etoolbar->priv->pending being TRUE
607    *      We should just create an item and return.
608    *
609    *  (2) The drag has finished, and drag_drop() wants us to
610    *      actually add a new item to the toolbar.
611    */
612
613   GdkAtom type = selection_data->type;
614   const char *data = (char *)selection_data->data;
615   
616   int ipos = -1;
617   char *name = NULL;
618   gboolean used = FALSE;
619   
620   /* Find out where the drop is occuring, and the name of what is being dropped. */
621   if (selection_data->length >= 0)
622     {
623       ipos = gtk_toolbar_get_drop_index (toolbar, x, y);
624       name = egg_toolbars_model_get_name (etoolbar->priv->model, type, data, FALSE);
625       if (name != NULL)
626         {
627           used = ((egg_toolbars_model_get_name_flags (etoolbar->priv->model, name) & EGG_TB_MODEL_NAME_USED) != 0);
628         }
629     }
630
631   /* If we just want a highlight item, then . */
632   if (etoolbar->priv->dnd_pending > 0)
633     {
634       etoolbar->priv->dnd_pending--;
635       
636       if (name != NULL && etoolbar->priv->dnd_toolbar == toolbar && !used)
637         {
638           etoolbar->priv->dnd_toolitem = create_item_from_action (etoolbar, name);
639           gtk_toolbar_set_drop_highlight_item (etoolbar->priv->dnd_toolbar,
640                                                etoolbar->priv->dnd_toolitem, ipos);
641         }
642     }
643   else
644     {
645       gtk_toolbar_set_drop_highlight_item (toolbar, NULL, 0);
646       etoolbar->priv->dnd_toolbar = NULL;
647       etoolbar->priv->dnd_toolitem = NULL;
648   
649       /* If we don't have a name to use yet, try to create one. */
650       if (name == NULL && selection_data->length >= 0)
651         {
652           name = egg_toolbars_model_get_name (etoolbar->priv->model, type, data, TRUE);
653         }
654   
655       if (name != NULL && !used)
656         {
657           gint tpos = get_toolbar_position (etoolbar, GTK_WIDGET (toolbar));
658           egg_toolbars_model_add_item (etoolbar->priv->model, tpos, ipos, name);
659           gtk_drag_finish (context, TRUE, context->action == GDK_ACTION_MOVE, time);
660         }
661       else
662         {  
663           gtk_drag_finish (context, FALSE, context->action == GDK_ACTION_MOVE, time);
664         }
665     }
666         
667   g_free (name);
668 }
669
670 static gboolean
671 toolbar_drag_drop_cb (GtkToolbar         *toolbar,
672                       GdkDragContext     *context,
673                       gint                x,
674                       gint                y,
675                       guint               time,
676                       EggEditableToolbar *etoolbar)
677 {
678   GdkAtom target;
679
680   target = gtk_drag_dest_find_target (GTK_WIDGET (toolbar), context, NULL);
681   if (target != GDK_NONE)
682     {
683       gtk_drag_get_data (GTK_WIDGET (toolbar), context, target, time);
684       return TRUE;
685     }
686   
687   return FALSE;
688 }
689
690 static gboolean
691 toolbar_drag_motion_cb (GtkToolbar         *toolbar,
692                         GdkDragContext     *context,
693                         gint                x,
694                         gint                y,
695                         guint               time,
696                         EggEditableToolbar *etoolbar)
697 {
698   GdkAtom target = gtk_drag_dest_find_target (GTK_WIDGET (toolbar), context, NULL);
699   if (target == GDK_NONE)
700     {
701       gdk_drag_status (context, 0, time);
702       return FALSE;
703     }
704
705   /* Make ourselves the current dnd toolbar, and request a highlight item. */
706   if (etoolbar->priv->dnd_toolbar != toolbar)
707     {
708       etoolbar->priv->dnd_toolbar = toolbar;
709       etoolbar->priv->dnd_toolitem = NULL;
710       etoolbar->priv->dnd_pending++;
711       gtk_drag_get_data (GTK_WIDGET (toolbar), context, target, time);
712     }
713   
714   /* If a highlight item is available, use it. */
715   else if (etoolbar->priv->dnd_toolitem)
716     {
717       gint ipos = gtk_toolbar_get_drop_index (etoolbar->priv->dnd_toolbar, x, y);
718       gtk_toolbar_set_drop_highlight_item (etoolbar->priv->dnd_toolbar,
719                                            etoolbar->priv->dnd_toolitem, ipos);
720     }
721
722   gdk_drag_status (context, context->suggested_action, time);
723
724   return TRUE;
725 }
726
727 static void
728 toolbar_drag_leave_cb (GtkToolbar         *toolbar,
729                        GdkDragContext     *context,
730                        guint               time,
731                        EggEditableToolbar *etoolbar)
732 {
733   gtk_toolbar_set_drop_highlight_item (toolbar, NULL, 0);
734
735   /* If we were the current dnd toolbar target, remove the item. */
736   if (etoolbar->priv->dnd_toolbar == toolbar)
737     {
738       etoolbar->priv->dnd_toolbar = NULL;
739       etoolbar->priv->dnd_toolitem = NULL;
740     }
741 }
742
743 static void
744 configure_drag_dest (EggEditableToolbar *etoolbar,
745                      GtkToolbar         *toolbar)
746 {
747   EggToolbarsItemType *type;
748   GtkTargetList *targets;
749   GList *list;
750
751   /* Make every toolbar able to receive drag-drops. */
752   gtk_drag_dest_set (GTK_WIDGET (toolbar), 0,
753                      dest_drag_types, G_N_ELEMENTS (dest_drag_types),
754                      GDK_ACTION_MOVE | GDK_ACTION_COPY);
755  
756   /* Add any specialist drag-drop abilities. */
757   targets = gtk_drag_dest_get_target_list (GTK_WIDGET (toolbar));
758   list = egg_toolbars_model_get_types (etoolbar->priv->model);
759   while (list)
760   {
761     type = list->data;
762     if (type->new_name != NULL || type->get_name != NULL)
763       gtk_target_list_add (targets, type->type, 0, 0);
764     list = list->next;
765   }
766 }
767
768 static void
769 toggled_visibility_cb (GtkToggleAction *action,
770                        EggEditableToolbar *etoolbar)
771 {
772   EggEditableToolbarPrivate *priv = etoolbar->priv;
773   GtkWidget *dock;
774   EggTbModelFlags flags;
775   gboolean visible;
776   gint i;
777   
778   visible = gtk_toggle_action_get_active (action);
779   for (i = 0; i < priv->visibility_actions->len; i++)
780     if (g_ptr_array_index (priv->visibility_actions, i) == action)
781       break;
782   
783   g_return_if_fail (i < priv->visibility_actions->len);
784   
785   dock = get_dock_nth (etoolbar, i);  
786   if (visible)
787     {
788       gtk_widget_show (dock);
789     }
790   else
791     {
792       gtk_widget_hide (dock);
793     }
794   
795   if (priv->save_hidden)
796     {      
797       flags = egg_toolbars_model_get_flags (priv->model, i);
798       
799       if (visible)
800         {
801           flags &= ~(EGG_TB_MODEL_HIDDEN);
802         }
803       else
804         {
805           flags |=  (EGG_TB_MODEL_HIDDEN);
806         }
807       
808       egg_toolbars_model_set_flags (priv->model, i, flags);
809     }
810 }
811
812 static void
813 toolbar_visibility_refresh (EggEditableToolbar *etoolbar)
814 {
815   EggEditableToolbarPrivate *priv = etoolbar->priv;
816   gint n_toolbars, n_items, i, j, k;
817   GtkToggleAction *action;
818   GList *list;
819   GString *string;
820   gboolean showing;
821   char action_name[40];
822   char *action_label;
823   char *tmp;            
824   
825   if (priv == NULL || priv->model == NULL || priv->manager == NULL ||
826       priv->visibility_paths == NULL || priv->actions == NULL)
827     {
828       return;
829     }
830
831   if (priv->visibility_actions == NULL)
832     {
833       priv->visibility_actions = g_ptr_array_new ();
834     }
835   
836   if (priv->visibility_id != 0)
837     {
838       gtk_ui_manager_remove_ui (priv->manager, priv->visibility_id);
839     }  
840   
841   priv->visibility_id = gtk_ui_manager_new_merge_id (priv->manager);
842   
843   showing = GTK_WIDGET_VISIBLE (etoolbar);
844   
845   n_toolbars = egg_toolbars_model_n_toolbars (priv->model);
846   for (i = 0; i < n_toolbars; i++)
847     {
848       string = g_string_sized_new (0);
849       n_items = egg_toolbars_model_n_items (priv->model, i);
850       for (k = 0, j = 0; j < n_items; j++)
851         {
852           GValue value = { 0, };
853           GtkAction *action;
854           const char *name;
855
856           name = egg_toolbars_model_item_nth (priv->model, i, j);
857           if (name == NULL) continue;
858           action = find_action (etoolbar, name);
859           if (action == NULL) continue;
860
861           g_value_init (&value, G_TYPE_STRING);
862           g_object_get_property (G_OBJECT (action), "label", &value);
863           name = g_value_get_string (&value);
864           if (name == NULL)
865             {
866                 g_value_unset (&value);
867                 continue;
868             }
869           k += g_utf8_strlen (name, -1) + 2;
870           if (j > 0)
871             {
872               g_string_append (string, ", ");
873               if (j > 1 && k > 25)
874                 {
875                   g_value_unset (&value);
876                   break;
877                 }
878             }
879           g_string_append (string, name);
880           g_value_unset (&value);
881         }
882       if (j < n_items)
883         {
884           g_string_append (string, " ...");
885         }
886       
887       tmp = g_string_free (string, FALSE);
888       for (j = 0, k = 0; tmp[j]; j++)
889       {
890         if (tmp[j] == '_') continue;
891         tmp[k] = tmp[j];
892         k++;
893       }
894       tmp[k] = 0;
895       /* Translaters: This string is for a toggle to display a toolbar.
896        * The name of the toolbar is automatically computed from the widgets
897        * on the toolbar, and is placed at the %s. Note the _ before the %s
898        * which is used to add mnemonics. We know that this is likely to
899        * produce duplicates, but don't worry about it. If your language
900        * normally has a mnemonic at the start, please use the _. If not,
901        * please remove. */
902       action_label = g_strdup_printf (_("Show “_%s”"), tmp);
903       g_free (tmp);
904       
905       sprintf(action_name, "ToolbarToggle%d", i);
906       
907       if (i >= priv->visibility_actions->len)
908         {
909           action = gtk_toggle_action_new (action_name, action_label, NULL, NULL);
910           g_ptr_array_add (priv->visibility_actions, action);
911           g_signal_connect_object (action, "toggled",
912                                    G_CALLBACK (toggled_visibility_cb),
913                                    etoolbar, 0);
914           gtk_action_group_add_action (priv->actions, GTK_ACTION (action));
915         }
916       else
917         {
918           action = g_ptr_array_index (priv->visibility_actions, i);
919           g_object_set (action, "label", action_label, NULL);
920         }
921
922       gtk_action_set_visible (GTK_ACTION (action), (egg_toolbars_model_get_flags (priv->model, i) 
923                                                     & EGG_TB_MODEL_NOT_REMOVABLE) == 0);
924       gtk_action_set_sensitive (GTK_ACTION (action), showing);
925       gtk_toggle_action_set_active (action, GTK_WIDGET_VISIBLE
926                                     (get_dock_nth (etoolbar, i)));
927       
928       for (list = priv->visibility_paths; list != NULL; list = g_list_next (list))
929         {
930           gtk_ui_manager_add_ui (priv->manager, priv->visibility_id,
931                                  (const char *)list->data, action_name, action_name,
932                                  GTK_UI_MANAGER_MENUITEM, FALSE);
933         }
934             
935       g_free (action_label);
936     }
937   
938   gtk_ui_manager_ensure_update (priv->manager);
939   
940   while (i < priv->visibility_actions->len)
941     {
942       action = g_ptr_array_index (priv->visibility_actions, i);
943       g_ptr_array_remove_index_fast (priv->visibility_actions, i);
944       gtk_action_group_remove_action (priv->actions, GTK_ACTION (action));
945       i++;
946     }
947 }
948
949 static GtkWidget *
950 create_dock (EggEditableToolbar *etoolbar)
951 {
952   GtkWidget *toolbar, *hbox;
953
954   hbox = gtk_hbox_new (0, FALSE);
955
956   toolbar = gtk_toolbar_new ();
957   gtk_toolbar_set_show_arrow (GTK_TOOLBAR (toolbar), TRUE);
958   gtk_widget_show (toolbar);
959   gtk_box_pack_start (GTK_BOX (hbox), toolbar, TRUE, TRUE, 0);
960
961   g_signal_connect (toolbar, "drag_drop",
962                     G_CALLBACK (toolbar_drag_drop_cb), etoolbar); 
963   g_signal_connect (toolbar, "drag_motion",
964                     G_CALLBACK (toolbar_drag_motion_cb), etoolbar);
965   g_signal_connect (toolbar, "drag_leave",
966                     G_CALLBACK (toolbar_drag_leave_cb), etoolbar);
967
968   g_signal_connect (toolbar, "drag_data_received",
969                     G_CALLBACK (toolbar_drag_data_received_cb), etoolbar);
970   g_signal_connect (toolbar, "popup_context_menu",
971                     G_CALLBACK (popup_context_menu_cb), etoolbar);
972
973   configure_drag_dest (etoolbar, GTK_TOOLBAR (toolbar));
974   
975   return hbox;
976 }
977
978 static void
979 set_fixed_style (EggEditableToolbar *t, GtkToolbarStyle style)
980 {
981   g_return_if_fail (GTK_IS_TOOLBAR (t->priv->fixed_toolbar));
982   gtk_toolbar_set_style (GTK_TOOLBAR (t->priv->fixed_toolbar),
983                          style == GTK_TOOLBAR_ICONS ? GTK_TOOLBAR_BOTH_HORIZ : style);
984 }
985
986 static void
987 unset_fixed_style (EggEditableToolbar *t)
988 {
989   g_return_if_fail (GTK_IS_TOOLBAR (t->priv->fixed_toolbar));
990   gtk_toolbar_unset_style (GTK_TOOLBAR (t->priv->fixed_toolbar));
991 }
992
993 static void
994 toolbar_changed_cb (EggToolbarsModel   *model,
995                     int                 position,
996                     EggEditableToolbar *etoolbar)
997 {
998   GtkWidget *toolbar;
999   EggTbModelFlags flags;
1000   GtkToolbarStyle style;
1001
1002   flags = egg_toolbars_model_get_flags (model, position);
1003   toolbar = get_toolbar_nth (etoolbar, position);
1004
1005   if (flags & EGG_TB_MODEL_ICONS)
1006   {
1007     style = GTK_TOOLBAR_ICONS;
1008   }
1009   else if (flags & EGG_TB_MODEL_TEXT)
1010   {
1011     style = GTK_TOOLBAR_TEXT;
1012   }
1013   else if (flags & EGG_TB_MODEL_BOTH)
1014   {
1015     style = GTK_TOOLBAR_BOTH;
1016   }
1017   else if (flags & EGG_TB_MODEL_BOTH_HORIZ)
1018   {
1019     style = GTK_TOOLBAR_BOTH_HORIZ;
1020   }
1021   else
1022   {
1023     gtk_toolbar_unset_style (GTK_TOOLBAR (toolbar));
1024     if (position == 0 && etoolbar->priv->fixed_toolbar)
1025       {
1026         unset_fixed_style (etoolbar);
1027       }
1028     return;
1029   }
1030
1031   gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), style);
1032   if (position == 0 && etoolbar->priv->fixed_toolbar)
1033     {
1034       set_fixed_style (etoolbar, style);
1035     }
1036
1037   toolbar_visibility_refresh (etoolbar);
1038 }
1039
1040 static void
1041 unparent_fixed (EggEditableToolbar *etoolbar)
1042 {
1043   GtkWidget *toolbar, *dock;
1044   g_return_if_fail (GTK_IS_TOOLBAR (etoolbar->priv->fixed_toolbar));
1045
1046   toolbar = etoolbar->priv->fixed_toolbar;
1047   dock = get_dock_nth (etoolbar, 0);
1048
1049   if (dock && toolbar->parent != NULL)
1050     {
1051       gtk_container_remove (GTK_CONTAINER (dock), toolbar);
1052     }
1053 }
1054
1055 static void
1056 update_fixed (EggEditableToolbar *etoolbar)
1057 {
1058   GtkWidget *toolbar, *dock;
1059   if (!etoolbar->priv->fixed_toolbar) return;
1060
1061   toolbar = etoolbar->priv->fixed_toolbar;
1062   dock = get_dock_nth (etoolbar, 0);
1063
1064   if (dock && toolbar && toolbar->parent == NULL)
1065     {
1066       gtk_box_pack_end (GTK_BOX (dock), toolbar, FALSE, TRUE, 0);
1067
1068       gtk_widget_show (toolbar);
1069   
1070       gtk_widget_set_size_request (dock, -1, -1);
1071       gtk_widget_queue_resize_no_redraw (dock);
1072     }
1073 }
1074
1075 static void
1076 toolbar_added_cb (EggToolbarsModel   *model,
1077                   int                 position,
1078                   EggEditableToolbar *etoolbar)
1079 {
1080   GtkWidget *dock;
1081
1082   dock = create_dock (etoolbar);
1083   if ((egg_toolbars_model_get_flags (model, position) & EGG_TB_MODEL_HIDDEN) == 0)
1084     gtk_widget_show (dock);
1085
1086   gtk_widget_set_size_request (dock, -1, MIN_TOOLBAR_HEIGHT);
1087
1088   gtk_box_pack_start (GTK_BOX (etoolbar), dock, TRUE, TRUE, 0);
1089
1090   gtk_box_reorder_child (GTK_BOX (etoolbar), dock, position);
1091
1092   gtk_widget_show_all (dock);
1093   
1094   update_fixed (etoolbar);
1095
1096   toolbar_visibility_refresh (etoolbar);
1097 }
1098
1099 static void
1100 toolbar_removed_cb (EggToolbarsModel   *model,
1101                     int                 position,
1102                     EggEditableToolbar *etoolbar)
1103 {
1104   GtkWidget *dock;
1105
1106   if (position == 0 && etoolbar->priv->fixed_toolbar != NULL)
1107     {
1108       unparent_fixed (etoolbar);
1109     }
1110
1111   dock = get_dock_nth (etoolbar, position);
1112   gtk_widget_destroy (dock);
1113
1114   update_fixed (etoolbar);
1115   
1116   toolbar_visibility_refresh (etoolbar);
1117 }
1118
1119 static void
1120 item_added_cb (EggToolbarsModel   *model,
1121                int                 tpos,
1122                int                 ipos,
1123                EggEditableToolbar *etoolbar)
1124 {
1125   GtkWidget *dock;
1126   GtkWidget *toolbar;
1127   GtkToolItem *item;
1128
1129   toolbar = get_toolbar_nth (etoolbar, tpos);
1130   item = create_item_from_position (etoolbar, tpos, ipos);
1131   if (item == NULL) return;
1132     
1133   gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, ipos);
1134   
1135   connect_widget_signals (GTK_WIDGET (item), etoolbar);
1136   configure_item_tooltip (item);
1137   configure_item_cursor (item, etoolbar);
1138   configure_item_sensitivity (item, etoolbar);
1139   
1140   dock = get_dock_nth (etoolbar, tpos);
1141   gtk_widget_set_size_request (dock, -1, -1);
1142   gtk_widget_queue_resize_no_redraw (dock);
1143
1144   toolbar_visibility_refresh (etoolbar);
1145 }
1146
1147 static void
1148 item_removed_cb (EggToolbarsModel   *model,
1149                  int                 toolbar_position,
1150                  int                 position,
1151                  EggEditableToolbar *etoolbar)
1152 {
1153   EggEditableToolbarPrivate *priv = etoolbar->priv;
1154   
1155   GtkWidget *toolbar;
1156   GtkWidget *item;
1157
1158   toolbar = get_toolbar_nth (etoolbar, toolbar_position);
1159   item = GTK_WIDGET (gtk_toolbar_get_nth_item
1160         (GTK_TOOLBAR (toolbar), position));
1161   g_return_if_fail (item != NULL);
1162
1163   if (item == priv->selected)
1164     {
1165       /* FIXME */
1166     }
1167
1168   gtk_container_remove (GTK_CONTAINER (toolbar), item);
1169
1170   toolbar_visibility_refresh (etoolbar);
1171 }
1172
1173 static void
1174 egg_editable_toolbar_build (EggEditableToolbar *etoolbar)
1175 {
1176   int i, l, n_items, n_toolbars;
1177   EggToolbarsModel *model = etoolbar->priv->model;
1178
1179   g_return_if_fail (model != NULL);
1180   g_return_if_fail (etoolbar->priv->manager != NULL);
1181
1182   n_toolbars = egg_toolbars_model_n_toolbars (model);
1183
1184   for (i = 0; i < n_toolbars; i++)
1185     {
1186       GtkWidget *toolbar, *dock;
1187
1188       dock = create_dock (etoolbar);
1189       if ((egg_toolbars_model_get_flags (model, i) & EGG_TB_MODEL_HIDDEN) == 0)
1190         gtk_widget_show (dock);
1191       gtk_box_pack_start (GTK_BOX (etoolbar), dock, TRUE, TRUE, 0);
1192       toolbar = get_toolbar_nth (etoolbar, i);
1193
1194       n_items = egg_toolbars_model_n_items (model, i);
1195       for (l = 0; l < n_items; l++)
1196         {
1197           GtkToolItem *item;
1198
1199           item = create_item_from_position (etoolbar, i, l);
1200           if (item)
1201             {
1202               gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, l);
1203               
1204               connect_widget_signals (GTK_WIDGET (item), etoolbar);
1205               configure_item_tooltip (item);
1206               configure_item_sensitivity (item, etoolbar);
1207             }
1208           else
1209             {
1210               egg_toolbars_model_remove_item (model, i, l);
1211               l--;
1212               n_items--;
1213             }
1214         }
1215
1216       if (n_items == 0)
1217         {
1218             gtk_widget_set_size_request (dock, -1, MIN_TOOLBAR_HEIGHT);
1219         }
1220     }
1221
1222   update_fixed (etoolbar);
1223
1224   /* apply styles */
1225   for (i = 0; i < n_toolbars; i ++)
1226     {
1227       toolbar_changed_cb (model, i, etoolbar);
1228     }
1229 }
1230
1231 static void
1232 egg_editable_toolbar_disconnect_model (EggEditableToolbar *toolbar)
1233 {
1234   EggToolbarsModel *model = toolbar->priv->model;
1235
1236   g_signal_handlers_disconnect_by_func
1237     (model, G_CALLBACK (item_added_cb), toolbar);
1238   g_signal_handlers_disconnect_by_func
1239     (model, G_CALLBACK (item_removed_cb), toolbar);
1240   g_signal_handlers_disconnect_by_func
1241     (model, G_CALLBACK (toolbar_added_cb), toolbar);
1242   g_signal_handlers_disconnect_by_func
1243     (model, G_CALLBACK (toolbar_removed_cb), toolbar);
1244   g_signal_handlers_disconnect_by_func
1245     (model, G_CALLBACK (toolbar_changed_cb), toolbar);
1246 }
1247
1248 static void
1249 egg_editable_toolbar_deconstruct (EggEditableToolbar *toolbar)
1250 {
1251   EggToolbarsModel *model = toolbar->priv->model;
1252   GList *children;
1253
1254   g_return_if_fail (model != NULL);
1255
1256   if (toolbar->priv->fixed_toolbar)
1257     {
1258        unset_fixed_style (toolbar);
1259        unparent_fixed (toolbar);
1260     }
1261
1262   children = gtk_container_get_children (GTK_CONTAINER (toolbar));
1263   g_list_foreach (children, (GFunc) gtk_widget_destroy, NULL);
1264   g_list_free (children);
1265 }
1266
1267 void
1268 egg_editable_toolbar_set_model (EggEditableToolbar *etoolbar,
1269                                 EggToolbarsModel   *model)
1270 {
1271   EggEditableToolbarPrivate *priv = etoolbar->priv;
1272
1273   if (priv->model == model) return;
1274
1275   if (priv->model)
1276     {
1277       egg_editable_toolbar_disconnect_model (etoolbar);
1278       egg_editable_toolbar_deconstruct (etoolbar);
1279
1280       g_object_unref (priv->model);
1281     }
1282
1283   priv->model = g_object_ref (model);
1284
1285   egg_editable_toolbar_build (etoolbar);
1286
1287   toolbar_visibility_refresh (etoolbar);
1288
1289   g_signal_connect (model, "item_added",
1290                     G_CALLBACK (item_added_cb), etoolbar);
1291   g_signal_connect (model, "item_removed",
1292                     G_CALLBACK (item_removed_cb), etoolbar);
1293   g_signal_connect (model, "toolbar_added",
1294                     G_CALLBACK (toolbar_added_cb), etoolbar);
1295   g_signal_connect (model, "toolbar_removed",
1296                     G_CALLBACK (toolbar_removed_cb), etoolbar);
1297   g_signal_connect (model, "toolbar_changed",
1298                     G_CALLBACK (toolbar_changed_cb), etoolbar);
1299 }
1300
1301 static void
1302 egg_editable_toolbar_init (EggEditableToolbar *etoolbar)
1303 {
1304   EggEditableToolbarPrivate *priv;
1305
1306   priv = etoolbar->priv = EGG_EDITABLE_TOOLBAR_GET_PRIVATE (etoolbar);
1307
1308   priv->save_hidden = TRUE;
1309   
1310   g_signal_connect (etoolbar, "notify::visible",
1311                     G_CALLBACK (toolbar_visibility_refresh), NULL);
1312 }
1313
1314 static void
1315 egg_editable_toolbar_dispose (GObject *object)
1316 {
1317   EggEditableToolbar *etoolbar = EGG_EDITABLE_TOOLBAR (object);
1318   EggEditableToolbarPrivate *priv = etoolbar->priv;
1319   GList *children;
1320
1321   if (priv->fixed_toolbar != NULL)
1322     {
1323       g_object_unref (priv->fixed_toolbar);
1324       priv->fixed_toolbar = NULL;
1325     }
1326
1327   if (priv->visibility_paths)
1328     {
1329       children = priv->visibility_paths;
1330       g_list_foreach (children, (GFunc) g_free, NULL);
1331       g_list_free (children);
1332       priv->visibility_paths = NULL;
1333     }
1334
1335   if (priv->manager != NULL)
1336     {
1337       if (priv->visibility_id)
1338         {
1339           gtk_ui_manager_remove_ui (priv->manager, priv->visibility_id);
1340           priv->visibility_id = 0;
1341         }
1342
1343       g_object_unref (priv->manager);
1344       priv->manager = NULL;
1345     }
1346
1347   if (priv->model)
1348     {
1349       egg_editable_toolbar_disconnect_model (etoolbar);
1350       g_object_unref (priv->model);
1351       priv->model = NULL;
1352     }
1353
1354   G_OBJECT_CLASS (parent_class)->dispose (object);
1355 }
1356
1357 static void
1358 egg_editable_toolbar_set_ui_manager (EggEditableToolbar *etoolbar,
1359                                      GtkUIManager       *manager)
1360 {
1361   static const GtkActionEntry actions[] = {
1362     { "MoveToolItem", STOCK_DRAG_MODE, N_("_Move on Toolbar"), NULL,
1363       N_("Move the selected item on the toolbar"), G_CALLBACK (move_item_cb) },
1364     { "RemoveToolItem", GTK_STOCK_REMOVE, N_("_Remove from Toolbar"), NULL,
1365       N_("Remove the selected item from the toolbar"), G_CALLBACK (remove_item_cb) },
1366     { "RemoveToolbar", GTK_STOCK_DELETE, N_("_Delete Toolbar"), NULL,
1367       N_("Remove the selected toolbar"), G_CALLBACK (remove_toolbar_cb) },
1368   };
1369   
1370   etoolbar->priv->manager = g_object_ref (manager);
1371
1372   etoolbar->priv->actions = gtk_action_group_new ("ToolbarActions");
1373   gtk_action_group_set_translation_domain (etoolbar->priv->actions, GETTEXT_PACKAGE);
1374   gtk_action_group_add_actions (etoolbar->priv->actions, actions,
1375                                 G_N_ELEMENTS (actions), etoolbar);
1376   gtk_ui_manager_insert_action_group (manager, etoolbar->priv->actions, -1);
1377   g_object_unref (etoolbar->priv->actions);
1378
1379   toolbar_visibility_refresh (etoolbar);
1380 }
1381
1382 GtkWidget * egg_editable_toolbar_get_selected (EggEditableToolbar   *etoolbar)
1383 {
1384   return etoolbar->priv->selected;
1385 }
1386
1387 void
1388 egg_editable_toolbar_set_selected (EggEditableToolbar *etoolbar,
1389                                    GtkWidget          *widget)
1390 {
1391   GtkWidget *toolbar, *toolitem;
1392   gboolean editable;
1393
1394   etoolbar->priv->selected = widget;
1395   
1396   toolbar = (widget != NULL) ? gtk_widget_get_ancestor (widget, GTK_TYPE_TOOLBAR) : NULL;
1397   toolitem = (widget != NULL) ? gtk_widget_get_ancestor (widget, GTK_TYPE_TOOL_ITEM) : NULL;
1398   
1399   if(toolbar != NULL)
1400     {
1401       gint tpos = get_toolbar_position (etoolbar, toolbar);
1402       editable = ((egg_toolbars_model_get_flags (etoolbar->priv->model, tpos) & EGG_TB_MODEL_NOT_EDITABLE) == 0);
1403     }
1404   else
1405     {
1406       editable = FALSE;
1407     }
1408   
1409   gtk_action_set_visible (find_action (etoolbar, "RemoveToolbar"), (toolbar != NULL) && (etoolbar->priv->edit_mode > 0));
1410   gtk_action_set_visible (find_action (etoolbar, "RemoveToolItem"), (toolitem != NULL) && editable);
1411   gtk_action_set_visible (find_action (etoolbar, "MoveToolItem"), (toolitem != NULL) && editable);
1412 }
1413
1414 static void
1415 set_edit_mode (EggEditableToolbar *etoolbar,
1416                gboolean mode)
1417 {
1418   EggEditableToolbarPrivate *priv = etoolbar->priv;
1419   int i, l, n_items;
1420
1421   i = priv->edit_mode;
1422   if (mode)
1423     {
1424       priv->edit_mode++;
1425     }
1426   else
1427     {
1428       g_return_if_fail (priv->edit_mode > 0);
1429       priv->edit_mode--;
1430     }
1431   i *= priv->edit_mode;
1432   
1433   if (i == 0)
1434     {
1435       for (i = get_n_toolbars (etoolbar)-1; i >= 0; i--)
1436         {
1437           GtkWidget *toolbar;
1438           
1439           toolbar = get_toolbar_nth (etoolbar, i);
1440           n_items = gtk_toolbar_get_n_items (GTK_TOOLBAR (toolbar));
1441
1442           if (n_items == 0 && priv->edit_mode == 0)
1443             {
1444               egg_toolbars_model_remove_toolbar (priv->model, i);
1445             }
1446           else
1447             {          
1448               for (l = 0; l < n_items; l++)
1449                 {
1450                   GtkToolItem *item;
1451               
1452                   item = gtk_toolbar_get_nth_item (GTK_TOOLBAR (toolbar), l);
1453                   
1454                   configure_item_cursor (item, etoolbar);
1455                   configure_item_sensitivity (item, etoolbar);
1456                 }
1457             }
1458         }
1459     }
1460 }
1461
1462 static void
1463 egg_editable_toolbar_set_property (GObject      *object,
1464                                    guint         prop_id,
1465                                    const GValue *value,
1466                                    GParamSpec   *pspec)
1467 {
1468   EggEditableToolbar *etoolbar = EGG_EDITABLE_TOOLBAR (object);
1469
1470   switch (prop_id)
1471     {
1472     case PROP_UI_MANAGER:
1473       egg_editable_toolbar_set_ui_manager (etoolbar, g_value_get_object (value));
1474       break;
1475     case PROP_TOOLBARS_MODEL:
1476       egg_editable_toolbar_set_model (etoolbar, g_value_get_object (value));
1477       break;
1478     case PROP_SELECTED:
1479       egg_editable_toolbar_set_selected (etoolbar, g_value_get_object (value));
1480       break;
1481     case PROP_POPUP_PATH:
1482       etoolbar->priv->popup_path = g_strdup (g_value_get_string (value));
1483       break;
1484     case PROP_EDIT_MODE:
1485       set_edit_mode (etoolbar, g_value_get_boolean (value));
1486       break;
1487     default:
1488       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1489       break;
1490     }
1491 }
1492
1493 static void
1494 egg_editable_toolbar_get_property (GObject    *object,
1495                                    guint       prop_id,
1496                                    GValue     *value,
1497                                    GParamSpec *pspec)
1498 {
1499   EggEditableToolbar *etoolbar = EGG_EDITABLE_TOOLBAR (object);
1500
1501   switch (prop_id)
1502     {
1503     case PROP_UI_MANAGER:
1504       g_value_set_object (value, etoolbar->priv->manager);
1505       break;
1506     case PROP_TOOLBARS_MODEL:
1507       g_value_set_object (value, etoolbar->priv->model);
1508       break;
1509     case PROP_SELECTED:
1510       g_value_set_object (value, etoolbar->priv->selected);
1511       break;
1512     case PROP_EDIT_MODE:
1513       g_value_set_boolean (value, etoolbar->priv->edit_mode>0);
1514       break;
1515     default:
1516       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1517       break;
1518     }
1519 }
1520
1521 static void
1522 egg_editable_toolbar_class_init (EggEditableToolbarClass *klass)
1523 {
1524   GObjectClass *object_class = G_OBJECT_CLASS (klass);
1525
1526   parent_class = g_type_class_peek_parent (klass);
1527
1528   object_class->dispose = egg_editable_toolbar_dispose;
1529   object_class->set_property = egg_editable_toolbar_set_property;
1530   object_class->get_property = egg_editable_toolbar_get_property;
1531
1532   egg_editable_toolbar_signals[ACTION_REQUEST] =
1533     g_signal_new ("action_request",
1534                   G_OBJECT_CLASS_TYPE (object_class),
1535                   G_SIGNAL_RUN_LAST,
1536                   G_STRUCT_OFFSET (EggEditableToolbarClass, action_request),
1537                   NULL, NULL, g_cclosure_marshal_VOID__STRING,
1538                   G_TYPE_NONE, 1, G_TYPE_STRING);
1539
1540   g_object_class_install_property (object_class,
1541                                    PROP_UI_MANAGER,
1542                                    g_param_spec_object ("ui-manager",
1543                                                         "UI-Mmanager",
1544                                                         "UI Manager",
1545                                                         GTK_TYPE_UI_MANAGER,
1546                                                         G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
1547   g_object_class_install_property (object_class,
1548                                    PROP_TOOLBARS_MODEL,
1549                                    g_param_spec_object ("model",
1550                                                         "Model",
1551                                                         "Toolbars Model",
1552                                                         EGG_TYPE_TOOLBARS_MODEL,
1553                                                         G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
1554   g_object_class_install_property (object_class,
1555                                    PROP_SELECTED,
1556                                    g_param_spec_object ("selected",
1557                                                         "Selected",
1558                                                         "Selected toolitem",
1559                                                         GTK_TYPE_TOOL_ITEM,
1560                                                         G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
1561
1562   g_object_class_install_property (object_class,
1563                                    PROP_POPUP_PATH,
1564                                    g_param_spec_string ("popup-path",
1565                                                         "popup-path",
1566                                                         "popup-path",
1567                                                         NULL,
1568                                                         G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
1569
1570   g_object_class_install_property (object_class,
1571                                    PROP_EDIT_MODE,
1572                                    g_param_spec_boolean ("edit-mode",
1573                                                          "Edit-Mode",
1574                                                          "Edit Mode",
1575                                                          FALSE,
1576                                                          G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
1577
1578   g_type_class_add_private (object_class, sizeof (EggEditableToolbarPrivate));
1579 }
1580
1581 GtkWidget *
1582 egg_editable_toolbar_new (GtkUIManager *manager,
1583                           const char *popup_path)
1584 {
1585     return GTK_WIDGET (g_object_new (EGG_TYPE_EDITABLE_TOOLBAR,
1586                                      "ui-manager", manager,
1587                                      "popup-path", popup_path,
1588                                      NULL));
1589 }
1590
1591 GtkWidget *
1592 egg_editable_toolbar_new_with_model (GtkUIManager *manager,
1593                                      EggToolbarsModel *model,
1594                                      const char *popup_path)
1595 {
1596   return GTK_WIDGET (g_object_new (EGG_TYPE_EDITABLE_TOOLBAR,
1597                                    "ui-manager", manager,
1598                                    "model", model,
1599                                    "popup-path", popup_path,
1600                                    NULL));
1601 }
1602
1603 gboolean
1604 egg_editable_toolbar_get_edit_mode (EggEditableToolbar *etoolbar)
1605 {
1606   EggEditableToolbarPrivate *priv = etoolbar->priv;
1607
1608   return priv->edit_mode > 0;
1609 }
1610
1611 void
1612 egg_editable_toolbar_set_edit_mode (EggEditableToolbar *etoolbar,
1613                                     gboolean mode)
1614 {
1615   set_edit_mode (etoolbar, mode);
1616   g_object_notify (G_OBJECT (etoolbar), "edit-mode");
1617 }
1618
1619 void
1620 egg_editable_toolbar_add_visibility (EggEditableToolbar *etoolbar,
1621                                      const char *path)
1622 {
1623   etoolbar->priv->visibility_paths = g_list_prepend
1624           (etoolbar->priv->visibility_paths, g_strdup (path));
1625 }
1626
1627 void
1628 egg_editable_toolbar_show (EggEditableToolbar *etoolbar,
1629                            const char *name)
1630 {
1631   EggEditableToolbarPrivate *priv = etoolbar->priv;
1632   EggToolbarsModel *model = priv->model;
1633   int i, n_toolbars;
1634
1635   n_toolbars = egg_toolbars_model_n_toolbars (model);
1636   for (i = 0; i < n_toolbars; i++)
1637     {
1638       const char *toolbar_name;
1639
1640       toolbar_name = egg_toolbars_model_toolbar_nth (model, i);
1641       if (strcmp (toolbar_name, name) == 0)
1642         {
1643           gtk_widget_show (get_dock_nth (etoolbar, i));
1644         }
1645     }
1646 }
1647
1648 void
1649 egg_editable_toolbar_hide (EggEditableToolbar *etoolbar,
1650                            const char *name)
1651 {
1652   EggEditableToolbarPrivate *priv = etoolbar->priv;
1653   EggToolbarsModel *model = priv->model;
1654   int i, n_toolbars;
1655
1656   n_toolbars = egg_toolbars_model_n_toolbars (model);
1657   for (i = 0; i < n_toolbars; i++)
1658     {
1659       const char *toolbar_name;
1660
1661       toolbar_name = egg_toolbars_model_toolbar_nth (model, i);
1662       if (strcmp (toolbar_name, name) == 0)
1663       {
1664         gtk_widget_hide (get_dock_nth (etoolbar, i));
1665       }
1666     }
1667 }
1668
1669 void
1670 egg_editable_toolbar_set_fixed (EggEditableToolbar *etoolbar,
1671                                 GtkToolbar *toolbar)
1672 {
1673   EggEditableToolbarPrivate *priv = etoolbar->priv;
1674
1675   g_return_if_fail (!toolbar || GTK_IS_TOOLBAR (toolbar));
1676
1677   if (priv->fixed_toolbar)
1678     {
1679       unparent_fixed (etoolbar);
1680       g_object_unref (priv->fixed_toolbar);
1681       priv->fixed_toolbar = NULL;
1682     }
1683
1684   if (toolbar)
1685     {
1686       priv->fixed_toolbar = GTK_WIDGET (toolbar);
1687       gtk_toolbar_set_show_arrow (toolbar, FALSE);
1688       g_object_ref_sink (toolbar);
1689     }
1690
1691   update_fixed (etoolbar);
1692 }
1693
1694 #define DEFAULT_ICON_HEIGHT 20
1695 #define DEFAULT_ICON_WIDTH 0
1696
1697 static void
1698 fake_expose_widget (GtkWidget *widget,
1699                     GdkPixmap *pixmap)
1700 {
1701   GdkWindow *tmp_window;
1702   GdkEventExpose event;
1703
1704   event.type = GDK_EXPOSE;
1705   event.window = pixmap;
1706   event.send_event = FALSE;
1707   event.area = widget->allocation;
1708   event.region = NULL;
1709   event.count = 0;
1710
1711   tmp_window = widget->window;
1712   widget->window = pixmap;
1713   gtk_widget_send_expose (widget, (GdkEvent *) &event);
1714   widget->window = tmp_window;
1715 }
1716
1717 /* We should probably experiment some more with this.
1718  * Right now the rendered icon is pretty good for most
1719  * themes. However, the icon is slightly large for themes
1720  * with large toolbar icons.
1721  */
1722 static GdkPixbuf *
1723 new_pixbuf_from_widget (GtkWidget *widget)
1724 {
1725   GtkWidget *window;
1726   GdkPixbuf *pixbuf;
1727   GtkRequisition requisition;
1728   GtkAllocation allocation;
1729   GdkPixmap *pixmap;
1730   GdkVisual *visual;
1731   gint icon_width;
1732   gint icon_height;
1733
1734   icon_width = DEFAULT_ICON_WIDTH;
1735
1736   if (!gtk_icon_size_lookup_for_settings (gtk_settings_get_default (), 
1737                                           GTK_ICON_SIZE_LARGE_TOOLBAR,
1738                                           NULL, 
1739                                           &icon_height))
1740     {
1741       icon_height = DEFAULT_ICON_HEIGHT;
1742     }
1743
1744   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1745   
1746   gtk_container_add (GTK_CONTAINER (window), widget);
1747   gtk_widget_realize (window);
1748   gtk_widget_show (widget);
1749   gtk_widget_realize (widget);
1750   gtk_widget_map (widget);
1751
1752   /* Gtk will never set the width or height of a window to 0. So setting the width to
1753    * 0 and than getting it will provide us with the minimum width needed to render
1754    * the icon correctly, without any additional window background noise.
1755    * This is needed mostly for pixmap based themes.
1756    */
1757   gtk_window_set_default_size (GTK_WINDOW (window), icon_width, icon_height);
1758   gtk_window_get_size (GTK_WINDOW (window),&icon_width, &icon_height);
1759
1760   gtk_widget_size_request (window, &requisition);
1761   allocation.x = 0;
1762   allocation.y = 0;
1763   allocation.width = icon_width;
1764   allocation.height = icon_height;
1765   gtk_widget_size_allocate (window, &allocation);
1766   gtk_widget_size_request (window, &requisition);
1767   
1768   /* Create a pixmap */
1769   visual = gtk_widget_get_visual (window);
1770   pixmap = gdk_pixmap_new (NULL, icon_width, icon_height, visual->depth);
1771   gdk_drawable_set_colormap (GDK_DRAWABLE (pixmap), gtk_widget_get_colormap (window));
1772
1773   /* Draw the window */
1774   gtk_widget_ensure_style (window);
1775   g_assert (window->style);
1776   g_assert (window->style->font_desc);
1777   
1778   fake_expose_widget (window, pixmap);
1779   fake_expose_widget (widget, pixmap);
1780   
1781   pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, icon_width, icon_height);
1782   gdk_pixbuf_get_from_drawable (pixbuf, pixmap, NULL, 0, 0, 0, 0, icon_width, icon_height);
1783
1784   gtk_widget_destroy (window);
1785
1786   return pixbuf;
1787 }
1788
1789 static GdkPixbuf *
1790 new_separator_pixbuf ()
1791 {
1792   GtkWidget *separator;
1793   GdkPixbuf *pixbuf;
1794
1795   separator = gtk_vseparator_new ();
1796   pixbuf = new_pixbuf_from_widget (separator);
1797   return pixbuf;
1798 }
1799
1800 static void
1801 update_separator_image (GtkImage *image)
1802 {
1803   GdkPixbuf *pixbuf = new_separator_pixbuf ();
1804   gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf);
1805   g_object_unref (pixbuf);
1806 }
1807
1808 static gboolean
1809 style_set_cb (GtkWidget *widget,
1810               GtkStyle *previous_style,
1811               GtkImage *image)
1812 {
1813
1814   update_separator_image (image);
1815   return FALSE;
1816 }
1817
1818 GtkWidget *
1819 _egg_editable_toolbar_new_separator_image (void)
1820 {
1821   GtkWidget *image = gtk_image_new ();
1822   update_separator_image (GTK_IMAGE (image));
1823   g_signal_connect (G_OBJECT (image), "style_set",
1824                     G_CALLBACK (style_set_cb), GTK_IMAGE (image));
1825
1826   return image;
1827 }
1828
1829 EggToolbarsModel *
1830 egg_editable_toolbar_get_model (EggEditableToolbar *etoolbar)
1831 {
1832   return etoolbar->priv->model;
1833 }