]> Pileus Git - ~andy/gtk/blob - gtk/gtkstatusbar.c
stylecontext: Do invalidation on first resize container
[~andy/gtk] / gtk / gtkstatusbar.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  * GtkStatusbar Copyright (C) 1998 Shawn T. Amundson
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 /*
20  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
21  * file for a list of people on the GTK+ Team.  See the ChangeLog
22  * files for a list of changes.  These files are distributed with
23  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
24  */
25
26 #include "config.h"
27
28 #include "gtkstatusbar.h"
29
30 #include "gtkboxprivate.h"
31 #include "gtkframe.h"
32 #include "gtklabel.h"
33 #include "gtkmarshalers.h"
34 #include "gtkwindow.h"
35 #include "gtkprivate.h"
36 #include "gtkintl.h"
37 #include "gtkbuildable.h"
38 #include "gtkorientable.h"
39 #include "gtktypebuiltins.h"
40 #include "a11y/gtkstatusbaraccessible.h"
41
42 /**
43  * SECTION:gtkstatusbar
44  * @title: GtkStatusbar
45  * @short_description: Report messages of minor importance to the user
46  *
47  * A #GtkStatusbar is usually placed along the bottom of an application's
48  * main #GtkWindow. It may provide a regular commentary of the application's
49  * status (as is usually the case in a web browser, for example), or may be
50  * used to simply output a message when the status changes, (when an upload
51  * is complete in an FTP client, for example).
52  *
53  * Status bars in GTK+ maintain a stack of messages. The message at
54  * the top of the each bar's stack is the one that will currently be displayed.
55  *
56  * Any messages added to a statusbar's stack must specify a
57  * <emphasis>context id</emphasis> that is used to uniquely identify
58  * the source of a message. This context id can be generated by
59  * gtk_statusbar_get_context_id(), given a message and the statusbar that
60  * it will be added to. Note that messages are stored in a stack, and when
61  * choosing which message to display, the stack structure is adhered to,
62  * regardless of the context identifier of a message.
63  *
64  * One could say that a statusbar maintains one stack of messages for
65  * display purposes, but allows multiple message producers to maintain
66  * sub-stacks of the messages they produced (via context ids).
67  *
68  * Status bars are created using gtk_statusbar_new().
69  *
70  * Messages are added to the bar's stack with gtk_statusbar_push().
71  *
72  * The message at the top of the stack can be removed using
73  * gtk_statusbar_pop(). A message can be removed from anywhere in the
74  * stack if its message id was recorded at the time it was added. This
75  * is done using gtk_statusbar_remove().
76  */
77 typedef struct _GtkStatusbarMsg GtkStatusbarMsg;
78
79 struct _GtkStatusbarPrivate
80 {
81   GtkWidget     *frame;
82   GtkWidget     *label;
83
84   GSList        *messages;
85   GSList        *keys;
86
87   guint          seq_context_id;
88   guint          seq_message_id;
89 };
90
91
92 struct _GtkStatusbarMsg
93 {
94   gchar *text;
95   guint context_id;
96   guint message_id;
97 };
98
99 enum
100 {
101   SIGNAL_TEXT_PUSHED,
102   SIGNAL_TEXT_POPPED,
103   SIGNAL_LAST
104 };
105
106 static void     gtk_statusbar_buildable_interface_init    (GtkBuildableIface *iface);
107 static GObject *gtk_statusbar_buildable_get_internal_child (GtkBuildable *buildable,
108                                                             GtkBuilder   *builder,
109                                                             const gchar  *childname);
110 static void     gtk_statusbar_update            (GtkStatusbar      *statusbar,
111                                                  guint              context_id,
112                                                  const gchar       *text);
113 static void     gtk_statusbar_realize           (GtkWidget         *widget);
114 static void     gtk_statusbar_destroy           (GtkWidget         *widget);
115 static void     gtk_statusbar_size_allocate     (GtkWidget         *widget,
116                                                  GtkAllocation     *allocation);
117 static void     gtk_statusbar_hierarchy_changed (GtkWidget         *widget,
118                                                  GtkWidget         *previous_toplevel);
119
120
121 static guint              statusbar_signals[SIGNAL_LAST] = { 0 };
122
123 G_DEFINE_TYPE_WITH_CODE (GtkStatusbar, gtk_statusbar, GTK_TYPE_BOX,
124                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
125                                                 gtk_statusbar_buildable_interface_init));
126
127 static void
128 gtk_statusbar_class_init (GtkStatusbarClass *class)
129 {
130   GtkWidgetClass *widget_class;
131
132   widget_class = (GtkWidgetClass *) class;
133
134   widget_class->realize = gtk_statusbar_realize;
135   widget_class->destroy = gtk_statusbar_destroy;
136   widget_class->size_allocate = gtk_statusbar_size_allocate;
137   widget_class->hierarchy_changed = gtk_statusbar_hierarchy_changed;
138
139   class->text_pushed = gtk_statusbar_update;
140   class->text_popped = gtk_statusbar_update;
141
142   /**
143    * GtkStatusbar::text-pushed:
144    * @statusbar: the object which received the signal
145    * @context_id: the context id of the relevant message/statusbar
146    * @text: the message that was pushed
147    *
148    * Is emitted whenever a new message gets pushed onto a statusbar's stack.
149    */
150   statusbar_signals[SIGNAL_TEXT_PUSHED] =
151     g_signal_new (I_("text-pushed"),
152                   G_OBJECT_CLASS_TYPE (class),
153                   G_SIGNAL_RUN_LAST,
154                   G_STRUCT_OFFSET (GtkStatusbarClass, text_pushed),
155                   NULL, NULL,
156                   _gtk_marshal_VOID__UINT_STRING,
157                   G_TYPE_NONE, 2,
158                   G_TYPE_UINT,
159                   G_TYPE_STRING);
160
161   /**
162    * GtkStatusbar::text-popped:
163    * @statusbar: the object which received the signal
164    * @context_id: the context id of the relevant message/statusbar
165    * @text: the message that was just popped
166    *
167    * Is emitted whenever a new message is popped off a statusbar's stack.
168    */
169   statusbar_signals[SIGNAL_TEXT_POPPED] =
170     g_signal_new (I_("text-popped"),
171                   G_OBJECT_CLASS_TYPE (class),
172                   G_SIGNAL_RUN_LAST,
173                   G_STRUCT_OFFSET (GtkStatusbarClass, text_popped),
174                   NULL, NULL,
175                   _gtk_marshal_VOID__UINT_STRING,
176                   G_TYPE_NONE, 2,
177                   G_TYPE_UINT,
178                   G_TYPE_STRING);
179
180   gtk_widget_class_install_style_property (widget_class,
181                                            g_param_spec_enum ("shadow-type",
182                                                               P_("Shadow type"),
183                                                               P_("Style of bevel around the statusbar text"),
184                                                               GTK_TYPE_SHADOW_TYPE,
185                                                               GTK_SHADOW_IN,
186                                                               GTK_PARAM_READABLE));
187
188    g_type_class_add_private (class, sizeof (GtkStatusbarPrivate));
189
190    gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_STATUSBAR_ACCESSIBLE);
191 }
192
193 static void
194 gtk_statusbar_init (GtkStatusbar *statusbar)
195 {
196   GtkStatusbarPrivate *priv;
197   GtkBox *box = GTK_BOX (statusbar);
198   GtkWidget *message_area;
199   GtkShadowType shadow_type;
200
201   statusbar->priv = G_TYPE_INSTANCE_GET_PRIVATE (statusbar,
202                                                  GTK_TYPE_STATUSBAR,
203                                                  GtkStatusbarPrivate);
204   priv = statusbar->priv;
205
206   gtk_widget_set_redraw_on_allocate (GTK_WIDGET (box), TRUE);
207
208   gtk_box_set_spacing (box, 2);
209   gtk_box_set_homogeneous (box, FALSE);
210
211   gtk_widget_style_get (GTK_WIDGET (statusbar), "shadow-type", &shadow_type, NULL);
212
213   priv->frame = gtk_frame_new (NULL);
214   gtk_frame_set_shadow_type (GTK_FRAME (priv->frame), shadow_type);
215   gtk_box_pack_start (box, priv->frame, TRUE, TRUE, 0);
216   gtk_widget_show (priv->frame);
217
218   message_area = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
219   gtk_container_add (GTK_CONTAINER (priv->frame), message_area);
220   gtk_widget_show (message_area);
221
222   priv->label = gtk_label_new ("");
223   gtk_label_set_single_line_mode (GTK_LABEL (priv->label), TRUE);
224   gtk_widget_set_halign (priv->label, GTK_ALIGN_START);
225   gtk_widget_set_valign (priv->label, GTK_ALIGN_CENTER);
226   gtk_label_set_ellipsize (GTK_LABEL (priv->label), PANGO_ELLIPSIZE_END);
227   gtk_container_add (GTK_CONTAINER (message_area), priv->label);
228   gtk_widget_show (priv->label);
229
230   priv->seq_context_id = 1;
231   priv->seq_message_id = 1;
232   priv->messages = NULL;
233   priv->keys = NULL;
234 }
235
236 static GtkBuildableIface *parent_buildable_iface;
237
238 static void
239 gtk_statusbar_buildable_interface_init (GtkBuildableIface *iface)
240 {
241   parent_buildable_iface = g_type_interface_peek_parent (iface);
242   iface->get_internal_child = gtk_statusbar_buildable_get_internal_child;
243 }
244
245 static GObject *
246 gtk_statusbar_buildable_get_internal_child (GtkBuildable *buildable,
247                                             GtkBuilder   *builder,
248                                             const gchar  *childname)
249 {
250   GtkStatusbar *statusbar = GTK_STATUSBAR (buildable);
251   GtkStatusbarPrivate *priv = statusbar->priv;
252
253     if (strcmp (childname, "message_area") == 0)
254       return G_OBJECT (gtk_bin_get_child (GTK_BIN (priv->frame)));
255
256     return parent_buildable_iface->get_internal_child (buildable,
257                                                        builder,
258                                                        childname);
259 }
260
261 /**
262  * gtk_statusbar_new:
263  *
264  * Creates a new #GtkStatusbar ready for messages.
265  *
266  * Returns: the new #GtkStatusbar
267  */
268 GtkWidget* 
269 gtk_statusbar_new (void)
270 {
271   return g_object_new (GTK_TYPE_STATUSBAR, NULL);
272 }
273
274 static void
275 gtk_statusbar_update (GtkStatusbar *statusbar,
276                       guint         context_id,
277                       const gchar  *text)
278 {
279   GtkStatusbarPrivate *priv;
280
281   g_return_if_fail (GTK_IS_STATUSBAR (statusbar));
282
283   priv = statusbar->priv;
284
285   if (!text)
286     text = "";
287
288   gtk_label_set_text (GTK_LABEL (priv->label), text);
289 }
290
291 /**
292  * gtk_statusbar_get_context_id:
293  * @statusbar: a #GtkStatusbar
294  * @context_description: textual description of what context 
295  *                       the new message is being used in
296  *
297  * Returns a new context identifier, given a description 
298  * of the actual context. Note that the description is 
299  * <emphasis>not</emphasis> shown in the UI.
300  *
301  * Returns: an integer id
302  */
303 guint
304 gtk_statusbar_get_context_id (GtkStatusbar *statusbar,
305                               const gchar  *context_description)
306 {
307   GtkStatusbarPrivate *priv;
308   gchar *string;
309   guint id;
310   
311   g_return_val_if_fail (GTK_IS_STATUSBAR (statusbar), 0);
312   g_return_val_if_fail (context_description != NULL, 0);
313
314   priv = statusbar->priv;
315
316   /* we need to preserve namespaces on object datas */
317   string = g_strconcat ("gtk-status-bar-context:", context_description, NULL);
318
319   id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (statusbar), string));
320   if (id == 0)
321     {
322       id = priv->seq_context_id++;
323       g_object_set_data_full (G_OBJECT (statusbar), string, GUINT_TO_POINTER (id), NULL);
324       priv->keys = g_slist_prepend (priv->keys, string);
325     }
326   else
327     g_free (string);
328
329   return id;
330 }
331
332 static GtkStatusbarMsg *
333 gtk_statusbar_msg_create (GtkStatusbar *statusbar,
334                           guint         context_id,
335                           const gchar  *text)
336 {
337   GtkStatusbarMsg *msg;
338
339   msg = g_slice_new (GtkStatusbarMsg);
340   msg->text = g_strdup (text);
341   msg->context_id = context_id;
342   msg->message_id = statusbar->priv->seq_message_id++;
343
344   return msg;
345 }
346
347 static void
348 gtk_statusbar_msg_free (GtkStatusbarMsg *msg)
349 {
350   g_free (msg->text);
351   g_slice_free (GtkStatusbarMsg, msg);
352 }
353
354 /**
355  * gtk_statusbar_push:
356  * @statusbar: a #GtkStatusbar
357  * @context_id: the message's context id, as returned by
358  *              gtk_statusbar_get_context_id()
359  * @text: the message to add to the statusbar
360  * 
361  * Pushes a new message onto a statusbar's stack.
362  *
363  * Returns: a message id that can be used with 
364  *          gtk_statusbar_remove().
365  */
366 guint
367 gtk_statusbar_push (GtkStatusbar *statusbar,
368                     guint         context_id,
369                     const gchar  *text)
370 {
371   GtkStatusbarPrivate *priv;
372   GtkStatusbarMsg *msg;
373
374   g_return_val_if_fail (GTK_IS_STATUSBAR (statusbar), 0);
375   g_return_val_if_fail (text != NULL, 0);
376
377   priv = statusbar->priv;
378
379   msg = gtk_statusbar_msg_create (statusbar, context_id, text);
380   priv->messages = g_slist_prepend (priv->messages, msg);
381
382   g_signal_emit (statusbar,
383                  statusbar_signals[SIGNAL_TEXT_PUSHED],
384                  0,
385                  msg->context_id,
386                  msg->text);
387
388   return msg->message_id;
389 }
390
391 /**
392  * gtk_statusbar_pop:
393  * @statusbar: a #GtkStatusbar
394  * @context_id: a context identifier
395  * 
396  * Removes the first message in the #GtkStatusbar's stack
397  * with the given context id. 
398  *
399  * Note that this may not change the displayed message, if 
400  * the message at the top of the stack has a different 
401  * context id.
402  */
403 void
404 gtk_statusbar_pop (GtkStatusbar *statusbar,
405                    guint         context_id)
406 {
407   GtkStatusbarPrivate *priv;
408   GtkStatusbarMsg *msg;
409
410   g_return_if_fail (GTK_IS_STATUSBAR (statusbar));
411
412   priv = statusbar->priv;
413
414   if (priv->messages)
415     {
416       GSList *list;
417
418       for (list = priv->messages; list; list = list->next)
419         {
420           msg = list->data;
421
422           if (msg->context_id == context_id)
423             {
424               priv->messages = g_slist_remove_link (priv->messages, list);
425               gtk_statusbar_msg_free (msg);
426               g_slist_free_1 (list);
427               break;
428             }
429         }
430     }
431
432   msg = priv->messages ? priv->messages->data : NULL;
433
434   g_signal_emit (statusbar,
435                  statusbar_signals[SIGNAL_TEXT_POPPED],
436                  0,
437                  (guint) (msg ? msg->context_id : 0),
438                  msg ? msg->text : NULL);
439 }
440
441 /**
442  * gtk_statusbar_remove:
443  * @statusbar: a #GtkStatusbar
444  * @context_id: a context identifier
445  * @message_id: a message identifier, as returned by gtk_statusbar_push()
446  *
447  * Forces the removal of a message from a statusbar's stack. 
448  * The exact @context_id and @message_id must be specified.
449  */
450 void
451 gtk_statusbar_remove (GtkStatusbar *statusbar,
452                       guint        context_id,
453                       guint        message_id)
454 {
455   GtkStatusbarPrivate *priv;
456   GtkStatusbarMsg *msg;
457
458   g_return_if_fail (GTK_IS_STATUSBAR (statusbar));
459   g_return_if_fail (message_id > 0);
460
461   priv = statusbar->priv;
462
463   msg = priv->messages ? priv->messages->data : NULL;
464   if (msg)
465     {
466       GSList *list;
467
468       /* care about signal emission if the topmost item is removed */
469       if (msg->context_id == context_id &&
470           msg->message_id == message_id)
471         {
472           gtk_statusbar_pop (statusbar, context_id);
473           return;
474         }
475       
476       for (list = priv->messages; list; list = list->next)
477         {
478           msg = list->data;
479           
480           if (msg->context_id == context_id &&
481               msg->message_id == message_id)
482             {
483               priv->messages = g_slist_remove_link (priv->messages, list);
484               gtk_statusbar_msg_free (msg);
485               g_slist_free_1 (list);
486               
487               break;
488             }
489         }
490     }
491 }
492
493 /**
494  * gtk_statusbar_remove_all:
495  * @statusbar: a #GtkStatusbar
496  * @context_id: a context identifier
497  *
498  * Forces the removal of all messages from a statusbar's
499  * stack with the exact @context_id.
500  *
501  * Since: 2.22
502  */
503 void
504 gtk_statusbar_remove_all (GtkStatusbar *statusbar,
505                           guint         context_id)
506 {
507   GtkStatusbarPrivate *priv;
508   GtkStatusbarMsg *msg;
509   GSList *prev, *list;
510
511   g_return_if_fail (GTK_IS_STATUSBAR (statusbar));
512
513   priv = statusbar->priv;
514
515   if (priv->messages == NULL)
516     return;
517
518   msg = priv->messages->data;
519
520   /* care about signal emission if the topmost item is removed */
521   if (msg->context_id == context_id)
522     {
523       gtk_statusbar_pop (statusbar, context_id);
524
525       prev = NULL;
526       list = priv->messages;
527     }
528   else
529     {
530       prev = priv->messages;
531       list = prev->next;
532     }
533
534   while (list != NULL)
535     {
536       msg = list->data;
537
538       if (msg->context_id == context_id)
539         {
540           if (prev == NULL)
541             priv->messages = list->next;
542           else
543             prev->next = list->next;
544
545           gtk_statusbar_msg_free (msg);
546           g_slist_free_1 (list);
547
548           if (prev == NULL)
549             prev = priv->messages;
550
551           if (prev)
552             list = prev->next;
553           else
554             list = NULL;
555         }
556       else
557         {
558           prev = list;
559           list = prev->next;
560         }
561     }
562 }
563
564 /**
565  * gtk_statusbar_get_message_area:
566  * @statusbar: a #GtkStatusbar
567  *
568  * Retrieves the box containing the label widget.
569  *
570  * Returns: (transfer none): a #GtkBox
571  *
572  * Since: 2.20
573  */
574 GtkWidget*
575 gtk_statusbar_get_message_area (GtkStatusbar *statusbar)
576 {
577   GtkStatusbarPrivate *priv;
578
579   g_return_val_if_fail (GTK_IS_STATUSBAR (statusbar), NULL);
580
581   priv = statusbar->priv;
582
583   return gtk_bin_get_child (GTK_BIN (priv->frame));
584 }
585
586 static void
587 gtk_statusbar_destroy (GtkWidget *widget)
588 {
589   GtkStatusbar *statusbar = GTK_STATUSBAR (widget);
590   GtkStatusbarPrivate *priv = statusbar->priv;
591
592   g_slist_free_full (priv->messages, (GDestroyNotify) gtk_statusbar_msg_free);
593   priv->messages = NULL;
594
595   g_slist_free_full (priv->keys, g_free);
596   priv->keys = NULL;
597
598   GTK_WIDGET_CLASS (gtk_statusbar_parent_class)->destroy (widget);
599 }
600
601 /* look for extra children between the frame containing
602  * the label and where we want to draw the resize grip
603  */
604 static gboolean
605 has_extra_children (GtkStatusbar *statusbar)
606 {
607   GtkStatusbarPrivate *priv = statusbar->priv;
608   GtkPackType child_pack_type, frame_pack_type;
609   GtkWidget *child, *frame;
610   GList *l, *children;
611   gboolean retval = FALSE;
612
613   frame = NULL;
614   children = _gtk_box_get_children (GTK_BOX (statusbar));
615   for (l = children; l; l = l->next)
616     {
617       frame = l->data;
618
619       if (frame == priv->frame)
620         break;
621     }
622
623   gtk_box_query_child_packing (GTK_BOX (statusbar), frame,
624                                NULL, NULL, NULL, &frame_pack_type);
625
626   for (l = l->next; l; l = l->next)
627     {
628       child = l->data;
629
630       if (!gtk_widget_get_visible (child))
631         continue;
632
633       gtk_box_query_child_packing (GTK_BOX (statusbar), child,
634                                    NULL, NULL, NULL, &child_pack_type);
635
636       if (frame_pack_type == GTK_PACK_START || child_pack_type == GTK_PACK_END)
637         {
638           retval = TRUE;
639           break;
640         }
641     }
642
643   g_list_free (children);
644
645   return retval;
646 }
647
648 static void
649 gtk_statusbar_size_allocate (GtkWidget     *widget,
650                              GtkAllocation *allocation)
651 {
652   GtkStatusbar *statusbar = GTK_STATUSBAR (widget);
653   GtkStatusbarPrivate *priv = statusbar->priv;
654   gboolean extra_children = FALSE;
655   gboolean has_resize_grip = FALSE;
656   GdkRectangle rect;
657   GtkWidget *window;
658   gint x, y;
659   GdkRectangle translated_rect;
660
661   window = gtk_widget_get_toplevel (widget);
662
663   if (GTK_IS_WINDOW (window) &&
664       gtk_window_resize_grip_is_visible (GTK_WINDOW (window)))
665     {
666       gtk_window_get_resize_grip_area (GTK_WINDOW (window), &rect);
667       if (gtk_widget_translate_coordinates (widget, window, 0, 0, &x, &y))
668         {
669           translated_rect.x = x;
670           translated_rect.y = y;
671           translated_rect.width = allocation->width;
672           translated_rect.height = allocation->height;
673
674           if (gdk_rectangle_intersect (&rect, &translated_rect, NULL))
675             {
676               has_resize_grip = TRUE;
677               extra_children = has_extra_children (statusbar);
678
679               /* If there are extra children, we don't want them to occupy
680                * the space where we draw the resize grip, so we temporarily
681                * shrink the allocation.
682                * If there are no extra children, we want the frame to get
683                * the full allocation, and we fix up the allocation of the
684                * label afterwards to make room for the grip.
685                */
686               if (extra_children)
687                 {
688                   allocation->width -= rect.width;
689                   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
690                     allocation->x += rect.width;
691                 }
692             }
693         }
694     }
695
696   /* chain up normally */
697   GTK_WIDGET_CLASS (gtk_statusbar_parent_class)->size_allocate (widget, allocation);
698
699   if (has_resize_grip)
700     {
701       if (extra_children)
702         {
703           allocation->width += rect.width;
704           if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
705             allocation->x -= rect.width;
706
707           gtk_widget_set_allocation (widget, allocation);
708         }
709       else
710         {
711           GtkAllocation child_allocation, frame_allocation;
712           GtkWidget *child;
713
714           /* Use the frame's child instead of statusbar->label directly, in case
715            * the label has been replaced by a container as the frame's child
716            * (and the label reparented into that container).
717            */
718           child = gtk_bin_get_child (GTK_BIN (priv->frame));
719
720           gtk_widget_get_allocation (child, &child_allocation);
721           gtk_widget_get_allocation (priv->frame, &frame_allocation);
722           if (child_allocation.width + rect.width > frame_allocation.width)
723             {
724               /* shrink the label to make room for the grip */
725               *allocation = child_allocation;
726               allocation->width = MAX (1, allocation->width - rect.width);
727               if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
728                 allocation->x += child_allocation.width - allocation->width;
729
730               gtk_widget_size_allocate (child, allocation);
731             }
732         }
733     }
734 }
735
736 static void
737 resize_grip_visible_changed (GObject    *object,
738                              GParamSpec *pspec,
739                              gpointer    user_data)
740 {
741   GtkStatusbar *statusbar = GTK_STATUSBAR (user_data);
742   GtkStatusbarPrivate *priv = statusbar->priv;
743
744   gtk_widget_queue_resize (priv->label);
745   gtk_widget_queue_resize (priv->frame);
746   gtk_widget_queue_resize (GTK_WIDGET (statusbar));
747 }
748
749 static void
750 gtk_statusbar_hierarchy_changed (GtkWidget *widget,
751                                  GtkWidget *previous_toplevel)
752 {
753   GtkWidget *window;
754
755   if (previous_toplevel)
756     g_signal_handlers_disconnect_by_func (previous_toplevel,
757                                           G_CALLBACK (resize_grip_visible_changed),
758                                           widget);
759   window = gtk_widget_get_toplevel (widget);
760   if (GTK_IS_WINDOW (window))
761     g_signal_connect (window, "notify::resize-grip-visible",
762                       G_CALLBACK (resize_grip_visible_changed), widget);
763
764   resize_grip_visible_changed (NULL, NULL, widget);
765 }
766
767 static void
768 gtk_statusbar_realize (GtkWidget *widget)
769 {
770   GTK_WIDGET_CLASS (gtk_statusbar_parent_class)->realize (widget);
771
772   resize_grip_visible_changed (NULL, NULL, widget);
773 }