]> Pileus Git - ~andy/gtk/blob - gtk/gtkalignment.c
Mega commit to change ->get_desired_size() for ->get_desired_width/height().
[~andy/gtk] / gtk / gtkalignment.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 Lesser 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  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser 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
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include "config.h"
28 #include "gtkalignment.h"
29 #include "gtkextendedlayout.h"
30 #include "gtkprivate.h"
31 #include "gtkintl.h"
32 #include "gtkalias.h"
33
34 enum {
35   PROP_0,
36
37   PROP_XALIGN,
38   PROP_YALIGN,
39   PROP_XSCALE,
40   PROP_YSCALE,
41
42   PROP_TOP_PADDING,
43   PROP_BOTTOM_PADDING,
44   PROP_LEFT_PADDING,
45   PROP_RIGHT_PADDING
46 };
47
48 #define GTK_ALIGNMENT_GET_PRIVATE(o)  (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_ALIGNMENT, GtkAlignmentPrivate))
49
50 struct _GtkAlignmentPrivate
51 {
52   guint padding_top;
53   guint padding_bottom;
54   guint padding_left;
55   guint padding_right;
56 };
57
58 static void gtk_alignment_size_allocate (GtkWidget         *widget,
59                                          GtkAllocation     *allocation);
60 static void gtk_alignment_set_property (GObject         *object,
61                                         guint            prop_id,
62                                         const GValue    *value,
63                                         GParamSpec      *pspec);
64 static void gtk_alignment_get_property (GObject         *object,
65                                         guint            prop_id,
66                                         GValue          *value,
67                                         GParamSpec      *pspec);
68
69 static void gtk_alignment_extended_layout_init  (GtkExtendedLayoutIface *iface);
70 static void gtk_alignment_get_desired_width     (GtkExtendedLayout *layout,
71                                                  gint              *minimum_size,
72                                                  gint              *natural_size);
73 static void gtk_alignment_get_desired_height    (GtkExtendedLayout *layout,
74                                                  gint              *minimum_size,
75                                                  gint              *natural_size);
76
77 G_DEFINE_TYPE_WITH_CODE (GtkAlignment, gtk_alignment, GTK_TYPE_BIN,
78                          G_IMPLEMENT_INTERFACE (GTK_TYPE_EXTENDED_LAYOUT,
79                                                 gtk_alignment_extended_layout_init))
80
81 static void
82 gtk_alignment_class_init (GtkAlignmentClass *class)
83 {
84   GObjectClass *gobject_class;
85   GtkWidgetClass *widget_class;
86
87   gobject_class = (GObjectClass*) class;
88   widget_class = (GtkWidgetClass*) class;
89   
90   gobject_class->set_property = gtk_alignment_set_property;
91   gobject_class->get_property = gtk_alignment_get_property;
92
93   widget_class->size_allocate = gtk_alignment_size_allocate;
94
95   g_object_class_install_property (gobject_class,
96                                    PROP_XALIGN,
97                                    g_param_spec_float("xalign",
98                                                       P_("Horizontal alignment"),
99                                                       P_("Horizontal position of child in available space. 0.0 is left aligned, 1.0 is right aligned"),
100                                                       0.0,
101                                                       1.0,
102                                                       0.5,
103                                                       GTK_PARAM_READWRITE));
104    
105   g_object_class_install_property (gobject_class,
106                                    PROP_YALIGN,
107                                    g_param_spec_float("yalign",
108                                                       P_("Vertical alignment"),
109                                                       P_("Vertical position of child in available space. 0.0 is top aligned, 1.0 is bottom aligned"),
110                                                       0.0,
111                                                       1.0,
112                                                       0.5,
113                                                       GTK_PARAM_READWRITE));
114   g_object_class_install_property (gobject_class,
115                                    PROP_XSCALE,
116                                    g_param_spec_float("xscale",
117                                                       P_("Horizontal scale"),
118                                                       P_("If available horizontal space is bigger than needed for the child, how much of it to use for the child. 0.0 means none, 1.0 means all"),
119                                                       0.0,
120                                                       1.0,
121                                                       1.0,
122                                                       GTK_PARAM_READWRITE));
123   g_object_class_install_property (gobject_class,
124                                    PROP_YSCALE,
125                                    g_param_spec_float("yscale",
126                                                       P_("Vertical scale"),
127                                                       P_("If available vertical space is bigger than needed for the child, how much of it to use for the child. 0.0 means none, 1.0 means all"),
128                                                       0.0,
129                                                       1.0,
130                                                       1.0,
131                                                       GTK_PARAM_READWRITE));
132
133
134 /**
135  * GtkAlignment:top-padding:
136  *
137  * The padding to insert at the top of the widget.
138  *
139  * Since: 2.4
140  */
141   g_object_class_install_property (gobject_class,
142                                    PROP_TOP_PADDING,
143                                    g_param_spec_uint("top-padding",
144                                                       P_("Top Padding"),
145                                                       P_("The padding to insert at the top of the widget."),
146                                                       0,
147                                                       G_MAXINT,
148                                                       0,
149                                                       GTK_PARAM_READWRITE));
150
151 /**
152  * GtkAlignment:bottom-padding:
153  *
154  * The padding to insert at the bottom of the widget.
155  *
156  * Since: 2.4
157  */
158   g_object_class_install_property (gobject_class,
159                                    PROP_BOTTOM_PADDING,
160                                    g_param_spec_uint("bottom-padding",
161                                                       P_("Bottom Padding"),
162                                                       P_("The padding to insert at the bottom of the widget."),
163                                                       0,
164                                                       G_MAXINT,
165                                                       0,
166                                                       GTK_PARAM_READWRITE));
167
168 /**
169  * GtkAlignment:left-padding:
170  *
171  * The padding to insert at the left of the widget.
172  *
173  * Since: 2.4
174  */
175   g_object_class_install_property (gobject_class,
176                                    PROP_LEFT_PADDING,
177                                    g_param_spec_uint("left-padding",
178                                                       P_("Left Padding"),
179                                                       P_("The padding to insert at the left of the widget."),
180                                                       0,
181                                                       G_MAXINT,
182                                                       0,
183                                                       GTK_PARAM_READWRITE));
184
185 /**
186  * GtkAlignment:right-padding:
187  *
188  * The padding to insert at the right of the widget.
189  *
190  * Since: 2.4
191  */
192   g_object_class_install_property (gobject_class,
193                                    PROP_RIGHT_PADDING,
194                                    g_param_spec_uint("right-padding",
195                                                       P_("Right Padding"),
196                                                       P_("The padding to insert at the right of the widget."),
197                                                       0,
198                                                       G_MAXINT,
199                                                       0,
200                                                       GTK_PARAM_READWRITE));
201
202   g_type_class_add_private (gobject_class, sizeof (GtkAlignmentPrivate));  
203 }
204
205 static void
206 gtk_alignment_init (GtkAlignment *alignment)
207 {
208   GtkAlignmentPrivate *priv;
209   
210   gtk_widget_set_has_window (GTK_WIDGET (alignment), FALSE);
211   gtk_widget_set_redraw_on_allocate (GTK_WIDGET (alignment), FALSE);
212
213   alignment->xalign = 0.5;
214   alignment->yalign = 0.5;
215   alignment->xscale = 1.0;
216   alignment->yscale = 1.0;
217
218   /* Initialize padding with default values: */
219   priv = GTK_ALIGNMENT_GET_PRIVATE (alignment);
220   priv->padding_top = 0;
221   priv->padding_bottom = 0;
222   priv->padding_left = 0;
223   priv->padding_right = 0;
224 }
225
226 GtkWidget*
227 gtk_alignment_new (gfloat xalign,
228                    gfloat yalign,
229                    gfloat xscale,
230                    gfloat yscale)
231 {
232   GtkAlignment *alignment;
233
234   alignment = g_object_new (GTK_TYPE_ALIGNMENT, NULL);
235
236   alignment->xalign = CLAMP (xalign, 0.0, 1.0);
237   alignment->yalign = CLAMP (yalign, 0.0, 1.0);
238   alignment->xscale = CLAMP (xscale, 0.0, 1.0);
239   alignment->yscale = CLAMP (yscale, 0.0, 1.0);
240
241   return GTK_WIDGET (alignment);
242 }
243
244 static void
245 gtk_alignment_set_property (GObject         *object,
246                             guint            prop_id,
247                             const GValue    *value,
248                             GParamSpec      *pspec)
249 {
250   GtkAlignment *alignment;
251   GtkAlignmentPrivate *priv;
252   
253   alignment = GTK_ALIGNMENT (object);
254   priv = GTK_ALIGNMENT_GET_PRIVATE (alignment);
255   
256   switch (prop_id)
257     {
258     case PROP_XALIGN:
259       gtk_alignment_set (alignment,
260                          g_value_get_float (value),
261                          alignment->yalign,
262                          alignment->xscale,
263                          alignment->yscale);
264       break;
265     case PROP_YALIGN:
266       gtk_alignment_set (alignment,
267                          alignment->xalign,
268                          g_value_get_float (value),
269                          alignment->xscale,
270                          alignment->yscale);
271       break;
272     case PROP_XSCALE:
273       gtk_alignment_set (alignment,
274                          alignment->xalign,
275                          alignment->yalign,
276                          g_value_get_float (value),
277                          alignment->yscale);
278       break;
279     case PROP_YSCALE:
280       gtk_alignment_set (alignment,
281                          alignment->xalign,
282                          alignment->yalign,
283                          alignment->xscale,
284                          g_value_get_float (value));
285       break;
286       
287     /* Padding: */
288     case PROP_TOP_PADDING:
289       gtk_alignment_set_padding (alignment,
290                          g_value_get_uint (value),
291                          priv->padding_bottom,
292                          priv->padding_left,
293                          priv->padding_right);
294       break;
295     case PROP_BOTTOM_PADDING:
296       gtk_alignment_set_padding (alignment,
297                          priv->padding_top,
298                          g_value_get_uint (value),
299                          priv->padding_left,
300                          priv->padding_right);
301       break;
302     case PROP_LEFT_PADDING:
303       gtk_alignment_set_padding (alignment,
304                          priv->padding_top,
305                          priv->padding_bottom,
306                          g_value_get_uint (value),
307                          priv->padding_right);
308       break;
309     case PROP_RIGHT_PADDING:
310       gtk_alignment_set_padding (alignment,
311                          priv->padding_top,
312                          priv->padding_bottom,
313                          priv->padding_left,
314                          g_value_get_uint (value));
315       break;
316     
317     default:
318       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
319       break;
320     }
321 }
322
323 static void
324 gtk_alignment_get_property (GObject         *object,
325                             guint            prop_id,
326                             GValue          *value,
327                             GParamSpec      *pspec)
328 {
329   GtkAlignment *alignment;
330   GtkAlignmentPrivate *priv;
331
332   alignment = GTK_ALIGNMENT (object);
333   priv = GTK_ALIGNMENT_GET_PRIVATE (alignment);
334    
335   switch (prop_id)
336     {
337     case PROP_XALIGN:
338       g_value_set_float(value, alignment->xalign);
339       break;
340     case PROP_YALIGN:
341       g_value_set_float(value, alignment->yalign);
342       break;
343     case PROP_XSCALE:
344       g_value_set_float(value, alignment->xscale);
345       break;
346     case PROP_YSCALE:
347       g_value_set_float(value, alignment->yscale);
348       break;
349
350     /* Padding: */
351     case PROP_TOP_PADDING:
352       g_value_set_uint (value, priv->padding_top);
353       break;
354     case PROP_BOTTOM_PADDING:
355       g_value_set_uint (value, priv->padding_bottom);
356       break;
357     case PROP_LEFT_PADDING:
358       g_value_set_uint (value, priv->padding_left);
359       break;
360     case PROP_RIGHT_PADDING:
361       g_value_set_uint (value, priv->padding_right);
362       break;
363       
364     default:
365       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
366       break;
367     }
368 }
369
370 void
371 gtk_alignment_set (GtkAlignment *alignment,
372                    gfloat        xalign,
373                    gfloat        yalign,
374                    gfloat        xscale,
375                    gfloat        yscale)
376 {
377   g_return_if_fail (GTK_IS_ALIGNMENT (alignment));
378
379   xalign = CLAMP (xalign, 0.0, 1.0);
380   yalign = CLAMP (yalign, 0.0, 1.0);
381   xscale = CLAMP (xscale, 0.0, 1.0);
382   yscale = CLAMP (yscale, 0.0, 1.0);
383
384   if (   (alignment->xalign != xalign)
385       || (alignment->yalign != yalign)
386       || (alignment->xscale != xscale)
387       || (alignment->yscale != yscale))
388     {
389       g_object_freeze_notify (G_OBJECT (alignment));
390       if (alignment->xalign != xalign)
391         {
392            alignment->xalign = xalign;
393            g_object_notify (G_OBJECT (alignment), "xalign");
394         }
395       if (alignment->yalign != yalign)
396         {
397            alignment->yalign = yalign;
398            g_object_notify (G_OBJECT (alignment), "yalign");
399         }
400       if (alignment->xscale != xscale)
401         {
402            alignment->xscale = xscale;
403            g_object_notify (G_OBJECT (alignment), "xscale");
404         }
405       if (alignment->yscale != yscale)
406         {
407            alignment->yscale = yscale;
408            g_object_notify (G_OBJECT (alignment), "yscale");
409         }
410       g_object_thaw_notify (G_OBJECT (alignment));
411
412       if (GTK_BIN (alignment)->child)
413         gtk_widget_queue_resize (GTK_BIN (alignment)->child);
414       gtk_widget_queue_draw (GTK_WIDGET (alignment));
415     }
416 }
417
418
419 static void
420 gtk_alignment_size_allocate (GtkWidget     *widget,
421                              GtkAllocation *allocation)
422 {
423   GtkAlignment *alignment;
424   GtkBin *bin;
425   GtkAllocation child_allocation;
426   GtkRequisition child_requisition;
427   gint width, height;
428   gint border_width;
429   gint padding_horizontal, padding_vertical;
430   GtkAlignmentPrivate *priv;
431
432   padding_horizontal = 0;
433   padding_vertical = 0;
434
435   widget->allocation = *allocation;
436   alignment = GTK_ALIGNMENT (widget);
437   bin = GTK_BIN (widget);
438   
439   if (bin->child && gtk_widget_get_visible (bin->child))
440     {
441       GtkExtendedLayout *layout = GTK_EXTENDED_LAYOUT (bin->child);
442
443       gtk_extended_layout_get_desired_size (layout, NULL, &child_requisition);
444
445       border_width = GTK_CONTAINER (alignment)->border_width;
446
447       priv = GTK_ALIGNMENT_GET_PRIVATE (widget);
448       padding_horizontal = priv->padding_left + priv->padding_right;
449       padding_vertical = priv->padding_top + priv->padding_bottom;
450
451       width  = MAX (1, allocation->width - padding_horizontal - 2 * border_width);
452       height = MAX (1, allocation->height - padding_vertical - 2 * border_width);
453
454       if (child_requisition.width > width)
455         gtk_extended_layout_get_height_for_width (layout, width, NULL,
456                                                   &child_requisition.height);
457       else if (child_requisition.height > height)
458         gtk_extended_layout_get_width_for_height (layout, height, NULL,
459                                                   &child_requisition.width);
460
461       if (width > child_requisition.width)
462         child_allocation.width = (child_requisition.width *
463                                   (1.0 - alignment->xscale) +
464                                   width * alignment->xscale);
465       else
466         child_allocation.width = width;
467
468       if (height > child_requisition.height)
469         child_allocation.height = (child_requisition.height *
470                                    (1.0 - alignment->yscale) +
471                                    height * alignment->yscale);
472       else
473         child_allocation.height = height;
474
475       if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
476         child_allocation.x = (1.0 - alignment->xalign) * (width - child_allocation.width) + allocation->x + border_width + priv->padding_right;
477       else 
478         child_allocation.x = alignment->xalign * (width - child_allocation.width) + allocation->x + border_width + priv->padding_left;
479
480       child_allocation.y = alignment->yalign * (height - child_allocation.height) + allocation->y + border_width + priv->padding_top;
481
482       gtk_widget_size_allocate (bin->child, &child_allocation);
483     }
484 }
485
486
487 static void
488 gtk_alignment_extended_layout_init (GtkExtendedLayoutIface *iface)
489 {
490   iface->get_desired_width  = gtk_alignment_get_desired_width;
491   iface->get_desired_height = gtk_alignment_get_desired_height;
492 }
493
494 static void
495 gtk_alignment_get_desired_size (GtkExtendedLayout *layout,
496                                 GtkOrientation     orientation,
497                                 gint              *minimum_size,
498                                 gint              *natural_size)
499 {
500   GtkWidget *child;
501   GtkAlignmentPrivate *priv;
502   gint minimum, natural;
503
504   priv = GTK_ALIGNMENT_GET_PRIVATE (layout);
505
506   natural = minimum = GTK_CONTAINER (layout)->border_width * 2;
507
508   if ((child = gtk_bin_get_child (GTK_BIN (layout))) && gtk_widget_get_visible (child))
509     {
510       gint child_min, child_nat;
511
512       /* Request extra space for the padding: */
513       if (orientation == GTK_ORIENTATION_HORIZONTAL)
514         {
515           minimum += (priv->padding_left + priv->padding_right);
516           gtk_extended_layout_get_desired_width (GTK_EXTENDED_LAYOUT (child), 
517                                                  &child_min, &child_nat);
518         }
519       else
520         {
521           minimum += (priv->padding_top + priv->padding_bottom);
522           gtk_extended_layout_get_desired_height (GTK_EXTENDED_LAYOUT (child), 
523                                                   &child_min, &child_nat);
524         }
525
526       natural = minimum;
527
528       minimum += child_min;
529       natural += child_nat;
530     }
531
532   if (minimum_size)
533     *minimum_size = minimum;
534
535   if (natural_size)
536     *natural_size = natural;
537 }
538
539 static void
540 gtk_alignment_get_desired_width (GtkExtendedLayout *layout,
541                                  gint              *minimum_size,
542                                  gint              *natural_size)
543 {
544   gtk_alignment_get_desired_size (layout, GTK_ORIENTATION_HORIZONTAL, minimum_size, natural_size);
545 }
546
547 static void
548 gtk_alignment_get_desired_height (GtkExtendedLayout *layout,
549                                   gint              *minimum_size,
550                                   gint              *natural_size)
551 {
552   gtk_alignment_get_desired_size (layout, GTK_ORIENTATION_VERTICAL, minimum_size, natural_size);
553 }
554
555 /**
556  * gtk_alignment_set_padding:
557  * @alignment: a #GtkAlignment
558  * @padding_top: the padding at the top of the widget
559  * @padding_bottom: the padding at the bottom of the widget
560  * @padding_left: the padding at the left of the widget
561  * @padding_right: the padding at the right of the widget.
562  *
563  * Sets the padding on the different sides of the widget.
564  * The padding adds blank space to the sides of the widget. For instance,
565  * this can be used to indent the child widget towards the right by adding
566  * padding on the left.
567  *
568  * Since: 2.4
569  */
570 void
571 gtk_alignment_set_padding (GtkAlignment    *alignment,
572                            guint            padding_top,
573                            guint            padding_bottom,
574                            guint            padding_left,
575                            guint            padding_right)
576 {
577   GtkAlignmentPrivate *priv;
578   
579   g_return_if_fail (GTK_IS_ALIGNMENT (alignment));
580
581   priv = GTK_ALIGNMENT_GET_PRIVATE (alignment);
582
583   g_object_freeze_notify (G_OBJECT (alignment));
584
585   if (priv->padding_top != padding_top)
586     {
587       priv->padding_top = padding_top;
588       g_object_notify (G_OBJECT (alignment), "top-padding");
589     }
590   if (priv->padding_bottom != padding_bottom)
591     {
592       priv->padding_bottom = padding_bottom;
593       g_object_notify (G_OBJECT (alignment), "bottom-padding");
594     }
595   if (priv->padding_left != padding_left)
596     {
597       priv->padding_left = padding_left;
598       g_object_notify (G_OBJECT (alignment), "left-padding");
599     }
600   if (priv->padding_right != padding_right)
601     {
602       priv->padding_right = padding_right;
603       g_object_notify (G_OBJECT (alignment), "right-padding");
604     }
605
606   g_object_thaw_notify (G_OBJECT (alignment));
607   
608   /* Make sure that the widget and children are redrawn with the new setting: */
609   if (GTK_BIN (alignment)->child)
610     gtk_widget_queue_resize (GTK_BIN (alignment)->child);
611
612   gtk_widget_queue_draw (GTK_WIDGET (alignment));
613 }
614
615 /**
616  * gtk_alignment_get_padding:
617  * @alignment: a #GtkAlignment
618  * @padding_top: (allow-none): location to store the padding for the top of the widget, or %NULL
619  * @padding_bottom: (allow-none): location to store the padding for the bottom of the widget, or %NULL
620  * @padding_left: (allow-none): location to store the padding for the left of the widget, or %NULL
621  * @padding_right: (allow-none): location to store the padding for the right of the widget, or %NULL
622  *
623  * Gets the padding on the different sides of the widget.
624  * See gtk_alignment_set_padding ().
625  *
626  * Since: 2.4
627  */
628 void
629 gtk_alignment_get_padding (GtkAlignment    *alignment,
630                            guint           *padding_top,
631                            guint           *padding_bottom,
632                            guint           *padding_left,
633                            guint           *padding_right)
634 {
635   GtkAlignmentPrivate *priv;
636  
637   g_return_if_fail (GTK_IS_ALIGNMENT (alignment));
638
639   priv = GTK_ALIGNMENT_GET_PRIVATE (alignment);
640   if(padding_top)
641     *padding_top = priv->padding_top;
642   if(padding_bottom)
643     *padding_bottom = priv->padding_bottom;
644   if(padding_left)
645     *padding_left = priv->padding_left;
646   if(padding_right)
647     *padding_right = priv->padding_right;
648 }
649
650 #define __GTK_ALIGNMENT_C__
651 #include "gtkaliasdef.c"