diff options
Diffstat (limited to 'tools/glade/glade/glade_menu_editor.c')
-rw-r--r-- | tools/glade/glade/glade_menu_editor.c | 3729 |
1 files changed, 3729 insertions, 0 deletions
diff --git a/tools/glade/glade/glade_menu_editor.c b/tools/glade/glade/glade_menu_editor.c new file mode 100644 index 00000000..2535f4b0 --- /dev/null +++ b/tools/glade/glade/glade_menu_editor.c @@ -0,0 +1,3729 @@ + +/* Gtk+ User Interface Builder + * Copyright (C) 1998 Damon Chaplin + * + * 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 of the License, 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. + */ + +/* + * The Menu Editor window, based on initial work by Javier Arriero País + * and John Looney. + */ + +#include <string.h> +#include <time.h> + +#include <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> +#include <gtk/gtkarrow.h> +#include <gtk/gtkaccellabel.h> +#include <gtk/gtkclist.h> +#include <gtk/gtkcombo.h> +#include <gtk/gtkentry.h> +#include <gtk/gtkeventbox.h> +#include <gtk/gtkfilechooserdialog.h> +#include <gtk/gtkframe.h> +#include <gtk/gtkhbox.h> +#include <gtk/gtkhbbox.h> +#include <gtk/gtkhseparator.h> +#include <gtk/gtkiconfactory.h> +#include <gtk/gtkimage.h> +#include <gtk/gtkimagemenuitem.h> +#include <gtk/gtklist.h> +#include <gtk/gtkmain.h> +#include <gtk/gtkmenu.h> +#include <gtk/gtkmenubar.h> +#include <gtk/gtkradiobutton.h> +#include <gtk/gtkradiomenuitem.h> +#include <gtk/gtkscrolledwindow.h> +#include <gtk/gtkseparatormenuitem.h> +#include <gtk/gtkstock.h> +#include <gtk/gtktable.h> +#include <gtk/gtktearoffmenuitem.h> +#include <gtk/gtkvbox.h> + +#include "gladeconfig.h" + +#ifdef USE_GNOME +#include <gnome.h> +#include "glade_gnome.h" +#endif + +#include "gb.h" +#include "glade_menu_editor.h" +#include "glade_keys_dialog.h" +#include "glade_project.h" +#include "gbwidget.h" +#include "tree.h" +#include "utils.h" + +#define GladeMenuEditorIndexKey "glade-menu-editor-index-key" +#define GladeMenuEditorStockIDKey "glade-menu-editor-stock-id" + +/* How many pixels to indent levels of the menu hierarchy in the clist. */ +#define GB_INDENT 10 + +/* The text to display if the item is a separator. */ +#define GB_SEPARATOR_TEXT "---" + +/* This sets the order of the clist columns. */ +#define GB_MENUED_NUM_COLS 8 + +#define GLD_COL_LABEL 0 +#define GLD_COL_TYPE 1 +#define GLD_COL_ACCEL 2 +#define GLD_COL_NAME 3 +#define GLD_COL_HANDLER 4 +#define GLD_COL_ACTIVE 5 +#define GLD_COL_GROUP 6 +#define GLD_COL_ICON 7 + +typedef enum + { + GB_MENU_ITEM_NORMAL, + GB_MENU_ITEM_CHECK, + GB_MENU_ITEM_RADIO + } +GbMenuItemType; + + +/* This holds information on one menu item. */ +typedef struct _GbMenuItemData GbMenuItemData; +struct _GbMenuItemData + { + gint stock_item_index; /* Stock menu item index, 0 == None. */ + gchar *label; /* Text to display. */ + gchar *name; /* Widget name. */ + gchar *handler; /* Handler to call when selected. */ + time_t last_mod_time; /* The time the handler was last updated. */ + gchar *icon; /* Icon filename, or Stock icon name. */ + gchar *tooltip; /* Tooltip text. */ + GbMenuItemType type; /* Type - normal/check/radio. */ + gboolean active; /* If the item is initially active. */ + GbMenuItemData *group; /* Points to the item data of the first widget + in the radio group, or NULL if it is in its + own group or is the first item in group. */ + guint8 modifiers; /* Control/Shift/Alt flags. */ + gchar *key; /* Name of accelerator key. */ + + gint level; /* Level in menu hierarchy. */ + gboolean generate_name; /* If the name should be auto-generated. */ + gboolean generate_handler; /* If the handler should be auto-generated. */ + + GladeWidgetData *wdata; /* The widget data associated with the old + menu item, if there was one. */ + }; + + +static void glade_menu_editor_class_init (GladeMenuEditorClass * klass); +static void glade_menu_editor_init (GladeMenuEditor * dialog); +static void glade_menu_editor_destroy (GtkObject *object); + +/* If the menu widget we are editing is destroyed, the menu editor is destroyed + as well. */ +static void glade_menu_editor_on_menu_destroyed (GtkWidget *menu, + GtkWidget *menued); + +static void on_menu_editor_ok (GtkWidget *button, + GladeMenuEditor *menued); +static void on_menu_editor_apply (GtkWidget *button, + GladeMenuEditor *menued); +static void on_menu_editor_close (GtkWidget *widget, + GladeMenuEditor *menued); + +/* This sets the menubar/popup menu whose children are displayed in the + menu editor, i.e. converted to items in the clist. */ +static void glade_menu_editor_set_menu (GladeMenuEditor *menued, + GtkMenuShell *menu); + +/* This updates the given widget, based on the settings in the menu editor. + It removes all the current children of the menu and recreates it. */ +static void glade_menu_editor_update_menu (GladeMenuEditor *menued); + + +static gboolean on_key_press (GtkWidget * widget, + GdkEventKey * event, + gpointer user_data); +static void on_clist_select_row (GtkWidget * clist, + gint row, + gint column, + GdkEventButton * event, + gpointer user_data); +static void on_clist_unselect_row (GtkWidget * clist, + gint row, + gint column, + GdkEventButton * event, + gpointer user_data); +static void on_entry_changed (GtkWidget * entry, + gpointer user_data); +static void on_icon_button_clicked (GtkWidget * button, + gpointer user_data); +static void on_icon_filesel_response (GtkWidget * filesel, + gint response_id, + GladeMenuEditor *menued); +static void on_stock_item_entry_changed (GtkWidget * entry, + gpointer user_data); +static gboolean on_label_entry_key_press (GtkWidget * widget, + GdkEventKey * event, + gpointer user_data); +static void on_radiobutton_toggled (GtkWidget * togglebutton, + gpointer user_data); +static void on_checkbutton_toggled (GtkWidget * togglebutton, + gpointer user_data); +static void on_state_button_toggled (GtkToggleButton * togglebutton, + gpointer user_data); +static void on_accel_key_button_clicked (GtkButton * button, + gpointer user_data); +static void on_up_button_clicked (GtkButton * button, + gpointer user_data); +static void on_down_button_clicked (GtkButton * button, + gpointer user_data); +static void on_left_button_clicked (GtkButton * button, + gpointer user_data); +static void on_right_button_clicked (GtkButton * button, + gpointer user_data); +static void on_add_button_clicked (GtkWidget * button, + gpointer user_data); +static void on_add_child_button_clicked (GtkWidget * button, + gpointer user_data); +static void on_add_separator_button_clicked (GtkWidget * button, + gpointer user_data); +static void add_item (GladeMenuEditor * menued, + gboolean as_child, + gboolean separator); +static void on_delete_button_clicked (GtkWidget * widget, + gpointer user_data); + +static void on_keys_dialog_clist_select (GtkWidget * widget, + gint row, + gint column, + GdkEventButton * bevent, + GladeMenuEditor * menued); +static void on_keys_dialog_response (GtkWidget * widget, gint response_id, + GladeMenuEditor * menued); + +static gint get_selected_row (GladeMenuEditor * menued); +static GbMenuItemData* get_selected_item (GladeMenuEditor * menued); +static void set_interface_state (GladeMenuEditor * menued); +static gchar *get_accel_string (gchar * key, + guint8 modifiers); +static void insert_item (GtkCList * clist, + GbMenuItemData * item, + gint row); +static void ensure_visible (GtkWidget *clist, + gint row); +static void update_current_item (GladeMenuEditor * menued); +static gboolean item_property_changed (gchar *new, gchar *old); +static gchar* copy_item_property (gchar *property); +static void clear_form (GladeMenuEditor * menued, + gboolean full); +static void show_item_properties (GladeMenuEditor * menued); +static void insert_items (GtkWidget * clist, + GList * items, + gint row); +static GList *remove_item_and_children (GtkWidget * clist, + gint row); +static GtkWidget* create_radio_menu_item (GtkMenuShell *menu, + GbMenuItemData *item, + GHashTable *group_hash); +static gchar* generate_name (GladeMenuEditor *menued, + gchar *label); +static gchar* generate_handler (GladeMenuEditor *menued, + gint row, + gchar *label, + gchar *name); +static void check_generated_handlers (GladeMenuEditor *menued); +static gboolean is_parent (GladeMenuEditor *menued, + gint row); +static void set_submenu (GladeMenuEditor *menued, + GtkMenuShell *menu, + gint level); +static void glade_menu_editor_reset (GladeMenuEditor *menued); +static void glade_menu_editor_free_item (GbMenuItemData *item); + +static void update_radio_groups (GladeMenuEditor * menued); +static void normalize_radio_groups (GladeMenuEditor * menued); +static GbMenuItemData* find_radio_group (GtkRadioMenuItem *menuitem, + GList **groups, + GbMenuItemData *item); +static void remove_from_radio_group (GladeMenuEditor * menued, + GbMenuItemData *item); + +static GtkWindowClass *parent_class = NULL; + + +GType +glade_menu_editor_get_type (void) +{ + static GType glade_menu_editor_type = 0; + + if (!glade_menu_editor_type) + { + GtkTypeInfo glade_menu_editor_info = + { + "GladeMenuEditor", + sizeof (GladeMenuEditor), + sizeof (GladeMenuEditorClass), + (GtkClassInitFunc) glade_menu_editor_class_init, + (GtkObjectInitFunc) glade_menu_editor_init, + /* reserved_1 */ NULL, + /* reserved_2 */ NULL, + (GtkClassInitFunc) NULL, + }; + + glade_menu_editor_type = gtk_type_unique (gtk_window_get_type (), + &glade_menu_editor_info); + } + + return glade_menu_editor_type; +} + +static void +glade_menu_editor_class_init (GladeMenuEditorClass * class) +{ + GtkObjectClass *object_class; + GtkWidgetClass *widget_class; + + object_class = (GtkObjectClass *) class; + widget_class = (GtkWidgetClass *) class; + + parent_class = gtk_type_class (gtk_window_get_type ()); + + object_class->destroy = glade_menu_editor_destroy; +} + + +/* FIXME: This is mostly a temporary hack until GtkEntry is fixed in GTK+ + 2.0.x so we don't get "changed" twice when we call gtk_entry_set_text(). + Though we also need this to set it to "" when NULL is passed in. */ +static void +set_entry_text (GtkEntry *entry, + const gchar *text) +{ + gint tmp_pos; + + GtkEditable *editable; + + g_return_if_fail (GTK_IS_ENTRY (entry)); + + if (!text) + text = ""; + + editable = GTK_EDITABLE (entry); + + g_signal_handlers_block_by_func (editable, on_entry_changed, NULL); + gtk_editable_delete_text (GTK_EDITABLE(entry), 0, -1); + g_signal_handlers_unblock_by_func (editable, on_entry_changed, NULL); + + tmp_pos = 0; + gtk_editable_insert_text (editable, text, strlen (text), &tmp_pos); +} + + +static void +glade_menu_editor_init (GladeMenuEditor * menued) +{ + menued->keys_dialog = NULL; + menued->filesel = NULL; + menued->project = NULL; + menued->menu = NULL; + menued->updating_widgets = FALSE; + menued->gnome_support = FALSE; +} + + +static void +glade_menu_editor_construct (GladeMenuEditor * menued, + GladeProject * project) +{ + GtkWidget *vbox2, *vbox1, *scrolled_win; + GtkWidget *hbox1; + GtkWidget *vbox3; + GtkWidget *table1; + GtkWidget *eventbox3; + GtkWidget *eventbox2; + GtkWidget *eventbox1; + GtkWidget *table2; + GSList *table2_group = NULL; + GtkWidget *table3; + GtkWidget *accel_key_button; + GtkWidget *hbox2; + GtkWidget *label9; + GtkWidget *label8; + GtkWidget *hbuttonbox3; + GtkWidget *arrow1; + GtkWidget *arrow2; + GtkWidget *arrow3; + GtkWidget *arrow4; + GtkWidget *button_table; + GtkWidget *hseparator1; + GtkWidget *hbuttonbox1; + GtkWidget *listitem; + GtkTooltips *tooltips; + gchar *titles[GB_MENUED_NUM_COLS]; + gint row; + GSList *elem; + gchar *stock_id; + GtkWidget *icon_hbox; + gchar *label_text; + gint idx; +#ifdef USE_GNOME + GnomeUIInfo *uiinfo; + GtkWidget *pixmap, *label, *hbox, *separator; +#endif + + menued->project = project; +#ifdef USE_GNOME + if (glade_project_get_gnome_support (project)) + menued->gnome_support = TRUE; +#endif + + tooltips = gtk_tooltips_new (); + + gtk_container_set_border_width (GTK_CONTAINER (menued), 8); + gtk_window_set_title (GTK_WINDOW (menued), _ ("Menu Editor")); + gtk_window_set_policy (GTK_WINDOW (menued), FALSE, TRUE, FALSE); + gtk_window_set_wmclass (GTK_WINDOW (menued), "menu_editor", "Glade"); + + vbox2 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox2); + gtk_container_add (GTK_CONTAINER (menued), vbox2); + + hbox1 = gtk_hbox_new (FALSE, 6); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox2), hbox1, TRUE, TRUE, 0); + + vbox1 = gtk_vbox_new (FALSE, 4); + gtk_widget_show (vbox1); + gtk_box_pack_start (GTK_BOX (hbox1), vbox1, TRUE, TRUE, 0); + + titles[GLD_COL_LABEL] = _("Label"); + titles[GLD_COL_TYPE] = _("Type"); + titles[GLD_COL_ACCEL] = _("Accelerator"); + titles[GLD_COL_NAME] = _("Name"); + titles[GLD_COL_HANDLER] = _("Handler"); + titles[GLD_COL_ACTIVE] = _("Active"); + titles[GLD_COL_GROUP] = _("Group"); + titles[GLD_COL_ICON] = _("Icon"); + + menued->clist = gtk_clist_new_with_titles (GB_MENUED_NUM_COLS, titles); + gtk_widget_show (menued->clist); + GTK_WIDGET_SET_FLAGS (menued->clist, GTK_CAN_FOCUS); + gtk_signal_connect (GTK_OBJECT (menued->clist), "key_press_event", + GTK_SIGNAL_FUNC (on_key_press), + NULL); + gtk_widget_set_usize (menued->clist, 300, -1); + gtk_signal_connect (GTK_OBJECT (menued->clist), "select_row", + GTK_SIGNAL_FUNC (on_clist_select_row), NULL); + gtk_signal_connect (GTK_OBJECT (menued->clist), "unselect_row", + GTK_SIGNAL_FUNC (on_clist_unselect_row), NULL); + gtk_clist_set_column_width (GTK_CLIST (menued->clist), GLD_COL_LABEL, 144); + gtk_clist_set_column_width (GTK_CLIST (menued->clist), GLD_COL_TYPE, 42); + gtk_clist_set_column_width (GTK_CLIST (menued->clist), GLD_COL_ACCEL, 120); + gtk_clist_set_column_width (GTK_CLIST (menued->clist), GLD_COL_NAME, 100); + gtk_clist_set_column_width (GTK_CLIST (menued->clist), GLD_COL_HANDLER, 172); + gtk_clist_set_column_width (GTK_CLIST (menued->clist), GLD_COL_ICON, 172); + gtk_clist_set_column_width (GTK_CLIST (menued->clist), GLD_COL_ACTIVE, 42); + gtk_clist_set_column_width (GTK_CLIST (menued->clist), GLD_COL_GROUP, 75); + gtk_clist_column_titles_show (GTK_CLIST (menued->clist)); + gtk_clist_column_titles_passive (GTK_CLIST (menued->clist)); + + scrolled_win = gtk_scrolled_window_new (NULL, NULL); + gtk_container_add (GTK_CONTAINER (scrolled_win), menued->clist); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_box_pack_start (GTK_BOX (vbox1), scrolled_win, TRUE, TRUE, 0); + gtk_widget_show (scrolled_win); + + hbuttonbox3 = gtk_hbutton_box_new (); + gtk_widget_show (hbuttonbox3); + gtk_box_pack_start (GTK_BOX (vbox1), hbuttonbox3, FALSE, TRUE, 0); + gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox3), + GTK_BUTTONBOX_SPREAD); + gtk_box_set_spacing (GTK_BOX (hbuttonbox3), 6); + + menued->up_button = gtk_button_new (); + gtk_widget_show (menued->up_button); + gtk_container_add (GTK_CONTAINER (hbuttonbox3), menued->up_button); + gtk_tooltips_set_tip (tooltips, menued->up_button, _ ("Move the item and its children up one place in the list"), NULL); + gtk_signal_connect (GTK_OBJECT (menued->up_button), "clicked", + GTK_SIGNAL_FUNC (on_up_button_clicked), + NULL); + + arrow1 = gtk_arrow_new (GTK_ARROW_UP, GTK_SHADOW_OUT); + gtk_widget_show (arrow1); + gtk_container_add (GTK_CONTAINER (menued->up_button), arrow1); + + menued->down_button = gtk_button_new (); + gtk_widget_show (menued->down_button); + gtk_container_add (GTK_CONTAINER (hbuttonbox3), menued->down_button); + gtk_tooltips_set_tip (tooltips, menued->down_button, _ ("Move the item and its children down one place in the list"), NULL); + gtk_signal_connect (GTK_OBJECT (menued->down_button), "clicked", + GTK_SIGNAL_FUNC (on_down_button_clicked), + NULL); + + arrow2 = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_OUT); + gtk_widget_show (arrow2); + gtk_container_add (GTK_CONTAINER (menued->down_button), arrow2); + + menued->left_button = gtk_button_new (); + gtk_widget_show (menued->left_button); + gtk_container_add (GTK_CONTAINER (hbuttonbox3), menued->left_button); + gtk_tooltips_set_tip (tooltips, menued->left_button, _ ("Move the item and its children up one level"), NULL); + gtk_signal_connect (GTK_OBJECT (menued->left_button), "clicked", + GTK_SIGNAL_FUNC (on_left_button_clicked), + NULL); + + arrow3 = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_OUT); + gtk_widget_show (arrow3); + gtk_container_add (GTK_CONTAINER (menued->left_button), arrow3); + + menued->right_button = gtk_button_new (); + gtk_widget_show (menued->right_button); + gtk_container_add (GTK_CONTAINER (hbuttonbox3), menued->right_button); + gtk_tooltips_set_tip (tooltips, menued->right_button, _ ("Move the item and its children down one level"), NULL); + gtk_signal_connect (GTK_OBJECT (menued->right_button), "clicked", + GTK_SIGNAL_FUNC (on_right_button_clicked), + NULL); + + arrow4 = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_OUT); + gtk_widget_show (arrow4); + gtk_container_add (GTK_CONTAINER (menued->right_button), arrow4); + + vbox3 = gtk_vbox_new (FALSE, 6); + gtk_widget_show (vbox3); + gtk_box_pack_start (GTK_BOX (hbox1), vbox3, FALSE, TRUE, 0); + + table1 = gtk_table_new (5, 3, FALSE); + gtk_widget_show (table1); + gtk_box_pack_start (GTK_BOX (vbox3), table1, FALSE, TRUE, 0); + gtk_table_set_row_spacings (GTK_TABLE (table1), 2); + gtk_table_set_col_spacings (GTK_TABLE (table1), 4); + row = 0; + + menued->stock_items = gtk_stock_list_ids (); + menued->stock_items = g_slist_sort (menued->stock_items, + glade_util_compare_stock_labels); + + if (!menued->gnome_support) + { + eventbox1 = gtk_event_box_new (); + gtk_widget_show (eventbox1); + gtk_table_attach (GTK_TABLE (table1), eventbox1, 0, 1, row, row + 1, + GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + gtk_tooltips_set_tip (tooltips, eventbox1, _ ("The stock item to use."), + NULL); + + menued->stock_label = gtk_label_new (_ ("Stock Item:")); + gtk_widget_show (menued->stock_label); + gtk_container_add (GTK_CONTAINER (eventbox1), menued->stock_label); + gtk_misc_set_alignment (GTK_MISC (menued->stock_label), 0, 0.5); + + menued->stock_combo = gtk_combo_new (); + gb_widget_set_usize (menued->stock_combo, 100, -1); + gtk_table_attach (GTK_TABLE (table1), menued->stock_combo, 1, 3, + row, row + 1, GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + gtk_combo_set_value_in_list (GTK_COMBO (menued->stock_combo), + FALSE, FALSE); + gtk_editable_set_editable (GTK_EDITABLE (GTK_COMBO (menued->stock_combo)->entry), + FALSE); + gtk_widget_show (menued->stock_combo); + + listitem = gtk_list_item_new_with_label (_("None")); + gtk_widget_show (listitem); + gtk_container_add (GTK_CONTAINER (GTK_COMBO (menued->stock_combo)->list), + listitem); + + for (elem = menued->stock_items, idx = 1; elem; elem = elem->next, idx++) + { + GtkStockItem item; + GtkWidget *listitem, *hbox, *image, *label; + GtkIconSet *icon_set; + GtkIconSize *sizes; + gint n_sizes, i; + gboolean has_menu_size; + + stock_id = elem->data; + +#if 0 + g_print ("Stock ID: %s\n", stock_id); +#endif + + /* Only show GTK+ stock items. */ + if (strncmp (stock_id, "gtk-", 4) != 0) + continue; + + /* Check that the icon has a menu size. */ + has_menu_size = FALSE; + icon_set = gtk_icon_factory_lookup_default (stock_id); + if (icon_set) + { + gtk_icon_set_get_sizes (icon_set, &sizes, &n_sizes); + for (i = 0; i < n_sizes; i++) + { + if (sizes[i] == GTK_ICON_SIZE_MENU) + has_menu_size = TRUE; + } + g_free (sizes); + } + + if (!has_menu_size) + { +#if 0 + g_print ("Skipping: %s\n", stock_id); +#endif + continue; + } + + if (gtk_stock_lookup (stock_id, &item)) + { + listitem = gtk_list_item_new (); + gtk_object_set_data (GTK_OBJECT (listitem), + GladeMenuEditorIndexKey, + GINT_TO_POINTER (idx)); + gtk_widget_show (listitem); + hbox = gtk_hbox_new (FALSE, 3); + gtk_container_add (GTK_CONTAINER (listitem), hbox); + gtk_widget_show (hbox); + + image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_MENU); + + if (image) + { + gtk_widget_show (image); + gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0); + } + + label = gtk_type_new (GTK_TYPE_ACCEL_LABEL); + gtk_label_set_text_with_mnemonic (GTK_LABEL (label), item.label); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + gtk_combo_set_item_string (GTK_COMBO (menued->stock_combo), + GTK_ITEM (listitem), stock_id); + + gtk_container_add (GTK_CONTAINER (GTK_COMBO (menued->stock_combo)->list), listitem); + } + else + { +#if 0 + g_print ("Lookup failed for stock_id: %s (probably a stock image only\n", stock_id); +#endif + } + } + gtk_signal_connect (GTK_OBJECT (GTK_COMBO (menued->stock_combo)->entry), + "changed", + GTK_SIGNAL_FUNC (on_stock_item_entry_changed), + NULL); + row++; + } + +#ifdef USE_GNOME + if (menued->gnome_support) + { + /* Gnome stock item. */ + eventbox1 = gtk_event_box_new (); + gtk_widget_show (eventbox1); + gtk_table_attach (GTK_TABLE (table1), eventbox1, 0, 1, row, row + 1, + GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + gtk_tooltips_set_tip (tooltips, eventbox1, + _ ("The stock Gnome item to use."), NULL); + + menued->stock_label = gtk_label_new (_ ("Stock Item:")); + gtk_widget_show (menued->stock_label); + gtk_container_add (GTK_CONTAINER (eventbox1), menued->stock_label); + gtk_misc_set_alignment (GTK_MISC (menued->stock_label), 0, 0.5); + + menued->stock_combo = gtk_combo_new (); + gb_widget_set_usize (menued->stock_combo, 100, -1); + gtk_table_attach (GTK_TABLE (table1), menued->stock_combo, 1, 3, + row, row + 1, GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + gtk_combo_set_value_in_list (GTK_COMBO (menued->stock_combo), + FALSE, FALSE); + gtk_editable_set_editable (GTK_EDITABLE (GTK_COMBO (menued->stock_combo)->entry), + FALSE); + gtk_widget_show (menued->stock_combo); + + for (uiinfo = GladeStockMenuItemValues; + uiinfo->type != GNOME_APP_UI_ENDOFINFO; + uiinfo++) + { + pixmap = NULL; + label = NULL; + label_text = NULL; + + if (uiinfo->type == GNOME_APP_UI_ITEM_CONFIGURABLE) + gnome_app_ui_configure_configurable (uiinfo); + + uiinfo->widget = NULL; + + switch (uiinfo->type) { + case GNOME_APP_UI_SEPARATOR: + uiinfo->widget = gtk_list_item_new (); + gtk_widget_show (uiinfo->widget); + separator = gtk_hseparator_new(); + gtk_widget_show (separator); + gtk_container_add (GTK_CONTAINER (uiinfo->widget), separator); + gtk_widget_set_sensitive (uiinfo->widget, FALSE); + gtk_combo_set_item_string (GTK_COMBO (menued->stock_combo), + GTK_ITEM (uiinfo->widget), ""); + gtk_container_add (GTK_CONTAINER (GTK_COMBO (menued->stock_combo)->list), uiinfo->widget); + break; + case GNOME_APP_UI_ITEM: + case GNOME_APP_UI_SUBTREE_STOCK: + if (uiinfo->pixmap_type == GNOME_APP_PIXMAP_STOCK) + pixmap = gtk_image_new_from_stock (uiinfo->pixmap_info, GTK_ICON_SIZE_MENU); + + if (uiinfo->label && uiinfo->label[0]) + { + uiinfo->widget = gtk_list_item_new (); + gtk_widget_show (uiinfo->widget); + hbox = gtk_hbox_new (FALSE, 3); + gtk_container_add (GTK_CONTAINER (uiinfo->widget), hbox); + gtk_widget_show (hbox); + + if (!pixmap && uiinfo->type != GNOME_APP_UI_SUBTREE_STOCK) + { + /* Create a dummy widget to fill in the space. */ + pixmap = gtk_alignment_new (0, 0, 0, 0); + gtk_widget_set_size_request (pixmap, 16, -1); + } + + if (pixmap) + { + gtk_widget_show (pixmap); + gtk_box_pack_start (GTK_BOX (hbox), pixmap, + FALSE, FALSE, 0); + } + + label = gtk_accel_label_new (""); + /* Most of the label text is from Gnome, but 2 of them are + ours, so we use a utility function to find the translation. + */ + label_text = glade_gnome_gettext (uiinfo->label); + gtk_label_set_text_with_mnemonic (GTK_LABEL (label), + label_text); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + gtk_combo_set_item_string (GTK_COMBO (menued->stock_combo), + GTK_ITEM (uiinfo->widget), + label_text); + + gtk_container_add (GTK_CONTAINER (GTK_COMBO (menued->stock_combo)->list), uiinfo->widget); + } + break; + + default: + g_warning ("Invalid UIINFO item type"); + } + } + gtk_signal_connect (GTK_OBJECT (GTK_COMBO (menued->stock_combo)->entry), + "changed", + GTK_SIGNAL_FUNC (on_stock_item_entry_changed), + NULL); + + row++; + } +#endif /* USE_GNOME */ + + /* Item Label. */ + eventbox1 = gtk_event_box_new (); + gtk_widget_show (eventbox1); + gtk_table_attach (GTK_TABLE (table1), eventbox1, 0, 1, row, row + 1, + GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + gtk_tooltips_set_tip (tooltips, eventbox1, _ ("The text of the menu item, or empty for separators."), NULL); + + menued->label_label = gtk_label_new (_ ("Label:")); + gtk_widget_show (menued->label_label); + gtk_container_add (GTK_CONTAINER (eventbox1), menued->label_label); + gtk_misc_set_alignment (GTK_MISC (menued->label_label), 0, 0.5); + + menued->label_entry = gtk_entry_new (); + gtk_widget_show (menued->label_entry); + gtk_table_attach (GTK_TABLE (table1), menued->label_entry, 1, 3, + row, row + 1, + GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + gtk_signal_connect (GTK_OBJECT (menued->label_entry), "changed", + GTK_SIGNAL_FUNC (on_entry_changed), NULL); + gtk_signal_connect (GTK_OBJECT (menued->label_entry), "key_press_event", + GTK_SIGNAL_FUNC (on_label_entry_key_press), + NULL); + row++; + + /* Item Name. */ + eventbox2 = gtk_event_box_new (); + gtk_widget_show (eventbox2); + gtk_table_attach (GTK_TABLE (table1), eventbox2, 0, 1, row, row + 1, + GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + gtk_tooltips_set_tip (tooltips, eventbox2, _ ("The name of the widget"), + NULL); + + menued->name_label = gtk_label_new (_ ("Name:")); + gtk_widget_show (menued->name_label); + gtk_container_add (GTK_CONTAINER (eventbox2), menued->name_label); + gtk_misc_set_alignment (GTK_MISC (menued->name_label), 0, 0.5); + + menued->name_entry = gtk_entry_new (); + gtk_widget_show (menued->name_entry); + gtk_table_attach (GTK_TABLE (table1), menued->name_entry, 1, 3, row, row + 1, + GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + gtk_signal_connect (GTK_OBJECT (menued->name_entry), "changed", + GTK_SIGNAL_FUNC (on_entry_changed), NULL); + row++; + + /* Item Handler. */ + eventbox3 = gtk_event_box_new (); + gtk_widget_show (eventbox3); + gtk_table_attach (GTK_TABLE (table1), eventbox3, 0, 1, row, row + 1, + GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + gtk_tooltips_set_tip (tooltips, eventbox3, _ ("The function to be called when the item is selected"), NULL); + + menued->handler_label = gtk_label_new (_ ("Handler:")); + gtk_widget_show (menued->handler_label); + gtk_container_add (GTK_CONTAINER (eventbox3), menued->handler_label); + gtk_misc_set_alignment (GTK_MISC (menued->handler_label), 0, 0.5); + + menued->handler_entry = gtk_entry_new (); + gtk_widget_show (menued->handler_entry); + gtk_table_attach (GTK_TABLE (table1), menued->handler_entry, 1, 3, + row, row + 1, + GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + gtk_signal_connect (GTK_OBJECT (menued->handler_entry), "changed", + GTK_SIGNAL_FUNC (on_entry_changed), NULL); + row++; + + /* Item Icon. */ + eventbox1 = gtk_event_box_new (); + gtk_widget_show (eventbox1); + gtk_table_attach (GTK_TABLE (table1), eventbox1, 0, 1, row, row + 1, + GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + gtk_tooltips_set_tip (tooltips, eventbox1, _ ("An optional icon to show on the left of the menu item."), NULL); + + menued->icon_label = gtk_label_new (_ ("Icon:")); + gtk_widget_show (menued->icon_label); + gtk_container_add (GTK_CONTAINER (eventbox1), menued->icon_label); + gtk_misc_set_alignment (GTK_MISC (menued->icon_label), 0, 0.5); + + icon_hbox = gtk_hbox_new (FALSE, 2); + gtk_table_attach (GTK_TABLE (table1), icon_hbox, 1, 3, row, row + 1, + GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show (icon_hbox); + + menued->icon_widget = gtk_combo_new (); + gtk_editable_set_editable (GTK_EDITABLE (GTK_COMBO (menued->icon_widget)->entry), + FALSE); + gb_widget_set_usize (menued->icon_widget, 100, -1); + + /* Add a "None" item first, so it is easy to reset the pixmap. */ + listitem = gtk_list_item_new_with_label (_("None")); + gtk_widget_show (listitem); + gtk_container_add (GTK_CONTAINER (GTK_COMBO (menued->icon_widget)->list), + listitem); + + for (elem = menued->stock_items; elem; elem = elem->next) + { + GtkStockItem item; + GtkWidget *listitem, *hbox, *image, *label; + GtkIconSet *icon_set; + GtkIconSize *sizes; + gint n_sizes, i; + gboolean has_menu_size; + + stock_id = elem->data; + +#if 0 + g_print ("Stock ID: %s\n", stock_id); +#endif + + /* Show only GTK+ stock items in GTK+ projects. */ + if (!menued->gnome_support && (strncmp (stock_id, "gtk-", 4) != 0)) + continue; +#if 0 + /* Only show GnomeDB stock items if GnomeDB support is on. */ + if (!menued->gnome_db_support && !strncmp (stock_id, "gnome-db-", 9)) + continue; +#endif + + /* Check that the icon has a menu size. */ + has_menu_size = FALSE; + icon_set = gtk_icon_factory_lookup_default (stock_id); + if (icon_set) + { + gtk_icon_set_get_sizes (icon_set, &sizes, &n_sizes); + for (i = 0; i < n_sizes; i++) + { + if (sizes[i] == GTK_ICON_SIZE_MENU) + has_menu_size = TRUE; + } + g_free (sizes); + } + + if (!has_menu_size) + { +#if 0 + g_print ("Skipping: %s\n", stock_id); +#endif + continue; + } + + image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_MENU); + if (image) + { + listitem = gtk_list_item_new (); + /* We store a pointer to the stock_id from the stock_items list, + so we don't need to free it, but if stock_items is destroyed it + will no longer be valid. */ + gtk_object_set_data (GTK_OBJECT (listitem), + GladeMenuEditorStockIDKey, stock_id); + gtk_widget_show (listitem); + hbox = gtk_hbox_new (FALSE, 3); + gtk_container_add (GTK_CONTAINER (listitem), hbox); + gtk_widget_show (hbox); + gtk_widget_show (image); + gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0); + + if (gtk_stock_lookup (stock_id, &item)) + label_text = item.label; + else + /* FIXME: We have no name to use for the image. */ + label_text = stock_id; + + label = gtk_type_new (GTK_TYPE_ACCEL_LABEL); + gtk_label_set_text_with_mnemonic (GTK_LABEL (label), label_text); + + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_combo_set_item_string (GTK_COMBO (menued->icon_widget), + GTK_ITEM (listitem), stock_id); + gtk_container_add (GTK_CONTAINER (GTK_COMBO (menued->icon_widget)->list), + listitem); + } + } + + gtk_signal_connect (GTK_OBJECT (GTK_COMBO (menued->icon_widget)->entry), + "changed", GTK_SIGNAL_FUNC (on_entry_changed), NULL); + + gtk_widget_show (menued->icon_widget); + gtk_box_pack_start (GTK_BOX (icon_hbox), menued->icon_widget, + TRUE, TRUE, 0); + + menued->icon_button = gtk_button_new_with_label ("..."); + gtk_widget_show (menued->icon_button); + gtk_box_pack_start (GTK_BOX (icon_hbox), menued->icon_button, + FALSE, FALSE, 0); + gtk_signal_connect (GTK_OBJECT (menued->icon_button), "clicked", + GTK_SIGNAL_FUNC (on_icon_button_clicked), NULL); + row++; + + /* Tooltip. */ + eventbox3 = gtk_event_box_new (); + gtk_widget_show (eventbox3); + gtk_table_attach (GTK_TABLE (table1), eventbox3, 0, 1, row, row + 1, + GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + gtk_tooltips_set_tip (tooltips, eventbox3, _ ("The tip to show when the mouse is over the item"), NULL); + + menued->tooltip_label = gtk_label_new (_ ("Tooltip:")); + gtk_widget_show (menued->tooltip_label); + gtk_container_add (GTK_CONTAINER (eventbox3), menued->tooltip_label); + gtk_misc_set_alignment (GTK_MISC (menued->tooltip_label), 0, 0.5); + + menued->tooltip_entry = gtk_entry_new (); + gtk_widget_show (menued->tooltip_entry); + gtk_table_attach (GTK_TABLE (table1), menued->tooltip_entry, 1, 3, + row, row + 1, + GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + gtk_signal_connect (GTK_OBJECT (menued->tooltip_entry), "changed", + GTK_SIGNAL_FUNC (on_entry_changed), NULL); + row++; + + /* Buttons to add/delete items. */ + button_table = gtk_table_new (2, 2, TRUE); + gtk_table_set_row_spacings (GTK_TABLE (button_table), 1); + gtk_table_set_col_spacings (GTK_TABLE (button_table), 2); + gtk_widget_show (button_table); + gtk_box_pack_start (GTK_BOX (vbox3), button_table, FALSE, TRUE, 0); + + menued->add_button = gtk_button_new_with_mnemonic (_("_Add")); + gtk_widget_show (menued->add_button); + gtk_table_attach (GTK_TABLE (button_table), menued->add_button, + 0, 1, 0, 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0); + gtk_tooltips_set_tip (tooltips, menued->add_button, + _ ("Add a new item below the selected item."), NULL); + gtk_signal_connect (GTK_OBJECT (menued->add_button), "clicked", + GTK_SIGNAL_FUNC (on_add_button_clicked), + NULL); + + menued->add_child_button = gtk_button_new_with_mnemonic (_("Add _Child")); + gtk_widget_show (menued->add_child_button); + gtk_table_attach (GTK_TABLE (button_table), menued->add_child_button, + 1, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0); + gtk_tooltips_set_tip (tooltips, menued->add_child_button, + _ ("Add a new child item below the selected item."), + NULL); + gtk_signal_connect (GTK_OBJECT (menued->add_child_button), "clicked", + GTK_SIGNAL_FUNC (on_add_child_button_clicked), + NULL); + + menued->add_separator_button = gtk_button_new_with_mnemonic (_("Add _Separator")); + gtk_widget_show (menued->add_separator_button); + gtk_table_attach (GTK_TABLE (button_table), menued->add_separator_button, + 0, 1, 1, 2, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0); + gtk_tooltips_set_tip (tooltips, menued->add_separator_button, + _ ("Add a separator below the selected item."), NULL); + gtk_signal_connect (GTK_OBJECT (menued->add_separator_button), "clicked", + GTK_SIGNAL_FUNC (on_add_separator_button_clicked), + NULL); + + menued->delete_button = gtk_button_new_with_mnemonic (_("_Delete")); + gtk_widget_show (menued->delete_button); + gtk_table_attach (GTK_TABLE (button_table), menued->delete_button, + 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0); + gtk_tooltips_set_tip (tooltips, menued->delete_button, + _ ("Delete the current item"), NULL); + gtk_signal_connect (GTK_OBJECT (menued->delete_button), "clicked", + GTK_SIGNAL_FUNC (on_delete_button_clicked), + NULL); + + /* Type radio options and toggle options. */ + menued->type_frame = gtk_frame_new (_ ("Item Type:")); + gtk_widget_show (menued->type_frame); + gtk_box_pack_start (GTK_BOX (vbox3), menued->type_frame, FALSE, TRUE, 0); + + table2 = gtk_table_new (3, 3, FALSE); + gtk_table_set_col_spacings (GTK_TABLE (table2), 4); + gtk_table_set_row_spacings (GTK_TABLE (table2), 1); + gtk_widget_show (table2); + gtk_container_add (GTK_CONTAINER (menued->type_frame), table2); + gtk_container_set_border_width (GTK_CONTAINER (table2), 4); + + eventbox1 = gtk_event_box_new (); + gtk_widget_show (eventbox1); + gtk_table_attach (GTK_TABLE (table2), eventbox1, 1, 2, 1, 2, + GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + gtk_tooltips_set_tip (tooltips, eventbox1, + _ ("If the item is initially on."), NULL); + + menued->state_label = gtk_label_new (_("Active:")); + gtk_misc_set_alignment (GTK_MISC (menued->state_label), 0, 0.5); + gtk_widget_show (menued->state_label); + gtk_container_add (GTK_CONTAINER (eventbox1), menued->state_label); + + menued->state_togglebutton = gtk_toggle_button_new_with_label (_("No")); + gtk_widget_show (menued->state_togglebutton); + gtk_table_attach (GTK_TABLE (table2), menued->state_togglebutton, 2, 3, 1, 2, + GTK_EXPAND | GTK_FILL, 0, 0, 0); + + hbox2 = gtk_hbox_new (FALSE, 4); + gtk_widget_show (hbox2); + gtk_table_attach (GTK_TABLE (table2), hbox2, 1, 3, 2, 3, + GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + + eventbox1 = gtk_event_box_new (); + gtk_widget_show (eventbox1); + gtk_box_pack_start (GTK_BOX (hbox2), eventbox1, FALSE, TRUE, 0); + gtk_tooltips_set_tip (tooltips, eventbox1, + _ ("The radio menu item's group"), NULL); + + menued->group_label = gtk_label_new (_ ("Group:")); + gtk_misc_set_alignment (GTK_MISC (menued->group_label), 0, 0.5); + gtk_widget_show (menued->group_label); + gtk_container_add (GTK_CONTAINER (eventbox1), menued->group_label); + + menued->group_combo = gtk_combo_new (); + gtk_editable_set_editable (GTK_EDITABLE (GTK_COMBO (menued->group_combo)->entry), + FALSE); + gtk_widget_set_usize (GTK_COMBO (menued->group_combo)->entry, 60, -1); + gtk_widget_show (menued->group_combo); + gtk_box_pack_start (GTK_BOX (hbox2), menued->group_combo, TRUE, TRUE, 0); + gtk_signal_connect (GTK_OBJECT (GTK_COMBO (menued->group_combo)->entry), + "changed", GTK_SIGNAL_FUNC (on_entry_changed), NULL); + + menued->radio_radiobutton = gtk_radio_button_new_with_label (table2_group, + _ ("Radio")); + table2_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (menued->radio_radiobutton)); + gtk_widget_show (menued->radio_radiobutton); + gtk_table_attach (GTK_TABLE (table2), menued->radio_radiobutton, 0, 1, 2, 3, + GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + + menued->check_radiobutton = gtk_radio_button_new_with_label (table2_group, + _ ("Check")); + table2_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (menued->check_radiobutton)); + gtk_widget_show (menued->check_radiobutton); + gtk_table_attach (GTK_TABLE (table2), menued->check_radiobutton, 0, 1, 1, 2, + GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + + menued->normal_radiobutton = gtk_radio_button_new_with_label (table2_group, + _ ("Normal")); + table2_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (menued->normal_radiobutton)); + gtk_widget_show (menued->normal_radiobutton); + gtk_table_attach (GTK_TABLE (table2), menued->normal_radiobutton, 0, 1, 0, 1, + GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (menued->normal_radiobutton), TRUE); + + + /* Accelerator key options. */ + menued->accel_frame = gtk_frame_new (_ ("Accelerator:")); + gtk_widget_show (menued->accel_frame); + gtk_box_pack_start (GTK_BOX (vbox3), menued->accel_frame, FALSE, TRUE, 0); + + table3 = gtk_table_new (2, 2, FALSE); + gtk_widget_show (table3); + gtk_container_add (GTK_CONTAINER (menued->accel_frame), table3); + gtk_container_set_border_width (GTK_CONTAINER (table3), 4); + gtk_table_set_row_spacings (GTK_TABLE (table3), 2); + gtk_table_set_col_spacings (GTK_TABLE (table3), 4); + + hbox2 = gtk_hbox_new (FALSE, 2); + gtk_widget_show (hbox2); + gtk_table_attach (GTK_TABLE (table3), hbox2, 1, 2, 1, 2, + GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + + menued->accel_key_entry = gtk_entry_new (); + gtk_widget_set_usize (menued->accel_key_entry, 100, -1); + gtk_widget_show (menued->accel_key_entry); + gtk_box_pack_start (GTK_BOX (hbox2), menued->accel_key_entry, + TRUE, TRUE, 0); + gtk_signal_connect (GTK_OBJECT (menued->accel_key_entry), "changed", + GTK_SIGNAL_FUNC (on_entry_changed), NULL); + + accel_key_button = gtk_button_new_with_label ("..."); + gtk_widget_show (accel_key_button); + gtk_box_pack_start (GTK_BOX (hbox2), accel_key_button, + FALSE, TRUE, 0); + gtk_signal_connect (GTK_OBJECT (accel_key_button), "clicked", + GTK_SIGNAL_FUNC (on_accel_key_button_clicked), + NULL); + + hbox2 = gtk_hbox_new (TRUE, 0); + gtk_widget_show (hbox2); + gtk_table_attach (GTK_TABLE (table3), hbox2, 1, 2, 0, 1, + GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + + menued->accel_ctrl_checkbutton = gtk_check_button_new_with_label (_ ("Ctrl")); + gtk_widget_show (menued->accel_ctrl_checkbutton); + gtk_box_pack_start (GTK_BOX (hbox2), menued->accel_ctrl_checkbutton, + TRUE, TRUE, 0); + + menued->accel_shift_checkbutton = gtk_check_button_new_with_label (_ ("Shift")); + gtk_widget_show (menued->accel_shift_checkbutton); + gtk_box_pack_start (GTK_BOX (hbox2), menued->accel_shift_checkbutton, + TRUE, TRUE, 0); + + menued->accel_alt_checkbutton = gtk_check_button_new_with_label (_ ("Alt")); + gtk_widget_show (menued->accel_alt_checkbutton); + gtk_box_pack_start (GTK_BOX (hbox2), menued->accel_alt_checkbutton, + TRUE, TRUE, 0); + + label9 = gtk_label_new (_ ("Key:")); + gtk_widget_show (label9); + gtk_table_attach (GTK_TABLE (table3), label9, 0, 1, 1, 2, + GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + gtk_misc_set_alignment (GTK_MISC (label9), 0, 0.5); + + label8 = gtk_label_new (_ ("Modifiers:")); + gtk_widget_show (label8); + gtk_table_attach (GTK_TABLE (table3), label8, 0, 1, 0, 1, + GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + gtk_misc_set_alignment (GTK_MISC (label8), 0, 0.5); + + + hseparator1 = gtk_hseparator_new (); + gtk_widget_show (hseparator1); + gtk_box_pack_start (GTK_BOX (vbox2), hseparator1, FALSE, TRUE, 8); + + + /* OK, Apply & Cancel buttons. */ + hbuttonbox1 = gtk_hbutton_box_new (); + gtk_widget_show (hbuttonbox1); + gtk_box_pack_start (GTK_BOX (vbox2), hbuttonbox1, FALSE, TRUE, 0); + gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox1), GTK_BUTTONBOX_END); + gtk_box_set_spacing (GTK_BOX (hbuttonbox1), 8); + + menued->cancel_button = gtk_button_new_from_stock (GTK_STOCK_CANCEL); + gtk_widget_show (menued->cancel_button); + gtk_container_add (GTK_CONTAINER (hbuttonbox1), menued->cancel_button); + GTK_WIDGET_SET_FLAGS (menued->cancel_button, GTK_CAN_DEFAULT); + + menued->apply_button = gtk_button_new_from_stock (GTK_STOCK_APPLY); + gtk_widget_show (menued->apply_button); + gtk_container_add (GTK_CONTAINER (hbuttonbox1), menued->apply_button); + GTK_WIDGET_SET_FLAGS (menued->apply_button, GTK_CAN_DEFAULT); + + menued->ok_button = gtk_button_new_from_stock (GTK_STOCK_OK); + gtk_widget_show (menued->ok_button); + gtk_container_add (GTK_CONTAINER (hbuttonbox1), menued->ok_button); + GTK_WIDGET_SET_FLAGS (menued->ok_button, GTK_CAN_DEFAULT); + gtk_widget_grab_default (menued->ok_button); + + + /* Now set up all the signal handlers. */ + gtk_signal_connect_after (GTK_OBJECT (menued->normal_radiobutton), "toggled", + GTK_SIGNAL_FUNC (on_radiobutton_toggled), NULL); + gtk_signal_connect_after (GTK_OBJECT (menued->check_radiobutton), "toggled", + GTK_SIGNAL_FUNC (on_radiobutton_toggled), NULL); + gtk_signal_connect_after (GTK_OBJECT (menued->radio_radiobutton), "toggled", + GTK_SIGNAL_FUNC (on_radiobutton_toggled), NULL); + gtk_signal_connect (GTK_OBJECT (menued->state_togglebutton), "toggled", + GTK_SIGNAL_FUNC (on_state_button_toggled), NULL); + gtk_signal_connect (GTK_OBJECT (menued->accel_ctrl_checkbutton), "toggled", + GTK_SIGNAL_FUNC (on_checkbutton_toggled), NULL); + gtk_signal_connect (GTK_OBJECT (menued->accel_shift_checkbutton), "toggled", + GTK_SIGNAL_FUNC (on_checkbutton_toggled), NULL); + gtk_signal_connect (GTK_OBJECT (menued->accel_alt_checkbutton), "toggled", + GTK_SIGNAL_FUNC (on_checkbutton_toggled), NULL); + + gtk_signal_connect (GTK_OBJECT (menued->ok_button), "clicked", + GTK_SIGNAL_FUNC (on_menu_editor_ok), menued); + gtk_signal_connect (GTK_OBJECT (menued->apply_button), "clicked", + GTK_SIGNAL_FUNC (on_menu_editor_apply), menued); + gtk_signal_connect (GTK_OBJECT (menued->cancel_button), "clicked", + GTK_SIGNAL_FUNC (on_menu_editor_close), menued); + + set_interface_state (menued); +} + + +GtkWidget* +glade_menu_editor_new (GladeProject *project, + GtkMenuShell *menu) +{ + GladeMenuEditor *menued; + + menued = gtk_type_new (glade_menu_editor_get_type ()); + glade_menu_editor_construct (menued, project); + glade_menu_editor_set_menu (menued, menu); + + return GTK_WIDGET (menued); +} + +static void +glade_menu_editor_destroy (GtkObject *object) +{ + GladeMenuEditor *menued; + GSList *elem; + + g_return_if_fail (object != NULL); + g_return_if_fail (GLADE_IS_MENU_EDITOR (object)); + + menued = GLADE_MENU_EDITOR (object); + + /* Free all the GbMenuItemData elements & disconnect our destroy handler + on the menu widget. */ + glade_menu_editor_reset (menued); + + if (menued->keys_dialog) + { + gtk_widget_destroy (menued->keys_dialog); + menued->keys_dialog = NULL; + } + + if (menued->filesel) + { + gtk_widget_destroy (menued->filesel); + menued->filesel = NULL; + } + + for (elem = menued->stock_items; elem; elem = elem->next) + g_free (elem->data); + g_slist_free (menued->stock_items); + menued->stock_items = NULL; +} + + +/************************************************************************** + * Signal Handlers + **************************************************************************/ + +static void +on_clist_select_row (GtkWidget * clist, + gint row, + gint column, + GdkEventButton * event, + gpointer user_data) +{ + GladeMenuEditor *menued; + + menued = GLADE_MENU_EDITOR (gtk_widget_get_toplevel (clist)); + + show_item_properties (menued); + + if (event && !GTK_WIDGET_HAS_FOCUS (clist)) + gtk_widget_grab_focus (clist); + + set_interface_state (menued); +} + +static void +on_clist_unselect_row (GtkWidget * clist, + gint row, + gint column, + GdkEventButton * event, + gpointer user_data) +{ + GladeMenuEditor *menued; + + menued = GLADE_MENU_EDITOR (gtk_widget_get_toplevel (clist)); + + clear_form (menued, FALSE); + + if (event && !GTK_WIDGET_HAS_FOCUS (clist)) + gtk_widget_grab_focus (clist); + + set_interface_state (menued); +} + +/* This will only call update_current_item if the text is different to the + corresponding item field, since we don't want to propogate updates when + we are setting the entry. */ +static void +on_entry_changed (GtkWidget * entry, + gpointer user_data) +{ + GladeMenuEditor *menued; + GtkCList *clist; + GbMenuItemData *item; + gchar *text, *item_text; + gboolean changed = FALSE; + gint row; + +#if 0 + g_print ("In on_entry_changed\n"); +#endif + + menued = GLADE_MENU_EDITOR (gtk_widget_get_toplevel (entry)); + + /* If we are setting the widget values, just return. */ +#if 1 + if (menued->updating_widgets) + return; +#endif + + clist = GTK_CLIST (menued->clist); + row = get_selected_row (menued); +#if 0 + g_print ("Selected row: %i\n", row); +#endif + if (row == -1) + return; + item = (GbMenuItemData *) gtk_clist_get_row_data (GTK_CLIST (menued->clist), + row); +#if 0 + g_print ("Item %p Label:%s Name:%s Handler:%s\n", item, item->label, + item->name, item->handler); +#endif + + text = (gchar*) gtk_entry_get_text (GTK_ENTRY (entry)); + + if (entry == menued->label_entry) + { + item_text = item->label; + } + else if (entry == menued->name_entry) + { + item_text = item->name; + } + else if (entry == menued->handler_entry) + { + item_text = item->handler; + } + else if (entry == GTK_COMBO (menued->icon_widget)->entry) + { + /* If the user selects the 'None' item from the combo, we reset the + text to "" and return. This callback will be called again. */ + if (!strcmp (text, _("None"))) + { + set_entry_text (GTK_ENTRY (entry), ""); + return; + } + + item_text = item->icon; + } + else if (entry == menued->tooltip_entry) + { + item_text = item->tooltip; + } + else if (entry == GTK_COMBO (menued->group_combo)->entry) + { + item_text = item->group ? item->group->name : item->name; + } + else if (entry == menued->accel_key_entry) + { + item_text = item->key; + } + else + return; + + if (item_text == NULL) + { + if (strlen (text) > 0) + changed = TRUE; + } + else + { + if (strcmp (text, item_text)) + changed = TRUE; + } + + if (changed) + { + if (entry == menued->label_entry) + { + if (item->generate_name) + { + glade_project_release_widget_name (menued->project, item->name); + g_free (item->name); + item->name = generate_name (menued, text); + set_entry_text (GTK_ENTRY (menued->name_entry), + item->name ? item->name : ""); + gtk_clist_set_text (clist, row, GLD_COL_NAME, + item->name ? item->name : ""); + if (item->generate_handler) + { + g_free (item->handler); + + item->handler = generate_handler (menued, row, text, + item->name); + set_entry_text (GTK_ENTRY (menued->handler_entry), + item->handler ? item->handler : ""); + gtk_clist_set_text (clist, row, GLD_COL_HANDLER, + item->handler ? item->handler : ""); + } + } + } + else if (entry == menued->name_entry) + { + item->generate_name = FALSE; + if (item->generate_handler) + { + g_free (item->handler); + item->handler = generate_handler (menued, row, item->label, + text); + set_entry_text (GTK_ENTRY (menued->handler_entry), + item->handler ? item->handler : ""); + gtk_clist_set_text (clist, row, GLD_COL_HANDLER, + item->handler ? item->handler : ""); + } + } + else if (entry == menued->handler_entry) + { + item->generate_handler = FALSE; + } + + update_current_item (menued); + set_interface_state (menued); + } +} + + +/* This will only call update_current_item if the text is different to the + corresponding item field, since we don't want to propogate updates when + we are setting the entry. */ +static void +on_stock_item_entry_changed (GtkWidget * entry, + gpointer user_data) +{ + GladeMenuEditor *menued; + GtkCList *clist; + GbMenuItemData *item; + gint row; + GtkListItem *listitem; + gint stock_item_index = 0; + const gchar *text; + + menued = GLADE_MENU_EDITOR (gtk_widget_get_toplevel (entry)); + + text = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (menued->stock_combo)->entry)); + /* FIXME GTK+ 1.3.x bug workaround. It emits "changed" twice, once when the + existing text is deleted. So we just return if the text is empty. */ + if (text[0] == '\0') + return; + + clist = GTK_CLIST (menued->clist); + row = get_selected_row (menued); + if (row == -1) + return; + item = (GbMenuItemData *) gtk_clist_get_row_data (GTK_CLIST (menued->clist), + row); + + /* Find the index of the selected item. */ + listitem = glade_util_gtk_combo_find (GTK_COMBO (menued->stock_combo)); + g_return_if_fail (listitem != NULL); + + if (menued->gnome_support) + { +#ifdef USE_GNOME + stock_item_index = g_list_index (GTK_LIST (GTK_COMBO (menued->stock_combo)->list)->children, listitem); +#endif + } + else + { + GtkStockItem item; + + stock_item_index = GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (listitem), GladeMenuEditorIndexKey)); + if (gtk_stock_lookup (text, &item)) + text = item.label; + else + stock_item_index = 0; + } + + if (item->stock_item_index != stock_item_index) + { + item->stock_item_index = stock_item_index; + + /* If the stock item is reset to 'None', get a new item name, and reset + generate_name/handler to TRUE. */ + if (stock_item_index == 0) + { + item->generate_name = TRUE; + item->generate_handler = TRUE; + + item->label = glade_project_new_widget_name (menued->project, + "item"); + item->name = g_strdup (item->label); + item->handler = generate_handler (menued, row, item->label, + item->name); + show_item_properties (menued); + + gtk_clist_set_text (clist, row, GLD_COL_LABEL, + item->label ? item->label : GB_SEPARATOR_TEXT); + gtk_clist_set_text (clist, row, GLD_COL_NAME, + item->name ? item->name : ""); + gtk_clist_set_text (clist, row, GLD_COL_HANDLER, + item->handler ? item->handler : ""); + } + else + { + /* These will trigger callbacks, and will generate the name + and handler if appropriate. */ + set_entry_text (GTK_ENTRY (menued->label_entry), text); + + /* Stock menu items are all normal items. */ + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (menued->normal_radiobutton), TRUE); + set_entry_text (GTK_ENTRY (GTK_COMBO (menued->group_combo)->entry), ""); + + /* Reset the accelerator keys, as that is handled automatically. */ + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (menued->accel_ctrl_checkbutton), FALSE); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (menued->accel_shift_checkbutton), FALSE); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (menued->accel_alt_checkbutton), FALSE); + set_entry_text (GTK_ENTRY (menued->accel_key_entry), ""); + } + } + + set_interface_state (menued); +} + + +static gboolean +on_label_entry_key_press (GtkWidget * widget, + GdkEventKey * event, + gpointer user_data) +{ + GladeMenuEditor *menued; + + menued = GLADE_MENU_EDITOR (gtk_widget_get_toplevel (widget)); + + /* If the Return key is pressed, we add a new item beneath the selected item. + This makes it very easy to add several menus. If the Control key is + pressed, we add the item as a child, otherwise we add it as a sibling. */ + if (event->keyval == GDK_Return) + { + if (event->state & GDK_CONTROL_MASK) + { + add_item (menued, TRUE, FALSE); + /* Since we are added a child, we may need to set the parent's + handler to NULL if it has been auto-generated. */ + check_generated_handlers (menued); + } + else + { + add_item (menued, FALSE, FALSE); + } + return TRUE; + } + return FALSE; +} + + +static void +on_radiobutton_toggled (GtkWidget * togglebutton, + gpointer user_data) +{ + GladeMenuEditor *menued; + GbMenuItemData *item; + GbMenuItemType type = 0; + gboolean changed = FALSE; + + menued = GLADE_MENU_EDITOR (gtk_widget_get_toplevel (GTK_WIDGET (togglebutton))); + item = get_selected_item (menued); + if (item == NULL) + return; + + if (togglebutton == menued->normal_radiobutton) + type = GB_MENU_ITEM_NORMAL; + else if (togglebutton == menued->check_radiobutton) + type = GB_MENU_ITEM_CHECK; + else if (togglebutton == menued->radio_radiobutton) + type = GB_MENU_ITEM_RADIO; + + if (GTK_TOGGLE_BUTTON (togglebutton)->active) + { + if (type != item->type) + changed = TRUE; + } + else + { + if (type == item->type) + changed = TRUE; + } + + if (changed) + { + update_current_item (menued); + set_interface_state (menued); + } +} + +static void +on_checkbutton_toggled (GtkWidget * togglebutton, + gpointer user_data) +{ + GladeMenuEditor *menued; + GbMenuItemData *item; + guint active; + guint8 currently_active = 0; + + menued = GLADE_MENU_EDITOR (gtk_widget_get_toplevel (GTK_WIDGET (togglebutton))); + active = GTK_TOGGLE_BUTTON (togglebutton)->active; + item = get_selected_item (menued); + if (item == NULL) + return; + if (togglebutton == menued->accel_ctrl_checkbutton) + currently_active = item->modifiers & GDK_CONTROL_MASK; + if (togglebutton == menued->accel_shift_checkbutton) + currently_active = item->modifiers & GDK_SHIFT_MASK; + if (togglebutton == menued->accel_alt_checkbutton) + currently_active = item->modifiers & GDK_MOD1_MASK; + + if ((active && !currently_active) || (!active && currently_active)) + { + update_current_item (menued); + set_interface_state (menued); + } +} + +static void +on_state_button_toggled (GtkToggleButton * togglebutton, + gpointer user_data) +{ + GladeMenuEditor *menued; + GbMenuItemData *item; + GtkWidget *label; + guint active; + + menued = GLADE_MENU_EDITOR (gtk_widget_get_toplevel (GTK_WIDGET (togglebutton))); + active = GTK_TOGGLE_BUTTON (togglebutton)->active; + label = GTK_BIN (togglebutton)->child; + gtk_label_set_text (GTK_LABEL (label), active ? _("Yes") : _("No")); + + item = get_selected_item (menued); + if (item == NULL) + return; + if ((item->active && !active) || (!item->active && active)) + update_current_item (menued); +} + + +/************************************************************************** + * Accelerator Keys Dialog. + **************************************************************************/ +static void +on_accel_key_button_clicked (GtkButton * button, + gpointer user_data) +{ + GladeMenuEditor *menued; + + menued = GLADE_MENU_EDITOR (gtk_widget_get_toplevel (GTK_WIDGET (button))); + + if (menued->keys_dialog == NULL) + { + menued->keys_dialog = glade_keys_dialog_new (); + gtk_window_set_position (GTK_WINDOW (menued->keys_dialog), + GTK_WIN_POS_MOUSE); + gtk_window_set_transient_for (GTK_WINDOW (menued->keys_dialog), + GTK_WINDOW (menued)); + gtk_signal_connect (GTK_OBJECT (GLADE_KEYS_DIALOG (menued->keys_dialog)->clist), + "select_row", + GTK_SIGNAL_FUNC (on_keys_dialog_clist_select), + menued); + gtk_signal_connect (GTK_OBJECT (GLADE_KEYS_DIALOG (menued->keys_dialog)), + "response", + GTK_SIGNAL_FUNC (on_keys_dialog_response), + menued); + } + + gtk_widget_show (GTK_WIDGET (menued->keys_dialog)); +} + +static void +on_keys_dialog_clist_select (GtkWidget * widget, gint row, gint column, + GdkEventButton * bevent, GladeMenuEditor * menued) +{ + if (bevent && bevent->type == GDK_2BUTTON_PRESS) + on_keys_dialog_response (widget, GTK_RESPONSE_OK, menued); +} + +static void +on_keys_dialog_response (GtkWidget * widget, gint response_id, + GladeMenuEditor * menued) +{ + gchar *key_symbol; + + if (response_id == GTK_RESPONSE_OK) + { + key_symbol = glade_keys_dialog_get_key_symbol (GLADE_KEYS_DIALOG (menued->keys_dialog)); + if (key_symbol) + { + set_entry_text (GTK_ENTRY (menued->accel_key_entry), key_symbol); + } + } + + glade_util_close_window (menued->keys_dialog); +} + + +/************************************************************************** + * Arrow Button callbacks. + **************************************************************************/ + +static void +on_up_button_clicked (GtkButton * button, + gpointer user_data) +{ + GladeMenuEditor *menued; + GtkWidget *clist; + GbMenuItemData *item, *prev_item; + gint row, new_row, i, level; + GList *items; + + menued = GLADE_MENU_EDITOR (gtk_widget_get_toplevel (GTK_WIDGET (button))); + clist = menued->clist; + row = get_selected_row (menued); + if (row == -1 || row == 0) + return; + + item = (GbMenuItemData *) gtk_clist_get_row_data (GTK_CLIST (clist), row); + level = item->level; + + /* Find the new position of the item and its children. */ + new_row = -1; + for (i = row - 1; i >= 0; i--) + { + prev_item = (GbMenuItemData *) gtk_clist_get_row_data (GTK_CLIST (clist), + i); + if (prev_item->level == level) + { + new_row = i; + break; + } + else if (prev_item->level < level) + break; + } + + /* Return if we can't move the item up. */ + if (new_row == -1) + return; + + /* Remove item and children. */ + items = remove_item_and_children (clist, row); + + /* Now insert at new position. */ + insert_items (clist, items, new_row); + ensure_visible (clist, new_row); + + g_list_free (items); + + /* Make sure all items in the group point to the first one. */ + normalize_radio_groups (menued); + + gtk_clist_select_row (GTK_CLIST (clist), new_row, 0); + set_interface_state (menued); +} + +static void +on_down_button_clicked (GtkButton * button, + gpointer user_data) +{ + GladeMenuEditor *menued; + GtkWidget *clist; + GbMenuItemData *item, *next_item; + gint row, new_row, i, level; + gboolean found_next_item; + GList *items; + + menued = GLADE_MENU_EDITOR (gtk_widget_get_toplevel (GTK_WIDGET (button))); + clist = menued->clist; + row = get_selected_row (menued); + if (row == -1 || row == GTK_CLIST (clist)->rows - 1) + return; + + item = (GbMenuItemData *) gtk_clist_get_row_data (GTK_CLIST (clist), row); + level = item->level; + + /* Find the new position of the item and its children. */ + new_row = -1; + found_next_item = FALSE; + for (i = row + 1; i < GTK_CLIST (clist)->rows; i++) + { + next_item = (GbMenuItemData *) gtk_clist_get_row_data (GTK_CLIST (clist), + i); + /* We have to skip all the children of the next item as well. */ + if (next_item->level == level) + { + if (found_next_item) + { + new_row = i; + break; + } + else + found_next_item = TRUE; + } + else if (next_item->level < level) + break; + } + + /* Return if we can't move the item up. */ + if (new_row == -1) + { + if (found_next_item) + new_row = i; + else + return; + } + + /* Remove item and children. */ + items = remove_item_and_children (clist, row); + /* Remember that the new_row needs to be shifted because we deleted items. */ + new_row -= g_list_length (items); + + /* Now insert at new position. */ + insert_items (clist, items, new_row); + ensure_visible (clist, new_row); + + g_list_free (items); + + /* Make sure all items in the group point to the first one. */ + normalize_radio_groups (menued); + + gtk_clist_select_row (GTK_CLIST (clist), new_row, 0); + set_interface_state (menued); +} + +static void +on_left_button_clicked (GtkButton * button, + gpointer user_data) +{ + GladeMenuEditor *menued; + GtkWidget *clist; + GbMenuItemData *item; + gint row, i, level; + + menued = GLADE_MENU_EDITOR (gtk_widget_get_toplevel (GTK_WIDGET (button))); + clist = menued->clist; + row = get_selected_row (menued); + if (row == -1) + return; + item = (GbMenuItemData *) gtk_clist_get_row_data (GTK_CLIST (clist), row); + level = item->level; + if (item->level > 0) + item->level--; + gtk_clist_set_shift (GTK_CLIST (clist), row, 0, 0, item->level * GB_INDENT); + + for (i = row + 1; i < GTK_CLIST (clist)->rows; i++) + { + item = (GbMenuItemData *) gtk_clist_get_row_data (GTK_CLIST (clist), i); + if (item->level <= level) + break; + item->level--; + gtk_clist_set_shift (GTK_CLIST (clist), i, 0, 0, + item->level * GB_INDENT); + } + check_generated_handlers (menued); + set_interface_state (menued); +} + +static void +on_right_button_clicked (GtkButton * button, + gpointer user_data) +{ + GladeMenuEditor *menued; + GtkWidget *clist; + GbMenuItemData *item, *prev_item; + gint row, i, level; + + menued = GLADE_MENU_EDITOR (gtk_widget_get_toplevel (GTK_WIDGET (button))); + clist = menued->clist; + row = get_selected_row (menued); + if (row == -1 || row == 0) + return; + item = (GbMenuItemData *) gtk_clist_get_row_data (GTK_CLIST (clist), row); + prev_item = (GbMenuItemData *) gtk_clist_get_row_data (GTK_CLIST (clist), + row - 1); + if (prev_item->level < item->level) + return; + + level = item->level; + item->level++; + gtk_clist_set_shift (GTK_CLIST (clist), row, 0, 0, item->level * GB_INDENT); + + for (i = row + 1; i < GTK_CLIST (clist)->rows; i++) + { + item = (GbMenuItemData *) gtk_clist_get_row_data (GTK_CLIST (clist), i); + if (item->level <= level) + break; + item->level++; + gtk_clist_set_shift (GTK_CLIST (clist), i, 0, 0, + item->level * GB_INDENT); + } + + check_generated_handlers (menued); + set_interface_state (menued); +} + +static void +on_add_button_clicked (GtkWidget * button, + gpointer user_data) +{ + GladeMenuEditor *menued; + menued = GLADE_MENU_EDITOR (gtk_widget_get_toplevel (GTK_WIDGET (button))); + add_item (menued, FALSE, FALSE); +} + +static void +on_add_child_button_clicked (GtkWidget * button, + gpointer user_data) +{ + GladeMenuEditor *menued; + menued = GLADE_MENU_EDITOR (gtk_widget_get_toplevel (GTK_WIDGET (button))); + add_item (menued, TRUE, FALSE); +} + + +/************************************************************************** + * + **************************************************************************/ + +static void +on_add_separator_button_clicked (GtkWidget * button, + gpointer user_data) +{ + GladeMenuEditor *menued; + menued = GLADE_MENU_EDITOR (gtk_widget_get_toplevel (GTK_WIDGET (button))); + add_item (menued, FALSE, TRUE); +} + +static gboolean +on_key_press (GtkWidget * widget, + GdkEventKey * event, + gpointer user_data) +{ + switch (event->keyval) + { + case GDK_Delete: + on_delete_button_clicked (widget, NULL); + break; + } + + return FALSE; +} + +static void +on_delete_button_clicked (GtkWidget * widget, + gpointer user_data) +{ + GladeMenuEditor *menued; + GtkWidget *clist; + GbMenuItemData *item; + gint row, level, i; + + menued = GLADE_MENU_EDITOR (gtk_widget_get_toplevel (GTK_WIDGET (widget))); + clist = menued->clist; + row = get_selected_row (menued); + if (row == -1) + return; + item = (GbMenuItemData *) gtk_clist_get_row_data (GTK_CLIST (clist), row); + level = item->level; + + gtk_clist_remove (GTK_CLIST (clist), row); + + /* Update any other items in the same radio group. */ + if (item->type == GB_MENU_ITEM_RADIO) + remove_from_radio_group (menued, item); + + glade_menu_editor_free_item (item); + + /* Move all children up a level */ + for (i = row; i < GTK_CLIST (clist)->rows; i++) + { + item = (GbMenuItemData *) gtk_clist_get_row_data (GTK_CLIST (clist), i); + if (item->level <= level) + break; + item->level--; + gtk_clist_set_shift (GTK_CLIST (clist), i, 0, 0, + item->level * GB_INDENT); + } + + gtk_clist_select_row (GTK_CLIST (clist), row, 0); + set_interface_state (menued); +} + + +/************************************************************************** + * File Selection for selecting icon xpm files. + **************************************************************************/ +#define GLADE_RESPONSE_CLEAR 1 + +static void +on_icon_button_clicked (GtkWidget * widget, + gpointer user_data) +{ + GladeMenuEditor *menued; + gchar *icon; + + menued = GLADE_MENU_EDITOR (gtk_widget_get_toplevel (GTK_WIDGET (widget))); + + if (menued->filesel == NULL) + { + menued->filesel = gtk_file_chooser_dialog_new (_("Select icon"), + GTK_WINDOW (menued), + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_CLEAR, GLADE_RESPONSE_CLEAR, + GTK_STOCK_OK, GTK_RESPONSE_OK, + NULL); + gtk_dialog_set_default_response (GTK_DIALOG (menued->filesel), + GTK_RESPONSE_OK); + + g_signal_connect (menued->filesel, "response", + GTK_SIGNAL_FUNC (on_icon_filesel_response), menued); + g_signal_connect (menued->filesel, "delete_event", + G_CALLBACK (gtk_true), NULL); + } + + icon = (gchar*) gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (menued->icon_widget)->entry)); + if (icon && *icon) + glade_util_set_file_selection_filename (menued->filesel, icon); + + gtk_window_present (GTK_WINDOW (menued->filesel)); +} + + +static void +on_icon_filesel_response (GtkWidget * filesel, + gint response_id, + GladeMenuEditor *menued) +{ + gchar *filename; + + if (response_id == GTK_RESPONSE_OK) + { + filename = glade_util_get_file_selection_filename (filesel); + set_entry_text (GTK_ENTRY (GTK_COMBO (menued->icon_widget)->entry), + filename); + g_free (filename); + } + else if (response_id == GLADE_RESPONSE_CLEAR) + { + set_entry_text (GTK_ENTRY (GTK_COMBO (menued->icon_widget)->entry), + ""); + } + + glade_util_close_window (menued->filesel); +} + + +/************************************************************************** + * Utility functions + **************************************************************************/ + + +/* This returns the index of the currently selected row in the clist, or -1 + if no item is currently selected. */ +static gint +get_selected_row (GladeMenuEditor * menued) +{ + if (GTK_CLIST (menued->clist)->selection == NULL) + return -1; + return GPOINTER_TO_INT (GTK_CLIST (menued->clist)->selection->data); +} + +/* This returns the currently selected item, or NULL if no item is currently + selected. */ +static GbMenuItemData* +get_selected_item (GladeMenuEditor * menued) +{ + GbMenuItemData *item; + gint row; + + row = get_selected_row (menued); + if (row == -1) + return NULL; + item = gtk_clist_get_row_data (GTK_CLIST (menued->clist), row); + return item; +} + +/* This set the sensitivity of the buttons according to the current state. */ +static void +set_interface_state (GladeMenuEditor * menued) +{ + GbMenuItemData *item, *tmp_item; + GtkCList *clist; + gboolean up_button_sens = FALSE, down_button_sens = FALSE; + gboolean left_button_sens = FALSE, right_button_sens = FALSE; + gboolean add_button_sens = FALSE, delete_button_sens = FALSE; + gboolean state_sens = FALSE, group_sens = FALSE; + gboolean form_sens = FALSE, type_sens = FALSE, accel_sens = FALSE; + gboolean label_sens = FALSE, icon_sens = FALSE; + gint index; + + clist = GTK_CLIST (menued->clist); + + /* Figure out which of the arrow buttons should be sensitive. */ + + /* The Add button is always sensitive, since empty labels are separators. */ + add_button_sens = TRUE; + + /* The Delete button and the entire form are sensitive if an item is + selected in the clist. */ + index = get_selected_row (menued); + if (index != -1) + { + form_sens = TRUE; + type_sens = TRUE; + label_sens = TRUE; + icon_sens = TRUE; + delete_button_sens = TRUE; + + if (index > 0) + up_button_sens = TRUE; + if (index < clist->rows - 1) + down_button_sens = TRUE; + + item = (GbMenuItemData *) gtk_clist_get_row_data (clist, index); + if (item->level > 0) + left_button_sens = TRUE; + + /* The accelerator modifier and key are sensitive if this is not a + toplevel item on a menubar. */ + if (!GTK_IS_MENU_BAR (menued->menu) || item->level != 0) + accel_sens = TRUE; + + if (index > 0) + { + tmp_item = (GbMenuItemData *) gtk_clist_get_row_data (clist, + index - 1); + if (tmp_item->level >= item->level) + right_button_sens = TRUE; + } + + /* Figure out if the radio group widgets should be sensitive. */ + if (GTK_TOGGLE_BUTTON (menued->radio_radiobutton)->active) + { + group_sens = TRUE; + state_sens = TRUE; + icon_sens = FALSE; + } + + if (GTK_TOGGLE_BUTTON (menued->check_radiobutton)->active) + { + state_sens = TRUE; + icon_sens = FALSE; + } + + if (item->stock_item_index) + { + /* For the 'New' menu item, a label and tooltip must be provided. */ + if (menued->gnome_support) + { +#ifdef USE_GNOME + if (item->stock_item_index != GladeStockMenuItemNew) + label_sens = FALSE; +#endif + } + else + { + /* We don't allow the "New" label to be changed for GTK+ stock + items now, to be compatable with libglade. It did make it a + bit too complicated anyway. */ +#if 0 + const char *stock_id = g_slist_nth_data (menued->stock_items, + item->stock_item_index - 1); + if (strcmp (stock_id, GTK_STOCK_NEW)) +#endif + label_sens = FALSE; + } + + icon_sens = FALSE; + type_sens = FALSE; + accel_sens = FALSE; + } + } + + /* Now set the sensitivity of the widgets. */ + gtk_widget_set_sensitive (menued->stock_label, form_sens); + gtk_widget_set_sensitive (menued->stock_combo, form_sens); + + gtk_widget_set_sensitive (menued->icon_label, icon_sens); + gtk_widget_set_sensitive (menued->icon_widget, icon_sens); + gtk_widget_set_sensitive (menued->icon_button, icon_sens); + + gtk_widget_set_sensitive (menued->name_label, form_sens); + gtk_widget_set_sensitive (menued->name_entry, form_sens); + gtk_widget_set_sensitive (menued->handler_label, form_sens); + gtk_widget_set_sensitive (menued->handler_entry, form_sens); + + gtk_widget_set_sensitive (menued->label_label, label_sens); + gtk_widget_set_sensitive (menued->label_entry, label_sens); + gtk_widget_set_sensitive (menued->tooltip_label, label_sens); + gtk_widget_set_sensitive (menued->tooltip_entry, label_sens); + + gtk_widget_set_sensitive (menued->add_button, add_button_sens); + gtk_widget_set_sensitive (menued->add_separator_button, add_button_sens); + gtk_widget_set_sensitive (menued->delete_button, delete_button_sens); + + gtk_widget_set_sensitive (menued->type_frame, type_sens); + gtk_widget_set_sensitive (menued->state_label, state_sens); + gtk_widget_set_sensitive (menued->state_togglebutton, state_sens); + gtk_widget_set_sensitive (menued->group_label, group_sens); + gtk_widget_set_sensitive (menued->group_combo, group_sens); + + gtk_widget_set_sensitive (menued->accel_frame, accel_sens); +} + + +/* This gets a string representing the accelerator key + modifiers. + It returns a pointer to a static buffer. */ +static gchar * +get_accel_string (gchar * key, guint8 modifiers) +{ + static gchar buffer[32]; + + buffer[0] = '\0'; + if (modifiers & GDK_CONTROL_MASK) + strcat (buffer, "C+"); + if (modifiers & GDK_SHIFT_MASK) + strcat (buffer, "S+"); + if (modifiers & GDK_MOD1_MASK) + strcat (buffer, "A+"); + if (key) + strcat (buffer, key); + return buffer; +} + + +static gchar* +get_stock_item_label (GladeMenuEditor * menued, gint stock_item_index) +{ + if (menued->gnome_support) + { +#ifdef USE_GNOME + /* Most of the label text is from Gnome, but 2 of them are ours, + so we use a utility function to find the translation. */ + return glade_gnome_gettext (GladeStockMenuItemValues[stock_item_index].label); +#else + /* This shouldn't happen. */ + g_warning ("Trying to use GNOME stock items in GTK+ version of Glade"); + return NULL; +#endif + } + else + { + gchar *stock_id; + + if (stock_item_index <= 0) + return _("None"); + + stock_id = g_slist_nth_data (menued->stock_items, stock_item_index - 1); + return stock_id; + +#if 0 + GtkStockItem item; + gtk_stock_lookup (stock_id, &item); + return item.label; +#endif + } +} + + +/* This shows the properties of the item currently selected in the clist. */ +static void +show_item_properties (GladeMenuEditor * menued) +{ + GbMenuItemData *item; + GtkWidget *clist; + + clist = menued->clist; + item = get_selected_item (menued); + if (item == NULL) + return; + + menued->updating_widgets = TRUE; + + /* Now set them to the item's properties. */ + set_entry_text (GTK_ENTRY (GTK_COMBO (menued->stock_combo)->entry), + get_stock_item_label (menued, item->stock_item_index)); +#if 0 + g_print ("Setting label_entry to: %s\n", item->label); +#endif + set_entry_text (GTK_ENTRY (menued->label_entry), item->label); +#if 0 + g_print ("Setting name_entry to: %s\n", item->name); +#endif + set_entry_text (GTK_ENTRY (menued->name_entry), item->name); +#if 0 + g_print ("Setting handler_entry to: %s\n", item->handler); +#endif + set_entry_text (GTK_ENTRY (menued->handler_entry), item->handler); + set_entry_text (GTK_ENTRY (GTK_COMBO (menued->icon_widget)->entry), + item->icon); + set_entry_text (GTK_ENTRY (menued->tooltip_entry), item->tooltip); + + if (item->type == GB_MENU_ITEM_NORMAL) + { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (menued->normal_radiobutton), TRUE); + set_entry_text (GTK_ENTRY (GTK_COMBO (menued->group_combo)->entry), ""); + } + else if (item->type == GB_MENU_ITEM_CHECK) + { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (menued->check_radiobutton), TRUE); + set_entry_text (GTK_ENTRY (GTK_COMBO (menued->group_combo)->entry), ""); + } + else + { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (menued->radio_radiobutton), TRUE); + set_entry_text (GTK_ENTRY (GTK_COMBO (menued->group_combo)->entry), + item->group ? item->group->name : item->name); + } + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (menued->state_togglebutton), item->active); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (menued->accel_ctrl_checkbutton), (item->modifiers & GDK_CONTROL_MASK) ? TRUE : FALSE); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (menued->accel_shift_checkbutton), (item->modifiers & GDK_SHIFT_MASK) ? TRUE : FALSE); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (menued->accel_alt_checkbutton), (item->modifiers & GDK_MOD1_MASK) ? TRUE : FALSE); + set_entry_text (GTK_ENTRY (menued->accel_key_entry), + item->key ? item->key : ""); + + update_radio_groups (menued); + + menued->updating_widgets = FALSE; +} + +/* This adds a new menuitem. If separator is FALSE, it adds a normal item + with the label 'New Item'. If separator is TRUE it adds a separator. + It is added to the clist beneath the currently selected item, or at the + end of the list if no item is selected. If as_child is TRUE it adds the + item as a child of the selected item, else it adds it as a sibling. +*/ +static void +add_item (GladeMenuEditor * menued, + gboolean as_child, + gboolean separator) +{ + GbMenuItemData *item, *selected_item; + GtkWidget *clist; + gint row; + + item = g_new0 (GbMenuItemData, 1); + item->stock_item_index = 0; + if (separator) + { + item->label = NULL; + item->name = glade_project_new_widget_name (menued->project, + _("separator")); + } + else + { + item->label = glade_project_new_widget_name (menued->project, "item"); + item->name = g_strdup (item->label); + } + item->handler = generate_handler (menued, -1, item->label, item->name); + /* This is a flag to indicate that the last_mod_time should be set when + the 'Apply' button is clicked. */ + item->last_mod_time = (time_t) -2; + item->icon = NULL; + item->tooltip = NULL; + item->type = GB_MENU_ITEM_NORMAL; + item->active = FALSE; + item->group = NULL; + item->modifiers = 0; + item->key = NULL; + item->level = 0; + item->generate_name = TRUE; + item->generate_handler = TRUE; + + clist = menued->clist; + row = get_selected_row (menued); + if (row != -1) + { + selected_item = (GbMenuItemData *) gtk_clist_get_row_data (GTK_CLIST (clist), row); + item->level = selected_item->level + (as_child ? 1 : 0); + insert_item (GTK_CLIST (clist), item, row + 1); + gtk_clist_select_row (GTK_CLIST (clist), row + 1, 0); + ensure_visible (clist, row + 1); + } + else + { + item->level = 0; + insert_item (GTK_CLIST (clist), item, -1); + gtk_clist_select_row (GTK_CLIST (clist), GTK_CLIST (clist)->rows - 1, 0); + ensure_visible (clist, GTK_CLIST (clist)->rows - 1); + } + + set_interface_state (menued); + gtk_widget_grab_focus (menued->label_entry); + gtk_editable_select_region (GTK_EDITABLE (menued->label_entry), 0, -1); +} + +/* This adds the item to the clist at the given position. */ +static void +insert_item (GtkCList * clist, GbMenuItemData * item, gint row) +{ + gchar *rowdata[GB_MENUED_NUM_COLS]; + + /* Empty labels are understood to be separators. */ + if (item->label && strlen (item->label) > 0) + rowdata[GLD_COL_LABEL] = item->label; + else + rowdata[GLD_COL_LABEL] = GB_SEPARATOR_TEXT; + if (item->type == GB_MENU_ITEM_NORMAL) + rowdata[GLD_COL_TYPE] = ""; + else if (item->type == GB_MENU_ITEM_CHECK) + rowdata[GLD_COL_TYPE] = _("Check"); + else if (item->type == GB_MENU_ITEM_RADIO) + rowdata[GLD_COL_TYPE] = _("Radio"); + rowdata[GLD_COL_ACCEL] = get_accel_string (item->key, item->modifiers); + rowdata[GLD_COL_NAME] = item->name ? item->name : ""; + rowdata[GLD_COL_HANDLER] = item->handler ? item->handler : ""; + rowdata[GLD_COL_ICON] = item->icon ? item->icon : ""; + rowdata[GLD_COL_ACTIVE] = item->active ? _("Yes") : ""; + rowdata[GLD_COL_GROUP] = item->group ? item->group->name : item->name; + if (item->type != GB_MENU_ITEM_RADIO || !rowdata[GLD_COL_GROUP]) + rowdata[GLD_COL_GROUP] = ""; + + if (row >= 0) + gtk_clist_insert (clist, row, rowdata); + else + row = gtk_clist_append (GTK_CLIST (clist), rowdata); + + gtk_clist_set_row_data (GTK_CLIST (clist), row, item); + gtk_clist_set_shift (GTK_CLIST (clist), row, 0, 0, item->level * GB_INDENT); +} + +/* This makes sure the given row is visible. */ +static void +ensure_visible (GtkWidget *clist, + gint row) +{ + if (gtk_clist_row_is_visible (GTK_CLIST (clist), row) + != GTK_VISIBILITY_FULL) + gtk_clist_moveto(GTK_CLIST(clist), row, -1, 0.5, 0); +} + + +static GbMenuItemData* +find_group_item (GladeMenuEditor * menued, char *group_name) +{ + gint rows, row; + + if (!group_name || !group_name[0]) + return NULL; + + rows = GTK_CLIST (menued->clist)->rows; + + for (row = 0; row < rows; row++) + { + GbMenuItemData *item; + item = gtk_clist_get_row_data (GTK_CLIST (menued->clist), row); + + if (item->name && !strcmp (item->name, group_name)) + return item; + } + + return NULL; +} + + +/* This updates the currently selected item in the clist, updating each field + if it is different to the settings in the form elements. */ +static void +update_current_item (GladeMenuEditor * menued) +{ + GbMenuItemData *item, *group_item; + GtkCList *clist; + gchar *name, *label, *handler, *tooltip, *group_name, *key; + gchar *icon; + GbMenuItemType type; + gint row; + guint8 modifiers; + gboolean active, update_accelerator = FALSE; + + clist = GTK_CLIST (menued->clist); + row = get_selected_row (menued); + if (row == -1) + return; + item = (GbMenuItemData *) gtk_clist_get_row_data (GTK_CLIST (menued->clist), + row); + + name = (gchar*) gtk_entry_get_text (GTK_ENTRY (menued->name_entry)); + if (item_property_changed (name, item->name)) + { + g_free (item->name); + item->name = copy_item_property (name); + gtk_clist_set_text (clist, row, GLD_COL_NAME, + item->name ? item->name : ""); + } + + label = (gchar*) gtk_entry_get_text (GTK_ENTRY (menued->label_entry)); + if (item_property_changed (label, item->label)) + { + g_free (item->label); + item->label = copy_item_property (label); + gtk_clist_set_text (clist, row, GLD_COL_LABEL, + item->label ? item->label : GB_SEPARATOR_TEXT); + } + + handler = (gchar*) gtk_entry_get_text (GTK_ENTRY (menued->handler_entry)); + if (item_property_changed (handler, item->handler)) + { + g_free (item->handler); + item->handler = copy_item_property (handler); + gtk_clist_set_text (clist, row, GLD_COL_HANDLER, + item->handler ? item->handler : ""); + + /* This is a flag to indicate that the last_mod_time should be set when + the 'Apply' button is clicked. */ + item->last_mod_time = (time_t) -2; + } + + icon = (gchar*) gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (menued->icon_widget)->entry)); + if (item_property_changed (icon, item->icon)) + { + g_free (item->icon); + item->icon = copy_item_property (icon); + gtk_clist_set_text (clist, row, GLD_COL_ICON, + item->icon ? item->icon : ""); + } + + tooltip = (gchar*) gtk_entry_get_text (GTK_ENTRY (menued->tooltip_entry)); + if (item_property_changed (tooltip, item->tooltip)) + { + g_free (item->tooltip); + item->tooltip = copy_item_property (tooltip); + } + + if (GTK_TOGGLE_BUTTON (menued->normal_radiobutton)->active) + type = GB_MENU_ITEM_NORMAL; + else if (GTK_TOGGLE_BUTTON (menued->check_radiobutton)->active) + type = GB_MENU_ITEM_CHECK; + else + type = GB_MENU_ITEM_RADIO; + if (item->type != type) + { + /* If the item is changing from a radio item to something else, make + sure other items in the same group no longer point to it. */ + if (item->type == GB_MENU_ITEM_RADIO) + remove_from_radio_group (menued, item); + + item->type = type; + if (type == GB_MENU_ITEM_NORMAL) + gtk_clist_set_text (clist, row, GLD_COL_TYPE, ""); + else if (type == GB_MENU_ITEM_CHECK) + gtk_clist_set_text (clist, row, GLD_COL_TYPE, _("Check")); + else if (type == GB_MENU_ITEM_RADIO) + gtk_clist_set_text (clist, row, GLD_COL_TYPE, _("Radio")); + } + + active = GTK_TOGGLE_BUTTON (menued->state_togglebutton)->active + ? TRUE : FALSE; + if (active != item->active) + { + item->active = active; + gtk_clist_set_text (clist, row, GLD_COL_ACTIVE, active ? _("Yes") : ""); + } + + group_name = (gchar*) gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (menued->group_combo)->entry)); + group_item = find_group_item (menued, group_name); + if (group_item != item->group) + { + char *group_text = group_item ? group_item->name : item->name; + if (item->type != GB_MENU_ITEM_RADIO) + group_text = NULL; + item->group = group_item; + gtk_clist_set_text (clist, row, GLD_COL_GROUP, + group_text ? group_text : ""); + } + + key = (gchar*) gtk_entry_get_text (GTK_ENTRY (menued->accel_key_entry)); + if (item_property_changed (key, item->key)) + { + g_free (item->key); + item->key = copy_item_property (key); + update_accelerator = TRUE; + } + + modifiers = 0; + if (GTK_TOGGLE_BUTTON (menued->accel_ctrl_checkbutton)->active) + modifiers |= GDK_CONTROL_MASK; + if (GTK_TOGGLE_BUTTON (menued->accel_shift_checkbutton)->active) + modifiers |= GDK_SHIFT_MASK; + if (GTK_TOGGLE_BUTTON (menued->accel_alt_checkbutton)->active) + modifiers |= GDK_MOD1_MASK; + if (modifiers != item->modifiers) + { + item->modifiers = modifiers; + update_accelerator = TRUE; + } + + if (update_accelerator) + gtk_clist_set_text (clist, row, GLD_COL_ACCEL, + get_accel_string (item->key, item->modifiers)); + + set_interface_state (menued); +} + +/* This checks if the new value is different to the old, but an empty string + in new is taken to be equal to NULL as well. */ +static gboolean +item_property_changed (gchar *new, gchar *old) +{ + if (old == NULL) + return (new == NULL || new[0] == '\0') ? FALSE : TRUE; + if (new == NULL) + return TRUE; + if (!strcmp (old, new)) + return FALSE; + return TRUE; +} + +/* This returns a copy of the given property string, or NULL if it is an + empty string. */ +static gchar* +copy_item_property (gchar *property) +{ + if (property[0] == '\0') + return NULL; + return g_strdup (property); +} + + +/* This clears the form, ready to add a new item. If full is TRUE it resets + the type checkbuttons, group and accelerator modifiers. When adding items + full is set to FALSE so the user can add several items of the same type. */ +static void +clear_form (GladeMenuEditor * menued, + gboolean full) +{ + gtk_list_select_item (GTK_LIST (GTK_COMBO (menued->stock_combo)->list), 0); + set_entry_text (GTK_ENTRY (GTK_COMBO (menued->icon_widget)->entry), ""); + set_entry_text (GTK_ENTRY (menued->label_entry), ""); + set_entry_text (GTK_ENTRY (menued->name_entry), ""); + set_entry_text (GTK_ENTRY (menued->handler_entry), ""); + set_entry_text (GTK_ENTRY (menued->tooltip_entry), ""); + set_entry_text (GTK_ENTRY (menued->accel_key_entry), ""); + + if (full) + { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (menued->normal_radiobutton), TRUE); + set_entry_text (GTK_ENTRY (GTK_COMBO (menued->group_combo)->entry), + ""); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (menued->accel_ctrl_checkbutton), FALSE); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (menued->accel_shift_checkbutton), FALSE); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (menued->accel_alt_checkbutton), FALSE); + } +} + + +/* This removes an item and its children from the clist, and returns a list + of the removed items. */ +static GList * +remove_item_and_children (GtkWidget * clist, + gint row) +{ + GList *items = NULL; + GbMenuItemData *item; + gint level; + + item = (GbMenuItemData *) gtk_clist_get_row_data (GTK_CLIST (clist), row); + level = item->level; + items = g_list_append (items, item); + gtk_clist_remove (GTK_CLIST (clist), row); + + while (row < GTK_CLIST (clist)->rows) + { + item = (GbMenuItemData *) gtk_clist_get_row_data (GTK_CLIST (clist), row); + if (item->level > level) + { + items = g_list_append (items, item); + gtk_clist_remove (GTK_CLIST (clist), row); + } + else + break; + } + return items; +} + +/* This inserts the given list of items at the given position in the clist. */ +static void +insert_items (GtkWidget * clist, + GList * items, + gint row) +{ + GbMenuItemData *item; + + while (items) + { + item = (GbMenuItemData *) items->data; + insert_item (GTK_CLIST (clist), item, row++); + items = items->next; + } +} + + +/* This returns the default name of the widget, given its label. The returned + string should be freed at some point. */ +static gchar* +generate_name (GladeMenuEditor *menued, + gchar *label) +{ + gchar *prefix, *name, *src, *dest; + + /* For empty labels, i.e. separators, use 'separator'. */ + if (label == NULL || label[0] == '\0') + { + return glade_project_new_widget_name (menued->project, _("separator")); + } + + prefix = g_malloc (strlen (label) + 1); + /* Convert spaces to underscores, and ignore periods (e.g. in "Open...") + and underscores (e.g. in "_Open"). */ + for (src = label, dest = prefix; *src; src++) + { + if (*src == ' ') + *dest++ = '_'; + else if (*src == '.') + continue; + else if (*src == '_') + continue; + else + *dest++ = *src; + } + *dest = '\0'; + + if (dest >= prefix + strlen (label) + 1) + g_warning ("Buffer overflow"); + + /* Get rid of any trailing digits. */ + dest--; + while (*dest >= '0' && *dest <= '9') + { + *dest = '\0'; + dest--; + } + + name = glade_project_new_widget_name (menued->project, prefix); + g_free (prefix); + + return name; +} + +/* This returns the default 'activate' handler name, given the name of the + item. The returned string should be freed at some point. */ +static gchar* +generate_handler (GladeMenuEditor *menued, gint row, gchar *label, gchar *name) +{ + gchar *handler, *start = "on_", *end = "_activate"; + + /* For empty labels, i.e. separators, and items with submenus, there is no + handler by default. */ + if (label == NULL || label[0] == '\0' || is_parent (menued, row)) + return NULL; + + handler = g_malloc (strlen (name) + strlen (start) + strlen (end) + 1); + strcpy (handler, start); + strcat (handler, name); + strcat (handler, end); + + return handler; +} + + +/* This makes sure the default handlers are updated as items are moved around. + */ +static void +check_generated_handlers (GladeMenuEditor *menued) +{ + GtkCList *clist; + GbMenuItemData *item; + gint row; + gchar *handler; + + clist = GTK_CLIST (menued->clist); + for (row = 0; row < clist->rows; row++) + { + item = (GbMenuItemData *) gtk_clist_get_row_data (clist, row); + if (item->generate_handler) + { + handler = generate_handler (menued, row, item->label, item->name); + if (item_property_changed (handler, item->handler)) + { + g_free (item->handler); + item->handler = handler; + gtk_clist_set_text (clist, row, GLD_COL_HANDLER, handler); + } + else + { + g_free (handler); + } + } + } +} + + +/* This returns TRUE id the item in the given row is a parent, or FALSE + if the row doesn't exist or isn't a parent. */ +static gboolean +is_parent (GladeMenuEditor *menued, + gint row) +{ + GtkCList *clist; + GbMenuItemData *item, *next_item; + + clist = GTK_CLIST (menued->clist); + if (row < 0 || row >= clist->rows - 1) + return FALSE; + item = (GbMenuItemData *) gtk_clist_get_row_data (clist, row); + next_item = (GbMenuItemData *) gtk_clist_get_row_data (clist, row + 1); + if (next_item->level > item->level) + return TRUE; + return FALSE; +} + + +static void +on_menu_editor_ok (GtkWidget *button, + GladeMenuEditor *menued) +{ + glade_menu_editor_update_menu (menued); + gtk_widget_destroy (GTK_WIDGET (menued)); +} + +static void +on_menu_editor_apply (GtkWidget *button, + GladeMenuEditor *menued) +{ + glade_menu_editor_update_menu (menued); +} + + +static void +on_menu_editor_close (GtkWidget *widget, + GladeMenuEditor *menued) +{ + gtk_widget_destroy (GTK_WIDGET (menued)); +} + + +/************************************************************************** + * Public functions + **************************************************************************/ + +/* This checks if the given icon string is a stock icon name, and if it is + it returns the stock ID instead. If not, it just returns the icon. */ +static gchar* +get_stock_id_from_icon_name (GladeMenuEditor *menued, + gchar *icon) +{ + if (!icon || *icon == '\0') + return NULL; + + /* We use the stock id instead of the text now, so we don't need this. */ +#if 0 + GList *clist; + + clist = GTK_LIST (GTK_COMBO (menued->icon_widget)->list)->children; + + while (clist && clist->data) + { + gchar* ltext = glade_util_gtk_combo_func (GTK_LIST_ITEM (clist->data)); + if (!ltext) + continue; + if (!strcmp (ltext, icon)) + { + gchar *stock_id = gtk_object_get_data (GTK_OBJECT (clist->data), + GladeMenuEditorStockIDKey); + return stock_id; + } + clist = clist->next; + } +#endif + + return icon; +} + + +/* This updates the menu, based on the settings in the menu editor. + It removes all the current children of the menu and recreates it. + Note that it has to reload all the xpm files for pixmaps, so its not + very efficient, but they're small so it shouldn't be too bad. */ +static void +glade_menu_editor_update_menu (GladeMenuEditor *menued) +{ + GbMenuItemData *item; + GtkWidget *menuitem, *label, *prev_item = NULL, *child_menu; + GtkCList *clist; + GtkMenuShell *current_menu; + GList *menus; + GHashTable *group_hash; + gchar *child_name; + gint i, level; + GbWidget *gbwidget; + GladeWidgetData *wdata; + GtkAccelGroup *accel_group; + GtkWidget *pixmap = NULL; + gboolean use_pixmap_menu_item; + gchar *stock_id, *icon_name; +#ifdef USE_GNOME + GnomeUIInfo *uiinfo = NULL; +#endif + + /* Remove existing children of the menu. Note that this will result in the + old widget names being released, so we need to reserve the new names, + even if they are the same. */ + while (menued->menu->children) + { + menuitem = menued->menu->children->data; + gtk_widget_destroy (menuitem); + } + + /* FIXME: This seems to be necessary to re-initialise the menu. I don't know + why. */ + menued->menu->menu_flag = TRUE; + + clist = GTK_CLIST (menued->clist); + + /* Now reserve all the the new widget names. */ + for (i = 0; i < clist->rows; i++) + { + item = (GbMenuItemData *) gtk_clist_get_row_data (clist, i); + + if (item->name && item->name[0]) + glade_project_reserve_name (menued->project, item->name); + } + + /* Now add widgets according to the items in the menu editor clist. */ + level = 0; + menus = g_list_append (NULL, menued->menu); + group_hash = g_hash_table_new (NULL, NULL); + + /* Make sure all items in the group point to the first one. */ + normalize_radio_groups (menued); + + for (i = 0; i < clist->rows; i++) + { + item = (GbMenuItemData *) gtk_clist_get_row_data (clist, i); + + icon_name = get_stock_id_from_icon_name (menued, item->icon); + stock_id = NULL; + + if (item->level > level) + { + child_menu = gb_widget_new_full ("GtkMenu", FALSE, NULL, NULL, 0, 0, + NULL, GB_CREATING, NULL); + child_name = g_strdup_printf ("%s_menu", prev_item->name); + gtk_widget_set_name (child_menu, child_name); + g_free (child_name); + level = item->level; + /* We use the menus GList as a stack, pushing menus onto the + front. */ + menus = g_list_prepend (menus, child_menu); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (prev_item), child_menu); + tree_add_widget (child_menu); + } + while (item->level < level) + { + /* This removes/pops the first menu in the list. */ + menus = g_list_remove_link (menus, menus); + level--; + } + current_menu = GTK_MENU_SHELL (menus->data); + + pixmap = NULL; + if (item->label && strlen (item->label) > 0) + { + label = gtk_accel_label_new (""); + gtk_label_set_text_with_mnemonic (GTK_LABEL (label), item->label); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_widget_show (label); + + use_pixmap_menu_item = FALSE; + + if (menued->gnome_support) + { +#ifdef USE_GNOME + if (item->stock_item_index) + { + uiinfo = &GladeStockMenuItemValues[item->stock_item_index]; + if (uiinfo->pixmap_type == GNOME_APP_PIXMAP_STOCK) + { + pixmap = gtk_image_new_from_stock (uiinfo->pixmap_info, + GTK_ICON_SIZE_MENU); + use_pixmap_menu_item = TRUE; + } + } +#endif + } + else + { + if (item->stock_item_index) + { + stock_id = g_slist_nth_data (menued->stock_items, + item->stock_item_index - 1); +#if 0 + g_print ("Stock ID for icon: %s\n", stock_id); +#endif + pixmap = gtk_image_new_from_stock (stock_id, + GTK_ICON_SIZE_MENU); + use_pixmap_menu_item = TRUE; + } + } + + if (!item->stock_item_index && icon_name) + { + pixmap = gb_widget_new ("GtkImage", NULL); + if (glade_util_check_is_stock_id (icon_name)) + { + gtk_image_set_from_stock (GTK_IMAGE (pixmap), icon_name, + GTK_ICON_SIZE_MENU); + } + else + { + gtk_image_set_from_file (GTK_IMAGE (pixmap), icon_name); + + /* Add the pixmap to the project, so the file is copied. + It should be removed from the project in the menuitem + GbWidget.destroy function. */ + glade_project_add_pixmap (menued->project, icon_name); + } + gb_widget_set_child_name (pixmap, GladeChildMenuItemImage); + use_pixmap_menu_item = TRUE; + } + + if (item->type == GB_MENU_ITEM_NORMAL) + { + if (use_pixmap_menu_item) + { + menuitem = gtk_image_menu_item_new (); + if (pixmap) + { + gtk_widget_show (pixmap); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menuitem), + pixmap); + } + } + else + { + menuitem = gtk_menu_item_new (); + } + gtk_container_add (GTK_CONTAINER (menuitem), label); + } + else if (item->type == GB_MENU_ITEM_CHECK) + { + menuitem = gtk_check_menu_item_new (); + if (item->active) + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menuitem), + TRUE); + gtk_container_add (GTK_CONTAINER (menuitem), label); + } + else + { + menuitem = create_radio_menu_item (current_menu, + item, group_hash); + if (item->active) + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menuitem), + TRUE); + gtk_container_add (GTK_CONTAINER (menuitem), label); + } + + gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (label), menuitem); + } + else + { + /* This creates a separator. */ + menuitem = gtk_separator_menu_item_new (); + } + gtk_widget_show (menuitem); + + /* Save the stock name and icon in the menuitem. */ + if (item->stock_item_index) + { + if (menued->gnome_support) + { +#ifdef USE_GNOME + gtk_object_set_data (GTK_OBJECT (menuitem), + GladeMenuItemStockIndexKey, + GINT_TO_POINTER (item->stock_item_index)); +#endif + } + else + { + gchar *stock_id; + + stock_id = g_slist_nth_data (menued->stock_items, + item->stock_item_index - 1); + + gtk_object_set_data_full (GTK_OBJECT (menuitem), + GladeMenuItemStockIDKey, + g_strdup (stock_id), + g_free); + } + } + else if (item->icon && pixmap) + { + gtk_object_set_data_full (GTK_OBJECT (pixmap), GladeIconKey, + g_strdup (icon_name), g_free); + } + + + /* Turn it into a GbWidget, and add the 'activate' handler and the + accelerator. */ + if (item->name == NULL || item->name[0] == '\0') + { + g_free (item->name); + item->name = glade_project_new_widget_name (menued->project, "item"); + } + + gbwidget = gb_widget_lookup_class (gtk_type_name (GTK_OBJECT_TYPE (menuitem))); + + if (item->wdata) + wdata = glade_widget_data_copy (item->wdata); + else + wdata = glade_widget_data_new (gbwidget); + + wdata->gbwidget = gbwidget; + + gb_widget_create_from_full (menuitem, NULL, wdata); + gtk_widget_set_name (menuitem, item->name); + + if (item->active) + wdata->flags |= GLADE_ACTIVE; + else + wdata->flags &= ~GLADE_ACTIVE; + + g_free (wdata->tooltip); + wdata->tooltip = g_strdup (item->tooltip); + /* FIXME: Should set tooltip? Or for Gnome install in appbar? */ + + if (item->handler && strlen (item->handler) > 0) + { + GladeSignal *signal = g_new (GladeSignal, 1); + signal->name = g_strdup ("activate"); + signal->handler = g_strdup (item->handler); + signal->object = NULL; + signal->after = FALSE; + signal->data = NULL; + + /* If the last mod time is set to the special value, we set it to + the current time now. */ + if (item->last_mod_time == (time_t) -2) { + item->last_mod_time = time (NULL); + if (item->last_mod_time == (time_t) -1) + g_warning ("Can't get current time"); + } + + signal->last_modification_time = item->last_mod_time; + wdata->signals = g_list_append (wdata->signals, signal); + } + + if (item->key && strlen (item->key) > 0) + { + GladeAccelerator *accel = g_new (GladeAccelerator, 1); + guint key; + accel->modifiers = item->modifiers; + accel->key = g_strdup (item->key); + accel->signal = g_strdup ("activate"); + wdata->accelerators = g_list_append (wdata->accelerators, accel); + + /* We can only add accelerators to menus, not menubars. */ + if (GTK_IS_MENU (current_menu)) + { + key = glade_keys_dialog_find_key (item->key); + accel_group = GTK_MENU (current_menu)->accel_group; + gtk_widget_add_accelerator (menuitem, "activate", accel_group, + key, item->modifiers, + GTK_ACCEL_VISIBLE); + } + } + + if (menued->gnome_support) + { +#ifdef USE_GNOME + /* For stock menu items, we use the configured accelerator keys. */ + if (GTK_IS_MENU (current_menu) + && item->stock_item_index && uiinfo->accelerator_key != 0) + { + accel_group = GTK_MENU (current_menu)->accel_group; + gtk_widget_add_accelerator (menuitem, "activate", accel_group, + uiinfo->accelerator_key, + uiinfo->ac_mods, + GTK_ACCEL_VISIBLE); + } +#endif + } + else + { + /* For stock menu items, we use the configured accelerator keys. */ + if (GTK_IS_MENU (current_menu) && item->stock_item_index && stock_id) + { + GtkStockItem item; + + gtk_stock_lookup (stock_id, &item); + if (item.keyval) + { + accel_group = GTK_MENU (current_menu)->accel_group; + gtk_widget_add_accelerator (menuitem, "activate", + accel_group, + item.keyval, item.modifier, + GTK_ACCEL_VISIBLE); + } + } + } + + /* Add the menuitem to the current menu. */ + gtk_menu_shell_append (current_menu, menuitem); + tree_add_widget (menuitem); + prev_item = menuitem; + } + g_list_free (menus); + g_hash_table_destroy (group_hash); + + /* Make sure the displayed item is correct. */ + show_item_properties (menued); +} + + +/* This creates a radio menu item using the appropriate radio group. + If the item's group is not NULL and doesn't points to the item itself, + then it will search the list of groups to find the group. If it can't find + a group, or it didn't have one it creates a new group. + If it creates a new group it adds this item to the list of groups. */ +static GtkWidget* +create_radio_menu_item (GtkMenuShell *menu, + GbMenuItemData *item, + GHashTable *group_hash) +{ + GtkWidget *menuitem; + GSList *group = NULL; + + if (item->group && item->group != item) + { + GtkRadioMenuItem *group_widget = g_hash_table_lookup (group_hash, + item->group); + if (group_widget) + group = gtk_radio_menu_item_get_group (group_widget); + } + + menuitem = gtk_radio_menu_item_new (group); + if (!group) + g_hash_table_insert (group_hash, item, menuitem); + + return menuitem; +} + + +static void +glade_menu_editor_on_menu_destroyed (GtkWidget *menu, + GtkWidget *menued) +{ + gtk_widget_destroy (menued); +} + + +/* This converts the given menu/menubar into the list of items displayed in + the menu editor. */ +static void +glade_menu_editor_set_menu (GladeMenuEditor *menued, + GtkMenuShell *menu) +{ + /* First clear any current items and radio groups. */ + glade_menu_editor_reset (menued); + + /* Connect to the destroy signal of the menu widget, so we can destroy the + menu editor when the widget is destroyed. */ + menued->menu_destroy_handler_id = gtk_signal_connect (GTK_OBJECT (menu), + "destroy", + GTK_SIGNAL_FUNC (glade_menu_editor_on_menu_destroyed), + menued); + + /* Now add each of the menus/menuitems in the given menu. */ + menued->menu = menu; + + set_submenu (menued, menu, 0); +} + + +/* This checks if the given icon_name is a stock ID, and if it is it returns + the text to display instead. If not, it returns icon_name. */ +static gchar* +get_icon_name_from_stock_id (GladeMenuEditor *menued, + gchar *icon_name) +{ + /* We use the stock id instead of the text now. */ +#if 0 + GtkStockItem item; + if (icon_name && gtk_stock_lookup (icon_name, &item)) + return item.label; + else +#endif + + return icon_name; +} + + +/* This recursively adds menus. */ +static void +set_submenu (GladeMenuEditor *menued, + GtkMenuShell *menu, + gint level) +{ + GbMenuItemData *item; + GtkWidget *menuitem, *label; + GList *child, *tmp_list; + GladeWidgetData *wdata; + gchar *icon_name; + GList *groups = NULL; + + child = menu->children; + while (child) + { + menuitem = GTK_WIDGET (child->data); + + if (!GTK_IS_MENU_ITEM (menuitem)) + { + g_warning ("Menu widget is not a menu item"); + child = child->next; + continue; + } + /* FIXME: We can't handle tearoff menuitems at present. */ + if (GTK_IS_TEAROFF_MENU_ITEM (menuitem)) + { + child = child->next; + continue; + } + + wdata = (GladeWidgetData*) gtk_object_get_data (GTK_OBJECT(menuitem), + GB_WIDGET_DATA_KEY); + if (wdata == NULL) + { + g_warning ("Menu widget has no GladeWidgetData"); + child = child->next; + continue; + } + + item = g_new0 (GbMenuItemData, 1); + + if (menued->gnome_support) + { +#ifdef USE_GNOME + item->stock_item_index = GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (menuitem), GladeMenuItemStockIndexKey)); +#endif + } + else + { + gchar *stock_id; + GSList *elem; + gint idx; + + stock_id = gtk_object_get_data (GTK_OBJECT (menuitem), + GladeMenuItemStockIDKey); + item->stock_item_index = 0; + if (stock_id) + { + for (elem = menued->stock_items, idx = 1; elem; + elem = elem->next, idx++) + { + if (!strcmp (elem->data, stock_id)) + { + item->stock_item_index = idx; + break; + } + } + } + } + + item->name = g_strdup (gtk_widget_get_name (menuitem)); + item->label = NULL; + item->handler = NULL; + item->last_mod_time = 0; + item->icon = NULL; + + /* If it isn't a stock item, we check if it has an icon set. */ + if (item->stock_item_index == 0 && GTK_IS_IMAGE_MENU_ITEM (menuitem)) + { + GtkWidget *image; + image = gtk_image_menu_item_get_image (GTK_IMAGE_MENU_ITEM (menuitem)); + if (image) + { + icon_name = gtk_object_get_data (GTK_OBJECT (image), GladeIconKey); + item->icon = g_strdup (get_icon_name_from_stock_id (menued, icon_name)); + } + } + + item->tooltip = g_strdup (wdata->tooltip); + item->type = GB_MENU_ITEM_NORMAL; + item->active = FALSE; + item->group = NULL; + item->modifiers = 0; + item->key = NULL; + item->level = level; + item->generate_name = FALSE; + item->generate_handler = FALSE; + item->wdata = glade_widget_data_copy (wdata); + + if (GTK_IS_RADIO_MENU_ITEM (menuitem)) + { + item->type = GB_MENU_ITEM_RADIO; + item->group = find_radio_group (GTK_RADIO_MENU_ITEM (menuitem), + &groups, item); + } + else if (GTK_IS_CHECK_MENU_ITEM (menuitem)) + item->type = GB_MENU_ITEM_CHECK; + + label = GTK_BIN (menuitem)->child; + if (label && GTK_IS_LABEL (label)) + item->label = glade_util_get_label_text (label); + + if (GTK_IS_CHECK_MENU_ITEM (menuitem) + && GTK_CHECK_MENU_ITEM (menuitem)->active) + item->active = TRUE; + + /* Find 'activate' handler in widget data. */ + tmp_list = item->wdata->signals; + while (tmp_list) + { + GladeSignal *signal = (GladeSignal *) tmp_list->data; + if (!strcmp (signal->name, "activate")) + { + item->handler = g_strdup (signal->handler); + item->last_mod_time = signal->last_modification_time; + + /* Remove the signal from our copy of the GladeWidgetData. */ + glade_widget_data_free_signal (signal); + item->wdata->signals = g_list_remove (item->wdata->signals, + signal); + break; + } + tmp_list = tmp_list->next; + } + + /* Find 'activate' accelerator in widget data. */ + tmp_list = item->wdata->accelerators; + while (tmp_list) + { + GladeAccelerator *accel = (GladeAccelerator *) tmp_list->data; + if (!strcmp (accel->signal, "activate")) + { + item->key = g_strdup (accel->key); + item->modifiers = accel->modifiers; + + /* Remove the accel from our copy of the GladeWidgetData. */ + glade_widget_data_free_accel (accel); + item->wdata->accelerators = g_list_remove (item->wdata->accelerators, + accel); + break; + } + tmp_list = tmp_list->next; + } + + + insert_item (GTK_CLIST (menued->clist), item, -1); + + if (GTK_MENU_ITEM (menuitem)->submenu) + { + set_submenu (menued, + GTK_MENU_SHELL (GTK_MENU_ITEM (menuitem)->submenu), + level + 1); + } + + child = child->next; + } + + g_list_free (groups); +} + + +/* This clears the clist, freeing all GbMenuItemDatas, and resets the radio + group combo, freeing all the current radio groups. */ +static void +glade_menu_editor_reset (GladeMenuEditor *menued) +{ + GbMenuItemData *item; + gint i; + + /* Disconnect our destroy handler on the menu widget. */ + if (menued->menu) + { + gtk_signal_disconnect (GTK_OBJECT (menued->menu), + menued->menu_destroy_handler_id); + menued->menu = NULL; + } + + for (i = 0; i < GTK_CLIST (menued->clist)->rows; i++) + { + item = (GbMenuItemData *) gtk_clist_get_row_data (GTK_CLIST (menued->clist), i); + glade_menu_editor_free_item (item); + + } + gtk_clist_clear (GTK_CLIST (menued->clist)); + + gtk_list_clear_items (GTK_LIST (GTK_COMBO (menued->group_combo)->list), + 0, -1); +} + + +static void +glade_menu_editor_free_item (GbMenuItemData *item) +{ + g_free (item->name); + g_free (item->label); + g_free (item->handler); + g_free (item->icon); + g_free (item->tooltip); + g_free (item->key); + + if (item->wdata) + glade_widget_data_free (item->wdata); + + g_free (item); +} + + +/* Make sure all item group fields point to the first item in the group. */ +static void +normalize_radio_groups (GladeMenuEditor * menued) +{ + GbMenuItemData *item, *group; + GList *groups = NULL; + gint rows, row; + + /* Step through each row checking each radio item. */ + rows = GTK_CLIST (menued->clist)->rows; + for (row = 0; row < rows; row++) + { + item = gtk_clist_get_row_data (GTK_CLIST (menued->clist), row); + if (item->type != GB_MENU_ITEM_RADIO) + continue; + + /* Follow the chain of groups until we get the real one. */ + group = item->group; + while (group && group->group && group->group != group) + group = group->group; + + /* If it is a new group, add it to the list. */ + if (!group || group == item) + { + groups = g_list_prepend (groups, item); + item->group = NULL; + } + /* Else check if the group item is after the current one. If it is, + then we make the current item the group leader. We assume that if + we haven't seen the group item yet, it must be after this one. */ + else if (!g_list_find (groups, group)) + { + groups = g_list_prepend (groups, item); + group->group = item; + item->group = NULL; + } + else + { + item->group = group; + } + } + + g_list_free (groups); +} + + +/* This recreates the list of available radio groups, and puts them in the + combo's drop-down list. */ +static void +update_radio_groups (GladeMenuEditor * menued) +{ + GbMenuItemData *item, *tmp_item; + GList *groups = NULL, *elem; + gint item_row, row, parent_row, rows; + GtkCombo *combo; + GtkList *list; + GtkWidget *li; + + /* Make sure all item group fields point to the first item in the group. */ + normalize_radio_groups (menued); + + item_row = get_selected_row (menued); + if (item_row == -1) + { + gtk_list_clear_items (GTK_LIST (GTK_COMBO (menued->group_combo)->list), + 0, -1); + return; + } + + item = gtk_clist_get_row_data (GTK_CLIST (menued->clist), item_row); + + /* Step backwards to find the parent item. */ + parent_row = -1; + for (row = item_row - 1; row > 0; row--) + { + tmp_item = gtk_clist_get_row_data (GTK_CLIST (menued->clist), row); + if (tmp_item->level < item->level) + { + parent_row = row; + break; + } + } + + /* Now step through the items, checking all items that are on the same + level as the current one, until we reach the end of the list or find an + item on a higher level. */ + rows = GTK_CLIST (menued->clist)->rows; + for (row = parent_row + 1; row < rows; row++) + { + tmp_item = gtk_clist_get_row_data (GTK_CLIST (menued->clist), row); + if (tmp_item->level < item->level) + break; + + if (tmp_item->level == item->level + && tmp_item->type == GB_MENU_ITEM_RADIO + && tmp_item->name && tmp_item->name[0]) + { + /* If the item has its group set to NULL or itself, then it is a new + group, so add its name. */ + if (!tmp_item->group || tmp_item->group == tmp_item) + groups = g_list_prepend (groups, tmp_item->name); + } + } + + groups = g_list_sort (groups, (GCompareFunc)strcmp); + + combo = GTK_COMBO (menued->group_combo); + list = GTK_LIST (combo->list); + + /* We have to block the combo's list's selection changed signal, or it + causes problems. */ + gtk_signal_handler_block (GTK_OBJECT (list), + GTK_COMBO (menued->group_combo)->list_change_id); + + gtk_list_clear_items (list, 0, -1); + + /* Add the special 'New' item to create a new group. */ + li = gtk_list_item_new_with_label (_("New")); + gtk_widget_show (li); + gtk_container_add (GTK_CONTAINER (list), li); + gtk_combo_set_item_string (combo, GTK_ITEM (li), ""); + + /* Add a separator. */ + li = gtk_list_item_new (); + gtk_widget_show (li); + gtk_container_add (GTK_CONTAINER (list), li); + + for (elem = groups; elem; elem = elem->next) + { + li = gtk_list_item_new_with_label (elem->data); + gtk_widget_show (li); + gtk_container_add (GTK_CONTAINER (list), li); + } + + gtk_signal_handler_unblock (GTK_OBJECT (list), + GTK_COMBO (menued->group_combo)->list_change_id); + g_list_free (groups); +} + + +/* This finds the group item to use for the given radiomenuitem widget. + It searches the list of group items to find the first one that is also in + the radiomenuitem's group list. If none is found, it creates a new group + and adds it to the list. */ +static GbMenuItemData* +find_radio_group (GtkRadioMenuItem *menuitem, GList **groups, + GbMenuItemData *item) +{ + GSList *item_group_list; + GList *elem; + + item_group_list = menuitem->group; + + /* The groups list contains pairs of GSList + GbMenuItemData*. */ + for (elem = *groups; elem; elem = elem->next->next) + { + GSList *elem_group = elem->data; + GbMenuItemData *elem_item = elem->next->data; + + if (elem_group == item_group_list) + return elem_item; + } + + /* We couldn't find an existing group that matches, so we create a new one. + */ + *groups = g_list_prepend (*groups, item); + *groups = g_list_prepend (*groups, item_group_list); + return NULL; +} + + +/* This is called to make sure that no items have the given item as their + group leader. It is called when an item is removed, or when its type is + changed from a radio item to something else. + + It steps through the list, checking for radio items with + the group field set to the given item. The first time it finds one, it + creates a new group. It sets the group field of all other items in the + same group to the new first item in the group. */ +static void +remove_from_radio_group (GladeMenuEditor * menued, + GbMenuItemData *item) +{ + GbMenuItemData *new_group_item = NULL; + gint rows, row; + + rows = GTK_CLIST (menued->clist)->rows; + for (row = 0; row < rows; row++) + { + GbMenuItemData *tmp_item; + tmp_item = gtk_clist_get_row_data (GTK_CLIST (menued->clist), row); + + if (tmp_item->type == GB_MENU_ITEM_RADIO + && tmp_item->group == item && tmp_item != item) + { + if (new_group_item) + { + tmp_item->group = new_group_item; + } + else + { + tmp_item->group = NULL; + new_group_item = tmp_item; + } + } + } +} + |