]> Pileus Git - ~andy/gtk/blob - gtk/gtkexpander.c
Bug 643805 - Allow GtkExpander to resize the toplevel upon expanding/collpasing
[~andy/gtk] / gtk / gtkexpander.c
1 /* GTK - The GIMP Toolkit
2  *
3  * Copyright (C) 2003 Sun Microsystems, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library 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  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library 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  * Authors:
21  *      Mark McLoughlin <mark@skynet.ie>
22  */
23
24 /**
25  * SECTION:gtkexpander
26  * @Short_description: A container which can hide its child
27  * @Title: GtkExpander
28  *
29  * A #GtkExpander allows the user to hide or show its child by clicking
30  * on an expander triangle similar to the triangles used in a #GtkTreeView.
31  *
32  * Normally you use an expander as you would use any other descendant
33  * of #GtkBin; you create the child widget and use gtk_container_add()
34  * to add it to the expander. When the expander is toggled, it will take
35  * care of showing and hiding the child automatically.
36  *
37  * <refsect2 id="expander-special-usage">
38  * <title>Special Usage</title>
39  * <para>
40  * There are situations in which you may prefer to show and hide the
41  * expanded widget yourself, such as when you want to actually create
42  * the widget at expansion time. In this case, create a #GtkExpander
43  * but do not add a child to it. The expander widget has an
44  * #GtkExpander:expanded property which can be used to monitor
45  * its expansion state. You should watch this property with a signal
46  * connection as follows:
47  * </para>
48  * <informalexample>
49  * <programlisting id="expander-callback-example">
50  * expander = gtk_expander_new_with_mnemonic ("_More Options");
51  * g_signal_connect (expander, "notify::expanded",
52  *                   G_CALLBACK (expander_callback), NULL);
53  *
54  * ...
55  *
56  * static void
57  * expander_callback (GObject    *object,
58  *                    GParamSpec *param_spec,
59  *                    gpointer    user_data)
60  * {
61  *   GtkExpander *expander;
62  *
63  *   expander = GTK_EXPANDER (object);
64  *
65  *   if (gtk_expander_get_expanded (expander))
66  *     {
67  *       /&ast; Show or create widgets &ast;/
68  *     }
69  *   else
70  *     {
71  *       /&ast; Hide or destroy widgets &ast;/
72  *     }
73  * }
74  * </programlisting>
75  * </informalexample>
76  * </refsect2>
77  * <refsect2 id="GtkExpander-BUILDER-UI">
78  * <title>GtkExpander as GtkBuildable</title>
79  * <para>
80  * The GtkExpander implementation of the GtkBuildable interface
81  * supports placing a child in the label position by specifying
82  * "label" as the "type" attribute of a &lt;child&gt; element.
83  * A normal content child can be specified without specifying
84  * a &lt;child&gt; type attribute.
85  * </para>
86  * <example>
87  * <title>A UI definition fragment with GtkExpander</title>
88  * <programlisting><![CDATA[
89  * <object class="GtkExpander">
90  *   <child type="label">
91  *     <object class="GtkLabel" id="expander-label"/>
92  *   </child>
93  *   <child>
94  *     <object class="GtkEntry" id="expander-content"/>
95  *   </child>
96  * </object>
97  * ]]></programlisting>
98  * </example>
99  * </refsect2>
100  *
101  */
102
103 #include "config.h"
104
105 #include <string.h>
106
107 #include "gtkexpander.h"
108
109 #include "gtklabel.h"
110 #include "gtkbuildable.h"
111 #include "gtkcontainer.h"
112 #include "gtkmarshalers.h"
113 #include "gtkmain.h"
114 #include "gtkintl.h"
115 #include "gtkprivate.h"
116 #include "gtkdnd.h"
117
118
119 #define DEFAULT_EXPANDER_SIZE 10
120 #define DEFAULT_EXPANDER_SPACING 2
121
122 enum
123 {
124   PROP_0,
125   PROP_EXPANDED,
126   PROP_LABEL,
127   PROP_USE_UNDERLINE,
128   PROP_USE_MARKUP,
129   PROP_SPACING,
130   PROP_LABEL_WIDGET,
131   PROP_LABEL_FILL,
132   PROP_RESIZE_TOPLEVEL
133 };
134
135 struct _GtkExpanderPrivate
136 {
137   GtkWidget        *label_widget;
138   GdkWindow        *event_window;
139   gint              spacing;
140
141   guint             expand_timer;
142
143   guint             expanded : 1;
144   guint             use_underline : 1;
145   guint             use_markup : 1; 
146   guint             button_down : 1;
147   guint             prelight : 1;
148   guint             label_fill : 1;
149   guint             resize_toplevel : 1;
150 };
151
152 static void gtk_expander_set_property (GObject          *object,
153                                        guint             prop_id,
154                                        const GValue     *value,
155                                        GParamSpec       *pspec);
156 static void gtk_expander_get_property (GObject          *object,
157                                        guint             prop_id,
158                                        GValue           *value,
159                                        GParamSpec       *pspec);
160
161 static void     gtk_expander_destroy        (GtkWidget        *widget);
162 static void     gtk_expander_realize        (GtkWidget        *widget);
163 static void     gtk_expander_unrealize      (GtkWidget        *widget);
164 static void     gtk_expander_size_allocate  (GtkWidget        *widget,
165                                              GtkAllocation    *allocation);
166 static void     gtk_expander_map            (GtkWidget        *widget);
167 static void     gtk_expander_unmap          (GtkWidget        *widget);
168 static gboolean gtk_expander_draw           (GtkWidget        *widget,
169                                              cairo_t          *cr);
170 static gboolean gtk_expander_button_press   (GtkWidget        *widget,
171                                              GdkEventButton   *event);
172 static gboolean gtk_expander_button_release (GtkWidget        *widget,
173                                              GdkEventButton   *event);
174 static gboolean gtk_expander_enter_notify   (GtkWidget        *widget,
175                                              GdkEventCrossing *event);
176 static gboolean gtk_expander_leave_notify   (GtkWidget        *widget,
177                                              GdkEventCrossing *event);
178 static gboolean gtk_expander_focus          (GtkWidget        *widget,
179                                              GtkDirectionType  direction);
180 static void     gtk_expander_grab_notify    (GtkWidget        *widget,
181                                              gboolean          was_grabbed);
182 static void     gtk_expander_state_flags_changed  (GtkWidget     *widget,
183                                                    GtkStateFlags  previous_state);
184 static gboolean gtk_expander_drag_motion    (GtkWidget        *widget,
185                                              GdkDragContext   *context,
186                                              gint              x,
187                                              gint              y,
188                                              guint             time);
189 static void     gtk_expander_drag_leave     (GtkWidget        *widget,
190                                              GdkDragContext   *context,
191                                              guint             time);
192
193 static void gtk_expander_add    (GtkContainer *container,
194                                  GtkWidget    *widget);
195 static void gtk_expander_remove (GtkContainer *container,
196                                  GtkWidget    *widget);
197 static void gtk_expander_forall (GtkContainer *container,
198                                  gboolean        include_internals,
199                                  GtkCallback     callback,
200                                  gpointer        callback_data);
201
202 static void gtk_expander_activate (GtkExpander *expander);
203
204 static void get_expander_bounds (GtkExpander  *expander,
205                                  GdkRectangle *rect);
206
207 /* GtkBuildable */
208 static void gtk_expander_buildable_init           (GtkBuildableIface *iface);
209 static void gtk_expander_buildable_add_child      (GtkBuildable *buildable,
210                                                    GtkBuilder   *builder,
211                                                    GObject      *child,
212                                                    const gchar  *type);
213
214
215 /* GtkWidget      */
216 static void  gtk_expander_get_preferred_width             (GtkWidget           *widget,
217                                                            gint                *minimum_size,
218                                                            gint                *natural_size);
219 static void  gtk_expander_get_preferred_height            (GtkWidget           *widget,
220                                                            gint                *minimum_size,
221                                                            gint                *natural_size);
222 static void  gtk_expander_get_preferred_height_for_width  (GtkWidget           *layout,
223                                                            gint                 width,
224                                                            gint                *minimum_height,
225                                                            gint                *natural_height);
226 static void  gtk_expander_get_preferred_width_for_height  (GtkWidget           *layout,
227                                                            gint                 width,
228                                                            gint                *minimum_height,
229                                                            gint                *natural_height);
230
231 G_DEFINE_TYPE_WITH_CODE (GtkExpander, gtk_expander, GTK_TYPE_BIN,
232                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
233                                                 gtk_expander_buildable_init))
234
235 static void
236 gtk_expander_class_init (GtkExpanderClass *klass)
237 {
238   GObjectClass *gobject_class;
239   GtkWidgetClass *widget_class;
240   GtkContainerClass *container_class;
241
242   gobject_class   = (GObjectClass *) klass;
243   widget_class    = (GtkWidgetClass *) klass;
244   container_class = (GtkContainerClass *) klass;
245
246   gobject_class->set_property = gtk_expander_set_property;
247   gobject_class->get_property = gtk_expander_get_property;
248
249   widget_class->destroy              = gtk_expander_destroy;
250   widget_class->realize              = gtk_expander_realize;
251   widget_class->unrealize            = gtk_expander_unrealize;
252   widget_class->size_allocate        = gtk_expander_size_allocate;
253   widget_class->map                  = gtk_expander_map;
254   widget_class->unmap                = gtk_expander_unmap;
255   widget_class->draw                 = gtk_expander_draw;
256   widget_class->button_press_event   = gtk_expander_button_press;
257   widget_class->button_release_event = gtk_expander_button_release;
258   widget_class->enter_notify_event   = gtk_expander_enter_notify;
259   widget_class->leave_notify_event   = gtk_expander_leave_notify;
260   widget_class->focus                = gtk_expander_focus;
261   widget_class->grab_notify          = gtk_expander_grab_notify;
262   widget_class->state_flags_changed  = gtk_expander_state_flags_changed;
263   widget_class->drag_motion          = gtk_expander_drag_motion;
264   widget_class->drag_leave           = gtk_expander_drag_leave;
265   widget_class->get_preferred_width            = gtk_expander_get_preferred_width;
266   widget_class->get_preferred_height           = gtk_expander_get_preferred_height;
267   widget_class->get_preferred_height_for_width = gtk_expander_get_preferred_height_for_width;
268   widget_class->get_preferred_width_for_height = gtk_expander_get_preferred_width_for_height;
269
270   container_class->add    = gtk_expander_add;
271   container_class->remove = gtk_expander_remove;
272   container_class->forall = gtk_expander_forall;
273
274   klass->activate = gtk_expander_activate;
275
276   g_type_class_add_private (klass, sizeof (GtkExpanderPrivate));
277
278   g_object_class_install_property (gobject_class,
279                                    PROP_EXPANDED,
280                                    g_param_spec_boolean ("expanded",
281                                                          P_("Expanded"),
282                                                          P_("Whether the expander has been opened to reveal the child widget"),
283                                                          FALSE,
284                                                          GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
285
286   g_object_class_install_property (gobject_class,
287                                    PROP_LABEL,
288                                    g_param_spec_string ("label",
289                                                         P_("Label"),
290                                                         P_("Text of the expander's label"),
291                                                         NULL,
292                                                         GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
293
294   g_object_class_install_property (gobject_class,
295                                    PROP_USE_UNDERLINE,
296                                    g_param_spec_boolean ("use-underline",
297                                                          P_("Use underline"),
298                                                          P_("If set, an underline in the text indicates the next character should be used for the mnemonic accelerator key"),
299                                                          FALSE,
300                                                          GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
301
302   g_object_class_install_property (gobject_class,
303                                    PROP_USE_MARKUP,
304                                    g_param_spec_boolean ("use-markup",
305                                                          P_("Use markup"),
306                                                          P_("The text of the label includes XML markup. See pango_parse_markup()"),
307                                                          FALSE,
308                                                          GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
309
310   g_object_class_install_property (gobject_class,
311                                    PROP_SPACING,
312                                    g_param_spec_int ("spacing",
313                                                      P_("Spacing"),
314                                                      P_("Space to put between the label and the child"),
315                                                      0,
316                                                      G_MAXINT,
317                                                      0,
318                                                      GTK_PARAM_READWRITE));
319
320   g_object_class_install_property (gobject_class,
321                                    PROP_LABEL_WIDGET,
322                                    g_param_spec_object ("label-widget",
323                                                         P_("Label widget"),
324                                                         P_("A widget to display in place of the usual expander label"),
325                                                         GTK_TYPE_WIDGET,
326                                                         GTK_PARAM_READWRITE));
327
328   g_object_class_install_property (gobject_class,
329                                    PROP_LABEL_FILL,
330                                    g_param_spec_boolean ("label-fill",
331                                                          P_("Label fill"),
332                                                          P_("Whether the label widget should fill all available horizontal space"),
333                                                          FALSE,
334                                                          GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
335
336   /**
337    * GtkExpander:resize-toplevel:
338    *
339    * When this property is %TRUE, the expander will resize the toplevel
340    * widget containing the expander upon expanding and collapsing.
341    *
342    * Since: 3.2
343    */
344   g_object_class_install_property (gobject_class,
345                                    PROP_RESIZE_TOPLEVEL,
346                                    g_param_spec_boolean ("resize-toplevel",
347                                                          P_("Resize tolevel"),
348                                                          P_("Whether the expander will resize the toplevel window upon expanding and collapsing"),
349                                                          FALSE,
350                                                          GTK_PARAM_READWRITE));
351
352   gtk_widget_class_install_style_property (widget_class,
353                                            g_param_spec_int ("expander-size",
354                                                              P_("Expander Size"),
355                                                              P_("Size of the expander arrow"),
356                                                              0,
357                                                              G_MAXINT,
358                                                              DEFAULT_EXPANDER_SIZE,
359                                                              GTK_PARAM_READABLE));
360
361   gtk_widget_class_install_style_property (widget_class,
362                                            g_param_spec_int ("expander-spacing",
363                                                              P_("Indicator Spacing"),
364                                                              P_("Spacing around expander arrow"),
365                                                              0,
366                                                              G_MAXINT,
367                                                              DEFAULT_EXPANDER_SPACING,
368                                                              GTK_PARAM_READABLE));
369
370   widget_class->activate_signal =
371     g_signal_new (I_("activate"),
372                   G_TYPE_FROM_CLASS (gobject_class),
373                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
374                   G_STRUCT_OFFSET (GtkExpanderClass, activate),
375                   NULL, NULL,
376                   _gtk_marshal_VOID__VOID,
377                   G_TYPE_NONE, 0);
378 }
379
380 static void
381 gtk_expander_init (GtkExpander *expander)
382 {
383   GtkExpanderPrivate *priv;
384
385   expander->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (expander,
386                                                        GTK_TYPE_EXPANDER,
387                                                        GtkExpanderPrivate);
388
389   gtk_widget_set_can_focus (GTK_WIDGET (expander), TRUE);
390   gtk_widget_set_has_window (GTK_WIDGET (expander), FALSE);
391
392   priv->label_widget = NULL;
393   priv->event_window = NULL;
394   priv->spacing = 0;
395
396   priv->expanded = FALSE;
397   priv->use_underline = FALSE;
398   priv->use_markup = FALSE;
399   priv->button_down = FALSE;
400   priv->prelight = FALSE;
401   priv->label_fill = FALSE;
402   priv->expand_timer = 0;
403   priv->resize_toplevel = 0;
404
405   gtk_drag_dest_set (GTK_WIDGET (expander), 0, NULL, 0, 0);
406   gtk_drag_dest_set_track_motion (GTK_WIDGET (expander), TRUE);
407 }
408
409 static void
410 gtk_expander_buildable_add_child (GtkBuildable  *buildable,
411                                   GtkBuilder    *builder,
412                                   GObject       *child,
413                                   const gchar   *type)
414 {
415   if (!type)
416     gtk_container_add (GTK_CONTAINER (buildable), GTK_WIDGET (child));
417   else if (strcmp (type, "label") == 0)
418     gtk_expander_set_label_widget (GTK_EXPANDER (buildable), GTK_WIDGET (child));
419   else
420     GTK_BUILDER_WARN_INVALID_CHILD_TYPE (GTK_EXPANDER (buildable), type);
421 }
422
423 static void
424 gtk_expander_buildable_init (GtkBuildableIface *iface)
425 {
426   iface->add_child = gtk_expander_buildable_add_child;
427 }
428
429 static void
430 gtk_expander_set_property (GObject      *object,
431                            guint         prop_id,
432                            const GValue *value,
433                            GParamSpec   *pspec)
434 {
435   GtkExpander *expander = GTK_EXPANDER (object);
436
437   switch (prop_id)
438     {
439     case PROP_EXPANDED:
440       gtk_expander_set_expanded (expander, g_value_get_boolean (value));
441       break;
442     case PROP_LABEL:
443       gtk_expander_set_label (expander, g_value_get_string (value));
444       break;
445     case PROP_USE_UNDERLINE:
446       gtk_expander_set_use_underline (expander, g_value_get_boolean (value));
447       break;
448     case PROP_USE_MARKUP:
449       gtk_expander_set_use_markup (expander, g_value_get_boolean (value));
450       break;
451     case PROP_SPACING:
452       gtk_expander_set_spacing (expander, g_value_get_int (value));
453       break;
454     case PROP_LABEL_WIDGET:
455       gtk_expander_set_label_widget (expander, g_value_get_object (value));
456       break;
457     case PROP_LABEL_FILL:
458       gtk_expander_set_label_fill (expander, g_value_get_boolean (value));
459       break;
460     case PROP_RESIZE_TOPLEVEL:
461       gtk_expander_set_resize_toplevel (expander, g_value_get_boolean (value));
462       break;
463     default:
464       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
465       break;
466     }
467 }
468
469 static void
470 gtk_expander_get_property (GObject    *object,
471                            guint       prop_id,
472                            GValue     *value,
473                            GParamSpec *pspec)
474 {
475   GtkExpander *expander = GTK_EXPANDER (object);
476   GtkExpanderPrivate *priv = expander->priv;
477
478   switch (prop_id)
479     {
480     case PROP_EXPANDED:
481       g_value_set_boolean (value, priv->expanded);
482       break;
483     case PROP_LABEL:
484       g_value_set_string (value, gtk_expander_get_label (expander));
485       break;
486     case PROP_USE_UNDERLINE:
487       g_value_set_boolean (value, priv->use_underline);
488       break;
489     case PROP_USE_MARKUP:
490       g_value_set_boolean (value, priv->use_markup);
491       break;
492     case PROP_SPACING:
493       g_value_set_int (value, priv->spacing);
494       break;
495     case PROP_LABEL_WIDGET:
496       g_value_set_object (value,
497                           priv->label_widget ?
498                           G_OBJECT (priv->label_widget) : NULL);
499       break;
500     case PROP_LABEL_FILL:
501       g_value_set_boolean (value, priv->label_fill);
502       break;
503     case PROP_RESIZE_TOPLEVEL:
504       g_value_set_boolean (value, gtk_expander_get_resize_toplevel (expander));
505       break;
506     default:
507       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
508       break;
509     }
510 }
511
512 static void
513 gtk_expander_destroy (GtkWidget *widget)
514 {
515   GtkExpanderPrivate *priv = GTK_EXPANDER (widget)->priv;
516
517   if (priv->expand_timer)
518     {
519       g_source_remove (priv->expand_timer);
520       priv->expand_timer = 0;
521     }
522
523   GTK_WIDGET_CLASS (gtk_expander_parent_class)->destroy (widget);
524 }
525
526 static void
527 gtk_expander_realize (GtkWidget *widget)
528 {
529   GtkAllocation allocation;
530   GtkExpanderPrivate *priv;
531   GdkWindow *window;
532   GdkWindowAttr attributes;
533   gint attributes_mask;
534   gint border_width;
535   GdkRectangle expander_rect;
536   gint label_height;
537
538   priv = GTK_EXPANDER (widget)->priv;
539
540   gtk_widget_set_realized (widget, TRUE);
541
542   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
543
544   get_expander_bounds (GTK_EXPANDER (widget), &expander_rect);
545
546   if (priv->label_widget && gtk_widget_get_visible (priv->label_widget))
547     {
548       GtkRequisition label_requisition;
549
550       gtk_widget_get_preferred_size (priv->label_widget,
551                                      &label_requisition, NULL);
552       label_height = label_requisition.height;
553     }
554   else
555     label_height = 0;
556
557   gtk_widget_get_allocation (widget, &allocation);
558
559   attributes.window_type = GDK_WINDOW_CHILD;
560   attributes.x = allocation.x + border_width;
561   attributes.y = allocation.y + border_width;
562   attributes.width = MAX (allocation.width - 2 * border_width, 1);
563   attributes.height = MAX (expander_rect.height, label_height - 2 * border_width);
564   attributes.wclass = GDK_INPUT_ONLY;
565   attributes.event_mask = gtk_widget_get_events (widget)
566                           | GDK_BUTTON_PRESS_MASK
567                           | GDK_BUTTON_RELEASE_MASK
568                           | GDK_ENTER_NOTIFY_MASK
569                           | GDK_LEAVE_NOTIFY_MASK;
570
571   attributes_mask = GDK_WA_X | GDK_WA_Y;
572
573   window = gtk_widget_get_parent_window (widget);
574   gtk_widget_set_window (widget, window);
575   g_object_ref (window);
576
577   priv->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
578                                        &attributes, attributes_mask);
579   gdk_window_set_user_data (priv->event_window, widget);
580 }
581
582 static void
583 gtk_expander_unrealize (GtkWidget *widget)
584 {
585   GtkExpanderPrivate *priv = GTK_EXPANDER (widget)->priv;
586
587   if (priv->event_window)
588     {
589       gdk_window_set_user_data (priv->event_window, NULL);
590       gdk_window_destroy (priv->event_window);
591       priv->event_window = NULL;
592     }
593
594   GTK_WIDGET_CLASS (gtk_expander_parent_class)->unrealize (widget);
595 }
596
597 static void
598 get_expander_bounds (GtkExpander  *expander,
599                      GdkRectangle *rect)
600 {
601   GtkAllocation allocation;
602   GtkWidget *widget;
603   GtkExpanderPrivate *priv;
604   gint border_width;
605   gint expander_size;
606   gint expander_spacing;
607   gboolean interior_focus;
608   gint focus_width;
609   gint focus_pad;
610   gboolean ltr;
611
612   widget = GTK_WIDGET (expander);
613   priv = expander->priv;
614
615   gtk_widget_get_allocation (widget, &allocation);
616
617   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
618
619   gtk_widget_style_get (widget,
620                         "interior-focus", &interior_focus,
621                         "focus-line-width", &focus_width,
622                         "focus-padding", &focus_pad,
623                         "expander-size", &expander_size,
624                         "expander-spacing", &expander_spacing,
625                         NULL);
626
627   ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
628
629   rect->x = allocation.x + border_width;
630   rect->y = allocation.y + border_width;
631
632   if (ltr)
633     rect->x += expander_spacing;
634   else
635     rect->x += allocation.width - 2 * border_width -
636                expander_spacing - expander_size;
637
638   if (priv->label_widget && gtk_widget_get_visible (priv->label_widget))
639     {
640       GtkAllocation label_allocation;
641
642       gtk_widget_get_allocation (priv->label_widget, &label_allocation);
643
644       if (expander_size < label_allocation.height)
645         rect->y += focus_width + focus_pad + (label_allocation.height - expander_size) / 2;
646       else
647         rect->y += expander_spacing;
648     }
649   else
650     {
651       rect->y += expander_spacing;
652     }
653
654   if (!interior_focus)
655     {
656       if (ltr)
657         rect->x += focus_width + focus_pad;
658       else
659         rect->x -= focus_width + focus_pad;
660       rect->y += focus_width + focus_pad;
661     }
662
663   rect->width = rect->height = expander_size;
664 }
665
666 static void
667 gtk_expander_size_allocate (GtkWidget     *widget,
668                             GtkAllocation *allocation)
669 {
670   GtkExpander *expander;
671   GtkWidget *child;
672   GtkExpanderPrivate *priv;
673   gboolean child_visible = FALSE;
674   guint border_width;
675   gint expander_size;
676   gint expander_spacing;
677   gboolean interior_focus;
678   gint focus_width;
679   gint focus_pad;
680   gint label_height, top_min_height;
681   gint label_xpad, label_xoffset;
682   gint child_ypad, child_yoffset;
683
684   expander = GTK_EXPANDER (widget);
685   child    = gtk_bin_get_child (GTK_BIN (widget));
686   priv     = expander->priv;
687
688   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
689
690   gtk_widget_set_allocation (widget, allocation);
691
692   gtk_widget_style_get (widget,
693                         "interior-focus", &interior_focus,
694                         "focus-line-width", &focus_width,
695                         "focus-padding", &focus_pad,
696                         "expander-size", &expander_size,
697                         "expander-spacing", &expander_spacing,
698                         NULL);
699
700
701   /* Calculate some offsets/padding first */
702   label_xoffset = border_width + expander_size + focus_width + 2 * expander_spacing + focus_pad;
703   label_xpad = 2 * border_width + expander_size + 2 * focus_width + 2 * expander_spacing + 2 * focus_pad;
704
705   child_yoffset  = border_width + priv->spacing + (interior_focus ? 0 : 2 * focus_width + 2 * focus_pad);
706   child_ypad     = 2 * border_width + priv->spacing + (interior_focus ? 0 : 2 * focus_width + 2 * focus_pad);
707   top_min_height = 2 * expander_spacing + expander_size;
708
709   child_visible = (child && gtk_widget_get_child_visible (child));
710
711   if (priv->label_widget && gtk_widget_get_visible (priv->label_widget))
712     {
713       GtkAllocation label_allocation;
714       gint          natural_label_width;
715       gboolean ltr;
716
717       gtk_widget_get_preferred_width (priv->label_widget, NULL, &natural_label_width);
718
719       if (priv->label_fill)
720         label_allocation.width = allocation->width - label_xpad;
721       else
722         label_allocation.width = MIN (natural_label_width, allocation->width - label_xpad);
723       label_allocation.width = MAX (label_allocation.width, 1);
724
725       /* We distribute the minimum height to the label widget and prioritize
726        * the child widget giving it the remaining height
727        */
728       gtk_widget_get_preferred_height_for_width (priv->label_widget,
729                                                  label_allocation.width, &label_height, NULL);
730
731       ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
732
733       if (priv->label_fill)
734         label_allocation.x = allocation->x + label_xoffset;
735       else if (ltr)
736         label_allocation.x = allocation->x + label_xoffset;
737       else
738         label_allocation.x = allocation->x + allocation->width -
739                              (label_allocation.width + label_xoffset);
740
741       label_allocation.y = allocation->y + border_width + focus_width + focus_pad;
742       label_allocation.height = MIN (label_height,
743                                      allocation->height - 2 * border_width -
744                                      2 * focus_width - 2 * focus_pad -
745                                      (child_visible ? priv->spacing : 0));
746       label_allocation.height = MAX (label_allocation.height, 1);
747
748       gtk_widget_size_allocate (priv->label_widget, &label_allocation);
749
750       label_height = label_allocation.height;
751     }
752   else
753     {
754       label_height = 0;
755     }
756
757   if (gtk_widget_get_realized (widget))
758     {
759       GdkRectangle rect;
760
761       get_expander_bounds (expander, &rect);
762
763       gdk_window_move_resize (priv->event_window,
764                               allocation->x + border_width,
765                               allocation->y + border_width,
766                               MAX (allocation->width - 2 * border_width, 1),
767                               MAX (rect.height, label_height - 2 * border_width));
768     }
769
770   if (child_visible)
771     {
772       GtkAllocation child_allocation;
773       gint top_height;
774
775       top_height = MAX (top_min_height,
776                         label_height + (interior_focus ? 2 * focus_width + 2 * focus_pad : 0));
777
778       child_allocation.x = allocation->x + border_width;
779       child_allocation.y = allocation->y + top_height + child_yoffset;
780
781       child_allocation.width = MAX (allocation->width - 2 * border_width, 1);
782       child_allocation.height = allocation->height - top_height - child_ypad;
783       child_allocation.height = MAX (child_allocation.height, 1);
784
785       gtk_widget_size_allocate (child, &child_allocation);
786     }
787 }
788
789 static void
790 gtk_expander_map (GtkWidget *widget)
791 {
792   GtkExpanderPrivate *priv = GTK_EXPANDER (widget)->priv;
793
794   if (priv->label_widget)
795     gtk_widget_map (priv->label_widget);
796
797   GTK_WIDGET_CLASS (gtk_expander_parent_class)->map (widget);
798
799   if (priv->event_window)
800     gdk_window_show (priv->event_window);
801 }
802
803 static void
804 gtk_expander_unmap (GtkWidget *widget)
805 {
806   GtkExpanderPrivate *priv = GTK_EXPANDER (widget)->priv;
807
808   if (priv->event_window)
809     gdk_window_hide (priv->event_window);
810
811   GTK_WIDGET_CLASS (gtk_expander_parent_class)->unmap (widget);
812
813   if (priv->label_widget)
814     gtk_widget_unmap (priv->label_widget);
815 }
816
817 static void
818 gtk_expander_paint_prelight (GtkExpander *expander,
819                              cairo_t     *cr)
820 {
821   GtkAllocation allocation;
822   GtkWidget *widget;
823   GtkContainer *container;
824   GtkExpanderPrivate *priv;
825   GdkRectangle area;
826   GtkStyleContext *context;
827   gboolean interior_focus;
828   int focus_width;
829   int focus_pad;
830   int expander_size;
831   int expander_spacing;
832   guint border_width;
833
834   priv = expander->priv;
835   widget = GTK_WIDGET (expander);
836   container = GTK_CONTAINER (expander);
837
838   gtk_widget_style_get (widget,
839                         "interior-focus", &interior_focus,
840                         "focus-line-width", &focus_width,
841                         "focus-padding", &focus_pad,
842                         "expander-size", &expander_size,
843                         "expander-spacing", &expander_spacing,
844                         NULL);
845
846   gtk_widget_get_allocation (widget, &allocation);
847
848   border_width = gtk_container_get_border_width (container);
849   area.x = border_width;
850   area.y = border_width;
851   area.width = allocation.width - (2 * border_width);
852
853   if (priv->label_widget && gtk_widget_get_visible (priv->label_widget))
854     {
855       GtkAllocation label_widget_allocation;
856
857       gtk_widget_get_allocation (priv->label_widget, &label_widget_allocation);
858       area.height = label_widget_allocation.height;
859     }
860   else
861     area.height = 0;
862
863   area.height += interior_focus ? (focus_width + focus_pad) * 2 : 0;
864   area.height = MAX (area.height, expander_size + 2 * expander_spacing);
865   area.height += !interior_focus ? (focus_width + focus_pad) * 2 : 0;
866
867   context = gtk_widget_get_style_context (widget);
868   gtk_render_background (context, cr,
869                          area.x, area.y,
870                          area.width, area.height);
871 }
872
873 static void
874 gtk_expander_paint (GtkExpander *expander,
875                     cairo_t     *cr)
876 {
877   GtkExpanderPrivate *priv = expander->priv;
878   GtkWidget *widget;
879   GdkRectangle clip;
880   GtkAllocation allocation;
881   GtkStyleContext *context;
882   GtkStateFlags state = 0;
883   gint size;
884
885   widget = GTK_WIDGET (expander);
886   context = gtk_widget_get_style_context (widget);
887
888   get_expander_bounds (expander, &clip);
889   gtk_widget_get_allocation (widget, &allocation);
890
891   gtk_style_context_save (context);
892
893   if (expander->priv->prelight)
894     {
895       state = GTK_STATE_FLAG_PRELIGHT;
896       gtk_style_context_set_state (context, state);
897       gtk_expander_paint_prelight (expander, cr);
898     }
899
900   gtk_widget_style_get (widget, "expander-size", &size, NULL);
901
902   state = gtk_style_context_get_state (context);
903
904   /* Set active flag as per the expanded state */
905   if (priv->expanded)
906     state |= GTK_STATE_FLAG_ACTIVE;
907
908   gtk_style_context_set_state (context, state);
909   gtk_style_context_add_class (context, GTK_STYLE_CLASS_EXPANDER);
910
911   /* The expander is the only animatable region */
912   gtk_style_context_push_animatable_region (context, GUINT_TO_POINTER (1));
913
914   gtk_render_expander (context, cr,
915                        clip.x - allocation.x,
916                        clip.y - allocation.y,
917                        size, size);
918
919   gtk_style_context_pop_animatable_region (context);
920   gtk_style_context_restore (context);
921 }
922
923 static void
924 gtk_expander_paint_focus (GtkExpander *expander,
925                           cairo_t     *cr)
926 {
927   GtkWidget *widget;
928   GtkExpanderPrivate *priv;
929   GdkRectangle rect;
930   GtkStyleContext *context;
931   gint x, y, width, height;
932   gboolean interior_focus;
933   gint border_width;
934   gint focus_width;
935   gint focus_pad;
936   gint expander_size;
937   gint expander_spacing;
938   gboolean ltr;
939   GtkAllocation allocation;
940
941   widget = GTK_WIDGET (expander);
942   priv = expander->priv;
943
944   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
945   gtk_widget_get_allocation (widget, &allocation);
946
947   gtk_widget_style_get (widget,
948                         "interior-focus", &interior_focus,
949                         "focus-line-width", &focus_width,
950                         "focus-padding", &focus_pad,
951                         "expander-size", &expander_size,
952                         "expander-spacing", &expander_spacing,
953                         NULL);
954
955   ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
956
957   width = height = 0;
958
959   if (priv->label_widget)
960     {
961       if (gtk_widget_get_visible (priv->label_widget))
962         {
963           GtkAllocation label_allocation;
964
965           gtk_widget_get_allocation (priv->label_widget, &label_allocation);
966           width  = label_allocation.width;
967           height = label_allocation.height;
968         }
969
970       width  += 2 * focus_pad + 2 * focus_width;
971       height += 2 * focus_pad + 2 * focus_width;
972
973       x = border_width;
974       y = border_width;
975
976       if (ltr)
977         {
978           if (interior_focus)
979             x += expander_spacing * 2 + expander_size;
980         }
981       else
982         {
983           x += allocation.width - 2 * border_width
984             - expander_spacing * 2 - expander_size - width;
985         }
986
987       if (!interior_focus)
988         {
989           width += expander_size + 2 * expander_spacing;
990           height = MAX (height, expander_size + 2 * expander_spacing);
991         }
992     }
993   else
994     {
995       get_expander_bounds (expander, &rect);
996
997       x = rect.x - allocation.x - focus_pad;
998       y = rect.y - allocation.y - focus_pad;
999       width = rect.width + 2 * focus_pad;
1000       height = rect.height + 2 * focus_pad;
1001     }
1002
1003   context = gtk_widget_get_style_context (widget);
1004   gtk_render_focus (context, cr,
1005                     x, y, width, height);
1006 }
1007
1008 static gboolean
1009 gtk_expander_draw (GtkWidget *widget,
1010                    cairo_t   *cr)
1011 {
1012   GtkExpander *expander = GTK_EXPANDER (widget);
1013
1014   gtk_expander_paint (expander, cr);
1015
1016   if (gtk_widget_has_focus (widget))
1017     gtk_expander_paint_focus (expander, cr);
1018
1019   GTK_WIDGET_CLASS (gtk_expander_parent_class)->draw (widget, cr);
1020
1021   return FALSE;
1022 }
1023
1024 static gboolean
1025 gtk_expander_button_press (GtkWidget      *widget,
1026                            GdkEventButton *event)
1027 {
1028   GtkExpander *expander = GTK_EXPANDER (widget);
1029
1030   if (event->button == 1 && event->window == expander->priv->event_window)
1031     {
1032       expander->priv->button_down = TRUE;
1033       return TRUE;
1034     }
1035
1036   return FALSE;
1037 }
1038
1039 static gboolean
1040 gtk_expander_button_release (GtkWidget      *widget,
1041                              GdkEventButton *event)
1042 {
1043   GtkExpander *expander = GTK_EXPANDER (widget);
1044
1045   if (event->button == 1 && expander->priv->button_down)
1046     {
1047       gtk_widget_activate (widget);
1048       expander->priv->button_down = FALSE;
1049       return TRUE;
1050     }
1051
1052   return FALSE;
1053 }
1054
1055 static void
1056 gtk_expander_grab_notify (GtkWidget *widget,
1057                           gboolean   was_grabbed)
1058 {
1059   if (!was_grabbed)
1060     GTK_EXPANDER (widget)->priv->button_down = FALSE;
1061 }
1062
1063 static void
1064 gtk_expander_state_flags_changed (GtkWidget    *widget,
1065                                   GtkStateFlags  previous_state)
1066 {
1067   if (!gtk_widget_is_sensitive (widget))
1068     GTK_EXPANDER (widget)->priv->button_down = FALSE;
1069 }
1070
1071 static void
1072 gtk_expander_redraw_expander (GtkExpander *expander)
1073 {
1074   GtkAllocation allocation;
1075   GtkWidget *widget = GTK_WIDGET (expander);
1076
1077   if (gtk_widget_get_realized (widget))
1078     {
1079       gtk_widget_get_allocation (widget, &allocation);
1080       gdk_window_invalidate_rect (gtk_widget_get_window (widget), &allocation, FALSE);
1081     }
1082 }
1083
1084 static gboolean
1085 gtk_expander_enter_notify (GtkWidget        *widget,
1086                            GdkEventCrossing *event)
1087 {
1088   GtkExpander *expander = GTK_EXPANDER (widget);
1089
1090   if (event->window == expander->priv->event_window &&
1091       event->detail != GDK_NOTIFY_INFERIOR)
1092     {
1093       expander->priv->prelight = TRUE;
1094
1095       if (expander->priv->label_widget)
1096         gtk_widget_set_state_flags (expander->priv->label_widget,
1097                                     GTK_STATE_FLAG_PRELIGHT,
1098                                     FALSE);
1099
1100       gtk_expander_redraw_expander (expander);
1101     }
1102
1103   return FALSE;
1104 }
1105
1106 static gboolean
1107 gtk_expander_leave_notify (GtkWidget        *widget,
1108                            GdkEventCrossing *event)
1109 {
1110   GtkExpander *expander = GTK_EXPANDER (widget);
1111
1112   if (event->window == expander->priv->event_window &&
1113       event->detail != GDK_NOTIFY_INFERIOR)
1114     {
1115       expander->priv->prelight = FALSE;
1116
1117       if (expander->priv->label_widget)
1118         gtk_widget_unset_state_flags (expander->priv->label_widget,
1119                                       GTK_STATE_FLAG_PRELIGHT);
1120
1121       gtk_expander_redraw_expander (expander);
1122     }
1123
1124   return FALSE;
1125 }
1126
1127 static gboolean
1128 expand_timeout (gpointer data)
1129 {
1130   GtkExpander *expander = GTK_EXPANDER (data);
1131   GtkExpanderPrivate *priv = expander->priv;
1132
1133   priv->expand_timer = 0;
1134   gtk_expander_set_expanded (expander, TRUE);
1135
1136   return FALSE;
1137 }
1138
1139 static gboolean
1140 gtk_expander_drag_motion (GtkWidget        *widget,
1141                           GdkDragContext   *context,
1142                           gint              x,
1143                           gint              y,
1144                           guint             time)
1145 {
1146   GtkExpander *expander = GTK_EXPANDER (widget);
1147   GtkExpanderPrivate *priv = expander->priv;
1148
1149   if (!priv->expanded && !priv->expand_timer)
1150     {
1151       GtkSettings *settings;
1152       guint timeout;
1153
1154       settings = gtk_widget_get_settings (widget);
1155       g_object_get (settings, "gtk-timeout-expand", &timeout, NULL);
1156
1157       priv->expand_timer = gdk_threads_add_timeout (timeout, (GSourceFunc) expand_timeout, expander);
1158     }
1159
1160   return TRUE;
1161 }
1162
1163 static void
1164 gtk_expander_drag_leave (GtkWidget      *widget,
1165                          GdkDragContext *context,
1166                          guint           time)
1167 {
1168   GtkExpander *expander = GTK_EXPANDER (widget);
1169   GtkExpanderPrivate *priv = expander->priv;
1170
1171   if (priv->expand_timer)
1172     {
1173       g_source_remove (priv->expand_timer);
1174       priv->expand_timer = 0;
1175     }
1176 }
1177
1178 typedef enum
1179 {
1180   FOCUS_NONE,
1181   FOCUS_WIDGET,
1182   FOCUS_LABEL,
1183   FOCUS_CHILD
1184 } FocusSite;
1185
1186 static gboolean
1187 focus_current_site (GtkExpander      *expander,
1188                     GtkDirectionType  direction)
1189 {
1190   GtkWidget *current_focus;
1191
1192   current_focus = gtk_container_get_focus_child (GTK_CONTAINER (expander));
1193
1194   if (!current_focus)
1195     return FALSE;
1196
1197   return gtk_widget_child_focus (current_focus, direction);
1198 }
1199
1200 static gboolean
1201 focus_in_site (GtkExpander      *expander,
1202                FocusSite         site,
1203                GtkDirectionType  direction)
1204 {
1205   switch (site)
1206     {
1207     case FOCUS_WIDGET:
1208       gtk_widget_grab_focus (GTK_WIDGET (expander));
1209       return TRUE;
1210     case FOCUS_LABEL:
1211       if (expander->priv->label_widget)
1212         return gtk_widget_child_focus (expander->priv->label_widget, direction);
1213       else
1214         return FALSE;
1215     case FOCUS_CHILD:
1216       {
1217         GtkWidget *child = gtk_bin_get_child (GTK_BIN (expander));
1218
1219         if (child && gtk_widget_get_child_visible (child))
1220           return gtk_widget_child_focus (child, direction);
1221         else
1222           return FALSE;
1223       }
1224     case FOCUS_NONE:
1225       break;
1226     }
1227
1228   g_assert_not_reached ();
1229   return FALSE;
1230 }
1231
1232 static FocusSite
1233 get_next_site (GtkExpander      *expander,
1234                FocusSite         site,
1235                GtkDirectionType  direction)
1236 {
1237   gboolean ltr;
1238
1239   ltr = gtk_widget_get_direction (GTK_WIDGET (expander)) != GTK_TEXT_DIR_RTL;
1240
1241   switch (site)
1242     {
1243     case FOCUS_NONE:
1244       switch (direction)
1245         {
1246         case GTK_DIR_TAB_BACKWARD:
1247         case GTK_DIR_LEFT:
1248         case GTK_DIR_UP:
1249           return FOCUS_CHILD;
1250         case GTK_DIR_TAB_FORWARD:
1251         case GTK_DIR_DOWN:
1252         case GTK_DIR_RIGHT:
1253           return FOCUS_WIDGET;
1254         }
1255     case FOCUS_WIDGET:
1256       switch (direction)
1257         {
1258         case GTK_DIR_TAB_BACKWARD:
1259         case GTK_DIR_UP:
1260           return FOCUS_NONE;
1261         case GTK_DIR_LEFT:
1262           return ltr ? FOCUS_NONE : FOCUS_LABEL;
1263         case GTK_DIR_TAB_FORWARD:
1264         case GTK_DIR_DOWN:
1265           return FOCUS_LABEL;
1266         case GTK_DIR_RIGHT:
1267           return ltr ? FOCUS_LABEL : FOCUS_NONE;
1268           break;
1269         }
1270     case FOCUS_LABEL:
1271       switch (direction)
1272         {
1273         case GTK_DIR_TAB_BACKWARD:
1274         case GTK_DIR_UP:
1275           return FOCUS_WIDGET;
1276         case GTK_DIR_LEFT:
1277           return ltr ? FOCUS_WIDGET : FOCUS_CHILD;
1278         case GTK_DIR_TAB_FORWARD:
1279         case GTK_DIR_DOWN:
1280           return FOCUS_CHILD;
1281         case GTK_DIR_RIGHT:
1282           return ltr ? FOCUS_CHILD : FOCUS_WIDGET;
1283           break;
1284         }
1285     case FOCUS_CHILD:
1286       switch (direction)
1287         {
1288         case GTK_DIR_TAB_BACKWARD:
1289         case GTK_DIR_LEFT:
1290         case GTK_DIR_UP:
1291           return FOCUS_LABEL;
1292         case GTK_DIR_TAB_FORWARD:
1293         case GTK_DIR_DOWN:
1294         case GTK_DIR_RIGHT:
1295           return FOCUS_NONE;
1296         }
1297     }
1298
1299   g_assert_not_reached ();
1300   return FOCUS_NONE;
1301 }
1302
1303 static void
1304 gtk_expander_resize_toplevel (GtkExpander *expander)
1305 {
1306   GtkExpanderPrivate *priv = expander->priv;
1307   GtkWidget *child = gtk_bin_get_child (GTK_BIN (expander));
1308
1309   if (child && priv->resize_toplevel &&
1310       gtk_widget_get_realized (GTK_WIDGET (expander)))
1311     {
1312       GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (expander));
1313
1314       if (toplevel && gtk_widget_get_realized (toplevel))
1315         {
1316           GtkAllocation toplevel_allocation;
1317
1318           gtk_widget_get_allocation (toplevel, &toplevel_allocation);
1319
1320           if (priv->expanded)
1321             {
1322               GtkRequisition child_requisition;
1323
1324               gtk_widget_get_preferred_size (child, &child_requisition, NULL);
1325
1326               toplevel_allocation.height += child_requisition.height;
1327             }
1328           else
1329             {
1330               GtkAllocation child_allocation;
1331
1332               gtk_widget_get_allocation (child, &child_allocation);
1333
1334               toplevel_allocation.height -= child_allocation.height;
1335             }
1336
1337           gtk_window_resize (GTK_WINDOW (toplevel),
1338                              toplevel_allocation.width,
1339                              toplevel_allocation.height);
1340         }
1341     }
1342 }
1343
1344 static gboolean
1345 gtk_expander_focus (GtkWidget        *widget,
1346                     GtkDirectionType  direction)
1347 {
1348   GtkExpander *expander = GTK_EXPANDER (widget);
1349
1350   if (!focus_current_site (expander, direction))
1351     {
1352       GtkWidget *old_focus_child;
1353       gboolean widget_is_focus;
1354       FocusSite site = FOCUS_NONE;
1355
1356       widget_is_focus = gtk_widget_is_focus (widget);
1357       old_focus_child = gtk_container_get_focus_child (GTK_CONTAINER (widget));
1358
1359       if (old_focus_child && old_focus_child == expander->priv->label_widget)
1360         site = FOCUS_LABEL;
1361       else if (old_focus_child)
1362         site = FOCUS_CHILD;
1363       else if (widget_is_focus)
1364         site = FOCUS_WIDGET;
1365
1366       while ((site = get_next_site (expander, site, direction)) != FOCUS_NONE)
1367         {
1368           if (focus_in_site (expander, site, direction))
1369             return TRUE;
1370         }
1371
1372       return FALSE;
1373     }
1374
1375   return TRUE;
1376 }
1377
1378 static void
1379 gtk_expander_add (GtkContainer *container,
1380                   GtkWidget    *widget)
1381 {
1382   GTK_CONTAINER_CLASS (gtk_expander_parent_class)->add (container, widget);
1383
1384   gtk_widget_set_child_visible (widget, GTK_EXPANDER (container)->priv->expanded);
1385   gtk_widget_queue_resize (GTK_WIDGET (container));
1386 }
1387
1388 static void
1389 gtk_expander_remove (GtkContainer *container,
1390                      GtkWidget    *widget)
1391 {
1392   GtkExpander *expander = GTK_EXPANDER (container);
1393
1394   if (GTK_EXPANDER (expander)->priv->label_widget == widget)
1395     gtk_expander_set_label_widget (expander, NULL);
1396   else
1397     GTK_CONTAINER_CLASS (gtk_expander_parent_class)->remove (container, widget);
1398 }
1399
1400 static void
1401 gtk_expander_forall (GtkContainer *container,
1402                      gboolean      include_internals,
1403                      GtkCallback   callback,
1404                      gpointer      callback_data)
1405 {
1406   GtkBin *bin = GTK_BIN (container);
1407   GtkExpanderPrivate *priv = GTK_EXPANDER (container)->priv;
1408   GtkWidget *child;
1409
1410   child = gtk_bin_get_child (bin);
1411   if (child)
1412     (* callback) (child, callback_data);
1413
1414   if (priv->label_widget)
1415     (* callback) (priv->label_widget, callback_data);
1416 }
1417
1418 static void
1419 gtk_expander_activate (GtkExpander *expander)
1420 {
1421   gtk_expander_set_expanded (expander, !expander->priv->expanded);
1422 }
1423
1424
1425 static void
1426 gtk_expander_get_preferred_width (GtkWidget *widget,
1427                                   gint      *minimum_size,
1428                                   gint      *natural_size)
1429 {
1430   GtkExpander *expander;
1431   GtkWidget *child;
1432   GtkExpanderPrivate *priv;
1433   gint border_width;
1434   gint expander_size;
1435   gint expander_spacing;
1436   gboolean interior_focus;
1437   gint focus_width;
1438   gint focus_pad;
1439
1440   child = gtk_bin_get_child (GTK_BIN (widget));
1441   expander = GTK_EXPANDER (widget);
1442   priv = expander->priv;
1443
1444   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
1445
1446   gtk_widget_style_get (GTK_WIDGET (widget),
1447                         "interior-focus", &interior_focus,
1448                         "focus-line-width", &focus_width,
1449                         "focus-padding", &focus_pad,
1450                         "expander-size", &expander_size,
1451                         "expander-spacing", &expander_spacing,
1452                         NULL);
1453
1454   *minimum_size = *natural_size =
1455     expander_size + 2 * expander_spacing +
1456     2 * focus_width + 2 * focus_pad;
1457
1458   if (priv->label_widget && gtk_widget_get_visible (priv->label_widget))
1459     {
1460       gint label_min, label_nat;
1461
1462       gtk_widget_get_preferred_width (priv->label_widget,
1463                                       &label_min, &label_nat);
1464
1465       *minimum_size += label_min;
1466       *natural_size += label_nat;
1467     }
1468
1469   if (child && gtk_widget_get_child_visible (child))
1470     {
1471       gint child_min, child_nat;
1472
1473       gtk_widget_get_preferred_width (child,
1474                                       &child_min, &child_nat);
1475
1476       *minimum_size = MAX (*minimum_size, child_min);
1477       *natural_size = MAX (*natural_size, child_nat);
1478
1479     }
1480
1481   *minimum_size += 2 * border_width;
1482   *natural_size += 2 * border_width;
1483 }
1484
1485 static void
1486 gtk_expander_get_preferred_height (GtkWidget *widget,
1487                                    gint      *minimum_size,
1488                                    gint      *natural_size)
1489 {
1490   GtkExpander *expander;
1491   GtkWidget *child;
1492   GtkExpanderPrivate *priv;
1493   gint border_width;
1494   gint expander_size;
1495   gint expander_spacing;
1496   gboolean interior_focus;
1497   gint focus_width;
1498   gint focus_pad;
1499
1500   child = gtk_bin_get_child (GTK_BIN (widget));
1501   expander = GTK_EXPANDER (widget);
1502   priv = expander->priv;
1503
1504   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
1505
1506   gtk_widget_style_get (GTK_WIDGET (widget),
1507                         "interior-focus", &interior_focus,
1508                         "focus-line-width", &focus_width,
1509                         "focus-padding", &focus_pad,
1510                         "expander-size", &expander_size,
1511                         "expander-spacing", &expander_spacing,
1512                         NULL);
1513
1514   *minimum_size = *natural_size =
1515     interior_focus ? (2 * focus_width + 2 * focus_pad) : 0;
1516
1517
1518   if (priv->label_widget && gtk_widget_get_visible (priv->label_widget))
1519     {
1520       gint label_min, label_nat;
1521
1522       gtk_widget_get_preferred_height (priv->label_widget,
1523                                        &label_min, &label_nat);
1524
1525       *minimum_size += label_min;
1526       *natural_size += label_nat;
1527     }
1528
1529   *minimum_size = MAX (*minimum_size, expander_size + 2 * expander_spacing);
1530   *natural_size = MAX (*natural_size, *minimum_size);
1531
1532   if (!interior_focus)
1533     {
1534       gint extra = 2 * focus_width + 2 * focus_pad;
1535       *minimum_size += extra;
1536       *natural_size += extra;
1537     }
1538
1539   if (child && gtk_widget_get_child_visible (child))
1540     {
1541       gint child_min, child_nat;
1542
1543       gtk_widget_get_preferred_height (child,
1544                                        &child_min, &child_nat);
1545
1546       *minimum_size += child_min + priv->spacing;
1547       *natural_size += child_nat + priv->spacing;
1548
1549     }
1550
1551   *minimum_size += 2 * border_width;
1552   *natural_size += 2 * border_width;
1553 }
1554
1555 static void
1556 gtk_expander_get_preferred_height_for_width (GtkWidget *widget,
1557                                              gint       width,
1558                                              gint      *minimum_height,
1559                                              gint      *natural_height)
1560 {
1561   GtkExpander *expander;
1562   GtkWidget *child;
1563   GtkExpanderPrivate *priv;
1564   gint border_width;
1565   gint expander_size;
1566   gint expander_spacing;
1567   gboolean interior_focus;
1568   gint focus_width;
1569   gint focus_pad;
1570   gint label_xpad;
1571
1572   child = gtk_bin_get_child (GTK_BIN (widget));
1573   expander = GTK_EXPANDER (widget);
1574   priv = expander->priv;
1575
1576   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
1577
1578   gtk_widget_style_get (GTK_WIDGET (widget),
1579                         "interior-focus", &interior_focus,
1580                         "focus-line-width", &focus_width,
1581                         "focus-padding", &focus_pad,
1582                         "expander-size", &expander_size,
1583                         "expander-spacing", &expander_spacing,
1584                         NULL);
1585
1586   label_xpad = 2 * border_width + expander_size + 2 * expander_spacing - 2 * focus_width + 2 * focus_pad;
1587
1588   *minimum_height = *natural_height =
1589     interior_focus ? (2 * focus_width + 2 * focus_pad) : 0;
1590
1591
1592   if (priv->label_widget && gtk_widget_get_visible (priv->label_widget))
1593     {
1594       gint label_min, label_nat;
1595
1596       gtk_widget_get_preferred_height_for_width (priv->label_widget,
1597                                                  MAX (width - label_xpad, 1),
1598                                                  &label_min, &label_nat);
1599
1600       *minimum_height += label_min;
1601       *natural_height += label_nat;
1602     }
1603
1604   *minimum_height = MAX (*minimum_height, expander_size + 2 * expander_spacing);
1605   *natural_height = MAX (*natural_height, *minimum_height);
1606
1607   if (!interior_focus)
1608     {
1609       gint extra = 2 * focus_width + 2 * focus_pad;
1610       *minimum_height += extra;
1611       *natural_height += extra;
1612     }
1613
1614   if (child && gtk_widget_get_child_visible (child))
1615     {
1616       gint child_min, child_nat;
1617
1618       gtk_widget_get_preferred_height_for_width (child,
1619                                                  MAX (width - 2 * border_width, 1),
1620                                                  &child_min, &child_nat);
1621
1622       *minimum_height += child_min + priv->spacing;
1623       *natural_height += child_nat + priv->spacing;
1624     }
1625
1626   *minimum_height += 2 * border_width;
1627   *natural_height += 2 * border_width;
1628 }
1629
1630 static void
1631 gtk_expander_get_preferred_width_for_height (GtkWidget *widget,
1632                                              gint       height,
1633                                              gint      *minimum_width,
1634                                              gint      *natural_width)
1635 {
1636   GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, minimum_width, natural_width);
1637 }
1638
1639
1640
1641 /**
1642  * gtk_expander_new:
1643  * @label: the text of the label
1644  *
1645  * Creates a new expander using @label as the text of the label.
1646  *
1647  * Return value: a new #GtkExpander widget.
1648  *
1649  * Since: 2.4
1650  */
1651 GtkWidget *
1652 gtk_expander_new (const gchar *label)
1653 {
1654   return g_object_new (GTK_TYPE_EXPANDER, "label", label, NULL);
1655 }
1656
1657 /**
1658  * gtk_expander_new_with_mnemonic:
1659  * @label: (allow-none): the text of the label with an underscore
1660  *     in front of the mnemonic character
1661  *
1662  * Creates a new expander using @label as the text of the label.
1663  * If characters in @label are preceded by an underscore, they are underlined.
1664  * If you need a literal underscore character in a label, use '__' (two
1665  * underscores). The first underlined character represents a keyboard
1666  * accelerator called a mnemonic.
1667  * Pressing Alt and that key activates the button.
1668  *
1669  * Return value: a new #GtkExpander widget.
1670  *
1671  * Since: 2.4
1672  */
1673 GtkWidget *
1674 gtk_expander_new_with_mnemonic (const gchar *label)
1675 {
1676   return g_object_new (GTK_TYPE_EXPANDER,
1677                        "label", label,
1678                        "use-underline", TRUE,
1679                        NULL);
1680 }
1681
1682 /**
1683  * gtk_expander_set_expanded:
1684  * @expander: a #GtkExpander
1685  * @expanded: whether the child widget is revealed
1686  *
1687  * Sets the state of the expander. Set to %TRUE, if you want
1688  * the child widget to be revealed, and %FALSE if you want the
1689  * child widget to be hidden.
1690  *
1691  * Since: 2.4
1692  */
1693 void
1694 gtk_expander_set_expanded (GtkExpander *expander,
1695                            gboolean     expanded)
1696 {
1697   GtkExpanderPrivate *priv;
1698   GtkWidget *child;
1699
1700   g_return_if_fail (GTK_IS_EXPANDER (expander));
1701
1702   priv = expander->priv;
1703
1704   expanded = expanded != FALSE;
1705
1706   if (priv->expanded != expanded)
1707     {
1708       GtkWidget *widget = GTK_WIDGET (expander);
1709       GtkSettings *settings = gtk_widget_get_settings (widget);
1710       GtkStyleContext *context;
1711       gboolean enable_animations;
1712
1713       context = gtk_widget_get_style_context (widget);
1714       priv->expanded = expanded;
1715
1716       g_object_get (settings, "gtk-enable-animations", &enable_animations, NULL);
1717
1718       if (enable_animations && gtk_widget_get_realized (widget))
1719         {
1720           gtk_style_context_save (context);
1721           gtk_style_context_add_class (context, GTK_STYLE_CLASS_EXPANDER);
1722
1723           gtk_style_context_notify_state_change (context,
1724                                                  gtk_widget_get_window (widget),
1725                                                  GUINT_TO_POINTER (1),
1726                                                  GTK_STATE_ACTIVE,
1727                                                  expanded);
1728           gtk_style_context_restore (context);
1729         }
1730
1731       child = gtk_bin_get_child (GTK_BIN (expander));
1732
1733       if (child)
1734         {
1735           gtk_widget_set_child_visible (child, priv->expanded);
1736           gtk_widget_queue_resize (widget);
1737           gtk_expander_resize_toplevel (expander);
1738         }
1739
1740       g_object_notify (G_OBJECT (expander), "expanded");
1741     }
1742 }
1743
1744 /**
1745  * gtk_expander_get_expanded:
1746  * @expander:a #GtkExpander
1747  *
1748  * Queries a #GtkExpander and returns its current state. Returns %TRUE
1749  * if the child widget is revealed.
1750  *
1751  * See gtk_expander_set_expanded().
1752  *
1753  * Return value: the current state of the expander
1754  *
1755  * Since: 2.4
1756  */
1757 gboolean
1758 gtk_expander_get_expanded (GtkExpander *expander)
1759 {
1760   g_return_val_if_fail (GTK_IS_EXPANDER (expander), FALSE);
1761
1762   return expander->priv->expanded;
1763 }
1764
1765 /**
1766  * gtk_expander_set_spacing:
1767  * @expander: a #GtkExpander
1768  * @spacing: distance between the expander and child in pixels
1769  *
1770  * Sets the spacing field of @expander, which is the number of
1771  * pixels to place between expander and the child.
1772  *
1773  * Since: 2.4
1774  */
1775 void
1776 gtk_expander_set_spacing (GtkExpander *expander,
1777                           gint         spacing)
1778 {
1779   g_return_if_fail (GTK_IS_EXPANDER (expander));
1780   g_return_if_fail (spacing >= 0);
1781
1782   if (expander->priv->spacing != spacing)
1783     {
1784       expander->priv->spacing = spacing;
1785
1786       gtk_widget_queue_resize (GTK_WIDGET (expander));
1787
1788       g_object_notify (G_OBJECT (expander), "spacing");
1789     }
1790 }
1791
1792 /**
1793  * gtk_expander_get_spacing:
1794  * @expander: a #GtkExpander
1795  *
1796  * Gets the value set by gtk_expander_set_spacing().
1797  *
1798  * Return value: spacing between the expander and child
1799  *
1800  * Since: 2.4
1801  */
1802 gint
1803 gtk_expander_get_spacing (GtkExpander *expander)
1804 {
1805   g_return_val_if_fail (GTK_IS_EXPANDER (expander), 0);
1806
1807   return expander->priv->spacing;
1808 }
1809
1810 /**
1811  * gtk_expander_set_label:
1812  * @expander: a #GtkExpander
1813  * @label: (allow-none): a string
1814  *
1815  * Sets the text of the label of the expander to @label.
1816  *
1817  * This will also clear any previously set labels.
1818  *
1819  * Since: 2.4
1820  */
1821 void
1822 gtk_expander_set_label (GtkExpander *expander,
1823                         const gchar *label)
1824 {
1825   g_return_if_fail (GTK_IS_EXPANDER (expander));
1826
1827   if (!label)
1828     {
1829       gtk_expander_set_label_widget (expander, NULL);
1830     }
1831   else
1832     {
1833       GtkWidget *child;
1834
1835       child = gtk_label_new (label);
1836       gtk_label_set_use_underline (GTK_LABEL (child), expander->priv->use_underline);
1837       gtk_label_set_use_markup (GTK_LABEL (child), expander->priv->use_markup);
1838       gtk_widget_show (child);
1839
1840       gtk_expander_set_label_widget (expander, child);
1841     }
1842
1843   g_object_notify (G_OBJECT (expander), "label");
1844 }
1845
1846 /**
1847  * gtk_expander_get_label:
1848  * @expander: a #GtkExpander
1849  *
1850  * Fetches the text from a label widget including any embedded
1851  * underlines indicating mnemonics and Pango markup, as set by
1852  * gtk_expander_set_label(). If the label text has not been set the
1853  * return value will be %NULL. This will be the case if you create an
1854  * empty button with gtk_button_new() to use as a container.
1855  *
1856  * Note that this function behaved differently in versions prior to
1857  * 2.14 and used to return the label text stripped of embedded
1858  * underlines indicating mnemonics and Pango markup. This problem can
1859  * be avoided by fetching the label text directly from the label
1860  * widget.
1861  *
1862  * Return value: The text of the label widget. This string is owned
1863  *     by the widget and must not be modified or freed.
1864  *
1865  * Since: 2.4
1866  */
1867 G_CONST_RETURN char *
1868 gtk_expander_get_label (GtkExpander *expander)
1869 {
1870   GtkExpanderPrivate *priv;
1871
1872   g_return_val_if_fail (GTK_IS_EXPANDER (expander), NULL);
1873
1874   priv = expander->priv;
1875
1876   if (GTK_IS_LABEL (priv->label_widget))
1877     return gtk_label_get_label (GTK_LABEL (priv->label_widget));
1878   else
1879     return NULL;
1880 }
1881
1882 /**
1883  * gtk_expander_set_use_underline:
1884  * @expander: a #GtkExpander
1885  * @use_underline: %TRUE if underlines in the text indicate mnemonics
1886  *
1887  * If true, an underline in the text of the expander label indicates
1888  * the next character should be used for the mnemonic accelerator key.
1889  *
1890  * Since: 2.4
1891  */
1892 void
1893 gtk_expander_set_use_underline (GtkExpander *expander,
1894                                 gboolean     use_underline)
1895 {
1896   GtkExpanderPrivate *priv;
1897
1898   g_return_if_fail (GTK_IS_EXPANDER (expander));
1899
1900   priv = expander->priv;
1901
1902   use_underline = use_underline != FALSE;
1903
1904   if (priv->use_underline != use_underline)
1905     {
1906       priv->use_underline = use_underline;
1907
1908       if (GTK_IS_LABEL (priv->label_widget))
1909         gtk_label_set_use_underline (GTK_LABEL (priv->label_widget), use_underline);
1910
1911       g_object_notify (G_OBJECT (expander), "use-underline");
1912     }
1913 }
1914
1915 /**
1916  * gtk_expander_get_use_underline:
1917  * @expander: a #GtkExpander
1918  *
1919  * Returns whether an embedded underline in the expander label
1920  * indicates a mnemonic. See gtk_expander_set_use_underline().
1921  *
1922  * Return value: %TRUE if an embedded underline in the expander
1923  *     label indicates the mnemonic accelerator keys
1924  *
1925  * Since: 2.4
1926  */
1927 gboolean
1928 gtk_expander_get_use_underline (GtkExpander *expander)
1929 {
1930   g_return_val_if_fail (GTK_IS_EXPANDER (expander), FALSE);
1931
1932   return expander->priv->use_underline;
1933 }
1934
1935 /**
1936  * gtk_expander_set_use_markup:
1937  * @expander: a #GtkExpander
1938  * @use_markup: %TRUE if the label's text should be parsed for markup
1939  *
1940  * Sets whether the text of the label contains markup in <link
1941  * linkend="PangoMarkupFormat">Pango's text markup
1942  * language</link>. See gtk_label_set_markup().
1943  *
1944  * Since: 2.4
1945  */
1946 void
1947 gtk_expander_set_use_markup (GtkExpander *expander,
1948                              gboolean     use_markup)
1949 {
1950   GtkExpanderPrivate *priv;
1951
1952   g_return_if_fail (GTK_IS_EXPANDER (expander));
1953
1954   priv = expander->priv;
1955
1956   use_markup = use_markup != FALSE;
1957
1958   if (priv->use_markup != use_markup)
1959     {
1960       priv->use_markup = use_markup;
1961
1962       if (GTK_IS_LABEL (priv->label_widget))
1963         gtk_label_set_use_markup (GTK_LABEL (priv->label_widget), use_markup);
1964
1965       g_object_notify (G_OBJECT (expander), "use-markup");
1966     }
1967 }
1968
1969 /**
1970  * gtk_expander_get_use_markup:
1971  * @expander: a #GtkExpander
1972  *
1973  * Returns whether the label's text is interpreted as marked up with
1974  * the <link linkend="PangoMarkupFormat">Pango text markup
1975  * language</link>. See gtk_expander_set_use_markup().
1976  *
1977  * Return value: %TRUE if the label's text will be parsed for markup
1978  *
1979  * Since: 2.4
1980  */
1981 gboolean
1982 gtk_expander_get_use_markup (GtkExpander *expander)
1983 {
1984   g_return_val_if_fail (GTK_IS_EXPANDER (expander), FALSE);
1985
1986   return expander->priv->use_markup;
1987 }
1988
1989 /**
1990  * gtk_expander_set_label_widget:
1991  * @expander: a #GtkExpander
1992  * @label_widget: (allow-none): the new label widget
1993  *
1994  * Set the label widget for the expander. This is the widget
1995  * that will appear embedded alongside the expander arrow.
1996  *
1997  * Since: 2.4
1998  */
1999 void
2000 gtk_expander_set_label_widget (GtkExpander *expander,
2001                                GtkWidget   *label_widget)
2002 {
2003   GtkExpanderPrivate *priv;
2004   GtkWidget          *widget;
2005
2006   g_return_if_fail (GTK_IS_EXPANDER (expander));
2007   g_return_if_fail (label_widget == NULL || GTK_IS_WIDGET (label_widget));
2008   g_return_if_fail (label_widget == NULL || gtk_widget_get_parent (label_widget) == NULL);
2009
2010   priv = expander->priv;
2011
2012   if (priv->label_widget == label_widget)
2013     return;
2014
2015   if (priv->label_widget)
2016     {
2017       gtk_widget_set_state_flags (priv->label_widget, 0, TRUE);
2018       gtk_widget_unparent (priv->label_widget);
2019     }
2020
2021   priv->label_widget = label_widget;
2022   widget = GTK_WIDGET (expander);
2023
2024   if (label_widget)
2025     {
2026       priv->label_widget = label_widget;
2027
2028       gtk_widget_set_parent (label_widget, widget);
2029
2030       if (priv->prelight)
2031         gtk_widget_set_state_flags (label_widget,
2032                                     GTK_STATE_FLAG_PRELIGHT,
2033                                     FALSE);
2034     }
2035
2036   if (gtk_widget_get_visible (widget))
2037     gtk_widget_queue_resize (widget);
2038
2039   g_object_freeze_notify (G_OBJECT (expander));
2040   g_object_notify (G_OBJECT (expander), "label-widget");
2041   g_object_notify (G_OBJECT (expander), "label");
2042   g_object_thaw_notify (G_OBJECT (expander));
2043 }
2044
2045 /**
2046  * gtk_expander_get_label_widget:
2047  * @expander: a #GtkExpander
2048  *
2049  * Retrieves the label widget for the frame. See
2050  * gtk_expander_set_label_widget().
2051  *
2052  * Return value: (transfer none): the label widget,
2053  *     or %NULL if there is none
2054  *
2055  * Since: 2.4
2056  */
2057 GtkWidget *
2058 gtk_expander_get_label_widget (GtkExpander *expander)
2059 {
2060   g_return_val_if_fail (GTK_IS_EXPANDER (expander), NULL);
2061
2062   return expander->priv->label_widget;
2063 }
2064
2065 /**
2066  * gtk_expander_set_label_fill:
2067  * @expander: a #GtkExpander
2068  * @label_fill: %TRUE if the label should should fill
2069  *     all available horizontal space
2070  *
2071  * Sets whether the label widget should fill all available
2072  * horizontal space allocated to @expander.
2073  *
2074  * Since: 2.22
2075  */
2076 void
2077 gtk_expander_set_label_fill (GtkExpander *expander,
2078                              gboolean     label_fill)
2079 {
2080   GtkExpanderPrivate *priv;
2081
2082   g_return_if_fail (GTK_IS_EXPANDER (expander));
2083
2084   priv = expander->priv;
2085
2086   label_fill = label_fill != FALSE;
2087
2088   if (priv->label_fill != label_fill)
2089     {
2090       priv->label_fill = label_fill;
2091
2092       if (priv->label_widget != NULL)
2093         gtk_widget_queue_resize (GTK_WIDGET (expander));
2094
2095       g_object_notify (G_OBJECT (expander), "label-fill");
2096     }
2097 }
2098
2099 /**
2100  * gtk_expander_get_label_fill:
2101  * @expander: a #GtkExpander
2102  *
2103  * Returns whether the label widget will fill all available
2104  * horizontal space allocated to @expander.
2105  *
2106  * Return value: %TRUE if the label widget will fill all
2107  *     available horizontal space
2108  *
2109  * Since: 2.22
2110  */
2111 gboolean
2112 gtk_expander_get_label_fill (GtkExpander *expander)
2113 {
2114   g_return_val_if_fail (GTK_IS_EXPANDER (expander), FALSE);
2115
2116   return expander->priv->label_fill;
2117 }
2118
2119 /**
2120  * gtk_expander_set_resize_toplevel:
2121  * @expander: a #GtkExpander
2122  * @resize_toplevel: whether to resize the toplevel
2123  *
2124  * Sets whether the expander will resize the toplevel widget
2125  * containing the expander upon resizing and collpasing.
2126  *
2127  * Since: 3.2
2128  */
2129 void
2130 gtk_expander_set_resize_toplevel (GtkExpander *expander,
2131                                   gboolean     resize_toplevel)
2132 {
2133   g_return_if_fail (GTK_IS_EXPANDER (expander));
2134
2135   if (expander->priv->resize_toplevel != resize_toplevel)
2136     {
2137       expander->priv->resize_toplevel = resize_toplevel ? TRUE : FALSE;
2138       g_object_notify (G_OBJECT (expander), "resize-toplevel");
2139     }
2140 }
2141
2142 /**
2143  * gtk_expander_get_resize_toplevel:
2144  * @expander: a #GtkExpander
2145  *
2146  * Returns whether the expander will resize the toplevel widget
2147  * containing the expander upon resizing and collpasing.
2148  *
2149  * Return value: the "resize toplevel" setting.
2150  *
2151  * Since: 3.2
2152  */
2153 gboolean
2154 gtk_expander_get_resize_toplevel (GtkExpander *expander)
2155 {
2156   g_return_val_if_fail (GTK_IS_EXPANDER (expander), FALSE);
2157
2158   return expander->priv->resize_toplevel;
2159 }