[PATCH] GtkMenu scrolling function added



General Information
===================
This patch enhances the GtkMenu widget with scrolling features,
and fix some position-related bugs.

REQUIREMENT
===========
GTK+ version 1.2.8.

Features
========
- enhance GtkMenu widget with scrolling features
- Automatically scrolling when the UP/DOWN arrow were clicked.
- The position of pop-up menu will be left-aligned, if nicessary.

Installation
============
change directory to gtk+-1.2.8/
patch -p0 < gtk+-1.2.8-menuscroll.patch

Modified files
==============
- gtk/gtkmenu.c
- gtk/gtkmenu.h
- gtk/gtkmenuitem.c
- gtk/gtkoptionmenu.c
- gtk/gtkmenushell.c

--
 Regards,
            Samuel Chen
 Animeta System Inc.,  samuel@animeta.com
diff -c -r gtk/gtkmenu.c ../gtk+-1.2.8.new/gtk/gtkmenu.c
*** gtk/gtkmenu.c	Sat Sep  4 07:50:37 1999
--- ../gtk+-1.2.8.new/gtk/gtkmenu.c	Thu Aug  3 16:59:33 2000
***************
*** 62,76 ****
--- 62,85 ----
  				     GdkEventExpose    *event);
  static gint gtk_menu_key_press	    (GtkWidget	       *widget,
  				     GdkEventKey       *event);
+ static gint gtk_menu_key_release	    (GtkWidget	       *widget,
+ 				     GdkEventKey       *event);
  static gint gtk_menu_motion_notify  (GtkWidget	       *widget,
  				     GdkEventMotion    *event);
  static void gtk_menu_deactivate	    (GtkMenuShell      *menu_shell);
  static void gtk_menu_show_all       (GtkWidget         *widget);
  static void gtk_menu_hide_all       (GtkWidget         *widget);
  static void gtk_menu_position       (GtkMenu           *menu);
+ static void gtk_menu_top_position	(GtkMenu			*menu,
+ 					gint				x,
+ 					gint				y,
+ 					gint				width,
+ 					gint				height) ;
  static void gtk_menu_reparent       (GtkMenu           *menu, 
  				     GtkWidget         *new_parent, 
  				     gboolean           unrealize);
+ static gboolean gtk_menu_timeout_up	(gpointer		data) ;
+ static gboolean	gtk_menu_timeout_down	(gpointer	data) ;
  
  static GtkMenuShellClass *parent_class = NULL;
  static const gchar	 *attach_data_key = "gtk-menu-attach-data";
***************
*** 126,131 ****
--- 135,141 ----
    widget_class->size_allocate = gtk_menu_size_allocate;
    widget_class->expose_event = gtk_menu_expose;
    widget_class->key_press_event = gtk_menu_key_press;
+   widget_class->key_release_event = gtk_menu_key_release;
    widget_class->motion_notify_event = gtk_menu_motion_notify;
    widget_class->show_all = gtk_menu_show_all;
    widget_class->hide_all = gtk_menu_hide_all;
***************
*** 192,197 ****
--- 202,215 ----
    menu->position_func = NULL;
    menu->position_func_data = NULL;
  
+ 	menu->first_scroll_child = -1 ;
+ 	menu->last_scroll_child = -1 ;
+ 	menu->top_scroll_arrow_lit = FALSE ;
+ 	menu->bottom_scroll_arrow_lit = FALSE ;
+ 	menu->scroll_up_source_tag = 0 ;
+ 	menu->scroll_down_source_tag = 0 ;
+ 
+ 
    menu->toplevel = gtk_window_new (GTK_WINDOW_POPUP);
    gtk_signal_connect (GTK_OBJECT (menu->toplevel),
  		      "event",
***************
*** 223,228 ****
--- 241,251 ----
    g_return_if_fail (GTK_IS_MENU (object));
  
    menu = GTK_MENU (object);
+ 
+ 	if (menu->scroll_up_source_tag)
+ 		g_source_remove (menu->scroll_up_source_tag) ;
+ 	if (menu->scroll_down_source_tag)
+ 		g_source_remove (menu->scroll_down_source_tag) ;
    
    gtk_object_ref (object);
    
***************
*** 418,423 ****
--- 441,449 ----
    
    widget = GTK_WIDGET (menu);
    menu_shell = GTK_MENU_SHELL (menu);
+ 
+ 	menu->top_scroll_arrow_lit = FALSE ;
+ 	menu->bottom_scroll_arrow_lit = FALSE ;
    
    menu_shell->parent_menu_shell = parent_menu_shell;
    menu_shell->active = TRUE;
***************
*** 534,539 ****
--- 560,567 ----
      }
  
    gtk_menu_shell_deselect (menu_shell);
+ 
+ 	gtk_menu_set_scrolling (menu, FALSE, FALSE) ;
    
    /* The X Grab, if present, will automatically be removed when we hide
     * the window */
***************
*** 892,911 ****
    menu_shell = GTK_MENU_SHELL (widget);
    
    widget->allocation = *allocation;
    if (GTK_WIDGET_REALIZED (widget))
      gdk_window_move_resize (widget->window,
  			    allocation->x, allocation->y,
! 			    allocation->width, allocation->height);
! 
  
    if (menu_shell->children)
      {
!       child_allocation.x = (GTK_CONTAINER (menu)->border_width +
! 			    widget->style->klass->xthickness);
!       child_allocation.y = (GTK_CONTAINER (menu)->border_width +
! 			    widget->style->klass->ythickness);
!       child_allocation.width = MAX (1, (gint)allocation->width - child_allocation.x * 2);
!       
        children = menu_shell->children;
        while (children)
  	{
--- 920,947 ----
    menu_shell = GTK_MENU_SHELL (widget);
    
    widget->allocation = *allocation;
+ 
    if (GTK_WIDGET_REALIZED (widget))
      gdk_window_move_resize (widget->window,
  			    allocation->x, allocation->y,
! 			    allocation->width, allocation->height) ;
  
    if (menu_shell->children)
      {
! 		guint child_num ;
! 
! 		child_allocation.x = GTK_CONTAINER (menu)->border_width +
! 							GTK_WIDGET (menu)->style->klass->xthickness ;
! 		child_allocation.y = GTK_CONTAINER (menu)->border_width +
! 							GTK_WIDGET (menu)->style->klass->ythickness ;
! 		child_allocation.width = MAX (1, 
! 				(gint)allocation->width - child_allocation.x * 2);
! 
! 		if (menu->first_scroll_child != -1)
! 			child_allocation.y = MENU_SCROLL_ARROW_HEIGHT ;
! 
! 		child_num = 0 ;
! 
        children = menu_shell->children;
        while (children)
  	{
***************
*** 917,929 ****
  	      GtkRequisition child_requisition;
  	      gtk_widget_get_child_requisition (child, &child_requisition);
  	      
! 	      child_allocation.height = child_requisition.height;
! 	      
! 	      gtk_widget_size_allocate (child, &child_allocation);
! 	      gtk_widget_queue_draw (child);
! 	      
! 	      child_allocation.y += child_allocation.height;
  	    }
  	}
      }
  }
--- 953,984 ----
  	      GtkRequisition child_requisition;
  	      gtk_widget_get_child_requisition (child, &child_requisition);
  	      
! 			if ((child_num < menu->first_scroll_child
! 				&& menu->first_scroll_child != -1)
! 				|| (child_num > menu->last_scroll_child
! 					&& menu->last_scroll_child != -1))
! 			{
! 				GtkAllocation outside_allocation ;
! 
! 				outside_allocation.x = -child_requisition.width ;
! 				outside_allocation.y = -child_requisition.height ;
! 				outside_allocation.width = child_requisition.width ;
! 				outside_allocation.height = child_requisition.height ;
! 
! 				gtk_widget_size_allocate (child, &outside_allocation) ;
! 			}
! 			else
! 			{
! 				child_allocation.height = child_requisition.height ;
! 
! 				gtk_widget_size_allocate (child, &child_allocation) ;
! 				gtk_widget_queue_draw (child) ;
! 
! 				child_allocation.y += child_allocation.height ;	
! 			}
  	    }
+ 
+ 		child_num ++ ;
  	}
      }
  }
***************
*** 936,947 ****
--- 991,1041 ----
    
    if (GTK_WIDGET_DRAWABLE (widget))
      {
+ 		guint border ;
+ 		guint width, height ;
+ 
+ 		border = GTK_CONTAINER (widget)->border_width +
+ 					widget->style->klass->ythickness + 1 ;
+ 		gdk_window_get_size (widget->window, &width, &height) ;
+ 
        gtk_paint_box (widget->style,
  		     widget->window,
  		     GTK_STATE_NORMAL,
  		     GTK_SHADOW_OUT,
  		     NULL, widget, "menu",
  		     0, 0, -1, -1);
+ 
+ 		if (GTK_MENU (widget)->first_scroll_child != -1)
+ 		{
+ 			gtk_paint_arrow (widget->style,
+ 					widget->window,
+ 					GTK_MENU (widget)->top_scroll_arrow_lit ?
+ 						GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
+ 					GTK_SHADOW_OUT,
+ 					NULL, widget, "menu",
+ 					GTK_ARROW_UP,
+ 					TRUE,
+ 					width / 2 - MENU_SCROLL_ARROW_HEIGHT / 2 + 1,
+ 					border-1,
+ 					MENU_SCROLL_ARROW_HEIGHT - 2,
+ 					MENU_SCROLL_ARROW_HEIGHT - 2) ;
+ 		}
+ 
+ 		if (GTK_MENU(widget)->last_scroll_child != -1)
+ 		{
+ 			gtk_paint_arrow (widget->style,
+ 					widget->window,
+ 					GTK_MENU (widget)->bottom_scroll_arrow_lit ?
+ 						GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
+ 					GTK_SHADOW_OUT,
+ 					NULL, widget, "menu",
+ 					GTK_ARROW_DOWN,
+ 					TRUE,
+ 					width / 2 - MENU_SCROLL_ARROW_HEIGHT / 2 + 1,
+ 					height - border - MENU_SCROLL_ARROW_HEIGHT +1 ,
+ 					MENU_SCROLL_ARROW_HEIGHT - 2,
+ 					MENU_SCROLL_ARROW_HEIGHT - 2) ;
+ 		}
      }
  }
  
***************
*** 1015,1020 ****
--- 1109,1130 ----
  }
  
  static gint
+ gtk_menu_key_release (GtkWidget *widget,
+ 			GdkEventKey *event)
+ {
+ 	GtkMenuShell *menu_shell;
+ 
+ 	g_return_val_if_fail (widget != NULL, FALSE);
+ 	g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
+ 	g_return_val_if_fail (event != NULL, FALSE);
+       
+ 	menu_shell = GTK_MENU_SHELL (widget);
+ 	menu_shell->ignore_enter = FALSE ;
+ 
+ 	return TRUE;
+ }
+ 
+ static gint
  gtk_menu_key_press (GtkWidget	*widget,
  		    GdkEventKey *event)
  {
***************
*** 1026,1031 ****
--- 1136,1142 ----
    g_return_val_if_fail (event != NULL, FALSE);
        
    menu_shell = GTK_MENU_SHELL (widget);
+   menu_shell->ignore_enter = TRUE ;
  
    if (GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
      return TRUE;
***************
*** 1104,1121 ****
  gtk_menu_motion_notify  (GtkWidget	   *widget,
  			 GdkEventMotion    *event)
  {
    g_return_val_if_fail (widget != NULL, FALSE);
    g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
    
    if (GTK_MENU_SHELL (widget)->ignore_enter)
      GTK_MENU_SHELL (widget)->ignore_enter = FALSE;
    else 
      {
!       gint width, height;
! 
!       gdk_window_get_size (event->window, &width, &height);
!       if (event->x >= 0 && event->x < width &&
! 	  event->y >= 0 && event->y < height)
  	{
  	  GdkEvent send_event;
  	  
--- 1215,1245 ----
  gtk_menu_motion_notify  (GtkWidget	   *widget,
  			 GdkEventMotion    *event)
  {
+ 	gboolean in_window ;
+ 	guint border ;
+ 	gint x, y, width, height ;
+ 	gboolean old_top_lit, old_bottom_lit ;
+ 	gboolean scroll_up, scroll_down ;
+ 
    g_return_val_if_fail (widget != NULL, FALSE);
    g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
+ 
+ 	gdk_window_get_pointer (widget->window, &x, &y, NULL);
+ 	gdk_window_get_size (widget->window, &width, &height) ;
+ 
+ 	in_window = FALSE ;
+ 	if (x >= 0 && x < width &&
+ 		y >= 0 && y < height)
+ 		in_window = TRUE ;
+ 
+ 	border = GTK_CONTAINER (widget)->border_width +
+ 			widget->style->klass->ythickness ;
    
    if (GTK_MENU_SHELL (widget)->ignore_enter)
      GTK_MENU_SHELL (widget)->ignore_enter = FALSE;
    else 
      {
! 	if (in_window)
  	{
  	  GdkEvent send_event;
  	  
***************
*** 1128,1133 ****
--- 1252,1293 ----
  	}
      }
  
+ 	old_top_lit = GTK_MENU (widget)->top_scroll_arrow_lit ;
+ 	old_bottom_lit = GTK_MENU (widget)->bottom_scroll_arrow_lit ;
+ 	GTK_MENU (widget)->top_scroll_arrow_lit = FALSE ;
+ 	GTK_MENU (widget)->bottom_scroll_arrow_lit = FALSE ;
+ 
+ 	scroll_up = FALSE ;
+ 	scroll_down = FALSE ;
+ 
+ 	if (y < border + MENU_SCROLL_ARROW_HEIGHT && y >= 0
+ 		&& x >= 0 && x < width
+ 		&& GTK_MENU (widget)->first_scroll_child != -1)
+ 	{
+ 		if (in_window)
+ 			GTK_MENU (widget)->top_scroll_arrow_lit = TRUE ;
+ 
+ 		if (GTK_MENU_SHELL(widget)->button)
+ 			scroll_up = TRUE ;
+ 	}
+ 
+ 	if (y >= height - border - MENU_SCROLL_ARROW_HEIGHT && y < height
+ 		&& x >= 0 && x < width
+ 		&& GTK_MENU (widget)->last_scroll_child != -1)
+ 	{
+ 		if (in_window)
+ 			GTK_MENU (widget)->bottom_scroll_arrow_lit = TRUE ;
+ 
+ 		if (GTK_MENU_SHELL(widget)->button)
+ 			scroll_down = TRUE ;
+ 	}
+ 
+ 	gtk_menu_set_scrolling (GTK_MENU (widget), scroll_up, scroll_down) ;
+ 
+ 	if (GTK_MENU (widget)->top_scroll_arrow_lit != old_top_lit
+ 		|| GTK_MENU (widget)->bottom_scroll_arrow_lit != old_bottom_lit)
+ 		gtk_menu_paint (widget) ;
+ 
    return FALSE;
  }
  
***************
*** 1171,1177 ****
--- 1331,1339 ----
    gtk_widget_size_request (widget, &requisition);
        
    if (menu->position_func)
+   {
      (* menu->position_func) (menu, &x, &y, menu->position_func_data);
+   }
    else
      {
        gint screen_width;
***************
*** 1184,1198 ****
        y = CLAMP (y - 2, 0, MAX (0, screen_height - requisition.height));
      }
  
!   /* FIXME: The MAX() here is because gtk_widget_set_uposition
!    * is broken. Once we provide an alternate interface that
!    * allows negative values, then we can remove them.
!    */
!   gtk_widget_set_uposition (GTK_MENU_SHELL (menu)->active ?
! 			        menu->toplevel : menu->tearoff_window, 
! 			    MAX (x, 0), MAX (y, 0));
  }
  
  /* Reparent the menu, taking care of the refcounting
   */
  static void 
--- 1346,1471 ----
        y = CLAMP (y - 2, 0, MAX (0, screen_height - requisition.height));
      }
  
! 	gtk_menu_top_position (menu, x, y, requisition.width, requisition.height) ;
  }
  
+ /* Set the actual position of the toplevel window.  Also, if part of the
+  * toplevel window would fall offscreen, truncate the window and note that
+  * we will use scroll arrows.
+  */
+ static void
+ gtk_menu_top_position (GtkMenu *menu,
+               gint  x,
+               gint  y,
+               gint  width,
+               gint  height)
+ {
+   gint border_height;
+ 
+   border_height = GTK_CONTAINER (menu)->border_width +
+                   GTK_WIDGET (menu)->style->klass->ythickness;
+ 
+ 	if (GTK_MENU_SHELL (menu)->active)
+ 	{
+ 		GList *child_list;
+ 		gint screen_height; 
+ 		gint on_line;
+ 		gint last_line ;
+ 		guint offset = -1 ;
+ 
+ 		child_list = GTK_MENU_SHELL (menu)->children;
+ 		screen_height = gdk_screen_height ();
+ 
+ 		on_line = y + border_height * 2 ;
+ 
+ 		if (y < 0)
+ 		{
+ 			menu->first_scroll_child = 0;
+ 			while (child_list && on_line < MENU_SCROLL_ARROW_HEIGHT)
+ 			{
+ 				GtkWidget *child;
+ 
+ 				child = GTK_WIDGET (child_list->data);
+ 				if (GTK_WIDGET_VISIBLE (child))
+ 				{                    
+ 					height -= child->requisition.height;
+ 					on_line += child->requisition.height;
+ 					y += child->requisition.height;
+ 				}
+ 				offset ++ ;
+ 				child_list = child_list->next;
+ 				menu->first_scroll_child++;
+ 			}
+ 			y -= MENU_SCROLL_ARROW_HEIGHT;
+ 			height += MENU_SCROLL_ARROW_HEIGHT;
+ 		}
+ 		else
+ 			menu->first_scroll_child = -1;
+ 
+ 		if (y + height >= screen_height)
+ 		{
+ 			if (offset < 0)
+ 				menu->last_scroll_child = menu->first_scroll_child ;
+ 			else
+ 				menu->last_scroll_child = offset ;
+ 
+ 			last_line = on_line ;
+ 
+ 			while (child_list)
+ 			{
+ 				GtkWidget *child;
+ 
+ 				child = GTK_WIDGET (child_list->data);
+ 
+ 				if (GTK_WIDGET_VISIBLE (child))
+ 				{
+ 					if (on_line + child->requisition.height +
+ 							MENU_SCROLL_ARROW_HEIGHT > screen_height
+ 						&& child_list->next)
+ 					{
+ 						if (last_line == on_line)
+ 						{
+ 							menu->last_scroll_child = 0 ;
+ 							on_line = screen_height - MENU_SCROLL_ARROW_HEIGHT ;
+ 							y = screen_height - (child->requisition.height +
+ 								MENU_SCROLL_ARROW_HEIGHT + border_height * 2) ;
+ 						}
+ 						break ;
+ 					}
+ 
+ 					if (on_line + child->requisition.height > screen_height)
+ 						break ;
+ 
+ 					last_line = on_line ;
+ 					on_line += child->requisition.height;
+ 				}
+ 				offset ++ ;
+ 				child_list = child_list->next;
+ 				menu->last_scroll_child++;
+ 
+ 				if (on_line > screen_height)
+ 					break ;
+ 			}
+ 
+ 			if (child_list && child_list->next)
+ 				on_line += MENU_SCROLL_ARROW_HEIGHT ;
+ 			if(menu->last_scroll_child < 0)
+ 				menu->last_scroll_child = menu->first_scroll_child;
+ 			height = on_line - y ;
+ 
+ 		}
+ 		else
+ 			menu->last_scroll_child = -1;
+ 
+ 		gtk_widget_set_uposition (menu->toplevel, x, y);
+ 		gtk_widget_set_usize (menu->toplevel, width, height);
+ 	}
+ 	else
+ 	{
+ 		gtk_widget_set_uposition (menu->tearoff_window, x, y);
+ 	}
+ }       
+ 
  /* Reparent the menu, taking care of the refcounting
   */
  static void 
***************
*** 1234,1239 ****
--- 1507,1884 ----
    gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_show_all, NULL);
  }
  
+ /*  Set the scrolling state of the menu.  It is harmless to call this
+  *  if there is no change in scrolling state.  (That is, it won't interrupt
+  *  or reset the scrolling timers if the scrolling settings are the same).
+  */
+ void
+ gtk_menu_set_scrolling (GtkMenu    *menu,
+            gboolean     scroll_up,
+            gboolean     scroll_down)
+ {
+   if (!scroll_up && menu->scroll_up_source_tag)
+     {
+       g_source_remove (menu->scroll_up_source_tag);
+       menu->scroll_up_source_tag = 0;
+     }
+ 
+   if (!scroll_down && menu->scroll_down_source_tag)
+     {
+       g_source_remove (menu->scroll_down_source_tag);
+       menu->scroll_down_source_tag = 0;
+     }
+ 
+   if(scroll_up && !menu->scroll_up_source_tag)
+     {                                       
+       if (gtk_menu_scroll_up (menu, 1))
+    menu->scroll_up_source_tag = g_timeout_add (MENU_SCROLL_TIMEOUT, gtk_menu_timeout_up, menu);
+     }
+ 
+   if(scroll_down && !menu->scroll_down_source_tag)
+     {
+       if (gtk_menu_scroll_down (menu, 1))
+    menu->scroll_down_source_tag = g_timeout_add (MENU_SCROLL_TIMEOUT, gtk_menu_timeout_down, menu);
+     }
+ } 
+ 
+ gboolean
+ gtk_menu_has_scroll (GtkMenu *menu)
+ {
+ 	gint x, y, width, height ;
+ 	gint screen_height ;
+ 	gint on_line ;
+ 	gint on_child ;
+ 	gint border ;
+ 	GList *children_copy;
+ 	GList *child;
+ 	GtkRequisition requisition;
+ 
+ 	if (menu->first_scroll_child != -1 
+ 		|| menu->last_scroll_child != -1)
+ 		return TRUE ;
+ 
+ 
+ 	// check scroll up
+ 	gdk_window_get_geometry (menu->toplevel->window,
+ 			&x, &y,
+ 			&width, &height,
+ 			NULL);
+ 	screen_height = gdk_screen_height ();  
+ 
+ 	border = GTK_CONTAINER (menu)->border_width +
+ 			GTK_WIDGET (menu)->style->klass->ythickness;
+ 
+ 	on_line = y + border * 2 ;
+ 	on_child = 0;
+ 	child = GTK_MENU_SHELL (menu)->children;
+ 	if (menu->first_scroll_child != -1)
+ 	{
+ 		on_child = menu->first_scroll_child ;
+ 		child = g_list_nth (child, menu->first_scroll_child) ;
+ 	}
+ 	while (child && on_line < screen_height)
+ 	{
+ 		GtkWidget *widget;     
+ 		widget = GTK_WIDGET (child->data);
+ 		if (GTK_WIDGET_VISIBLE (widget))
+ 		{
+   			gtk_widget_size_request (widget, &requisition);
+ 
+ 			if (on_line + requisition.height + 
+ 					MENU_SCROLL_ARROW_HEIGHT >= screen_height
+ 				&& child->next)
+ 				break ;
+ 
+ 			on_line += requisition.height ;
+ 		}
+ 
+ 		on_child ++ ;
+ 		child = child->next;
+ 	}
+ 
+ 	if (child)
+ 		return TRUE ;
+ 
+ 	// check scroll down
+ 	on_line = screen_height - border * 2 ;
+ 	children_copy = g_list_copy (GTK_MENU_SHELL (menu)->children);
+ 	children_copy = g_list_reverse(children_copy);  
+ 	on_child = g_list_length (GTK_MENU_SHELL (menu)->children) -1;
+ 	child = children_copy ;
+ 	if (menu->last_scroll_child != -1)
+ 	{
+ 		on_child = menu->last_scroll_child ;
+ 		child = g_list_nth (child, 
+ 			g_list_length (children_copy)-1 - on_child) ;
+ 	}
+ 	while (child && on_line >= y)
+ 	{
+ 		GtkWidget *widget;
+ 
+ 		widget = GTK_WIDGET (child->data);
+ 		if ((on_child <= menu->last_scroll_child
+ 			|| menu->last_scroll_child == -1)
+ 			&& GTK_WIDGET_VISIBLE (widget))
+ 		{
+   			gtk_widget_size_request (widget, &requisition);
+ 
+ 			if (on_line + requisition.height +
+ 					MENU_SCROLL_ARROW_HEIGHT < y
+ 				&& child->next)
+ 				break ;
+ 
+ 			on_line += requisition.height ;
+ 		}
+ 
+ 		on_child--;
+ 		child = child->next;
+     }
+ 
+ 	g_list_free(children_copy);
+ 
+ 	if (on_child >= 0)
+ 		return TRUE ;
+ 
+ 	return FALSE ;
+ }
+ 
+ gboolean
+ gtk_menu_scroll_up (GtkMenu    *menu,
+            guint        scroll_count)
+ {
+   gboolean return_value;
+   gint x, y, width, height;
+   gint screen_height;
+   gint on_line;
+   gint last_line;
+   gint on_child;
+   gint border;
+   GList *child;
+   GtkAllocation allocation;
+ 
+   return_value = TRUE;
+ 
+   gdk_window_get_geometry (menu->toplevel->window,
+               &x, &y,
+               &width, &height,
+               NULL);
+   screen_height = gdk_screen_height ();  
+ 
+   border = GTK_CONTAINER (menu)->border_width +
+            GTK_WIDGET (menu)->style->klass->ythickness;
+ 
+   menu->first_scroll_child -= scroll_count ;
+   if (menu->first_scroll_child <= 0) {
+     menu->first_scroll_child = -1;
+     return_value = FALSE;
+   }
+ 
+   on_line = y + border * 2 ;
+   last_line = on_line;
+   if (menu->first_scroll_child != -1)
+     on_line += MENU_SCROLL_ARROW_HEIGHT;
+ 
+   on_child = 0;
+   child = GTK_MENU_SHELL (menu)->children;
+   if (menu->first_scroll_child != -1)
+   {
+     on_child = menu->first_scroll_child ;
+ 	child = g_list_nth (child, menu->first_scroll_child) ;
+   }
+ 
+   while (child && on_line <= screen_height)
+     {
+       GtkWidget *widget;     
+       widget = GTK_WIDGET (child->data);
+ 
+       if (GTK_WIDGET_VISIBLE (widget))
+    {
+ 		if (on_line + widget->allocation.height + 
+ 				MENU_SCROLL_ARROW_HEIGHT > screen_height
+ 			&& child->next)
+ 			break ;
+ 
+ 		if (on_line + widget->allocation.height > screen_height)
+ 			break ;
+ 
+      last_line = on_line;
+      on_line += widget->allocation.height;
+    }
+ 
+       on_child++;
+       child = child->next;
+     }
+ 
+   if (child)
+     {
+       menu->last_scroll_child = on_child - 1;
+       if (menu->last_scroll_child < menu->first_scroll_child
+ 		&& menu->first_scroll_child != -1)
+    menu->first_scroll_child = menu->last_scroll_child;  
+       on_line += MENU_SCROLL_ARROW_HEIGHT ;
+     }
+   else
+     {
+       menu->last_scroll_child = -1 ;
+     }
+ 
+   gdk_window_move_resize (menu->toplevel->window,
+              x, y, width, on_line - y);
+ 
+   allocation.x = 0;
+   allocation.y = 0;
+   allocation.width = width;
+   allocation.height = on_line - y;
+   gtk_widget_size_allocate (GTK_WIDGET (menu), &allocation);
+ 
+   return return_value;
+ }
+ 
+ gboolean
+ gtk_menu_scroll_down (GtkMenu  *menu,
+              guint      scroll_count)
+ {
+   gboolean return_value;
+   gint x, y, width, height;
+   gint screen_height;
+   gint on_line;
+   gint last_line;
+   gint new_height;
+   gint on_child;
+   gint border;
+   gint child_height = 0 ;
+   GList *children_copy;
+   GList *child;
+   GtkAllocation allocation;
+ 
+   return_value = TRUE;
+ 
+   gdk_window_get_geometry (menu->toplevel->window,
+               &x, &y,
+               &width, &height,              
+               NULL);
+   screen_height = gdk_screen_height ();
+ 
+   border = GTK_CONTAINER (menu)->border_width +
+            GTK_WIDGET (menu)->style->klass->ythickness;
+ 
+   menu->last_scroll_child += scroll_count ;
+   if (menu->last_scroll_child >=
+         g_list_length (GTK_MENU_SHELL (menu)->children)-1) {
+     menu->last_scroll_child = -1;
+     return_value = FALSE;
+   }
+ 
+   on_line = screen_height - border * 2 ;
+   if (menu->last_scroll_child != -1)
+     on_line -= MENU_SCROLL_ARROW_HEIGHT ;
+   last_line = on_line;
+ 
+   children_copy = g_list_copy (GTK_MENU_SHELL (menu)->children);
+   children_copy = g_list_reverse(children_copy);  
+   on_child = g_list_length (GTK_MENU_SHELL (menu)->children) -1;
+   child = children_copy ;
+   if (menu->last_scroll_child != -1)
+   {
+     on_child = menu->last_scroll_child ;
+ 	child = g_list_nth (child, 
+ 		g_list_length (children_copy)-1 - on_child) ;
+   }
+ 
+   while (child && on_line >= y)
+     {
+       GtkWidget *widget;
+       widget = GTK_WIDGET (child->data);
+ 
+       if (GTK_WIDGET_VISIBLE (widget))
+    {
+         child_height = widget->allocation.height ;
+ 
+ 		if (on_line - widget->allocation.height - 
+ 				MENU_SCROLL_ARROW_HEIGHT < y
+ 			&& child->next)
+ 		{
+ 			if (last_line == on_line)
+ 			{
+ 				y = on_line - MENU_SCROLL_ARROW_HEIGHT
+ 					- widget->allocation.height ;
+ 				on_line -= widget->allocation.height ;
+ 			}
+ 			break ;
+ 		}
+ 
+ 		if (on_line + widget->allocation.height < y)
+ 			break ;
+ 
+      last_line = on_line;
+      on_line -= widget->allocation.height;
+    }
+ 
+       on_child--;
+       child = child->next;
+     }
+ 
+   if (child)
+     {
+       menu->first_scroll_child = on_child + 1;
+ 
+       if (menu->first_scroll_child > menu->last_scroll_child
+ 		&& menu->last_scroll_child != -1)
+    menu->first_scroll_child = menu->last_scroll_child;
+       on_line -= MENU_SCROLL_ARROW_HEIGHT ;
+     }
+   else
+     {
+       menu->first_scroll_child = -1;
+     }
+ 
+   new_height = screen_height - on_line;
+ 
+   gdk_window_move_resize (menu->toplevel->window,
+              x, y, width, new_height);
+ 
+   allocation.x = 0;       
+   allocation.y = 0;
+   allocation.width = width;
+   allocation.height = new_height;
+   gtk_widget_size_allocate (GTK_WIDGET (menu), &allocation);
+ 
+   g_list_free(children_copy);
+ 
+   return return_value;
+ }                        
+ 
+ static gboolean
+ gtk_menu_timeout_up (gpointer  data)
+ {
+   GtkMenu *menu;
+ 
+   menu = GTK_MENU (data);
+ 
+   if (gtk_menu_scroll_up (menu, 1))
+     return TRUE;
+   else
+     {
+       menu->scroll_up_source_tag = 0;
+       return FALSE;
+     }
+ }
+ 
+ static gboolean
+ gtk_menu_timeout_down (gpointer    data)
+ {
+   GtkMenu *menu;
+ 
+   menu = GTK_MENU (data);
+ 
+   if (gtk_menu_scroll_down (menu, 1))
+     return TRUE;
+   else
+     {
+       menu->scroll_down_source_tag = 0;
+       return FALSE;
+     }
+ }    
  
  static void
  gtk_menu_hide_all (GtkWidget *widget)
diff -c -r gtk/gtkmenu.h ../gtk+-1.2.8.new/gtk/gtkmenu.h
*** gtk/gtkmenu.h	Wed Mar  1 13:00:35 2000
--- ../gtk+-1.2.8.new/gtk/gtkmenu.h	Thu Aug  3 16:54:26 2000
***************
*** 37,42 ****
--- 37,44 ----
  extern "C" {
  #endif /* __cplusplus */
  
+ #define MENU_SCROLL_ARROW_HEIGHT			16
+ #define MENU_SCROLL_TIMEOUT					200
  
  #define GTK_TYPE_MENU			(gtk_menu_get_type ())
  #define GTK_MENU(obj)			(GTK_CHECK_CAST ((obj), GTK_TYPE_MENU, GtkMenu))
***************
*** 74,79 ****
--- 76,91 ----
    GtkWidget *tearoff_window;
  
    guint torn_off : 1;
+ 
+   /* Internal information used for the scroll arrow handling */
+   gint first_scroll_child;
+   gint last_scroll_child;
+ 
+   gboolean top_scroll_arrow_lit;
+   gboolean bottom_scroll_arrow_lit;
+ 
+   guint scroll_up_source_tag;
+   guint scroll_down_source_tag;   
  };
  
  struct _GtkMenuClass
***************
*** 158,163 ****
--- 170,182 ----
  void       gtk_menu_reorder_child         (GtkMenu             *menu,
                                             GtkWidget           *child,
                                             gint                position);
+ 
+ void     gtk_menu_set_scrolling (GtkMenu *menu, 
+ 								gboolean scroll_up,
+ 								gboolean scroll_down) ;
+ gboolean gtk_menu_has_scroll (GtkMenu *menu) ;
+ gboolean gtk_menu_scroll_up (GtkMenu *menu, guint scroll_count) ;
+ gboolean gtk_menu_scroll_down (GtkMenu *menu, guint scroll_count) ;
  
  #ifdef __cplusplus
  }
diff -c -r gtk/gtkmenuitem.c ../gtk+-1.2.8.new/gtk/gtkmenuitem.c
*** gtk/gtkmenuitem.c	Sat Sep  4 07:50:37 1999
--- ../gtk+-1.2.8.new/gtk/gtkmenuitem.c	Thu Aug  3 12:31:42 2000
***************
*** 699,704 ****
--- 699,712 ----
        return;
      }
  
+ 	if (gtk_menu_has_scroll(menu)) {  // check scroll
+ 		if (tx + GTK_WIDGET(menu_item)->allocation.width 
+ 				+ GTK_WIDGET(menu)->requisition.width < screen_width)
+ 			tx += GTK_WIDGET (menu_item)->allocation.width ;
+ 		else
+ 			tx -= GTK_WIDGET(menu)->requisition.width ;
+ 	}
+ 
    switch (menu_item->submenu_placement)
      {
      case GTK_TOP_BOTTOM:
diff -c -r gtk/gtkmenushell.c ../gtk+-1.2.8.new/gtk/gtkmenushell.c
*** gtk/gtkmenushell.c	Sat Feb  5 02:29:29 2000
--- ../gtk+-1.2.8.new/gtk/gtkmenushell.c	Thu Aug  3 15:00:04 2000
***************
*** 32,42 ****
  #include "gtkmenushell.h"
  #include "gtksignal.h"
  
  
  #define MENU_SHELL_TIMEOUT   500
  #define MENU_SHELL_CLASS(w)  GTK_MENU_SHELL_CLASS (GTK_OBJECT (w)->klass)
  
- 
  enum {
    DEACTIVATE,
    SELECTION_DONE,
--- 32,43 ----
  #include "gtkmenushell.h"
  #include "gtksignal.h"
  
+ #include "gtkmenu.h"
+ 
  
  #define MENU_SHELL_TIMEOUT   500
  #define MENU_SHELL_CLASS(w)  GTK_MENU_SHELL_CLASS (GTK_OBJECT (w)->klass)
  
  enum {
    DEACTIVATE,
    SELECTION_DONE,
***************
*** 444,449 ****
--- 445,491 ----
  	}
      }
  
+ 	if (GTK_IS_MENU(widget))
+ 	{
+ 		// check scroll arrow lit
+ 		gboolean in_window ;
+ 		guint border ;
+ 		gint x, y, width, height ;
+ 		gboolean scroll_up, scroll_down ;
+ 
+ 		GtkMenu *menu = GTK_MENU (widget) ;
+ 
+ 		gdk_window_get_pointer (widget->window, &x, &y, NULL) ;
+ 		gdk_window_get_size (widget->window, &width, &height) ;
+ 
+ 		in_window = FALSE ;
+ 		if (x >= 0 && x < width &&
+ 			y >= 0 && y < height)
+ 			in_window = TRUE ;
+ 		border = GTK_CONTAINER (widget)->border_width +
+ 				widget->style->klass->ythickness ;
+ 
+ 		scroll_up = FALSE ;
+ 		scroll_down = FALSE ;
+ 
+ 		if (y < border + MENU_SCROLL_ARROW_HEIGHT && y >= 0
+ 			&& x >= 0 && x < width
+ 			&& menu->first_scroll_child != -1)
+ 		{
+ 			scroll_up = TRUE ;
+ 		}
+ 
+ 		if (y >= height - border - MENU_SCROLL_ARROW_HEIGHT && y < height
+ 			&& x >= 0 && x < width
+ 			&& menu->last_scroll_child != -1)
+ 		{
+ 			scroll_down = TRUE ;
+ 		}
+ 
+ 		gtk_menu_set_scrolling (GTK_MENU (widget), scroll_up, scroll_down) ;
+ 	}
+ 
+ 
    return TRUE;
  }
  
***************
*** 475,480 ****
--- 517,546 ----
  
        deactivate = TRUE;
  
+ 	if (GTK_IS_MENU(widget))
+ 	{	// check arrow scroll
+ 		gint width, height, x, y ;
+ 		gint border ;
+ 		GtkMenu *menu = GTK_MENU(widget) ;
+ 
+ 		border = GTK_CONTAINER (widget)->border_width +
+ 			widget->style->klass->ythickness ;
+ 
+ 		gdk_window_get_pointer (widget->window, &x, &y, NULL) ;
+ 		gdk_window_get_size (widget->window, &width, &height) ;
+ 
+ 		if (menu->first_scroll_child != -1
+ 			&& y < border + MENU_SCROLL_ARROW_HEIGHT && y >= 0
+ 			&& x >= 0 && x < width)
+ 			return FALSE ;
+ 
+ 		if (menu->last_scroll_child != -1
+ 			&& y >= height - border - MENU_SCROLL_ARROW_HEIGHT && y < height
+ 			&& x >= 0 && x < width)
+ 			return TRUE ;
+ 
+ 	}
+ 
        if ((event->time - menu_shell->activate_time) > MENU_SHELL_TIMEOUT)
  	{
  	  if (menu_item && (menu_shell->active_menu_item == menu_item) &&
***************
*** 898,905 ****
  	    }
  	}
        
!       if (node)
! 	gtk_menu_shell_select_item (menu_shell, node->data);
      }
  }
  
--- 964,994 ----
  	    }
  	}
        
! 		if (node)
! 		{
! 			gtk_menu_shell_select_item (menu_shell, node->data);
! 
! 			if (menu_shell->active_menu_item && GTK_IS_MENU(menu_shell))
! 			{
! 				gint idx ;
! 				gint first_idx ;
! 				gint last_idx ;
! 
! 				first_idx = GTK_MENU(menu_shell)->first_scroll_child ;
! 				last_idx = GTK_MENU(menu_shell)->last_scroll_child ;
! 				idx = g_list_index (menu_shell->children, menu_shell->active_menu_item) ;
! 				if (first_idx < 0)
! 					first_idx = 0 ;
! 				if (last_idx < 0)
! 					last_idx = g_list_length (menu_shell->children) ;
! 	
! 				if (idx < first_idx)
! 					gtk_menu_scroll_up (GTK_MENU(menu_shell), first_idx - idx) ;
! 	
! 				if (idx > last_idx)
! 					gtk_menu_scroll_down (GTK_MENU(menu_shell), idx - last_idx) ;
! 			}
! 		}
      }
  }
  
diff -c -r gtk/gtkoptionmenu.c ../gtk+-1.2.8.new/gtk/gtkoptionmenu.c
*** gtk/gtkoptionmenu.c	Thu Jan 27 22:39:55 2000
--- ../gtk+-1.2.8.new/gtk/gtkoptionmenu.c	Thu Aug  3 14:38:41 2000
***************
*** 618,624 ****
    GtkWidget *child;
    GtkRequisition requisition;
    GList *children;
-   gint shift_menu;
    gint screen_width;
    gint screen_height;
    gint menu_xpos;
--- 618,623 ----
***************
*** 666,695 ****
    screen_width = gdk_screen_width ();
    screen_height = gdk_screen_height ();
  
!   shift_menu = FALSE;
!   if (menu_ypos < 0)
!     {
!       menu_ypos = 0;
!       shift_menu = TRUE;
!     }
!   else if ((menu_ypos + height) > screen_height)
!     {
!       menu_ypos -= ((menu_ypos + height) - screen_height);
!       shift_menu = TRUE;
!     }
! 
!   if (shift_menu)
!     {
!       if ((menu_xpos + GTK_WIDGET (option_menu)->allocation.width + width) <= screen_width)
! 	menu_xpos += GTK_WIDGET (option_menu)->allocation.width;
!       else
! 	menu_xpos -= width;
!     }
! 
    if (menu_xpos < 0)
!     menu_xpos = 0;
!   else if ((menu_xpos + width) > screen_width)
!     menu_xpos -= ((menu_xpos + width) - screen_width);
  
    *x = menu_xpos;
    *y = menu_ypos;
--- 665,674 ----
    screen_width = gdk_screen_width ();
    screen_height = gdk_screen_height ();
  
!   if (menu_xpos + width >= screen_width)
! 		menu_xpos -= width ;
    if (menu_xpos < 0)
! 		menu_xpos += GTK_WIDGET(option_menu)->allocation.width ;
  
    *x = menu_xpos;
    *y = menu_ypos;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]