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