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