diff options
author | Owen Taylor <otaylor@redhat.com> | 2001-10-16 21:02:24 +0000 |
---|---|---|
committer | Owen Taylor <otaylor@src.gnome.org> | 2001-10-16 21:02:24 +0000 |
commit | 1ebe3b518b7fe1ac64d96aa4ee025d6aac435442 (patch) | |
tree | 5521b85a657a92cee4289904e0f8a9dce9890190 /gtk | |
parent | 3612439d217cfb55f1066a95f5f0ee9e2124f8c4 (diff) | |
download | gtk+-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.c | 457 | ||||
-rw-r--r-- | gtk/gtkradiobutton.c | 181 | ||||
-rw-r--r-- | gtk/gtkwidget.c | 161 | ||||
-rw-r--r-- | gtk/gtkwidget.h | 7 |
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); |