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