gtkframe.c 29.9 KB
Newer Older
Cody Russell's avatar
Cody Russell committed
1
/* GTK - The GIMP Toolkit
Elliot Lee's avatar
Elliot Lee committed
2 3 4
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
Elliot Lee's avatar
Elliot Lee committed
6 7 8 9 10 11
 * 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
12
 * Lesser General Public License for more details.
Elliot Lee's avatar
Elliot Lee committed
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
Javier Jardón's avatar
Javier Jardón committed
15
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
Elliot Lee's avatar
Elliot Lee committed
16
 */
17 18

/*
19
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
20 21 22 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
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
 */

25
#include "config.h"
26
#include <string.h>
Elliot Lee's avatar
Elliot Lee committed
27
#include "gtkframe.h"
28
#include "gtklabel.h"
29
#include "gtkprivate.h"
30
#include "gtktypebuiltins.h"
31
#include "gtkintl.h"
32
#include "gtkbuildable.h"
33
#include "gtkwidgetpath.h"
34

35
#include "a11y/gtkframeaccessible.h"
Elliot Lee's avatar
Elliot Lee committed
36

37 38 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
/**
 * SECTION:gtkframe
 * @Short_description: A bin with a decorative frame and optional label
 * @Title: GtkFrame
 *
 * The frame widget is a Bin that surrounds its child
 * with a decorative frame and an optional label.
 * If present, the label is drawn in a gap in the
 * top side of the frame. The position of the
 * label can be controlled with gtk_frame_set_label_align().
 *
 * <refsect2 id="GtkFrame-BUILDER-UI">
 * <title>GtkFrame as GtkBuildable</title>
 * <para>
 * The GtkFrame implementation of the GtkBuildable interface
 * supports placing a child in the label position by specifying
 * "label" as the "type" attribute of a &lt;child&gt; element.
 * A normal content child can be specified without specifying
 * a &lt;child&gt; type attribute.
 * </para>
 * <example>
 * <title>A UI definition fragment with GtkFrame</title>
 * <programlisting><![CDATA[
 * <object class="GtkFrame">
 *   <child type="label">
 *     <object class="GtkLabel" id="frame-label"/>
 *   </child>
 *   <child>
 *     <object class="GtkEntry" id="frame-content"/>
 *   </child>
 * </object>
 * ]]></programlisting>
 * </example>
 * </refsect2>
 */


74 75 76
#define LABEL_PAD 1
#define LABEL_SIDE_PAD 2

77
struct _GtkFramePrivate
78 79 80 81 82 83 84 85 86 87
{
  /* Properties */
  GtkWidget *label_widget;

  gint16 shadow_type;
  gfloat label_xalign;
  gfloat label_yalign;
  /* Properties */

  GtkAllocation child_allocation;
88
  GtkAllocation label_allocation;
89 90
};

91
enum {
92 93 94 95
  PROP_0,
  PROP_LABEL,
  PROP_LABEL_XALIGN,
  PROP_LABEL_YALIGN,
96
  PROP_SHADOW_TYPE,
97
  PROP_LABEL_WIDGET
98 99
};

100 101 102 103 104 105 106 107
static void gtk_frame_set_property (GObject      *object,
				    guint         param_id,
				    const GValue *value,
				    GParamSpec   *pspec);
static void gtk_frame_get_property (GObject     *object,
				    guint        param_id,
				    GValue      *value,
				    GParamSpec  *pspec);
108 109
static gboolean gtk_frame_draw      (GtkWidget      *widget,
				     cairo_t        *cr);
Elliot Lee's avatar
Elliot Lee committed
110 111
static void gtk_frame_size_allocate (GtkWidget      *widget,
				     GtkAllocation  *allocation);
112 113 114 115 116 117
static void gtk_frame_remove        (GtkContainer   *container,
				     GtkWidget      *child);
static void gtk_frame_forall        (GtkContainer   *container,
				     gboolean	     include_internals,
			             GtkCallback     callback,
			             gpointer        callback_data);
118 119
static GtkWidgetPath * gtk_frame_get_path_for_child (GtkContainer *container,
                                                     GtkWidget    *child);
120 121 122 123 124

static void gtk_frame_compute_child_allocation      (GtkFrame      *frame,
						     GtkAllocation *child_allocation);
static void gtk_frame_real_compute_child_allocation (GtkFrame      *frame,
						     GtkAllocation *child_allocation);
Elliot Lee's avatar
Elliot Lee committed
125

126 127
/* GtkBuildable */
static void gtk_frame_buildable_init                (GtkBuildableIface *iface);
128
static void gtk_frame_buildable_add_child           (GtkBuildable *buildable,
129 130 131 132
						     GtkBuilder   *builder,
						     GObject      *child,
						     const gchar  *type);

133
static void gtk_frame_get_preferred_width           (GtkWidget           *widget,
134 135
                                                     gint                *minimum_size,
						     gint                *natural_size);
136
static void gtk_frame_get_preferred_height          (GtkWidget           *widget,
137 138
						     gint                *minimum_size,
						     gint                *natural_size);
139
static void gtk_frame_get_preferred_height_for_width(GtkWidget           *layout,
140 141 142
						     gint                 width,
						     gint                *minimum_height,
						     gint                *natural_height);
143
static void gtk_frame_get_preferred_width_for_height(GtkWidget           *layout,
144 145 146 147
						     gint                 width,
						     gint                *minimum_height,
						     gint                *natural_height);

148

149 150
G_DEFINE_TYPE_WITH_CODE (GtkFrame, gtk_frame, GTK_TYPE_BIN,
			 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
151
						gtk_frame_buildable_init))
Elliot Lee's avatar
Elliot Lee committed
152 153 154 155

static void
gtk_frame_class_init (GtkFrameClass *class)
{
156
  GObjectClass *gobject_class;
Elliot Lee's avatar
Elliot Lee committed
157
  GtkWidgetClass *widget_class;
158
  GtkContainerClass *container_class;
Elliot Lee's avatar
Elliot Lee committed
159

160
  gobject_class = (GObjectClass*) class;
161 162
  widget_class = GTK_WIDGET_CLASS (class);
  container_class = GTK_CONTAINER_CLASS (class);
Elliot Lee's avatar
Elliot Lee committed
163

164 165 166 167 168 169
  gobject_class->set_property = gtk_frame_set_property;
  gobject_class->get_property = gtk_frame_get_property;

  g_object_class_install_property (gobject_class,
                                   PROP_LABEL,
                                   g_param_spec_string ("label",
170 171
                                                        P_("Label"),
                                                        P_("Text of the frame's label"),
172
                                                        NULL,
173 174
                                                        GTK_PARAM_READABLE |
							GTK_PARAM_WRITABLE));
175 176
  g_object_class_install_property (gobject_class,
				   PROP_LABEL_XALIGN,
177
				   g_param_spec_float ("label-xalign",
178 179
						       P_("Label xalign"),
						       P_("The horizontal alignment of the label"),
180 181
						       0.0,
						       1.0,
182
						       0.0,
183
						       GTK_PARAM_READWRITE));
184 185
  g_object_class_install_property (gobject_class,
				   PROP_LABEL_YALIGN,
186
				   g_param_spec_float ("label-yalign",
187 188
						       P_("Label yalign"),
						       P_("The vertical alignment of the label"),
189 190 191
						       0.0,
						       1.0,
						       0.5,
192
						       GTK_PARAM_READWRITE));
193 194
  g_object_class_install_property (gobject_class,
                                   PROP_SHADOW_TYPE,
195
                                   g_param_spec_enum ("shadow-type",
196 197
                                                      P_("Frame shadow"),
                                                      P_("Appearance of the frame border"),
198 199
						      GTK_TYPE_SHADOW_TYPE,
						      GTK_SHADOW_ETCHED_IN,
200
                                                      GTK_PARAM_READWRITE));
201 202 203

  g_object_class_install_property (gobject_class,
                                   PROP_LABEL_WIDGET,
204
                                   g_param_spec_object ("label-widget",
205 206
                                                        P_("Label widget"),
                                                        P_("A widget to display in place of the usual frame label"),
207
                                                        GTK_TYPE_WIDGET,
208
                                                        GTK_PARAM_READWRITE));
209

210 211 212 213 214 215
  widget_class->draw                           = gtk_frame_draw;
  widget_class->size_allocate                  = gtk_frame_size_allocate;
  widget_class->get_preferred_width            = gtk_frame_get_preferred_width;
  widget_class->get_preferred_height           = gtk_frame_get_preferred_height;
  widget_class->get_preferred_height_for_width = gtk_frame_get_preferred_height_for_width;
  widget_class->get_preferred_width_for_height = gtk_frame_get_preferred_width_for_height;
216 217 218

  container_class->remove = gtk_frame_remove;
  container_class->forall = gtk_frame_forall;
219
  container_class->get_path_for_child = gtk_frame_get_path_for_child;
220 221

  class->compute_child_allocation = gtk_frame_real_compute_child_allocation;
222

223
  g_type_class_add_private (class, sizeof (GtkFramePrivate));
224 225

  gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_FRAME_ACCESSIBLE);
Elliot Lee's avatar
Elliot Lee committed
226 227
}

228 229 230
static void
gtk_frame_buildable_init (GtkBuildableIface *iface)
{
231
  iface->add_child = gtk_frame_buildable_add_child;
232 233 234
}

static void
235 236 237 238
gtk_frame_buildable_add_child (GtkBuildable *buildable,
			       GtkBuilder   *builder,
			       GObject      *child,
			       const gchar  *type)
239 240 241 242 243 244 245 246 247
{
  if (type && strcmp (type, "label") == 0)
    gtk_frame_set_label_widget (GTK_FRAME (buildable), GTK_WIDGET (child));
  else if (!type)
    gtk_container_add (GTK_CONTAINER (buildable), GTK_WIDGET (child));
  else
    GTK_BUILDER_WARN_INVALID_CHILD_TYPE (GTK_FRAME (buildable), type);
}

Elliot Lee's avatar
Elliot Lee committed
248 249 250
static void
gtk_frame_init (GtkFrame *frame)
{
251
  GtkFramePrivate *priv;
252
  GtkStyleContext *context;
253 254 255

  frame->priv = G_TYPE_INSTANCE_GET_PRIVATE (frame,
                                             GTK_TYPE_FRAME,
256
                                             GtkFramePrivate);
257 258 259 260 261 262
  priv = frame->priv;

  priv->label_widget = NULL;
  priv->shadow_type = GTK_SHADOW_ETCHED_IN;
  priv->label_xalign = 0.0;
  priv->label_yalign = 0.5;
263 264 265

  context = gtk_widget_get_style_context (GTK_WIDGET (frame));
  gtk_style_context_add_class (context, GTK_STYLE_CLASS_FRAME);
Elliot Lee's avatar
Elliot Lee committed
266 267
}

268 269 270 271 272
static void 
gtk_frame_set_property (GObject         *object,
			guint            prop_id,
			const GValue    *value,
			GParamSpec      *pspec)
273
{
274
  GtkFrame *frame = GTK_FRAME (object);
275
  GtkFramePrivate *priv = frame->priv;
276

277
  switch (prop_id)
278
    {
279 280
    case PROP_LABEL:
      gtk_frame_set_label (frame, g_value_get_string (value));
281
      break;
282
    case PROP_LABEL_XALIGN:
283
      gtk_frame_set_label_align (frame, g_value_get_float (value), 
284
				 priv->label_yalign);
285
      break;
286
    case PROP_LABEL_YALIGN:
287
      gtk_frame_set_label_align (frame, priv->label_xalign,
288
				 g_value_get_float (value));
289
      break;
290
    case PROP_SHADOW_TYPE:
291
      gtk_frame_set_shadow_type (frame, g_value_get_enum (value));
292
      break;
293 294 295 296 297
    case PROP_LABEL_WIDGET:
      gtk_frame_set_label_widget (frame, g_value_get_object (value));
      break;
    default:      
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Tim Janik's avatar
Tim Janik committed
298
      break;
299 300 301
    }
}

302 303 304 305 306
static void 
gtk_frame_get_property (GObject         *object,
			guint            prop_id,
			GValue          *value,
			GParamSpec      *pspec)
307
{
308
  GtkFrame *frame = GTK_FRAME (object);
309
  GtkFramePrivate *priv = frame->priv;
310

311
  switch (prop_id)
312
    {
313 314
    case PROP_LABEL:
      g_value_set_string (value, gtk_frame_get_label (frame));
315
      break;
316
    case PROP_LABEL_XALIGN:
317
      g_value_set_float (value, priv->label_xalign);
318
      break;
319
    case PROP_LABEL_YALIGN:
320
      g_value_set_float (value, priv->label_yalign);
321
      break;
322
    case PROP_SHADOW_TYPE:
323
      g_value_set_enum (value, priv->shadow_type);
324 325 326
      break;
    case PROP_LABEL_WIDGET:
      g_value_set_object (value,
327 328
                          priv->label_widget ?
                          G_OBJECT (priv->label_widget) : NULL);
329 330
      break;
    default:
331
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
332 333 334 335
      break;
    }
}

336 337 338 339 340 341 342 343 344
/**
 * gtk_frame_new:
 * @label: the text to use as the label of the frame
 * 
 * Creates a new #GtkFrame, with optional label @label.
 * If @label is %NULL, the label is omitted.
 * 
 * Return value: a new #GtkFrame widget
 **/
Elliot Lee's avatar
Elliot Lee committed
345 346 347
GtkWidget*
gtk_frame_new (const gchar *label)
{
Manish Singh's avatar
Manish Singh committed
348
  return g_object_new (GTK_TYPE_FRAME, "label", label, NULL);
Elliot Lee's avatar
Elliot Lee committed
349 350
}

351
static void
352 353
gtk_frame_remove (GtkContainer *container,
		  GtkWidget    *child)
354
{
355
  GtkFrame *frame = GTK_FRAME (container);
356
  GtkFramePrivate *priv = frame->priv;
357

358
  if (priv->label_widget == child)
359 360
    gtk_frame_set_label_widget (frame, NULL);
  else
Matthias Clasen's avatar
Matthias Clasen committed
361
    GTK_CONTAINER_CLASS (gtk_frame_parent_class)->remove (container, child);
362
}
363

364 365 366 367 368 369 370 371
static void
gtk_frame_forall (GtkContainer *container,
		  gboolean      include_internals,
		  GtkCallback   callback,
		  gpointer      callback_data)
{
  GtkBin *bin = GTK_BIN (container);
  GtkFrame *frame = GTK_FRAME (container);
372
  GtkFramePrivate *priv = frame->priv;
Javier Jardón's avatar
Javier Jardón committed
373
  GtkWidget *child;
374

Javier Jardón's avatar
Javier Jardón committed
375 376 377
  child = gtk_bin_get_child (bin);
  if (child)
    (* callback) (child, callback_data);
378

379 380
  if (priv->label_widget)
    (* callback) (priv->label_widget, callback_data);
381 382
}

383 384 385 386 387 388 389 390 391 392
static GtkWidgetPath *
gtk_frame_get_path_for_child (GtkContainer *container,
                              GtkWidget    *child)
{
  GtkFramePrivate *priv = GTK_FRAME (container)->priv;
  GtkWidgetPath *path;

  path = GTK_CONTAINER_CLASS (gtk_frame_parent_class)->get_path_for_child (container, child);

  if (child == priv->label_widget)
393 394 395
    gtk_widget_path_iter_add_class (path,
                                    gtk_widget_path_length (path) - 2,
                                    GTK_STYLE_CLASS_FRAME);
396 397 398 399

  return path;
}

400 401 402
/**
 * gtk_frame_set_label:
 * @frame: a #GtkFrame
403 404
 * @label: (allow-none): the text to use as the label of the frame
 *
405 406 407
 * Sets the text of the label. If @label is %NULL,
 * the current label is removed.
 **/
Elliot Lee's avatar
Elliot Lee committed
408 409 410 411 412 413
void
gtk_frame_set_label (GtkFrame *frame,
		     const gchar *label)
{
  g_return_if_fail (GTK_IS_FRAME (frame));

414
  if (!label)
Elliot Lee's avatar
Elliot Lee committed
415
    {
416
      gtk_frame_set_label_widget (frame, NULL);
Elliot Lee's avatar
Elliot Lee committed
417 418 419
    }
  else
    {
420 421 422 423
      GtkWidget *child = gtk_label_new (label);
      gtk_widget_show (child);

      gtk_frame_set_label_widget (frame, child);
Elliot Lee's avatar
Elliot Lee committed
424
    }
425
}
Elliot Lee's avatar
Elliot Lee committed
426

427 428 429 430
/**
 * gtk_frame_get_label:
 * @frame: a #GtkFrame
 * 
431
 * If the frame's label widget is a #GtkLabel, returns the
432 433 434 435 436 437
 * text in the label widget. (The frame will have a #GtkLabel
 * for the label widget if a non-%NULL argument was passed
 * to gtk_frame_new().)
 * 
 * Return value: the text in the label, or %NULL if there
 *               was no label widget or the lable widget was not
438 439
 *               a #GtkLabel. This string is owned by GTK+ and
 *               must not be modified or freed.
440
 **/
441
const gchar *
442 443
gtk_frame_get_label (GtkFrame *frame)
{
444
  GtkFramePrivate *priv;
445

446
  g_return_val_if_fail (GTK_IS_FRAME (frame), NULL);
Elliot Lee's avatar
Elliot Lee committed
447

448 449 450 451
  priv = frame->priv;

  if (GTK_IS_LABEL (priv->label_widget))
    return gtk_label_get_text (GTK_LABEL (priv->label_widget));
452 453 454
  else
    return NULL;
}
Elliot Lee's avatar
Elliot Lee committed
455

456 457 458 459 460
/**
 * gtk_frame_set_label_widget:
 * @frame: a #GtkFrame
 * @label_widget: the new label widget
 * 
461
 * Sets the label widget for the frame. This is the widget that
462 463 464 465 466 467 468
 * will appear embedded in the top edge of the frame as a
 * title.
 **/
void
gtk_frame_set_label_widget (GtkFrame  *frame,
			    GtkWidget *label_widget)
{
469
  GtkFramePrivate *priv;
470
  gboolean need_resize = FALSE;
471

472 473
  g_return_if_fail (GTK_IS_FRAME (frame));
  g_return_if_fail (label_widget == NULL || GTK_IS_WIDGET (label_widget));
474
  g_return_if_fail (label_widget == NULL || gtk_widget_get_parent (label_widget) == NULL);
475 476 477 478

  priv = frame->priv;

  if (priv->label_widget == label_widget)
479
    return;
480 481

  if (priv->label_widget)
482
    {
483 484
      need_resize = gtk_widget_get_visible (priv->label_widget);
      gtk_widget_unparent (priv->label_widget);
485 486
    }

487 488
  priv->label_widget = label_widget;

489 490
  if (label_widget)
    {
491
      priv->label_widget = label_widget;
492
      gtk_widget_set_parent (label_widget, GTK_WIDGET (frame));
493
      need_resize |= gtk_widget_get_visible (label_widget);
494
    }
495

496
  if (gtk_widget_get_visible (GTK_WIDGET (frame)) && need_resize)
497
    gtk_widget_queue_resize (GTK_WIDGET (frame));
498

499
  g_object_freeze_notify (G_OBJECT (frame));
500
  g_object_notify (G_OBJECT (frame), "label-widget");
501 502
  g_object_notify (G_OBJECT (frame), "label");
  g_object_thaw_notify (G_OBJECT (frame));
Elliot Lee's avatar
Elliot Lee committed
503 504
}

505 506 507 508 509 510 511
/**
 * gtk_frame_get_label_widget:
 * @frame: a #GtkFrame
 *
 * Retrieves the label widget for the frame. See
 * gtk_frame_set_label_widget().
 *
512
 * Return value: (transfer none): the label widget, or %NULL if there is none.
513 514 515 516 517 518
 **/
GtkWidget *
gtk_frame_get_label_widget (GtkFrame *frame)
{
  g_return_val_if_fail (GTK_IS_FRAME (frame), NULL);

519
  return frame->priv->label_widget;
520 521
}

522 523 524 525 526 527 528
/**
 * gtk_frame_set_label_align:
 * @frame: a #GtkFrame
 * @xalign: The position of the label along the top edge
 *   of the widget. A value of 0.0 represents left alignment;
 *   1.0 represents right alignment.
 * @yalign: The y alignment of the label. A value of 0.0 aligns under 
529 530 531
 *   the frame; 1.0 aligns above the frame. If the values are exactly
 *   0.0 or 1.0 the gap in the frame won't be painted because the label
 *   will be completely above or below the frame.
532 533 534 535
 * 
 * Sets the alignment of the frame widget's label. The
 * default values for a newly created frame are 0.0 and 0.5.
 **/
Elliot Lee's avatar
Elliot Lee committed
536 537 538 539 540
void
gtk_frame_set_label_align (GtkFrame *frame,
			   gfloat    xalign,
			   gfloat    yalign)
{
541
  GtkFramePrivate *priv;
542

Elliot Lee's avatar
Elliot Lee committed
543 544
  g_return_if_fail (GTK_IS_FRAME (frame));

545 546
  priv = frame->priv;

Elliot Lee's avatar
Elliot Lee committed
547 548 549
  xalign = CLAMP (xalign, 0.0, 1.0);
  yalign = CLAMP (yalign, 0.0, 1.0);

550
  g_object_freeze_notify (G_OBJECT (frame));
551
  if (xalign != priv->label_xalign)
Elliot Lee's avatar
Elliot Lee committed
552
    {
553
      priv->label_xalign = xalign;
554
      g_object_notify (G_OBJECT (frame), "label-xalign");
555
    }
Elliot Lee's avatar
Elliot Lee committed
556

557
  if (yalign != priv->label_yalign)
558
    {
559
      priv->label_yalign = yalign;
560
      g_object_notify (G_OBJECT (frame), "label-yalign");
Elliot Lee's avatar
Elliot Lee committed
561
    }
562

563
  g_object_thaw_notify (G_OBJECT (frame));
564
  gtk_widget_queue_resize (GTK_WIDGET (frame));
Elliot Lee's avatar
Elliot Lee committed
565 566
}

567 568 569
/**
 * gtk_frame_get_label_align:
 * @frame: a #GtkFrame
570 571 572 573
 * @xalign: (out) (allow-none): location to store X alignment of
 *     frame's label, or %NULL
 * @yalign: (out) (allow-none): location to store X alignment of
 *     frame's label, or %NULL
574 575 576 577 578 579 580 581 582
 * 
 * Retrieves the X and Y alignment of the frame's label. See
 * gtk_frame_set_label_align().
 **/
void
gtk_frame_get_label_align (GtkFrame *frame,
		           gfloat   *xalign,
			   gfloat   *yalign)
{
583
  GtkFramePrivate *priv;
584

585 586
  g_return_if_fail (GTK_IS_FRAME (frame));

587 588
  priv = frame->priv;

589
  if (xalign)
590
    *xalign = priv->label_xalign;
591
  if (yalign)
592
    *yalign = priv->label_yalign;
593 594
}

595 596 597 598 599 600 601
/**
 * gtk_frame_set_shadow_type:
 * @frame: a #GtkFrame
 * @type: the new #GtkShadowType
 * 
 * Sets the shadow type for @frame.
 **/
Elliot Lee's avatar
Elliot Lee committed
602 603 604 605
void
gtk_frame_set_shadow_type (GtkFrame      *frame,
			   GtkShadowType  type)
{
606
  GtkFramePrivate *priv;
607 608
  GtkWidget *widget;

Elliot Lee's avatar
Elliot Lee committed
609 610
  g_return_if_fail (GTK_IS_FRAME (frame));

611 612 613
  priv = frame->priv;

  if ((GtkShadowType) priv->shadow_type != type)
Elliot Lee's avatar
Elliot Lee committed
614
    {
615
      widget = GTK_WIDGET (frame);
616
      priv->shadow_type = type;
617
      g_object_notify (G_OBJECT (frame), "shadow-type");
Elliot Lee's avatar
Elliot Lee committed
618

619
      if (gtk_widget_is_drawable (widget))
Elliot Lee's avatar
Elliot Lee committed
620
	{
621
	  gtk_widget_queue_draw (widget);
Elliot Lee's avatar
Elliot Lee committed
622
	}
623
      
624
      gtk_widget_queue_resize (widget);
Elliot Lee's avatar
Elliot Lee committed
625 626 627
    }
}

628 629 630 631 632 633 634 635 636 637 638 639 640 641
/**
 * gtk_frame_get_shadow_type:
 * @frame: a #GtkFrame
 *
 * Retrieves the shadow type of the frame. See
 * gtk_frame_set_shadow_type().
 *
 * Return value: the current shadow type of the frame.
 **/
GtkShadowType
gtk_frame_get_shadow_type (GtkFrame *frame)
{
  g_return_val_if_fail (GTK_IS_FRAME (frame), GTK_SHADOW_ETCHED_IN);

642
  return frame->priv->shadow_type;
643 644
}

645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668
static void
get_padding_and_border (GtkFrame *frame,
                        GtkBorder *border)
{
  GtkStyleContext *context;
  GtkStateFlags state;

  context = gtk_widget_get_style_context (GTK_WIDGET (frame));
  state = gtk_widget_get_state_flags (GTK_WIDGET (frame));

  gtk_style_context_get_padding (context, state, border);

  if (frame->priv->shadow_type != GTK_SHADOW_NONE)
    {
      GtkBorder tmp;

      gtk_style_context_get_border (context, state, &tmp);
      border->top += tmp.top;
      border->right += tmp.right;
      border->bottom += tmp.bottom;
      border->left += tmp.left;
    }
}

669 670 671
static gboolean
gtk_frame_draw (GtkWidget *widget,
		cairo_t   *cr)
Elliot Lee's avatar
Elliot Lee committed
672 673
{
  GtkFrame *frame;
674
  GtkFramePrivate *priv;
675
  GtkStyleContext *context;
676
  gint x, y, width, height;
677
  GtkAllocation allocation;
678
  GtkBorder padding;
Elliot Lee's avatar
Elliot Lee committed
679

680 681
  frame = GTK_FRAME (widget);
  priv = frame->priv;
Elliot Lee's avatar
Elliot Lee committed
682

683
  gtk_widget_get_allocation (widget, &allocation);
684 685
  get_padding_and_border (frame, &padding);
  context = gtk_widget_get_style_context (widget);
686

687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719
  x = priv->child_allocation.x - allocation.x - padding.left;
  y = priv->child_allocation.y - allocation.y - padding.top;
  width = priv->child_allocation.width + padding.left + padding.right;
  height =  priv->child_allocation.height + padding.top + padding.bottom;

  if (priv->shadow_type != GTK_SHADOW_NONE)
    {
      if (priv->label_widget)
        {
          gfloat xalign;
          gint height_extra;
          gint x2;

          if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
            xalign = priv->label_xalign;
          else
            xalign = 1 - priv->label_xalign;

          height_extra = MAX (0, priv->label_allocation.height - padding.top)
            - priv->label_yalign * priv->label_allocation.height;
          y -= height_extra;
          height += height_extra;

          x2 = padding.left + (priv->child_allocation.width - priv->label_allocation.width - 2 * LABEL_PAD - 2 * LABEL_SIDE_PAD) * xalign + LABEL_SIDE_PAD;
          /* If the label is completely over or under the frame we can omit the gap */
          if (priv->label_yalign == 0.0 || priv->label_yalign == 1.0)
            gtk_render_frame (context, cr, x, y, width, height);
          else
            gtk_render_frame_gap (context, cr,
                                  x, y, width, height,
                                  GTK_POS_TOP, x2,
                                  x2 + priv->label_allocation.width + 2 * LABEL_PAD);
        }
720
      else
721
        gtk_render_frame (context, cr, x, y, width, height);
Elliot Lee's avatar
Elliot Lee committed
722
    }
723

724
  GTK_WIDGET_CLASS (gtk_frame_parent_class)->draw (widget, cr);
Elliot Lee's avatar
Elliot Lee committed
725 726 727 728

  return FALSE;
}

729 730 731 732 733
static void
gtk_frame_size_allocate (GtkWidget     *widget,
			 GtkAllocation *allocation)
{
  GtkFrame *frame = GTK_FRAME (widget);
734
  GtkFramePrivate *priv = frame->priv;
735
  GtkBin *bin = GTK_BIN (widget);
736
  GtkAllocation new_allocation;
Javier Jardón's avatar
Javier Jardón committed
737
  GtkWidget *child;
Elliot Lee's avatar
Elliot Lee committed
738

739
  gtk_widget_set_allocation (widget, allocation);
Elliot Lee's avatar
Elliot Lee committed
740

741 742 743 744 745
  gtk_frame_compute_child_allocation (frame, &new_allocation);
  
  /* If the child allocation changed, that means that the frame is drawn
   * in a new place, so we must redraw the entire widget.
   */
746 747 748 749
  if (gtk_widget_get_mapped (widget))
    {
      gdk_window_invalidate_rect (gtk_widget_get_window (widget), allocation, FALSE);
    }
Javier Jardón's avatar
Javier Jardón committed
750 751 752 753

  child = gtk_bin_get_child (bin);
  if (child && gtk_widget_get_visible (child))
    gtk_widget_size_allocate (child, &new_allocation);
754 755 756 757

  priv->child_allocation = new_allocation;

  if (priv->label_widget && gtk_widget_get_visible (priv->label_widget))
Elliot Lee's avatar
Elliot Lee committed
758
    {
759
      GtkBorder padding;
760
      gint nat_width, width, height;
761 762
      gfloat xalign;

763
      get_padding_and_border (frame, &padding);
764

765
      if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
766
	xalign = priv->label_xalign;
767
      else
768 769
	xalign = 1 - priv->label_xalign;

770
      gtk_widget_get_preferred_width (priv->label_widget, NULL, &nat_width);
771 772 773
      width = new_allocation.width - 2 * LABEL_PAD - 2 * LABEL_SIDE_PAD;
      width = MIN (width, nat_width);

774 775
      gtk_widget_get_preferred_height_for_width (priv->label_widget, width,
                                                 &height, NULL);
776 777 778 779


      priv->label_allocation.x = priv->child_allocation.x + LABEL_SIDE_PAD +
	(new_allocation.width - width - 2 * LABEL_PAD - 2 * LABEL_SIDE_PAD) * xalign + LABEL_PAD;
780

781
      priv->label_allocation.width = width;
782

783
      priv->label_allocation.y = priv->child_allocation.y - MAX (height, padding.top);
784 785 786
      priv->label_allocation.height = height;

      gtk_widget_size_allocate (priv->label_widget, &priv->label_allocation);
Elliot Lee's avatar
Elliot Lee committed
787 788 789
    }
}

790 791 792 793 794 795
static void
gtk_frame_compute_child_allocation (GtkFrame      *frame,
				    GtkAllocation *child_allocation)
{
  g_return_if_fail (GTK_IS_FRAME (frame));
  g_return_if_fail (child_allocation != NULL);
Elliot Lee's avatar
Elliot Lee committed
796

797 798
  GTK_FRAME_GET_CLASS (frame)->compute_child_allocation (frame, child_allocation);
}
Elliot Lee's avatar
Elliot Lee committed
799

800 801 802 803
static void
gtk_frame_real_compute_child_allocation (GtkFrame      *frame,
					 GtkAllocation *child_allocation)
{
804
  GtkFramePrivate *priv = frame->priv;
805
  GtkWidget *widget = GTK_WIDGET (frame);
806
  GtkAllocation allocation;
807
  GtkBorder padding;
808
  gint top_margin;
809
  guint border_width;
Elliot Lee's avatar
Elliot Lee committed
810

811
  gtk_widget_get_allocation (widget, &allocation);
812
  get_padding_and_border (frame, &padding);
813 814
  border_width = gtk_container_get_border_width (GTK_CONTAINER (frame));

815
  if (priv->label_widget)
816
    {
817 818
      gint nat_width, width, height;

819
      gtk_widget_get_preferred_width (priv->label_widget, NULL, &nat_width);
820

821
      width = allocation.width;
822
      width -= 2 * LABEL_PAD + 2 * LABEL_SIDE_PAD;
823
      width -= (border_width * 2) + padding.left + padding.right;
824 825 826

      width = MIN (width, nat_width);

827 828
      gtk_widget_get_preferred_height_for_width (priv->label_widget, width,
                                                 &height, NULL);
829

830
      top_margin = MAX (height, padding.top);
Elliot Lee's avatar
Elliot Lee committed
831
    }
832
  else
833
    top_margin = padding.top;
834

835
  child_allocation->x = border_width + padding.left;
836
  child_allocation->y = border_width + top_margin;
837 838 839 840
  child_allocation->width = MAX (1, (gint) (allocation.width - (border_width * 2) -
					    padding.left - padding.right));
  child_allocation->height = MAX (1, (gint) (allocation.height - child_allocation->y -
					     border_width - padding.bottom));
841 842 843

  child_allocation->x += allocation.x;
  child_allocation->y += allocation.y;
Elliot Lee's avatar
Elliot Lee committed
844
}
845

846
static void
847 848 849 850
gtk_frame_get_preferred_size (GtkWidget      *request,
                              GtkOrientation  orientation,
                              gint           *minimum_size,
                              gint           *natural_size)
851
{
852
  GtkFrame *frame = GTK_FRAME (request);
853
  GtkFramePrivate *priv = frame->priv;
854
  GtkBorder padding;
855
  GtkWidget *widget = GTK_WIDGET (request);
Javier Jardón's avatar
Javier Jardón committed
856
  GtkWidget *child;
857 858 859
  GtkBin *bin = GTK_BIN (widget);
  gint child_min, child_nat;
  gint minimum, natural;
860
  guint border_width;
861

862 863 864
  get_padding_and_border (frame, &padding);
  border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));

865
  if (priv->label_widget && gtk_widget_get_visible (priv->label_widget))
866 867 868
    {
      if (orientation == GTK_ORIENTATION_HORIZONTAL)
        {
869 870
          gtk_widget_get_preferred_width (priv->label_widget,
                                          &child_min, &child_nat);
871 872 873 874 875
          minimum = child_min + 2 * LABEL_PAD + 2 * LABEL_SIDE_PAD;
          natural = child_nat + 2 * LABEL_PAD + 2 * LABEL_SIDE_PAD;
        }
      else
        {
876 877
          gtk_widget_get_preferred_height (priv->label_widget,
                                           &child_min, &child_nat);
878 879
          minimum = MAX (0, child_min - padding.top);
          natural = MAX (0, child_nat - padding.top);
880 881 882 883 884 885 886 887
        }
    }
  else
    {
      minimum = 0;
      natural = 0;
    }

Javier Jardón's avatar
Javier Jardón committed
888 889
  child = gtk_bin_get_child (bin);
  if (child && gtk_widget_get_visible (child))
890 891 892
    {
      if (orientation == GTK_ORIENTATION_HORIZONTAL)
        {
893 894
          gtk_widget_get_preferred_width (child,
                                          &child_min, &child_nat);
895 896 897 898 899
          minimum = MAX (minimum, child_min);
          natural = MAX (natural, child_nat);
        }
      else
        {
900 901
          gtk_widget_get_preferred_height (child,
                                           &child_min, &child_nat);
902 903 904 905 906 907 908
          minimum += child_min;
          natural += child_nat;
        }
    }

  if (orientation == GTK_ORIENTATION_HORIZONTAL)
    {
909 910
      minimum += (border_width * 2) + padding.left + padding.right;
      natural += (border_width * 2) + padding.left + padding.right;
911 912 913
    }
  else
    {
914 915
      minimum += (border_width * 2) + padding.top + padding.bottom;
      natural += (border_width * 2) + padding.top + padding.bottom;
916 917 918 919 920 921 922 923 924 925
    }

 if (minimum_size)
    *minimum_size = minimum;

  if (natural_size)
    *natural_size = natural;
}

static void
926 927 928
gtk_frame_get_preferred_width (GtkWidget *widget,
                               gint      *minimum_size,
                               gint      *natural_size)
929
{
930
  gtk_frame_get_preferred_size (widget, GTK_ORIENTATION_HORIZONTAL, minimum_size, natural_size);
931 932 933
}

static void
934 935 936
gtk_frame_get_preferred_height (GtkWidget *widget,
                                gint      *minimum_size,
                                gint      *natural_size)
937
{
938
  gtk_frame_get_preferred_size (widget, GTK_ORIENTATION_VERTICAL, minimum_size, natural_size);
939 940
}

941 942

static void
943 944 945 946
gtk_frame_get_preferred_height_for_width (GtkWidget *request,
                                          gint       width,
                                          gint      *minimum_height,
                                          gint      *natural_height)
947 948 949 950
{
  GtkWidget *widget = GTK_WIDGET (request);
  GtkWidget *child;
  GtkFrame *frame = GTK_FRAME (widget);
951
  GtkFramePrivate *priv = frame->priv;
952
  GtkBin *bin = GTK_BIN (widget);
953
  GtkBorder padding;
954 955 956 957
  gint child_min, child_nat, label_width;
  gint minimum, natural;
  guint border_width;

958
  get_padding_and_border (frame, &padding);
959
  border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
960 961 962

  minimum = (border_width * 2) + padding.top + padding.bottom;
  natural = (border_width * 2) + padding.top + padding.bottom;
963

964
  width -= (border_width * 2) + padding.left + padding.right;
965 966 967 968
  label_width = width - 2 * LABEL_PAD + 2 * LABEL_SIDE_PAD;

  if (priv->label_widget && gtk_widget_get_visible (priv->label_widget))
    {
969 970
      gtk_widget_get_preferred_height_for_width (priv->label_widget,
                                                 label_width, &child_min, &child_nat);
971 972 973 974 975 976 977
      minimum += child_min;
      natural += child_nat;
    }

  child = gtk_bin_get_child (bin);
  if (child && gtk_widget_get_visible (child))
    {
978 979
      gtk_widget_get_preferred_height_for_width (child,
                                                 width, &child_min, &child_nat);
980 981 982 983 984 985 986 987 988 989 990 991
      minimum += child_min;
      natural += child_nat;
    }

 if (minimum_height)
    *minimum_height = minimum;

  if (natural_height)
    *natural_height = natural;
}

static void
992 993 994 995
gtk_frame_get_preferred_width_for_height (GtkWidget *widget,
                                          gint       height,
                                          gint      *minimum_width,
                                          gint      *natural_width)
996
{
997
  GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, minimum_width, natural_width);
998 999
}