/* * 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-file-helpers.h" #include "ephy-debug.h" #include "ephy-dnd.h" #include "eggtoolitem.h" #include "eggtoolbar.h" #include #include #include /* 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_MENU_MERGE }; static GObjectClass *parent_class = NULL; struct EphyEditableToolbarPrivate { EggMenuMerge *merge; GNode *available_actions; GNode *toolbars; char *filename; GtkWidget *editor; GtkWidget *main_box; GtkWidget *table; GtkWidget *label_zone; GtkWidget *action_zone; GtkWidget *scrolled_window; guint ui_id; gboolean toolbars_dirty; gboolean editor_pos_dirty; gboolean editor_sheet_dirty; }; typedef struct { GtkWidget *widget; } ToolbarNode; typedef struct { gboolean separator; EggAction *action; } ItemNode; 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 ToolbarNode * toolbar_node_new (void) { ToolbarNode *node; node = g_new0 (ToolbarNode, 1); node->widget = NULL; return node; } static ItemNode * item_node_new (EggAction *action, gboolean separator) { ItemNode *item; item = g_new0 (ItemNode, 1); item->action = action; item->separator = separator; return item; } static void free_node (GNode *node) { g_free (node->data); } static EggAction * find_action (EphyEditableToolbar *t, const char *name) { GList *l = t->priv->merge->action_groups; EggAction *action = 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 GNode * find_node_from_action (EphyEditableToolbar *t, EggAction *action) { GNode *n1, *n2; for (n1 = t->priv->toolbars->children; n1 != NULL; n1 = n1->next) { for (n2 = n1->children; n2 != NULL; n2 = n2->next) { ItemNode *item = (ItemNode *) (n2->data); if (!item->separator && item->action == action) return n2; } } return NULL; } static EggAction * impl_get_action (EphyEditableToolbar *etoolbar, const char *type, const char *name) { EggAction *action; if (type == NULL) { action = find_action (etoolbar, name); } else { action = NULL; } return action; } static void add_action (EphyEditableToolbar *t, GNode *parent, GNode *sibling, const char *type, const char *name) { EggAction *action = NULL; gboolean separator; ItemNode *item; GNode *node; separator = (strcmp (name, "separator") == 0); if (!separator) { action = ephy_editable_toolbar_get_action (t, type, name); g_return_if_fail (action != NULL); } item = item_node_new (action, separator); node = g_node_new (item); g_node_insert_before (parent, sibling, node); } static void parse_item_list (EphyEditableToolbar *t, xmlNodePtr child, GNode *parent) { while (child) { if (xmlStrEqual (child->name, "toolitem")) { xmlChar *verb; verb = xmlGetProp (child, "verb"); add_action (t, parent, NULL, NULL, verb); xmlFree (verb); } else if (xmlStrEqual (child->name, "separator")) { add_action (t, parent, NULL, NULL, "separator"); } child = child->next; } } static GNode * add_toolbar (EphyEditableToolbar *t) { ToolbarNode *toolbar; GNode *node; toolbar = toolbar_node_new (); node = g_node_new (toolbar); g_node_append (t->priv->toolbars, node); return node; } static void parse_toolbars (EphyEditableToolbar *t, xmlNodePtr child) { while (child) { if (xmlStrEqual (child->name, "toolbar")) { GNode *node; node = add_toolbar (t); parse_item_list (t, child->children, node); } child = child->next; } } static void load_defaults (EphyEditableToolbar *t) { xmlDocPtr doc; xmlNodePtr child; xmlNodePtr root; const char *xml_filepath; LOG ("Load default toolbar info") xml_filepath = ephy_file ("epiphany-toolbar.xml"); doc = xmlParseFile (xml_filepath); root = xmlDocGetRootElement (doc); child = root->children; while (child) { if (xmlStrEqual (child->name, "available")) { t->priv->available_actions = g_node_new (NULL); parse_item_list (t, child->children, t->priv->available_actions); } else if (xmlStrEqual (child->name, "default") && t->priv->toolbars == NULL) { t->priv->toolbars = g_node_new (NULL); parse_toolbars (t, child->children); } child = child->next; } } static void load_toolbar (EphyEditableToolbar *t) { xmlDocPtr doc; xmlNodePtr root; const char *xml_filepath = t->priv->filename; LOG ("Load custom toolbar") if (!g_file_test (xml_filepath, G_FILE_TEST_EXISTS)) return; doc = xmlParseFile (xml_filepath); root = xmlDocGetRootElement (doc); t->priv->toolbars = g_node_new (NULL); parse_toolbars (t, root->children); } static xmlDocPtr toolbar_list_to_xml (EphyEditableToolbar *t, GNode *tl) { GNode *l1, *l2; xmlDocPtr doc; xmlIndentTreeOutput = TRUE; doc = xmlNewDoc ("1.0"); doc->children = xmlNewDocNode (doc, NULL, "toolbars", NULL); for (l1 = tl->children; l1 != NULL; l1 = l1->next) { xmlNodePtr tnode; tnode = xmlNewChild (doc->children, NULL, "toolbar", NULL); for (l2 = l1->children; l2 != NULL; l2 = l2->next) { xmlNodePtr node; ItemNode *item = (ItemNode *) (l2->data); if (item->separator) { node = xmlNewChild (tnode, NULL, "separator", NULL); } else { node = xmlNewChild (tnode, NULL, "toolitem", NULL); xmlSetProp (node, "verb", item->action->name); } } } return doc; } static char * toolbar_list_to_string (EphyEditableToolbar *t, GNode *tl) { GString *s; GNode *l1, *l2; char *result; int k = 0; s = g_string_new (NULL); g_string_append (s, ""); for (l1 = tl->children; l1 != NULL; l1 = l1->next) { int i = 0; g_string_append_printf (s, "\n", k); for (l2 = l1->children; l2 != NULL; l2 = l2->next) { ItemNode *item = (ItemNode *) (l2->data); if (item->separator) { g_string_append_printf (s, "" "" "\n", i); } else { g_string_append_printf (s, "" "" "\n", i, item->action->name); } i++; } g_string_append (s, "\n"); k++; } g_string_append (s, ""); result = s->str; g_string_free (s, FALSE); return result; } static gboolean ui_update (gpointer data) { EphyEditableToolbar *etoolbar = EPHY_EDITABLE_TOOLBAR (data); 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_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) { GNode *toolbar; GNode *parent; GNode *sibling; const char *type = NULL; GdkAtom target; toolbar = (GNode *)g_object_get_data (G_OBJECT (widget), "toolbar_node"); if (!toolbar) { sibling = (GNode *)g_object_get_data (G_OBJECT (widget), "item_node"); parent = sibling->parent; } else { sibling = NULL; parent = toolbar; } target = gtk_drag_dest_find_target (widget, context, NULL); if (target == gdk_atom_intern (EPHY_DND_URL_TYPE, FALSE)) { type = EPHY_DND_URL_TYPE; } add_action (etoolbar, parent, sibling, type, selection_data->data); etoolbar->priv->toolbars_dirty = TRUE; queue_ui_update (etoolbar); } static void drag_data_delete_cb (GtkWidget *widget, GdkDragContext *context, EphyEditableToolbar *etoolbar) { GNode *node; node = (GNode *)g_object_get_data (G_OBJECT (widget), "item_node"); free_node (node); g_node_destroy (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; 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 void setup_toolbars (EphyEditableToolbar *etoolbar, GNode *toolbars) { GNode *l1, *l2; int k = 0; char path[255]; for (l1 = toolbars->children; l1 != NULL; l1 = l1->next) { int i = 0; GtkWidget *toolbar; ToolbarNode *node = (ToolbarNode *)l1->data; sprintf (path, "/Toolbar%d", k); toolbar = egg_menu_merge_get_widget (etoolbar->priv->merge, path); g_object_set_data (G_OBJECT (toolbar), "toolbar_node", l1); node->widget = toolbar; if (!g_object_get_data (G_OBJECT (toolbar), "drag_dest_set")) { LOG ("Setup drag dest for toolbar %s", path) g_object_set_data (G_OBJECT (toolbar), "drag_dest_set", GINT_TO_POINTER (TRUE)); gtk_drag_dest_set (toolbar, GTK_DEST_DEFAULT_ALL, dest_drag_types, 2, GDK_ACTION_MOVE | GDK_ACTION_COPY); g_signal_connect (toolbar, "drag_data_received", G_CALLBACK (drag_data_received_cb), etoolbar); } for (l2 = l1->children; l2 != NULL; l2 = l2->next) { ItemNode *node = (ItemNode *) (l2->data); GtkWidget *toolitem; const char *type; if (node->separator) { type ="ToolSeparator"; } else { type ="ToolItem"; } sprintf (path, "/Toolbar%d/PlaceHolder%d/%s", k, i, type); toolitem = egg_menu_merge_get_widget (etoolbar->priv->merge, path); g_object_set_data (G_OBJECT (toolitem), "item_node", l2); 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_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); } i++; } k++; } } static void ensure_toolbars_min_size (EphyEditableToolbar *t) { GNode *n; int i = 0; for (n = t->priv->toolbars->children; n != NULL; n = n->next) { ToolbarNode *node = (ToolbarNode *) (n->data); GtkWidget *toolbar; toolbar = node->widget; if (g_node_n_children (n) == 0) { gtk_widget_set_size_request (toolbar, -1, 20); } else { gtk_widget_set_size_request (toolbar, -1, -1); } i++; } } static void ephy_editable_toolbar_save (EphyEditableToolbar *t) { xmlDocPtr doc; doc = toolbar_list_to_xml (t, t->priv->toolbars); xmlSaveFormatFile (t->priv->filename, doc, 1); xmlFreeDoc (doc); } static void do_merge (EphyEditableToolbar *t) { char *str; guint ui_id; str = toolbar_list_to_string (t, t->priv->toolbars); 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); setup_toolbars (t, t->priv->toolbars); ensure_toolbars_min_size (t); ephy_editable_toolbar_save (t); g_free (str); } static void ephy_editable_toolbar_set_merge (EphyEditableToolbar *t, EggMenuMerge *merge) { t->priv->merge = merge; LOG ("Got MenuMerge") load_toolbar (t); load_defaults (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; } } 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; } } 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)); } static void ephy_editable_toolbar_init (EphyEditableToolbar *t) { t->priv = g_new0 (EphyEditableToolbarPrivate, 1); t->priv->merge = NULL; t->priv->available_actions = NULL; t->priv->toolbars = NULL; t->priv->filename = g_build_filename (ephy_dot_dir (), "toolbar.xml", 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; } 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_node_children_foreach (t->priv->available_actions, G_IN_ORDER, (GNodeForeachFunc)free_node, NULL); g_node_children_foreach (t->priv->toolbars, G_IN_ORDER, (GNodeForeachFunc)free_node, NULL); g_node_destroy (t->priv->available_actions); g_node_destroy (t->priv->toolbars); g_free (t->priv->filename); 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 GList * build_to_drag_actions_list (EphyEditableToolbar *etoolbar) { GNode *l; GList *result = NULL; for (l = etoolbar->priv->available_actions->children; l != NULL; l = l->next) { ItemNode *item; item = (ItemNode *) (l->data); if (!find_node_from_action (etoolbar, item->action)) { result = g_list_append (result, item); } } return result; } static void hide_editor (EphyEditableToolbar *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) { hide_editor (etoolbar); } static void editor_add_toolbar_cb (GtkWidget *button, EphyEditableToolbar *etoolbar) { add_toolbar (etoolbar); 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) { 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) { 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; 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 update_editor_sheet (EphyEditableToolbar *etoolbar) { GList *l; GList *to_drag; int x, y, height, width; GtkWidget *table; GtkWidget *viewport; 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); to_drag = build_to_drag_actions_list (etoolbar); 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; ItemNode *node = (ItemNode *) (l->data); EggAction *action = EGG_ACTION (node->action); 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) { ToolbarNode *last_node; GtkWidget *last_toolbar; GtkWidget *editor; int x, y, height, width; last_node = (ToolbarNode *)(g_node_last_child (etoolbar->priv->toolbars))->data; last_toolbar = last_node->widget; g_assert (EGG_IS_TOOLBAR (last_toolbar)); editor_get_dimensions (etoolbar, 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; 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 (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_all_actions_sensitive (EphyEditableToolbar *etoolbar) { GNode *l; for (l = etoolbar->priv->available_actions->children; l != NULL; l = l->next) { ItemNode *node = (ItemNode *) (l->data); g_object_set (node->action, "sensitive", TRUE, NULL); } } void ephy_editable_toolbar_edit (EphyEditableToolbar *etoolbar) { set_all_actions_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); }