]> Pileus Git - ~andy/gtk/blob - gtk/gtktable.c
new function gdk_window_at_pointer() to retrive the current GdkWindow the
[~andy/gtk] / gtk / gtktable.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
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.
8  *
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.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 #include "gtktable.h"
20
21 enum
22 {
23   ARG_0,
24   ARG_N_ROWS,
25   ARG_N_COLUMNS,
26   ARG_COLUMN_SPACING,
27   ARG_ROW_SPACING,
28   ARG_HOMOGENEOUS,
29 };
30
31 enum
32 {
33   CHILD_ARG_0,
34   CHILD_ARG_LEFT_ATTACH,
35   CHILD_ARG_RIGHT_ATTACH,
36   CHILD_ARG_TOP_ATTACH,
37   CHILD_ARG_BOTTOM_ATTACH,
38   CHILD_ARG_X_OPTIONS,
39   CHILD_ARG_Y_OPTIONS,
40   CHILD_ARG_X_PADDING,
41   CHILD_ARG_Y_PADDING
42 };
43   
44
45 static void gtk_table_class_init    (GtkTableClass  *klass);
46 static void gtk_table_init          (GtkTable       *table);
47 static void gtk_table_finalize      (GtkObject      *object);
48 static void gtk_table_map           (GtkWidget      *widget);
49 static void gtk_table_unmap         (GtkWidget      *widget);
50 static void gtk_table_draw          (GtkWidget      *widget,
51                                      GdkRectangle   *area);
52 static gint gtk_table_expose        (GtkWidget      *widget,
53                                      GdkEventExpose *event);
54 static void gtk_table_size_request  (GtkWidget      *widget,
55                                      GtkRequisition *requisition);
56 static void gtk_table_size_allocate (GtkWidget      *widget,
57                                      GtkAllocation  *allocation);
58 static void gtk_table_add           (GtkContainer   *container,
59                                      GtkWidget      *widget);
60 static void gtk_table_remove        (GtkContainer   *container,
61                                      GtkWidget      *widget);
62 static void gtk_table_foreach       (GtkContainer   *container,
63                                      GtkCallback     callback,
64                                      gpointer        callback_data);
65 static void gtk_table_get_arg       (GtkObject      *object,
66                                      GtkArg         *arg,
67                                      guint           arg_id);
68 static void gtk_table_set_arg       (GtkObject      *object,
69                                      GtkArg         *arg,
70                                      guint           arg_id);
71 static void gtk_table_set_child_arg (GtkContainer   *container,
72                                      GtkWidget      *child,
73                                      GtkArg         *arg,
74                                      guint           arg_id);
75 static void gtk_table_get_child_arg (GtkContainer   *container,
76                                      GtkWidget      *child,
77                                      GtkArg         *arg,
78                                      guint           arg_id);
79 static GtkType gtk_table_child_type (GtkContainer   *container);
80
81
82 static void gtk_table_size_request_init  (GtkTable *table);
83 static void gtk_table_size_request_pass1 (GtkTable *table);
84 static void gtk_table_size_request_pass2 (GtkTable *table);
85 static void gtk_table_size_request_pass3 (GtkTable *table);
86
87 static void gtk_table_size_allocate_init  (GtkTable *table);
88 static void gtk_table_size_allocate_pass1 (GtkTable *table);
89 static void gtk_table_size_allocate_pass2 (GtkTable *table);
90
91
92 static GtkContainerClass *parent_class = NULL;
93
94
95 GtkType
96 gtk_table_get_type (void)
97 {
98   static GtkType table_type = 0;
99   
100   if (!table_type)
101     {
102       GtkTypeInfo table_info =
103       {
104         "GtkTable",
105         sizeof (GtkTable),
106         sizeof (GtkTableClass),
107         (GtkClassInitFunc) gtk_table_class_init,
108         (GtkObjectInitFunc) gtk_table_init,
109         gtk_table_set_arg,
110         gtk_table_get_arg,
111       };
112       
113       table_type = gtk_type_unique (gtk_container_get_type (), &table_info);
114     }
115   
116   return table_type;
117 }
118
119 static void
120 gtk_table_class_init (GtkTableClass *class)
121 {
122   GtkObjectClass *object_class;
123   GtkWidgetClass *widget_class;
124   GtkContainerClass *container_class;
125   
126   object_class = (GtkObjectClass*) class;
127   widget_class = (GtkWidgetClass*) class;
128   container_class = (GtkContainerClass*) class;
129   
130   parent_class = gtk_type_class (gtk_container_get_type ());
131   
132   gtk_object_add_arg_type ("GtkTable::n_rows", GTK_TYPE_UINT, GTK_ARG_READWRITE, ARG_N_ROWS);
133   gtk_object_add_arg_type ("GtkTable::n_columns", GTK_TYPE_UINT, GTK_ARG_READWRITE, ARG_N_COLUMNS);
134   gtk_object_add_arg_type ("GtkTable::row_spacing", GTK_TYPE_UINT, GTK_ARG_READWRITE, ARG_ROW_SPACING);
135   gtk_object_add_arg_type ("GtkTable::column_spacing", GTK_TYPE_UINT, GTK_ARG_READWRITE, ARG_COLUMN_SPACING);
136   gtk_object_add_arg_type ("GtkTable::homogeneous", GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_HOMOGENEOUS);
137   gtk_container_add_child_arg_type ("GtkTable::left_attach", GTK_TYPE_UINT, GTK_ARG_READWRITE, CHILD_ARG_LEFT_ATTACH);
138   gtk_container_add_child_arg_type ("GtkTable::right_attach", GTK_TYPE_UINT, GTK_ARG_READWRITE, CHILD_ARG_RIGHT_ATTACH);
139   gtk_container_add_child_arg_type ("GtkTable::top_attach", GTK_TYPE_UINT, GTK_ARG_READWRITE, CHILD_ARG_TOP_ATTACH);
140   gtk_container_add_child_arg_type ("GtkTable::bottom_attach", GTK_TYPE_UINT, GTK_ARG_READWRITE, CHILD_ARG_BOTTOM_ATTACH);
141   gtk_container_add_child_arg_type ("GtkTable::x_options", GTK_TYPE_ATTACH_OPTIONS, GTK_ARG_READWRITE, CHILD_ARG_X_OPTIONS);
142   gtk_container_add_child_arg_type ("GtkTable::y_options", GTK_TYPE_ATTACH_OPTIONS, GTK_ARG_READWRITE, CHILD_ARG_Y_OPTIONS);
143   gtk_container_add_child_arg_type ("GtkTable::x_padding", GTK_TYPE_UINT, GTK_ARG_READWRITE, CHILD_ARG_X_PADDING);
144   gtk_container_add_child_arg_type ("GtkTable::y_padding", GTK_TYPE_UINT, GTK_ARG_READWRITE, CHILD_ARG_Y_PADDING);
145
146   object_class->finalize = gtk_table_finalize;
147   
148   widget_class->map = gtk_table_map;
149   widget_class->unmap = gtk_table_unmap;
150   widget_class->draw = gtk_table_draw;
151   widget_class->expose_event = gtk_table_expose;
152   widget_class->size_request = gtk_table_size_request;
153   widget_class->size_allocate = gtk_table_size_allocate;
154   
155   container_class->add = gtk_table_add;
156   container_class->remove = gtk_table_remove;
157   container_class->foreach = gtk_table_foreach;
158   container_class->child_type = gtk_table_child_type;
159   container_class->set_child_arg = gtk_table_set_child_arg;
160   container_class->get_child_arg = gtk_table_get_child_arg;
161 }
162
163 static GtkType
164 gtk_table_child_type (GtkContainer   *container)
165 {
166   return GTK_TYPE_WIDGET;
167 }
168
169 static void
170 gtk_table_get_arg (GtkObject      *object,
171                    GtkArg         *arg,
172                    guint           arg_id)
173 {
174   GtkTable *table;
175
176   table = GTK_TABLE (object);
177
178   switch (arg_id)
179     {
180     case ARG_N_ROWS:
181       GTK_VALUE_UINT (*arg) = table->nrows;
182       break;
183     case ARG_N_COLUMNS:
184       GTK_VALUE_UINT (*arg) = table->ncols;
185       break;
186     case ARG_ROW_SPACING:
187       GTK_VALUE_UINT (*arg) = table->row_spacing;
188       break;
189     case ARG_COLUMN_SPACING:
190       GTK_VALUE_UINT (*arg) = table->column_spacing;
191       break;
192     case ARG_HOMOGENEOUS:
193       GTK_VALUE_BOOL (*arg) = table->homogeneous;
194       break;
195     default:
196       arg->type = GTK_TYPE_INVALID;
197       break;
198     }
199 }
200
201 static void
202 gtk_table_set_arg (GtkObject      *object,
203                    GtkArg         *arg,
204                    guint           arg_id)
205 {
206   GtkTable *table;
207
208   table = GTK_TABLE (object);
209
210   switch (arg_id)
211     {
212     case ARG_N_ROWS:
213       gtk_table_resize (table, GTK_VALUE_UINT (*arg), table->ncols);
214       break;
215     case ARG_N_COLUMNS:
216       gtk_table_resize (table, table->nrows, GTK_VALUE_UINT (*arg));
217       break;
218     case ARG_ROW_SPACING:
219       gtk_table_set_row_spacings (table, GTK_VALUE_UINT (*arg));
220       break;
221     case ARG_COLUMN_SPACING:
222       gtk_table_set_col_spacings (table, GTK_VALUE_UINT (*arg));
223       break;
224     case ARG_HOMOGENEOUS:
225       gtk_table_set_homogeneous (table, GTK_VALUE_BOOL (*arg));
226       break;
227     default:
228       break;
229     }
230 }
231
232 static void
233 gtk_table_set_child_arg (GtkContainer   *container,
234                          GtkWidget      *child,
235                          GtkArg         *arg,
236                          guint           arg_id)
237 {
238   GtkTable *table;
239   GtkTableChild *table_child;
240   GList *list;
241
242   table = GTK_TABLE (container);
243   table_child = NULL;
244   for (list = table->children; list; list = list->next)
245     {
246       table_child = list->data;
247
248       if (table_child->widget == child)
249         break;
250     }
251   if (!list)
252     return;
253
254   switch (arg_id)
255     {
256     case CHILD_ARG_LEFT_ATTACH:
257       if (GTK_VALUE_UINT (*arg) < table_child->right_attach)
258         table_child->left_attach = GTK_VALUE_UINT (*arg);
259       break;
260     case CHILD_ARG_RIGHT_ATTACH:
261       if (GTK_VALUE_UINT (*arg) > table_child->left_attach)
262         table_child->right_attach = GTK_VALUE_UINT (*arg);
263       if (table_child->right_attach >= table->ncols)
264         gtk_table_resize (table, table->ncols, table_child->right_attach);
265       break;
266     case CHILD_ARG_TOP_ATTACH:
267       if (GTK_VALUE_UINT (*arg) < table_child->bottom_attach)
268         table_child->top_attach = GTK_VALUE_UINT (*arg);
269       break;
270     case CHILD_ARG_BOTTOM_ATTACH:
271       if (GTK_VALUE_UINT (*arg) > table_child->top_attach)
272         table_child->bottom_attach = GTK_VALUE_UINT (*arg);
273       if (table_child->bottom_attach >= table->nrows)
274         gtk_table_resize (table, table_child->bottom_attach, table->ncols);
275       break;
276     case CHILD_ARG_X_OPTIONS:
277       table_child->xexpand = (GTK_VALUE_FLAGS (*arg) & GTK_EXPAND) != 0;
278       table_child->xshrink = (GTK_VALUE_FLAGS (*arg) & GTK_SHRINK) != 0;
279       table_child->xfill = (GTK_VALUE_FLAGS (*arg) & GTK_FILL) != 0;
280       break;
281     case CHILD_ARG_Y_OPTIONS:
282       table_child->yexpand = (GTK_VALUE_FLAGS (*arg) & GTK_EXPAND) != 0;
283       table_child->yshrink = (GTK_VALUE_FLAGS (*arg) & GTK_SHRINK) != 0;
284       table_child->yfill = (GTK_VALUE_FLAGS (*arg) & GTK_FILL) != 0;
285       break;
286     case CHILD_ARG_X_PADDING:
287       table_child->xpadding = GTK_VALUE_UINT (*arg);
288       break;
289     case CHILD_ARG_Y_PADDING:
290       table_child->ypadding = GTK_VALUE_UINT (*arg);
291       break;
292     default:
293       break;
294     }
295   if (GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_VISIBLE (table))
296     gtk_widget_queue_resize (GTK_WIDGET (table));
297 }
298
299 static void
300 gtk_table_get_child_arg (GtkContainer   *container,
301                          GtkWidget      *child,
302                          GtkArg         *arg,
303                          guint           arg_id)
304 {
305   GtkTable *table;
306   GtkTableChild *table_child;
307   GList *list;
308
309   table = GTK_TABLE (container);
310   table_child = NULL;
311   for (list = table->children; list; list = list->next)
312     {
313       table_child = list->data;
314
315       if (table_child->widget == child)
316         break;
317     }
318   if (!list)
319     return;
320
321   switch (arg_id)
322     {
323     case CHILD_ARG_LEFT_ATTACH:
324       GTK_VALUE_UINT (*arg) = table_child->left_attach;
325       break;
326     case CHILD_ARG_RIGHT_ATTACH:
327       GTK_VALUE_UINT (*arg) = table_child->right_attach;
328       break;
329     case CHILD_ARG_TOP_ATTACH:
330       GTK_VALUE_UINT (*arg) = table_child->top_attach;
331       break;
332     case CHILD_ARG_BOTTOM_ATTACH:
333       GTK_VALUE_UINT (*arg) = table_child->bottom_attach;
334       break;
335     case CHILD_ARG_X_OPTIONS:
336       GTK_VALUE_FLAGS (*arg) = (table_child->xexpand * GTK_EXPAND |
337                                 table_child->xshrink * GTK_SHRINK |
338                                 table_child->xfill * GTK_FILL);
339       break;
340     case CHILD_ARG_Y_OPTIONS:
341       GTK_VALUE_FLAGS (*arg) = (table_child->yexpand * GTK_EXPAND |
342                                 table_child->yshrink * GTK_SHRINK |
343                                 table_child->yfill * GTK_FILL);
344       break;
345     case CHILD_ARG_X_PADDING:
346       GTK_VALUE_UINT (*arg) = table_child->xpadding;
347       break;
348     case CHILD_ARG_Y_PADDING:
349       GTK_VALUE_UINT (*arg) = table_child->ypadding;
350       break;
351     default:
352       arg->type = GTK_TYPE_INVALID;
353       break;
354     }
355 }
356
357 static void
358 gtk_table_init (GtkTable *table)
359 {
360   GTK_WIDGET_SET_FLAGS (table, GTK_NO_WINDOW | GTK_BASIC);
361   
362   table->children = NULL;
363   table->rows = NULL;
364   table->cols = NULL;
365   table->nrows = 0;
366   table->ncols = 0;
367   table->column_spacing = 0;
368   table->row_spacing = 0;
369   table->homogeneous = FALSE;
370
371   gtk_table_resize (table, 1, 1);
372 }
373
374 GtkWidget*
375 gtk_table_new (guint    rows,
376                guint    columns,
377                gboolean homogeneous)
378 {
379   GtkTable *table;
380
381   g_return_val_if_fail (rows >= 1, NULL);
382   g_return_val_if_fail (columns >= 1, NULL);
383   
384   table = gtk_type_new (gtk_table_get_type ());
385   
386   table->homogeneous = (homogeneous ? TRUE : FALSE);
387
388   gtk_table_resize (table, rows, columns);
389   
390   return GTK_WIDGET (table);
391 }
392
393 void
394 gtk_table_resize (GtkTable *table,
395                   guint     n_rows,
396                   guint     n_cols)
397 {
398   g_return_if_fail (table != NULL);
399   g_return_if_fail (GTK_IS_TABLE (table));
400
401   n_rows = MAX (n_rows, 1);
402   n_cols = MAX (n_cols, 1);
403   
404   if (n_rows != table->nrows ||
405       n_cols != table->ncols)
406     {
407       GList *list;
408       
409       for (list = table->children; list; list = list->next)
410         {
411           GtkTableChild *child;
412           
413           child = list->data;
414           
415           n_rows = MAX (n_rows, child->bottom_attach);
416           n_cols = MAX (n_cols, child->right_attach);
417         }
418       
419       if (n_rows != table->nrows)
420         {
421           guint i;
422
423           i = table->nrows;
424           table->nrows = n_rows;
425           table->rows = g_realloc (table->rows, table->nrows * sizeof (GtkTableRowCol));
426           
427           for (; i < table->nrows; i++)
428             {
429               table->rows[i].requisition = 0;
430               table->rows[i].allocation = 0;
431               table->rows[i].spacing = table->row_spacing;
432               table->rows[i].need_expand = 0;
433               table->rows[i].need_shrink = 0;
434               table->rows[i].expand = 0;
435               table->rows[i].shrink = 0;
436             }
437         }
438
439       if (n_cols != table->ncols)
440         {
441           guint i;
442
443           i = table->ncols;
444           table->ncols = n_cols;
445           table->cols = g_realloc (table->cols, table->ncols * sizeof (GtkTableRowCol));
446           
447           for (; i < table->ncols; i++)
448             {
449               table->cols[i].requisition = 0;
450               table->cols[i].allocation = 0;
451               table->cols[i].spacing = table->column_spacing;
452               table->cols[i].need_expand = 0;
453               table->cols[i].need_shrink = 0;
454               table->cols[i].expand = 0;
455               table->cols[i].shrink = 0;
456             }
457         }
458     }
459 }
460
461 void
462 gtk_table_attach (GtkTable        *table,
463                   GtkWidget       *child,
464                   guint            left_attach,
465                   guint            right_attach,
466                   guint            top_attach,
467                   guint            bottom_attach,
468                   GtkAttachOptions xoptions,
469                   GtkAttachOptions yoptions,
470                   guint            xpadding,
471                   guint            ypadding)
472 {
473   GtkTableChild *table_child;
474   
475   g_return_if_fail (table != NULL);
476   g_return_if_fail (GTK_IS_TABLE (table));
477   g_return_if_fail (child != NULL);
478   g_return_if_fail (GTK_IS_WIDGET (child));
479   g_return_if_fail (child->parent == NULL);
480   
481   /* g_return_if_fail (left_attach >= 0); */
482   g_return_if_fail (left_attach < right_attach);
483   /* g_return_if_fail (top_attach >= 0); */
484   g_return_if_fail (top_attach < bottom_attach);
485   
486   if (right_attach >= table->ncols)
487     gtk_table_resize (table, table->nrows, right_attach);
488   
489   if (bottom_attach >= table->nrows)
490     gtk_table_resize (table, bottom_attach, table->ncols);
491   
492   table_child = g_new (GtkTableChild, 1);
493   table_child->widget = child;
494   table_child->left_attach = left_attach;
495   table_child->right_attach = right_attach;
496   table_child->top_attach = top_attach;
497   table_child->bottom_attach = bottom_attach;
498   table_child->xexpand = (xoptions & GTK_EXPAND) != 0;
499   table_child->xshrink = (xoptions & GTK_SHRINK) != 0;
500   table_child->xfill = (xoptions & GTK_FILL) != 0;
501   table_child->xpadding = xpadding;
502   table_child->yexpand = (yoptions & GTK_EXPAND) != 0;
503   table_child->yshrink = (yoptions & GTK_SHRINK) != 0;
504   table_child->yfill = (yoptions & GTK_FILL) != 0;
505   table_child->ypadding = ypadding;
506   
507   table->children = g_list_prepend (table->children, table_child);
508   
509   gtk_widget_set_parent (child, GTK_WIDGET (table));
510   
511   if (GTK_WIDGET_VISIBLE (GTK_WIDGET (table)))
512     {
513       if (GTK_WIDGET_REALIZED (GTK_WIDGET (table)) &&
514           !GTK_WIDGET_REALIZED (child))
515         gtk_widget_realize (child);
516       
517       if (GTK_WIDGET_MAPPED (GTK_WIDGET (table)) &&
518           !GTK_WIDGET_MAPPED (child))
519         gtk_widget_map (child);
520     }
521   
522   if (GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_VISIBLE (table))
523     gtk_widget_queue_resize (GTK_WIDGET (table));
524 }
525
526 void
527 gtk_table_attach_defaults (GtkTable  *table,
528                            GtkWidget *widget,
529                            guint      left_attach,
530                            guint      right_attach,
531                            guint      top_attach,
532                            guint      bottom_attach)
533 {
534   gtk_table_attach (table, widget,
535                     left_attach, right_attach,
536                     top_attach, bottom_attach,
537                     GTK_EXPAND | GTK_FILL,
538                     GTK_EXPAND | GTK_FILL,
539                     0, 0);
540 }
541
542 void
543 gtk_table_set_row_spacing (GtkTable *table,
544                            guint     row,
545                            guint     spacing)
546 {
547   g_return_if_fail (table != NULL);
548   g_return_if_fail (GTK_IS_TABLE (table));
549   g_return_if_fail (row + 1 < table->nrows);
550   
551   if (table->rows[row].spacing != spacing)
552     {
553       table->rows[row].spacing = spacing;
554       
555       if (GTK_WIDGET_VISIBLE (table))
556         gtk_widget_queue_resize (GTK_WIDGET (table));
557     }
558 }
559
560 void
561 gtk_table_set_col_spacing (GtkTable *table,
562                            guint     column,
563                            guint     spacing)
564 {
565   g_return_if_fail (table != NULL);
566   g_return_if_fail (GTK_IS_TABLE (table));
567   g_return_if_fail (column + 1 < table->ncols);
568   
569   if (table->cols[column].spacing != spacing)
570     {
571       table->cols[column].spacing = spacing;
572       
573       if (GTK_WIDGET_VISIBLE (table))
574         gtk_widget_queue_resize (GTK_WIDGET (table));
575     }
576 }
577
578 void
579 gtk_table_set_row_spacings (GtkTable *table,
580                             guint     spacing)
581 {
582   guint row;
583   
584   g_return_if_fail (table != NULL);
585   g_return_if_fail (GTK_IS_TABLE (table));
586   
587   table->row_spacing = spacing;
588   for (row = 0; row + 1 < table->nrows; row++)
589     table->rows[row].spacing = spacing;
590   
591   if (GTK_WIDGET_VISIBLE (table))
592     gtk_widget_queue_resize (GTK_WIDGET (table));
593 }
594
595 void
596 gtk_table_set_col_spacings (GtkTable *table,
597                             guint     spacing)
598 {
599   guint col;
600   
601   g_return_if_fail (table != NULL);
602   g_return_if_fail (GTK_IS_TABLE (table));
603   
604   table->column_spacing = spacing;
605   for (col = 0; col + 1 < table->ncols; col++)
606     table->cols[col].spacing = spacing;
607   
608   if (GTK_WIDGET_VISIBLE (table))
609     gtk_widget_queue_resize (GTK_WIDGET (table));
610 }
611
612 void
613 gtk_table_set_homogeneous (GtkTable *table,
614                            gboolean  homogeneous)
615 {
616   g_return_if_fail (table != NULL);
617   g_return_if_fail (GTK_IS_TABLE (table));
618
619   homogeneous = (homogeneous != 0);
620   if (homogeneous != table->homogeneous)
621     {
622       table->homogeneous = homogeneous;
623       
624       if (GTK_WIDGET_VISIBLE (table))
625         gtk_widget_queue_resize (GTK_WIDGET (table));
626     }
627 }
628
629 static void
630 gtk_table_finalize (GtkObject *object)
631 {
632   GtkTable *table;
633   
634   g_return_if_fail (object != NULL);
635   g_return_if_fail (GTK_IS_TABLE (object));
636   
637   table = GTK_TABLE (object);
638   
639   g_free (table->rows);
640   g_free (table->cols);
641   
642   (* GTK_OBJECT_CLASS (parent_class)->finalize) (object);
643 }
644
645 static void
646 gtk_table_map (GtkWidget *widget)
647 {
648   GtkTable *table;
649   GtkTableChild *child;
650   GList *children;
651   
652   g_return_if_fail (widget != NULL);
653   g_return_if_fail (GTK_IS_TABLE (widget));
654   
655   table = GTK_TABLE (widget);
656   GTK_WIDGET_SET_FLAGS (table, GTK_MAPPED);
657   
658   children = table->children;
659   while (children)
660     {
661       child = children->data;
662       children = children->next;
663       
664       if (GTK_WIDGET_VISIBLE (child->widget) &&
665           !GTK_WIDGET_MAPPED (child->widget))
666         gtk_widget_map (child->widget);
667     }
668 }
669
670 static void
671 gtk_table_unmap (GtkWidget *widget)
672 {
673   GtkTable *table;
674   GtkTableChild *child;
675   GList *children;
676   
677   g_return_if_fail (widget != NULL);
678   g_return_if_fail (GTK_IS_TABLE (widget));
679   
680   table = GTK_TABLE (widget);
681   GTK_WIDGET_UNSET_FLAGS (table, GTK_MAPPED);
682   
683   children = table->children;
684   while (children)
685     {
686       child = children->data;
687       children = children->next;
688       
689       if (GTK_WIDGET_VISIBLE (child->widget) &&
690           GTK_WIDGET_MAPPED (child->widget))
691         gtk_widget_unmap (child->widget);
692     }
693 }
694
695 static void
696 gtk_table_draw (GtkWidget    *widget,
697                 GdkRectangle *area)
698 {
699   GtkTable *table;
700   GtkTableChild *child;
701   GList *children;
702   GdkRectangle child_area;
703   
704   g_return_if_fail (widget != NULL);
705   g_return_if_fail (GTK_IS_TABLE (widget));
706   
707   if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget))
708     {
709       table = GTK_TABLE (widget);
710       
711       children = table->children;
712       while (children)
713         {
714           child = children->data;
715           children = children->next;
716           
717           if (gtk_widget_intersect (child->widget, area, &child_area))
718             gtk_widget_draw (child->widget, &child_area);
719         }
720     }
721 }
722
723 static gint
724 gtk_table_expose (GtkWidget      *widget,
725                   GdkEventExpose *event)
726 {
727   GtkTable *table;
728   GtkTableChild *child;
729   GList *children;
730   GdkEventExpose child_event;
731   
732   g_return_val_if_fail (widget != NULL, FALSE);
733   g_return_val_if_fail (GTK_IS_TABLE (widget), FALSE);
734   
735   if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget))
736     {
737       table = GTK_TABLE (widget);
738       
739       child_event = *event;
740       
741       children = table->children;
742       while (children)
743         {
744           child = children->data;
745           children = children->next;
746           
747           if (GTK_WIDGET_NO_WINDOW (child->widget) &&
748               gtk_widget_intersect (child->widget, &event->area, &child_event.area))
749             gtk_widget_event (child->widget, (GdkEvent*) &child_event);
750         }
751     }
752   
753   return FALSE;
754 }
755
756 static void
757 gtk_table_size_request (GtkWidget      *widget,
758                         GtkRequisition *requisition)
759 {
760   GtkTable *table;
761   gint row, col;
762   
763   g_return_if_fail (widget != NULL);
764   g_return_if_fail (GTK_IS_TABLE (widget));
765   g_return_if_fail (requisition != NULL);
766   
767   table = GTK_TABLE (widget);
768   
769   requisition->width = 0;
770   requisition->height = 0;
771   
772   gtk_table_size_request_init (table);
773   gtk_table_size_request_pass1 (table);
774   gtk_table_size_request_pass2 (table);
775   gtk_table_size_request_pass3 (table);
776   gtk_table_size_request_pass2 (table);
777   
778   for (col = 0; col < table->ncols; col++)
779     requisition->width += table->cols[col].requisition;
780   for (col = 0; col + 1 < table->ncols; col++)
781     requisition->width += table->cols[col].spacing;
782   
783   for (row = 0; row < table->nrows; row++)
784     requisition->height += table->rows[row].requisition;
785   for (row = 0; row + 1 < table->nrows; row++)
786     requisition->height += table->rows[row].spacing;
787   
788   requisition->width += GTK_CONTAINER (table)->border_width * 2;
789   requisition->height += GTK_CONTAINER (table)->border_width * 2;
790 }
791
792 static void
793 gtk_table_size_allocate (GtkWidget     *widget,
794                          GtkAllocation *allocation)
795 {
796   GtkTable *table;
797   
798   g_return_if_fail (widget != NULL);
799   g_return_if_fail (GTK_IS_TABLE (widget));
800   g_return_if_fail (allocation != NULL);
801   
802   widget->allocation = *allocation;
803   table = GTK_TABLE (widget);
804   
805   gtk_table_size_allocate_init (table);
806   gtk_table_size_allocate_pass1 (table);
807   gtk_table_size_allocate_pass2 (table);
808 }
809
810 static void
811 gtk_table_add (GtkContainer *container,
812                GtkWidget    *widget)
813 {
814   g_return_if_fail (container != NULL);
815   g_return_if_fail (GTK_IS_TABLE (container));
816   g_return_if_fail (widget != NULL);
817   
818   gtk_table_attach_defaults (GTK_TABLE (container), widget, 0, 1, 0, 1);
819 }
820
821 static void
822 gtk_table_remove (GtkContainer *container,
823                   GtkWidget    *widget)
824 {
825   GtkTable *table;
826   GtkTableChild *child;
827   GList *children;
828   
829   g_return_if_fail (container != NULL);
830   g_return_if_fail (GTK_IS_TABLE (container));
831   g_return_if_fail (widget != NULL);
832   
833   table = GTK_TABLE (container);
834   children = table->children;
835   
836   while (children)
837     {
838       child = children->data;
839       children = children->next;
840       
841       if (child->widget == widget)
842         {
843           gboolean was_visible = GTK_WIDGET_VISIBLE (widget);
844           
845           gtk_widget_unparent (widget);
846           
847           table->children = g_list_remove (table->children, child);
848           g_free (child);
849           
850           if (was_visible && GTK_WIDGET_VISIBLE (container))
851             gtk_widget_queue_resize (GTK_WIDGET (container));
852           break;
853         }
854     }
855 }
856
857 static void
858 gtk_table_foreach (GtkContainer *container,
859                    GtkCallback     callback,
860                    gpointer        callback_data)
861 {
862   GtkTable *table;
863   GtkTableChild *child;
864   GList *children;
865   
866   g_return_if_fail (container != NULL);
867   g_return_if_fail (GTK_IS_TABLE (container));
868   g_return_if_fail (callback != NULL);
869   
870   table = GTK_TABLE (container);
871   children = table->children;
872   
873   while (children)
874     {
875       child = children->data;
876       children = children->next;
877       
878       (* callback) (child->widget, callback_data);
879     }
880 }
881
882 static void
883 gtk_table_size_request_init (GtkTable *table)
884 {
885   GtkTableChild *child;
886   GList *children;
887   gint row, col;
888   
889   for (row = 0; row < table->nrows; row++)
890     table->rows[row].requisition = 0;
891   for (col = 0; col < table->ncols; col++)
892     table->cols[col].requisition = 0;
893   
894   children = table->children;
895   while (children)
896     {
897       child = children->data;
898       children = children->next;
899       
900       if (GTK_WIDGET_VISIBLE (child->widget))
901         gtk_widget_size_request (child->widget, &child->widget->requisition);
902     }
903 }
904
905 static void
906 gtk_table_size_request_pass1 (GtkTable *table)
907 {
908   GtkTableChild *child;
909   GList *children;
910   gint width;
911   gint height;
912   
913   children = table->children;
914   while (children)
915     {
916       child = children->data;
917       children = children->next;
918       
919       if (GTK_WIDGET_VISIBLE (child->widget))
920         {
921           /* Child spans a single column.
922            */
923           if (child->left_attach == (child->right_attach - 1))
924             {
925               width = child->widget->requisition.width + child->xpadding * 2;
926               table->cols[child->left_attach].requisition = MAX (table->cols[child->left_attach].requisition, width);
927             }
928           
929           /* Child spans a single row.
930            */
931           if (child->top_attach == (child->bottom_attach - 1))
932             {
933               height = child->widget->requisition.height + child->ypadding * 2;
934               table->rows[child->top_attach].requisition = MAX (table->rows[child->top_attach].requisition, height);
935             }
936         }
937     }
938 }
939
940 static void
941 gtk_table_size_request_pass2 (GtkTable *table)
942 {
943   gint max_width;
944   gint max_height;
945   gint row, col;
946   
947   if (table->homogeneous)
948     {
949       max_width = 0;
950       max_height = 0;
951       
952       for (col = 0; col < table->ncols; col++)
953         max_width = MAX (max_width, table->cols[col].requisition);
954       for (row = 0; row < table->nrows; row++)
955         max_height = MAX (max_height, table->rows[row].requisition);
956       
957       for (col = 0; col < table->ncols; col++)
958         table->cols[col].requisition = max_width;
959       for (row = 0; row < table->nrows; row++)
960         table->rows[row].requisition = max_height;
961     }
962 }
963
964 static void
965 gtk_table_size_request_pass3 (GtkTable *table)
966 {
967   GtkTableChild *child;
968   GList *children;
969   gint width, height;
970   gint row, col;
971   gint extra;
972   
973   children = table->children;
974   while (children)
975     {
976       child = children->data;
977       children = children->next;
978       
979       if (GTK_WIDGET_VISIBLE (child->widget))
980         {
981           /* Child spans multiple columns.
982            */
983           if (child->left_attach != (child->right_attach - 1))
984             {
985               /* Check and see if there is already enough space
986                *  for the child.
987                */
988               width = 0;
989               for (col = child->left_attach; col < child->right_attach; col++)
990                 {
991                   width += table->cols[col].requisition;
992                   if ((col + 1) < child->right_attach)
993                     width += table->cols[col].spacing;
994                 }
995               
996               /* If we need to request more space for this child to fill
997                *  its requisition, then divide up the needed space evenly
998                *  amongst the columns it spans.
999                */
1000               if (width < child->widget->requisition.width + child->xpadding * 2)
1001                 {
1002                   width = child->widget->requisition.width + child->xpadding * 2 - width;
1003                   
1004                   for (col = child->left_attach; col < child->right_attach; col++)
1005                     {
1006                       extra = width / (child->right_attach - col);
1007                       table->cols[col].requisition += extra;
1008                       width -= extra;
1009                     }
1010                 }
1011             }
1012           
1013           /* Child spans multiple rows.
1014            */
1015           if (child->top_attach != (child->bottom_attach - 1))
1016             {
1017               /* Check and see if there is already enough space
1018                *  for the child.
1019                */
1020               height = 0;
1021               for (row = child->top_attach; row < child->bottom_attach; row++)
1022                 {
1023                   height += table->rows[row].requisition;
1024                   if ((row + 1) < child->bottom_attach)
1025                     height += table->rows[row].spacing;
1026                 }
1027               
1028               /* If we need to request more space for this child to fill
1029                *  its requisition, then divide up the needed space evenly
1030                *  amongst the columns it spans.
1031                */
1032               if (height < child->widget->requisition.height + child->ypadding * 2)
1033                 {
1034                   height = child->widget->requisition.height + child->ypadding * 2 - height;
1035                   
1036                   for (row = child->top_attach; row < child->bottom_attach; row++)
1037                     {
1038                       extra = height / (child->bottom_attach - row);
1039                       table->rows[row].requisition += extra;
1040                       height -= extra;
1041                     }
1042                 }
1043             }
1044         }
1045     }
1046 }
1047
1048 static void
1049 gtk_table_size_allocate_init (GtkTable *table)
1050 {
1051   GtkTableChild *child;
1052   GList *children;
1053   gint row, col;
1054   gint has_expand;
1055   gint has_shrink;
1056   
1057   /* Initialize the rows and cols.
1058    *  By default, rows and cols do not expand and do shrink.
1059    *  Those values are modified by the children that occupy
1060    *  the rows and cols.
1061    */
1062   for (col = 0; col < table->ncols; col++)
1063     {
1064       table->cols[col].allocation = table->cols[col].requisition;
1065       table->cols[col].need_expand = FALSE;
1066       table->cols[col].need_shrink = TRUE;
1067       table->cols[col].expand = FALSE;
1068       table->cols[col].shrink = TRUE;
1069     }
1070   for (row = 0; row < table->nrows; row++)
1071     {
1072       table->rows[row].allocation = table->rows[row].requisition;
1073       table->rows[row].need_expand = FALSE;
1074       table->rows[row].need_shrink = TRUE;
1075       table->rows[row].expand = FALSE;
1076       table->rows[row].shrink = TRUE;
1077     }
1078   
1079   /* Loop over all the children and adjust the row and col values
1080    *  based on whether the children want to be allowed to expand
1081    *  or shrink. This loop handles children that occupy a single
1082    *  row or column.
1083    */
1084   children = table->children;
1085   while (children)
1086     {
1087       child = children->data;
1088       children = children->next;
1089       
1090       if (GTK_WIDGET_VISIBLE (child->widget))
1091         {
1092           if (child->left_attach == (child->right_attach - 1))
1093             {
1094               if (child->xexpand)
1095                 table->cols[child->left_attach].expand = TRUE;
1096               
1097               if (!child->xshrink)
1098                 table->cols[child->left_attach].shrink = FALSE;
1099             }
1100           
1101           if (child->top_attach == (child->bottom_attach - 1))
1102             {
1103               if (child->yexpand)
1104                 table->rows[child->top_attach].expand = TRUE;
1105               
1106               if (!child->yshrink)
1107                 table->rows[child->top_attach].shrink = FALSE;
1108             }
1109         }
1110     }
1111   
1112   /* Loop over all the children again and this time handle children
1113    *  which span multiple rows or columns.
1114    */
1115   children = table->children;
1116   while (children)
1117     {
1118       child = children->data;
1119       children = children->next;
1120       
1121       if (GTK_WIDGET_VISIBLE (child->widget))
1122         {
1123           if (child->left_attach != (child->right_attach - 1))
1124             {
1125               if (child->xexpand)
1126                 {
1127                   has_expand = FALSE;
1128                   for (col = child->left_attach; col < child->right_attach; col++)
1129                     if (table->cols[col].expand)
1130                       {
1131                         has_expand = TRUE;
1132                         break;
1133                       }
1134                   
1135                   if (!has_expand)
1136                     for (col = child->left_attach; col < child->right_attach; col++)
1137                       table->cols[col].need_expand = TRUE;
1138                 }
1139               
1140               if (!child->xshrink)
1141                 {
1142                   has_shrink = TRUE;
1143                   for (col = child->left_attach; col < child->right_attach; col++)
1144                     if (!table->cols[col].shrink)
1145                       {
1146                         has_shrink = FALSE;
1147                         break;
1148                       }
1149                   
1150                   if (has_shrink)
1151                     for (col = child->left_attach; col < child->right_attach; col++)
1152                       table->cols[col].need_shrink = FALSE;
1153                 }
1154             }
1155           
1156           if (child->top_attach != (child->bottom_attach - 1))
1157             {
1158               if (child->yexpand)
1159                 {
1160                   has_expand = FALSE;
1161                   for (row = child->top_attach; row < child->bottom_attach; row++)
1162                     if (table->rows[row].expand)
1163                       {
1164                         has_expand = TRUE;
1165                         break;
1166                       }
1167                   
1168                   if (!has_expand)
1169                     for (row = child->top_attach; row < child->bottom_attach; row++)
1170                       table->rows[row].need_expand = TRUE;
1171                 }
1172               
1173               if (!child->yshrink)
1174                 {
1175                   has_shrink = TRUE;
1176                   for (row = child->top_attach; row < child->bottom_attach; row++)
1177                     if (!table->rows[row].shrink)
1178                       {
1179                         has_shrink = FALSE;
1180                         break;
1181                       }
1182                   
1183                   if (has_shrink)
1184                     for (row = child->top_attach; row < child->bottom_attach; row++)
1185                       table->rows[row].need_shrink = FALSE;
1186                 }
1187             }
1188         }
1189     }
1190   
1191   /* Loop over the columns and set the expand and shrink values
1192    *  if the column can be expanded or shrunk.
1193    */
1194   for (col = 0; col < table->ncols; col++)
1195     {
1196       if (table->cols[col].need_expand)
1197         table->cols[col].expand = TRUE;
1198       if (!table->cols[col].need_shrink)
1199         table->cols[col].shrink = FALSE;
1200     }
1201   
1202   /* Loop over the rows and set the expand and shrink values
1203    *  if the row can be expanded or shrunk.
1204    */
1205   for (row = 0; row < table->nrows; row++)
1206     {
1207       if (table->rows[row].need_expand)
1208         table->rows[row].expand = TRUE;
1209       if (!table->rows[row].need_shrink)
1210         table->rows[row].shrink = FALSE;
1211     }
1212 }
1213
1214 static void
1215 gtk_table_size_allocate_pass1 (GtkTable *table)
1216 {
1217   gint real_width;
1218   gint real_height;
1219   gint width, height;
1220   gint row, col;
1221   gint nexpand;
1222   gint nshrink;
1223   gint extra;
1224   
1225   /* If we were allocated more space than we requested
1226    *  then we have to expand any expandable rows and columns
1227    *  to fill in the extra space.
1228    */
1229   
1230   real_width = GTK_WIDGET (table)->allocation.width - GTK_CONTAINER (table)->border_width * 2;
1231   real_height = GTK_WIDGET (table)->allocation.height - GTK_CONTAINER (table)->border_width * 2;
1232   
1233   if (table->homogeneous)
1234     {
1235       nexpand = 0;
1236       for (col = 0; col < table->ncols; col++)
1237         if (table->cols[col].expand)
1238           {
1239             nexpand += 1;
1240             break;
1241           }
1242       
1243       if (nexpand > 0)
1244         {
1245           width = real_width;
1246           
1247           for (col = 0; col + 1 < table->ncols; col++)
1248             width -= table->cols[col].spacing;
1249           
1250           for (col = 0; col < table->ncols; col++)
1251             {
1252               extra = width / (table->ncols - col);
1253               table->cols[col].allocation = MAX (1, extra);
1254               width -= extra;
1255             }
1256         }
1257     }
1258   else
1259     {
1260       width = 0;
1261       nexpand = 0;
1262       nshrink = 0;
1263       
1264       for (col = 0; col < table->ncols; col++)
1265         {
1266           width += table->cols[col].requisition;
1267           if (table->cols[col].expand)
1268             nexpand += 1;
1269           if (table->cols[col].shrink)
1270             nshrink += 1;
1271         }
1272       for (col = 0; col + 1 < table->ncols; col++)
1273         width += table->cols[col].spacing;
1274       
1275       /* Check to see if we were allocated more width than we requested.
1276        */
1277       if ((width < real_width) && (nexpand >= 1))
1278         {
1279           width = real_width - width;
1280           
1281           for (col = 0; col < table->ncols; col++)
1282             if (table->cols[col].expand)
1283               {
1284                 extra = width / nexpand;
1285                 table->cols[col].allocation += extra;
1286                 
1287                 width -= extra;
1288                 nexpand -= 1;
1289               }
1290         }
1291       
1292       /* Check to see if we were allocated less width than we requested.
1293        */
1294       if ((width > real_width) && (nshrink >= 1))
1295         {
1296           width = width - real_width;
1297           
1298           for (col = 0; col < table->ncols; col++)
1299             if (table->cols[col].shrink)
1300               {
1301                 extra = width / nshrink;
1302                 table->cols[col].allocation = MAX (1, table->cols[col].allocation - extra);
1303                 
1304                 width -= extra;
1305                 nshrink -= 1;
1306               }
1307         }
1308     }
1309   
1310   if (table->homogeneous)
1311     {
1312       nexpand = 0;
1313       for (row = 0; row < table->nrows; row++)
1314         if (table->rows[row].expand)
1315           {
1316             nexpand += 1;
1317             break;
1318           }
1319       
1320       if (nexpand > 0)
1321         {
1322           height = real_height;
1323           
1324           for (row = 0; row + 1 < table->nrows; row++)
1325             height -= table->rows[row].spacing;
1326           
1327           
1328           for (row = 0; row < table->nrows; row++)
1329             {
1330               extra = height / (table->nrows - row);
1331               table->rows[row].allocation = MAX (1, extra);
1332               height -= extra;
1333             }
1334         }
1335     }
1336   else
1337     {
1338       height = 0;
1339       nexpand = 0;
1340       nshrink = 0;
1341       
1342       for (row = 0; row < table->nrows; row++)
1343         {
1344           height += table->rows[row].requisition;
1345           if (table->rows[row].expand)
1346             nexpand += 1;
1347           if (table->rows[row].shrink)
1348             nshrink += 1;
1349         }
1350       for (row = 0; row + 1 < table->nrows; row++)
1351         height += table->rows[row].spacing;
1352       
1353       /* Check to see if we were allocated more height than we requested.
1354        */
1355       if ((height < real_height) && (nexpand >= 1))
1356         {
1357           height = real_height - height;
1358           
1359           for (row = 0; row < table->nrows; row++)
1360             if (table->rows[row].expand)
1361               {
1362                 extra = height / nexpand;
1363                 table->rows[row].allocation += extra;
1364                 
1365                 height -= extra;
1366                 nexpand -= 1;
1367               }
1368         }
1369       
1370       /* Check to see if we were allocated less height than we requested.
1371        */
1372       if ((height > real_height) && (nshrink >= 1))
1373         {
1374           height = height - real_height;
1375           
1376           for (row = 0; row < table->nrows; row++)
1377             if (table->rows[row].shrink)
1378               {
1379                 extra = height / nshrink;
1380                 table->rows[row].allocation = MAX (1, table->rows[row].allocation - extra);
1381                 
1382                 height -= extra;
1383                 nshrink -= 1;
1384               }
1385         }
1386     }
1387 }
1388
1389 static void
1390 gtk_table_size_allocate_pass2 (GtkTable *table)
1391 {
1392   GtkTableChild *child;
1393   GList *children;
1394   gint max_width;
1395   gint max_height;
1396   gint x, y;
1397   gint row, col;
1398   GtkAllocation allocation;
1399   
1400   children = table->children;
1401   while (children)
1402     {
1403       child = children->data;
1404       children = children->next;
1405       
1406       if (GTK_WIDGET_VISIBLE (child->widget))
1407         {
1408           x = GTK_WIDGET (table)->allocation.x + GTK_CONTAINER (table)->border_width;
1409           y = GTK_WIDGET (table)->allocation.y + GTK_CONTAINER (table)->border_width;
1410           max_width = 0;
1411           max_height = 0;
1412           
1413           for (col = 0; col < child->left_attach; col++)
1414             {
1415               x += table->cols[col].allocation;
1416               x += table->cols[col].spacing;
1417             }
1418           
1419           for (col = child->left_attach; col < child->right_attach; col++)
1420             {
1421               max_width += table->cols[col].allocation;
1422               if ((col + 1) < child->right_attach)
1423                 max_width += table->cols[col].spacing;
1424             }
1425           
1426           for (row = 0; row < child->top_attach; row++)
1427             {
1428               y += table->rows[row].allocation;
1429               y += table->rows[row].spacing;
1430             }
1431           
1432           for (row = child->top_attach; row < child->bottom_attach; row++)
1433             {
1434               max_height += table->rows[row].allocation;
1435               if ((row + 1) < child->bottom_attach)
1436                 max_height += table->rows[row].spacing;
1437             }
1438           
1439           if (child->xfill)
1440             {
1441               allocation.width = MAX (1, max_width - child->xpadding * 2);
1442               allocation.x = x + (max_width - allocation.width) / 2;
1443             }
1444           else
1445             {
1446               allocation.width = child->widget->requisition.width;
1447               allocation.x = x + (max_width - allocation.width) / 2;
1448             }
1449           
1450           if (child->yfill)
1451             {
1452               allocation.height = MAX (1, max_height - child->ypadding * 2);
1453               allocation.y = y + (max_height - allocation.height) / 2;
1454             }
1455           else
1456             {
1457               allocation.height = child->widget->requisition.height;
1458               allocation.y = y + (max_height - allocation.height) / 2;
1459             }
1460           
1461           gtk_widget_size_allocate (child->widget, &allocation);
1462         }
1463     }
1464 }