]> Pileus Git - ~andy/gtk/blob - gtk/gtkexpander.c
ba1af464bdbf61c4974cac5cf003d0a2859ac7dd
[~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 toplevel"),
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   state = gtk_widget_get_state_flags (widget);
891
892   get_expander_bounds (expander, &clip);
893   gtk_widget_get_allocation (widget, &allocation);
894
895   gtk_style_context_save (context);
896
897   state &= ~(GTK_STATE_FLAG_PRELIGHT);
898   if (expander->priv->prelight)
899     {
900       state |= GTK_STATE_FLAG_PRELIGHT;
901       gtk_style_context_set_state (context, state);
902       gtk_expander_paint_prelight (expander, cr);
903     }
904
905   gtk_widget_style_get (widget, "expander-size", &size, NULL);
906
907   /* Set active flag as per the expanded state */
908   if (priv->expanded)
909     state |= GTK_STATE_FLAG_ACTIVE;
910   else
911     state &= ~(GTK_STATE_FLAG_ACTIVE);
912
913   gtk_style_context_set_state (context, state);
914   gtk_style_context_add_class (context, GTK_STYLE_CLASS_EXPANDER);
915
916   /* The expander is the only animatable region */
917   gtk_style_context_push_animatable_region (context, GUINT_TO_POINTER (1));
918
919   gtk_render_expander (context, cr,
920                        clip.x - allocation.x,
921                        clip.y - allocation.y,
922                        size, size);
923
924   gtk_style_context_pop_animatable_region (context);
925   gtk_style_context_restore (context);
926 }
927
928 static void
929 gtk_expander_paint_focus (GtkExpander *expander,
930                           cairo_t     *cr)
931 {
932   GtkWidget *widget;
933   GtkExpanderPrivate *priv;
934   GdkRectangle rect;
935   GtkStyleContext *context;
936   gint x, y, width, height;
937   gboolean interior_focus;
938   gint border_width;
939   gint focus_width;
940   gint focus_pad;
941   gint expander_size;
942   gint expander_spacing;
943   gboolean ltr;
944   GtkAllocation allocation;
945
946   widget = GTK_WIDGET (expander);
947   priv = expander->priv;
948
949   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
950   gtk_widget_get_allocation (widget, &allocation);
951
952   gtk_widget_style_get (widget,
953                         "interior-focus", &interior_focus,
954                         "focus-line-width", &focus_width,
955                         "focus-padding", &focus_pad,
956                         "expander-size", &expander_size,
957                         "expander-spacing", &expander_spacing,
958                         NULL);
959
960   ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
961
962   width = height = 0;
963
964   if (priv->label_widget)
965     {
966       if (gtk_widget_get_visible (priv->label_widget))
967         {
968           GtkAllocation label_allocation;
969
970           gtk_widget_get_allocation (priv->label_widget, &label_allocation);
971           width  = label_allocation.width;
972           height = label_allocation.height;
973         }
974
975       width  += 2 * focus_pad + 2 * focus_width;
976       height += 2 * focus_pad + 2 * focus_width;
977
978       x = border_width;
979       y = border_width;
980
981       if (ltr)
982         {
983           if (interior_focus)
984             x += expander_spacing * 2 + expander_size;
985         }
986       else
987         {
988           x += allocation.width - 2 * border_width
989             - expander_spacing * 2 - expander_size - width;
990         }
991
992       if (!interior_focus)
993         {
994           width += expander_size + 2 * expander_spacing;
995           height = MAX (height, expander_size + 2 * expander_spacing);
996         }
997     }
998   else
999     {
1000       get_expander_bounds (expander, &rect);
1001
1002       x = rect.x - allocation.x - focus_pad;
1003       y = rect.y - allocation.y - focus_pad;
1004       width = rect.width + 2 * focus_pad;
1005       height = rect.height + 2 * focus_pad;
1006     }
1007
1008   context = gtk_widget_get_style_context (widget);
1009   gtk_render_focus (context, cr,
1010                     x, y, width, height);
1011 }
1012
1013 static gboolean
1014 gtk_expander_draw (GtkWidget *widget,
1015                    cairo_t   *cr)
1016 {
1017   GtkExpander *expander = GTK_EXPANDER (widget);
1018
1019   gtk_expander_paint (expander, cr);
1020
1021   if (gtk_widget_has_visible_focus (widget))
1022     gtk_expander_paint_focus (expander, cr);
1023
1024   GTK_WIDGET_CLASS (gtk_expander_parent_class)->draw (widget, cr);
1025
1026   return FALSE;
1027 }
1028
1029 static gboolean
1030 gtk_expander_button_press (GtkWidget      *widget,
1031                            GdkEventButton *event)
1032 {
1033   GtkExpander *expander = GTK_EXPANDER (widget);
1034
1035   if (event->button == GDK_BUTTON_PRIMARY && event->window == expander->priv->event_window)
1036     {
1037       expander->priv->button_down = TRUE;
1038       return TRUE;
1039     }
1040
1041   return FALSE;
1042 }
1043
1044 static gboolean
1045 gtk_expander_button_release (GtkWidget      *widget,
1046                              GdkEventButton *event)
1047 {
1048   GtkExpander *expander = GTK_EXPANDER (widget);
1049
1050   if (event->button == GDK_BUTTON_PRIMARY && expander->priv->button_down)
1051     {
1052       gtk_widget_activate (widget);
1053       expander->priv->button_down = FALSE;
1054       return TRUE;
1055     }
1056
1057   return FALSE;
1058 }
1059
1060 static void
1061 gtk_expander_grab_notify (GtkWidget *widget,
1062                           gboolean   was_grabbed)
1063 {
1064   if (!was_grabbed)
1065     GTK_EXPANDER (widget)->priv->button_down = FALSE;
1066 }
1067
1068 static void
1069 gtk_expander_state_flags_changed (GtkWidget    *widget,
1070                                   GtkStateFlags  previous_state)
1071 {
1072   if (!gtk_widget_is_sensitive (widget))
1073     GTK_EXPANDER (widget)->priv->button_down = FALSE;
1074 }
1075
1076 static void
1077 gtk_expander_redraw_expander (GtkExpander *expander)
1078 {
1079   GtkAllocation allocation;
1080   GtkWidget *widget = GTK_WIDGET (expander);
1081
1082   if (gtk_widget_get_realized (widget))
1083     {
1084       gtk_widget_get_allocation (widget, &allocation);
1085       gdk_window_invalidate_rect (gtk_widget_get_window (widget), &allocation, FALSE);
1086     }
1087 }
1088
1089 static gboolean
1090 gtk_expander_enter_notify (GtkWidget        *widget,
1091                            GdkEventCrossing *event)
1092 {
1093   GtkExpander *expander = GTK_EXPANDER (widget);
1094
1095   if (event->window == expander->priv->event_window &&
1096       event->detail != GDK_NOTIFY_INFERIOR)
1097     {
1098       expander->priv->prelight = TRUE;
1099
1100       if (expander->priv->label_widget)
1101         gtk_widget_set_state_flags (expander->priv->label_widget,
1102                                     GTK_STATE_FLAG_PRELIGHT,
1103                                     FALSE);
1104
1105       gtk_expander_redraw_expander (expander);
1106     }
1107
1108   return FALSE;
1109 }
1110
1111 static gboolean
1112 gtk_expander_leave_notify (GtkWidget        *widget,
1113                            GdkEventCrossing *event)
1114 {
1115   GtkExpander *expander = GTK_EXPANDER (widget);
1116
1117   if (event->window == expander->priv->event_window &&
1118       event->detail != GDK_NOTIFY_INFERIOR)
1119     {
1120       expander->priv->prelight = FALSE;
1121
1122       if (expander->priv->label_widget)
1123         gtk_widget_unset_state_flags (expander->priv->label_widget,
1124                                       GTK_STATE_FLAG_PRELIGHT);
1125
1126       gtk_expander_redraw_expander (expander);
1127     }
1128
1129   return FALSE;
1130 }
1131
1132 static gboolean
1133 expand_timeout (gpointer data)
1134 {
1135   GtkExpander *expander = GTK_EXPANDER (data);
1136   GtkExpanderPrivate *priv = expander->priv;
1137
1138   priv->expand_timer = 0;
1139   gtk_expander_set_expanded (expander, TRUE);
1140
1141   return FALSE;
1142 }
1143
1144 static gboolean
1145 gtk_expander_drag_motion (GtkWidget        *widget,
1146                           GdkDragContext   *context,
1147                           gint              x,
1148                           gint              y,
1149                           guint             time)
1150 {
1151   GtkExpander *expander = GTK_EXPANDER (widget);
1152   GtkExpanderPrivate *priv = expander->priv;
1153
1154   if (!priv->expanded && !priv->expand_timer)
1155     {
1156       GtkSettings *settings;
1157       guint timeout;
1158
1159       settings = gtk_widget_get_settings (widget);
1160       g_object_get (settings, "gtk-timeout-expand", &timeout, NULL);
1161
1162       priv->expand_timer = gdk_threads_add_timeout (timeout, (GSourceFunc) expand_timeout, expander);
1163     }
1164
1165   return TRUE;
1166 }
1167
1168 static void
1169 gtk_expander_drag_leave (GtkWidget      *widget,
1170                          GdkDragContext *context,
1171                          guint           time)
1172 {
1173   GtkExpander *expander = GTK_EXPANDER (widget);
1174   GtkExpanderPrivate *priv = expander->priv;
1175
1176   if (priv->expand_timer)
1177     {
1178       g_source_remove (priv->expand_timer);
1179       priv->expand_timer = 0;
1180     }
1181 }
1182
1183 typedef enum
1184 {
1185   FOCUS_NONE,
1186   FOCUS_WIDGET,
1187   FOCUS_LABEL,
1188   FOCUS_CHILD
1189 } FocusSite;
1190
1191 static gboolean
1192 focus_current_site (GtkExpander      *expander,
1193                     GtkDirectionType  direction)
1194 {
1195   GtkWidget *current_focus;
1196
1197   current_focus = gtk_container_get_focus_child (GTK_CONTAINER (expander));
1198
1199   if (!current_focus)
1200     return FALSE;
1201
1202   return gtk_widget_child_focus (current_focus, direction);
1203 }
1204
1205 static gboolean
1206 focus_in_site (GtkExpander      *expander,
1207                FocusSite         site,
1208                GtkDirectionType  direction)
1209 {
1210   switch (site)
1211     {
1212     case FOCUS_WIDGET:
1213       gtk_widget_grab_focus (GTK_WIDGET (expander));
1214       return TRUE;
1215     case FOCUS_LABEL:
1216       if (expander->priv->label_widget)
1217         return gtk_widget_child_focus (expander->priv->label_widget, direction);
1218       else
1219         return FALSE;
1220     case FOCUS_CHILD:
1221       {
1222         GtkWidget *child = gtk_bin_get_child (GTK_BIN (expander));
1223
1224         if (child && gtk_widget_get_child_visible (child))
1225           return gtk_widget_child_focus (child, direction);
1226         else
1227           return FALSE;
1228       }
1229     case FOCUS_NONE:
1230       break;
1231     }
1232
1233   g_assert_not_reached ();
1234   return FALSE;
1235 }
1236
1237 static FocusSite
1238 get_next_site (GtkExpander      *expander,
1239                FocusSite         site,
1240                GtkDirectionType  direction)
1241 {
1242   gboolean ltr;
1243
1244   ltr = gtk_widget_get_direction (GTK_WIDGET (expander)) != GTK_TEXT_DIR_RTL;
1245
1246   switch (site)
1247     {
1248     case FOCUS_NONE:
1249       switch (direction)
1250         {
1251         case GTK_DIR_TAB_BACKWARD:
1252         case GTK_DIR_LEFT:
1253         case GTK_DIR_UP:
1254           return FOCUS_CHILD;
1255         case GTK_DIR_TAB_FORWARD:
1256         case GTK_DIR_DOWN:
1257         case GTK_DIR_RIGHT:
1258           return FOCUS_WIDGET;
1259         }
1260     case FOCUS_WIDGET:
1261       switch (direction)
1262         {
1263         case GTK_DIR_TAB_BACKWARD:
1264         case GTK_DIR_UP:
1265           return FOCUS_NONE;
1266         case GTK_DIR_LEFT:
1267           return ltr ? FOCUS_NONE : FOCUS_LABEL;
1268         case GTK_DIR_TAB_FORWARD:
1269         case GTK_DIR_DOWN:
1270           return FOCUS_LABEL;
1271         case GTK_DIR_RIGHT:
1272           return ltr ? FOCUS_LABEL : FOCUS_NONE;
1273           break;
1274         }
1275     case FOCUS_LABEL:
1276       switch (direction)
1277         {
1278         case GTK_DIR_TAB_BACKWARD:
1279         case GTK_DIR_UP:
1280           return FOCUS_WIDGET;
1281         case GTK_DIR_LEFT:
1282           return ltr ? FOCUS_WIDGET : FOCUS_CHILD;
1283         case GTK_DIR_TAB_FORWARD:
1284         case GTK_DIR_DOWN:
1285           return FOCUS_CHILD;
1286         case GTK_DIR_RIGHT:
1287           return ltr ? FOCUS_CHILD : FOCUS_WIDGET;
1288           break;
1289         }
1290     case FOCUS_CHILD:
1291       switch (direction)
1292         {
1293         case GTK_DIR_TAB_BACKWARD:
1294         case GTK_DIR_LEFT:
1295         case GTK_DIR_UP:
1296           return FOCUS_LABEL;
1297         case GTK_DIR_TAB_FORWARD:
1298         case GTK_DIR_DOWN:
1299         case GTK_DIR_RIGHT:
1300           return FOCUS_NONE;
1301         }
1302     }
1303
1304   g_assert_not_reached ();
1305   return FOCUS_NONE;
1306 }
1307
1308 static void
1309 gtk_expander_resize_toplevel (GtkExpander *expander)
1310 {
1311   GtkExpanderPrivate *priv = expander->priv;
1312   GtkWidget *child = gtk_bin_get_child (GTK_BIN (expander));
1313
1314   if (child && priv->resize_toplevel &&
1315       gtk_widget_get_realized (GTK_WIDGET (expander)))
1316     {
1317       GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (expander));
1318
1319       if (toplevel && gtk_widget_get_realized (toplevel))
1320         {
1321           GtkAllocation toplevel_allocation;
1322
1323           gtk_widget_get_allocation (toplevel, &toplevel_allocation);
1324
1325           if (priv->expanded)
1326             {
1327               GtkRequisition child_requisition;
1328
1329               gtk_widget_get_preferred_size (child, &child_requisition, NULL);
1330
1331               toplevel_allocation.height += child_requisition.height;
1332             }
1333           else
1334             {
1335               GtkAllocation child_allocation;
1336
1337               gtk_widget_get_allocation (child, &child_allocation);
1338
1339               toplevel_allocation.height -= child_allocation.height;
1340             }
1341
1342           gtk_window_resize (GTK_WINDOW (toplevel),
1343                              toplevel_allocation.width,
1344                              toplevel_allocation.height);
1345         }
1346     }
1347 }
1348
1349 static gboolean
1350 gtk_expander_focus (GtkWidget        *widget,
1351                     GtkDirectionType  direction)
1352 {
1353   GtkExpander *expander = GTK_EXPANDER (widget);
1354
1355   if (!focus_current_site (expander, direction))
1356     {
1357       GtkWidget *old_focus_child;
1358       gboolean widget_is_focus;
1359       FocusSite site = FOCUS_NONE;
1360
1361       widget_is_focus = gtk_widget_is_focus (widget);
1362       old_focus_child = gtk_container_get_focus_child (GTK_CONTAINER (widget));
1363
1364       if (old_focus_child && old_focus_child == expander->priv->label_widget)
1365         site = FOCUS_LABEL;
1366       else if (old_focus_child)
1367         site = FOCUS_CHILD;
1368       else if (widget_is_focus)
1369         site = FOCUS_WIDGET;
1370
1371       while ((site = get_next_site (expander, site, direction)) != FOCUS_NONE)
1372         {
1373           if (focus_in_site (expander, site, direction))
1374             return TRUE;
1375         }
1376
1377       return FALSE;
1378     }
1379
1380   return TRUE;
1381 }
1382
1383 static void
1384 gtk_expander_add (GtkContainer *container,
1385                   GtkWidget    *widget)
1386 {
1387   GTK_CONTAINER_CLASS (gtk_expander_parent_class)->add (container, widget);
1388
1389   gtk_widget_set_child_visible (widget, GTK_EXPANDER (container)->priv->expanded);
1390   gtk_widget_queue_resize (GTK_WIDGET (container));
1391 }
1392
1393 static void
1394 gtk_expander_remove (GtkContainer *container,
1395                      GtkWidget    *widget)
1396 {
1397   GtkExpander *expander = GTK_EXPANDER (container);
1398
1399   if (GTK_EXPANDER (expander)->priv->label_widget == widget)
1400     gtk_expander_set_label_widget (expander, NULL);
1401   else
1402     GTK_CONTAINER_CLASS (gtk_expander_parent_class)->remove (container, widget);
1403 }
1404
1405 static void
1406 gtk_expander_forall (GtkContainer *container,
1407                      gboolean      include_internals,
1408                      GtkCallback   callback,
1409                      gpointer      callback_data)
1410 {
1411   GtkBin *bin = GTK_BIN (container);
1412   GtkExpanderPrivate *priv = GTK_EXPANDER (container)->priv;
1413   GtkWidget *child;
1414
1415   child = gtk_bin_get_child (bin);
1416   if (child)
1417     (* callback) (child, callback_data);
1418
1419   if (priv->label_widget)
1420     (* callback) (priv->label_widget, callback_data);
1421 }
1422
1423 static void
1424 gtk_expander_activate (GtkExpander *expander)
1425 {
1426   gtk_expander_set_expanded (expander, !expander->priv->expanded);
1427 }
1428
1429
1430 static void
1431 gtk_expander_get_preferred_width (GtkWidget *widget,
1432                                   gint      *minimum_size,
1433                                   gint      *natural_size)
1434 {
1435   GtkExpander *expander;
1436   GtkWidget *child;
1437   GtkExpanderPrivate *priv;
1438   gint border_width;
1439   gint expander_size;
1440   gint expander_spacing;
1441   gboolean interior_focus;
1442   gint focus_width;
1443   gint focus_pad;
1444
1445   child = gtk_bin_get_child (GTK_BIN (widget));
1446   expander = GTK_EXPANDER (widget);
1447   priv = expander->priv;
1448
1449   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
1450
1451   gtk_widget_style_get (GTK_WIDGET (widget),
1452                         "interior-focus", &interior_focus,
1453                         "focus-line-width", &focus_width,
1454                         "focus-padding", &focus_pad,
1455                         "expander-size", &expander_size,
1456                         "expander-spacing", &expander_spacing,
1457                         NULL);
1458
1459   *minimum_size = *natural_size =
1460     expander_size + 2 * expander_spacing +
1461     2 * focus_width + 2 * focus_pad;
1462
1463   if (priv->label_widget && gtk_widget_get_visible (priv->label_widget))
1464     {
1465       gint label_min, label_nat;
1466
1467       gtk_widget_get_preferred_width (priv->label_widget,
1468                                       &label_min, &label_nat);
1469
1470       *minimum_size += label_min;
1471       *natural_size += label_nat;
1472     }
1473
1474   if (child && gtk_widget_get_child_visible (child))
1475     {
1476       gint child_min, child_nat;
1477
1478       gtk_widget_get_preferred_width (child,
1479                                       &child_min, &child_nat);
1480
1481       *minimum_size = MAX (*minimum_size, child_min);
1482       *natural_size = MAX (*natural_size, child_nat);
1483
1484     }
1485
1486   *minimum_size += 2 * border_width;
1487   *natural_size += 2 * border_width;
1488 }
1489
1490 static void
1491 gtk_expander_get_preferred_height (GtkWidget *widget,
1492                                    gint      *minimum_size,
1493                                    gint      *natural_size)
1494 {
1495   GtkExpander *expander;
1496   GtkWidget *child;
1497   GtkExpanderPrivate *priv;
1498   gint border_width;
1499   gint expander_size;
1500   gint expander_spacing;
1501   gboolean interior_focus;
1502   gint focus_width;
1503   gint focus_pad;
1504
1505   child = gtk_bin_get_child (GTK_BIN (widget));
1506   expander = GTK_EXPANDER (widget);
1507   priv = expander->priv;
1508
1509   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
1510
1511   gtk_widget_style_get (GTK_WIDGET (widget),
1512                         "interior-focus", &interior_focus,
1513                         "focus-line-width", &focus_width,
1514                         "focus-padding", &focus_pad,
1515                         "expander-size", &expander_size,
1516                         "expander-spacing", &expander_spacing,
1517                         NULL);
1518
1519   *minimum_size = *natural_size =
1520     interior_focus ? (2 * focus_width + 2 * focus_pad) : 0;
1521
1522
1523   if (priv->label_widget && gtk_widget_get_visible (priv->label_widget))
1524     {
1525       gint label_min, label_nat;
1526
1527       gtk_widget_get_preferred_height (priv->label_widget,
1528                                        &label_min, &label_nat);
1529
1530       *minimum_size += label_min;
1531       *natural_size += label_nat;
1532     }
1533
1534   *minimum_size = MAX (*minimum_size, expander_size + 2 * expander_spacing);
1535   *natural_size = MAX (*natural_size, *minimum_size);
1536
1537   if (!interior_focus)
1538     {
1539       gint extra = 2 * focus_width + 2 * focus_pad;
1540       *minimum_size += extra;
1541       *natural_size += extra;
1542     }
1543
1544   if (child && gtk_widget_get_child_visible (child))
1545     {
1546       gint child_min, child_nat;
1547
1548       gtk_widget_get_preferred_height (child,
1549                                        &child_min, &child_nat);
1550
1551       *minimum_size += child_min + priv->spacing;
1552       *natural_size += child_nat + priv->spacing;
1553
1554     }
1555
1556   *minimum_size += 2 * border_width;
1557   *natural_size += 2 * border_width;
1558 }
1559
1560 static void
1561 gtk_expander_get_preferred_height_for_width (GtkWidget *widget,
1562                                              gint       width,
1563                                              gint      *minimum_height,
1564                                              gint      *natural_height)
1565 {
1566   GtkExpander *expander;
1567   GtkWidget *child;
1568   GtkExpanderPrivate *priv;
1569   gint border_width;
1570   gint expander_size;
1571   gint expander_spacing;
1572   gboolean interior_focus;
1573   gint focus_width;
1574   gint focus_pad;
1575   gint label_xpad;
1576
1577   child = gtk_bin_get_child (GTK_BIN (widget));
1578   expander = GTK_EXPANDER (widget);
1579   priv = expander->priv;
1580
1581   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
1582
1583   gtk_widget_style_get (GTK_WIDGET (widget),
1584                         "interior-focus", &interior_focus,
1585                         "focus-line-width", &focus_width,
1586                         "focus-padding", &focus_pad,
1587                         "expander-size", &expander_size,
1588                         "expander-spacing", &expander_spacing,
1589                         NULL);
1590
1591   label_xpad = 2 * border_width + expander_size + 2 * expander_spacing - 2 * focus_width + 2 * focus_pad;
1592
1593   *minimum_height = *natural_height =
1594     interior_focus ? (2 * focus_width + 2 * focus_pad) : 0;
1595
1596
1597   if (priv->label_widget && gtk_widget_get_visible (priv->label_widget))
1598     {
1599       gint label_min, label_nat;
1600
1601       gtk_widget_get_preferred_height_for_width (priv->label_widget,
1602                                                  MAX (width - label_xpad, 1),
1603                                                  &label_min, &label_nat);
1604
1605       *minimum_height += label_min;
1606       *natural_height += label_nat;
1607     }
1608
1609   *minimum_height = MAX (*minimum_height, expander_size + 2 * expander_spacing);
1610   *natural_height = MAX (*natural_height, *minimum_height);
1611
1612   if (!interior_focus)
1613     {
1614       gint extra = 2 * focus_width + 2 * focus_pad;
1615       *minimum_height += extra;
1616       *natural_height += extra;
1617     }
1618
1619   if (child && gtk_widget_get_child_visible (child))
1620     {
1621       gint child_min, child_nat;
1622
1623       gtk_widget_get_preferred_height_for_width (child,
1624                                                  MAX (width - 2 * border_width, 1),
1625                                                  &child_min, &child_nat);
1626
1627       *minimum_height += child_min + priv->spacing;
1628       *natural_height += child_nat + priv->spacing;
1629     }
1630
1631   *minimum_height += 2 * border_width;
1632   *natural_height += 2 * border_width;
1633 }
1634
1635 static void
1636 gtk_expander_get_preferred_width_for_height (GtkWidget *widget,
1637                                              gint       height,
1638                                              gint      *minimum_width,
1639                                              gint      *natural_width)
1640 {
1641   GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, minimum_width, natural_width);
1642 }
1643
1644
1645
1646 /**
1647  * gtk_expander_new:
1648  * @label: the text of the label
1649  *
1650  * Creates a new expander using @label as the text of the label.
1651  *
1652  * Return value: a new #GtkExpander widget.
1653  *
1654  * Since: 2.4
1655  */
1656 GtkWidget *
1657 gtk_expander_new (const gchar *label)
1658 {
1659   return g_object_new (GTK_TYPE_EXPANDER, "label", label, NULL);
1660 }
1661
1662 /**
1663  * gtk_expander_new_with_mnemonic:
1664  * @label: (allow-none): the text of the label with an underscore
1665  *     in front of the mnemonic character
1666  *
1667  * Creates a new expander using @label as the text of the label.
1668  * If characters in @label are preceded by an underscore, they are underlined.
1669  * If you need a literal underscore character in a label, use '__' (two
1670  * underscores). The first underlined character represents a keyboard
1671  * accelerator called a mnemonic.
1672  * Pressing Alt and that key activates the button.
1673  *
1674  * Return value: a new #GtkExpander widget.
1675  *
1676  * Since: 2.4
1677  */
1678 GtkWidget *
1679 gtk_expander_new_with_mnemonic (const gchar *label)
1680 {
1681   return g_object_new (GTK_TYPE_EXPANDER,
1682                        "label", label,
1683                        "use-underline", TRUE,
1684                        NULL);
1685 }
1686
1687 /**
1688  * gtk_expander_set_expanded:
1689  * @expander: a #GtkExpander
1690  * @expanded: whether the child widget is revealed
1691  *
1692  * Sets the state of the expander. Set to %TRUE, if you want
1693  * the child widget to be revealed, and %FALSE if you want the
1694  * child widget to be hidden.
1695  *
1696  * Since: 2.4
1697  */
1698 void
1699 gtk_expander_set_expanded (GtkExpander *expander,
1700                            gboolean     expanded)
1701 {
1702   GtkExpanderPrivate *priv;
1703   GtkWidget *child;
1704
1705   g_return_if_fail (GTK_IS_EXPANDER (expander));
1706
1707   priv = expander->priv;
1708
1709   expanded = expanded != FALSE;
1710
1711   if (priv->expanded != expanded)
1712     {
1713       GtkWidget *widget = GTK_WIDGET (expander);
1714       GtkSettings *settings = gtk_widget_get_settings (widget);
1715       GtkStyleContext *context;
1716       gboolean enable_animations;
1717
1718       context = gtk_widget_get_style_context (widget);
1719       priv->expanded = expanded;
1720
1721       g_object_get (settings, "gtk-enable-animations", &enable_animations, NULL);
1722
1723       if (enable_animations && gtk_widget_get_realized (widget))
1724         {
1725           gtk_style_context_save (context);
1726           gtk_style_context_add_class (context, GTK_STYLE_CLASS_EXPANDER);
1727
1728           gtk_style_context_notify_state_change (context,
1729                                                  gtk_widget_get_window (widget),
1730                                                  GUINT_TO_POINTER (1),
1731                                                  GTK_STATE_ACTIVE,
1732                                                  expanded);
1733           gtk_style_context_restore (context);
1734         }
1735
1736       child = gtk_bin_get_child (GTK_BIN (expander));
1737
1738       if (child)
1739         {
1740           gtk_widget_set_child_visible (child, priv->expanded);
1741           gtk_widget_queue_resize (widget);
1742           gtk_expander_resize_toplevel (expander);
1743         }
1744
1745       g_object_notify (G_OBJECT (expander), "expanded");
1746     }
1747 }
1748
1749 /**
1750  * gtk_expander_get_expanded:
1751  * @expander:a #GtkExpander
1752  *
1753  * Queries a #GtkExpander and returns its current state. Returns %TRUE
1754  * if the child widget is revealed.
1755  *
1756  * See gtk_expander_set_expanded().
1757  *
1758  * Return value: the current state of the expander
1759  *
1760  * Since: 2.4
1761  */
1762 gboolean
1763 gtk_expander_get_expanded (GtkExpander *expander)
1764 {
1765   g_return_val_if_fail (GTK_IS_EXPANDER (expander), FALSE);
1766
1767   return expander->priv->expanded;
1768 }
1769
1770 /**
1771  * gtk_expander_set_spacing:
1772  * @expander: a #GtkExpander
1773  * @spacing: distance between the expander and child in pixels
1774  *
1775  * Sets the spacing field of @expander, which is the number of
1776  * pixels to place between expander and the child.
1777  *
1778  * Since: 2.4
1779  */
1780 void
1781 gtk_expander_set_spacing (GtkExpander *expander,
1782                           gint         spacing)
1783 {
1784   g_return_if_fail (GTK_IS_EXPANDER (expander));
1785   g_return_if_fail (spacing >= 0);
1786
1787   if (expander->priv->spacing != spacing)
1788     {
1789       expander->priv->spacing = spacing;
1790
1791       gtk_widget_queue_resize (GTK_WIDGET (expander));
1792
1793       g_object_notify (G_OBJECT (expander), "spacing");
1794     }
1795 }
1796
1797 /**
1798  * gtk_expander_get_spacing:
1799  * @expander: a #GtkExpander
1800  *
1801  * Gets the value set by gtk_expander_set_spacing().
1802  *
1803  * Return value: spacing between the expander and child
1804  *
1805  * Since: 2.4
1806  */
1807 gint
1808 gtk_expander_get_spacing (GtkExpander *expander)
1809 {
1810   g_return_val_if_fail (GTK_IS_EXPANDER (expander), 0);
1811
1812   return expander->priv->spacing;
1813 }
1814
1815 /**
1816  * gtk_expander_set_label:
1817  * @expander: a #GtkExpander
1818  * @label: (allow-none): a string
1819  *
1820  * Sets the text of the label of the expander to @label.
1821  *
1822  * This will also clear any previously set labels.
1823  *
1824  * Since: 2.4
1825  */
1826 void
1827 gtk_expander_set_label (GtkExpander *expander,
1828                         const gchar *label)
1829 {
1830   g_return_if_fail (GTK_IS_EXPANDER (expander));
1831
1832   if (!label)
1833     {
1834       gtk_expander_set_label_widget (expander, NULL);
1835     }
1836   else
1837     {
1838       GtkWidget *child;
1839
1840       child = gtk_label_new (label);
1841       gtk_label_set_use_underline (GTK_LABEL (child), expander->priv->use_underline);
1842       gtk_label_set_use_markup (GTK_LABEL (child), expander->priv->use_markup);
1843       gtk_widget_show (child);
1844
1845       gtk_expander_set_label_widget (expander, child);
1846     }
1847
1848   g_object_notify (G_OBJECT (expander), "label");
1849 }
1850
1851 /**
1852  * gtk_expander_get_label:
1853  * @expander: a #GtkExpander
1854  *
1855  * Fetches the text from a label widget including any embedded
1856  * underlines indicating mnemonics and Pango markup, as set by
1857  * gtk_expander_set_label(). If the label text has not been set the
1858  * return value will be %NULL. This will be the case if you create an
1859  * empty button with gtk_button_new() to use as a container.
1860  *
1861  * Note that this function behaved differently in versions prior to
1862  * 2.14 and used to return the label text stripped of embedded
1863  * underlines indicating mnemonics and Pango markup. This problem can
1864  * be avoided by fetching the label text directly from the label
1865  * widget.
1866  *
1867  * Return value: The text of the label widget. This string is owned
1868  *     by the widget and must not be modified or freed.
1869  *
1870  * Since: 2.4
1871  */
1872 const char *
1873 gtk_expander_get_label (GtkExpander *expander)
1874 {
1875   GtkExpanderPrivate *priv;
1876
1877   g_return_val_if_fail (GTK_IS_EXPANDER (expander), NULL);
1878
1879   priv = expander->priv;
1880
1881   if (GTK_IS_LABEL (priv->label_widget))
1882     return gtk_label_get_label (GTK_LABEL (priv->label_widget));
1883   else
1884     return NULL;
1885 }
1886
1887 /**
1888  * gtk_expander_set_use_underline:
1889  * @expander: a #GtkExpander
1890  * @use_underline: %TRUE if underlines in the text indicate mnemonics
1891  *
1892  * If true, an underline in the text of the expander label indicates
1893  * the next character should be used for the mnemonic accelerator key.
1894  *
1895  * Since: 2.4
1896  */
1897 void
1898 gtk_expander_set_use_underline (GtkExpander *expander,
1899                                 gboolean     use_underline)
1900 {
1901   GtkExpanderPrivate *priv;
1902
1903   g_return_if_fail (GTK_IS_EXPANDER (expander));
1904
1905   priv = expander->priv;
1906
1907   use_underline = use_underline != FALSE;
1908
1909   if (priv->use_underline != use_underline)
1910     {
1911       priv->use_underline = use_underline;
1912
1913       if (GTK_IS_LABEL (priv->label_widget))
1914         gtk_label_set_use_underline (GTK_LABEL (priv->label_widget), use_underline);
1915
1916       g_object_notify (G_OBJECT (expander), "use-underline");
1917     }
1918 }
1919
1920 /**
1921  * gtk_expander_get_use_underline:
1922  * @expander: a #GtkExpander
1923  *
1924  * Returns whether an embedded underline in the expander label
1925  * indicates a mnemonic. See gtk_expander_set_use_underline().
1926  *
1927  * Return value: %TRUE if an embedded underline in the expander
1928  *     label indicates the mnemonic accelerator keys
1929  *
1930  * Since: 2.4
1931  */
1932 gboolean
1933 gtk_expander_get_use_underline (GtkExpander *expander)
1934 {
1935   g_return_val_if_fail (GTK_IS_EXPANDER (expander), FALSE);
1936
1937   return expander->priv->use_underline;
1938 }
1939
1940 /**
1941  * gtk_expander_set_use_markup:
1942  * @expander: a #GtkExpander
1943  * @use_markup: %TRUE if the label's text should be parsed for markup
1944  *
1945  * Sets whether the text of the label contains markup in <link
1946  * linkend="PangoMarkupFormat">Pango's text markup
1947  * language</link>. See gtk_label_set_markup().
1948  *
1949  * Since: 2.4
1950  */
1951 void
1952 gtk_expander_set_use_markup (GtkExpander *expander,
1953                              gboolean     use_markup)
1954 {
1955   GtkExpanderPrivate *priv;
1956
1957   g_return_if_fail (GTK_IS_EXPANDER (expander));
1958
1959   priv = expander->priv;
1960
1961   use_markup = use_markup != FALSE;
1962
1963   if (priv->use_markup != use_markup)
1964     {
1965       priv->use_markup = use_markup;
1966
1967       if (GTK_IS_LABEL (priv->label_widget))
1968         gtk_label_set_use_markup (GTK_LABEL (priv->label_widget), use_markup);
1969
1970       g_object_notify (G_OBJECT (expander), "use-markup");
1971     }
1972 }
1973
1974 /**
1975  * gtk_expander_get_use_markup:
1976  * @expander: a #GtkExpander
1977  *
1978  * Returns whether the label's text is interpreted as marked up with
1979  * the <link linkend="PangoMarkupFormat">Pango text markup
1980  * language</link>. See gtk_expander_set_use_markup().
1981  *
1982  * Return value: %TRUE if the label's text will be parsed for markup
1983  *
1984  * Since: 2.4
1985  */
1986 gboolean
1987 gtk_expander_get_use_markup (GtkExpander *expander)
1988 {
1989   g_return_val_if_fail (GTK_IS_EXPANDER (expander), FALSE);
1990
1991   return expander->priv->use_markup;
1992 }
1993
1994 /**
1995  * gtk_expander_set_label_widget:
1996  * @expander: a #GtkExpander
1997  * @label_widget: (allow-none): the new label widget
1998  *
1999  * Set the label widget for the expander. This is the widget
2000  * that will appear embedded alongside the expander arrow.
2001  *
2002  * Since: 2.4
2003  */
2004 void
2005 gtk_expander_set_label_widget (GtkExpander *expander,
2006                                GtkWidget   *label_widget)
2007 {
2008   GtkExpanderPrivate *priv;
2009   GtkWidget          *widget;
2010
2011   g_return_if_fail (GTK_IS_EXPANDER (expander));
2012   g_return_if_fail (label_widget == NULL || GTK_IS_WIDGET (label_widget));
2013   g_return_if_fail (label_widget == NULL || gtk_widget_get_parent (label_widget) == NULL);
2014
2015   priv = expander->priv;
2016
2017   if (priv->label_widget == label_widget)
2018     return;
2019
2020   if (priv->label_widget)
2021     {
2022       gtk_widget_set_state_flags (priv->label_widget, 0, TRUE);
2023       gtk_widget_unparent (priv->label_widget);
2024     }
2025
2026   priv->label_widget = label_widget;
2027   widget = GTK_WIDGET (expander);
2028
2029   if (label_widget)
2030     {
2031       priv->label_widget = label_widget;
2032
2033       gtk_widget_set_parent (label_widget, widget);
2034
2035       if (priv->prelight)
2036         gtk_widget_set_state_flags (label_widget,
2037                                     GTK_STATE_FLAG_PRELIGHT,
2038                                     FALSE);
2039     }
2040
2041   if (gtk_widget_get_visible (widget))
2042     gtk_widget_queue_resize (widget);
2043
2044   g_object_freeze_notify (G_OBJECT (expander));
2045   g_object_notify (G_OBJECT (expander), "label-widget");
2046   g_object_notify (G_OBJECT (expander), "label");
2047   g_object_thaw_notify (G_OBJECT (expander));
2048 }
2049
2050 /**
2051  * gtk_expander_get_label_widget:
2052  * @expander: a #GtkExpander
2053  *
2054  * Retrieves the label widget for the frame. See
2055  * gtk_expander_set_label_widget().
2056  *
2057  * Return value: (transfer none): the label widget,
2058  *     or %NULL if there is none
2059  *
2060  * Since: 2.4
2061  */
2062 GtkWidget *
2063 gtk_expander_get_label_widget (GtkExpander *expander)
2064 {
2065   g_return_val_if_fail (GTK_IS_EXPANDER (expander), NULL);
2066
2067   return expander->priv->label_widget;
2068 }
2069
2070 /**
2071  * gtk_expander_set_label_fill:
2072  * @expander: a #GtkExpander
2073  * @label_fill: %TRUE if the label should should fill
2074  *     all available horizontal space
2075  *
2076  * Sets whether the label widget should fill all available
2077  * horizontal space allocated to @expander.
2078  *
2079  * Since: 2.22
2080  */
2081 void
2082 gtk_expander_set_label_fill (GtkExpander *expander,
2083                              gboolean     label_fill)
2084 {
2085   GtkExpanderPrivate *priv;
2086
2087   g_return_if_fail (GTK_IS_EXPANDER (expander));
2088
2089   priv = expander->priv;
2090
2091   label_fill = label_fill != FALSE;
2092
2093   if (priv->label_fill != label_fill)
2094     {
2095       priv->label_fill = label_fill;
2096
2097       if (priv->label_widget != NULL)
2098         gtk_widget_queue_resize (GTK_WIDGET (expander));
2099
2100       g_object_notify (G_OBJECT (expander), "label-fill");
2101     }
2102 }
2103
2104 /**
2105  * gtk_expander_get_label_fill:
2106  * @expander: a #GtkExpander
2107  *
2108  * Returns whether the label widget will fill all available
2109  * horizontal space allocated to @expander.
2110  *
2111  * Return value: %TRUE if the label widget will fill all
2112  *     available horizontal space
2113  *
2114  * Since: 2.22
2115  */
2116 gboolean
2117 gtk_expander_get_label_fill (GtkExpander *expander)
2118 {
2119   g_return_val_if_fail (GTK_IS_EXPANDER (expander), FALSE);
2120
2121   return expander->priv->label_fill;
2122 }
2123
2124 /**
2125  * gtk_expander_set_resize_toplevel:
2126  * @expander: a #GtkExpander
2127  * @resize_toplevel: whether to resize the toplevel
2128  *
2129  * Sets whether the expander will resize the toplevel widget
2130  * containing the expander upon resizing and collpasing.
2131  *
2132  * Since: 3.2
2133  */
2134 void
2135 gtk_expander_set_resize_toplevel (GtkExpander *expander,
2136                                   gboolean     resize_toplevel)
2137 {
2138   g_return_if_fail (GTK_IS_EXPANDER (expander));
2139
2140   if (expander->priv->resize_toplevel != resize_toplevel)
2141     {
2142       expander->priv->resize_toplevel = resize_toplevel ? TRUE : FALSE;
2143       g_object_notify (G_OBJECT (expander), "resize-toplevel");
2144     }
2145 }
2146
2147 /**
2148  * gtk_expander_get_resize_toplevel:
2149  * @expander: a #GtkExpander
2150  *
2151  * Returns whether the expander will resize the toplevel widget
2152  * containing the expander upon resizing and collpasing.
2153  *
2154  * Return value: the "resize toplevel" setting.
2155  *
2156  * Since: 3.2
2157  */
2158 gboolean
2159 gtk_expander_get_resize_toplevel (GtkExpander *expander)
2160 {
2161   g_return_val_if_fail (GTK_IS_EXPANDER (expander), FALSE);
2162
2163   return expander->priv->resize_toplevel;
2164 }