aboutsummaryrefslogblamecommitdiffstats
path: root/lib/widgets/ephy-editable-toolbar.c
blob: eddb39bd9ebe71b95e0e6691f9737b0b4d65ffa2 (plain) (tree)



















                                                                              
                                
                       
                     
                        
                       
 
                                








                                                                                         
                                          
 






                                              
                                       
  
 






                                                                                   
 


               
                            


                       

                                         


                                 

                          
                            
                         


                                   
 

                                
                    



                                    
                           
 
                                 
 

                            
 

























                                                                                           

 





                                                      


                                                                  











                                                           






                                               

                                                                         











                                                      




                                                                     

                                                       



















                                                                           






                                               

                                                               


                                         
           





                                                        

                                                     
 


                                     

                                
                          
 

                                                               
                                                                                               


                     
                                                                                                 
                                                   






                                         
 

                                          





                                                                   
                                                                                         



                                                                                     


                                                      

 



                                                   
 
                               
 

                                                               
                                                                                      
                                        
                                                                      
 
                                              
                                   

 






                                                   
 

                           
 

                                                               

                                                                                  
 
                                               
 


                                                            

 














                                                                   
           
                                                                                
 
                            
 


                                                               
                                                    

                                                                        
         








                                                                          



           
                                                                                   
 
                            
 


                                                               
                                                    

                                                                       
         








                                                                                       



           
                                                                           
 
                          
 


                                                               
                                                     
                                                                       
 

                                                                    
                                                                   








                                                                      
 
                                              


           
                                                                  
 

                            
 


                                                               
                                                                          
                                        
 
                                                    


                                                                       
 









                                                                        
         

                      


           
                                                                              
 
                          
 


                                                        
                                              
 








                                                             

           
                                 
 
                  
                    
 
                                                        
 
                                                             
                                       


                                 
                                                 
                                                










                                                          

                                                      





                                                                                  
 

                               


                                                                                    

         


                                                                                  
 



                     
                                                              
 


                                                        
                                                                 


           
                                                                   
 


                                                          






                                                                            




                                                                                  


                                                          

                               


                                                               


           

                                                                             


                                                        



                               



                                                                            
                     














                                                                                


                                                                                















                                                                   


                                                                   










                                                                  


                                                                        
                                            







                                                                                  






                                                                                       







                                                         
                               
                           


                                            
                                   
                                     









                                                                




                                                     



















                                                                            

           
                                                                         

                                                               
                        

                          




                                   





                         

                                                  
                                        







                                                                 


                                                 
                                                      
 

                                                         
 
                                                                      
                                                                
                                                                       
                                                              




                                                                                
                                                                       
                                                                 
                                                                        
                                                               




                                                                                 







                                                                                   

 
           

                                           

                                                               






                                                                  

                                                               
                                          



                                                                            





                                                                        

                                                               
                                                                


                                                












                                                               

                                                               


                                                  






                                                          

                                                               


                                                  


           

                                            









                                   

                                                               






















































                                                                                        

                                               

                                        



                                      

                                                   
                 
                              
                                

                            
 

                                                               



















                                                                               


                                                                                 
 

                  
                                                           

                                                                            
                                                 




                                     




                                                                        
                                                  




                                                      
                                                          



                                                                


                                                                          
 

























                                                                               
 





                                                      


                                

                                                                          
                                                                      




                                                                         


                                                                

 







                                               

                                                                          








                                                                        
                                                                                   








                                                                   
           
                                           

                                                   
 
                                                               

                                          

                                              



                                                       

 
           
                                                                            
 
                          
 


                                                               
                             
         

                                                               


         


                                                          

                                                               
                                         





                                                                              
                                









                                                                                      
 
/*
 *  Copyright (C) 2000 Marco Pesenti Gritti
 *            (C) 2001, 2002 Jorn Baayen
 *
 *  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 "ephy-editable-toolbar.h"
#include "ephy-toolbars-group.h"
#include "ephy-debug.h"
#include "ephy-dnd.h"
#include "eggtoolitem.h"
#include "eggtoolbar.h"

#include <libgnome/gnome-i18n.h>
#include <string.h>

/* This is copied from gtkscrollbarwindow.c */
#define DEFAULT_SCROLLBAR_SPACING  3

#define SCROLLBAR_SPACING(w)                                                            \
  (GTK_SCROLLED_WINDOW_GET_CLASS (w)->scrollbar_spacing >= 0 ?                          \
   GTK_SCROLLED_WINDOW_GET_CLASS (w)->scrollbar_spacing : DEFAULT_SCROLLBAR_SPACING)

static GtkTargetEntry dest_drag_types [] =
{
        { "EPHY_TOOLBAR_BUTTON", 0, 0 },
    /* FIXME generic way to add types */
        { EPHY_DND_URL_TYPE, 0, EPHY_DND_URL }
};

static GtkTargetEntry source_drag_types [] =
{
        { "EPHY_TOOLBAR_BUTTON", 0, 0 }
};

static void ephy_editable_toolbar_class_init (EphyEditableToolbarClass *klass);
static void ephy_editable_toolbar_init   (EphyEditableToolbar *t);
static void ephy_editable_toolbar_finalize   (GObject *object);
static void do_merge             (EphyEditableToolbar *t);
static void setup_editor             (EphyEditableToolbar *etoolbar);
static void update_editor_sheet      (EphyEditableToolbar *etoolbar);
static gboolean update_editor_position       (EphyEditableToolbar *etoolbar);

enum
{
    PROP_0,
    PROP_TOOLBARS_GROUP,
    PROP_MENU_MERGE
};

static GObjectClass *parent_class = NULL;

struct EphyEditableToolbarPrivate
{
    EggMenuMerge *merge;

    GtkWidget *editor;
    GtkWidget *main_box;
    GtkWidget *table;
    GtkWidget *label_zone;
    GtkWidget *action_zone;
    GtkWidget *scrolled_window;

    GtkWidget *last_toolbar;

    guint ui_id;

    gboolean toolbars_dirty;
    gboolean editor_pos_dirty;
    gboolean editor_sheet_dirty;
    gboolean edit_mode;

    EphyToolbarsGroup *group;

    GList *actions_list;
};

GType
ephy_editable_toolbar_get_type (void)
{
        static GType ephy_editable_toolbar_type = 0;

        if (ephy_editable_toolbar_type == 0)
        {
                static const GTypeInfo our_info =
                {
                        sizeof (EphyEditableToolbarClass),
                        NULL, /* base_init */
                        NULL, /* base_finalize */
                        (GClassInitFunc) ephy_editable_toolbar_class_init,
                        NULL,
                        NULL, /* class_data */
                        sizeof (EphyEditableToolbar),
                        0, /* n_preallocs */
                        (GInstanceInitFunc) ephy_editable_toolbar_init
                };

                ephy_editable_toolbar_type = g_type_register_static (G_TYPE_OBJECT,
                                             "EphyEditableToolbar",
                                             &our_info, 0);
        }

        return ephy_editable_toolbar_type;
}

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

    g_return_val_if_fail (IS_EPHY_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 EggAction *
impl_get_action (EphyEditableToolbar *etoolbar,
         const char *type,
         const char *name)
{
    EggAction *action;

    g_return_val_if_fail (IS_EPHY_EDITABLE_TOOLBAR (etoolbar), NULL);

    if (type == NULL)
    {
        action = find_action (etoolbar, name);
    }
    else
    {
        action = NULL;
    }

    return action;
}

static gboolean
ui_update (gpointer data)
{
    EphyEditableToolbar *etoolbar = EPHY_EDITABLE_TOOLBAR (data);

    g_return_val_if_fail (etoolbar != NULL, FALSE);

    if (etoolbar->priv->toolbars_dirty)
    {
        LOG ("Update ui: toolbars")
        do_merge (etoolbar);
        etoolbar->priv->toolbars_dirty = FALSE;
    }

    if (etoolbar->priv->editor_sheet_dirty)
    {
        LOG ("Update ui: editor sheet")
        update_editor_sheet (etoolbar);
        etoolbar->priv->editor_sheet_dirty = FALSE;
    }

    if (etoolbar->priv->editor_pos_dirty)
    {
        LOG ("Update ui: editor position (in idle)")
        g_idle_add ((GSourceFunc)update_editor_position, etoolbar);
        etoolbar->priv->editor_pos_dirty = FALSE;
    }

    return FALSE;
}

static void
queue_ui_update (EphyEditableToolbar *etoolbar)
{
    g_return_if_fail (IS_EPHY_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_,
               EphyEditableToolbar *etoolbar)
{
    EphyToolbarsToolbar *toolbar;
    EphyToolbarsToolbar *parent;
    EphyToolbarsItem *sibling;
    const char *type = NULL;
    GdkAtom target;
    EggAction *action;

    g_return_if_fail (IS_EPHY_EDITABLE_TOOLBAR (etoolbar));

    toolbar = (EphyToolbarsToolbar *)g_object_get_data (G_OBJECT (widget), "toolbar_data");

    if (!toolbar)
    {
        sibling = (EphyToolbarsItem *)g_object_get_data (G_OBJECT (widget), "item_data");
        g_return_if_fail (sibling != NULL);
        parent = sibling->parent;
    }
    else
    {
        sibling = NULL;
        parent = toolbar;
    }

    g_return_if_fail (parent != NULL);

    target = gtk_drag_dest_find_target (widget, context, NULL);
    if (target == gdk_atom_intern (EPHY_DND_URL_TYPE, FALSE))
    {
        type = EPHY_DND_URL_TYPE;
    }

    action = ephy_editable_toolbar_get_action (etoolbar, type, selection_data->data);
    if (action)
    {
        ephy_toolbars_group_add_item (etoolbar->priv->group, parent, sibling,
                          action->name);
        etoolbar->priv->toolbars_dirty = TRUE;
        queue_ui_update (etoolbar);
    }
}

static void
drag_data_delete_cb (GtkWidget *widget,
                     GdkDragContext *context,
                     EphyEditableToolbar *etoolbar)
{
    EphyToolbarsItem *node;

    g_return_if_fail (IS_EPHY_EDITABLE_TOOLBAR (etoolbar));

    node = (EphyToolbarsItem *)g_object_get_data (G_OBJECT (widget), "item_data");
    g_return_if_fail (node != NULL);
    ephy_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,
                  EphyEditableToolbar *etoolbar)
{
    EggAction *action;
    const char *target;

    g_return_if_fail (IS_EPHY_EDITABLE_TOOLBAR (etoolbar));

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

    LOG ("Drag data get %s", action->name);

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

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

    path = ephy_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 (EphyToolbarsItem *item, EphyEditableToolbar *etoolbar)
{
    GtkWidget *toolitem;

    g_return_if_fail (IS_EPHY_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));

        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 (EphyToolbarsItem *item, EphyEditableToolbar *etoolbar)
{
    GtkWidget *toolitem;

    g_return_if_fail (IS_EPHY_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));

        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
setup_toolbar (EphyToolbarsToolbar *toolbar, EphyEditableToolbar *etoolbar)
{
    GtkWidget *widget;

    g_return_if_fail (IS_EPHY_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"))
    {
        LOG ("Setup drag dest for toolbar %s", toolbar->id)
        g_object_set_data (G_OBJECT (widget), "drag_dest_set",
                   GINT_TO_POINTER (TRUE));
        gtk_drag_dest_set (widget, GTK_DEST_DEFAULT_ALL,
                   dest_drag_types, 2,
                   GDK_ACTION_MOVE | GDK_ACTION_COPY);
        g_signal_connect (widget, "drag_data_received",
                  G_CALLBACK (drag_data_received_cb),
                  etoolbar);
    }

    etoolbar->priv->last_toolbar = widget;
}

static void
setup_item (EphyToolbarsItem *item, EphyEditableToolbar *etoolbar)
{
    GtkWidget *toolitem;
    char *path;

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

    path = ephy_toolbars_group_get_path (etoolbar->priv->group, item);
    g_return_if_fail (path != NULL);

    toolitem = get_item_widget (etoolbar, item);
    g_object_set_data (G_OBJECT (toolitem), "item_data", item);

    LOG ("Setup drag dest for toolbar item %s %p", path, toolitem);

    if (!g_object_get_data (G_OBJECT (toolitem), "drag_dest_set"))
    {
        g_object_set_data (G_OBJECT (toolitem), "drag_dest_set",
                   GINT_TO_POINTER (TRUE));
        gtk_drag_dest_set (toolitem, GTK_DEST_DEFAULT_ALL,
                   dest_drag_types, 2,
                   GDK_ACTION_COPY | GDK_ACTION_MOVE);
        g_signal_connect (toolitem, "drag_data_received",
                  G_CALLBACK (drag_data_received_cb),
                  etoolbar);
    }

    g_free (path);
}

static void
ensure_toolbar_min_size (EphyToolbarsToolbar *toolbar, EphyEditableToolbar *t)
{
    GtkWidget *widget;

    g_return_if_fail (IS_EPHY_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 (EphyEditableToolbar *t)
{
    char *str;
    guint ui_id;

    g_return_if_fail (IS_EPHY_EDITABLE_TOOLBAR (t));

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

    LOG ("Merge UI\n%s", str)

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

    if (t->priv->ui_id != 0)
    {
        LOG ("Remove old toolbar")

        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);

    ephy_toolbars_group_foreach_toolbar (t->priv->group,
                         (EphyToolbarsGroupForeachToolbarFunc)
                         setup_toolbar, t);
    ephy_toolbars_group_foreach_item (t->priv->group,
                      (EphyToolbarsGroupForeachItemFunc)
                      setup_item, t);

    if (t->priv->edit_mode)
    {
        ephy_toolbars_group_foreach_item (t->priv->group,
                          (EphyToolbarsGroupForeachItemFunc)
                              connect_item_drag_source, t);
    }

    ephy_toolbars_group_foreach_toolbar (t->priv->group,
                         (EphyToolbarsGroupForeachToolbarFunc)
                         ensure_toolbar_min_size, t);

    g_free (str);
}

static void
ensure_action (EphyToolbarsItem *item, EphyEditableToolbar *t)
{
    g_return_if_fail (IS_EPHY_EDITABLE_TOOLBAR (t));
    g_return_if_fail (item != NULL);

    ephy_editable_toolbar_get_action (t, NULL, item->action);
}

static void
group_changed_cb (EphyToolbarsGroup *group, EphyEditableToolbar *t)
{
    g_return_if_fail (IS_EPHY_TOOLBARS_GROUP (group));
    g_return_if_fail (IS_EPHY_EDITABLE_TOOLBAR (t));

    t->priv->toolbars_dirty = TRUE;

    ephy_toolbars_group_foreach_item (t->priv->group,
                      (EphyToolbarsGroupForeachItemFunc)
                          ensure_action, t);

    queue_ui_update (t);
}

static void
ephy_editable_toolbar_set_group (EphyEditableToolbar *t, EphyToolbarsGroup *group)
{
    g_return_if_fail (IS_EPHY_TOOLBARS_GROUP (group));
    g_return_if_fail (IS_EPHY_EDITABLE_TOOLBAR (t));

    t->priv->group = group;

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

static void
ephy_editable_toolbar_set_merge (EphyEditableToolbar *t, EggMenuMerge *merge)
{
    g_return_if_fail (EGG_IS_MENU_MERGE (merge));
    g_return_if_fail (IS_EPHY_EDITABLE_TOOLBAR (t));

    t->priv->merge = merge;

    LOG ("Got MenuMerge")

    ephy_toolbars_group_foreach_item (t->priv->group,
                      (EphyToolbarsGroupForeachItemFunc)
                          ensure_action, t);

    do_merge (t);
}

static void
ephy_editable_toolbar_set_property (GObject *object,
                    guint prop_id,
                    const GValue *value,
                    GParamSpec *pspec)
{
        EphyEditableToolbar *t = EPHY_EDITABLE_TOOLBAR (object);

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

static void
ephy_editable_toolbar_get_property (GObject *object,
                                    guint prop_id,
                                    GValue *value,
                                    GParamSpec *pspec)
{
        EphyEditableToolbar *t = EPHY_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
ephy_editable_toolbar_class_init (EphyEditableToolbarClass *klass)
{
        GObjectClass *object_class = G_OBJECT_CLASS (klass);

        parent_class = g_type_class_peek_parent (klass);

        object_class->finalize = ephy_editable_toolbar_finalize;
    object_class->set_property = ephy_editable_toolbar_set_property;
    object_class->get_property = ephy_editable_toolbar_get_property;

    klass->get_action = impl_get_action;

    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",
                                                              EPHY_TOOLBARS_GROUP_TYPE,
                                                              G_PARAM_READWRITE));
}

static void
ephy_editable_toolbar_init (EphyEditableToolbar *t)
{
        t->priv = g_new0 (EphyEditableToolbarPrivate, 1);

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

static void
ephy_editable_toolbar_finalize (GObject *object)
{
    EphyEditableToolbar *t = EPHY_EDITABLE_TOOLBAR (object);

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

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

        g_free (t->priv);

    LOG ("EphyEditableToolbar finalized")

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

EphyEditableToolbar *
ephy_editable_toolbar_new (EggMenuMerge *merge)
{
    EphyEditableToolbar *t;

    t = EPHY_EDITABLE_TOOLBAR (g_object_new (EPHY_EDITABLE_TOOLBAR_TYPE,
                   "MenuMerge", merge,
                   NULL));

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

    return t;
}

static void
editor_get_dimensions (EphyEditableToolbar *etoolbar, GtkWidget *toolbar,
               int *x, int *y, int *width, int *height)
{
    GtkWidget *vbox;
    GtkBin *popwin;
    GtkWidget *widget;
    GtkWidget *table;
    GtkWidget *label_zone;
    GtkWidget *action_zone;
    GtkWidget *popup;
    GtkRequisition requisition;
    int avail_height;
    int avail_width;
    int work_height;
    int work_width;

    widget = toolbar;
    popup  = etoolbar->priv->scrolled_window;
    popwin = GTK_BIN (etoolbar->priv->editor);
    vbox = etoolbar->priv->main_box;
    table = etoolbar->priv->table;
    action_zone = etoolbar->priv->action_zone;
    label_zone = etoolbar->priv->label_zone;

    gdk_window_get_origin (toolbar->window, x, y);

    *y += toolbar->allocation.y + toolbar->allocation.height;

    avail_height = gdk_screen_height () - *y;
    avail_width = gdk_screen_width () - *x;

    gtk_widget_size_request (table, &requisition);

    *width = MIN (avail_width, requisition.width);
    *height = MIN (avail_height, requisition.height);

    work_width = (2 * GTK_BIN (popwin)->child->style->xthickness +
              2 * GTK_CONTAINER (popwin)->border_width +
              2 * GTK_CONTAINER (popwin->child)->border_width +
              2 * GTK_CONTAINER (vbox)->border_width +
              2 * GTK_CONTAINER (popup)->border_width +
              2 * GTK_CONTAINER (GTK_BIN (popup)->child)->border_width +
              2 * GTK_BIN (popup)->child->style->xthickness);
    *width += work_width;

    work_height = (2 * GTK_BIN (popwin)->child->style->ythickness +
               2 * GTK_CONTAINER (popwin)->border_width +
               2 * GTK_CONTAINER (popwin->child)->border_width +
               2 * GTK_CONTAINER (vbox)->border_width +
               2 * GTK_CONTAINER (popup)->border_width +
               2 * GTK_CONTAINER (GTK_BIN (popup)->child)->border_width +
               2 * GTK_BIN (popup)->child->style->ythickness);
    *height += work_height;

    gtk_widget_size_request (label_zone, &requisition);
    *height += requisition.height;
    gtk_widget_size_request (action_zone, &requisition);
    *height += requisition.height;
    /* Vbox spacing */
    *height += 2 * 12;

    *x += (toolbar->allocation.x + toolbar->allocation.width)/2 - (*width / 2);
}

static void
hide_editor (EphyEditableToolbar *etoolbar)
{
    g_return_if_fail (IS_EPHY_EDITABLE_TOOLBAR (etoolbar));

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

static void
editor_close_cb (GtkWidget *button, EphyEditableToolbar *etoolbar)
{
    g_return_if_fail (IS_EPHY_EDITABLE_TOOLBAR (etoolbar));

    etoolbar->priv->edit_mode = FALSE;
    ephy_toolbars_group_foreach_item (etoolbar->priv->group,
                      (EphyToolbarsGroupForeachItemFunc)
                          disconnect_item_drag_source,
                      etoolbar);
    hide_editor (etoolbar);
}

static void
editor_add_toolbar_cb (GtkWidget *button, EphyEditableToolbar *etoolbar)
{
    g_return_if_fail (IS_EPHY_EDITABLE_TOOLBAR (etoolbar));

    ephy_toolbars_group_add_toolbar (etoolbar->priv->group);

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

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

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

static void
editor_drag_data_delete_cb (GtkWidget *widget,
                            GdkDragContext *context,
                            EphyEditableToolbar *etoolbar)
{
    g_return_if_fail (IS_EPHY_EDITABLE_TOOLBAR (etoolbar));

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

static void
setup_editor (EphyEditableToolbar *etoolbar)
{
    GtkWidget *editor;
    GtkWidget *scrolled_window;
    GtkWidget *vbox;
    GtkWidget *label_hbox;
    GtkWidget *image;
    GtkWidget *label;
    GtkWidget *bbox;
    GtkWidget *button;
    GtkWidget *frame;

    g_return_if_fail (IS_EPHY_EDITABLE_TOOLBAR (etoolbar));

    editor = gtk_window_new (GTK_WINDOW_POPUP);
    etoolbar->priv->editor = editor;
    frame = gtk_frame_new (NULL);
    gtk_widget_show (frame);
    gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
    gtk_container_add (GTK_CONTAINER (editor), frame);
    vbox = gtk_vbox_new (FALSE, 12);
    etoolbar->priv->main_box = vbox;
    gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
    gtk_widget_show (vbox);
    gtk_container_add (GTK_CONTAINER (frame), 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);
    etoolbar->priv->label_zone = label_hbox;
    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);
    bbox = gtk_hbutton_box_new ();
    gtk_box_set_spacing (GTK_BOX (bbox), 10);
    etoolbar->priv->action_zone = bbox;
    gtk_widget_show (bbox);
    gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox),
                       GTK_BUTTONBOX_END);
    gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, FALSE, 0);
    button = gtk_button_new_from_stock (GTK_STOCK_ADD);
    gtk_widget_show (button);
    gtk_button_set_label (GTK_BUTTON (button), _("Add Toolbar"));
    g_signal_connect (button, "clicked",
              G_CALLBACK (editor_add_toolbar_cb),
              etoolbar);
    gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 0);
    button = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
    gtk_widget_show (button);
    g_signal_connect (button, "clicked",
              G_CALLBACK (editor_close_cb),
              etoolbar);
    gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 0);

    update_editor_sheet (etoolbar);
    update_editor_position (etoolbar);
}

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

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

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

    g_return_if_fail (IS_EPHY_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_table_set_col_spacings (GTK_TABLE (table), 12);
    gtk_table_set_row_spacings (GTK_TABLE (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, 2, GDK_ACTION_MOVE);
    g_signal_connect (table, "drag_data_received",
              G_CALLBACK (editor_drag_data_received_cb),
              etoolbar);

    ephy_toolbars_group_foreach_available (etoolbar->priv->group,
                           (EphyToolbarsGroupForeachItemFunc)
                           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)
    {
        GtkWidget *event_box;
        GtkWidget *vbox;
        GtkWidget *icon;
        GtkWidget *label;
        EphyToolbarsItem *node = (EphyToolbarsItem *) (l->data);
        EggAction *action;

        action = ephy_editable_toolbar_get_action
            (etoolbar, NULL, node->action);
        g_return_if_fail (action != 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,
                     GDK_ACTION_MOVE);
        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);

        g_object_set_data (G_OBJECT (event_box), "egg-action", action);

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

        icon = gtk_image_new_from_stock
            (action->stock_id ? action->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 = gtk_label_new_with_mnemonic (action->label);
        gtk_widget_show (label);
        gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, TRUE, 0);

        gtk_table_attach_defaults (GTK_TABLE (etoolbar->priv->table),
                       event_box, x, x + 1, y, y + 1);

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

    g_list_free (to_drag);
}

static gboolean
update_editor_position (EphyEditableToolbar *etoolbar)
{
    GtkWidget *editor;
    int x, y, height, width;

    g_return_val_if_fail (IS_EPHY_EDITABLE_TOOLBAR (etoolbar), FALSE);

    editor_get_dimensions (etoolbar, etoolbar->priv->last_toolbar,
                   &x, &y, &width, &height);
    editor = etoolbar->priv->editor;
    gtk_widget_set_size_request (GTK_WIDGET (editor), width, height);
    gtk_window_move (GTK_WINDOW (editor), x, y);

    LOG ("Editor moved to %d %d %d %d", x, y, width, height)

    return FALSE;
}

static gboolean
button_press_cb (GtkWidget *w,
         GdkEvent *event,
         EphyEditableToolbar *etoolbar)
{
    GtkWidget *widget;
    GtkWidget *toolitem;

    g_return_val_if_fail (IS_EPHY_EDITABLE_TOOLBAR (etoolbar), FALSE);

    widget = gtk_get_event_widget (event);
    toolitem = gtk_widget_get_ancestor (widget, EGG_TYPE_TOOL_ITEM);

    if (toolitem == NULL) return FALSE;

    switch (event->type)
    {
        case GDK_BUTTON_PRESS:
            gtk_drag_begin (toolitem,
                    gtk_target_list_new (source_drag_types, 1),
                    GDK_ACTION_MOVE, 1, event);
            return TRUE;
        default:
            break;
    }

    return FALSE;
}

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

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

    gtk_widget_show (GTK_WIDGET (editor));
    gtk_grab_add (editor);

    g_signal_connect (editor, "button_press_event",
              G_CALLBACK (button_press_cb),
              etoolbar);
}

static void
set_action_sensitive (EphyToolbarsItem *item, EphyEditableToolbar *etoolbar)
{
    EggAction *action;

    g_return_if_fail (IS_EPHY_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
ephy_editable_toolbar_edit (EphyEditableToolbar *etoolbar)
{
    g_return_if_fail (IS_EPHY_EDITABLE_TOOLBAR (etoolbar));

    etoolbar->priv->edit_mode = TRUE;
    ephy_toolbars_group_foreach_item (etoolbar->priv->group,
                      (EphyToolbarsGroupForeachItemFunc)
                          connect_item_drag_source, etoolbar);
    ephy_toolbars_group_foreach_item (etoolbar->priv->group,
                      (EphyToolbarsGroupForeachItemFunc)
                          set_action_sensitive, etoolbar);
    setup_editor (etoolbar);
    show_editor (etoolbar);
}

EggAction *
ephy_editable_toolbar_get_action (EphyEditableToolbar *etoolbar,
                  const char *type,
                  const char *name)
{
     EphyEditableToolbarClass *klass = EPHY_EDITABLE_TOOLBAR_GET_CLASS (etoolbar);
     return klass->get_action (etoolbar, type, name);
}