1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the Free
16 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 #include "gtkcontainer.h"
20 #include "gtksignal.h"
40 typedef void (*GtkContainerSignal1) (GtkObject *object,
43 typedef void (*GtkContainerSignal2) (GtkObject *object,
47 typedef gint (*GtkContainerSignal3) (GtkObject *object,
50 typedef gint (*GtkContainerSignal4) (GtkObject *object,
54 static void gtk_container_marshal_signal_1 (GtkObject *object,
58 static void gtk_container_marshal_signal_2 (GtkObject *object,
62 static void gtk_container_marshal_signal_3 (GtkObject *object,
66 static void gtk_container_marshal_signal_4 (GtkObject *object,
72 static void gtk_container_class_init (GtkContainerClass *klass);
73 static void gtk_container_init (GtkContainer *container);
74 static void gtk_container_set_arg (GtkContainer *container,
77 static gint gtk_real_container_need_resize (GtkContainer *container);
78 static gint gtk_real_container_focus (GtkContainer *container,
79 GtkDirectionType direction);
80 static gint gtk_container_focus_tab (GtkContainer *container,
82 GtkDirectionType direction);
83 static gint gtk_container_focus_up_down (GtkContainer *container,
85 GtkDirectionType direction);
86 static gint gtk_container_focus_left_right (GtkContainer *container,
88 GtkDirectionType direction);
89 static gint gtk_container_focus_move (GtkContainer *container,
91 GtkDirectionType direction);
92 static void gtk_container_children_callback (GtkWidget *widget,
93 gpointer client_data);
94 static void gtk_container_show_all (GtkWidget *widget);
95 static void gtk_container_hide_all (GtkWidget *widget);
99 static gint container_signals[LAST_SIGNAL] = { 0 };
103 gtk_container_get_type ()
105 static guint container_type = 0;
109 GtkTypeInfo container_info =
112 sizeof (GtkContainer),
113 sizeof (GtkContainerClass),
114 (GtkClassInitFunc) gtk_container_class_init,
115 (GtkObjectInitFunc) gtk_container_init,
116 (GtkArgSetFunc) gtk_container_set_arg,
117 (GtkArgGetFunc) NULL,
120 container_type = gtk_type_unique (gtk_widget_get_type (), &container_info);
123 return container_type;
127 gtk_container_class_init (GtkContainerClass *class)
129 GtkObjectClass *object_class;
130 GtkWidgetClass *widget_class;
132 object_class = (GtkObjectClass*) class;
133 widget_class = (GtkWidgetClass*) class;
135 gtk_object_add_arg_type ("GtkContainer::border_width", GTK_TYPE_LONG, ARG_BORDER_WIDTH);
136 gtk_object_add_arg_type ("GtkContainer::auto_resize", GTK_TYPE_BOOL, ARG_AUTO_RESIZE);
137 gtk_object_add_arg_type ("GtkContainer::block_resize", GTK_TYPE_BOOL, ARG_BLOCK_RESIZE);
138 gtk_object_add_arg_type ("GtkContainer::child", GTK_TYPE_WIDGET, ARG_CHILD);
140 container_signals[ADD] =
141 gtk_signal_new ("add",
144 GTK_SIGNAL_OFFSET (GtkContainerClass, add),
145 gtk_container_marshal_signal_1,
148 container_signals[REMOVE] =
149 gtk_signal_new ("remove",
152 GTK_SIGNAL_OFFSET (GtkContainerClass, remove),
153 gtk_container_marshal_signal_1,
156 container_signals[NEED_RESIZE] =
157 gtk_signal_new ("need_resize",
160 GTK_SIGNAL_OFFSET (GtkContainerClass, need_resize),
161 gtk_container_marshal_signal_4,
164 container_signals[FOREACH] =
165 gtk_signal_new ("foreach",
168 GTK_SIGNAL_OFFSET (GtkContainerClass, foreach),
169 gtk_container_marshal_signal_2,
171 GTK_TYPE_C_CALLBACK);
172 container_signals[FOCUS] =
173 gtk_signal_new ("focus",
176 GTK_SIGNAL_OFFSET (GtkContainerClass, focus),
177 gtk_container_marshal_signal_3,
178 GTK_TYPE_DIRECTION_TYPE, 1,
179 GTK_TYPE_DIRECTION_TYPE);
181 gtk_object_class_add_signals (object_class, container_signals, LAST_SIGNAL);
183 /* Other container classes should overwrite show_all and hide_all,
184 unless they make all their children accessable
185 through gtk_container_foreach.
187 widget_class->show_all = gtk_container_show_all;
188 widget_class->hide_all = gtk_container_hide_all;
190 class->need_resize = gtk_real_container_need_resize;
191 class->focus = gtk_real_container_focus;
195 gtk_container_init (GtkContainer *container)
197 container->focus_child = NULL;
198 container->border_width = 0;
199 container->auto_resize = TRUE;
200 container->need_resize = FALSE;
201 container->block_resize = FALSE;
205 gtk_container_set_arg (GtkContainer *container,
211 case ARG_BORDER_WIDTH:
212 gtk_container_border_width (container, GTK_VALUE_LONG (*arg));
214 case ARG_AUTO_RESIZE:
215 if (GTK_VALUE_BOOL (*arg))
216 gtk_container_enable_resize (container);
218 gtk_container_disable_resize (container);
220 case ARG_BLOCK_RESIZE:
221 if (GTK_VALUE_BOOL (*arg))
222 gtk_container_block_resize (container);
224 gtk_container_unblock_resize (container);
227 gtk_container_add (container, GTK_WIDGET (GTK_VALUE_OBJECT (*arg)));
230 g_assert_not_reached ();
235 gtk_container_border_width (GtkContainer *container,
238 g_return_if_fail (container != NULL);
239 g_return_if_fail (GTK_IS_CONTAINER (container));
241 if (container->border_width != border_width)
243 container->border_width = border_width;
245 if (container->widget.parent && GTK_WIDGET_VISIBLE (container))
246 gtk_container_need_resize (GTK_CONTAINER (container->widget.parent));
251 gtk_container_add (GtkContainer *container,
254 gtk_signal_emit (GTK_OBJECT (container), container_signals[ADD], widget);
258 gtk_container_remove (GtkContainer *container,
261 g_return_if_fail (container != NULL);
262 g_return_if_fail (GTK_IS_CONTAINER (container));
264 if (container->focus_child == widget)
265 container->focus_child = NULL;
267 gtk_signal_emit (GTK_OBJECT (container), container_signals[REMOVE], widget);
271 gtk_container_disable_resize (GtkContainer *container)
273 g_return_if_fail (container != NULL);
274 g_return_if_fail (GTK_IS_CONTAINER (container));
276 container->auto_resize = FALSE;
280 gtk_container_enable_resize (GtkContainer *container)
282 g_return_if_fail (container != NULL);
283 g_return_if_fail (GTK_IS_CONTAINER (container));
285 container->auto_resize = TRUE;
286 if (container->need_resize)
288 container->need_resize = FALSE;
289 gtk_widget_queue_resize (GTK_WIDGET (container));
294 gtk_container_block_resize (GtkContainer *container)
296 g_return_if_fail (container != NULL);
297 g_return_if_fail (GTK_IS_CONTAINER (container));
299 container->block_resize = TRUE;
303 gtk_container_unblock_resize (GtkContainer *container)
305 g_return_if_fail (container != NULL);
306 g_return_if_fail (GTK_IS_CONTAINER (container));
308 container->block_resize = FALSE;
312 gtk_container_need_resize (GtkContainer *container)
316 g_return_val_if_fail (container != NULL, FALSE);
317 g_return_val_if_fail (GTK_IS_CONTAINER (container), FALSE);
321 if (!container->block_resize)
323 if (container->auto_resize)
324 gtk_signal_emit (GTK_OBJECT (container),
325 container_signals[NEED_RESIZE],
328 container->need_resize = TRUE;
335 gtk_container_foreach (GtkContainer *container,
336 GtkCallback callback,
337 gpointer callback_data)
339 gtk_signal_emit (GTK_OBJECT (container),
340 container_signals[FOREACH],
341 callback, callback_data);
345 gtk_container_focus (GtkContainer *container,
346 GtkDirectionType direction)
350 gtk_signal_emit (GTK_OBJECT (container),
351 container_signals[FOCUS],
352 direction, &return_val);
358 gtk_container_children (GtkContainer *container)
364 gtk_container_foreach (container,
365 gtk_container_children_callback,
368 return g_list_reverse (children);
373 gtk_container_marshal_signal_1 (GtkObject *object,
378 GtkContainerSignal1 rfunc;
380 rfunc = (GtkContainerSignal1) func;
382 (* rfunc) (object, GTK_VALUE_OBJECT (args[0]), func_data);
386 gtk_container_marshal_signal_2 (GtkObject *object,
391 GtkContainerSignal2 rfunc;
393 rfunc = (GtkContainerSignal2) func;
396 GTK_VALUE_C_CALLBACK(args[0]).func,
397 GTK_VALUE_C_CALLBACK(args[0]).func_data,
402 gtk_container_marshal_signal_3 (GtkObject *object,
407 GtkContainerSignal3 rfunc;
410 rfunc = (GtkContainerSignal3) func;
411 return_val = GTK_RETLOC_ENUM (args[1]);
413 *return_val = (* rfunc) (object, GTK_VALUE_ENUM(args[0]), func_data);
417 gtk_container_marshal_signal_4 (GtkObject *object,
422 GtkContainerSignal4 rfunc;
425 rfunc = (GtkContainerSignal4) func;
426 return_val = GTK_RETLOC_BOOL (args[0]);
428 *return_val = (* rfunc) (object, func_data);
432 gtk_real_container_need_resize (GtkContainer *container)
434 g_return_val_if_fail (container != NULL, FALSE);
435 g_return_val_if_fail (GTK_IS_CONTAINER (container), FALSE);
437 if (GTK_WIDGET_VISIBLE (container) && container->widget.parent)
438 return gtk_container_need_resize (GTK_CONTAINER (container->widget.parent));
444 gtk_real_container_focus (GtkContainer *container,
445 GtkDirectionType direction)
452 g_return_val_if_fail (container != NULL, FALSE);
453 g_return_val_if_fail (GTK_IS_CONTAINER (container), FALSE);
455 /* Fail if the container is insensitive
457 if (!GTK_WIDGET_SENSITIVE (container))
462 if (GTK_WIDGET_CAN_FOCUS (container))
464 gtk_widget_grab_focus (GTK_WIDGET (container));
469 /* Get a list of the containers children
471 children = gtk_container_children (container);
475 /* Remove any children which are insensitive
480 if (!GTK_WIDGET_SENSITIVE (tmp_list->data))
482 tmp_list2 = tmp_list;
483 tmp_list = tmp_list->next;
485 children = g_list_remove_link (children, tmp_list2);
486 g_list_free_1 (tmp_list2);
489 tmp_list = tmp_list->next;
494 case GTK_DIR_TAB_FORWARD:
495 case GTK_DIR_TAB_BACKWARD:
496 return_val = gtk_container_focus_tab (container, children, direction);
500 return_val = gtk_container_focus_up_down (container, children, direction);
504 return_val = gtk_container_focus_left_right (container, children, direction);
508 g_list_free (children);
516 gtk_container_focus_tab (GtkContainer *container,
518 GtkDirectionType direction)
526 length = g_list_length (children);
528 /* sort the children in the y direction */
529 for (i = 1; i < length; i++)
532 tmp_list = g_list_nth (children, j);
533 child = tmp_list->data;
537 child2 = tmp_list->prev->data;
538 if (child->allocation.y < child2->allocation.y)
540 tmp_list->data = tmp_list->prev->data;
541 tmp_list = tmp_list->prev;
548 tmp_list->data = child;
551 /* sort the children in the x direction while
552 * maintaining the y direction sort.
554 for (i = 1; i < length; i++)
557 tmp_list = g_list_nth (children, j);
558 child = tmp_list->data;
562 child2 = tmp_list->prev->data;
563 if ((child->allocation.x < child2->allocation.x) &&
564 (child->allocation.y >= child2->allocation.y))
566 tmp_list->data = tmp_list->prev->data;
567 tmp_list = tmp_list->prev;
574 tmp_list->data = child;
577 /* if we are going backwards then reverse the order
580 if (direction == GTK_DIR_TAB_BACKWARD)
581 children = g_list_reverse (children);
583 return gtk_container_focus_move (container, children, direction);
587 gtk_container_focus_up_down (GtkContainer *container,
589 GtkDirectionType direction)
600 /* return failure if there isn't a focus child */
601 if (container->focus_child)
603 focus_width = container->focus_child->allocation.width / 2;
604 focus_x = container->focus_child->allocation.x + focus_width;
608 focus_width = GTK_WIDGET (container)->allocation.width;
609 if (GTK_WIDGET_NO_WINDOW (container))
610 focus_x = GTK_WIDGET (container)->allocation.x;
615 length = g_list_length (children);
617 /* sort the children in the y direction */
618 for (i = 1; i < length; i++)
621 tmp_list = g_list_nth (children, j);
622 child = tmp_list->data;
626 child2 = tmp_list->prev->data;
627 if (child->allocation.y < child2->allocation.y)
629 tmp_list->data = tmp_list->prev->data;
630 tmp_list = tmp_list->prev;
637 tmp_list->data = child;
640 /* sort the children in distance in the x direction
641 * in distance from the current focus child while maintaining the
642 * sort in the y direction
644 for (i = 1; i < length; i++)
647 tmp_list = g_list_nth (children, j);
648 child = tmp_list->data;
649 dist1 = (child->allocation.x + child->allocation.width / 2) - focus_x;
653 child2 = tmp_list->prev->data;
654 dist2 = (child2->allocation.x + child2->allocation.width / 2) - focus_x;
656 if ((dist1 < dist2) &&
657 (child->allocation.y >= child2->allocation.y))
659 tmp_list->data = tmp_list->prev->data;
660 tmp_list = tmp_list->prev;
667 tmp_list->data = child;
670 /* go and invalidate any widget which is too
671 * far from the focus widget.
673 if (!container->focus_child &&
674 (direction == GTK_DIR_UP))
675 focus_x += focus_width;
680 child = tmp_list->data;
682 dist1 = (child->allocation.x + child->allocation.width / 2) - focus_x;
683 if (((direction == GTK_DIR_DOWN) && (dist1 < 0)) ||
684 ((direction == GTK_DIR_UP) && (dist1 > 0)))
685 tmp_list->data = NULL;
687 tmp_list = tmp_list->next;
690 if (direction == GTK_DIR_UP)
691 children = g_list_reverse (children);
693 return gtk_container_focus_move (container, children, direction);
697 gtk_container_focus_left_right (GtkContainer *container,
699 GtkDirectionType direction)
710 /* return failure if there isn't a focus child */
711 if (container->focus_child)
713 focus_height = container->focus_child->allocation.height / 2;
714 focus_y = container->focus_child->allocation.y + focus_height;
718 focus_height = GTK_WIDGET (container)->allocation.height;
719 if (GTK_WIDGET_NO_WINDOW (container))
720 focus_y = GTK_WIDGET (container)->allocation.y;
725 length = g_list_length (children);
727 /* sort the children in the x direction */
728 for (i = 1; i < length; i++)
731 tmp_list = g_list_nth (children, j);
732 child = tmp_list->data;
736 child2 = tmp_list->prev->data;
737 if (child->allocation.x < child2->allocation.x)
739 tmp_list->data = tmp_list->prev->data;
740 tmp_list = tmp_list->prev;
747 tmp_list->data = child;
750 /* sort the children in distance in the y direction
751 * in distance from the current focus child while maintaining the
752 * sort in the x direction
754 for (i = 1; i < length; i++)
757 tmp_list = g_list_nth (children, j);
758 child = tmp_list->data;
759 dist1 = (child->allocation.y + child->allocation.height / 2) - focus_y;
763 child2 = tmp_list->prev->data;
764 dist2 = (child2->allocation.y + child2->allocation.height / 2) - focus_y;
766 if ((dist1 < dist2) &&
767 (child->allocation.x >= child2->allocation.x))
769 tmp_list->data = tmp_list->prev->data;
770 tmp_list = tmp_list->prev;
777 tmp_list->data = child;
780 /* go and invalidate any widget which is too
781 * far from the focus widget.
783 if (!container->focus_child &&
784 (direction == GTK_DIR_LEFT))
785 focus_y += focus_height;
790 child = tmp_list->data;
792 dist1 = (child->allocation.y + child->allocation.height / 2) - focus_y;
793 if (((direction == GTK_DIR_RIGHT) && (dist1 < 0)) ||
794 ((direction == GTK_DIR_LEFT) && (dist1 > 0)))
795 tmp_list->data = NULL;
797 tmp_list = tmp_list->next;
800 if (direction == GTK_DIR_LEFT)
801 children = g_list_reverse (children);
803 return gtk_container_focus_move (container, children, direction);
807 gtk_container_focus_move (GtkContainer *container,
809 GtkDirectionType direction)
811 GtkWidget *focus_child;
814 focus_child = container->focus_child;
815 container->focus_child = NULL;
819 child = children->data;
820 children = children->next;
827 if (focus_child == child)
831 if (GTK_WIDGET_VISIBLE (child) &&
832 GTK_IS_CONTAINER (child) &&
833 !GTK_WIDGET_HAS_FOCUS (child))
834 if (gtk_container_focus (GTK_CONTAINER (child), direction))
838 else if (GTK_WIDGET_VISIBLE (child))
840 if (GTK_WIDGET_CAN_FOCUS (child))
842 gtk_widget_grab_focus (child);
845 else if (GTK_IS_CONTAINER (child))
847 if (gtk_container_focus (GTK_CONTAINER (child), direction))
858 gtk_container_children_callback (GtkWidget *widget,
859 gpointer client_data)
863 children = (GList**) client_data;
864 *children = g_list_prepend (*children, widget);
868 gtk_container_show_all (GtkWidget *widget)
870 GtkContainer *container;
872 g_return_if_fail (widget != NULL);
873 g_return_if_fail (GTK_IS_CONTAINER (widget));
874 container = GTK_CONTAINER (widget);
876 /* First show children, then self.
877 This makes sure that toplevel windows get shown as last widget.
878 Otherwise the user would see the widgets get
879 visible one after another.
881 gtk_container_foreach (container, (GtkCallback) gtk_widget_show_all, NULL);
882 gtk_widget_show (widget);
887 gtk_container_hide_all (GtkWidget *widget)
889 GtkContainer *container;
891 g_return_if_fail (widget != NULL);
892 g_return_if_fail (GTK_IS_CONTAINER (widget));
893 container = GTK_CONTAINER (widget);
895 /* First hide self, then children.
896 This is the reverse order of gtk_container_show_all.
898 gtk_widget_hide (widget);
899 gtk_container_foreach (container, (GtkCallback) gtk_widget_hide_all, NULL);