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