1 /* GTK - The GIMP Toolkit
3 * Copyright (C) 2003 Sun Microsystems, Inc.
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.
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.
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.
21 * Mark McLoughlin <mark@skynet.ie>
26 #include "gtkexpander.h"
29 #include "gtkcontainer.h"
30 #include "gtkmarshalers.h"
33 #include "gtkprivate.h"
34 #include <gdk/gdkkeysyms.h>
36 #define GTK_EXPANDER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_EXPANDER, GtkExpanderPrivate))
38 #define DEFAULT_EXPANDER_SIZE 10
39 #define DEFAULT_EXPANDER_SPACING 2
51 struct _GtkExpanderPrivate
53 GtkWidget *label_widget;
54 GdkWindow *event_window;
57 GtkExpanderStyle expander_style;
58 guint animation_timeout;
61 guint use_underline : 1;
62 guint button_down : 1;
66 static void gtk_expander_class_init (GtkExpanderClass *klass);
67 static void gtk_expander_init (GtkExpander *expander);
69 static void gtk_expander_set_property (GObject *object,
73 static void gtk_expander_get_property (GObject *object,
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);
103 static void gtk_expander_add (GtkContainer *container,
105 static void gtk_expander_remove (GtkContainer *container,
107 static void gtk_expander_forall (GtkContainer *container,
108 gboolean include_internals,
109 GtkCallback callback,
110 gpointer callback_data);
112 static void gtk_expander_activate (GtkExpander *expander);
114 static GtkBinClass *parent_class = NULL;
117 gtk_expander_get_type (void)
119 static GType expander_type = 0;
123 static const GTypeInfo expander_info =
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),
133 (GInstanceInitFunc) gtk_expander_init,
136 expander_type = g_type_register_static (GTK_TYPE_BIN,
141 return expander_type;
145 gtk_expander_class_init (GtkExpanderClass *klass)
147 GObjectClass *gobject_class;
148 GtkWidgetClass *widget_class;
149 GtkContainerClass *container_class;
151 parent_class = g_type_class_peek_parent (klass);
153 gobject_class = (GObjectClass *) klass;
154 widget_class = (GtkWidgetClass *) klass;
155 container_class = (GtkContainerClass *) klass;
157 gobject_class->set_property = gtk_expander_set_property;
158 gobject_class->get_property = gtk_expander_get_property;
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;
175 container_class->add = gtk_expander_add;
176 container_class->remove = gtk_expander_remove;
177 container_class->forall = gtk_expander_forall;
179 klass->activate = gtk_expander_activate;
181 g_type_class_add_private (klass, sizeof (GtkExpanderPrivate));
183 g_object_class_install_property (gobject_class,
185 g_param_spec_boolean ("expanded",
187 _("Whether the expander has been opened to reveal the child widget"),
189 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
191 g_object_class_install_property (gobject_class,
193 g_param_spec_string ("label",
195 _("Text of the expander's label"),
197 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
199 g_object_class_install_property (gobject_class,
201 g_param_spec_boolean ("use_underline",
203 _("If set, an underline in the text indicates the next character should be used for the mnemonic accelerator key"),
205 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
207 g_object_class_install_property (gobject_class,
209 g_param_spec_int ("spacing",
211 _("Space to put between the label and the child"),
217 g_object_class_install_property (gobject_class,
219 g_param_spec_object ("label_widget",
221 _("A widget to display in place of the usual expander label"),
225 gtk_widget_class_install_style_property (widget_class,
226 g_param_spec_int ("expander-size",
228 _("Size of the expander arrow"),
231 DEFAULT_EXPANDER_SIZE,
234 gtk_widget_class_install_style_property (widget_class,
235 g_param_spec_int ("expander-spacing",
236 _("Indicator Spacing"),
237 _("Spacing around expander arrow"),
240 DEFAULT_EXPANDER_SPACING,
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),
249 _gtk_marshal_VOID__VOID,
254 gtk_expander_init (GtkExpander *expander)
256 GtkExpanderPrivate *priv;
258 expander->priv = priv = GTK_EXPANDER_GET_PRIVATE (expander);
260 GTK_WIDGET_SET_FLAGS (expander, GTK_CAN_FOCUS);
261 GTK_WIDGET_SET_FLAGS (expander, GTK_NO_WINDOW);
263 priv->label_widget = NULL;
264 priv->event_window = NULL;
267 priv->expander_style = GTK_EXPANDER_COLLAPSED;
268 priv->animation_timeout = 0;
270 priv->expanded = FALSE;
271 priv->use_underline = FALSE;
272 priv->button_down = FALSE;
273 priv->prelight = FALSE;
277 gtk_expander_set_property (GObject *object,
282 GtkExpander *expander = GTK_EXPANDER (object);
287 gtk_expander_set_expanded (expander, g_value_get_boolean (value));
290 gtk_expander_set_label (expander, g_value_get_string (value));
292 case PROP_USE_UNDERLINE:
293 gtk_expander_set_use_underline (expander, g_value_get_boolean (value));
296 gtk_expander_set_spacing (expander, g_value_get_int (value));
298 case PROP_LABEL_WIDGET:
299 gtk_expander_set_label_widget (expander, g_value_get_object (value));
302 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
308 gtk_expander_get_property (GObject *object,
313 GtkExpander *expander = GTK_EXPANDER (object);
314 GtkExpanderPrivate *priv = expander->priv;
319 g_value_set_boolean (value, priv->expanded);
322 g_value_set_string (value, gtk_expander_get_label (expander));
324 case PROP_USE_UNDERLINE:
325 g_value_set_boolean (value, priv->use_underline);
328 g_value_set_int (value, priv->spacing);
330 case PROP_LABEL_WIDGET:
331 g_value_set_object (value,
333 G_OBJECT (priv->label_widget) : NULL);
336 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
342 gtk_expander_realize (GtkWidget *widget)
344 GtkExpanderPrivate *priv;
345 GdkWindowAttr attributes;
346 gint attributes_mask;
349 priv = GTK_EXPANDER (widget)->priv;
350 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
352 border_width = GTK_CONTAINER (widget)->border_width;
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;
366 attributes_mask = GDK_WA_X | GDK_WA_Y;
368 widget->window = gtk_widget_get_parent_window (widget);
369 g_object_ref (widget->window);
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);
375 widget->style = gtk_style_attach (widget->style, widget->window);
376 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
380 gtk_expander_unrealize (GtkWidget *widget)
382 GtkExpanderPrivate *priv = GTK_EXPANDER (widget)->priv;
384 if (priv->event_window)
386 gdk_window_set_user_data (priv->event_window, NULL);
387 gdk_window_destroy (priv->event_window);
388 priv->event_window = NULL;
391 if (priv->animation_timeout)
393 g_source_remove (priv->animation_timeout);
394 priv->animation_timeout = 0;
397 GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
401 gtk_expander_size_request (GtkWidget *widget,
402 GtkRequisition *requisition)
404 GtkExpander *expander;
406 GtkExpanderPrivate *priv;
409 gint expander_spacing;
410 gboolean interior_focus;
414 bin = GTK_BIN (widget);
415 expander = GTK_EXPANDER (widget);
416 priv = expander->priv;
418 border_width = GTK_CONTAINER (widget)->border_width;
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,
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;
432 if (priv->label_widget && GTK_WIDGET_VISIBLE (priv->label_widget))
434 GtkRequisition label_requisition;
436 gtk_widget_size_request (priv->label_widget, &label_requisition);
438 requisition->width += label_requisition.width;
439 requisition->height += label_requisition.height;
442 requisition->height = MAX (expander_size + 2 * expander_spacing, requisition->height);
445 requisition->height += 2 * focus_width + 2 * focus_pad;
447 if (bin->child && GTK_WIDGET_CHILD_VISIBLE (bin->child))
449 GtkRequisition child_requisition;
451 gtk_widget_size_request (bin->child, &child_requisition);
453 requisition->width = MAX (requisition->width, child_requisition.width);
454 requisition->height += child_requisition.height + priv->spacing;
457 requisition->width += 2 * border_width;
458 requisition->height += 2 * border_width;
462 get_expander_bounds (GtkExpander *expander,
467 GtkExpanderPrivate *priv;
470 gint expander_spacing;
471 gboolean interior_focus;
476 widget = GTK_WIDGET (expander);
477 bin = GTK_BIN (expander);
478 priv = expander->priv;
480 border_width = GTK_CONTAINER (expander)->border_width;
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,
490 ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
492 rect->x = widget->allocation.x + border_width;
493 rect->y = widget->allocation.y + border_width;
496 rect->x += expander_spacing;
498 rect->x += widget->allocation.width - 2 * border_width -
499 expander_spacing - expander_size;
501 if (priv->label_widget && GTK_WIDGET_VISIBLE (priv->label_widget))
503 GtkAllocation label_allocation;
505 label_allocation = priv->label_widget->allocation;
507 if (expander_size < label_allocation.height)
508 rect->y += focus_width + focus_pad + (label_allocation.height - expander_size) / 2;
510 rect->y += expander_spacing;
514 rect->y += expander_spacing;
520 rect->x += focus_width + focus_pad;
522 rect->x -= focus_width + focus_pad;
523 rect->y += focus_width + focus_pad;
526 rect->width = rect->height = expander_size;
530 gtk_expander_size_allocate (GtkWidget *widget,
531 GtkAllocation *allocation)
533 GtkExpander *expander;
535 GtkExpanderPrivate *priv;
536 GtkRequisition child_requisition;
537 gboolean child_visible = FALSE;
540 gint expander_spacing;
541 gboolean interior_focus;
546 expander = GTK_EXPANDER (widget);
547 bin = GTK_BIN (widget);
548 priv = expander->priv;
550 border_width = GTK_CONTAINER (widget)->border_width;
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,
560 child_requisition.width = 0;
561 child_requisition.height = 0;
562 if (bin->child && GTK_WIDGET_CHILD_VISIBLE (bin->child))
564 child_visible = TRUE;
565 gtk_widget_get_child_requisition (bin->child, &child_requisition);
568 widget->allocation = *allocation;
570 if (priv->label_widget && GTK_WIDGET_VISIBLE (priv->label_widget))
572 GtkAllocation label_allocation;
573 GtkRequisition label_requisition;
576 gtk_widget_get_child_requisition (priv->label_widget, &label_requisition);
578 ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
580 label_allocation.x = widget->allocation.x + border_width + focus_width + focus_pad;
582 label_allocation.x += expander_size + 2 * expander_spacing;
583 label_allocation.y = widget->allocation.y + border_width + focus_width + focus_pad;
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);
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);
597 gtk_widget_size_allocate (priv->label_widget, &label_allocation);
599 label_height = label_allocation.height;
606 if (GTK_WIDGET_REALIZED (widget))
610 get_expander_bounds (expander, &rect);
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);
619 GtkAllocation child_allocation;
622 top_height = MAX (2 * expander_spacing + expander_size,
624 (interior_focus ? 2 * focus_width + 2 * focus_pad : 0));
626 child_allocation.x = widget->allocation.x + border_width;
627 child_allocation.y = widget->allocation.y + border_width + top_height + priv->spacing;
630 child_allocation.y += 2 * focus_width + 2 * focus_pad;
632 child_allocation.width = MAX (allocation->width - 2 * border_width, 1);
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);
639 gtk_widget_size_allocate (bin->child, &child_allocation);
644 gtk_expander_map (GtkWidget *widget)
646 GtkExpanderPrivate *priv = GTK_EXPANDER (widget)->priv;
648 if (priv->label_widget)
649 gtk_widget_map (priv->label_widget);
651 GTK_WIDGET_CLASS (parent_class)->map (widget);
653 if (priv->event_window)
654 gdk_window_show (priv->event_window);
658 gtk_expander_unmap (GtkWidget *widget)
660 GtkExpanderPrivate *priv = GTK_EXPANDER (widget)->priv;
662 if (priv->event_window)
663 gdk_window_hide (priv->event_window);
665 GTK_WIDGET_CLASS (parent_class)->unmap (widget);
667 if (priv->label_widget)
668 gtk_widget_unmap (priv->label_widget);
672 gtk_expander_paint (GtkExpander *expander)
678 widget = GTK_WIDGET (expander);
680 get_expander_bounds (expander, &clip);
682 state = widget->state;
683 if (expander->priv->prelight)
684 state = GTK_STATE_PRELIGHT;
686 gtk_paint_expander (widget->style,
692 clip.x + clip.width / 2,
693 clip.y + clip.height / 2,
694 expander->priv->expander_style);
698 gtk_expander_paint_focus (GtkExpander *expander,
702 GtkExpanderPrivate *priv;
703 gint x, y, width, height;
704 gboolean interior_focus;
709 gint expander_spacing;
712 widget = GTK_WIDGET (expander);
713 priv = expander->priv;
715 border_width = GTK_CONTAINER (widget)->border_width;
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,
725 ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
727 x = widget->allocation.x + border_width;
728 y = widget->allocation.y + border_width;
730 if (ltr && interior_focus)
731 x += expander_spacing * 2 + expander_size;
735 if (priv->label_widget && GTK_WIDGET_VISIBLE (priv->label_widget))
737 GtkAllocation label_allocation = priv->label_widget->allocation;
739 width = label_allocation.width;
740 height = label_allocation.height;
745 width += expander_size + 2 * expander_spacing;
746 height = MAX (height, expander_size + 2 * expander_spacing);
749 width += 2 * focus_pad + 2 * focus_width;
750 height += 2 * focus_pad + 2 * focus_width;
752 gtk_paint_focus (widget->style, widget->window, GTK_WIDGET_STATE (widget),
753 area, widget, "expander",
754 x, y, width, height);
758 gtk_expander_expose (GtkWidget *widget,
759 GdkEventExpose *event)
761 if (GTK_WIDGET_DRAWABLE (widget))
763 GtkExpander *expander = GTK_EXPANDER (widget);
765 gtk_expander_paint (expander);
767 if (GTK_WIDGET_HAS_FOCUS (expander))
768 gtk_expander_paint_focus (expander, &event->area);
770 if (expander->priv->label_widget)
771 gtk_container_propagate_expose (GTK_CONTAINER (widget),
772 expander->priv->label_widget,
775 GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
782 gtk_expander_button_press (GtkWidget *widget,
783 GdkEventButton *event)
785 GtkExpander *expander = GTK_EXPANDER (widget);
787 if (event->button == 1 && event->window == expander->priv->event_window)
789 expander->priv->button_down = TRUE;
797 gtk_expander_button_release (GtkWidget *widget,
798 GdkEventButton *event)
800 GtkExpander *expander = GTK_EXPANDER (widget);
802 if (event->button == 1 && expander->priv->button_down)
804 gtk_widget_activate (widget);
805 expander->priv->button_down = FALSE;
813 gtk_expander_grab_notify (GtkWidget *widget,
814 gboolean was_grabbed)
817 GTK_EXPANDER (widget)->priv->button_down = FALSE;
821 gtk_expander_state_changed (GtkWidget *widget,
822 GtkStateType previous_state)
824 if (!GTK_WIDGET_IS_SENSITIVE (widget))
825 GTK_EXPANDER (widget)->priv->button_down = FALSE;
829 gtk_expander_redraw_expander (GtkExpander *expander)
833 get_expander_bounds (expander, &bounds);
835 gtk_widget_queue_draw_area (GTK_WIDGET (expander),
843 gtk_expander_enter_notify (GtkWidget *widget,
844 GdkEventCrossing *event)
846 GtkExpander *expander = GTK_EXPANDER (widget);
847 GtkWidget *event_widget;
849 event_widget = gtk_get_event_widget ((GdkEvent *) event);
851 if (event_widget == widget &&
852 event->detail != GDK_NOTIFY_INFERIOR)
854 expander->priv->prelight = TRUE;
855 gtk_expander_redraw_expander (expander);
862 gtk_expander_leave_notify (GtkWidget *widget,
863 GdkEventCrossing *event)
865 GtkExpander *expander = GTK_EXPANDER (widget);
866 GtkWidget *event_widget;
868 event_widget = gtk_get_event_widget ((GdkEvent *) event);
870 if (event_widget == widget &&
871 event->detail != GDK_NOTIFY_INFERIOR)
873 expander->priv->prelight = FALSE;
874 gtk_expander_redraw_expander (expander);
889 focus_current_site (GtkExpander *expander,
890 GtkDirectionType direction)
892 GtkWidget *current_focus;
894 current_focus = GTK_CONTAINER (expander)->focus_child;
899 return gtk_widget_child_focus (current_focus, direction);
903 focus_in_site (GtkExpander *expander,
905 GtkDirectionType direction)
910 gtk_widget_grab_focus (GTK_WIDGET (expander));
913 if (expander->priv->label_widget)
914 return gtk_widget_child_focus (expander->priv->label_widget, direction);
919 GtkWidget *child = gtk_bin_get_child (GTK_BIN (expander));
921 if (child && GTK_WIDGET_CHILD_VISIBLE (child))
922 return gtk_widget_child_focus (child, direction);
930 g_assert_not_reached ();
935 get_next_site (GtkExpander *expander,
937 GtkDirectionType direction)
941 ltr = gtk_widget_get_direction (GTK_WIDGET (expander)) != GTK_TEXT_DIR_RTL;
948 case GTK_DIR_TAB_BACKWARD:
952 case GTK_DIR_TAB_FORWARD:
960 case GTK_DIR_TAB_BACKWARD:
964 return ltr ? FOCUS_NONE : FOCUS_LABEL;
965 case GTK_DIR_TAB_FORWARD:
969 return ltr ? FOCUS_LABEL : FOCUS_NONE;
975 case GTK_DIR_TAB_BACKWARD:
979 return ltr ? FOCUS_WIDGET : FOCUS_CHILD;
980 case GTK_DIR_TAB_FORWARD:
984 return ltr ? FOCUS_CHILD : FOCUS_WIDGET;
990 case GTK_DIR_TAB_BACKWARD:
994 case GTK_DIR_TAB_FORWARD:
1001 g_assert_not_reached ();
1006 gtk_expander_focus (GtkWidget *widget,
1007 GtkDirectionType direction)
1009 GtkExpander *expander = GTK_EXPANDER (widget);
1011 if (!focus_current_site (expander, direction))
1013 GtkWidget *old_focus_child;
1014 gboolean widget_is_focus;
1015 FocusSite site = FOCUS_NONE;
1017 widget_is_focus = gtk_widget_is_focus (widget);
1018 old_focus_child = GTK_CONTAINER (widget)->focus_child;
1020 if (old_focus_child && old_focus_child == expander->priv->label_widget)
1022 else if (old_focus_child)
1024 else if (widget_is_focus)
1025 site = FOCUS_WIDGET;
1027 while ((site = get_next_site (expander, site, direction)) != FOCUS_NONE)
1029 if (focus_in_site (expander, site, direction))
1040 gtk_expander_add (GtkContainer *container,
1043 GTK_CONTAINER_CLASS (parent_class)->add (container, widget);
1045 gtk_widget_set_child_visible (widget, GTK_EXPANDER (container)->priv->expanded);
1046 gtk_widget_queue_resize (GTK_WIDGET (container));
1050 gtk_expander_remove (GtkContainer *container,
1053 GtkExpander *expander = GTK_EXPANDER (container);
1055 if (GTK_EXPANDER (expander)->priv->label_widget == widget)
1056 gtk_expander_set_label_widget (expander, NULL);
1058 GTK_CONTAINER_CLASS (parent_class)->remove (container, widget);
1062 gtk_expander_forall (GtkContainer *container,
1063 gboolean include_internals,
1064 GtkCallback callback,
1065 gpointer callback_data)
1067 GtkBin *bin = GTK_BIN (container);
1068 GtkExpanderPrivate *priv = GTK_EXPANDER (container)->priv;
1071 (* callback) (bin->child, callback_data);
1073 if (priv->label_widget)
1074 (* callback) (priv->label_widget, callback_data);
1078 gtk_expander_activate (GtkExpander *expander)
1080 gtk_expander_set_expanded (expander, !expander->priv->expanded);
1086 * @label: the text of the label
1088 * Creates a new expander using @label as the text of the label.
1090 * Return value: a new #GtkExpander widget.
1095 gtk_expander_new (const gchar *label)
1097 return g_object_new (GTK_TYPE_EXPANDER, "label", label, NULL);
1101 * gtk_expander_new_with_mnemonic:
1102 * @label: the text of the label with an underscore in front of the
1103 * mnemonic character
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.
1112 * Return value: a new #GtkExpander widget.
1117 gtk_expander_new_with_mnemonic (const gchar *label)
1119 return g_object_new (GTK_TYPE_EXPANDER,
1121 "use_underline", TRUE,
1126 gtk_expander_animation_timeout (GtkExpander *expander)
1128 GtkExpanderPrivate *priv = expander->priv;
1130 gboolean finish = FALSE;
1132 if (GTK_WIDGET_REALIZED (expander))
1134 get_expander_bounds (expander, &area);
1135 gdk_window_invalidate_rect (GTK_WIDGET (expander)->window, &area, TRUE);
1140 if (priv->expander_style == GTK_EXPANDER_COLLAPSED)
1142 priv->expander_style = GTK_EXPANDER_SEMI_EXPANDED;
1146 priv->expander_style = GTK_EXPANDER_EXPANDED;
1152 if (priv->expander_style == GTK_EXPANDER_EXPANDED)
1154 priv->expander_style = GTK_EXPANDER_SEMI_COLLAPSED;
1158 priv->expander_style = GTK_EXPANDER_COLLAPSED;
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));
1175 gtk_expander_start_animation (GtkExpander *expander)
1177 GtkExpanderPrivate *priv = expander->priv;
1179 if (!GTK_WIDGET_REALIZED (expander))
1182 if (priv->animation_timeout)
1183 g_source_remove (priv->animation_timeout);
1185 priv->animation_timeout =
1187 (GSourceFunc) gtk_expander_animation_timeout,
1192 * gtk_expander_set_expanded:
1193 * @expander: a #GtkExpander
1194 * @expanded: whether the child widget is revealed
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.
1203 gtk_expander_set_expanded (GtkExpander *expander,
1206 GtkExpanderPrivate *priv;
1208 g_return_if_fail (GTK_IS_EXPANDER (expander));
1210 priv = expander->priv;
1212 expanded = expanded != FALSE;
1214 if (priv->expanded != expanded)
1216 priv->expanded = expanded;
1218 if (GTK_WIDGET_VISIBLE (expander))
1219 gtk_expander_start_animation (expander);
1221 else if (GTK_BIN (expander)->child)
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));
1229 g_object_notify (G_OBJECT (expander), "expanded");
1234 * gtk_expander_get_expanded:
1235 * @expander:a #GtkExpander
1237 * Queries a #GtkExpander and returns its current state. Returns %TRUE
1238 * if the child widget is revealed.
1240 * See gtk_expander_set_expanded().
1242 * Return value: the current state of the expander.
1247 gtk_expander_get_expanded (GtkExpander *expander)
1249 g_return_val_if_fail (GTK_IS_EXPANDER (expander), FALSE);
1251 return expander->priv->expanded;
1255 * gtk_expander_set_spacing:
1256 * @expander: a #GtkExpander
1257 * @spacing: distance between the expander and child in pixels.
1259 * Sets the spacing field of @expander, which is the number of pixels to
1260 * place between expander and the child.
1265 gtk_expander_set_spacing (GtkExpander *expander,
1268 g_return_if_fail (GTK_IS_EXPANDER (expander));
1269 g_return_if_fail (spacing >= 0);
1271 if (expander->priv->spacing != spacing)
1273 expander->priv->spacing = spacing;
1275 gtk_widget_queue_resize (GTK_WIDGET (expander));
1277 g_object_notify (G_OBJECT (expander), "spacing");
1282 * gtk_expander_get_spacing:
1283 * @expander: a #GtkExpander
1285 * Gets the value set by gtk_expander_set_spacing().
1287 * Return value: spacing between the expander and child.
1292 gtk_expander_get_spacing (GtkExpander *expander)
1294 g_return_val_if_fail (GTK_IS_EXPANDER (expander), 0);
1296 return expander->priv->spacing;
1300 * gtk_expander_set_label:
1301 * @expander: a #GtkExpander
1304 * Sets the text of the label of the expander to @label.
1306 * This will also clear any previously set labels.
1311 gtk_expander_set_label (GtkExpander *expander,
1314 g_return_if_fail (GTK_IS_EXPANDER (expander));
1318 gtk_expander_set_label_widget (expander, NULL);
1324 child = gtk_label_new (label);
1325 gtk_label_set_use_underline (GTK_LABEL (child), expander->priv->use_underline);
1326 gtk_widget_show (child);
1328 gtk_expander_set_label_widget (expander, child);
1331 g_object_notify (G_OBJECT (expander), "label");
1335 * gtk_expander_get_label:
1336 * @expander: a #GtkExpander
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.
1344 * Return value: The text of the label widget. This string is owned
1345 * by the widget and must not be modified or freed.
1349 G_CONST_RETURN char *
1350 gtk_expander_get_label (GtkExpander *expander)
1352 GtkExpanderPrivate *priv;
1354 g_return_val_if_fail (GTK_IS_EXPANDER (expander), NULL);
1356 priv = expander->priv;
1358 if (priv->label_widget && GTK_IS_LABEL (priv->label_widget))
1359 return gtk_label_get_text (GTK_LABEL (priv->label_widget));
1365 * gtk_expander_set_use_underline:
1366 * @expander: a #GtkExpander
1367 * @use_underline: %TRUE if underlines in the text indicate mnemonics
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.
1375 gtk_expander_set_use_underline (GtkExpander *expander,
1376 gboolean use_underline)
1378 GtkExpanderPrivate *priv;
1380 g_return_if_fail (GTK_IS_EXPANDER (expander));
1382 priv = expander->priv;
1384 use_underline = use_underline != FALSE;
1386 if (priv->use_underline != use_underline)
1388 priv->use_underline = use_underline;
1390 if (priv->label_widget && GTK_IS_LABEL (priv->label_widget))
1391 gtk_label_set_use_underline (GTK_LABEL (priv->label_widget), use_underline);
1393 g_object_notify (G_OBJECT (expander), "use_underline");
1398 * gtk_expander_get_use_underline:
1399 * @expander: a #GtkExpander
1401 * Returns whether an embedded underline in the expander label indicates a
1402 * mnemonic. See gtk_expander_set_use_underline().
1404 * Return value: %TRUE if an embedded underline in the expander label
1405 * indicates the mnemonic accelerator keys.
1410 gtk_expander_get_use_underline (GtkExpander *expander)
1412 g_return_val_if_fail (GTK_IS_EXPANDER (expander), FALSE);
1414 return expander->priv->use_underline;
1418 * gtk_expander_set_label_widget:
1419 * @expander: a #GtkExpander
1420 * @label_widget: the new label widget
1422 * Set the label widget for the expander. This is the widget
1423 * that will appear embedded alongside the expander arrow.
1428 gtk_expander_set_label_widget (GtkExpander *expander,
1429 GtkWidget *label_widget)
1431 GtkExpanderPrivate *priv;
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);
1437 priv = expander->priv;
1439 if (priv->label_widget == label_widget)
1442 if (priv->label_widget)
1443 gtk_widget_unparent (priv->label_widget);
1445 priv->label_widget = label_widget;
1449 priv->label_widget = label_widget;
1450 gtk_widget_set_parent (label_widget, GTK_WIDGET (expander));
1453 if (GTK_WIDGET_VISIBLE (expander))
1454 gtk_widget_queue_resize (GTK_WIDGET (expander));
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));
1463 * gtk_expander_get_label_widget:
1464 * @expander: a #GtkExpander
1466 * Retrieves the label widget for the frame. See
1467 * gtk_expander_set_label_widget().
1469 * Return value: the label widget, or %NULL if there is none.
1474 gtk_expander_get_label_widget (GtkExpander *expander)
1476 g_return_val_if_fail (GTK_IS_EXPANDER (expander), NULL);
1478 return expander->priv->label_widget;