summaryrefslogtreecommitdiff
path: root/gtk
diff options
context:
space:
mode:
authorAlexander Larsson <alexl@redhat.com>2001-03-21 18:58:28 +0000
committerAlexander Larsson <alexl@src.gnome.org>2001-03-21 18:58:28 +0000
commitde5eafede29f9c39e51fd78bc183fed60fe04a52 (patch)
tree46d98fc60ec0f146b58312e6046fa44c8fbf79cb /gtk
parenta24e9b107402d29e8aa3e2fc3c73652e7d3b6575 (diff)
downloadgtk+-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.c76
-rw-r--r--gtk/gtkbutton.h26
-rw-r--r--gtk/gtkcheckbutton.c29
-rw-r--r--gtk/gtkcheckbutton.h5
-rw-r--r--gtk/gtkdialog.c3
-rw-r--r--gtk/gtkentry.c12
-rw-r--r--gtk/gtkitemfactory.c19
-rw-r--r--gtk/gtklabel.c274
-rw-r--r--gtk/gtklabel.h39
-rw-r--r--gtk/gtkmarshal.list1
-rw-r--r--gtk/gtkmarshalers.list1
-rw-r--r--gtk/gtkmenu.c36
-rw-r--r--gtk/gtkmenu.h6
-rw-r--r--gtk/gtkmenuitem.c23
-rw-r--r--gtk/gtkmenushell.c10
-rw-r--r--gtk/gtknotebook.c43
-rw-r--r--gtk/gtkwidget.c42
-rw-r--r--gtk/gtkwidget.h6
-rw-r--r--gtk/gtkwindow.c200
-rw-r--r--gtk/gtkwindow.h15
-rw-r--r--gtk/testgtk.c43
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);