From b36f3a13160e8747019c40f28df89a9a9c3917ea Mon Sep 17 00:00:00 2001 From: Marco Pesenti Gritti Date: Mon, 09 May 2005 12:03:36 +0000 Subject: Implement epiphany like toolbar editor. Based on patch by Nickolay V. Shmyrev --- diff --git a/ChangeLog b/ChangeLog index 905fc6e..e2e1e67 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,91 @@ +2005-05-09 Marco Pesenti Gritti + + * configure.ac: + * cut-n-paste/Makefile.am: + * cut-n-paste/recent-files/Makefile.am: + * cut-n-paste/toolbar-editor/.cvsignore: + * cut-n-paste/toolbar-editor/Makefile.am: + * cut-n-paste/toolbar-editor/egg-editable-toolbar.c: + (egg_editable_toolbar_get_type), (get_toolbar_position), + (get_n_toolbars), (get_dock_nth), (get_toolbar_nth), (find_action), + (drag_data_delete_cb), (drag_begin_cb), (drag_end_cb), + (drag_data_get_cb), (set_drag_cursor), (unset_drag_cursor), + (set_item_drag_source), (create_item_from_action), (create_item), + (data_is_separator), (drag_data_received_cb), (remove_toolbar_cb), + (popup_toolbar_context_menu_cb), (free_dragged_item), + (toolbar_drag_drop_cb), (toolbar_drag_motion_cb), + (toolbar_drag_leave_cb), (create_dock), (set_fixed_style), + (unset_fixed_style), (toolbar_changed_cb), (unparent_fixed), + (update_fixed), (toolbar_added_cb), (toolbar_removed_cb), + (item_added_cb), (item_removed_cb), + (egg_editable_toolbar_construct), + (egg_editable_toolbar_disconnect_model), + (egg_editable_toolbar_deconstruct), + (egg_editable_toolbar_set_model), + (egg_editable_toolbar_set_ui_manager), + (egg_editable_toolbar_set_property), + (egg_editable_toolbar_get_property), + (egg_editable_toolbar_class_init), (egg_editable_toolbar_init), + (egg_editable_toolbar_finalize), (egg_editable_toolbar_new), + (egg_editable_toolbar_new_with_model), + (egg_editable_toolbar_get_edit_mode), + (egg_editable_toolbar_set_edit_mode), (egg_editable_toolbar_show), + (egg_editable_toolbar_hide), (egg_editable_toolbar_set_fixed), + (egg_editable_toolbar_set_drag_dest), (fake_expose_widget), + (new_pixbuf_from_widget), (new_separator_pixbuf), + (update_separator_image), (style_set_cb), + (_egg_editable_toolbar_new_separator_image), + (egg_editable_toolbar_get_model): + * cut-n-paste/toolbar-editor/egg-editable-toolbar.h: + * cut-n-paste/toolbar-editor/egg-marshal.c: + * cut-n-paste/toolbar-editor/egg-toolbar-editor.c: + (egg_toolbar_editor_get_type), (compare_actions), (find_action), + (egg_toolbar_editor_set_ui_manager), (toolbar_removed_cb), + (egg_toolbar_editor_set_model), (egg_toolbar_editor_set_property), + (egg_toolbar_editor_get_property), (egg_toolbar_editor_class_init), + (egg_toolbar_editor_finalize), (egg_toolbar_editor_new), + (drag_begin_cb), (drag_end_cb), (editor_drag_data_received_cb), + (editor_drag_data_delete_cb), (drag_data_get_cb), + (elide_underscores), (set_drag_cursor), (event_box_realize_cb), + (editor_create_item), (update_editor_sheet), (setup_editor), + (egg_toolbar_editor_init), (egg_toolbar_editor_add_action), + (parse_item_list), (model_has_action), (update_actions_list), + (egg_toolbar_editor_load_actions): + * cut-n-paste/toolbar-editor/egg-toolbar-editor.h: + * cut-n-paste/toolbar-editor/egg-toolbars-model.c: + (egg_toolbars_model_get_type), (egg_toolbars_model_to_xml), + (safe_save_xml), (egg_toolbars_model_save), (toolbars_toolbar_new), + (toolbars_item_new), (free_toolbar_node), (free_item_node), + (egg_toolbars_model_get_flags), (egg_toolbars_model_set_flags), + (egg_toolbars_model_add_separator), (impl_add_item), + (parse_item_list), (egg_toolbars_model_add_toolbar), + (parse_toolbars), (egg_toolbars_model_load), (impl_get_item_id), + (impl_get_item_data), (impl_get_item_type), + (_egg_accumulator_STRING), (egg_toolbars_model_class_init), + (egg_toolbars_model_init), (free_toolbar), + (egg_toolbars_model_finalize), (egg_toolbars_model_new), + (egg_toolbars_model_remove_toolbar), + (egg_toolbars_model_remove_item), (egg_toolbars_model_move_item), + (egg_toolbars_model_n_items), (egg_toolbars_model_item_nth), + (egg_toolbars_model_n_toolbars), (egg_toolbars_model_toolbar_nth), + (egg_toolbars_model_add_item), (egg_toolbars_model_get_item_id), + (egg_toolbars_model_get_item_data), + (egg_toolbars_model_get_item_type): + * cut-n-paste/toolbar-editor/egg-toolbars-model.h: + * cut-n-paste/toolbar-editor/eggmarshalers.list: + * data/Makefile.am: + * data/evince-toolbar.xml: + * data/evince-ui.xml: + * data/hand-open.png: + * po/POTFILES.in: + * shell/Makefile.am: + * shell/ev-window.c: (update_chrome_visibility), + (ev_window_cmd_edit_toolbar_cb), (ev_window_cmd_edit_toolbar), + (ev_window_dispose), (ev_window_init): + + Implement epiphany like toolbar editor. Based on patch by + Nickolay V. Shmyrev + 2005-05-09 Marco Pesenti Gritti * shell/ev-sidebar-thumbnails.c: (page_changed_cb): diff --git a/configure.ac b/configure.ac index 5f0cd9c..bc9efa7 100644 --- a/configure.ac +++ b/configure.ac @@ -33,6 +33,7 @@ AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,"$GETTEXT_PACKAGE", [Gettext package.]) POPPLER_REQUIRED=0.3.0 PKG_CHECK_MODULES(LIBEVPRIVATE, gtk+-2.0 >= 2.4.0) +PKG_CHECK_MODULES(TOOLBAR_EDITOR, gtk+-2.0 >= 2.4.0 libgnomeui-2.0 >= 2.4.0) PKG_CHECK_MODULES(RECENT_FILES, gtk+-2.0 >= 2.4.0 libgnomeui-2.0 >= 2.4.0) PKG_CHECK_MODULES(ZOOM_CONTROL, gtk+-2.0 >= 2.4.0) PKG_CHECK_MODULES(SHELL, gtk+-2.0 >= 2.6.0 libgnomeui-2.0 gnome-vfs-2.0 libgnomeprint-2.2 libgnomeprintui-2.2 libglade-2.0 gconf-2.0 poppler-glib >= $POPPLER_REQUIRED) @@ -221,6 +222,7 @@ Makefile cut-n-paste/Makefile cut-n-paste/recent-files/Makefile cut-n-paste/zoom-control/Makefile +cut-n-paste/toolbar-editor/Makefile data/Makefile lib/Makefile pdf/Makefile diff --git a/cut-n-paste/Makefile.am b/cut-n-paste/Makefile.am index 785d7a2..ce69b61 100644 --- a/cut-n-paste/Makefile.am +++ b/cut-n-paste/Makefile.am @@ -1 +1 @@ -SUBDIRS = recent-files zoom-control +SUBDIRS = recent-files zoom-control toolbar-editor diff --git a/cut-n-paste/recent-files/Makefile.am b/cut-n-paste/recent-files/Makefile.am index 63cde66..aa490a4 100644 --- a/cut-n-paste/recent-files/Makefile.am +++ b/cut-n-paste/recent-files/Makefile.am @@ -1,7 +1,7 @@ NULL = INCLUDES = \ - $(RECENT_FILES_CFLAGS) \ + $(EGG_CFLAGS) \ $(NULL) # Uses the deprecated GnomeIconTheme diff --git a/cut-n-paste/toolbar-editor/.cvsignore b/cut-n-paste/toolbar-editor/.cvsignore new file mode 100644 index 0000000..cf69a33 --- /dev/null +++ b/cut-n-paste/toolbar-editor/.cvsignore @@ -0,0 +1,4 @@ +Makefile +Makefile.in +eggmarshalers.c +eggmarshalers.h diff --git a/cut-n-paste/toolbar-editor/Makefile.am b/cut-n-paste/toolbar-editor/Makefile.am new file mode 100644 index 0000000..7d57262 --- /dev/null +++ b/cut-n-paste/toolbar-editor/Makefile.am @@ -0,0 +1,35 @@ + +INCLUDES = $(TOOLBAR_EDITOR_CFLAGS) \ + -DCURSOR_DIR=\"$(pkgdatadir)\" + +BUILT_SOURCES = eggmarshalers.c eggmarshalers.h + +noinst_LTLIBRARIES = libtoolbareditor.la + +libtoolbareditor_la_SOURCES = \ + egg-toolbars-model.c \ + egg-editable-toolbar.c \ + egg-toolbar-editor.c \ + egg-marshal.c + +noinst_HEADERS = \ + egg-toolbars-model.h \ + egg-editable-toolbar.h \ + egg-toolbar-editor.h + +eggmarshalers.h: eggmarshalers.list + cd $(srcdir) \ + && $(GLIB_GENMARSHAL) --prefix=_egg_marshal eggmarshalers.list --header > xgen-emh \ + && cp xgen-emh eggmarshalers.h \ + && rm -f xgen-emh xgen-emh~ + +eggmarshalers.c: eggmarshalers.list + cd $(srcdir) \ + && $(GLIB_GENMARSHAL) --prefix=_egg_marshal eggmarshalers.list --body > xgen-emc \ + && cp xgen-emc eggmarshalers.c \ + && rm -f xgen-emc xgen-emc~ + +egg-marshal.c: eggmarshalers.c eggmarshalers.h + +EXTRA_DIST= \ + eggmarshalers.list diff --git a/cut-n-paste/toolbar-editor/egg-editable-toolbar.c b/cut-n-paste/toolbar-editor/egg-editable-toolbar.c new file mode 100644 index 0000000..02791ee --- /dev/null +++ b/cut-n-paste/toolbar-editor/egg-editable-toolbar.c @@ -0,0 +1,1468 @@ +/* + * Copyright (C) 2003-2004 Marco Pesenti Gritti + * Copyright (C) 2004 Christian Persch + * + * 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. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "egg-editable-toolbar.h" +#include "egg-toolbars-model.h" +#include "egg-toolbar-editor.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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); + +#define MIN_TOOLBAR_HEIGHT 20 + +static GtkTargetEntry dest_drag_types[] = { + {EGG_TOOLBAR_ITEM_TYPE, GTK_TARGET_SAME_APP, 0}, +}; +static int n_dest_drag_types = G_N_ELEMENTS (dest_drag_types); + +enum +{ + PROP_0, + PROP_TOOLBARS_MODEL, + PROP_UI_MANAGER +}; + +enum +{ + ACTION_REQUEST, + LAST_SIGNAL +}; + +static guint egg_editable_toolbar_signals[LAST_SIGNAL] = { 0 }; + +static GObjectClass *parent_class = NULL; + +#define EGG_EDITABLE_TOOLBAR_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EGG_TYPE_EDITABLE_TOOLBAR, EggEditableToolbarPrivate)) + +struct EggEditableToolbarPrivate +{ + GtkUIManager *manager; + EggToolbarsModel *model; + gboolean edit_mode; + GtkWidget *selected_toolbar; + GtkToolItem *fixed; + GtkWidget *fixed_toolbar; + + gboolean pending; + GtkToolbar *target_toolbar; + GtkWidget *dragged_item; +}; + +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 (GTK_TYPE_VBOX, + "EggEditableToolbar", + &our_info, 0); + } + + return egg_editable_toolbar_type; +} + +static int +get_toolbar_position (EggEditableToolbar *etoolbar, GtkWidget *toolbar) +{ + GList *l; + int result; + + l = gtk_container_get_children (GTK_CONTAINER (etoolbar)); + result = g_list_index (l, toolbar->parent); + g_list_free (l); + + return result; +} + +static int +get_n_toolbars (EggEditableToolbar *etoolbar) +{ + GList *l; + int result; + + l = gtk_container_get_children (GTK_CONTAINER (etoolbar)); + result = g_list_length (l); + g_list_free (l); + + return result; +} + +static GtkWidget * +get_dock_nth (EggEditableToolbar *etoolbar, + int position) +{ + GList *l; + GtkWidget *result; + + l = gtk_container_get_children (GTK_CONTAINER (etoolbar)); + result = g_list_nth_data (l, position); + g_list_free (l); + + return result; +} + +static GtkWidget * +get_toolbar_nth (EggEditableToolbar *etoolbar, + int position) +{ + GList *l; + GtkWidget *dock; + GtkWidget *result; + + dock = get_dock_nth (etoolbar, position); + + l = gtk_container_get_children (GTK_CONTAINER (dock)); + result = GTK_WIDGET (l->data); + g_list_free (l); + + return result; +} + +static GtkAction * +find_action (EggEditableToolbar *t, + const char *name) +{ + GList *l; + GtkAction *action = NULL; + + l = gtk_ui_manager_get_action_groups (t->priv->manager); + + g_return_val_if_fail (name != NULL, NULL); + + for (; l != NULL; l = l->next) + { + GtkAction *tmp; + + tmp = gtk_action_group_get_action (GTK_ACTION_GROUP (l->data), name); + if (tmp) + action = tmp; + } + + return action; +} + +static void +drag_data_delete_cb (GtkWidget *widget, + GdkDragContext *context, + EggEditableToolbar *etoolbar) +{ + int pos, toolbar_pos; + + g_return_if_fail (EGG_IS_EDITABLE_TOOLBAR (etoolbar)); + + pos = gtk_toolbar_get_item_index (GTK_TOOLBAR (widget->parent), + GTK_TOOL_ITEM (widget)); + toolbar_pos = get_toolbar_position (etoolbar, widget->parent); + + egg_toolbars_model_remove_item (etoolbar->priv->model, + toolbar_pos, pos); +} + +static void +drag_begin_cb (GtkWidget *widget, + GdkDragContext *context, + EggEditableToolbar *etoolbar) +{ + gtk_widget_hide (widget); +} + +static void +drag_end_cb (GtkWidget *widget, + GdkDragContext *context, + EggEditableToolbar *etoolbar) +{ + gtk_widget_show (widget); +} + +static void +drag_data_get_cb (GtkWidget *widget, + GdkDragContext *context, + GtkSelectionData *selection_data, + guint info, + guint32 time, + EggEditableToolbar *etoolbar) +{ + const char *id, *type; + char *target; + + g_return_if_fail (EGG_IS_EDITABLE_TOOLBAR (etoolbar)); + + type = g_object_get_data (G_OBJECT (widget), "type"); + id = g_object_get_data (G_OBJECT (widget), "id"); + if (strcmp (id, "separator") == 0) + { + target = g_strdup (id); + } + else + { + target = egg_toolbars_model_get_item_data (etoolbar->priv->model, + type, id); + } + + gtk_selection_data_set (selection_data, + selection_data->target, 8, target, strlen (target)); + + g_free (target); +} + +static void +set_drag_cursor (GtkWidget *widget) +{ + if (widget->window) + { + GdkCursor *cursor; + GdkPixbuf *pixbuf; + + pixbuf = gdk_pixbuf_new_from_file (CURSOR_DIR "/hand-open.png", NULL); + cursor = gdk_cursor_new_from_pixbuf (gdk_display_get_default (), + pixbuf, 12, 12); + gdk_window_set_cursor (widget->window, cursor); + gdk_cursor_unref (cursor); + g_object_unref (pixbuf); + } +} + +static void +unset_drag_cursor (GtkWidget *widget) +{ + if (widget->window) + { + gdk_window_set_cursor (widget->window, NULL); + } +} + +static void +set_item_drag_source (EggToolbarsModel *model, + GtkWidget *item, + GtkAction *action, + gboolean is_separator, + const char *type) +{ + GtkTargetEntry target_entry; + const char *id; + + target_entry.target = (char *)type; + target_entry.flags = GTK_TARGET_SAME_APP; + target_entry.info = 0; + + gtk_drag_source_set (item, GDK_BUTTON1_MASK, + &target_entry, 1, + GDK_ACTION_MOVE); + + if (is_separator) + { + GtkWidget *icon; + GdkPixbuf *pixbuf; + + id = "separator"; + + icon = _egg_editable_toolbar_new_separator_image (); + pixbuf = gtk_image_get_pixbuf (GTK_IMAGE (icon)); + gtk_drag_source_set_icon_pixbuf (item, pixbuf); + } + else + { + const char *stock_id; + GValue value = { 0, }; + GdkPixbuf *pixbuf; + + id = gtk_action_get_name (action); + + g_value_init (&value, G_TYPE_STRING); + g_object_get_property (G_OBJECT (action), "stock_id", &value); + stock_id = g_value_get_string (&value); + + if (stock_id != NULL) + { + pixbuf = gtk_widget_render_icon (item, stock_id, + GTK_ICON_SIZE_LARGE_TOOLBAR, NULL); + } + else + { + pixbuf = gtk_widget_render_icon (item, GTK_STOCK_DND, + GTK_ICON_SIZE_LARGE_TOOLBAR, NULL); + } + + gtk_drag_source_set_icon_pixbuf (item, pixbuf); + g_object_unref (pixbuf); + + g_value_unset (&value); + } + + g_object_set_data_full (G_OBJECT (item), "id", + g_strdup (id), g_free); + g_object_set_data_full (G_OBJECT (item), "type", + g_strdup (type), g_free); +} + +static GtkWidget * +create_item_from_action (EggEditableToolbar *t, + const char *action_name, + const char *type, + gboolean is_separator, + GtkAction **ret_action) +{ + GtkWidget *item; + GtkAction *action; + + if (is_separator) + { + item = GTK_WIDGET (gtk_separator_tool_item_new ()); + action = NULL; + } + else + { + g_return_val_if_fail (action_name != NULL, NULL); + + g_signal_emit (G_OBJECT (t), egg_editable_toolbar_signals[ACTION_REQUEST], + 0, action_name); + + action = find_action (t, action_name); + if (action) + { + item = gtk_action_create_tool_item (action); + } + else + { + return NULL; + } + } + + gtk_widget_show (item); + + g_signal_connect (item, "drag_begin", + G_CALLBACK (drag_begin_cb), t); + g_signal_connect (item, "drag_end", + G_CALLBACK (drag_end_cb), t); + g_signal_connect (item, "drag_data_get", + G_CALLBACK (drag_data_get_cb), t); + g_signal_connect (item, "drag_data_delete", + G_CALLBACK (drag_data_delete_cb), t); + + if (t->priv->edit_mode) + { + set_drag_cursor (item); + gtk_widget_set_sensitive (item, TRUE); + set_item_drag_source (t->priv->model, item, action, + is_separator, type); + gtk_tool_item_set_use_drag_window (GTK_TOOL_ITEM (item), TRUE); + } + + if (ret_action) + { + *ret_action = action; + } + + return item; +} + +static GtkWidget * +create_item (EggEditableToolbar *t, + EggToolbarsModel *model, + int toolbar_position, + int position, + GtkAction **ret_action) +{ + const char *action_name, *type; + gboolean is_separator; + + egg_toolbars_model_item_nth (model, toolbar_position, position, + &is_separator, &action_name, &type); + return create_item_from_action (t, action_name, type, + is_separator, ret_action); +} + +static gboolean +data_is_separator (const char *data) +{ + return strcmp (data, "separator") == 0; +} + +static void +drag_data_received_cb (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *selection_data, + guint info, + guint time, + EggEditableToolbar *etoolbar) +{ + char *type; + char *id; + + GdkAtom target; + + target = gtk_drag_dest_find_target (widget, context, NULL); + type = egg_toolbars_model_get_item_type (etoolbar->priv->model, target); + id = egg_toolbars_model_get_item_id (etoolbar->priv->model, type, selection_data->data); + + /* This function can be called for two reasons + * + * (1) drag_motion() needs an item to pass to + * gtk_toolbar_set_drop_highlight_item(). We can + * recognize this case by etoolbar->priv->pending being TRUE + * We should just create an item and return. + * + * (2) The drag has finished, and drag_drop() wants us to + * actually add a new item to the toolbar. + */ + + if (id == NULL) + { + etoolbar->priv->pending = FALSE; + g_free (type); + return; + } + + if (etoolbar->priv->pending) + { + etoolbar->priv->pending = FALSE; + etoolbar->priv->dragged_item = + create_item_from_action (etoolbar, id, type, + data_is_separator (id), NULL); + g_object_ref (etoolbar->priv->dragged_item); + gtk_object_sink (GTK_OBJECT (etoolbar->priv->dragged_item)); + } + else + { + int pos, toolbar_pos; + + pos = gtk_toolbar_get_drop_index (GTK_TOOLBAR (widget), x, y); + toolbar_pos = get_toolbar_position (etoolbar, widget); + + if (data_is_separator (selection_data->data)) + { + egg_toolbars_model_add_separator (etoolbar->priv->model, + toolbar_pos, pos); + } + else + { + egg_toolbars_model_add_item (etoolbar->priv->model, + toolbar_pos, pos, id, type); + } + + gtk_drag_finish (context, TRUE, context->action == GDK_ACTION_MOVE, + time); + } + + g_free (type); + g_free (id); +} + +static void +remove_toolbar_cb (GtkWidget *menuitem, + EggEditableToolbar *etoolbar) +{ + int pos; + + pos = get_toolbar_position (etoolbar, etoolbar->priv->selected_toolbar); + egg_toolbars_model_remove_toolbar (etoolbar->priv->model, pos); +} + +static void +popup_toolbar_context_menu_cb (GtkWidget *toolbar, + gint x, + gint y, + gint button_number, + EggEditableToolbar *t) +{ + GtkWidget *menu; + GtkWidget *item; + GtkWidget *image; + + if (t->priv->edit_mode) + { + EggTbModelFlags flags; + int position; + + t->priv->selected_toolbar = toolbar; + + menu = gtk_menu_new (); + + 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_signal_connect (item, "activate", + G_CALLBACK (remove_toolbar_cb), + t); + + position = get_toolbar_position (t, toolbar); + flags = egg_toolbars_model_get_flags (t->priv->model, position); + if (flags & EGG_TB_MODEL_NOT_REMOVABLE) + { + gtk_widget_set_sensitive (GTK_WIDGET (item), FALSE); + } + + gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 2, + gtk_get_current_event_time ()); + } +} + +static void +free_dragged_item (EggEditableToolbar *etoolbar) +{ + if (etoolbar->priv->dragged_item) + { + gtk_widget_destroy (etoolbar->priv->dragged_item); + g_object_unref (etoolbar->priv->dragged_item); + etoolbar->priv->dragged_item = NULL; + } +} + +static gboolean +toolbar_drag_drop_cb (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time, + EggEditableToolbar *etoolbar) +{ + GdkAtom target; + + target = gtk_drag_dest_find_target (widget, context, NULL); + if (target != GDK_NONE) + { + gtk_drag_get_data (widget, context, + target, + time); + return TRUE; + } + + free_dragged_item (etoolbar); + + return FALSE; +} + +static gboolean +toolbar_drag_motion_cb (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time, + EggEditableToolbar *etoolbar) +{ + GdkAtom target; + int index; + GtkToolbar *toolbar = GTK_TOOLBAR (widget); + GtkToolItem *item; + GtkWidget *source; + + source = gtk_drag_get_source_widget (context); + if (source) + { + EggTbModelFlags flags; + int pos; + gboolean is_item; + + pos = get_toolbar_position (etoolbar, widget); + flags = egg_toolbars_model_get_flags (etoolbar->priv->model, pos); + + is_item = etoolbar->priv->edit_mode && + (gtk_widget_get_ancestor (source, EGG_TYPE_EDITABLE_TOOLBAR) || + gtk_widget_get_ancestor (source, EGG_TYPE_TOOLBAR_EDITOR)); + + if ((flags & EGG_TB_MODEL_ACCEPT_ITEMS_ONLY) && !is_item) + { + gdk_drag_status (context, 0, time); + return FALSE; + } + + if (gtk_widget_is_ancestor (source, widget)) + { + context->suggested_action = GDK_ACTION_MOVE; + } + } + + target = gtk_drag_dest_find_target (widget, context, NULL); + if (target == GDK_NONE) + { + gdk_drag_status (context, 0, time); + return FALSE; + } + + if (etoolbar->priv->target_toolbar != toolbar) + { + if (etoolbar->priv->target_toolbar) + gtk_toolbar_set_drop_highlight_item + (etoolbar->priv->target_toolbar, NULL, 0); + + free_dragged_item (etoolbar); + etoolbar->priv->pending = TRUE; + + etoolbar->priv->target_toolbar = toolbar; + + gtk_drag_get_data (widget, context, target, time); + } + + if (etoolbar->priv->dragged_item != NULL && + etoolbar->priv->edit_mode) + { + item = GTK_TOOL_ITEM (etoolbar->priv->dragged_item); + + index = gtk_toolbar_get_drop_index (toolbar, x, y); + gtk_toolbar_set_drop_highlight_item (toolbar, item, index); + } + + gdk_drag_status (context, context->suggested_action, time); + + return TRUE; +} + +static void +toolbar_drag_leave_cb (GtkToolbar *toolbar, + GdkDragContext *context, + guint time, + EggEditableToolbar *etoolbar) +{ + /* This is a workaround for bug 125557. Sometimes + * we seemingly enter another toolbar *before* leaving + * the current one. + * + * In that case etoolbar->priv->target_toolbar will + * have been set to something else and the highlighting + * will already have been turned off + */ + + if (etoolbar->priv->target_toolbar == toolbar) + { + gtk_toolbar_set_drop_highlight_item (toolbar, NULL, 0); + + etoolbar->priv->target_toolbar = NULL; + free_dragged_item (etoolbar); + } +} + +static GtkWidget * +create_dock (EggEditableToolbar *t) +{ + GtkWidget *toolbar, *hbox; + + hbox = gtk_hbox_new (0, FALSE); + gtk_widget_show (hbox); + + toolbar = gtk_toolbar_new (); + gtk_toolbar_set_show_arrow (GTK_TOOLBAR (toolbar), TRUE); + gtk_widget_show (toolbar); + gtk_box_pack_start (GTK_BOX (hbox), toolbar, TRUE, TRUE, 0); + + gtk_drag_dest_set (toolbar, 0, + dest_drag_types, n_dest_drag_types, + GDK_ACTION_MOVE | GDK_ACTION_COPY); + + g_signal_connect (toolbar, "drag_drop", + G_CALLBACK (toolbar_drag_drop_cb), t); + g_signal_connect (toolbar, "drag_motion", + G_CALLBACK (toolbar_drag_motion_cb), t); + g_signal_connect (toolbar, "drag_leave", + G_CALLBACK (toolbar_drag_leave_cb), t); + + g_signal_connect (toolbar, "drag_data_received", + G_CALLBACK (drag_data_received_cb), t); + g_signal_connect (toolbar, "popup_context_menu", + G_CALLBACK (popup_toolbar_context_menu_cb), t); + + return hbox; +} + +static void +set_fixed_style (EggEditableToolbar *t, GtkToolbarStyle style) +{ + g_return_if_fail (GTK_IS_TOOLBAR (t->priv->fixed_toolbar)); + gtk_toolbar_set_style (GTK_TOOLBAR (t->priv->fixed_toolbar), style); +} + +static void +unset_fixed_style (EggEditableToolbar *t) +{ + g_return_if_fail (GTK_IS_TOOLBAR (t->priv->fixed_toolbar)); + gtk_toolbar_unset_style (GTK_TOOLBAR (t->priv->fixed_toolbar)); +} + +static void +toolbar_changed_cb (EggToolbarsModel *model, + int position, + EggEditableToolbar *t) +{ + GtkWidget *toolbar; + EggTbModelFlags flags; + GtkToolbarStyle style; + + flags = egg_toolbars_model_get_flags (model, position); + toolbar = get_toolbar_nth (t, position); + + if (flags & EGG_TB_MODEL_ICONS_ONLY) + { + style = GTK_TOOLBAR_ICONS; + } + else if (flags & EGG_TB_MODEL_TEXT_ONLY) + { + style = GTK_TOOLBAR_TEXT; + } + else if (flags & EGG_TB_MODEL_ICONS_TEXT) + { + style = GTK_TOOLBAR_BOTH; + } + else if (flags & EGG_TB_MODEL_ICONS_TEXT_HORIZ) + { + style = GTK_TOOLBAR_BOTH_HORIZ; + } + else + { + gtk_toolbar_unset_style (GTK_TOOLBAR (toolbar)); + if (position == 0 && t->priv->fixed_toolbar) + { + unset_fixed_style (t); + } + return; + } + + gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), style); + if (position == 0 && t->priv->fixed_toolbar) + { + set_fixed_style (t, style); + } +} + +static void +unparent_fixed (EggEditableToolbar *t) +{ + GtkWidget *toolbar, *dock; + g_return_if_fail (GTK_IS_TOOLBAR (t->priv->fixed_toolbar)); + + toolbar = t->priv->fixed_toolbar; + dock = get_dock_nth (t, 0); + + if (dock && toolbar->parent != NULL) + { + gtk_container_remove (GTK_CONTAINER (dock), toolbar); + } +} + +static void +update_fixed (EggEditableToolbar *t) +{ + GtkWidget *toolbar, *dock; + if (!t->priv->fixed_toolbar) return; + + toolbar = t->priv->fixed_toolbar; + dock = get_dock_nth (t, 0); + + if (dock && toolbar && toolbar->parent == NULL) + { + gtk_box_pack_end (GTK_BOX (dock), toolbar, FALSE, TRUE, 0); + + gtk_widget_show_all (toolbar); + } +} + +static void +toolbar_added_cb (EggToolbarsModel *model, + int position, + EggEditableToolbar *t) +{ + GtkWidget *dock; + + dock = create_dock (t); + + gtk_widget_set_size_request (dock, -1, MIN_TOOLBAR_HEIGHT); + + gtk_box_pack_start (GTK_BOX (t), dock, TRUE, TRUE, 0); + + gtk_box_reorder_child (GTK_BOX (t), dock, position); + + gtk_widget_show_all (dock); + + update_fixed (t); +} + +static void +toolbar_removed_cb (EggToolbarsModel *model, + int position, + EggEditableToolbar *t) +{ + GtkWidget *toolbar; + + toolbar = get_dock_nth (t, position); + gtk_widget_destroy (toolbar); + + update_fixed (t); +} + +static void +item_added_cb (EggToolbarsModel *model, + int toolbar_position, + int position, + EggEditableToolbar *t) +{ + GtkWidget *dock; + GtkWidget *toolbar; + GtkWidget *item; + GtkAction *action; + + toolbar = get_toolbar_nth (t, toolbar_position); + item = create_item (t, model, toolbar_position, position, &action); + gtk_toolbar_insert (GTK_TOOLBAR (toolbar), + GTK_TOOL_ITEM (item), position); + + dock = get_dock_nth (t, toolbar_position); + gtk_widget_set_size_request (dock, -1, -1); + gtk_widget_queue_resize_no_redraw (dock); + + /* FIXME Hack to make tooltip work from gtk */ + if (action) + { + g_object_notify (G_OBJECT (action), "tooltip"); + } +} + +static void +item_removed_cb (EggToolbarsModel *model, + int toolbar_position, + int position, + EggEditableToolbar *t) +{ + GtkWidget *toolbar; + GtkWidget *item; + + toolbar = get_toolbar_nth (t, toolbar_position); + item = GTK_WIDGET (gtk_toolbar_get_nth_item + (GTK_TOOLBAR (toolbar), position)); + g_return_if_fail (item != NULL); + gtk_container_remove (GTK_CONTAINER (toolbar), item); + + if (egg_toolbars_model_n_items (model, toolbar_position) == 0) + { + egg_toolbars_model_remove_toolbar (model, toolbar_position); + } +} + +static void +egg_editable_toolbar_construct (EggEditableToolbar *t) +{ + int i, l, n_items, n_toolbars; + EggToolbarsModel *model = t->priv->model; + + g_return_if_fail (model != NULL); + g_return_if_fail (t->priv->manager != NULL); + + n_toolbars = egg_toolbars_model_n_toolbars (model); + + for (i = 0; i < n_toolbars; i++) + { + GtkWidget *toolbar, *dock; + EggTbModelFlags flags; + + dock = create_dock (t); + gtk_box_pack_start (GTK_BOX (t), dock, TRUE, TRUE, 0); + toolbar = get_toolbar_nth (t, i); + + flags = egg_toolbars_model_get_flags (model, i); + if (flags & EGG_TB_MODEL_ICONS_ONLY) + { + gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_ICONS); + if (i == 0 && t->priv->fixed_toolbar) + { + set_fixed_style (t, GTK_TOOLBAR_ICONS); + } + } + + n_items = egg_toolbars_model_n_items (model, i); + for (l = 0; l < n_items; l++) + { + GtkWidget *item; + GtkAction *action; + + item = create_item (t, model, i, l, &action); + if (item) + { + gtk_toolbar_insert (GTK_TOOLBAR (toolbar), + GTK_TOOL_ITEM (item), l); + /* FIXME Hack to make tooltip work from gtk */ + if (action) + { + g_object_notify (G_OBJECT (action), "tooltip"); + } + } + else + { + egg_toolbars_model_remove_item (model, i, l); + l--; + n_items--; + } + } + + if (n_items == 0) + { + gtk_widget_set_size_request (dock, -1, MIN_TOOLBAR_HEIGHT); + } + } + + update_fixed (t); +} + +static void +egg_editable_toolbar_disconnect_model (EggEditableToolbar *toolbar) +{ + EggToolbarsModel *model = toolbar->priv->model; + + g_signal_handlers_disconnect_by_func + (model, G_CALLBACK (item_added_cb), toolbar); + g_signal_handlers_disconnect_by_func + (model, G_CALLBACK (item_removed_cb), toolbar); + g_signal_handlers_disconnect_by_func + (model, G_CALLBACK (toolbar_added_cb), toolbar); + g_signal_handlers_disconnect_by_func + (model, G_CALLBACK (toolbar_removed_cb), toolbar); + g_signal_handlers_disconnect_by_func + (model, G_CALLBACK (toolbar_changed_cb), toolbar); +} + +static void +egg_editable_toolbar_deconstruct (EggEditableToolbar *toolbar) +{ + EggToolbarsModel *model = toolbar->priv->model; + GList *children; + + g_return_if_fail (model != NULL); + + if (toolbar->priv->fixed_toolbar) + { + unset_fixed_style (toolbar); + unparent_fixed (toolbar); + } + + children = gtk_container_get_children (GTK_CONTAINER (toolbar)); + g_list_foreach (children, (GFunc) gtk_widget_destroy, NULL); + g_list_free (children); +} + +void +egg_editable_toolbar_set_model (EggEditableToolbar *toolbar, + EggToolbarsModel *model) +{ + g_return_if_fail (EGG_IS_TOOLBARS_MODEL (model)); + g_return_if_fail (EGG_IS_EDITABLE_TOOLBAR (toolbar)); + g_return_if_fail (toolbar->priv->manager); + + if (toolbar->priv->model == model) return; + + if (toolbar->priv->model) + { + egg_editable_toolbar_disconnect_model (toolbar); + egg_editable_toolbar_deconstruct (toolbar); + + g_object_unref (toolbar->priv->model); + } + + toolbar->priv->model = g_object_ref (model); + + egg_editable_toolbar_construct (toolbar); + + g_signal_connect (model, "item_added", + G_CALLBACK (item_added_cb), toolbar); + g_signal_connect (model, "item_removed", + G_CALLBACK (item_removed_cb), toolbar); + g_signal_connect (model, "toolbar_added", + G_CALLBACK (toolbar_added_cb), toolbar); + g_signal_connect (model, "toolbar_removed", + G_CALLBACK (toolbar_removed_cb), toolbar); + g_signal_connect (model, "toolbar_changed", + G_CALLBACK (toolbar_changed_cb), toolbar); +} + +static void +egg_editable_toolbar_set_ui_manager (EggEditableToolbar *t, + GtkUIManager *manager) +{ + g_return_if_fail (GTK_IS_UI_MANAGER (manager)); + + t->priv->manager = g_object_ref (manager); +} + +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_UI_MANAGER: + egg_editable_toolbar_set_ui_manager (t, g_value_get_object (value)); + break; + case PROP_TOOLBARS_MODEL: + egg_editable_toolbar_set_model (t, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + 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_UI_MANAGER: + g_value_set_object (value, t->priv->manager); + break; + case PROP_TOOLBARS_MODEL: + g_value_set_object (value, t->priv->model); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + 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; + + egg_editable_toolbar_signals[ACTION_REQUEST] = + g_signal_new ("action_request", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EggEditableToolbarClass, action_request), + NULL, NULL, g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, G_TYPE_STRING); + + g_object_class_install_property (object_class, + PROP_UI_MANAGER, + g_param_spec_object ("ui-manager", + "UI-Mmanager", + "UI Manager", + GTK_TYPE_UI_MANAGER, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_TOOLBARS_MODEL, + g_param_spec_object ("model", + "Model", + "Toolbars Model", + EGG_TYPE_TOOLBARS_MODEL, + G_PARAM_READWRITE)); + + g_type_class_add_private (object_class, sizeof (EggEditableToolbarPrivate)); +} + +static void +egg_editable_toolbar_init (EggEditableToolbar *t) +{ + t->priv = EGG_EDITABLE_TOOLBAR_GET_PRIVATE (t); +} + +static void +egg_editable_toolbar_finalize (GObject *object) +{ + EggEditableToolbar *t = EGG_EDITABLE_TOOLBAR (object); + + if (t->priv->fixed_toolbar) + { + g_object_unref (t->priv->fixed_toolbar); + } + + if (t->priv->manager) + { + g_object_unref (t->priv->manager); + } + + if (t->priv->model) + { + egg_editable_toolbar_disconnect_model (t); + g_object_unref (t->priv->model); + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +GtkWidget * +egg_editable_toolbar_new (GtkUIManager *manager) +{ + return GTK_WIDGET (g_object_new (EGG_TYPE_EDITABLE_TOOLBAR, + "ui-manager", manager, + NULL)); +} + +GtkWidget * +egg_editable_toolbar_new_with_model (GtkUIManager *manager, + EggToolbarsModel *model) +{ + return GTK_WIDGET (g_object_new (EGG_TYPE_EDITABLE_TOOLBAR, + "ui-manager", manager, + "model", model, + NULL)); +} + +gboolean +egg_editable_toolbar_get_edit_mode (EggEditableToolbar *etoolbar) +{ + return etoolbar->priv->edit_mode; +} + +void +egg_editable_toolbar_set_edit_mode (EggEditableToolbar *etoolbar, + gboolean mode) +{ + int i, l, n_toolbars, n_items; + + etoolbar->priv->edit_mode = mode; + + n_toolbars = get_n_toolbars (etoolbar); + for (i = 0; i < n_toolbars; i++) + { + GtkWidget *toolbar; + + toolbar = get_toolbar_nth (etoolbar, i); + n_items = gtk_toolbar_get_n_items (GTK_TOOLBAR (toolbar)); + for (l = 0; l < n_items; l++) + { + GtkToolItem *item; + const char *action_name, *type; + gboolean is_separator; + GtkAction *action = NULL; + + egg_toolbars_model_item_nth (etoolbar->priv->model, i, l, + &is_separator, &action_name, &type); + action = find_action (etoolbar, action_name); + + item = gtk_toolbar_get_nth_item (GTK_TOOLBAR (toolbar), l); + gtk_tool_item_set_use_drag_window (item, mode); + + if (mode) + { + set_drag_cursor (GTK_WIDGET (item)); + gtk_widget_set_sensitive (GTK_WIDGET (item), TRUE); + set_item_drag_source (etoolbar->priv->model, GTK_WIDGET (item), + action, is_separator, type); + } + else + { + unset_drag_cursor (GTK_WIDGET (item)); + gtk_drag_source_unset (GTK_WIDGET (item)); + + if (!is_separator) + { + g_object_notify (G_OBJECT (action), "sensitive"); + } + } + } + } +} + +void +egg_editable_toolbar_show (EggEditableToolbar *etoolbar, + const char *name) +{ + int i, n_toolbars; + EggToolbarsModel *model = etoolbar->priv->model; + + g_return_if_fail (model != NULL); + + n_toolbars = egg_toolbars_model_n_toolbars (model); + for (i = 0; i < n_toolbars; i++) + { + const char *toolbar_name; + + toolbar_name = egg_toolbars_model_toolbar_nth (model, i); + if (strcmp (toolbar_name, name) == 0) + { + gtk_widget_show (get_dock_nth (etoolbar, i)); + } + } +} + +void +egg_editable_toolbar_hide (EggEditableToolbar *etoolbar, + const char *name) +{ + int i, n_toolbars; + EggToolbarsModel *model = etoolbar->priv->model; + + g_return_if_fail (model != NULL); + + n_toolbars = egg_toolbars_model_n_toolbars (model); + for (i = 0; i < n_toolbars; i++) + { + const char *toolbar_name; + + toolbar_name = egg_toolbars_model_toolbar_nth (model, i); + if (strcmp (toolbar_name, name) == 0) + { + gtk_widget_hide (get_dock_nth (etoolbar, i)); + } + } +} + +void +egg_editable_toolbar_set_fixed (EggEditableToolbar *toolbar, + GtkToolItem *fixed) +{ + g_return_if_fail (EGG_IS_EDITABLE_TOOLBAR (toolbar)); + g_return_if_fail (!fixed || GTK_IS_TOOL_ITEM (fixed)); + + if (!toolbar->priv->fixed_toolbar) + { + toolbar->priv->fixed_toolbar = gtk_toolbar_new (); + gtk_toolbar_set_show_arrow (GTK_TOOLBAR (toolbar->priv->fixed_toolbar), FALSE); + g_object_ref (toolbar->priv->fixed_toolbar); + gtk_object_sink (GTK_OBJECT (toolbar->priv->fixed_toolbar)); + } + + if (toolbar->priv->fixed) + { + gtk_container_remove (GTK_CONTAINER (toolbar->priv->fixed_toolbar), + GTK_WIDGET (toolbar->priv->fixed)); + g_object_unref (toolbar->priv->fixed); + } + + toolbar->priv->fixed = fixed; + + if (fixed) + { + g_object_ref (fixed); + gtk_object_sink (GTK_OBJECT (fixed)); + + gtk_toolbar_insert (GTK_TOOLBAR (toolbar->priv->fixed_toolbar), fixed, 0); + } +} + +void +egg_editable_toolbar_set_drag_dest (EggEditableToolbar *etoolbar, + const GtkTargetEntry *targets, + gint n_targets, + const char *toolbar_name) +{ + int i, n_toolbars; + EggToolbarsModel *model = etoolbar->priv->model; + + g_return_if_fail (model != NULL); + + n_toolbars = egg_toolbars_model_n_toolbars (model); + for (i = 0; i < n_toolbars; i++) + { + const char *name; + + name = egg_toolbars_model_toolbar_nth (model, i); + if (strcmp (toolbar_name, name) == 0) + { + GtkWidget *widget = get_toolbar_nth (etoolbar, i); + + gtk_drag_dest_unset (widget); + gtk_drag_dest_set (widget, 0, + targets, n_targets, + GDK_ACTION_MOVE | GDK_ACTION_COPY); + } + } +} + +#define DEFAULT_ICON_HEIGHT 20 +#define DEFAULT_ICON_WIDTH 0 + +static void +fake_expose_widget (GtkWidget *widget, + GdkPixmap *pixmap) +{ + GdkWindow *tmp_window; + GdkEventExpose event; + + event.type = GDK_EXPOSE; + event.window = pixmap; + event.send_event = FALSE; + event.area = widget->allocation; + event.region = NULL; + event.count = 0; + + tmp_window = widget->window; + widget->window = pixmap; + gtk_widget_send_expose (widget, (GdkEvent *) &event); + widget->window = tmp_window; +} + +/* We should probably experiment some more with this. + * Right now the rendered icon is pretty good for most + * themes. However, the icon is slightly large for themes + * with large toolbar icons. + */ +static GdkPixbuf * +new_pixbuf_from_widget (GtkWidget *widget) +{ + GtkWidget *window; + GdkPixbuf *pixbuf; + GtkRequisition requisition; + GtkAllocation allocation; + GdkPixmap *pixmap; + GdkVisual *visual; + gint icon_width; + gint icon_height; + + icon_width = DEFAULT_ICON_WIDTH; + + if (!gtk_icon_size_lookup_for_settings (gtk_settings_get_default (), + GTK_ICON_SIZE_LARGE_TOOLBAR, + NULL, + &icon_height)) + { + icon_height = DEFAULT_ICON_HEIGHT; + } + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + + gtk_container_add (GTK_CONTAINER (window), widget); + gtk_widget_realize (window); + gtk_widget_show (widget); + gtk_widget_realize (widget); + gtk_widget_map (widget); + + /* Gtk will never set the width or height of a window to 0. So setting the width to + * 0 and than getting it will provide us with the minimum width needed to render + * the icon correctly, without any additional window background noise. + * This is needed mostly for pixmap based themes. + */ + gtk_window_set_default_size (GTK_WINDOW (window), icon_width, icon_height); + gtk_window_get_size (GTK_WINDOW (window),&icon_width, &icon_height); + + gtk_widget_size_request (window, &requisition); + allocation.x = 0; + allocation.y = 0; + allocation.width = icon_width; + allocation.height = icon_height; + gtk_widget_size_allocate (window, &allocation); + gtk_widget_size_request (window, &requisition); + + /* Create a pixmap */ + visual = gtk_widget_get_visual (window); + pixmap = gdk_pixmap_new (NULL, icon_width, icon_height, gdk_visual_get_best_depth()); + gdk_drawable_set_colormap (GDK_DRAWABLE (pixmap), gtk_widget_get_colormap (window)); + + /* Draw the window */ + gtk_widget_ensure_style (window); + g_assert (window->style); + g_assert (window->style->font_desc); + + fake_expose_widget (window, pixmap); + fake_expose_widget (widget, pixmap); + + pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, icon_width, icon_height); + gdk_pixbuf_get_from_drawable (pixbuf, pixmap, NULL, 0, 0, 0, 0, icon_width, icon_height); + + return pixbuf; +} + +static GdkPixbuf * +new_separator_pixbuf () +{ + GtkWidget *separator; + GdkPixbuf *pixbuf; + + separator = gtk_vseparator_new (); + pixbuf = new_pixbuf_from_widget (separator); + gtk_widget_destroy (separator); + return pixbuf; +} + +static void +update_separator_image (GtkImage *image) +{ + GdkPixbuf *pixbuf = new_separator_pixbuf (); + gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf); + g_object_unref (pixbuf); +} + +static gboolean +style_set_cb (GtkWidget *widget, + GtkStyle *previous_style, + GtkImage *image) +{ + + update_separator_image (image); + return FALSE; +} + +GtkWidget * +_egg_editable_toolbar_new_separator_image (void) +{ + GtkWidget *image = gtk_image_new (); + update_separator_image (GTK_IMAGE (image)); + g_signal_connect (G_OBJECT (image), "style_set", + G_CALLBACK (style_set_cb), GTK_IMAGE (image)); + + return image; +} + +EggToolbarsModel * +egg_editable_toolbar_get_model (EggEditableToolbar *etoolbar) +{ + return etoolbar->priv->model; +} diff --git a/cut-n-paste/toolbar-editor/egg-editable-toolbar.h b/cut-n-paste/toolbar-editor/egg-editable-toolbar.h new file mode 100644 index 0000000..2aa087b --- /dev/null +++ b/cut-n-paste/toolbar-editor/egg-editable-toolbar.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2003-2004 Marco Pesenti Gritti + * Copyright (C) 2004 Christian Persch + * + * 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. + * + * $Id$ + */ + +#ifndef EGG_EDITABLE_TOOLBAR_H +#define EGG_EDITABLE_TOOLBAR_H + +#include "egg-toolbars-model.h" + +#include +#include +#include +#include +#include + +G_BEGIN_DECLS + +typedef struct EggEditableToolbarClass EggEditableToolbarClass; + +#define EGG_TYPE_EDITABLE_TOOLBAR (egg_editable_toolbar_get_type ()) +#define EGG_EDITABLE_TOOLBAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_EDITABLE_TOOLBAR, EggEditableToolbar)) +#define EGG_EDITABLE_TOOLBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_EDITABLE_TOOLBAR, EggEditableToolbarClass)) +#define EGG_IS_EDITABLE_TOOLBAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_EDITABLE_TOOLBAR)) +#define EGG_IS_EDITABLE_TOOLBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_EDITABLE_TOOLBAR)) +#define EGG_EDITABLE_TOOLBAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_EDITABLE_TOOLBAR, EggEditableToolbarClass)) + + +typedef struct EggEditableToolbar EggEditableToolbar; +typedef struct EggEditableToolbarPrivate EggEditableToolbarPrivate; + +struct EggEditableToolbar +{ + GtkVBox parent_object; + + /*< private >*/ + EggEditableToolbarPrivate *priv; +}; + +struct EggEditableToolbarClass +{ + GtkVBoxClass parent_class; + + void (* action_request) (EggEditableToolbar *etoolbar, + const char *action_name); +}; + +GType egg_editable_toolbar_get_type (void); +GtkWidget *egg_editable_toolbar_new (GtkUIManager *manager); +GtkWidget *egg_editable_toolbar_new_with_model (GtkUIManager *manager, + EggToolbarsModel *model); +void egg_editable_toolbar_set_model (EggEditableToolbar *etoolbar, + EggToolbarsModel *model); +EggToolbarsModel *egg_editable_toolbar_get_model (EggEditableToolbar *etoolbar); +void egg_editable_toolbar_set_edit_mode (EggEditableToolbar *etoolbar, + gboolean mode); +gboolean egg_editable_toolbar_get_edit_mode (EggEditableToolbar *etoolbar); +void egg_editable_toolbar_show (EggEditableToolbar *etoolbar, + const char *name); +void egg_editable_toolbar_hide (EggEditableToolbar *etoolbar, + const char *name); +void egg_editable_toolbar_set_drag_dest (EggEditableToolbar *etoolbar, + const GtkTargetEntry *targets, + gint n_targets, + const char *toolbar_name); +void egg_editable_toolbar_set_fixed (EggEditableToolbar *etoolbar, + GtkToolItem *fixed); + + +/* Private Functions */ + +GtkWidget *_egg_editable_toolbar_new_separator_image (void); + +G_END_DECLS + +#endif diff --git a/cut-n-paste/toolbar-editor/egg-marshal.c b/cut-n-paste/toolbar-editor/egg-marshal.c new file mode 100644 index 0000000..4eae7e8 --- /dev/null +++ b/cut-n-paste/toolbar-editor/egg-marshal.c @@ -0,0 +1,3 @@ +#include "eggmarshalers.h" +#include "eggmarshalers.c" + diff --git a/cut-n-paste/toolbar-editor/egg-toolbar-editor.c b/cut-n-paste/toolbar-editor/egg-toolbar-editor.c new file mode 100644 index 0000000..b341945 --- /dev/null +++ b/cut-n-paste/toolbar-editor/egg-toolbar-editor.c @@ -0,0 +1,706 @@ +/* + * 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. + * + * $Id$ + */ + +#include "egg-toolbar-editor.h" +#include "egg-editable-toolbar.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static GtkTargetEntry dest_drag_types[] = { + {EGG_TOOLBAR_ITEM_TYPE, GTK_TARGET_SAME_APP, 0}, +}; +static int n_dest_drag_types = G_N_ELEMENTS (dest_drag_types); + +static GtkTargetEntry source_drag_types[] = { + {EGG_TOOLBAR_ITEM_TYPE, GTK_TARGET_SAME_APP, 0}, +}; +static int n_source_drag_types = G_N_ELEMENTS (source_drag_types); + +static void egg_toolbar_editor_class_init (EggToolbarEditorClass *klass); +static void egg_toolbar_editor_init (EggToolbarEditor *t); +static void egg_toolbar_editor_finalize (GObject *object); +static void update_actions_list (EggToolbarEditor *editor); +static void update_editor_sheet (EggToolbarEditor *editor); + +enum +{ + PROP_0, + PROP_UI_MANAGER, + PROP_TOOLBARS_MODEL +}; + +static GObjectClass *parent_class = NULL; + +#define EGG_TOOLBAR_EDITOR_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EGG_TYPE_TOOLBAR_EDITOR, EggToolbarEditorPrivate)) + +struct EggToolbarEditorPrivate +{ + GtkUIManager *manager; + EggToolbarsModel *model; + + GtkWidget *table; + GtkWidget *scrolled_window; + + GList *default_actions_list; + GList *actions_list; +}; + +GType +egg_toolbar_editor_get_type (void) +{ + static GType egg_toolbar_editor_type = 0; + + if (egg_toolbar_editor_type == 0) + { + static const GTypeInfo our_info = { + sizeof (EggToolbarEditorClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) egg_toolbar_editor_class_init, + NULL, + NULL, /* class_data */ + sizeof (EggToolbarEditor), + 0, /* n_preallocs */ + (GInstanceInitFunc) egg_toolbar_editor_init + }; + + egg_toolbar_editor_type = g_type_register_static (GTK_TYPE_VBOX, + "EggToolbarEditor", + &our_info, 0); + } + + return egg_toolbar_editor_type; +} + +static gint +compare_actions (gconstpointer a, + gconstpointer b) +{ + GValue value_a = { 0, }, value_b = { 0, }; + const char *short_label_a, *short_label_b; + int ret; + + g_value_init (&value_a, G_TYPE_STRING); + g_object_get_property (G_OBJECT (a), "short_label", &value_a); + short_label_a = g_value_get_string (&value_a); + + g_value_init (&value_b, G_TYPE_STRING); + g_object_get_property (G_OBJECT (b), "short_label", &value_b); + short_label_b = g_value_get_string (&value_b); + + ret = g_utf8_collate (short_label_a, short_label_b); + + g_value_unset (&value_a); + g_value_unset (&value_b); + + return ret; +} + +static GtkAction * +find_action (EggToolbarEditor *t, + const char *name) +{ + GList *l; + GtkAction *action = NULL; + + l = gtk_ui_manager_get_action_groups (t->priv->manager); + + g_return_val_if_fail (EGG_IS_TOOLBAR_EDITOR (t), NULL); + g_return_val_if_fail (name != NULL, NULL); + + for (; l != NULL; l = l->next) + { + GtkAction *tmp; + + tmp = gtk_action_group_get_action (GTK_ACTION_GROUP (l->data), name); + if (tmp) + action = tmp; + } + + return action; +} + +static void +egg_toolbar_editor_set_ui_manager (EggToolbarEditor *t, + GtkUIManager *manager) +{ + g_return_if_fail (GTK_IS_UI_MANAGER (manager)); + + t->priv->manager = g_object_ref (manager); +} + +static void +toolbar_removed_cb (EggToolbarsModel *model, + int position, + EggToolbarEditor *editor) +{ + update_actions_list (editor); + update_editor_sheet (editor); +} + +static void +egg_toolbar_editor_set_model (EggToolbarEditor *t, + EggToolbarsModel *model) +{ + g_return_if_fail (EGG_IS_TOOLBAR_EDITOR (t)); + + t->priv->model = g_object_ref (model); + + g_signal_connect_object (model, "toolbar_removed", + G_CALLBACK (toolbar_removed_cb), t, 0); +} + +static void +egg_toolbar_editor_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + EggToolbarEditor *t = EGG_TOOLBAR_EDITOR (object); + + switch (prop_id) + { + case PROP_UI_MANAGER: + egg_toolbar_editor_set_ui_manager (t, g_value_get_object (value)); + break; + case PROP_TOOLBARS_MODEL: + egg_toolbar_editor_set_model (t, g_value_get_object (value)); + break; + } +} + +static void +egg_toolbar_editor_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + EggToolbarEditor *t = EGG_TOOLBAR_EDITOR (object); + + switch (prop_id) + { + case PROP_UI_MANAGER: + g_value_set_object (value, t->priv->manager); + break; + case PROP_TOOLBARS_MODEL: + g_value_set_object (value, t->priv->model); + break; + } +} + +static void +egg_toolbar_editor_class_init (EggToolbarEditorClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + object_class->finalize = egg_toolbar_editor_finalize; + object_class->set_property = egg_toolbar_editor_set_property; + object_class->get_property = egg_toolbar_editor_get_property; + + g_object_class_install_property (object_class, + PROP_UI_MANAGER, + g_param_spec_object ("ui-manager", + "UI-Manager", + "UI Manager", + GTK_TYPE_UI_MANAGER, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, + PROP_TOOLBARS_MODEL, + g_param_spec_object ("model", + "Model", + "Toolbars Model", + EGG_TYPE_TOOLBARS_MODEL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_type_class_add_private (object_class, sizeof (EggToolbarEditorPrivate)); +} + +static void +egg_toolbar_editor_finalize (GObject *object) +{ + EggToolbarEditor *editor = EGG_TOOLBAR_EDITOR (object); + + if (editor->priv->manager) + { + g_object_unref (editor->priv->manager); + } + + if (editor->priv->model) + { + g_object_unref (editor->priv->model); + } + + g_list_free (editor->priv->default_actions_list); + g_list_free (editor->priv->actions_list); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +GtkWidget * +egg_toolbar_editor_new (GtkUIManager *manager, + EggToolbarsModel *model) +{ + return GTK_WIDGET (g_object_new (EGG_TYPE_TOOLBAR_EDITOR, + "ui-manager", manager, + "model", model, + NULL)); +} + +static void +drag_begin_cb (GtkWidget *widget, + GdkDragContext *context) +{ + gtk_widget_hide (widget); +} + +static void +drag_end_cb (GtkWidget *widget, + GdkDragContext *context) +{ + gtk_widget_show (widget); +} + +static void +editor_drag_data_received_cb (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *selection_data, + guint info, + guint time_, + EggToolbarEditor *editor) +{ + GtkAction *action; + const char *data; + + g_return_if_fail (EGG_IS_TOOLBAR_EDITOR (editor)); + g_return_if_fail (selection_data != NULL); + + if (selection_data->length <= 0 || selection_data->data == NULL) return; + + data = (const char *) selection_data->data; + + if (strcmp (data, "separator") == 0) return; + + action = find_action (editor, data); + g_return_if_fail (action != NULL); + + if (g_list_find (editor->priv->default_actions_list, action)) + { + editor->priv->actions_list = g_list_insert_sorted + (editor->priv->actions_list, action, compare_actions); + } + + update_editor_sheet (editor); +} + +static void +editor_drag_data_delete_cb (GtkWidget *widget, + GdkDragContext *context, + EggToolbarEditor *editor) +{ + GtkAction *action; + g_return_if_fail (EGG_IS_TOOLBAR_EDITOR (editor)); + + action = GTK_ACTION (g_object_get_data (G_OBJECT (widget), "egg-action")); + if (action) + { + editor->priv->actions_list = g_list_remove + (editor->priv->actions_list, action); + } + + update_editor_sheet (editor); +} + +static void +drag_data_get_cb (GtkWidget *widget, + GdkDragContext *context, + GtkSelectionData *selection_data, + guint info, + guint32 time, + EggToolbarEditor *editor) +{ + GtkAction *action; + const char *target; + + action = GTK_ACTION (g_object_get_data (G_OBJECT (widget), "egg-action")); + + if (action) + { + target = gtk_action_get_name (action); + } + else + { + target = "separator"; + } + + gtk_selection_data_set (selection_data, + selection_data->target, 8, target, strlen (target)); +} + +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 void +set_drag_cursor (GtkWidget *widget) +{ + GdkCursor *cursor; + GdkPixbuf *pixbuf; + + pixbuf = gdk_pixbuf_new_from_file (CURSOR_DIR "/hand-open.png", NULL); + cursor = gdk_cursor_new_from_pixbuf (gdk_display_get_default (), pixbuf, 12, 12); + + gdk_window_set_cursor (widget->window, cursor); + gdk_cursor_unref (cursor); + g_object_unref (pixbuf); +} + +static void +event_box_realize_cb (GtkWidget *widget, GtkImage *icon) +{ + GtkImageType type; + + set_drag_cursor (widget); + + type = gtk_image_get_storage_type (icon); + if (type == GTK_IMAGE_STOCK) + { + gchar *stock_id; + GdkPixbuf *pixbuf; + gtk_image_get_stock (icon, &stock_id, NULL); + pixbuf = gtk_widget_render_icon (widget, stock_id, + GTK_ICON_SIZE_LARGE_TOOLBAR, NULL); + gtk_drag_source_set_icon_pixbuf (widget, pixbuf); + g_object_unref (pixbuf); + } + else if (type == GTK_IMAGE_PIXBUF) + { + GdkPixbuf *pixbuf = gtk_image_get_pixbuf (icon); + gtk_drag_source_set_icon_pixbuf (widget, pixbuf); + } +} + +static GtkWidget * +editor_create_item (EggToolbarEditor *editor, + GtkImage *icon, + const char *label_text, + GdkDragAction action) +{ + GtkWidget *event_box; + GtkWidget *vbox; + GtkWidget *label; + gchar *label_no_mnemonic = NULL; + + event_box = gtk_event_box_new (); + gtk_event_box_set_visible_window (GTK_EVENT_BOX (event_box), FALSE); + gtk_widget_show (event_box); + gtk_drag_source_set (event_box, + GDK_BUTTON1_MASK, + source_drag_types, n_source_drag_types, action); + g_signal_connect (event_box, "drag_data_get", + G_CALLBACK (drag_data_get_cb), editor); + g_signal_connect (event_box, "drag_data_delete", + G_CALLBACK (editor_drag_data_delete_cb), editor); + g_signal_connect_after (event_box, "realize", + G_CALLBACK (event_box_realize_cb), icon); + + if (action == GDK_ACTION_MOVE) + { + g_signal_connect (event_box, "drag_begin", + G_CALLBACK (drag_begin_cb), NULL); + g_signal_connect (event_box, "drag_end", + G_CALLBACK (drag_end_cb), NULL); + } + + vbox = gtk_vbox_new (0, FALSE); + gtk_widget_show (vbox); + gtk_container_add (GTK_CONTAINER (event_box), vbox); + + gtk_widget_show (GTK_WIDGET (icon)); + gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (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 (EggToolbarEditor *editor) +{ + GList *l; + GList *to_drag; + int x, y, height, width; + GtkWidget *table; + GtkWidget *viewport; + GtkWidget *item; + GtkWidget *icon; + + g_return_if_fail (EGG_IS_TOOLBAR_EDITOR (editor)); + + viewport = GTK_BIN (editor->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); + editor->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 (editor->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), editor); + + to_drag = editor->priv->actions_list; + + x = y = 0; + width = 4; + height = (g_list_length (to_drag)) / width + 1; + gtk_table_resize (GTK_TABLE (editor->priv->table), height, width); + + for (l = to_drag; l != NULL; l = l->next) + { + GtkAction *action = (l->data); + const char *stock_id, *short_label; + GValue value = { 0, }; + + g_value_init (&value, G_TYPE_STRING); + g_object_get_property (G_OBJECT (action), "stock_id", &value); + stock_id = g_value_get_string (&value); + icon = gtk_image_new_from_stock + (stock_id ? stock_id : GTK_STOCK_DND, + GTK_ICON_SIZE_LARGE_TOOLBAR); + g_value_unset (&value); + + g_value_init (&value, G_TYPE_STRING); + g_object_get_property (G_OBJECT (action), "short_label", &value); + short_label = g_value_get_string (&value); + item = editor_create_item (editor, GTK_IMAGE (icon), + short_label, GDK_ACTION_MOVE); + g_value_unset (&value); + g_object_set_data (G_OBJECT (item), "egg-action", action); + gtk_table_attach_defaults (GTK_TABLE (editor->priv->table), + item, x, x + 1, y, y + 1); + + x++; + if (x >= width) + { + x = 0; + y++; + } + } + + icon = _egg_editable_toolbar_new_separator_image (); + item = editor_create_item (editor, GTK_IMAGE (icon), _("Separator"), + GDK_ACTION_COPY); + gtk_table_attach_defaults (GTK_TABLE (editor->priv->table), + item, x, x + 1, y, y + 1); +} + +static void +setup_editor (EggToolbarEditor *editor) +{ + GtkWidget *scrolled_window; + GtkWidget *label_hbox; + GtkWidget *image; + GtkWidget *label; + + g_return_if_fail (EGG_IS_TOOLBAR_EDITOR (editor)); + + gtk_container_set_border_width (GTK_CONTAINER (editor), 12); + scrolled_window = gtk_scrolled_window_new (NULL, NULL); + editor->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 (editor), scrolled_window, TRUE, TRUE, 0); + label_hbox = gtk_hbox_new (FALSE, 6); + gtk_widget_show (label_hbox); + gtk_box_pack_start (GTK_BOX (editor), 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); +} + +static void +egg_toolbar_editor_init (EggToolbarEditor *t) +{ + t->priv = EGG_TOOLBAR_EDITOR_GET_PRIVATE (t); + + t->priv->manager = NULL; + t->priv->default_actions_list = NULL; + t->priv->actions_list = NULL; + + setup_editor (t); +} + +void +egg_toolbar_editor_add_action (EggToolbarEditor *editor, + const char *action_name) +{ + GtkAction *action; + + action = find_action (editor, action_name); + g_return_if_fail (action != NULL); + + editor->priv->default_actions_list = g_list_insert_sorted + (editor->priv->default_actions_list, action, compare_actions); +} + +static void +parse_item_list (EggToolbarEditor *t, + xmlNodePtr child) +{ + while (child) + { + if (xmlStrEqual (child->name, "toolitem")) + { + xmlChar *name; + + name = xmlGetProp (child, "name"); + egg_toolbar_editor_add_action (t, name); + xmlFree (name); + } + child = child->next; + } +} + +static gboolean +model_has_action (EggToolbarsModel *model, GtkAction *action) +{ + int i, l, n_items, n_toolbars; + + n_toolbars = egg_toolbars_model_n_toolbars (model); + for (i = 0; i < n_toolbars; i++) + { + n_items = egg_toolbars_model_n_items (model, i); + for (l = 0; l < n_items; l++) + { + const char *name; + const char *action_name; + gboolean sep; + + egg_toolbars_model_item_nth (model, i, l, &sep, &name, NULL); + action_name = gtk_action_get_name (action); + if (!sep && strcmp (name, action_name) == 0) return TRUE; + } + } + + return FALSE; +} + +static void +update_actions_list (EggToolbarEditor *editor) +{ + GList *l; + + if (editor->priv->actions_list) + g_list_free (editor->priv->actions_list); + + /* Remove the already used items */ + editor->priv->actions_list = NULL; + + for (l = editor->priv->default_actions_list; l != NULL; l = l->next) + { + GtkAction *action = GTK_ACTION (l->data); + + if (!model_has_action (editor->priv->model, action)) + { + editor->priv->actions_list = g_list_insert_sorted + (editor->priv->actions_list, action, compare_actions); + } + } +} + +void +egg_toolbar_editor_load_actions (EggToolbarEditor *editor, + const char *xml_file) +{ + xmlDocPtr doc; + xmlNodePtr root; + xmlNodePtr child; + + doc = xmlParseFile (xml_file); + root = xmlDocGetRootElement (doc); + child = root->children; + + while (child) + { + if (xmlStrEqual (child->name, "available")) + { + parse_item_list (editor, child->children); + } + child = child->next; + } + + xmlFreeDoc (doc); + + update_actions_list (editor); + update_editor_sheet (editor); +} diff --git a/cut-n-paste/toolbar-editor/egg-toolbar-editor.h b/cut-n-paste/toolbar-editor/egg-toolbar-editor.h new file mode 100644 index 0000000..e1e1f35 --- /dev/null +++ b/cut-n-paste/toolbar-editor/egg-toolbar-editor.h @@ -0,0 +1,67 @@ +/* + * 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. + */ + +#ifndef EGG_TOOLBAR_EDITOR_H +#define EGG_TOOLBAR_EDITOR_H + +#include +#include + +#include "egg-toolbars-model.h" + +G_BEGIN_DECLS + +typedef struct EggToolbarEditorClass EggToolbarEditorClass; + +#define EGG_TYPE_TOOLBAR_EDITOR (egg_toolbar_editor_get_type ()) +#define EGG_TOOLBAR_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_TOOLBAR_EDITOR, EggToolbarEditor)) +#define EGG_TOOLBAR_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_TOOLBAR_EDITOR, EggToolbarEditorClass)) +#define EGG_IS_TOOLBAR_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_TOOLBAR_EDITOR)) +#define EGG_IS_TOOLBAR_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_TOOLBAR_EDITOR)) +#define EGG_TOOLBAR_EDITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_TOOLBAR_EDITOR, EggToolbarEditorClass)) + + +typedef struct EggToolbarEditor EggToolbarEditor; +typedef struct EggToolbarEditorPrivate EggToolbarEditorPrivate; + +struct EggToolbarEditor +{ + GtkVBox parent_object; + + /*< private >*/ + EggToolbarEditorPrivate *priv; +}; + +struct EggToolbarEditorClass +{ + GtkVBoxClass parent_class; +}; + + +GType egg_toolbar_editor_get_type (void); +GtkWidget *egg_toolbar_editor_new (GtkUIManager *manager, + EggToolbarsModel *model); +void egg_toolbar_editor_load_actions (EggToolbarEditor *editor, + const char *xml_file); + +void egg_toolbar_editor_add_action (EggToolbarEditor *editor, + const char *action_name); + +G_END_DECLS + +#endif diff --git a/cut-n-paste/toolbar-editor/egg-toolbars-model.c b/cut-n-paste/toolbar-editor/egg-toolbars-model.c new file mode 100644 index 0000000..8da9908 --- /dev/null +++ b/cut-n-paste/toolbar-editor/egg-toolbars-model.c @@ -0,0 +1,830 @@ +/* + * Copyright (C) 2002-2004 Marco Pesenti Gritti + * Copyright (C) 2004 Christian Persch + * + * 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. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "egg-toolbars-model.h" +#include "eggmarshalers.h" + +#include +#include +#include +#include + +static void egg_toolbars_model_class_init (EggToolbarsModelClass *klass); +static void egg_toolbars_model_init (EggToolbarsModel *t); +static void egg_toolbars_model_finalize (GObject *object); + +enum +{ + ITEM_ADDED, + ITEM_REMOVED, + TOOLBAR_ADDED, + TOOLBAR_CHANGED, + TOOLBAR_REMOVED, + GET_ITEM_TYPE, + GET_ITEM_ID, + GET_ITEM_DATA, + LAST_SIGNAL +}; + +typedef struct +{ + char *name; + EggTbModelFlags flags; +} EggToolbarsToolbar; + +typedef struct +{ + char *id; + char *type; + gboolean separator; +} EggToolbarsItem; + +static guint signals[LAST_SIGNAL] = { 0 }; + +static GObjectClass *parent_class = NULL; + +#define EGG_TOOLBARS_MODEL_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EGG_TYPE_TOOLBARS_MODEL, EggToolbarsModelPrivate)) + +struct EggToolbarsModelPrivate +{ + GNode *toolbars; +}; + +GType +egg_toolbars_model_get_type (void) +{ + static GType type = 0; + + if (type == 0) + { + static const GTypeInfo our_info = { + sizeof (EggToolbarsModelClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) egg_toolbars_model_class_init, + NULL, + NULL, /* class_data */ + sizeof (EggToolbarsModel), + 0, /* n_preallocs */ + (GInstanceInitFunc) egg_toolbars_model_init + }; + + type = g_type_register_static (G_TYPE_OBJECT, + "EggToolbarsModel", + &our_info, 0); + } + + return type; +} + +static xmlDocPtr +egg_toolbars_model_to_xml (EggToolbarsModel *t) +{ + GNode *l1, *l2, *tl; + xmlDocPtr doc; + + g_return_val_if_fail (EGG_IS_TOOLBARS_MODEL (t), NULL); + + tl = t->priv->toolbars; + + xmlIndentTreeOutput = TRUE; + doc = xmlNewDoc ("1.0"); + doc->children = xmlNewDocNode (doc, NULL, "toolbars", NULL); + + for (l1 = tl->children; l1 != NULL; l1 = l1->next) + { + xmlNodePtr tnode; + EggToolbarsToolbar *toolbar = l1->data; + + tnode = xmlNewChild (doc->children, NULL, "toolbar", NULL); + xmlSetProp (tnode, "name", toolbar->name); + + for (l2 = l1->children; l2 != NULL; l2 = l2->next) + { + xmlNodePtr node; + EggToolbarsItem *item = l2->data; + + if (item->separator) + { + node = xmlNewChild (tnode, NULL, "separator", NULL); + } + else + { + char *data; + + node = xmlNewChild (tnode, NULL, "toolitem", NULL); + data = egg_toolbars_model_get_item_data (t, item->type, item->id); + xmlSetProp (node, "type", item->type); + xmlSetProp (node, "name", data); + g_free (data); + } + } + } + + return doc; +} + +static gboolean +safe_save_xml (const char *xml_file, xmlDocPtr doc) +{ + char *tmp_file; + char *old_file; + gboolean old_exist; + gboolean retval = TRUE; + + tmp_file = g_strconcat (xml_file, ".tmp", NULL); + old_file = g_strconcat (xml_file, ".old", NULL); + + if (xmlSaveFormatFile (tmp_file, doc, 1) <= 0) + { + g_warning ("Failed to write XML data to %s", tmp_file); + goto failed; + } + + old_exist = g_file_test (xml_file, G_FILE_TEST_EXISTS); + + if (old_exist) + { + if (rename (xml_file, old_file) < 0) + { + g_warning ("Failed to rename %s to %s", xml_file, old_file); + retval = FALSE; + goto failed; + } + } + + if (rename (tmp_file, xml_file) < 0) + { + g_warning ("Failed to rename %s to %s", tmp_file, xml_file); + + if (rename (old_file, xml_file) < 0) + { + g_warning ("Failed to restore %s from %s", xml_file, tmp_file); + } + retval = FALSE; + goto failed; + } + + if (old_exist) + { + if (unlink (old_file) < 0) + { + g_warning ("Failed to delete old file %s", old_file); + } + } + + failed: + g_free (old_file); + g_free (tmp_file); + + return retval; +} + +void +egg_toolbars_model_save (EggToolbarsModel *t, + const char *xml_file, + const char *version) +{ + xmlDocPtr doc; + xmlNodePtr root; + + g_return_if_fail (EGG_IS_TOOLBARS_MODEL (t)); + + doc = egg_toolbars_model_to_xml (t); + root = xmlDocGetRootElement (doc); + xmlSetProp (root, "version", version); + safe_save_xml (xml_file, doc); + xmlFreeDoc (doc); +} + +static EggToolbarsToolbar * +toolbars_toolbar_new (const char *name) +{ + EggToolbarsToolbar *toolbar; + + toolbar = g_new (EggToolbarsToolbar, 1); + toolbar->name = g_strdup (name); + toolbar->flags = 0; + + return toolbar; +} + +static EggToolbarsItem * +toolbars_item_new (const char *id, + const char *type, + gboolean separator) +{ + EggToolbarsItem *item; + + g_return_val_if_fail (id != NULL, NULL); + g_return_val_if_fail (type != NULL, NULL); + + item = g_new (EggToolbarsItem, 1); + item->id = g_strdup (id); + item->type = g_strdup (type); + item->separator = separator; + + return item; +} + +static void +free_toolbar_node (GNode *toolbar_node) +{ + EggToolbarsToolbar *toolbar = toolbar_node->data; + + g_free (toolbar->name); + g_free (toolbar); + + g_node_destroy (toolbar_node); +} + +static void +free_item_node (GNode *item_node) +{ + EggToolbarsItem *item = item_node->data; + + g_free (item->id); + g_free (item->type); + g_free (item); + + g_node_destroy (item_node); +} + +EggTbModelFlags +egg_toolbars_model_get_flags (EggToolbarsModel *t, + int toolbar_position) +{ + GNode *toolbar_node; + EggToolbarsToolbar *toolbar; + + toolbar_node = g_node_nth_child (t->priv->toolbars, toolbar_position); + g_return_val_if_fail (toolbar_node != NULL, 0); + + toolbar = toolbar_node->data; + + return toolbar->flags; +} + +void +egg_toolbars_model_set_flags (EggToolbarsModel *t, + int toolbar_position, + EggTbModelFlags flags) +{ + GNode *toolbar_node; + EggToolbarsToolbar *toolbar; + + toolbar_node = g_node_nth_child (t->priv->toolbars, toolbar_position); + g_return_if_fail (toolbar_node != NULL); + + toolbar = toolbar_node->data; + + toolbar->flags = flags; + + g_signal_emit (G_OBJECT (t), signals[TOOLBAR_CHANGED], + 0, toolbar_position); +} + +void +egg_toolbars_model_add_separator (EggToolbarsModel *t, + int toolbar_position, + int position) +{ + GNode *parent_node; + GNode *node; + EggToolbarsItem *item; + int real_position; + + g_return_if_fail (EGG_IS_TOOLBARS_MODEL (t)); + + parent_node = g_node_nth_child (t->priv->toolbars, toolbar_position); + item = toolbars_item_new ("separator", EGG_TOOLBAR_ITEM_TYPE, TRUE); + node = g_node_new (item); + g_node_insert (parent_node, position, node); + + real_position = g_node_child_position (parent_node, node); + + g_signal_emit (G_OBJECT (t), signals[ITEM_ADDED], 0, + toolbar_position, real_position); +} + +static gboolean +impl_add_item (EggToolbarsModel *t, + int toolbar_position, + int position, + const char *id, + const char *type) +{ + GNode *parent_node; + GNode *node; + EggToolbarsItem *item; + int real_position; + + g_return_val_if_fail (EGG_IS_TOOLBARS_MODEL (t), FALSE); + g_return_val_if_fail (id != NULL, FALSE); + g_return_val_if_fail (type != NULL, FALSE); + + parent_node = g_node_nth_child (t->priv->toolbars, toolbar_position); + item = toolbars_item_new (id, type, FALSE); + node = g_node_new (item); + g_node_insert (parent_node, position, node); + + real_position = g_node_child_position (parent_node, node); + + g_signal_emit (G_OBJECT (t), signals[ITEM_ADDED], 0, + toolbar_position, real_position); + + return TRUE; +} + +static void +parse_item_list (EggToolbarsModel *t, + xmlNodePtr child, + int position) +{ + while (child) + { + if (xmlStrEqual (child->name, "toolitem")) + { + xmlChar *name, *type; + char *id; + + name = xmlGetProp (child, "name"); + type = xmlGetProp (child, "type"); + if (type == NULL) + { + type = xmlStrdup (EGG_TOOLBAR_ITEM_TYPE); + } + + if (name != NULL && name[0] != '\0' && type != NULL) + { + id = egg_toolbars_model_get_item_id (t, type, name); + if (id != NULL) + { + egg_toolbars_model_add_item (t, position, -1, id, type); + } + g_free (id); + } + xmlFree (name); + xmlFree (type); + } + else if (xmlStrEqual (child->name, "separator")) + { + egg_toolbars_model_add_separator (t, position, -1); + } + + child = child->next; + } +} + +int +egg_toolbars_model_add_toolbar (EggToolbarsModel *t, + int position, + const char *name) +{ + GNode *node; + int real_position; + + g_return_val_if_fail (EGG_IS_TOOLBARS_MODEL (t), -1); + + node = g_node_new (toolbars_toolbar_new (name)); + g_node_insert (t->priv->toolbars, position, node); + + real_position = g_node_child_position (t->priv->toolbars, node); + + g_signal_emit (G_OBJECT (t), signals[TOOLBAR_ADDED], + 0, real_position); + + return g_node_child_position (t->priv->toolbars, node); +} + +static void +parse_toolbars (EggToolbarsModel *t, + xmlNodePtr child) +{ + while (child) + { + if (xmlStrEqual (child->name, "toolbar")) + { + xmlChar *name; + xmlChar *style; + int position; + + name = xmlGetProp (child, "name"); + position = egg_toolbars_model_add_toolbar (t, -1, name); + xmlFree (name); + + style = xmlGetProp (child, "style"); + if (style && xmlStrEqual (style, "icons-only")) + { + /* FIXME: use toolbar position instead of 0 */ + egg_toolbars_model_set_flags (t, 0, EGG_TB_MODEL_ICONS_ONLY); + } + xmlFree (style); + + parse_item_list (t, child->children, position); + } + + child = child->next; + } +} + +gboolean +egg_toolbars_model_load (EggToolbarsModel *t, + const char *xml_file) +{ + xmlDocPtr doc; + xmlNodePtr root; + + g_return_val_if_fail (EGG_IS_TOOLBARS_MODEL (t), FALSE); + + if (!xml_file || !g_file_test (xml_file, G_FILE_TEST_EXISTS)) return FALSE; + + doc = xmlParseFile (xml_file); + if (doc == NULL) + { + g_warning ("Failed to load XML data from %s", xml_file); + return FALSE; + } + root = xmlDocGetRootElement (doc); + + parse_toolbars (t, root->children); + + xmlFreeDoc (doc); + + return TRUE; +} + +static char * +impl_get_item_id (EggToolbarsModel *t, + const char *type, + const char *data) +{ + if (strcmp (type, EGG_TOOLBAR_ITEM_TYPE) == 0) + { + return g_strdup (data); + } + + return NULL; +} + +static char * +impl_get_item_data (EggToolbarsModel *t, + const char *type, + const char *id) +{ + if (strcmp (type, EGG_TOOLBAR_ITEM_TYPE) == 0) + { + return g_strdup (id); + } + + return NULL; +} + +static char * +impl_get_item_type (EggToolbarsModel *t, + GdkAtom type) +{ + if (gdk_atom_intern (EGG_TOOLBAR_ITEM_TYPE, FALSE) == type) + { + return g_strdup (EGG_TOOLBAR_ITEM_TYPE); + } + + return NULL; +} + +static gboolean +_egg_accumulator_STRING (GSignalInvocationHint *ihint, + GValue *return_accu, + const GValue *handler_return, + gpointer dummy) +{ + gboolean continue_emission; + const char *retval; + + retval = g_value_get_string (handler_return); + g_value_set_string (return_accu, retval); + continue_emission = !retval || !retval[0]; + + return continue_emission; +} + + +static void +egg_toolbars_model_class_init (EggToolbarsModelClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + object_class->finalize = egg_toolbars_model_finalize; + + klass->add_item = impl_add_item; + klass->get_item_id = impl_get_item_id; + klass->get_item_data = impl_get_item_data; + klass->get_item_type = impl_get_item_type; + + signals[ITEM_ADDED] = + g_signal_new ("item_added", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EggToolbarsModelClass, item_added), + NULL, NULL, _egg_marshal_VOID__INT_INT, + G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT); + signals[TOOLBAR_ADDED] = + g_signal_new ("toolbar_added", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EggToolbarsModelClass, toolbar_added), + NULL, NULL, g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, 1, G_TYPE_INT); + signals[ITEM_REMOVED] = + g_signal_new ("item_removed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EggToolbarsModelClass, item_removed), + NULL, NULL, _egg_marshal_VOID__INT_INT, + G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT); + signals[TOOLBAR_REMOVED] = + g_signal_new ("toolbar_removed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EggToolbarsModelClass, toolbar_removed), + NULL, NULL, g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, 1, G_TYPE_INT); + signals[TOOLBAR_CHANGED] = + g_signal_new ("toolbar_changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EggToolbarsModelClass, toolbar_changed), + NULL, NULL, g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, 1, G_TYPE_INT); + signals[GET_ITEM_TYPE] = + g_signal_new ("get_item_type", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EggToolbarsModelClass, get_item_type), + _egg_accumulator_STRING, NULL, + _egg_marshal_STRING__POINTER, + G_TYPE_STRING, 1, G_TYPE_POINTER); + signals[GET_ITEM_ID] = + g_signal_new ("get_item_id", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EggToolbarsModelClass, get_item_id), + _egg_accumulator_STRING, NULL, + _egg_marshal_STRING__STRING_STRING, + G_TYPE_STRING, 2, G_TYPE_STRING, G_TYPE_STRING); + signals[GET_ITEM_DATA] = + g_signal_new ("get_item_data", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EggToolbarsModelClass, get_item_data), + _egg_accumulator_STRING, NULL, + _egg_marshal_STRING__STRING_STRING, + G_TYPE_STRING, 2, G_TYPE_STRING, G_TYPE_STRING); + + g_type_class_add_private (object_class, sizeof (EggToolbarsModelPrivate)); +} + +static void +egg_toolbars_model_init (EggToolbarsModel *t) +{ + t->priv =EGG_TOOLBARS_MODEL_GET_PRIVATE (t); + + t->priv->toolbars = g_node_new (NULL); +} + +static void +free_toolbar (GNode *toolbar_node) +{ + g_node_children_foreach (toolbar_node, G_TRAVERSE_ALL, + (GNodeForeachFunc) free_item_node, NULL); + free_toolbar_node (toolbar_node); +} + +static void +egg_toolbars_model_finalize (GObject *object) +{ + EggToolbarsModel *t = EGG_TOOLBARS_MODEL (object); + + g_node_children_foreach (t->priv->toolbars, G_TRAVERSE_ALL, + (GNodeForeachFunc) free_toolbar, NULL); + g_node_destroy (t->priv->toolbars); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +EggToolbarsModel * +egg_toolbars_model_new (void) +{ + return EGG_TOOLBARS_MODEL (g_object_new (EGG_TYPE_TOOLBARS_MODEL, NULL)); +} + +void +egg_toolbars_model_remove_toolbar (EggToolbarsModel *t, + int position) +{ + GNode *node; + EggTbModelFlags flags; + + g_return_if_fail (EGG_IS_TOOLBARS_MODEL (t)); + + flags = egg_toolbars_model_get_flags (t, position); + + if (!(flags & EGG_TB_MODEL_NOT_REMOVABLE)) + { + node = g_node_nth_child (t->priv->toolbars, position); + g_return_if_fail (node != NULL); + + free_toolbar_node (node); + + g_signal_emit (G_OBJECT (t), signals[TOOLBAR_REMOVED], + 0, position); + } +} + +void +egg_toolbars_model_remove_item (EggToolbarsModel *t, + int toolbar_position, + int position) +{ + GNode *node, *toolbar; + + g_return_if_fail (EGG_IS_TOOLBARS_MODEL (t)); + + toolbar = g_node_nth_child (t->priv->toolbars, toolbar_position); + g_return_if_fail (toolbar != NULL); + + node = g_node_nth_child (toolbar, position); + g_return_if_fail (node != NULL); + + free_item_node (node); + + g_signal_emit (G_OBJECT (t), signals[ITEM_REMOVED], 0, + toolbar_position, position); +} + +void +egg_toolbars_model_move_item (EggToolbarsModel *t, + int toolbar_position, + int position, + int new_toolbar_position, + int new_position) +{ + GNode *node, *toolbar, *new_toolbar; + + g_return_if_fail (EGG_IS_TOOLBARS_MODEL (t)); + + toolbar = g_node_nth_child (t->priv->toolbars, toolbar_position); + g_return_if_fail (toolbar != NULL); + + new_toolbar = g_node_nth_child (t->priv->toolbars, new_toolbar_position); + g_return_if_fail (new_toolbar != NULL); + + node = g_node_nth_child (toolbar, position); + g_return_if_fail (node != NULL); + + g_node_unlink (node); + + g_signal_emit (G_OBJECT (t), signals[ITEM_REMOVED], 0, + toolbar_position, position); + + g_node_insert (new_toolbar, new_position, node); + + g_signal_emit (G_OBJECT (t), signals[ITEM_ADDED], 0, + new_toolbar_position, new_position); +} + +int +egg_toolbars_model_n_items (EggToolbarsModel *t, + int toolbar_position) +{ + GNode *toolbar; + + toolbar = g_node_nth_child (t->priv->toolbars, toolbar_position); + g_return_val_if_fail (toolbar != NULL, -1); + + return g_node_n_children (toolbar); +} + +void +egg_toolbars_model_item_nth (EggToolbarsModel *t, + int toolbar_position, + int position, + gboolean *is_separator, + const char **id, + const char **type) +{ + GNode *toolbar; + GNode *item; + EggToolbarsItem *idata; + + toolbar = g_node_nth_child (t->priv->toolbars, toolbar_position); + g_return_if_fail (toolbar != NULL); + + item = g_node_nth_child (toolbar, position); + g_return_if_fail (item != NULL); + + idata = item->data; + + *is_separator = idata->separator; + + if (id) + { + *id = idata->id; + } + + if (type) + { + *type = idata->type; + } +} + +int +egg_toolbars_model_n_toolbars (EggToolbarsModel *t) +{ + return g_node_n_children (t->priv->toolbars); +} + +const char * +egg_toolbars_model_toolbar_nth (EggToolbarsModel *t, + int position) +{ + GNode *toolbar; + EggToolbarsToolbar *tdata; + + toolbar = g_node_nth_child (t->priv->toolbars, position); + g_return_val_if_fail (toolbar != NULL, NULL); + + tdata = toolbar->data; + + return tdata->name; +} + +gboolean +egg_toolbars_model_add_item (EggToolbarsModel *t, + int toolbar_position, + int position, + const char *id, + const char *type) +{ + EggToolbarsModelClass *klass = EGG_TOOLBARS_MODEL_GET_CLASS (t); + return klass->add_item (t, toolbar_position, position, id, type); +} + +char * +egg_toolbars_model_get_item_id (EggToolbarsModel *t, + const char *type, + const char *name) +{ + char *retval; + + g_signal_emit (t, signals[GET_ITEM_ID], 0, type, name, &retval); + + return retval; +} + +char * +egg_toolbars_model_get_item_data (EggToolbarsModel *t, + const char *type, + const char *id) +{ + char *retval; + + g_signal_emit (t, signals[GET_ITEM_DATA], 0, type, id, &retval); + + return retval; +} + +char * +egg_toolbars_model_get_item_type (EggToolbarsModel *t, + GdkAtom type) +{ + char *retval; + + g_signal_emit (t, signals[GET_ITEM_TYPE], 0, type, &retval); + + return retval; +} diff --git a/cut-n-paste/toolbar-editor/egg-toolbars-model.h b/cut-n-paste/toolbar-editor/egg-toolbars-model.h new file mode 100644 index 0000000..11d9bbe --- /dev/null +++ b/cut-n-paste/toolbar-editor/egg-toolbars-model.h @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2003-2004 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. + */ + +#ifndef EGG_TOOLBARS_MODEL_H +#define EGG_TOOLBARS_MODEL_H + +#include +#include +#include + +G_BEGIN_DECLS + + +#define EGG_TYPE_TOOLBARS_MODEL (egg_toolbars_model_get_type ()) +#define EGG_TOOLBARS_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_TOOLBARS_MODEL, EggToolbarsModel)) +#define EGG_TOOLBARS_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_TOOLBARS_MODEL, EggToolbarsModelClass)) +#define EGG_IS_TOOLBARS_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_TOOLBARS_MODEL)) +#define EGG_IS_TOOLBARS_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_TOOLBARS_MODEL)) +#define EGG_TOOLBARS_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_TOOLBARS_MODEL, EggToolbarsModelClass)) + +typedef struct EggToolbarsModel EggToolbarsModel; +typedef struct EggToolbarsModelPrivate EggToolbarsModelPrivate; +typedef struct EggToolbarsModelClass EggToolbarsModelClass; + +#define EGG_TOOLBAR_ITEM_TYPE "application/x-toolbar-item" + +typedef enum +{ + EGG_TB_MODEL_NOT_REMOVABLE = 1 << 0, + EGG_TB_MODEL_ICONS_ONLY = 1 << 1, + EGG_TB_MODEL_TEXT_ONLY = 1 << 2, + EGG_TB_MODEL_ICONS_TEXT = 1 << 3, + EGG_TB_MODEL_ICONS_TEXT_HORIZ = 1 << 4, + EGG_TB_MODEL_ACCEPT_ITEMS_ONLY = 1 << 5 +} EggTbModelFlags; + +struct EggToolbarsModel +{ + GObject parent_object; + + /*< private >*/ + EggToolbarsModelPrivate *priv; +}; + +struct EggToolbarsModelClass +{ + GObjectClass parent_class; + + /* Signals */ + void (* item_added) (EggToolbarsModel *model, + int toolbar_position, + int position); + void (* item_removed) (EggToolbarsModel *model, + int toolbar_position, + int position); + void (* toolbar_added) (EggToolbarsModel *model, + int position); + void (* toolbar_changed) (EggToolbarsModel *model, + int position); + void (* toolbar_removed) (EggToolbarsModel *model, + int position); + char * (* get_item_type) (EggToolbarsModel *model, + GdkAtom dnd_type); + char * (* get_item_id) (EggToolbarsModel *model, + const char *type, + const char *data); + char * (* get_item_data) (EggToolbarsModel *model, + const char *type, + const char *id); + + /* Virtual Table */ + gboolean (* add_item) (EggToolbarsModel *t, + int toolbar_position, + int position, + const char *id, + const char *type); +}; + +GType egg_toolbars_model_get_type (void); +EggToolbarsModel *egg_toolbars_model_new (void); +gboolean egg_toolbars_model_load (EggToolbarsModel *model, + const char *xml_file); +void egg_toolbars_model_save (EggToolbarsModel *model, + const char *xml_file, + const char *version); +int egg_toolbars_model_add_toolbar (EggToolbarsModel *model, + int position, + const char *name); +EggTbModelFlags egg_toolbars_model_get_flags (EggToolbarsModel *model, + int toolbar_position); +void egg_toolbars_model_set_flags (EggToolbarsModel *model, + int toolbar_position, + EggTbModelFlags flags); +void egg_toolbars_model_add_separator (EggToolbarsModel *model, + int toolbar_position, + int position); +char *egg_toolbars_model_get_item_type (EggToolbarsModel *model, + GdkAtom dnd_type); +char *egg_toolbars_model_get_item_id (EggToolbarsModel *model, + const char *type, + const char *name); +char *egg_toolbars_model_get_item_data (EggToolbarsModel *model, + const char *type, + const char *id); +gboolean egg_toolbars_model_add_item (EggToolbarsModel *model, + int toolbar_position, + int position, + const char *id, + const char *type); +void egg_toolbars_model_remove_toolbar (EggToolbarsModel *model, + int position); +void egg_toolbars_model_remove_item (EggToolbarsModel *model, + int toolbar_position, + int position); +void egg_toolbars_model_move_item (EggToolbarsModel *model, + int toolbar_position, + int position, + int new_toolbar_position, + int new_position); +int egg_toolbars_model_n_items (EggToolbarsModel *model, + int toolbar_position); +void egg_toolbars_model_item_nth (EggToolbarsModel *model, + int toolbar_position, + int position, + gboolean *is_separator, + const char **id, + const char **type); +int egg_toolbars_model_n_toolbars (EggToolbarsModel *model); +const char *egg_toolbars_model_toolbar_nth (EggToolbarsModel *model, + int position); + +G_END_DECLS + +#endif diff --git a/cut-n-paste/toolbar-editor/eggmarshalers.list b/cut-n-paste/toolbar-editor/eggmarshalers.list new file mode 100644 index 0000000..7b79b0f --- /dev/null +++ b/cut-n-paste/toolbar-editor/eggmarshalers.list @@ -0,0 +1,19 @@ +VOID:OBJECT,OBJECT +VOID:OBJECT,STRING,LONG,LONG +VOID:OBJECT,LONG +VOID:OBJECT,STRING,STRING +VOID:UINT,UINT +BOOLEAN:INT +BOOLEAN:ENUM +BOOLEAN:VOID +OBJECT:VOID +VOID:VOID +VOID:INT,INT +VOID:UINT,UINT +VOID:BOOLEAN +VOID:OBJECT,ENUM,BOXED +VOID:BOXED +BOOLEAN:BOOLEAN +BOOLEAN:OBJECT,STRING,STRING +STRING:POINTER +STRING:STRING,STRING diff --git a/data/Makefile.am b/data/Makefile.am index 0b1bd18..9eece2b 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -6,6 +6,8 @@ NULL = uidir = $(pkgdatadir) ui_DATA = \ evince-ui.xml \ + evince-toolbar.xml \ + hand-open.png \ $(NULL) gladedir = $(pkgdatadir) diff --git a/data/evince-toolbar.xml b/data/evince-toolbar.xml new file mode 100644 index 0000000..1f44055 --- /dev/null +++ b/data/evince-toolbar.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/evince-ui.xml b/data/evince-ui.xml index 973a0d7..4df3061 100644 --- a/data/evince-ui.xml +++ b/data/evince-ui.xml @@ -17,6 +17,8 @@ + + @@ -52,20 +54,6 @@ - - - - - - - - - - diff --git a/data/hand-open.png b/data/hand-open.png new file mode 100644 index 0000000..55ac7b8 --- /dev/null +++ b/data/hand-open.png Binary files differ diff --git a/po/POTFILES.in b/po/POTFILES.in index 8ede0c2..efc30ec 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -5,6 +5,7 @@ cut-n-paste/zoom-control/ephy-zoom.h data/evince-password.glade data/evince.desktop.in data/evince.schemas.in +cut-n-paste/toolbar-editor/egg-toolbar-editor.c dvi/mdvi-lib/dviread.c dvi/mdvi-lib/font.c dvi/mdvi-lib/fontmap.c diff --git a/shell/Makefile.am b/shell/Makefile.am index 498be8a..032735d 100644 --- a/shell/Makefile.am +++ b/shell/Makefile.am @@ -6,6 +6,7 @@ INCLUDES= \ -I$(top_srcdir)/lib \ -I$(top_srcdir)/cut-n-paste/recent-files/ \ -I$(top_srcdir)/cut-n-paste/zoom-control/ \ + -I$(top_srcdir)/cut-n-paste/toolbar-editor/ \ -I$(top_srcdir)/backend \ -I$(top_srcdir)/pdf \ -I$(top_srcdir)/pixbuf \ @@ -86,6 +87,7 @@ evince_LDADD= \ $(top_builddir)/cut-n-paste/recent-files/librecent.la \ $(top_builddir)/cut-n-paste/zoom-control/libephymisc.la \ $(top_builddir)/cut-n-paste/zoom-control/libephywidgets.la \ + $(top_builddir)/cut-n-paste/toolbar-editor/libtoolbareditor.la \ $(top_builddir)/lib/libev.la \ libevbackendfactory.la \ $(NULL) diff --git a/shell/ev-window.c b/shell/ev-window.c index 652d435..8bdfc20 100644 --- a/shell/ev-window.c +++ b/shell/ev-window.c @@ -47,6 +47,9 @@ #include "egg-recent-view-gtk.h" #include "egg-recent-view.h" #include "egg-recent-model.h" +#include "egg-toolbar-editor.h" +#include "egg-toolbars-model.h" +#include "egg-editable-toolbar.h" #include "ephy-zoom.h" #include "ephy-zoom-action.h" @@ -92,8 +95,13 @@ struct _EvWindowPrivate { GtkWidget *view; GtkWidget *page_view; GtkWidget *password_view; + GtkActionGroup *action_group; GtkUIManager *ui_manager; + + gchar *toolbar_file; + EggToolbarsModel *toolbar_model; + GtkWidget *statusbar; guint help_message_cid; guint view_message_cid; @@ -294,7 +302,10 @@ update_chrome_visibility (EvWindow *window) findbar = (priv->chrome & EV_CHROME_FINDBAR) != 0; set_widget_visibility (priv->menubar, menubar); + set_widget_visibility (priv->toolbar_dock, toolbar); + set_action_sensitive (window, "EditToolbar", toolbar); + set_widget_visibility (priv->sidebar, sidebar); set_widget_visibility (priv->find_bar, findbar); set_widget_visibility (priv->statusbar, statusbar); @@ -1597,6 +1608,49 @@ ev_window_set_page_mode (EvWindow *window, } static void +ev_window_cmd_edit_toolbar_cb (GtkDialog *dialog, gint response, gpointer data) +{ + EvWindow *ev_window = EV_WINDOW (data); + egg_editable_toolbar_set_edit_mode (EGG_EDITABLE_TOOLBAR (ev_window->priv->toolbar), FALSE); + egg_toolbars_model_save (ev_window->priv->toolbar_model, ev_window->priv->toolbar_file, "1.0"); + gtk_widget_destroy (GTK_WIDGET (dialog)); +} + +static void +ev_window_cmd_edit_toolbar (GtkAction *action, EvWindow *ev_window) +{ + GtkWidget *dialog; + GtkWidget *editor; + g_return_if_fail (EV_IS_WINDOW (ev_window)); + + dialog = gtk_dialog_new_with_buttons (_("Toolbar editor"), GTK_WINDOW (ev_window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_CLOSE, + GTK_RESPONSE_CANCEL, + NULL); + gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); + gtk_window_set_default_size (GTK_WINDOW (dialog), 500, 400); + + editor = egg_toolbar_editor_new (ev_window->priv->ui_manager, + ev_window->priv->toolbar_model); + gtk_container_set_border_width (GTK_CONTAINER (editor), 5); + gtk_box_set_spacing (GTK_BOX (EGG_TOOLBAR_EDITOR (editor)), 5); + + gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), editor); + egg_toolbar_editor_load_actions (EGG_TOOLBAR_EDITOR (editor), + DATADIR"/evince-toolbar.xml"); + + egg_editable_toolbar_set_edit_mode + (EGG_EDITABLE_TOOLBAR (ev_window->priv->toolbar), TRUE); + + gtk_widget_show_all (dialog); + + g_signal_connect (G_OBJECT (dialog), "response", + G_CALLBACK (ev_window_cmd_edit_toolbar_cb), + ev_window); +} + +static void ev_window_cmd_view_zoom_in (GtkAction *action, EvWindow *ev_window) { g_return_if_fail (EV_IS_WINDOW (ev_window)); @@ -2114,6 +2168,13 @@ ev_window_dispose (GObject *object) EvWindow *window = EV_WINDOW (object); EvWindowPrivate *priv = window->priv; + if (priv->toolbar_model) { + g_object_unref (priv->toolbar_model); + g_free (priv->toolbar_file); + priv->toolbar_model = NULL; + priv->toolbar_file = NULL; + } + if (priv->ui_manager) { g_object_unref (priv->ui_manager); priv->ui_manager = NULL; @@ -2217,6 +2278,9 @@ static const GtkActionEntry entries[] = { { "EditFindNext", NULL, N_("Find Ne_xt"), "G", N_("Find next occurrence of the word or phrase"), G_CALLBACK (ev_window_cmd_edit_find_next) }, + { "EditToolbar", NULL, N_("Toolbar..."), NULL, + N_("Open Toolbar Editor Dialog"), + G_CALLBACK (ev_window_cmd_edit_toolbar) }, /* View menu */ { "ViewZoomIn", GTK_STOCK_ZOOM_IN, NULL, "plus", @@ -2541,6 +2605,20 @@ ev_window_init (EvWindow *ev_window) ev_window->priv->menubar, FALSE, FALSE, 0); + /* Toolbar editor */ + ev_window->priv->toolbar_model = egg_toolbars_model_new (); + + ev_window->priv->toolbar_file = g_build_filename + (g_get_home_dir (), ".gnome2/evince_toolbar.xml", NULL); + + if (!g_file_test (ev_window->priv->toolbar_file, G_FILE_TEST_EXISTS)) { + egg_toolbars_model_load (ev_window->priv->toolbar_model, + DATADIR"/evince-toolbar.xml"); + } else { + egg_toolbars_model_load (ev_window->priv->toolbar_model, + ev_window->priv->toolbar_file); + } + /* This sucks, but there is no way to have a draw=no, expand=true separator * in a GtkUIManager-built toolbar. So, just add another toolbar. * See gtk+ bug 166489. @@ -2550,9 +2628,10 @@ ev_window_init (EvWindow *ev_window) FALSE, FALSE, 0); gtk_widget_show (toolbar_dock); - ev_window->priv->toolbar = - gtk_ui_manager_get_widget (ev_window->priv->ui_manager, - "/ToolBar"); + ev_window->priv->toolbar = egg_editable_toolbar_new_with_model + (ev_window->priv->ui_manager, ev_window->priv->toolbar_model); + egg_editable_toolbar_show (EGG_EDITABLE_TOOLBAR (ev_window->priv->toolbar), + "DefaultToolBar"); gtk_box_pack_start (GTK_BOX (toolbar_dock), ev_window->priv->toolbar, TRUE, TRUE, 0); gtk_widget_show (ev_window->priv->toolbar); @@ -2560,6 +2639,7 @@ ev_window_init (EvWindow *ev_window) ev_window->priv->fullscreen_toolbar = gtk_ui_manager_get_widget (ev_window->priv->ui_manager, "/LeaveFullscreenToolbar"); gtk_toolbar_set_show_arrow (GTK_TOOLBAR (ev_window->priv->fullscreen_toolbar), TRUE); + /* Add the main area */ ev_window->priv->hpaned = gtk_hpaned_new (); -- cgit v0.9.1