gtkstatusbar.c 22.2 KB
Newer Older
Cody Russell's avatar
Cody Russell committed
1
/* GTK - The GIMP Toolkit
Shawn Amundson's avatar
Shawn Amundson committed
2 3 4 5
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 * GtkStatusbar Copyright (C) 1998 Shawn T. Amundson
 *
 * This library is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU Lesser General Public
Shawn Amundson's avatar
Shawn Amundson committed
7 8 9 10 11 12
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 * Lesser General Public License for more details.
Shawn Amundson's avatar
Shawn Amundson committed
14
 *
15
 * You should have received a copy of the GNU Lesser General Public
16 17 18
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
Shawn Amundson's avatar
Shawn Amundson committed
19 20
 */

21
/*
22
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
23 24
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * 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

Shawn Amundson's avatar
Shawn Amundson committed
30 31
#include "gtkframe.h"
#include "gtklabel.h"
32
#include "gtkmarshalers.h"
Shawn Amundson's avatar
Shawn Amundson committed
33
#include "gtkstatusbar.h"
Owen Taylor's avatar
Owen Taylor committed
34
#include "gtkwindow.h"
35
#include "gtkprivate.h"
36
#include "gtkintl.h"
37
#include "gtkbuildable.h"
38
#include "gtktypebuiltins.h"
Shawn Amundson's avatar
Shawn Amundson committed
39

40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
/**
 * SECTION:gtkstatusbar
 * @title: GtkStatusbar
 * @short_description: Report messages of minor importance to the user
 *
 * A #GtkStatusbar is usually placed along the bottom of an application's
 * main #GtkWindow. It may provide a regular commentary of the application's
 * status (as is usually the case in a web browser, for example), or may be
 * used to simply output a message when the status changes, (when an upload
 * is complete in an FTP client, for example).
 *
 * Status bars in GTK+ maintain a stack of messages. The message at
 * the top of the each bar's stack is the one that will currently be displayed.
 *
 * Any messages added to a statusbar's stack must specify a
 * <emphasis>context id</emphasis> that is used to uniquely identify
 * the source of a message. This context id can be generated by
 * gtk_statusbar_get_context_id(), given a message and the statusbar that
 * it will be added to. Note that messages are stored in a stack, and when
 * choosing which message to display, the stack structure is adhered to,
 * regardless of the context identifier of a message.
 *
 * One could say that a statusbar maintains one stack of messages for
 * display purposes, but allows multiple message producers to maintain
 * sub-stacks of the messages they produced (via context ids).
 *
 * Status bars are created using gtk_statusbar_new().
 *
 * Messages are added to the bar's stack with gtk_statusbar_push().
 *
 * The message at the top of the stack can be removed using
 * gtk_statusbar_pop(). A message can be removed from anywhere in the
 * stack if its message id was recorded at the time it was added. This
 * is done using gtk_statusbar_remove().
 */
75 76
typedef struct _GtkStatusbarMsg GtkStatusbarMsg;

77
struct _GtkStatusbarPrivate
78 79 80 81 82 83 84 85 86 87 88 89
{
  GtkWidget     *frame;
  GtkWidget     *label;

  GSList        *messages;
  GSList        *keys;

  guint          seq_context_id;
  guint          seq_message_id;
};


90 91 92 93 94 95
struct _GtkStatusbarMsg
{
  gchar *text;
  guint context_id;
  guint message_id;
};
96 97 98 99 100 101 102 103

enum
{
  SIGNAL_TEXT_PUSHED,
  SIGNAL_TEXT_POPPED,
  SIGNAL_LAST
};

104 105 106 107
static void     gtk_statusbar_buildable_interface_init    (GtkBuildableIface *iface);
static GObject *gtk_statusbar_buildable_get_internal_child (GtkBuildable *buildable,
                                                            GtkBuilder   *builder,
                                                            const gchar  *childname);
108 109 110
static void     gtk_statusbar_update            (GtkStatusbar      *statusbar,
						 guint              context_id,
						 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 115 116
                                                 GtkAllocation     *allocation);
static void     gtk_statusbar_hierarchy_changed (GtkWidget         *widget,
                                                 GtkWidget         *previous_toplevel);
117

Owen Taylor's avatar
Owen Taylor committed
118

119
static guint              statusbar_signals[SIGNAL_LAST] = { 0 };
Shawn Amundson's avatar
Shawn Amundson committed
120

121 122 123
G_DEFINE_TYPE_WITH_CODE (GtkStatusbar, gtk_statusbar, GTK_TYPE_HBOX,
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
                                                gtk_statusbar_buildable_interface_init));
Shawn Amundson's avatar
Shawn Amundson committed
124 125 126 127 128 129 130 131

static void
gtk_statusbar_class_init (GtkStatusbarClass *class)
{
  GtkWidgetClass *widget_class;

  widget_class = (GtkWidgetClass *) class;

132
  widget_class->realize = gtk_statusbar_realize;
133
  widget_class->destroy = gtk_statusbar_destroy;
134
  widget_class->size_allocate = gtk_statusbar_size_allocate;
135 136
  widget_class->hierarchy_changed = gtk_statusbar_hierarchy_changed;

137 138
  class->text_pushed = gtk_statusbar_update;
  class->text_popped = gtk_statusbar_update;
139

Matthias Clasen's avatar
Matthias Clasen committed
140
  /**
141
   * GtkStatusbar::text-pushed:
Matthias Clasen's avatar
Matthias Clasen committed
142 143 144 145
   * @statusbar: the object which received the signal
   * @context_id: the context id of the relevant message/statusbar
   * @text: the message that was pushed
   *
146 147
   * Is emitted whenever a new message gets pushed onto a statusbar's stack.
   */
148
  statusbar_signals[SIGNAL_TEXT_PUSHED] =
149
    g_signal_new (I_("text-pushed"),
Manish Singh's avatar
Manish Singh committed
150 151 152 153 154 155 156 157
		  G_OBJECT_CLASS_TYPE (class),
		  G_SIGNAL_RUN_LAST,
		  G_STRUCT_OFFSET (GtkStatusbarClass, text_pushed),
		  NULL, NULL,
		  _gtk_marshal_VOID__UINT_STRING,
		  G_TYPE_NONE, 2,
		  G_TYPE_UINT,
		  G_TYPE_STRING);
158 159 160

  /**
   * GtkStatusbar::text-popped:
Matthias Clasen's avatar
Matthias Clasen committed
161 162 163
   * @statusbar: the object which received the signal
   * @context_id: the context id of the relevant message/statusbar
   * @text: the message that was just popped
164 165 166
   *
   * Is emitted whenever a new message is popped off a statusbar's stack.
   */
167
  statusbar_signals[SIGNAL_TEXT_POPPED] =
168
    g_signal_new (I_("text-popped"),
Manish Singh's avatar
Manish Singh committed
169 170 171 172 173 174 175 176
		  G_OBJECT_CLASS_TYPE (class),
		  G_SIGNAL_RUN_LAST,
		  G_STRUCT_OFFSET (GtkStatusbarClass, text_popped),
		  NULL, NULL,
		  _gtk_marshal_VOID__UINT_STRING,
		  G_TYPE_NONE, 2,
		  G_TYPE_UINT,
		  G_TYPE_STRING);
177 178

  gtk_widget_class_install_style_property (widget_class,
179
                                           g_param_spec_enum ("shadow-type",
180 181
                                                              P_("Shadow type"),
                                                              P_("Style of bevel around the statusbar text"),
182 183
                                                              GTK_TYPE_SHADOW_TYPE,
                                                              GTK_SHADOW_IN,
184
                                                              GTK_PARAM_READABLE));
185

186
   g_type_class_add_private (class, sizeof (GtkStatusbarPrivate));
Shawn Amundson's avatar
Shawn Amundson committed
187 188 189 190 191
}

static void
gtk_statusbar_init (GtkStatusbar *statusbar)
{
192
  GtkStatusbarPrivate *priv;
193
  GtkBox *box = GTK_BOX (statusbar);
194
  GtkWidget *message_area;
195
  GtkShadowType shadow_type;
196 197 198

  statusbar->priv = G_TYPE_INSTANCE_GET_PRIVATE (statusbar,
                                                 GTK_TYPE_STATUSBAR,
199
                                                 GtkStatusbarPrivate);
200
  priv = statusbar->priv;
201

202 203
  gtk_widget_set_redraw_on_allocate (GTK_WIDGET (box), TRUE);

204 205
  gtk_box_set_spacing (box, 2);
  gtk_box_set_homogeneous (box, FALSE);
Shawn Amundson's avatar
Shawn Amundson committed
206

207
  gtk_widget_style_get (GTK_WIDGET (statusbar), "shadow-type", &shadow_type, NULL);
208 209 210 211 212

  priv->frame = gtk_frame_new (NULL);
  gtk_frame_set_shadow_type (GTK_FRAME (priv->frame), shadow_type);
  gtk_box_pack_start (box, priv->frame, TRUE, TRUE, 0);
  gtk_widget_show (priv->frame);
Shawn Amundson's avatar
Shawn Amundson committed
213

214
  message_area = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
215
  gtk_container_add (GTK_CONTAINER (priv->frame), message_area);
216 217
  gtk_widget_show (message_area);

218 219 220 221 222 223 224 225 226 227 228
  priv->label = gtk_label_new ("");
  gtk_label_set_single_line_mode (GTK_LABEL (priv->label), TRUE);
  gtk_misc_set_alignment (GTK_MISC (priv->label), 0.0, 0.5);
  gtk_label_set_ellipsize (GTK_LABEL (priv->label), PANGO_ELLIPSIZE_END);
  gtk_container_add (GTK_CONTAINER (message_area), priv->label);
  gtk_widget_show (priv->label);

  priv->seq_context_id = 1;
  priv->seq_message_id = 1;
  priv->messages = NULL;
  priv->keys = NULL;
Shawn Amundson's avatar
Shawn Amundson committed
229 230
}

231 232 233 234 235 236 237 238 239 240 241 242 243 244
static GtkBuildableIface *parent_buildable_iface;

static void
gtk_statusbar_buildable_interface_init (GtkBuildableIface *iface)
{
  parent_buildable_iface = g_type_interface_peek_parent (iface);
  iface->get_internal_child = gtk_statusbar_buildable_get_internal_child;
}

static GObject *
gtk_statusbar_buildable_get_internal_child (GtkBuildable *buildable,
                                            GtkBuilder   *builder,
                                            const gchar  *childname)
{
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 252 253 254 255

    return parent_buildable_iface->get_internal_child (buildable,
                                                       builder,
                                                       childname);
}

256 257 258 259 260 261 262
/**
 * gtk_statusbar_new:
 *
 * Creates a new #GtkStatusbar ready for messages.
 *
 * Returns: the new #GtkStatusbar
 */
Shawn Amundson's avatar
Shawn Amundson committed
263
GtkWidget* 
264
gtk_statusbar_new (void)
Shawn Amundson's avatar
Shawn Amundson committed
265
{
Manish Singh's avatar
Manish Singh committed
266
  return g_object_new (GTK_TYPE_STATUSBAR, NULL);
267 268 269 270
}

static void
gtk_statusbar_update (GtkStatusbar *statusbar,
271
		      guint	    context_id,
272 273
		      const gchar  *text)
{
274
  GtkStatusbarPrivate *priv;
275

276
  g_return_if_fail (GTK_IS_STATUSBAR (statusbar));
Shawn Amundson's avatar
Shawn Amundson committed
277

278 279
  priv = statusbar->priv;

280 281
  if (!text)
    text = "";
Shawn Amundson's avatar
Shawn Amundson committed
282

283
  gtk_label_set_text (GTK_LABEL (priv->label), text);
Shawn Amundson's avatar
Shawn Amundson committed
284 285
}

286 287 288 289 290 291 292 293 294 295 296 297
/**
 * gtk_statusbar_get_context_id:
 * @statusbar: a #GtkStatusbar
 * @context_description: textual description of what context 
 *                       the new message is being used in
 *
 * Returns a new context identifier, given a description 
 * of the actual context. Note that the description is 
 * <emphasis>not</emphasis> shown in the UI.
 *
 * Returns: an integer id
 */
298 299 300 301
guint
gtk_statusbar_get_context_id (GtkStatusbar *statusbar,
			      const gchar  *context_description)
{
302
  GtkStatusbarPrivate *priv;
303
  gchar *string;
Yevgen Muntyan's avatar
Yevgen Muntyan committed
304
  guint id;
305 306 307 308
  
  g_return_val_if_fail (GTK_IS_STATUSBAR (statusbar), 0);
  g_return_val_if_fail (context_description != NULL, 0);

309 310
  priv = statusbar->priv;

311 312 313
  /* we need to preserve namespaces on object datas */
  string = g_strconcat ("gtk-status-bar-context:", context_description, NULL);

314 315
  id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (statusbar), string));
  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 322 323
    }
  else
    g_free (string);

324
  return id;
325 326
}

327 328 329 330 331 332 333 334 335 336 337 338
/**
 * gtk_statusbar_push:
 * @statusbar: a #GtkStatusbar
 * @context_id: the message's context id, as returned by
 *              gtk_statusbar_get_context_id()
 * @text: the message to add to the statusbar
 * 
 * Pushes a new message onto a statusbar's stack.
 *
 * Returns: a message id that can be used with 
 *          gtk_statusbar_remove().
 */
339 340
guint
gtk_statusbar_push (GtkStatusbar *statusbar,
341
		    guint	  context_id,
342
		    const gchar  *text)
Shawn Amundson's avatar
Shawn Amundson committed
343
{
344
  GtkStatusbarPrivate *priv;
Shawn Amundson's avatar
Shawn Amundson committed
345
  GtkStatusbarMsg *msg;
346 347 348

  g_return_val_if_fail (GTK_IS_STATUSBAR (statusbar), 0);
  g_return_val_if_fail (text != NULL, 0);
Shawn Amundson's avatar
Shawn Amundson committed
349

350 351
  priv = statusbar->priv;

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++;
Shawn Amundson's avatar
Shawn Amundson committed
356

357
  priv->messages = g_slist_prepend (priv->messages, msg);
Shawn Amundson's avatar
Shawn Amundson committed
358

Manish Singh's avatar
Manish Singh committed
359 360 361 362 363
  g_signal_emit (statusbar,
		 statusbar_signals[SIGNAL_TEXT_PUSHED],
		 0,
		 msg->context_id,
		 msg->text);
Shawn Amundson's avatar
Shawn Amundson committed
364

365
  return msg->message_id;
Shawn Amundson's avatar
Shawn Amundson committed
366 367
}

368 369 370 371 372 373 374 375 376 377 378 379
/**
 * gtk_statusbar_pop:
 * @statusbar: a #GtkStatusBar
 * @context_id: a context identifier
 * 
 * Removes the first message in the #GtkStatusBar's stack
 * with the given context id. 
 *
 * Note that this may not change the displayed message, if 
 * the message at the top of the stack has a different 
 * context id.
 */
380
void
381 382
gtk_statusbar_pop (GtkStatusbar *statusbar,
		   guint	 context_id)
Shawn Amundson's avatar
Shawn Amundson committed
383
{
384
  GtkStatusbarPrivate *priv;
385
  GtkStatusbarMsg *msg;
Shawn Amundson's avatar
Shawn Amundson committed
386

387
  g_return_if_fail (GTK_IS_STATUSBAR (statusbar));
Shawn Amundson's avatar
Shawn Amundson committed
388

389 390 391
  priv = statusbar->priv;

  if (priv->messages)
392
    {
393
      GSList *list;
Owen Taylor's avatar
Owen Taylor committed
394

395
      for (list = priv->messages; list; list = list->next)
Owen Taylor's avatar
Owen Taylor committed
396 397 398 399 400
	{
	  msg = list->data;

	  if (msg->context_id == context_id)
	    {
401
	      priv->messages = g_slist_remove_link (priv->messages,
Owen Taylor's avatar
Owen Taylor committed
402 403
							 list);
	      g_free (msg->text);
404
              g_slice_free (GtkStatusbarMsg, msg);
Owen Taylor's avatar
Owen Taylor committed
405 406 407 408
	      g_slist_free_1 (list);
	      break;
	    }
	}
409 410
    }

411
  msg = priv->messages ? priv->messages->data : NULL;
Shawn Amundson's avatar
Shawn Amundson committed
412

Manish Singh's avatar
Manish Singh committed
413 414 415 416 417
  g_signal_emit (statusbar,
		 statusbar_signals[SIGNAL_TEXT_POPPED],
		 0,
		 (guint) (msg ? msg->context_id : 0),
		 msg ? msg->text : NULL);
Shawn Amundson's avatar
Shawn Amundson committed
418 419
}

420 421 422 423 424 425 426 427 428
/**
 * gtk_statusbar_remove:
 * @statusbar: a #GtkStatusBar
 * @context_id: a context identifier
 * @message_id: a message identifier, as returned by gtk_statusbar_push()
 *
 * Forces the removal of a message from a statusbar's stack. 
 * The exact @context_id and @message_id must be specified.
 */
Shawn Amundson's avatar
Shawn Amundson committed
429
void
430 431 432
gtk_statusbar_remove (GtkStatusbar *statusbar,
		      guint	   context_id,
		      guint        message_id)
Shawn Amundson's avatar
Shawn Amundson committed
433
{
434
  GtkStatusbarPrivate *priv;
435
  GtkStatusbarMsg *msg;
Shawn Amundson's avatar
Shawn Amundson committed
436

437
  g_return_if_fail (GTK_IS_STATUSBAR (statusbar));
438
  g_return_if_fail (message_id > 0);
Shawn Amundson's avatar
Shawn Amundson committed
439

440 441 442
  priv = statusbar->priv;

  msg = priv->messages ? priv->messages->data : NULL;
443 444
  if (msg)
    {
445 446 447 448 449
      GSList *list;

      /* care about signal emission if the topmost item is removed */
      if (msg->context_id == context_id &&
	  msg->message_id == message_id)
450
	{
451
	  gtk_statusbar_pop (statusbar, context_id);
452 453 454
	  return;
	}
      
455
      for (list = priv->messages; list; list = list->next)
456 457 458
	{
	  msg = list->data;
	  
459 460
	  if (msg->context_id == context_id &&
	      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 468 469
	      
	      break;
	    }
	}
Shawn Amundson's avatar
Shawn Amundson committed
470 471 472
    }
}

473 474 475 476 477 478 479 480 481 482 483 484 485 486
/**
 * gtk_statusbar_remove_all:
 * @statusbar: a #GtkStatusBar
 * @context_id: a context identifier
 *
 * Forces the removal of all messages from a statusbar's
 * stack with the exact @context_id.
 *
 * Since: 2.22
 */
void
gtk_statusbar_remove_all (GtkStatusbar *statusbar,
                          guint         context_id)
{
487
  GtkStatusbarPrivate *priv;
488 489 490 491 492
  GtkStatusbarMsg *msg;
  GSList *prev, *list;

  g_return_if_fail (GTK_IS_STATUSBAR (statusbar));

493 494 495
  priv = statusbar->priv;

  if (priv->messages == NULL)
496 497
    return;

498
  msg = priv->messages->data;
499 500 501 502 503 504 505

  /* care about signal emission if the topmost item is removed */
  if (msg->context_id == context_id)
    {
      gtk_statusbar_pop (statusbar, context_id);

      prev = NULL;
506
      list = priv->messages;
507 508 509
    }
  else
    {
510
      prev = priv->messages;
511 512 513 514 515 516 517 518 519 520
      list = prev->next;
    }

  while (list != NULL)
    {
      msg = list->data;

      if (msg->context_id == context_id)
        {
          if (prev == NULL)
521
            priv->messages = list->next;
522 523 524 525 526 527 528 529
          else
            prev->next = list->next;

          g_free (msg->text);
          g_slice_free (GtkStatusbarMsg, msg);
          g_slist_free_1 (list);

          if (prev == NULL)
530
            prev = priv->messages;
531

532 533 534 535
          if (prev)
            list = prev->next;
          else
            list = NULL;
536 537 538 539 540 541 542 543 544
        }
      else
        {
          prev = list;
          list = prev->next;
        }
    }
}

545 546 547 548 549 550
/**
 * gtk_statusbar_get_message_area:
 * @statusbar: a #GtkStatusBar
 *
 * Retrieves the box containing the label widget.
 *
Matthias Clasen's avatar
Matthias Clasen committed
551
 * Returns: (transfer none): a #GtkBox
552 553 554 555 556 557
 *
 * Since: 2.20
 */
GtkWidget*
gtk_statusbar_get_message_area (GtkStatusbar *statusbar)
{
558
  GtkStatusbarPrivate *priv;
559

560 561
  g_return_val_if_fail (GTK_IS_STATUSBAR (statusbar), NULL);

562 563 564
  priv = statusbar->priv;

  return gtk_bin_get_child (GTK_BIN (priv->frame));
565 566
}

Shawn Amundson's avatar
Shawn Amundson committed
567
static void
568
gtk_statusbar_destroy (GtkWidget *widget)
Shawn Amundson's avatar
Shawn Amundson committed
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 577 578 579
    {
      GtkStatusbarMsg *msg;

      msg = list->data;
      g_free (msg->text);
580
      g_slice_free (GtkStatusbarMsg, msg);
581
    }
582 583
  g_slist_free (priv->messages);
  priv->messages = NULL;
Shawn Amundson's avatar
Shawn Amundson committed
584

585
  for (list = priv->keys; list; list = list->next)
586
    g_free (list->data);
587 588
  g_slist_free (priv->keys);
  priv->keys = NULL;
589

590
  GTK_WIDGET_CLASS (gtk_statusbar_parent_class)->destroy (widget);
591
}
Owen Taylor's avatar
Owen Taylor committed
592

593
/* look for extra children between the frame containing
594
 * the label and where we want to draw the resize grip
595 596 597 598
 */
static gboolean
has_extra_children (GtkStatusbar *statusbar)
{
599
  GtkStatusbarPrivate *priv = statusbar->priv;
600 601
  GtkPackType child_pack_type, frame_pack_type;
  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 610
    {
      frame = l->data;

611
      if (frame == priv->frame)
612
        break;
613
    }
614 615 616 617

  gtk_box_query_child_packing (GTK_BOX (statusbar), frame,
                               NULL, NULL, NULL, &frame_pack_type);

618 619 620 621
  for (l = l->next; l; l = l->next)
    {
      child = l->data;

622
      if (!gtk_widget_get_visible (child))
623
        continue;
624

625
      gtk_box_query_child_packing (GTK_BOX (statusbar), child,
626 627 628
                                   NULL, NULL, NULL, &child_pack_type);

      if (frame_pack_type == GTK_PACK_START || child_pack_type == GTK_PACK_END)
629
        {
630 631 632
          retval = TRUE;
          break;
        }
633 634
    }

635 636
  g_list_free (children);

637
  return retval;
638 639
}

640
static void
641 642
gtk_statusbar_size_allocate (GtkWidget     *widget,
                             GtkAllocation *allocation)
643
{
644
  GtkStatusbar *statusbar = GTK_STATUSBAR (widget);
645
  GtkStatusbarPrivate *priv = statusbar->priv;
Matthias Clasen's avatar
2.5.5  
Matthias Clasen committed
646
  gboolean extra_children = FALSE;
647
  gboolean has_resize_grip = FALSE;
Matthias Clasen's avatar
2.5.5  
Matthias Clasen committed
648
  GdkRectangle rect;
649 650 651
  GtkWidget *window;
  gint x, y;
  GdkRectangle translated_rect;
652

653
  window = gtk_widget_get_toplevel (widget);
654

655 656
  if (GTK_IS_WINDOW (window) &&
      gtk_window_resize_grip_is_visible (GTK_WINDOW (window)))
657
    {
658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690
      gtk_window_get_resize_grip_area (GTK_WINDOW (window), &rect);
      if (gtk_widget_translate_coordinates (gtk_widget_get_parent (widget),
                                            window,
                                            allocation->x,
                                            allocation->y,
                                            &x,
                                            &y))
        {
          translated_rect.x = x;
          translated_rect.y = y;
          translated_rect.width = allocation->width;
          translated_rect.height = allocation->height;

          if (gdk_rectangle_intersect (&rect, &translated_rect, NULL))
            {
              has_resize_grip = TRUE;
              extra_children = has_extra_children (statusbar);

              /* If there are extra children, we don't want them to occupy
               * the space where we draw the resize grip, so we temporarily
               * shrink the allocation.
               * If there are no extra children, we want the frame to get
               * the full allocation, and we fix up the allocation of the
               * label afterwards to make room for the grip.
               */
              if (extra_children)
                {
                  allocation->width -= rect.width;
                  if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
                    allocation->x += rect.width;
                }
            }
        }
691
    }
692

693
  /* chain up normally */
Matthias Clasen's avatar
Matthias Clasen committed
694
  GTK_WIDGET_CLASS (gtk_statusbar_parent_class)->size_allocate (widget, allocation);
695

696
  if (has_resize_grip)
697
    {
698 699 700 701 702 703 704 705
      if (extra_children)
        {
          allocation->width += rect.width;
          if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
            allocation->x -= rect.width;

          gtk_widget_set_allocation (widget, allocation);
        }
706
      else
707
        {
708
          GtkAllocation child_allocation, frame_allocation;
709
          GtkWidget *child;
710

711 712 713 714 715
          /* Use the frame's child instead of statusbar->label directly, in case
           * the label has been replaced by a container as the frame's child
           * (and the label reparented into that container).
           */
          child = gtk_bin_get_child (GTK_BIN (priv->frame));
716

717 718
          gtk_widget_get_allocation (child, &child_allocation);
          gtk_widget_get_allocation (priv->frame, &frame_allocation);
719 720 721 722 723 724 725 726 727 728 729
          if (child_allocation.width + rect.width > frame_allocation.width)
            {
              /* shrink the label to make room for the grip */
              *allocation = child_allocation;
              allocation->width = MAX (1, allocation->width - rect.width);
              if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
                allocation->x += child_allocation.width - allocation->width;

              gtk_widget_size_allocate (child, allocation);
            }
        }
730 731
    }
}
732

733
static void
734 735 736
resize_grip_visible_changed (GObject    *object,
                             GParamSpec *pspec,
                             gpointer    user_data)
737
{
738
  GtkStatusbar *statusbar = GTK_STATUSBAR (user_data);
739
  GtkStatusbarPrivate *priv = statusbar->priv;
740

741 742
  gtk_widget_queue_resize (priv->label);
  gtk_widget_queue_resize (priv->frame);
743
  gtk_widget_queue_resize (GTK_WIDGET (statusbar));
744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759
}

static void
gtk_statusbar_hierarchy_changed (GtkWidget *widget,
                                 GtkWidget *previous_toplevel)
{
  GtkWidget *window;

  if (previous_toplevel)
    g_signal_handlers_disconnect_by_func (previous_toplevel,
                                          G_CALLBACK (resize_grip_visible_changed),
                                          widget);
  window = gtk_widget_get_toplevel (widget);
  if (GTK_IS_WINDOW (window))
    g_signal_connect (window, "notify::resize-grip-visible",
                      G_CALLBACK (resize_grip_visible_changed), widget);
760 761 762 763 764 765 766 767 768 769

  resize_grip_visible_changed (NULL, NULL, widget);
}

static void
gtk_statusbar_realize (GtkWidget *widget)
{
  GTK_WIDGET_CLASS (gtk_statusbar_parent_class)->realize (widget);

  resize_grip_visible_changed (NULL, NULL, widget);
770
}