* Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GTK+ Team. See the ChangeLog
* files for a list of changes. These files are distributed with
- * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
#define GTK_MENU_INTERNALS
-
-#include <config.h>
-#include <string.h> /* memset */
+#include "config.h"
+#include <string.h>
#include "gdk/gdkkeysyms.h"
#include "gtkaccellabel.h"
#include "gtkaccelmap.h"
#include "gtkbindings.h"
-#include "gtklabel.h"
+#include "gtkcheckmenuitem.h"
+#include <gobject/gvaluecollector.h>
#include "gtkmain.h"
#include "gtkmarshalers.h"
#include "gtkmenu.h"
-#include "gtkmenuitem.h"
#include "gtktearoffmenuitem.h"
#include "gtkwindow.h"
#include "gtkhbox.h"
#include "gtkalias.h"
-#define MENU_ITEM_CLASS(w) GTK_MENU_ITEM_GET_CLASS (w)
-
-#define DEFAULT_POPUP_DELAY 225
+#define DEFAULT_POPUP_DELAY 225
#define DEFAULT_POPDOWN_DELAY 1000
#define NAVIGATION_REGION_OVERSHOOT 50 /* How much the navigation region
* extends below the submenu
*/
-#define MENU_SCROLL_STEP1 8
-#define MENU_SCROLL_STEP2 15
-#define MENU_SCROLL_ARROW_HEIGHT 16
-#define MENU_SCROLL_FAST_ZONE 8
-#define MENU_SCROLL_TIMEOUT1 50
-#define MENU_SCROLL_TIMEOUT2 20
+#define MENU_SCROLL_STEP1 8
+#define MENU_SCROLL_STEP2 15
+#define MENU_SCROLL_FAST_ZONE 8
+#define MENU_SCROLL_TIMEOUT1 50
+#define MENU_SCROLL_TIMEOUT2 20
#define ATTACH_INFO_KEY "gtk-menu-child-attach-info-key"
#define ATTACHED_MENUS "gtk-attached-menus"
struct _GtkMenuPrivate
{
- gboolean seen_item_enter;
-
- gboolean have_position;
gint x;
gint y;
+ gboolean initially_pushed_in;
/* info used for the table */
guint *heights;
gint monitor_num;
/* Cached layout information */
- gboolean have_layout;
gint n_rows;
gint n_columns;
GtkStateType lower_arrow_state;
GtkStateType upper_arrow_state;
- gboolean ignore_button_release;
- gboolean initially_pushed_in;
+ guint have_layout : 1;
+ guint seen_item_enter : 1;
+ guint have_position : 1;
+ guint ignore_button_release : 1;
+ guint no_toggle_size : 1;
};
typedef struct
enum {
PROP_0,
+ PROP_ACTIVE,
+ PROP_ACCEL_GROUP,
+ PROP_ACCEL_PATH,
+ PROP_ATTACH_WIDGET,
PROP_TEAROFF_STATE,
- PROP_TEAROFF_TITLE
+ PROP_TEAROFF_TITLE,
+ PROP_MONITOR,
+ PROP_RESERVE_TOGGLE_SIZE
};
enum {
CHILD_PROP_BOTTOM_ATTACH
};
-static void gtk_menu_class_init (GtkMenuClass *klass);
-static void gtk_menu_init (GtkMenu *menu);
static void gtk_menu_set_property (GObject *object,
guint prop_id,
const GValue *value,
GValue *value,
GParamSpec *pspec);
static void gtk_menu_destroy (GtkObject *object);
-static void gtk_menu_finalize (GObject *object);
static void gtk_menu_realize (GtkWidget *widget);
static void gtk_menu_unrealize (GtkWidget *widget);
static void gtk_menu_size_request (GtkWidget *widget,
static void _gtk_menu_refresh_accel_paths (GtkMenu *menu,
gboolean group_changed);
-static GtkMenuShellClass *parent_class = NULL;
-static const gchar attach_data_key[] = "gtk-menu-attach-data";
+static const gchar attach_data_key[] = "gtk-menu-attach-data";
static guint menu_signals[LAST_SIGNAL] = { 0 };
return G_TYPE_INSTANCE_GET_PRIVATE (menu, GTK_TYPE_MENU, GtkMenuPrivate);
}
-GType
-gtk_menu_get_type (void)
-{
- static GType menu_type = 0;
-
- if (!menu_type)
- {
- static const GTypeInfo menu_info =
- {
- sizeof (GtkMenuClass),
- NULL, /* base_init */
- NULL, /* base_finalize */
- (GClassInitFunc) gtk_menu_class_init,
- NULL, /* class_finalize */
- NULL, /* class_data */
- sizeof (GtkMenu),
- 0, /* n_preallocs */
- (GInstanceInitFunc) gtk_menu_init,
- };
-
- menu_type = g_type_register_static (GTK_TYPE_MENU_SHELL, I_("GtkMenu"),
- &menu_info, 0);
- }
-
- return menu_type;
-}
+G_DEFINE_TYPE (GtkMenu, gtk_menu, GTK_TYPE_MENU_SHELL)
static void
menu_queue_resize (GtkMenu *menu)
gtk_widget_queue_resize (GTK_WIDGET (menu));
}
+static void
+attach_info_free (AttachInfo *info)
+{
+ g_slice_free (AttachInfo, info);
+}
+
static AttachInfo *
get_attach_info (GtkWidget *child)
{
if (!ai)
{
- ai = g_new0 (AttachInfo, 1);
- g_object_set_data_full (object, I_(ATTACH_INFO_KEY), ai, g_free);
+ ai = g_slice_new0 (AttachInfo);
+ g_object_set_data_full (object, I_(ATTACH_INFO_KEY), ai,
+ (GDestroyNotify) attach_info_free);
}
return ai;
GtkMenuShellClass *menu_shell_class = GTK_MENU_SHELL_CLASS (class);
GtkBindingSet *binding_set;
- parent_class = g_type_class_peek_parent (class);
-
- gobject_class->finalize = gtk_menu_finalize;
gobject_class->set_property = gtk_menu_set_property;
gobject_class->get_property = gtk_menu_get_property;
menu_shell_class->move_current = gtk_menu_move_current;
menu_signals[MOVE_SCROLL] =
- _gtk_binding_signal_new (I_("move_scroll"),
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_CALLBACK (gtk_menu_real_move_scroll),
- NULL, NULL,
- _gtk_marshal_VOID__ENUM,
- G_TYPE_NONE, 1,
- GTK_TYPE_SCROLL_TYPE);
-
+ g_signal_new_class_handler (I_("move-scroll"),
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_CALLBACK (gtk_menu_real_move_scroll),
+ NULL, NULL,
+ _gtk_marshal_VOID__ENUM,
+ G_TYPE_NONE, 1,
+ GTK_TYPE_SCROLL_TYPE);
+
+ /**
+ * GtkMenu:active:
+ *
+ * The index of the currently selected menu item, or -1 if no
+ * menu item is selected.
+ *
+ * Since: 2.14
+ **/
+ g_object_class_install_property (gobject_class,
+ PROP_ACTIVE,
+ g_param_spec_int ("active",
+ P_("Active"),
+ P_("The currently selected menu item"),
+ -1, G_MAXINT, -1,
+ GTK_PARAM_READWRITE));
+
+ /**
+ * GtkMenu:accel-group:
+ *
+ * The accel group holding accelerators for the menu.
+ *
+ * Since: 2.14
+ **/
+ g_object_class_install_property (gobject_class,
+ PROP_ACCEL_GROUP,
+ g_param_spec_object ("accel-group",
+ P_("Accel Group"),
+ P_("The accel group holding accelerators for the menu"),
+ GTK_TYPE_ACCEL_GROUP,
+ GTK_PARAM_READWRITE));
+
+ /**
+ * GtkMenu:accel-path:
+ *
+ * An accel path used to conveniently construct accel paths of child items.
+ *
+ * Since: 2.14
+ **/
+ g_object_class_install_property (gobject_class,
+ PROP_ACCEL_PATH,
+ g_param_spec_string ("accel-path",
+ P_("Accel Path"),
+ P_("An accel path used to conveniently construct accel paths of child items"),
+ NULL,
+ GTK_PARAM_READWRITE));
+
+ /**
+ * GtkMenu:attach-widget:
+ *
+ * The widget the menu is attached to. Setting this property attaches
+ * the menu without a #GtkMenuDetachFunc. If you need to use a detacher,
+ * use gtk_menu_attach_to_widget() directly.
+ *
+ * Since: 2.14
+ **/
+ g_object_class_install_property (gobject_class,
+ PROP_ATTACH_WIDGET,
+ g_param_spec_object ("attach-widget",
+ P_("Attach Widget"),
+ P_("The widget the menu is attached to"),
+ GTK_TYPE_WIDGET,
+ GTK_PARAM_READWRITE));
+
g_object_class_install_property (gobject_class,
PROP_TEAROFF_TITLE,
g_param_spec_string ("tearoff-title",
P_("Tearoff Title"),
P_("A title that may be displayed by the window manager when this menu is torn-off"),
- "",
+ NULL,
GTK_PARAM_READWRITE));
/**
FALSE,
GTK_PARAM_READWRITE));
+ /**
+ * GtkMenu:monitor:
+ *
+ * The monitor the menu will be popped up on.
+ *
+ * Since: 2.14
+ **/
+ g_object_class_install_property (gobject_class,
+ PROP_MONITOR,
+ g_param_spec_int ("monitor",
+ P_("Monitor"),
+ P_("The monitor the menu will be popped up on"),
+ -1, G_MAXINT, -1,
+ GTK_PARAM_READWRITE));
+
gtk_widget_class_install_style_property (widget_class,
g_param_spec_int ("vertical-padding",
P_("Vertical Padding"),
1,
GTK_PARAM_READABLE));
+ /**
+ * GtkMenu:reserve-toggle-size:
+ *
+ * A boolean that indicates whether the menu reserves space for
+ * toggles and icons, regardless of their actual presence.
+ *
+ * This property should only be changed from its default value
+ * for special-purposes such as tabular menus. Regular menus that
+ * are connected to a menu bar or context menus should reserve
+ * toggle space for consistency.
+ *
+ * Since: 2.18
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_RESERVE_TOGGLE_SIZE,
+ g_param_spec_boolean ("reserve-toggle-size",
+ P_("Reserve Toggle Size"),
+ P_("A boolean that indicates whether the menu reserves space for toggles and icons"),
+ TRUE,
+ GTK_PARAM_READWRITE));
+
gtk_widget_class_install_style_property (widget_class,
g_param_spec_int ("horizontal-padding",
P_("Horizontal Padding"),
TRUE,
GTK_PARAM_READABLE));
+ /**
+ * GtkMenu:arrow-placement:
+ *
+ * Indicates where scroll arrows should be placed.
+ *
+ * Since: 2.16
+ **/
+ gtk_widget_class_install_style_property (widget_class,
+ g_param_spec_enum ("arrow-placement",
+ P_("Arrow Placement"),
+ P_("Indicates where scroll arrows should be placed"),
+ GTK_TYPE_ARROW_PLACEMENT,
+ GTK_ARROWS_BOTH,
+ GTK_PARAM_READABLE));
gtk_container_class_install_child_property (container_class,
CHILD_PROP_LEFT_ATTACH,
-1, INT_MAX, -1,
GTK_PARAM_READWRITE));
+ /**
+ * GtkMenu::arrow-scaling
+ *
+ * Arbitrary constant to scale down the size of the scroll arrow.
+ *
+ * Since: 2.16
+ */
+ gtk_widget_class_install_style_property (widget_class,
+ g_param_spec_float ("arrow-scaling",
+ P_("Arrow Scaling"),
+ P_("Arbitrary constant to scale down the size of the scroll arrow"),
+ 0.0, 1.0, 0.7,
+ GTK_PARAM_READABLE));
+
binding_set = gtk_binding_set_by_class (class);
gtk_binding_entry_add_signal (binding_set,
GDK_Up, 0,
- I_("move_current"), 1,
+ I_("move-current"), 1,
GTK_TYPE_MENU_DIRECTION_TYPE,
GTK_MENU_DIR_PREV);
gtk_binding_entry_add_signal (binding_set,
GDK_KP_Up, 0,
- "move_current", 1,
+ "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,
+ "move-current", 1,
GTK_TYPE_MENU_DIRECTION_TYPE,
GTK_MENU_DIR_NEXT);
gtk_binding_entry_add_signal (binding_set,
GDK_KP_Down, 0,
- "move_current", 1,
+ "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,
+ "move-current", 1,
GTK_TYPE_MENU_DIRECTION_TYPE,
GTK_MENU_DIR_PARENT);
gtk_binding_entry_add_signal (binding_set,
GDK_KP_Left, 0,
- "move_current", 1,
+ "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,
+ "move-current", 1,
GTK_TYPE_MENU_DIRECTION_TYPE,
GTK_MENU_DIR_CHILD);
gtk_binding_entry_add_signal (binding_set,
GDK_KP_Right, 0,
- "move_current", 1,
+ "move-current", 1,
GTK_TYPE_MENU_DIRECTION_TYPE,
GTK_MENU_DIR_CHILD);
gtk_binding_entry_add_signal (binding_set,
GDK_Home, 0,
- "move_scroll", 1,
+ "move-scroll", 1,
GTK_TYPE_SCROLL_TYPE,
GTK_SCROLL_START);
gtk_binding_entry_add_signal (binding_set,
GDK_KP_Home, 0,
- "move_scroll", 1,
+ "move-scroll", 1,
GTK_TYPE_SCROLL_TYPE,
GTK_SCROLL_START);
gtk_binding_entry_add_signal (binding_set,
GDK_End, 0,
- "move_scroll", 1,
+ "move-scroll", 1,
GTK_TYPE_SCROLL_TYPE,
GTK_SCROLL_END);
gtk_binding_entry_add_signal (binding_set,
GDK_KP_End, 0,
- "move_scroll", 1,
+ "move-scroll", 1,
GTK_TYPE_SCROLL_TYPE,
GTK_SCROLL_END);
gtk_binding_entry_add_signal (binding_set,
GDK_Page_Up, 0,
- "move_scroll", 1,
+ "move-scroll", 1,
GTK_TYPE_SCROLL_TYPE,
GTK_SCROLL_PAGE_UP);
gtk_binding_entry_add_signal (binding_set,
GDK_KP_Page_Up, 0,
- "move_scroll", 1,
+ "move-scroll", 1,
GTK_TYPE_SCROLL_TYPE,
GTK_SCROLL_PAGE_UP);
gtk_binding_entry_add_signal (binding_set,
GDK_Page_Down, 0,
- "move_scroll", 1,
+ "move-scroll", 1,
GTK_TYPE_SCROLL_TYPE,
GTK_SCROLL_PAGE_DOWN);
gtk_binding_entry_add_signal (binding_set,
GDK_KP_Page_Down, 0,
- "move_scroll", 1,
+ "move-scroll", 1,
GTK_TYPE_SCROLL_TYPE,
GTK_SCROLL_PAGE_DOWN);
}
-static void
+static void
gtk_menu_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
- GtkMenu *menu;
-
- menu = GTK_MENU (object);
-
+ GtkMenu *menu = GTK_MENU (object);
+
switch (prop_id)
{
+ case PROP_ACTIVE:
+ gtk_menu_set_active (menu, g_value_get_int (value));
+ break;
+ case PROP_ACCEL_GROUP:
+ gtk_menu_set_accel_group (menu, g_value_get_object (value));
+ break;
+ case PROP_ACCEL_PATH:
+ gtk_menu_set_accel_path (menu, g_value_get_string (value));
+ break;
+ case PROP_ATTACH_WIDGET:
+ {
+ GtkWidget *widget;
+
+ widget = gtk_menu_get_attach_widget (menu);
+ if (widget)
+ gtk_menu_detach (menu);
+
+ widget = (GtkWidget*) g_value_get_object (value);
+ if (widget)
+ gtk_menu_attach_to_widget (menu, widget, NULL);
+ }
+ break;
case PROP_TEAROFF_STATE:
gtk_menu_set_tearoff_state (menu, g_value_get_boolean (value));
break;
case PROP_TEAROFF_TITLE:
gtk_menu_set_title (menu, g_value_get_string (value));
- break;
+ break;
+ case PROP_MONITOR:
+ gtk_menu_set_monitor (menu, g_value_get_int (value));
+ break;
+ case PROP_RESERVE_TOGGLE_SIZE:
+ gtk_menu_set_reserve_toggle_size (menu, g_value_get_boolean (value));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
-static void
+static void
gtk_menu_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
- GtkMenu *menu;
-
- menu = GTK_MENU (object);
-
+ GtkMenu *menu = GTK_MENU (object);
+
switch (prop_id)
{
+ case PROP_ACTIVE:
+ g_value_set_int (value, g_list_index (GTK_MENU_SHELL (menu)->children, gtk_menu_get_active (menu)));
+ break;
+ case PROP_ACCEL_GROUP:
+ g_value_set_object (value, gtk_menu_get_accel_group (menu));
+ break;
+ case PROP_ACCEL_PATH:
+ g_value_set_string (value, gtk_menu_get_accel_path (menu));
+ break;
+ case PROP_ATTACH_WIDGET:
+ g_value_set_object (value, gtk_menu_get_attach_widget (menu));
+ break;
case PROP_TEAROFF_STATE:
g_value_set_boolean (value, gtk_menu_get_tearoff_state (menu));
break;
case PROP_TEAROFF_TITLE:
g_value_set_string (value, gtk_menu_get_title (menu));
break;
+ case PROP_MONITOR:
+ g_value_set_int (value, gtk_menu_get_monitor (menu));
+ break;
+ case PROP_RESERVE_TOGGLE_SIZE:
+ g_value_set_boolean (value, gtk_menu_get_reserve_toggle_size (menu));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
"child", menu,
NULL),
"signal::event", gtk_menu_window_event, menu,
- "signal::size_request", gtk_menu_window_size_request, menu,
+ "signal::size-request", gtk_menu_window_size_request, menu,
"signal::destroy", gtk_widget_destroyed, &menu->toplevel,
NULL);
gtk_window_set_resizable (GTK_WINDOW (menu->toplevel), FALSE);
priv->lower_arrow_state = GTK_STATE_NORMAL;
priv->have_layout = FALSE;
+ priv->monitor_num = -1;
}
static void
gtk_menu_destroy (GtkObject *object)
{
- GtkMenu *menu;
+ GtkMenu *menu = GTK_MENU (object);
GtkMenuAttachData *data;
GtkMenuPrivate *priv;
- g_return_if_fail (GTK_IS_MENU (object));
-
- menu = GTK_MENU (object);
-
- gtk_menu_stop_scrolling (menu);
+ gtk_menu_remove_scroll_timeout (menu);
data = g_object_get_data (G_OBJECT (object), attach_data_key);
if (data)
priv->title = NULL;
}
- GTK_OBJECT_CLASS (parent_class)->destroy (object);
-}
-
-static void
-gtk_menu_finalize (GObject *object)
-{
- GtkMenu *menu = GTK_MENU (object);
-
- g_free (menu->accel_path);
-
- G_OBJECT_CLASS (parent_class)->finalize (object);
+ GTK_OBJECT_CLASS (gtk_menu_parent_class)->destroy (object);
}
static void
g_object_ref_sink (menu);
- data = g_new (GtkMenuAttachData, 1);
+ data = g_slice_new (GtkMenuAttachData);
data->attach_widget = attach_widget;
- g_signal_connect (attach_widget, "screen_changed",
+ g_signal_connect (attach_widget, "screen-changed",
G_CALLBACK (attach_widget_screen_changed), menu);
attach_widget_screen_changed (attach_widget, NULL, menu);
{
list = g_list_prepend (list, menu);
}
- g_object_set_data_full (G_OBJECT (attach_widget), I_(ATTACHED_MENUS), list, (GtkDestroyNotify) g_list_free);
-
+ g_object_set_data_full (G_OBJECT (attach_widget), I_(ATTACHED_MENUS), list,
+ (GDestroyNotify) g_list_free);
+
if (GTK_WIDGET_STATE (menu) != GTK_STATE_NORMAL)
gtk_widget_set_state (GTK_WIDGET (menu), GTK_STATE_NORMAL);
list = g_object_steal_data (G_OBJECT (data->attach_widget), ATTACHED_MENUS);
list = g_list_remove (list, menu);
if (list)
- g_object_set_data_full (G_OBJECT (data->attach_widget), I_(ATTACHED_MENUS), list, (GtkDestroyNotify) g_list_free);
+ g_object_set_data_full (G_OBJECT (data->attach_widget), I_(ATTACHED_MENUS), list,
+ (GDestroyNotify) g_list_free);
else
g_object_set_data (G_OBJECT (data->attach_widget), I_(ATTACHED_MENUS), NULL);
if (GTK_WIDGET_REALIZED (menu))
gtk_widget_unrealize (GTK_WIDGET (menu));
- g_free (data);
+ g_slice_free (GtkMenuAttachData, data);
/* Fallback title for menu comes from attach widget */
gtk_menu_update_title (menu);
gtk_menu_remove (GtkContainer *container,
GtkWidget *widget)
{
- GtkMenu *menu;
+ GtkMenu *menu = GTK_MENU (container);
- g_return_if_fail (GTK_IS_MENU (container));
g_return_if_fail (GTK_IS_MENU_ITEM (widget));
- menu = GTK_MENU (container);
-
/* Clear out old_active_menu_item if it matches the item we are removing
*/
if (menu->old_active_menu_item == widget)
menu->old_active_menu_item = NULL;
}
- GTK_CONTAINER_CLASS (parent_class)->remove (container, widget);
+ GTK_CONTAINER_CLASS (gtk_menu_parent_class)->remove (container, widget);
g_object_set_data (G_OBJECT (widget), I_(ATTACH_INFO_KEY), NULL);
menu_queue_resize (menu);
if (GTK_WIDGET_REALIZED (menu_shell))
gtk_widget_set_parent_window (child, menu->bin_window);
- GTK_MENU_SHELL_CLASS (parent_class)->insert (menu_shell, child, position);
+ GTK_MENU_SHELL_CLASS (gtk_menu_parent_class)->insert (menu_shell, child, position);
menu_queue_resize (menu);
}
* a mouse button press, such as a mouse button release or a keypress,
* @button should be 0.
*
- * The @activate_time parameter should be the time stamp of the event that
- * initiated the popup. If such an event is not available, use
- * gtk_get_current_event_time() instead.
- *
+ * The @activate_time parameter is used to conflict-resolve initiation of
+ * concurrent requests for mouse/keyboard grab requests. To function
+ * properly, this needs to be the time stamp of the user event (such as
+ * a mouse click or key press) that caused the initiation of the popup.
+ * Only if no such event is available, gtk_get_current_event_time() can
+ * be used instead.
*/
void
gtk_menu_popup (GtkMenu *menu,
GtkMenuShell *menu_shell;
gboolean grab_keyboard;
GtkMenuPrivate *priv;
+ GtkWidget *parent_toplevel;
g_return_if_fail (GTK_IS_MENU (menu));
gtk_menu_reparent (menu, menu->toplevel, FALSE);
}
-
+
+ parent_toplevel = NULL;
if (parent_menu_shell)
+ parent_toplevel = gtk_widget_get_toplevel (parent_menu_shell);
+ else if (!g_object_get_data (G_OBJECT (menu), "gtk-menu-explicit-screen"))
{
- GtkWidget *toplevel;
-
- toplevel = gtk_widget_get_toplevel (parent_menu_shell);
- if (GTK_IS_WINDOW (toplevel))
- gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
- GTK_WINDOW (menu->toplevel));
+ GtkWidget *attach_widget = gtk_menu_get_attach_widget (menu);
+ if (attach_widget)
+ parent_toplevel = gtk_widget_get_toplevel (attach_widget);
}
+
+ /* Set transient for to get the right window group and parent relationship */
+ if (GTK_IS_WINDOW (parent_toplevel))
+ gtk_window_set_transient_for (GTK_WINDOW (menu->toplevel),
+ GTK_WINDOW (parent_toplevel));
menu->parent_menu_item = parent_menu_item;
menu->position_func = func;
gtk_menu_scroll_to (menu, menu->scroll_offset);
+ /* if no item is selected, select the first one */
+ if (!menu_shell->active_menu_item)
+ {
+ gboolean touchscreen_mode;
+
+ g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
+ "gtk-touchscreen-mode", &touchscreen_mode,
+ NULL);
+
+ if (touchscreen_mode)
+ gtk_menu_shell_select_first (menu_shell, TRUE);
+ }
+
/* Once everything is set up correctly, map the toplevel window on
the screen.
*/
if (xgrab_shell == widget)
popup_grab_on_window (widget->window, activate_time, grab_keyboard); /* Should always succeed */
gtk_grab_add (GTK_WIDGET (menu));
+
+ if (parent_menu_shell)
+ {
+ gboolean keyboard_mode;
+
+ keyboard_mode = _gtk_menu_shell_get_keyboard_mode (GTK_MENU_SHELL (parent_menu_shell));
+ _gtk_menu_shell_set_keyboard_mode (menu_shell, keyboard_mode);
+ }
+ else if (menu_shell->button == 0) /* a keynav-activated context menu */
+ _gtk_menu_shell_set_keyboard_mode (menu_shell, TRUE);
+
+ _gtk_menu_shell_update_mnemonics (menu_shell);
}
void
menu_shell = GTK_MENU_SHELL (menu);
private = gtk_menu_get_private (menu);
-
+
menu_shell->parent_menu_shell = NULL;
menu_shell->active = FALSE;
menu_shell->ignore_enter = FALSE;
/* The X Grab, if present, will automatically be removed when we hide
* the window */
gtk_widget_hide (menu->toplevel);
- gtk_window_group_add_window (gtk_window_get_group (NULL), GTK_WINDOW (menu->toplevel));
+ gtk_window_set_transient_for (GTK_WINDOW (menu->toplevel), NULL);
if (menu->torn_off)
{
}
}
+
+/**
+ * gtk_menu_set_accel_group:
+ * @accel_group: (allow-none):
+ */
void
gtk_menu_set_accel_group (GtkMenu *menu,
GtkAccelGroup *accel_group)
/**
* gtk_menu_set_accel_path
* @menu: a valid #GtkMenu
- * @accel_path: a valid accelerator path
+ * @accel_path: (allow-none): a valid accelerator path
*
* Sets an accelerator path for this menu from which accelerator paths
* for its immediate children, its menu items, can be constructed.
* Assigning accel paths to menu items then enables the user to change
* their accelerators at runtime. More details about accelerator paths
* and their default setups can be found at gtk_accel_map_add_entry().
+ *
+ * Note that @accel_path string will be stored in a #GQuark. Therefore, if you
+ * pass a static string, you can save some memory by interning it first with
+ * g_intern_static_string().
*/
void
gtk_menu_set_accel_path (GtkMenu *menu,
if (accel_path)
g_return_if_fail (accel_path[0] == '<' && strchr (accel_path, '/')); /* simplistic check */
- g_free (menu->accel_path);
- menu->accel_path = g_strdup (accel_path);
+ /* FIXME: accel_path should be defined as const gchar* */
+ menu->accel_path = (gchar*)g_intern_string (accel_path);
if (menu->accel_path)
_gtk_menu_refresh_accel_paths (menu, FALSE);
}
+/**
+ * gtk_menu_get_accel_path
+ * @menu: a valid #GtkMenu
+ *
+ * Retrieves the accelerator path set on the menu.
+ *
+ * Returns: the accelerator path set on the menu.
+ *
+ * Since: 2.14
+ */
+const gchar*
+gtk_menu_get_accel_path (GtkMenu *menu)
+{
+ g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
+
+ return menu->accel_path;
+}
+
typedef struct {
GtkMenu *menu;
gboolean group_changed;
}
static void
-_gtk_menu_refresh_accel_paths (GtkMenu *menu,
- gboolean group_changed)
+_gtk_menu_refresh_accel_paths (GtkMenu *menu,
+ gboolean group_changed)
{
g_return_if_fail (GTK_IS_MENU (menu));
-
+
if (menu->accel_path && menu->accel_group)
{
AccelPropagation prop;
else if (GTK_IS_WIDGET (attach))
{
toplevel = gtk_widget_get_toplevel (attach);
- if (GTK_WIDGET_TOPLEVEL (toplevel))
+ if (gtk_widget_is_toplevel (toplevel))
return toplevel;
}
{
GtkWidget *toplevel;
- menu->tearoff_window = gtk_widget_new (GTK_TYPE_WINDOW,
+ menu->tearoff_window = g_object_new (GTK_TYPE_WINDOW,
"type", GTK_WINDOW_TOPLEVEL,
"screen", gtk_widget_get_screen (menu->toplevel),
"app-paintable", TRUE,
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);
height/2,
height));
g_object_connect (menu->tearoff_adjustment,
- "signal::value_changed", gtk_menu_scrollbar_changed, menu,
+ "signal::value-changed", gtk_menu_scrollbar_changed, menu,
NULL);
menu->tearoff_scrollbar = gtk_vscrollbar_new (menu->tearoff_adjustment);
* @title: a string containing the title for the menu.
*
* Sets the title string for the menu. The title is displayed when the menu
- * is shown as a tearoff menu.
+ * is shown as a tearoff menu. If @title is %NULL, the menu will see if it is
+ * attached to a parent menu item, and if so it will try to use the same text as
+ * that menu item's label.
**/
-void
+void
gtk_menu_set_title (GtkMenu *menu,
const gchar *title)
{
GtkMenuPrivate *priv;
+ char *old_title;
g_return_if_fail (GTK_IS_MENU (menu));
priv = gtk_menu_get_private (menu);
- if (strcmp (title ? title : "", priv->title ? priv->title : "") != 0)
- {
- g_free (priv->title);
- priv->title = g_strdup (title);
+ old_title = priv->title;
+ priv->title = g_strdup (title);
+ g_free (old_title);
- gtk_menu_update_title (menu);
- g_object_notify (G_OBJECT (menu), "tearoff-title");
- }
+ gtk_menu_update_title (menu);
+ g_object_notify (G_OBJECT (menu), "tearoff-title");
}
/**
}
}
+static void
+get_arrows_border (GtkMenu *menu,
+ GtkBorder *border)
+{
+ guint scroll_arrow_height;
+ GtkArrowPlacement arrow_placement;
+
+ gtk_widget_style_get (GTK_WIDGET (menu),
+ "scroll-arrow-vlength", &scroll_arrow_height,
+ "arrow_placement", &arrow_placement,
+ NULL);
+
+ switch (arrow_placement)
+ {
+ case GTK_ARROWS_BOTH:
+ border->top = menu->upper_arrow_visible ? scroll_arrow_height : 0;
+ border->bottom = menu->lower_arrow_visible ? scroll_arrow_height : 0;
+ break;
+
+ case GTK_ARROWS_START:
+ border->top = (menu->upper_arrow_visible ||
+ menu->lower_arrow_visible) ? scroll_arrow_height : 0;
+ border->bottom = 0;
+ break;
+
+ case GTK_ARROWS_END:
+ border->top = 0;
+ border->bottom = (menu->upper_arrow_visible ||
+ menu->lower_arrow_visible) ? scroll_arrow_height : 0;
+ break;
+ }
+
+ border->left = border->right = 0;
+}
+
static void
gtk_menu_realize (GtkWidget *widget)
{
GList *children;
guint vertical_padding;
guint horizontal_padding;
+ GtkBorder arrow_border;
g_return_if_fail (GTK_IS_MENU (widget));
attributes.width = MAX (1, widget->allocation.width - attributes.x * 2);
attributes.height = MAX (1, widget->allocation.height - attributes.y * 2);
- if (menu->upper_arrow_visible)
- {
- attributes.y += MENU_SCROLL_ARROW_HEIGHT;
- attributes.height -= MENU_SCROLL_ARROW_HEIGHT;
- }
-
- if (menu->lower_arrow_visible)
- attributes.height -= MENU_SCROLL_ARROW_HEIGHT;
+ get_arrows_border (menu, &arrow_border);
+ attributes.y += arrow_border.top;
+ attributes.height -= arrow_border.top;
+ attributes.height -= arrow_border.bottom;
menu->view_window = gdk_window_new (widget->window, &attributes, attributes_mask);
gdk_window_set_user_data (menu->view_window, menu);
static void
gtk_menu_unrealize (GtkWidget *widget)
{
- GtkMenu *menu;
-
- g_return_if_fail (GTK_IS_MENU (widget));
-
- menu = GTK_MENU (widget);
+ GtkMenu *menu = GTK_MENU (widget);
menu_grab_transfer_window_destroy (menu);
gdk_window_destroy (menu->bin_window);
menu->bin_window = NULL;
- (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
+ GTK_WIDGET_CLASS (gtk_menu_parent_class)->unrealize (widget);
}
static void
priv->heights[t] = MAX (priv->heights[t], part);
}
+ /* If the menu doesn't include any images or check items
+ * reserve the space so that all menus are consistent.
+ * We only do this for 'ordinary' menus, not for combobox
+ * menus or multi-column menus
+ */
+ if (max_toggle_size == 0 &&
+ gtk_menu_get_n_columns (menu) == 1 &&
+ !priv->no_toggle_size)
+ {
+ guint toggle_spacing;
+ guint indicator_size;
+
+ gtk_style_get (widget->style,
+ GTK_TYPE_CHECK_MENU_ITEM,
+ "toggle-spacing", &toggle_spacing,
+ "indicator-size", &indicator_size,
+ NULL);
+
+ max_toggle_size = indicator_size + toggle_spacing;
+ }
+
for (i = 0; i < gtk_menu_get_n_rows (menu); i++)
requisition->height += priv->heights[i];
- requisition->width += max_toggle_size + max_accel_width;
+ requisition->width += 2 * max_toggle_size + max_accel_width;
requisition->width *= gtk_menu_get_n_columns (menu);
gtk_widget_style_get (GTK_WIDGET (menu),
gint width, height;
guint vertical_padding;
guint horizontal_padding;
-
+
g_return_if_fail (GTK_IS_MENU (widget));
g_return_if_fail (allocation != NULL);
if (menu_shell->active)
gtk_menu_scroll_to (menu, menu->scroll_offset);
- if (menu->upper_arrow_visible && !menu->tearoff_active)
+ if (!menu->tearoff_active)
{
- y += MENU_SCROLL_ARROW_HEIGHT;
- height -= MENU_SCROLL_ARROW_HEIGHT;
- }
+ GtkBorder arrow_border;
- if (menu->lower_arrow_visible && !menu->tearoff_active)
- height -= MENU_SCROLL_ARROW_HEIGHT;
+ get_arrows_border (menu, &arrow_border);
+ y += arrow_border.top;
+ height -= arrow_border.top;
+ height -= arrow_border.bottom;
+ }
if (GTK_WIDGET_REALIZED (widget))
{
}
}
+static void
+get_arrows_visible_area (GtkMenu *menu,
+ GdkRectangle *border,
+ GdkRectangle *upper,
+ GdkRectangle *lower,
+ gint *arrow_space)
+{
+ GtkWidget *widget = GTK_WIDGET (menu);
+ guint vertical_padding;
+ guint horizontal_padding;
+ gint scroll_arrow_height;
+ GtkArrowPlacement arrow_placement;
+
+ gtk_widget_style_get (widget,
+ "vertical-padding", &vertical_padding,
+ "horizontal-padding", &horizontal_padding,
+ "scroll-arrow-vlength", &scroll_arrow_height,
+ "arrow-placement", &arrow_placement,
+ NULL);
+
+ border->x = GTK_CONTAINER (widget)->border_width + widget->style->xthickness + horizontal_padding;
+ border->y = GTK_CONTAINER (widget)->border_width + widget->style->ythickness + vertical_padding;
+ gdk_drawable_get_size (widget->window, &border->width, &border->height);
+
+ switch (arrow_placement)
+ {
+ case GTK_ARROWS_BOTH:
+ upper->x = border->x;
+ upper->y = border->y;
+ upper->width = border->width - 2 * border->x;
+ upper->height = scroll_arrow_height;
+
+ lower->x = border->x;
+ lower->y = border->height - border->y - scroll_arrow_height;
+ lower->width = border->width - 2 * border->x;
+ lower->height = scroll_arrow_height;
+ break;
+
+ case GTK_ARROWS_START:
+ upper->x = border->x;
+ upper->y = border->y;
+ upper->width = (border->width - 2 * border->x) / 2;
+ upper->height = scroll_arrow_height;
+
+ lower->x = border->x + upper->width;
+ lower->y = border->y;
+ lower->width = (border->width - 2 * border->x) / 2;
+ lower->height = scroll_arrow_height;
+ break;
+
+ case GTK_ARROWS_END:
+ upper->x = border->x;
+ upper->y = border->height - border->y - scroll_arrow_height;
+ upper->width = (border->width - 2 * border->x) / 2;
+ upper->height = scroll_arrow_height;
+
+ lower->x = border->x + upper->width;
+ lower->y = border->height - border->y - scroll_arrow_height;
+ lower->width = (border->width - 2 * border->x) / 2;
+ lower->height = scroll_arrow_height;
+ break;
+
+ default:
+ g_assert_not_reached();
+ upper->x = upper->y = upper->width = upper->height = 0;
+ lower->x = lower->y = lower->width = lower->height = 0;
+ }
+
+ *arrow_space = scroll_arrow_height - 2 * widget->style->ythickness;
+}
+
static void
gtk_menu_paint (GtkWidget *widget,
GdkEventExpose *event)
{
GtkMenu *menu;
GtkMenuPrivate *priv;
- gint width, height;
- gint border_x, border_y;
- guint vertical_padding;
- guint horizontal_padding;
-
+ GdkRectangle border;
+ GdkRectangle upper;
+ GdkRectangle lower;
+ gint arrow_space;
+
g_return_if_fail (GTK_IS_MENU (widget));
menu = GTK_MENU (widget);
priv = gtk_menu_get_private (menu);
- gtk_widget_style_get (GTK_WIDGET (menu),
- "vertical-padding", &vertical_padding,
- "horizontal-padding", &horizontal_padding,
- NULL);
-
- border_x = GTK_CONTAINER (widget)->border_width + widget->style->xthickness + horizontal_padding;
- border_y = GTK_CONTAINER (widget)->border_width + widget->style->ythickness + vertical_padding;
- gdk_drawable_get_size (widget->window, &width, &height);
+ get_arrows_visible_area (menu, &border, &upper, &lower, &arrow_space);
if (event->window == widget->window)
{
- gint arrow_space = MENU_SCROLL_ARROW_HEIGHT - 2 * widget->style->ythickness;
- gint arrow_size = 0.7 * arrow_space;
+ gfloat arrow_scaling;
+ gint arrow_size;
+
+ gtk_widget_style_get (widget, "arrow-scaling", &arrow_scaling, NULL);
+ arrow_size = arrow_scaling * arrow_space;
gtk_paint_box (widget->style,
widget->window,
GTK_STATE_NORMAL,
GTK_SHADOW_OUT,
- NULL, widget, "menu",
+ &event->area, widget, "menu",
0, 0, -1, -1);
if (menu->upper_arrow_visible && !menu->tearoff_active)
gtk_paint_box (widget->style,
widget->window,
priv->upper_arrow_state,
- GTK_SHADOW_OUT,
- NULL, widget, "menu",
- border_x,
- border_y,
- width - 2 * border_x,
- MENU_SCROLL_ARROW_HEIGHT);
+ GTK_SHADOW_OUT,
+ &event->area, widget, "menu_scroll_arrow_up",
+ upper.x,
+ upper.y,
+ upper.width,
+ upper.height);
gtk_paint_arrow (widget->style,
widget->window,
priv->upper_arrow_state,
GTK_SHADOW_OUT,
- NULL, widget, "menu_scroll_arrow_up",
+ &event->area, widget, "menu_scroll_arrow_up",
GTK_ARROW_UP,
TRUE,
- (width - arrow_size ) / 2,
- border_y + widget->style->ythickness + (arrow_space - arrow_size)/2,
+ upper.x + (upper.width - arrow_size) / 2,
+ upper.y + widget->style->ythickness + (arrow_space - arrow_size) / 2,
arrow_size, arrow_size);
}
gtk_paint_box (widget->style,
widget->window,
priv->lower_arrow_state,
- GTK_SHADOW_OUT,
- NULL, widget, "menu",
- border_x,
- height - border_y - MENU_SCROLL_ARROW_HEIGHT,
- width - 2*border_x,
- MENU_SCROLL_ARROW_HEIGHT);
+ GTK_SHADOW_OUT,
+ &event->area, widget, "menu_scroll_arrow_down",
+ lower.x,
+ lower.y,
+ lower.width,
+ lower.height);
gtk_paint_arrow (widget->style,
widget->window,
priv->lower_arrow_state,
GTK_SHADOW_OUT,
- NULL, widget, "menu_scroll_arrow_down",
+ &event->area, widget, "menu_scroll_arrow_down",
GTK_ARROW_DOWN,
TRUE,
- (width - arrow_size) / 2,
- height - border_y - MENU_SCROLL_ARROW_HEIGHT +
- widget->style->ythickness + (arrow_space - arrow_size)/2,
+ lower.x + (lower.width - arrow_size) / 2,
+ lower.y + widget->style->ythickness + (arrow_space - arrow_size) / 2,
arrow_size, arrow_size);
}
}
else if (event->window == menu->bin_window)
{
+ gint y = -border.y + menu->scroll_offset;
+
+ if (!menu->tearoff_active)
+ {
+ GtkBorder arrow_border;
+
+ get_arrows_border (menu, &arrow_border);
+ y -= arrow_border.top;
+ }
+
gtk_paint_box (widget->style,
menu->bin_window,
GTK_STATE_NORMAL,
GTK_SHADOW_OUT,
- NULL, widget, "menu",
- - border_x, menu->scroll_offset - border_y,
- width, height);
+ &event->area, widget, "menu",
+ - border.x, y,
+ border.width, border.height);
}
}
if (GTK_WIDGET_DRAWABLE (widget))
{
gtk_menu_paint (widget, event);
-
- (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
+
+ GTK_WIDGET_CLASS (gtk_menu_parent_class)->expose_event (widget, event);
}
return FALSE;
_gtk_menu_refresh_accel_paths (menu, FALSE);
- GTK_WIDGET_CLASS (parent_class)->show (widget);
+ GTK_WIDGET_CLASS (gtk_menu_parent_class)->show (widget);
}
static gboolean
-gtk_menu_button_scroll (GtkWidget *widget,
+gtk_menu_button_scroll (GtkMenu *menu,
GdkEventButton *event)
{
- if (GTK_IS_MENU (widget))
+ if (menu->upper_arrow_prelight || menu->lower_arrow_prelight)
{
- GtkMenu *menu = GTK_MENU (widget);
+ gboolean touchscreen_mode;
- if (menu->upper_arrow_prelight || menu->lower_arrow_prelight)
- {
- GtkSettings *settings = gtk_widget_get_settings (widget);
- gboolean touchscreen_mode;
+ g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
+ "gtk-touchscreen-mode", &touchscreen_mode,
+ NULL);
- g_object_get (G_OBJECT (settings),
- "gtk-touchscreen-mode", &touchscreen_mode,
- NULL);
+ if (touchscreen_mode)
+ gtk_menu_handle_scrolling (menu,
+ event->x_root, event->y_root,
+ event->type == GDK_BUTTON_PRESS,
+ FALSE);
- if (touchscreen_mode)
- gtk_menu_handle_scrolling (menu,
- event->x_root, event->y_root,
- event->type == GDK_BUTTON_PRESS,
- FALSE);
+ return TRUE;
+ }
- return TRUE;
- }
+ return FALSE;
+}
+
+static gboolean
+pointer_in_menu_window (GtkWidget *widget,
+ gdouble x_root,
+ gdouble y_root)
+{
+ GtkMenu *menu = GTK_MENU (widget);
+
+ if (GTK_WIDGET_MAPPED (menu->toplevel))
+ {
+ GtkMenuShell *menu_shell;
+ gint window_x, window_y;
+
+ gdk_window_get_position (menu->toplevel->window, &window_x, &window_y);
+
+ if (x_root >= window_x && x_root < window_x + widget->allocation.width &&
+ y_root >= window_y && y_root < window_y + widget->allocation.height)
+ return TRUE;
+
+ menu_shell = GTK_MENU_SHELL (widget);
+
+ if (GTK_IS_MENU (menu_shell->parent_menu_shell))
+ return pointer_in_menu_window (menu_shell->parent_menu_shell,
+ x_root, y_root);
}
return FALSE;
if (event->type != GDK_BUTTON_PRESS)
return FALSE;
- /* Don't pop down the menu for presses over scroll arrows
+ /* Don't pass down to menu shell for presses over scroll arrows
*/
- if (gtk_menu_button_scroll (widget, event))
+ if (gtk_menu_button_scroll (GTK_MENU (widget), event))
return TRUE;
- return GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event);
+ /* Don't pass down to menu shell if a non-menuitem part of the menu
+ * was clicked. The check for the event_widget being a GtkMenuShell
+ * works because we have the pointer grabbed on menu_shell->window
+ * with owner_events=TRUE, so all events that are either outside
+ * the menu or on its border are delivered relative to
+ * menu_shell->window.
+ */
+ if (GTK_IS_MENU_SHELL (gtk_get_event_widget ((GdkEvent *) event)) &&
+ pointer_in_menu_window (widget, event->x_root, event->y_root))
+ return TRUE;
+
+ return GTK_WIDGET_CLASS (gtk_menu_parent_class)->button_press_event (widget, event);
}
static gboolean
gtk_menu_button_release (GtkWidget *widget,
GdkEventButton *event)
{
- if (GTK_IS_MENU (widget))
- {
- GtkMenuPrivate *priv = gtk_menu_get_private (GTK_MENU (widget));
+ GtkMenuPrivate *priv = gtk_menu_get_private (GTK_MENU (widget));
- if (priv->ignore_button_release)
- {
- priv->ignore_button_release = FALSE;
- return FALSE;
- }
+ if (priv->ignore_button_release)
+ {
+ priv->ignore_button_release = FALSE;
+ return FALSE;
}
if (event->type != GDK_BUTTON_RELEASE)
return FALSE;
- /* Don't pop down the menu for releases over scroll arrows
+ /* Don't pass down to menu shell for releases over scroll arrows
*/
- if (gtk_menu_button_scroll (widget, event))
+ if (gtk_menu_button_scroll (GTK_MENU (widget), event))
return TRUE;
- return GTK_WIDGET_CLASS (parent_class)->button_release_event (widget, event);
+ /* Don't pass down to menu shell if a non-menuitem part of the menu
+ * was clicked (see comment in button_press()).
+ */
+ if (GTK_IS_MENU_SHELL (gtk_get_event_widget ((GdkEvent *) event)) &&
+ pointer_in_menu_window (widget, event->x_root, event->y_root))
+ {
+ /* Ugly: make sure menu_shell->button gets reset to 0 when we
+ * bail out early here so it is in a consistent state for the
+ * next button_press/button_release in GtkMenuShell.
+ * See bug #449371.
+ */
+ if (GTK_MENU_SHELL (widget)->active)
+ GTK_MENU_SHELL (widget)->button = 0;
+
+ return TRUE;
+ }
+
+ return GTK_WIDGET_CLASS (gtk_menu_parent_class)->button_release_event (widget, event);
}
static const gchar *
gtk_menu_stop_navigating_submenu (menu);
- if (GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
+ if (GTK_WIDGET_CLASS (gtk_menu_parent_class)->key_press_event (widget, event))
return TRUE;
display = gtk_widget_get_display (widget);
"gtk-can-change-accels", &can_change_accels,
NULL);
- if (accel)
+ if (accel && *accel)
{
guint keyval = 0;
GdkModifierType mods = 0;
- gboolean handled = FALSE;
gtk_accelerator_parse (accel, &keyval, &mods);
* thing, to properly consider i18n etc., but that probably requires
* AccelGroup changes etc.
*/
- if (event->keyval == keyval &&
- (mods & event->state) == mods)
- gtk_menu_shell_cancel (menu_shell);
-
- g_free (accel);
-
- if (handled)
- return TRUE;
+ if (event->keyval == keyval && (mods & event->state) == mods)
+ {
+ gtk_menu_shell_cancel (menu_shell);
+ g_free (accel);
+ return TRUE;
+ }
}
+
+ g_free (accel);
switch (event->keyval)
{
* (basically, those items are accelerator-locked).
*/
/* g_print("item has no path or is locked, menu prefix: %s\n", menu->accel_path); */
- gdk_display_beep (display);
+ gtk_widget_error_bell (widget);
}
else
{
* locked already
*/
/* g_print("failed to change\n"); */
- gdk_display_beep (display);
+ gtk_widget_error_bell (widget);
}
}
}
static gboolean
check_threshold (GtkWidget *widget,
- int start_x, int start_y,
- int x, int y)
+ gint start_x,
+ gint start_y,
+ gint x,
+ gint y)
{
#define THRESHOLD 8
}
static gboolean
-definitely_within_item (GtkWidget *widget,
- int x,
- int y)
+definitely_within_item (GtkWidget *widget,
+ gint x,
+ gint y)
{
GdkWindow *window = GTK_MENU_ITEM (widget)->event_window;
int w, h;
}
static gboolean
-gtk_menu_motion_notify (GtkWidget *widget,
- GdkEventMotion *event)
+gtk_menu_motion_notify (GtkWidget *widget,
+ GdkEventMotion *event)
{
GtkWidget *menu_item;
GtkMenu *menu;
if (definitely_within_item (menu_item, event->x, event->y))
menu_shell->activate_time = 0;
-
+
need_enter = (menu->navigation_region != NULL || menu_shell->ignore_enter);
/* Check to see if we are within an active submenu's navigation region
send_event->crossing.y_root = event->y_root;
send_event->crossing.x = event->x;
send_event->crossing.y = event->y;
+ send_event->crossing.state = event->state;
/* We send the event to 'widget', the currently active menu,
* instead of 'menu', the menu that the pointer is in. This
static gboolean
get_double_arrows (GtkMenu *menu)
{
- GtkMenuPrivate *priv = gtk_menu_get_private (menu);
- gboolean double_arrows;
+ GtkMenuPrivate *priv = gtk_menu_get_private (menu);
+ gboolean double_arrows;
+ GtkArrowPlacement arrow_placement;
gtk_widget_style_get (GTK_WIDGET (menu),
"double-arrows", &double_arrows,
+ "arrow-placement", &arrow_placement,
NULL);
+ if (arrow_placement != GTK_ARROWS_BOTH)
+ return TRUE;
+
return double_arrows || (priv->initially_pushed_in &&
menu->scroll_offset != 0);
}
gint offset;
gint view_width, view_height;
gboolean double_arrows;
-
+ GtkBorder arrow_border;
+
widget = GTK_WIDGET (menu);
offset = menu->scroll_offset + step;
+ get_arrows_border (menu, &arrow_border);
+
double_arrows = get_double_arrows (menu);
/* If we scroll upward and the non-visible top part
* screen space than just scrolling to the top.
*/
if (!double_arrows)
- if ((step < 0) && (offset < MENU_SCROLL_ARROW_HEIGHT))
+ if ((step < 0) && (offset < arrow_border.top))
offset = 0;
/* Don't scroll over the top if we weren't before: */
/* Don't scroll past the bottom if we weren't before: */
if (menu->scroll_offset > 0)
- view_height -= MENU_SCROLL_ARROW_HEIGHT;
+ view_height -= arrow_border.top;
/* When both arrows are always shown, reduce
* view height even more.
*/
if (double_arrows)
- view_height -= MENU_SCROLL_ARROW_HEIGHT;
+ view_height -= arrow_border.bottom;
if ((menu->scroll_offset + view_height <= widget->requisition.height) &&
(offset + view_height > widget->requisition.height))
}
static gboolean
-gtk_menu_scroll_timeout (gpointer data)
+gtk_menu_scroll_timeout (gpointer data)
{
- GtkMenu *menu;
- GtkSettings *settings;
- gboolean touchscreen_mode;
-
- GDK_THREADS_ENTER ();
+ GtkMenu *menu;
+ gboolean touchscreen_mode;
menu = GTK_MENU (data);
- settings = gtk_widget_get_settings (GTK_WIDGET (menu));
- g_object_get (settings,
+ g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
"gtk-touchscreen-mode", &touchscreen_mode,
NULL);
gtk_menu_do_timeout_scroll (menu, touchscreen_mode);
- GDK_THREADS_LEAVE ();
-
return TRUE;
}
static gboolean
gtk_menu_scroll_timeout_initial (gpointer data)
{
- GtkMenu *menu;
- GtkSettings *settings;
- guint timeout;
- gboolean touchscreen_mode;
-
- GDK_THREADS_ENTER ();
+ GtkMenu *menu;
+ guint timeout;
+ gboolean touchscreen_mode;
menu = GTK_MENU (data);
- settings = gtk_widget_get_settings (GTK_WIDGET (menu));
- g_object_get (settings,
+ g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
"gtk-timeout-repeat", &timeout,
"gtk-touchscreen-mode", &touchscreen_mode,
NULL);
gtk_menu_remove_scroll_timeout (menu);
- menu->timeout_id = g_timeout_add (timeout, gtk_menu_scroll_timeout, menu);
-
- GDK_THREADS_LEAVE ();
+ menu->timeout_id = gdk_threads_add_timeout (timeout,
+ gtk_menu_scroll_timeout,
+ menu);
return FALSE;
}
static void
gtk_menu_start_scrolling (GtkMenu *menu)
{
- GtkSettings *settings;
- guint timeout;
- gboolean touchscreen_mode;
+ guint timeout;
+ gboolean touchscreen_mode;
- settings = gtk_widget_get_settings (GTK_WIDGET (menu));
- g_object_get (settings,
+ g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
"gtk-timeout-repeat", &timeout,
"gtk-touchscreen-mode", &touchscreen_mode,
NULL);
gtk_menu_do_timeout_scroll (menu, touchscreen_mode);
- menu->timeout_id = g_timeout_add (timeout, gtk_menu_scroll_timeout_initial,
- menu);
+ menu->timeout_id = gdk_threads_add_timeout (timeout,
+ gtk_menu_scroll_timeout_initial,
+ menu);
}
static gboolean
return TRUE;
}
+static void
+get_arrows_sensitive_area (GtkMenu *menu,
+ GdkRectangle *upper,
+ GdkRectangle *lower)
+{
+ gint width, height;
+ gint border;
+ guint vertical_padding;
+ gint win_x, win_y;
+ gint scroll_arrow_height;
+ GtkArrowPlacement arrow_placement;
+
+ gdk_drawable_get_size (GTK_WIDGET (menu)->window, &width, &height);
+
+ gtk_widget_style_get (GTK_WIDGET (menu),
+ "vertical-padding", &vertical_padding,
+ "scroll-arrow-vlength", &scroll_arrow_height,
+ "arrow-placement", &arrow_placement,
+ NULL);
+
+ border = GTK_CONTAINER (menu)->border_width +
+ GTK_WIDGET (menu)->style->ythickness + vertical_padding;
+
+ gdk_window_get_position (GTK_WIDGET (menu)->window, &win_x, &win_y);
+
+ switch (arrow_placement)
+ {
+ case GTK_ARROWS_BOTH:
+ if (upper)
+ {
+ upper->x = win_x;
+ upper->y = win_y;
+ upper->width = width;
+ upper->height = scroll_arrow_height + border;
+ }
+
+ if (lower)
+ {
+ lower->x = win_x;
+ lower->y = win_y + height - border - scroll_arrow_height;
+ lower->width = width;
+ lower->height = scroll_arrow_height + border;
+ }
+ break;
+
+ case GTK_ARROWS_START:
+ if (upper)
+ {
+ upper->x = win_x;
+ upper->y = win_y;
+ upper->width = width / 2;
+ upper->height = scroll_arrow_height + border;
+ }
+
+ if (lower)
+ {
+ lower->x = win_x + width / 2;
+ lower->y = win_y;
+ lower->width = width / 2;
+ lower->height = scroll_arrow_height + border;
+ }
+ break;
+
+ case GTK_ARROWS_END:
+ if (upper)
+ {
+ upper->x = win_x;
+ upper->y = win_y + height - border - scroll_arrow_height;
+ upper->width = width / 2;
+ upper->height = scroll_arrow_height + border;
+ }
+
+ if (lower)
+ {
+ lower->x = win_x + width / 2;
+ lower->y = win_y + height - border - scroll_arrow_height;
+ lower->width = width / 2;
+ lower->height = scroll_arrow_height + border;
+ }
+ break;
+ }
+}
+
+
static void
gtk_menu_handle_scrolling (GtkMenu *menu,
gint x,
{
GtkMenuShell *menu_shell;
GtkMenuPrivate *priv;
- gint width, height;
- gint border;
GdkRectangle rect;
gboolean in_arrow;
gboolean scroll_fast = FALSE;
- guint vertical_padding;
gint top_x, top_y;
- gint win_x, win_y;
- GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (menu));
gboolean touchscreen_mode;
priv = gtk_menu_get_private (menu);
menu_shell = GTK_MENU_SHELL (menu);
- gdk_drawable_get_size (GTK_WIDGET (menu)->window, &width, &height);
-
- g_object_get (G_OBJECT (settings),
+ g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
"gtk-touchscreen-mode", &touchscreen_mode,
NULL);
- gtk_widget_style_get (GTK_WIDGET (menu),
- "vertical-padding", &vertical_padding,
- NULL);
-
- border = GTK_CONTAINER (menu)->border_width +
- GTK_WIDGET (menu)->style->ythickness + vertical_padding;
-
gdk_window_get_position (menu->toplevel->window, &top_x, &top_y);
x -= top_x;
y -= top_y;
- gdk_window_get_position (GTK_WIDGET (menu)->window, &win_x, &win_y);
-
/* upper arrow handling */
- rect.x = win_x;
- rect.y = win_y;
- rect.width = width;
- rect.height = MENU_SCROLL_ARROW_HEIGHT + border;
+ get_arrows_sensitive_area (menu, &rect, NULL);
in_arrow = FALSE;
if (menu->upper_arrow_visible && !menu->tearoff_active &&
if (priv->upper_arrow_state != GTK_STATE_INSENSITIVE)
{
+ gboolean arrow_pressed = FALSE;
+
if (menu->upper_arrow_visible && !menu->tearoff_active)
{
if (touchscreen_mode)
{
- if (enter && menu->upper_arrow_prelight &&
- menu->timeout_id == 0)
+ if (enter && menu->upper_arrow_prelight)
{
- /* Deselect the active item so that
- * any submenus are popped down
- */
- gtk_menu_shell_deselect (menu_shell);
-
- gtk_menu_remove_scroll_timeout (menu);
- menu->scroll_step = -MENU_SCROLL_STEP2; /* always fast */
-
- if (!motion)
+ if (menu->timeout_id == 0)
{
- /* Only do stuff on click. */
- gtk_menu_start_scrolling (menu);
- priv->upper_arrow_state = GTK_STATE_ACTIVE;
+ /* Deselect the active item so that
+ * any submenus are popped down
+ */
+ gtk_menu_shell_deselect (menu_shell);
+
+ gtk_menu_remove_scroll_timeout (menu);
+ menu->scroll_step = -MENU_SCROLL_STEP2; /* always fast */
+
+ if (!motion)
+ {
+ /* Only do stuff on click. */
+ gtk_menu_start_scrolling (menu);
+ arrow_pressed = TRUE;
+ }
+ }
+ else
+ {
+ arrow_pressed = TRUE;
}
-
- gdk_window_invalidate_rect (GTK_WIDGET (menu)->window,
- &rect, FALSE);
}
else if (!enter)
{
- gdk_window_invalidate_rect (GTK_WIDGET (menu)->window,
- &rect, FALSE);
-
gtk_menu_stop_scrolling (menu);
}
}
{
menu->upper_arrow_prelight = TRUE;
menu->scroll_fast = scroll_fast;
- gdk_window_invalidate_rect (GTK_WIDGET (menu)->window,
- &rect, FALSE);
/* Deselect the active item so that
* any submenus are popped down
-MENU_SCROLL_STEP2 : -MENU_SCROLL_STEP1;
menu->timeout_id =
- g_timeout_add (scroll_fast ?
- MENU_SCROLL_TIMEOUT2 : MENU_SCROLL_TIMEOUT1,
- gtk_menu_scroll_timeout, menu);
+ gdk_threads_add_timeout (scroll_fast ?
+ MENU_SCROLL_TIMEOUT2 :
+ MENU_SCROLL_TIMEOUT1,
+ gtk_menu_scroll_timeout, menu);
}
else if (!enter && !in_arrow && menu->upper_arrow_prelight)
{
- gdk_window_invalidate_rect (GTK_WIDGET (menu)->window,
- &rect, FALSE);
-
gtk_menu_stop_scrolling (menu);
}
}
}
- priv->upper_arrow_state = menu->upper_arrow_prelight ?
- GTK_STATE_PRELIGHT : GTK_STATE_NORMAL;
+ /* gtk_menu_start_scrolling() might have hit the top of the
+ * menu, so check if the button isn't insensitive before
+ * changing it to something else.
+ */
+ if (priv->upper_arrow_state != GTK_STATE_INSENSITIVE)
+ {
+ GtkStateType arrow_state = GTK_STATE_NORMAL;
+
+ if (arrow_pressed)
+ arrow_state = GTK_STATE_ACTIVE;
+ else if (menu->upper_arrow_prelight)
+ arrow_state = GTK_STATE_PRELIGHT;
+
+ if (arrow_state != priv->upper_arrow_state)
+ {
+ priv->upper_arrow_state = arrow_state;
+
+ gdk_window_invalidate_rect (GTK_WIDGET (menu)->window,
+ &rect, FALSE);
+ }
+ }
}
/* lower arrow handling */
- rect.x = win_x;
- rect.y = win_y + height - border - MENU_SCROLL_ARROW_HEIGHT;
- rect.width = width;
- rect.height = MENU_SCROLL_ARROW_HEIGHT + border;
+ get_arrows_sensitive_area (menu, NULL, &rect);
in_arrow = FALSE;
if (menu->lower_arrow_visible && !menu->tearoff_active &&
if (priv->lower_arrow_state != GTK_STATE_INSENSITIVE)
{
+ gboolean arrow_pressed = FALSE;
+
if (menu->lower_arrow_visible && !menu->tearoff_active)
{
if (touchscreen_mode)
{
- if (enter && menu->lower_arrow_prelight &&
- menu->timeout_id == 0)
+ if (enter && menu->lower_arrow_prelight)
{
- /* Deselect the active item so that
- * any submenus are popped down
- */
- gtk_menu_shell_deselect (menu_shell);
-
- gtk_menu_remove_scroll_timeout (menu);
- menu->scroll_step = MENU_SCROLL_STEP2; /* always fast */
-
- if (!motion)
+ if (menu->timeout_id == 0)
{
- /* Only do stuff on click. */
- gtk_menu_start_scrolling (menu);
- priv->lower_arrow_state = GTK_STATE_ACTIVE;
+ /* Deselect the active item so that
+ * any submenus are popped down
+ */
+ gtk_menu_shell_deselect (menu_shell);
+
+ gtk_menu_remove_scroll_timeout (menu);
+ menu->scroll_step = MENU_SCROLL_STEP2; /* always fast */
+
+ if (!motion)
+ {
+ /* Only do stuff on click. */
+ gtk_menu_start_scrolling (menu);
+ arrow_pressed = TRUE;
+ }
+ }
+ else
+ {
+ arrow_pressed = TRUE;
}
-
- gdk_window_invalidate_rect (GTK_WIDGET (menu)->window,
- &rect, FALSE);
}
else if (!enter)
{
- gdk_window_invalidate_rect (GTK_WIDGET (menu)->window,
- &rect, FALSE);
-
gtk_menu_stop_scrolling (menu);
}
}
{
menu->lower_arrow_prelight = TRUE;
menu->scroll_fast = scroll_fast;
- gdk_window_invalidate_rect (GTK_WIDGET (menu)->window,
- &rect, FALSE);
/* Deselect the active item so that
* any submenus are popped down
MENU_SCROLL_STEP2 : MENU_SCROLL_STEP1;
menu->timeout_id =
- g_timeout_add (scroll_fast ?
- MENU_SCROLL_TIMEOUT2 : MENU_SCROLL_TIMEOUT1,
- gtk_menu_scroll_timeout, menu);
+ gdk_threads_add_timeout (scroll_fast ?
+ MENU_SCROLL_TIMEOUT2 :
+ MENU_SCROLL_TIMEOUT1,
+ gtk_menu_scroll_timeout, menu);
}
else if (!enter && !in_arrow && menu->lower_arrow_prelight)
{
- gdk_window_invalidate_rect (GTK_WIDGET (menu)->window,
- &rect, FALSE);
-
gtk_menu_stop_scrolling (menu);
}
}
}
- priv->lower_arrow_state = menu->lower_arrow_prelight ?
- GTK_STATE_PRELIGHT : GTK_STATE_NORMAL;
+ /* gtk_menu_start_scrolling() might have hit the bottom of the
+ * menu, so check if the button isn't insensitive before
+ * changing it to something else.
+ */
+ if (priv->lower_arrow_state != GTK_STATE_INSENSITIVE)
+ {
+ GtkStateType arrow_state = GTK_STATE_NORMAL;
+
+ if (arrow_pressed)
+ arrow_state = GTK_STATE_ACTIVE;
+ else if (menu->lower_arrow_prelight)
+ arrow_state = GTK_STATE_PRELIGHT;
+
+ if (arrow_state != priv->lower_arrow_state)
+ {
+ priv->lower_arrow_state = arrow_state;
+
+ gdk_window_invalidate_rect (GTK_WIDGET (menu)->window,
+ &rect, FALSE);
+ }
+ }
}
}
gtk_menu_enter_notify (GtkWidget *widget,
GdkEventCrossing *event)
{
- GtkSettings *settings = gtk_widget_get_settings (widget);
GtkWidget *menu_item;
- gboolean touchscreen_mode;
+ gboolean touchscreen_mode;
+
+ if (event->mode == GDK_CROSSING_GTK_GRAB ||
+ event->mode == GDK_CROSSING_GTK_UNGRAB ||
+ event->mode == GDK_CROSSING_STATE_CHANGED)
+ return TRUE;
- g_object_get (G_OBJECT (settings),
+ g_object_get (gtk_widget_get_settings (widget),
"gtk-touchscreen-mode", &touchscreen_mode,
NULL);
event->x_root, event->y_root))
return TRUE;
- return GTK_WIDGET_CLASS (parent_class)->enter_notify_event (widget, event);
+ return GTK_WIDGET_CLASS (gtk_menu_parent_class)->enter_notify_event (widget, event);
}
static gboolean
GtkMenuItem *menu_item;
GtkWidget *event_widget;
+ if (event->mode == GDK_CROSSING_GTK_GRAB ||
+ event->mode == GDK_CROSSING_GTK_UNGRAB ||
+ event->mode == GDK_CROSSING_STATE_CHANGED)
+ return TRUE;
+
menu = GTK_MENU (widget);
menu_shell = GTK_MENU_SHELL (widget);
}
}
- return GTK_WIDGET_CLASS (parent_class)->leave_notify_event (widget, event);
+ return GTK_WIDGET_CLASS (gtk_menu_parent_class)->leave_notify_event (widget, event);
}
static void
{
gdk_region_destroy (menu->navigation_region);
menu->navigation_region = NULL;
- }
-
+ }
if (menu->navigation_timeout)
{
g_source_remove (menu->navigation_timeout);
GtkMenu *menu = user_data;
GdkWindow *child_window;
- GDK_THREADS_ENTER ();
-
gtk_menu_stop_navigating_submenu (menu);
if (GTK_WIDGET_REALIZED (menu))
send_event->crossing.time = GDK_CURRENT_TIME; /* Bogus */
send_event->crossing.send_event = TRUE;
- GTK_WIDGET_CLASS (parent_class)->enter_notify_event (GTK_WIDGET (menu), (GdkEventCrossing *)send_event);
+ GTK_WIDGET_CLASS (gtk_menu_parent_class)->enter_notify_event (GTK_WIDGET (menu), (GdkEventCrossing *)send_event);
gdk_event_free (send_event);
}
}
- GDK_THREADS_LEAVE ();
-
return FALSE;
}
"gtk-menu-popdown-delay", &popdown_delay,
NULL);
- menu->navigation_timeout = g_timeout_add (popdown_delay,
- gtk_menu_stop_navigating_submenu_cb, menu);
+ menu->navigation_timeout = gdk_threads_add_timeout (popdown_delay,
+ gtk_menu_stop_navigating_submenu_cb,
+ menu);
#ifdef DRAW_STAY_UP_TRIANGLE
draw_stay_up_triangle (gdk_get_default_root_window(),
GdkScreen *screen;
GdkScreen *pointer_screen;
GdkRectangle monitor;
-
+
g_return_if_fail (GTK_IS_MENU (menu));
widget = GTK_WIDGET (menu);
screen = gtk_widget_get_screen (widget);
gdk_display_get_pointer (gdk_screen_get_display (screen),
&pointer_screen, &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 a size_request was queued while we weren't popped up,
private->monitor_num = gdk_screen_get_monitor_at_point (screen, x, y);
private->initially_pushed_in = FALSE;
+
+ /* Set the type hint here to allow custom position functions to set a different hint */
+ if (!GTK_WIDGET_VISIBLE (menu->toplevel))
+ gtk_window_set_type_hint (GTK_WINDOW (menu->toplevel), GDK_WINDOW_TYPE_HINT_POPUP_MENU);
if (menu->position_func)
{
(* menu->position_func) (menu, &x, &y, &private->initially_pushed_in,
menu->position_func_data);
+
if (private->monitor_num < 0)
private->monitor_num = gdk_screen_get_monitor_at_point (screen, x, y);
}
if (scroll_offset > 0)
- scroll_offset += MENU_SCROLL_ARROW_HEIGHT;
+ {
+ GtkBorder arrow_border;
+
+ get_arrows_border (menu, &arrow_border);
+ scroll_offset += arrow_border.top;
+ }
gtk_window_move (GTK_WINDOW (GTK_MENU_SHELL (menu)->active ? menu->toplevel : menu->tearoff_window),
x, y);
static void
gtk_menu_stop_scrolling (GtkMenu *menu)
{
- GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (menu));
gboolean touchscreen_mode;
gtk_menu_remove_scroll_timeout (menu);
- g_object_get (G_OBJECT (settings),
- "gtk-touchscreen-mode", &touchscreen_mode,
- NULL);
+ g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
+ "gtk-touchscreen-mode", &touchscreen_mode,
+ NULL);
if (!touchscreen_mode)
{
guint vertical_padding;
guint horizontal_padding;
gboolean double_arrows;
-
+ GtkBorder arrow_border;
+
widget = GTK_WIDGET (menu);
if (menu->tearoff_active &&
if (!menu->upper_arrow_visible || !menu->lower_arrow_visible)
gtk_widget_queue_draw (GTK_WIDGET (menu));
- view_height -= 2 * MENU_SCROLL_ARROW_HEIGHT;
- y += MENU_SCROLL_ARROW_HEIGHT;
-
menu->upper_arrow_visible = menu->lower_arrow_visible = TRUE;
+ get_arrows_border (menu, &arrow_border);
+ y += arrow_border.top;
+ view_height -= arrow_border.top;
+ view_height -= arrow_border.bottom;
+
if (offset <= 0)
priv->upper_arrow_state = GTK_STATE_INSENSITIVE;
- else
+ else if (priv->upper_arrow_state == GTK_STATE_INSENSITIVE)
priv->upper_arrow_state = menu->upper_arrow_prelight ?
GTK_STATE_PRELIGHT : GTK_STATE_NORMAL;
if (offset >= menu_height - view_height)
priv->lower_arrow_state = GTK_STATE_INSENSITIVE;
- else
+ else if (priv->lower_arrow_state == GTK_STATE_INSENSITIVE)
priv->lower_arrow_state = menu->lower_arrow_prelight ?
GTK_STATE_PRELIGHT : GTK_STATE_NORMAL;
last_visible = menu->upper_arrow_visible;
menu->upper_arrow_visible = offset > 0;
- if (menu->upper_arrow_visible)
- view_height -= MENU_SCROLL_ARROW_HEIGHT;
+ /* upper_arrow_visible may have changed, so requery the border */
+ get_arrows_border (menu, &arrow_border);
+ view_height -= arrow_border.top;
if ((last_visible != menu->upper_arrow_visible) &&
!menu->upper_arrow_visible)
last_visible = menu->lower_arrow_visible;
menu->lower_arrow_visible = offset < menu_height - view_height;
- if (menu->lower_arrow_visible)
- view_height -= MENU_SCROLL_ARROW_HEIGHT;
+ /* lower_arrow_visible may have changed, so requery the border */
+ get_arrows_border (menu, &arrow_border);
+ view_height -= arrow_border.bottom;
if ((last_visible != menu->lower_arrow_visible) &&
!menu->lower_arrow_visible)
}
}
- if (menu->upper_arrow_visible)
- y += MENU_SCROLL_ARROW_HEIGHT;
+ y += arrow_border.top;
}
/* Scroll the menu: */
}
static void
-gtk_menu_scroll_item_visible (GtkMenuShell *menu_shell,
- GtkWidget *menu_item)
+gtk_menu_scroll_item_visible (GtkMenuShell *menu_shell,
+ GtkWidget *menu_item)
{
GtkMenu *menu;
gint child_offset, child_height;
{
guint vertical_padding;
gboolean double_arrows;
-
+
y = menu->scroll_offset;
gdk_drawable_get_size (GTK_WIDGET (menu)->window, &width, &height);
}
else
{
- arrow_height = 0;
- if (menu->upper_arrow_visible && !menu->tearoff_active)
- arrow_height += MENU_SCROLL_ARROW_HEIGHT;
- if (menu->lower_arrow_visible && !menu->tearoff_active)
- arrow_height += MENU_SCROLL_ARROW_HEIGHT;
+ GtkBorder arrow_border;
+
+ arrow_height = 0;
+
+ get_arrows_border (menu, &arrow_border);
+ if (!menu->tearoff_active)
+ arrow_height = arrow_border.top + arrow_border.bottom;
if (child_offset + child_height > y + height - arrow_height)
{
arrow_height = 0;
if ((!last_child && !menu->tearoff_active) || double_arrows)
- arrow_height += MENU_SCROLL_ARROW_HEIGHT;
+ arrow_height += arrow_border.bottom;
y = child_offset + child_height - height + arrow_height;
if (((y > 0) && !menu->tearoff_active) || double_arrows)
{
/* Need upper arrow */
- arrow_height += MENU_SCROLL_ARROW_HEIGHT;
+ arrow_height += arrow_border.top;
y = child_offset + child_height - height + arrow_height;
}
/* Ignore the enter event we might get if the pointer is on the menu
}
static void
-gtk_menu_select_item (GtkMenuShell *menu_shell,
- GtkWidget *menu_item)
+gtk_menu_select_item (GtkMenuShell *menu_shell,
+ GtkWidget *menu_item)
{
GtkMenu *menu = GTK_MENU (menu_shell);
if (GTK_WIDGET_REALIZED (GTK_WIDGET (menu)))
gtk_menu_scroll_item_visible (menu_shell, menu_item);
- GTK_MENU_SHELL_CLASS (parent_class)->select_item (menu_shell, menu_item);
+ GTK_MENU_SHELL_CLASS (gtk_menu_parent_class)->select_item (menu_shell, menu_item);
}
* (the image of the menu) is left in place.
*/
static void
-gtk_menu_reparent (GtkMenu *menu,
- GtkWidget *new_parent,
- gboolean unrealize)
+gtk_menu_reparent (GtkMenu *menu,
+ GtkWidget *new_parent,
+ gboolean unrealize)
{
GtkObject *object = GTK_OBJECT (menu);
GtkWidget *widget = GTK_WIDGET (menu);
/**
* gtk_menu_set_screen:
* @menu: a #GtkMenu.
- * @screen: a #GdkScreen, or %NULL if the screen should be
+ * @screen: (allow-none): a #GdkScreen, or %NULL if the screen should be
* determined by the widget the menu is attached to.
*
* Sets the #GdkScreen on which the menu will be displayed.
- *
+ *
* Since: 2.2
**/
void
}
static void
-gtk_menu_move_current (GtkMenuShell *menu_shell,
- GtkMenuDirectionType direction)
+gtk_menu_move_current (GtkMenuShell *menu_shell,
+ GtkMenuDirectionType direction)
{
GtkMenu *menu = GTK_MENU (menu_shell);
gint i;
}
}
- GTK_MENU_SHELL_CLASS (parent_class)->move_current (menu_shell, direction);
+ GTK_MENU_SHELL_CLASS (gtk_menu_parent_class)->move_current (menu_shell, direction);
}
static gint
gint menu_height = (widget->allocation.height
- 2 * (container->border_width
+ widget->style->ythickness));
-
- if (menu->upper_arrow_visible && !menu->tearoff_active)
- menu_height -= MENU_SCROLL_ARROW_HEIGHT;
- if (menu->lower_arrow_visible && !menu->tearoff_active)
- menu_height -= MENU_SCROLL_ARROW_HEIGHT;
+ if (!menu->tearoff_active)
+ {
+ GtkBorder arrow_border;
+
+ get_arrows_border (menu, &arrow_border);
+ menu_height -= arrow_border.top;
+ menu_height -= arrow_border.bottom;
+ }
+
return menu_height;
}
height = widget->requisition.height;
height -= (GTK_CONTAINER (widget)->border_width + widget->style->ythickness) * 2;
- if (menu->upper_arrow_visible && !menu->tearoff_active)
- height -= MENU_SCROLL_ARROW_HEIGHT;
+ if (!menu->tearoff_active)
+ {
+ GtkBorder arrow_border;
- if (menu->lower_arrow_visible && !menu->tearoff_active)
- height -= MENU_SCROLL_ARROW_HEIGHT;
+ get_arrows_border (menu, &arrow_border);
+ height -= arrow_border.top;
+ height -= arrow_border.bottom;
+ }
return height;
}
{
GtkWidget *new_child;
gboolean new_upper_arrow_visible = menu->upper_arrow_visible && !menu->tearoff_active;
+ GtkBorder arrow_border;
+
+ get_arrows_border (menu, &arrow_border);
if (menu->scroll_offset != old_offset)
step = menu->scroll_offset - old_offset;
- step -= (new_upper_arrow_visible - old_upper_arrow_visible) * MENU_SCROLL_ARROW_HEIGHT;
+ step -= (new_upper_arrow_visible - old_upper_arrow_visible) * arrow_border.top;
new_child = child_at (menu, child_offset + step);
if (new_child)
priv->monitor_num = monitor_num;
}
+/**
+ * gtk_menu_get_monitor:
+ * @menu: a #GtkMenu
+ *
+ * Retrieves the number of the monitor on which to show the menu.
+ *
+ * Returns: the number of the monitor on which the menu should
+ * be popped up or -1, if no monitor has been set
+ *
+ * Since: 2.14
+ **/
+gint
+gtk_menu_get_monitor (GtkMenu *menu)
+{
+ GtkMenuPrivate *priv;
+ g_return_val_if_fail (GTK_IS_MENU (menu), -1);
+
+ priv = gtk_menu_get_private (menu);
+
+ return priv->monitor_num;
+}
+
/**
* gtk_menu_get_for_attach_widget:
* @widget: a #GtkWidget
* Returns a list of the menus which are attached to this widget.
* This list is owned by GTK+ and must not be modified.
*
- * Return value: the list of menus attached to his widget.
+ * Return value: (element-type GtkWidget) (transfer none): the list of menus attached to his widget.
*
* Since: 2.6
**/
}
}
+/**
+ * gtk_menu_set_reserve_toggle_size:
+ * @menu: a #GtkMenu
+ * @reserve_toggle_size: whether to reserve size for toggles
+ *
+ * Sets whether the menu should reserve space for drawing toggles
+ * or icons, regardless of their actual presence.
+ *
+ * Since: 2.18
+ */
+void
+gtk_menu_set_reserve_toggle_size (GtkMenu *menu,
+ gboolean reserve_toggle_size)
+{
+ GtkMenuPrivate *priv = gtk_menu_get_private (menu);
+ gboolean no_toggle_size;
+
+ no_toggle_size = !reserve_toggle_size;
+
+ if (priv->no_toggle_size != no_toggle_size)
+ {
+ priv->no_toggle_size = no_toggle_size;
+
+ g_object_notify (G_OBJECT (menu), "reserve-toggle-size");
+ }
+}
+
+/**
+ * gtk_menu_get_reserve_toggle_size:
+ * @menu: a #GtkMenu
+ *
+ * Returns whether the menu reserves space for toggles and
+ * icons, regardless of their actual presence.
+ *
+ * Returns: Whether the menu reserves toggle space
+ *
+ * Since: 2.18
+ */
+gboolean
+gtk_menu_get_reserve_toggle_size (GtkMenu *menu)
+{
+ GtkMenuPrivate *priv = gtk_menu_get_private (menu);
+
+ return !priv->no_toggle_size;
+}
+
#define __GTK_MENU_C__
#include "gtkaliasdef.c"