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_get_arg (GtkContainer *container,
77 static void gtk_container_set_arg (GtkContainer *container,
80 static gint gtk_real_container_need_resize (GtkContainer *container);
81 static gint gtk_real_container_focus (GtkContainer *container,
82 GtkDirectionType direction);
83 static gint gtk_container_focus_tab (GtkContainer *container,
85 GtkDirectionType direction);
86 static gint gtk_container_focus_up_down (GtkContainer *container,
88 GtkDirectionType direction);
89 static gint gtk_container_focus_left_right (GtkContainer *container,
91 GtkDirectionType direction);
92 static gint gtk_container_focus_move (GtkContainer *container,
94 GtkDirectionType direction);
95 static void gtk_container_children_callback (GtkWidget *widget,
96 gpointer client_data);
97 static void gtk_container_show_all (GtkWidget *widget);
98 static void gtk_container_hide_all (GtkWidget *widget);
102 static gint container_signals[LAST_SIGNAL] = { 0 };
106 gtk_container_get_type ()
108 static guint container_type = 0;
112 GtkTypeInfo container_info =
115 sizeof (GtkContainer),
116 sizeof (GtkContainerClass),
117 (GtkClassInitFunc) gtk_container_class_init,
118 (GtkObjectInitFunc) gtk_container_init,
119 (GtkArgSetFunc) gtk_container_set_arg,
120 (GtkArgGetFunc) gtk_container_get_arg,
123 container_type = gtk_type_unique (gtk_widget_get_type (), &container_info);
126 return container_type;
130 gtk_container_class_init (GtkContainerClass *class)
132 GtkObjectClass *object_class;
133 GtkWidgetClass *widget_class;
135 object_class = (GtkObjectClass*) class;
136 widget_class = (GtkWidgetClass*) class;
138 gtk_object_add_arg_type ("GtkContainer::border_width", GTK_TYPE_LONG, ARG_BORDER_WIDTH);
139 gtk_object_add_arg_type ("GtkContainer::auto_resize", GTK_TYPE_BOOL, ARG_AUTO_RESIZE);
140 gtk_object_add_arg_type ("GtkContainer::block_resize", GTK_TYPE_BOOL, ARG_BLOCK_RESIZE);
141 gtk_object_add_arg_type ("GtkContainer::child", GTK_TYPE_WIDGET, ARG_CHILD);
143 container_signals[ADD] =
144 gtk_signal_new ("add",
147 GTK_SIGNAL_OFFSET (GtkContainerClass, add),
148 gtk_container_marshal_signal_1,
151 container_signals[REMOVE] =
152 gtk_signal_new ("remove",
155 GTK_SIGNAL_OFFSET (GtkContainerClass, remove),
156 gtk_container_marshal_signal_1,
159 container_signals[NEED_RESIZE] =
160 gtk_signal_new ("need_resize",
163 GTK_SIGNAL_OFFSET (GtkContainerClass, need_resize),
164 gtk_container_marshal_signal_4,
167 container_signals[FOREACH] =
168 gtk_signal_new ("foreach",
171 GTK_SIGNAL_OFFSET (GtkContainerClass, foreach),
172 gtk_container_marshal_signal_2,
174 GTK_TYPE_C_CALLBACK);
175 container_signals[FOCUS] =
176 gtk_signal_new ("focus",
179 GTK_SIGNAL_OFFSET (GtkContainerClass, focus),
180 gtk_container_marshal_signal_3,
181 GTK_TYPE_DIRECTION_TYPE, 1,
182 GTK_TYPE_DIRECTION_TYPE);
184 gtk_object_class_add_signals (object_class, container_signals, LAST_SIGNAL);
186 /* Other container classes should overwrite show_all and hide_all,
187 unless they make all their children accessable
188 through gtk_container_foreach.
190 widget_class->show_all = gtk_container_show_all;
191 widget_class->hide_all = gtk_container_hide_all;
193 class->need_resize = gtk_real_container_need_resize;
194 class->focus = gtk_real_container_focus;
198 gtk_container_init (GtkContainer *container)
200 container->focus_child = NULL;
201 container->border_width = 0;
202 container->auto_resize = TRUE;
203 container->need_resize = FALSE;
204 container->block_resize = FALSE;
208 gtk_container_set_arg (GtkContainer *container,
214 case ARG_BORDER_WIDTH:
215 gtk_container_border_width (container, GTK_VALUE_LONG (*arg));
217 case ARG_AUTO_RESIZE:
218 if (GTK_VALUE_BOOL (*arg))
219 gtk_container_enable_resize (container);
221 gtk_container_disable_resize (container);
223 case ARG_BLOCK_RESIZE:
224 if (GTK_VALUE_BOOL (*arg))
225 gtk_container_block_resize (container);
227 gtk_container_unblock_resize (container);
230 gtk_container_add (container, GTK_WIDGET (GTK_VALUE_OBJECT (*arg)));
233 arg->type = GTK_TYPE_INVALID;
239 gtk_container_get_arg (GtkContainer *container,
245 case ARG_BORDER_WIDTH:
246 GTK_VALUE_LONG (*arg) = container->border_width;
248 case ARG_AUTO_RESIZE:
249 GTK_VALUE_BOOL (*arg) = container->auto_resize;
251 case ARG_BLOCK_RESIZE:
252 GTK_VALUE_BOOL (*arg) = container->block_resize;
255 arg->type = GTK_TYPE_INVALID;
261 gtk_container_border_width (GtkContainer *container,
264 g_return_if_fail (container != NULL);
265 g_return_if_fail (GTK_IS_CONTAINER (container));
267 if (container->border_width != border_width)
269 container->border_width = border_width;
271 if (container->widget.parent && GTK_WIDGET_VISIBLE (container))
272 gtk_container_need_resize (GTK_CONTAINER (container->widget.parent));
277 gtk_container_add (GtkContainer *container,
280 gtk_signal_emit (GTK_OBJECT (container), container_signals[ADD], widget);
284 gtk_container_remove (GtkContainer *container,
287 g_return_if_fail (container != NULL);
288 g_return_if_fail (GTK_IS_CONTAINER (container));
290 if (container->focus_child == widget)
291 container->focus_child = NULL;
293 gtk_signal_emit (GTK_OBJECT (container), container_signals[REMOVE], widget);
297 gtk_container_disable_resize (GtkContainer *container)
299 g_return_if_fail (container != NULL);
300 g_return_if_fail (GTK_IS_CONTAINER (container));
302 container->auto_resize = FALSE;
306 gtk_container_enable_resize (GtkContainer *container)
308 g_return_if_fail (container != NULL);
309 g_return_if_fail (GTK_IS_CONTAINER (container));
311 container->auto_resize = TRUE;
312 if (container->need_resize)
314 container->need_resize = FALSE;
315 gtk_widget_queue_resize (GTK_WIDGET (container));
320 gtk_container_block_resize (GtkContainer *container)
322 g_return_if_fail (container != NULL);
323 g_return_if_fail (GTK_IS_CONTAINER (container));
325 container->block_resize = TRUE;
329 gtk_container_unblock_resize (GtkContainer *container)
331 g_return_if_fail (container != NULL);
332 g_return_if_fail (GTK_IS_CONTAINER (container));
334 container->block_resize = FALSE;
338 gtk_container_need_resize (GtkContainer *container)
342 g_return_val_if_fail (container != NULL, FALSE);
343 g_return_val_if_fail (GTK_IS_CONTAINER (container), FALSE);
347 if (!container->block_resize)
349 if (container->auto_resize)
350 gtk_signal_emit (GTK_OBJECT (container),
351 container_signals[NEED_RESIZE],
354 container->need_resize = TRUE;
361 gtk_container_foreach (GtkContainer *container,
362 GtkCallback callback,
363 gpointer callback_data)
365 gtk_signal_emit (GTK_OBJECT (container),
366 container_signals[FOREACH],
367 callback, callback_data);
371 gtk_container_focus (GtkContainer *container,
372 GtkDirectionType direction)
376 gtk_signal_emit (GTK_OBJECT (container),
377 container_signals[FOCUS],
378 direction, &return_val);
384 gtk_container_children (GtkContainer *container)
390 gtk_container_foreach (container,
391 gtk_container_children_callback,
394 return g_list_reverse (children);
399 gtk_container_marshal_signal_1 (GtkObject *object,
404 GtkContainerSignal1 rfunc;
406 rfunc = (GtkContainerSignal1) func;
408 (* rfunc) (object, GTK_VALUE_OBJECT (args[0]), func_data);
412 gtk_container_marshal_signal_2 (GtkObject *object,
417 GtkContainerSignal2 rfunc;
419 rfunc = (GtkContainerSignal2) func;
422 GTK_VALUE_C_CALLBACK(args[0]).func,
423 GTK_VALUE_C_CALLBACK(args[0]).func_data,
428 gtk_container_marshal_signal_3 (GtkObject *object,
433 GtkContainerSignal3 rfunc;
436 rfunc = (GtkContainerSignal3) func;
437 return_val = GTK_RETLOC_ENUM (args[1]);
439 *return_val = (* rfunc) (object, GTK_VALUE_ENUM(args[0]), func_data);
443 gtk_container_marshal_signal_4 (GtkObject *object,
448 GtkContainerSignal4 rfunc;
451 rfunc = (GtkContainerSignal4) func;
452 return_val = GTK_RETLOC_BOOL (args[0]);
454 *return_val = (* rfunc) (object, func_data);
458 gtk_real_container_need_resize (GtkContainer *container)
460 g_return_val_if_fail (container != NULL, FALSE);
461 g_return_val_if_fail (GTK_IS_CONTAINER (container), FALSE);
463 if (GTK_WIDGET_VISIBLE (container) && container->widget.parent)
464 return gtk_container_need_resize (GTK_CONTAINER (container->widget.parent));
470 gtk_real_container_focus (GtkContainer *container,
471 GtkDirectionType direction)
478 g_return_val_if_fail (container != NULL, FALSE);
479 g_return_val_if_fail (GTK_IS_CONTAINER (container), FALSE);
481 /* Fail if the container is insensitive
483 if (!GTK_WIDGET_SENSITIVE (container))
488 if (GTK_WIDGET_CAN_FOCUS (container))
490 gtk_widget_grab_focus (GTK_WIDGET (container));
495 /* Get a list of the containers children
497 children = gtk_container_children (container);
501 /* Remove any children which are insensitive
506 if (!GTK_WIDGET_SENSITIVE (tmp_list->data))
508 tmp_list2 = tmp_list;
509 tmp_list = tmp_list->next;
511 children = g_list_remove_link (children, tmp_list2);
512 g_list_free_1 (tmp_list2);
515 tmp_list = tmp_list->next;
520 case GTK_DIR_TAB_FORWARD:
521 case GTK_DIR_TAB_BACKWARD:
522 return_val = gtk_container_focus_tab (container, children, direction);
526 return_val = gtk_container_focus_up_down (container, children, direction);
530 return_val = gtk_container_focus_left_right (container, children, direction);
534 g_list_free (children);
542 gtk_container_focus_tab (GtkContainer *container,
544 GtkDirectionType direction)
552 length = g_list_length (children);
554 /* sort the children in the y direction */
555 for (i = 1; i < length; i++)
558 tmp_list = g_list_nth (children, j);
559 child = tmp_list->data;
563 child2 = tmp_list->prev->data;
564 if (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 /* sort the children in the x direction while
578 * maintaining the y direction sort.
580 for (i = 1; i < length; i++)
583 tmp_list = g_list_nth (children, j);
584 child = tmp_list->data;
588 child2 = tmp_list->prev->data;
589 if ((child->allocation.x < child2->allocation.x) &&
590 (child->allocation.y >= child2->allocation.y))
592 tmp_list->data = tmp_list->prev->data;
593 tmp_list = tmp_list->prev;
600 tmp_list->data = child;
603 /* if we are going backwards then reverse the order
606 if (direction == GTK_DIR_TAB_BACKWARD)
607 children = g_list_reverse (children);
609 return gtk_container_focus_move (container, children, direction);
613 gtk_container_focus_up_down (GtkContainer *container,
615 GtkDirectionType direction)
626 /* return failure if there isn't a focus child */
627 if (container->focus_child)
629 focus_width = container->focus_child->allocation.width / 2;
630 focus_x = container->focus_child->allocation.x + focus_width;
634 focus_width = GTK_WIDGET (container)->allocation.width;
635 if (GTK_WIDGET_NO_WINDOW (container))
636 focus_x = GTK_WIDGET (container)->allocation.x;
641 length = g_list_length (children);
643 /* sort the children in the y direction */
644 for (i = 1; i < length; i++)
647 tmp_list = g_list_nth (children, j);
648 child = tmp_list->data;
652 child2 = tmp_list->prev->data;
653 if (child->allocation.y < child2->allocation.y)
655 tmp_list->data = tmp_list->prev->data;
656 tmp_list = tmp_list->prev;
663 tmp_list->data = child;
666 /* sort the children in distance in the x direction
667 * in distance from the current focus child while maintaining the
668 * sort in the y direction
670 for (i = 1; i < length; i++)
673 tmp_list = g_list_nth (children, j);
674 child = tmp_list->data;
675 dist1 = (child->allocation.x + child->allocation.width / 2) - focus_x;
679 child2 = tmp_list->prev->data;
680 dist2 = (child2->allocation.x + child2->allocation.width / 2) - focus_x;
682 if ((dist1 < dist2) &&
683 (child->allocation.y >= child2->allocation.y))
685 tmp_list->data = tmp_list->prev->data;
686 tmp_list = tmp_list->prev;
693 tmp_list->data = child;
696 /* go and invalidate any widget which is too
697 * far from the focus widget.
699 if (!container->focus_child &&
700 (direction == GTK_DIR_UP))
701 focus_x += focus_width;
706 child = tmp_list->data;
708 dist1 = (child->allocation.x + child->allocation.width / 2) - focus_x;
709 if (((direction == GTK_DIR_DOWN) && (dist1 < 0)) ||
710 ((direction == GTK_DIR_UP) && (dist1 > 0)))
711 tmp_list->data = NULL;
713 tmp_list = tmp_list->next;
716 if (direction == GTK_DIR_UP)
717 children = g_list_reverse (children);
719 return gtk_container_focus_move (container, children, direction);
723 gtk_container_focus_left_right (GtkContainer *container,
725 GtkDirectionType direction)
736 /* return failure if there isn't a focus child */
737 if (container->focus_child)
739 focus_height = container->focus_child->allocation.height / 2;
740 focus_y = container->focus_child->allocation.y + focus_height;
744 focus_height = GTK_WIDGET (container)->allocation.height;
745 if (GTK_WIDGET_NO_WINDOW (container))
746 focus_y = GTK_WIDGET (container)->allocation.y;
751 length = g_list_length (children);
753 /* sort the children in the x direction */
754 for (i = 1; i < length; i++)
757 tmp_list = g_list_nth (children, j);
758 child = tmp_list->data;
762 child2 = tmp_list->prev->data;
763 if (child->allocation.x < child2->allocation.x)
765 tmp_list->data = tmp_list->prev->data;
766 tmp_list = tmp_list->prev;
773 tmp_list->data = child;
776 /* sort the children in distance in the y direction
777 * in distance from the current focus child while maintaining the
778 * sort in the x direction
780 for (i = 1; i < length; i++)
783 tmp_list = g_list_nth (children, j);
784 child = tmp_list->data;
785 dist1 = (child->allocation.y + child->allocation.height / 2) - focus_y;
789 child2 = tmp_list->prev->data;
790 dist2 = (child2->allocation.y + child2->allocation.height / 2) - focus_y;
792 if ((dist1 < dist2) &&
793 (child->allocation.x >= child2->allocation.x))
795 tmp_list->data = tmp_list->prev->data;
796 tmp_list = tmp_list->prev;
803 tmp_list->data = child;
806 /* go and invalidate any widget which is too
807 * far from the focus widget.
809 if (!container->focus_child &&
810 (direction == GTK_DIR_LEFT))
811 focus_y += focus_height;
816 child = tmp_list->data;
818 dist1 = (child->allocation.y + child->allocation.height / 2) - focus_y;
819 if (((direction == GTK_DIR_RIGHT) && (dist1 < 0)) ||
820 ((direction == GTK_DIR_LEFT) && (dist1 > 0)))
821 tmp_list->data = NULL;
823 tmp_list = tmp_list->next;
826 if (direction == GTK_DIR_LEFT)
827 children = g_list_reverse (children);
829 return gtk_container_focus_move (container, children, direction);
833 gtk_container_focus_move (GtkContainer *container,
835 GtkDirectionType direction)
837 GtkWidget *focus_child;
840 focus_child = container->focus_child;
841 container->focus_child = NULL;
845 child = children->data;
846 children = children->next;
853 if (focus_child == child)
857 if (GTK_WIDGET_VISIBLE (child) &&
858 GTK_IS_CONTAINER (child) &&
859 !GTK_WIDGET_HAS_FOCUS (child))
860 if (gtk_container_focus (GTK_CONTAINER (child), direction))
864 else if (GTK_WIDGET_VISIBLE (child))
866 if (GTK_WIDGET_CAN_FOCUS (child))
868 gtk_widget_grab_focus (child);
871 else if (GTK_IS_CONTAINER (child))
873 if (gtk_container_focus (GTK_CONTAINER (child), direction))
884 gtk_container_children_callback (GtkWidget *widget,
885 gpointer client_data)
889 children = (GList**) client_data;
890 *children = g_list_prepend (*children, widget);
894 gtk_container_show_all (GtkWidget *widget)
896 GtkContainer *container;
898 g_return_if_fail (widget != NULL);
899 g_return_if_fail (GTK_IS_CONTAINER (widget));
900 container = GTK_CONTAINER (widget);
902 /* First show children, then self.
903 This makes sure that toplevel windows get shown as last widget.
904 Otherwise the user would see the widgets get
905 visible one after another.
907 gtk_container_foreach (container, (GtkCallback) gtk_widget_show_all, NULL);
908 gtk_widget_show (widget);
913 gtk_container_hide_all (GtkWidget *widget)
915 GtkContainer *container;
917 g_return_if_fail (widget != NULL);
918 g_return_if_fail (GTK_IS_CONTAINER (widget));
919 container = GTK_CONTAINER (widget);
921 /* First hide self, then children.
922 This is the reverse order of gtk_container_show_all.
924 gtk_widget_hide (widget);
925 gtk_container_foreach (container, (GtkCallback) gtk_widget_hide_all, NULL);