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