]> Pileus Git - ~andy/gtk/blob - gtk/gtkexpander.c
Deprecate widget flag: GTK_WIDGET_VISIBLE
[~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 #include "config.h"
25 #include <string.h>
26 #include "gtkexpander.h"
27
28 #include "gtklabel.h"
29 #include "gtkbuildable.h"
30 #include "gtkcontainer.h"
31 #include "gtkmarshalers.h"
32 #include "gtkmain.h"
33 #include "gtkintl.h"
34 #include "gtkprivate.h"
35 #include <gdk/gdkkeysyms.h>
36 #include "gtkdnd.h"
37 #include "gtkalias.h"
38
39 #define GTK_EXPANDER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_EXPANDER, GtkExpanderPrivate))
40
41 #define DEFAULT_EXPANDER_SIZE 10
42 #define DEFAULT_EXPANDER_SPACING 2
43
44 enum
45 {
46   PROP_0,
47   PROP_EXPANDED,
48   PROP_LABEL,
49   PROP_USE_UNDERLINE,
50   PROP_USE_MARKUP,
51   PROP_SPACING,
52   PROP_LABEL_WIDGET
53 };
54
55 struct _GtkExpanderPrivate
56 {
57   GtkWidget        *label_widget;
58   GdkWindow        *event_window;
59   gint              spacing;
60
61   GtkExpanderStyle  expander_style;
62   guint             animation_timeout;
63   guint             expand_timer;
64
65   guint             expanded : 1;
66   guint             use_underline : 1;
67   guint             use_markup : 1; 
68   guint             button_down : 1;
69   guint             prelight : 1;
70 };
71
72 static void gtk_expander_set_property (GObject          *object,
73                                        guint             prop_id,
74                                        const GValue     *value,
75                                        GParamSpec       *pspec);
76 static void gtk_expander_get_property (GObject          *object,
77                                        guint             prop_id,
78                                        GValue           *value,
79                                        GParamSpec       *pspec);
80
81 static void gtk_expander_destroy (GtkObject *object);
82
83 static void     gtk_expander_realize        (GtkWidget        *widget);
84 static void     gtk_expander_unrealize      (GtkWidget        *widget);
85 static void     gtk_expander_size_request   (GtkWidget        *widget,
86                                              GtkRequisition   *requisition);
87 static void     gtk_expander_size_allocate  (GtkWidget        *widget,
88                                              GtkAllocation    *allocation);
89 static void     gtk_expander_map            (GtkWidget        *widget);
90 static void     gtk_expander_unmap          (GtkWidget        *widget);
91 static gboolean gtk_expander_expose         (GtkWidget        *widget,
92                                              GdkEventExpose   *event);
93 static gboolean gtk_expander_button_press   (GtkWidget        *widget,
94                                              GdkEventButton   *event);
95 static gboolean gtk_expander_button_release (GtkWidget        *widget,
96                                              GdkEventButton   *event);
97 static gboolean gtk_expander_enter_notify   (GtkWidget        *widget,
98                                              GdkEventCrossing *event);
99 static gboolean gtk_expander_leave_notify   (GtkWidget        *widget,
100                                              GdkEventCrossing *event);
101 static gboolean gtk_expander_focus          (GtkWidget        *widget,
102                                              GtkDirectionType  direction);
103 static void     gtk_expander_grab_notify    (GtkWidget        *widget,
104                                              gboolean          was_grabbed);
105 static void     gtk_expander_state_changed  (GtkWidget        *widget,
106                                              GtkStateType      previous_state);
107 static gboolean gtk_expander_drag_motion    (GtkWidget        *widget,
108                                              GdkDragContext   *context,
109                                              gint              x,
110                                              gint              y,
111                                              guint             time);
112 static void     gtk_expander_drag_leave     (GtkWidget        *widget,
113                                              GdkDragContext   *context,
114                                              guint             time);
115
116 static void gtk_expander_add    (GtkContainer *container,
117                                  GtkWidget    *widget);
118 static void gtk_expander_remove (GtkContainer *container,
119                                  GtkWidget    *widget);
120 static void gtk_expander_forall (GtkContainer *container,
121                                  gboolean        include_internals,
122                                  GtkCallback     callback,
123                                  gpointer        callback_data);
124
125 static void gtk_expander_activate (GtkExpander *expander);
126
127 static void get_expander_bounds (GtkExpander  *expander,
128                                  GdkRectangle *rect);
129
130 /* GtkBuildable */
131 static void gtk_expander_buildable_init           (GtkBuildableIface *iface);
132 static void gtk_expander_buildable_add_child      (GtkBuildable *buildable,
133                                                    GtkBuilder   *builder,
134                                                    GObject      *child,
135                                                    const gchar  *type);
136
137 G_DEFINE_TYPE_WITH_CODE (GtkExpander, gtk_expander, GTK_TYPE_BIN,
138                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
139                                                 gtk_expander_buildable_init))
140
141 static void
142 gtk_expander_class_init (GtkExpanderClass *klass)
143 {
144   GObjectClass *gobject_class;
145   GtkObjectClass *object_class;
146   GtkWidgetClass *widget_class;
147   GtkContainerClass *container_class;
148
149   gobject_class   = (GObjectClass *) klass;
150   object_class    = (GtkObjectClass *) klass;
151   widget_class    = (GtkWidgetClass *) klass;
152   container_class = (GtkContainerClass *) klass;
153
154   gobject_class->set_property = gtk_expander_set_property;
155   gobject_class->get_property = gtk_expander_get_property;
156
157   object_class->destroy = gtk_expander_destroy;
158
159   widget_class->realize              = gtk_expander_realize;
160   widget_class->unrealize            = gtk_expander_unrealize;
161   widget_class->size_request         = gtk_expander_size_request;
162   widget_class->size_allocate        = gtk_expander_size_allocate;
163   widget_class->map                  = gtk_expander_map;
164   widget_class->unmap                = gtk_expander_unmap;
165   widget_class->expose_event         = gtk_expander_expose;
166   widget_class->button_press_event   = gtk_expander_button_press;
167   widget_class->button_release_event = gtk_expander_button_release;
168   widget_class->enter_notify_event   = gtk_expander_enter_notify;
169   widget_class->leave_notify_event   = gtk_expander_leave_notify;
170   widget_class->focus                = gtk_expander_focus;
171   widget_class->grab_notify          = gtk_expander_grab_notify;
172   widget_class->state_changed        = gtk_expander_state_changed;
173   widget_class->drag_motion          = gtk_expander_drag_motion;
174   widget_class->drag_leave           = gtk_expander_drag_leave;
175
176   container_class->add    = gtk_expander_add;
177   container_class->remove = gtk_expander_remove;
178   container_class->forall = gtk_expander_forall;
179
180   klass->activate = gtk_expander_activate;
181
182   g_type_class_add_private (klass, sizeof (GtkExpanderPrivate));
183
184   g_object_class_install_property (gobject_class,
185                                    PROP_EXPANDED,
186                                    g_param_spec_boolean ("expanded",
187                                                          P_("Expanded"),
188                                                          P_("Whether the expander has been opened to reveal the child widget"),
189                                                          FALSE,
190                                                          GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
191
192   g_object_class_install_property (gobject_class,
193                                    PROP_LABEL,
194                                    g_param_spec_string ("label",
195                                                         P_("Label"),
196                                                         P_("Text of the expander's label"),
197                                                         NULL,
198                                                         GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
199
200   g_object_class_install_property (gobject_class,
201                                    PROP_USE_UNDERLINE,
202                                    g_param_spec_boolean ("use-underline",
203                                                          P_("Use underline"),
204                                                          P_("If set, an underline in the text indicates the next character should be used for the mnemonic accelerator key"),
205                                                          FALSE,
206                                                          GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
207
208   g_object_class_install_property (gobject_class,
209                                    PROP_USE_MARKUP,
210                                    g_param_spec_boolean ("use-markup",
211                                                          P_("Use markup"),
212                                                          P_("The text of the label includes XML markup. See pango_parse_markup()"),
213                                                          FALSE,
214                                                          GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
215
216   g_object_class_install_property (gobject_class,
217                                    PROP_SPACING,
218                                    g_param_spec_int ("spacing",
219                                                      P_("Spacing"),
220                                                      P_("Space to put between the label and the child"),
221                                                      0,
222                                                      G_MAXINT,
223                                                      0,
224                                                      GTK_PARAM_READWRITE));
225
226   g_object_class_install_property (gobject_class,
227                                    PROP_LABEL_WIDGET,
228                                    g_param_spec_object ("label-widget",
229                                                         P_("Label widget"),
230                                                         P_("A widget to display in place of the usual expander label"),
231                                                         GTK_TYPE_WIDGET,
232                                                         GTK_PARAM_READWRITE));
233
234   gtk_widget_class_install_style_property (widget_class,
235                                            g_param_spec_int ("expander-size",
236                                                              P_("Expander Size"),
237                                                              P_("Size of the expander arrow"),
238                                                              0,
239                                                              G_MAXINT,
240                                                              DEFAULT_EXPANDER_SIZE,
241                                                              GTK_PARAM_READABLE));
242
243   gtk_widget_class_install_style_property (widget_class,
244                                            g_param_spec_int ("expander-spacing",
245                                                              P_("Indicator Spacing"),
246                                                              P_("Spacing around expander arrow"),
247                                                              0,
248                                                              G_MAXINT,
249                                                              DEFAULT_EXPANDER_SPACING,
250                                                              GTK_PARAM_READABLE));
251
252   widget_class->activate_signal =
253     g_signal_new (I_("activate"),
254                   G_TYPE_FROM_CLASS (gobject_class),
255                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
256                   G_STRUCT_OFFSET (GtkExpanderClass, activate),
257                   NULL, NULL,
258                   _gtk_marshal_VOID__VOID,
259                   G_TYPE_NONE, 0);
260 }
261
262 static void
263 gtk_expander_init (GtkExpander *expander)
264 {
265   GtkExpanderPrivate *priv;
266
267   expander->priv = priv = GTK_EXPANDER_GET_PRIVATE (expander);
268
269   GTK_WIDGET_SET_FLAGS (expander, GTK_CAN_FOCUS);
270   GTK_WIDGET_SET_FLAGS (expander, GTK_NO_WINDOW);
271
272   priv->label_widget = NULL;
273   priv->event_window = NULL;
274   priv->spacing = 0;
275
276   priv->expander_style = GTK_EXPANDER_COLLAPSED;
277   priv->animation_timeout = 0;
278
279   priv->expanded = FALSE;
280   priv->use_underline = FALSE;
281   priv->use_markup = FALSE;
282   priv->button_down = FALSE;
283   priv->prelight = FALSE;
284   priv->expand_timer = 0;
285
286   gtk_drag_dest_set (GTK_WIDGET (expander), 0, NULL, 0, 0);
287   gtk_drag_dest_set_track_motion (GTK_WIDGET (expander), TRUE);
288 }
289
290 static void
291 gtk_expander_buildable_add_child (GtkBuildable  *buildable,
292                                   GtkBuilder    *builder,
293                                   GObject       *child,
294                                   const gchar   *type)
295 {
296   if (!type)
297     gtk_container_add (GTK_CONTAINER (buildable), GTK_WIDGET (child));
298   else if (strcmp (type, "label") == 0)
299     gtk_expander_set_label_widget (GTK_EXPANDER (buildable), GTK_WIDGET (child));
300   else
301     GTK_BUILDER_WARN_INVALID_CHILD_TYPE (GTK_EXPANDER (buildable), type);
302 }
303
304 static void
305 gtk_expander_buildable_init (GtkBuildableIface *iface)
306 {
307   iface->add_child = gtk_expander_buildable_add_child;
308 }
309
310 static void
311 gtk_expander_set_property (GObject      *object,
312                            guint         prop_id,
313                            const GValue *value,
314                            GParamSpec   *pspec)
315 {
316   GtkExpander *expander = GTK_EXPANDER (object);
317                                                                                                              
318   switch (prop_id)
319     {
320     case PROP_EXPANDED:
321       gtk_expander_set_expanded (expander, g_value_get_boolean (value));
322       break;
323     case PROP_LABEL:
324       gtk_expander_set_label (expander, g_value_get_string (value));
325       break;
326     case PROP_USE_UNDERLINE:
327       gtk_expander_set_use_underline (expander, g_value_get_boolean (value));
328       break;
329     case PROP_USE_MARKUP:
330       gtk_expander_set_use_markup (expander, g_value_get_boolean (value));
331       break;
332     case PROP_SPACING:
333       gtk_expander_set_spacing (expander, g_value_get_int (value));
334       break;
335     case PROP_LABEL_WIDGET:
336       gtk_expander_set_label_widget (expander, g_value_get_object (value));
337       break;
338     default:
339       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
340       break;
341     }
342 }
343
344 static void
345 gtk_expander_get_property (GObject    *object,
346                            guint       prop_id,
347                            GValue     *value,
348                            GParamSpec *pspec)
349 {
350   GtkExpander *expander = GTK_EXPANDER (object);
351   GtkExpanderPrivate *priv = expander->priv;
352
353   switch (prop_id)
354     {
355     case PROP_EXPANDED:
356       g_value_set_boolean (value, priv->expanded);
357       break;
358     case PROP_LABEL:
359       g_value_set_string (value, gtk_expander_get_label (expander));
360       break;
361     case PROP_USE_UNDERLINE:
362       g_value_set_boolean (value, priv->use_underline);
363       break;
364     case PROP_USE_MARKUP:
365       g_value_set_boolean (value, priv->use_markup);
366       break;
367     case PROP_SPACING:
368       g_value_set_int (value, priv->spacing);
369       break;
370     case PROP_LABEL_WIDGET:
371       g_value_set_object (value,
372                           priv->label_widget ?
373                           G_OBJECT (priv->label_widget) : NULL);
374       break;
375     default:
376       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
377       break;
378     }
379 }
380
381 static void
382 gtk_expander_destroy (GtkObject *object)
383 {
384   GtkExpanderPrivate *priv = GTK_EXPANDER (object)->priv;
385   
386   if (priv->animation_timeout)
387     {
388       g_source_remove (priv->animation_timeout);
389       priv->animation_timeout = 0;
390     }
391   
392   GTK_OBJECT_CLASS (gtk_expander_parent_class)->destroy (object);
393 }
394
395 static void
396 gtk_expander_realize (GtkWidget *widget)
397 {
398   GtkExpanderPrivate *priv;
399   GdkWindowAttr attributes;
400   gint attributes_mask;
401   gint border_width;
402   GdkRectangle expander_rect;
403   gint label_height;
404
405   priv = GTK_EXPANDER (widget)->priv;
406   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
407
408   border_width = GTK_CONTAINER (widget)->border_width;
409
410   get_expander_bounds (GTK_EXPANDER (widget), &expander_rect);
411   
412   if (priv->label_widget && gtk_widget_get_visible (priv->label_widget))
413     {
414       GtkRequisition label_requisition;
415
416       gtk_widget_get_child_requisition (priv->label_widget, &label_requisition);
417       label_height = label_requisition.height;
418     }
419   else
420     label_height = 0;
421
422   attributes.window_type = GDK_WINDOW_CHILD;
423   attributes.x = widget->allocation.x + border_width;
424   attributes.y = widget->allocation.y + border_width;
425   attributes.width = MAX (widget->allocation.width - 2 * border_width, 1);
426   attributes.height = MAX (expander_rect.height, label_height - 2 * border_width);
427   attributes.wclass = GDK_INPUT_ONLY;
428   attributes.event_mask = gtk_widget_get_events (widget)     |
429                                 GDK_BUTTON_PRESS_MASK        |
430                                 GDK_BUTTON_RELEASE_MASK      |
431                                 GDK_ENTER_NOTIFY_MASK        |
432                                 GDK_LEAVE_NOTIFY_MASK;
433
434   attributes_mask = GDK_WA_X | GDK_WA_Y;
435
436   widget->window = gtk_widget_get_parent_window (widget);
437   g_object_ref (widget->window);
438
439   priv->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
440                                        &attributes, attributes_mask);
441   gdk_window_set_user_data (priv->event_window, widget);
442
443   widget->style = gtk_style_attach (widget->style, widget->window);
444 }
445
446 static void
447 gtk_expander_unrealize (GtkWidget *widget)
448 {
449   GtkExpanderPrivate *priv = GTK_EXPANDER (widget)->priv;
450
451   if (priv->event_window)
452     {
453       gdk_window_set_user_data (priv->event_window, NULL);
454       gdk_window_destroy (priv->event_window);
455       priv->event_window = NULL;
456     }
457
458   GTK_WIDGET_CLASS (gtk_expander_parent_class)->unrealize (widget);
459 }
460
461 static void
462 gtk_expander_size_request (GtkWidget      *widget,
463                            GtkRequisition *requisition)
464 {
465   GtkExpander *expander;
466   GtkBin *bin;
467   GtkExpanderPrivate *priv;
468   gint border_width;
469   gint expander_size;
470   gint expander_spacing;
471   gboolean interior_focus;
472   gint focus_width;
473   gint focus_pad;
474
475   bin = GTK_BIN (widget);
476   expander = GTK_EXPANDER (widget);
477   priv = expander->priv;
478
479   border_width = GTK_CONTAINER (widget)->border_width;
480
481   gtk_widget_style_get (widget,
482                         "interior-focus", &interior_focus,
483                         "focus-line-width", &focus_width,
484                         "focus-padding", &focus_pad,
485                         "expander-size", &expander_size,
486                         "expander-spacing", &expander_spacing,
487                         NULL);
488
489   requisition->width = expander_size + 2 * expander_spacing +
490                        2 * focus_width + 2 * focus_pad;
491   requisition->height = interior_focus ? (2 * focus_width + 2 * focus_pad) : 0;
492
493   if (priv->label_widget && gtk_widget_get_visible (priv->label_widget))
494     {
495       GtkRequisition label_requisition;
496
497       gtk_widget_size_request (priv->label_widget, &label_requisition);
498
499       requisition->width  += label_requisition.width;
500       requisition->height += label_requisition.height;
501     }
502
503   requisition->height = MAX (expander_size + 2 * expander_spacing, requisition->height);
504
505   if (!interior_focus)
506     requisition->height += 2 * focus_width + 2 * focus_pad;
507
508   if (bin->child && GTK_WIDGET_CHILD_VISIBLE (bin->child))
509     {
510       GtkRequisition child_requisition;
511
512       gtk_widget_size_request (bin->child, &child_requisition);
513
514       requisition->width = MAX (requisition->width, child_requisition.width);
515       requisition->height += child_requisition.height + priv->spacing;
516     }
517
518   requisition->width  += 2 * border_width;
519   requisition->height += 2 * border_width;
520 }
521
522 static void
523 get_expander_bounds (GtkExpander  *expander,
524                      GdkRectangle *rect)
525 {
526   GtkWidget *widget;
527   GtkExpanderPrivate *priv;
528   gint border_width;
529   gint expander_size;
530   gint expander_spacing;
531   gboolean interior_focus;
532   gint focus_width;
533   gint focus_pad;
534   gboolean ltr;
535
536   widget = GTK_WIDGET (expander);
537   priv = expander->priv;
538
539   border_width = GTK_CONTAINER (expander)->border_width;
540
541   gtk_widget_style_get (widget,
542                         "interior-focus", &interior_focus,
543                         "focus-line-width", &focus_width,
544                         "focus-padding", &focus_pad,
545                         "expander-size", &expander_size,
546                         "expander-spacing", &expander_spacing,
547                         NULL);
548
549   ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
550
551   rect->x = widget->allocation.x + border_width;
552   rect->y = widget->allocation.y + border_width;
553
554   if (ltr)
555     rect->x += expander_spacing;
556   else
557     rect->x += widget->allocation.width - 2 * border_width -
558                expander_spacing - expander_size;
559
560   if (priv->label_widget && gtk_widget_get_visible (priv->label_widget))
561     {
562       GtkAllocation label_allocation;
563
564       label_allocation = priv->label_widget->allocation;
565
566       if (expander_size < label_allocation.height)
567         rect->y += focus_width + focus_pad + (label_allocation.height - expander_size) / 2;
568       else
569         rect->y += expander_spacing;
570     }
571   else
572     {
573       rect->y += expander_spacing;
574     }
575
576   if (!interior_focus)
577     {
578       if (ltr)
579         rect->x += focus_width + focus_pad;
580       else
581         rect->x -= focus_width + focus_pad;
582       rect->y += focus_width + focus_pad;
583     }
584
585   rect->width = rect->height = expander_size;
586 }
587
588 static void
589 gtk_expander_size_allocate (GtkWidget     *widget,
590                             GtkAllocation *allocation)
591 {
592   GtkExpander *expander;
593   GtkBin *bin;
594   GtkExpanderPrivate *priv;
595   GtkRequisition child_requisition;
596   gboolean child_visible = FALSE;
597   gint border_width;
598   gint expander_size;
599   gint expander_spacing;
600   gboolean interior_focus;
601   gint focus_width;
602   gint focus_pad;
603   gint label_height;
604
605   expander = GTK_EXPANDER (widget);
606   bin = GTK_BIN (widget);
607   priv = expander->priv;
608
609   border_width = GTK_CONTAINER (widget)->border_width;
610
611   gtk_widget_style_get (widget,
612                         "interior-focus", &interior_focus,
613                         "focus-line-width", &focus_width,
614                         "focus-padding", &focus_pad,
615                         "expander-size", &expander_size,
616                         "expander-spacing", &expander_spacing,
617                         NULL);
618
619   child_requisition.width = 0;
620   child_requisition.height = 0;
621   if (bin->child && GTK_WIDGET_CHILD_VISIBLE (bin->child))
622     {
623       child_visible = TRUE;
624       gtk_widget_get_child_requisition (bin->child, &child_requisition);
625     }
626
627   widget->allocation = *allocation;
628
629   if (priv->label_widget && gtk_widget_get_visible (priv->label_widget))
630     {
631       GtkAllocation label_allocation;
632       GtkRequisition label_requisition;
633       gboolean ltr;
634
635       gtk_widget_get_child_requisition (priv->label_widget, &label_requisition);
636
637       ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
638
639       if (ltr)
640         label_allocation.x = (widget->allocation.x +
641                               border_width + focus_width + focus_pad +
642                               expander_size + 2 * expander_spacing);
643       else
644         label_allocation.x = (widget->allocation.x + widget->allocation.width -
645                               (label_requisition.width +
646                                border_width + focus_width + focus_pad +
647                                expander_size + 2 * expander_spacing));
648
649       label_allocation.y = widget->allocation.y + border_width + focus_width + focus_pad;
650
651       label_allocation.width = MIN (label_requisition.width,
652                                     allocation->width - 2 * border_width -
653                                     expander_size - 2 * expander_spacing -
654                                     2 * focus_width - 2 * focus_pad);
655       label_allocation.width = MAX (label_allocation.width, 1);
656
657       label_allocation.height = MIN (label_requisition.height,
658                                      allocation->height - 2 * border_width -
659                                      2 * focus_width - 2 * focus_pad -
660                                      (child_visible ? priv->spacing : 0));
661       label_allocation.height = MAX (label_allocation.height, 1);
662
663       gtk_widget_size_allocate (priv->label_widget, &label_allocation);
664
665       label_height = label_allocation.height;
666     }
667   else
668     {
669       label_height = 0;
670     }
671
672   if (GTK_WIDGET_REALIZED (widget))
673     {
674       GdkRectangle rect;
675
676       get_expander_bounds (expander, &rect);
677
678       gdk_window_move_resize (priv->event_window,
679                               allocation->x + border_width, 
680                               allocation->y + border_width, 
681                               MAX (allocation->width - 2 * border_width, 1), 
682                               MAX (rect.height, label_height - 2 * border_width));
683     }
684
685   if (child_visible)
686     {
687       GtkAllocation child_allocation;
688       gint top_height;
689
690       top_height = MAX (2 * expander_spacing + expander_size,
691                         label_height +
692                         (interior_focus ? 2 * focus_width + 2 * focus_pad : 0));
693
694       child_allocation.x = widget->allocation.x + border_width;
695       child_allocation.y = widget->allocation.y + border_width + top_height + priv->spacing;
696
697       if (!interior_focus)
698         child_allocation.y += 2 * focus_width + 2 * focus_pad;
699
700       child_allocation.width = MAX (allocation->width - 2 * border_width, 1);
701
702       child_allocation.height = allocation->height - top_height -
703                                 2 * border_width - priv->spacing -
704                                 (!interior_focus ? 2 * focus_width + 2 * focus_pad : 0);
705       child_allocation.height = MAX (child_allocation.height, 1);
706
707       gtk_widget_size_allocate (bin->child, &child_allocation);
708     }
709 }
710
711 static void
712 gtk_expander_map (GtkWidget *widget)
713 {
714   GtkExpanderPrivate *priv = GTK_EXPANDER (widget)->priv;
715
716   if (priv->label_widget)
717     gtk_widget_map (priv->label_widget);
718
719   GTK_WIDGET_CLASS (gtk_expander_parent_class)->map (widget);
720
721   if (priv->event_window)
722     gdk_window_show (priv->event_window);
723 }
724
725 static void
726 gtk_expander_unmap (GtkWidget *widget)
727 {
728   GtkExpanderPrivate *priv = GTK_EXPANDER (widget)->priv;
729
730   if (priv->event_window)
731     gdk_window_hide (priv->event_window);
732
733   GTK_WIDGET_CLASS (gtk_expander_parent_class)->unmap (widget);
734
735   if (priv->label_widget)
736     gtk_widget_unmap (priv->label_widget);
737 }
738
739 static void
740 gtk_expander_paint_prelight (GtkExpander *expander)
741 {
742   GtkWidget *widget;
743   GtkContainer *container;
744   GtkExpanderPrivate *priv;
745   GdkRectangle area;
746   gboolean interior_focus;
747   int focus_width;
748   int focus_pad;
749   int expander_size;
750   int expander_spacing;
751
752   priv = expander->priv;
753   widget = GTK_WIDGET (expander);
754   container = GTK_CONTAINER (expander);
755
756   gtk_widget_style_get (widget,
757                         "interior-focus", &interior_focus,
758                         "focus-line-width", &focus_width,
759                         "focus-padding", &focus_pad,
760                         "expander-size", &expander_size,
761                         "expander-spacing", &expander_spacing,
762                         NULL);
763
764   area.x = widget->allocation.x + container->border_width;
765   area.y = widget->allocation.y + container->border_width;
766   area.width = widget->allocation.width - (2 * container->border_width);
767
768   if (priv->label_widget && gtk_widget_get_visible (priv->label_widget))
769     area.height = priv->label_widget->allocation.height;
770   else
771     area.height = 0;
772
773   area.height += interior_focus ? (focus_width + focus_pad) * 2 : 0;
774   area.height = MAX (area.height, expander_size + 2 * expander_spacing);
775   area.height += !interior_focus ? (focus_width + focus_pad) * 2 : 0;
776
777   gtk_paint_flat_box (widget->style, widget->window,
778                       GTK_STATE_PRELIGHT,
779                       GTK_SHADOW_ETCHED_OUT,
780                       &area, widget, "expander",
781                       area.x, area.y,
782                       area.width, area.height);
783 }
784
785 static void
786 gtk_expander_paint (GtkExpander *expander)
787 {
788   GtkWidget *widget;
789   GdkRectangle clip;
790   GtkStateType state;
791
792   widget = GTK_WIDGET (expander);
793
794   get_expander_bounds (expander, &clip);
795
796   state = widget->state;
797   if (expander->priv->prelight)
798     {
799       state = GTK_STATE_PRELIGHT;
800
801       gtk_expander_paint_prelight (expander);
802     }
803
804   gtk_paint_expander (widget->style,
805                       widget->window,
806                       state,
807                       &clip,
808                       widget,
809                       "expander",
810                       clip.x + clip.width / 2,
811                       clip.y + clip.height / 2,
812                       expander->priv->expander_style);
813 }
814
815 static void
816 gtk_expander_paint_focus (GtkExpander  *expander,
817                           GdkRectangle *area)
818 {
819   GtkWidget *widget;
820   GtkExpanderPrivate *priv;
821   GdkRectangle rect;
822   gint x, y, width, height;
823   gboolean interior_focus;
824   gint border_width;
825   gint focus_width;
826   gint focus_pad;
827   gint expander_size;
828   gint expander_spacing;
829   gboolean ltr;
830
831   widget = GTK_WIDGET (expander);
832   priv = expander->priv;
833
834   border_width = GTK_CONTAINER (widget)->border_width;
835
836   gtk_widget_style_get (widget,
837                         "interior-focus", &interior_focus,
838                         "focus-line-width", &focus_width,
839                         "focus-padding", &focus_pad,
840                         "expander-size", &expander_size,
841                         "expander-spacing", &expander_spacing,
842                         NULL);
843
844   ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
845   
846   width = height = 0;
847
848   if (priv->label_widget)
849     {
850       if (gtk_widget_get_visible (priv->label_widget))
851         {
852           GtkAllocation label_allocation = priv->label_widget->allocation;
853
854           width  = label_allocation.width;
855           height = label_allocation.height;
856         }
857
858       width  += 2 * focus_pad + 2 * focus_width;
859       height += 2 * focus_pad + 2 * focus_width;
860
861       x = widget->allocation.x + border_width;
862       y = widget->allocation.y + border_width;
863
864       if (ltr)
865         {
866           if (interior_focus)
867             x += expander_spacing * 2 + expander_size;
868         }
869       else
870         {
871           x += widget->allocation.width - 2 * border_width
872             - expander_spacing * 2 - expander_size - width;
873         }
874
875       if (!interior_focus)
876         {
877           width += expander_size + 2 * expander_spacing;
878           height = MAX (height, expander_size + 2 * expander_spacing);
879         }
880     }
881   else
882     {
883       get_expander_bounds (expander, &rect);
884
885       x = rect.x - focus_pad;
886       y = rect.y - focus_pad;
887       width = rect.width + 2 * focus_pad;
888       height = rect.height + 2 * focus_pad;
889     }
890       
891   gtk_paint_focus (widget->style, widget->window, GTK_WIDGET_STATE (widget),
892                    area, widget, "expander",
893                    x, y, width, height);
894 }
895
896 static gboolean
897 gtk_expander_expose (GtkWidget      *widget,
898                      GdkEventExpose *event)
899 {
900   if (gtk_widget_is_drawable (widget))
901     {
902       GtkExpander *expander = GTK_EXPANDER (widget);
903
904       gtk_expander_paint (expander);
905
906       if (gtk_widget_has_focus (widget))
907         gtk_expander_paint_focus (expander, &event->area);
908
909       GTK_WIDGET_CLASS (gtk_expander_parent_class)->expose_event (widget, event);
910     }
911
912   return FALSE;
913 }
914
915 static gboolean
916 gtk_expander_button_press (GtkWidget      *widget,
917                            GdkEventButton *event)
918 {
919   GtkExpander *expander = GTK_EXPANDER (widget);
920
921   if (event->button == 1 && event->window == expander->priv->event_window)
922     {
923       expander->priv->button_down = TRUE;
924       return TRUE;
925     }
926
927   return FALSE;
928 }
929
930 static gboolean
931 gtk_expander_button_release (GtkWidget      *widget,
932                              GdkEventButton *event)
933 {
934   GtkExpander *expander = GTK_EXPANDER (widget);
935
936   if (event->button == 1 && expander->priv->button_down)
937     {
938       gtk_widget_activate (widget);
939       expander->priv->button_down = FALSE;
940       return TRUE;
941     }
942
943   return FALSE;
944 }
945
946 static void
947 gtk_expander_grab_notify (GtkWidget *widget,
948                           gboolean   was_grabbed)
949 {
950   if (!was_grabbed)
951     GTK_EXPANDER (widget)->priv->button_down = FALSE;
952 }
953
954 static void
955 gtk_expander_state_changed (GtkWidget    *widget,
956                             GtkStateType  previous_state)
957 {
958   if (!gtk_widget_is_sensitive (widget))
959     GTK_EXPANDER (widget)->priv->button_down = FALSE;
960 }
961
962 static void
963 gtk_expander_redraw_expander (GtkExpander *expander)
964 {
965   GtkWidget *widget;
966
967   widget = GTK_WIDGET (expander);
968
969   if (GTK_WIDGET_REALIZED (widget))
970     gdk_window_invalidate_rect (widget->window, &widget->allocation, FALSE);
971 }
972
973 static gboolean
974 gtk_expander_enter_notify (GtkWidget        *widget,
975                            GdkEventCrossing *event)
976 {
977   GtkExpander *expander = GTK_EXPANDER (widget);
978   GtkWidget *event_widget;
979
980   event_widget = gtk_get_event_widget ((GdkEvent *) event);
981
982   if (event_widget == widget &&
983       event->detail != GDK_NOTIFY_INFERIOR)
984     {
985       expander->priv->prelight = TRUE;
986
987       if (expander->priv->label_widget)
988         gtk_widget_set_state (expander->priv->label_widget, GTK_STATE_PRELIGHT);
989
990       gtk_expander_redraw_expander (expander);
991     }
992
993   return FALSE;
994 }
995
996 static gboolean
997 gtk_expander_leave_notify (GtkWidget        *widget,
998                            GdkEventCrossing *event)
999 {
1000   GtkExpander *expander = GTK_EXPANDER (widget);
1001   GtkWidget *event_widget;
1002
1003   event_widget = gtk_get_event_widget ((GdkEvent *) event);
1004
1005   if (event_widget == widget &&
1006       event->detail != GDK_NOTIFY_INFERIOR)
1007     {
1008       expander->priv->prelight = FALSE;
1009
1010       if (expander->priv->label_widget)
1011         gtk_widget_set_state (expander->priv->label_widget, GTK_STATE_NORMAL);
1012
1013       gtk_expander_redraw_expander (expander);
1014     }
1015
1016   return FALSE;
1017 }
1018
1019 static gboolean
1020 expand_timeout (gpointer data)
1021 {
1022   GtkExpander *expander = GTK_EXPANDER (data);
1023   GtkExpanderPrivate *priv = expander->priv;
1024
1025   priv->expand_timer = 0;
1026   gtk_expander_set_expanded (expander, TRUE);
1027
1028   return FALSE;
1029 }
1030
1031 static gboolean
1032 gtk_expander_drag_motion (GtkWidget        *widget,
1033                           GdkDragContext   *context,
1034                           gint              x,
1035                           gint              y,
1036                           guint             time)
1037 {
1038   GtkExpander *expander = GTK_EXPANDER (widget);
1039   GtkExpanderPrivate *priv = expander->priv;
1040
1041   if (!priv->expanded && !priv->expand_timer)
1042     {
1043       GtkSettings *settings;
1044       guint timeout;
1045
1046       settings = gtk_widget_get_settings (widget);
1047       g_object_get (settings, "gtk-timeout-expand", &timeout, NULL);
1048
1049       priv->expand_timer = gdk_threads_add_timeout (timeout, (GSourceFunc) expand_timeout, expander);
1050     }
1051
1052   return TRUE;
1053 }
1054
1055 static void
1056 gtk_expander_drag_leave (GtkWidget      *widget,
1057                          GdkDragContext *context,
1058                          guint           time)
1059 {
1060   GtkExpander *expander = GTK_EXPANDER (widget);
1061   GtkExpanderPrivate *priv = expander->priv;
1062
1063   if (priv->expand_timer)
1064     {
1065       g_source_remove (priv->expand_timer);
1066       priv->expand_timer = 0;
1067     }
1068 }
1069
1070 typedef enum
1071 {
1072   FOCUS_NONE,
1073   FOCUS_WIDGET,
1074   FOCUS_LABEL,
1075   FOCUS_CHILD
1076 } FocusSite;
1077
1078 static gboolean
1079 focus_current_site (GtkExpander      *expander,
1080                     GtkDirectionType  direction)
1081 {
1082   GtkWidget *current_focus;
1083
1084   current_focus = GTK_CONTAINER (expander)->focus_child;
1085
1086   if (!current_focus)
1087     return FALSE;
1088
1089   return gtk_widget_child_focus (current_focus, direction);
1090 }
1091
1092 static gboolean
1093 focus_in_site (GtkExpander      *expander,
1094                FocusSite         site,
1095                GtkDirectionType  direction)
1096 {
1097   switch (site)
1098     {
1099     case FOCUS_WIDGET:
1100       gtk_widget_grab_focus (GTK_WIDGET (expander));
1101       return TRUE;
1102     case FOCUS_LABEL:
1103       if (expander->priv->label_widget)
1104         return gtk_widget_child_focus (expander->priv->label_widget, direction);
1105       else
1106         return FALSE;
1107     case FOCUS_CHILD:
1108       {
1109         GtkWidget *child = gtk_bin_get_child (GTK_BIN (expander));
1110
1111         if (child && GTK_WIDGET_CHILD_VISIBLE (child))
1112           return gtk_widget_child_focus (child, direction);
1113         else
1114           return FALSE;
1115       }
1116     case FOCUS_NONE:
1117       break;
1118     }
1119
1120   g_assert_not_reached ();
1121   return FALSE;
1122 }
1123
1124 static FocusSite
1125 get_next_site (GtkExpander      *expander,
1126                FocusSite         site,
1127                GtkDirectionType  direction)
1128 {
1129   gboolean ltr;
1130
1131   ltr = gtk_widget_get_direction (GTK_WIDGET (expander)) != GTK_TEXT_DIR_RTL;
1132
1133   switch (site)
1134     {
1135     case FOCUS_NONE:
1136       switch (direction)
1137         {
1138         case GTK_DIR_TAB_BACKWARD:
1139         case GTK_DIR_LEFT:
1140         case GTK_DIR_UP:
1141           return FOCUS_CHILD;
1142         case GTK_DIR_TAB_FORWARD:
1143         case GTK_DIR_DOWN:
1144         case GTK_DIR_RIGHT:
1145           return FOCUS_WIDGET;
1146         }
1147     case FOCUS_WIDGET:
1148       switch (direction)
1149         {
1150         case GTK_DIR_TAB_BACKWARD:
1151         case GTK_DIR_UP:
1152           return FOCUS_NONE;
1153         case GTK_DIR_LEFT:
1154           return ltr ? FOCUS_NONE : FOCUS_LABEL;
1155         case GTK_DIR_TAB_FORWARD:
1156         case GTK_DIR_DOWN:
1157           return FOCUS_LABEL;
1158         case GTK_DIR_RIGHT:
1159           return ltr ? FOCUS_LABEL : FOCUS_NONE;
1160           break;
1161         }
1162     case FOCUS_LABEL:
1163       switch (direction)
1164         {
1165         case GTK_DIR_TAB_BACKWARD:
1166         case GTK_DIR_UP:
1167           return FOCUS_WIDGET;
1168         case GTK_DIR_LEFT:
1169           return ltr ? FOCUS_WIDGET : FOCUS_CHILD;
1170         case GTK_DIR_TAB_FORWARD:
1171         case GTK_DIR_DOWN:
1172           return FOCUS_CHILD;
1173         case GTK_DIR_RIGHT:
1174           return ltr ? FOCUS_CHILD : FOCUS_WIDGET;
1175           break;
1176         }
1177     case FOCUS_CHILD:
1178       switch (direction)
1179         {
1180         case GTK_DIR_TAB_BACKWARD:
1181         case GTK_DIR_LEFT:
1182         case GTK_DIR_UP:
1183           return FOCUS_LABEL;
1184         case GTK_DIR_TAB_FORWARD:
1185         case GTK_DIR_DOWN:
1186         case GTK_DIR_RIGHT:
1187           return FOCUS_NONE;
1188         }
1189     }
1190
1191   g_assert_not_reached ();
1192   return FOCUS_NONE;
1193 }
1194
1195 static gboolean
1196 gtk_expander_focus (GtkWidget        *widget,
1197                     GtkDirectionType  direction)
1198 {
1199   GtkExpander *expander = GTK_EXPANDER (widget);
1200   
1201   if (!focus_current_site (expander, direction))
1202     {
1203       GtkWidget *old_focus_child;
1204       gboolean widget_is_focus;
1205       FocusSite site = FOCUS_NONE;
1206       
1207       widget_is_focus = gtk_widget_is_focus (widget);
1208       old_focus_child = GTK_CONTAINER (widget)->focus_child;
1209       
1210       if (old_focus_child && old_focus_child == expander->priv->label_widget)
1211         site = FOCUS_LABEL;
1212       else if (old_focus_child)
1213         site = FOCUS_CHILD;
1214       else if (widget_is_focus)
1215         site = FOCUS_WIDGET;
1216
1217       while ((site = get_next_site (expander, site, direction)) != FOCUS_NONE)
1218         {
1219           if (focus_in_site (expander, site, direction))
1220             return TRUE;
1221         }
1222
1223       return FALSE;
1224     }
1225
1226   return TRUE;
1227 }
1228
1229 static void
1230 gtk_expander_add (GtkContainer *container,
1231                   GtkWidget    *widget)
1232 {
1233   GTK_CONTAINER_CLASS (gtk_expander_parent_class)->add (container, widget);
1234
1235   gtk_widget_set_child_visible (widget, GTK_EXPANDER (container)->priv->expanded);
1236   gtk_widget_queue_resize (GTK_WIDGET (container));
1237 }
1238
1239 static void
1240 gtk_expander_remove (GtkContainer *container,
1241                      GtkWidget    *widget)
1242 {
1243   GtkExpander *expander = GTK_EXPANDER (container);
1244
1245   if (GTK_EXPANDER (expander)->priv->label_widget == widget)
1246     gtk_expander_set_label_widget (expander, NULL);
1247   else
1248     GTK_CONTAINER_CLASS (gtk_expander_parent_class)->remove (container, widget);
1249 }
1250
1251 static void
1252 gtk_expander_forall (GtkContainer *container,
1253                      gboolean      include_internals,
1254                      GtkCallback   callback,
1255                      gpointer      callback_data)
1256 {
1257   GtkBin *bin = GTK_BIN (container);
1258   GtkExpanderPrivate *priv = GTK_EXPANDER (container)->priv;
1259
1260   if (bin->child)
1261     (* callback) (bin->child, callback_data);
1262
1263   if (priv->label_widget)
1264     (* callback) (priv->label_widget, callback_data);
1265 }
1266
1267 static void
1268 gtk_expander_activate (GtkExpander *expander)
1269 {
1270   gtk_expander_set_expanded (expander, !expander->priv->expanded);
1271 }
1272
1273 /**
1274  * gtk_expander_new:
1275  * @label: the text of the label
1276  * 
1277  * Creates a new expander using @label as the text of the label.
1278  * 
1279  * Return value: a new #GtkExpander widget.
1280  *
1281  * Since: 2.4
1282  **/
1283 GtkWidget *
1284 gtk_expander_new (const gchar *label)
1285 {
1286   return g_object_new (GTK_TYPE_EXPANDER, "label", label, NULL);
1287 }
1288
1289 /**
1290  * gtk_expander_new_with_mnemonic:
1291  * @label: (allow-none): the text of the label with an underscore in front of the
1292  *         mnemonic character
1293  *
1294  * Creates a new expander using @label as the text of the label.
1295  * If characters in @label are preceded by an underscore, they are underlined.
1296  * If you need a literal underscore character in a label, use '__' (two 
1297  * underscores). The first underlined character represents a keyboard 
1298  * accelerator called a mnemonic.
1299  * Pressing Alt and that key activates the button.
1300  * 
1301  * Return value: a new #GtkExpander widget.
1302  *
1303  * Since: 2.4
1304  **/
1305 GtkWidget *
1306 gtk_expander_new_with_mnemonic (const gchar *label)
1307 {
1308   return g_object_new (GTK_TYPE_EXPANDER,
1309                        "label", label,
1310                        "use-underline", TRUE,
1311                        NULL);
1312 }
1313
1314 static gboolean
1315 gtk_expander_animation_timeout (GtkExpander *expander)
1316 {
1317   GtkExpanderPrivate *priv = expander->priv;
1318   GdkRectangle area;
1319   gboolean finish = FALSE;
1320
1321   if (GTK_WIDGET_REALIZED (expander))
1322     {
1323       get_expander_bounds (expander, &area);
1324       gdk_window_invalidate_rect (GTK_WIDGET (expander)->window, &area, TRUE);
1325     }
1326
1327   if (priv->expanded)
1328     {
1329       if (priv->expander_style == GTK_EXPANDER_COLLAPSED)
1330         {
1331           priv->expander_style = GTK_EXPANDER_SEMI_EXPANDED;
1332         }
1333       else
1334         {
1335           priv->expander_style = GTK_EXPANDER_EXPANDED;
1336           finish = TRUE;
1337         }
1338     }
1339   else
1340     {
1341       if (priv->expander_style == GTK_EXPANDER_EXPANDED)
1342         {
1343           priv->expander_style = GTK_EXPANDER_SEMI_COLLAPSED;
1344         }
1345       else
1346         {
1347           priv->expander_style = GTK_EXPANDER_COLLAPSED;
1348           finish = TRUE;
1349         }
1350     }
1351
1352   if (finish)
1353     {
1354       priv->animation_timeout = 0;
1355       if (GTK_BIN (expander)->child)
1356         gtk_widget_set_child_visible (GTK_BIN (expander)->child, priv->expanded);
1357       gtk_widget_queue_resize (GTK_WIDGET (expander));
1358     }
1359
1360   return !finish;
1361 }
1362
1363 static void
1364 gtk_expander_start_animation (GtkExpander *expander)
1365 {
1366   GtkExpanderPrivate *priv = expander->priv;
1367
1368   if (priv->animation_timeout)
1369     g_source_remove (priv->animation_timeout);
1370
1371   priv->animation_timeout =
1372                 gdk_threads_add_timeout (50,
1373                                (GSourceFunc) gtk_expander_animation_timeout,
1374                                expander);
1375 }
1376
1377 /**
1378  * gtk_expander_set_expanded:
1379  * @expander: a #GtkExpander
1380  * @expanded: whether the child widget is revealed
1381  *
1382  * Sets the state of the expander. Set to %TRUE, if you want
1383  * the child widget to be revealed, and %FALSE if you want the
1384  * child widget to be hidden.
1385  *
1386  * Since: 2.4
1387  **/
1388 void
1389 gtk_expander_set_expanded (GtkExpander *expander,
1390                            gboolean     expanded)
1391 {
1392   GtkExpanderPrivate *priv;
1393
1394   g_return_if_fail (GTK_IS_EXPANDER (expander));
1395
1396   priv = expander->priv;
1397
1398   expanded = expanded != FALSE;
1399
1400   if (priv->expanded != expanded)
1401     {
1402       GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (expander));
1403       gboolean     enable_animations;
1404
1405       priv->expanded = expanded;
1406
1407       g_object_get (settings, "gtk-enable-animations", &enable_animations, NULL);
1408
1409       if (enable_animations && GTK_WIDGET_REALIZED (expander))
1410         {
1411           gtk_expander_start_animation (expander);
1412         }
1413       else
1414         {
1415           priv->expander_style = expanded ? GTK_EXPANDER_EXPANDED :
1416                                             GTK_EXPANDER_COLLAPSED;
1417
1418           if (GTK_BIN (expander)->child)
1419             {
1420               gtk_widget_set_child_visible (GTK_BIN (expander)->child, priv->expanded);
1421               gtk_widget_queue_resize (GTK_WIDGET (expander));
1422             }
1423         }
1424
1425       g_object_notify (G_OBJECT (expander), "expanded");
1426     }
1427 }
1428
1429 /**
1430  * gtk_expander_get_expanded:
1431  * @expander:a #GtkExpander
1432  *
1433  * Queries a #GtkExpander and returns its current state. Returns %TRUE
1434  * if the child widget is revealed.
1435  *
1436  * See gtk_expander_set_expanded().
1437  *
1438  * Return value: the current state of the expander.
1439  *
1440  * Since: 2.4
1441  **/
1442 gboolean
1443 gtk_expander_get_expanded (GtkExpander *expander)
1444 {
1445   g_return_val_if_fail (GTK_IS_EXPANDER (expander), FALSE);
1446
1447   return expander->priv->expanded;
1448 }
1449
1450 /**
1451  * gtk_expander_set_spacing:
1452  * @expander: a #GtkExpander
1453  * @spacing: distance between the expander and child in pixels.
1454  *
1455  * Sets the spacing field of @expander, which is the number of pixels to
1456  * place between expander and the child.
1457  *
1458  * Since: 2.4
1459  **/
1460 void
1461 gtk_expander_set_spacing (GtkExpander *expander,
1462                           gint         spacing)
1463 {
1464   g_return_if_fail (GTK_IS_EXPANDER (expander));
1465   g_return_if_fail (spacing >= 0);
1466
1467   if (expander->priv->spacing != spacing)
1468     {
1469       expander->priv->spacing = spacing;
1470
1471       gtk_widget_queue_resize (GTK_WIDGET (expander));
1472
1473       g_object_notify (G_OBJECT (expander), "spacing");
1474     }
1475 }
1476
1477 /**
1478  * gtk_expander_get_spacing:
1479  * @expander: a #GtkExpander
1480  *
1481  * Gets the value set by gtk_expander_set_spacing().
1482  *
1483  * Return value: spacing between the expander and child.
1484  *
1485  * Since: 2.4
1486  **/
1487 gint
1488 gtk_expander_get_spacing (GtkExpander *expander)
1489 {
1490   g_return_val_if_fail (GTK_IS_EXPANDER (expander), 0);
1491
1492   return expander->priv->spacing;
1493 }
1494
1495 /**
1496  * gtk_expander_set_label:
1497  * @expander: a #GtkExpander
1498  * @label: (allow-none): a string
1499  *
1500  * Sets the text of the label of the expander to @label.
1501  *
1502  * This will also clear any previously set labels.
1503  *
1504  * Since: 2.4
1505  **/
1506 void
1507 gtk_expander_set_label (GtkExpander *expander,
1508                         const gchar *label)
1509 {
1510   g_return_if_fail (GTK_IS_EXPANDER (expander));
1511
1512   if (!label)
1513     {
1514       gtk_expander_set_label_widget (expander, NULL);
1515     }
1516   else
1517     {
1518       GtkWidget *child;
1519
1520       child = gtk_label_new (label);
1521       gtk_label_set_use_underline (GTK_LABEL (child), expander->priv->use_underline);
1522       gtk_label_set_use_markup (GTK_LABEL (child), expander->priv->use_markup);
1523       gtk_widget_show (child);
1524
1525       gtk_expander_set_label_widget (expander, child);
1526     }
1527
1528   g_object_notify (G_OBJECT (expander), "label");
1529 }
1530
1531 /**
1532  * gtk_expander_get_label:
1533  * @expander: a #GtkExpander
1534  *
1535  * Fetches the text from a label widget including any embedded
1536  * underlines indicating mnemonics and Pango markup, as set by
1537  * gtk_expander_set_label(). If the label text has not been set the
1538  * return value will be %NULL. This will be the case if you create an
1539  * empty button with gtk_button_new() to use as a container.
1540  *
1541  * Note that this function behaved differently in versions prior to
1542  * 2.14 and used to return the label text stripped of embedded
1543  * underlines indicating mnemonics and Pango markup. This problem can
1544  * be avoided by fetching the label text directly from the label
1545  * widget.
1546  *
1547  * Return value: The text of the label widget. This string is owned
1548  * by the widget and must not be modified or freed.
1549  *
1550  * Since: 2.4
1551  **/
1552 G_CONST_RETURN char *
1553 gtk_expander_get_label (GtkExpander *expander)
1554 {
1555   GtkExpanderPrivate *priv;
1556
1557   g_return_val_if_fail (GTK_IS_EXPANDER (expander), NULL);
1558
1559   priv = expander->priv;
1560
1561   if (GTK_IS_LABEL (priv->label_widget))
1562     return gtk_label_get_label (GTK_LABEL (priv->label_widget));
1563   else
1564     return NULL;
1565 }
1566
1567 /**
1568  * gtk_expander_set_use_underline:
1569  * @expander: a #GtkExpander
1570  * @use_underline: %TRUE if underlines in the text indicate mnemonics
1571  *
1572  * If true, an underline in the text of the expander label indicates
1573  * the next character should be used for the mnemonic accelerator key.
1574  *
1575  * Since: 2.4
1576  **/
1577 void
1578 gtk_expander_set_use_underline (GtkExpander *expander,
1579                                 gboolean     use_underline)
1580 {
1581   GtkExpanderPrivate *priv;
1582
1583   g_return_if_fail (GTK_IS_EXPANDER (expander));
1584
1585   priv = expander->priv;
1586
1587   use_underline = use_underline != FALSE;
1588
1589   if (priv->use_underline != use_underline)
1590     {
1591       priv->use_underline = use_underline;
1592
1593       if (GTK_IS_LABEL (priv->label_widget))
1594         gtk_label_set_use_underline (GTK_LABEL (priv->label_widget), use_underline);
1595
1596       g_object_notify (G_OBJECT (expander), "use-underline");
1597     }
1598 }
1599
1600 /**
1601  * gtk_expander_get_use_underline:
1602  * @expander: a #GtkExpander
1603  *
1604  * Returns whether an embedded underline in the expander label indicates a
1605  * mnemonic. See gtk_expander_set_use_underline().
1606  *
1607  * Return value: %TRUE if an embedded underline in the expander label
1608  *               indicates the mnemonic accelerator keys.
1609  *
1610  * Since: 2.4
1611  **/
1612 gboolean
1613 gtk_expander_get_use_underline (GtkExpander *expander)
1614 {
1615   g_return_val_if_fail (GTK_IS_EXPANDER (expander), FALSE);
1616
1617   return expander->priv->use_underline;
1618 }
1619
1620 /**
1621  * gtk_expander_set_use_markup:
1622  * @expander: a #GtkExpander
1623  * @use_markup: %TRUE if the label's text should be parsed for markup
1624  *
1625  * Sets whether the text of the label contains markup in <link
1626  * linkend="PangoMarkupFormat">Pango's text markup
1627  * language</link>. See gtk_label_set_markup().
1628  *
1629  * Since: 2.4
1630  **/
1631 void
1632 gtk_expander_set_use_markup (GtkExpander *expander,
1633                              gboolean     use_markup)
1634 {
1635   GtkExpanderPrivate *priv;
1636
1637   g_return_if_fail (GTK_IS_EXPANDER (expander));
1638
1639   priv = expander->priv;
1640
1641   use_markup = use_markup != FALSE;
1642
1643   if (priv->use_markup != use_markup)
1644     {
1645       priv->use_markup = use_markup;
1646
1647       if (GTK_IS_LABEL (priv->label_widget))
1648         gtk_label_set_use_markup (GTK_LABEL (priv->label_widget), use_markup);
1649
1650       g_object_notify (G_OBJECT (expander), "use-markup");
1651     }
1652 }
1653
1654 /**
1655  * gtk_expander_get_use_markup:
1656  * @expander: a #GtkExpander
1657  *
1658  * Returns whether the label's text is interpreted as marked up with
1659  * the <link linkend="PangoMarkupFormat">Pango text markup
1660  * language</link>. See gtk_expander_set_use_markup ().
1661  *
1662  * Return value: %TRUE if the label's text will be parsed for markup
1663  *
1664  * Since: 2.4
1665  **/
1666 gboolean
1667 gtk_expander_get_use_markup (GtkExpander *expander)
1668 {
1669   g_return_val_if_fail (GTK_IS_EXPANDER (expander), FALSE);
1670
1671   return expander->priv->use_markup;
1672 }
1673
1674 /**
1675  * gtk_expander_set_label_widget:
1676  * @expander: a #GtkExpander
1677  * @label_widget: (allow-none): the new label widget
1678  *
1679  * Set the label widget for the expander. This is the widget
1680  * that will appear embedded alongside the expander arrow.
1681  *
1682  * Since: 2.4
1683  **/
1684 void
1685 gtk_expander_set_label_widget (GtkExpander *expander,
1686                                GtkWidget   *label_widget)
1687 {
1688   GtkExpanderPrivate *priv;
1689   GtkWidget          *widget;
1690
1691   g_return_if_fail (GTK_IS_EXPANDER (expander));
1692   g_return_if_fail (label_widget == NULL || GTK_IS_WIDGET (label_widget));
1693   g_return_if_fail (label_widget == NULL || label_widget->parent == NULL);
1694
1695   priv = expander->priv;
1696
1697   if (priv->label_widget == label_widget)
1698     return;
1699
1700   if (priv->label_widget)
1701     {
1702       gtk_widget_set_state (priv->label_widget, GTK_STATE_NORMAL);
1703       gtk_widget_unparent (priv->label_widget);
1704     }
1705
1706   priv->label_widget = label_widget;
1707   widget = GTK_WIDGET (expander);
1708
1709   if (label_widget)
1710     {
1711       priv->label_widget = label_widget;
1712
1713       gtk_widget_set_parent (label_widget, widget);
1714
1715       if (priv->prelight)
1716         gtk_widget_set_state (label_widget, GTK_STATE_PRELIGHT);
1717     }
1718
1719   if (gtk_widget_get_visible (widget))
1720     gtk_widget_queue_resize (widget);
1721
1722   g_object_freeze_notify (G_OBJECT (expander));
1723   g_object_notify (G_OBJECT (expander), "label-widget");
1724   g_object_notify (G_OBJECT (expander), "label");
1725   g_object_thaw_notify (G_OBJECT (expander));
1726 }
1727
1728 /**
1729  * gtk_expander_get_label_widget:
1730  * @expander: a #GtkExpander
1731  *
1732  * Retrieves the label widget for the frame. See
1733  * gtk_expander_set_label_widget().
1734  *
1735  * Return value: the label widget, or %NULL if there is none.
1736  * 
1737  * Since: 2.4
1738  **/
1739 GtkWidget *
1740 gtk_expander_get_label_widget (GtkExpander *expander)
1741 {
1742   g_return_val_if_fail (GTK_IS_EXPANDER (expander), NULL);
1743
1744   return expander->priv->label_widget;
1745 }
1746
1747 #define __GTK_EXPANDER_C__
1748 #include "gtkaliasdef.c"