summaryrefslogtreecommitdiff
path: root/gtk
diff options
context:
space:
mode:
authorOwen Taylor <otaylor@redhat.com>1998-08-12 16:49:13 +0000
committerOwen Taylor <otaylor@src.gnome.org>1998-08-12 16:49:13 +0000
commit4af7480f8f64bd7709500bc27af91e2243898969 (patch)
tree1045a3d1e843fdbb3a0390ee591e9d84257ec8ee /gtk
parent5d270c2f450000cea208443142e7284c23e7af9d (diff)
downloadgdk-pixbuf-4af7480f8f64bd7709500bc27af91e2243898969.tar.gz
Added gdk_text/string_extents() - too calculate all the metrics at once of
Tue Jul 21 12:42:01 1998 Owen Taylor <otaylor@redhat.com> * gdk/gdk.h gdk/gdkfont.c: Added gdk_text/string_extents() - too calculate all the metrics at once of a string, including things which weren't calculated before. * gtk/Makefile.am gtk/gtk.h gtk/gtktearoffmenu.[ch]: New MenuItem type, that when put as the first thing in a menu, makes the menu tearoff. Currently drawn as a dashed line. * gtk/gtkmenuitem.h gtk/gtkcheckmenuitem.c: Added a flag "hide_on_activate" to the MenuItem class structure to allow check and radio buttons to be changed with <Space> without hiding the menu. * gtk/gtkaccellabel.[ch]: Added new capabilities to set a underline_group and underline_mods for the label - accelerators added in the underline group matching underline_mods will be displayed as an underline character. This doesn't work - Save As needs to be underlined as Save _As. * gtk/gtkitemfactory.c: - Create a AccelGroup for each MenuShell we create. - If an '&' appears before a character 'c' in the path, then make 'c' an accelerator in the menu's accel group, and if the menuitem is menubar <alt>C an accelerator in the itemfactory's accel group. * gtk/gtklabel.[ch]: Add support for a pattern arg - which is a string. If an '_' appears in this string, the corresponding position in the label is underlined. Add gtk_label_parse_uline() convenience function which takes a string with embedded underlines, sets the pattern and label, and returns the accelerator keyval. * gtk/gtkmenu.[ch]: Make menus no longer a toplevel widget. Instead, they create a GtkWindow and add themselves to that. (When torn off, another new feature, they create another GtkWindow to hold the torn off menu) New function gtk_menu_set_tearoff_state() * gtk/gtkenums.h gtk/gtkmenushell.[ch] gtk/gtkenums.h: Added action signals for keyboard navigation of menus. * gtk/gtkmenushell.c: Key press handler which activates bindings for navigation, and accelerators, for handling underline accelerators. Exported functions to select and activate menu items in a menushell. * gtk/testgtk.c: Added a new "Item Factory" test which tests GtkItemFactory and the new keyboard navigation of menus.
Diffstat (limited to 'gtk')
-rw-r--r--gtk/Makefile.am2
-rw-r--r--gtk/gtk.defs6
-rw-r--r--gtk/gtk.h1
-rw-r--r--gtk/gtkcheckmenuitem.c2
-rw-r--r--gtk/gtkenums.h9
-rw-r--r--gtk/gtkitem.c3
-rw-r--r--gtk/gtkitemfactory.c114
-rw-r--r--gtk/gtklabel.c354
-rw-r--r--gtk/gtklabel.h12
-rw-r--r--gtk/gtkmenu.c480
-rw-r--r--gtk/gtkmenu.h8
-rw-r--r--gtk/gtkmenubar.c26
-rw-r--r--gtk/gtkmenuitem.c67
-rw-r--r--gtk/gtkmenuitem.h10
-rw-r--r--gtk/gtkmenushell.c355
-rw-r--r--gtk/gtkmenushell.h12
-rw-r--r--gtk/gtktearoffmenuitem.c250
-rw-r--r--gtk/gtktearoffmenuitem.h64
-rw-r--r--gtk/gtktoolbar.c2
-rw-r--r--gtk/gtktypebuiltins.h1
-rw-r--r--gtk/gtktypebuiltins_evals.c7
-rw-r--r--gtk/gtktypebuiltins_ids.c2
-rw-r--r--gtk/gtktypebuiltins_vars.c1
-rw-r--r--gtk/gtkwidget.c14
-rw-r--r--gtk/testgtk.c120
25 files changed, 1607 insertions, 315 deletions
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 0828c1acb..f61ae5d56 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -84,6 +84,7 @@ libgtk_1_1_la_SOURCES = \
gtkstyle.c \
gtkstatusbar.c \
gtktable.c \
+ gtktearoffmenuitem.c \
gtktext.c \
gtktipsquery.c \
gtktogglebutton.c \
@@ -190,6 +191,7 @@ gtkinclude_HEADERS = \
gtkstyle.h \
gtkstatusbar.h \
gtktable.h \
+ gtktearoffmenuitem.h \
gtktext.h \
gtktipsquery.h \
gtktogglebutton.h \
diff --git a/gtk/gtk.defs b/gtk/gtk.defs
index 181a95047..024fcf5d0 100644
--- a/gtk/gtk.defs
+++ b/gtk/gtk.defs
@@ -91,6 +91,12 @@
(exact GTK_MATCH_EXACT)
(last GTK_MATCH_LAST))
+(define-enum GtkMenuDirectionType
+ (parent GTK_MENU_DIR_PARENT)
+ (child GTK_MENU_DIR_CHILD)
+ (next GTK_MENU_DIR_NEXT)
+ (prev GTK_MENU_DIR_PREV))
+
(define-enum GtkMenuFactoryType
(menu GTK_MENU_FACTORY_MENU)
(menu-bar GTK_MENU_FACTORY_MENU_BAR)
diff --git a/gtk/gtk.h b/gtk/gtk.h
index 7eb797f3f..d5ce6a4a9 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -101,6 +101,7 @@
#include <gtk/gtkstyle.h>
#include <gtk/gtkstatusbar.h>
#include <gtk/gtktable.h>
+#include <gtk/gtktearoffmenuitem.h>
#include <gtk/gtktext.h>
#include <gtk/gtktipsquery.h>
#include <gtk/gtktogglebutton.h>
diff --git a/gtk/gtkcheckmenuitem.c b/gtk/gtkcheckmenuitem.c
index 62ff1644d..c21050c26 100644
--- a/gtk/gtkcheckmenuitem.c
+++ b/gtk/gtkcheckmenuitem.c
@@ -151,6 +151,8 @@ gtk_check_menu_item_class_init (GtkCheckMenuItemClass *klass)
menu_item_class->activate = gtk_check_menu_item_activate;
menu_item_class->toggle_size = 12;
+ menu_item_class->hide_on_activate = FALSE;
+
klass->toggled = NULL;
klass->draw_indicator = gtk_real_check_menu_item_draw_indicator;
}
diff --git a/gtk/gtkenums.h b/gtk/gtkenums.h
index 86504ea75..bddc81bbe 100644
--- a/gtk/gtkenums.h
+++ b/gtk/gtkenums.h
@@ -107,6 +107,15 @@ typedef enum
GTK_MATCH_LAST
} GtkMatchType;
+/* Menu keyboard movement types */
+typedef enum
+{
+ GTK_MENU_DIR_PARENT,
+ GTK_MENU_DIR_CHILD,
+ GTK_MENU_DIR_NEXT,
+ GTK_MENU_DIR_PREV
+} GtkMenuDirectionType;
+
typedef enum
{
GTK_MENU_FACTORY_MENU,
diff --git a/gtk/gtkitem.c b/gtk/gtkitem.c
index 869bc574f..a4a6f6a7b 100644
--- a/gtk/gtkitem.c
+++ b/gtk/gtkitem.c
@@ -190,7 +190,8 @@ gtk_item_realize (GtkWidget *widget)
GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK |
GDK_ENTER_NOTIFY_MASK |
- GDK_LEAVE_NOTIFY_MASK);
+ GDK_LEAVE_NOTIFY_MASK |
+ GDK_POINTER_MOTION_MASK);
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
diff --git a/gtk/gtkitemfactory.c b/gtk/gtkitemfactory.c
index 0a624f060..dd605d234 100644
--- a/gtk/gtkitemfactory.c
+++ b/gtk/gtkitemfactory.c
@@ -27,8 +27,10 @@
#include "gtk/gtkmenuitem.h"
#include "gtk/gtkradiomenuitem.h"
#include "gtk/gtkcheckmenuitem.h"
+#include "gtk/gtktearoffmenuitem.h"
#include "gtk/gtkaccellabel.h"
#include "gdk/gdkprivate.h" /* for gdk_progname */
+#include "gdk/gdkkeysyms.h"
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
@@ -98,6 +100,8 @@ static const gchar *key_type_check_item = "<CheckItem>";
static GQuark quark_type_check_item = 0;
static const gchar *key_type_toggle_item = "<ToggleItem>";
static GQuark quark_type_toggle_item = 0;
+static const gchar *key_type_tearoff_item = "<Tearoff>";
+static GQuark quark_type_tearoff_item = 0;
static const gchar *key_type_separator_item = "<Separator>";
static GQuark quark_type_separator_item = 0;
static const gchar *key_type_branch = "<Branch>";
@@ -210,6 +214,7 @@ gtk_item_factory_class_init (GtkItemFactoryClass *class)
quark_type_radio_item = g_quark_from_static_string (key_type_radio_item);
quark_type_check_item = g_quark_from_static_string (key_type_check_item);
quark_type_toggle_item = g_quark_from_static_string (key_type_toggle_item);
+ quark_type_tearoff_item = g_quark_from_static_string (key_type_tearoff_item);
quark_type_separator_item = g_quark_from_static_string (key_type_separator_item);
quark_type_branch = g_quark_from_static_string (key_type_branch);
quark_type_last_branch = g_quark_from_static_string (key_type_last_branch);
@@ -547,6 +552,7 @@ gtk_item_factory_construct (GtkItemFactory *ifactory,
const gchar *path,
GtkAccelGroup *accel_group)
{
+ GtkAccelGroup *menu_group;
guint len;
g_return_if_fail (ifactory != NULL);
@@ -579,6 +585,10 @@ gtk_item_factory_construct (GtkItemFactory *ifactory,
NULL);
gtk_object_ref (GTK_OBJECT (ifactory));
gtk_object_sink (GTK_OBJECT (ifactory));
+
+ menu_group = gtk_accel_group_new ();
+ gtk_accel_group_attach (menu_group, GTK_OBJECT (ifactory->widget));
+
/*
gtk_signal_connect_object_while_alive (GTK_OBJECT (ifactory->widget),
"destroy",
@@ -880,6 +890,44 @@ gtk_item_factory_get_widget_by_action (GtkItemFactory *ifactory,
return NULL;
}
+static gboolean
+gtk_item_factory_parse_path (gchar *str,
+ gchar **path,
+ gchar **parent_path,
+ gchar **item)
+{
+ gchar *p, *q;
+
+ *path = g_strdup (str);
+
+ p = q = *path;
+ while (*p)
+ {
+ if (*p != '_')
+ {
+ *q++ = *p;
+ }
+ p++;
+ }
+ *q = 0;
+
+ *parent_path = g_strdup (*path);
+ p = strrchr (*parent_path, '/');
+ if (!p)
+ {
+ g_warning ("GtkItemFactory: invalid entry path `%s'", str);
+ return FALSE;
+ }
+ *p = 0;
+
+ p = strrchr (str, '/');
+ p++;
+
+ *item = g_strdup (p);
+
+ return TRUE;
+}
+
void
gtk_item_factory_create_item (GtkItemFactory *ifactory,
GtkItemFactoryEntry *entry,
@@ -889,11 +937,15 @@ gtk_item_factory_create_item (GtkItemFactory *ifactory,
GtkWidget *parent;
GtkWidget *widget;
GSList *radio_group;
+ gchar *name;
gchar *parent_path;
- gchar *p;
+ gchar *path;
+ guint accel_key;
guint type_id;
GtkType type;
gchar *item_type_path;
+ GtkAccelGroup *parent_accel_group = NULL;
+ GSList *tmp_list;
g_return_if_fail (ifactory != NULL);
g_return_if_fail (GTK_IS_ITEM_FACTORY (ifactory));
@@ -923,6 +975,8 @@ gtk_item_factory_create_item (GtkItemFactory *ifactory,
type = GTK_TYPE_RADIO_MENU_ITEM;
else if (type_id == quark_type_check_item)
type = GTK_TYPE_CHECK_MENU_ITEM;
+ else if (type_id == quark_type_tearoff_item)
+ type = GTK_TYPE_TEAROFF_MENU_ITEM;
else if (type_id == quark_type_toggle_item)
type = GTK_TYPE_CHECK_MENU_ITEM;
else if (type_id == quark_type_separator_item)
@@ -949,16 +1003,11 @@ gtk_item_factory_create_item (GtkItemFactory *ifactory,
return;
}
}
-
- parent_path = g_strdup (entry->path);
- p = strrchr (parent_path, '/');
- if (!p)
- {
- g_warning ("GtkItemFactory: invalid entry path `%s'", entry->path);
- return;
- }
- *p = 0;
+ if (!gtk_item_factory_parse_path (entry->path,
+ &path, &parent_path, &name))
+ return;
+
parent = gtk_item_factory_get_widget (ifactory, parent_path);
if (!parent)
{
@@ -977,9 +1026,10 @@ gtk_item_factory_create_item (GtkItemFactory *ifactory,
g_free (parent_path);
g_return_if_fail (parent != NULL);
-
- p = strrchr (entry->path, '/');
- p++;
+
+ tmp_list = gtk_accel_groups_from_object (GTK_OBJECT (parent));
+ if (tmp_list)
+ parent_accel_group = tmp_list->data;
widget = gtk_widget_new (type,
"GtkWidget::visible", TRUE,
@@ -993,22 +1043,50 @@ gtk_item_factory_create_item (GtkItemFactory *ifactory,
if (GTK_IS_CHECK_MENU_ITEM (widget))
gtk_check_menu_item_set_show_toggle (GTK_CHECK_MENU_ITEM (widget), TRUE);
- if (type_id != quark_type_separator_item && *p)
+ if ((type_id != quark_type_separator_item) &&
+ (type_id != quark_type_tearoff_item) &&
+ *name)
{
GtkWidget *label;
-
+
label =
gtk_widget_new (GTK_TYPE_ACCEL_LABEL,
- "GtkLabel::label", p,
"GtkWidget::visible", TRUE,
"GtkWidget::parent", widget,
"GtkAccelLabel::accel_widget", widget,
"GtkMisc::xalign", 0.0,
NULL);
+
+ accel_key = gtk_label_parse_uline (GTK_LABEL (label), name);
+
+ if ((accel_key != GDK_VoidSymbol) && GTK_IS_MENU_BAR (parent))
+ {
+ gtk_widget_add_accelerator (widget,
+ "activate_item",
+ ifactory->accel_group,
+ accel_key, GDK_MOD1_MASK,
+ GTK_ACCEL_LOCKED);
+ }
+
+ if ((accel_key != GDK_VoidSymbol) && parent_accel_group)
+ {
+ gtk_widget_add_accelerator (widget,
+ "activate_item",
+ parent_accel_group,
+ accel_key, 0,
+ GTK_ACCEL_LOCKED);
+ }
}
+
+ g_free (name);
+
if (type_id == quark_type_branch ||
type_id == quark_type_last_branch)
{
+ GtkAccelGroup *menu_group;
+
+ menu_group = gtk_accel_group_new ();
+
if (type_id == quark_type_last_branch)
gtk_menu_item_right_justify (GTK_MENU_ITEM (widget));
@@ -1016,11 +1094,13 @@ gtk_item_factory_create_item (GtkItemFactory *ifactory,
widget =
gtk_widget_new (GTK_TYPE_MENU,
NULL);
+
+ gtk_accel_group_attach (menu_group, GTK_OBJECT (widget));
gtk_menu_item_set_submenu (GTK_MENU_ITEM (parent), widget);
}
gtk_item_factory_add_item (ifactory,
- entry->path, entry->accelerator,
+ path, entry->accelerator,
entry->callback, entry->callback_action, callback_data,
callback_type,
item_type_path,
diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c
index e96db1b3a..9606ef549 100644
--- a/gtk/gtklabel.c
+++ b/gtk/gtklabel.c
@@ -18,14 +18,26 @@
*/
#include <string.h>
#include "gtklabel.h"
-
+#include "gdk/gdkkeysyms.h"
enum {
ARG_0,
ARG_LABEL,
+ ARG_PATTERN,
ARG_JUSTIFY
};
+typedef struct _GtkLabelRow GtkLabelRow;
+
+struct _GtkLabelRow {
+ gint index;
+ gint width;
+ gint height;
+ gint len;
+};
+
+GMemChunk *row_mem_chunk = NULL;
+
static void gtk_label_class_init (GtkLabelClass *klass);
static void gtk_label_init (GtkLabel *label);
static void gtk_label_set_arg (GtkObject *object,
@@ -43,6 +55,7 @@ static void gtk_label_state_changed (GtkWidget *widget,
guint previous_state);
static void gtk_label_style_set (GtkWidget *widget,
GtkStyle *previous_style);
+static void gtk_label_free_rows (GtkLabel *label);
@@ -87,6 +100,7 @@ gtk_label_class_init (GtkLabelClass *class)
parent_class = gtk_type_class (gtk_misc_get_type ());
gtk_object_add_arg_type ("GtkLabel::label", GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_LABEL);
+ gtk_object_add_arg_type ("GtkLabel::pattern", GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_PATTERN);
gtk_object_add_arg_type ("GtkLabel::justify", GTK_TYPE_JUSTIFICATION, GTK_ARG_READWRITE, ARG_JUSTIFY);
object_class->set_arg = gtk_label_set_arg;
@@ -113,6 +127,9 @@ gtk_label_set_arg (GtkObject *object,
case ARG_LABEL:
gtk_label_set (label, GTK_VALUE_STRING (*arg) ? GTK_VALUE_STRING (*arg) : "");
break;
+ case ARG_PATTERN:
+ gtk_label_set_pattern (label, GTK_VALUE_STRING (*arg));
+ break;
case ARG_JUSTIFY:
gtk_label_set_justify (label, GTK_VALUE_ENUM (*arg));
break;
@@ -135,6 +152,9 @@ gtk_label_get_arg (GtkObject *object,
case ARG_LABEL:
GTK_VALUE_STRING (*arg) = g_strdup (label->label);
break;
+ case ARG_PATTERN:
+ GTK_VALUE_STRING (*arg) = g_strdup (label->pattern);
+ break;
case ARG_JUSTIFY:
GTK_VALUE_ENUM (*arg) = label->jtype;
break;
@@ -154,6 +174,7 @@ gtk_label_init (GtkLabel *label)
label->max_width = 0;
label->jtype = GTK_JUSTIFY_CENTER;
label->needs_clear = FALSE;
+ label->pattern = NULL;
gtk_label_set (label, "");
}
@@ -181,18 +202,57 @@ gtk_label_set (GtkLabel *label,
g_return_if_fail (label != NULL);
g_return_if_fail (GTK_IS_LABEL (label));
g_return_if_fail (str != NULL);
+
+ if (!row_mem_chunk)
+ row_mem_chunk = g_mem_chunk_create (GtkLabelRow, 64, G_ALLOC_AND_FREE);
if (label->label)
g_free (label->label);
label->label = g_strdup (str);
if (label->row)
- g_slist_free (label->row);
- label->row = NULL;
- label->row = g_slist_append (label->row, label->label);
+ gtk_label_free_rows (label);
+
p = label->label;
- while ((p = strchr(p, '\n')))
- label->row = g_slist_append (label->row, ++p);
+ do {
+ GtkLabelRow *row = g_chunk_new (GtkLabelRow, row_mem_chunk);
+ label->row = g_slist_append (label->row, row);
+
+ row->index = p - label->label;
+
+ p = strchr(p, '\n');
+ if (p)
+ {
+ p++;
+ row->len = (p - label->label) - row->index;
+ }
+ else
+ row->len = strlen (label->label) - row->index;
+ } while (p);
+
+ if (GTK_WIDGET_VISIBLE (label))
+ {
+ if (GTK_WIDGET_MAPPED (label))
+ gdk_window_clear_area (GTK_WIDGET (label)->window,
+ GTK_WIDGET (label)->allocation.x,
+ GTK_WIDGET (label)->allocation.y,
+ GTK_WIDGET (label)->allocation.width,
+ GTK_WIDGET (label)->allocation.height);
+
+ gtk_widget_queue_resize (GTK_WIDGET (label));
+ }
+}
+
+void
+gtk_label_set_pattern (GtkLabel *label,
+ const gchar *pattern)
+{
+ g_return_if_fail (label != NULL);
+ g_return_if_fail (GTK_IS_LABEL (label));
+
+ if (label->pattern)
+ g_free (label->pattern);
+ label->pattern = g_strdup (pattern);
if (GTK_WIDGET_VISIBLE (label))
{
@@ -255,17 +315,128 @@ gtk_label_finalize (GtkObject *object)
label = GTK_LABEL (object);
g_free (label->label);
- g_slist_free (label->row);
+ gtk_label_free_rows (label);
(* GTK_OBJECT_CLASS (parent_class)->finalize) (object);
}
+static gint
+gtk_label_process_row (GtkLabel *label,
+ GtkLabelRow *row,
+ gint x, gint y,
+ gboolean draw)
+{
+ GtkWidget *widget = GTK_WIDGET (label);
+
+ char lastc;
+ gint i, j;
+ gint offset;
+ gint height;
+ gint pattern_length;
+
+ if (label->pattern)
+ pattern_length = strlen (label->pattern);
+ else
+ pattern_length = 0;
+
+ offset = 0;
+ height = widget->style->font->ascent + widget->style->font->descent;
+
+ if (draw)
+ {
+ if (label->jtype == GTK_JUSTIFY_CENTER)
+ offset = (label->max_width - row->width) / 2;
+ else if (label->jtype == GTK_JUSTIFY_RIGHT)
+ offset = (label->max_width - row->width);
+ }
+
+ if (label->pattern && (row->index < pattern_length))
+ lastc = label->pattern[0];
+ else
+ lastc = ' ';
+
+ j = 0;
+ for (i=1; i<=row->len; i++)
+ {
+ char c;
+
+ if (label->pattern && (row->index + i < pattern_length))
+ c = label->pattern[row->index+i];
+ else
+ c = ' ';
+
+ if ((i == row->len) || (lastc != c))
+ {
+ gint width = 0;
+
+ if (lastc == '_')
+ {
+ gint descent;
+ gint rbearing;
+ gint lbearing;
+
+ gdk_text_extents (widget->style->font,
+ &label->label[row->index+j], i - j,
+ &lbearing, &rbearing, &width, NULL, &descent);
+
+ if (draw)
+ {
+ if (widget->state == GTK_STATE_INSENSITIVE)
+ gdk_draw_line (widget->window,
+ widget->style->white_gc,
+ offset + x + lbearing, y + descent + 2,
+ offset + x + rbearing + 1, y + descent + 2);
+
+ gdk_draw_line (widget->window,
+ widget->style->fg_gc[widget->state],
+ offset + x + lbearing - 1, y + descent + 1,
+ offset + x + rbearing, y + descent + 1);
+ }
+
+ height = MAX (height,
+ widget->style->font->ascent + descent + 2);
+ }
+ else if (i != row->len)
+ {
+ width = gdk_text_width (widget->style->font,
+ &label->label[row->index+j],
+ i - j);
+ }
+
+ if (draw)
+ {
+ if (widget->state == GTK_STATE_INSENSITIVE)
+ gdk_draw_text (widget->window, widget->style->font,
+ widget->style->white_gc,
+ offset + x + 1, y + 1,
+ &label->label[row->index+j], i - j);
+
+ gdk_draw_text (widget->window, widget->style->font,
+ widget->style->fg_gc[widget->state],
+ offset + x, y,
+ &label->label[row->index+j], i - j);
+ }
+
+
+ offset += width;
+
+ if (i != row->len)
+ {
+ lastc = c;
+ j = i;
+ }
+ }
+ }
+
+ return height;
+}
+
static void
gtk_label_size_request (GtkWidget *widget,
GtkRequisition *requisition)
{
GtkLabel *label;
- GSList *row;
+ GSList *tmp_list;
gint width;
g_return_if_fail (widget != NULL);
@@ -274,38 +445,35 @@ gtk_label_size_request (GtkWidget *widget,
label = GTK_LABEL (widget);
- row = label->row;
+ requisition->height = label->misc.ypad * 2;
+
+ tmp_list = label->row;
width = 0;
- while (row)
+ while (tmp_list)
{
- if (row->next)
- width = MAX (width,
- gdk_text_width (GTK_WIDGET (label)->style->font,
- row->data,
- (gchar*) row->next->data - (gchar*) row->data - 1));
- else
- width = MAX (width, gdk_string_width (GTK_WIDGET (label)->style->font, row->data));
- row = row->next;
+ GtkLabelRow *row = tmp_list->data;
+
+ row->width = gdk_text_width (GTK_WIDGET (label)->style->font,
+ &label->label [row->index],
+ row->len);
+ width = MAX (width, row->width);
+
+ requisition->height += gtk_label_process_row (label, row, 0, 0, FALSE) + 2;
+
+ tmp_list = tmp_list->next;
}
label->max_width = width;
requisition->width = width + label->misc.xpad * 2;
- requisition->height = ((GTK_WIDGET (label)->style->font->ascent +
- GTK_WIDGET (label)->style->font->descent + 2) *
- g_slist_length(label->row) +
- label->misc.ypad * 2);
}
-
+
static gint
gtk_label_expose (GtkWidget *widget,
GdkEventExpose *event)
{
GtkLabel *label;
GtkMisc *misc;
- GSList *row;
- gint state;
- gint offset;
- gint len;
+ GSList *tmp_list;
gint x, y;
g_return_val_if_fail (widget != NULL, FALSE);
@@ -317,13 +485,11 @@ gtk_label_expose (GtkWidget *widget,
label = GTK_LABEL (widget);
misc = GTK_MISC (widget);
- state = widget->state;
-
/*
* GC Clipping
*/
gdk_gc_set_clip_rectangle (widget->style->white_gc, &event->area);
- gdk_gc_set_clip_rectangle (widget->style->fg_gc[state], &event->area);
+ gdk_gc_set_clip_rectangle (widget->style->fg_gc[widget->state], &event->area);
/* We clear the whole allocation here so that if a partial
* expose is triggered we don't just clear part and mess up
@@ -351,59 +517,41 @@ gtk_label_expose (GtkWidget *widget,
(widget->requisition.height - misc->ypad * 2)) *
misc->yalign + widget->style->font->ascent) + 1.5;
- row = label->row;
- while (row && row->next)
- {
- len = (gchar*) row->next->data - (gchar*) row->data - 1;
- offset = 0;
-
- if (label->jtype == GTK_JUSTIFY_CENTER)
- offset = (label->max_width - gdk_text_width (widget->style->font, row->data, len)) / 2;
-
- else if (label->jtype == GTK_JUSTIFY_RIGHT)
- offset = (label->max_width - gdk_text_width (widget->style->font, row->data, len));
-
- if (state == GTK_STATE_INSENSITIVE)
- gdk_draw_text (widget->window, widget->style->font,
- widget->style->white_gc,
- offset + x + 1, y + 1, row->data, len);
-
- gdk_draw_text (widget->window, widget->style->font,
- widget->style->fg_gc[state],
- offset + x, y, row->data, len);
- row = row->next;
- y += widget->style->font->ascent + widget->style->font->descent + 2;
- }
-
/*
* COMMENT: we can avoid gdk_text_width() calls here storing in label->row
* the widths of the rows calculated in gtk_label_set.
* Once we have a wrapping interface we can support GTK_JUSTIFY_FILL.
*/
- offset = 0;
-
- if (label->jtype == GTK_JUSTIFY_CENTER)
- offset = (label->max_width - gdk_string_width (widget->style->font, row->data)) / 2;
-
- else if (label->jtype == GTK_JUSTIFY_RIGHT)
- offset = (label->max_width - gdk_string_width (widget->style->font, row->data));
-
- if (state == GTK_STATE_INSENSITIVE)
- gdk_draw_string (widget->window, widget->style->font,
- widget->style->white_gc,
- offset + x + 1, y + 1, row->data);
-
- gdk_draw_string (widget->window, widget->style->font,
- widget->style->fg_gc[state],
- offset + x, y, row->data);
+
+ tmp_list = label->row;
+ while (tmp_list)
+ {
+ y += gtk_label_process_row (label, tmp_list->data, x, y, TRUE) + 2;
+ tmp_list = tmp_list->next;
+ }
gdk_gc_set_clip_mask (widget->style->white_gc, NULL);
- gdk_gc_set_clip_mask (widget->style->fg_gc[state], NULL);
+ gdk_gc_set_clip_mask (widget->style->fg_gc[widget->state], NULL);
}
return TRUE;
}
+static void
+gtk_label_free_rows (GtkLabel *label)
+{
+ GSList *tmp_list;
+
+ tmp_list = label->row;
+ while (tmp_list)
+ {
+ g_mem_chunk_free (row_mem_chunk, tmp_list->data);
+ tmp_list = tmp_list->next;
+ }
+ g_slist_free (label->row);
+ label->row = NULL;
+}
+
static void
gtk_label_state_changed (GtkWidget *widget,
guint previous_state)
@@ -420,3 +568,67 @@ gtk_label_style_set (GtkWidget *widget,
GTK_LABEL (widget)->needs_clear = TRUE;
}
+guint
+gtk_label_parse_uline (GtkLabel *label,
+ const gchar *string)
+{
+ guint accel_key = GDK_VoidSymbol;
+ const gchar *p;
+ gchar *q, *r;
+ gchar *name, *pattern;
+
+ gint length;
+ gboolean underscore;
+
+ length = strlen (string);
+
+ name = g_new (gchar, length+1);
+ pattern = g_new (gchar, length+1);
+
+ underscore = FALSE;
+
+ p = string;
+ q = name;
+ r = pattern;
+ underscore = FALSE;
+
+ while (*p)
+ {
+ if (underscore)
+ {
+ if (*p == '_')
+ *r++ = ' ';
+ else
+ {
+ *r++ = '_';
+ if (accel_key == GDK_VoidSymbol)
+ accel_key = gdk_keyval_to_lower (*p);
+ }
+
+ *q++ = *p;
+ underscore = FALSE;
+ }
+ else
+ {
+ if (*p == '_')
+ underscore = TRUE;
+ else
+ {
+ *q++ = *p;
+ *r++ = ' ';
+ }
+ }
+ p++;
+ }
+ *q = 0;
+
+ gtk_label_set (label, name);
+ gtk_label_set_pattern (label, pattern);
+
+ g_free (name);
+ g_free (pattern);
+
+ return accel_key;
+}
+
+
diff --git a/gtk/gtklabel.h b/gtk/gtklabel.h
index 331b2a788..17275dae9 100644
--- a/gtk/gtklabel.h
+++ b/gtk/gtklabel.h
@@ -44,6 +44,8 @@ struct _GtkLabel
GtkMisc misc;
gchar *label;
+ gchar *pattern;
+
GSList *row;
guint max_width : 16;
guint jtype : 2;
@@ -60,12 +62,22 @@ GtkType gtk_label_get_type (void);
GtkWidget* gtk_label_new (const gchar *string);
void gtk_label_set (GtkLabel *label,
const gchar *string);
+void gtk_label_set_pattern (GtkLabel *label,
+ const gchar *pattern);
void gtk_label_set_justify (GtkLabel *label,
GtkJustification jtype);
void gtk_label_get (GtkLabel *label,
gchar **string);
+/* 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);
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
diff --git a/gtk/gtkmenu.c b/gtk/gtkmenu.c
index d3cb999e1..9025e071c 100644
--- a/gtk/gtkmenu.c
+++ b/gtk/gtkmenu.c
@@ -18,10 +18,13 @@
*/
#include <ctype.h>
#include "gdk/gdkkeysyms.h"
+#include "gtkbindings.h"
+#include "gtklabel.h"
#include "gtkmain.h"
#include "gtkmenu.h"
#include "gtkmenuitem.h"
#include "gtksignal.h"
+#include "gtkwindow.h"
#define MENU_ITEM_CLASS(w) GTK_MENU_ITEM_CLASS (GTK_OBJECT (w)->klass)
@@ -39,9 +42,6 @@ struct _GtkMenuAttachData
static void gtk_menu_class_init (GtkMenuClass *klass);
static void gtk_menu_init (GtkMenu *menu);
static void gtk_menu_destroy (GtkObject *object);
-static void gtk_menu_show (GtkWidget *widget);
-static void gtk_menu_map (GtkWidget *widget);
-static void gtk_menu_unmap (GtkWidget *widget);
static void gtk_menu_realize (GtkWidget *widget);
static void gtk_menu_size_request (GtkWidget *widget,
GtkRequisition *requisition);
@@ -52,14 +52,14 @@ static void gtk_menu_draw (GtkWidget *widget,
GdkRectangle *area);
static gint gtk_menu_expose (GtkWidget *widget,
GdkEventExpose *event);
-static gint gtk_menu_configure (GtkWidget *widget,
- GdkEventConfigure *event);
static gint gtk_menu_key_press (GtkWidget *widget,
GdkEventKey *event);
-static void gtk_menu_check_resize (GtkContainer *container);
+static gint gtk_menu_motion_notify (GtkWidget *widget,
+ GdkEventMotion *event);
static void gtk_menu_deactivate (GtkMenuShell *menu_shell);
static void gtk_menu_show_all (GtkWidget *widget);
static void gtk_menu_hide_all (GtkWidget *widget);
+static void gtk_menu_position (GtkMenu *menu);
static GtkMenuShellClass *parent_class = NULL;
static const gchar *attach_data_key = "gtk-menu-attach-data";
@@ -97,6 +97,8 @@ gtk_menu_class_init (GtkMenuClass *class)
GtkWidgetClass *widget_class;
GtkContainerClass *container_class;
GtkMenuShellClass *menu_shell_class;
+
+ GtkBindingSet *binding_set;
object_class = (GtkObjectClass*) class;
widget_class = (GtkWidgetClass*) class;
@@ -106,57 +108,89 @@ gtk_menu_class_init (GtkMenuClass *class)
object_class->destroy = gtk_menu_destroy;
- widget_class->show = gtk_menu_show;
- widget_class->map = gtk_menu_map;
- widget_class->unmap = gtk_menu_unmap;
widget_class->realize = gtk_menu_realize;
widget_class->draw = gtk_menu_draw;
widget_class->size_request = gtk_menu_size_request;
widget_class->size_allocate = gtk_menu_size_allocate;
widget_class->expose_event = gtk_menu_expose;
- widget_class->configure_event = gtk_menu_configure;
widget_class->key_press_event = gtk_menu_key_press;
+ widget_class->motion_notify_event = gtk_menu_motion_notify;
widget_class->show_all = gtk_menu_show_all;
widget_class->hide_all = gtk_menu_hide_all;
- container_class->check_resize = gtk_menu_check_resize;
-
menu_shell_class->submenu_placement = GTK_LEFT_RIGHT;
menu_shell_class->deactivate = gtk_menu_deactivate;
+
+ binding_set = gtk_binding_set_by_class (class);
+ gtk_binding_entry_add_signal (binding_set,
+ GDK_Up, 0,
+ "move_current", 1,
+ GTK_TYPE_MENU_DIRECTION_TYPE,
+ GTK_MENU_DIR_PREV);
+ gtk_binding_entry_add_signal (binding_set,
+ GDK_Down, 0,
+ "move_current", 1,
+ GTK_TYPE_MENU_DIRECTION_TYPE,
+ GTK_MENU_DIR_NEXT);
+ gtk_binding_entry_add_signal (binding_set,
+ GDK_Left, 0,
+ "move_current", 1,
+ GTK_TYPE_MENU_DIRECTION_TYPE,
+ GTK_MENU_DIR_PARENT);
+ gtk_binding_entry_add_signal (binding_set,
+ GDK_Right, 0,
+ "move_current", 1,
+ GTK_TYPE_MENU_DIRECTION_TYPE,
+ GTK_MENU_DIR_CHILD);
}
static void
gtk_menu_init (GtkMenu *menu)
{
- GTK_WIDGET_SET_FLAGS (menu, GTK_TOPLEVEL);
-
- gtk_container_set_resize_mode (GTK_CONTAINER (menu), GTK_RESIZE_QUEUE);
-
menu->parent_menu_item = NULL;
menu->old_active_menu_item = NULL;
menu->accel_group = NULL;
menu->position_func = NULL;
menu->position_func_data = NULL;
+ menu->toplevel = gtk_window_new (GTK_WINDOW_POPUP);
+ gtk_signal_connect_object (GTK_OBJECT (menu->toplevel), "key_press_event",
+ GTK_SIGNAL_FUNC (gtk_menu_key_press),
+ GTK_OBJECT (menu));
+ gtk_window_set_policy (GTK_WINDOW (menu->toplevel),
+ FALSE, FALSE, TRUE);
+
+ gtk_container_add (GTK_CONTAINER (menu->toplevel), GTK_WIDGET (menu));
+
+ menu->tearoff_window = NULL;
+ menu->torn_off = FALSE;
+
MENU_NEEDS_RESIZE (menu) = TRUE;
}
static void
gtk_menu_destroy (GtkObject *object)
{
+ GtkMenu *menu;
GtkMenuAttachData *data;
g_return_if_fail (object != NULL);
g_return_if_fail (GTK_IS_MENU (object));
+
+ menu = GTK_MENU (object);
gtk_widget_ref (GTK_WIDGET (object));
data = gtk_object_get_data (object, attach_data_key);
if (data)
- gtk_menu_detach (GTK_MENU (object));
+ gtk_menu_detach (menu);
- gtk_menu_set_accel_group (GTK_MENU (object), NULL);
+ gtk_menu_set_accel_group (menu, NULL);
+ gtk_widget_destroy (menu->toplevel);
+ if (menu->tearoff_window)
+ gtk_widget_destroy (menu->tearoff_window);
+
if (GTK_OBJECT_CLASS (parent_class)->destroy)
(* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
@@ -283,22 +317,75 @@ gtk_menu_popup (GtkMenu *menu,
guint button,
guint32 activate_time)
{
+ GtkWidget *widget;
GtkWidget *xgrab_shell;
GtkWidget *parent;
+ GdkEvent *current_event;
+ GtkMenuShell *menu_shell;
g_return_if_fail (menu != NULL);
g_return_if_fail (GTK_IS_MENU (menu));
- GTK_MENU_SHELL (menu)->parent_menu_shell = parent_menu_shell;
- GTK_MENU_SHELL (menu)->active = TRUE;
- GTK_MENU_SHELL (menu)->button = button;
+ widget = GTK_WIDGET (menu);
+ menu_shell = GTK_MENU_SHELL (menu);
+
+ menu_shell->parent_menu_shell = parent_menu_shell;
+ menu_shell->active = TRUE;
+ menu_shell->button = button;
+
+ /* If we are popping up the menu from something other than, a button
+ * press then, as a heuristic, we ignore enter events for the menu
+ * until we get a MOTION_NOTIFY.
+ */
+
+ current_event = gtk_get_current_event();
+ if (current_event)
+ {
+ if ((current_event->type != GDK_BUTTON_PRESS) &&
+ (current_event->type != GDK_ENTER_NOTIFY))
+ menu_shell->ignore_enter = TRUE;
+ }
+
+ if (menu->torn_off)
+ {
+ GdkPixmap *pixmap;
+ GdkGC *gc;
+ GdkGCValues gc_values;
+
+ gc_values.subwindow_mode = GDK_INCLUDE_INFERIORS;
+ gc = gdk_gc_new_with_values (widget->window,
+ &gc_values, GDK_GC_SUBWINDOW);
+
+ pixmap = gdk_pixmap_new (widget->window,
+ widget->requisition.width,
+ widget->requisition.height,
+ -1);
+
+ gdk_draw_pixmap (pixmap, gc,
+ widget->window,
+ 0, 0, 0, 0, -1, -1);
+ gdk_gc_unref(gc);
+
+ gdk_window_set_back_pixmap (menu->tearoff_window->window, pixmap, FALSE);
+ gdk_pixmap_unref (pixmap);
+
+ gtk_container_remove (GTK_CONTAINER (menu->tearoff_window), widget);
+ gtk_container_add (GTK_CONTAINER (menu->toplevel), widget);
+ }
menu->parent_menu_item = parent_menu_item;
menu->position_func = func;
menu->position_func_data = data;
- GTK_MENU_SHELL (menu)->activate_time = activate_time;
-
+ menu_shell->activate_time = activate_time;
+
+ gtk_menu_position (menu);
+
+ /* We need to show the menu _here_ because code expects to be
+ * able to tell if the menu is onscreen by looking at the
+ * GTK_WIDGET_VISIBLE (menu)
+ */
gtk_widget_show (GTK_WIDGET (menu));
+ gtk_widget_show (menu->toplevel);
/* Find the last viewable ancestor, and make an X grab on it
*/
@@ -328,12 +415,22 @@ gtk_menu_popup (GtkMenu *menu,
if (xgrab_shell && (!GTK_MENU_SHELL (xgrab_shell)->have_xgrab))
{
GdkCursor *cursor = gdk_cursor_new (GDK_ARROW);
-
- GTK_MENU_SHELL (xgrab_shell)->have_xgrab =
- (gdk_pointer_grab (xgrab_shell->window, TRUE,
- GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
- GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK,
- NULL, cursor, activate_time) == 0);
+
+ if ((gdk_pointer_grab (xgrab_shell->window, TRUE,
+ GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
+ GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
+ GDK_POINTER_MOTION_MASK,
+ NULL, cursor, activate_time) == 0))
+ {
+ if (gdk_keyboard_grab (xgrab_shell->window, TRUE,
+ activate_time) == 0)
+ GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
+ else
+ {
+ gdk_pointer_ungrab (activate_time);
+ }
+ }
+
gdk_cursor_destroy (cursor);
}
@@ -352,6 +449,7 @@ gtk_menu_popdown (GtkMenu *menu)
menu_shell->parent_menu_shell = NULL;
menu_shell->active = FALSE;
+ menu_shell->ignore_enter = FALSE;
if (menu_shell->active_menu_item)
{
@@ -362,9 +460,26 @@ gtk_menu_popdown (GtkMenu *menu)
/* The X Grab, if present, will automatically be removed when we hide
* the window */
- gtk_widget_hide (GTK_WIDGET (menu));
+ gtk_widget_hide (menu->toplevel);
+
+ if (menu->torn_off)
+ {
+ if (GTK_BIN (menu->toplevel)->child)
+ gtk_widget_reparent (GTK_WIDGET (menu), menu->tearoff_window);
+ else
+ /* We popped up the menu from the tearoff, so we need to
+ * release the grab - we aren't actually hiding the menu.
+ */
+ if (menu_shell->have_xgrab)
+ {
+ gdk_pointer_ungrab (GDK_CURRENT_TIME);
+ gdk_keyboard_ungrab (GDK_CURRENT_TIME);
+ }
+ }
+ else
+ gtk_widget_hide (GTK_WIDGET (menu));
+
menu_shell->have_xgrab = FALSE;
-
gtk_grab_remove (GTK_WIDGET (menu));
}
@@ -435,99 +550,77 @@ gtk_menu_set_accel_group (GtkMenu *menu,
}
-static void
-gtk_menu_show (GtkWidget *widget)
-{
- g_return_if_fail (widget != NULL);
- g_return_if_fail (GTK_IS_MENU (widget));
-
- GTK_WIDGET_SET_FLAGS (widget, GTK_VISIBLE);
- if (MENU_NEEDS_RESIZE (widget))
- gtk_container_check_resize (GTK_CONTAINER (widget));
- gtk_widget_map (widget);
-}
-
void
gtk_menu_reposition (GtkMenu *menu)
{
- GtkWidget *widget;
-
g_return_if_fail (menu != NULL);
g_return_if_fail (GTK_IS_MENU (menu));
- widget = GTK_WIDGET (menu);
-
- if (GTK_WIDGET_DRAWABLE (menu))
- {
- gint x, y;
-
- gdk_window_get_pointer (NULL, &x, &y, NULL);
-
- if (menu->position_func)
- (* menu->position_func) (menu, &x, &y, menu->position_func_data);
- else
- {
- gint screen_width;
- gint screen_height;
-
- screen_width = gdk_screen_width ();
- screen_height = gdk_screen_height ();
-
- x -= 2;
- y -= 2;
-
- if ((x + widget->requisition.width) > screen_width)
- x -= ((x + widget->requisition.width) - screen_width);
- if (x < 0)
- x = 0;
- if ((y + widget->requisition.height) > screen_height)
- y -= ((y + widget->requisition.height) - screen_height);
- if (y < 0)
- y = 0;
- }
-
- gdk_window_move (widget->window, x, y);
- }
+ if (GTK_WIDGET_DRAWABLE (menu) && !menu->torn_off)
+ gtk_menu_position (menu);
}
-static void
-gtk_menu_map (GtkWidget *widget)
+
+void
+gtk_menu_set_tearoff_state (GtkMenu *menu,
+ gboolean torn_off)
{
- GtkMenu *menu;
- GtkMenuShell *menu_shell;
- GtkWidget *child;
- GList *children;
-
- g_return_if_fail (widget != NULL);
- g_return_if_fail (GTK_IS_MENU (widget));
-
- menu = GTK_MENU (widget);
- menu_shell = GTK_MENU_SHELL (widget);
- GTK_WIDGET_SET_FLAGS (menu_shell, GTK_MAPPED);
+ g_return_if_fail (menu != NULL);
+ g_return_if_fail (GTK_IS_MENU (menu));
- gtk_menu_reposition (menu);
-
- children = menu_shell->children;
- while (children)
+ if (menu->torn_off != torn_off)
{
- child = children->data;
- children = children->next;
+ menu->torn_off = torn_off;
- if (GTK_WIDGET_VISIBLE (child) && !GTK_WIDGET_MAPPED (child))
- gtk_widget_map (child);
- }
-
- gdk_window_show (widget->window);
-}
+ if (menu->torn_off)
+ {
+ if (GTK_WIDGET_VISIBLE (menu))
+ gtk_menu_popdown (menu);
-static void
-gtk_menu_unmap (GtkWidget *widget)
-{
- g_return_if_fail (widget != NULL);
- g_return_if_fail (GTK_IS_MENU (widget));
-
- GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
- gdk_window_hide (widget->window);
+ if (!menu->tearoff_window)
+ {
+ GtkWidget *attach_widget;
+
+ menu->tearoff_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_signal_connect_object (GTK_OBJECT (menu->tearoff_window),
+ "key_press_event",
+ GTK_SIGNAL_FUNC (gtk_menu_key_press),
+ GTK_OBJECT (menu));
+ gtk_widget_realize (menu->tearoff_window);
+
+ attach_widget = gtk_menu_get_attach_widget (menu);
+ if (GTK_IS_MENU_ITEM (attach_widget))
+ {
+ GtkWidget *child = GTK_BIN (attach_widget)->child;
+ if (GTK_IS_LABEL (child))
+ {
+ gchar *ret;
+ gtk_label_get (GTK_LABEL (child), &ret);
+ gdk_window_set_title (menu->tearoff_window->window, ret);
+ }
+ }
+
+ gdk_window_set_decorations (menu->tearoff_window->window,
+ GDK_DECOR_ALL |
+ GDK_DECOR_RESIZEH |
+ GDK_DECOR_MINIMIZE |
+ GDK_DECOR_MAXIMIZE);
+ gtk_window_set_policy (GTK_WINDOW (menu->tearoff_window),
+ FALSE, FALSE, TRUE);
+ }
+ gtk_widget_reparent (GTK_WIDGET (menu), menu->tearoff_window);
+
+ gtk_menu_position (menu);
+
+ gtk_widget_show (GTK_WIDGET (menu));
+ gtk_widget_show (menu->tearoff_window);
+ }
+ else
+ {
+ gtk_widget_hide (menu->tearoff_window);
+ gtk_widget_reparent (GTK_WIDGET (menu), menu->toplevel);
+ }
+ }
}
static void
@@ -541,6 +634,7 @@ gtk_menu_realize (GtkWidget *widget)
GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+ attributes.window_type = GDK_WINDOW_CHILD;
attributes.x = widget->allocation.x;
attributes.y = widget->allocation.y;
attributes.width = widget->allocation.width;
@@ -548,14 +642,11 @@ gtk_menu_realize (GtkWidget *widget)
attributes.wclass = GDK_INPUT_OUTPUT;
attributes.visual = gtk_widget_get_visual (widget);
attributes.colormap = gtk_widget_get_colormap (widget);
- attributes.window_type = GDK_WINDOW_TEMP;
attributes.event_mask = gtk_widget_get_events (widget);
- attributes.event_mask |= (GDK_EXPOSURE_MASK |
- GDK_KEY_PRESS_MASK |
- GDK_STRUCTURE_MASK);
+ attributes.event_mask |= (GDK_EXPOSURE_MASK | GDK_KEY_PRESS_MASK);
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
- widget->window = gdk_window_new (NULL, &attributes, attributes_mask);
+ widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
gdk_window_set_user_data (widget->window, widget);
widget->style = gtk_style_attach (widget->style, widget->window);
@@ -639,6 +730,11 @@ gtk_menu_size_allocate (GtkWidget *widget,
menu_shell = GTK_MENU_SHELL (widget);
widget->allocation = *allocation;
+ if (GTK_WIDGET_REALIZED (widget))
+ gdk_window_move_resize (widget->window,
+ allocation->x, allocation->y,
+ allocation->width, allocation->height);
+
if (menu_shell->children)
{
@@ -665,13 +761,6 @@ gtk_menu_size_allocate (GtkWidget *widget,
}
}
}
-
- if (GTK_WIDGET_REALIZED (widget))
- {
- gdk_window_resize (widget->window,
- widget->requisition.width,
- widget->requisition.height);
- }
}
static void
@@ -759,55 +848,35 @@ gtk_menu_expose (GtkWidget *widget,
}
static gint
-gtk_menu_configure (GtkWidget *widget,
- GdkEventConfigure *event)
+gtk_menu_key_press (GtkWidget *widget,
+ GdkEventKey *event)
{
- GtkAllocation allocation;
+ GtkMenuShell *menu_shell;
+ gboolean delete = FALSE;
g_return_val_if_fail (widget != NULL, FALSE);
g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
g_return_val_if_fail (event != NULL, FALSE);
-
- /* If the window was merely moved, do nothing */
- if ((widget->allocation.width == event->width) &&
- (widget->allocation.height == event->height))
- return FALSE;
-
- if (MENU_NEEDS_RESIZE (widget))
- {
- MENU_NEEDS_RESIZE (widget) = FALSE;
- allocation.x = 0;
- allocation.y = 0;
- allocation.width = event->width;
- allocation.height = event->height;
-
- gtk_widget_size_allocate (widget, &allocation);
+ menu_shell = GTK_MENU_SHELL (widget);
+
+ if (GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
+ return TRUE;
+
+ switch (event->keyval)
+ {
+ case GDK_Delete:
+ case GDK_KP_Delete:
+ case GDK_BackSpace:
+ delete = TRUE;
+ break;
+ default:
+ break;
}
-
- return FALSE;
-}
-static gint
-gtk_menu_key_press (GtkWidget *widget,
- GdkEventKey *event)
-{
- gboolean delete;
-
- g_return_val_if_fail (widget != NULL, FALSE);
- g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
- g_return_val_if_fail (event != NULL, FALSE);
-
- delete = (event->keyval == GDK_Delete ||
- event->keyval == GDK_KP_Delete ||
- event->keyval == GDK_BackSpace);
-
+ /* Modify the accelerators */
if (delete || gtk_accelerator_valid (event->keyval, event->keyval))
{
- GtkMenuShell *menu_shell;
-
- menu_shell = GTK_MENU_SHELL (widget);
-
if (menu_shell->active_menu_item &&
GTK_BIN (menu_shell->active_menu_item)->child &&
GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu == NULL)
@@ -861,32 +930,27 @@ gtk_menu_key_press (GtkWidget *widget,
return FALSE;
}
-static void
-gtk_menu_check_resize (GtkContainer *container)
+static gint
+gtk_menu_motion_notify (GtkWidget *widget,
+ GdkEventMotion *event)
{
- GtkAllocation allocation;
- GtkWidget *widget;
-
- g_return_if_fail (container != NULL);
- g_return_if_fail (GTK_IS_MENU (container));
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
- widget = GTK_WIDGET (container);
-
- if (GTK_WIDGET_VISIBLE (container))
+ if (GTK_MENU_SHELL (widget)->ignore_enter)
+ GTK_MENU_SHELL (widget)->ignore_enter = FALSE;
+ else
{
- MENU_NEEDS_RESIZE (container) = FALSE;
-
- gtk_widget_size_request (widget, &widget->requisition);
-
- allocation.x = widget->allocation.x;
- allocation.y = widget->allocation.y;
- allocation.width = widget->requisition.width;
- allocation.height = widget->requisition.height;
-
- gtk_widget_size_allocate (widget, &allocation);
+ GdkEvent send_event;
+
+ send_event.crossing.type = GDK_ENTER_NOTIFY;
+ send_event.crossing.window = event->window;
+ send_event.crossing.time = event->time;
+
+ gtk_widget_event (widget, &send_event);
}
- else
- MENU_NEEDS_RESIZE (container) = TRUE;
+
+ return FALSE;
}
static void
@@ -933,3 +997,51 @@ gtk_menu_hide_all (GtkWidget *widget)
/* Hide children, but not self. */
gtk_container_foreach (container, (GtkCallback) gtk_widget_hide_all, NULL);
}
+
+static void
+gtk_menu_position (GtkMenu *menu)
+{
+ GtkWidget *widget;
+ gint x, y;
+
+ g_return_if_fail (menu != NULL);
+ g_return_if_fail (GTK_IS_MENU (menu));
+
+ widget = GTK_WIDGET (menu);
+
+ gdk_window_get_pointer (NULL, &x, &y, NULL);
+
+ /* We need the requisition to figure out the right place to
+ * popup the menu. In fact, we always need to ask here, since
+ * if one a size_request was queued while we weren't popped up,
+ * the requisition won't have been recomputed yet.
+ */
+ gtk_widget_size_request (widget, &widget->requisition);
+
+ if (menu->position_func)
+ (* menu->position_func) (menu, &x, &y, menu->position_func_data);
+ else
+ {
+ gint screen_width;
+ gint screen_height;
+
+ screen_width = gdk_screen_width ();
+ screen_height = gdk_screen_height ();
+
+ x -= 2;
+ y -= 2;
+
+ if ((x + widget->requisition.width) > screen_width)
+ x -= ((x + widget->requisition.width) - screen_width);
+ if (x < 0)
+ x = 0;
+ if ((y + widget->requisition.height) > screen_height)
+ y -= ((y + widget->requisition.height) - screen_height);
+ if (y < 0)
+ y = 0;
+ }
+
+ gtk_widget_set_uposition (GTK_MENU_SHELL (menu)->active ?
+ menu->toplevel : menu->tearoff_window,
+ x, y);
+}
diff --git a/gtk/gtkmenu.h b/gtk/gtkmenu.h
index ac47f54ba..8600627dd 100644
--- a/gtk/gtkmenu.h
+++ b/gtk/gtkmenu.h
@@ -58,6 +58,11 @@ struct _GtkMenu
GtkAccelGroup *accel_group;
GtkMenuPositionFunc position_func;
gpointer position_func_data;
+
+ GtkWidget *toplevel;
+ GtkWidget *tearoff_window;
+
+ guint torn_off : 1;
};
struct _GtkMenuClass
@@ -94,7 +99,8 @@ void gtk_menu_attach_to_widget (GtkMenu *menu,
GtkMenuDetachFunc detacher);
GtkWidget* gtk_menu_get_attach_widget (GtkMenu *menu);
void gtk_menu_detach (GtkMenu *menu);
-
+void gtk_menu_set_tearoff_state (GtkMenu *menu,
+ gboolean torn_off);
#ifdef __cplusplus
}
diff --git a/gtk/gtkmenubar.c b/gtk/gtkmenubar.c
index 1983e3dd4..3f04e5193 100644
--- a/gtk/gtkmenubar.c
+++ b/gtk/gtkmenubar.c
@@ -16,6 +16,8 @@
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
+#include "gdk/gdkkeysyms.h"
+#include "gtkbindings.h"
#include "gtkmain.h"
#include "gtkmenubar.h"
#include "gtkmenuitem.h"
@@ -69,6 +71,8 @@ gtk_menu_bar_class_init (GtkMenuBarClass *class)
GtkWidgetClass *widget_class;
GtkMenuShellClass *menu_shell_class;
+ GtkBindingSet *binding_set;
+
widget_class = (GtkWidgetClass*) class;
menu_shell_class = (GtkMenuShellClass*) class;
@@ -78,6 +82,28 @@ gtk_menu_bar_class_init (GtkMenuBarClass *class)
widget_class->expose_event = gtk_menu_bar_expose;
menu_shell_class->submenu_placement = GTK_TOP_BOTTOM;
+
+ binding_set = gtk_binding_set_by_class (class);
+ gtk_binding_entry_add_signal (binding_set,
+ GDK_Left, 0,
+ "move_current", 1,
+ GTK_TYPE_MENU_DIRECTION_TYPE,
+ GTK_MENU_DIR_PREV);
+ gtk_binding_entry_add_signal (binding_set,
+ GDK_Right, 0,
+ "move_current", 1,
+ GTK_TYPE_MENU_DIRECTION_TYPE,
+ GTK_MENU_DIR_NEXT);
+ gtk_binding_entry_add_signal (binding_set,
+ GDK_Up, 0,
+ "move_current", 1,
+ GTK_TYPE_MENU_DIRECTION_TYPE,
+ GTK_MENU_DIR_PARENT);
+ gtk_binding_entry_add_signal (binding_set,
+ GDK_Down, 0,
+ "move_current", 1,
+ GTK_TYPE_MENU_DIRECTION_TYPE,
+ GTK_MENU_DIR_CHILD);
}
static void
diff --git a/gtk/gtkmenuitem.c b/gtk/gtkmenuitem.c
index e4bf0f0f9..ba79c57db 100644
--- a/gtk/gtkmenuitem.c
+++ b/gtk/gtkmenuitem.c
@@ -20,6 +20,7 @@
#include "gtkaccellabel.h"
#include "gtkmain.h"
#include "gtkmenu.h"
+#include "gtkmenubar.h"
#include "gtkmenuitem.h"
#include "gtksignal.h"
@@ -32,6 +33,7 @@
enum {
ACTIVATE,
+ ACTIVATE_ITEM,
LAST_SIGNAL
};
@@ -51,6 +53,7 @@ static gint gtk_menu_item_expose (GtkWidget *widget,
GdkEventExpose *event);
static void gtk_real_menu_item_select (GtkItem *item);
static void gtk_real_menu_item_deselect (GtkItem *item);
+static void gtk_real_menu_item_activate_item (GtkMenuItem *item);
static gint gtk_menu_item_select_timeout (gpointer data);
static void gtk_menu_item_position_menu (GtkMenu *menu,
gint *x,
@@ -110,6 +113,14 @@ gtk_menu_item_class_init (GtkMenuItemClass *klass)
gtk_marshal_NONE__NONE,
GTK_TYPE_NONE, 0);
+ menu_item_signals[ACTIVATE_ITEM] =
+ gtk_signal_new ("activate_item",
+ GTK_RUN_FIRST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (GtkMenuItemClass, activate_item),
+ gtk_signal_default_marshaller,
+ GTK_TYPE_NONE, 0);
+
gtk_object_class_add_signals (object_class, menu_item_signals, LAST_SIGNAL);
object_class->destroy = gtk_menu_item_destroy;
@@ -126,8 +137,10 @@ gtk_menu_item_class_init (GtkMenuItemClass *klass)
item_class->deselect = gtk_real_menu_item_deselect;
klass->activate = NULL;
+ klass->activate_item = gtk_real_menu_item_activate_item;
klass->toggle_size = 0;
+ klass->hide_on_activate = TRUE;
}
static void
@@ -493,8 +506,16 @@ gtk_real_menu_item_select (GtkItem *item)
menu_item = GTK_MENU_ITEM (item);
- if (menu_item->submenu && !GTK_WIDGET_VISIBLE (menu_item->submenu))
- menu_item->timer = gtk_timeout_add (SELECT_TIMEOUT, gtk_menu_item_select_timeout, menu_item);
+ /* if (menu_item->submenu && !GTK_WIDGET_VISIBLE (menu_item->submenu))*/
+ if (menu_item->submenu)
+ {
+ /* Boy this is a hack! */
+ GdkEvent *current_event = gtk_get_current_event();
+ if (current_event && (current_event->type != GDK_ENTER_NOTIFY))
+ gtk_menu_item_select_timeout (menu_item);
+ else
+ menu_item->timer = gtk_timeout_add (SELECT_TIMEOUT, gtk_menu_item_select_timeout, menu_item);
+ }
gtk_widget_set_state (GTK_WIDGET (menu_item), GTK_STATE_PRELIGHT);
gtk_widget_draw (GTK_WIDGET (menu_item), NULL);
@@ -522,6 +543,38 @@ gtk_real_menu_item_deselect (GtkItem *item)
gtk_widget_draw (GTK_WIDGET (menu_item), NULL);
}
+static void
+gtk_real_menu_item_activate_item (GtkMenuItem *menu_item)
+{
+ GtkWidget *widget;
+
+ g_return_if_fail (menu_item != NULL);
+ g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
+
+ widget = GTK_WIDGET (menu_item);
+
+ if (widget->parent &&
+ GTK_IS_MENU_SHELL (widget->parent))
+ {
+ if (menu_item->submenu == NULL)
+ gtk_menu_shell_activate_item (GTK_MENU_SHELL (widget->parent),
+ widget, TRUE);
+ else
+ {
+ GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget->parent);
+
+ if (!menu_shell->active)
+ {
+ gtk_grab_add (GTK_WIDGET (menu_shell));
+ menu_shell->have_grab = TRUE;
+ menu_shell->active = TRUE;
+ }
+
+ gtk_menu_shell_select_item (GTK_MENU_SHELL (widget->parent), widget);
+ }
+ }
+}
+
static gint
gtk_menu_item_select_timeout (gpointer data)
{
@@ -538,6 +591,16 @@ gtk_menu_item_select_timeout (gpointer data)
GTK_MENU_SHELL (GTK_WIDGET (menu_item)->parent)->button,
0);
+ /* This is a bit of a hack - we want to select the first item
+ * of menus hanging of a menu bar, but not for cascading submenus
+ */
+ if (GTK_IS_MENU_BAR (GTK_WIDGET (menu_item)->parent))
+ {
+ GtkMenuShell *submenu = GTK_MENU_SHELL (menu_item->submenu);
+ if (submenu->children)
+ gtk_menu_shell_select_item (submenu, submenu->children->data);
+ }
+
return FALSE;
}
diff --git a/gtk/gtkmenuitem.h b/gtk/gtkmenuitem.h
index 82a9244fe..fdfafed31 100644
--- a/gtk/gtkmenuitem.h
+++ b/gtk/gtkmenuitem.h
@@ -63,8 +63,16 @@ struct _GtkMenuItemClass
GtkItemClass parent_class;
guint toggle_size;
+ /* If the following flag is true, then we should always hide
+ * the menu when the MenuItem is activated. Otherwise, the
+ * it is up to the caller. For instance, when navigating
+ * a menu with the keyboard, <Space> doesn't hide, but
+ * <Return> does.
+ */
+ guint hide_on_activate : 1;
- void (* activate) (GtkMenuItem *menu_item);
+ void (* activate) (GtkMenuItem *menu_item);
+ void (* activate_item) (GtkMenuItem *menu_item);
};
diff --git a/gtk/gtkmenushell.c b/gtk/gtkmenushell.c
index d37155d5d..333e1c129 100644
--- a/gtk/gtkmenushell.c
+++ b/gtk/gtkmenushell.c
@@ -16,8 +16,11 @@
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
+#include "gdk/gdkkeysyms.h"
+#include "gtkbindings.h"
#include "gtkmain.h"
#include "gtkmenuitem.h"
+#include "gtktearoffmenuitem.h" /* FIXME */
#include "gtkmenushell.h"
#include "gtksignal.h"
@@ -29,9 +32,73 @@
enum {
DEACTIVATE,
SELECTION_DONE,
+ MOVE_CURRENT,
+ ACTIVATE_CURRENT,
+ CANCEL,
LAST_SIGNAL
};
+typedef void (*GtkMenuShellSignal1) (GtkObject *object,
+ GtkMenuDirectionType arg1,
+ gpointer data);
+typedef void (*GtkMenuShellSignal2) (GtkObject *object,
+ gboolean arg1,
+ gpointer data);
+
+/* Terminology:
+ *
+ * A menu item can be "selected", this means that it is displayed
+ * in the prelight state, and if it has a submenu, that submenu
+ * will be popped up.
+ *
+ * A menu is "active" when it is visible onscreen and the user
+ * is selecting from it. A menubar is not active until the user
+ * clicks on one of its menuitems. When a menu is active,
+ * passing the mouse over a submenu will pop it up.
+ *
+ * menu_shell->active_menu_item, is however, not an "active"
+ * menu item (there is no such thing) but rather, the selected
+ * menu item in that MenuShell, if there is one.
+ *
+ * There is also is a concept of the current menu and a current
+ * menu item. The current menu item is the selected menu item
+ * that is furthest down in the heirarchy. (Every active menu_shell
+ * does not necessarily contain a selected menu item, but if
+ * it does, then menu_shell->parent_menu_shell must also contain
+ * a selected menu item. The current menu is the menu that
+ * contains the current menu_item. It will always have a GTK
+ * grab and receive all key presses.
+ *
+ *
+ * Action signals:
+ *
+ * ::move_current (GtkMenuDirection *dir)
+ * Moves the current menu item in direction 'dir':
+ *
+ * GTK_MENU_DIR_PARENT: To the parent menu shell
+ * GTK_MENU_DIR_CHILD: To the child menu shell (if this item has
+ * a submenu.
+ * GTK_MENU_DIR_NEXT/PREV: To the next or previous item
+ * in this menu.
+ *
+ * As a a bit of a hack to get movement between menus and
+ * menubars working, if submenu_placement is different for
+ * the menu and its MenuShell then the following apply:
+ *
+ * - For 'parent' the current menu is not just moved to
+ * the parent, but moved to the previous entry in the parent
+ * - For 'child', if there is no child, then current is
+ * moved to the next item in the parent.
+ *
+ *
+ * ::activate_current (GBoolean *force_hide)
+ * Activate the current item. If 'force_hide' is true, hide
+ * the current menu item always. Otherwise, only hide
+ * it if menu_item->klass->hide_on_activate is true.
+ *
+ * ::cancel ()
+ * Cancels the current selection
+ */
static void gtk_menu_shell_class_init (GtkMenuShellClass *klass);
static void gtk_menu_shell_init (GtkMenuShell *menu_shell);
@@ -41,6 +108,8 @@ static gint gtk_menu_shell_button_press (GtkWidget *widget,
GdkEventButton *event);
static gint gtk_menu_shell_button_release (GtkWidget *widget,
GdkEventButton *event);
+static gint gtk_menu_shell_key_press (GtkWidget *widget,
+ GdkEventKey *event);
static gint gtk_menu_shell_enter_notify (GtkWidget *widget,
GdkEventCrossing *event);
static gint gtk_menu_shell_leave_notify (GtkWidget *widget,
@@ -57,8 +126,14 @@ static gint gtk_menu_shell_is_item (GtkMenuShell *menu_shell,
GtkWidget *child);
static GtkWidget *gtk_menu_shell_get_item (GtkMenuShell *menu_shell,
GdkEvent *event);
-static GtkType gtk_menu_shell_child_type (GtkContainer *container);
+static GtkType gtk_menu_shell_child_type (GtkContainer *container);
+static void gtk_menu_shell_deselect (GtkMenuShell *menu_shell);
+static void gtk_real_menu_shell_move_current (GtkMenuShell *menu_shell,
+ GtkMenuDirectionType direction);
+static void gtk_real_menu_shell_activate_current (GtkMenuShell *menu_shell,
+ gboolean force_hide);
+static void gtk_real_menu_shell_cancel (GtkMenuShell *menu_shell);
static GtkContainerClass *parent_class = NULL;
static guint menu_shell_signals[LAST_SIGNAL] = { 0 };
@@ -96,6 +171,8 @@ gtk_menu_shell_class_init (GtkMenuShellClass *klass)
GtkWidgetClass *widget_class;
GtkContainerClass *container_class;
+ GtkBindingSet *binding_set;
+
object_class = (GtkObjectClass*) klass;
widget_class = (GtkWidgetClass*) klass;
container_class = (GtkContainerClass*) klass;
@@ -116,12 +193,37 @@ gtk_menu_shell_class_init (GtkMenuShellClass *klass)
GTK_SIGNAL_OFFSET (GtkMenuShellClass, selection_done),
gtk_marshal_NONE__NONE,
GTK_TYPE_NONE, 0);
+ menu_shell_signals[MOVE_CURRENT] =
+ gtk_signal_new ("move_current",
+ GTK_RUN_LAST | GTK_RUN_ACTION,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (GtkMenuShellClass, move_current),
+ gtk_marshal_NONE__ENUM,
+ GTK_TYPE_NONE, 1,
+ GTK_TYPE_MENU_DIRECTION_TYPE);
+ menu_shell_signals[ACTIVATE_CURRENT] =
+ gtk_signal_new ("activate_current",
+ GTK_RUN_LAST | GTK_RUN_ACTION,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (GtkMenuShellClass, activate_current),
+ gtk_marshal_NONE__BOOL,
+ GTK_TYPE_NONE, 1,
+ GTK_TYPE_BOOL);
+ menu_shell_signals[CANCEL] =
+ gtk_signal_new ("cancel",
+ GTK_RUN_LAST | GTK_RUN_ACTION,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (GtkMenuShellClass, cancel),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+
gtk_object_class_add_signals (object_class, menu_shell_signals, LAST_SIGNAL);
widget_class->map = gtk_menu_shell_map;
widget_class->realize = gtk_menu_shell_realize;
widget_class->button_press_event = gtk_menu_shell_button_press;
widget_class->button_release_event = gtk_menu_shell_button_release;
+ widget_class->key_press_event = gtk_menu_shell_key_press;
widget_class->enter_notify_event = gtk_menu_shell_enter_notify;
widget_class->leave_notify_event = gtk_menu_shell_leave_notify;
@@ -133,6 +235,24 @@ gtk_menu_shell_class_init (GtkMenuShellClass *klass)
klass->submenu_placement = GTK_TOP_BOTTOM;
klass->deactivate = gtk_real_menu_shell_deactivate;
klass->selection_done = NULL;
+ klass->move_current = gtk_real_menu_shell_move_current;
+ klass->activate_current = gtk_real_menu_shell_activate_current;
+ klass->cancel = gtk_real_menu_shell_cancel;
+
+ binding_set = gtk_binding_set_by_class (klass);
+ gtk_binding_entry_add_signal (binding_set,
+ GDK_Escape, 0,
+ "cancel", 0);
+ gtk_binding_entry_add_signal (binding_set,
+ GDK_Return, 0,
+ "activate_current", 1,
+ GTK_TYPE_BOOL,
+ TRUE);
+ gtk_binding_entry_add_signal (binding_set,
+ GDK_space, 0,
+ "activate_current", 1,
+ GTK_TYPE_BOOL,
+ FALSE);
}
static GtkType
@@ -282,6 +402,7 @@ gtk_menu_shell_realize (GtkWidget *widget)
attributes.event_mask |= (GDK_EXPOSURE_MASK |
GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK |
+ GDK_KEY_PRESS_MASK |
GDK_ENTER_NOTIFY_MASK |
GDK_LEAVE_NOTIFY_MASK);
@@ -388,16 +509,7 @@ gtk_menu_shell_button_release (GtkWidget *widget,
{
if (GTK_MENU_ITEM (menu_item)->submenu == NULL)
{
- gtk_menu_shell_deactivate (menu_shell);
-
- /* flush the x-queue, so any grabs are removed and
- * the menu is actually taken down
- */
- gdk_flush ();
- gtk_widget_activate (menu_item);
-
- gtk_signal_emit (GTK_OBJECT (menu_shell), menu_shell_signals[SELECTION_DONE]);
-
+ gtk_menu_shell_activate_item (menu_shell, menu_item, TRUE);
return TRUE;
}
}
@@ -434,6 +546,32 @@ gtk_menu_shell_button_release (GtkWidget *widget,
}
static gint
+gtk_menu_shell_key_press (GtkWidget *widget,
+ GdkEventKey *event)
+{
+ GtkMenuShell *menu_shell;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ menu_shell = GTK_MENU_SHELL (widget);
+
+ if (!menu_shell->active_menu_item && menu_shell->parent_menu_shell)
+ return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent *)event);
+
+ if (gtk_bindings_activate (GTK_OBJECT (widget),
+ event->keyval,
+ event->state))
+ return TRUE;
+
+ if (gtk_accel_groups_activate (GTK_OBJECT (widget), event->keyval, event->state))
+ return TRUE;
+
+ return FALSE;
+}
+
+static gint
gtk_menu_shell_enter_notify (GtkWidget *widget,
GdkEventCrossing *event)
{
@@ -445,7 +583,8 @@ gtk_menu_shell_enter_notify (GtkWidget *widget,
g_return_val_if_fail (event != NULL, FALSE);
menu_shell = GTK_MENU_SHELL (widget);
- if (menu_shell->active)
+
+ if (menu_shell->active && !menu_shell->ignore_enter)
{
menu_item = gtk_get_event_widget ((GdkEvent*) event);
@@ -459,15 +598,7 @@ gtk_menu_shell_enter_notify (GtkWidget *widget,
if ((event->detail != GDK_NOTIFY_INFERIOR) &&
(GTK_WIDGET_STATE (menu_item) != GTK_STATE_PRELIGHT))
{
- if (menu_shell->active_menu_item)
- gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
-
- menu_shell->active_menu_item = menu_item;
- gtk_menu_item_set_placement (GTK_MENU_ITEM (menu_shell->active_menu_item),
- MENU_SHELL_CLASS (menu_shell)->submenu_placement);
- gtk_menu_item_select (GTK_MENU_ITEM (menu_shell->active_menu_item));
- if (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
- gtk_widget_activate (menu_shell->active_menu_item);
+ gtk_menu_shell_select_item (menu_shell, menu_item);
}
}
else if (menu_shell->parent_menu_shell)
@@ -516,8 +647,7 @@ gtk_menu_shell_leave_notify (GtkWidget *widget,
if ((event->detail != GDK_NOTIFY_INFERIOR) &&
(GTK_WIDGET_STATE (menu_item) != GTK_STATE_NORMAL))
{
- gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
- menu_shell->active_menu_item = NULL;
+ gtk_menu_shell_deselect (menu_shell);
}
}
else if (menu_shell->parent_menu_shell)
@@ -613,6 +743,7 @@ gtk_real_menu_shell_deactivate (GtkMenuShell *menu_shell)
{
menu_shell->have_xgrab = FALSE;
gdk_pointer_ungrab (GDK_CURRENT_TIME);
+ gdk_keyboard_ungrab (GDK_CURRENT_TIME);
}
}
}
@@ -655,3 +786,181 @@ gtk_menu_shell_get_item (GtkMenuShell *menu_shell,
return NULL;
}
+/* Handlers for action signals */
+
+void
+gtk_menu_shell_select_item (GtkMenuShell *menu_shell,
+ GtkWidget *menu_item)
+{
+ g_return_if_fail (menu_shell != NULL);
+ g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
+ g_return_if_fail (menu_item != NULL);
+ g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
+
+ if (menu_shell->active_menu_item)
+ gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
+
+ menu_shell->active_menu_item = menu_item;
+ gtk_menu_item_set_placement (GTK_MENU_ITEM (menu_shell->active_menu_item),
+ MENU_SHELL_CLASS (menu_shell)->submenu_placement);
+ gtk_menu_item_select (GTK_MENU_ITEM (menu_shell->active_menu_item));
+
+ /* This allows the bizarre radio buttons-with-submenus-display-history
+ * behavior
+ */
+ if (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
+ gtk_widget_activate (menu_shell->active_menu_item);
+}
+
+static void
+gtk_menu_shell_deselect (GtkMenuShell *menu_shell)
+{
+ gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
+ menu_shell->active_menu_item = NULL;
+}
+
+void
+gtk_menu_shell_activate_item (GtkMenuShell *menu_shell,
+ GtkWidget *menu_item,
+ gboolean force_deactivate)
+{
+ gboolean deactivate = force_deactivate;
+
+ g_return_if_fail (menu_shell != NULL);
+ g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
+ g_return_if_fail (menu_item != NULL);
+ g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
+
+ if (!deactivate)
+ {
+ deactivate = GTK_MENU_ITEM_CLASS (GTK_OBJECT (menu_item)->klass)->hide_on_activate;
+ }
+
+ if (deactivate)
+ {
+ gtk_menu_shell_deactivate (menu_shell);
+
+ /* flush the x-queue, so any grabs are removed and
+ * the menu is actually taken down
+ */
+ gdk_flush ();
+ }
+ gtk_widget_activate (menu_item);
+
+ if (deactivate)
+ gtk_signal_emit (GTK_OBJECT (menu_shell), menu_shell_signals[SELECTION_DONE]);
+}
+
+/* Distance should be +/- 1 */
+static void
+gtk_menu_shell_move_selected (GtkMenuShell *menu_shell,
+ gint distance)
+{
+ if (menu_shell->active_menu_item)
+ {
+ GList *node = g_list_find (menu_shell->children,
+ menu_shell->active_menu_item);
+ GList *start_node = node;
+
+ if (distance > 0)
+ {
+ node = node->next;
+ while (node != start_node &&
+ (!node || !GTK_WIDGET_SENSITIVE (node->data)))
+ {
+ if (!node)
+ node = menu_shell->children;
+ else
+ node = node->next;
+ }
+ }
+ else
+ {
+ node = node->prev;
+ while (node != start_node &&
+ (!node || !GTK_WIDGET_SENSITIVE (node->data)))
+ {
+ if (!node)
+ node = g_list_last (menu_shell->children);
+ else
+ node = node->prev;
+ }
+ }
+
+ if (node)
+ gtk_menu_shell_select_item (menu_shell, node->data);
+ }
+}
+
+static void
+gtk_real_menu_shell_move_current (GtkMenuShell *menu_shell,
+ GtkMenuDirectionType direction)
+{
+ GtkMenuShell *parent_menu_shell = NULL;
+
+ if (menu_shell->parent_menu_shell)
+ parent_menu_shell = GTK_MENU_SHELL (menu_shell->parent_menu_shell);
+
+ switch (direction)
+ {
+ case GTK_MENU_DIR_PARENT:
+ if (parent_menu_shell)
+ {
+ if (GTK_MENU_SHELL_CLASS (GTK_OBJECT (parent_menu_shell)->klass)->submenu_placement ==
+ GTK_MENU_SHELL_CLASS (GTK_OBJECT (menu_shell)->klass)->submenu_placement)
+ gtk_menu_shell_deselect (menu_shell);
+ else
+ gtk_menu_shell_move_selected (parent_menu_shell, -1);
+ }
+ break;
+
+ case GTK_MENU_DIR_CHILD:
+ if (GTK_BIN (menu_shell->active_menu_item)->child &&
+ GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
+ {
+ menu_shell = GTK_MENU_SHELL (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu);
+ if (menu_shell->children)
+ gtk_menu_shell_select_item (menu_shell, menu_shell->children->data);
+ }
+ else
+ {
+ /* Try to find a menu running the opposite direction */
+ while (parent_menu_shell &&
+ (GTK_MENU_SHELL_CLASS (GTK_OBJECT (parent_menu_shell)->klass)->submenu_placement ==
+ GTK_MENU_SHELL_CLASS (GTK_OBJECT (menu_shell)->klass)->submenu_placement))
+ parent_menu_shell = GTK_MENU_SHELL (parent_menu_shell->parent_menu_shell);
+
+ if (parent_menu_shell)
+ gtk_menu_shell_move_selected (parent_menu_shell, 1);
+ }
+ break;
+
+ case GTK_MENU_DIR_PREV:
+ gtk_menu_shell_move_selected (menu_shell, -1);
+ break;
+ case GTK_MENU_DIR_NEXT:
+ gtk_menu_shell_move_selected (menu_shell, 1);
+ break;
+ }
+}
+
+static void
+gtk_real_menu_shell_activate_current (GtkMenuShell *menu_shell,
+ gboolean force_hide)
+{
+ if (menu_shell->active_menu_item &&
+ GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu == NULL)
+ {
+ gtk_menu_shell_activate_item (menu_shell,
+ menu_shell->active_menu_item,
+ force_hide);
+ }
+}
+
+static void
+gtk_real_menu_shell_cancel (GtkMenuShell *menu_shell)
+{
+ gtk_menu_shell_deactivate (menu_shell);
+ gtk_signal_emit (GTK_OBJECT (menu_shell), menu_shell_signals[SELECTION_DONE]);
+}
+
diff --git a/gtk/gtkmenushell.h b/gtk/gtkmenushell.h
index 808e50ab8..965551e31 100644
--- a/gtk/gtkmenushell.h
+++ b/gtk/gtkmenushell.h
@@ -54,6 +54,7 @@ struct _GtkMenuShell
guint button : 2;
guint ignore_leave : 1;
guint menu_flag : 1;
+ guint ignore_enter : 1;
guint32 activate_time;
};
@@ -66,6 +67,12 @@ struct _GtkMenuShellClass
void (*deactivate) (GtkMenuShell *menu_shell);
void (*selection_done) (GtkMenuShell *menu_shell);
+
+ void (*move_current) (GtkMenuShell *menu_shell,
+ GtkMenuDirectionType direction);
+ void (*activate_current) (GtkMenuShell *menu_shell,
+ gboolean force_hide);
+ void (*cancel) (GtkMenuShell *menu_shell);
};
@@ -78,6 +85,11 @@ void gtk_menu_shell_insert (GtkMenuShell *menu_shell,
GtkWidget *child,
gint position);
void gtk_menu_shell_deactivate (GtkMenuShell *menu_shell);
+void gtk_menu_shell_select_item (GtkMenuShell *menu_shell,
+ GtkWidget *menu_item);
+void gtk_menu_shell_activate_item (GtkMenuShell *menu_shell,
+ GtkWidget *menu_item,
+ gboolean force_deactivate);
#ifdef __cplusplus
diff --git a/gtk/gtktearoffmenuitem.c b/gtk/gtktearoffmenuitem.c
new file mode 100644
index 000000000..432754d5b
--- /dev/null
+++ b/gtk/gtktearoffmenuitem.c
@@ -0,0 +1,250 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#include "gtkmenu.h"
+#include "gtksignal.h"
+#include "gtktearoffmenuitem.h"
+
+#define ARROW_SIZE 10
+#define TEAR_LENGTH 5
+#define BORDER_SPACING 3
+
+static void gtk_tearoff_menu_item_class_init (GtkTearoffMenuItemClass *klass);
+static void gtk_tearoff_menu_item_init (GtkTearoffMenuItem *tearoff_menu_item);
+static void gtk_tearoff_menu_item_size_request (GtkWidget *widget,
+ GtkRequisition *requisition);
+static void gtk_tearoff_menu_item_draw (GtkWidget *widget,
+ GdkRectangle *area);
+static gint gtk_tearoff_menu_item_expose (GtkWidget *widget,
+ GdkEventExpose *event);
+static void gtk_tearoff_menu_item_activate (GtkMenuItem *menu_item);
+static gint gtk_tearoff_menu_item_delete_cb (GtkMenuItem *menu_item,
+ GdkEventAny *event);
+
+GtkType
+gtk_tearoff_menu_item_get_type (void)
+{
+ static GtkType tearoff_menu_item_type = 0;
+
+ if (!tearoff_menu_item_type)
+ {
+ GtkTypeInfo tearoff_menu_item_info =
+ {
+ "GtkTearoffMenuItem",
+ sizeof (GtkTearoffMenuItem),
+ sizeof (GtkTearoffMenuItemClass),
+ (GtkClassInitFunc) gtk_tearoff_menu_item_class_init,
+ (GtkObjectInitFunc) gtk_tearoff_menu_item_init,
+ /* reserved_1 */ NULL,
+ /* reserved_2 */ NULL,
+ (GtkClassInitFunc) NULL,
+ };
+
+ tearoff_menu_item_type = gtk_type_unique (gtk_menu_item_get_type (), &tearoff_menu_item_info);
+ }
+
+ return tearoff_menu_item_type;
+}
+
+GtkWidget*
+gtk_tearoff_menu_item_new (void)
+{
+ return GTK_WIDGET (gtk_type_new (gtk_tearoff_menu_item_get_type ()));
+}
+
+static void
+gtk_tearoff_menu_item_class_init (GtkTearoffMenuItemClass *klass)
+{
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+ GtkMenuItemClass *menu_item_class;
+
+ object_class = (GtkObjectClass*) klass;
+ widget_class = (GtkWidgetClass*) klass;
+ menu_item_class = (GtkMenuItemClass*) klass;
+
+ widget_class->draw = gtk_tearoff_menu_item_draw;
+ widget_class->expose_event = gtk_tearoff_menu_item_expose;
+ widget_class->size_request = gtk_tearoff_menu_item_size_request;
+
+ menu_item_class->activate = gtk_tearoff_menu_item_activate;
+}
+
+static void
+gtk_tearoff_menu_item_init (GtkTearoffMenuItem *tearoff_menu_item)
+{
+ tearoff_menu_item->torn_off = FALSE;
+}
+
+static void
+gtk_tearoff_menu_item_size_request (GtkWidget *widget,
+ GtkRequisition *requisition)
+{
+ GtkTearoffMenuItem *tearoff;
+
+ tearoff = GTK_TEAROFF_MENU_ITEM (widget);
+
+ requisition->width = (GTK_CONTAINER (widget)->border_width +
+ widget->style->klass->xthickness +
+ BORDER_SPACING) * 2;
+ requisition->height = (GTK_CONTAINER (widget)->border_width +
+ widget->style->klass->ythickness) * 2;
+
+ if (tearoff->torn_off)
+ {
+ requisition->height += ARROW_SIZE;
+ }
+ else
+ {
+ requisition->height += widget->style->klass->ythickness;
+ }
+}
+
+static void
+gtk_tearoff_menu_item_paint (GtkWidget *widget,
+ GdkRectangle *area)
+{
+ GtkMenuItem *menu_item;
+ GtkTearoffMenuItem *tearoff_item;
+ GtkShadowType shadow_type;
+ gint width, height;
+ gint x, y;
+ gint right_max;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GTK_IS_TEAROFF_MENU_ITEM (widget));
+
+ if (GTK_WIDGET_DRAWABLE (widget))
+ {
+ menu_item = GTK_MENU_ITEM (widget);
+ tearoff_item = GTK_TEAROFF_MENU_ITEM (widget);
+
+ gtk_style_set_background (widget->style, widget->window, widget->state);
+ gdk_window_clear_area (widget->window, area->x, area->y, area->width, area->height);
+
+ x = GTK_CONTAINER (menu_item)->border_width;
+ y = GTK_CONTAINER (menu_item)->border_width;
+ width = widget->allocation.width - x * 2;
+ height = widget->allocation.height - y * 2;
+ right_max = x + width;
+
+ if (widget->state == GTK_STATE_PRELIGHT)
+ gtk_draw_shadow (widget->style,
+ widget->window,
+ GTK_STATE_PRELIGHT,
+ GTK_SHADOW_OUT,
+ x, y, width, height);
+
+ if (tearoff_item->torn_off)
+ {
+ gint arrow_x;
+
+ if (widget->state == GTK_STATE_PRELIGHT)
+ shadow_type = GTK_SHADOW_IN;
+ else
+ shadow_type = GTK_SHADOW_OUT;
+
+ if (menu_item->toggle_size > ARROW_SIZE)
+ {
+ arrow_x = x + (menu_item->toggle_size - ARROW_SIZE)/2;
+ x += menu_item->toggle_size + BORDER_SPACING;
+ }
+ else
+ {
+ arrow_x = ARROW_SIZE / 2;
+ x += 2 * ARROW_SIZE;
+ }
+
+ gtk_draw_arrow (widget->style, widget->window,
+ widget->state, shadow_type, GTK_ARROW_LEFT, FALSE,
+ arrow_x, y + height / 2 - 5,
+ ARROW_SIZE, ARROW_SIZE);
+ }
+
+ while (x < right_max)
+ {
+ gtk_draw_hline (widget->style, widget->window, GTK_STATE_NORMAL,
+ x, MIN (x+TEAR_LENGTH, right_max),
+ y + (height - widget->style->klass->ythickness)/2);
+ x += 2 * TEAR_LENGTH;
+ }
+ }
+}
+
+static void
+gtk_tearoff_menu_item_draw (GtkWidget *widget,
+ GdkRectangle *area)
+{
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GTK_IS_TEAROFF_MENU_ITEM (widget));
+ g_return_if_fail (area != NULL);
+
+ gtk_tearoff_menu_item_paint (widget, area);
+}
+
+static gint
+gtk_tearoff_menu_item_expose (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_TEAROFF_MENU_ITEM (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ gtk_tearoff_menu_item_paint (widget, &event->area);
+
+ return FALSE;
+}
+
+static gint
+gtk_tearoff_menu_item_delete_cb (GtkMenuItem *menu_item, GdkEventAny *event)
+{
+ gtk_tearoff_menu_item_activate (menu_item);
+ return TRUE;
+}
+
+static void
+gtk_tearoff_menu_item_activate (GtkMenuItem *menu_item)
+{
+ GtkTearoffMenuItem *tearoff_menu_item;
+
+ g_return_if_fail (menu_item != NULL);
+ g_return_if_fail (GTK_IS_TEAROFF_MENU_ITEM (menu_item));
+
+ tearoff_menu_item = GTK_TEAROFF_MENU_ITEM (menu_item);
+ tearoff_menu_item->torn_off = !tearoff_menu_item->torn_off;
+
+ if (GTK_IS_MENU (GTK_WIDGET (menu_item)->parent))
+ {
+ GtkMenu *menu = GTK_MENU (GTK_WIDGET (menu_item)->parent);
+ gboolean need_connect;
+
+ need_connect = (tearoff_menu_item->torn_off && !menu->tearoff_window);
+
+ gtk_menu_set_tearoff_state (GTK_MENU (GTK_WIDGET (menu_item)->parent),
+ tearoff_menu_item->torn_off);
+
+ if (need_connect)
+ gtk_signal_connect_object (GTK_OBJECT (menu->tearoff_window),
+ "delete_event",
+ GTK_SIGNAL_FUNC (gtk_tearoff_menu_item_delete_cb),
+ GTK_OBJECT (menu_item));
+ }
+
+ gtk_widget_queue_resize (GTK_WIDGET (menu_item));
+}
+
diff --git a/gtk/gtktearoffmenuitem.h b/gtk/gtktearoffmenuitem.h
new file mode 100644
index 000000000..614d361fe
--- /dev/null
+++ b/gtk/gtktearoffmenuitem.h
@@ -0,0 +1,64 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __GTK_MENU_TEAROFF_ITEM_H__
+#define __GTK_MENU_TEAROFF_ITEM_H__
+
+
+#include <gdk/gdk.h>
+#include <gtk/gtkmenuitem.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+
+#define GTK_TYPE_TEAROFF_MENU_ITEM (gtk_tearoff_menu_item_get_type ())
+#define GTK_TEAROFF_MENU_ITEM(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_TEAROFF_MENU_ITEM, GtkTearoffMenuItem))
+#define GTK_TEAROFF_MENU_ITEM_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_TEAROFF_MENU_ITEM, GtkTearoffMenuItemClass))
+#define GTK_IS_TEAROFF_MENU_ITEM(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_TEAROFF_MENU_ITEM))
+#define GTK_IS_TEAROFF_MENU_ITEM_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TEAROFF_MENU_ITEM))
+
+
+typedef struct _GtkTearoffMenuItem GtkTearoffMenuItem;
+typedef struct _GtkTearoffMenuItemClass GtkTearoffMenuItemClass;
+
+struct _GtkTearoffMenuItem
+{
+ GtkMenuItem menu_item;
+
+ guint torn_off : 1;
+};
+
+struct _GtkTearoffMenuItemClass
+{
+ GtkMenuItemClass parent_class;
+};
+
+
+GtkType gtk_tearoff_menu_item_get_type (void);
+GtkWidget* gtk_tearoff_menu_item_new (void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_TEAROFF_MENU_ITEM_H__ */
diff --git a/gtk/gtktoolbar.c b/gtk/gtktoolbar.c
index c38ca05fe..6e62117ad 100644
--- a/gtk/gtktoolbar.c
+++ b/gtk/gtktoolbar.c
@@ -775,6 +775,8 @@ gtk_toolbar_insert_element (GtkToolbar *toolbar,
gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (child->widget), FALSE);
}
+ GTK_WIDGET_UNSET_FLAGS (child->widget, GTK_CAN_FOCUS);
+
if (callback)
gtk_signal_connect (GTK_OBJECT (child->widget), "clicked",
callback, user_data);
diff --git a/gtk/gtktypebuiltins.h b/gtk/gtktypebuiltins.h
index 156a4aa5f..ca787940c 100644
--- a/gtk/gtktypebuiltins.h
+++ b/gtk/gtktypebuiltins.h
@@ -13,6 +13,7 @@ extern GtkType GTK_TYPE_CURVE_TYPE;
extern GtkType GTK_TYPE_DIRECTION_TYPE;
extern GtkType GTK_TYPE_JUSTIFICATION;
extern GtkType GTK_TYPE_MATCH_TYPE;
+extern GtkType GTK_TYPE_MENU_DIRECTION_TYPE;
extern GtkType GTK_TYPE_MENU_FACTORY_TYPE;
extern GtkType GTK_TYPE_METRIC_TYPE;
extern GtkType GTK_TYPE_ORIENTATION;
diff --git a/gtk/gtktypebuiltins_evals.c b/gtk/gtktypebuiltins_evals.c
index ba0eee19d..53d010f89 100644
--- a/gtk/gtktypebuiltins_evals.c
+++ b/gtk/gtktypebuiltins_evals.c
@@ -95,6 +95,13 @@ static GtkEnumValue _gtk_match_type_values[] = {
{ GTK_MATCH_LAST, "GTK_MATCH_LAST", "last" },
{ 0, NULL, NULL }
};
+static GtkEnumValue _gtk_menu_direction_type_values[] = {
+ { GTK_MENU_DIR_PARENT, "GTK_MENU_DIR_PARENT", "parent" },
+ { GTK_MENU_DIR_CHILD, "GTK_MENU_DIR_CHILD", "child" },
+ { GTK_MENU_DIR_NEXT, "GTK_MENU_DIR_NEXT", "next" },
+ { GTK_MENU_DIR_PREV, "GTK_MENU_DIR_PREV", "prev" },
+ { 0, NULL, NULL }
+};
static GtkEnumValue _gtk_menu_factory_type_values[] = {
{ GTK_MENU_FACTORY_MENU, "GTK_MENU_FACTORY_MENU", "menu" },
{ GTK_MENU_FACTORY_MENU_BAR, "GTK_MENU_FACTORY_MENU_BAR", "menu-bar" },
diff --git a/gtk/gtktypebuiltins_ids.c b/gtk/gtktypebuiltins_ids.c
index feb499a9a..f348794a9 100644
--- a/gtk/gtktypebuiltins_ids.c
+++ b/gtk/gtktypebuiltins_ids.c
@@ -26,6 +26,8 @@
GTK_TYPE_ENUM, _gtk_justification_values },
{ "GtkMatchType", &GTK_TYPE_MATCH_TYPE,
GTK_TYPE_ENUM, _gtk_match_type_values },
+ { "GtkMenuDirectionType", &GTK_TYPE_MENU_DIRECTION_TYPE,
+ GTK_TYPE_ENUM, _gtk_menu_direction_type_values },
{ "GtkMenuFactoryType", &GTK_TYPE_MENU_FACTORY_TYPE,
GTK_TYPE_ENUM, _gtk_menu_factory_type_values },
{ "GtkMetricType", &GTK_TYPE_METRIC_TYPE,
diff --git a/gtk/gtktypebuiltins_vars.c b/gtk/gtktypebuiltins_vars.c
index e0ae2a8a4..921dfeb27 100644
--- a/gtk/gtktypebuiltins_vars.c
+++ b/gtk/gtktypebuiltins_vars.c
@@ -13,6 +13,7 @@ GtkType GTK_TYPE_CURVE_TYPE = 0;
GtkType GTK_TYPE_DIRECTION_TYPE = 0;
GtkType GTK_TYPE_JUSTIFICATION = 0;
GtkType GTK_TYPE_MATCH_TYPE = 0;
+GtkType GTK_TYPE_MENU_DIRECTION_TYPE = 0;
GtkType GTK_TYPE_MENU_FACTORY_TYPE = 0;
GtkType GTK_TYPE_METRIC_TYPE = 0;
GtkType GTK_TYPE_ORIENTATION = 0;
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 647963995..f22179a5a 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -113,7 +113,7 @@ typedef struct _GtkStateData GtkStateData;
struct _GtkStateData
{
GtkStateType state;
- guint state_restauration : 1;
+ guint state_restoration : 1;
guint parent_sensitive : 1;
};
@@ -2479,7 +2479,7 @@ gtk_widget_set_state (GtkWidget *widget,
GtkStateData data;
data.state = state;
- data.state_restauration = FALSE;
+ data.state_restoration = FALSE;
if (widget->parent)
data.parent_sensitive = (GTK_WIDGET_IS_SENSITIVE (widget->parent) != FALSE);
else
@@ -2524,7 +2524,7 @@ gtk_widget_set_sensitive (GtkWidget *widget,
GTK_WIDGET_UNSET_FLAGS (widget, GTK_SENSITIVE);
data.state = GTK_WIDGET_STATE (widget);
}
- data.state_restauration = TRUE;
+ data.state_restoration = TRUE;
if (widget->parent)
data.parent_sensitive = (GTK_WIDGET_IS_SENSITIVE (widget->parent) != FALSE);
@@ -2566,7 +2566,7 @@ gtk_widget_set_parent (GtkWidget *widget,
data.state = GTK_WIDGET_STATE (parent);
else
data.state = GTK_WIDGET_STATE (widget);
- data.state_restauration = FALSE;
+ data.state_restoration = FALSE;
data.parent_sensitive = (GTK_WIDGET_IS_SENSITIVE (parent) != FALSE);
gtk_widget_propagate_state (widget, &data);
@@ -3767,7 +3767,7 @@ gtk_widget_propagate_state (GtkWidget *widget,
if (GTK_WIDGET_IS_SENSITIVE (widget))
{
- if (data->state_restauration)
+ if (data->state_restoration)
GTK_WIDGET_STATE (widget) = GTK_WIDGET_SAVED_STATE (widget);
else
GTK_WIDGET_STATE (widget) = data->state;
@@ -3775,7 +3775,7 @@ gtk_widget_propagate_state (GtkWidget *widget,
else
{
GTK_WIDGET_STATE (widget) = GTK_STATE_INSENSITIVE;
- if (!data->state_restauration &&
+ if (!data->state_restoration &&
data->state != GTK_STATE_INSENSITIVE)
GTK_WIDGET_SAVED_STATE (widget) = data->state;
}
@@ -3783,7 +3783,7 @@ gtk_widget_propagate_state (GtkWidget *widget,
else
{
GTK_WIDGET_UNSET_FLAGS (widget, GTK_PARENT_SENSITIVE);
- if (!data->state_restauration)
+ if (!data->state_restoration)
{
if (data->state != GTK_STATE_INSENSITIVE)
GTK_WIDGET_SAVED_STATE (widget) = data->state;
diff --git a/gtk/testgtk.c b/gtk/testgtk.c
index dec2fa2d9..6962c27a2 100644
--- a/gtk/testgtk.c
+++ b/gtk/testgtk.c
@@ -1949,7 +1949,7 @@ create_tooltips (void)
*/
static GtkWidget*
-create_menu (int depth)
+create_menu (gint depth, gboolean tearoff)
{
GtkWidget *menu;
GtkWidget *menuitem;
@@ -1963,6 +1963,13 @@ create_menu (int depth)
menu = gtk_menu_new ();
group = NULL;
+ if (tearoff)
+ {
+ menuitem = gtk_tearoff_menu_item_new ();
+ gtk_menu_append (GTK_MENU (menu), menuitem);
+ gtk_widget_show (menuitem);
+ }
+
for (i = 0, j = 1; i < 5; i++, j++)
{
sprintf (buf, "item %2d - %d", depth, j);
@@ -1975,7 +1982,7 @@ create_menu (int depth)
if (i == 3)
gtk_widget_set_sensitive (menuitem, FALSE);
- gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), create_menu (depth - 1));
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), create_menu (depth - 1, TRUE));
}
return menu;
@@ -2007,6 +2014,9 @@ create_menus (void)
GTK_SIGNAL_FUNC (gtk_true),
NULL);
+ accel_group = gtk_accel_group_new ();
+ gtk_accel_group_attach (accel_group, GTK_OBJECT (window));
+
gtk_window_set_title (GTK_WINDOW (window), "menus");
gtk_container_border_width (GTK_CONTAINER (window), 0);
@@ -2019,7 +2029,7 @@ create_menus (void)
gtk_box_pack_start (GTK_BOX (box1), menubar, FALSE, TRUE, 0);
gtk_widget_show (menubar);
- menu = create_menu (2);
+ menu = create_menu (2, TRUE);
menuitem = gtk_menu_item_new_with_label ("test\nline2");
gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), menu);
@@ -2027,12 +2037,12 @@ create_menus (void)
gtk_widget_show (menuitem);
menuitem = gtk_menu_item_new_with_label ("foo");
- gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), create_menu (3));
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), create_menu (3, TRUE));
gtk_menu_bar_append (GTK_MENU_BAR (menubar), menuitem);
gtk_widget_show (menuitem);
-
+
menuitem = gtk_menu_item_new_with_label ("bar");
- gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), create_menu (4));
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), create_menu (4, TRUE));
gtk_menu_item_right_justify (GTK_MENU_ITEM (menuitem));
gtk_menu_bar_append (GTK_MENU_BAR (menubar), menuitem);
gtk_widget_show (menuitem);
@@ -2042,8 +2052,7 @@ create_menus (void)
gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0);
gtk_widget_show (box2);
- menu = create_menu (1);
- accel_group = gtk_accel_group_get_default ();
+ menu = create_menu (1, FALSE);
gtk_menu_set_accel_group (GTK_MENU (menu), accel_group);
menuitem = gtk_check_menu_item_new_with_label ("Accelerate Me");
@@ -2112,6 +2121,99 @@ create_menus (void)
gtk_widget_destroy (window);
}
+static GtkItemFactoryEntry menu_items[] =
+{
+ { "/_File", NULL, NULL, 0, "<Branch>" },
+ { "/File/tearoff1", NULL, NULL, 0, "<Tearoff>" },
+ { "/File/_New", "<control>N", NULL, 0 },
+ { "/File/_Open", "<control>O", NULL, 0 },
+ { "/File/_Save", "<control>S", NULL, 0 },
+ { "/File/Save _As...", NULL, NULL, 0 },
+ { "/File/sep1", NULL, NULL, 0, "<Separator>" },
+ { "/File/_Quit", "<control>Q", NULL, 0 },
+
+ { "/_Preferences", NULL, NULL, 0, "<Branch>" },
+ { "/_Preferences/_Color", NULL, NULL, 0, "<Branch>" },
+ { "/_Preferences/Color/_Red", NULL, NULL, 0, "<RadioItem>" },
+ { "/_Preferences/Color/_Green", NULL, NULL, 0, "<RadioItem>" },
+ { "/_Preferences/Color/_Blue", NULL, NULL, 0, "<RadioItem>" },
+ { "/_Preferences/_Shape", NULL, NULL, 0, "<Branch>" },
+ { "/_Preferences/Shape/_Square", NULL, NULL, 0, "<RadioItem>" },
+ { "/_Preferences/Shape/_Rectangle", NULL, NULL, 0, "<RadioItem>" },
+ { "/_Preferences/Shape/_Oval", NULL, NULL, 0, "<RadioItem>" },
+
+ { "/_Help", NULL, NULL, 0, "<LastBranch>" },
+ { "/Help/_About", NULL, NULL, 0 },
+};
+
+static int nmenu_items = sizeof (menu_items) / sizeof (menu_items[0]);
+
+static void
+create_item_factory (void)
+{
+ static GtkWidget *window = NULL;
+
+ if (!window)
+ {
+ GtkWidget *box1;
+ GtkWidget *box2;
+ GtkWidget *separator;
+ GtkWidget *label;
+ GtkWidget *button;
+ GtkAccelGroup *accel_group;
+ GtkItemFactory *item_factory;
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_signal_connect (GTK_OBJECT (window), "destroy",
+ GTK_SIGNAL_FUNC(gtk_widget_destroyed),
+ &window);
+ gtk_signal_connect (GTK_OBJECT (window), "delete-event",
+ GTK_SIGNAL_FUNC (gtk_true),
+ NULL);
+
+ accel_group = gtk_accel_group_new ();
+ item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>", accel_group);
+ gtk_item_factory_create_items (item_factory, nmenu_items, menu_items, NULL);
+ gtk_accel_group_attach (accel_group, GTK_OBJECT (window));
+ gtk_window_set_title (GTK_WINDOW (window), "Item Factory");
+ gtk_container_border_width (GTK_CONTAINER (window), 0);
+
+ box1 = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (window), box1);
+
+ gtk_box_pack_start (GTK_BOX (box1),
+ gtk_item_factory_get_widget (item_factory, "<main>"),
+ FALSE, FALSE, 0);
+
+ label = gtk_label_new ("Type\n<alt>\nto start");
+ gtk_widget_set_usize (label, 200, 200);
+ gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
+ gtk_box_pack_start (GTK_BOX (box1), label, TRUE, TRUE, 0);
+
+
+ separator = gtk_hseparator_new ();
+ gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0);
+
+
+ box2 = gtk_vbox_new (FALSE, 10);
+ gtk_container_border_width (GTK_CONTAINER (box2), 10);
+ gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, TRUE, 0);
+
+ button = gtk_button_new_with_label ("close");
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ GTK_OBJECT (window));
+ gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
+ GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+ gtk_widget_grab_default (button);
+
+ gtk_widget_show_all (window);
+ }
+ else
+ gtk_widget_destroy (window);
+}
+
/*
* GtkScrolledWindow
*/
@@ -3145,6 +3247,7 @@ create_list (void)
separator = gtk_hseparator_new ();
gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0);
+
box2 = gtk_vbox_new (FALSE, 10);
gtk_container_border_width (GTK_CONTAINER (box2), 10);
gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, TRUE, 0);
@@ -7291,6 +7394,7 @@ create_main_window (void)
{ "font selection", create_font_selection },
{ "gamma curve", create_gamma_curve },
{ "handle box", create_handle_box },
+ { "item factory", create_item_factory },
{ "list", create_list },
{ "menus", create_menus },
{ "modal window", create_modal_window },