]> Pileus Git - ~andy/gtk/blob - gtk/gtkstatusbar.c
Merge branch 'master' into broadway
[~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
30 #include "gtkframe.h"
31 #include "gtklabel.h"
32 #include "gtkmarshalers.h"
33 #include "gtkstatusbar.h"
34 #include "gtkwindow.h"
35 #include "gtkprivate.h"
36 #include "gtkintl.h"
37 #include "gtkbuildable.h"
38 #include "gtktypebuiltins.h"
39
40 /**
41  * SECTION:gtkstatusbar
42  * @title: GtkStatusbar
43  * @short_description: Report messages of minor importance to the user
44  *
45  * A #GtkStatusbar is usually placed along the bottom of an application's
46  * main #GtkWindow. It may provide a regular commentary of the application's
47  * status (as is usually the case in a web browser, for example), or may be
48  * used to simply output a message when the status changes, (when an upload
49  * is complete in an FTP client, for example).
50  *
51  * Status bars in GTK+ maintain a stack of messages. The message at
52  * the top of the each bar's stack is the one that will currently be displayed.
53  *
54  * Any messages added to a statusbar's stack must specify a
55  * <emphasis>context id</emphasis> that is used to uniquely identify
56  * the source of a message. This context id can be generated by
57  * gtk_statusbar_get_context_id(), given a message and the statusbar that
58  * it will be added to. Note that messages are stored in a stack, and when
59  * choosing which message to display, the stack structure is adhered to,
60  * regardless of the context identifier of a message.
61  *
62  * One could say that a statusbar maintains one stack of messages for
63  * display purposes, but allows multiple message producers to maintain
64  * sub-stacks of the messages they produced (via context ids).
65  *
66  * Status bars are created using gtk_statusbar_new().
67  *
68  * Messages are added to the bar's stack with gtk_statusbar_push().
69  *
70  * The message at the top of the stack can be removed using
71  * gtk_statusbar_pop(). A message can be removed from anywhere in the
72  * stack if its message id was recorded at the time it was added. This
73  * is done using gtk_statusbar_remove().
74  */
75 typedef struct _GtkStatusbarMsg GtkStatusbarMsg;
76
77 struct _GtkStatusbarPrivate
78 {
79   GtkWidget     *frame;
80   GtkWidget     *label;
81
82   GSList        *messages;
83   GSList        *keys;
84
85   guint          seq_context_id;
86   guint          seq_message_id;
87 };
88
89
90 struct _GtkStatusbarMsg
91 {
92   gchar *text;
93   guint context_id;
94   guint message_id;
95 };
96
97 enum
98 {
99   SIGNAL_TEXT_PUSHED,
100   SIGNAL_TEXT_POPPED,
101   SIGNAL_LAST
102 };
103
104 static void     gtk_statusbar_buildable_interface_init    (GtkBuildableIface *iface);
105 static GObject *gtk_statusbar_buildable_get_internal_child (GtkBuildable *buildable,
106                                                             GtkBuilder   *builder,
107                                                             const gchar  *childname);
108 static void     gtk_statusbar_update            (GtkStatusbar      *statusbar,
109                                                  guint              context_id,
110                                                  const gchar       *text);
111 static void     gtk_statusbar_realize           (GtkWidget         *widget);
112 static void     gtk_statusbar_destroy           (GtkWidget         *widget);
113 static void     gtk_statusbar_size_allocate     (GtkWidget         *widget,
114                                                  GtkAllocation     *allocation);
115 static void     gtk_statusbar_hierarchy_changed (GtkWidget         *widget,
116                                                  GtkWidget         *previous_toplevel);
117
118
119 static guint              statusbar_signals[SIGNAL_LAST] = { 0 };
120
121 G_DEFINE_TYPE_WITH_CODE (GtkStatusbar, gtk_statusbar, GTK_TYPE_HBOX,
122                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
123                                                 gtk_statusbar_buildable_interface_init));
124
125 static void
126 gtk_statusbar_class_init (GtkStatusbarClass *class)
127 {
128   GtkWidgetClass *widget_class;
129
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, 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           if (prev)
533             list = prev->next;
534           else
535             list = NULL;
536         }
537       else
538         {
539           prev = list;
540           list = prev->next;
541         }
542     }
543 }
544
545 /**
546  * gtk_statusbar_get_message_area:
547  * @statusbar: a #GtkStatusBar
548  *
549  * Retrieves the box containing the label widget.
550  *
551  * Returns: (transfer none): a #GtkBox
552  *
553  * Since: 2.20
554  */
555 GtkWidget*
556 gtk_statusbar_get_message_area (GtkStatusbar *statusbar)
557 {
558   GtkStatusbarPrivate *priv;
559
560   g_return_val_if_fail (GTK_IS_STATUSBAR (statusbar), NULL);
561
562   priv = statusbar->priv;
563
564   return gtk_bin_get_child (GTK_BIN (priv->frame));
565 }
566
567 static void
568 gtk_statusbar_destroy (GtkWidget *widget)
569 {
570   GtkStatusbar *statusbar = GTK_STATUSBAR (widget);
571   GtkStatusbarPrivate *priv = statusbar->priv;
572   GSList *list;
573
574   for (list = priv->messages; list; list = list->next)
575     {
576       GtkStatusbarMsg *msg;
577
578       msg = list->data;
579       g_free (msg->text);
580       g_slice_free (GtkStatusbarMsg, msg);
581     }
582   g_slist_free (priv->messages);
583   priv->messages = NULL;
584
585   for (list = priv->keys; list; list = list->next)
586     g_free (list->data);
587   g_slist_free (priv->keys);
588   priv->keys = NULL;
589
590   GTK_WIDGET_CLASS (gtk_statusbar_parent_class)->destroy (widget);
591 }
592
593 /* look for extra children between the frame containing
594  * the label and where we want to draw the resize grip
595  */
596 static gboolean
597 has_extra_children (GtkStatusbar *statusbar)
598 {
599   GtkStatusbarPrivate *priv = statusbar->priv;
600   GtkPackType child_pack_type, frame_pack_type;
601   GtkWidget *child, *frame;
602   GList *l, *children;
603   gboolean retval = FALSE;
604
605   frame = NULL;
606   children = _gtk_box_get_children (GTK_BOX (statusbar));
607   for (l = children; l; l = l->next)
608     {
609       frame = l->data;
610
611       if (frame == priv->frame)
612         break;
613     }
614
615   gtk_box_query_child_packing (GTK_BOX (statusbar), frame,
616                                NULL, NULL, NULL, &frame_pack_type);
617
618   for (l = l->next; l; l = l->next)
619     {
620       child = l->data;
621
622       if (!gtk_widget_get_visible (child))
623         continue;
624
625       gtk_box_query_child_packing (GTK_BOX (statusbar), child,
626                                    NULL, NULL, NULL, &child_pack_type);
627
628       if (frame_pack_type == GTK_PACK_START || child_pack_type == GTK_PACK_END)
629         {
630           retval = TRUE;
631           break;
632         }
633     }
634
635   g_list_free (children);
636
637   return retval;
638 }
639
640 static void
641 gtk_statusbar_size_allocate (GtkWidget     *widget,
642                              GtkAllocation *allocation)
643 {
644   GtkStatusbar *statusbar = GTK_STATUSBAR (widget);
645   GtkStatusbarPrivate *priv = statusbar->priv;
646   gboolean extra_children = FALSE;
647   gboolean has_resize_grip = FALSE;
648   GdkRectangle rect;
649   GtkWidget *window;
650   gint x, y;
651   GdkRectangle translated_rect;
652
653   window = gtk_widget_get_toplevel (widget);
654
655   if (GTK_IS_WINDOW (window) &&
656       gtk_window_resize_grip_is_visible (GTK_WINDOW (window)))
657     {
658       gtk_window_get_resize_grip_area (GTK_WINDOW (window), &rect);
659       if (gtk_widget_translate_coordinates (gtk_widget_get_parent (widget),
660                                             window,
661                                             allocation->x,
662                                             allocation->y,
663                                             &x,
664                                             &y))
665         {
666           translated_rect.x = x;
667           translated_rect.y = y;
668           translated_rect.width = allocation->width;
669           translated_rect.height = allocation->height;
670
671           if (gdk_rectangle_intersect (&rect, &translated_rect, NULL))
672             {
673               has_resize_grip = TRUE;
674               extra_children = has_extra_children (statusbar);
675
676               /* If there are extra children, we don't want them to occupy
677                * the space where we draw the resize grip, so we temporarily
678                * shrink the allocation.
679                * If there are no extra children, we want the frame to get
680                * the full allocation, and we fix up the allocation of the
681                * label afterwards to make room for the grip.
682                */
683               if (extra_children)
684                 {
685                   allocation->width -= rect.width;
686                   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
687                     allocation->x += rect.width;
688                 }
689             }
690         }
691     }
692
693   /* chain up normally */
694   GTK_WIDGET_CLASS (gtk_statusbar_parent_class)->size_allocate (widget, allocation);
695
696   if (has_resize_grip)
697     {
698       if (extra_children)
699         {
700           allocation->width += rect.width;
701           if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
702             allocation->x -= rect.width;
703
704           gtk_widget_set_allocation (widget, allocation);
705         }
706       else
707         {
708           GtkAllocation child_allocation, frame_allocation;
709           GtkWidget *child;
710
711           /* Use the frame's child instead of statusbar->label directly, in case
712            * the label has been replaced by a container as the frame's child
713            * (and the label reparented into that container).
714            */
715           child = gtk_bin_get_child (GTK_BIN (priv->frame));
716
717           gtk_widget_get_allocation (child, &child_allocation);
718           gtk_widget_get_allocation (priv->frame, &frame_allocation);
719           if (child_allocation.width + rect.width > frame_allocation.width)
720             {
721               /* shrink the label to make room for the grip */
722               *allocation = child_allocation;
723               allocation->width = MAX (1, allocation->width - rect.width);
724               if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
725                 allocation->x += child_allocation.width - allocation->width;
726
727               gtk_widget_size_allocate (child, allocation);
728             }
729         }
730     }
731 }
732
733 static void
734 resize_grip_visible_changed (GObject    *object,
735                              GParamSpec *pspec,
736                              gpointer    user_data)
737 {
738   GtkStatusbar *statusbar = GTK_STATUSBAR (user_data);
739   GtkStatusbarPrivate *priv = statusbar->priv;
740
741   gtk_widget_queue_resize (priv->label);
742   gtk_widget_queue_resize (priv->frame);
743   gtk_widget_queue_resize (GTK_WIDGET (statusbar));
744 }
745
746 static void
747 gtk_statusbar_hierarchy_changed (GtkWidget *widget,
748                                  GtkWidget *previous_toplevel)
749 {
750   GtkWidget *window;
751
752   if (previous_toplevel)
753     g_signal_handlers_disconnect_by_func (previous_toplevel,
754                                           G_CALLBACK (resize_grip_visible_changed),
755                                           widget);
756   window = gtk_widget_get_toplevel (widget);
757   if (GTK_IS_WINDOW (window))
758     g_signal_connect (window, "notify::resize-grip-visible",
759                       G_CALLBACK (resize_grip_visible_changed), widget);
760
761   resize_grip_visible_changed (NULL, NULL, widget);
762 }
763
764 static void
765 gtk_statusbar_realize (GtkWidget *widget)
766 {
767   GTK_WIDGET_CLASS (gtk_statusbar_parent_class)->realize (widget);
768
769   resize_grip_visible_changed (NULL, NULL, widget);
770 }