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