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"
33 typedef void (*GtkContainerSignal1) (GtkObject *object,
36 typedef void (*GtkContainerSignal2) (GtkObject *object,
40 typedef gint (*GtkContainerSignal3) (GtkObject *object,
43 typedef gint (*GtkContainerSignal4) (GtkObject *object,
47 static void gtk_container_marshal_signal_1 (GtkObject *object,
51 static void gtk_container_marshal_signal_2 (GtkObject *object,
55 static void gtk_container_marshal_signal_3 (GtkObject *object,
59 static void gtk_container_marshal_signal_4 (GtkObject *object,
65 static void gtk_container_class_init (GtkContainerClass *klass);
66 static void gtk_container_init (GtkContainer *container);
67 static void gtk_container_arg (GtkContainer *container,
69 static gint gtk_real_container_need_resize (GtkContainer *container);
70 static gint gtk_real_container_focus (GtkContainer *container,
71 GtkDirectionType direction);
72 static gint gtk_container_focus_tab (GtkContainer *container,
74 GtkDirectionType direction);
75 static gint gtk_container_focus_up_down (GtkContainer *container,
77 GtkDirectionType direction);
78 static gint gtk_container_focus_left_right (GtkContainer *container,
80 GtkDirectionType direction);
81 static gint gtk_container_focus_move (GtkContainer *container,
83 GtkDirectionType direction);
84 static void gtk_container_children_callback (GtkWidget *widget,
85 gpointer client_data);
88 static gint container_signals[LAST_SIGNAL] = { 0 };
92 gtk_container_get_type ()
94 static guint container_type = 0;
98 GtkTypeInfo container_info =
101 sizeof (GtkContainer),
102 sizeof (GtkContainerClass),
103 (GtkClassInitFunc) gtk_container_class_init,
104 (GtkObjectInitFunc) gtk_container_init,
105 (GtkArgFunc) gtk_container_arg,
108 container_type = gtk_type_unique (gtk_widget_get_type (), &container_info);
111 return container_type;
115 gtk_container_class_init (GtkContainerClass *class)
117 GtkObjectClass *object_class;
118 GtkWidgetClass *widget_class;
120 object_class = (GtkObjectClass*) class;
121 widget_class = (GtkWidgetClass*) class;
123 gtk_object_add_arg_type ("GtkContainer::border_width", GTK_TYPE_LONG);
124 gtk_object_add_arg_type ("GtkContainer::auto_resize", GTK_TYPE_BOOL);
125 gtk_object_add_arg_type ("GtkContainer::block_resize", GTK_TYPE_BOOL);
126 gtk_object_add_arg_type ("GtkContainer::child", GTK_TYPE_WIDGET);
128 container_signals[ADD] =
129 gtk_signal_new ("add",
132 GTK_SIGNAL_OFFSET (GtkContainerClass, add),
133 gtk_container_marshal_signal_1,
136 container_signals[REMOVE] =
137 gtk_signal_new ("remove",
140 GTK_SIGNAL_OFFSET (GtkContainerClass, remove),
141 gtk_container_marshal_signal_1,
144 container_signals[NEED_RESIZE] =
145 gtk_signal_new ("need_resize",
148 GTK_SIGNAL_OFFSET (GtkContainerClass, need_resize),
149 gtk_container_marshal_signal_4,
152 container_signals[FOREACH] =
153 gtk_signal_new ("foreach",
156 GTK_SIGNAL_OFFSET (GtkContainerClass, foreach),
157 gtk_container_marshal_signal_2,
159 GTK_TYPE_C_CALLBACK);
160 container_signals[FOCUS] =
161 gtk_signal_new ("focus",
164 GTK_SIGNAL_OFFSET (GtkContainerClass, focus),
165 gtk_container_marshal_signal_3,
166 GTK_TYPE_DIRECTION_TYPE, 1,
167 GTK_TYPE_DIRECTION_TYPE);
169 gtk_object_class_add_signals (object_class, container_signals, LAST_SIGNAL);
171 class->need_resize = gtk_real_container_need_resize;
172 class->focus = gtk_real_container_focus;
176 gtk_container_init (GtkContainer *container)
178 container->focus_child = NULL;
179 container->border_width = 0;
180 container->auto_resize = TRUE;
181 container->need_resize = FALSE;
182 container->block_resize = FALSE;
186 gtk_container_arg (GtkContainer *container,
189 if (strcmp (arg->name, "border_width") == 0)
191 gtk_container_border_width (container, GTK_VALUE_LONG (*arg));
193 else if (strcmp (arg->name, "auto_resize") == 0)
195 if (GTK_VALUE_BOOL (*arg))
196 gtk_container_enable_resize (container);
198 gtk_container_disable_resize (container);
200 else if (strcmp (arg->name, "block_resize") == 0)
202 if (GTK_VALUE_BOOL (*arg))
203 gtk_container_block_resize (container);
205 gtk_container_unblock_resize (container);
207 else if (strcmp (arg->name, "child") == 0)
209 gtk_container_add (container, GTK_WIDGET (GTK_VALUE_OBJECT (*arg)));
214 gtk_container_border_width (GtkContainer *container,
217 g_return_if_fail (container != NULL);
218 g_return_if_fail (GTK_IS_CONTAINER (container));
220 if (container->border_width != border_width)
222 container->border_width = border_width;
224 if (container->widget.parent && GTK_WIDGET_VISIBLE (container))
225 gtk_container_need_resize (GTK_CONTAINER (container->widget.parent));
230 gtk_container_add (GtkContainer *container,
233 gtk_signal_emit (GTK_OBJECT (container), container_signals[ADD], widget);
237 gtk_container_remove (GtkContainer *container,
240 g_return_if_fail (container != NULL);
241 g_return_if_fail (GTK_IS_CONTAINER (container));
243 if (container->focus_child == widget)
244 container->focus_child = NULL;
246 gtk_signal_emit (GTK_OBJECT (container), container_signals[REMOVE], widget);
250 gtk_container_disable_resize (GtkContainer *container)
252 g_return_if_fail (container != NULL);
253 g_return_if_fail (GTK_IS_CONTAINER (container));
255 container->auto_resize = FALSE;
259 gtk_container_enable_resize (GtkContainer *container)
261 g_return_if_fail (container != NULL);
262 g_return_if_fail (GTK_IS_CONTAINER (container));
264 container->auto_resize = TRUE;
265 if (container->need_resize)
267 container->need_resize = FALSE;
268 gtk_widget_queue_resize (GTK_WIDGET (container));
273 gtk_container_block_resize (GtkContainer *container)
275 g_return_if_fail (container != NULL);
276 g_return_if_fail (GTK_IS_CONTAINER (container));
278 container->block_resize = TRUE;
282 gtk_container_unblock_resize (GtkContainer *container)
284 g_return_if_fail (container != NULL);
285 g_return_if_fail (GTK_IS_CONTAINER (container));
287 container->block_resize = FALSE;
291 gtk_container_need_resize (GtkContainer *container)
295 g_return_val_if_fail (container != NULL, FALSE);
296 g_return_val_if_fail (GTK_IS_CONTAINER (container), FALSE);
300 if (!container->block_resize)
302 if (container->auto_resize)
303 gtk_signal_emit (GTK_OBJECT (container),
304 container_signals[NEED_RESIZE],
307 container->need_resize = TRUE;
314 gtk_container_foreach (GtkContainer *container,
315 GtkCallback callback,
316 gpointer callback_data)
318 gtk_signal_emit (GTK_OBJECT (container),
319 container_signals[FOREACH],
320 callback, callback_data);
324 gtk_container_focus (GtkContainer *container,
325 GtkDirectionType direction)
329 gtk_signal_emit (GTK_OBJECT (container),
330 container_signals[FOCUS],
331 direction, &return_val);
337 gtk_container_children (GtkContainer *container)
343 gtk_container_foreach (container,
344 gtk_container_children_callback,
347 return g_list_reverse (children);
352 gtk_container_marshal_signal_1 (GtkObject *object,
357 GtkContainerSignal1 rfunc;
359 rfunc = (GtkContainerSignal1) func;
361 (* rfunc) (object, GTK_VALUE_OBJECT (args[0]), func_data);
365 gtk_container_marshal_signal_2 (GtkObject *object,
370 GtkContainerSignal2 rfunc;
372 rfunc = (GtkContainerSignal2) func;
375 GTK_VALUE_C_CALLBACK(args[0]).func,
376 GTK_VALUE_C_CALLBACK(args[0]).func_data,
381 gtk_container_marshal_signal_3 (GtkObject *object,
386 GtkContainerSignal3 rfunc;
389 rfunc = (GtkContainerSignal3) func;
390 return_val = GTK_RETLOC_ENUM (args[1]);
392 *return_val = (* rfunc) (object, GTK_VALUE_ENUM(args[0]), func_data);
396 gtk_container_marshal_signal_4 (GtkObject *object,
401 GtkContainerSignal4 rfunc;
404 rfunc = (GtkContainerSignal4) func;
405 return_val = GTK_RETLOC_BOOL (args[0]);
407 *return_val = (* rfunc) (object, func_data);
411 gtk_real_container_need_resize (GtkContainer *container)
413 g_return_val_if_fail (container != NULL, FALSE);
414 g_return_val_if_fail (GTK_IS_CONTAINER (container), FALSE);
416 if (GTK_WIDGET_VISIBLE (container) && container->widget.parent)
417 return gtk_container_need_resize (GTK_CONTAINER (container->widget.parent));
423 gtk_real_container_focus (GtkContainer *container,
424 GtkDirectionType direction)
431 g_return_val_if_fail (container != NULL, FALSE);
432 g_return_val_if_fail (GTK_IS_CONTAINER (container), FALSE);
434 /* Fail if the container is insensitive
436 if (!GTK_WIDGET_SENSITIVE (container))
441 if (GTK_WIDGET_CAN_FOCUS (container))
443 gtk_widget_grab_focus (GTK_WIDGET (container));
448 /* Get a list of the containers children
450 children = gtk_container_children (container);
454 /* Remove any children which are insensitive
459 if (!GTK_WIDGET_SENSITIVE (tmp_list->data))
461 tmp_list2 = tmp_list;
462 tmp_list = tmp_list->next;
464 children = g_list_remove_link (children, tmp_list2);
465 g_list_free_1 (tmp_list2);
468 tmp_list = tmp_list->next;
473 case GTK_DIR_TAB_FORWARD:
474 case GTK_DIR_TAB_BACKWARD:
475 return_val = gtk_container_focus_tab (container, children, direction);
479 return_val = gtk_container_focus_up_down (container, children, direction);
483 return_val = gtk_container_focus_left_right (container, children, direction);
487 g_list_free (children);
495 gtk_container_focus_tab (GtkContainer *container,
497 GtkDirectionType direction)
505 length = g_list_length (children);
507 /* sort the children in the y direction */
508 for (i = 1; i < length; i++)
511 tmp_list = g_list_nth (children, j);
512 child = tmp_list->data;
516 child2 = tmp_list->prev->data;
517 if (child->allocation.y < child2->allocation.y)
519 tmp_list->data = tmp_list->prev->data;
520 tmp_list = tmp_list->prev;
527 tmp_list->data = child;
530 /* sort the children in the x direction while
531 * maintaining the y direction sort.
533 for (i = 1; i < length; i++)
536 tmp_list = g_list_nth (children, j);
537 child = tmp_list->data;
541 child2 = tmp_list->prev->data;
542 if ((child->allocation.x < child2->allocation.x) &&
543 (child->allocation.y >= child2->allocation.y))
545 tmp_list->data = tmp_list->prev->data;
546 tmp_list = tmp_list->prev;
553 tmp_list->data = child;
556 /* if we are going backwards then reverse the order
559 if (direction == GTK_DIR_TAB_BACKWARD)
560 children = g_list_reverse (children);
562 return gtk_container_focus_move (container, children, direction);
566 gtk_container_focus_up_down (GtkContainer *container,
568 GtkDirectionType direction)
579 /* return failure if there isn't a focus child */
580 if (container->focus_child)
582 focus_width = container->focus_child->allocation.width / 2;
583 focus_x = container->focus_child->allocation.x + focus_width;
587 focus_width = GTK_WIDGET (container)->allocation.width;
588 if (GTK_WIDGET_NO_WINDOW (container))
589 focus_x = GTK_WIDGET (container)->allocation.x;
594 length = g_list_length (children);
596 /* sort the children in the y direction */
597 for (i = 1; i < length; i++)
600 tmp_list = g_list_nth (children, j);
601 child = tmp_list->data;
605 child2 = tmp_list->prev->data;
606 if (child->allocation.y < child2->allocation.y)
608 tmp_list->data = tmp_list->prev->data;
609 tmp_list = tmp_list->prev;
616 tmp_list->data = child;
619 /* sort the children in distance in the x direction
620 * in distance from the current focus child while maintaining the
621 * sort in the y direction
623 for (i = 1; i < length; i++)
626 tmp_list = g_list_nth (children, j);
627 child = tmp_list->data;
628 dist1 = (child->allocation.x + child->allocation.width / 2) - focus_x;
632 child2 = tmp_list->prev->data;
633 dist2 = (child2->allocation.x + child2->allocation.width / 2) - focus_x;
635 if ((dist1 < dist2) &&
636 (child->allocation.y >= child2->allocation.y))
638 tmp_list->data = tmp_list->prev->data;
639 tmp_list = tmp_list->prev;
646 tmp_list->data = child;
649 /* go and invalidate any widget which is too
650 * far from the focus widget.
652 if (!container->focus_child &&
653 (direction == GTK_DIR_UP))
654 focus_x += focus_width;
659 child = tmp_list->data;
661 dist1 = (child->allocation.x + child->allocation.width / 2) - focus_x;
662 if (((direction == GTK_DIR_DOWN) && (dist1 < 0)) ||
663 ((direction == GTK_DIR_UP) && (dist1 > 0)))
664 tmp_list->data = NULL;
666 tmp_list = tmp_list->next;
669 if (direction == GTK_DIR_UP)
670 children = g_list_reverse (children);
672 return gtk_container_focus_move (container, children, direction);
676 gtk_container_focus_left_right (GtkContainer *container,
678 GtkDirectionType direction)
689 /* return failure if there isn't a focus child */
690 if (container->focus_child)
692 focus_height = container->focus_child->allocation.height / 2;
693 focus_y = container->focus_child->allocation.y + focus_height;
697 focus_height = GTK_WIDGET (container)->allocation.height;
698 if (GTK_WIDGET_NO_WINDOW (container))
699 focus_y = GTK_WIDGET (container)->allocation.y;
704 length = g_list_length (children);
706 /* sort the children in the x direction */
707 for (i = 1; i < length; i++)
710 tmp_list = g_list_nth (children, j);
711 child = tmp_list->data;
715 child2 = tmp_list->prev->data;
716 if (child->allocation.x < child2->allocation.x)
718 tmp_list->data = tmp_list->prev->data;
719 tmp_list = tmp_list->prev;
726 tmp_list->data = child;
729 /* sort the children in distance in the y direction
730 * in distance from the current focus child while maintaining the
731 * sort in the x direction
733 for (i = 1; i < length; i++)
736 tmp_list = g_list_nth (children, j);
737 child = tmp_list->data;
738 dist1 = (child->allocation.y + child->allocation.height / 2) - focus_y;
742 child2 = tmp_list->prev->data;
743 dist2 = (child2->allocation.y + child2->allocation.height / 2) - focus_y;
745 if ((dist1 < dist2) &&
746 (child->allocation.x >= child2->allocation.x))
748 tmp_list->data = tmp_list->prev->data;
749 tmp_list = tmp_list->prev;
756 tmp_list->data = child;
759 /* go and invalidate any widget which is too
760 * far from the focus widget.
762 if (!container->focus_child &&
763 (direction == GTK_DIR_LEFT))
764 focus_y += focus_height;
769 child = tmp_list->data;
771 dist1 = (child->allocation.y + child->allocation.height / 2) - focus_y;
772 if (((direction == GTK_DIR_RIGHT) && (dist1 < 0)) ||
773 ((direction == GTK_DIR_LEFT) && (dist1 > 0)))
774 tmp_list->data = NULL;
776 tmp_list = tmp_list->next;
779 if (direction == GTK_DIR_LEFT)
780 children = g_list_reverse (children);
782 return gtk_container_focus_move (container, children, direction);
786 gtk_container_focus_move (GtkContainer *container,
788 GtkDirectionType direction)
790 GtkWidget *focus_child;
793 focus_child = container->focus_child;
794 container->focus_child = NULL;
798 child = children->data;
799 children = children->next;
806 if (focus_child == child)
810 if (GTK_WIDGET_VISIBLE (child) &&
811 GTK_IS_CONTAINER (child) &&
812 !GTK_WIDGET_HAS_FOCUS (child))
813 if (gtk_container_focus (GTK_CONTAINER (child), direction))
817 else if (GTK_WIDGET_VISIBLE (child))
819 if (GTK_WIDGET_CAN_FOCUS (child))
821 gtk_widget_grab_focus (child);
824 else if (GTK_IS_CONTAINER (child))
826 if (gtk_container_focus (GTK_CONTAINER (child), direction))
837 gtk_container_children_callback (GtkWidget *widget,
838 gpointer client_data)
842 children = (GList**) client_data;
843 *children = g_list_prepend (*children, widget);