]> Pileus Git - ~andy/gtk/blob - gtk/gtktable.c
Initial revision
[~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 Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18 #include "gtktable.h"
19
20
21 static void gtk_table_class_init    (GtkTableClass  *klass);
22 static void gtk_table_init          (GtkTable       *table);
23 static void gtk_table_destroy       (GtkObject      *object);
24 static void gtk_table_map           (GtkWidget      *widget);
25 static void gtk_table_unmap         (GtkWidget      *widget);
26 static void gtk_table_draw          (GtkWidget      *widget,
27                                      GdkRectangle   *area);
28 static gint gtk_table_expose        (GtkWidget      *widget,
29                                      GdkEventExpose *event);
30 static void gtk_table_size_request  (GtkWidget      *widget,
31                                      GtkRequisition *requisition);
32 static void gtk_table_size_allocate (GtkWidget      *widget,
33                                      GtkAllocation  *allocation);
34 static void gtk_table_add           (GtkContainer   *container,
35                                      GtkWidget      *widget);
36 static void gtk_table_remove        (GtkContainer   *container,
37                                      GtkWidget      *widget);
38 static void gtk_table_foreach       (GtkContainer   *container,
39                                      GtkCallback     callback,
40                                      gpointer        callback_data);
41
42 static void gtk_table_size_request_init  (GtkTable *table);
43 static void gtk_table_size_request_pass1 (GtkTable *table);
44 static void gtk_table_size_request_pass2 (GtkTable *table);
45 static void gtk_table_size_request_pass3 (GtkTable *table);
46
47 static void gtk_table_size_allocate_init  (GtkTable *table);
48 static void gtk_table_size_allocate_pass1 (GtkTable *table);
49 static void gtk_table_size_allocate_pass2 (GtkTable *table);
50
51
52 static GtkContainerClass *parent_class = NULL;
53
54
55 guint
56 gtk_table_get_type ()
57 {
58   static guint table_type = 0;
59
60   if (!table_type)
61     {
62       GtkTypeInfo table_info =
63       {
64         "GtkTable",
65         sizeof (GtkTable),
66         sizeof (GtkTableClass),
67         (GtkClassInitFunc) gtk_table_class_init,
68         (GtkObjectInitFunc) gtk_table_init,
69         (GtkArgFunc) NULL,
70       };
71
72       table_type = gtk_type_unique (gtk_container_get_type (), &table_info);
73     }
74
75   return table_type;
76 }
77
78 static void
79 gtk_table_class_init (GtkTableClass *class)
80 {
81   GtkObjectClass *object_class;
82   GtkWidgetClass *widget_class;
83   GtkContainerClass *container_class;
84
85   object_class = (GtkObjectClass*) class;
86   widget_class = (GtkWidgetClass*) class;
87   container_class = (GtkContainerClass*) class;
88
89   parent_class = gtk_type_class (gtk_container_get_type ());
90
91   object_class->destroy = gtk_table_destroy;
92
93   widget_class->map = gtk_table_map;
94   widget_class->unmap = gtk_table_unmap;
95   widget_class->draw = gtk_table_draw;
96   widget_class->expose_event = gtk_table_expose;
97   widget_class->size_request = gtk_table_size_request;
98   widget_class->size_allocate = gtk_table_size_allocate;
99
100   container_class->add = gtk_table_add;
101   container_class->remove = gtk_table_remove;
102   container_class->foreach = gtk_table_foreach;
103 }
104
105 static void
106 gtk_table_init (GtkTable *table)
107 {
108   GTK_WIDGET_SET_FLAGS (table, GTK_NO_WINDOW | GTK_BASIC);
109
110   table->children = NULL;
111   table->rows = NULL;
112   table->cols = NULL;
113   table->nrows = 0;
114   table->ncols = 0;
115   table->homogeneous = FALSE;
116 }
117
118 GtkWidget*
119 gtk_table_new (gint rows,
120                gint columns,
121                gint homogeneous)
122 {
123   GtkTable *table;
124   gint row, col;
125
126   table = gtk_type_new (gtk_table_get_type ());
127
128   table->nrows = rows;
129   table->ncols = columns;
130   table->homogeneous = (homogeneous ? TRUE : FALSE);
131
132   table->rows = g_new (GtkTableRowCol, table->nrows);
133   table->cols = g_new (GtkTableRowCol, table->ncols);
134
135   for (row = 0; row < table->nrows; row++)
136     {
137       table->rows[row].requisition = 0;
138       table->rows[row].allocation = 0;
139       table->rows[row].spacing = 0;
140       table->rows[row].need_expand = 0;
141       table->rows[row].need_shrink = 0;
142       table->rows[row].expand = 0;
143       table->rows[row].shrink = 0;
144     }
145
146   for (col = 0; col < table->ncols; col++)
147     {
148       table->cols[col].requisition = 0;
149       table->cols[col].allocation = 0;
150       table->cols[col].spacing = 0;
151       table->cols[col].need_expand = 0;
152       table->cols[col].need_shrink = 0;
153       table->cols[col].expand = 0;
154       table->cols[col].shrink = 0;
155     }
156
157   return GTK_WIDGET (table);
158 }
159
160 void
161 gtk_table_attach (GtkTable  *table,
162                   GtkWidget *child,
163                   gint       left_attach,
164                   gint       right_attach,
165                   gint       top_attach,
166                   gint       bottom_attach,
167                   gint       xoptions,
168                   gint       yoptions,
169                   gint       xpadding,
170                   gint       ypadding)
171 {
172   GtkTableChild *table_child;
173
174   g_return_if_fail (table != NULL);
175   g_return_if_fail (GTK_IS_TABLE (table));
176   g_return_if_fail (child != NULL);
177
178   g_return_if_fail ((left_attach >= 0) && (left_attach < table->ncols));
179   g_return_if_fail ((left_attach < right_attach) && (right_attach <= table->ncols));
180   g_return_if_fail ((top_attach >= 0) && (top_attach < table->nrows));
181   g_return_if_fail ((top_attach < bottom_attach) && (bottom_attach <= table->nrows));
182
183   table_child = g_new (GtkTableChild, 1);
184   table_child->widget = child;
185   table_child->left_attach = left_attach;
186   table_child->right_attach = right_attach;
187   table_child->top_attach = top_attach;
188   table_child->bottom_attach = bottom_attach;
189   table_child->xexpand = (xoptions & GTK_EXPAND) != 0;
190   table_child->xshrink = (xoptions & GTK_SHRINK) != 0;
191   table_child->xfill = (xoptions & GTK_FILL) != 0;
192   table_child->xpadding = xpadding;
193   table_child->yexpand = (yoptions & GTK_EXPAND) != 0;
194   table_child->yshrink = (yoptions & GTK_SHRINK) != 0;
195   table_child->yfill = (yoptions & GTK_FILL) != 0;
196   table_child->ypadding = ypadding;
197
198   table->children = g_list_prepend (table->children, table_child);
199
200   gtk_widget_set_parent (child, GTK_WIDGET (table));
201
202   if (GTK_WIDGET_VISIBLE (GTK_WIDGET (table)))
203     {
204       if (GTK_WIDGET_REALIZED (GTK_WIDGET (table)) &&
205           !GTK_WIDGET_REALIZED (child))
206         gtk_widget_realize (child);
207       
208       if (GTK_WIDGET_MAPPED (GTK_WIDGET (table)) &&
209           !GTK_WIDGET_MAPPED (child))
210         gtk_widget_map (child);
211     }
212
213   if (GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_VISIBLE (table))
214     gtk_widget_queue_resize (child);
215 }
216
217 void
218 gtk_table_attach_defaults (GtkTable  *table,
219                            GtkWidget *widget,
220                            gint       left_attach,
221                            gint       right_attach,
222                            gint       top_attach,
223                            gint       bottom_attach)
224 {
225   gtk_table_attach (table, widget,
226                     left_attach, right_attach,
227                     top_attach, bottom_attach,
228                     GTK_EXPAND | GTK_FILL,
229                     GTK_EXPAND | GTK_FILL,
230                     0, 0);
231 }
232
233 void
234 gtk_table_set_row_spacing (GtkTable *table,
235                            gint      row,
236                            gint      spacing)
237 {
238   g_return_if_fail (table != NULL);
239   g_return_if_fail (GTK_IS_TABLE (table));
240   g_return_if_fail ((row >= 0) && (row < (table->nrows - 1)));
241
242   if (table->rows[row].spacing != spacing)
243     {
244       table->rows[row].spacing = spacing;
245
246       if (GTK_WIDGET_VISIBLE (table))
247         gtk_widget_queue_resize (GTK_WIDGET (table));
248     }
249 }
250
251 void
252 gtk_table_set_col_spacing (GtkTable *table,
253                            gint      column,
254                            gint      spacing)
255 {
256   g_return_if_fail (table != NULL);
257   g_return_if_fail (GTK_IS_TABLE (table));
258   g_return_if_fail ((column >= 0) && (column < (table->ncols - 1)));
259
260   if (table->cols[column].spacing != spacing)
261     {
262       table->cols[column].spacing = spacing;
263
264       if (GTK_WIDGET_VISIBLE (table))
265         gtk_widget_queue_resize (GTK_WIDGET (table));
266     }
267 }
268
269 void
270 gtk_table_set_row_spacings (GtkTable *table,
271                             gint      spacing)
272 {
273   gint row;
274
275   g_return_if_fail (table != NULL);
276   g_return_if_fail (GTK_IS_TABLE (table));
277
278   for (row = 0; row < table->nrows - 1; row++)
279     table->rows[row].spacing = spacing;
280
281   if (GTK_WIDGET_VISIBLE (table))
282     gtk_widget_queue_resize (GTK_WIDGET (table));
283 }
284
285 void
286 gtk_table_set_col_spacings (GtkTable *table,
287                             gint      spacing)
288 {
289   gint col;
290
291   g_return_if_fail (table != NULL);
292   g_return_if_fail (GTK_IS_TABLE (table));
293
294   for (col = 0; col < table->ncols - 1; col++)
295     table->cols[col].spacing = spacing;
296
297   if (GTK_WIDGET_VISIBLE (table))
298     gtk_widget_queue_resize (GTK_WIDGET (table));
299 }
300
301
302 static void
303 gtk_table_destroy (GtkObject *object)
304 {
305   GtkTable *table;
306   GtkTableChild *child;
307   GList *children;
308
309   g_return_if_fail (object != NULL);
310   g_return_if_fail (GTK_IS_TABLE (object));
311
312   table = GTK_TABLE (object);
313
314   children = table->children;
315   while (children)
316     {
317       child = children->data;
318       children = children->next;
319
320       child->widget->parent = NULL;
321       gtk_object_unref (GTK_OBJECT (child->widget));
322       gtk_widget_destroy (child->widget);
323       g_free (child);
324     }
325
326   g_list_free (table->children);
327   g_free (table->rows);
328   g_free (table->cols);
329
330   if (GTK_OBJECT_CLASS (parent_class)->destroy)
331     (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
332 }
333
334 static void
335 gtk_table_map (GtkWidget *widget)
336 {
337   GtkTable *table;
338   GtkTableChild *child;
339   GList *children;
340
341   g_return_if_fail (widget != NULL);
342   g_return_if_fail (GTK_IS_TABLE (widget));
343
344   table = GTK_TABLE (widget);
345   GTK_WIDGET_SET_FLAGS (table, GTK_MAPPED);
346
347   children = table->children;
348   while (children)
349     {
350       child = children->data;
351       children = children->next;
352
353       if (GTK_WIDGET_VISIBLE (child->widget) &&
354           !GTK_WIDGET_MAPPED (child->widget))
355         gtk_widget_map (child->widget);
356     }
357 }
358
359 static void
360 gtk_table_unmap (GtkWidget *widget)
361 {
362   GtkTable *table;
363   GtkTableChild *child;
364   GList *children;
365
366   g_return_if_fail (widget != NULL);
367   g_return_if_fail (GTK_IS_TABLE (widget));
368
369   table = GTK_TABLE (widget);
370   GTK_WIDGET_UNSET_FLAGS (table, GTK_MAPPED);
371
372   children = table->children;
373   while (children)
374     {
375       child = children->data;
376       children = children->next;
377
378       if (GTK_WIDGET_VISIBLE (child->widget) &&
379           GTK_WIDGET_MAPPED (child->widget))
380         gtk_widget_unmap (child->widget);
381     }
382 }
383
384 static void
385 gtk_table_draw (GtkWidget    *widget,
386                 GdkRectangle *area)
387 {
388   GtkTable *table;
389   GtkTableChild *child;
390   GList *children;
391   GdkRectangle child_area;
392
393   g_return_if_fail (widget != NULL);
394   g_return_if_fail (GTK_IS_TABLE (widget));
395
396   if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget))
397     {
398       table = GTK_TABLE (widget);
399
400       children = table->children;
401       while (children)
402         {
403           child = children->data;
404           children = children->next;
405
406           if (gtk_widget_intersect (child->widget, area, &child_area))
407             gtk_widget_draw (child->widget, &child_area);
408         }
409     }
410 }
411
412 static gint
413 gtk_table_expose (GtkWidget      *widget,
414                   GdkEventExpose *event)
415 {
416   GtkTable *table;
417   GtkTableChild *child;
418   GList *children;
419   GdkEventExpose child_event;
420
421   g_return_val_if_fail (widget != NULL, FALSE);
422   g_return_val_if_fail (GTK_IS_TABLE (widget), FALSE);
423
424   if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget))
425     {
426       table = GTK_TABLE (widget);
427
428       child_event = *event;
429
430       children = table->children;
431       while (children)
432         {
433           child = children->data;
434           children = children->next;
435
436           if (GTK_WIDGET_NO_WINDOW (child->widget) &&
437               gtk_widget_intersect (child->widget, &event->area, &child_event.area))
438             gtk_widget_event (child->widget, (GdkEvent*) &child_event);
439         }
440     }
441
442   return FALSE;
443 }
444
445 static void
446 gtk_table_size_request (GtkWidget      *widget,
447                         GtkRequisition *requisition)
448 {
449   GtkTable *table;
450   gint row, col;
451
452   g_return_if_fail (widget != NULL);
453   g_return_if_fail (GTK_IS_TABLE (widget));
454   g_return_if_fail (requisition != NULL);
455
456   table = GTK_TABLE (widget);
457
458   requisition->width = 0;
459   requisition->height = 0;
460
461   gtk_table_size_request_init (table);
462   gtk_table_size_request_pass1 (table);
463   gtk_table_size_request_pass2 (table);
464   gtk_table_size_request_pass3 (table);
465   gtk_table_size_request_pass2 (table);
466
467   for (col = 0; col < table->ncols; col++)
468     requisition->width += table->cols[col].requisition;
469   for (col = 0; col < table->ncols - 1; col++)
470     requisition->width += table->cols[col].spacing;
471
472   for (row = 0; row < table->nrows; row++)
473     requisition->height += table->rows[row].requisition;
474   for (row = 0; row < table->nrows - 1; row++)
475     requisition->height += table->rows[row].spacing;
476
477   requisition->width += GTK_CONTAINER (table)->border_width * 2;
478   requisition->height += GTK_CONTAINER (table)->border_width * 2;
479 }
480
481 static void
482 gtk_table_size_allocate (GtkWidget     *widget,
483                          GtkAllocation *allocation)
484 {
485   GtkTable *table;
486
487   g_return_if_fail (widget != NULL);
488   g_return_if_fail (GTK_IS_TABLE (widget));
489   g_return_if_fail (allocation != NULL);
490
491   widget->allocation = *allocation;
492   table = GTK_TABLE (widget);
493
494   gtk_table_size_allocate_init (table);
495   gtk_table_size_allocate_pass1 (table);
496   gtk_table_size_allocate_pass2 (table);
497 }
498
499 static void
500 gtk_table_add (GtkContainer *container,
501                GtkWidget    *widget)
502 {
503   g_return_if_fail (container != NULL);
504   g_return_if_fail (GTK_IS_TABLE (container));
505   g_return_if_fail (widget != NULL);
506
507   gtk_table_attach_defaults (GTK_TABLE (container), widget, 0, 1, 0, 1);
508 }
509
510 static void
511 gtk_table_remove (GtkContainer *container,
512                   GtkWidget    *widget)
513 {
514   GtkTable *table;
515   GtkTableChild *child;
516   GList *children;
517
518   g_return_if_fail (container != NULL);
519   g_return_if_fail (GTK_IS_TABLE (container));
520   g_return_if_fail (widget != NULL);
521
522   table = GTK_TABLE (container);
523   children = table->children;
524
525   while (children)
526     {
527       child = children->data;
528       children = children->next;
529
530       if (child->widget == widget)
531         {
532           gtk_widget_unparent (widget);
533
534           table->children = g_list_remove (table->children, child);
535           g_free (child);
536
537           if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_VISIBLE (container))
538             gtk_widget_queue_resize (GTK_WIDGET (container));
539           break;
540         }
541     }
542 }
543
544 static void
545 gtk_table_foreach (GtkContainer *container,
546                    GtkCallback     callback,
547                    gpointer        callback_data)
548 {
549   GtkTable *table;
550   GtkTableChild *child;
551   GList *children;
552
553   g_return_if_fail (container != NULL);
554   g_return_if_fail (GTK_IS_TABLE (container));
555   g_return_if_fail (callback != NULL);
556
557   table = GTK_TABLE (container);
558   children = table->children;
559
560   while (children)
561     {
562       child = children->data;
563       children = children->next;
564
565       (* callback) (child->widget, callback_data);
566     }
567 }
568
569 static void
570 gtk_table_size_request_init (GtkTable *table)
571 {
572   GtkTableChild *child;
573   GList *children;
574   gint row, col;
575
576   for (row = 0; row < table->nrows; row++)
577     table->rows[row].requisition = 0;
578   for (col = 0; col < table->ncols; col++)
579     table->cols[col].requisition = 0;
580
581   children = table->children;
582   while (children)
583     {
584       child = children->data;
585       children = children->next;
586
587       if (GTK_WIDGET_VISIBLE (child->widget))
588         gtk_widget_size_request (child->widget, &child->widget->requisition);
589     }
590 }
591
592 static void
593 gtk_table_size_request_pass1 (GtkTable *table)
594 {
595   GtkTableChild *child;
596   GList *children;
597   gint width;
598   gint height;
599
600   children = table->children;
601   while (children)
602     {
603       child = children->data;
604       children = children->next;
605
606       if (GTK_WIDGET_VISIBLE (child->widget))
607         {
608           /* Child spans a single column.
609            */
610           if (child->left_attach == (child->right_attach - 1))
611             {
612               width = child->widget->requisition.width + child->xpadding * 2;
613               table->cols[child->left_attach].requisition = MAX (table->cols[child->left_attach].requisition, width);
614             }
615
616           /* Child spans a single row.
617            */
618           if (child->top_attach == (child->bottom_attach - 1))
619             {
620               height = child->widget->requisition.height + child->ypadding * 2;
621               table->rows[child->top_attach].requisition = MAX (table->rows[child->top_attach].requisition, height);
622             }
623         }
624     }
625 }
626
627 static void
628 gtk_table_size_request_pass2 (GtkTable *table)
629 {
630   gint max_width;
631   gint max_height;
632   gint row, col;
633
634   if (table->homogeneous)
635     {
636       max_width = 0;
637       max_height = 0;
638
639       for (col = 0; col < table->ncols; col++)
640         max_width = MAX (max_width, table->cols[col].requisition);
641       for (row = 0; row < table->nrows; row++)
642         max_height = MAX (max_height, table->rows[row].requisition);
643
644       for (col = 0; col < table->ncols; col++)
645         table->cols[col].requisition = max_width;
646       for (row = 0; row < table->nrows; row++)
647         table->rows[row].requisition = max_height;
648     }
649 }
650
651 static void
652 gtk_table_size_request_pass3 (GtkTable *table)
653 {
654   GtkTableChild *child;
655   GList *children;
656   gint width, height;
657   gint row, col;
658   gint extra;
659
660   children = table->children;
661   while (children)
662     {
663       child = children->data;
664       children = children->next;
665
666       if (GTK_WIDGET_VISIBLE (child->widget))
667         {
668           /* Child spans multiple columns.
669            */
670           if (child->left_attach != (child->right_attach - 1))
671             {
672               /* Check and see if there is already enough space
673                *  for the child.
674                */
675               width = 0;
676               for (col = child->left_attach; col < child->right_attach; col++)
677                 {
678                   width += table->cols[col].requisition;
679                   if ((col + 1) < child->right_attach)
680                     width += table->cols[col].spacing;
681                 }
682
683               /* If we need to request more space for this child to fill
684                *  its requisition, then divide up the needed space evenly
685                *  amongst the columns it spans.
686                */
687               if (width < child->widget->requisition.width)
688                 {
689                   width = child->widget->requisition.width - width;
690                   extra = width / (child->right_attach - child->left_attach);
691
692                   for (col = child->left_attach; col < child->right_attach; col++)
693                     {
694                       if ((col + 1) < child->right_attach)
695                         table->cols[col].requisition += extra;
696                       else
697                         table->cols[col].requisition += width;
698                       width -= extra;
699                     }
700                 }
701             }
702
703           /* Child spans multiple rows.
704            */
705           if (child->top_attach != (child->bottom_attach - 1))
706             {
707               /* Check and see if there is already enough space
708                *  for the child.
709                */
710               height = 0;
711               for (row = child->top_attach; row < child->bottom_attach; row++)
712                 {
713                   height += table->rows[row].requisition;
714                   if ((row + 1) < child->bottom_attach)
715                     height += table->rows[row].spacing;
716                 }
717
718               /* If we need to request more space for this child to fill
719                *  its requisition, then divide up the needed space evenly
720                *  amongst the columns it spans.
721                */
722               if (height < child->widget->requisition.height)
723                 {
724                   height = child->widget->requisition.height - height;
725                   extra = height / (child->bottom_attach - child->top_attach);
726
727                   for (row = child->top_attach; row < child->bottom_attach; row++)
728                     {
729                       if ((row + 1) < child->bottom_attach)
730                         table->rows[row].requisition += extra;
731                       else
732                         table->rows[row].requisition += height;
733                       height -= extra;
734                     }
735                 }
736             }
737         }
738     }
739 }
740
741 static void
742 gtk_table_size_allocate_init (GtkTable *table)
743 {
744   GtkTableChild *child;
745   GList *children;
746   gint row, col;
747   gint has_expand;
748   gint has_shrink;
749
750   /* Initialize the rows and cols.
751    *  By default, rows and cols do not expand and do shrink.
752    *  Those values are modified by the children that occupy
753    *  the rows and cols.
754    */
755   for (col = 0; col < table->ncols; col++)
756     {
757       table->cols[col].allocation = table->cols[col].requisition;
758       table->cols[col].need_expand = FALSE;
759       table->cols[col].need_shrink = TRUE;
760       table->cols[col].expand = FALSE;
761       table->cols[col].shrink = TRUE;
762     }
763   for (row = 0; row < table->nrows; row++)
764     {
765       table->rows[row].allocation = table->rows[row].requisition;
766       table->rows[row].need_expand = FALSE;
767       table->rows[row].need_shrink = TRUE;
768       table->rows[row].expand = FALSE;
769       table->rows[row].shrink = TRUE;
770     }
771
772   /* Loop over all the children and adjust the row and col values
773    *  based on whether the children want to be allowed to expand
774    *  or shrink. This loop handles children that occupy a single
775    *  row or column.
776    */
777   children = table->children;
778   while (children)
779     {
780       child = children->data;
781       children = children->next;
782
783       if (GTK_WIDGET_VISIBLE (child->widget))
784         {
785           if (child->left_attach == (child->right_attach - 1))
786             {
787               if (child->xexpand)
788                 table->cols[child->left_attach].expand = TRUE;
789
790               if (!child->xshrink)
791                 table->cols[child->left_attach].shrink = FALSE;
792             }
793
794           if (child->top_attach == (child->bottom_attach - 1))
795             {
796               if (child->yexpand)
797                 table->rows[child->top_attach].expand = TRUE;
798
799               if (!child->yshrink)
800                 table->rows[child->top_attach].shrink = FALSE;
801             }
802         }
803     }
804
805   /* Loop over all the children again and this time handle children
806    *  which span multiple rows or columns.
807    */
808   children = table->children;
809   while (children)
810     {
811       child = children->data;
812       children = children->next;
813
814       if (GTK_WIDGET_VISIBLE (child->widget))
815         {
816           if (child->left_attach != (child->right_attach - 1))
817             {
818               if (child->xexpand)
819                 {
820                   has_expand = FALSE;
821                   for (col = child->left_attach; col < child->right_attach; col++)
822                     if (table->cols[col].expand)
823                       {
824                         has_expand = TRUE;
825                         break;
826                       }
827
828                   if (!has_expand)
829                     for (col = child->left_attach; col < child->right_attach; col++)
830                       table->cols[col].need_expand = TRUE;
831                 }
832
833               if (!child->xshrink)
834                 {
835                   has_shrink = TRUE;
836                   for (col = child->left_attach; col < child->right_attach; col++)
837                     if (!table->cols[col].shrink)
838                       {
839                         has_shrink = FALSE;
840                         break;
841                       }
842
843                   if (has_shrink)
844                     for (col = child->left_attach; col < child->right_attach; col++)
845                       table->cols[col].need_shrink = FALSE;
846                 }
847             }
848
849           if (child->top_attach != (child->bottom_attach - 1))
850             {
851               if (child->yexpand)
852                 {
853                   has_expand = FALSE;
854                   for (row = child->top_attach; row < child->bottom_attach; row++)
855                     if (table->rows[row].expand)
856                       {
857                         has_expand = TRUE;
858                         break;
859                       }
860
861                   if (!has_expand)
862                     for (row = child->top_attach; row < child->bottom_attach; row++)
863                       table->rows[row].need_expand = TRUE;
864                 }
865
866               if (!child->yshrink)
867                 {
868                   has_shrink = TRUE;
869                   for (row = child->top_attach; row < child->bottom_attach; row++)
870                     if (!table->rows[row].shrink)
871                       {
872                         has_shrink = FALSE;
873                         break;
874                       }
875
876                   if (has_shrink)
877                     for (row = child->top_attach; row < child->bottom_attach; row++)
878                       table->rows[row].need_shrink = FALSE;
879                 }
880             }
881         }
882     }
883
884   /* Loop over the columns and set the expand and shrink values
885    *  if the column can be expanded or shrunk.
886    */
887   for (col = 0; col < table->ncols; col++)
888     {
889       if (table->cols[col].need_expand)
890         table->cols[col].expand = TRUE;
891       if (!table->cols[col].need_shrink)
892         table->cols[col].shrink = FALSE;
893     }
894
895   /* Loop over the rows and set the expand and shrink values
896    *  if the row can be expanded or shrunk.
897    */
898   for (row = 0; row < table->nrows; row++)
899     {
900       if (table->rows[row].need_expand)
901         table->rows[row].expand = TRUE;
902       if (!table->rows[row].need_shrink)
903         table->rows[row].shrink = FALSE;
904     }
905 }
906
907 static void
908 gtk_table_size_allocate_pass1 (GtkTable *table)
909 {
910   gint real_width;
911   gint real_height;
912   gint width, height;
913   gint row, col;
914   gint nexpand;
915   gint nshrink;
916   gint extra;
917
918   /* If we were allocated more space than we requested
919    *  then we have to expand any expandable rows and columns
920    *  to fill in the extra space.
921    */
922
923   real_width = GTK_WIDGET (table)->allocation.width - GTK_CONTAINER (table)->border_width * 2;
924   real_height = GTK_WIDGET (table)->allocation.height - GTK_CONTAINER (table)->border_width * 2;
925
926   if (table->homogeneous)
927     {
928       nexpand = 0;
929       for (col = 0; col < table->ncols; col++)
930         if (table->cols[col].expand)
931           {
932             nexpand += 1;
933             break;
934           }
935
936       if (nexpand > 0)
937         {
938           width = real_width;
939
940           for (col = 0; col < table->ncols - 1; col++)
941             width -= table->cols[col].spacing;
942
943           extra = width / table->ncols;
944
945           for (col = 0; col < table->ncols; col++)
946             {
947               if ((col + 1) == table->ncols)
948                 table->cols[col].allocation = width;
949               else
950                 table->cols[col].allocation = extra;
951
952               width -= extra;
953             }
954         }
955     }
956   else
957     {
958       width = 0;
959       nexpand = 0;
960       nshrink = 0;
961
962       for (col = 0; col < table->ncols; col++)
963         {
964           width += table->cols[col].requisition;
965           if (table->cols[col].expand)
966             nexpand += 1;
967           if (table->cols[col].shrink)
968             nshrink += 1;
969         }
970       for (col = 0; col < table->ncols - 1; col++)
971         width += table->cols[col].spacing;
972
973       /* Check to see if we were allocated more width than we requested.
974        */
975       if ((width < real_width) && (nexpand >= 1))
976         {
977           width = real_width - width;
978           extra = width / nexpand;
979
980           for (col = 0; col < table->ncols; col++)
981             if (table->cols[col].expand)
982               {
983                 if (nexpand == 1)
984                   table->cols[col].allocation += width;
985                 else
986                   table->cols[col].allocation += extra;
987
988                 width -= extra;
989                 nexpand -= 1;
990               }
991         }
992
993       /* Check to see if we were allocated less width than we requested.
994        */
995       if ((width > real_width) && (nshrink >= 1))
996         {
997           width = width - real_width;
998           extra = width / nshrink;
999
1000           for (col = 0; col < table->ncols; col++)
1001             if (table->cols[col].shrink)
1002               {
1003                 if (nshrink == 1)
1004                   table->cols[col].allocation -= width;
1005                 else
1006                   table->cols[col].allocation -= extra;
1007
1008                 width -= extra;
1009                 nshrink -= 1;
1010               }
1011         }
1012     }
1013
1014   if (table->homogeneous)
1015     {
1016       nexpand = 0;
1017       for (row = 0; row < table->nrows; row++)
1018         if (table->rows[row].expand)
1019           {
1020             nexpand += 1;
1021             break;
1022           }
1023
1024       if (nexpand > 0)
1025         {
1026           height = real_height;
1027
1028           for (row = 0; row < table->nrows - 1; row++)
1029             height -= table->rows[row].spacing;
1030
1031           extra = height / table->nrows;
1032
1033           for (row = 0; row < table->nrows; row++)
1034             {
1035               if ((row + 1) == table->nrows)
1036                 table->rows[row].allocation = height;
1037               else
1038                 table->rows[row].allocation = extra;
1039
1040               height -= extra;
1041             }
1042         }
1043     }
1044   else
1045     {
1046       height = 0;
1047       nexpand = 0;
1048       nshrink = 0;
1049
1050       for (row = 0; row < table->nrows; row++)
1051         {
1052           height += table->rows[row].requisition;
1053           if (table->rows[row].expand)
1054             nexpand += 1;
1055           if (table->rows[row].shrink)
1056             nshrink += 1;
1057         }
1058       for (row = 0; row < table->nrows - 1; row++)
1059         height += table->rows[row].spacing;
1060
1061       /* Check to see if we were allocated more height than we requested.
1062        */
1063       if ((height < real_height) && (nexpand >= 1))
1064         {
1065           height = real_height - height;
1066           extra = height / nexpand;
1067
1068           for (row = 0; row < table->nrows; row++)
1069             if (table->rows[row].expand)
1070               {
1071                 if (nexpand == 1)
1072                   table->rows[row].allocation += height;
1073                 else
1074                   table->rows[row].allocation += extra;
1075
1076                 height -= extra;
1077                 nexpand -= 1;
1078               }
1079         }
1080
1081       /* Check to see if we were allocated less height than we requested.
1082        */
1083       if ((height > real_height) && (nshrink >= 1))
1084         {
1085           height = height - real_height;
1086           extra = height / nshrink;
1087
1088           for (row = 0; row < table->nrows; row++)
1089             if (table->rows[row].shrink)
1090               {
1091                 if (nshrink == 1)
1092                   table->rows[row].allocation -= height;
1093                 else
1094                   table->rows[row].allocation -= extra;
1095
1096                 height -= extra;
1097                 nshrink -= 1;
1098               }
1099         }
1100     }
1101 }
1102
1103 static void
1104 gtk_table_size_allocate_pass2 (GtkTable *table)
1105 {
1106   GtkTableChild *child;
1107   GList *children;
1108   gint max_width;
1109   gint max_height;
1110   gint x, y;
1111   gint row, col;
1112   GtkAllocation allocation;
1113
1114   children = table->children;
1115   while (children)
1116     {
1117       child = children->data;
1118       children = children->next;
1119
1120       if (GTK_WIDGET_VISIBLE (child->widget))
1121         {
1122           x = GTK_WIDGET (table)->allocation.x + GTK_CONTAINER (table)->border_width;
1123           y = GTK_WIDGET (table)->allocation.y + GTK_CONTAINER (table)->border_width;
1124           max_width = 0;
1125           max_height = 0;
1126
1127           for (col = 0; col < child->left_attach; col++)
1128             {
1129               x += table->cols[col].allocation;
1130               x += table->cols[col].spacing;
1131             }
1132
1133           for (col = child->left_attach; col < child->right_attach; col++)
1134             {
1135               max_width += table->cols[col].allocation;
1136               if ((col + 1) < child->right_attach)
1137                 max_width += table->cols[col].spacing;
1138             }
1139
1140           for (row = 0; row < child->top_attach; row++)
1141             {
1142               y += table->rows[row].allocation;
1143               y += table->rows[row].spacing;
1144             }
1145
1146           for (row = child->top_attach; row < child->bottom_attach; row++)
1147             {
1148               max_height += table->rows[row].allocation;
1149               if ((row + 1) < child->bottom_attach)
1150                 max_height += table->rows[row].spacing;
1151             }
1152
1153           if (child->xfill)
1154             {
1155               allocation.width = max_width - child->xpadding * 2;
1156               allocation.x = x + (max_width - allocation.width) / 2;
1157             }
1158           else
1159             {
1160               allocation.width = child->widget->requisition.width;
1161               allocation.x = x + (max_width - allocation.width) / 2;
1162             }
1163
1164           if (child->yfill)
1165             {
1166               allocation.height = max_height - child->ypadding * 2;
1167               allocation.y = y + (max_height - allocation.height) / 2;
1168             }
1169           else
1170             {
1171               allocation.height = child->widget->requisition.height;
1172               allocation.y = y + (max_height - allocation.height) / 2;
1173             }
1174
1175           gtk_widget_size_allocate (child->widget, &allocation);
1176         }
1177     }
1178 }