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