forked from AuroraMiddleware/gtk
gtkmenu*: Simplify event handling
Instead of delegating on the parent shell of a menu item/shell on a variety of situations, Simplify event handling so: 1) Menu item selection is handled entirely on GtkMenuItem through crossing events. 2) The deepmost menu shell handles clicks inside and outside of it. This avoids the rather hard to follow gtk_widget_event() calls going on all throughout the handling of crossing and button events, and makes menus work again.
This commit is contained in:
parent
33c5f3c193
commit
40ab7e1c95
259
gtk/gtkmenu.c
259
gtk/gtkmenu.c
@ -279,14 +279,10 @@ static void gtk_menu_real_move_scroll (GtkMenu *menu,
|
||||
GtkScrollType type);
|
||||
|
||||
static void gtk_menu_stop_navigating_submenu (GtkMenu *menu);
|
||||
static gboolean gtk_menu_stop_navigating_submenu_cb (gpointer user_data);
|
||||
static gboolean gtk_menu_navigating_submenu (GtkMenu *menu,
|
||||
gint event_x,
|
||||
gint event_y);
|
||||
static void gtk_menu_set_submenu_navigation_region (GtkMenu *menu,
|
||||
GtkMenuItem *menu_item,
|
||||
GdkEventCrossing *event);
|
||||
|
||||
|
||||
static void gtk_menu_deactivate (GtkMenuShell *menu_shell);
|
||||
static void gtk_menu_position (GtkMenu *menu,
|
||||
gboolean set_scroll_offset);
|
||||
@ -1131,10 +1127,6 @@ gtk_menu_window_event (GtkWidget *window,
|
||||
|
||||
switch (event->type)
|
||||
{
|
||||
case GDK_KEY_PRESS:
|
||||
case GDK_KEY_RELEASE:
|
||||
handled = gtk_widget_event (menu, event);
|
||||
break;
|
||||
case GDK_WINDOW_STATE:
|
||||
/* Window for the menu has been closed by the display server or by GDK.
|
||||
* Update the internal state as if the user had clicked outside the
|
||||
@ -3342,8 +3334,6 @@ gtk_menu_motion_notify (GtkWidget *widget,
|
||||
GtkWidget *parent;
|
||||
GdkDevice *source_device;
|
||||
|
||||
gboolean need_enter;
|
||||
|
||||
source_device = gdk_event_get_source_device ((GdkEvent *) event);
|
||||
|
||||
if (GTK_IS_MENU (widget) &&
|
||||
@ -3379,8 +3369,6 @@ gtk_menu_motion_notify (GtkWidget *widget,
|
||||
if (definitely_within_item (menu_item, event->x, event->y))
|
||||
menu_shell->priv->activate_time = 0;
|
||||
|
||||
need_enter = (gtk_menu_has_navigation_triangle (menu) || menu_shell->priv->ignore_enter);
|
||||
|
||||
/* Check to see if we are within an active submenu's navigation region
|
||||
*/
|
||||
if (gtk_menu_navigating_submenu (menu, event->x_root, event->y_root))
|
||||
@ -3398,42 +3386,6 @@ gtk_menu_motion_notify (GtkWidget *widget,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (need_enter)
|
||||
{
|
||||
/* The menu is now sensitive to enter events on its items, but
|
||||
* was previously sensitive. So we fake an enter event.
|
||||
*/
|
||||
menu_shell->priv->ignore_enter = FALSE;
|
||||
|
||||
if (event->x >= 0 && event->x < gdk_window_get_width (event->window) &&
|
||||
event->y >= 0 && event->y < gdk_window_get_height (event->window))
|
||||
{
|
||||
GdkEvent *send_event = gdk_event_new (GDK_ENTER_NOTIFY);
|
||||
gboolean result;
|
||||
|
||||
send_event->crossing.window = g_object_ref (event->window);
|
||||
send_event->crossing.time = event->time;
|
||||
send_event->crossing.send_event = TRUE;
|
||||
send_event->crossing.x_root = event->x_root;
|
||||
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;
|
||||
gdk_event_set_device (send_event, gdk_event_get_device ((GdkEvent *) event));
|
||||
|
||||
/* We send the event to 'widget', the currently active menu,
|
||||
* instead of 'menu', the menu that the pointer is in. This
|
||||
* will ensure that the event will be ignored unless the
|
||||
* menuitem is a child of the active menu or some parent
|
||||
* menu of the active menu.
|
||||
*/
|
||||
result = gtk_widget_event (widget, send_event);
|
||||
gdk_event_free (send_event);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@ -3733,8 +3685,6 @@ static gboolean
|
||||
gtk_menu_enter_notify (GtkWidget *widget,
|
||||
GdkEventCrossing *event)
|
||||
{
|
||||
GtkWidget *menu_item;
|
||||
GtkWidget *parent;
|
||||
GdkDevice *source_device;
|
||||
|
||||
if (event->mode == GDK_CROSSING_GTK_GRAB ||
|
||||
@ -3743,10 +3693,8 @@ gtk_menu_enter_notify (GtkWidget *widget,
|
||||
return TRUE;
|
||||
|
||||
source_device = gdk_event_get_source_device ((GdkEvent *) event);
|
||||
menu_item = gtk_get_event_widget ((GdkEvent*) event);
|
||||
|
||||
if (GTK_IS_MENU (widget) &&
|
||||
gdk_device_get_source (source_device) != GDK_SOURCE_TOUCHSCREEN)
|
||||
if (gdk_device_get_source (source_device) != GDK_SOURCE_TOUCHSCREEN)
|
||||
{
|
||||
GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
|
||||
|
||||
@ -3755,64 +3703,14 @@ gtk_menu_enter_notify (GtkWidget *widget,
|
||||
event->x_root, event->y_root, TRUE, TRUE);
|
||||
}
|
||||
|
||||
if (gdk_device_get_source (source_device) != GDK_SOURCE_TOUCHSCREEN &&
|
||||
GTK_IS_MENU_ITEM (menu_item))
|
||||
{
|
||||
GtkWidget *menu = gtk_widget_get_parent (menu_item);
|
||||
|
||||
if (GTK_IS_MENU (menu))
|
||||
{
|
||||
GtkMenuPrivate *priv = (GTK_MENU (menu))->priv;
|
||||
GtkMenuShell *menu_shell = GTK_MENU_SHELL (menu);
|
||||
|
||||
if (priv->seen_item_enter)
|
||||
{
|
||||
/* This is the second enter we see for an item
|
||||
* on this menu. This means a release should always
|
||||
* mean activate.
|
||||
*/
|
||||
menu_shell->priv->activate_time = 0;
|
||||
}
|
||||
else if ((event->detail != GDK_NOTIFY_NONLINEAR &&
|
||||
event->detail != GDK_NOTIFY_NONLINEAR_VIRTUAL))
|
||||
{
|
||||
if (definitely_within_item (menu_item, event->x, event->y))
|
||||
{
|
||||
/* This is an actual user-enter (ie. not a pop-under)
|
||||
* In this case, the user must either have entered
|
||||
* sufficiently far enough into the item, or he must move
|
||||
* far enough away from the enter point. (see
|
||||
* gtk_menu_motion_notify())
|
||||
*/
|
||||
menu_shell->priv->activate_time = 0;
|
||||
}
|
||||
}
|
||||
|
||||
priv->seen_item_enter = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* If this is a faked enter (see gtk_menu_motion_notify), 'widget'
|
||||
* will not correspond to the event widget's parent. Check to see
|
||||
* if we are in the parent's navigation region.
|
||||
*/
|
||||
parent = gtk_widget_get_parent (menu_item);
|
||||
if (GTK_IS_MENU_ITEM (menu_item) && GTK_IS_MENU (parent) &&
|
||||
gtk_menu_navigating_submenu (GTK_MENU (parent),
|
||||
event->x_root, event->y_root))
|
||||
return TRUE;
|
||||
|
||||
return GTK_WIDGET_CLASS (gtk_menu_parent_class)->enter_notify_event (widget, event);
|
||||
return GDK_EVENT_STOP;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_menu_leave_notify (GtkWidget *widget,
|
||||
GdkEventCrossing *event)
|
||||
{
|
||||
GtkMenuShell *menu_shell;
|
||||
GtkMenu *menu;
|
||||
GtkMenuItem *menu_item;
|
||||
GtkWidget *event_widget;
|
||||
GdkDevice *source_device;
|
||||
|
||||
if (event->mode == GDK_CROSSING_GTK_GRAB ||
|
||||
@ -3821,7 +3719,6 @@ gtk_menu_leave_notify (GtkWidget *widget,
|
||||
return TRUE;
|
||||
|
||||
menu = GTK_MENU (widget);
|
||||
menu_shell = GTK_MENU_SHELL (widget);
|
||||
|
||||
if (gtk_menu_navigating_submenu (menu, event->x_root, event->y_root))
|
||||
return TRUE;
|
||||
@ -3831,37 +3728,7 @@ gtk_menu_leave_notify (GtkWidget *widget,
|
||||
if (gdk_device_get_source (source_device) != GDK_SOURCE_TOUCHSCREEN)
|
||||
gtk_menu_handle_scrolling (menu, event->x_root, event->y_root, FALSE, TRUE);
|
||||
|
||||
event_widget = gtk_get_event_widget ((GdkEvent*) event);
|
||||
|
||||
if (!GTK_IS_MENU_ITEM (event_widget))
|
||||
return TRUE;
|
||||
|
||||
menu_item = GTK_MENU_ITEM (event_widget);
|
||||
|
||||
/* Here we check to see if we're leaving an active menu item
|
||||
* with a submenu, in which case we enter submenu navigation mode.
|
||||
*/
|
||||
if (menu_shell->priv->active_menu_item != NULL
|
||||
&& menu_item->priv->submenu != NULL
|
||||
&& menu_item->priv->submenu_placement == GTK_LEFT_RIGHT)
|
||||
{
|
||||
if (GTK_MENU_SHELL (menu_item->priv->submenu)->priv->active)
|
||||
{
|
||||
gtk_menu_set_submenu_navigation_region (menu, menu_item, event);
|
||||
return TRUE;
|
||||
}
|
||||
else if (menu_item == GTK_MENU_ITEM (menu_shell->priv->active_menu_item))
|
||||
{
|
||||
/* We are leaving an active menu item with nonactive submenu.
|
||||
* Deselect it so we don't surprise the user with by popping
|
||||
* up a submenu _after_ he left the item.
|
||||
*/
|
||||
gtk_menu_shell_deselect (menu_shell);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return GTK_WIDGET_CLASS (gtk_menu_parent_class)->leave_notify_event (widget, event);
|
||||
return GDK_EVENT_STOP;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@ -4019,43 +3886,6 @@ gtk_menu_stop_navigating_submenu (GtkMenu *menu)
|
||||
}
|
||||
}
|
||||
|
||||
/* When the timeout is elapsed, the navigation region is destroyed
|
||||
* and the menuitem under the pointer (if any) is selected.
|
||||
*/
|
||||
static gboolean
|
||||
gtk_menu_stop_navigating_submenu_cb (gpointer user_data)
|
||||
{
|
||||
GtkMenuPopdownData *popdown_data = user_data;
|
||||
GtkMenu *menu = popdown_data->menu;
|
||||
GtkMenuPrivate *priv = menu->priv;
|
||||
GdkWindow *child_window;
|
||||
|
||||
gtk_menu_stop_navigating_submenu (menu);
|
||||
|
||||
if (gtk_widget_get_realized (GTK_WIDGET (menu)))
|
||||
{
|
||||
child_window = gdk_window_get_device_position (priv->bin_window,
|
||||
popdown_data->device,
|
||||
NULL, NULL, NULL);
|
||||
|
||||
if (child_window)
|
||||
{
|
||||
GdkEvent *send_event = gdk_event_new (GDK_ENTER_NOTIFY);
|
||||
|
||||
send_event->crossing.window = g_object_ref (child_window);
|
||||
send_event->crossing.time = GDK_CURRENT_TIME; /* Bogus */
|
||||
send_event->crossing.send_event = TRUE;
|
||||
gdk_event_set_device (send_event, popdown_data->device);
|
||||
|
||||
GTK_WIDGET_CLASS (gtk_menu_parent_class)->enter_notify_event (GTK_WIDGET (menu), (GdkEventCrossing *)send_event);
|
||||
|
||||
gdk_event_free (send_event);
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_menu_navigating_submenu (GtkMenu *menu,
|
||||
gint event_x,
|
||||
@ -4103,87 +3933,6 @@ gtk_menu_navigating_submenu (GtkMenu *menu,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_menu_set_submenu_navigation_region (GtkMenu *menu,
|
||||
GtkMenuItem *menu_item,
|
||||
GdkEventCrossing *event)
|
||||
{
|
||||
GtkMenuPrivate *priv = menu->priv;
|
||||
gint submenu_left = 0;
|
||||
gint submenu_right = 0;
|
||||
gint submenu_top = 0;
|
||||
gint submenu_bottom = 0;
|
||||
gint width = 0;
|
||||
GtkWidget *event_widget;
|
||||
GtkMenuPopdownData *popdown_data;
|
||||
GdkWindow *window;
|
||||
|
||||
g_return_if_fail (menu_item->priv->submenu != NULL);
|
||||
g_return_if_fail (event != NULL);
|
||||
|
||||
event_widget = gtk_get_event_widget ((GdkEvent*) event);
|
||||
|
||||
window = gtk_widget_get_window (menu_item->priv->submenu);
|
||||
gdk_window_get_origin (window, &submenu_left, &submenu_top);
|
||||
|
||||
submenu_right = submenu_left + gdk_window_get_width (window);
|
||||
submenu_bottom = submenu_top + gdk_window_get_height (window);
|
||||
|
||||
width = gdk_window_get_width (gtk_widget_get_window (event_widget));
|
||||
|
||||
if (event->x >= 0 && event->x < width)
|
||||
{
|
||||
gtk_menu_stop_navigating_submenu (menu);
|
||||
|
||||
/* The navigation region is the triangle closest to the x/y
|
||||
* location of the rectangle. This is why the width or height
|
||||
* can be negative.
|
||||
*/
|
||||
if (menu_item->priv->submenu_direction == GTK_DIRECTION_RIGHT)
|
||||
{
|
||||
/* right */
|
||||
priv->navigation_x = submenu_left;
|
||||
priv->navigation_width = event->x_root - submenu_left;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* left */
|
||||
priv->navigation_x = submenu_right;
|
||||
priv->navigation_width = event->x_root - submenu_right;
|
||||
}
|
||||
|
||||
if (event->y < 0)
|
||||
{
|
||||
/* top */
|
||||
priv->navigation_y = event->y_root;
|
||||
priv->navigation_height = submenu_top - event->y_root - NAVIGATION_REGION_OVERSHOOT;
|
||||
|
||||
if (priv->navigation_height >= 0)
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* bottom */
|
||||
priv->navigation_y = event->y_root;
|
||||
priv->navigation_height = submenu_bottom - event->y_root + NAVIGATION_REGION_OVERSHOOT;
|
||||
|
||||
if (priv->navigation_height <= 0)
|
||||
return;
|
||||
}
|
||||
|
||||
popdown_data = g_new (GtkMenuPopdownData, 1);
|
||||
popdown_data->menu = menu;
|
||||
popdown_data->device = gdk_event_get_device ((GdkEvent *) event);
|
||||
|
||||
priv->navigation_timeout = gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT,
|
||||
MENU_POPDOWN_DELAY,
|
||||
gtk_menu_stop_navigating_submenu_cb,
|
||||
popdown_data,
|
||||
(GDestroyNotify) g_free);
|
||||
g_source_set_name_by_id (priv->navigation_timeout, "[gtk+] gtk_menu_stop_navigating_submenu_cb");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_menu_deactivate (GtkMenuShell *menu_shell)
|
||||
{
|
||||
|
@ -1213,18 +1213,37 @@ static gboolean
|
||||
gtk_menu_item_enter (GtkWidget *widget,
|
||||
GdkEventCrossing *event)
|
||||
{
|
||||
g_return_val_if_fail (event != NULL, FALSE);
|
||||
GtkWidget *menu_shell;
|
||||
|
||||
return gtk_widget_event (gtk_widget_get_parent (widget), (GdkEvent *) event);
|
||||
if (event->mode == GDK_CROSSING_GTK_GRAB ||
|
||||
event->mode == GDK_CROSSING_GTK_UNGRAB ||
|
||||
event->mode == GDK_CROSSING_STATE_CHANGED)
|
||||
return GDK_EVENT_STOP;
|
||||
|
||||
if (gdk_event_get_device ((GdkEvent*) event) ==
|
||||
gdk_event_get_source_device ((GdkEvent*) event))
|
||||
return GDK_EVENT_STOP;
|
||||
|
||||
menu_shell = gtk_widget_get_parent (widget);
|
||||
|
||||
if (GTK_IS_MENU_SHELL (menu_shell) && GTK_IS_MENU_ITEM (widget) &&
|
||||
GTK_MENU_SHELL (menu_shell)->priv->active)
|
||||
gtk_menu_shell_select_item (GTK_MENU_SHELL (menu_shell), widget);
|
||||
|
||||
return GDK_EVENT_STOP;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_menu_item_leave (GtkWidget *widget,
|
||||
GdkEventCrossing *event)
|
||||
{
|
||||
g_return_val_if_fail (event != NULL, FALSE);
|
||||
GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
|
||||
GtkWidget *menu_shell = gtk_widget_get_parent (widget);
|
||||
|
||||
return gtk_widget_event (gtk_widget_get_parent (widget), (GdkEvent*) event);
|
||||
if (GTK_IS_MENU_SHELL (menu_shell) && !menu_item->priv->submenu)
|
||||
gtk_menu_shell_deselect (GTK_MENU_SHELL (menu_shell));
|
||||
|
||||
return GDK_EVENT_STOP;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1567,14 +1586,7 @@ gtk_menu_item_popup_timeout (gpointer data)
|
||||
parent = gtk_widget_get_parent (GTK_WIDGET (menu_item));
|
||||
|
||||
if (GTK_IS_MENU_SHELL (parent) && GTK_MENU_SHELL (parent)->priv->active)
|
||||
{
|
||||
gtk_menu_item_real_popup_submenu (GTK_WIDGET (menu_item), info->trigger_event, TRUE);
|
||||
if (info->trigger_event &&
|
||||
info->trigger_event->type != GDK_BUTTON_PRESS &&
|
||||
info->trigger_event->type != GDK_ENTER_NOTIFY &&
|
||||
priv->submenu)
|
||||
GTK_MENU_SHELL (priv->submenu)->priv->ignore_enter = TRUE;
|
||||
}
|
||||
gtk_menu_item_real_popup_submenu (GTK_WIDGET (menu_item), info->trigger_event, TRUE);
|
||||
|
||||
priv->timer = 0;
|
||||
|
||||
|
@ -123,10 +123,6 @@ 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,
|
||||
GdkEventCrossing *event);
|
||||
static void gtk_menu_shell_screen_changed (GtkWidget *widget,
|
||||
GdkScreen *previous_screen);
|
||||
static gboolean gtk_menu_shell_grab_broken (GtkWidget *widget,
|
||||
@ -142,10 +138,6 @@ static void gtk_menu_shell_real_insert (GtkMenuShell *menu_shell,
|
||||
GtkWidget *child,
|
||||
gint position);
|
||||
static void gtk_real_menu_shell_deactivate (GtkMenuShell *menu_shell);
|
||||
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 GType gtk_menu_shell_child_type (GtkContainer *container);
|
||||
static void gtk_menu_shell_real_select_item (GtkMenuShell *menu_shell,
|
||||
GtkWidget *menu_item);
|
||||
@ -191,8 +183,6 @@ gtk_menu_shell_class_init (GtkMenuShellClass *klass)
|
||||
widget_class->button_release_event = gtk_menu_shell_button_release;
|
||||
widget_class->grab_broken_event = gtk_menu_shell_grab_broken;
|
||||
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;
|
||||
widget_class->screen_changed = gtk_menu_shell_screen_changed;
|
||||
|
||||
container_class->add = gtk_menu_shell_add;
|
||||
@ -598,6 +588,22 @@ gtk_menu_shell_activate (GtkMenuShell *menu_shell)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_menu_shell_deactivate_and_emit_done (GtkMenuShell *menu_shell)
|
||||
{
|
||||
gtk_menu_shell_deactivate (menu_shell);
|
||||
g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
|
||||
}
|
||||
|
||||
static GtkMenuShell *
|
||||
gtk_menu_shell_get_toplevel_shell (GtkMenuShell *menu_shell)
|
||||
{
|
||||
while (menu_shell->priv->parent_menu_shell)
|
||||
menu_shell = GTK_MENU_SHELL (menu_shell->priv->parent_menu_shell);
|
||||
|
||||
return menu_shell;
|
||||
}
|
||||
|
||||
static gint
|
||||
gtk_menu_shell_button_press (GtkWidget *widget,
|
||||
GdkEventButton *event)
|
||||
@ -605,24 +611,19 @@ gtk_menu_shell_button_press (GtkWidget *widget,
|
||||
GtkMenuShell *menu_shell;
|
||||
GtkMenuShellPrivate *priv;
|
||||
GtkWidget *menu_item;
|
||||
GtkWidget *parent;
|
||||
|
||||
if (event->type != GDK_BUTTON_PRESS)
|
||||
return FALSE;
|
||||
|
||||
menu_shell = GTK_MENU_SHELL (widget);
|
||||
priv = menu_shell->priv;
|
||||
|
||||
if (priv->parent_menu_shell)
|
||||
return gtk_widget_event (priv->parent_menu_shell, (GdkEvent*) event);
|
||||
menu_shell = GTK_MENU_SHELL (widget);
|
||||
priv = menu_shell->priv;
|
||||
menu_item = gtk_get_event_target_with_type ((GdkEvent *) event, GTK_TYPE_MENU_ITEM);
|
||||
|
||||
menu_item = gtk_menu_shell_get_item (menu_shell, (GdkEvent *)event);
|
||||
|
||||
if (menu_item && _gtk_menu_item_is_selectable (menu_item))
|
||||
if (menu_item &&
|
||||
_gtk_menu_item_is_selectable (menu_item) &&
|
||||
gtk_widget_get_parent (menu_item) == widget)
|
||||
{
|
||||
parent = gtk_widget_get_parent (menu_item);
|
||||
|
||||
if (menu_item != GTK_MENU_SHELL (parent)->priv->active_menu_item)
|
||||
if (menu_item != menu_shell->priv->active_menu_item)
|
||||
{
|
||||
/* select the menu item *before* activating the shell, so submenus
|
||||
* which might be open are closed the friendly way. If we activate
|
||||
@ -631,8 +632,15 @@ gtk_menu_shell_button_press (GtkWidget *widget,
|
||||
* menu item also fixes up the state as if enter_notify() would
|
||||
* have run before (which normally selects the item).
|
||||
*/
|
||||
if (GTK_MENU_SHELL_GET_CLASS (parent)->submenu_placement != GTK_TOP_BOTTOM)
|
||||
gtk_menu_shell_select_item (GTK_MENU_SHELL (parent), menu_item);
|
||||
if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM)
|
||||
gtk_menu_shell_select_item (menu_shell, menu_item);
|
||||
}
|
||||
|
||||
if (GTK_MENU_ITEM (menu_item)->priv->submenu != NULL &&
|
||||
!gtk_widget_get_visible (GTK_MENU_ITEM (menu_item)->priv->submenu))
|
||||
{
|
||||
_gtk_menu_item_popup_submenu (menu_item, FALSE);
|
||||
priv->activated_submenu = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
@ -649,7 +657,6 @@ gtk_menu_shell_button_press (GtkWidget *widget,
|
||||
menu_item != priv->active_menu_item)
|
||||
{
|
||||
gtk_menu_shell_activate (menu_shell);
|
||||
priv->button = event->button;
|
||||
|
||||
if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement == GTK_TOP_BOTTOM)
|
||||
{
|
||||
@ -658,34 +665,13 @@ gtk_menu_shell_button_press (GtkWidget *widget,
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!initially_active)
|
||||
{
|
||||
gtk_menu_shell_deactivate (menu_shell);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
widget = gtk_get_event_widget ((GdkEvent*) event);
|
||||
if (widget == GTK_WIDGET (menu_shell))
|
||||
else if (!initially_active)
|
||||
{
|
||||
gtk_menu_shell_deactivate (menu_shell);
|
||||
g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (menu_item &&
|
||||
_gtk_menu_item_is_selectable (menu_item) &&
|
||||
GTK_MENU_ITEM (menu_item)->priv->submenu != NULL &&
|
||||
!gtk_widget_get_visible (GTK_MENU_ITEM (menu_item)->priv->submenu))
|
||||
{
|
||||
_gtk_menu_item_popup_submenu (menu_item, FALSE);
|
||||
priv->activated_submenu = TRUE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@ -700,8 +686,7 @@ gtk_menu_shell_grab_broken (GtkWidget *widget,
|
||||
{
|
||||
/* Unset the active menu item so gtk_menu_popdown() doesn't see it. */
|
||||
gtk_menu_shell_deselect (menu_shell);
|
||||
gtk_menu_shell_deactivate (menu_shell);
|
||||
g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
|
||||
gtk_menu_shell_deactivate_and_emit_done (menu_shell);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
@ -713,6 +698,18 @@ gtk_menu_shell_button_release (GtkWidget *widget,
|
||||
{
|
||||
GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
|
||||
GtkMenuShellPrivate *priv = menu_shell->priv;
|
||||
GtkMenuShell *parent_shell = GTK_MENU_SHELL (priv->parent_menu_shell);
|
||||
gboolean activated_submenu = FALSE;
|
||||
|
||||
if (parent_shell)
|
||||
{
|
||||
/* If a submenu was just activated, it is its shell which is receiving
|
||||
* the button release event. In this case, we must check the parent
|
||||
* shell to know about the submenu state.
|
||||
*/
|
||||
activated_submenu = parent_shell->priv->activated_submenu;
|
||||
parent_shell->priv->activated_submenu = FALSE;
|
||||
}
|
||||
|
||||
if (priv->parent_menu_shell &&
|
||||
(event->time - GTK_MENU_SHELL (priv->parent_menu_shell)->priv->activate_time) < MENU_SHELL_TIMEOUT)
|
||||
@ -723,101 +720,23 @@ gtk_menu_shell_button_release (GtkWidget *widget,
|
||||
* https://bugzilla.gnome.org/show_bug.cgi?id=703069
|
||||
*/
|
||||
GTK_MENU_SHELL (priv->parent_menu_shell)->priv->activate_time = 0;
|
||||
return TRUE;
|
||||
return GDK_EVENT_STOP;
|
||||
}
|
||||
|
||||
if (priv->active)
|
||||
{
|
||||
GtkWidget *menu_item;
|
||||
gboolean deactivate = TRUE;
|
||||
|
||||
if (priv->button && (event->button != priv->button))
|
||||
{
|
||||
priv->button = 0;
|
||||
if (priv->parent_menu_shell)
|
||||
return gtk_widget_event (priv->parent_menu_shell, (GdkEvent*) event);
|
||||
}
|
||||
gint button = priv->button;
|
||||
|
||||
priv->button = 0;
|
||||
menu_item = gtk_menu_shell_get_item (menu_shell, (GdkEvent*) event);
|
||||
|
||||
if ((event->time - priv->activate_time) > MENU_SHELL_TIMEOUT)
|
||||
if (button && (event->button != button) && priv->parent_menu_shell)
|
||||
{
|
||||
if (menu_item && (priv->active_menu_item == menu_item) &&
|
||||
_gtk_menu_item_is_selectable (menu_item))
|
||||
{
|
||||
GtkWidget *submenu = GTK_MENU_ITEM (menu_item)->priv->submenu;
|
||||
|
||||
if (submenu == NULL)
|
||||
{
|
||||
gtk_menu_shell_activate_item (menu_shell, menu_item, TRUE);
|
||||
deactivate = FALSE;
|
||||
}
|
||||
else if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM ||
|
||||
priv->activated_submenu)
|
||||
{
|
||||
GTimeVal *popup_time;
|
||||
gint64 usec_since_popup = 0;
|
||||
|
||||
popup_time = g_object_get_data (G_OBJECT (submenu),
|
||||
"gtk-menu-exact-popup-time");
|
||||
|
||||
if (popup_time)
|
||||
{
|
||||
GTimeVal current_time;
|
||||
|
||||
g_get_current_time (¤t_time);
|
||||
|
||||
usec_since_popup = ((gint64) current_time.tv_sec * 1000 * 1000 +
|
||||
(gint64) current_time.tv_usec -
|
||||
(gint64) popup_time->tv_sec * 1000 * 1000 -
|
||||
(gint64) popup_time->tv_usec);
|
||||
|
||||
g_object_set_data (G_OBJECT (submenu),
|
||||
"gtk-menu-exact-popup-time", NULL);
|
||||
}
|
||||
|
||||
/* Only close the submenu on click if we opened the
|
||||
* menu explicitly (usec_since_popup == 0) or
|
||||
* enough time has passed since it was opened by
|
||||
* GtkMenuItem's timeout (usec_since_popup > delay).
|
||||
*/
|
||||
if (!priv->activated_submenu &&
|
||||
(usec_since_popup == 0 ||
|
||||
usec_since_popup > MENU_POPDOWN_DELAY * 1000))
|
||||
{
|
||||
_gtk_menu_item_popdown_submenu (menu_item);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_menu_item_select (GTK_MENU_ITEM (menu_item));
|
||||
}
|
||||
|
||||
deactivate = FALSE;
|
||||
}
|
||||
}
|
||||
else if (menu_item &&
|
||||
!_gtk_menu_item_is_selectable (menu_item) &&
|
||||
GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM)
|
||||
{
|
||||
deactivate = FALSE;
|
||||
}
|
||||
else if (priv->parent_menu_shell)
|
||||
{
|
||||
priv->active = TRUE;
|
||||
gtk_widget_event (priv->parent_menu_shell, (GdkEvent*) event);
|
||||
deactivate = FALSE;
|
||||
}
|
||||
|
||||
/* If we ended up on an item with a submenu, leave the menu up. */
|
||||
if (menu_item &&
|
||||
(priv->active_menu_item == menu_item) &&
|
||||
GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM)
|
||||
{
|
||||
deactivate = FALSE;
|
||||
}
|
||||
gtk_menu_shell_deactivate_and_emit_done (gtk_menu_shell_get_toplevel_shell (menu_shell));
|
||||
return GDK_EVENT_STOP;
|
||||
}
|
||||
else /* a very fast press-release */
|
||||
|
||||
if ((event->time - priv->activate_time) <= MENU_SHELL_TIMEOUT)
|
||||
{
|
||||
/* We only ever want to prevent deactivation on the first
|
||||
* press/release. Setting the time to zero is a bit of a
|
||||
@ -827,19 +746,72 @@ gtk_menu_shell_button_release (GtkWidget *widget,
|
||||
* serious harm if we lose.
|
||||
*/
|
||||
priv->activate_time = 0;
|
||||
deactivate = FALSE;
|
||||
return GDK_EVENT_STOP;
|
||||
}
|
||||
|
||||
if (deactivate)
|
||||
menu_item = gtk_get_event_target_with_type ((GdkEvent *) event, GTK_TYPE_MENU_ITEM);
|
||||
|
||||
if (menu_item)
|
||||
{
|
||||
gtk_menu_shell_deactivate (menu_shell);
|
||||
g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
|
||||
GtkWidget *submenu = GTK_MENU_ITEM (menu_item)->priv->submenu;
|
||||
GtkWidget *parent_menu_item_shell = gtk_widget_get_parent (menu_item);
|
||||
|
||||
if (!_gtk_menu_item_is_selectable (GTK_WIDGET (menu_item)))
|
||||
return GDK_EVENT_STOP;
|
||||
|
||||
if (submenu == NULL)
|
||||
{
|
||||
gtk_menu_shell_activate_item (menu_shell, GTK_WIDGET (menu_item), TRUE);
|
||||
return GDK_EVENT_STOP;
|
||||
}
|
||||
else if (GTK_MENU_SHELL (parent_menu_item_shell)->priv->parent_menu_shell &&
|
||||
(activated_submenu ||
|
||||
GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM))
|
||||
{
|
||||
GTimeVal *popup_time;
|
||||
gint64 usec_since_popup = 0;
|
||||
|
||||
popup_time = g_object_get_data (G_OBJECT (menu_shell),
|
||||
"gtk-menu-exact-popup-time");
|
||||
if (popup_time)
|
||||
{
|
||||
GTimeVal current_time;
|
||||
|
||||
g_get_current_time (¤t_time);
|
||||
|
||||
usec_since_popup = ((gint64) current_time.tv_sec * 1000 * 1000 +
|
||||
(gint64) current_time.tv_usec -
|
||||
(gint64) popup_time->tv_sec * 1000 * 1000 -
|
||||
(gint64) popup_time->tv_usec);
|
||||
|
||||
g_object_set_data (G_OBJECT (menu_shell),
|
||||
"gtk-menu-exact-popup-time", NULL);
|
||||
}
|
||||
|
||||
/* Only close the submenu on click if we opened the
|
||||
* menu explicitly (usec_since_popup == 0) or
|
||||
* enough time has passed since it was opened by
|
||||
* GtkMenuItem's timeout (usec_since_popup > delay).
|
||||
*/
|
||||
if (!activated_submenu &&
|
||||
(usec_since_popup == 0 ||
|
||||
usec_since_popup > MENU_POPDOWN_DELAY * 1000))
|
||||
{
|
||||
_gtk_menu_item_popdown_submenu (menu_item);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_menu_item_select (GTK_MENU_ITEM (menu_item));
|
||||
}
|
||||
|
||||
return GDK_EVENT_STOP;
|
||||
}
|
||||
}
|
||||
|
||||
priv->activated_submenu = FALSE;
|
||||
gtk_menu_shell_deactivate_and_emit_done (gtk_menu_shell_get_toplevel_shell (menu_shell));
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
return GDK_EVENT_STOP;
|
||||
}
|
||||
|
||||
void
|
||||
@ -929,126 +901,6 @@ gtk_menu_shell_key_press (GtkWidget *widget,
|
||||
return gtk_menu_shell_activate_mnemonic (menu_shell, event);
|
||||
}
|
||||
|
||||
static gint
|
||||
gtk_menu_shell_enter_notify (GtkWidget *widget,
|
||||
GdkEventCrossing *event)
|
||||
{
|
||||
GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
|
||||
GtkMenuShellPrivate *priv = menu_shell->priv;
|
||||
|
||||
if (event->mode == GDK_CROSSING_GTK_GRAB ||
|
||||
event->mode == GDK_CROSSING_GTK_UNGRAB ||
|
||||
event->mode == GDK_CROSSING_STATE_CHANGED)
|
||||
return TRUE;
|
||||
|
||||
if (priv->active)
|
||||
{
|
||||
GtkWidget *menu_item;
|
||||
GtkWidget *parent;
|
||||
|
||||
menu_item = gtk_get_event_widget ((GdkEvent*) event);
|
||||
|
||||
if (!menu_item)
|
||||
return TRUE;
|
||||
|
||||
if (GTK_IS_MENU_ITEM (menu_item) &&
|
||||
!_gtk_menu_item_is_selectable (menu_item))
|
||||
{
|
||||
priv->in_unselectable_item = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
parent = gtk_widget_get_parent (menu_item);
|
||||
if (parent == widget &&
|
||||
GTK_IS_MENU_ITEM (menu_item))
|
||||
{
|
||||
if (priv->ignore_enter)
|
||||
return TRUE;
|
||||
|
||||
if (event->detail != GDK_NOTIFY_INFERIOR)
|
||||
{
|
||||
if ((gtk_widget_get_state_flags (menu_item) & GTK_STATE_FLAG_PRELIGHT) == 0)
|
||||
gtk_menu_shell_select_item (menu_shell, menu_item);
|
||||
|
||||
/* If any mouse button is down, and there is a submenu
|
||||
* that is not yet visible, activate it. It's sufficient
|
||||
* to check for any button's mask (not only the one
|
||||
* matching menu_shell->button), because there is no
|
||||
* situation a mouse button could be pressed while
|
||||
* entering a menu item where we wouldn't want to show
|
||||
* its submenu.
|
||||
*/
|
||||
if ((event->state & (GDK_BUTTON1_MASK|GDK_BUTTON2_MASK|GDK_BUTTON3_MASK)) &&
|
||||
GTK_MENU_ITEM (menu_item)->priv->submenu != NULL)
|
||||
{
|
||||
GTK_MENU_SHELL (parent)->priv->activated_submenu = TRUE;
|
||||
|
||||
if (!gtk_widget_get_visible (GTK_MENU_ITEM (menu_item)->priv->submenu))
|
||||
{
|
||||
GdkDevice *source_device;
|
||||
|
||||
source_device = gdk_event_get_source_device ((GdkEvent *) event);
|
||||
|
||||
if (gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN)
|
||||
_gtk_menu_item_popup_submenu (menu_item, TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (priv->parent_menu_shell)
|
||||
{
|
||||
gtk_widget_event (priv->parent_menu_shell, (GdkEvent*) event);
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gint
|
||||
gtk_menu_shell_leave_notify (GtkWidget *widget,
|
||||
GdkEventCrossing *event)
|
||||
{
|
||||
if (event->mode == GDK_CROSSING_GTK_GRAB ||
|
||||
event->mode == GDK_CROSSING_GTK_UNGRAB ||
|
||||
event->mode == GDK_CROSSING_STATE_CHANGED)
|
||||
return TRUE;
|
||||
|
||||
if (gtk_widget_get_visible (widget))
|
||||
{
|
||||
GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
|
||||
GtkMenuShellPrivate *priv = menu_shell->priv;
|
||||
GtkWidget *event_widget = gtk_get_event_widget ((GdkEvent*) event);
|
||||
GtkMenuItem *menu_item;
|
||||
|
||||
if (!event_widget || !GTK_IS_MENU_ITEM (event_widget))
|
||||
return TRUE;
|
||||
|
||||
menu_item = GTK_MENU_ITEM (event_widget);
|
||||
|
||||
if (!_gtk_menu_item_is_selectable (event_widget))
|
||||
{
|
||||
priv->in_unselectable_item = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if ((priv->active_menu_item == event_widget) &&
|
||||
(menu_item->priv->submenu == NULL))
|
||||
{
|
||||
if ((event->detail != GDK_NOTIFY_INFERIOR) &&
|
||||
(gtk_widget_get_state_flags (GTK_WIDGET (menu_item)) & GTK_STATE_FLAG_PRELIGHT) != 0)
|
||||
{
|
||||
gtk_menu_shell_deselect (menu_shell);
|
||||
}
|
||||
}
|
||||
else if (priv->parent_menu_shell)
|
||||
{
|
||||
gtk_widget_event (priv->parent_menu_shell, (GdkEvent*) event);
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_menu_shell_screen_changed (GtkWidget *widget,
|
||||
GdkScreen *previous_screen)
|
||||
@ -1144,43 +996,6 @@ gtk_real_menu_shell_deactivate (GtkMenuShell *menu_shell)
|
||||
}
|
||||
}
|
||||
|
||||
static gint
|
||||
gtk_menu_shell_is_item (GtkMenuShell *menu_shell,
|
||||
GtkWidget *child)
|
||||
{
|
||||
GtkWidget *parent;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), FALSE);
|
||||
g_return_val_if_fail (child != NULL, FALSE);
|
||||
|
||||
parent = gtk_widget_get_parent (child);
|
||||
while (GTK_IS_MENU_SHELL (parent))
|
||||
{
|
||||
if (parent == (GtkWidget*) menu_shell)
|
||||
return TRUE;
|
||||
parent = GTK_MENU_SHELL (parent)->priv->parent_menu_shell;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static GtkWidget*
|
||||
gtk_menu_shell_get_item (GtkMenuShell *menu_shell,
|
||||
GdkEvent *event)
|
||||
{
|
||||
GtkWidget *menu_item;
|
||||
|
||||
menu_item = gtk_get_event_widget ((GdkEvent*) event);
|
||||
|
||||
while (menu_item && !GTK_IS_MENU_ITEM (menu_item))
|
||||
menu_item = gtk_widget_get_parent (menu_item);
|
||||
|
||||
if (menu_item && gtk_menu_shell_is_item (menu_shell, menu_item))
|
||||
return menu_item;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Handlers for action signals */
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user