+
+static gint
+get_visible_size (GtkMenu *menu)
+{
+ GtkWidget *widget = GTK_WIDGET (menu);
+ GtkContainer *container = GTK_CONTAINER (menu);
+
+ 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;
+
+ return menu_height;
+}
+
+/* Find the sensitive on-screen child containing @y, or if none,
+ * the nearest selectable onscreen child. (%NULL if none)
+ */
+static GtkWidget *
+child_at (GtkMenu *menu,
+ gint y)
+{
+ GtkMenuShell *menu_shell = GTK_MENU_SHELL (menu);
+ GtkWidget *child = NULL;
+ gint child_offset = 0;
+ GList *children;
+ gint menu_height;
+ gint lower, upper; /* Onscreen bounds */
+
+ menu_height = get_visible_size (menu);
+ lower = menu->scroll_offset;
+ upper = menu->scroll_offset + menu_height;
+
+ for (children = menu_shell->children; children; children = children->next)
+ {
+ if (GTK_WIDGET_VISIBLE (children->data))
+ {
+ GtkRequisition child_requisition;
+
+ gtk_widget_size_request (children->data, &child_requisition);
+
+ if (_gtk_menu_item_is_selectable (children->data) &&
+ child_offset >= lower &&
+ child_offset + child_requisition.height <= upper)
+ {
+ child = children->data;
+
+ if (child_offset + child_requisition.height > y &&
+ !GTK_IS_TEAROFF_MENU_ITEM (child))
+ return child;
+ }
+
+ child_offset += child_requisition.height;
+ }
+ }
+
+ return child;
+}
+
+static void
+gtk_menu_real_move_scroll (GtkMenu *menu,
+ GtkScrollType type)
+{
+ GtkMenuShell *menu_shell = GTK_MENU_SHELL (menu);
+
+ switch (type)
+ {
+ case GTK_SCROLL_PAGE_UP:
+ case GTK_SCROLL_PAGE_DOWN:
+ {
+ gint page_size = get_visible_size (menu);
+ gint old_offset;
+ gint child_offset = 0;
+ gboolean old_upper_arrow_visible;
+ gint step;
+
+ if (type == GTK_SCROLL_PAGE_UP)
+ step = - page_size;
+ else
+ step = page_size;
+
+ if (menu_shell->active_menu_item)
+ {
+ gint child_height;
+
+ compute_child_offset (menu, menu_shell->active_menu_item,
+ &child_offset, &child_height, NULL);
+ child_offset += child_height / 2;
+ }
+
+ menu_shell->ignore_enter = TRUE;
+ old_upper_arrow_visible = menu->upper_arrow_visible && !menu->tearoff_active;
+ old_offset = menu->scroll_offset;
+ gtk_menu_scroll_to (menu, menu->scroll_offset + step);
+
+ if (menu_shell->active_menu_item)
+ {
+ GtkWidget *new_child;
+ gboolean new_upper_arrow_visible = menu->upper_arrow_visible && !menu->tearoff_active;
+
+ 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;
+
+ new_child = child_at (menu, child_offset + step);
+ if (new_child)
+ gtk_menu_shell_select_item (menu_shell, new_child);
+ }
+ }
+ break;
+ case GTK_SCROLL_START:
+ /* Ignore the enter event we might get if the pointer is on the menu
+ */
+ menu_shell->ignore_enter = TRUE;
+ gtk_menu_scroll_to (menu, 0);
+ gtk_menu_shell_select_first (menu_shell, TRUE);
+ break;
+ case GTK_SCROLL_END:
+ /* Ignore the enter event we might get if the pointer is on the menu
+ */
+ menu_shell->ignore_enter = TRUE;
+ gtk_menu_scroll_to (menu, G_MAXINT);
+ _gtk_menu_shell_select_last (menu_shell, TRUE);
+ break;
+ default:
+ break;
+ }
+}