]> Pileus Git - ~andy/gtk/blob - gtk/gtkfixed.c
Fix another GtkFixed regression, in gtk_fixed_forall()
[~andy/gtk] / gtk / gtkfixed.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 /**
28  * SECTION:gtkfixed
29  * @Short_description: A container which allows you to position
30  * widgets at fixed coordinates
31  * @Title: GtkFixed
32  *
33  * The #GtkFixed widget is a container which can place child widgets
34  * at fixed positions and with fixed sizes, given in pixels. #GtkFixed
35  * performs no automatic layout management.
36  *
37  * For most applications, you should not use this container! It keeps
38  * you from having to learn about the other GTK+ containers, but it
39  * results in broken applications.  With #GtkFixed, the following
40  * things will result in truncated text, overlapping widgets, and
41  * other display bugs:
42  * <itemizedlist>
43  *  <listitem><para>
44  *   Themes, which may change widget sizes.
45  *  </para></listitem>
46  *  <listitem><para>
47  *   Fonts other than the one you used to write the app will of course
48  *   change the size of widgets containing text; keep in mind that
49  *   users may use a larger font because of difficulty reading the
50  *   default, or they may be using Windows or the framebuffer port of
51  *   GTK+, where different fonts are available.
52  *  </para></listitem>
53  *  <listitem><para>
54  *   Translation of text into other languages changes its size. Also,
55  *   display of non-English text will use a different font in many
56  *   cases.
57  *  </para></listitem>
58  * </itemizedlist>
59  *
60  * In addition, the fixed widget can't properly be mirrored in
61  * right-to-left languages such as Hebrew and Arabic. i.e. normally
62  * GTK+ will flip the interface to put labels to the right of the
63  * thing they label, but it can't do that with #GtkFixed. So your
64  * application will not be usable in right-to-left languages.
65  *
66  * Finally, fixed positioning makes it kind of annoying to add/remove
67  * GUI elements, since you have to reposition all the other
68  * elements. This is a long-term maintenance problem for your
69  * application.
70  *
71  * If you know none of these things are an issue for your application,
72  * and prefer the simplicity of #GtkFixed, by all means use the
73  * widget. But you should be aware of the tradeoffs.
74  */
75
76 #include "config.h"
77
78 #include "gtkfixed.h"
79
80 #include "gtkprivate.h"
81 #include "gtkintl.h"
82
83
84 struct _GtkFixedPrivate
85 {
86   GList *children;
87 };
88
89 enum {
90   CHILD_PROP_0,
91   CHILD_PROP_X,
92   CHILD_PROP_Y
93 };
94
95 static void gtk_fixed_realize       (GtkWidget        *widget);
96 static void gtk_fixed_get_preferred_width  (GtkWidget *widget,
97                                             gint      *minimum,
98                                             gint      *natural);
99 static void gtk_fixed_get_preferred_height (GtkWidget *widget,
100                                             gint      *minimum,
101                                             gint      *natural);
102 static void gtk_fixed_size_allocate (GtkWidget        *widget,
103                                      GtkAllocation    *allocation);
104 static void gtk_fixed_add           (GtkContainer     *container,
105                                      GtkWidget        *widget);
106 static void gtk_fixed_remove        (GtkContainer     *container,
107                                      GtkWidget        *widget);
108 static void gtk_fixed_forall        (GtkContainer     *container,
109                                      gboolean          include_internals,
110                                      GtkCallback       callback,
111                                      gpointer          callback_data);
112 static GType gtk_fixed_child_type   (GtkContainer     *container);
113
114 static void gtk_fixed_set_child_property (GtkContainer *container,
115                                           GtkWidget    *child,
116                                           guint         property_id,
117                                           const GValue *value,
118                                           GParamSpec   *pspec);
119 static void gtk_fixed_get_child_property (GtkContainer *container,
120                                           GtkWidget    *child,
121                                           guint         property_id,
122                                           GValue       *value,
123                                           GParamSpec   *pspec);
124
125 G_DEFINE_TYPE (GtkFixed, gtk_fixed, GTK_TYPE_CONTAINER)
126
127 static void
128 gtk_fixed_class_init (GtkFixedClass *class)
129 {
130   GtkWidgetClass *widget_class;
131   GtkContainerClass *container_class;
132
133   widget_class = (GtkWidgetClass*) class;
134   container_class = (GtkContainerClass*) class;
135
136   widget_class->realize = gtk_fixed_realize;
137   widget_class->get_preferred_width = gtk_fixed_get_preferred_width;
138   widget_class->get_preferred_height = gtk_fixed_get_preferred_height;
139   widget_class->size_allocate = gtk_fixed_size_allocate;
140
141   container_class->add = gtk_fixed_add;
142   container_class->remove = gtk_fixed_remove;
143   container_class->forall = gtk_fixed_forall;
144   container_class->child_type = gtk_fixed_child_type;
145   container_class->set_child_property = gtk_fixed_set_child_property;
146   container_class->get_child_property = gtk_fixed_get_child_property;
147   gtk_container_class_handle_border_width (container_class);
148
149   gtk_container_class_install_child_property (container_class,
150                                               CHILD_PROP_X,
151                                               g_param_spec_int ("x",
152                                                                 P_("X position"),
153                                                                 P_("X position of child widget"),
154                                                                 G_MININT, G_MAXINT, 0,
155                                                                 GTK_PARAM_READWRITE));
156
157   gtk_container_class_install_child_property (container_class,
158                                               CHILD_PROP_Y,
159                                               g_param_spec_int ("y",
160                                                                 P_("Y position"),
161                                                                 P_("Y position of child widget"),
162                                                                 G_MININT, G_MAXINT, 0,
163                                                                 GTK_PARAM_READWRITE));
164
165   g_type_class_add_private (class, sizeof (GtkFixedPrivate));
166 }
167
168 static GType
169 gtk_fixed_child_type (GtkContainer *container)
170 {
171   return GTK_TYPE_WIDGET;
172 }
173
174 static void
175 gtk_fixed_init (GtkFixed *fixed)
176 {
177   fixed->priv = G_TYPE_INSTANCE_GET_PRIVATE (fixed, GTK_TYPE_FIXED, GtkFixedPrivate);
178
179   gtk_widget_set_has_window (GTK_WIDGET (fixed), FALSE);
180
181   fixed->priv->children = NULL;
182 }
183
184 /**
185  * gtk_fixed_new:
186  *
187  * Creates a new #GtkFixed.
188  *
189  * Returns: a new #GtkFixed.
190  */
191 GtkWidget*
192 gtk_fixed_new (void)
193 {
194   return g_object_new (GTK_TYPE_FIXED, NULL);
195 }
196
197 static GtkFixedChild*
198 get_child (GtkFixed  *fixed,
199            GtkWidget *widget)
200 {
201   GtkFixedPrivate *priv = fixed->priv;
202   GList *children;
203
204   for (children = priv->children; children; children = children->next)
205     {
206       GtkFixedChild *child;
207
208       child = children->data;
209
210       if (child->widget == widget)
211         return child;
212     }
213
214   return NULL;
215 }
216
217 /**
218  * gtk_fixed_put:
219  * @fixed: a #GtkFixed.
220  * @widget: the widget to add.
221  * @x: the horizontal position to place the widget at.
222  * @y: the vertical position to place the widget at.
223  *
224  * Adds a widget to a #GtkFixed container at the given position.
225  */
226 void
227 gtk_fixed_put (GtkFixed  *fixed,
228                GtkWidget *widget,
229                gint       x,
230                gint       y)
231 {
232   GtkFixedPrivate *priv = fixed->priv;
233   GtkFixedChild *child_info;
234
235   g_return_if_fail (GTK_IS_FIXED (fixed));
236   g_return_if_fail (GTK_IS_WIDGET (widget));
237
238   child_info = g_new (GtkFixedChild, 1);
239   child_info->widget = widget;
240   child_info->x = x;
241   child_info->y = y;
242
243   gtk_widget_set_parent (widget, GTK_WIDGET (fixed));
244
245   priv->children = g_list_append (priv->children, child_info);
246 }
247
248 static void
249 gtk_fixed_move_internal (GtkFixed      *fixed,
250                          GtkFixedChild *child,
251                          gint           x,
252                          gint           y)
253 {
254   g_return_if_fail (GTK_IS_FIXED (fixed));
255   g_return_if_fail (gtk_widget_get_parent (child->widget) == GTK_WIDGET (fixed));
256
257   gtk_widget_freeze_child_notify (child->widget);
258
259   if (child->x != x)
260     {
261       child->x = x;
262       gtk_widget_child_notify (child->widget, "x");
263     }
264
265   if (child->y != y)
266     {
267       child->y = y;
268       gtk_widget_child_notify (child->widget, "y");
269     }
270
271   gtk_widget_thaw_child_notify (child->widget);
272
273   if (gtk_widget_get_visible (child->widget) &&
274       gtk_widget_get_visible (GTK_WIDGET (fixed)))
275     gtk_widget_queue_resize (GTK_WIDGET (fixed));
276 }
277
278 /**
279  * gtk_fixed_move:
280  * @fixed: a #GtkFixed.
281  * @widget: the child widget.
282  * @x: the horizontal position to move the widget to.
283  * @y: the vertical position to move the widget to.
284  *
285  * Moves a child of a #GtkFixed container to the given position.
286  */
287 void
288 gtk_fixed_move (GtkFixed  *fixed,
289                 GtkWidget *widget,
290                 gint       x,
291                 gint       y)
292 {
293   gtk_fixed_move_internal (fixed, get_child (fixed, widget), x, y);
294 }
295
296 static void
297 gtk_fixed_set_child_property (GtkContainer *container,
298                               GtkWidget    *child,
299                               guint         property_id,
300                               const GValue *value,
301                               GParamSpec   *pspec)
302 {
303   GtkFixed *fixed = GTK_FIXED (container);
304   GtkFixedChild *fixed_child;
305
306   fixed_child = get_child (fixed, child);
307
308   switch (property_id)
309     {
310     case CHILD_PROP_X:
311       gtk_fixed_move_internal (fixed,
312                                fixed_child,
313                                g_value_get_int (value),
314                                fixed_child->y);
315       break;
316     case CHILD_PROP_Y:
317       gtk_fixed_move_internal (fixed,
318                                fixed_child,
319                                fixed_child->x,
320                                g_value_get_int (value));
321       break;
322     default:
323       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
324       break;
325     }
326 }
327
328 static void
329 gtk_fixed_get_child_property (GtkContainer *container,
330                               GtkWidget    *child,
331                               guint         property_id,
332                               GValue       *value,
333                               GParamSpec   *pspec)
334 {
335   GtkFixedChild *fixed_child;
336
337   fixed_child = get_child (GTK_FIXED (container), child);
338   
339   switch (property_id)
340     {
341     case CHILD_PROP_X:
342       g_value_set_int (value, fixed_child->x);
343       break;
344     case CHILD_PROP_Y:
345       g_value_set_int (value, fixed_child->y);
346       break;
347     default:
348       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
349       break;
350     }
351 }
352
353 static void
354 gtk_fixed_realize (GtkWidget *widget)
355 {
356   GtkAllocation allocation;
357   GdkWindow *window;
358   GdkWindowAttr attributes;
359   gint attributes_mask;
360
361   if (!gtk_widget_get_has_window (widget))
362     GTK_WIDGET_CLASS (gtk_fixed_parent_class)->realize (widget);
363   else
364     {
365       gtk_widget_set_realized (widget, TRUE);
366
367       gtk_widget_get_allocation (widget, &allocation);
368
369       attributes.window_type = GDK_WINDOW_CHILD;
370       attributes.x = allocation.x;
371       attributes.y = allocation.y;
372       attributes.width = allocation.width;
373       attributes.height = allocation.height;
374       attributes.wclass = GDK_INPUT_OUTPUT;
375       attributes.visual = gtk_widget_get_visual (widget);
376       attributes.event_mask = gtk_widget_get_events (widget);
377       attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK;
378
379       attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
380
381       window = gdk_window_new (gtk_widget_get_parent_window (widget),
382                                &attributes, attributes_mask);
383       gtk_widget_set_window (widget, window);
384       gdk_window_set_user_data (window, widget);
385
386       gtk_style_context_set_background (gtk_widget_get_style_context (widget),
387                                         window);
388     }
389 }
390
391 static void
392 gtk_fixed_get_preferred_width (GtkWidget *widget,
393                                gint      *minimum,
394                                gint      *natural)
395 {
396   GtkFixed *fixed = GTK_FIXED (widget);
397   GtkFixedPrivate *priv = fixed->priv;
398   GtkFixedChild *child;
399   GList *children;
400   gint child_min, child_nat;
401
402   *minimum = 0;
403   *natural = 0;
404
405   for (children = priv->children; children; children = children->next)
406     {
407       child = children->data;
408
409       if (!gtk_widget_get_visible (child->widget))
410         continue;
411
412       gtk_widget_get_preferred_width (child->widget, &child_min, &child_nat);
413
414       *minimum = MAX (*minimum, child->x + child_min);
415       *natural = MAX (*natural, child->x + child_nat);
416     }
417 }
418
419 static void
420 gtk_fixed_get_preferred_height (GtkWidget *widget,
421                                 gint      *minimum,
422                                 gint      *natural)
423 {
424   GtkFixed *fixed = GTK_FIXED (widget);
425   GtkFixedPrivate *priv = fixed->priv;
426   GtkFixedChild *child;
427   GList *children;
428   gint child_min, child_nat;
429
430   *minimum = 0;
431   *natural = 0;
432
433   for (children = priv->children; children; children = children->next)
434     {
435       child = children->data;
436
437       if (!gtk_widget_get_visible (child->widget))
438         continue;
439
440       gtk_widget_get_preferred_height (child->widget, &child_min, &child_nat);
441
442       *minimum = MAX (*minimum, child->y + child_min);
443       *natural = MAX (*natural, child->y + child_nat);
444     }
445 }
446
447 static void
448 gtk_fixed_size_allocate (GtkWidget     *widget,
449                          GtkAllocation *allocation)
450 {
451   GtkFixed *fixed = GTK_FIXED (widget);
452   GtkFixedPrivate *priv = fixed->priv;
453   GtkFixedChild *child;
454   GtkAllocation child_allocation;
455   GtkRequisition child_requisition;
456   GList *children;
457
458   gtk_widget_set_allocation (widget, allocation);
459
460   if (gtk_widget_get_has_window (widget))
461     {
462       if (gtk_widget_get_realized (widget))
463         gdk_window_move_resize (gtk_widget_get_window (widget),
464                                 allocation->x,
465                                 allocation->y,
466                                 allocation->width,
467                                 allocation->height);
468     }
469
470   for (children = priv->children; children; children = children->next)
471     {
472       child = children->data;
473
474       if (!gtk_widget_get_visible (child->widget))
475         continue;
476
477       gtk_widget_get_preferred_size (child->widget, &child_requisition, NULL);
478       child_allocation.x = child->x;
479       child_allocation.y = child->y;
480
481       if (!gtk_widget_get_has_window (widget))
482         {
483           child_allocation.x += allocation->x;
484           child_allocation.y += allocation->y;
485         }
486
487       child_allocation.width = child_requisition.width;
488       child_allocation.height = child_requisition.height;
489       gtk_widget_size_allocate (child->widget, &child_allocation);
490     }
491 }
492
493 static void
494 gtk_fixed_add (GtkContainer *container,
495                GtkWidget    *widget)
496 {
497   gtk_fixed_put (GTK_FIXED (container), widget, 0, 0);
498 }
499
500 static void
501 gtk_fixed_remove (GtkContainer *container,
502                   GtkWidget    *widget)
503 {
504   GtkFixed *fixed = GTK_FIXED (container);
505   GtkFixedPrivate *priv = fixed->priv;
506   GtkFixedChild *child;
507   GtkWidget *widget_container = GTK_WIDGET (container);
508   GList *children;
509
510   for (children = priv->children; children; children = children->next)
511     {
512       child = children->data;
513
514       if (child->widget == widget)
515         {
516           gboolean was_visible = gtk_widget_get_visible (widget);
517
518           gtk_widget_unparent (widget);
519
520           priv->children = g_list_remove_link (priv->children, children);
521           g_list_free (children);
522           g_free (child);
523
524           if (was_visible && gtk_widget_get_visible (widget_container))
525             gtk_widget_queue_resize (widget_container);
526
527           break;
528         }
529     }
530 }
531
532 static void
533 gtk_fixed_forall (GtkContainer *container,
534                   gboolean      include_internals,
535                   GtkCallback   callback,
536                   gpointer      callback_data)
537 {
538   GtkFixed *fixed = GTK_FIXED (container);
539   GtkFixedPrivate *priv = fixed->priv;
540   GtkFixedChild *child;
541   GList *children;
542
543   children = priv->children;
544   while (children)
545     {
546       child = children->data;
547       children = children->next;
548
549       (* callback) (child->widget, callback_data);
550     }
551 }