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