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