diff options
author | Alexander Larsson <alexl@redhat.com> | 2001-03-21 18:58:28 +0000 |
---|---|---|
committer | Alexander Larsson <alexl@src.gnome.org> | 2001-03-21 18:58:28 +0000 |
commit | de5eafede29f9c39e51fd78bc183fed60fe04a52 (patch) | |
tree | 46d98fc60ec0f146b58312e6046fa44c8fbf79cb /gtk | |
parent | a24e9b107402d29e8aa3e2fc3c73652e7d3b6575 (diff) | |
download | gtk+-de5eafede29f9c39e51fd78bc183fed60fe04a52.tar.gz |
Renamed gtk_button_new_stock() to gtk_button_new_from_stock() and removed
2001-03-21 Alexander Larsson <alexl@redhat.com>
* gtk/gtkbutton.[ch]:
* gtk/gtkdialog.c:
Renamed gtk_button_new_stock() to gtk_button_new_from_stock() and
removed accel_group argument. Renamed gtk_button_new_accel() to
gtk_button_new_with_mnemonic() and removed accel_group argument.
* gtk/gtkcheckbutton.[ch]:
New function gtk_check_button_new_with_mnemonic().
* gtk/gtkentry.c:
Override activate_mnemonic and just grab focus.
* gtk/gtkitemfactory.c:
Don't add menu uline accel group, instead use mnemonics support.
* gtk/gtklabel.[ch]:
New support for mnemonics.
* gtk/gtkmarshal.list:
Needed BOOLEAN:BOOLEAN for activate_mnemonic.
* gtk/gtkmenu.[c]:
* gtkmenushell.c:
Use mnemonics instead of accel groups for uline
support in menu items.
Removed gtk_menu_get_uline_accel_group() and
gtk_menu_ensure_uline_accel_group().
* gtk/gtkmenuitem.c:
Override activate_mnemonic to handle switching between
menu items if there are collisions.
* gtk/gtknotebook.c:
Connect to activate_mnemonic on the tab_label, so that
activating it switches to that notebook page.
* gtk/gtkwidget.[ch]:
Add activate_mnemonic signal. New function
gtk_widget_activate_mnemonic() to emit it.
Default implementation does activate/grab_focus.
* gtk/gtkwindow.[ch]:
Add support for mnemonics in windows.
New functions:
gtk_window_add_mnemonic, gtk_window_remove_mnemonic,
gtk_window_activate_mnemonic, gtk_window_set_mnemonic_modifier
* gtk/testgtk.c:
Update to function name changes.
Diffstat (limited to 'gtk')
-rw-r--r-- | gtk/gtkbutton.c | 76 | ||||
-rw-r--r-- | gtk/gtkbutton.h | 26 | ||||
-rw-r--r-- | gtk/gtkcheckbutton.c | 29 | ||||
-rw-r--r-- | gtk/gtkcheckbutton.h | 5 | ||||
-rw-r--r-- | gtk/gtkdialog.c | 3 | ||||
-rw-r--r-- | gtk/gtkentry.c | 12 | ||||
-rw-r--r-- | gtk/gtkitemfactory.c | 19 | ||||
-rw-r--r-- | gtk/gtklabel.c | 274 | ||||
-rw-r--r-- | gtk/gtklabel.h | 39 | ||||
-rw-r--r-- | gtk/gtkmarshal.list | 1 | ||||
-rw-r--r-- | gtk/gtkmarshalers.list | 1 | ||||
-rw-r--r-- | gtk/gtkmenu.c | 36 | ||||
-rw-r--r-- | gtk/gtkmenu.h | 6 | ||||
-rw-r--r-- | gtk/gtkmenuitem.c | 23 | ||||
-rw-r--r-- | gtk/gtkmenushell.c | 10 | ||||
-rw-r--r-- | gtk/gtknotebook.c | 43 | ||||
-rw-r--r-- | gtk/gtkwidget.c | 42 | ||||
-rw-r--r-- | gtk/gtkwidget.h | 6 | ||||
-rw-r--r-- | gtk/gtkwindow.c | 200 | ||||
-rw-r--r-- | gtk/gtkwindow.h | 15 | ||||
-rw-r--r-- | gtk/testgtk.c | 43 |
21 files changed, 706 insertions, 203 deletions
diff --git a/gtk/gtkbutton.c b/gtk/gtkbutton.c index 6640bc2a4c..fedd51ac55 100644 --- a/gtk/gtkbutton.c +++ b/gtk/gtkbutton.c @@ -319,9 +319,17 @@ gtk_button_new_with_label (const gchar *label) return button; } +/** + * gtk_button_new_from_stock: + * @stock_id: the name of the stock item + * @returns: a new #GtkButton + * + * Creates a new #GtkButton containing the image and text from a stock item. + * Some stock ids have preprocessor macros like #GTK_STOCK_BUTTON_OK and + * #GTK_STOCK_BUTTON_APPLY. + **/ GtkWidget* -gtk_button_new_stock (const gchar *stock_id, - GtkAccelGroup *accel_group) +gtk_button_new_from_stock (const gchar *stock_id) { GtkWidget *button; GtkStockItem item; @@ -331,34 +339,12 @@ gtk_button_new_stock (const gchar *stock_id, GtkWidget *label; GtkWidget *image; GtkWidget *hbox; - guint keyval; button = gtk_button_new (); - label = gtk_label_new (NULL); - keyval = gtk_label_parse_uline (GTK_LABEL (label), - item.label); - - if (keyval && accel_group) - { - gtk_widget_add_accelerator (button, - "clicked", - accel_group, - keyval, - GDK_MOD1_MASK, - GTK_ACCEL_LOCKED); - } - - /* Also add the stock accelerator if one was specified. */ - if (item.keyval && accel_group) - { - gtk_widget_add_accelerator (button, - "clicked", - accel_group, - item.keyval, - item.modifier, - GTK_ACCEL_LOCKED); - } + label = gtk_label_new_with_mnemonic (item.label); + + gtk_label_set_mnemonic_widget (GTK_LABEL (label), button); image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON); hbox = gtk_hbox_new (FALSE, 0); @@ -371,37 +357,37 @@ gtk_button_new_stock (const gchar *stock_id, } else { - button = gtk_button_new_accel (stock_id, accel_group); + button = gtk_button_new_with_mnemonic (stock_id); } return button; } +/** + * gtk_button_new_with_mnemonic: + * @label: The text of the button, with an underscore in front of the + * mnemonic character + * @returns: a new #GtkButton + * + * Creates a new #GtkButton containing a label. + * If characters in @label are preceded by an underscore, they are underlined + * indicating that they represent a keyboard accelerator called a mnemonic. + * Pressing Alt and that key activates the button. + **/ GtkWidget* -gtk_button_new_accel (const gchar *uline_label, - GtkAccelGroup *accel_group) +gtk_button_new_with_mnemonic (const gchar *label) { GtkWidget *button; - GtkWidget *label; - guint keyval; + GtkWidget *label_widget; button = gtk_button_new (); - label = gtk_label_new (NULL); - keyval = gtk_label_parse_uline (GTK_LABEL (label), uline_label); + label_widget = gtk_label_new_with_mnemonic (label); - if (keyval && accel_group) - { - gtk_widget_add_accelerator (button, - "clicked", - accel_group, - keyval, - GDK_MOD1_MASK, - GTK_ACCEL_LOCKED); - } + gtk_label_set_mnemonic_widget (GTK_LABEL (label_widget), button); - gtk_container_add (GTK_CONTAINER (button), label); - gtk_widget_show (label); + gtk_container_add (GTK_CONTAINER (button), label_widget); + gtk_widget_show (label_widget); return button; } diff --git a/gtk/gtkbutton.h b/gtk/gtkbutton.h index 553813994c..0e9934931a 100644 --- a/gtk/gtkbutton.h +++ b/gtk/gtkbutton.h @@ -77,20 +77,18 @@ struct _GtkButtonClass GtkType gtk_button_get_type (void) G_GNUC_CONST; -GtkWidget* gtk_button_new (void); -GtkWidget* gtk_button_new_with_label (const gchar *label); -GtkWidget* gtk_button_new_stock (const gchar *stock_id, - GtkAccelGroup *accel_group); -GtkWidget* gtk_button_new_accel (const gchar *uline_label, - GtkAccelGroup *accel_group); -void gtk_button_pressed (GtkButton *button); -void gtk_button_released (GtkButton *button); -void gtk_button_clicked (GtkButton *button); -void gtk_button_enter (GtkButton *button); -void gtk_button_leave (GtkButton *button); -void gtk_button_set_relief (GtkButton *button, - GtkReliefStyle newstyle); -GtkReliefStyle gtk_button_get_relief (GtkButton *button); +GtkWidget* gtk_button_new (void); +GtkWidget* gtk_button_new_with_label (const gchar *label); +GtkWidget* gtk_button_new_from_stock (const gchar *stock_id); +GtkWidget* gtk_button_new_with_mnemonic (const gchar *label); +void gtk_button_pressed (GtkButton *button); +void gtk_button_released (GtkButton *button); +void gtk_button_clicked (GtkButton *button); +void gtk_button_enter (GtkButton *button); +void gtk_button_leave (GtkButton *button); +void gtk_button_set_relief (GtkButton *button, + GtkReliefStyle newstyle); +GtkReliefStyle gtk_button_get_relief (GtkButton *button); #ifdef __cplusplus diff --git a/gtk/gtkcheckbutton.c b/gtk/gtkcheckbutton.c index 257cafefac..e89854bb8e 100644 --- a/gtk/gtkcheckbutton.c +++ b/gtk/gtkcheckbutton.c @@ -123,6 +123,35 @@ gtk_check_button_new_with_label (const gchar *label) return check_button; } +/** + * gtk_check_button_new_with_mnemonic: + * @label: The text of the button, with an underscore in front of the + * mnemonic character + * @returns: a new #GtkCheckButton + * + * Creates a new #GtkCheckButton containing a label. + * If characters in @label are preceded by an underscore, they are underlined + * indicating that they represent a keyboard accelerator called a mnemonic. + * Pressing Alt and that key activates the checkbutton. + **/ +GtkWidget* +gtk_check_button_new_with_mnemonic (const gchar *label) +{ + GtkWidget *check_button; + GtkWidget *label_widget; + + check_button = gtk_check_button_new (); + label_widget = gtk_label_new_with_mnemonic (label); + gtk_misc_set_alignment (GTK_MISC (label_widget), 0.0, 0.5); + gtk_label_set_mnemonic_widget (GTK_LABEL (label_widget), check_button); + + gtk_container_add (GTK_CONTAINER (check_button), label_widget); + gtk_widget_show (label_widget); + + return check_button; +} + + /* This should only be called when toggle_button->draw_indicator * is true. */ diff --git a/gtk/gtkcheckbutton.h b/gtk/gtkcheckbutton.h index 4abb4d72e9..b0ff3c45c0 100644 --- a/gtk/gtkcheckbutton.h +++ b/gtk/gtkcheckbutton.h @@ -66,8 +66,9 @@ struct _GtkCheckButtonClass GtkType gtk_check_button_get_type (void) G_GNUC_CONST; -GtkWidget* gtk_check_button_new (void); -GtkWidget* gtk_check_button_new_with_label (const gchar *label); +GtkWidget* gtk_check_button_new (void); +GtkWidget* gtk_check_button_new_with_label (const gchar *label); +GtkWidget* gtk_check_button_new_with_mnemonic (const gchar *label); #ifdef __cplusplus diff --git a/gtk/gtkdialog.c b/gtk/gtkdialog.c index edb2b3555a..b4af6a7a26 100644 --- a/gtk/gtkdialog.c +++ b/gtk/gtkdialog.c @@ -471,8 +471,7 @@ gtk_dialog_add_button (GtkDialog *dialog, g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL); g_return_val_if_fail (button_text != NULL, NULL); - button = gtk_button_new_stock (button_text, - gtk_window_get_default_accel_group (GTK_WINDOW (dialog))); + button = gtk_button_new_from_stock (button_text); GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); diff --git a/gtk/gtkentry.c b/gtk/gtkentry.c index 88fd888594..e07e08d451 100644 --- a/gtk/gtkentry.c +++ b/gtk/gtkentry.c @@ -250,6 +250,8 @@ static void gtk_entry_paste (GtkEntry *entry, static void gtk_entry_update_primary_selection (GtkEntry *entry); static void gtk_entry_popup_menu (GtkEntry *entry, GdkEventButton *event); +static gboolean gtk_entry_activate_mnemonic (GtkWidget *widget, + gboolean group_cycling); static GtkWidgetClass *parent_class = NULL; @@ -341,6 +343,7 @@ gtk_entry_class_init (GtkEntryClass *class) widget_class->style_set = gtk_entry_style_set; widget_class->direction_changed = gtk_entry_direction_changed; widget_class->state_changed = gtk_entry_state_changed; + widget_class->activate_mnemonic = gtk_entry_activate_mnemonic; widget_class->drag_motion = gtk_entry_drag_motion; widget_class->drag_leave = gtk_entry_drag_leave; @@ -3133,6 +3136,15 @@ activate_cb (GtkWidget *menuitem, gtk_signal_emit_by_name (GTK_OBJECT (entry), signal); } + +static gboolean +gtk_entry_activate_mnemonic (GtkWidget *widget, + gboolean group_cycling) +{ + gtk_widget_grab_focus (widget); + return TRUE; +} + static void append_action_signal (GtkEntry *entry, GtkWidget *menu, diff --git a/gtk/gtkitemfactory.c b/gtk/gtkitemfactory.c index d8806c794c..3e66ee5413 100644 --- a/gtk/gtkitemfactory.c +++ b/gtk/gtkitemfactory.c @@ -1228,24 +1228,7 @@ gtk_item_factory_create_item (GtkItemFactory *ifactory, "GtkMisc::xalign", 0.0, NULL); - accel_key = gtk_label_parse_uline (GTK_LABEL (label), name); - - if (accel_key != GDK_VoidSymbol) - { - if (GTK_IS_MENU_BAR (parent)) - gtk_widget_add_accelerator (widget, - "activate_item", - ifactory->accel_group, - accel_key, GDK_MOD1_MASK, - GTK_ACCEL_LOCKED); - - if (GTK_IS_MENU (parent)) - gtk_widget_add_accelerator (widget, - "activate_item", - gtk_menu_ensure_uline_accel_group (GTK_MENU (parent)), - accel_key, 0, - GTK_ACCEL_LOCKED); - } + gtk_label_set_text_with_mnemonic (GTK_LABEL (label), name); } g_free (name); diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c index 43217df259..aa340021fd 100644 --- a/gtk/gtklabel.c +++ b/gtk/gtklabel.c @@ -26,11 +26,15 @@ #include <math.h> #include <string.h> #include "gtklabel.h" +#include "gtksignal.h" +#include "gtkwindow.h" #include "gdk/gdkkeysyms.h" #include "gtkclipboard.h" #include "gdk/gdki18n.h" #include <pango/pango.h> #include "gtkintl.h" +#include "gtkmenuitem.h" +#include "gtknotebook.h" struct _GtkLabelSelectionInfo { @@ -39,6 +43,7 @@ struct _GtkLabelSelectionInfo gint selection_end; }; + enum { PROP_0, PROP_LABEL, @@ -49,7 +54,7 @@ enum { PROP_PATTERN, PROP_WRAP, PROP_SELECTABLE, - PROP_ACCEL_KEYVAL + PROP_MNEMONIC_KEYVAL }; static void gtk_label_class_init (GtkLabelClass *klass); @@ -57,13 +62,11 @@ static void gtk_label_init (GtkLabel *label); static void gtk_label_set_property (GObject *object, guint prop_id, const GValue *value, - GParamSpec *pspec, - const gchar *trailer); + GParamSpec *pspec); static void gtk_label_get_property (GObject *object, guint prop_id, GValue *value, - GParamSpec *pspec, - const gchar *trailer); + GParamSpec *pspec); static void gtk_label_finalize (GObject *object); static void gtk_label_size_request (GtkWidget *widget, GtkRequisition *requisition); @@ -106,6 +109,7 @@ static void set_markup (GtkLabel *label, const gchar *str, gboolean with_uline); static void gtk_label_recalculate (GtkLabel *label); +static void gtk_label_hierarchy_changed (GtkWidget *widget); static void gtk_label_create_window (GtkLabel *label); static void gtk_label_destroy_window (GtkLabel *label); @@ -117,6 +121,9 @@ static void gtk_label_select_region_index (GtkLabel *label, gint anchor_index, gint end_index); +static gboolean gtk_label_activate_mnemonic (GtkWidget *widget, + gboolean group_cycling); + static GtkMiscClass *parent_class = NULL; @@ -175,6 +182,8 @@ gtk_label_class_init (GtkLabelClass *class) widget_class->button_press_event = gtk_label_button_press; widget_class->button_release_event = gtk_label_button_release; widget_class->motion_notify_event = gtk_label_motion; + widget_class->hierarchy_changed = gtk_label_hierarchy_changed; + widget_class->activate_mnemonic = gtk_label_activate_mnemonic; g_object_class_install_property (G_OBJECT_CLASS(object_class), PROP_LABEL, @@ -201,7 +210,7 @@ gtk_label_class_init (GtkLabelClass *class) PROP_USE_UNDERLINE, g_param_spec_boolean ("use_underline", _("Use underline"), - _("If set, an underline in the text indicates the next character should be used for the accelerator key"), + _("If set, an underline in the text indicates the next character should be used for the mnemonic accelerator key"), FALSE, G_PARAM_READWRITE)); @@ -237,22 +246,22 @@ gtk_label_class_init (GtkLabelClass *class) FALSE, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, - PROP_ACCEL_KEYVAL, - g_param_spec_uint ("accel_keyval", - _("Accelerator key value"), - _("The accelerator key for this label."), + PROP_MNEMONIC_KEYVAL, + g_param_spec_uint ("mnemonic_keyval", + _("Mnemonic accelerator key value"), + _("The mnemonic accelerator key for this label."), 0, G_MAXUINT, GDK_VoidSymbol, G_PARAM_READABLE)); + } static void gtk_label_set_property (GObject *object, guint prop_id, const GValue *value, - GParamSpec *pspec, - const gchar *trailer) + GParamSpec *pspec) { GtkLabel *label; @@ -298,8 +307,7 @@ static void gtk_label_get_property (GObject *object, guint prop_id, GValue *value, - GParamSpec *pspec, - const gchar *trailer) + GParamSpec *pspec) { GtkLabel *label; @@ -328,8 +336,8 @@ gtk_label_get_property (GObject *object, case PROP_SELECTABLE: g_value_set_boolean (value, gtk_label_get_selectable (label)); break; - case PROP_ACCEL_KEYVAL: - g_value_set_uint (value, label->accel_keyval); + case PROP_MNEMONIC_KEYVAL: + g_value_set_uint (value, label->mnemonic_keyval); break; default: @@ -351,14 +359,24 @@ gtk_label_init (GtkLabel *label) label->use_underline = FALSE; label->use_markup = FALSE; - label->accel_keyval = GDK_VoidSymbol; + label->mnemonic_keyval = GDK_VoidSymbol; label->layout = NULL; label->text = NULL; label->attrs = NULL; + + label->mnemonic_widget = NULL; + label->mnemonic_window = NULL; gtk_label_set_text (label, ""); } +/** + * gtk_label_new: + * @str: The text of the label + * @returns: a new #GtkLabel + * + * Creates a new #GtkLabel, containing the text in @str. + **/ GtkWidget* gtk_label_new (const gchar *str) { @@ -373,20 +391,138 @@ gtk_label_new (const gchar *str) } /** - * gtk_label_get_accel_keyval: + * gtk_label_new_with_mnemonic: + * @str: The text of the label, with an underscore in front of the + * mnemonic character + * @returns: a new #GtkLabel + * + * Creates a new #GtkLabel, containing the text in @str. + * + * If characters in @str are preceded by an underscore, they are underlined + * indicating that they represent a keyboard accelerator called a mnemonic. + * The mnemonic key can be used to activate another widget, chosen automatically, + * or explicitly using @gtk_label_set_mnemonic_widget. + **/ +GtkWidget* +gtk_label_new_with_mnemonic (const gchar *str) +{ + GtkLabel *label; + + label = gtk_type_new (GTK_TYPE_LABEL); + + if (str && *str) + gtk_label_set_text_with_mnemonic (label, str); + + return GTK_WIDGET (label); +} + +static gboolean +gtk_label_activate_mnemonic (GtkWidget *widget, + gboolean group_cycling) +{ + GtkWidget *parent; + + if (GTK_LABEL (widget)->mnemonic_widget) + return gtk_widget_activate_mnemonic (GTK_LABEL (widget)->mnemonic_widget, group_cycling); + + /* Try to find the widget to activate by traversing the widget + * hierarachy. + */ + + parent = widget->parent; + while (parent) + { + if (GTK_WIDGET_CAN_FOCUS (parent) || + (!group_cycling && GTK_WIDGET_GET_CLASS (parent)->activate_signal) || + (parent->parent && GTK_IS_NOTEBOOK (parent->parent)) || + (GTK_IS_MENU_ITEM (parent))) + return gtk_widget_activate_mnemonic (parent, group_cycling); + parent = parent->parent; + } + + g_warning ("Couldn't find a target for a mnemonic activation."); + gdk_beep (); + + return FALSE; +} + +static void +gtk_label_setup_mnemonic (GtkLabel *label, guint last_key) +{ + GtkWidget *toplevel; + + if ((last_key != GDK_VoidSymbol) && label->mnemonic_window) + gtk_window_remove_mnemonic (label->mnemonic_window, + last_key, + GTK_WIDGET (label)); + + if (label->mnemonic_keyval == GDK_VoidSymbol) + return; + + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (label)); + + if (GTK_IS_WINDOW (toplevel)) + { + gtk_window_add_mnemonic (GTK_WINDOW (toplevel), + label->mnemonic_keyval, + GTK_WIDGET (label)); + label->mnemonic_window = GTK_WINDOW (toplevel); + } +} + +static void +gtk_label_hierarchy_changed (GtkWidget *widget) +{ + GtkLabel *label = GTK_LABEL (widget); + + gtk_label_setup_mnemonic (label, label->mnemonic_keyval); +} + + +/** + * gtk_label_set_mnemonic_widget: + * @label: a #GtkLabel + * @widget: the target #GtkWidget + * + * If the label has been set so that it has an mnemonic key (using i.e. + * @gtk_label_set_markup_with_mnemonic, @gtk_label_set_text_with_mnemonic, + * @gtk_label_new_with_mnemonic or the use_underline property) the label can be + * associated with a widget that is the target of the mnemonic. When the label + * is inside a widget (like a #GtkButton or a #GtkNotebook tab) it is automatically + * associated with the correct widget, but sometimes (i.e. when the target is + * a #GtkEntry next to the label) you need to set it explicitly using this + * function. + * + * The target widget will be accelerated by emitting "activate_mnemonic" on it. + * The default handler for this signal will activate the widget if there are no + * mnemonic collisions and toggle focus between the colliding widgets otherwise. + **/ +void +gtk_label_set_mnemonic_widget (GtkLabel *label, + GtkWidget *widget) +{ + g_return_if_fail (GTK_IS_LABEL (label)); + g_return_if_fail (GTK_IS_WIDGET (widget)); + + label->mnemonic_widget = widget; +} + + +/** + * gtk_label_get_mnemonic_keyval: * @label: a #GtkLabel * @Returns: GDK keyval usable for accelerators, or GDK_VoidSymbol * - * If the label text was set using gtk_label_set_markup_with_accel, - * gtk_label_parse_uline, or using the use_underline property this function - * returns the keyval for the first underlined accelerator. + * If the label has been set so that it has an mnemonic key this function + * returns the keyval used for the mnemonic accelerator. If there is no + * mnemonic set up it returns #GDK_VoidSymbol. **/ guint -gtk_label_get_accel_keyval (GtkLabel *label) +gtk_label_get_mnemonic_keyval (GtkLabel *label) { g_return_val_if_fail (GTK_IS_LABEL (label), GDK_VoidSymbol); - return label->accel_keyval; + return label->mnemonic_keyval; } static void @@ -446,7 +582,7 @@ gtk_label_set_attributes_internal (GtkLabel *label, } -/* Calculates text, attrs and accel_keyval from +/* Calculates text, attrs and mnemonic_keyval from * label, use_underline and use_markup */ static void gtk_label_recalculate (GtkLabel *label) @@ -465,12 +601,25 @@ gtk_label_recalculate (GtkLabel *label) } if (!label->use_underline) - label->accel_keyval = GDK_VoidSymbol; + { + guint keyval = label->mnemonic_keyval; + label->mnemonic_keyval = GDK_VoidSymbol; + gtk_label_setup_mnemonic (label, keyval); + } gtk_label_clear_layout (label); gtk_widget_queue_resize (GTK_WIDGET (label)); } +/** + * gtk_label_set_text: + * label: a #GtkLabel + * str: a string + * + * Sets the text of the label to @str. + * + * This will also clear any previously set mnemonic accelerators. + **/ void gtk_label_set_text (GtkLabel *label, const gchar *str) @@ -538,9 +687,9 @@ set_markup (GtkLabel *label, } if (accel_char != 0) - label->accel_keyval = gdk_keyval_to_lower (gdk_unicode_to_keyval (accel_char)); + label->mnemonic_keyval = gdk_keyval_to_lower (gdk_unicode_to_keyval (accel_char)); else - label->accel_keyval = GDK_VoidSymbol; + label->mnemonic_keyval = GDK_VoidSymbol; } /** @@ -565,31 +714,32 @@ gtk_label_set_markup (GtkLabel *label, } /** - * gtk_label_set_markup_with_accel: + * gtk_label_set_markup_with_mnemonic: * @label: a #GtkLabel * @str: a markup string (see <link linkend="PangoMarkupFormat">Pango markup format</link>) * * Parses @str which is marked up with the Pango text markup language, * setting the label's text and attribute list based on the parse results. * If characters in @str are preceded by an underscore, they are underlined - * indicating that they represent a keyboard accelerator, and the GDK - * keyval for the first underlined accelerator is returned. If there are - * no underlines in the text, GDK_VoidSymbol will be returned. + * indicating that they represent a keyboard accelerator called a mnemonic. * - * Return value: GDK keyval for accelerator + * The mnemonic key can be used to activate another widget, chosen automatically, + * or explicitly using @gtk_label_set_mnemonic_widget. **/ -guint -gtk_label_set_markup_with_accel (GtkLabel *label, - const gchar *str) +void +gtk_label_set_markup_with_mnemonic (GtkLabel *label, + const gchar *str) { - g_return_val_if_fail (GTK_IS_LABEL (label), GDK_VoidSymbol); + guint last_keyval; + g_return_if_fail (GTK_IS_LABEL (label)); + last_keyval = label->mnemonic_keyval; gtk_label_set_label_internal (label, g_strdup (str ? str : "")); gtk_label_set_use_markup_internal (label, TRUE); gtk_label_set_use_underline_internal (label, TRUE); gtk_label_recalculate (label); - return label->accel_keyval; + gtk_label_setup_mnemonic (label, last_keyval); } /** @@ -741,7 +891,7 @@ gtk_label_finalize (GObject *object) pango_attr_list_unref (label->attrs); g_free (label->select_info); - + G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -1207,22 +1357,64 @@ gtk_label_set_uline_text_internal (GtkLabel *label, g_free (pattern); - label->accel_keyval = accel_key; + label->mnemonic_keyval = accel_key; } guint gtk_label_parse_uline (GtkLabel *label, const gchar *str) { + guint keyval; + guint orig_keyval; + g_return_val_if_fail (GTK_IS_LABEL (label), GDK_VoidSymbol); g_return_val_if_fail (str != NULL, GDK_VoidSymbol); + orig_keyval = label->mnemonic_keyval; + gtk_label_set_label_internal (label, g_strdup (str ? str : "")); gtk_label_set_use_markup_internal (label, FALSE); gtk_label_set_use_underline_internal (label, TRUE); gtk_label_recalculate (label); - return label->accel_keyval; + + keyval = label->mnemonic_keyval; + label->mnemonic_keyval = GDK_VoidSymbol; + + gtk_label_setup_mnemonic (label, orig_keyval); + + return keyval; +} + +/** + * gtk_label_set_text_with_mnemonic: + * @label: a #GtkLabel + * @str: a string + * + * Sets the label's text from the string @str. + * If characters in @str are preceded by an underscore, they are underlined + * indicating that they represent a keyboard accelerator called a mnemonic. + * The mnemonic key can be used to activate another widget, chosen automatically, + * or explicitly using @gtk_label_set_mnemonic_widget. + **/ +void +gtk_label_set_text_with_mnemonic (GtkLabel *label, + const gchar *str) +{ + guint last_keyval; + + g_return_if_fail (GTK_IS_LABEL (label)); + g_return_if_fail (str != NULL); + + last_keyval = label->mnemonic_keyval; + + gtk_label_set_label_internal (label, g_strdup (str ? str : "")); + gtk_label_set_use_markup_internal (label, FALSE); + gtk_label_set_use_underline_internal (label, TRUE); + + gtk_label_recalculate (label); + + gtk_label_setup_mnemonic (label, last_keyval); } @@ -1659,8 +1851,6 @@ gtk_label_select_region (GtkLabel *label, if (label->text && label->select_info) { - GtkClipboard *clipboard; - if (start_offset < 0) start_offset = 0; diff --git a/gtk/gtklabel.h b/gtk/gtklabel.h index d9bca17405..49a1626e2a 100644 --- a/gtk/gtklabel.h +++ b/gtk/gtklabel.h @@ -29,6 +29,7 @@ #include <gdk/gdk.h> #include <gtk/gtkmisc.h> +#include <gtk/gtkwindow.h> #ifdef __cplusplus @@ -60,13 +61,16 @@ struct _GtkLabel guint use_underline : 1; guint use_markup : 1; - guint accel_keyval; + guint mnemonic_keyval; gchar *text; PangoAttrList *attrs; PangoLayout *layout; + GtkWidget *mnemonic_widget; + GtkWindow *mnemonic_window; + GtkLabelSelectionInfo *select_info; }; @@ -75,8 +79,9 @@ struct _GtkLabelClass GtkMiscClass parent_class; }; -GtkType gtk_label_get_type (void) G_GNUC_CONST; -GtkWidget *gtk_label_new (const char *str); +GtkType gtk_label_get_type (void) G_GNUC_CONST; +GtkWidget *gtk_label_new (const char *str); +GtkWidget *gtk_label_new_with_mnemonic (const char *str); void gtk_label_set_text (GtkLabel *label, const char *str); @@ -87,22 +92,26 @@ void gtk_label_set_attributes (GtkLabel *label, void gtk_label_set_markup (GtkLabel *label, const gchar *str); -guint gtk_label_set_markup_with_accel (GtkLabel *label, - const gchar *str); - -guint gtk_label_get_accel_keyval (GtkLabel *label); -void gtk_label_set_justify (GtkLabel *label, - GtkJustification jtype); -void gtk_label_set_pattern (GtkLabel *label, - const gchar *pattern); -void gtk_label_set_line_wrap (GtkLabel *label, - gboolean wrap); +void gtk_label_set_markup_with_mnemonic (GtkLabel *label, + const gchar *str); + +guint gtk_label_get_mnemonic_keyval (GtkLabel *label); +void gtk_label_set_justify (GtkLabel *label, + GtkJustification jtype); +void gtk_label_set_pattern (GtkLabel *label, + const gchar *pattern); +void gtk_label_set_line_wrap (GtkLabel *label, + gboolean wrap); /* Convenience function to set the name and pattern by parsing * a string with embedded underscores, and return the appropriate * key symbol for the accelerator. */ -guint gtk_label_parse_uline (GtkLabel *label, - const gchar *string); +guint gtk_label_parse_uline (GtkLabel *label, + const gchar *string); +void gtk_label_set_text_with_mnemonic (GtkLabel *label, + const gchar *string); +void gtk_label_set_mnemonic_widget (GtkLabel *label, + GtkWidget *widget); void gtk_label_set_selectable (GtkLabel *label, gboolean setting); diff --git a/gtk/gtkmarshal.list b/gtk/gtkmarshal.list index cb14bde099..f193de669e 100644 --- a/gtk/gtkmarshal.list +++ b/gtk/gtkmarshal.list @@ -25,6 +25,7 @@ BOOLEAN:BOXED BOOLEAN:OBJECT,INT,INT,UINT BOOLEAN:OBJECT,STRING,STRING,BOXED BOOLEAN:VOID +BOOLEAN:BOOLEAN ENUM:ENUM INT:OBJECT,BOXED,BOXED INT:POINTER diff --git a/gtk/gtkmarshalers.list b/gtk/gtkmarshalers.list index cb14bde099..f193de669e 100644 --- a/gtk/gtkmarshalers.list +++ b/gtk/gtkmarshalers.list @@ -25,6 +25,7 @@ BOOLEAN:BOXED BOOLEAN:OBJECT,INT,INT,UINT BOOLEAN:OBJECT,STRING,STRING,BOXED BOOLEAN:VOID +BOOLEAN:BOOLEAN ENUM:ENUM INT:OBJECT,BOXED,BOXED INT:POINTER diff --git a/gtk/gtkmenu.c b/gtk/gtkmenu.c index 0a6d22fa7e..4d26c93c68 100644 --- a/gtk/gtkmenu.c +++ b/gtk/gtkmenu.c @@ -116,7 +116,6 @@ static void gtk_menu_remove (GtkContainer *menu, static GtkMenuShellClass *parent_class = NULL; static const gchar *attach_data_key = "gtk-menu-attach-data"; -static GQuark quark_uline_accel_group = 0; GtkType gtk_menu_get_type (void) @@ -248,6 +247,7 @@ gtk_menu_init (GtkMenu *menu) NULL); gtk_window_set_policy (GTK_WINDOW (menu->toplevel), FALSE, FALSE, TRUE); + gtk_window_set_mnemonic_modifier (GTK_WINDOW (menu->toplevel), 0); /* Refloat the menu, so that reference counting for the menu isn't * affected by it being a child of the toplevel @@ -739,38 +739,6 @@ gtk_menu_get_accel_group (GtkMenu *menu) return menu->accel_group; } -GtkAccelGroup* -gtk_menu_ensure_uline_accel_group (GtkMenu *menu) -{ - GtkAccelGroup *accel_group; - - g_return_val_if_fail (GTK_IS_MENU (menu), NULL); - - if (!quark_uline_accel_group) - quark_uline_accel_group = g_quark_from_static_string ("GtkMenu-uline-accel-group"); - - accel_group = gtk_object_get_data_by_id (GTK_OBJECT (menu), quark_uline_accel_group); - if (!accel_group) - { - accel_group = gtk_accel_group_new (); - gtk_accel_group_attach (accel_group, GTK_OBJECT (menu)); - gtk_object_set_data_by_id_full (GTK_OBJECT (menu), - quark_uline_accel_group, - accel_group, - (GtkDestroyNotify) gtk_accel_group_unref); - } - - return accel_group; -} - -GtkAccelGroup* -gtk_menu_get_uline_accel_group (GtkMenu *menu) -{ - g_return_val_if_fail (GTK_IS_MENU (menu), NULL); - - return gtk_object_get_data_by_id (GTK_OBJECT (menu), quark_uline_accel_group); -} - void gtk_menu_reposition (GtkMenu *menu) { @@ -844,6 +812,7 @@ gtk_menu_set_tearoff_state (GtkMenu *menu, NULL); gtk_window_set_type_hint (GTK_WINDOW (menu->tearoff_window), GDK_WINDOW_TYPE_HINT_MENU); + gtk_window_set_mnemonic_modifier (GTK_WINDOW (menu->tearoff_window), 0); gtk_widget_set_app_paintable (menu->tearoff_window, TRUE); gtk_signal_connect (GTK_OBJECT (menu->tearoff_window), "event", @@ -1420,7 +1389,6 @@ gtk_menu_key_press (GtkWidget *widget, (delete || (gtk_accelerator_valid (event->keyval, event->state) && (event->state || - !gtk_menu_get_uline_accel_group (GTK_MENU (menu_shell)) || (event->keyval >= GDK_F1 && event->keyval <= GDK_F35))))) { GtkMenuItem *menu_item; diff --git a/gtk/gtkmenu.h b/gtk/gtkmenu.h index 1d478c30dd..30c63824b8 100644 --- a/gtk/gtkmenu.h +++ b/gtk/gtkmenu.h @@ -147,12 +147,6 @@ void gtk_menu_set_accel_group (GtkMenu *menu, GtkAccelGroup *accel_group); GtkAccelGroup* gtk_menu_get_accel_group (GtkMenu *menu); -/* get the accelerator group that is used internally by the menu for - * underline accelerators while the menu is popped up. - */ -GtkAccelGroup* gtk_menu_get_uline_accel_group (GtkMenu *menu); -GtkAccelGroup* gtk_menu_ensure_uline_accel_group (GtkMenu *menu); - /* A reference count is kept for a widget when it is attached to * a particular widget. This is typically a menu item; it may also diff --git a/gtk/gtkmenuitem.c b/gtk/gtkmenuitem.c index ada859ffb1..d728377ba3 100644 --- a/gtk/gtkmenuitem.c +++ b/gtk/gtkmenuitem.c @@ -67,6 +67,8 @@ static void gtk_real_menu_item_toggle_size_request (GtkMenuItem *menu_item, gint *requisition); static void gtk_real_menu_item_toggle_size_allocate (GtkMenuItem *menu_item, gint allocation); +static gboolean gtk_menu_item_activate_mnemonic (GtkWidget *widget, + gboolean group_cycling); static gint gtk_menu_item_select_timeout (gpointer data); static void gtk_menu_item_popup_submenu (gpointer data); @@ -82,6 +84,7 @@ static void gtk_menu_item_forall (GtkContainer *container, GtkCallback callback, gpointer callback_data); + static GtkItemClass *parent_class; static guint menu_item_signals[LAST_SIGNAL] = { 0 }; static guint32 last_submenu_deselect_time = 0; @@ -137,6 +140,7 @@ gtk_menu_item_class_init (GtkMenuItemClass *klass) widget_class->expose_event = gtk_menu_item_expose; widget_class->show_all = gtk_menu_item_show_all; widget_class->hide_all = gtk_menu_item_hide_all; + widget_class->activate_mnemonic = gtk_menu_item_activate_mnemonic; container_class->forall = gtk_menu_item_forall; @@ -601,6 +605,25 @@ gtk_real_menu_item_deselect (GtkItem *item) gtk_widget_draw (GTK_WIDGET (menu_item), NULL); } +static gboolean +gtk_menu_item_activate_mnemonic (GtkWidget *widget, + gboolean group_cycling) +{ + if (group_cycling) + { + if (widget->parent && + GTK_IS_MENU_SHELL (widget->parent)) + gtk_menu_shell_select_item (GTK_MENU_SHELL (widget->parent), + widget); + + } + else + gtk_signal_emit (GTK_OBJECT (widget), menu_item_signals[ACTIVATE_ITEM]); + + return TRUE; +} + + static void gtk_real_menu_item_activate_item (GtkMenuItem *menu_item) { diff --git a/gtk/gtkmenushell.c b/gtk/gtkmenushell.c index a59acf4a28..cdd00988c3 100644 --- a/gtk/gtkmenushell.c +++ b/gtk/gtkmenushell.c @@ -31,7 +31,7 @@ #include "gtktearoffmenuitem.h" /* FIXME */ #include "gtkmenushell.h" #include "gtksignal.h" - +#include "gtkwindow.h" #define MENU_SHELL_TIMEOUT 500 @@ -551,6 +551,7 @@ gtk_menu_shell_key_press (GtkWidget *widget, GdkEventKey *event) { GtkMenuShell *menu_shell; + GtkWidget *toplevel; g_return_val_if_fail (widget != NULL, FALSE); g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE); @@ -566,6 +567,13 @@ gtk_menu_shell_key_press (GtkWidget *widget, event->state)) return TRUE; + toplevel = gtk_widget_get_toplevel (widget); + if (GTK_IS_WINDOW (toplevel) && + gtk_window_activate_mnemonic (GTK_WINDOW (toplevel), + event->keyval, + event->state)) + return TRUE; + if (gtk_accel_groups_activate (GTK_OBJECT (widget), event->keyval, event->state)) return TRUE; diff --git a/gtk/gtknotebook.c b/gtk/gtknotebook.c index 997f9cb2f6..390538de91 100644 --- a/gtk/gtknotebook.c +++ b/gtk/gtknotebook.c @@ -94,6 +94,8 @@ struct _GtkNotebookPage GtkRequisition requisition; GtkAllocation allocation; + + guint activate_mnemonic_signal; }; #ifdef G_DISABLE_CHECKS @@ -1972,6 +1974,10 @@ gtk_notebook_real_remove (GtkNotebook *notebook, if (GTK_WIDGET_VISIBLE (page->child) && GTK_WIDGET_VISIBLE (notebook)) need_resize = TRUE; + if (page->tab_label && page->activate_mnemonic_signal) + gtk_signal_disconnect (page->tab_label, + page->activate_mnemonic_signal); + gtk_widget_unparent (page->child); if (page->tab_label) @@ -3618,6 +3624,35 @@ gtk_notebook_insert_page (GtkNotebook *notebook, gtk_notebook_insert_page_menu (notebook, child, tab_label, NULL, position); } + +static gint +gtk_notebook_page_compare_tab (gconstpointer a, + gconstpointer b) +{ + return (((GtkNotebookPage *) a)->tab_label != b); +} + +static gboolean +gtk_notebook_activate_mnemonic_switch_page (GtkWidget *child, + gboolean overload, + gpointer data) +{ + GtkNotebook *notebook = GTK_NOTEBOOK (data); + GtkNotebookPage *page; + GList *list; + + list = g_list_find_custom (notebook->children, child, + gtk_notebook_page_compare_tab); + if (!list) + return TRUE; + + + page = list->data; + + gtk_notebook_switch_page (notebook, page, -1); + return TRUE; +} + /** * gtk_notebook_insert_page_menu: * @notebook: a #GtkNotebook @@ -3661,6 +3696,7 @@ gtk_notebook_insert_page_menu (GtkNotebook *notebook, page->allocation.height = 0; page->default_menu = FALSE; page->default_tab = FALSE; + page->activate_mnemonic_signal = 0; nchildren = g_list_length (notebook->children); if ((position < 0) || (position > nchildren)) @@ -3741,6 +3777,13 @@ gtk_notebook_insert_page_menu (GtkNotebook *notebook, gtk_widget_hide (tab_label); } } + + if (tab_label) + page->activate_mnemonic_signal = + gtk_signal_connect (GTK_OBJECT (tab_label), + "activate_mnemonic", + (GtkSignalFunc) gtk_notebook_activate_mnemonic_switch_page, + notebook); } /** diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index 7492e8cf82..c63ff4a590 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -62,6 +62,7 @@ enum { DIRECTION_CHANGED, ADD_ACCELERATOR, REMOVE_ACCELERATOR, + ACTIVATE_MNEMONIC, GRAB_FOCUS, EVENT, BUTTON_PRESS_EVENT, @@ -188,6 +189,8 @@ static gint gtk_widget_event_internal (GtkWidget *widget, static void gtk_widget_propagate_hierarchy_changed (GtkWidget *widget, gpointer client_data); +static gboolean gtk_widget_real_activate_mnemonic (GtkWidget *widget, + gboolean group_cycling); static GtkWidgetAuxInfo* gtk_widget_aux_info_new (void); static void gtk_widget_aux_info_destroy (GtkWidgetAuxInfo *aux_info); @@ -290,6 +293,7 @@ gtk_widget_class_init (GtkWidgetClass *klass) klass->direction_changed = gtk_widget_direction_changed; klass->add_accelerator = (void*) gtk_accel_group_handle_add; klass->remove_accelerator = (void*) gtk_accel_group_handle_remove; + klass->activate_mnemonic = gtk_widget_real_activate_mnemonic; klass->grab_focus = gtk_widget_real_grab_focus; klass->event = NULL; klass->button_press_event = NULL; @@ -460,6 +464,14 @@ gtk_widget_class_init (GtkWidgetClass *klass) widget_signals[REMOVE_ACCELERATOR] = gtk_accel_group_create_remove (GTK_CLASS_TYPE (object_class), GTK_RUN_LAST, GTK_SIGNAL_OFFSET (GtkWidgetClass, remove_accelerator)); + widget_signals[ACTIVATE_MNEMONIC] = + gtk_signal_new ("activate_mnemonic", + GTK_RUN_LAST, + GTK_CLASS_TYPE (object_class), + GTK_SIGNAL_OFFSET (GtkWidgetClass, activate_mnemonic), + gtk_marshal_BOOLEAN__BOOLEAN, + GTK_TYPE_BOOL, 1, + GTK_TYPE_BOOL); widget_signals[GRAB_FOCUS] = gtk_signal_new ("grab_focus", GTK_RUN_LAST | GTK_RUN_ACTION, @@ -2175,6 +2187,36 @@ gtk_widget_accelerator_signal (GtkWidget *widget, return 0; } +gboolean +gtk_widget_activate_mnemonic (GtkWidget *widget, + gboolean group_cycling) +{ + gboolean handled = FALSE; + + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + + if (!GTK_WIDGET_IS_SENSITIVE (widget)) + return TRUE; + + gtk_signal_emit_by_name (GTK_OBJECT (widget), + "activate_mnemonic", + group_cycling, + &handled); + return handled; +} + +static gboolean +gtk_widget_real_activate_mnemonic (GtkWidget *widget, + gboolean group_cycling) +{ + if (group_cycling) + gtk_widget_grab_focus (widget); + else if (!group_cycling) + gtk_widget_activate (widget); + return TRUE; +} + + static gint gtk_widget_real_key_press_event (GtkWidget *widget, GdkEventKey *event) diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h index 3b475e410e..dbad8c98e8 100644 --- a/gtk/gtkwidget.h +++ b/gtk/gtkwidget.h @@ -268,6 +268,10 @@ struct _GtkWidgetClass guint accel_key, GdkModifierType accel_mods); + /* Mnemonics */ + gboolean (* activate_mnemonic) (GtkWidget *widget, + gboolean group_cycling); + /* explicit focus */ void (* grab_focus) (GtkWidget *widget); @@ -464,6 +468,8 @@ guint gtk_widget_accelerator_signal (GtkWidget *widget, void gtk_widget_lock_accelerators (GtkWidget *widget); void gtk_widget_unlock_accelerators (GtkWidget *widget); gboolean gtk_widget_accelerators_locked (GtkWidget *widget); +gboolean gtk_widget_activate_mnemonic (GtkWidget *widget, + gboolean group_cycling); gboolean gtk_widget_event (GtkWidget *widget, GdkEvent *event); gint gtk_widget_send_expose (GtkWidget *widget, diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index 989e7b9130..62067deacb 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -99,6 +99,14 @@ typedef struct { GtkWindowLastGeometryInfo last; } GtkWindowGeometryInfo; +typedef struct { + GtkWindow *window; + guint keyval; + + GSList *targets; +} GtkWindowMnemonic; + + static void gtk_window_class_init (GtkWindowClass *klass); static void gtk_window_init (GtkWindow *window); static void gtk_window_shutdown (GObject *object); @@ -182,6 +190,7 @@ static void gtk_window_geometry_destroy (GtkWindowGeometryInfo *info); static GSList *toplevel_list = NULL; +static GHashTable *mnemonic_hash_table = NULL; static GtkBinClass *parent_class = NULL; static guint window_signals[LAST_SIGNAL] = { 0 }; @@ -194,6 +203,36 @@ static void gtk_window_get_property (GObject *object, GValue *value, GParamSpec *pspec); + +static guint +mnemonic_hash (gconstpointer key) +{ + const GtkWindowMnemonic *k; + guint h; + + k = (GtkWindowMnemonic *)key; + + h = (gulong) k->window; + h ^= k->keyval << 16; + h ^= k->keyval >> 16; + + return h; +} + +static gboolean +mnemonic_equal (gconstpointer a, gconstpointer b) +{ + const GtkWindowMnemonic *ka; + const GtkWindowMnemonic *kb; + + ka = (GtkWindowMnemonic *)a; + kb = (GtkWindowMnemonic *)b; + + return + (ka->window == kb->window) && + (ka->keyval == kb->keyval); +} + GtkType gtk_window_get_type (void) { @@ -373,6 +412,11 @@ gtk_window_class_init (GtkWindowClass *klass) gtk_marshal_BOOLEAN__BOXED, GTK_TYPE_BOOL, 1, GTK_TYPE_GDK_EVENT); + + + if (!mnemonic_hash_table) + mnemonic_hash_table = g_hash_table_new (mnemonic_hash, + mnemonic_equal); } static void @@ -407,6 +451,7 @@ gtk_window_init (GtkWindow *window) window->frame_bottom = 0; window->type_hint = GDK_WINDOW_TYPE_HINT_NORMAL; window->decorated = TRUE; + window->mnemonic_modifier = GDK_MOD1_MASK; gtk_widget_ref (GTK_WIDGET (window)); gtk_object_sink (GTK_OBJECT (window)); @@ -801,6 +846,132 @@ gtk_window_get_default_accel_group (GtkWindow *window) } void +gtk_window_add_mnemonic (GtkWindow *window, + guint keyval, + GtkWidget *target) +{ + GtkWindowMnemonic key; + GtkWindowMnemonic *mnemonic; + + g_return_if_fail (window != NULL); + g_return_if_fail (GTK_IS_WINDOW (window)); + g_return_if_fail (GTK_IS_WIDGET (target)); + + key.window = window; + key.keyval = keyval; + + mnemonic = g_hash_table_lookup (mnemonic_hash_table, &key); + + if (mnemonic) + mnemonic->targets = g_slist_prepend (mnemonic->targets, target); + else + { + mnemonic = g_new (GtkWindowMnemonic, 1); + *mnemonic = key; + mnemonic->targets = g_slist_prepend (NULL, target); + g_hash_table_insert (mnemonic_hash_table, mnemonic, mnemonic); + } +} + +void +gtk_window_remove_mnemonic (GtkWindow *window, + guint keyval, + GtkWidget *target) +{ + GtkWindowMnemonic key; + GtkWindowMnemonic *mnemonic; + + g_return_if_fail (window != NULL); + g_return_if_fail (GTK_IS_WINDOW (window)); + g_return_if_fail (GTK_IS_WIDGET (target)); + + key.window = window; + key.keyval = keyval; + + mnemonic = g_hash_table_lookup (mnemonic_hash_table, &key); + + g_assert (mnemonic); + + if (mnemonic) + { + mnemonic->targets = g_slist_remove (mnemonic->targets, target); + + if (mnemonic->targets == NULL) + { + g_hash_table_remove (mnemonic_hash_table, mnemonic); + g_free (mnemonic); + } + } +} + +gboolean +gtk_window_activate_mnemonic (GtkWindow *window, + guint keyval, + GdkModifierType modifier) +{ + GtkWindowMnemonic key; + GtkWindowMnemonic *mnemonic; + GSList *list; + GtkWidget *widget, *chosen_widget; + gboolean overloaded; + + g_return_val_if_fail (window != NULL, FALSE); + g_return_val_if_fail (GTK_IS_WINDOW (window), FALSE); + + if (modifier != window->mnemonic_modifier) + return FALSE; + + key.window = window; + key.keyval = keyval; + + mnemonic = g_hash_table_lookup (mnemonic_hash_table, &key); + + if (!mnemonic) + return FALSE; + + overloaded = FALSE; + chosen_widget = NULL; + list = mnemonic->targets; + while (list) + { + widget = GTK_WIDGET (list->data); + + if (GTK_WIDGET_IS_SENSITIVE (widget) && + GTK_WIDGET_MAPPED (widget)) + { + if (chosen_widget) + { + overloaded = TRUE; + break; + } + else + chosen_widget = widget; + } + list = g_slist_next (list); + } + + if (chosen_widget) + { + /* For round robin we put the activated entry on + * the end of the list after activation */ + mnemonic->targets = g_slist_remove (mnemonic->targets, chosen_widget); + mnemonic->targets = g_slist_append (mnemonic->targets, chosen_widget); + return gtk_widget_activate_mnemonic (chosen_widget, overloaded); + } + return FALSE; +} + +void +gtk_window_set_mnemonic_modifier (GtkWindow *window, + GdkModifierType modifier) +{ + g_return_if_fail (window != NULL); + g_return_if_fail (GTK_IS_WINDOW (window)); + + window->mnemonic_modifier = modifier; +} + +void gtk_window_set_position (GtkWindow *window, GtkWindowPosition position) { @@ -1358,6 +1529,24 @@ gtk_window_destroy (GtkObject *object) GTK_OBJECT_CLASS (parent_class)->destroy (object); } +static gboolean +gtk_window_mnemonic_hash_remove (gpointer key, + gpointer value, + gpointer user) +{ + GtkWindowMnemonic *mnemonic = key; + GtkWindow *window = user; + + if (mnemonic->window == window) + { + g_slist_free (mnemonic->targets); + g_free (mnemonic); + + return TRUE; + } + return FALSE; +} + static void gtk_window_finalize (GObject *object) { @@ -1373,6 +1562,10 @@ gtk_window_finalize (GObject *object) g_free (window->wmclass_name); g_free (window->wmclass_class); + g_hash_table_foreach_remove (mnemonic_hash_table, + gtk_window_mnemonic_hash_remove, + window); + G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -1877,7 +2070,12 @@ gtk_window_key_press_event (GtkWidget *widget, { handled = gtk_widget_event (window->focus_widget, (GdkEvent*) event); } - + + if (!handled) + handled = gtk_window_activate_mnemonic (window, + event->keyval, + event->state); + if (!handled) handled = gtk_accel_groups_activate (GTK_OBJECT (window), event->keyval, event->state); diff --git a/gtk/gtkwindow.h b/gtk/gtkwindow.h index e49ec209e2..0de5aa5e6f 100644 --- a/gtk/gtkwindow.h +++ b/gtk/gtkwindow.h @@ -99,6 +99,8 @@ struct _GtkWindow guint frame_top; guint frame_right; guint frame_bottom; + + GdkModifierType mnemonic_modifier; }; struct _GtkWindowClass @@ -175,6 +177,19 @@ GList* gtk_window_list_toplevels (void); /* Get the "built-in" accel group (convenience thing) */ GtkAccelGroup* gtk_window_get_default_accel_group (GtkWindow *window); +void gtk_window_add_mnemonic (GtkWindow *window, + guint keyval, + GtkWidget *target); +void gtk_window_remove_mnemonic (GtkWindow *window, + guint keyval, + GtkWidget *target); +gboolean gtk_window_activate_mnemonic (GtkWindow *window, + guint keyval, + GdkModifierType modifier); +void gtk_window_set_mnemonic_modifier (GtkWindow *window, + GdkModifierType modifier); + + void gtk_window_present (GtkWindow *window); void gtk_window_iconify (GtkWindow *window); void gtk_window_deiconify (GtkWindow *window); diff --git a/gtk/testgtk.c b/gtk/testgtk.c index b5b9c36909..664a60f437 100644 --- a/gtk/testgtk.c +++ b/gtk/testgtk.c @@ -189,13 +189,13 @@ create_buttons (void) gtk_box_pack_start (GTK_BOX (box1), table, TRUE, TRUE, 0); button[0] = gtk_button_new_with_label ("button1"); - button[1] = gtk_button_new_accel ("_button2", accel_group); + button[1] = gtk_button_new_with_mnemonic ("_button2"); button[2] = gtk_button_new_with_label ("button3"); - button[3] = gtk_button_new_stock (GTK_STOCK_BUTTON_OK, NULL); + button[3] = gtk_button_new_from_stock (GTK_STOCK_BUTTON_OK); button[4] = gtk_button_new_with_label ("button5"); button[5] = gtk_button_new_with_label ("button6"); button[6] = gtk_button_new_with_label ("button7"); - button[7] = gtk_button_new_stock (GTK_STOCK_BUTTON_CLOSE, accel_group); + button[7] = gtk_button_new_from_stock (GTK_STOCK_BUTTON_CLOSE); button[8] = gtk_button_new_with_label ("button9"); gtk_signal_connect (GTK_OBJECT (button[0]), "clicked", @@ -2079,8 +2079,6 @@ void create_labels (void) if (!window) { - guint keyval; - window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_signal_connect (GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), @@ -2188,24 +2186,23 @@ void create_labels (void) /* There's also a gtk_label_set_markup() without accel if you * don't have an accelerator key */ - keyval = - gtk_label_set_markup_with_accel (GTK_LABEL (label), - "This <span foreground=\"blue\" background=\"orange\">label</span> has " - "<b>markup</b> _such as " - "<big><i>Big Italics</i></big>\n" - "<tt>Monospace font</tt>\n" - "<u>Underline!</u>\n" - "foo\n" - "<span foreground=\"green\" background=\"red\">Ugly colors</span>\n" - "and nothing on this line,\n" - "or this.\n" - "or this either\n" - "or even on this one\n" - "la <big>la <big>la <big>la <big>la</big></big></big></big>\n" - "but this _word is <span foreground=\"purple\"><big>purple</big></span>\n" - "<span underline=\"double\">We like <sup>superscript</sup> and <sub>subscript</sub> too</span>"); - - g_return_if_fail (keyval == GDK_s); + gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), + "This <span foreground=\"blue\" background=\"orange\">label</span> has " + "<b>markup</b> _such as " + "<big><i>Big Italics</i></big>\n" + "<tt>Monospace font</tt>\n" + "<u>Underline!</u>\n" + "foo\n" + "<span foreground=\"green\" background=\"red\">Ugly colors</span>\n" + "and nothing on this line,\n" + "or this.\n" + "or this either\n" + "or even on this one\n" + "la <big>la <big>la <big>la <big>la</big></big></big></big>\n" + "but this _word is <span foreground=\"purple\"><big>purple</big></span>\n" + "<span underline=\"double\">We like <sup>superscript</sup> and <sub>subscript</sub> too</span>"); + + g_assert (gtk_label_get_mnemonic_keyval (GTK_LABEL (label)) == GDK_s); gtk_container_add (GTK_CONTAINER (frame), label); gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0); |