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