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