aboutsummaryrefslogblamecommitdiffstats
path: root/lib/egg/egg-editable-toolbar.c
blob: 8e875842eec506a16ab3c46bf0b70ba99f9d2063 (plain) (tree)






















                                                                              
                    


                   
                                                          






                                           
                                             
  
 


                                                              
                                             




















                                                                                 


























                                         
                      

                    


































                                                                               






















































































                                                                           

                 










                                                                










                                                              
 
                                                









































































































































































































                                                                              

                                       




























                                                                             



                                                                  
























                                                                            

                                 














                                                                          

































                                                                                 


                                                 




                                               
            












                                                                         















                                                                 

                             


                                                            
                                 
                                                            


                                                      





















































































































































































































                                                                                








                                                  


                                                                














                                                        





                                                                



































































                                                                          









































































                                                                                  
                                                                        

















                                                               

























                                                









                                                   
                                  



















                                                                       


                                                     





                                                             







                                                  
                  






























                                                                          





                                                                        
                                                            
                                                                       
                                                                
                                                                   
                                                           








                     




                                                               














































                                                                             



                                  







                                                                  















                                                                             
/*
 *  Copyright (C) 2003 Marco Pesenti Gritti
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "egg-editable-toolbar.h"
#include "egg-toolbars-group.h"
#include "eggtoolitem.h"
#include "eggtoolbar.h"
#include "eggseparatortoolitem.h"
#include "eggintl.h"

#include <string.h>

#define EGG_TOOLBAR_ITEM_TYPE "application/x-toolbar-item"

enum
{
  X_TOOLBAR_ITEM
};

static GtkTargetEntry dest_drag_types[] = {
  {EGG_TOOLBAR_ITEM_TYPE, 0, X_TOOLBAR_ITEM},
};

static int n_dest_drag_types = G_N_ELEMENTS (dest_drag_types);

static GtkTargetEntry source_drag_types[] = {
  {EGG_TOOLBAR_ITEM_TYPE, 0, X_TOOLBAR_ITEM},
};

static int n_source_drag_types = G_N_ELEMENTS (source_drag_types);

enum
{
  RESPONSE_ADD_TOOLBAR
};

static void egg_editable_toolbar_class_init (EggEditableToolbarClass *klass);
static void egg_editable_toolbar_init       (EggEditableToolbar *t);
static void egg_editable_toolbar_finalize   (GObject *object);
static void do_merge                (EggEditableToolbar *t);
static void setup_editor            (EggEditableToolbar *etoolbar,
                         GtkWidget          *window);
static void update_editor_sheet         (EggEditableToolbar *etoolbar);
static void egg_editable_toolbar_remove_cb  (EggAction          *action,
                         EggEditableToolbar *etoolbar);
static void egg_editable_toolbar_edit_cb    (EggAction          *action,
                         EggEditableToolbar *etoolbar);

enum
{
  PROP_0,
  PROP_TOOLBARS_GROUP,
  PROP_MENU_MERGE
};

static GObjectClass *parent_class = NULL;

struct EggEditableToolbarPrivate
{
  EggMenuMerge *merge;

  GtkWidget *editor;
  GtkWidget *table;
  GtkWidget *scrolled_window;

  GtkWidget *last_toolbar;

  guint ui_id;

  gboolean toolbars_dirty;
  gboolean editor_sheet_dirty;
  gboolean edit_mode;

  EggToolbarsGroup *group;

  GList *actions_list;

  GList *drag_types;
};

typedef struct
{
  EggEditableToolbar *etoolbar;
  EggToolbarsToolbar *t;
} ContextMenuData;

GType
egg_editable_toolbar_get_type (void)
{
  static GType egg_editable_toolbar_type = 0;

  if (egg_editable_toolbar_type == 0)
    {
      static const GTypeInfo our_info = {
    sizeof (EggEditableToolbarClass),
    NULL,           /* base_init */
    NULL,           /* base_finalize */
    (GClassInitFunc) egg_editable_toolbar_class_init,
    NULL,
    NULL,           /* class_data */
    sizeof (EggEditableToolbar),
    0,          /* n_preallocs */
    (GInstanceInitFunc) egg_editable_toolbar_init
      };

      egg_editable_toolbar_type = g_type_register_static (G_TYPE_OBJECT,
                              "EggEditableToolbar",
                              &our_info, 0);
    }

  return egg_editable_toolbar_type;
}

static EggAction *
find_action (EggEditableToolbar *t,
         const char         *name)
{
  GList *l = t->priv->merge->action_groups;
  EggAction *action = NULL;

  g_return_val_if_fail (IS_EGG_EDITABLE_TOOLBAR (t), NULL);
  g_return_val_if_fail (name != NULL, NULL);

  for (; l != NULL; l = l->next)
    {
      EggAction *tmp;

      tmp = egg_action_group_get_action (EGG_ACTION_GROUP (l->data), name);
      if (tmp)
    action = tmp;
    }

  return action;
}

static char *
impl_get_action_name (EggEditableToolbar *etoolbar,
              const char         *drag_type,
              const char     *data)
{
  return NULL;
}

static EggAction *
impl_get_action (EggEditableToolbar *etoolbar,
         const char     *name)
{
  EggAction *action;

  g_return_val_if_fail (IS_EGG_EDITABLE_TOOLBAR (etoolbar), NULL);

  action = find_action (etoolbar, name);

  return action;
}

static gboolean
ui_update (gpointer data)
{
  EggEditableToolbar *etoolbar = EGG_EDITABLE_TOOLBAR (data);

  g_return_val_if_fail (etoolbar != NULL, FALSE);

  if (etoolbar->priv->toolbars_dirty)
    {
      do_merge (etoolbar);
      etoolbar->priv->toolbars_dirty = FALSE;
    }

  if (etoolbar->priv->editor_sheet_dirty)
    {
      update_editor_sheet (etoolbar);
      etoolbar->priv->editor_sheet_dirty = FALSE;
    }

  return FALSE;
}

static void
queue_ui_update (EggEditableToolbar *etoolbar)
{
  g_return_if_fail (IS_EGG_EDITABLE_TOOLBAR (etoolbar));

  g_idle_add (ui_update, etoolbar);
}

static void
drag_data_received_cb (GtkWidget          *widget,
               GdkDragContext     *context,
               gint                x,
               gint                y,
               GtkSelectionData   *selection_data,
               guint               info,
               guint               time_,
               EggEditableToolbar *etoolbar)
{
  EggToolbarsToolbar *toolbar;
  const char *type = NULL;
  EggAction *action = NULL;
  int pos;
  GdkAtom target;
  GList *l;

  g_return_if_fail (IS_EGG_EDITABLE_TOOLBAR (etoolbar));

  toolbar =
    (EggToolbarsToolbar *) g_object_get_data (G_OBJECT (widget),
                          "toolbar_data");
  pos = egg_toolbar_get_drop_index (EGG_TOOLBAR (widget), x, y);

  /* HACK placeholder are implemented as separators */
  pos = pos / 3 + 1;

  target = gtk_drag_dest_find_target (widget, context, NULL);

  for (l = etoolbar->priv->drag_types; l != NULL; l = l->next)
    {
      char *drag_type = (char *)l->data;

      if (gdk_atom_intern (drag_type, FALSE) == target)
        {
          type = drag_type;
        }
    }

  if (strcmp (type, EGG_TOOLBAR_ITEM_TYPE) != 0)
    {
      char *name;

      name = egg_editable_toolbar_get_action_name
    (etoolbar, type, selection_data->data);
      if (name != NULL)
    {
      action = egg_editable_toolbar_get_action (etoolbar, name);
      g_free (name);
    }
    }
  else
    {
      action =
    egg_editable_toolbar_get_action (etoolbar, selection_data->data);
    }

  if (action)
    {
      egg_toolbars_group_add_item (etoolbar->priv->group, toolbar, pos,
                   action->name);
      etoolbar->priv->toolbars_dirty = TRUE;
    }
  else if (strcmp (selection_data->data, "separator") == 0)
    {
      egg_toolbars_group_add_item (etoolbar->priv->group, toolbar, pos,
                   "separator");
      etoolbar->priv->toolbars_dirty = TRUE;
    }

  queue_ui_update (etoolbar);
}

static void
drag_data_delete_cb (GtkWidget          *widget,
             GdkDragContext     *context,
             EggEditableToolbar *etoolbar)
{
  EggToolbarsItem *node;

  g_return_if_fail (IS_EGG_EDITABLE_TOOLBAR (etoolbar));

  node =
    (EggToolbarsItem *) g_object_get_data (G_OBJECT (widget), "item_data");
  g_return_if_fail (node != NULL);
  egg_toolbars_group_remove_item (etoolbar->priv->group, node);

  etoolbar->priv->toolbars_dirty = TRUE;
  queue_ui_update (etoolbar);
}

static void
drag_data_get_cb (GtkWidget          *widget,
          GdkDragContext     *context,
          GtkSelectionData   *selection_data,
          guint               info,
          guint32             time,
          EggEditableToolbar *etoolbar)
{
  EggAction *action;
  const char *target;

  g_return_if_fail (IS_EGG_EDITABLE_TOOLBAR (etoolbar));

  action = EGG_ACTION (g_object_get_data (G_OBJECT (widget), "egg-action"));

  if (action)
    {
      target = action->name;
    }
  else
    {
      target = "separator";
    }

  gtk_selection_data_set (selection_data,
              selection_data->target, 8, target, strlen (target));
}

static void
toolbar_drag_data_delete_cb (GtkWidget          *widget,
                 GdkDragContext     *context,
                 EggEditableToolbar *etoolbar)
{
  EggToolbarsToolbar *t;

  g_return_if_fail (IS_EGG_EDITABLE_TOOLBAR (etoolbar));

  t = (EggToolbarsToolbar *) g_object_get_data (G_OBJECT (widget),
                        "toolbar_drag_data");
  g_return_if_fail (t != NULL);

  egg_toolbars_group_remove_toolbar (etoolbar->priv->group, t);

  etoolbar->priv->toolbars_dirty = TRUE;
  queue_ui_update (etoolbar);
}

static void
toolbar_drag_data_get_cb (GtkWidget          *widget,
              GdkDragContext     *context,
              GtkSelectionData   *selection_data,
              guint               info,
              guint32             time,
              EggEditableToolbar *etoolbar)
{
  EggToolbarsToolbar *t;
  char *target;

  g_return_if_fail (IS_EGG_EDITABLE_TOOLBAR (etoolbar));

  t = (EggToolbarsToolbar *) g_object_get_data (G_OBJECT (widget),
                        "toolbar_drag_data");

  target = t->id;

  gtk_selection_data_set (selection_data,
              selection_data->target, 8, target, strlen (target));
}

static void
egg_editable_toolbar_remove_cb (EggAction          *action,
                EggEditableToolbar *etoolbar)
{
  EggToolbarsToolbar *t;

  t = g_object_get_data (G_OBJECT (etoolbar), "popup_toolbar");

  egg_toolbars_group_remove_toolbar (etoolbar->priv->group, t);
}

static void
egg_editable_toolbar_edit_cb (EggAction          *action,
                  EggEditableToolbar *etoolbar)
{
  egg_editable_toolbar_edit (etoolbar, NULL);
}

static GtkWidget *
get_item_widget (EggEditableToolbar *t,
         gpointer            data)
{
  GtkWidget *widget;
  char *path;

  path = egg_toolbars_group_get_path (t->priv->group, data);
  g_return_val_if_fail (path != NULL, NULL);

  widget = egg_menu_merge_get_widget (t->priv->merge, path);
  g_free (path);

  return widget;
}

static void
connect_item_drag_source (EggToolbarsItem    *item,
              EggEditableToolbar *etoolbar)
{
  GtkWidget *toolitem;

  g_return_if_fail (IS_EGG_EDITABLE_TOOLBAR (etoolbar));
  g_return_if_fail (item != NULL);

  toolitem = get_item_widget (etoolbar, item);

  if (!g_object_get_data (G_OBJECT (toolitem), "drag_source_set"))
    {
      g_object_set_data (G_OBJECT (toolitem), "drag_source_set",
             GINT_TO_POINTER (TRUE));

      egg_tool_item_set_use_drag_window (EGG_TOOL_ITEM (toolitem), TRUE);

      g_object_set_data (G_OBJECT (toolitem), "item_data", item);

      gtk_drag_source_set (toolitem, GDK_BUTTON1_MASK,
               source_drag_types, n_source_drag_types,
               GDK_ACTION_MOVE);
      g_signal_connect (toolitem, "drag_data_get",
            G_CALLBACK (drag_data_get_cb), etoolbar);
      g_signal_connect (toolitem, "drag_data_delete",
            G_CALLBACK (drag_data_delete_cb), etoolbar);
    }
}

static void
disconnect_item_drag_source (EggToolbarsItem    *item,
                 EggEditableToolbar *etoolbar)
{
  GtkWidget *toolitem;

  g_return_if_fail (IS_EGG_EDITABLE_TOOLBAR (etoolbar));
  g_return_if_fail (item != NULL);

  toolitem = get_item_widget (etoolbar, item);

  if (g_object_get_data (G_OBJECT (toolitem), "drag_source_set"))
    {
      g_object_set_data (G_OBJECT (toolitem), "drag_source_set",
             GINT_TO_POINTER (FALSE));

      egg_tool_item_set_use_drag_window (EGG_TOOL_ITEM (toolitem), FALSE);

      gtk_drag_source_unset (toolitem);

      g_signal_handlers_disconnect_by_func (toolitem,
                        G_CALLBACK (drag_data_get_cb),
                        etoolbar);
      g_signal_handlers_disconnect_by_func (toolitem,
                        G_CALLBACK (drag_data_delete_cb),
                        etoolbar);
    }
}

static void
connect_toolbar_drag_source (EggToolbarsToolbar *t,
                 EggEditableToolbar *etoolbar)
{
  GtkWidget *tb;

  g_return_if_fail (IS_EGG_EDITABLE_TOOLBAR (etoolbar));
  g_return_if_fail (t != NULL);

  tb = get_item_widget (etoolbar, t);

  g_return_if_fail (tb != NULL);

  if (!g_object_get_data (G_OBJECT (tb), "drag_source_set"))
    {
      g_object_set_data (G_OBJECT (tb), "drag_source_set",
             GINT_TO_POINTER (TRUE));

      g_object_set_data (G_OBJECT (tb), "toolbar_drag_data", t);

      gtk_drag_source_set (tb, GDK_BUTTON1_MASK,
               source_drag_types, n_source_drag_types,
               GDK_ACTION_MOVE);

      g_signal_connect (tb, "drag_data_get",
            G_CALLBACK (toolbar_drag_data_get_cb), etoolbar);
      g_signal_connect (tb, "drag_data_delete",
            G_CALLBACK (toolbar_drag_data_delete_cb), etoolbar);
    }
}

static void
disconnect_toolbar_drag_source (EggToolbarsToolbar *t,
                EggEditableToolbar *etoolbar)
{
  GtkWidget *tb;

  g_return_if_fail (IS_EGG_EDITABLE_TOOLBAR (etoolbar));
  g_return_if_fail (t != NULL);

  tb = get_item_widget (etoolbar, t);

  g_return_if_fail (tb != NULL);

  if (g_object_get_data (G_OBJECT (tb), "drag_source_set"))
    {
      g_object_set_data (G_OBJECT (tb), "drag_source_set",
             GINT_TO_POINTER (FALSE));

      gtk_drag_source_unset (tb);

      g_signal_handlers_disconnect_by_func (tb,
                        G_CALLBACK
                        (toolbar_drag_data_get_cb),
                        etoolbar);
      g_signal_handlers_disconnect_by_func (tb,
                        G_CALLBACK
                        (toolbar_drag_data_delete_cb),
                        etoolbar);
    }
}

static void
popup_toolbar_context_menu (EggToolbar      *toolbar,
                ContextMenuData *data)
{
  GtkWidget *menu;
  GtkWidget *item;
  GtkWidget *image;
  EggEditableToolbar *etoolbar = EGG_EDITABLE_TOOLBAR (data->etoolbar);

  menu = gtk_menu_new ();

  if (etoolbar->priv->edit_mode)
  {
    item = gtk_image_menu_item_new_with_mnemonic (_("_Remove Toolbar"));
    gtk_widget_show (item);
    image = gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU);
    gtk_widget_show (image);
    gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
    g_object_set_data (G_OBJECT (etoolbar), "popup_toolbar", data->t);
    g_signal_connect (item, "activate",
              G_CALLBACK (egg_editable_toolbar_remove_cb),
              etoolbar);
  }
  else
  {
    item = gtk_image_menu_item_new_with_mnemonic (_("_Edit Toolbars..."));
    gtk_widget_show (item);
    image = gtk_image_new_from_stock (GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU);
    gtk_widget_show (image);
    gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
    g_signal_connect (item, "activate",
              G_CALLBACK (egg_editable_toolbar_edit_cb),
              etoolbar);
  }

  gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 2,
          gtk_get_current_event_time ());
}

static GtkTargetList *
get_dest_targets (EggEditableToolbar *etoolbar)
{
  GList *l;
  GtkTargetList *targets;
  int i = 0;

  targets = gtk_target_list_new (NULL, 0);

  for (l = etoolbar->priv->drag_types; l != NULL; l = l->next)
    {
      char *type = (char *)l->data;
      gtk_target_list_add (targets, gdk_atom_intern (type, FALSE), 0, i);
      i++;
    }

  return targets;
}

static void
setup_toolbar (EggToolbarsToolbar *toolbar,
           EggEditableToolbar *etoolbar)
{
  GtkWidget *widget;
  ContextMenuData *data;
  int signal_id;

  g_return_if_fail (IS_EGG_EDITABLE_TOOLBAR (etoolbar));
  g_return_if_fail (toolbar != NULL);

  widget = get_item_widget (etoolbar, toolbar);
  g_object_set_data (G_OBJECT (widget), "toolbar_data", toolbar);

  if (!g_object_get_data (G_OBJECT (widget), "drag_dest_set"))
    {
      GtkTargetList *targets;

      g_object_set_data (G_OBJECT (widget), "drag_dest_set",
             GINT_TO_POINTER (TRUE));
      gtk_drag_dest_set (widget, GTK_DEST_DEFAULT_ALL,
             NULL, 0,
             GDK_ACTION_MOVE | GDK_ACTION_COPY);
      targets = get_dest_targets (etoolbar);
      gtk_drag_dest_set_target_list (widget, targets);
      gtk_target_list_unref (targets);
      g_signal_connect (widget, "drag_data_received",
            G_CALLBACK (drag_data_received_cb), etoolbar);
    }

  if (!g_object_get_data (G_OBJECT (widget), "popup_signal_id"))
    {
      data = g_new0 (ContextMenuData, 1);
      data->etoolbar = etoolbar;
      data->t = toolbar;

      signal_id = g_signal_connect_data (widget,
                     "popup_context_menu",
                     G_CALLBACK
                     (popup_toolbar_context_menu), data,
                     (GClosureNotify) g_free, 0);

      g_object_set_data (G_OBJECT (widget), "popup_signal_id",
             GINT_TO_POINTER (signal_id));
    }

  etoolbar->priv->last_toolbar = widget;
}

static void
ensure_toolbar_min_size (EggToolbarsToolbar *toolbar,
             EggEditableToolbar *t)
{
  GtkWidget *widget;

  g_return_if_fail (IS_EGG_EDITABLE_TOOLBAR (t));
  g_return_if_fail (toolbar != NULL);

  widget = get_item_widget (t, toolbar);

  if (EGG_TOOLBAR (widget)->num_children == 0)
    {
      gtk_widget_set_size_request (widget, -1, 20);
    }
  else
    {
      gtk_widget_set_size_request (widget, -1, -1);
    }
}

static void
do_merge (EggEditableToolbar *t)
{
  char *str;
  guint ui_id;

  g_return_if_fail (IS_EGG_EDITABLE_TOOLBAR (t));

  str = egg_toolbars_group_to_string (t->priv->group);
  g_return_if_fail (str != NULL);

  ui_id = egg_menu_merge_add_ui_from_string (t->priv->merge, str, -1, NULL);

  if (t->priv->ui_id != 0)
    {
      egg_menu_merge_remove_ui (t->priv->merge, t->priv->ui_id);
    }

  t->priv->ui_id = ui_id;

  egg_menu_merge_ensure_update (t->priv->merge);

  egg_toolbars_group_foreach_toolbar (t->priv->group,
                      (EggToolbarsGroupForeachToolbarFunc)
                      setup_toolbar, t);

  if (t->priv->edit_mode)
    {
      egg_toolbars_group_foreach_item (t->priv->group,
                       (EggToolbarsGroupForeachItemFunc)
                       connect_item_drag_source, t);

      egg_toolbars_group_foreach_toolbar (t->priv->group,
                      (EggToolbarsGroupForeachToolbarFunc)
                      connect_toolbar_drag_source, t);
    }

  egg_toolbars_group_foreach_toolbar (t->priv->group,
                      (EggToolbarsGroupForeachToolbarFunc)
                      ensure_toolbar_min_size, t);

  g_free (str);
}

static void
ensure_action (EggToolbarsItem *item,
           EggEditableToolbar *t)
{
  g_return_if_fail (IS_EGG_EDITABLE_TOOLBAR (t));
  g_return_if_fail (item != NULL);

  egg_editable_toolbar_get_action (t, item->action);
}

static void
group_changed_cb (EggToolbarsGroup   *group,
          EggEditableToolbar *t)
{
  g_return_if_fail (IS_EGG_TOOLBARS_GROUP (group));
  g_return_if_fail (IS_EGG_EDITABLE_TOOLBAR (t));

  t->priv->toolbars_dirty = TRUE;

  egg_toolbars_group_foreach_item (t->priv->group,
                   (EggToolbarsGroupForeachItemFunc)
                   ensure_action, t);

  queue_ui_update (t);
}

static void
egg_editable_toolbar_set_group (EggEditableToolbar *t,
                EggToolbarsGroup   *group)
{
  g_return_if_fail (IS_EGG_TOOLBARS_GROUP (group));
  g_return_if_fail (IS_EGG_EDITABLE_TOOLBAR (t));

  t->priv->group = group;

  g_signal_connect_object (group, "changed",
               G_CALLBACK (group_changed_cb), t, 0);
}

static void
egg_editable_toolbar_set_merge (EggEditableToolbar *t,
                EggMenuMerge       *merge)
{
  g_return_if_fail (EGG_IS_MENU_MERGE (merge));
  g_return_if_fail (IS_EGG_EDITABLE_TOOLBAR (t));

  t->priv->merge = merge;

  egg_toolbars_group_foreach_item (t->priv->group,
                   (EggToolbarsGroupForeachItemFunc)
                   ensure_action, t);

  do_merge (t);
}

static void
egg_editable_toolbar_set_property (GObject      *object,
                   guint         prop_id,
                   const GValue *value,
                   GParamSpec   *pspec)
{
  EggEditableToolbar *t = EGG_EDITABLE_TOOLBAR (object);

  switch (prop_id)
    {
    case PROP_MENU_MERGE:
      egg_editable_toolbar_set_merge (t, g_value_get_object (value));
      break;
    case PROP_TOOLBARS_GROUP:
      egg_editable_toolbar_set_group (t, g_value_get_object (value));
      break;
    }
}

static void
egg_editable_toolbar_get_property (GObject    *object,
                   guint       prop_id,
                   GValue     *value,
                   GParamSpec *pspec)
{
  EggEditableToolbar *t = EGG_EDITABLE_TOOLBAR (object);

  switch (prop_id)
    {
    case PROP_MENU_MERGE:
      g_value_set_object (value, t->priv->merge);
      break;
    case PROP_TOOLBARS_GROUP:
      g_value_set_object (value, t->priv->group);
      break;
    }
}

static void
egg_editable_toolbar_class_init (EggEditableToolbarClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  parent_class = g_type_class_peek_parent (klass);

  object_class->finalize = egg_editable_toolbar_finalize;
  object_class->set_property = egg_editable_toolbar_set_property;
  object_class->get_property = egg_editable_toolbar_get_property;

  klass->get_action = impl_get_action;
  klass->get_action_name = impl_get_action_name;

  g_object_class_install_property (object_class,
                   PROP_MENU_MERGE,
                   g_param_spec_object ("MenuMerge",
                            "MenuMerge",
                            "Menu merge",
                            EGG_TYPE_MENU_MERGE,
                            G_PARAM_READWRITE));
  g_object_class_install_property (object_class,
                   PROP_TOOLBARS_GROUP,
                   g_param_spec_object ("ToolbarsGroup",
                            "ToolbarsGroup",
                            "Toolbars Group",
                            EGG_TOOLBARS_GROUP_TYPE,
                            G_PARAM_READWRITE));
}

static void
egg_editable_toolbar_init (EggEditableToolbar *t)
{
  t->priv = g_new0 (EggEditableToolbarPrivate, 1);

  t->priv->merge = NULL;
  t->priv->editor = NULL;
  t->priv->ui_id = 0;
  t->priv->toolbars_dirty = FALSE;
  t->priv->editor_sheet_dirty = FALSE;
  t->priv->edit_mode = FALSE;
  t->priv->actions_list = NULL;
  t->priv->drag_types = NULL;

  egg_editable_toolbar_add_drag_type (t, EGG_TOOLBAR_ITEM_TYPE);
}

static void
egg_editable_toolbar_finalize (GObject *object)
{
  EggEditableToolbar *t = EGG_EDITABLE_TOOLBAR (object);

  g_return_if_fail (object != NULL);
  g_return_if_fail (IS_EGG_EDITABLE_TOOLBAR (object));

  if (t->priv->editor)
    {
      gtk_widget_destroy (t->priv->editor);
    }

  if (t->priv->drag_types)
    {
      g_list_foreach (t->priv->drag_types, (GFunc)g_free, NULL);
      g_list_free (t->priv->drag_types);
    }

  g_free (t->priv);

  G_OBJECT_CLASS (parent_class)->finalize (object);
}

EggEditableToolbar *
egg_editable_toolbar_new (EggMenuMerge     *merge,
              EggToolbarsGroup *group)
{
  EggEditableToolbar *t;

  t = EGG_EDITABLE_TOOLBAR (g_object_new (EGG_EDITABLE_TOOLBAR_TYPE,
                      "ToolbarsGroup", group,
                      "MenuMerge", merge, NULL));

  g_return_val_if_fail (t->priv != NULL, NULL);

  return t;
}

static void
hide_editor (EggEditableToolbar *etoolbar)
{
  g_return_if_fail (IS_EGG_EDITABLE_TOOLBAR (etoolbar));

  gtk_widget_hide (GTK_WIDGET (etoolbar->priv->editor));
}

static void
editor_drag_data_received_cb (GtkWidget          *widget,
                  GdkDragContext     *context,
                  gint                x,
                  gint                y,
                  GtkSelectionData   *selection_data,
                  guint               info,
                  guint               time_,
                  EggEditableToolbar *etoolbar)
{
  g_return_if_fail (IS_EGG_EDITABLE_TOOLBAR (etoolbar));

  etoolbar->priv->editor_sheet_dirty = TRUE;
  queue_ui_update (etoolbar);
}

static void
editor_drag_data_delete_cb (GtkWidget          *widget,
                GdkDragContext     *context,
                EggEditableToolbar *etoolbar)
{
  g_return_if_fail (IS_EGG_EDITABLE_TOOLBAR (etoolbar));

  etoolbar->priv->editor_sheet_dirty = TRUE;
  queue_ui_update (etoolbar);
}

static void
editor_close (EggEditableToolbar * etoolbar)
{
  g_return_if_fail (IS_EGG_EDITABLE_TOOLBAR (etoolbar));

  etoolbar->priv->edit_mode = FALSE;
  egg_toolbars_group_foreach_item (etoolbar->priv->group,
                   (EggToolbarsGroupForeachItemFunc)
                   disconnect_item_drag_source, etoolbar);
  egg_toolbars_group_foreach_toolbar (etoolbar->priv->group,
                      (EggToolbarsGroupForeachToolbarFunc)
                      disconnect_toolbar_drag_source,
                      etoolbar);
  hide_editor (etoolbar);
}

static void
editor_add_toolbar (EggEditableToolbar *etoolbar)
{
  g_return_if_fail (IS_EGG_EDITABLE_TOOLBAR (etoolbar));

  egg_toolbars_group_add_toolbar (etoolbar->priv->group);

  etoolbar->priv->toolbars_dirty = TRUE;
  queue_ui_update (etoolbar);
}

static void
dialog_response_cb (GtkDialog          *dialog,
            gint                response_id,
            EggEditableToolbar *etoolbar)
{
  switch (response_id)
    {
    case RESPONSE_ADD_TOOLBAR:
      editor_add_toolbar (etoolbar);
      break;
    case GTK_RESPONSE_CLOSE:
      editor_close (etoolbar);
      break;
    }
}

static void
setup_editor (EggEditableToolbar *etoolbar,
          GtkWidget          *window)
{
  GtkWidget *editor;
  GtkWidget *scrolled_window;
  GtkWidget *vbox;
  GtkWidget *label_hbox;
  GtkWidget *image;
  GtkWidget *label;

  g_return_if_fail (IS_EGG_EDITABLE_TOOLBAR (etoolbar));

  editor = gtk_dialog_new (),
    gtk_dialog_set_has_separator (GTK_DIALOG (editor), FALSE);
  gtk_widget_set_size_request (GTK_WIDGET (editor), 500, 330);
  gtk_window_set_transient_for (GTK_WINDOW (editor), GTK_WINDOW (window));
  gtk_window_set_title (GTK_WINDOW (editor), "Toolbar editor");
  etoolbar->priv->editor = editor;

  vbox = gtk_vbox_new (FALSE, 12);
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
  gtk_widget_show (vbox);
  gtk_container_add (GTK_CONTAINER (GTK_DIALOG (editor)->vbox), vbox);
  scrolled_window = gtk_scrolled_window_new (NULL, NULL);
  etoolbar->priv->scrolled_window = scrolled_window;
  gtk_widget_show (scrolled_window);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
                  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
  gtk_box_pack_start (GTK_BOX (vbox), scrolled_window, TRUE, TRUE, 0);
  label_hbox = gtk_hbox_new (FALSE, 6);
  gtk_widget_show (label_hbox);
  gtk_box_pack_start (GTK_BOX (vbox), label_hbox, FALSE, FALSE, 0);
  image =
    gtk_image_new_from_stock (GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_DIALOG);
  gtk_widget_show (image);
  gtk_box_pack_start (GTK_BOX (label_hbox), image, FALSE, FALSE, 0);
  label = gtk_label_new (_("Drag an item onto the toolbars above to add it, "
               "from the toolbars in the items table to remove it."));
  gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
  gtk_widget_show (label);
  gtk_box_pack_start (GTK_BOX (label_hbox), label, FALSE, TRUE, 0);

  gtk_dialog_add_button (GTK_DIALOG (editor),
             _("_Add a New Toolbar"), RESPONSE_ADD_TOOLBAR);
  gtk_dialog_add_button (GTK_DIALOG (editor),
             GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);

  g_signal_connect (editor, "response",
            G_CALLBACK (dialog_response_cb), etoolbar);

  update_editor_sheet (etoolbar);
}

static void
add_to_list (EggToolbarsItem  *item,
         GList           **l)
{
  g_return_if_fail (item != NULL);

  *l = g_list_append (*l, item);
}

static gchar *
elide_underscores (const gchar *original)
{
  gchar *q, *result;
  const gchar *p;
  gboolean last_underscore;

  q = result = g_malloc (strlen (original) + 1);
  last_underscore = FALSE;
  
  for (p = original; *p; p++)
    {
      if (!last_underscore && *p == '_')
    last_underscore = TRUE;
      else
    {
      last_underscore = FALSE;
      *q++ = *p;
    }
    }
  
  *q = '\0';
  
  return result;
}

static GtkWidget *
editor_create_item (EggEditableToolbar *etoolbar,
            const char         *stock_id,
            const char         *label_text,
            GdkDragAction   action)
{
  GtkWidget *event_box;
  GtkWidget *vbox;
  GtkWidget *icon;
  GtkWidget *label;
  gchar *label_no_mnemonic = NULL;

  event_box = gtk_event_box_new ();
  gtk_widget_show (event_box);
  gtk_drag_source_set (event_box,
               GDK_BUTTON1_MASK,
               source_drag_types, 1, action);
  g_signal_connect (event_box, "drag_data_get",
            G_CALLBACK (drag_data_get_cb), etoolbar);
  g_signal_connect (event_box, "drag_data_delete",
            G_CALLBACK (editor_drag_data_delete_cb), etoolbar);

  vbox = gtk_vbox_new (0, FALSE);
  gtk_widget_show (vbox);
  gtk_container_add (GTK_CONTAINER (event_box), vbox);

  icon = gtk_image_new_from_stock
    (stock_id ? stock_id : GTK_STOCK_DND,
     GTK_ICON_SIZE_LARGE_TOOLBAR);
  gtk_widget_show (icon);
  gtk_box_pack_start (GTK_BOX (vbox), icon, FALSE, TRUE, 0);
  label_no_mnemonic = elide_underscores (label_text);
  label = gtk_label_new (label_no_mnemonic);
  g_free (label_no_mnemonic);
  gtk_widget_show (label);
  gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, TRUE, 0);

  return event_box;
}

static void
update_editor_sheet (EggEditableToolbar *etoolbar)
{
  GList *l;
  GList *to_drag = NULL;
  int x, y, height, width;
  GtkWidget *table;
  GtkWidget *viewport;
  GtkWidget *item;

  g_return_if_fail (IS_EGG_EDITABLE_TOOLBAR (etoolbar));

  viewport = GTK_BIN (etoolbar->priv->scrolled_window)->child;
  if (viewport)
    {
      table = GTK_BIN (viewport)->child;
      gtk_container_remove (GTK_CONTAINER (viewport), table);
    }
  table = gtk_table_new (0, 0, TRUE);
  etoolbar->priv->table = table;
  gtk_container_set_border_width (GTK_CONTAINER (table), 12);
  gtk_widget_show (table);
  gtk_scrolled_window_add_with_viewport
    (GTK_SCROLLED_WINDOW (etoolbar->priv->scrolled_window), table);
  gtk_drag_dest_set (table, GTK_DEST_DEFAULT_ALL,
             dest_drag_types, n_dest_drag_types, GDK_ACTION_MOVE);
  g_signal_connect (table, "drag_data_received",
            G_CALLBACK (editor_drag_data_received_cb), etoolbar);

  egg_toolbars_group_foreach_available (etoolbar->priv->group,
                    (EggToolbarsGroupForeachItemFunc)
                    add_to_list, &to_drag);

  x = y = 0;
  width = 4;
  height = (g_list_length (to_drag) - 1) / width + 1;
  gtk_table_resize (GTK_TABLE (etoolbar->priv->table), height, width);

  for (l = to_drag; l != NULL; l = l->next)
    {
      EggToolbarsItem *node = (EggToolbarsItem *) (l->data);
      EggAction *action;

      action = egg_editable_toolbar_get_action (etoolbar, node->action);
      g_return_if_fail (action != NULL);

      item = editor_create_item (etoolbar, action->stock_id,
                 action->short_label, GDK_ACTION_MOVE);
      g_object_set_data (G_OBJECT (item), "egg-action", action);
      gtk_table_attach_defaults (GTK_TABLE (etoolbar->priv->table),
                         item, x, x + 1, y, y + 1);

      x++;
      if (x >= width)
    {
      x = 0;
      y++;
    }
    }

  item = editor_create_item (etoolbar, NULL, _("Separator"),
                 GDK_ACTION_COPY);
  gtk_table_attach_defaults (GTK_TABLE (etoolbar->priv->table),
                     item, x, x + 1, y, y + 1);

  g_list_free (to_drag);
}

static void
show_editor (EggEditableToolbar *etoolbar)
{
  GtkWidget *editor = etoolbar->priv->editor;

  g_return_if_fail (IS_EGG_EDITABLE_TOOLBAR (etoolbar));
  g_return_if_fail (editor != NULL);

  gtk_widget_show (GTK_WIDGET (editor));
}

static void
set_action_sensitive (EggToolbarsItem    *item,
              EggEditableToolbar *etoolbar)
{
  EggAction *action;

  g_return_if_fail (IS_EGG_EDITABLE_TOOLBAR (etoolbar));
  g_return_if_fail (item != NULL);

  if (!item->separator)
    {
      action = find_action (etoolbar, item->action);
      g_object_set (action, "sensitive", TRUE, NULL);
    }
}

void
egg_editable_toolbar_edit (EggEditableToolbar *etoolbar,
               GtkWidget          *window)
{
  g_return_if_fail (IS_EGG_EDITABLE_TOOLBAR (etoolbar));

  etoolbar->priv->edit_mode = TRUE;
  egg_toolbars_group_foreach_item (etoolbar->priv->group,
                   (EggToolbarsGroupForeachItemFunc)
                   connect_item_drag_source, etoolbar);
  egg_toolbars_group_foreach_item (etoolbar->priv->group,
                   (EggToolbarsGroupForeachItemFunc)
                   set_action_sensitive, etoolbar);
  egg_toolbars_group_foreach_toolbar (etoolbar->priv->group,
                      (EggToolbarsGroupForeachToolbarFunc)
                      connect_toolbar_drag_source, etoolbar);

  setup_editor (etoolbar, window);
  show_editor (etoolbar);
}

void
egg_editable_toolbar_add_drag_type (EggEditableToolbar *etoolbar,
                    const char         *drag_type)
{
  etoolbar->priv->drag_types = g_list_append
    (etoolbar->priv->drag_types, g_strdup (drag_type));
}

char *
egg_editable_toolbar_get_action_name (EggEditableToolbar *etoolbar,
                      const char         *drag_type,
                      const char         *data)
{
  EggEditableToolbarClass *klass = EGG_EDITABLE_TOOLBAR_GET_CLASS (etoolbar);
  return klass->get_action_name (etoolbar, drag_type, data);
}

EggAction *
egg_editable_toolbar_get_action (EggEditableToolbar *etoolbar,
                 const char         *name)
{
  EggEditableToolbarClass *klass = EGG_EDITABLE_TOOLBAR_GET_CLASS (etoolbar);
  return klass->get_action (etoolbar, name);
}