X-Git-Url: http://git.chise.org/gitweb/?p=chise%2Fxemacs-chise.git.1;a=blobdiff_plain;f=src%2Fmenubar-gtk.c;h=387e15072f88fbf5fc79500f5c4659b3284829d6;hp=8d2dac90fccae5ae5b2a77009a42aaba3881c39d;hb=0a57cba46779af884cc537d18923dcb6313b9904;hpb=7ee7b9fd0c91842f267cf6e412286d647f68e32a diff --git a/src/menubar-gtk.c b/src/menubar-gtk.c index 8d2dac9..387e150 100644 --- a/src/menubar-gtk.c +++ b/src/menubar-gtk.c @@ -1,4 +1,4 @@ -/* Implements an elisp-programmable menubar -- X interface. +/* Implements an elisp-programmable menubar -- Gtk interface. Copyright (C) 1993, 1994 Free Software Foundation, Inc. Copyright (C) 1995 Tinker Systems and INS Engineering Corp. @@ -46,7 +46,7 @@ Boston, MA 02111-1307, USA. */ #define SUBMENU_TYPE 1 #define POPUP_TYPE 2 -static GtkWidget *menu_descriptor_to_widget_1 (Lisp_Object descr); +static GtkWidget *menu_descriptor_to_widget_1 (Lisp_Object descr, GtkAccelGroup* accel_group); #define FRAME_MENUBAR_DATA(frame) ((frame)->menubar_data) #define XFRAME_MENUBAR_DATA_LASTBUFF(frame) (XCAR ((frame)->menubar_data)) @@ -110,14 +110,14 @@ gtk_xemacs_menubar_get_type (void) return xemacs_menubar_type; } -static GtkWidgetClass *parent_class; +static GtkWidgetClass *menubar_parent_class; static void gtk_xemacs_menubar_class_init (GtkXEmacsMenubarClass *klass) { GtkWidgetClass *widget_class; widget_class = (GtkWidgetClass*) klass; - parent_class = (GtkWidgetClass *) gtk_type_class (gtk_menu_bar_get_type ()); + menubar_parent_class = (GtkWidgetClass *) gtk_type_class (gtk_menu_bar_get_type ()); widget_class->size_request = gtk_xemacs_menubar_size_request; } @@ -131,7 +131,7 @@ static void gtk_xemacs_menubar_size_request (GtkWidget *widget, GtkRequisition * GtkXEmacsMenubar *x = GTK_XEMACS_MENUBAR (widget); GtkRequisition frame_size; - parent_class->size_request (widget, requisition); + menubar_parent_class->size_request (widget, requisition); /* #### BILL! ** We should really only do this if the menu has not been detached! @@ -154,6 +154,117 @@ gtk_xemacs_menubar_new (struct frame *f) return (GTK_WIDGET (menubar)); } +/* + * Label with XEmacs accelerator character support. + * + * The default interfaces to GtkAccelLabel does not understand XEmacs + * keystroke printing conventions, nor is it convenient in the places where is + * it needed. This subclass provides an alternative interface more suited to + * XEmacs needs but does not add new functionality. + */ +#define GTK_TYPE_XEMACS_ACCEL_LABEL (gtk_xemacs_accel_label_get_type ()) +#define GTK_XEMACS_ACCEL_LABEL(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_ACCEL_LABEL, GtkXEmacsAccelLabel)) +#define GTK_XEMACS_ACCEL_LABEL_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_ACCEL_LABEL, GtkXEmacsAccelLabelClass)) +#define GTK_IS_XEMACS_ACCEL_LABEL(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_XEMACS_ACCEL_LABEL)) +#define GTK_IS_XEMACS_ACCEL_LABEL_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_XEMACS_ACCEL_LABEL)) + +typedef struct _GtkXEmacsAccelLabel GtkXEmacsAccelLabel; +typedef struct _GtkXEmacsAccelLabelClass GtkXEmacsAccelLabelClass; + +/* Instance structure. No additional fields required. */ +struct _GtkXEmacsAccelLabel +{ + GtkAccelLabel label; +}; + +/* Class structure. No additional fields required. */ +struct _GtkXEmacsAccelLabelClass +{ + GtkAccelLabelClass parent_class; +}; + +static GtkType gtk_xemacs_accel_label_get_type(void); +static GtkWidget* gtk_xemacs_accel_label_new(const gchar *string); +static void gtk_xemacs_set_accel_keys(GtkXEmacsAccelLabel* l, + Lisp_Object keys); +static void gtk_xemacs_accel_label_class_init(GtkXEmacsAccelLabelClass *klass); +static void gtk_xemacs_accel_label_init(GtkXEmacsAccelLabel *xemacs); + +static GtkType +gtk_xemacs_accel_label_get_type(void) +{ + static GtkType xemacs_accel_label_type = 0; + + if (!xemacs_accel_label_type) + { + static const GtkTypeInfo xemacs_accel_label_info = + { + "GtkXEmacsAccelLabel", + sizeof (GtkXEmacsAccelLabel), + sizeof (GtkXEmacsAccelLabelClass), + (GtkClassInitFunc) gtk_xemacs_accel_label_class_init, + (GtkObjectInitFunc) gtk_xemacs_accel_label_init, + /* reserved_1 */ NULL, + /* reserved_2 */ NULL, + (GtkClassInitFunc) NULL, + }; + + xemacs_accel_label_type = gtk_type_unique (gtk_accel_label_get_type(), &xemacs_accel_label_info); + } + + return xemacs_accel_label_type; +} + +static void +gtk_xemacs_accel_label_class_init(GtkXEmacsAccelLabelClass *klass) +{ + /* Nothing to do. */ +} + +static void +gtk_xemacs_accel_label_init(GtkXEmacsAccelLabel *xemacs) +{ + /* Nothing to do. */ +} + +static GtkWidget* +gtk_xemacs_accel_label_new (const gchar *string) +{ + GtkXEmacsAccelLabel *xemacs_accel_label; + + xemacs_accel_label = gtk_type_new (GTK_TYPE_XEMACS_ACCEL_LABEL); + + if (string && *string) + gtk_label_set_text (GTK_LABEL (xemacs_accel_label), string); + + return GTK_WIDGET (xemacs_accel_label); +} + +/* Make the string the accelerator string for the label. */ +static void +gtk_xemacs_set_accel_keys(GtkXEmacsAccelLabel* l, Lisp_Object keys) +{ + g_return_if_fail (l != NULL); + g_return_if_fail (GTK_IS_XEMACS_ACCEL_LABEL (l)); + + /* Disable the standard way of finding the accelerator string for the + label. */ + gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL(l), NULL); + + /* Set the string straight from the object. */ + if (STRINGP (keys) && XSTRING_LENGTH (keys)) + { + C_STRING_TO_EXTERNAL_MALLOC(XSTRING_DATA (keys), + l->label.accel_string, + Qctext); + } + else + { + /* l->label.accel_string = NULL;*/ + } +} + + /* We now return you to your regularly scheduled menus... */ int dockable_menubar; @@ -314,7 +425,8 @@ __activate_menu(GtkMenuItem *item, gpointer user_data) } else { - next = menu_descriptor_to_widget_1 (child); + next = menu_descriptor_to_widget_1 (child, + gtk_menu_ensure_uline_accel_group (GTK_MENU (item->submenu))); } if (!next) @@ -349,6 +461,63 @@ __kill_stupid_gtk_timer (GtkObject *obj, gpointer user_data) } } +/* Convert the XEmacs menu accelerator representation to Gtk mnemonic form. If + no accelerator has been provided, put one at the start of the string (this + mirrors the behaviour under X). This algorithm is also found in + dialog-gtk.el:gtk-popup-convert-underscores. +*/ +static char * +convert_underscores(const char *name) +{ + char *rval; + int i,j; + int found_accel = FALSE; + int underscores = 0; + + for (i = 0; name[i]; ++i) + if (name[i] == '%' && name[i+1] == '_') + { + found_accel = TRUE; + } + else if (name[i] == '_') + { + underscores++; + } + + /* Allocate space for the original string, plus zero byte plus extra space + for all quoted underscores plus possible additional leading accelerator. */ + rval = xmalloc_and_zero (strlen(name) + 1 + underscores + + (found_accel ? 0 : 1)); + + if (!found_accel) + rval[0] = '_'; + + for (i = 0, j = (found_accel ? 0 : 1); name[i]; i++) + { + if (name[i]=='%') + { + i++; + if (!(name[i])) + continue; + + if ((name[i] != '_') && (name[i] != '%')) + i--; + + found_accel = TRUE; + } + else if (name[i] == '_') + { + rval[j++] = '_'; + } + + rval[j++] = name[i]; + } + + return rval; +} + + +/* Remove the XEmacs menu accellerator representation from a string. */ static char * remove_underscores(const char *name) { @@ -362,7 +531,9 @@ remove_underscores(const char *name) if (!(name[i])) continue; - if ((name[i] == '_')) + if ((name[i] != '_') && (name[i] != '%')) + i--; + else continue; } rval[j++] = name[i]; @@ -375,7 +546,8 @@ remove_underscores(const char *name) DESCR is either a list (meaning a submenu), a vector, or nil (if you include a :filter keyword) */ static GtkWidget * -menu_convert (Lisp_Object desc, GtkWidget *reuse) +menu_convert (Lisp_Object desc, GtkWidget *reuse, + GtkAccelGroup* menubar_accel_group) { GtkWidget *menu_item = NULL; GtkWidget *submenu = NULL; @@ -392,8 +564,23 @@ menu_convert (Lisp_Object desc, GtkWidget *reuse) if (!reuse) { - char *temp_menu_name = remove_underscores (XSTRING_DATA (XCAR (desc))); - menu_item = gtk_menu_item_new_with_label (temp_menu_name); + char *temp_menu_name = convert_underscores (XSTRING_DATA (XCAR (desc))); + GtkWidget* accel_label = gtk_xemacs_accel_label_new(NULL); + guint accel_key; + + gtk_misc_set_alignment (GTK_MISC (accel_label), 0.0, 0.5); + accel_key = gtk_label_parse_uline (GTK_LABEL (accel_label), temp_menu_name); + + menu_item = gtk_menu_item_new (); + gtk_container_add (GTK_CONTAINER (menu_item), accel_label); + gtk_widget_show (accel_label); + + if (menubar_accel_group) + gtk_widget_add_accelerator (menu_item, + "activate_item", + menubar_accel_group, + accel_key, GDK_MOD1_MASK, + GTK_ACCEL_LOCKED); free (temp_menu_name); } else @@ -542,28 +729,13 @@ menu_convert (Lisp_Object desc, GtkWidget *reuse) return (menu_item); } -static struct frame * -__get_channel (GtkWidget *w) -{ - struct frame *f = NULL; - - for (; w; w = w->parent) - { - if ((f = (struct frame *) gtk_object_get_data (GTK_OBJECT (w), "xemacs::frame"))) - return (f); - } - - return (selected_frame()); -} - - /* Called whenever a button, radio, or toggle is selected in the menu */ static void __generic_button_callback (GtkMenuItem *item, gpointer user_data) { Lisp_Object callback, function, data, channel; - XSETFRAME (channel, __get_channel (GTK_WIDGET (item))); + XSETFRAME (channel, gtk_widget_to_frame (GTK_WIDGET (item))); VOID_TO_LISP (callback, user_data); @@ -576,7 +748,8 @@ __generic_button_callback (GtkMenuItem *item, gpointer user_data) /* This function cannot GC. It is only called from menu_item_descriptor_to_widget_value, which prohibits GC. */ -static GtkWidget *menu_descriptor_to_widget_1 (Lisp_Object descr) +static GtkWidget * +menu_descriptor_to_widget_1 (Lisp_Object descr, GtkAccelGroup* accel_group) { if (STRINGP (descr)) { @@ -589,7 +762,7 @@ static GtkWidget *menu_descriptor_to_widget_1 (Lisp_Object descr) else if (LISTP (descr)) { /* It is a submenu */ - return (menu_convert (descr, NULL)); + return (menu_convert (descr, NULL, accel_group)); } else if (VECTORP (descr)) { @@ -610,6 +783,7 @@ static GtkWidget *menu_descriptor_to_widget_1 (Lisp_Object descr) int plist_p; int selected_spec = 0, included_spec = 0; GtkWidget *widget = NULL; + guint accel_key; if (length < 2) signal_simple_error ("button descriptors must be at least 2 long", descr); @@ -705,8 +879,9 @@ static GtkWidget *menu_descriptor_to_widget_1 (Lisp_Object descr) sprintf (label_buffer, "%s ", XSTRING_DATA (name)); } - temp_label = remove_underscores (label_buffer); - main_label = gtk_accel_label_new (temp_label); + temp_label = convert_underscores (label_buffer); + main_label = gtk_xemacs_accel_label_new (NULL); + accel_key = gtk_label_parse_uline (GTK_LABEL (main_label), temp_label); free (temp_label); } @@ -822,36 +997,23 @@ static GtkWidget *menu_descriptor_to_widget_1 (Lisp_Object descr) GTK_SIGNAL_FUNC (__generic_button_callback), LISP_TO_VOID (callback)); - /* We cheat here... GtkAccelLabel usually builds its - `accel_string' from the widget it is attached to, but we do - not want to go thru the overhead of converting our nice - string back into the modifier + key format that requires, - just so that they can convert it back into a (possibly - different/wrong) string - - We set the label string manually, and things should 'just - work' - - In an ideal world we would just subclass GtkLabel ourselves, - but I have known for a very long time that this is not an - ideal world. - - #### Should do menu shortcuts `correctly' one of these days. + /* Now that all the information about the menu item is know, set the + remaining properties. */ if (main_label) { - GtkAccelLabel *l = GTK_ACCEL_LABEL (main_label); - gtk_container_add (GTK_CONTAINER (widget), main_label); - gtk_accel_label_set_accel_widget (l, NULL); - gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5); + gtk_misc_set_alignment (GTK_MISC (main_label), 0.0, 0.5); + gtk_xemacs_set_accel_keys(GTK_XEMACS_ACCEL_LABEL(main_label), keys); - if (STRINGP (keys) && XSTRING_LENGTH (keys)) - { - l->accel_string = g_strdup (XSTRING_DATA (keys)); - } + if (accel_group) + gtk_widget_add_accelerator (widget, + "activate_item", + accel_group, + accel_key, 0, + GTK_ACCEL_LOCKED); } return (widget); @@ -859,11 +1021,12 @@ static GtkWidget *menu_descriptor_to_widget_1 (Lisp_Object descr) else { return (NULL); - /* abort (); ???? */ + /* ABORT (); ???? */ } } -static GtkWidget *menu_descriptor_to_widget (Lisp_Object descr) +static GtkWidget * +menu_descriptor_to_widget (Lisp_Object descr, GtkAccelGroup* accel_group) { int count = specpdl_depth (); GtkWidget *rval = NULL; @@ -873,7 +1036,7 @@ static GtkWidget *menu_descriptor_to_widget (Lisp_Object descr) gc_currently_forbidden = 1; /* Cannot GC from here on out... */ - rval = menu_descriptor_to_widget_1 (descr); + rval = menu_descriptor_to_widget_1 (descr, accel_group); unbind_to (count, Qnil); return (rval); @@ -883,23 +1046,24 @@ static gboolean menu_can_reuse_widget (GtkWidget *child, const char *label) { /* Everything up at the top level was done using - ** gtk_menu_item_new_with_label(), but we still double check to make + ** gtk_xemacs_accel_label_new(), but we still double check to make ** sure we don't seriously foobar ourselves. */ - char *temp_label = NULL; - gpointer possible_child = g_list_nth_data (gtk_container_children (GTK_CONTAINER (child)), 0); + gpointer possible_child = + g_list_nth_data (gtk_container_children (GTK_CONTAINER (child)), 0); + gboolean ret_val = FALSE; if (possible_child && GTK_IS_LABEL (possible_child)) { - if (!temp_label) temp_label = remove_underscores (label); + char *temp_label = remove_underscores (label); + if (!strcmp (GTK_LABEL (possible_child)->label, temp_label)) - { - free (temp_label); - return (TRUE); - } + ret_val = TRUE; + + free (temp_label); } - if (temp_label) free (temp_label); - return (FALSE); + + return ret_val; } /* Converts a menubar description into a GtkMenuBar... a menubar is a @@ -915,6 +1079,7 @@ menu_create_menubar (struct frame *f, Lisp_Object descr) GtkWidget *menubar = FRAME_GTK_MENUBAR_WIDGET (f); GUI_ID id = (GUI_ID) gtk_object_get_data (GTK_OBJECT (menubar), XEMACS_MENU_GUIID_TAG); guint menu_position = 0; + GtkAccelGroup *menubar_accel_group; /* Remove any existing protection for old menu items */ ungcpro_popup_callbacks (id); @@ -922,6 +1087,8 @@ menu_create_menubar (struct frame *f, Lisp_Object descr) /* GCPRO the whole damn thing */ gcpro_popup_callbacks (id, descr); + menubar_accel_group = gtk_accel_group_new(); + EXTERNAL_LIST_LOOP (tail, value) { gpointer current_child = g_list_nth_data (GTK_MENU_SHELL (menubar)->children, menu_position); @@ -939,7 +1106,7 @@ menu_create_menubar (struct frame *f, Lisp_Object descr) /* It is a button description */ GtkWidget *item; - item = menu_descriptor_to_widget (item_descr); + item = menu_descriptor_to_widget (item_descr, menubar_accel_group); gtk_widget_set_name (item, "XEmacsMenuButton"); if (!item) @@ -961,12 +1128,13 @@ menu_create_menubar (struct frame *f, Lisp_Object descr) if (current_child && menu_can_reuse_widget (GTK_WIDGET (current_child), XSTRING_DATA (XCAR (item_descr)))) { - widget = menu_convert (item_descr, GTK_WIDGET (current_child)); + widget = menu_convert (item_descr, GTK_WIDGET (current_child), + menubar_accel_group); reused_p = TRUE; } else { - widget = menu_convert (item_descr, NULL); + widget = menu_convert (item_descr, NULL, menubar_accel_group); if (current_child) gtk_widget_destroy (GTK_WIDGET (current_child)); gtk_menu_bar_insert (GTK_MENU_BAR (menubar), widget, menu_position); } @@ -1005,6 +1173,10 @@ menu_create_menubar (struct frame *f, Lisp_Object descr) } } } + + /* Attach the new accelerator group to the frame. */ + gtk_window_add_accel_group (GTK_WINDOW (FRAME_GTK_SHELL_WIDGET(f)), + menubar_accel_group); } @@ -1216,11 +1388,21 @@ static void gtk_popup_menu (Lisp_Object menu_desc, Lisp_Object event) { struct Lisp_Event *eev = NULL; - GtkWidget *widget = menu_descriptor_to_widget (menu_desc); - GtkWidget *menu = GTK_MENU_ITEM (widget)->submenu; - gpointer id = gtk_object_get_data (GTK_OBJECT (widget), XEMACS_MENU_GUIID_TAG); - + GtkWidget *widget = NULL; + GtkWidget *menu = NULL; + gpointer id = NULL; + + /* Do basic error checking first... */ + if (SYMBOLP (menu_desc)) + menu_desc = Fsymbol_value (menu_desc); + CHECK_CONS (menu_desc); + CHECK_STRING (XCAR (menu_desc)); + + /* Now lets get down to business... */ + widget = menu_descriptor_to_widget (menu_desc, NULL); + menu = GTK_MENU_ITEM (widget)->submenu; gtk_widget_set_name (widget, "XEmacsPopupMenu"); + id = gtk_object_get_data (GTK_OBJECT (widget), XEMACS_MENU_GUIID_TAG); __activate_menu (GTK_MENU_ITEM (widget), id); @@ -1261,7 +1443,7 @@ See the definition of `popup-menu' for more information on the format of MENU. */ (menu)) { - GtkWidget *w = menu_descriptor_to_widget (menu); + GtkWidget *w = menu_descriptor_to_widget (menu, NULL); return (w ? build_gtk_object (GTK_OBJECT (w)) : Qnil); }