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