Index: gtk/gtkclist.c =================================================================== RCS file: /cvs/gnome/gtk+/gtk/gtkclist.c,v retrieving revision 1.156.2.25 diff -u -r1.156.2.25 gtkclist.c --- gtk/gtkclist.c 2001/03/01 00:18:20 1.156.2.25 +++ gtk/gtkclist.c 2001/04/26 01:38:40 @@ -683,7 +683,7 @@ widget_class->expose_event = gtk_clist_expose; widget_class->size_request = gtk_clist_size_request; widget_class->size_allocate = gtk_clist_size_allocate; - widget_class->key_press_event = gtk_clist_key_press; + widget_class->focus_in_event = gtk_clist_focus_in; widget_class->focus_out_event = gtk_clist_focus_out; widget_class->draw_focus = gtk_clist_draw_focus; @@ -4439,7 +4439,6 @@ * gtk_clist_draw * gtk_clist_expose * gtk_clist_style_set - * gtk_clist_key_press * gtk_clist_button_press * gtk_clist_button_release * gtk_clist_motion @@ -4910,34 +4909,6 @@ } static gint -gtk_clist_key_press (GtkWidget *widget, - GdkEventKey *event) -{ - g_return_val_if_fail (widget != NULL, FALSE); - g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE); - g_return_val_if_fail (event != NULL, FALSE); - - if (GTK_WIDGET_CLASS (parent_class)->key_press_event && - GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event)) - return TRUE; - - switch (event->keyval) - { - case GDK_Tab: - case GDK_ISO_Left_Tab: - if (event->state & GDK_SHIFT_MASK) - return gtk_container_focus (GTK_CONTAINER (widget), - GTK_DIR_TAB_BACKWARD); - else - return gtk_container_focus (GTK_CONTAINER (widget), - GTK_DIR_TAB_FORWARD); - default: - break; - } - return FALSE; -} - -static gint gtk_clist_button_press (GtkWidget *widget, GdkEventButton *event) { @@ -6583,10 +6554,15 @@ g_return_val_if_fail (container != NULL, FALSE); g_return_val_if_fail (GTK_IS_CLIST (container), FALSE); + clist = GTK_CLIST (container); + if (!GTK_WIDGET_IS_SENSITIVE (container)) return FALSE; - clist = GTK_CLIST (container); + if ((GTK_WIDGET_HAS_FOCUS (container) && + GTK_CLIST_CHILD_HAS_FOCUS (clist))) + return FALSE; + focus_child = container->focus_child; old_row = clist->focus_row; Index: gtk/gtkcontainer.c =================================================================== RCS file: /cvs/gnome/gtk+/gtk/gtkcontainer.c,v retrieving revision 1.60.2.8 diff -u -r1.60.2.8 gtkcontainer.c --- gtk/gtkcontainer.c 1999/09/26 23:46:57 1.60.2.8 +++ gtk/gtkcontainer.c 2001/04/26 01:38:44 @@ -82,13 +82,13 @@ static void gtk_container_real_set_focus_child (GtkContainer *container, GtkWidget *widget); static gint gtk_container_focus_tab (GtkContainer *container, - GList *children, + GList **children, GtkDirectionType direction); static gint gtk_container_focus_up_down (GtkContainer *container, - GList *children, + GList **children, GtkDirectionType direction); static gint gtk_container_focus_left_right (GtkContainer *container, - GList *children, + GList **children, GtkDirectionType direction); static gint gtk_container_focus_move (GtkContainer *container, GList *children, @@ -1191,6 +1191,8 @@ g_return_val_if_fail (container != NULL, FALSE); g_return_val_if_fail (GTK_IS_CONTAINER (container), FALSE); + + return_val = FALSE; gtk_signal_emit (GTK_OBJECT (container), container_signals[FOCUS], @@ -1397,6 +1399,9 @@ !GTK_WIDGET_IS_SENSITIVE (container)) return FALSE; + if (GTK_WIDGET_HAS_FOCUS (container)) + return FALSE; + return_val = FALSE; if (GTK_WIDGET_CAN_FOCUS (container)) @@ -1440,15 +1445,15 @@ { case GTK_DIR_TAB_FORWARD: case GTK_DIR_TAB_BACKWARD: - return_val = gtk_container_focus_tab (container, children, direction); + 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); + 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); + return_val = gtk_container_focus_left_right (container, &children, direction); break; } @@ -1461,7 +1466,7 @@ static gint gtk_container_focus_tab (GtkContainer *container, - GList *children, + GList **children, GtkDirectionType direction) { GtkWidget *child; @@ -1470,13 +1475,13 @@ guint length; guint i, j; - length = g_list_length (children); + length = g_list_length (*children); /* sort the children in the y direction */ for (i = 1; i < length; i++) { j = i; - tmp_list = g_list_nth (children, j); + tmp_list = g_list_nth (*children, j); child = tmp_list->data; while (j > 0) @@ -1501,7 +1506,7 @@ for (i = 1; i < length; i++) { j = i; - tmp_list = g_list_nth (children, j); + tmp_list = g_list_nth (*children, j); child = tmp_list->data; while (j > 0) @@ -1525,14 +1530,14 @@ * of the children. */ if (direction == GTK_DIR_TAB_BACKWARD) - children = g_list_reverse (children); + *children = g_list_reverse (*children); - return gtk_container_focus_move (container, children, direction); + return gtk_container_focus_move (container, *children, direction); } static gint gtk_container_focus_up_down (GtkContainer *container, - GList *children, + GList **children, GtkDirectionType direction) { GtkWidget *child; @@ -1559,13 +1564,13 @@ focus_x = 0; } - length = g_list_length (children); + length = g_list_length (*children); /* sort the children in the y direction */ for (i = 1; i < length; i++) { j = i; - tmp_list = g_list_nth (children, j); + tmp_list = g_list_nth (*children, j); child = tmp_list->data; while (j > 0) @@ -1591,7 +1596,7 @@ for (i = 1; i < length; i++) { j = i; - tmp_list = g_list_nth (children, j); + tmp_list = g_list_nth (*children, j); child = tmp_list->data; dist1 = (child->allocation.x + child->allocation.width / 2) - focus_x; @@ -1621,7 +1626,7 @@ (direction == GTK_DIR_UP)) focus_x += focus_width; - tmp_list = children; + tmp_list = *children; while (tmp_list) { child = tmp_list->data; @@ -1629,20 +1634,28 @@ dist1 = (child->allocation.x + child->allocation.width / 2) - focus_x; if (((direction == GTK_DIR_DOWN) && (dist1 < 0)) || ((direction == GTK_DIR_UP) && (dist1 > 0))) - tmp_list->data = NULL; + { + GList *tmp_list2; + + tmp_list2 = tmp_list; + tmp_list = tmp_list->next; - tmp_list = tmp_list->next; + *children = g_list_remove_link (*children, tmp_list2); + g_list_free_1 (tmp_list2); + } + else + tmp_list = tmp_list->next; } if (direction == GTK_DIR_UP) - children = g_list_reverse (children); + *children = g_list_reverse (*children); - return gtk_container_focus_move (container, children, direction); + return gtk_container_focus_move (container, *children, direction); } static gint gtk_container_focus_left_right (GtkContainer *container, - GList *children, + GList **children, GtkDirectionType direction) { GtkWidget *child; @@ -1669,13 +1682,13 @@ focus_y = 0; } - length = g_list_length (children); + length = g_list_length (*children); /* sort the children in the x direction */ for (i = 1; i < length; i++) { j = i; - tmp_list = g_list_nth (children, j); + tmp_list = g_list_nth (*children, j); child = tmp_list->data; while (j > 0) @@ -1701,7 +1714,7 @@ for (i = 1; i < length; i++) { j = i; - tmp_list = g_list_nth (children, j); + tmp_list = g_list_nth (*children, j); child = tmp_list->data; dist1 = (child->allocation.y + child->allocation.height / 2) - focus_y; @@ -1731,7 +1744,7 @@ (direction == GTK_DIR_LEFT)) focus_y += focus_height; - tmp_list = children; + tmp_list = *children; while (tmp_list) { child = tmp_list->data; @@ -1739,15 +1752,23 @@ dist1 = (child->allocation.y + child->allocation.height / 2) - focus_y; if (((direction == GTK_DIR_RIGHT) && (dist1 < 0)) || ((direction == GTK_DIR_LEFT) && (dist1 > 0))) - tmp_list->data = NULL; + { + GList *tmp_list2; + + tmp_list2 = tmp_list; + tmp_list = tmp_list->next; - tmp_list = tmp_list->next; + *children = g_list_remove_link (*children, tmp_list2); + g_list_free_1 (tmp_list2); + } + else + tmp_list = tmp_list->next; } if (direction == GTK_DIR_LEFT) - children = g_list_reverse (children); + *children = g_list_reverse (*children); - return gtk_container_focus_move (container, children, direction); + return gtk_container_focus_move (container, *children, direction); } static gint @@ -1766,18 +1787,13 @@ child = children->data; children = children->next; - if (!child) - continue; - if (focus_child) { if (focus_child == child) { focus_child = NULL; - if (GTK_WIDGET_DRAWABLE (child) && - GTK_IS_CONTAINER (child) && - !GTK_WIDGET_HAS_FOCUS (child)) + if (GTK_WIDGET_DRAWABLE (child) && GTK_IS_CONTAINER (child)) if (gtk_container_focus (GTK_CONTAINER (child), direction)) return TRUE; } Index: gtk/gtkentry.c =================================================================== RCS file: /cvs/gnome/gtk+/gtk/gtkentry.c,v retrieving revision 1.75.2.10 diff -u -r1.75.2.10 gtkentry.c --- gtk/gtkentry.c 2001/04/02 03:50:58 1.75.2.10 +++ gtk/gtkentry.c 2001/04/26 01:39:02 @@ -1184,8 +1188,7 @@ } break; case GDK_Return: - return_val = TRUE; - gtk_widget_activate (widget); + return_val = gtk_widget_activate (widget); break; /* The next two keys should not be inserted literally. Any others ??? */ case GDK_Tab: Index: gtk/gtkhbbox.c =================================================================== RCS file: /cvs/gnome/gtk+/gtk/gtkhbbox.c,v retrieving revision 1.9 diff -u -r1.9 gtkhbbox.c --- gtk/gtkhbbox.c 1999/02/24 07:34:45 1.9 +++ gtk/gtkhbbox.c 2001/04/26 01:39:03 @@ -24,15 +24,19 @@ * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ +#include #include "gtkhbbox.h" +#include "gtkwindow.h" static void gtk_hbutton_box_class_init (GtkHButtonBoxClass *klass); static void gtk_hbutton_box_init (GtkHButtonBox *box); -static void gtk_hbutton_box_size_request (GtkWidget *widget, - GtkRequisition *requisition); -static void gtk_hbutton_box_size_allocate (GtkWidget *widget, - GtkAllocation *allocation); +static void gtk_hbutton_box_size_request (GtkWidget *widget, + GtkRequisition *requisition); +static void gtk_hbutton_box_size_allocate (GtkWidget *widget, + GtkAllocation *allocation); +static gint gtk_hbutton_box_focus (GtkContainer *container, + GtkDirectionType direction); static gint default_spacing = 30; static gint default_layout_style = GTK_BUTTONBOX_EDGE; @@ -66,11 +70,15 @@ gtk_hbutton_box_class_init (GtkHButtonBoxClass *class) { GtkWidgetClass *widget_class; + GtkContainerClass *container_class; - widget_class = (GtkWidgetClass*) class; + widget_class = (GtkWidgetClass *) class; + container_class = (GtkContainerClass *) class; widget_class->size_request = gtk_hbutton_box_size_request; widget_class->size_allocate = gtk_hbutton_box_size_allocate; + + container_class->focus = gtk_hbutton_box_focus; } static void @@ -280,3 +288,283 @@ } } +/* Utility function to give focus to a widget, or to do gtk_container_focus() if + * it is a container. + */ +static gint +grab_focus (GtkHButtonBox *hbbox, GtkWidget *widget, GtkDirectionType direction) +{ + if (!widget) + return FALSE; + + if (!(GTK_WIDGET_DRAWABLE (widget) && GTK_WIDGET_IS_SENSITIVE (widget))) + return FALSE; + + if (GTK_IS_CONTAINER (widget)) + return gtk_container_focus (GTK_CONTAINER (widget), direction); + else if (GTK_WIDGET_CAN_FOCUS (widget)) + { + gtk_widget_grab_focus (widget); + return TRUE; + } + else + return FALSE; +} + +/* Starts trying to focus the box's widgets in sequence */ +static gint +try_focus (GtkHButtonBox *hbbox, GList *child, GtkDirectionType direction, + gboolean forwards, GtkPackType pack) +{ + while (child) + { + GtkBoxChild *box_child; + GtkWidget *widget; + + box_child = child->data; + widget = box_child->widget; + + if (forwards) + child = child->next; + else + child = child->prev; + + if (box_child->pack != pack) + continue; + + if (grab_focus (hbbox, widget, direction)) + return TRUE; + } + + return FALSE; +} + +/* Returns the node that contains the specified child widget */ +static GList * +find_child (GtkHButtonBox *hbbox, GtkWidget *widget) +{ + GList *children; + + for (children = GTK_BOX (hbbox)->children; children; children = children->next) + { + GtkBoxChild *child; + + child = children->data; + if (child->widget == widget) + return children; + } + + return NULL; +} + +/* Changes the focus in Tab or left/right directions */ +static gint +focus_along_children (GtkHButtonBox *hbbox, GtkDirectionType direction) +{ + GtkContainer *container; + GtkBox *box; + + container = GTK_CONTAINER (hbbox); + box = GTK_BOX (hbbox); + + if (container->focus_child) + { + GtkWidget *widget; + GList *child; + GtkBoxChild *box_child; + + widget = container->focus_child; + gtk_container_set_focus_child (container, NULL); + + g_assert (GTK_WIDGET_DRAWABLE (widget) && GTK_WIDGET_IS_SENSITIVE (widget)); + + child = find_child (hbbox, widget); + g_assert (child != NULL); + + box_child = child->data; + + if (direction == GTK_DIR_TAB_FORWARD || direction == GTK_DIR_RIGHT) + { + if (box_child->pack == GTK_PACK_START) + return (try_focus (hbbox, GTK_IS_CONTAINER (widget) ? child : child->next, + direction, TRUE, GTK_PACK_START) + || try_focus (hbbox, g_list_last (child), direction, FALSE, GTK_PACK_END)); + else if (box_child->pack == GTK_PACK_END) + return try_focus (hbbox, GTK_IS_CONTAINER (widget) ? child : child->prev, + direction, FALSE, GTK_PACK_END); + else + g_assert_not_reached (); + } + else if (direction == GTK_DIR_TAB_BACKWARD || direction == GTK_DIR_LEFT) + { + if (box_child->pack == GTK_PACK_START) + return try_focus (hbbox, GTK_IS_CONTAINER (widget) ? child : child->prev, + direction, FALSE, GTK_PACK_START); + else if (box_child->pack == GTK_PACK_END) + return (try_focus (hbbox, GTK_IS_CONTAINER (widget) ? child : child->next, + direction, TRUE, GTK_PACK_END) + || try_focus (hbbox, g_list_last (child), direction, FALSE, GTK_PACK_START)); + } + else + g_assert_not_reached (); + } + else + { + if (direction == GTK_DIR_TAB_FORWARD || direction == GTK_DIR_RIGHT) + return (try_focus (hbbox, box->children, direction, TRUE, GTK_PACK_START) + || try_focus (hbbox, g_list_last (box->children), direction, FALSE, GTK_PACK_END)); + else if (direction == GTK_DIR_TAB_BACKWARD || direction == GTK_DIR_LEFT) + return (try_focus (hbbox, box->children, direction, TRUE, GTK_PACK_END) + || try_focus (hbbox, g_list_last (box->children), direction, FALSE, GTK_PACK_START)); + else + { + g_assert_not_reached (); + return FALSE; + } + } + + return FALSE; +} + +/* Finds a focusable child that is the most overlapping to the specified coordinates */ +static GtkWidget * +find_most_overlapping_focusable_child (GtkHButtonBox *hbbox, int x1, int x2) +{ + GList *children; + GtkWidget *overlap; + gint bx1; + int max_overlap; + + overlap = NULL; + max_overlap = 0; + + gdk_window_get_origin (GTK_WIDGET (hbbox)->window, &bx1, NULL); + + for (children = GTK_BOX (hbbox)->children; children; children = children->next) + { + GtkBoxChild *box_child; + GtkWidget *widget; + int wx1, wx2; + int o; + + box_child = children->data; + widget = box_child->widget; + + if (!(GTK_WIDGET_DRAWABLE (widget) + && GTK_WIDGET_IS_SENSITIVE (widget) + && (GTK_IS_CONTAINER (widget) || GTK_WIDGET_CAN_FOCUS (widget)))) + continue; + + wx1 = bx1 + widget->allocation.x; + wx2 = wx1 + widget->allocation.width; + + if (x1 >= wx2 || x2 <= wx1) + continue; + + o = MIN (x2, wx2) - MAX (x1, wx1); + + g_assert (o >= 0); + + if (!overlap || o > max_overlap) + { + overlap = widget; + max_overlap = o; + } + } + + return overlap; +} + +/* Changes the focus in up/down directions */ +static gint +focus_outside (GtkHButtonBox *hbbox, GtkDirectionType direction) +{ + GtkContainer *container; + GtkWidget *toplevel; + + container = GTK_CONTAINER (hbbox); + + if (container->focus_child) + { + if (GTK_IS_CONTAINER (container->focus_child)) + { + GtkWidget *focus_child; + + focus_child = container->focus_child; + gtk_container_set_focus_child (container, NULL); + + if (gtk_container_focus (GTK_CONTAINER (focus_child), direction)) + return TRUE; + } + else + return FALSE; /* pass focus on to something outside the HButtonBox */ + } + + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (hbbox)); + + if (GTK_IS_WINDOW (toplevel)) + { + GtkWidget *focus_widget; + GtkWidget *widget; + + focus_widget = GTK_WINDOW (toplevel)->focus_widget; + + if (focus_widget) + { + int x1, x2; + gint fx; + + x1 = focus_widget->allocation.x; + x2 = focus_widget->allocation.x + focus_widget->allocation.width; + + gdk_window_get_origin (focus_widget->parent->window, &fx, NULL); + + x1 += fx; + x2 += fx; + + widget = find_most_overlapping_focusable_child (hbbox, x1, x2); + } + else + { + gint tx; + + gdk_window_get_origin (toplevel->window, &tx, NULL); + widget = find_most_overlapping_focusable_child (hbbox, + tx, + tx + toplevel->allocation.width); + } + + return grab_focus (hbbox, widget, direction); + } + else + return FALSE; +} + +/* Focus handler for horizontal boxes */ +static gint +gtk_hbutton_box_focus (GtkContainer *container, GtkDirectionType direction) +{ + GtkHButtonBox *hbbox; + + g_return_val_if_fail (container != NULL, FALSE); + g_return_val_if_fail (GTK_IS_HBUTTON_BOX (container), FALSE); + + hbbox = GTK_HBUTTON_BOX (container); + + switch (direction) + { + case GTK_DIR_TAB_FORWARD: + case GTK_DIR_RIGHT: + case GTK_DIR_TAB_BACKWARD: + case GTK_DIR_LEFT: + return focus_along_children (hbbox, direction); + + case GTK_DIR_UP: + case GTK_DIR_DOWN: + return focus_outside (hbbox, direction); + + default: + g_assert_not_reached (); + return FALSE; + } +} Index: gtk/gtkhbox.c =================================================================== RCS file: /cvs/gnome/gtk+/gtk/gtkhbox.c,v retrieving revision 1.14 diff -u -r1.14 gtkhbox.c --- gtk/gtkhbox.c 1999/02/24 07:34:47 1.14 +++ gtk/gtkhbox.c 2001/04/26 01:39:05 @@ -21,18 +21,22 @@ * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS * file for a list of people on the GTK+ Team. See the ChangeLog * files for a list of changes. These files are distributed with - * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ +#include #include "gtkhbox.h" +#include "gtkwindow.h" -static void gtk_hbox_class_init (GtkHBoxClass *klass); -static void gtk_hbox_init (GtkHBox *box); -static void gtk_hbox_size_request (GtkWidget *widget, - GtkRequisition *requisition); -static void gtk_hbox_size_allocate (GtkWidget *widget, - GtkAllocation *allocation); +static void gtk_hbox_class_init (GtkHBoxClass *klass); +static void gtk_hbox_init (GtkHBox *box); +static void gtk_hbox_size_request (GtkWidget *widget, + GtkRequisition *requisition); +static void gtk_hbox_size_allocate (GtkWidget *widget, + GtkAllocation *allocation); +static gint gtk_hbox_focus (GtkContainer *container, + GtkDirectionType direction); GtkType @@ -64,11 +68,15 @@ gtk_hbox_class_init (GtkHBoxClass *class) { GtkWidgetClass *widget_class; + GtkContainerClass *container_class; - widget_class = (GtkWidgetClass*) class; + widget_class = (GtkWidgetClass *) class; + container_class = (GtkContainerClass *) class; widget_class->size_request = gtk_hbox_size_request; widget_class->size_allocate = gtk_hbox_size_allocate; + + container_class->focus = gtk_hbox_focus; } static void @@ -325,5 +333,284 @@ x -= (child_width + box->spacing); } } + } +} + +/* Utility function to give focus to a widget, or to do gtk_container_focus() if + * it is a container. + */ +static gint +grab_focus (GtkHBox *hbox, GtkWidget *widget, GtkDirectionType direction) +{ + if (!widget) + return FALSE; + + if (!(GTK_WIDGET_DRAWABLE (widget) && GTK_WIDGET_IS_SENSITIVE (widget))) + return FALSE; + + if (GTK_IS_CONTAINER (widget)) + return gtk_container_focus (GTK_CONTAINER (widget), direction); + else if (GTK_WIDGET_CAN_FOCUS (widget)) + { + gtk_widget_grab_focus (widget); + return TRUE; + } + else + return FALSE; +} + +/* Starts trying to focus the box's widgets in sequence */ +static gint +try_focus (GtkHBox *hbox, GList *child, GtkDirectionType direction, + gboolean forwards, GtkPackType pack) +{ + while (child) + { + GtkBoxChild *box_child; + GtkWidget *widget; + + box_child = child->data; + widget = box_child->widget; + + if (forwards) + child = child->next; + else + child = child->prev; + + if (box_child->pack != pack) + continue; + + if (grab_focus (hbox, widget, direction)) + return TRUE; + } + + return FALSE; +} + +/* Returns the node that contains the specified child widget */ +static GList * +find_child (GtkHBox *hbox, GtkWidget *widget) +{ + GList *children; + + for (children = GTK_BOX (hbox)->children; children; children = children->next) + { + GtkBoxChild *child; + + child = children->data; + if (child->widget == widget) + return children; + } + + return NULL; +} + +/* Changes the focus in Tab or left/right directions */ +static gint +focus_along_children (GtkHBox *hbox, GtkDirectionType direction) +{ + GtkContainer *container; + GtkBox *box; + + container = GTK_CONTAINER (hbox); + box = GTK_BOX (hbox); + + if (container->focus_child) + { + GtkWidget *widget; + GList *child; + GtkBoxChild *box_child; + + widget = container->focus_child; + gtk_container_set_focus_child (container, NULL); + + g_assert (GTK_WIDGET_DRAWABLE (widget) && GTK_WIDGET_IS_SENSITIVE (widget)); + + child = find_child (hbox, widget); + g_assert (child != NULL); + + box_child = child->data; + + if (direction == GTK_DIR_TAB_FORWARD || direction == GTK_DIR_RIGHT) + { + if (box_child->pack == GTK_PACK_START) + return (try_focus (hbox, GTK_IS_CONTAINER (widget) ? child : child->next, + direction, TRUE, GTK_PACK_START) + || try_focus (hbox, g_list_last (child), direction, FALSE, GTK_PACK_END)); + else if (box_child->pack == GTK_PACK_END) + return try_focus (hbox, GTK_IS_CONTAINER (widget) ? child : child->prev, + direction, FALSE, GTK_PACK_END); + else + g_assert_not_reached (); + } + else if (direction == GTK_DIR_TAB_BACKWARD || direction == GTK_DIR_LEFT) + { + if (box_child->pack == GTK_PACK_START) + return try_focus (hbox, GTK_IS_CONTAINER (widget) ? child : child->prev, + direction, FALSE, GTK_PACK_START); + else if (box_child->pack == GTK_PACK_END) + return (try_focus (hbox, GTK_IS_CONTAINER (widget) ? child : child->next, + direction, TRUE, GTK_PACK_END) + || try_focus (hbox, g_list_last (child), direction, FALSE, GTK_PACK_START)); + } + else + g_assert_not_reached (); + } + else + { + if (direction == GTK_DIR_TAB_FORWARD || direction == GTK_DIR_RIGHT) + return (try_focus (hbox, box->children, direction, TRUE, GTK_PACK_START) + || try_focus (hbox, g_list_last (box->children), direction, FALSE, GTK_PACK_END)); + else if (direction == GTK_DIR_TAB_BACKWARD || direction == GTK_DIR_LEFT) + return (try_focus (hbox, box->children, direction, TRUE, GTK_PACK_END) + || try_focus (hbox, g_list_last (box->children), direction, FALSE, GTK_PACK_START)); + else + { + g_assert_not_reached (); + return FALSE; + } + } + + return FALSE; +} + +/* Finds a focusable child that is the most overlapping to the specified coordinates */ +static GtkWidget * +find_most_overlapping_focusable_child (GtkHBox *hbox, int x1, int x2) +{ + GList *children; + GtkWidget *overlap; + gint bx1; + int max_overlap; + + overlap = NULL; + max_overlap = 0; + + gdk_window_get_origin (GTK_WIDGET (hbox)->window, &bx1, NULL); + + for (children = GTK_BOX (hbox)->children; children; children = children->next) + { + GtkBoxChild *box_child; + GtkWidget *widget; + int wx1, wx2; + int o; + + box_child = children->data; + widget = box_child->widget; + + if (!(GTK_WIDGET_DRAWABLE (widget) + && GTK_WIDGET_IS_SENSITIVE (widget) + && (GTK_IS_CONTAINER (widget) || GTK_WIDGET_CAN_FOCUS (widget)))) + continue; + + wx1 = bx1 + widget->allocation.x; + wx2 = wx1 + widget->allocation.width; + + if (x1 >= wx2 || x2 <= wx1) + continue; + + o = MIN (x2, wx2) - MAX (x1, wx1); + + g_assert (o >= 0); + + if (!overlap || o > max_overlap) + { + overlap = widget; + max_overlap = o; + } + } + + return overlap; +} + +/* Changes the focus in up/down directions */ +static gint +focus_outside (GtkHBox *hbox, GtkDirectionType direction) +{ + GtkContainer *container; + GtkWidget *toplevel; + + container = GTK_CONTAINER (hbox); + + if (container->focus_child) + { + if (GTK_IS_CONTAINER (container->focus_child)) + { + GtkWidget *focus_child; + + focus_child = container->focus_child; + gtk_container_set_focus_child (container, NULL); + + if (gtk_container_focus (GTK_CONTAINER (focus_child), direction)) + return TRUE; + } + else + return FALSE; /* pass focus on to something outside the HBox */ + } + + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (hbox)); + + if (GTK_IS_WINDOW (toplevel)) + { + GtkWidget *focus_widget; + GtkWidget *widget; + + focus_widget = GTK_WINDOW (toplevel)->focus_widget; + + if (focus_widget) + { + int x1, x2; + gint fx; + + x1 = focus_widget->allocation.x; + x2 = focus_widget->allocation.x + focus_widget->allocation.width; + + gdk_window_get_origin (focus_widget->parent->window, &fx, NULL); + + x1 += fx; + x2 += fx; + + widget = find_most_overlapping_focusable_child (hbox, x1, x2); + } + else + { + gint tx; + + gdk_window_get_origin (toplevel->window, &tx, NULL); + widget = find_most_overlapping_focusable_child (hbox, tx, tx + toplevel->allocation.width); + } + + return grab_focus (hbox, widget, direction); + } + else + return FALSE; +} + +/* Focus handler for horizontal boxes */ +static gint +gtk_hbox_focus (GtkContainer *container, GtkDirectionType direction) +{ + GtkHBox *hbox; + + g_return_val_if_fail (container != NULL, FALSE); + g_return_val_if_fail (GTK_IS_HBOX (container), FALSE); + + hbox = GTK_HBOX (container); + + switch (direction) + { + case GTK_DIR_TAB_FORWARD: + case GTK_DIR_RIGHT: + case GTK_DIR_TAB_BACKWARD: + case GTK_DIR_LEFT: + return focus_along_children (hbox, direction); + + case GTK_DIR_UP: + case GTK_DIR_DOWN: + return focus_outside (hbox, direction); + + default: + g_assert_not_reached (); + return FALSE; } } Index: gtk/gtknotebook.c =================================================================== RCS file: /cvs/gnome/gtk+/gtk/gtknotebook.c,v retrieving revision 1.65.2.10 diff -u -r1.65.2.10 gtknotebook.c --- gtk/gtknotebook.c 2001/01/30 23:27:58 1.65.2.10 +++ gtk/gtknotebook.c 2001/04/26 01:39:14 @@ -196,7 +196,8 @@ static void gtk_notebook_switch_page (GtkNotebook *notebook, GtkNotebookPage *page, gint page_num); -static gint gtk_notebook_page_select (GtkNotebook *notebook); +static gboolean gtk_notebook_page_select (GtkNotebook *notebook, + gboolean unset_focus_child); static void gtk_notebook_switch_focus_tab (GtkNotebook *notebook, GList *new_child); static void gtk_notebook_menu_switch_page (GtkWidget *widget, @@ -1049,7 +1050,7 @@ } } else if (event->button == 2) - gtk_notebook_page_select (GTK_NOTEBOOK (widget)); + gtk_notebook_page_select (GTK_NOTEBOOK (widget), FALSE); else if (event->button == 3) gtk_notebook_switch_focus_tab (notebook, gtk_notebook_search_page (notebook, @@ -1077,7 +1078,7 @@ } } else if (event->button == 2) - gtk_notebook_page_select (GTK_NOTEBOOK (widget)); + gtk_notebook_page_select (GTK_NOTEBOOK (widget), FALSE); else if (event->button == 3) gtk_notebook_switch_focus_tab (notebook, gtk_notebook_search_page (notebook, NULL, @@ -1285,7 +1286,6 @@ GdkEventKey *event) { GtkNotebook *notebook; - GtkDirectionType direction = 0; GList *list; g_return_val_if_fail (widget != NULL, FALSE); @@ -1299,65 +1299,6 @@ switch (event->keyval) { - case GDK_Up: - switch (notebook->tab_pos) - { - case GTK_POS_BOTTOM: - gtk_notebook_page_select (GTK_NOTEBOOK (widget)); - return TRUE; - case GTK_POS_TOP: - return FALSE; - default: - direction = GTK_DIR_UP; - break; - } - break; - case GDK_Left: - switch (notebook->tab_pos) - { - case GTK_POS_RIGHT: - gtk_notebook_page_select (GTK_NOTEBOOK (widget)); - return TRUE; - case GTK_POS_LEFT: - return FALSE; - default: - direction = GTK_DIR_LEFT; - break; - } - break; - case GDK_Down: - switch (notebook->tab_pos) - { - case GTK_POS_TOP: - gtk_notebook_page_select (GTK_NOTEBOOK (widget)); - return TRUE; - case GTK_POS_BOTTOM: - return FALSE; - default: - direction = GTK_DIR_DOWN; - break; - } - break; - case GDK_Right: - switch (notebook->tab_pos) - { - case GTK_POS_LEFT: - gtk_notebook_page_select (GTK_NOTEBOOK (widget)); - return TRUE; - case GTK_POS_RIGHT: - return FALSE; - default: - direction = GTK_DIR_RIGHT; - break; - } - break; - case GDK_Tab: - case GDK_ISO_Left_Tab: - if (event->state & GDK_SHIFT_MASK) - direction = GTK_DIR_TAB_BACKWARD; - else - direction = GTK_DIR_TAB_FORWARD; - break; case GDK_Home: list = gtk_notebook_search_page (notebook, NULL, STEP_NEXT, TRUE); if (list) @@ -1370,12 +1311,12 @@ return TRUE; case GDK_Return: case GDK_space: - gtk_notebook_page_select (GTK_NOTEBOOK (widget)); + gtk_notebook_page_select (GTK_NOTEBOOK (widget), FALSE); return TRUE; default: - return FALSE; + break; } - return gtk_container_focus (GTK_CONTAINER (widget), direction); + return FALSE; } static gint @@ -1623,21 +1564,55 @@ gtk_notebook_focus (GtkContainer *container, GtkDirectionType direction) { + GtkWidget *old_focus_child; GtkNotebook *notebook; GtkNotebookPage *page = NULL; GtkNotebookPage *old_page = NULL; - + g_return_val_if_fail (container != NULL, FALSE); g_return_val_if_fail (GTK_IS_NOTEBOOK (container), FALSE); notebook = GTK_NOTEBOOK (container); + + old_focus_child = container->focus_child; if (!GTK_WIDGET_DRAWABLE (notebook) || !GTK_WIDGET_IS_SENSITIVE (container) || !notebook->children || !notebook->cur_page) return FALSE; - + + if (GTK_WIDGET_HAS_FOCUS (container)) + switch (direction) + { + case GTK_DIR_UP: + if (notebook->tab_pos == GTK_POS_BOTTOM) + return gtk_notebook_page_select (notebook, TRUE); + else if (notebook->tab_pos == GTK_POS_TOP) + return FALSE; + break; + case GTK_DIR_LEFT: + if (notebook->tab_pos == GTK_POS_RIGHT) + return gtk_notebook_page_select (notebook, TRUE); + else if (notebook->tab_pos == GTK_POS_LEFT) + return FALSE; + break; + case GTK_DIR_DOWN: + if (notebook->tab_pos == GTK_POS_TOP) + return gtk_notebook_page_select (notebook, TRUE); + else if (notebook->tab_pos == GTK_POS_BOTTOM) + return FALSE; + break; + case GTK_DIR_RIGHT: + if (notebook->tab_pos == GTK_POS_LEFT) + return gtk_notebook_page_select (notebook, TRUE); + else if (notebook->tab_pos == GTK_POS_RIGHT) + return FALSE; + break; + default: + break; + } + if (!notebook->show_tabs) { if (GTK_WIDGET_DRAWABLE (notebook->cur_page->child) && @@ -1645,13 +1620,13 @@ { if (GTK_IS_CONTAINER (notebook->cur_page->child)) { - if (gtk_container_focus - (GTK_CONTAINER (notebook->cur_page->child), direction)) + gtk_container_set_focus_child (container, NULL); + if (gtk_container_focus (GTK_CONTAINER (notebook->cur_page->child), direction)) return TRUE; } else if (GTK_WIDGET_CAN_FOCUS (notebook->cur_page->child)) { - if (!container->focus_child) + if (!old_focus_child) { gtk_widget_grab_focus (notebook->cur_page->child); return TRUE; @@ -1659,20 +1634,20 @@ } } return FALSE; - } + } if (notebook->focus_tab) old_page = notebook->focus_tab->data; - if (container->focus_child && old_page && - container->focus_child == old_page->child && notebook->child_has_focus) + if (old_focus_child && old_page && + old_focus_child == old_page->child && notebook->child_has_focus) { - if (GTK_WIDGET_DRAWABLE (container->focus_child)) + if (GTK_WIDGET_DRAWABLE (old_focus_child)) { - if (GTK_IS_CONTAINER (container->focus_child) && - !GTK_WIDGET_HAS_FOCUS (container->focus_child)) + if (GTK_IS_CONTAINER (old_focus_child)) { - if (gtk_container_focus (GTK_CONTAINER (container->focus_child), + gtk_container_set_focus_child (container, NULL); + if (gtk_container_focus (GTK_CONTAINER (old_focus_child), direction)) return TRUE; } @@ -1706,22 +1681,26 @@ if (notebook->focus_tab) { - if (!GTK_WIDGET_HAS_FOCUS (container)) - gtk_widget_grab_focus (GTK_WIDGET (container)); + gtk_widget_grab_focus (GTK_WIDGET (container)); page = notebook->focus_tab->data; if (GTK_WIDGET_MAPPED (page->tab_label)) - gtk_notebook_focus_changed (notebook, old_page); + { + gtk_notebook_focus_changed (notebook, old_page); + return TRUE; + } else { gtk_notebook_pages_allocate (notebook, &(GTK_WIDGET (notebook)->allocation)); gtk_notebook_expose_tabs (notebook); } + return TRUE; } gtk_notebook_focus_changed (notebook, old_page); + return FALSE; } @@ -3337,8 +3316,9 @@ page_num); } -static gint -gtk_notebook_page_select (GtkNotebook *notebook) +static gboolean +gtk_notebook_page_select (GtkNotebook *notebook, + gboolean reset_focus_child) { GtkNotebookPage *page; @@ -3355,6 +3335,11 @@ { if (GTK_IS_CONTAINER (page->child)) { + if (reset_focus_child) + { + /* maintain ::focus movement propagation invariants here */ + gtk_container_set_focus_child (GTK_CONTAINER (notebook), NULL); + } if (gtk_container_focus (GTK_CONTAINER (page->child), GTK_DIR_TAB_FORWARD)) return TRUE; Index: gtk/gtkplug.c =================================================================== RCS file: /cvs/gnome/gtk+/gtk/gtkplug.c,v retrieving revision 1.6.2.7 diff -u -r1.6.2.7 gtkplug.c --- gtk/gtkplug.c 2001/03/15 05:45:28 1.6.2.7 +++ gtk/gtkplug.c 2001/04/26 01:39:15 @@ -229,8 +229,12 @@ } return_val = FALSE; - if (window->focus_widget) - return_val = gtk_widget_event (window->focus_widget, (GdkEvent*) event); + if (window->focus_widget && + window->focus_widget != widget && + GTK_WIDGET_IS_SENSITIVE (window->focus_widget)) + { + return_val = gtk_widget_event (window->focus_widget, (GdkEvent*) event); + } #if 0 if (!return_val && gtk_window_check_accelerator (window, event->keyval, event->state)) @@ -244,9 +248,10 @@ case GDK_space: if (window->focus_widget) { - gtk_widget_activate (window->focus_widget); - return_val = TRUE; + if (GTK_WIDGET_IS_SENSITIVE (window->focus_widget)) + return_val = gtk_widget_activate (window->focus_widget); } + break; case GDK_Return: case GDK_KP_Enter: @@ -254,13 +259,11 @@ (!window->focus_widget || !GTK_WIDGET_RECEIVES_DEFAULT (window->focus_widget))) { - gtk_widget_activate (window->default_widget); - return_val = TRUE; + return_val = gtk_widget_activate (window->default_widget); } else if (window->focus_widget) { - gtk_widget_activate (window->focus_widget); - return_val = TRUE; + return_val = gtk_widget_activate (window->focus_widget); } break; case GDK_Up: Index: gtk/gtkvbbox.c =================================================================== RCS file: /cvs/gnome/gtk+/gtk/gtkvbbox.c,v retrieving revision 1.10 diff -u -r1.10 gtkvbbox.c --- gtk/gtkvbbox.c 1999/02/24 07:36:53 1.10 +++ gtk/gtkvbbox.c 2001/04/26 01:39:19 @@ -24,15 +24,19 @@ * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ +#include #include "gtkvbbox.h" +#include "gtkwindow.h" static void gtk_vbutton_box_class_init (GtkVButtonBoxClass *klass); static void gtk_vbutton_box_init (GtkVButtonBox *box); -static void gtk_vbutton_box_size_request (GtkWidget *widget, - GtkRequisition *requisition); -static void gtk_vbutton_box_size_allocate (GtkWidget *widget, - GtkAllocation *allocation); +static void gtk_vbutton_box_size_request (GtkWidget *widget, + GtkRequisition *requisition); +static void gtk_vbutton_box_size_allocate (GtkWidget *widget, + GtkAllocation *allocation); +static gint gtk_vbutton_box_focus (GtkContainer *container, + GtkDirectionType direction); static gint default_spacing = 10; static GtkButtonBoxStyle default_layout_style = GTK_BUTTONBOX_EDGE; @@ -66,11 +70,15 @@ gtk_vbutton_box_class_init (GtkVButtonBoxClass *class) { GtkWidgetClass *widget_class; + GtkContainerClass *container_class; - widget_class = (GtkWidgetClass*) class; + widget_class = (GtkWidgetClass *) class; + container_class = (GtkContainerClass *) class; widget_class->size_request = gtk_vbutton_box_size_request; widget_class->size_allocate = gtk_vbutton_box_size_allocate; + + container_class->focus = gtk_vbutton_box_focus; } static void @@ -282,5 +290,284 @@ } } } - +/* Utility function to give focus to a widget, or to do gtk_container_focus() if + * it is a container. + */ +static gint +grab_focus (GtkVButtonBox *vbbox, GtkWidget *widget, GtkDirectionType direction) +{ + if (!widget) + return FALSE; + + if (!(GTK_WIDGET_DRAWABLE (widget) && GTK_WIDGET_IS_SENSITIVE (widget))) + return FALSE; + + if (GTK_IS_CONTAINER (widget)) + return gtk_container_focus (GTK_CONTAINER (widget), direction); + else if (GTK_WIDGET_CAN_FOCUS (widget)) + { + gtk_widget_grab_focus (widget); + return TRUE; + } + else + return FALSE; +} + +/* Starts trying to focus the box's widgets in sequence */ +static gint +try_focus (GtkVButtonBox *vbbox, GList *child, GtkDirectionType direction, + gboolean forwards, GtkPackType pack) +{ + while (child) + { + GtkBoxChild *box_child; + GtkWidget *widget; + + box_child = child->data; + widget = box_child->widget; + + if (forwards) + child = child->next; + else + child = child->prev; + + if (box_child->pack != pack) + continue; + + if (grab_focus (vbbox, widget, direction)) + return TRUE; + } + + return FALSE; +} + +/* Returns the node that contains the specified child widget */ +static GList * +find_child (GtkVButtonBox *vbbox, GtkWidget *widget) +{ + GList *children; + + for (children = GTK_BOX (vbbox)->children; children; children = children->next) + { + GtkBoxChild *child; + + child = children->data; + if (child->widget == widget) + return children; + } + + return NULL; +} + +/* Changes the focus in Tab or up/down directions */ +static gint +focus_along_children (GtkVButtonBox *vbbox, GtkDirectionType direction) +{ + GtkContainer *container; + GtkBox *box; + + container = GTK_CONTAINER (vbbox); + box = GTK_BOX (vbbox); + + if (container->focus_child) + { + GtkWidget *widget; + GList *child; + GtkBoxChild *box_child; + + widget = container->focus_child; + gtk_container_set_focus_child (container, NULL); + + g_assert (GTK_WIDGET_DRAWABLE (widget) && GTK_WIDGET_IS_SENSITIVE (widget)); + + child = find_child (vbbox, widget); + g_assert (child != NULL); + + box_child = child->data; + + if (direction == GTK_DIR_TAB_FORWARD || direction == GTK_DIR_DOWN) + { + if (box_child->pack == GTK_PACK_START) + return (try_focus (vbbox, GTK_IS_CONTAINER (widget) ? child : child->next, + direction, TRUE, GTK_PACK_START) + || try_focus (vbbox, g_list_last (child), direction, FALSE, GTK_PACK_END)); + else if (box_child->pack == GTK_PACK_END) + return try_focus (vbbox, GTK_IS_CONTAINER (widget) ? child : child->prev, + direction, FALSE, GTK_PACK_END); + else + g_assert_not_reached (); + } + else if (direction == GTK_DIR_TAB_BACKWARD || direction == GTK_DIR_UP) + { + if (box_child->pack == GTK_PACK_START) + return try_focus (vbbox, GTK_IS_CONTAINER (widget) ? child : child->prev, + direction, FALSE, GTK_PACK_START); + else if (box_child->pack == GTK_PACK_END) + return (try_focus (vbbox, GTK_IS_CONTAINER (widget) ? child : child->next, + direction, TRUE, GTK_PACK_END) + || try_focus (vbbox, g_list_last (child), direction, FALSE, GTK_PACK_START)); + } + else + g_assert_not_reached (); + } + else + { + if (direction == GTK_DIR_TAB_FORWARD || direction == GTK_DIR_DOWN) + return (try_focus (vbbox, box->children, direction, TRUE, GTK_PACK_START) + || try_focus (vbbox, g_list_last (box->children), direction, FALSE, GTK_PACK_END)); + else if (direction == GTK_DIR_TAB_BACKWARD || direction == GTK_DIR_UP) + return (try_focus (vbbox, box->children, direction, TRUE, GTK_PACK_END) + || try_focus (vbbox, g_list_last (box->children), direction, FALSE, GTK_PACK_START)); + else + { + g_assert_not_reached (); + return FALSE; + } + } + + return FALSE; +} + +/* Finds a focusable child that is the most overlapping to the specified coordinates */ +static GtkWidget * +find_most_overlapping_focusable_child (GtkVButtonBox *vbbox, int y1, int y2) +{ + GList *children; + GtkWidget *overlap; + gint by1; + int max_overlap; + + overlap = NULL; + max_overlap = 0; + + gdk_window_get_origin (GTK_WIDGET (vbbox)->window, NULL, &by1); + + for (children = GTK_BOX (vbbox)->children; children; children = children->next) + { + GtkBoxChild *box_child; + GtkWidget *widget; + int wy1, wy2; + int o; + + box_child = children->data; + widget = box_child->widget; + + if (!(GTK_WIDGET_DRAWABLE (widget) + && GTK_WIDGET_IS_SENSITIVE (widget) + && (GTK_IS_CONTAINER (widget) || GTK_WIDGET_CAN_FOCUS (widget)))) + continue; + + wy1 = by1 + widget->allocation.y; + wy2 = wy1 + widget->allocation.height; + + if (y1 >= wy2 || y2 <= wy1) + continue; + + o = MIN (y2, wy2) - MAX (y1, wy1); + + g_assert (o >= 0); + + if (!overlap || o > max_overlap) + { + overlap = widget; + max_overlap = o; + } + } + + return overlap; +} + +/* Changes the focus in left/right directions */ +static gint +focus_outside (GtkVButtonBox *vbbox, GtkDirectionType direction) +{ + GtkContainer *container; + GtkWidget *toplevel; + + container = GTK_CONTAINER (vbbox); + + if (container->focus_child) + { + if (GTK_IS_CONTAINER (container->focus_child)) + { + GtkWidget *focus_child; + + focus_child = container->focus_child; + gtk_container_set_focus_child (container, NULL); + + if (gtk_container_focus (GTK_CONTAINER (focus_child), direction)) + return TRUE; + } + else + return FALSE; /* pass focus on to something outside the VButtonBox */ + } + + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (vbbox)); + + if (GTK_IS_WINDOW (toplevel)) + { + GtkWidget *focus_widget; + GtkWidget *widget; + + focus_widget = GTK_WINDOW (toplevel)->focus_widget; + + if (focus_widget) + { + int y1, y2; + gint fy; + + y1 = focus_widget->allocation.y; + y2 = focus_widget->allocation.y + focus_widget->allocation.height; + + gdk_window_get_origin (focus_widget->parent->window, NULL, &fy); + + y1 += fy; + y2 += fy; + + widget = find_most_overlapping_focusable_child (vbbox, y1, y2); + } + else + { + gint ty; + + gdk_window_get_origin (toplevel->window, NULL, &ty); + widget = find_most_overlapping_focusable_child (vbbox, + ty, + ty + toplevel->allocation.height); + } + + return grab_focus (vbbox, widget, direction); + } + else + return FALSE; +} + +/* Focus handler for vertical boxes */ +static gint +gtk_vbutton_box_focus (GtkContainer *container, GtkDirectionType direction) +{ + GtkVButtonBox *vbbox; + + g_return_val_if_fail (container != NULL, FALSE); + g_return_val_if_fail (GTK_IS_VBUTTON_BOX (container), FALSE); + + vbbox = GTK_VBUTTON_BOX (container); + + switch (direction) + { + case GTK_DIR_TAB_FORWARD: + case GTK_DIR_DOWN: + case GTK_DIR_TAB_BACKWARD: + case GTK_DIR_UP: + return focus_along_children (vbbox, direction); + + case GTK_DIR_RIGHT: + case GTK_DIR_LEFT: + return focus_outside (vbbox, direction); + + default: + g_assert_not_reached (); + return FALSE; + } +} Index: gtk/gtkvbox.c =================================================================== RCS file: /cvs/gnome/gtk+/gtk/gtkvbox.c,v retrieving revision 1.14 diff -u -r1.14 gtkvbox.c --- gtk/gtkvbox.c 1999/02/24 07:36:55 1.14 +++ gtk/gtkvbox.c 2001/04/26 01:39:20 @@ -21,18 +21,22 @@ * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS * file for a list of people on the GTK+ Team. See the ChangeLog * files for a list of changes. These files are distributed with - * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ +#include #include "gtkvbox.h" +#include "gtkwindow.h" -static void gtk_vbox_class_init (GtkVBoxClass *klass); -static void gtk_vbox_init (GtkVBox *box); -static void gtk_vbox_size_request (GtkWidget *widget, - GtkRequisition *requisition); -static void gtk_vbox_size_allocate (GtkWidget *widget, - GtkAllocation *allocation); +static void gtk_vbox_class_init (GtkVBoxClass *klass); +static void gtk_vbox_init (GtkVBox *box); +static void gtk_vbox_size_request (GtkWidget *widget, + GtkRequisition *requisition); +static void gtk_vbox_size_allocate (GtkWidget *widget, + GtkAllocation *allocation); +static gint gtk_vbox_focus (GtkContainer *container, + GtkDirectionType direction); GtkType @@ -64,11 +68,15 @@ gtk_vbox_class_init (GtkVBoxClass *class) { GtkWidgetClass *widget_class; + GtkContainerClass *container_class; - widget_class = (GtkWidgetClass*) class; + widget_class = (GtkWidgetClass *) class; + container_class = (GtkContainerClass *) class; widget_class->size_request = gtk_vbox_size_request; widget_class->size_allocate = gtk_vbox_size_allocate; + + container_class->focus = gtk_vbox_focus; } static void @@ -323,5 +331,284 @@ y -= (child_height + box->spacing); } } + } +} + +/* Utility function to give focus to a widget, or to do gtk_container_focus() if + * it is a container. + */ +static gint +grab_focus (GtkVBox *vbox, GtkWidget *widget, GtkDirectionType direction) +{ + if (!widget) + return FALSE; + + if (!(GTK_WIDGET_DRAWABLE (widget) && GTK_WIDGET_IS_SENSITIVE (widget))) + return FALSE; + + if (GTK_IS_CONTAINER (widget)) + return gtk_container_focus (GTK_CONTAINER (widget), direction); + else if (GTK_WIDGET_CAN_FOCUS (widget)) + { + gtk_widget_grab_focus (widget); + return TRUE; + } + else + return FALSE; +} + +/* Starts trying to focus the box's widgets in sequence */ +static gint +try_focus (GtkVBox *vbox, GList *child, GtkDirectionType direction, + gboolean forwards, GtkPackType pack) +{ + while (child) + { + GtkBoxChild *box_child; + GtkWidget *widget; + + box_child = child->data; + widget = box_child->widget; + + if (forwards) + child = child->next; + else + child = child->prev; + + if (box_child->pack != pack) + continue; + + if (grab_focus (vbox, widget, direction)) + return TRUE; + } + + return FALSE; +} + +/* Returns the node that contains the specified child widget */ +static GList * +find_child (GtkVBox *vbox, GtkWidget *widget) +{ + GList *children; + + for (children = GTK_BOX (vbox)->children; children; children = children->next) + { + GtkBoxChild *child; + + child = children->data; + if (child->widget == widget) + return children; + } + + return NULL; +} + +/* Changes the focus in Tab or up/down directions */ +static gint +focus_along_children (GtkVBox *vbox, GtkDirectionType direction) +{ + GtkContainer *container; + GtkBox *box; + + container = GTK_CONTAINER (vbox); + box = GTK_BOX (vbox); + + if (container->focus_child) + { + GtkWidget *widget; + GList *child; + GtkBoxChild *box_child; + + widget = container->focus_child; + gtk_container_set_focus_child (container, NULL); + + g_assert (GTK_WIDGET_DRAWABLE (widget) && GTK_WIDGET_IS_SENSITIVE (widget)); + + child = find_child (vbox, widget); + g_assert (child != NULL); + + box_child = child->data; + + if (direction == GTK_DIR_TAB_FORWARD || direction == GTK_DIR_DOWN) + { + if (box_child->pack == GTK_PACK_START) + return (try_focus (vbox, GTK_IS_CONTAINER (widget) ? child : child->next, + direction, TRUE, GTK_PACK_START) + || try_focus (vbox, g_list_last (child), direction, FALSE, GTK_PACK_END)); + else if (box_child->pack == GTK_PACK_END) + return try_focus (vbox, GTK_IS_CONTAINER (widget) ? child : child->prev, + direction, FALSE, GTK_PACK_END); + else + g_assert_not_reached (); + } + else if (direction == GTK_DIR_TAB_BACKWARD || direction == GTK_DIR_UP) + { + if (box_child->pack == GTK_PACK_START) + return try_focus (vbox, GTK_IS_CONTAINER (widget) ? child : child->prev, + direction, FALSE, GTK_PACK_START); + else if (box_child->pack == GTK_PACK_END) + return (try_focus (vbox, GTK_IS_CONTAINER (widget) ? child : child->next, + direction, TRUE, GTK_PACK_END) + || try_focus (vbox, g_list_last (child), direction, FALSE, GTK_PACK_START)); + } + else + g_assert_not_reached (); + } + else + { + if (direction == GTK_DIR_TAB_FORWARD || direction == GTK_DIR_DOWN) + return (try_focus (vbox, box->children, direction, TRUE, GTK_PACK_START) + || try_focus (vbox, g_list_last (box->children), direction, FALSE, GTK_PACK_END)); + else if (direction == GTK_DIR_TAB_BACKWARD || direction == GTK_DIR_UP) + return (try_focus (vbox, box->children, direction, TRUE, GTK_PACK_END) + || try_focus (vbox, g_list_last (box->children), direction, FALSE, GTK_PACK_START)); + else + { + g_assert_not_reached (); + return FALSE; + } + } + + return FALSE; +} + +/* Finds a focusable child that is the most overlapping to the specified coordinates */ +static GtkWidget * +find_most_overlapping_focusable_child (GtkVBox *vbox, int y1, int y2) +{ + GList *children; + GtkWidget *overlap; + gint by1; + int max_overlap; + + overlap = NULL; + max_overlap = 0; + + gdk_window_get_origin (GTK_WIDGET (vbox)->window, NULL, &by1); + + for (children = GTK_BOX (vbox)->children; children; children = children->next) + { + GtkBoxChild *box_child; + GtkWidget *widget; + int wy1, wy2; + int o; + + box_child = children->data; + widget = box_child->widget; + + if (!(GTK_WIDGET_DRAWABLE (widget) + && GTK_WIDGET_IS_SENSITIVE (widget) + && (GTK_IS_CONTAINER (widget) || GTK_WIDGET_CAN_FOCUS (widget)))) + continue; + + wy1 = by1 + widget->allocation.y; + wy2 = wy1 + widget->allocation.height; + + if (y1 >= wy2 || y2 <= wy1) + continue; + + o = MIN (y2, wy2) - MAX (y1, wy1); + + g_assert (o >= 0); + + if (!overlap || o > max_overlap) + { + overlap = widget; + max_overlap = o; + } + } + + return overlap; +} + +/* Changes the focus in left/right directions */ +static gint +focus_outside (GtkVBox *vbox, GtkDirectionType direction) +{ + GtkContainer *container; + GtkWidget *toplevel; + + container = GTK_CONTAINER (vbox); + + if (container->focus_child) + { + if (GTK_IS_CONTAINER (container->focus_child)) + { + GtkWidget *focus_child; + + focus_child = container->focus_child; + gtk_container_set_focus_child (container, NULL); + + if (gtk_container_focus (GTK_CONTAINER (focus_child), direction)) + return TRUE; + } + else + return FALSE; /* pass focus on to something outside the VBox */ + } + + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (vbox)); + + if (GTK_IS_WINDOW (toplevel)) + { + GtkWidget *focus_widget; + GtkWidget *widget; + + focus_widget = GTK_WINDOW (toplevel)->focus_widget; + + if (focus_widget) + { + int y1, y2; + gint fy; + + y1 = focus_widget->allocation.y; + y2 = focus_widget->allocation.y + focus_widget->allocation.height; + + gdk_window_get_origin (focus_widget->parent->window, NULL, &fy); + + y1 += fy; + y2 += fy; + + widget = find_most_overlapping_focusable_child (vbox, y1, y2); + } + else + { + gint ty; + + gdk_window_get_origin (toplevel->window, NULL, &ty); + widget = find_most_overlapping_focusable_child (vbox, ty, ty + toplevel->allocation.height); + } + + return grab_focus (vbox, widget, direction); + } + else + return FALSE; +} + +/* Focus handler for vertical boxes */ +static gint +gtk_vbox_focus (GtkContainer *container, GtkDirectionType direction) +{ + GtkVBox *vbox; + + g_return_val_if_fail (container != NULL, FALSE); + g_return_val_if_fail (GTK_IS_VBOX (container), FALSE); + + vbox = GTK_VBOX (container); + + switch (direction) + { + case GTK_DIR_TAB_FORWARD: + case GTK_DIR_DOWN: + case GTK_DIR_TAB_BACKWARD: + case GTK_DIR_UP: + return focus_along_children (vbox, direction); + + case GTK_DIR_RIGHT: + case GTK_DIR_LEFT: + return focus_outside (vbox, direction); + + default: + g_assert_not_reached (); + return FALSE; } } Index: gtk/gtkwidget.c =================================================================== RCS file: /cvs/gnome/gtk+/gtk/gtkwidget.c,v retrieving revision 1.147.2.27 diff -u -r1.147.2.27 gtkwidget.c --- gtk/gtkwidget.c 2001/03/01 09:40:38 1.147.2.27 +++ gtk/gtkwidget.c 2001/04/26 01:39:28 @@ -3113,7 +3113,9 @@ { widget = GTK_WINDOW (toplevel)->focus_widget; - if (widget == focus_widget) + if (widget == focus_widget && + GTK_CONTAINER (toplevel)->focus_child && + GTK_CONTAINER (focus_widget->parent)->focus_child == focus_widget) { /* We call gtk_window_set_focus() here so that the * toplevel window can request the focus if necessary. Index: gtkwindow.c =================================================================== RCS file: /cvs/gnome/gtk+/gtk/gtkwindow.c,v retrieving revision 1.75.2.29 diff -u -r1.75.2.29 gtkwindow.c --- gtkwindow.c 2001/04/25 22:30:52 1.75.2.29 +++ gtkwindow.c 2001/05/07 18:27:32 @@ -117,6 +117,8 @@ static gint gtk_window_client_event (GtkWidget *widget, GdkEventClient *event); static void gtk_window_check_resize (GtkContainer *container); +static gint gtk_window_focus (GtkContainer *container, + GtkDirectionType direction); static void gtk_window_real_set_focus (GtkWindow *window, GtkWidget *focus); @@ -251,6 +253,7 @@ widget_class->expose_event = gtk_window_expose; container_class->check_resize = gtk_window_check_resize; + container_class->focus = gtk_window_focus; klass->set_focus = gtk_window_real_set_focus; } @@ -533,7 +536,8 @@ if (window->focus_widget) { if (GTK_WIDGET_IS_SENSITIVE (window->focus_widget)) - gtk_widget_activate (window->focus_widget); + return gtk_widget_activate (window->focus_widget); + return TRUE; } @@ -547,10 +551,7 @@ g_return_val_if_fail (GTK_IS_WINDOW (window), FALSE); if (window->default_widget && GTK_WIDGET_IS_SENSITIVE (window->default_widget)) - { - gtk_widget_activate (window->default_widget); - return TRUE; - } + return gtk_widget_activate (window->default_widget); return FALSE; } @@ -1270,8 +1271,7 @@ if (window->focus_widget) { if (GTK_WIDGET_IS_SENSITIVE (window->focus_widget)) - gtk_widget_activate (window->focus_widget); - handled = TRUE; + handled = gtk_widget_activate (window->focus_widget); } break; case GDK_Return: @@ -1279,14 +1279,12 @@ if (window->default_widget && GTK_WIDGET_IS_SENSITIVE (window->default_widget) && (!window->focus_widget || !GTK_WIDGET_RECEIVES_DEFAULT (window->focus_widget))) { - gtk_widget_activate (window->default_widget); - handled = TRUE; + handled = gtk_widget_activate (window->default_widget); } else if (window->focus_widget) { if (GTK_WIDGET_IS_SENSITIVE (window->focus_widget)) - gtk_widget_activate (window->focus_widget); - handled = TRUE; + handled = gtk_widget_activate (window->focus_widget); } break; case GDK_Up: @@ -1528,6 +1526,61 @@ if (GTK_WIDGET_VISIBLE (container)) gtk_window_move_resize (window); +} + +static gint +gtk_window_focus (GtkContainer *container, + GtkDirectionType direction) +{ + GtkBin *bin = GTK_BIN (container); + GtkWindow *window = GTK_WINDOW (container); + GtkWidget *old_focus_child = container->focus_child; + GtkWidget *parent; + + /* We need a special implementation here to deal properly with wrapping + * around in the tab chain without the danger of going into an + * infinite loop. + */ + if (old_focus_child) + { + if (GTK_IS_CONTAINER (old_focus_child) && + GTK_WIDGET_DRAWABLE (old_focus_child) && + GTK_WIDGET_IS_SENSITIVE (old_focus_child) && + gtk_container_focus (GTK_CONTAINER (old_focus_child), direction)) + return TRUE; + } + + if (window->focus_widget) + { + /* Wrapped off the end, clear the focus setting for the toplpevel */ + parent = window->focus_widget->parent; + while (parent) + { + gtk_container_set_focus_child (GTK_CONTAINER (parent), NULL); + parent = GTK_WIDGET (parent)->parent; + } + + gtk_window_set_focus (GTK_WINDOW (container), NULL); + } + + /* Now try to focus the first widget in the window */ + if (bin->child && + GTK_WIDGET_DRAWABLE (bin->child) && + GTK_WIDGET_IS_SENSITIVE (bin->child)) + { + if (GTK_IS_CONTAINER (bin->child)) + { + if (gtk_container_focus (GTK_CONTAINER (bin->child), direction)) + return TRUE; + } + else if (GTK_WIDGET_CAN_FOCUS (bin->child)) + { + gtk_widget_grab_focus (bin->child); + return TRUE; + } + } + + return FALSE; } static void