summaryrefslogtreecommitdiff
path: root/gtk
diff options
context:
space:
mode:
authorOwen Taylor <otaylor@redhat.com>2001-10-16 21:02:24 +0000
committerOwen Taylor <otaylor@src.gnome.org>2001-10-16 21:02:24 +0000
commit1ebe3b518b7fe1ac64d96aa4ee025d6aac435442 (patch)
tree5521b85a657a92cee4289904e0f8a9dce9890190 /gtk
parent3612439d217cfb55f1066a95f5f0ee9e2124f8c4 (diff)
downloadgtk+-1ebe3b518b7fe1ac64d96aa4ee025d6aac435442.tar.gz
Add a utility function to translate coordinates relative to one widget's
Tue Oct 16 15:50:03 2001 Owen Taylor <otaylor@redhat.com> * gtk/gtkwidget.c (gtk_widget_translate_coordinates): Add a utility function to translate coordinates relative to one widget's allocation to coordinates relative to another widget's allocation. * gtk/gtkradiobutton.c: Add a special ->focus() implementation that: - only accepts external focus if there is no active member of the group or the button is active. - makes arrow keys move the active button as well as the focus - make tab tab out directly. This makes a radio button group act as a single focus location. (#53577). * gtk/gtkcontainer.c (gtk_container_focus): Remove prefiltering - it was only a small optimization that didn't matter and made things more complicated. * gtk/gtkcontainer.c (gtk_container_focus_tab): Get rid of custom sorter for FOCUS_TAB as we did for the other focus directions, sort by center of widgets, not upper-left corner. (Shouldn't matter in general.) * gtk/gtkcontainer.c: Restructure code to remove duplicate code from the different types of focusing: encapsulate sorting the widgets for the focus direction into one routine (gtk_container_focus_sort()) and then share the work of moving the focus between the different focus directions. * gtk/gtkcontainer.c: Fix bug where arrow navigation might not work correctly with focus chains containing non-immediate children. Sorting was being done using allocation coordinates for each widget in the focus chain, and if there were intermediate window-widgets, these allocations would not be in the same coordinate system.
Diffstat (limited to 'gtk')
-rw-r--r--gtk/gtkcontainer.c457
-rw-r--r--gtk/gtkradiobutton.c181
-rw-r--r--gtk/gtkwidget.c161
-rw-r--r--gtk/gtkwidget.h7
4 files changed, 563 insertions, 243 deletions
diff --git a/gtk/gtkcontainer.c b/gtk/gtkcontainer.c
index 1a28589858..4399afa560 100644
--- a/gtk/gtkcontainer.c
+++ b/gtk/gtkcontainer.c
@@ -80,15 +80,10 @@ static gboolean gtk_container_focus (GtkWidget *widget,
GtkDirectionType direction);
static void gtk_container_real_set_focus_child (GtkContainer *container,
GtkWidget *widget);
-static gboolean gtk_container_focus_tab (GtkContainer *container,
+static GList * gtk_container_focus_sort (GtkContainer *container,
GList *children,
GtkDirectionType direction);
-static gboolean gtk_container_focus_up_down (GtkContainer *container,
- GList **children,
- GtkDirectionType direction);
-static gboolean gtk_container_focus_left_right (GtkContainer *container,
- GList **children,
- GtkDirectionType direction);
+
static gboolean gtk_container_focus_move (GtkContainer *container,
GList *children,
GtkDirectionType direction);
@@ -1536,38 +1531,12 @@ get_focus_chain (GtkContainer *container)
return g_object_get_data (G_OBJECT (container), "gtk-container-focus-chain");
}
-static GList*
-filter_unfocusable (GtkContainer *container,
- GList *list)
-{
- GList *tmp_list;
- GList *tmp_list2;
-
- tmp_list = list;
- while (tmp_list)
- {
- if (GTK_WIDGET_IS_SENSITIVE (tmp_list->data) &&
- GTK_WIDGET_DRAWABLE (tmp_list->data) &&
- (GTK_IS_CONTAINER (tmp_list->data) || GTK_WIDGET_CAN_FOCUS (tmp_list->data)))
- tmp_list = tmp_list->next;
- else
- {
- tmp_list2 = tmp_list;
- tmp_list = tmp_list->next;
-
- list = g_list_remove_link (list, tmp_list2);
- g_list_free_1 (tmp_list2);
- }
- }
-
- return list;
-}
-
static gboolean
gtk_container_focus (GtkWidget *widget,
GtkDirectionType direction)
{
GList *children;
+ GList *sorted_children;
gint return_val;
GtkContainer *container;
@@ -1591,183 +1560,141 @@ gtk_container_focus (GtkWidget *widget,
* chain to override.
*/
if (container->has_focus_chain)
- {
- children = g_list_copy (get_focus_chain (container));
- }
+ children = g_list_copy (get_focus_chain (container));
else
- {
- children = NULL;
- gtk_container_forall (container,
- gtk_container_children_callback,
- &children);
- children = g_list_reverse (children);
- }
+ children = gtk_container_get_children (container);
- if (children)
+ if (container->has_focus_chain &&
+ (direction == GTK_DIR_TAB_FORWARD ||
+ direction == GTK_DIR_TAB_BACKWARD))
{
- /* Remove any children which are inappropriate for focus movement
- */
- children = filter_unfocusable (container, children);
-
- switch (direction)
- {
- case GTK_DIR_TAB_FORWARD:
- case GTK_DIR_TAB_BACKWARD:
- if (container->has_focus_chain)
- {
- if (direction == GTK_DIR_TAB_BACKWARD)
- children = g_list_reverse (children);
- return_val = gtk_container_focus_move (container, children, direction);
- }
- else
- return_val = gtk_container_focus_tab (container, children, direction);
- break;
- case GTK_DIR_UP:
- case GTK_DIR_DOWN:
- return_val = gtk_container_focus_up_down (container, &children, direction);
- break;
- case GTK_DIR_LEFT:
- case GTK_DIR_RIGHT:
- return_val = gtk_container_focus_left_right (container, &children, direction);
- break;
- }
-
- g_list_free (children);
+ sorted_children = g_list_copy (children);
+
+ if (direction == GTK_DIR_TAB_BACKWARD)
+ sorted_children = g_list_reverse (sorted_children);
}
+ else
+ sorted_children = gtk_container_focus_sort (container, children, direction);
+
+ return_val = gtk_container_focus_move (container, sorted_children, direction);
+
+ g_list_free (sorted_children);
+ g_list_free (children);
}
return return_val;
}
-static gboolean
-gtk_container_focus_tab (GtkContainer *container,
- GList *children,
- GtkDirectionType direction)
+static gint
+tab_compare (gconstpointer a,
+ gconstpointer b)
{
- GtkWidget *child;
- GtkWidget *child2;
- GList *tmp_list;
- guint length;
- guint i, j;
+ const GtkWidget *child1 = a;
+ const GtkWidget *child2 = b;
- length = g_list_length (children);
+ gint y1 = child1->allocation.y + child1->allocation.height / 2;
+ gint y2 = child2->allocation.y + child2->allocation.height / 2;
- /* sort the children in the y direction */
- for (i = 1; i < length; i++)
+ if (y1 == y2)
{
- j = i;
- tmp_list = g_list_nth (children, j);
- child = tmp_list->data;
-
- while (j > 0)
- {
- child2 = tmp_list->prev->data;
- if (child->allocation.y < child2->allocation.y)
- {
- tmp_list->data = tmp_list->prev->data;
- tmp_list = tmp_list->prev;
- j--;
- }
- else
- break;
- }
-
- tmp_list->data = child;
+ gint x1 = child1->allocation.x + child1->allocation.width / 2;
+ gint x2 = child2->allocation.x + child2->allocation.width / 2;
+
+ return (x1 < x2) ? -1 : ((x1 == x2) ? 0 : 1);
}
+ else
+ return (y1 < y2) ? -1 : 1;
+}
- /* sort the children in the x direction while
- * maintaining the y direction sort.
+static GList *
+gtk_container_focus_sort_tab (GtkContainer *container,
+ GList *children,
+ GtkDirectionType direction)
+{
+ children = g_list_sort (children, tab_compare);
+
+ /* if we are going backwards then reverse the order
+ * of the children.
*/
- for (i = 1; i < length; i++)
+ if (direction == GTK_DIR_TAB_BACKWARD)
+ children = g_list_reverse (children);
+
+ return children;
+}
+
+/* Get coordinates of @widget's allocation with respect to
+ * allocation of @container.
+ */
+static gboolean
+get_allocation_coords (GtkContainer *container,
+ GtkWidget *widget,
+ GdkRectangle *allocation)
+{
+ GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
+
+ *allocation = widget->allocation;
+
+ return gtk_widget_translate_coordinates (widget, GTK_WIDGET (container),
+ 0, 0, &allocation->x, &allocation->y);
+}
+
+/* Look for a child in @children that is intermediate between
+ * the focus widget and container. This widget, if it exists,
+ * acts as the starting widget for focus navigation.
+ */
+static GtkWidget *
+find_old_focus (GtkContainer *container,
+ GList *children)
+{
+ GList *tmp_list = children;
+ while (tmp_list)
{
- j = i;
- tmp_list = g_list_nth (children, j);
- child = tmp_list->data;
+ GtkWidget *child = tmp_list->data;
+ GtkWidget *widget = child;
- while (j > 0)
+ while (widget && widget != (GtkWidget *)container)
{
- child2 = tmp_list->prev->data;
- if ((child->allocation.x < child2->allocation.x) &&
- (child->allocation.y == child2->allocation.y))
- {
- tmp_list->data = tmp_list->prev->data;
- tmp_list = tmp_list->prev;
- j--;
- }
- else
- break;
+ GtkWidget *parent = widget->parent;
+ if (parent && ((GtkContainer *)parent)->focus_child != widget)
+ goto next;
+
+ widget = parent;
}
- tmp_list->data = child;
- }
+ return child;
- /* if we are going backwards then reverse the order
- * of the children.
- */
- if (direction == GTK_DIR_TAB_BACKWARD)
- children = g_list_reverse (children);
+ next:
+ tmp_list = tmp_list->next;
+ }
- return gtk_container_focus_move (container, children, direction);
+ return NULL;
}
static gboolean
-old_focus_coords (GtkContainer *container, GdkRectangle *old_focus_rect)
+old_focus_coords (GtkContainer *container,
+ GdkRectangle *old_focus_rect)
{
GtkWidget *widget = GTK_WIDGET (container);
GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
- if (toplevel &&
- GTK_IS_WINDOW (toplevel) && GTK_WINDOW (toplevel)->focus_widget &&
- GTK_WIDGET_REALIZED (container) &&
- GTK_WIDGET_REALIZED (GTK_WINDOW (toplevel)->focus_widget))
+ if (toplevel && GTK_IS_WINDOW (toplevel) && GTK_WINDOW (toplevel)->focus_widget)
{
GtkWidget *old_focus = GTK_WINDOW (toplevel)->focus_widget;
- GdkWindow *old_parent_window = old_focus->parent ? old_focus->parent->window : old_focus->window;
- GdkWindow *new_parent_window = widget->window;
- GdkWindow *toplevel_window = toplevel->window;
-
- *old_focus_rect = old_focus->allocation;
- /* Translate coordinates to the toplevel */
-
- while (old_parent_window != toplevel_window)
- {
- gint dx, dy;
-
- gdk_window_get_position (old_parent_window, &dx, &dy);
-
- old_focus_rect->x += dx;
- old_focus_rect->y += dy;
-
- old_parent_window = gdk_window_get_parent (old_parent_window);
- }
-
- /* Translate coordinates back to the new container */
-
- while (new_parent_window != toplevel_window)
- {
- gint dx, dy;
-
- gdk_window_get_position (new_parent_window, &dx, &dy);
-
- old_focus_rect->x -= dx;
- old_focus_rect->y -= dy;
-
- new_parent_window = gdk_window_get_parent (new_parent_window);
- }
-
- return TRUE;
+ return get_allocation_coords (container, old_focus, old_focus_rect);
}
-
- return FALSE;
+ else
+ return FALSE;
}
typedef struct _CompareInfo CompareInfo;
struct _CompareInfo
{
+ GtkContainer *container;
gint x;
gint y;
+ gboolean reverse;
};
static gint
@@ -1775,68 +1702,83 @@ up_down_compare (gconstpointer a,
gconstpointer b,
gpointer data)
{
- const GtkWidget *child1 = a;
- const GtkWidget *child2 = b;
+ GdkRectangle allocation1;
+ GdkRectangle allocation2;
CompareInfo *compare = data;
+ gint y1, y2;
- gint y1 = child1->allocation.y + child1->allocation.height / 2;
- gint y2 = child2->allocation.y + child2->allocation.height / 2;
+ get_allocation_coords (compare->container, (GtkWidget *)a, &allocation1);
+ get_allocation_coords (compare->container, (GtkWidget *)b, &allocation2);
+
+ y1 = allocation1.y + allocation1.height / 2;
+ y2 = allocation2.y + allocation2.height / 2;
if (y1 == y2)
{
- gint x1 = abs (child1->allocation.x + child1->allocation.width / 2 - compare->x);
- gint x2 = abs (child2->allocation.x + child2->allocation.width / 2 - compare->x);
+ gint x1 = abs (allocation1.x + allocation1.width / 2 - compare->x);
+ gint x2 = abs (allocation2.x + allocation2.width / 2 - compare->x);
- if (compare->y < y1)
- return (x1 < x2) ? -1 : ((x1 == x2) ? 0 : 1);
- else
+ if (compare->reverse)
return (x1 < x2) ? 1 : ((x1 == x2) ? 0 : -1);
+ else
+ return (x1 < x2) ? -1 : ((x1 == x2) ? 0 : 1);
}
else
return (y1 < y2) ? -1 : 1;
}
-static gboolean
-gtk_container_focus_up_down (GtkContainer *container,
- GList **children,
- GtkDirectionType direction)
+static GList *
+gtk_container_focus_sort_up_down (GtkContainer *container,
+ GList *children,
+ GtkDirectionType direction)
{
CompareInfo compare;
GList *tmp_list;
+ GtkWidget *old_focus;
- if (container->focus_child)
+ compare.container = container;
+ compare.reverse = (direction == GTK_DIR_UP);
+
+ old_focus = find_old_focus (container, children);
+ if (old_focus)
{
+ GdkRectangle old_allocation;
gint compare_x1;
gint compare_x2;
gint compare_y;
-
+
/* Delete widgets from list that don't match minimum criteria */
- compare_x1 = container->focus_child->allocation.x;
- compare_x2 = container->focus_child->allocation.x + container->focus_child->allocation.width;
+ get_allocation_coords (container, old_focus, &old_allocation);
+
+ compare_x1 = old_allocation.x;
+ compare_x2 = old_allocation.x + old_allocation.width;
if (direction == GTK_DIR_UP)
- compare_y = container->focus_child->allocation.y;
+ compare_y = old_allocation.y;
else
- compare_y = container->focus_child->allocation.y + container->focus_child->allocation.height;
+ compare_y = old_allocation.y + old_allocation.height;
- tmp_list = *children;
+ tmp_list = children;
while (tmp_list)
{
GtkWidget *child = tmp_list->data;
GList *next = tmp_list->next;
gint child_x1, child_x2;
+ GdkRectangle child_allocation;
- if (child != container->focus_child)
+ if (child != old_focus)
{
- child_x1 = child->allocation.x;
- child_x2 = child->allocation.x + child->allocation.width;
+ get_allocation_coords (container, child, &child_allocation);
+
+ child_x1 = child_allocation.x;
+ child_x2 = child_allocation.x + child_allocation.width;
if ((child_x2 <= compare_x1 || child_x1 >= compare_x2) /* No horizontal overlap */ ||
- (direction == GTK_DIR_DOWN && child->allocation.y + child->allocation.height < compare_y) || /* Not below */
- (direction == GTK_DIR_UP && child->allocation.y > compare_y)) /* Not above */
+ (direction == GTK_DIR_DOWN && child_allocation.y + child_allocation.height < compare_y) || /* Not below */
+ (direction == GTK_DIR_UP && child_allocation.y > compare_y)) /* Not above */
{
- *children = g_list_delete_link (*children, tmp_list);
+ children = g_list_delete_link (children, tmp_list);
}
}
@@ -1844,7 +1786,7 @@ gtk_container_focus_up_down (GtkContainer *container,
}
compare.x = (compare_x1 + compare_x2) / 2;
- compare.y = container->focus_child->allocation.y + container->focus_child->allocation.height / 2;
+ compare.y = old_allocation.y + old_allocation.height / 2;
}
else
{
@@ -1871,12 +1813,12 @@ gtk_container_focus_up_down (GtkContainer *container,
compare.y = (direction == GTK_DIR_DOWN) ? 0 : + widget->allocation.height;
}
- *children = g_list_sort_with_data (*children, up_down_compare, &compare);
+ children = g_list_sort_with_data (children, up_down_compare, &compare);
- if (direction == GTK_DIR_UP)
- *children = g_list_reverse (*children);
+ if (compare.reverse)
+ children = g_list_reverse (children);
- return gtk_container_focus_move (container, *children, direction);
+ return children;
}
static gint
@@ -1884,68 +1826,84 @@ left_right_compare (gconstpointer a,
gconstpointer b,
gpointer data)
{
- const GtkWidget *child1 = a;
- const GtkWidget *child2 = b;
+ GdkRectangle allocation1;
+ GdkRectangle allocation2;
CompareInfo *compare = data;
+ gint x1, x2;
+
+ get_allocation_coords (compare->container, (GtkWidget *)a, &allocation1);
+ get_allocation_coords (compare->container, (GtkWidget *)b, &allocation2);
- gint x1 = child1->allocation.x + child1->allocation.width / 2;
- gint x2 = child2->allocation.x + child2->allocation.width / 2;
+ x1 = allocation1.x + allocation1.width / 2;
+ x2 = allocation2.x + allocation2.width / 2;
if (x1 == x2)
{
- gint y1 = abs (child1->allocation.y + child1->allocation.height / 2 - compare->y);
- gint y2 = abs (child2->allocation.y + child2->allocation.height / 2 - compare->y);
+ gint y1 = abs (allocation1.y + allocation1.height / 2 - compare->y);
+ gint y2 = abs (allocation2.y + allocation2.height / 2 - compare->y);
- if (compare->x < x1)
- return (y1 < y2) ? -1 : ((y1 == y2) ? 0 : 1);
- else
+ if (compare->reverse)
return (y1 < y2) ? 1 : ((y1 == y2) ? 0 : -1);
+ else
+ return (y1 < y2) ? -1 : ((y1 == y2) ? 0 : 1);
}
else
return (x1 < x2) ? -1 : 1;
}
-static gboolean
-gtk_container_focus_left_right (GtkContainer *container,
- GList **children,
- GtkDirectionType direction)
+static GList *
+gtk_container_focus_sort_left_right (GtkContainer *container,
+ GList *children,
+ GtkDirectionType direction)
{
CompareInfo compare;
GList *tmp_list;
+ GtkWidget *old_focus;
- if (container->focus_child)
+ compare.container = container;
+ compare.reverse = (direction == GTK_DIR_LEFT);
+
+ old_focus = find_old_focus (container, children);
+ if (old_focus)
{
+ GdkRectangle old_allocation;
+
gint compare_y1;
gint compare_y2;
gint compare_x;
/* Delete widgets from list that don't match minimum criteria */
- compare_y1 = container->focus_child->allocation.y;
- compare_y2 = container->focus_child->allocation.y + container->focus_child->allocation.height;
+ get_allocation_coords (container, old_focus, &old_allocation);
+
+ compare_y1 = old_allocation.y;
+ compare_y2 = old_allocation.y + old_allocation.height;
if (direction == GTK_DIR_LEFT)
- compare_x = container->focus_child->allocation.x;
+ compare_x = old_allocation.x;
else
- compare_x = container->focus_child->allocation.x + container->focus_child->allocation.width;
+ compare_x = old_allocation.x + old_allocation.width;
- tmp_list = *children;
+ tmp_list = children;
while (tmp_list)
{
GtkWidget *child = tmp_list->data;
GList *next = tmp_list->next;
gint child_y1, child_y2;
+ GdkRectangle child_allocation;
- if (child != container->focus_child)
+ if (child != old_focus)
{
- child_y1 = child->allocation.y;
- child_y2 = child->allocation.y + child->allocation.height;
+ get_allocation_coords (container, child, &child_allocation);
+
+ child_y1 = child_allocation.y;
+ child_y2 = child_allocation.y + child_allocation.height;
if ((child_y2 <= compare_y1 || child_y1 >= compare_y2) /* No vertical overlap */ ||
- (direction == GTK_DIR_RIGHT && child->allocation.x + child->allocation.width < compare_x) || /* Not to left */
- (direction == GTK_DIR_LEFT && child->allocation.x > compare_x)) /* Not to right */
+ (direction == GTK_DIR_RIGHT && child_allocation.x + child_allocation.width < compare_x) || /* Not to left */
+ (direction == GTK_DIR_LEFT && child_allocation.x > compare_x)) /* Not to right */
{
- *children = g_list_delete_link (*children, tmp_list);
+ children = g_list_delete_link (children, tmp_list);
}
}
@@ -1953,7 +1911,7 @@ gtk_container_focus_left_right (GtkContainer *container,
}
compare.y = (compare_y1 + compare_y2) / 2;
- compare.x = container->focus_child->allocation.x + container->focus_child->allocation.width / 2;
+ compare.x = old_allocation.x + old_allocation.width / 2;
}
else
{
@@ -1980,12 +1938,51 @@ gtk_container_focus_left_right (GtkContainer *container,
compare.x = (direction == GTK_DIR_RIGHT) ? 0 : widget->allocation.width;
}
- *children = g_list_sort_with_data (*children, left_right_compare, &compare);
+ children = g_list_sort_with_data (children, left_right_compare, &compare);
+
+ if (compare.reverse)
+ children = g_list_reverse (children);
+
+ return children;
+}
+
+/**
+ * gtk_container_focus_sort:
+ * @container: a #GtkContainer
+ * @children: a list of descendents of @container (they don't
+ * have to be direct children.
+ * @direction: focus direction
+ *
+ * Sorts @children in the correct order for focusing with
+ * direction type @direction.
+ *
+ * Return value: a copy of @children, sorted in correct focusing order,
+ * with children that aren't suitable for focusing in this direction
+ * removed.
+ **/
+static GList *
+gtk_container_focus_sort (GtkContainer *container,
+ GList *children,
+ GtkDirectionType direction)
+{
+ children = g_list_copy (children);
+
+ switch (direction)
+ {
+ case GTK_DIR_TAB_FORWARD:
+ case GTK_DIR_TAB_BACKWARD:
+ return gtk_container_focus_sort_tab (container, children, direction);
+ case GTK_DIR_UP:
+ case GTK_DIR_DOWN:
+ return gtk_container_focus_sort_up_down (container, children, direction);
+ case GTK_DIR_LEFT:
+ case GTK_DIR_RIGHT:
+ return gtk_container_focus_sort_left_right (container, children, direction);
+ }
- if (direction == GTK_DIR_LEFT)
- *children = g_list_reverse (*children);
+ g_assert_not_reached ();
- return gtk_container_focus_move (container, *children, direction);
+ return NULL;
}
static gboolean
diff --git a/gtk/gtkradiobutton.c b/gtk/gtkradiobutton.c
index 245bec87e9..e5cf6a318e 100644
--- a/gtk/gtkradiobutton.c
+++ b/gtk/gtkradiobutton.c
@@ -35,19 +35,20 @@ enum {
};
-static void gtk_radio_button_class_init (GtkRadioButtonClass *klass);
-static void gtk_radio_button_init (GtkRadioButton *radio_button);
-static void gtk_radio_button_destroy (GtkObject *object);
-static void gtk_radio_button_clicked (GtkButton *button);
-static void gtk_radio_button_draw_indicator (GtkCheckButton *check_button,
- GdkRectangle *area);
-static void gtk_radio_button_set_arg (GtkObject *object,
- GtkArg *arg,
- guint arg_id);
-static void gtk_radio_button_get_arg (GtkObject *object,
- GtkArg *arg,
- guint arg_id);
-
+static void gtk_radio_button_class_init (GtkRadioButtonClass *klass);
+static void gtk_radio_button_init (GtkRadioButton *radio_button);
+static void gtk_radio_button_destroy (GtkObject *object);
+static gboolean gtk_radio_button_focus (GtkWidget *widget,
+ GtkDirectionType direction);
+static void gtk_radio_button_clicked (GtkButton *button);
+static void gtk_radio_button_draw_indicator (GtkCheckButton *check_button,
+ GdkRectangle *area);
+static void gtk_radio_button_set_arg (GtkObject *object,
+ GtkArg *arg,
+ guint arg_id);
+static void gtk_radio_button_get_arg (GtkObject *object,
+ GtkArg *arg,
+ guint arg_id);
static GtkCheckButtonClass *parent_class = NULL;
@@ -83,8 +84,10 @@ gtk_radio_button_class_init (GtkRadioButtonClass *class)
GtkObjectClass *object_class;
GtkButtonClass *button_class;
GtkCheckButtonClass *check_button_class;
+ GtkWidgetClass *widget_class;
object_class = (GtkObjectClass*) class;
+ widget_class = (GtkWidgetClass*) class;
button_class = (GtkButtonClass*) class;
check_button_class = (GtkCheckButtonClass*) class;
@@ -96,6 +99,8 @@ gtk_radio_button_class_init (GtkRadioButtonClass *class)
object_class->get_arg = gtk_radio_button_get_arg;
object_class->destroy = gtk_radio_button_destroy;
+ widget_class->focus = gtk_radio_button_focus;
+
button_class->clicked = gtk_radio_button_clicked;
check_button_class->draw_indicator = gtk_radio_button_draw_indicator;
@@ -331,6 +336,156 @@ gtk_radio_button_destroy (GtkObject *object)
}
static void
+get_coordinates (GtkWidget *widget,
+ GtkWidget *reference,
+ gint *x,
+ gint *y)
+{
+ *x = widget->allocation.x + widget->allocation.width / 2;
+ *y = widget->allocation.y + widget->allocation.height / 2;
+
+ gtk_widget_translate_coordinates (widget, reference, *x, *y, x, y);
+}
+
+static gint
+left_right_compare (gconstpointer a,
+ gconstpointer b,
+ gpointer data)
+{
+ gint x1, y1, x2, y2;
+
+ get_coordinates ((GtkWidget *)a, data, &x1, &y1);
+ get_coordinates ((GtkWidget *)b, data, &x2, &y2);
+
+ if (y1 == y2)
+ return (x1 < x2) ? -1 : ((x1 == x2) ? 0 : 1);
+ else
+ return (y1 < y2) ? -1 : 1;
+}
+
+static gint
+up_down_compare (gconstpointer a,
+ gconstpointer b,
+ gpointer data)
+{
+ gint x1, y1, x2, y2;
+
+ get_coordinates ((GtkWidget *)a, data, &x1, &y1);
+ get_coordinates ((GtkWidget *)b, data, &x2, &y2);
+
+ if (x1 == x2)
+ return (y1 < y2) ? -1 : ((y1 == y2) ? 0 : 1);
+ else
+ return (x1 < x2) ? -1 : 1;
+}
+
+static gboolean
+gtk_radio_button_focus (GtkWidget *widget,
+ GtkDirectionType direction)
+{
+ GtkRadioButton *radio_button = GTK_RADIO_BUTTON (widget);
+ GSList *tmp_slist;
+
+ if (gtk_widget_is_focus (widget))
+ {
+ GSList *focus_list, *tmp_list;
+ GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
+ GtkWidget *new_focus = NULL;
+
+ focus_list = g_slist_copy (radio_button->group);
+
+ switch (direction)
+ {
+ case GTK_DIR_TAB_FORWARD:
+ case GTK_DIR_TAB_BACKWARD:
+ return FALSE;
+ case GTK_DIR_LEFT:
+ case GTK_DIR_RIGHT:
+ focus_list = g_slist_sort_with_data (focus_list, left_right_compare, toplevel);
+ break;
+ case GTK_DIR_UP:
+ case GTK_DIR_DOWN:
+ focus_list = g_slist_sort_with_data (focus_list, up_down_compare, toplevel);
+ break;
+ }
+
+ if (direction == GTK_DIR_LEFT || direction == GTK_DIR_UP)
+ focus_list = g_slist_reverse (focus_list);
+
+ tmp_list = g_slist_find (focus_list, widget);
+
+ if (tmp_list)
+ {
+ tmp_list = tmp_list->next;
+
+ while (tmp_list)
+ {
+ GtkWidget *child = tmp_list->data;
+
+ if (GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_IS_SENSITIVE (child))
+ {
+ new_focus = child;
+ break;
+ }
+
+ tmp_list = tmp_list->next;
+ }
+ }
+
+ if (!new_focus)
+ {
+ tmp_list = focus_list;
+
+ while (tmp_list)
+ {
+ GtkWidget *child = tmp_list->data;
+
+ if (GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_IS_SENSITIVE (child))
+ {
+ new_focus = child;
+ break;
+ }
+
+ tmp_list = tmp_list->next;
+ }
+ }
+
+ g_slist_free (focus_list);
+
+ if (new_focus)
+ {
+ gtk_widget_grab_focus (new_focus);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (new_focus), TRUE);
+ }
+
+ return TRUE;
+ }
+ else
+ {
+ GtkRadioButton *selected_button = NULL;
+
+ /* We accept the focus if, we don't have the focus and
+ * - we are the currently active button in the group
+ * - there is no currently active radio button.
+ */
+
+ tmp_slist = radio_button->group;
+ while (tmp_slist)
+ {
+ if (GTK_TOGGLE_BUTTON (tmp_slist->data)->active)
+ selected_button = tmp_slist->data;
+ tmp_slist = tmp_slist->next;
+ }
+
+ if (selected_button && selected_button != radio_button)
+ return FALSE;
+
+ gtk_widget_grab_focus (widget);
+ return TRUE;
+ }
+}
+
+static void
gtk_radio_button_clicked (GtkButton *button)
{
GtkToggleButton *toggle_button;
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 52f0203573..882bd57f34 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -2317,6 +2317,167 @@ gtk_widget_size_allocate (GtkWidget *widget,
}
}
+/**
+ * gtk_widget_common_ancestor:
+ * @widget_a: a #GtkWidget
+ * @widget_b: a #GtkWidget
+ *
+ * Find the common ancestor of @widget_a and @widget_b that
+ * is closest to the two widgets.
+ *
+ * Return value: the closest common ancestor of @widget_a and
+ * @widget_b or %NULL if @widget_a and @widget_b do not
+ * share a common ancestor.
+ **/
+static GtkWidget *
+gtk_widget_common_ancestor (GtkWidget *widget_a,
+ GtkWidget *widget_b)
+{
+ GtkWidget *parent_a;
+ GtkWidget *parent_b;
+ gint depth_a = 0;
+ gint depth_b = 0;
+
+ parent_a = widget_a;
+ while (parent_a->parent)
+ {
+ parent_a = parent_a->parent;
+ depth_a++;
+ }
+
+ parent_b = widget_b;
+ while (parent_b->parent)
+ {
+ parent_b = parent_b->parent;
+ depth_b++;
+ }
+
+ if (parent_a != parent_b)
+ return NULL;
+
+ while (depth_a > depth_b)
+ {
+ widget_a = widget_a->parent;
+ depth_a--;
+ }
+
+ while (depth_b > depth_a)
+ {
+ widget_b = widget_b->parent;
+ depth_b--;
+ }
+
+ while (widget_a != widget_b)
+ {
+ widget_a = widget_a->parent;
+ widget_b = widget_b->parent;
+ }
+
+ return widget_a;
+}
+
+/**
+ * gtk_widget_translate_coordinates:
+ * @src_widget: a #GtkWidget
+ * @dest_widget: a #GtkWidget
+ * @src_x: X position relative to @src_widget
+ * @src_y: Y position relative to @src_widget
+ * @dest_x: location to store X position relative to @dest_widget
+ * @dest_y: location to store Y position relative to @dest_widget
+ *
+ * Translate coordinates relative to @src_widget's allocation to coordinates
+ * relative to @dest_widget's allocations. In order to perform this
+ * operation, both widgets must be realized, and must share a common
+ * toplevel.
+ *
+ * Return value: %FALSE if either widget was not realized, or there
+ * was no common ancestor. In this case, nothing is stored in
+ * *@dest_x and *@dest_y. Otherwise %TRUE.
+ **/
+gboolean
+gtk_widget_translate_coordinates (GtkWidget *src_widget,
+ GtkWidget *dest_widget,
+ gint src_x,
+ gint src_y,
+ gint *dest_x,
+ gint *dest_y)
+{
+ GtkWidget *ancestor;
+ GdkWindow *window;
+
+ g_return_if_fail (GTK_IS_WIDGET (src_widget));
+ g_return_if_fail (GTK_IS_WIDGET (dest_widget));
+
+ ancestor = gtk_widget_common_ancestor (src_widget, dest_widget);
+ if (!ancestor || !GTK_WIDGET_REALIZED (src_widget) || !GTK_WIDGET_REALIZED (dest_widget))
+ return FALSE;
+
+ /* Translate from allocation relative to window relative */
+ if (!GTK_WIDGET_NO_WINDOW (src_widget) && src_widget->parent)
+ {
+ gint wx, wy;
+ gdk_window_get_position (src_widget->window, &wx, &wy);
+
+ src_x -= wx - src_widget->allocation.x;
+ src_y -= wy - src_widget->allocation.y;
+ }
+ else
+ {
+ src_x += src_widget->allocation.x;
+ src_y += src_widget->allocation.y;
+ }
+
+ /* Translate to the common ancestor */
+ window = src_widget->window;
+ while (window != ancestor->window)
+ {
+ gint dx, dy;
+
+ gdk_window_get_position (window, &dx, &dy);
+
+ src_x += dx;
+ src_y += dy;
+
+ window = gdk_window_get_parent (window);
+ }
+
+ /* And back */
+ window = dest_widget->window;
+ while (window != ancestor->window)
+ {
+ gint dx, dy;
+
+ gdk_window_get_position (window, &dx, &dy);
+
+ src_x -= dx;
+ src_y -= dy;
+
+ window = gdk_window_get_parent (window);
+ }
+
+ /* Translate from window relative to allocation relative */
+ if (!GTK_WIDGET_NO_WINDOW (dest_widget) && dest_widget->parent)
+ {
+ gint wx, wy;
+ gdk_window_get_position (dest_widget->window, &wx, &wy);
+
+ src_x += wx - dest_widget->allocation.x;
+ src_y += wy - dest_widget->allocation.y;
+ }
+ else
+ {
+ src_x -= dest_widget->allocation.x;
+ src_y -= dest_widget->allocation.y;
+ }
+
+ if (dest_x)
+ *dest_x = src_x;
+ if (dest_y)
+ *dest_y = src_y;
+
+ return TRUE;
+}
+
static void
gtk_widget_real_size_allocate (GtkWidget *widget,
GtkAllocation *allocation)
diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h
index 60997c9118..111c779268 100644
--- a/gtk/gtkwidget.h
+++ b/gtk/gtkwidget.h
@@ -621,6 +621,13 @@ void gtk_widget_get_pointer (GtkWidget *widget,
gboolean gtk_widget_is_ancestor (GtkWidget *widget,
GtkWidget *ancestor);
+gboolean gtk_widget_translate_coordinates (GtkWidget *src_widget,
+ GtkWidget *dest_widget,
+ gint src_x,
+ gint src_y,
+ gint *dest_x,
+ gint *dest_y);
+
/* Hide widget and return TRUE.
*/
gboolean gtk_widget_hide_on_delete (GtkWidget *widget);