diff src/gtkutils.c @ 8143:8633dc570442

[gaim-migrate @ 8851] sf cvs fing sucks. This is the rest of the accessibility stuff from that cool Nathan guy. It makes shift+f10 bring up context menus. Please check over stuff and make sure neither of us broke anything. Also, someone think of a good way to phrase that thing about this that's in the ChangeLog. Thank you, and have a nice day. committer: Tailor Script <tailor@pidgin.im>
author Mark Doliner <mark@kingant.net>
date Mon, 19 Jan 2004 22:40:17 +0000
parents 4971193f761d
children 77d1252b3803
line wrap: on
line diff
--- a/src/gtkutils.c	Mon Jan 19 22:13:50 2004 +0000
+++ b/src/gtkutils.c	Mon Jan 19 22:40:17 2004 +0000
@@ -1389,3 +1389,175 @@
 	atk_relation_set_add (set, relation);
 	g_object_unref (relation);
 }
+
+static void
+gaim_gtk_menu_position_func(GtkMenu *menu,
+							gint *x,
+							gint *y,
+							gboolean *push_in,
+							gpointer data)
+{
+	GtkWidget *widget;
+	GtkRequisition requisition;
+	GdkScreen *screen;
+	GdkRectangle monitor;
+	gint monitor_num;
+	gint space_left, space_right, space_above, space_below;
+	gint needed_width;
+	gint needed_height;
+	gint xthickness;
+	gint ythickness;
+	gboolean rtl;
+
+	g_return_if_fail(GTK_IS_MENU(menu));
+
+	widget     = GTK_WIDGET(menu);
+	screen     = gtk_widget_get_screen(widget);
+	xthickness = widget->style->xthickness;
+	ythickness = widget->style->ythickness;
+	rtl        = (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL);
+
+	/*
+	 * 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,
+	 * the requisition won't have been recomputed yet.
+	 */
+	gtk_widget_size_request (widget, &requisition);
+
+	monitor_num = gdk_screen_get_monitor_at_point (screen, *x, *y);
+
+	push_in = FALSE;
+  
+	/*
+	 * The placement of popup menus horizontally works like this (with
+	 * RTL in parentheses)
+	 *
+	 * - If there is enough room to the right (left) of the mouse cursor,
+	 *   position the menu there.
+	 * 
+	 * - Otherwise, if if there is enough room to the left (right) of the 
+	 *   mouse cursor, position the menu there.
+	 * 
+	 * - Otherwise if the menu is smaller than the monitor, position it
+	 *   on the side of the mouse cursor that has the most space available
+	 *
+	 * - Otherwise (if there is simply not enough room for the menu on the
+	 *   monitor), position it as far left (right) as possible.
+	 *
+	 * Positioning in the vertical direction is similar: first try below
+	 * mouse cursor, then above.
+	 */
+	gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
+
+	space_left = *x - monitor.x;
+	space_right = monitor.x + monitor.width - *x - 1;
+	space_above = *y - monitor.y;
+	space_below = monitor.y + monitor.height - *y - 1;
+
+	/* position horizontally */
+
+	/* the amount of space we need to position the menu. Note the
+	 * menu is offset "xthickness" pixels 
+	 */
+	needed_width = requisition.width - xthickness;
+
+	if (needed_width <= space_left ||
+	    needed_width <= space_right)
+	{
+		if ((rtl  && needed_width <= space_left) ||
+		    (!rtl && needed_width >  space_right))
+		{
+			/* position left */
+			*x = *x + xthickness - requisition.width + 1;
+		}
+		else
+		{
+			/* position right */
+			*x = *x - xthickness;
+		}
+
+		/* x is clamped on-screen further down */
+	}
+	else if (requisition.width <= monitor.width)
+	{
+		/* the menu is too big to fit on either side of the mouse
+		 * cursor, but smaller than the monitor. Position it on
+		 * the side that has the most space
+		 */
+		if (space_left > space_right)
+		{
+			/* left justify */
+			*x = monitor.x;
+		}
+		else
+		{
+			/* right justify */
+			*x = monitor.x + monitor.width - requisition.width;
+		}
+	}
+	else /* menu is simply too big for the monitor */
+	{
+		if (rtl)
+		{
+			/* right justify */
+			*x = monitor.x + monitor.width - requisition.width;
+		}
+		else
+		{
+			/* left justify */
+			*x = monitor.x;
+		}
+	}
+
+	/* Position vertically. The algorithm is the same as above, but
+	 * simpler because we don't have to take RTL into account.
+	 */
+	needed_height = requisition.height - ythickness;
+
+	if (needed_height <= space_above ||
+	    needed_height <= space_below)
+	{
+		if (needed_height <= space_below)
+			*y = *y - ythickness;
+		else
+			*y = *y + ythickness - requisition.height + 1;
+	  
+		*y = CLAMP (*y, monitor.y,
+			   monitor.y + monitor.height - requisition.height);
+	}
+	else if (needed_height > space_below && needed_height > space_above)
+	{
+		if (space_below >= space_above)
+			*y = monitor.y + monitor.height - requisition.height;
+		else
+			*y = monitor.y;
+	}
+	else
+	{
+		*y = monitor.y;
+	}
+}
+
+void
+gaim_gtk_treeview_popup_menu_position_func(GtkMenu *menu,
+										   gint *x,
+										   gint *y,
+										   gboolean *push_in,
+										   gpointer data)
+{
+	GtkWidget *widget = GTK_WIDGET(data);
+	GtkTreeView *tv = GTK_TREE_VIEW(data);
+	GtkTreePath *path;
+	GtkTreeViewColumn *col;
+	GdkRectangle rect;
+	gint ythickness = GTK_WIDGET(menu)->style->ythickness;
+
+	gdk_window_get_origin (widget->window, x, y);
+	gtk_tree_view_get_cursor (tv, &path, &col);
+	gtk_tree_view_get_cell_area (tv, path, col, &rect);
+	
+	*x += rect.x+rect.width;
+	*y += rect.y+rect.height+ythickness;
+	gaim_gtk_menu_position_func (menu, x, y, push_in, data);
+}