diff options
Diffstat (limited to 'gtk/gtkcontainer.c')
-rw-r--r-- | gtk/gtkcontainer.c | 844 |
1 files changed, 844 insertions, 0 deletions
diff --git a/gtk/gtkcontainer.c b/gtk/gtkcontainer.c new file mode 100644 index 000000000..977fabc29 --- /dev/null +++ b/gtk/gtkcontainer.c @@ -0,0 +1,844 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <string.h> +#include "gtkcontainer.h" +#include "gtksignal.h" + + +enum { + ADD, + REMOVE, + NEED_RESIZE, + FOREACH, + FOCUS, + LAST_SIGNAL +}; + + +typedef void (*GtkContainerSignal1) (GtkObject *object, + gpointer arg1, + gpointer data); +typedef void (*GtkContainerSignal2) (GtkObject *object, + gpointer arg1, + gpointer arg2, + gpointer data); +typedef gint (*GtkContainerSignal3) (GtkObject *object, + gint arg1, + gpointer data); +typedef gint (*GtkContainerSignal4) (GtkObject *object, + gpointer data); + + +static void gtk_container_marshal_signal_1 (GtkObject *object, + GtkSignalFunc func, + gpointer func_data, + GtkArg *args); +static void gtk_container_marshal_signal_2 (GtkObject *object, + GtkSignalFunc func, + gpointer func_data, + GtkArg *args); +static void gtk_container_marshal_signal_3 (GtkObject *object, + GtkSignalFunc func, + gpointer func_data, + GtkArg *args); +static void gtk_container_marshal_signal_4 (GtkObject *object, + GtkSignalFunc func, + gpointer func_data, + GtkArg *args); + + +static void gtk_container_class_init (GtkContainerClass *klass); +static void gtk_container_init (GtkContainer *container); +static void gtk_container_arg (GtkContainer *container, + GtkArg *arg); +static gint gtk_real_container_need_resize (GtkContainer *container); +static gint gtk_real_container_focus (GtkContainer *container, + GtkDirectionType direction); +static gint gtk_container_focus_tab (GtkContainer *container, + GList *children, + GtkDirectionType direction); +static gint gtk_container_focus_up_down (GtkContainer *container, + GList *children, + GtkDirectionType direction); +static gint gtk_container_focus_left_right (GtkContainer *container, + GList *children, + GtkDirectionType direction); +static gint gtk_container_focus_move (GtkContainer *container, + GList *children, + GtkDirectionType direction); +static void gtk_container_children_callback (GtkWidget *widget, + gpointer client_data); + + +static gint container_signals[LAST_SIGNAL] = { 0 }; + + +guint +gtk_container_get_type () +{ + static guint container_type = 0; + + if (!container_type) + { + GtkTypeInfo container_info = + { + "GtkContainer", + sizeof (GtkContainer), + sizeof (GtkContainerClass), + (GtkClassInitFunc) gtk_container_class_init, + (GtkObjectInitFunc) gtk_container_init, + (GtkArgFunc) gtk_container_arg, + }; + + container_type = gtk_type_unique (gtk_widget_get_type (), &container_info); + } + + return container_type; +} + +static void +gtk_container_class_init (GtkContainerClass *class) +{ + GtkObjectClass *object_class; + GtkWidgetClass *widget_class; + + object_class = (GtkObjectClass*) class; + widget_class = (GtkWidgetClass*) class; + + gtk_object_add_arg_type ("GtkContainer::border_width", GTK_TYPE_LONG); + gtk_object_add_arg_type ("GtkContainer::auto_resize", GTK_TYPE_BOOL); + gtk_object_add_arg_type ("GtkContainer::block_resize", GTK_TYPE_BOOL); + gtk_object_add_arg_type ("GtkContainer::child", GTK_TYPE_WIDGET); + + container_signals[ADD] = + gtk_signal_new ("add", + GTK_RUN_FIRST, + object_class->type, + GTK_SIGNAL_OFFSET (GtkContainerClass, add), + gtk_container_marshal_signal_1, + GTK_TYPE_NONE, 1, + GTK_TYPE_WIDGET); + container_signals[REMOVE] = + gtk_signal_new ("remove", + GTK_RUN_FIRST, + object_class->type, + GTK_SIGNAL_OFFSET (GtkContainerClass, remove), + gtk_container_marshal_signal_1, + GTK_TYPE_NONE, 1, + GTK_TYPE_WIDGET); + container_signals[NEED_RESIZE] = + gtk_signal_new ("need_resize", + GTK_RUN_FIRST, + object_class->type, + GTK_SIGNAL_OFFSET (GtkContainerClass, need_resize), + gtk_container_marshal_signal_4, + GTK_TYPE_BOOL, 0, + GTK_TYPE_WIDGET); + container_signals[FOREACH] = + gtk_signal_new ("foreach", + GTK_RUN_FIRST, + object_class->type, + GTK_SIGNAL_OFFSET (GtkContainerClass, foreach), + gtk_container_marshal_signal_2, + GTK_TYPE_NONE, 1, + GTK_TYPE_C_CALLBACK); + container_signals[FOCUS] = + gtk_signal_new ("focus", + GTK_RUN_FIRST, + object_class->type, + GTK_SIGNAL_OFFSET (GtkContainerClass, focus), + gtk_container_marshal_signal_3, + GTK_TYPE_DIRECTION_TYPE, 1, + GTK_TYPE_DIRECTION_TYPE); + + gtk_object_class_add_signals (object_class, container_signals, LAST_SIGNAL); + + class->need_resize = gtk_real_container_need_resize; + class->focus = gtk_real_container_focus; +} + +static void +gtk_container_init (GtkContainer *container) +{ + container->focus_child = NULL; + container->border_width = 0; + container->auto_resize = TRUE; + container->need_resize = FALSE; + container->block_resize = FALSE; +} + +static void +gtk_container_arg (GtkContainer *container, + GtkArg *arg) +{ + if (strcmp (arg->name, "border_width") == 0) + { + gtk_container_border_width (container, GTK_VALUE_LONG (*arg)); + } + else if (strcmp (arg->name, "auto_resize") == 0) + { + if (GTK_VALUE_BOOL (*arg)) + gtk_container_enable_resize (container); + else + gtk_container_disable_resize (container); + } + else if (strcmp (arg->name, "block_resize") == 0) + { + if (GTK_VALUE_BOOL (*arg)) + gtk_container_block_resize (container); + else + gtk_container_unblock_resize (container); + } + else if (strcmp (arg->name, "child") == 0) + { + gtk_container_add (container, GTK_WIDGET (GTK_VALUE_OBJECT (*arg))); + } +} + +void +gtk_container_border_width (GtkContainer *container, + gint border_width) +{ + g_return_if_fail (container != NULL); + g_return_if_fail (GTK_IS_CONTAINER (container)); + + if (container->border_width != border_width) + { + container->border_width = border_width; + + if (container->widget.parent && GTK_WIDGET_VISIBLE (container)) + gtk_container_need_resize (GTK_CONTAINER (container->widget.parent)); + } +} + +void +gtk_container_add (GtkContainer *container, + GtkWidget *widget) +{ + gtk_signal_emit (GTK_OBJECT (container), container_signals[ADD], widget); +} + +void +gtk_container_remove (GtkContainer *container, + GtkWidget *widget) +{ + g_return_if_fail (container != NULL); + g_return_if_fail (GTK_IS_CONTAINER (container)); + + if (container->focus_child == widget) + container->focus_child = NULL; + + gtk_signal_emit (GTK_OBJECT (container), container_signals[REMOVE], widget); +} + +void +gtk_container_disable_resize (GtkContainer *container) +{ + g_return_if_fail (container != NULL); + g_return_if_fail (GTK_IS_CONTAINER (container)); + + container->auto_resize = FALSE; +} + +void +gtk_container_enable_resize (GtkContainer *container) +{ + g_return_if_fail (container != NULL); + g_return_if_fail (GTK_IS_CONTAINER (container)); + + container->auto_resize = TRUE; + if (container->need_resize) + { + container->need_resize = FALSE; + gtk_widget_queue_resize (GTK_WIDGET (container)); + } +} + +void +gtk_container_block_resize (GtkContainer *container) +{ + g_return_if_fail (container != NULL); + g_return_if_fail (GTK_IS_CONTAINER (container)); + + container->block_resize = TRUE; +} + +void +gtk_container_unblock_resize (GtkContainer *container) +{ + g_return_if_fail (container != NULL); + g_return_if_fail (GTK_IS_CONTAINER (container)); + + container->block_resize = FALSE; +} + +gint +gtk_container_need_resize (GtkContainer *container) +{ + gint return_val; + + g_return_val_if_fail (container != NULL, FALSE); + g_return_val_if_fail (GTK_IS_CONTAINER (container), FALSE); + + return_val = FALSE; + + if (!container->block_resize) + { + if (container->auto_resize) + gtk_signal_emit (GTK_OBJECT (container), + container_signals[NEED_RESIZE], + &return_val); + else + container->need_resize = TRUE; + } + + return return_val; +} + +void +gtk_container_foreach (GtkContainer *container, + GtkCallback callback, + gpointer callback_data) +{ + gtk_signal_emit (GTK_OBJECT (container), + container_signals[FOREACH], + callback, callback_data); +} + +gint +gtk_container_focus (GtkContainer *container, + GtkDirectionType direction) +{ + gint return_val; + + gtk_signal_emit (GTK_OBJECT (container), + container_signals[FOCUS], + direction, &return_val); + + return return_val; +} + +GList* +gtk_container_children (GtkContainer *container) +{ + GList *children; + + children = NULL; + + gtk_container_foreach (container, + gtk_container_children_callback, + &children); + + return g_list_reverse (children); +} + + +static void +gtk_container_marshal_signal_1 (GtkObject *object, + GtkSignalFunc func, + gpointer func_data, + GtkArg *args) +{ + GtkContainerSignal1 rfunc; + + rfunc = (GtkContainerSignal1) func; + + (* rfunc) (object, GTK_VALUE_OBJECT (args[0]), func_data); +} + +static void +gtk_container_marshal_signal_2 (GtkObject *object, + GtkSignalFunc func, + gpointer func_data, + GtkArg *args) +{ + GtkContainerSignal2 rfunc; + + rfunc = (GtkContainerSignal2) func; + + (* rfunc) (object, + GTK_VALUE_C_CALLBACK(args[0]).func, + GTK_VALUE_C_CALLBACK(args[0]).func_data, + func_data); +} + +static void +gtk_container_marshal_signal_3 (GtkObject *object, + GtkSignalFunc func, + gpointer func_data, + GtkArg *args) +{ + GtkContainerSignal3 rfunc; + gint *return_val; + + rfunc = (GtkContainerSignal3) func; + return_val = GTK_RETLOC_ENUM (args[1]); + + *return_val = (* rfunc) (object, GTK_VALUE_ENUM(args[0]), func_data); +} + +static void +gtk_container_marshal_signal_4 (GtkObject *object, + GtkSignalFunc func, + gpointer func_data, + GtkArg *args) +{ + GtkContainerSignal4 rfunc; + gint *return_val; + + rfunc = (GtkContainerSignal4) func; + return_val = GTK_RETLOC_BOOL (args[0]); + + *return_val = (* rfunc) (object, func_data); +} + +static gint +gtk_real_container_need_resize (GtkContainer *container) +{ + g_return_val_if_fail (container != NULL, FALSE); + g_return_val_if_fail (GTK_IS_CONTAINER (container), FALSE); + + if (GTK_WIDGET_VISIBLE (container) && container->widget.parent) + return gtk_container_need_resize (GTK_CONTAINER (container->widget.parent)); + + return FALSE; +} + +static gint +gtk_real_container_focus (GtkContainer *container, + GtkDirectionType direction) +{ + GList *children; + GList *tmp_list; + GList *tmp_list2; + gint return_val; + + g_return_val_if_fail (container != NULL, FALSE); + g_return_val_if_fail (GTK_IS_CONTAINER (container), FALSE); + + /* Fail if the container is insensitive + */ + if (!GTK_WIDGET_SENSITIVE (container)) + return FALSE; + + return_val = FALSE; + + if (GTK_WIDGET_CAN_FOCUS (container)) + { + gtk_widget_grab_focus (GTK_WIDGET (container)); + return_val = TRUE; + } + else + { + /* Get a list of the containers children + */ + children = gtk_container_children (container); + + if (children) + { + /* Remove any children which are insensitive + */ + tmp_list = children; + while (tmp_list) + { + if (!GTK_WIDGET_SENSITIVE (tmp_list->data)) + { + tmp_list2 = tmp_list; + 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; + } + + switch (direction) + { + case GTK_DIR_TAB_FORWARD: + case GTK_DIR_TAB_BACKWARD: + 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); + } + } + + return return_val; +} + +static gint +gtk_container_focus_tab (GtkContainer *container, + GList *children, + GtkDirectionType direction) +{ + GtkWidget *child; + GtkWidget *child2; + GList *tmp_list; + gint length; + gint i, j; + + 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); + 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; + } + + /* sort the children in the x direction while + * maintaining the y direction sort. + */ + for (i = 1; i < length; i++) + { + j = i; + tmp_list = g_list_nth (children, j); + child = tmp_list->data; + + while (j > 0) + { + 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; + } + + tmp_list->data = child; + } + + /* if we are going backwards then reverse the order + * of the children. + */ + if (direction == GTK_DIR_TAB_BACKWARD) + children = g_list_reverse (children); + + return gtk_container_focus_move (container, children, direction); +} + +static gint +gtk_container_focus_up_down (GtkContainer *container, + GList *children, + GtkDirectionType direction) +{ + GtkWidget *child; + GtkWidget *child2; + GList *tmp_list; + gint dist1, dist2; + gint focus_x; + gint focus_width; + gint length; + gint i, j; + + /* return failure if there isn't a focus child */ + if (container->focus_child) + { + focus_width = container->focus_child->allocation.width / 2; + focus_x = container->focus_child->allocation.x + focus_width; + } + else + { + focus_width = GTK_WIDGET (container)->allocation.width; + if (GTK_WIDGET_NO_WINDOW (container)) + focus_x = GTK_WIDGET (container)->allocation.x; + else + focus_x = 0; + } + + 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); + 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; + } + + /* sort the children in distance in the x direction + * in distance from the current focus child while maintaining the + * sort in the y direction + */ + for (i = 1; i < length; i++) + { + j = i; + tmp_list = g_list_nth (children, j); + child = tmp_list->data; + dist1 = (child->allocation.x + child->allocation.width / 2) - focus_x; + + while (j > 0) + { + child2 = tmp_list->prev->data; + dist2 = (child2->allocation.x + child2->allocation.width / 2) - focus_x; + + if ((dist1 < dist2) && + (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; + } + + /* go and invalidate any widget which is too + * far from the focus widget. + */ + if (!container->focus_child && + (direction == GTK_DIR_UP)) + focus_x += focus_width; + + tmp_list = children; + while (tmp_list) + { + child = tmp_list->data; + + 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; + + tmp_list = tmp_list->next; + } + + if (direction == GTK_DIR_UP) + children = g_list_reverse (children); + + return gtk_container_focus_move (container, children, direction); +} + +static gint +gtk_container_focus_left_right (GtkContainer *container, + GList *children, + GtkDirectionType direction) +{ + GtkWidget *child; + GtkWidget *child2; + GList *tmp_list; + gint dist1, dist2; + gint focus_y; + gint focus_height; + gint length; + gint i, j; + + /* return failure if there isn't a focus child */ + if (container->focus_child) + { + focus_height = container->focus_child->allocation.height / 2; + focus_y = container->focus_child->allocation.y + focus_height; + } + else + { + focus_height = GTK_WIDGET (container)->allocation.height; + if (GTK_WIDGET_NO_WINDOW (container)) + focus_y = GTK_WIDGET (container)->allocation.y; + else + focus_y = 0; + } + + 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); + child = tmp_list->data; + + while (j > 0) + { + child2 = tmp_list->prev->data; + if (child->allocation.x < child2->allocation.x) + { + tmp_list->data = tmp_list->prev->data; + tmp_list = tmp_list->prev; + j--; + } + else + break; + } + + tmp_list->data = child; + } + + /* sort the children in distance in the y direction + * in distance from the current focus child while maintaining the + * sort in the x direction + */ + for (i = 1; i < length; i++) + { + j = i; + tmp_list = g_list_nth (children, j); + child = tmp_list->data; + dist1 = (child->allocation.y + child->allocation.height / 2) - focus_y; + + while (j > 0) + { + child2 = tmp_list->prev->data; + dist2 = (child2->allocation.y + child2->allocation.height / 2) - focus_y; + + if ((dist1 < dist2) && + (child->allocation.x >= child2->allocation.x)) + { + tmp_list->data = tmp_list->prev->data; + tmp_list = tmp_list->prev; + j--; + } + else + break; + } + + tmp_list->data = child; + } + + /* go and invalidate any widget which is too + * far from the focus widget. + */ + if (!container->focus_child && + (direction == GTK_DIR_LEFT)) + focus_y += focus_height; + + tmp_list = children; + while (tmp_list) + { + child = tmp_list->data; + + 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; + + tmp_list = tmp_list->next; + } + + if (direction == GTK_DIR_LEFT) + children = g_list_reverse (children); + + return gtk_container_focus_move (container, children, direction); +} + +static gint +gtk_container_focus_move (GtkContainer *container, + GList *children, + GtkDirectionType direction) +{ + GtkWidget *focus_child; + GtkWidget *child; + + focus_child = container->focus_child; + container->focus_child = NULL; + + while (children) + { + child = children->data; + children = children->next; + + if (!child) + continue; + + if (focus_child) + { + if (focus_child == child) + { + focus_child = NULL; + + if (GTK_WIDGET_VISIBLE (child) && + GTK_IS_CONTAINER (child) && + !GTK_WIDGET_HAS_FOCUS (child)) + if (gtk_container_focus (GTK_CONTAINER (child), direction)) + return TRUE; + } + } + else if (GTK_WIDGET_VISIBLE (child)) + { + if (GTK_WIDGET_CAN_FOCUS (child)) + { + gtk_widget_grab_focus (child); + return TRUE; + } + else if (GTK_IS_CONTAINER (child)) + { + if (gtk_container_focus (GTK_CONTAINER (child), direction)) + return TRUE; + } + } + } + + return FALSE; +} + + +static void +gtk_container_children_callback (GtkWidget *widget, + gpointer client_data) +{ + GList **children; + + children = (GList**) client_data; + *children = g_list_prepend (*children, widget); +} |