]> Pileus Git - ~andy/gtk/blob - gtk/gtkexpander.c
Patch from Jaiserca <jaiserca@inf.upv.es> to add a "use_markup" property.
[~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                                                          _("Expanded"),
195                                                          _("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                                                         _("Label"),
203                                                         _("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                                                          _("Use underline"),
211                                                          _("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                                                          _("Use markup"),
219                                                          _("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                                                      _("Spacing"),
227                                                      _("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                                                         _("Label widget"),
237                                                         _("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                                                              _("Expander Size"),
244                                                              _("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                                                              _("Indicator Spacing"),
253                                                              _("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 (GtkExpander *expander)
704 {
705   GtkWidget *widget;
706   GdkRectangle clip;
707   GtkStateType state;
708
709   widget = GTK_WIDGET (expander);
710
711   get_expander_bounds (expander, &clip);
712
713   state = widget->state;
714   if (expander->priv->prelight)
715     state = GTK_STATE_PRELIGHT;
716
717   gtk_paint_expander (widget->style,
718                       widget->window,
719                       state,
720                       &clip,
721                       widget,
722                       "expander",
723                       clip.x + clip.width / 2,
724                       clip.y + clip.height / 2,
725                       expander->priv->expander_style);
726 }
727
728 static void
729 gtk_expander_paint_focus (GtkExpander  *expander,
730                           GdkRectangle *area)
731 {
732   GtkWidget *widget;
733   GtkExpanderPrivate *priv;
734   gint x, y, width, height;
735   gboolean interior_focus;
736   gint border_width;
737   gint focus_width;
738   gint focus_pad;
739   gint expander_size;
740   gint expander_spacing;
741   gboolean ltr;
742
743   widget = GTK_WIDGET (expander);
744   priv = expander->priv;
745
746   border_width = GTK_CONTAINER (widget)->border_width;
747
748   gtk_widget_style_get (widget,
749                         "interior-focus", &interior_focus,
750                         "focus-line-width", &focus_width,
751                         "focus-padding", &focus_pad,
752                         "expander-size", &expander_size,
753                         "expander-spacing", &expander_spacing,
754                         NULL);
755
756   ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
757   
758   x = widget->allocation.x + border_width;
759   y = widget->allocation.y + border_width;
760
761   if (ltr && interior_focus)
762     x += expander_spacing * 2 + expander_size;
763
764   width = height = 0;
765
766   if (priv->label_widget && GTK_WIDGET_VISIBLE (priv->label_widget))
767     {
768       GtkAllocation label_allocation = priv->label_widget->allocation;
769
770       width  = label_allocation.width;
771       height = label_allocation.height;
772     }
773
774   if (!interior_focus)
775     {
776       width += expander_size + 2 * expander_spacing;
777       height = MAX (height, expander_size + 2 * expander_spacing);
778     }
779       
780   width  += 2 * focus_pad + 2 * focus_width;
781   height += 2 * focus_pad + 2 * focus_width;
782
783   gtk_paint_focus (widget->style, widget->window, GTK_WIDGET_STATE (widget),
784                    area, widget, "expander",
785                    x, y, width, height);
786 }
787
788 static gboolean
789 gtk_expander_expose (GtkWidget      *widget,
790                      GdkEventExpose *event)
791 {
792   if (GTK_WIDGET_DRAWABLE (widget))
793     {
794       GtkExpander *expander = GTK_EXPANDER (widget);
795
796       gtk_expander_paint (expander);
797
798       if (GTK_WIDGET_HAS_FOCUS (expander))
799         gtk_expander_paint_focus (expander, &event->area);
800
801       if (expander->priv->label_widget)
802         gtk_container_propagate_expose (GTK_CONTAINER (widget),
803                                         expander->priv->label_widget,
804                                         event);
805
806       GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
807     }
808
809   return FALSE;
810 }
811
812 static gboolean
813 gtk_expander_button_press (GtkWidget      *widget,
814                            GdkEventButton *event)
815 {
816   GtkExpander *expander = GTK_EXPANDER (widget);
817
818   if (event->button == 1 && event->window == expander->priv->event_window)
819     {
820       expander->priv->button_down = TRUE;
821       return TRUE;
822     }
823
824   return FALSE;
825 }
826
827 static gboolean
828 gtk_expander_button_release (GtkWidget      *widget,
829                              GdkEventButton *event)
830 {
831   GtkExpander *expander = GTK_EXPANDER (widget);
832
833   if (event->button == 1 && expander->priv->button_down)
834     {
835       gtk_widget_activate (widget);
836       expander->priv->button_down = FALSE;
837       return TRUE;
838     }
839
840   return FALSE;
841 }
842
843 static void
844 gtk_expander_grab_notify (GtkWidget *widget,
845                           gboolean   was_grabbed)
846 {
847   if (!was_grabbed)
848     GTK_EXPANDER (widget)->priv->button_down = FALSE;
849 }
850
851 static void
852 gtk_expander_state_changed (GtkWidget    *widget,
853                             GtkStateType  previous_state)
854 {
855   if (!GTK_WIDGET_IS_SENSITIVE (widget))
856     GTK_EXPANDER (widget)->priv->button_down = FALSE;
857 }
858
859 static void
860 gtk_expander_redraw_expander (GtkExpander *expander)
861 {
862   GdkRectangle bounds;
863
864   get_expander_bounds (expander, &bounds);
865
866   gtk_widget_queue_draw_area (GTK_WIDGET (expander),
867                               bounds.x,
868                               bounds.y,
869                               bounds.width,
870                               bounds.height);
871 }
872
873 static gboolean
874 gtk_expander_enter_notify (GtkWidget        *widget,
875                            GdkEventCrossing *event)
876 {
877   GtkExpander *expander = GTK_EXPANDER (widget);
878   GtkWidget *event_widget;
879
880   event_widget = gtk_get_event_widget ((GdkEvent *) event);
881
882   if (event_widget == widget &&
883       event->detail != GDK_NOTIFY_INFERIOR)
884     {
885       expander->priv->prelight = TRUE;
886       gtk_expander_redraw_expander (expander);
887     }
888
889   return FALSE;
890 }
891
892 static gboolean
893 gtk_expander_leave_notify (GtkWidget        *widget,
894                            GdkEventCrossing *event)
895 {
896   GtkExpander *expander = GTK_EXPANDER (widget);
897   GtkWidget *event_widget;
898
899   event_widget = gtk_get_event_widget ((GdkEvent *) event);
900
901   if (event_widget == widget &&
902       event->detail != GDK_NOTIFY_INFERIOR)
903     {
904       expander->priv->prelight = FALSE;
905       gtk_expander_redraw_expander (expander);
906     }
907
908   return FALSE;
909 }
910
911 typedef enum
912 {
913   FOCUS_NONE,
914   FOCUS_WIDGET,
915   FOCUS_LABEL,
916   FOCUS_CHILD
917 } FocusSite;
918
919 static gboolean
920 focus_current_site (GtkExpander      *expander,
921                     GtkDirectionType  direction)
922 {
923   GtkWidget *current_focus;
924
925   current_focus = GTK_CONTAINER (expander)->focus_child;
926
927   if (!current_focus)
928     return FALSE;
929
930   return gtk_widget_child_focus (current_focus, direction);
931 }
932
933 static gboolean
934 focus_in_site (GtkExpander      *expander,
935                FocusSite         site,
936                GtkDirectionType  direction)
937 {
938   switch (site)
939     {
940     case FOCUS_WIDGET:
941       gtk_widget_grab_focus (GTK_WIDGET (expander));
942       return TRUE;
943     case FOCUS_LABEL:
944       if (expander->priv->label_widget)
945         return gtk_widget_child_focus (expander->priv->label_widget, direction);
946       else
947         return FALSE;
948     case FOCUS_CHILD:
949       {
950         GtkWidget *child = gtk_bin_get_child (GTK_BIN (expander));
951
952         if (child && GTK_WIDGET_CHILD_VISIBLE (child))
953           return gtk_widget_child_focus (child, direction);
954         else
955           return FALSE;
956       }
957     case FOCUS_NONE:
958       break;
959     }
960
961   g_assert_not_reached ();
962   return FALSE;
963 }
964
965 static FocusSite
966 get_next_site (GtkExpander      *expander,
967                FocusSite         site,
968                GtkDirectionType  direction)
969 {
970   gboolean ltr;
971
972   ltr = gtk_widget_get_direction (GTK_WIDGET (expander)) != GTK_TEXT_DIR_RTL;
973
974   switch (site)
975     {
976     case FOCUS_NONE:
977       switch (direction)
978         {
979         case GTK_DIR_TAB_BACKWARD:
980         case GTK_DIR_LEFT:
981         case GTK_DIR_UP:
982           return FOCUS_CHILD;
983         case GTK_DIR_TAB_FORWARD:
984         case GTK_DIR_DOWN:
985         case GTK_DIR_RIGHT:
986           return FOCUS_WIDGET;
987         }
988     case FOCUS_WIDGET:
989       switch (direction)
990         {
991         case GTK_DIR_TAB_BACKWARD:
992         case GTK_DIR_UP:
993           return FOCUS_NONE;
994         case GTK_DIR_LEFT:
995           return ltr ? FOCUS_NONE : FOCUS_LABEL;
996         case GTK_DIR_TAB_FORWARD:
997         case GTK_DIR_DOWN:
998           return FOCUS_LABEL;
999         case GTK_DIR_RIGHT:
1000           return ltr ? FOCUS_LABEL : FOCUS_NONE;
1001           break;
1002         }
1003     case FOCUS_LABEL:
1004       switch (direction)
1005         {
1006         case GTK_DIR_TAB_BACKWARD:
1007         case GTK_DIR_UP:
1008           return FOCUS_WIDGET;
1009         case GTK_DIR_LEFT:
1010           return ltr ? FOCUS_WIDGET : FOCUS_CHILD;
1011         case GTK_DIR_TAB_FORWARD:
1012         case GTK_DIR_DOWN:
1013           return FOCUS_CHILD;
1014         case GTK_DIR_RIGHT:
1015           return ltr ? FOCUS_CHILD : FOCUS_WIDGET;
1016           break;
1017         }
1018     case FOCUS_CHILD:
1019       switch (direction)
1020         {
1021         case GTK_DIR_TAB_BACKWARD:
1022         case GTK_DIR_LEFT:
1023         case GTK_DIR_UP:
1024           return FOCUS_LABEL;
1025         case GTK_DIR_TAB_FORWARD:
1026         case GTK_DIR_DOWN:
1027         case GTK_DIR_RIGHT:
1028           return FOCUS_NONE;
1029         }
1030     }
1031
1032   g_assert_not_reached ();
1033   return FOCUS_NONE;
1034 }
1035
1036 static gboolean
1037 gtk_expander_focus (GtkWidget        *widget,
1038                     GtkDirectionType  direction)
1039 {
1040   GtkExpander *expander = GTK_EXPANDER (widget);
1041   
1042   if (!focus_current_site (expander, direction))
1043     {
1044       GtkWidget *old_focus_child;
1045       gboolean widget_is_focus;
1046       FocusSite site = FOCUS_NONE;
1047       
1048       widget_is_focus = gtk_widget_is_focus (widget);
1049       old_focus_child = GTK_CONTAINER (widget)->focus_child;
1050       
1051       if (old_focus_child && old_focus_child == expander->priv->label_widget)
1052         site = FOCUS_LABEL;
1053       else if (old_focus_child)
1054         site = FOCUS_CHILD;
1055       else if (widget_is_focus)
1056         site = FOCUS_WIDGET;
1057
1058       while ((site = get_next_site (expander, site, direction)) != FOCUS_NONE)
1059         {
1060           if (focus_in_site (expander, site, direction))
1061             return TRUE;
1062         }
1063
1064       return FALSE;
1065     }
1066
1067   return TRUE;
1068 }
1069
1070 static void
1071 gtk_expander_add (GtkContainer *container,
1072                   GtkWidget    *widget)
1073 {
1074   GTK_CONTAINER_CLASS (parent_class)->add (container, widget);
1075
1076   gtk_widget_set_child_visible (widget, GTK_EXPANDER (container)->priv->expanded);
1077   gtk_widget_queue_resize (GTK_WIDGET (container));
1078 }
1079
1080 static void
1081 gtk_expander_remove (GtkContainer *container,
1082                      GtkWidget    *widget)
1083 {
1084   GtkExpander *expander = GTK_EXPANDER (container);
1085
1086   if (GTK_EXPANDER (expander)->priv->label_widget == widget)
1087     gtk_expander_set_label_widget (expander, NULL);
1088   else
1089     GTK_CONTAINER_CLASS (parent_class)->remove (container, widget);
1090 }
1091
1092 static void
1093 gtk_expander_forall (GtkContainer *container,
1094                      gboolean      include_internals,
1095                      GtkCallback   callback,
1096                      gpointer      callback_data)
1097 {
1098   GtkBin *bin = GTK_BIN (container);
1099   GtkExpanderPrivate *priv = GTK_EXPANDER (container)->priv;
1100
1101   if (bin->child)
1102     (* callback) (bin->child, callback_data);
1103
1104   if (priv->label_widget)
1105     (* callback) (priv->label_widget, callback_data);
1106 }
1107
1108 static void
1109 gtk_expander_activate (GtkExpander *expander)
1110 {
1111   gtk_expander_set_expanded (expander, !expander->priv->expanded);
1112 }
1113
1114
1115 /**
1116  * gtk_expander_new:
1117  * @label: the text of the label
1118  * 
1119  * Creates a new expander using @label as the text of the label.
1120  * 
1121  * Return value: a new #GtkExpander widget.
1122  *
1123  * Since: 2.4
1124  **/
1125 GtkWidget *
1126 gtk_expander_new (const gchar *label)
1127 {
1128   return g_object_new (GTK_TYPE_EXPANDER, "label", label, NULL);
1129 }
1130
1131 /**
1132  * gtk_expander_new_with_mnemonic:
1133  * @label: the text of the label with an underscore in front of the
1134  *         mnemonic character
1135  * 
1136  * Creates a new expander using @label as the text of the label.
1137  * If characters in @label are preceded by an underscore, they are underlined.
1138  * If you need a literal underscore character in a label, use '__' (two 
1139  * underscores). The first underlined character represents a keyboard 
1140  * accelerator called a mnemonic.
1141  * Pressing Alt and that key activates the button.
1142  * 
1143  * Return value: a new #GtkExpander widget.
1144  *
1145  * Since: 2.4
1146  **/
1147 GtkWidget *
1148 gtk_expander_new_with_mnemonic (const gchar *label)
1149 {
1150   return g_object_new (GTK_TYPE_EXPANDER,
1151                        "label", label,
1152                        "use_underline", TRUE,
1153                        NULL);
1154 }
1155
1156 static gboolean
1157 gtk_expander_animation_timeout (GtkExpander *expander)
1158 {
1159   GtkExpanderPrivate *priv = expander->priv;
1160   GdkRectangle area;
1161   gboolean finish = FALSE;
1162
1163   if (GTK_WIDGET_REALIZED (expander))
1164     {
1165       get_expander_bounds (expander, &area);
1166       gdk_window_invalidate_rect (GTK_WIDGET (expander)->window, &area, TRUE);
1167     }
1168
1169   if (priv->expanded)
1170     {
1171       if (priv->expander_style == GTK_EXPANDER_COLLAPSED)
1172         {
1173           priv->expander_style = GTK_EXPANDER_SEMI_EXPANDED;
1174         }
1175       else
1176         {
1177           priv->expander_style = GTK_EXPANDER_EXPANDED;
1178           finish = TRUE;
1179         }
1180     }
1181   else
1182     {
1183       if (priv->expander_style == GTK_EXPANDER_EXPANDED)
1184         {
1185           priv->expander_style = GTK_EXPANDER_SEMI_COLLAPSED;
1186         }
1187       else
1188         {
1189           priv->expander_style = GTK_EXPANDER_COLLAPSED;
1190           finish = TRUE;
1191         }
1192     }
1193
1194   if (finish)
1195     {
1196       priv->animation_timeout = 0;
1197       if (GTK_BIN (expander)->child)
1198         gtk_widget_set_child_visible (GTK_BIN (expander)->child, priv->expanded);
1199       gtk_widget_queue_resize (GTK_WIDGET (expander));
1200     }
1201
1202   return !finish;
1203 }
1204
1205 static void
1206 gtk_expander_start_animation (GtkExpander *expander)
1207 {
1208   GtkExpanderPrivate *priv = expander->priv;
1209
1210   if (priv->animation_timeout)
1211     g_source_remove (priv->animation_timeout);
1212
1213   priv->animation_timeout =
1214                 g_timeout_add (50,
1215                                (GSourceFunc) gtk_expander_animation_timeout,
1216                                expander);
1217 }
1218
1219 /**
1220  * gtk_expander_set_expanded:
1221  * @expander: a #GtkExpander
1222  * @expanded: whether the child widget is revealed
1223  *
1224  * Sets the state of the expander. Set to %TRUE, if you want
1225  * the child widget to be revealed, and %FALSE if you want the
1226  * child widget to be hidden.
1227  *
1228  * Since: 2.4
1229  **/
1230 void
1231 gtk_expander_set_expanded (GtkExpander *expander,
1232                            gboolean     expanded)
1233 {
1234   GtkExpanderPrivate *priv;
1235
1236   g_return_if_fail (GTK_IS_EXPANDER (expander));
1237
1238   priv = expander->priv;
1239
1240   expanded = expanded != FALSE;
1241
1242   if (priv->expanded != expanded)
1243     {
1244       priv->expanded = expanded;
1245
1246       if (GTK_WIDGET_REALIZED (expander))
1247         {
1248           gtk_expander_start_animation (expander);
1249         }
1250       else 
1251         {
1252           priv->expander_style = expanded ? GTK_EXPANDER_EXPANDED :
1253                                             GTK_EXPANDER_COLLAPSED;
1254
1255           if (GTK_BIN (expander)->child)
1256             {
1257               gtk_widget_set_child_visible (GTK_BIN (expander)->child, priv->expanded);
1258               gtk_widget_queue_resize (GTK_WIDGET (expander));
1259             }
1260         }
1261
1262       g_object_notify (G_OBJECT (expander), "expanded");
1263     }
1264 }
1265
1266 /**
1267  * gtk_expander_get_expanded:
1268  * @expander:a #GtkExpander
1269  *
1270  * Queries a #GtkExpander and returns its current state. Returns %TRUE
1271  * if the child widget is revealed.
1272  *
1273  * See gtk_expander_set_expanded().
1274  *
1275  * Return value: the current state of the expander.
1276  *
1277  * Since: 2.4
1278  **/
1279 gboolean
1280 gtk_expander_get_expanded (GtkExpander *expander)
1281 {
1282   g_return_val_if_fail (GTK_IS_EXPANDER (expander), FALSE);
1283
1284   return expander->priv->expanded;
1285 }
1286
1287 /**
1288  * gtk_expander_set_spacing:
1289  * @expander: a #GtkExpander
1290  * @spacing: distance between the expander and child in pixels.
1291  *
1292  * Sets the spacing field of @expander, which is the number of pixels to
1293  * place between expander and the child.
1294  *
1295  * Since: 2.4
1296  **/
1297 void
1298 gtk_expander_set_spacing (GtkExpander *expander,
1299                           gint         spacing)
1300 {
1301   g_return_if_fail (GTK_IS_EXPANDER (expander));
1302   g_return_if_fail (spacing >= 0);
1303
1304   if (expander->priv->spacing != spacing)
1305     {
1306       expander->priv->spacing = spacing;
1307
1308       gtk_widget_queue_resize (GTK_WIDGET (expander));
1309
1310       g_object_notify (G_OBJECT (expander), "spacing");
1311     }
1312 }
1313
1314 /**
1315  * gtk_expander_get_spacing:
1316  * @expander: a #GtkExpander
1317  *
1318  * Gets the value set by gtk_expander_set_spacing().
1319  *
1320  * Return value: spacing between the expander and child.
1321  *
1322  * Since: 2.4
1323  **/
1324 gint
1325 gtk_expander_get_spacing (GtkExpander *expander)
1326 {
1327   g_return_val_if_fail (GTK_IS_EXPANDER (expander), 0);
1328
1329   return expander->priv->spacing;
1330 }
1331
1332 /**
1333  * gtk_expander_set_label:
1334  * @expander: a #GtkExpander
1335  * @label: a string
1336  *
1337  * Sets the text of the label of the expander to @label.
1338  *
1339  * This will also clear any previously set labels.
1340  *
1341  * Since: 2.4
1342  **/
1343 void
1344 gtk_expander_set_label (GtkExpander *expander,
1345                         const gchar *label)
1346 {
1347   g_return_if_fail (GTK_IS_EXPANDER (expander));
1348
1349   if (!label)
1350     {
1351       gtk_expander_set_label_widget (expander, NULL);
1352     }
1353   else
1354     {
1355       GtkWidget *child;
1356
1357       child = gtk_label_new (label);
1358       gtk_label_set_use_underline (GTK_LABEL (child), expander->priv->use_underline);
1359       gtk_label_set_use_markup (GTK_LABEL (child), expander->priv->use_markup);
1360       gtk_widget_show (child);
1361
1362       gtk_expander_set_label_widget (expander, child);
1363     }
1364
1365   g_object_notify (G_OBJECT (expander), "label");
1366 }
1367
1368 /**
1369  * gtk_expander_get_label:
1370  * @expander: a #GtkExpander
1371  *
1372  * Fetches the text from the label of the expander, as set by
1373  * gtk_expander_set_label(). If the label text has not
1374  * been set the return value will be %NULL. This will be the
1375  * case if you create an empty button with gtk_button_new() to
1376  * use as a container.
1377  *
1378  * Return value: The text of the label widget. This string is owned
1379  * by the widget and must not be modified or freed.
1380  *
1381  * Since: 2.4
1382  **/
1383 G_CONST_RETURN char *
1384 gtk_expander_get_label (GtkExpander *expander)
1385 {
1386   GtkExpanderPrivate *priv;
1387
1388   g_return_val_if_fail (GTK_IS_EXPANDER (expander), NULL);
1389
1390   priv = expander->priv;
1391
1392   if (priv->label_widget && GTK_IS_LABEL (priv->label_widget))
1393     return gtk_label_get_text (GTK_LABEL (priv->label_widget));
1394   else
1395     return NULL;
1396 }
1397
1398 /**
1399  * gtk_expander_set_use_underline:
1400  * @expander: a #GtkExpander
1401  * @use_underline: %TRUE if underlines in the text indicate mnemonics
1402  *
1403  * If true, an underline in the text of the expander label indicates
1404  * the next character should be used for the mnemonic accelerator key.
1405  *
1406  * Since: 2.4
1407  **/
1408 void
1409 gtk_expander_set_use_underline (GtkExpander *expander,
1410                                 gboolean     use_underline)
1411 {
1412   GtkExpanderPrivate *priv;
1413
1414   g_return_if_fail (GTK_IS_EXPANDER (expander));
1415
1416   priv = expander->priv;
1417
1418   use_underline = use_underline != FALSE;
1419
1420   if (priv->use_underline != use_underline)
1421     {
1422       priv->use_underline = use_underline;
1423
1424       if (priv->label_widget && GTK_IS_LABEL (priv->label_widget))
1425         gtk_label_set_use_underline (GTK_LABEL (priv->label_widget), use_underline);
1426
1427       g_object_notify (G_OBJECT (expander), "use_underline");
1428     }
1429 }
1430
1431 /**
1432  * gtk_expander_get_use_underline:
1433  * @expander: a #GtkExpander
1434  *
1435  * Returns whether an embedded underline in the expander label indicates a
1436  * mnemonic. See gtk_expander_set_use_underline().
1437  *
1438  * Return value: %TRUE if an embedded underline in the expander label
1439  *               indicates the mnemonic accelerator keys.
1440  *
1441  * Since: 2.4
1442  **/
1443 gboolean
1444 gtk_expander_get_use_underline (GtkExpander *expander)
1445 {
1446   g_return_val_if_fail (GTK_IS_EXPANDER (expander), FALSE);
1447
1448   return expander->priv->use_underline;
1449 }
1450
1451 /**
1452  * gtk_expander_set_use_markup:
1453  * @expander: a #GtkExpander
1454  * @use_markup: %TRUE if the label's text should be parsed for markup
1455  *
1456  * Sets whether the text of the label contains markup in <link
1457  * linkend="PangoMarkupFormat">Pango's text markup
1458  * language</link>. See gtk_label_set_markup().
1459  *
1460  * Since: 2.4
1461  **/
1462 void
1463 gtk_expander_set_use_markup (GtkExpander *expander,
1464                              gboolean     use_markup)
1465 {
1466   GtkExpanderPrivate *priv;
1467
1468   g_return_if_fail (GTK_IS_EXPANDER (expander));
1469
1470   priv = expander->priv;
1471
1472   use_markup = use_markup != FALSE;
1473
1474   if (priv->use_markup != use_markup)
1475     {
1476       priv->use_markup = use_markup;
1477
1478       if (priv->label_widget && GTK_IS_LABEL (priv->label_widget))
1479         gtk_label_set_use_markup (GTK_LABEL (priv->label_widget), use_markup);
1480
1481       g_object_notify (G_OBJECT (expander), "use_markup");
1482     }
1483 }
1484
1485 /**
1486  * gtk_expander_get_use_markup:
1487  * @expander: a #GtkExpander
1488  *
1489  * Returns whether the label's text is interpreted as marked up with
1490  * the <link linkend="PangoMarkupFormat">Pango text markup
1491  * language</link>. See gtk_expander_set_use_markup ().
1492  *
1493  * Return value: %TRUE if the label's text will be parsed for markup
1494  *
1495  * Since: 2.4
1496  **/
1497 gboolean
1498 gtk_expander_get_use_markup (GtkExpander *expander)
1499 {
1500   g_return_val_if_fail (GTK_IS_EXPANDER (expander), FALSE);
1501
1502   return expander->priv->use_markup;
1503 }
1504
1505 /**
1506  * gtk_expander_set_label_widget:
1507  * @expander: a #GtkExpander
1508  * @label_widget: the new label widget
1509  *
1510  * Set the label widget for the expander. This is the widget
1511  * that will appear embedded alongside the expander arrow.
1512  *
1513  * Since: 2.4
1514  **/
1515 void
1516 gtk_expander_set_label_widget (GtkExpander *expander,
1517                                GtkWidget   *label_widget)
1518 {
1519   GtkExpanderPrivate *priv;
1520
1521   g_return_if_fail (GTK_IS_EXPANDER (expander));
1522   g_return_if_fail (label_widget == NULL || GTK_IS_WIDGET (label_widget));
1523   g_return_if_fail (label_widget == NULL || label_widget->parent == NULL);
1524
1525   priv = expander->priv;
1526
1527   if (priv->label_widget == label_widget)
1528     return;
1529
1530   if (priv->label_widget)
1531     gtk_widget_unparent (priv->label_widget);
1532
1533   priv->label_widget = label_widget;
1534
1535   if (label_widget)
1536     {
1537       priv->label_widget = label_widget;
1538       gtk_widget_set_parent (label_widget, GTK_WIDGET (expander));
1539     }
1540
1541   if (GTK_WIDGET_VISIBLE (expander))
1542     gtk_widget_queue_resize (GTK_WIDGET (expander));
1543
1544   g_object_freeze_notify (G_OBJECT (expander));
1545   g_object_notify (G_OBJECT (expander), "label_widget");
1546   g_object_notify (G_OBJECT (expander), "label");
1547   g_object_thaw_notify (G_OBJECT (expander));
1548 }
1549
1550 /**
1551  * gtk_expander_get_label_widget:
1552  * @expander: a #GtkExpander
1553  *
1554  * Retrieves the label widget for the frame. See
1555  * gtk_expander_set_label_widget().
1556  *
1557  * Return value: the label widget, or %NULL if there is none.
1558  * 
1559  * Since: 2.4
1560  **/
1561 GtkWidget *
1562 gtk_expander_get_label_widget (GtkExpander *expander)
1563 {
1564   g_return_val_if_fail (GTK_IS_EXPANDER (expander), NULL);
1565
1566   return expander->priv->label_widget;
1567 }