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