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),
256 gtk_expander_init (GtkExpander *expander)
258 GtkExpanderPrivate *priv;
260 expander->priv = priv = GTK_EXPANDER_GET_PRIVATE (expander);
262 GTK_WIDGET_SET_FLAGS (expander, GTK_CAN_FOCUS);
263 GTK_WIDGET_SET_FLAGS (expander, GTK_NO_WINDOW);
265 priv->label_widget = NULL;
266 priv->event_window = NULL;
269 priv->expander_style = GTK_EXPANDER_COLLAPSED;
270 priv->animation_timeout = 0;
272 priv->expanded = FALSE;
273 priv->use_underline = FALSE;
274 priv->button_down = FALSE;
275 priv->prelight = FALSE;
279 gtk_expander_set_property (GObject *object,
284 GtkExpander *expander = GTK_EXPANDER (object);
289 gtk_expander_set_expanded (expander, g_value_get_boolean (value));
292 gtk_expander_set_label (expander, g_value_get_string (value));
294 case PROP_USE_UNDERLINE:
295 gtk_expander_set_use_underline (expander, g_value_get_boolean (value));
298 gtk_expander_set_spacing (expander, g_value_get_int (value));
300 case PROP_LABEL_WIDGET:
301 gtk_expander_set_label_widget (expander, g_value_get_object (value));
304 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
310 gtk_expander_get_property (GObject *object,
315 GtkExpander *expander = GTK_EXPANDER (object);
316 GtkExpanderPrivate *priv = expander->priv;
321 g_value_set_boolean (value, priv->expanded);
324 g_value_set_string (value, gtk_expander_get_label (expander));
326 case PROP_USE_UNDERLINE:
327 g_value_set_boolean (value, priv->use_underline);
330 g_value_set_int (value, priv->spacing);
332 case PROP_LABEL_WIDGET:
333 g_value_set_object (value,
335 G_OBJECT (priv->label_widget) : NULL);
338 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
344 gtk_expander_realize (GtkWidget *widget)
346 GtkExpanderPrivate *priv;
347 GdkWindowAttr attributes;
348 gint attributes_mask;
351 priv = GTK_EXPANDER (widget)->priv;
352 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
354 border_width = GTK_CONTAINER (widget)->border_width;
356 attributes.window_type = GDK_WINDOW_CHILD;
357 attributes.x = widget->allocation.x + border_width;
358 attributes.y = widget->allocation.y + border_width;
359 attributes.width = widget->allocation.width - 2 * border_width;
360 attributes.height = widget->allocation.height - 2 * border_width;
361 attributes.wclass = GDK_INPUT_ONLY;
362 attributes.event_mask = gtk_widget_get_events (widget) |
364 GDK_BUTTON_PRESS_MASK |
365 GDK_BUTTON_RELEASE_MASK |
366 GDK_ENTER_NOTIFY_MASK |
367 GDK_LEAVE_NOTIFY_MASK;
369 attributes_mask = GDK_WA_X | GDK_WA_Y;
371 widget->window = gtk_widget_get_parent_window (widget);
372 g_object_ref (widget->window);
374 priv->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
375 &attributes, attributes_mask);
376 gdk_window_set_user_data (priv->event_window, widget);
378 widget->style = gtk_style_attach (widget->style, widget->window);
379 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
383 gtk_expander_unrealize (GtkWidget *widget)
385 GtkExpanderPrivate *priv = GTK_EXPANDER (widget)->priv;
387 if (priv->event_window)
389 gdk_window_set_user_data (priv->event_window, NULL);
390 gdk_window_destroy (priv->event_window);
391 priv->event_window = NULL;
394 if (priv->animation_timeout)
396 g_source_remove (priv->animation_timeout);
397 priv->animation_timeout = 0;
400 GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
404 gtk_expander_size_request (GtkWidget *widget,
405 GtkRequisition *requisition)
407 GtkExpander *expander;
409 GtkExpanderPrivate *priv;
412 gint expander_spacing;
413 gboolean interior_focus;
417 bin = GTK_BIN (widget);
418 expander = GTK_EXPANDER (widget);
419 priv = expander->priv;
421 border_width = GTK_CONTAINER (widget)->border_width;
423 gtk_widget_style_get (widget,
424 "interior-focus", &interior_focus,
425 "focus-line-width", &focus_width,
426 "focus-padding", &focus_pad,
427 "expander-size", &expander_size,
428 "expander-spacing", &expander_spacing,
431 requisition->width = expander_size + 2 * expander_spacing +
432 2 * focus_width + 2 * focus_pad;
433 requisition->height = interior_focus ? (2 * focus_width + 2 * focus_pad) : 0;
435 if (priv->label_widget && GTK_WIDGET_VISIBLE (priv->label_widget))
437 GtkRequisition label_requisition;
439 gtk_widget_size_request (priv->label_widget, &label_requisition);
441 requisition->width += label_requisition.width;
442 requisition->height += label_requisition.height;
445 requisition->height = MAX (expander_size + 2 * expander_spacing, requisition->height);
448 requisition->height += 2 * focus_width + 2 * focus_pad;
450 if (bin->child && GTK_WIDGET_CHILD_VISIBLE (bin->child))
452 GtkRequisition child_requisition;
454 gtk_widget_size_request (bin->child, &child_requisition);
456 requisition->width = MAX (requisition->width, child_requisition.width);
457 requisition->height += child_requisition.height + priv->spacing;
460 requisition->width += 2 * border_width;
461 requisition->height += 2 * border_width;
465 get_expander_bounds (GtkExpander *expander,
470 GtkExpanderPrivate *priv;
473 gint expander_spacing;
474 gboolean interior_focus;
479 widget = GTK_WIDGET (expander);
480 bin = GTK_BIN (expander);
481 priv = expander->priv;
483 border_width = GTK_CONTAINER (expander)->border_width;
485 gtk_widget_style_get (widget,
486 "interior-focus", &interior_focus,
487 "focus-line-width", &focus_width,
488 "focus-padding", &focus_pad,
489 "expander-size", &expander_size,
490 "expander-spacing", &expander_spacing,
493 ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
495 rect->x = widget->allocation.x + border_width;
496 rect->y = widget->allocation.y + border_width;
499 rect->x += expander_spacing;
501 rect->x += widget->allocation.width - 2 * border_width -
502 expander_spacing - expander_size;
504 if (priv->label_widget && GTK_WIDGET_VISIBLE (priv->label_widget))
506 GtkAllocation label_allocation;
508 label_allocation = priv->label_widget->allocation;
510 if (expander_size < label_allocation.height)
511 rect->y += focus_width + focus_pad + (label_allocation.height - expander_size) / 2;
513 rect->y += expander_spacing;
517 rect->y += expander_spacing;
523 rect->x += focus_width + focus_pad;
525 rect->x -= focus_width + focus_pad;
526 rect->y += focus_width + focus_pad;
529 rect->width = rect->height = expander_size;
533 gtk_expander_size_allocate (GtkWidget *widget,
534 GtkAllocation *allocation)
536 GtkExpander *expander;
538 GtkExpanderPrivate *priv;
539 GtkRequisition child_requisition;
540 gboolean child_visible = FALSE;
543 gint expander_spacing;
544 gboolean interior_focus;
549 expander = GTK_EXPANDER (widget);
550 bin = GTK_BIN (widget);
551 priv = expander->priv;
553 border_width = GTK_CONTAINER (widget)->border_width;
555 gtk_widget_style_get (widget,
556 "interior-focus", &interior_focus,
557 "focus-line-width", &focus_width,
558 "focus-padding", &focus_pad,
559 "expander-size", &expander_size,
560 "expander-spacing", &expander_spacing,
563 child_requisition.width = 0;
564 child_requisition.height = 0;
565 if (bin->child && GTK_WIDGET_CHILD_VISIBLE (bin->child))
567 child_visible = TRUE;
568 gtk_widget_get_child_requisition (bin->child, &child_requisition);
571 widget->allocation = *allocation;
573 if (priv->label_widget && GTK_WIDGET_VISIBLE (priv->label_widget))
575 GtkAllocation label_allocation;
576 GtkRequisition label_requisition;
579 gtk_widget_get_child_requisition (priv->label_widget, &label_requisition);
581 ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
583 label_allocation.x = widget->allocation.x + border_width + focus_width + focus_pad;
585 label_allocation.x += expander_size + 2 * expander_spacing;
586 label_allocation.y = widget->allocation.y + border_width + focus_width + focus_pad;
588 label_allocation.width = MIN (label_requisition.width,
589 allocation->width - 2 * border_width -
590 expander_size - 2 * expander_spacing -
591 2 * focus_width - 2 * focus_pad);
592 label_allocation.width = MAX (label_allocation.width, 1);
594 label_allocation.height = MIN (label_requisition.height,
595 allocation->height - 2 * border_width -
596 2 * focus_width - 2 * focus_pad -
597 (child_visible ? priv->spacing : 0));
598 label_allocation.height = MAX (label_allocation.height, 1);
600 gtk_widget_size_allocate (priv->label_widget, &label_allocation);
602 label_height = label_allocation.height;
609 if (GTK_WIDGET_REALIZED (widget))
613 get_expander_bounds (expander, &rect);
615 gdk_window_move_resize (priv->event_window,
616 allocation->x + border_width, rect.y,
617 MAX (allocation->width - 2 * border_width, 1), rect.width);
622 GtkAllocation child_allocation;
625 top_height = MAX (2 * expander_spacing + expander_size,
627 (interior_focus ? 2 * focus_width + 2 * focus_pad : 0));
629 child_allocation.x = widget->allocation.x + border_width;
630 child_allocation.y = widget->allocation.y + border_width + top_height + priv->spacing;
633 child_allocation.y += 2 * focus_width + 2 * focus_pad;
635 child_allocation.width = MAX (allocation->width - 2 * border_width, 1);
637 child_allocation.height = allocation->height - top_height -
638 2 * border_width - priv->spacing -
639 (!interior_focus ? 2 * focus_width + 2 * focus_pad : 0);
640 child_allocation.height = MAX (child_allocation.height, 1);
642 gtk_widget_size_allocate (bin->child, &child_allocation);
647 gtk_expander_map (GtkWidget *widget)
649 GtkExpanderPrivate *priv = GTK_EXPANDER (widget)->priv;
651 if (priv->label_widget)
652 gtk_widget_map (priv->label_widget);
654 GTK_WIDGET_CLASS (parent_class)->map (widget);
656 if (priv->event_window)
657 gdk_window_show (priv->event_window);
661 gtk_expander_unmap (GtkWidget *widget)
663 GtkExpanderPrivate *priv = GTK_EXPANDER (widget)->priv;
665 if (priv->event_window)
666 gdk_window_hide (priv->event_window);
668 GTK_WIDGET_CLASS (parent_class)->unmap (widget);
670 if (priv->label_widget)
671 gtk_widget_unmap (priv->label_widget);
675 gtk_expander_paint (GtkExpander *expander)
681 widget = GTK_WIDGET (expander);
683 get_expander_bounds (expander, &clip);
685 state = widget->state;
686 if (expander->priv->prelight)
687 state = GTK_STATE_PRELIGHT;
689 gtk_paint_expander (widget->style,
695 clip.x + clip.width / 2,
696 clip.y + clip.height / 2,
697 expander->priv->expander_style);
701 gtk_expander_paint_focus (GtkExpander *expander,
705 GtkExpanderPrivate *priv;
706 gint x, y, width, height;
707 gboolean interior_focus;
712 gint expander_spacing;
715 widget = GTK_WIDGET (expander);
716 priv = expander->priv;
718 border_width = GTK_CONTAINER (widget)->border_width;
720 gtk_widget_style_get (widget,
721 "interior-focus", &interior_focus,
722 "focus-line-width", &focus_width,
723 "focus-padding", &focus_pad,
724 "expander-size", &expander_size,
725 "expander-spacing", &expander_spacing,
728 ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
730 x = widget->allocation.x + border_width;
731 y = widget->allocation.y + border_width;
733 if (ltr && interior_focus)
734 x += expander_spacing * 2 + expander_size;
738 if (priv->label_widget && GTK_WIDGET_VISIBLE (priv->label_widget))
740 GtkAllocation label_allocation = priv->label_widget->allocation;
742 width = label_allocation.width;
743 height = label_allocation.height;
748 width += expander_size + 2 * expander_spacing;
749 height = MAX (height, expander_size + 2 * expander_spacing);
752 width += 2 * focus_pad + 2 * focus_width;
753 height += 2 * focus_pad + 2 * focus_width;
755 gtk_paint_focus (widget->style, widget->window, GTK_WIDGET_STATE (widget),
756 area, widget, "expander",
757 x, y, width, height);
761 gtk_expander_expose (GtkWidget *widget,
762 GdkEventExpose *event)
764 if (GTK_WIDGET_DRAWABLE (widget))
766 GtkExpander *expander = GTK_EXPANDER (widget);
768 gtk_expander_paint (expander);
770 if (GTK_WIDGET_HAS_FOCUS (expander))
771 gtk_expander_paint_focus (expander, &event->area);
773 if (expander->priv->label_widget)
774 gtk_container_propagate_expose (GTK_CONTAINER (widget),
775 expander->priv->label_widget,
778 GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
785 gtk_expander_button_press (GtkWidget *widget,
786 GdkEventButton *event)
788 GtkExpander *expander = GTK_EXPANDER (widget);
790 if (event->button == 1 && event->window == expander->priv->event_window)
792 expander->priv->button_down = TRUE;
800 gtk_expander_button_release (GtkWidget *widget,
801 GdkEventButton *event)
803 GtkExpander *expander = GTK_EXPANDER (widget);
805 if (event->button == 1 && expander->priv->button_down)
807 gtk_expander_activate (expander);
808 expander->priv->button_down = FALSE;
816 gtk_expander_grab_notify (GtkWidget *widget,
817 gboolean was_grabbed)
820 GTK_EXPANDER (widget)->priv->button_down = FALSE;
824 gtk_expander_state_changed (GtkWidget *widget,
825 GtkStateType previous_state)
827 if (!GTK_WIDGET_IS_SENSITIVE (widget))
828 GTK_EXPANDER (widget)->priv->button_down = FALSE;
832 gtk_expander_redraw_expander (GtkExpander *expander)
836 get_expander_bounds (expander, &bounds);
838 gtk_widget_queue_draw_area (GTK_WIDGET (expander),
846 gtk_expander_enter_notify (GtkWidget *widget,
847 GdkEventCrossing *event)
849 GtkExpander *expander = GTK_EXPANDER (widget);
850 GtkWidget *event_widget;
852 event_widget = gtk_get_event_widget ((GdkEvent *) event);
854 if (event_widget == widget &&
855 event->detail != GDK_NOTIFY_INFERIOR)
857 expander->priv->prelight = TRUE;
858 gtk_expander_redraw_expander (expander);
865 gtk_expander_leave_notify (GtkWidget *widget,
866 GdkEventCrossing *event)
868 GtkExpander *expander = GTK_EXPANDER (widget);
869 GtkWidget *event_widget;
871 event_widget = gtk_get_event_widget ((GdkEvent *) event);
873 if (event_widget == widget &&
874 event->detail != GDK_NOTIFY_INFERIOR)
876 expander->priv->prelight = FALSE;
877 gtk_expander_redraw_expander (expander);
892 focus_current_site (GtkExpander *expander,
893 GtkDirectionType direction)
895 GtkWidget *current_focus;
897 current_focus = GTK_CONTAINER (expander)->focus_child;
902 return gtk_widget_child_focus (current_focus, direction);
906 focus_in_site (GtkExpander *expander,
908 GtkDirectionType direction)
913 gtk_widget_grab_focus (GTK_WIDGET (expander));
916 if (expander->priv->label_widget)
917 return gtk_widget_child_focus (expander->priv->label_widget, direction);
922 GtkWidget *child = gtk_bin_get_child (GTK_BIN (expander));
924 if (child && GTK_WIDGET_CHILD_VISIBLE (child))
925 return gtk_widget_child_focus (child, direction);
933 g_assert_not_reached ();
938 get_next_site (GtkExpander *expander,
940 GtkDirectionType direction)
944 ltr = gtk_widget_get_direction (GTK_WIDGET (expander)) != GTK_TEXT_DIR_RTL;
951 case GTK_DIR_TAB_BACKWARD:
955 case GTK_DIR_TAB_FORWARD:
963 case GTK_DIR_TAB_BACKWARD:
967 return ltr ? FOCUS_NONE : FOCUS_LABEL;
968 case GTK_DIR_TAB_FORWARD:
972 return ltr ? FOCUS_LABEL : FOCUS_NONE;
978 case GTK_DIR_TAB_BACKWARD:
982 return ltr ? FOCUS_WIDGET : FOCUS_CHILD;
983 case GTK_DIR_TAB_FORWARD:
987 return ltr ? FOCUS_CHILD : FOCUS_WIDGET;
993 case GTK_DIR_TAB_BACKWARD:
997 case GTK_DIR_TAB_FORWARD:
1004 g_assert_not_reached ();
1009 gtk_expander_focus (GtkWidget *widget,
1010 GtkDirectionType direction)
1012 GtkExpander *expander = GTK_EXPANDER (widget);
1014 if (!focus_current_site (expander, direction))
1016 GtkWidget *old_focus_child;
1017 gboolean widget_is_focus;
1018 FocusSite site = FOCUS_NONE;
1020 widget_is_focus = gtk_widget_is_focus (widget);
1021 old_focus_child = GTK_CONTAINER (widget)->focus_child;
1023 if (old_focus_child && old_focus_child == expander->priv->label_widget)
1025 else if (old_focus_child)
1027 else if (widget_is_focus)
1028 site = FOCUS_WIDGET;
1030 while ((site = get_next_site (expander, site, direction)) != FOCUS_NONE)
1032 if (focus_in_site (expander, site, direction))
1043 gtk_expander_add (GtkContainer *container,
1046 GTK_CONTAINER_CLASS (parent_class)->add (container, widget);
1048 gtk_widget_set_child_visible (widget, GTK_EXPANDER (container)->priv->expanded);
1049 gtk_widget_queue_resize (GTK_WIDGET (container));
1053 gtk_expander_remove (GtkContainer *container,
1056 GtkExpander *expander = GTK_EXPANDER (container);
1058 if (GTK_EXPANDER (expander)->priv->label_widget == widget)
1059 gtk_expander_set_label_widget (expander, NULL);
1061 GTK_CONTAINER_CLASS (parent_class)->remove (container, widget);
1065 gtk_expander_forall (GtkContainer *container,
1066 gboolean include_internals,
1067 GtkCallback callback,
1068 gpointer callback_data)
1070 GtkBin *bin = GTK_BIN (container);
1071 GtkExpanderPrivate *priv = GTK_EXPANDER (container)->priv;
1074 (* callback) (bin->child, callback_data);
1076 if (priv->label_widget)
1077 (* callback) (priv->label_widget, callback_data);
1081 gtk_expander_activate (GtkExpander *expander)
1083 gtk_expander_set_expanded (expander, !expander->priv->expanded);
1089 * @label: the text of the label
1091 * Creates a new expander using @label as the text of the label.
1093 * Return value: a new #GtkExpander widget.
1098 gtk_expander_new (const gchar *label)
1100 return g_object_new (GTK_TYPE_EXPANDER, "label", label, NULL);
1104 * gtk_expander_new_with_mnemonic:
1105 * @label: the text of the label with an underscore in front of the
1106 * mnemonic character
1108 * Creates a new expander using @label as the text of the label.
1109 * If characters in @label are preceded by an underscore, they are underlined.
1110 * If you need a literal underscore character in a label, use '__' (two
1111 * underscores). The first underlined character represents a keyboard
1112 * accelerator called a mnemonic.
1113 * Pressing Alt and that key activates the button.
1115 * Return value: a new #GtkExpander widget.
1120 gtk_expander_new_with_mnemonic (const gchar *label)
1122 return g_object_new (GTK_TYPE_EXPANDER,
1124 "use_underline", TRUE,
1129 gtk_expander_animation_timeout (GtkExpander *expander)
1131 GtkExpanderPrivate *priv = expander->priv;
1133 gboolean finish = FALSE;
1135 if (GTK_WIDGET_REALIZED (expander))
1137 get_expander_bounds (expander, &area);
1138 gdk_window_invalidate_rect (GTK_WIDGET (expander)->window, &area, TRUE);
1143 if (priv->expander_style == GTK_EXPANDER_COLLAPSED)
1145 priv->expander_style = GTK_EXPANDER_SEMI_EXPANDED;
1149 priv->expander_style = GTK_EXPANDER_EXPANDED;
1155 if (priv->expander_style == GTK_EXPANDER_EXPANDED)
1157 priv->expander_style = GTK_EXPANDER_SEMI_COLLAPSED;
1161 priv->expander_style = GTK_EXPANDER_COLLAPSED;
1168 priv->animation_timeout = 0;
1169 gtk_widget_set_child_visible (GTK_BIN (expander)->child, priv->expanded);
1170 gtk_widget_queue_resize (GTK_WIDGET (expander));
1177 gtk_expander_start_animation (GtkExpander *expander)
1179 GtkExpanderPrivate *priv = expander->priv;
1181 if (!GTK_WIDGET_REALIZED (expander))
1184 if (priv->animation_timeout)
1185 g_source_remove (priv->animation_timeout);
1187 priv->animation_timeout =
1189 (GSourceFunc) gtk_expander_animation_timeout,
1194 * gtk_expander_set_expanded:
1195 * @expander: a #GtkExpander
1196 * @expanded: whether the child widget is revealed
1198 * Sets the state of the expander. Set to %TRUE, if you want
1199 * the child widget to be revealed, and %FALSE if you want the
1200 * child widget to be hidden.
1205 gtk_expander_set_expanded (GtkExpander *expander,
1208 GtkExpanderPrivate *priv;
1210 g_return_if_fail (GTK_IS_EXPANDER (expander));
1212 priv = expander->priv;
1214 expanded = expanded != FALSE;
1216 if (priv->expanded != expanded)
1218 priv->expanded = expanded;
1220 if (GTK_WIDGET_VISIBLE (expander))
1221 gtk_expander_start_animation (expander);
1223 else if (GTK_BIN (expander)->child)
1225 priv->expander_style = expanded ? GTK_EXPANDER_EXPANDED :
1226 GTK_EXPANDER_COLLAPSED;
1227 gtk_widget_set_child_visible (GTK_BIN (expander)->child, priv->expanded);
1228 gtk_widget_queue_resize (GTK_WIDGET (expander));
1231 g_object_notify (G_OBJECT (expander), "expanded");
1236 * gtk_expander_get_expanded:
1237 * @expander:a #GtkExpander
1239 * Queries a #GtkExpander and returns its current state. Returns %TRUE
1240 * if the child widget is revealed.
1242 * See gtk_expander_set_expanded().
1244 * Return value: the current state of the expander.
1249 gtk_expander_get_expanded (GtkExpander *expander)
1251 g_return_val_if_fail (GTK_IS_EXPANDER (expander), FALSE);
1253 return expander->priv->expanded;
1257 * gtk_expander_set_spacing:
1258 * @expander: a #GtkExpander
1259 * @spacing: distance between the expander and child in pixels.
1261 * Sets the spacing field of @expander, which is the number of pixels to
1262 * place between expander and the child.
1267 gtk_expander_set_spacing (GtkExpander *expander,
1270 g_return_if_fail (GTK_IS_EXPANDER (expander));
1271 g_return_if_fail (spacing >= 0);
1273 if (expander->priv->spacing != spacing)
1275 expander->priv->spacing = spacing;
1277 gtk_widget_queue_resize (GTK_WIDGET (expander));
1279 g_object_notify (G_OBJECT (expander), "spacing");
1284 * gtk_expander_get_spacing:
1285 * @expander: a #GtkExpander
1287 * Gets the value set by gtk_expander_set_spacing().
1289 * Return value: spacing between the expander and child.
1294 gtk_expander_get_spacing (GtkExpander *expander)
1296 g_return_val_if_fail (GTK_IS_EXPANDER (expander), 0);
1298 return expander->priv->spacing;
1302 * gtk_expander_set_label:
1303 * @expander: a #GtkExpander
1306 * Sets the text of the label of the expander to @label.
1308 * This will also clear any previously set labels.
1313 gtk_expander_set_label (GtkExpander *expander,
1316 g_return_if_fail (GTK_IS_EXPANDER (expander));
1320 gtk_expander_set_label_widget (expander, NULL);
1326 child = gtk_label_new (label);
1327 gtk_label_set_use_underline (GTK_LABEL (child), expander->priv->use_underline);
1328 gtk_widget_show (child);
1330 gtk_expander_set_label_widget (expander, child);
1333 g_object_notify (G_OBJECT (expander), "label");
1337 * gtk_expander_get_label:
1338 * @expander: a #GtkExpander
1340 * Fetches the text from the label of the expander, as set by
1341 * gtk_expander_set_label(). If the label text has not
1342 * been set the return value will be %NULL. This will be the
1343 * case if you create an empty button with gtk_button_new() to
1344 * use as a container.
1346 * Return value: The text of the label widget. This string is owned
1347 * by the widget and must not be modified or freed.
1351 G_CONST_RETURN char *
1352 gtk_expander_get_label (GtkExpander *expander)
1354 GtkExpanderPrivate *priv;
1356 g_return_val_if_fail (GTK_IS_EXPANDER (expander), NULL);
1358 priv = expander->priv;
1360 if (priv->label_widget && GTK_IS_LABEL (priv->label_widget))
1361 return gtk_label_get_text (GTK_LABEL (priv->label_widget));
1367 * gtk_expander_set_use_underline:
1368 * @expander: a #GtkExpander
1369 * @use_underline: %TRUE if underlines in the text indicate mnemonics
1371 * If true, an underline in the text of the expander label indicates
1372 * the next character should be used for the mnemonic accelerator key.
1377 gtk_expander_set_use_underline (GtkExpander *expander,
1378 gboolean use_underline)
1380 GtkExpanderPrivate *priv;
1382 g_return_if_fail (GTK_IS_EXPANDER (expander));
1384 priv = expander->priv;
1386 use_underline = use_underline != FALSE;
1388 if (priv->use_underline != use_underline)
1390 priv->use_underline = use_underline;
1392 if (priv->label_widget && GTK_IS_LABEL (priv->label_widget))
1393 gtk_label_set_use_underline (GTK_LABEL (priv->label_widget), use_underline);
1395 g_object_notify (G_OBJECT (expander), "use_underline");
1400 * gtk_expander_get_use_underline:
1401 * @expander: a #GtkExpander
1403 * Returns whether an embedded underline in the expander label indicates a
1404 * mnemonic. See gtk_expander_set_use_underline().
1406 * Return value: %TRUE if an embedded underline in the expander label
1407 * indicates the mnemonic accelerator keys.
1412 gtk_expander_get_use_underline (GtkExpander *expander)
1414 g_return_val_if_fail (GTK_IS_EXPANDER (expander), FALSE);
1416 return expander->priv->use_underline;
1420 * gtk_expander_set_label_widget:
1421 * @expander: a #GtkExpander
1422 * @label_widget: the new label widget
1424 * Set the label widget for the expander. This is the widget
1425 * that will appear embedded alongside the expander arrow.
1430 gtk_expander_set_label_widget (GtkExpander *expander,
1431 GtkWidget *label_widget)
1433 GtkExpanderPrivate *priv;
1435 g_return_if_fail (GTK_IS_EXPANDER (expander));
1436 g_return_if_fail (label_widget == NULL || GTK_IS_WIDGET (label_widget));
1437 g_return_if_fail (label_widget == NULL || label_widget->parent == NULL);
1439 priv = expander->priv;
1441 if (priv->label_widget == label_widget)
1444 if (priv->label_widget)
1445 gtk_widget_unparent (priv->label_widget);
1447 priv->label_widget = label_widget;
1451 priv->label_widget = label_widget;
1452 gtk_widget_set_parent (label_widget, GTK_WIDGET (expander));
1455 if (GTK_WIDGET_VISIBLE (expander))
1456 gtk_widget_queue_resize (GTK_WIDGET (expander));
1458 g_object_freeze_notify (G_OBJECT (expander));
1459 g_object_notify (G_OBJECT (expander), "label_widget");
1460 g_object_notify (G_OBJECT (expander), "label");
1461 g_object_thaw_notify (G_OBJECT (expander));
1465 * gtk_expander_get_label_widget:
1466 * @expander: a #GtkExpander
1468 * Retrieves the label widget for the frame. See
1469 * gtk_expander_set_label_widget().
1471 * Return value: the label widget, or %NULL if there is none.
1476 gtk_expander_get_label_widget (GtkExpander *expander)
1478 g_return_val_if_fail (GTK_IS_EXPANDER (expander), NULL);
1480 return expander->priv->label_widget;