]> Pileus Git - ~andy/gtk/blob - gtk/gtkpacker.c
I submitted this patch twice to gtk-devel-list, and received no comments,
[~andy/gtk] / gtk / gtkpacker.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  * 
4  * GtkPacker Widget 
5  * Copyright (C) 1998 Shawn T. Amundson, James S. Mitchell, Michael L. Staiger
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23
24 /* 
25  * This file contains modified code derived from Tk 8.0.  Below is the header of
26  * the relevant file.  The file 'license.terms' is included inline below.
27  * 
28  * tkPack.c --
29  *
30  *      This file contains code to implement the "packer"
31  *      geometry manager for Tk.
32  *
33  * Copyright (c) 1990-1994 The Regents of the University of California.
34  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
35  *
36  * See the file "license.terms" for information on usage and redistribution
37  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
38  *
39  * SCCS: @(#) tkPack.c 1.64 96/05/03 10:51:52
40  *
41  * The file license.terms is below.  NOTE: THE FOLLOWING APPLIES ONLY TO
42  * PORTIONS DERIVED FROM TK 8.0.  THE LICENSE FOR THIS FILE IS LGPL, AS
43  * STATED ABOVE AND ALLOWED BELOW.  
44 -- BEGIN license.terms --
45 This software is copyrighted by the Regents of the University of
46 California, Sun Microsystems, Inc., and other parties.  The following
47 terms apply to all files associated with the software unless explicitly
48 disclaimed in individual files.
49
50 The authors hereby grant permission to use, copy, modify, distribute,
51 and license this software and its documentation for any purpose, provided
52 that existing copyright notices are retained in all copies and that this
53 notice is included verbatim in any distributions. No written agreement,
54 license, or royalty fee is required for any of the authorized uses.
55 Modifications to this software may be copyrighted by their authors
56 and need not follow the licensing terms described here, provided that
57 the new terms are clearly indicated on the first page of each file where
58 they apply.
59
60 IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
61 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
62 ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
63 DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
64 POSSIBILITY OF SUCH DAMAGE.
65
66 THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
67 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
68 FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE
69 IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
70 NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
71 MODIFICATIONS.
72
73 GOVERNMENT USE: If you are acquiring this software on behalf of the
74 U.S. government, the Government shall have only "Restricted Rights"
75 in the software and related documentation as defined in the Federal 
76 Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2).  If you
77 are acquiring the software on behalf of the Department of Defense, the
78 software shall be classified as "Commercial Computer Software" and the
79 Government shall have only "Restricted Rights" as defined in Clause
80 252.227-7013 (c) (1) of DFARs.  Notwithstanding the foregoing, the
81 authors grant the U.S. Government and others acting in its behalf
82 permission to use and distribute the software in accordance with the
83 terms specified in this license.
84 -- END license.terms --
85  *
86  */
87
88
89 #include "gtkpacker.h"
90
91
92 enum {
93   ARG_0,
94   ARG_SPACING,
95   ARG_D_BORDER_WIDTH,
96   ARG_D_PAD_X,
97   ARG_D_PAD_Y,
98   ARG_D_IPAD_X,
99   ARG_D_IPAD_Y
100 };
101
102 enum {
103   CHILD_ARG_0,
104   CHILD_ARG_SIDE,
105   CHILD_ARG_ANCHOR,
106   CHILD_ARG_EXPAND,
107   CHILD_ARG_FILL_X,
108   CHILD_ARG_FILL_Y,
109   CHILD_ARG_USE_DEFAULT,
110   CHILD_ARG_BORDER_WIDTH,
111   CHILD_ARG_PAD_X,
112   CHILD_ARG_PAD_Y,
113   CHILD_ARG_I_PAD_X,
114   CHILD_ARG_I_PAD_Y,
115   CHILD_ARG_POSITION
116 };
117
118 static void gtk_packer_class_init    (GtkPackerClass   *klass);
119 static void gtk_packer_init          (GtkPacker        *packer);
120 static void gtk_packer_map           (GtkWidget        *widget);
121 static void gtk_packer_unmap         (GtkWidget        *widget);
122 static void gtk_packer_draw          (GtkWidget        *widget,
123                                       GdkRectangle     *area);
124 static gint gtk_packer_expose        (GtkWidget        *widget,
125                                       GdkEventExpose   *event);
126 static void gtk_packer_size_request  (GtkWidget      *widget,
127                                       GtkRequisition *requisition);
128 static void gtk_packer_size_allocate (GtkWidget      *widget,
129                                       GtkAllocation  *allocation);
130 static void gtk_packer_container_add (GtkContainer   *container,
131                                       GtkWidget      *child);
132 static void gtk_packer_remove        (GtkContainer   *container,
133                                       GtkWidget      *widget);
134 static void gtk_packer_forall        (GtkContainer   *container,
135                                       gboolean        include_internals,
136                                       GtkCallback     callback,
137                                       gpointer        callback_data);
138 static void gtk_packer_set_arg       (GtkObject      *object,
139                                       GtkArg         *arg,
140                                       guint           arg_id);
141 static void gtk_packer_get_arg       (GtkObject      *object,
142                                       GtkArg         *arg,
143                                       guint           arg_id);
144 static void gtk_packer_get_child_arg (GtkContainer   *container,
145                                       GtkWidget      *child,
146                                       GtkArg         *arg,
147                                       guint           arg_id);
148 static void gtk_packer_set_child_arg (GtkContainer   *container,
149                                       GtkWidget      *child,
150                                       GtkArg         *arg,
151                                       guint           arg_id);
152 static GtkType gtk_packer_child_type (GtkContainer   *container);
153      
154
155 static GtkPackerClass *parent_class;
156
157 GtkType
158 gtk_packer_get_type (void)
159 {
160   static GtkType packer_type = 0;
161
162   if (!packer_type)
163     {
164       static const GtkTypeInfo packer_info =
165       {
166         "GtkPacker",
167         sizeof (GtkPacker),
168         sizeof (GtkPackerClass),
169         (GtkClassInitFunc) gtk_packer_class_init,
170         (GtkObjectInitFunc) gtk_packer_init,
171         /* reserved_1 */ NULL,
172         /* reserved_2 */ NULL,
173         (GtkClassInitFunc) NULL,
174       };
175
176       packer_type = gtk_type_unique (GTK_TYPE_CONTAINER, &packer_info);
177     }
178   
179   return packer_type;
180 }
181
182 static void
183 gtk_packer_class_init (GtkPackerClass *klass)
184 {
185   GtkObjectClass *object_class;
186   GtkWidgetClass *widget_class;
187   GtkContainerClass *container_class;
188   
189   object_class = (GtkObjectClass*) klass;
190   widget_class = (GtkWidgetClass*) klass;
191   container_class = (GtkContainerClass*) klass;
192   parent_class = gtk_type_class (GTK_TYPE_CONTAINER);
193   
194   gtk_object_add_arg_type ("GtkPacker::spacing", GTK_TYPE_UINT, GTK_ARG_READWRITE, ARG_SPACING);
195   gtk_object_add_arg_type ("GtkPacker::default_border_width", GTK_TYPE_UINT, GTK_ARG_READWRITE, ARG_D_BORDER_WIDTH);
196   gtk_object_add_arg_type ("GtkPacker::default_pad_x", GTK_TYPE_UINT, GTK_ARG_READWRITE, ARG_D_PAD_X);
197   gtk_object_add_arg_type ("GtkPacker::default_pad_y", GTK_TYPE_UINT, GTK_ARG_READWRITE, ARG_D_PAD_Y);
198   gtk_object_add_arg_type ("GtkPacker::default_ipad_x", GTK_TYPE_UINT, GTK_ARG_READWRITE, ARG_D_IPAD_X);
199   gtk_object_add_arg_type ("GtkPacker::default_ipad_y", GTK_TYPE_UINT, GTK_ARG_READWRITE, ARG_D_IPAD_Y);
200
201   gtk_container_add_child_arg_type ("GtkPacker::side", GTK_TYPE_SIDE_TYPE, GTK_ARG_READWRITE, CHILD_ARG_SIDE);
202   gtk_container_add_child_arg_type ("GtkPacker::anchor", GTK_TYPE_ANCHOR_TYPE, GTK_ARG_READWRITE, CHILD_ARG_ANCHOR);
203   gtk_container_add_child_arg_type ("GtkPacker::expand", GTK_TYPE_BOOL, GTK_ARG_READWRITE, CHILD_ARG_EXPAND);
204   gtk_container_add_child_arg_type ("GtkPacker::fill_x", GTK_TYPE_BOOL, GTK_ARG_READWRITE, CHILD_ARG_FILL_X);
205   gtk_container_add_child_arg_type ("GtkPacker::fill_y", GTK_TYPE_BOOL, GTK_ARG_READWRITE, CHILD_ARG_FILL_Y);
206   gtk_container_add_child_arg_type ("GtkPacker::use_default", GTK_TYPE_BOOL, GTK_ARG_READWRITE, CHILD_ARG_USE_DEFAULT);
207   gtk_container_add_child_arg_type ("GtkPacker::border_width", GTK_TYPE_UINT, GTK_ARG_READWRITE, CHILD_ARG_BORDER_WIDTH);
208   gtk_container_add_child_arg_type ("GtkPacker::pad_x", GTK_TYPE_UINT, GTK_ARG_READWRITE, CHILD_ARG_PAD_X);
209   gtk_container_add_child_arg_type ("GtkPacker::pad_y", GTK_TYPE_UINT, GTK_ARG_READWRITE, CHILD_ARG_PAD_Y);
210   gtk_container_add_child_arg_type ("GtkPacker::ipad_x", GTK_TYPE_UINT, GTK_ARG_READWRITE, CHILD_ARG_I_PAD_X);
211   gtk_container_add_child_arg_type ("GtkPacker::ipad_y", GTK_TYPE_UINT, GTK_ARG_READWRITE, CHILD_ARG_I_PAD_Y);
212   gtk_container_add_child_arg_type ("GtkPacker::position", GTK_TYPE_LONG, GTK_ARG_READWRITE, CHILD_ARG_POSITION);
213
214   object_class->set_arg = gtk_packer_set_arg;
215   object_class->get_arg = gtk_packer_get_arg;
216
217   widget_class->map = gtk_packer_map;
218   widget_class->unmap = gtk_packer_unmap;
219   widget_class->draw = gtk_packer_draw;
220   widget_class->expose_event = gtk_packer_expose;
221   
222   widget_class->size_request = gtk_packer_size_request;
223   widget_class->size_allocate = gtk_packer_size_allocate;
224   
225   container_class->add = gtk_packer_container_add;
226   container_class->remove = gtk_packer_remove;
227   container_class->forall = gtk_packer_forall;
228   container_class->child_type = gtk_packer_child_type;
229   container_class->get_child_arg = gtk_packer_get_child_arg;
230   container_class->set_child_arg = gtk_packer_set_child_arg;
231 }
232
233 static void
234 gtk_packer_set_arg (GtkObject    *object,
235                     GtkArg       *arg,
236                     guint         arg_id)
237 {
238   GtkPacker *packer;
239
240   packer = GTK_PACKER (object);
241
242   switch (arg_id)
243     {
244     case ARG_SPACING:
245       gtk_packer_set_spacing (packer, GTK_VALUE_UINT (*arg));
246       break;
247     case ARG_D_BORDER_WIDTH:
248       gtk_packer_set_default_border_width (packer, GTK_VALUE_UINT (*arg));
249       break;
250     case ARG_D_PAD_X:
251       gtk_packer_set_default_pad (packer,
252                                   GTK_VALUE_UINT (*arg),
253                                   packer->default_pad_y);
254       break;
255     case ARG_D_PAD_Y:
256       gtk_packer_set_default_pad (packer,
257                                   packer->default_pad_x,
258                                   GTK_VALUE_UINT (*arg));
259       break;
260     case ARG_D_IPAD_X:
261       gtk_packer_set_default_ipad (packer,
262                                    GTK_VALUE_UINT (*arg),
263                                    packer->default_i_pad_y);
264       break;
265     case ARG_D_IPAD_Y:
266       gtk_packer_set_default_ipad (packer,
267                                    packer->default_i_pad_x,
268                                    GTK_VALUE_UINT (*arg));
269       break;
270     default:
271       break;
272     }
273 }
274
275 static void
276 gtk_packer_get_arg (GtkObject    *object,
277                     GtkArg       *arg,
278                     guint         arg_id)
279 {
280   GtkPacker *packer;
281
282   packer = GTK_PACKER (object);
283
284   switch (arg_id)
285     {
286     case ARG_SPACING:
287       GTK_VALUE_UINT (*arg) = packer->spacing;
288       break;
289     case ARG_D_BORDER_WIDTH:
290       GTK_VALUE_UINT (*arg) = packer->default_border_width;
291       break;
292     case ARG_D_PAD_X:
293       GTK_VALUE_UINT (*arg) = packer->default_pad_x;
294       break;
295     case ARG_D_PAD_Y:
296       GTK_VALUE_UINT (*arg) = packer->default_pad_y;
297       break;
298     case ARG_D_IPAD_X:
299       GTK_VALUE_UINT (*arg) = packer->default_i_pad_x;
300       break;
301     case ARG_D_IPAD_Y:
302       GTK_VALUE_UINT (*arg) = packer->default_i_pad_y;
303       break;
304     default:
305       arg->type = GTK_TYPE_INVALID;
306       break;
307     }
308 }
309
310 static GtkType
311 gtk_packer_child_type (GtkContainer   *container)
312 {
313   return GTK_TYPE_WIDGET;
314 }
315
316 static void
317 gtk_packer_set_child_arg (GtkContainer   *container,
318                           GtkWidget      *child,
319                           GtkArg         *arg,
320                           guint           arg_id)
321 {
322   GtkPacker *packer;
323   GtkPackerChild *child_info = NULL;
324   
325   packer = GTK_PACKER (container);
326
327   if (arg_id != CHILD_ARG_POSITION)
328     {
329       GList *list;
330       
331       list = packer->children;
332       while (list)
333         {
334           child_info = list->data;
335           if (child_info->widget == child)
336             break;
337           
338           list = list->next;
339         }
340       if (!list)
341         return;
342     }
343
344   switch (arg_id)
345     {
346     case CHILD_ARG_SIDE:
347       child_info->side = GTK_VALUE_ENUM (*arg);
348       break;
349     case CHILD_ARG_ANCHOR:
350       child_info->anchor = GTK_VALUE_ENUM (*arg);
351       break;
352     case CHILD_ARG_EXPAND:
353       if (GTK_VALUE_BOOL (*arg))
354         child_info->options |= GTK_PACK_EXPAND;
355       else
356         child_info->options &= ~GTK_PACK_EXPAND;
357       break;
358     case CHILD_ARG_FILL_X:
359       if (GTK_VALUE_BOOL (*arg))
360         child_info->options |= GTK_FILL_X;
361       else
362         child_info->options &= ~GTK_FILL_X;
363       break;
364     case CHILD_ARG_FILL_Y:
365       if (GTK_VALUE_BOOL (*arg))
366         child_info->options |= GTK_FILL_Y;
367       else
368         child_info->options &= ~GTK_FILL_Y;
369       break;
370     case CHILD_ARG_USE_DEFAULT:
371       child_info->use_default = (GTK_VALUE_BOOL (*arg) != 0);
372       break;
373     case CHILD_ARG_BORDER_WIDTH:
374       if (!child_info->use_default)
375         child_info->border_width = GTK_VALUE_UINT (*arg);
376       break;
377     case CHILD_ARG_PAD_X:
378       if (!child_info->use_default)
379         child_info->pad_x = GTK_VALUE_UINT (*arg);
380       break;
381     case CHILD_ARG_PAD_Y:
382       if (!child_info->use_default)
383         child_info->pad_y = GTK_VALUE_UINT (*arg);
384       break;
385     case CHILD_ARG_I_PAD_X:
386       if (!child_info->use_default)
387         child_info->i_pad_x = GTK_VALUE_UINT (*arg);
388       break;
389     case CHILD_ARG_I_PAD_Y:
390       if (!child_info->use_default)
391         child_info->i_pad_y = GTK_VALUE_UINT (*arg);
392       break;
393     case CHILD_ARG_POSITION:
394       gtk_packer_reorder_child (packer,
395                                 child,
396                                 GTK_VALUE_LONG (*arg));
397       break;
398     default:
399       break;
400     }
401
402   if (arg_id != CHILD_ARG_POSITION &&
403       GTK_WIDGET_VISIBLE (packer) &&
404       GTK_WIDGET_VISIBLE (child))
405     gtk_widget_queue_resize (child);
406 }
407
408 static void
409 gtk_packer_get_child_arg (GtkContainer   *container,
410                           GtkWidget      *child,
411                           GtkArg         *arg,
412                           guint           arg_id)
413 {
414   GtkPacker *packer;
415   GtkPackerChild *child_info = NULL;
416   GList * list;
417   
418   packer = GTK_PACKER (container);
419
420   if (arg_id != CHILD_ARG_POSITION)
421     {
422       list = packer->children;
423       while (list)
424         {
425           child_info = list->data;
426           if (child_info->widget == child)
427             break;
428           
429           list = list->next;
430         }
431       if (!list)
432         {
433           arg->type = GTK_TYPE_INVALID;
434           return;
435         }
436     }
437
438   switch (arg_id)
439     {
440     case CHILD_ARG_SIDE:
441       GTK_VALUE_ENUM (*arg) = child_info->side;
442       break;
443     case CHILD_ARG_ANCHOR:
444       GTK_VALUE_ENUM (*arg) = child_info->anchor;
445       break;
446     case CHILD_ARG_EXPAND:
447       GTK_VALUE_BOOL (*arg) = (child_info->options & GTK_PACK_EXPAND) != 0;
448       break;
449     case CHILD_ARG_FILL_X:
450       GTK_VALUE_BOOL (*arg) = (child_info->options & GTK_FILL_X) != 0;
451       break;
452     case CHILD_ARG_FILL_Y:
453       GTK_VALUE_BOOL (*arg) = (child_info->options & GTK_FILL_Y) != 0;
454       break;
455     case CHILD_ARG_USE_DEFAULT:
456       GTK_VALUE_BOOL (*arg) = child_info->use_default;
457       break;
458     case CHILD_ARG_BORDER_WIDTH:
459       GTK_VALUE_UINT (*arg) = child_info->border_width;
460       break;
461     case CHILD_ARG_PAD_X:
462       GTK_VALUE_UINT (*arg) = child_info->pad_x;
463       break;
464     case CHILD_ARG_PAD_Y:
465       GTK_VALUE_UINT (*arg) = child_info->pad_y;
466       break;
467     case CHILD_ARG_I_PAD_X:
468       GTK_VALUE_UINT (*arg) = child_info->i_pad_x;
469       break;
470     case CHILD_ARG_I_PAD_Y:
471       GTK_VALUE_UINT (*arg) = child_info->i_pad_y;
472       break;
473     case CHILD_ARG_POSITION:
474       GTK_VALUE_LONG (*arg) = 0;
475       for (list = packer->children; list; list = list->next)
476         {
477           child_info = list->data;
478           if (child_info->widget == child)
479             break;
480           GTK_VALUE_LONG (*arg)++;
481         }
482       if (!list)
483         GTK_VALUE_LONG (*arg) = -1;
484       break;
485     default:
486       arg->type = GTK_TYPE_INVALID;
487       break;
488     }
489 }
490
491 static void
492 gtk_packer_init (GtkPacker *packer)
493 {
494   GTK_WIDGET_SET_FLAGS (packer, GTK_NO_WINDOW);
495   
496   packer->children = NULL;
497   packer->spacing = 0;
498 }
499
500 void
501 gtk_packer_set_spacing (GtkPacker *packer,
502                         guint      spacing)
503 {
504   g_return_if_fail (packer != NULL);
505   g_return_if_fail (GTK_IS_PACKER (packer));
506   
507   if (spacing != packer->spacing) 
508     {
509       packer->spacing = spacing;
510       gtk_widget_queue_resize (GTK_WIDGET (packer));
511     }
512 }
513
514 GtkWidget*
515 gtk_packer_new (void)
516 {
517   return GTK_WIDGET (gtk_type_new (GTK_TYPE_PACKER));
518 }
519
520 static void
521 redo_defaults_children (GtkPacker *packer)
522 {
523   GList *list;
524   GtkPackerChild *child;
525   
526   list = g_list_first(packer->children);
527   while (list != NULL) 
528     {
529       child = list->data;
530       
531       if (child->use_default) 
532         {
533           child->border_width = packer->default_border_width;
534           child->pad_x = packer->default_pad_x;
535           child->pad_y = packer->default_pad_y;
536           child->i_pad_x = packer->default_i_pad_x;
537           child->i_pad_y = packer->default_i_pad_y;
538           gtk_widget_queue_resize (GTK_WIDGET (child->widget));
539         }
540       list = g_list_next(list);
541     }
542 }
543
544 void
545 gtk_packer_set_default_border_width (GtkPacker *packer,
546                                      guint      border)
547 {
548   g_return_if_fail (packer != NULL);
549   g_return_if_fail (GTK_IS_PACKER (packer));
550   
551   if (packer->default_border_width != border) 
552     {
553       packer->default_border_width = border;;
554       redo_defaults_children (packer);
555     }
556 }
557 void
558 gtk_packer_set_default_pad (GtkPacker *packer,
559                             guint      pad_x,
560                             guint      pad_y)
561 {
562   g_return_if_fail (packer != NULL);
563   g_return_if_fail (GTK_IS_PACKER (packer));
564   
565   if (packer->default_pad_x != pad_x ||
566       packer->default_pad_y != pad_y) 
567     {
568       packer->default_pad_x = pad_x;
569       packer->default_pad_y = pad_y;
570       redo_defaults_children (packer);
571     }
572 }
573
574 void
575 gtk_packer_set_default_ipad (GtkPacker *packer,
576                              guint      i_pad_x,
577                              guint      i_pad_y)
578 {
579   g_return_if_fail (packer != NULL);
580   g_return_if_fail (GTK_IS_PACKER (packer));
581   
582   if (packer->default_i_pad_x != i_pad_x ||
583       packer->default_i_pad_y != i_pad_y) {
584     
585     packer->default_i_pad_x = i_pad_x;
586     packer->default_i_pad_y = i_pad_y;
587     redo_defaults_children (packer);
588   }
589 }
590
591 static void 
592 gtk_packer_container_add (GtkContainer *packer, GtkWidget *child)
593 {
594   gtk_packer_add_defaults(GTK_PACKER(packer), child,
595                           GTK_SIDE_TOP, GTK_ANCHOR_CENTER, 0);
596 }
597
598 void 
599 gtk_packer_add_defaults (GtkPacker       *packer,
600                          GtkWidget       *child,
601                          GtkSideType      side,
602                          GtkAnchorType    anchor,
603                          GtkPackerOptions options)
604 {
605   GtkPackerChild *pchild;
606   
607   g_return_if_fail (packer != NULL);
608   g_return_if_fail (GTK_IS_PACKER (packer));
609   g_return_if_fail (child != NULL);
610   g_return_if_fail (GTK_IS_WIDGET (child));
611   
612   pchild = (GtkPackerChild*) g_malloc(sizeof(GtkPackerChild));
613   
614   pchild->widget = child;
615   pchild->side = side;
616   pchild->options = options;
617   pchild->anchor = anchor;
618   
619   pchild->use_default = 1;
620   
621   pchild->border_width = packer->default_border_width;
622   pchild->pad_x = packer->default_pad_x;
623   pchild->pad_y = packer->default_pad_y;
624   pchild->i_pad_x = packer->default_i_pad_x;
625   pchild->i_pad_y = packer->default_i_pad_y;
626   
627   packer->children = g_list_append(packer->children, (gpointer) pchild);
628   
629   gtk_widget_set_parent (child, GTK_WIDGET (packer));
630   
631   if (GTK_WIDGET_VISIBLE (GTK_WIDGET (packer))) 
632     {
633       if (GTK_WIDGET_REALIZED (GTK_WIDGET (packer)) &&
634           !GTK_WIDGET_REALIZED (child)) 
635         gtk_widget_realize (child);
636       
637       if (GTK_WIDGET_MAPPED (GTK_WIDGET (packer)) &&
638           !GTK_WIDGET_MAPPED (child)) 
639         gtk_widget_map (child);
640     }
641
642   if (GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_VISIBLE (packer))
643     gtk_widget_queue_resize (child);
644   
645 }
646
647 void 
648 gtk_packer_add (GtkPacker       *packer,
649                 GtkWidget       *child,
650                 GtkSideType      side,
651                 GtkAnchorType    anchor,
652                 GtkPackerOptions options,
653                 guint            border_width,
654                 guint            pad_x,
655                 guint            pad_y,
656                 guint            i_pad_x,
657                 guint            i_pad_y)
658 {
659   GtkPackerChild *pchild;
660   
661   g_return_if_fail (packer != NULL);
662   g_return_if_fail (GTK_IS_PACKER (packer));
663   g_return_if_fail (child != NULL);
664   g_return_if_fail (GTK_IS_WIDGET (child));
665   
666   pchild = (GtkPackerChild*) g_malloc(sizeof(GtkPackerChild));
667   
668   pchild->widget = child;
669   pchild->side = side;
670   pchild->options = options;
671   pchild->anchor = anchor;
672   
673   pchild->use_default = 0;
674   
675   pchild->border_width = border_width;
676   pchild->pad_x = pad_x;
677   pchild->pad_y = pad_y;
678   pchild->i_pad_x = i_pad_x;
679   pchild->i_pad_y = i_pad_y;
680   
681   packer->children = g_list_append(packer->children, (gpointer) pchild);
682   
683   gtk_widget_set_parent (child, GTK_WIDGET (packer));
684   
685   if (GTK_WIDGET_VISIBLE (GTK_WIDGET (packer))) 
686     {
687       if (GTK_WIDGET_REALIZED (GTK_WIDGET (packer)) &&
688           !GTK_WIDGET_REALIZED (child)) 
689         gtk_widget_realize (child);
690       
691       if (GTK_WIDGET_MAPPED (GTK_WIDGET (packer)) &&
692           !GTK_WIDGET_MAPPED (child)) 
693         gtk_widget_map (child);
694     }
695   
696   if (GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_VISIBLE (packer))
697     gtk_widget_queue_resize (child);
698   
699 }
700
701 void
702 gtk_packer_set_child_packing (GtkPacker       *packer,
703                               GtkWidget       *child,
704                               GtkSideType      side,
705                               GtkAnchorType    anchor,
706                               GtkPackerOptions options,
707                               guint            border_width,
708                               guint            pad_x,
709                               guint            pad_y,
710                               guint            i_pad_x,
711                               guint            i_pad_y)
712 {
713   GList *list;
714   GtkPackerChild *pchild;
715   
716   g_return_if_fail (packer != NULL);
717   g_return_if_fail (GTK_IS_PACKER (packer));
718   g_return_if_fail (child != NULL);
719   
720   list = g_list_first(packer->children);
721   while (list != NULL) 
722     {
723       pchild = (GtkPackerChild*) list->data;
724       if (pchild->widget == child) 
725         {
726           pchild->side = side;
727           pchild->anchor = anchor;
728           pchild->options = options;
729           
730           pchild->use_default = 0;
731           
732           pchild->border_width = border_width;
733           pchild->pad_x = pad_x;
734           pchild->pad_y = pad_y;
735           pchild->i_pad_x = i_pad_x;
736           pchild->i_pad_y = i_pad_y;
737           
738           if (GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_VISIBLE (packer))
739             gtk_widget_queue_resize (child);
740           return;
741         }
742       list = g_list_next(list);
743     }
744
745   g_warning ("couldn't find child `%s' amongst the packer's children", gtk_type_name (GTK_OBJECT_TYPE (child)));
746 }
747
748 void
749 gtk_packer_reorder_child (GtkPacker *packer,
750                           GtkWidget *child,
751                           gint       position)
752 {
753   GList *list;
754
755   g_return_if_fail (packer != NULL);
756   g_return_if_fail (GTK_IS_PACKER (packer));
757   g_return_if_fail (child != NULL);
758
759   list = packer->children;
760   while (list)
761     {
762       GtkPackerChild *child_info;
763
764       child_info = list->data;
765       if (child_info->widget == child)
766         break;
767
768       list = list->next;
769     }
770
771   if (list && packer->children->next)
772     {
773       GList *tmp_list;
774
775       if (list->next)
776         list->next->prev = list->prev;
777       if (list->prev)
778         list->prev->next = list->next;
779       else
780         packer->children = list->next;
781
782       tmp_list = packer->children;
783       while (position && tmp_list->next)
784         {
785           position--;
786           tmp_list = tmp_list->next;
787         }
788
789       if (position)
790         {
791           tmp_list->next = list;
792           list->prev = tmp_list;
793           list->next = NULL;
794         }
795       else
796         {
797           if (tmp_list->prev)
798             tmp_list->prev->next = list;
799           else
800             packer->children = list;
801           list->prev = tmp_list->prev;
802           tmp_list->prev = list;
803           list->next = tmp_list;
804         }
805
806       if (GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_VISIBLE (packer))
807         gtk_widget_queue_resize (child);
808     }
809 }
810
811 static void 
812 gtk_packer_remove (GtkContainer *container,
813                    GtkWidget    *widget) 
814 {
815   GtkPacker *packer;
816   GtkPackerChild *child;
817   GList *children;
818   gint visible;
819   
820   g_return_if_fail (container != NULL);
821   g_return_if_fail (widget != NULL);
822   
823   packer = GTK_PACKER (container);
824   
825   children = g_list_first(packer->children);
826   while (children) 
827     {
828       child = children->data;
829       
830       if (child->widget == widget) 
831         {
832           visible = GTK_WIDGET_VISIBLE (widget);
833           gtk_widget_unparent (widget);
834           
835           packer->children = g_list_remove_link (packer->children, children);
836           g_list_free (children);
837           g_free (child);
838           
839           if (visible && GTK_WIDGET_VISIBLE (container))
840             gtk_widget_queue_resize (GTK_WIDGET (container));
841           
842           break;
843         }
844       
845       children = g_list_next(children);
846     }
847 }
848
849 static void 
850 gtk_packer_map (GtkWidget *widget)
851 {
852   GtkPacker *packer;
853   GtkPackerChild *child;
854   GList *children;
855   
856   g_return_if_fail (widget != NULL);
857   g_return_if_fail (GTK_IS_PACKER (widget));
858   
859   packer = GTK_PACKER (widget);
860   GTK_WIDGET_SET_FLAGS (packer, GTK_MAPPED);
861   
862   children = g_list_first(packer->children);
863   while (children != NULL) 
864     {
865       child = children->data;
866       children = g_list_next(children);
867       
868       if (GTK_WIDGET_VISIBLE (child->widget) &&
869           !GTK_WIDGET_MAPPED (child->widget))
870         gtk_widget_map (child->widget);
871     }
872 }
873
874 static void 
875 gtk_packer_unmap (GtkWidget *widget)
876 {
877   GtkPacker *packer;
878   GtkPackerChild *child;
879   GList *children;
880   
881   g_return_if_fail (widget != NULL);
882   g_return_if_fail (GTK_IS_PACKER (widget));
883   
884   packer = GTK_PACKER (widget);
885   GTK_WIDGET_UNSET_FLAGS (packer, GTK_MAPPED);
886   
887   children = g_list_first(packer->children);
888   while (children) 
889     {
890       child = children->data;
891       children = g_list_next(children);
892       
893       if (GTK_WIDGET_VISIBLE (child->widget) &&
894           GTK_WIDGET_MAPPED (child->widget))
895         gtk_widget_unmap (child->widget);
896     }
897 }
898
899 static void 
900 gtk_packer_draw (GtkWidget *widget, GdkRectangle     *area)
901 {
902   GtkPacker *packer;
903   GtkPackerChild *child;
904   GdkRectangle child_area;
905   GList *children;
906
907   g_return_if_fail (widget != NULL);
908   g_return_if_fail (GTK_IS_PACKER (widget));
909  
910   if (GTK_WIDGET_DRAWABLE (widget)) 
911     {
912       packer = GTK_PACKER (widget);
913       
914       children = g_list_first(packer->children);
915       while (children != NULL) 
916         {
917           child = children->data;
918           children = g_list_next(children);
919           
920           if (gtk_widget_intersect (child->widget, area, &child_area))
921             gtk_widget_draw (child->widget, &child_area);
922         }
923     }
924   
925 }
926
927 static gint 
928 gtk_packer_expose (GtkWidget *widget, GdkEventExpose *event)
929 {
930   GtkPacker *packer;
931   GtkPackerChild *child;
932   GdkEventExpose child_event;
933   GList *children;
934   
935   g_return_val_if_fail (widget != NULL, FALSE);
936   g_return_val_if_fail (GTK_IS_PACKER (widget), FALSE);
937   g_return_val_if_fail (event != NULL, FALSE);
938   
939   if (GTK_WIDGET_DRAWABLE (widget)) 
940     {
941       packer = GTK_PACKER (widget);
942       
943       child_event = *event;
944       
945       children = g_list_first(packer->children);
946       while (children) 
947         {
948           child = children->data;
949           children = g_list_next(children);
950           
951           if (GTK_WIDGET_NO_WINDOW (child->widget) &&
952               gtk_widget_intersect (child->widget, &event->area, &child_event.area))
953             gtk_widget_event (child->widget, (GdkEvent*) &child_event);
954         }
955     }   
956   
957   return FALSE;
958 }
959
960 static void 
961 gtk_packer_size_request (GtkWidget *widget, GtkRequisition *requisition)
962 {
963   GtkPacker *packer;
964   GtkContainer *container;
965   GtkPackerChild *child;
966   GList *children;
967   gint nvis_vert_children;
968   gint nvis_horz_children;
969   gint width, height;
970   gint maxWidth, maxHeight;
971   
972   g_return_if_fail (widget != NULL);
973   g_return_if_fail (GTK_IS_PACKER (widget));
974   g_return_if_fail (requisition != NULL);
975   
976   packer = GTK_PACKER (widget);
977   container = GTK_CONTAINER (widget);
978
979   requisition->width = 0;
980   requisition->height = 0;
981   nvis_vert_children = 0;
982   nvis_horz_children = 0;
983   
984   width = height = maxWidth = maxHeight = 0;
985   
986   children = g_list_first(packer->children);
987   while (children != NULL) 
988     {
989       child = children->data;
990       
991       if (GTK_WIDGET_VISIBLE (child->widget)) 
992         {
993           gtk_widget_size_request (child->widget, &child->widget->requisition);
994           
995           if((child->side == GTK_SIDE_TOP) || (child->side == GTK_SIDE_BOTTOM))
996             {
997               maxWidth = MAX (maxWidth,
998                               (child->widget->requisition.width +
999                                2 * child->border_width +
1000                                child->pad_x + child->i_pad_x +
1001                                width));
1002               height += (child->widget->requisition.height +
1003                          2 * child->border_width +
1004                          child->pad_y + child->i_pad_y);
1005             } 
1006           else 
1007             {
1008               maxHeight = MAX (maxHeight,
1009                                (child->widget->requisition.height +
1010                                 2 * child->border_width +
1011                                 child->pad_y + child->i_pad_y +
1012                                 height));
1013               width += (child->widget->requisition.width +
1014                         2 * child->border_width +
1015                         child->pad_x + child->i_pad_x);
1016             }
1017         }
1018
1019       children = g_list_next(children);
1020     }
1021
1022   requisition->width = MAX (maxWidth, width) + 2 * container->border_width;
1023   requisition->height = MAX (maxHeight, height) + 2 * container->border_width;
1024 }
1025
1026 static gint
1027 YExpansion (GList *children, gint cavityHeight)
1028 {
1029   GList *list;
1030   GtkPackerChild *child;
1031   GtkWidget *widget;
1032   gint numExpand, minExpand, curExpand;
1033   gint childHeight;
1034   
1035   minExpand = cavityHeight;
1036   numExpand = 0;
1037   
1038   list = children;
1039   while (list != NULL) 
1040     {
1041       child = list->data;
1042       widget = child->widget;
1043       childHeight = (widget->requisition.height +
1044                      2 * child->border_width +
1045                      child->i_pad_y +
1046                      child->pad_y);
1047       if ((child->side == GTK_SIDE_LEFT) || (child->side == GTK_SIDE_RIGHT)) 
1048         {
1049           curExpand = (cavityHeight - childHeight)/numExpand;
1050           minExpand = MIN(minExpand, curExpand);
1051         } 
1052       else 
1053         {
1054           cavityHeight -= childHeight;
1055           if (child->options & GTK_PACK_EXPAND)
1056             numExpand++;
1057         }
1058       list = g_list_next(list);
1059     } 
1060   curExpand = cavityHeight/numExpand; 
1061   if (curExpand < minExpand)
1062     minExpand = curExpand;
1063   return (minExpand < 0) ? 0 : minExpand;
1064 }
1065
1066 static gint
1067 XExpansion (GList *children, gint cavityWidth)
1068 {
1069   GList *list;
1070   GtkPackerChild *child;
1071   GtkWidget *widget;
1072   gint numExpand, minExpand, curExpand;
1073   gint childWidth;
1074   
1075   minExpand = cavityWidth;
1076   numExpand = 0;
1077   
1078   list = children;
1079   while (list != NULL) 
1080     {
1081       child = list->data;
1082       widget = child->widget;
1083       childWidth = (widget->requisition.width +
1084                     2 * child->border_width +
1085                     child->i_pad_x +
1086                     child->pad_x);
1087
1088       if ((child->side == GTK_SIDE_TOP) || (child->side == GTK_SIDE_BOTTOM)) 
1089         {
1090           curExpand = (cavityWidth - childWidth)/numExpand;
1091           minExpand = MIN(minExpand, curExpand); 
1092         } 
1093       else 
1094         {
1095           cavityWidth -= childWidth;
1096           if (child->options & GTK_PACK_EXPAND)
1097             numExpand++;
1098         }
1099       list = g_list_next(list);
1100     } 
1101   curExpand = cavityWidth/numExpand;
1102   if (curExpand < minExpand)
1103     minExpand = curExpand;
1104   return (minExpand < 0) ? 0 : minExpand; 
1105 }
1106
1107 static void 
1108 gtk_packer_size_allocate (GtkWidget *widget, GtkAllocation  *allocation)
1109 {
1110   GtkPacker *packer;
1111   GtkContainer *container;
1112   GtkAllocation child_allocation;
1113   GList *list;
1114   GtkPackerChild *child;
1115   gint cavityX, cavityY;
1116   gint cavityWidth, cavityHeight;
1117   gint width, height, x, y;
1118   gint frameHeight, frameWidth, frameX, frameY;
1119   gint borderX, borderY;
1120   
1121   g_return_if_fail (widget != NULL);
1122   g_return_if_fail (GTK_IS_PACKER (widget));
1123   g_return_if_fail (allocation != NULL);
1124
1125   packer = GTK_PACKER (widget);
1126   container = GTK_CONTAINER (widget);
1127
1128   x = y = 0;
1129
1130   widget->allocation = *allocation;
1131   
1132   cavityX = widget->allocation.x + container->border_width;
1133   cavityY = widget->allocation.y + container->border_width;
1134   cavityWidth = widget->allocation.width - 2 * container->border_width;
1135   cavityHeight = widget->allocation.height - 2 * container->border_width;
1136
1137   list = g_list_first (packer->children);
1138   while (list != NULL)
1139     {
1140       child = list->data;
1141       
1142       if ((child->side == GTK_SIDE_TOP) || (child->side == GTK_SIDE_BOTTOM)) 
1143         {
1144           frameWidth = cavityWidth;
1145           frameHeight = (child->widget->requisition.height +
1146                          2 * child->border_width +
1147                          child->pad_y +
1148                          child->i_pad_y);
1149           if (child->options & GTK_PACK_EXPAND)
1150             frameHeight += YExpansion(list, cavityHeight);
1151           cavityHeight -= frameHeight;
1152           if (cavityHeight < 0) 
1153             {
1154               frameHeight += cavityHeight;
1155               cavityHeight = 0;
1156             }
1157           frameX = cavityX;
1158           if (child->side == GTK_SIDE_TOP) 
1159             {
1160               frameY = cavityY;
1161               cavityY += frameHeight;
1162             } 
1163           else 
1164             {
1165               frameY = cavityY + cavityHeight;
1166             }
1167         } 
1168       else 
1169         {
1170           frameHeight = cavityHeight;
1171           frameWidth = (child->widget->requisition.width +
1172                         2 * child->border_width +
1173                         child->pad_x +
1174                         child->i_pad_x);
1175           if (child->options & GTK_PACK_EXPAND)
1176             frameWidth += XExpansion(list, cavityWidth);
1177           cavityWidth -= frameWidth;
1178           if (cavityWidth < 0) {
1179             frameWidth += cavityWidth;
1180             cavityWidth = 0;
1181           }
1182           frameY = cavityY;
1183           if (child->side == GTK_SIDE_LEFT) 
1184             {
1185               frameX = cavityX;
1186               cavityX += frameWidth;
1187             } 
1188           else 
1189             {
1190               frameX = cavityX + cavityWidth;
1191             }
1192         }
1193       
1194       borderX = child->pad_x + 2 * child->border_width;
1195       borderY = child->pad_y + 2 * child->border_width;
1196       
1197       width = (child->widget->requisition.width +
1198                2 * child->border_width +
1199                child->i_pad_x);
1200       if ((child->options & GTK_FILL_X) || (width > (frameWidth - borderX)))
1201         width = frameWidth - borderX;
1202
1203       height = (child->widget->requisition.height +
1204                 2 * child->border_width +
1205                 child->i_pad_y);
1206       if ((child->options & GTK_FILL_Y) || (height > (frameHeight - borderY)))
1207         height = frameHeight - borderY;
1208       
1209       borderX /= 2;
1210       borderY /= 2;
1211       switch (child->anchor) 
1212         {
1213         case GTK_ANCHOR_N:
1214           x = frameX + (frameWidth - width)/2;
1215           y = frameY + borderY;
1216           break;
1217         case GTK_ANCHOR_NE:
1218           x = frameX + frameWidth - width - borderX;
1219           y = frameY + borderY;
1220           break;
1221         case GTK_ANCHOR_E:
1222           x = frameX + frameWidth - width - borderX;
1223           y = frameY + (frameHeight - height)/2;
1224           break;
1225         case GTK_ANCHOR_SE:
1226           x = frameX + frameWidth - width - borderX;
1227           y = frameY + frameHeight - height - borderY;
1228           break;
1229         case GTK_ANCHOR_S:
1230           x = frameX + (frameWidth - width)/2;
1231           y = frameY + frameHeight - height - borderY;
1232           break;
1233         case GTK_ANCHOR_SW:
1234           x = frameX + borderX;
1235           y = frameY + frameHeight - height - borderY;
1236           break;
1237         case GTK_ANCHOR_W:
1238           x = frameX + borderX;
1239           y = frameY + (frameHeight - height)/2;
1240           break;
1241         case GTK_ANCHOR_NW:
1242           x = frameX + borderX;
1243           y = frameY + borderY;
1244           break;
1245         case GTK_ANCHOR_CENTER:
1246           x = frameX + (frameWidth - width)/2;
1247           y = frameY + (frameHeight - height)/2;
1248           break;
1249         default:
1250           g_warning ("gtk_packer_size_allocate(): bad anchor type: %d", child->anchor);
1251         } 
1252  
1253         if (width <= 0 || height <= 0) 
1254           {
1255             gtk_widget_unmap(child->widget);
1256           } 
1257         else 
1258           {
1259             child_allocation.x = x;
1260             child_allocation.y = y;
1261             child_allocation.width = width;
1262             child_allocation.height = height;
1263             gtk_widget_size_allocate (child->widget, &child_allocation);
1264             
1265             if (GTK_WIDGET_MAPPED (widget) &&
1266                 !(GTK_WIDGET_MAPPED (child->widget)))
1267               gtk_widget_map(child->widget); 
1268           }
1269         
1270         list = g_list_next(list);
1271     }
1272 }
1273
1274 static void
1275 gtk_packer_forall (GtkContainer *container,
1276                    gboolean      include_internals,
1277                    GtkCallback   callback,
1278                    gpointer      callback_data)
1279 {
1280   GtkPacker *packer;
1281   GtkPackerChild *child;
1282   GList *children;
1283   
1284   g_return_if_fail (container != NULL);
1285   g_return_if_fail (GTK_IS_PACKER (container));
1286   g_return_if_fail (callback != NULL);
1287   
1288   packer = GTK_PACKER (container);
1289   
1290   children = g_list_first (packer->children);
1291   while (children != NULL) 
1292     {
1293       child = children->data;
1294       children = g_list_next(children);
1295       
1296       (* callback) (child->widget, callback_data);
1297     }
1298 }
1299