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 "gtkbuildable.h"
30 #include "gtkcontainer.h"
31 #include "gtkmarshalers.h"
34 #include "gtkprivate.h"
35 #include <gdk/gdkkeysyms.h>
38 #define GTK_EXPANDER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_EXPANDER, GtkExpanderPrivate))
40 #define DEFAULT_EXPANDER_SIZE 10
41 #define DEFAULT_EXPANDER_SPACING 2
54 struct _GtkExpanderPrivate
56 GtkWidget *label_widget;
57 GdkWindow *event_window;
60 GtkExpanderStyle expander_style;
61 guint animation_timeout;
65 guint use_underline : 1;
67 guint button_down : 1;
71 static void gtk_expander_set_property (GObject *object,
75 static void gtk_expander_get_property (GObject *object,
80 static void gtk_expander_destroy (GtkObject *object);
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,
111 static void gtk_expander_drag_leave (GtkWidget *widget,
112 GdkDragContext *context,
115 static void gtk_expander_add (GtkContainer *container,
117 static void gtk_expander_remove (GtkContainer *container,
119 static void gtk_expander_forall (GtkContainer *container,
120 gboolean include_internals,
121 GtkCallback callback,
122 gpointer callback_data);
124 static void gtk_expander_activate (GtkExpander *expander);
126 static void get_expander_bounds (GtkExpander *expander,
130 static void gtk_expander_buildable_init (GtkBuildableIface *iface);
131 static void gtk_expander_buildable_add_child (GtkBuildable *buildable,
136 G_DEFINE_TYPE_WITH_CODE (GtkExpander, gtk_expander, GTK_TYPE_BIN,
137 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
138 gtk_expander_buildable_init))
141 gtk_expander_class_init (GtkExpanderClass *klass)
143 GObjectClass *gobject_class;
144 GtkObjectClass *object_class;
145 GtkWidgetClass *widget_class;
146 GtkContainerClass *container_class;
148 gobject_class = (GObjectClass *) klass;
149 object_class = (GtkObjectClass *) klass;
150 widget_class = (GtkWidgetClass *) klass;
151 container_class = (GtkContainerClass *) klass;
153 gobject_class->set_property = gtk_expander_set_property;
154 gobject_class->get_property = gtk_expander_get_property;
156 object_class->destroy = gtk_expander_destroy;
158 widget_class->realize = gtk_expander_realize;
159 widget_class->unrealize = gtk_expander_unrealize;
160 widget_class->size_request = gtk_expander_size_request;
161 widget_class->size_allocate = gtk_expander_size_allocate;
162 widget_class->map = gtk_expander_map;
163 widget_class->unmap = gtk_expander_unmap;
164 widget_class->expose_event = gtk_expander_expose;
165 widget_class->button_press_event = gtk_expander_button_press;
166 widget_class->button_release_event = gtk_expander_button_release;
167 widget_class->enter_notify_event = gtk_expander_enter_notify;
168 widget_class->leave_notify_event = gtk_expander_leave_notify;
169 widget_class->focus = gtk_expander_focus;
170 widget_class->grab_notify = gtk_expander_grab_notify;
171 widget_class->state_changed = gtk_expander_state_changed;
172 widget_class->drag_motion = gtk_expander_drag_motion;
173 widget_class->drag_leave = gtk_expander_drag_leave;
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 P_("Whether the expander has been opened to reveal the child widget"),
189 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
191 g_object_class_install_property (gobject_class,
193 g_param_spec_string ("label",
195 P_("Text of the expander's label"),
197 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
199 g_object_class_install_property (gobject_class,
201 g_param_spec_boolean ("use-underline",
203 P_("If set, an underline in the text indicates the next character should be used for the mnemonic accelerator key"),
205 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
207 g_object_class_install_property (gobject_class,
209 g_param_spec_boolean ("use-markup",
211 P_("The text of the label includes XML markup. See pango_parse_markup()"),
213 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
215 g_object_class_install_property (gobject_class,
217 g_param_spec_int ("spacing",
219 P_("Space to put between the label and the child"),
223 GTK_PARAM_READWRITE));
225 g_object_class_install_property (gobject_class,
227 g_param_spec_object ("label-widget",
229 P_("A widget to display in place of the usual expander label"),
231 GTK_PARAM_READWRITE));
233 gtk_widget_class_install_style_property (widget_class,
234 g_param_spec_int ("expander-size",
236 P_("Size of the expander arrow"),
239 DEFAULT_EXPANDER_SIZE,
240 GTK_PARAM_READABLE));
242 gtk_widget_class_install_style_property (widget_class,
243 g_param_spec_int ("expander-spacing",
244 P_("Indicator Spacing"),
245 P_("Spacing around expander arrow"),
248 DEFAULT_EXPANDER_SPACING,
249 GTK_PARAM_READABLE));
251 widget_class->activate_signal =
252 g_signal_new (I_("activate"),
253 G_TYPE_FROM_CLASS (gobject_class),
254 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
255 G_STRUCT_OFFSET (GtkExpanderClass, activate),
257 _gtk_marshal_VOID__VOID,
262 gtk_expander_init (GtkExpander *expander)
264 GtkExpanderPrivate *priv;
266 expander->priv = priv = GTK_EXPANDER_GET_PRIVATE (expander);
268 gtk_widget_set_can_focus (GTK_WIDGET (expander), TRUE);
269 gtk_widget_set_has_window (GTK_WIDGET (expander), FALSE);
271 priv->label_widget = NULL;
272 priv->event_window = NULL;
275 priv->expander_style = GTK_EXPANDER_COLLAPSED;
276 priv->animation_timeout = 0;
278 priv->expanded = FALSE;
279 priv->use_underline = FALSE;
280 priv->use_markup = FALSE;
281 priv->button_down = FALSE;
282 priv->prelight = FALSE;
283 priv->expand_timer = 0;
285 gtk_drag_dest_set (GTK_WIDGET (expander), 0, NULL, 0, 0);
286 gtk_drag_dest_set_track_motion (GTK_WIDGET (expander), TRUE);
290 gtk_expander_buildable_add_child (GtkBuildable *buildable,
296 gtk_container_add (GTK_CONTAINER (buildable), GTK_WIDGET (child));
297 else if (strcmp (type, "label") == 0)
298 gtk_expander_set_label_widget (GTK_EXPANDER (buildable), GTK_WIDGET (child));
300 GTK_BUILDER_WARN_INVALID_CHILD_TYPE (GTK_EXPANDER (buildable), type);
304 gtk_expander_buildable_init (GtkBuildableIface *iface)
306 iface->add_child = gtk_expander_buildable_add_child;
310 gtk_expander_set_property (GObject *object,
315 GtkExpander *expander = GTK_EXPANDER (object);
320 gtk_expander_set_expanded (expander, g_value_get_boolean (value));
323 gtk_expander_set_label (expander, g_value_get_string (value));
325 case PROP_USE_UNDERLINE:
326 gtk_expander_set_use_underline (expander, g_value_get_boolean (value));
328 case PROP_USE_MARKUP:
329 gtk_expander_set_use_markup (expander, g_value_get_boolean (value));
332 gtk_expander_set_spacing (expander, g_value_get_int (value));
334 case PROP_LABEL_WIDGET:
335 gtk_expander_set_label_widget (expander, g_value_get_object (value));
338 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
344 gtk_expander_get_property (GObject *object,
349 GtkExpander *expander = GTK_EXPANDER (object);
350 GtkExpanderPrivate *priv = expander->priv;
355 g_value_set_boolean (value, priv->expanded);
358 g_value_set_string (value, gtk_expander_get_label (expander));
360 case PROP_USE_UNDERLINE:
361 g_value_set_boolean (value, priv->use_underline);
363 case PROP_USE_MARKUP:
364 g_value_set_boolean (value, priv->use_markup);
367 g_value_set_int (value, priv->spacing);
369 case PROP_LABEL_WIDGET:
370 g_value_set_object (value,
372 G_OBJECT (priv->label_widget) : NULL);
375 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
381 gtk_expander_destroy (GtkObject *object)
383 GtkExpanderPrivate *priv = GTK_EXPANDER (object)->priv;
385 if (priv->animation_timeout)
387 g_source_remove (priv->animation_timeout);
388 priv->animation_timeout = 0;
391 GTK_OBJECT_CLASS (gtk_expander_parent_class)->destroy (object);
395 gtk_expander_realize (GtkWidget *widget)
397 GtkExpanderPrivate *priv;
398 GdkWindowAttr attributes;
399 gint attributes_mask;
401 GdkRectangle expander_rect;
404 priv = GTK_EXPANDER (widget)->priv;
405 gtk_widget_set_realized (widget, TRUE);
407 border_width = GTK_CONTAINER (widget)->border_width;
409 get_expander_bounds (GTK_EXPANDER (widget), &expander_rect);
411 if (priv->label_widget && gtk_widget_get_visible (priv->label_widget))
413 GtkRequisition label_requisition;
415 gtk_widget_get_child_requisition (priv->label_widget, &label_requisition);
416 label_height = label_requisition.height;
421 attributes.window_type = GDK_WINDOW_CHILD;
422 attributes.x = widget->allocation.x + border_width;
423 attributes.y = widget->allocation.y + border_width;
424 attributes.width = MAX (widget->allocation.width - 2 * border_width, 1);
425 attributes.height = MAX (expander_rect.height, label_height - 2 * border_width);
426 attributes.wclass = GDK_INPUT_ONLY;
427 attributes.event_mask = gtk_widget_get_events (widget) |
428 GDK_BUTTON_PRESS_MASK |
429 GDK_BUTTON_RELEASE_MASK |
430 GDK_ENTER_NOTIFY_MASK |
431 GDK_LEAVE_NOTIFY_MASK;
433 attributes_mask = GDK_WA_X | GDK_WA_Y;
435 widget->window = gtk_widget_get_parent_window (widget);
436 g_object_ref (widget->window);
438 priv->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
439 &attributes, attributes_mask);
440 gdk_window_set_user_data (priv->event_window, widget);
442 widget->style = gtk_style_attach (widget->style, widget->window);
446 gtk_expander_unrealize (GtkWidget *widget)
448 GtkExpanderPrivate *priv = GTK_EXPANDER (widget)->priv;
450 if (priv->event_window)
452 gdk_window_set_user_data (priv->event_window, NULL);
453 gdk_window_destroy (priv->event_window);
454 priv->event_window = NULL;
457 GTK_WIDGET_CLASS (gtk_expander_parent_class)->unrealize (widget);
461 gtk_expander_size_request (GtkWidget *widget,
462 GtkRequisition *requisition)
464 GtkExpander *expander;
466 GtkExpanderPrivate *priv;
469 gint expander_spacing;
470 gboolean interior_focus;
474 bin = GTK_BIN (widget);
475 expander = GTK_EXPANDER (widget);
476 priv = expander->priv;
478 border_width = GTK_CONTAINER (widget)->border_width;
480 gtk_widget_style_get (widget,
481 "interior-focus", &interior_focus,
482 "focus-line-width", &focus_width,
483 "focus-padding", &focus_pad,
484 "expander-size", &expander_size,
485 "expander-spacing", &expander_spacing,
488 requisition->width = expander_size + 2 * expander_spacing +
489 2 * focus_width + 2 * focus_pad;
490 requisition->height = interior_focus ? (2 * focus_width + 2 * focus_pad) : 0;
492 if (priv->label_widget && gtk_widget_get_visible (priv->label_widget))
494 GtkRequisition label_requisition;
496 gtk_widget_size_request (priv->label_widget, &label_requisition);
498 requisition->width += label_requisition.width;
499 requisition->height += label_requisition.height;
502 requisition->height = MAX (expander_size + 2 * expander_spacing, requisition->height);
505 requisition->height += 2 * focus_width + 2 * focus_pad;
507 if (bin->child && GTK_WIDGET_CHILD_VISIBLE (bin->child))
509 GtkRequisition child_requisition;
511 gtk_widget_size_request (bin->child, &child_requisition);
513 requisition->width = MAX (requisition->width, child_requisition.width);
514 requisition->height += child_requisition.height + priv->spacing;
517 requisition->width += 2 * border_width;
518 requisition->height += 2 * border_width;
522 get_expander_bounds (GtkExpander *expander,
526 GtkExpanderPrivate *priv;
529 gint expander_spacing;
530 gboolean interior_focus;
535 widget = GTK_WIDGET (expander);
536 priv = expander->priv;
538 border_width = GTK_CONTAINER (expander)->border_width;
540 gtk_widget_style_get (widget,
541 "interior-focus", &interior_focus,
542 "focus-line-width", &focus_width,
543 "focus-padding", &focus_pad,
544 "expander-size", &expander_size,
545 "expander-spacing", &expander_spacing,
548 ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
550 rect->x = widget->allocation.x + border_width;
551 rect->y = widget->allocation.y + border_width;
554 rect->x += expander_spacing;
556 rect->x += widget->allocation.width - 2 * border_width -
557 expander_spacing - expander_size;
559 if (priv->label_widget && gtk_widget_get_visible (priv->label_widget))
561 GtkAllocation label_allocation;
563 label_allocation = priv->label_widget->allocation;
565 if (expander_size < label_allocation.height)
566 rect->y += focus_width + focus_pad + (label_allocation.height - expander_size) / 2;
568 rect->y += expander_spacing;
572 rect->y += expander_spacing;
578 rect->x += focus_width + focus_pad;
580 rect->x -= focus_width + focus_pad;
581 rect->y += focus_width + focus_pad;
584 rect->width = rect->height = expander_size;
588 gtk_expander_size_allocate (GtkWidget *widget,
589 GtkAllocation *allocation)
591 GtkExpander *expander;
593 GtkExpanderPrivate *priv;
594 GtkRequisition child_requisition;
595 gboolean child_visible = FALSE;
598 gint expander_spacing;
599 gboolean interior_focus;
604 expander = GTK_EXPANDER (widget);
605 bin = GTK_BIN (widget);
606 priv = expander->priv;
608 border_width = GTK_CONTAINER (widget)->border_width;
610 gtk_widget_style_get (widget,
611 "interior-focus", &interior_focus,
612 "focus-line-width", &focus_width,
613 "focus-padding", &focus_pad,
614 "expander-size", &expander_size,
615 "expander-spacing", &expander_spacing,
618 child_requisition.width = 0;
619 child_requisition.height = 0;
620 if (bin->child && GTK_WIDGET_CHILD_VISIBLE (bin->child))
622 child_visible = TRUE;
623 gtk_widget_get_child_requisition (bin->child, &child_requisition);
626 widget->allocation = *allocation;
628 if (priv->label_widget && gtk_widget_get_visible (priv->label_widget))
630 GtkAllocation label_allocation;
631 GtkRequisition label_requisition;
634 gtk_widget_get_child_requisition (priv->label_widget, &label_requisition);
636 ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
639 label_allocation.x = (widget->allocation.x +
640 border_width + focus_width + focus_pad +
641 expander_size + 2 * expander_spacing);
643 label_allocation.x = (widget->allocation.x + widget->allocation.width -
644 (label_requisition.width +
645 border_width + focus_width + focus_pad +
646 expander_size + 2 * expander_spacing));
648 label_allocation.y = widget->allocation.y + border_width + focus_width + focus_pad;
650 label_allocation.width = MIN (label_requisition.width,
651 allocation->width - 2 * border_width -
652 expander_size - 2 * expander_spacing -
653 2 * focus_width - 2 * focus_pad);
654 label_allocation.width = MAX (label_allocation.width, 1);
656 label_allocation.height = MIN (label_requisition.height,
657 allocation->height - 2 * border_width -
658 2 * focus_width - 2 * focus_pad -
659 (child_visible ? priv->spacing : 0));
660 label_allocation.height = MAX (label_allocation.height, 1);
662 gtk_widget_size_allocate (priv->label_widget, &label_allocation);
664 label_height = label_allocation.height;
671 if (gtk_widget_get_realized (widget))
675 get_expander_bounds (expander, &rect);
677 gdk_window_move_resize (priv->event_window,
678 allocation->x + border_width,
679 allocation->y + border_width,
680 MAX (allocation->width - 2 * border_width, 1),
681 MAX (rect.height, label_height - 2 * border_width));
686 GtkAllocation child_allocation;
689 top_height = MAX (2 * expander_spacing + expander_size,
691 (interior_focus ? 2 * focus_width + 2 * focus_pad : 0));
693 child_allocation.x = widget->allocation.x + border_width;
694 child_allocation.y = widget->allocation.y + border_width + top_height + priv->spacing;
697 child_allocation.y += 2 * focus_width + 2 * focus_pad;
699 child_allocation.width = MAX (allocation->width - 2 * border_width, 1);
701 child_allocation.height = allocation->height - top_height -
702 2 * border_width - priv->spacing -
703 (!interior_focus ? 2 * focus_width + 2 * focus_pad : 0);
704 child_allocation.height = MAX (child_allocation.height, 1);
706 gtk_widget_size_allocate (bin->child, &child_allocation);
711 gtk_expander_map (GtkWidget *widget)
713 GtkExpanderPrivate *priv = GTK_EXPANDER (widget)->priv;
715 if (priv->label_widget)
716 gtk_widget_map (priv->label_widget);
718 GTK_WIDGET_CLASS (gtk_expander_parent_class)->map (widget);
720 if (priv->event_window)
721 gdk_window_show (priv->event_window);
725 gtk_expander_unmap (GtkWidget *widget)
727 GtkExpanderPrivate *priv = GTK_EXPANDER (widget)->priv;
729 if (priv->event_window)
730 gdk_window_hide (priv->event_window);
732 GTK_WIDGET_CLASS (gtk_expander_parent_class)->unmap (widget);
734 if (priv->label_widget)
735 gtk_widget_unmap (priv->label_widget);
739 gtk_expander_paint_prelight (GtkExpander *expander)
742 GtkContainer *container;
743 GtkExpanderPrivate *priv;
745 gboolean interior_focus;
749 int expander_spacing;
751 priv = expander->priv;
752 widget = GTK_WIDGET (expander);
753 container = GTK_CONTAINER (expander);
755 gtk_widget_style_get (widget,
756 "interior-focus", &interior_focus,
757 "focus-line-width", &focus_width,
758 "focus-padding", &focus_pad,
759 "expander-size", &expander_size,
760 "expander-spacing", &expander_spacing,
763 area.x = widget->allocation.x + container->border_width;
764 area.y = widget->allocation.y + container->border_width;
765 area.width = widget->allocation.width - (2 * container->border_width);
767 if (priv->label_widget && gtk_widget_get_visible (priv->label_widget))
768 area.height = priv->label_widget->allocation.height;
772 area.height += interior_focus ? (focus_width + focus_pad) * 2 : 0;
773 area.height = MAX (area.height, expander_size + 2 * expander_spacing);
774 area.height += !interior_focus ? (focus_width + focus_pad) * 2 : 0;
776 gtk_paint_flat_box (widget->style, widget->window,
778 GTK_SHADOW_ETCHED_OUT,
779 &area, widget, "expander",
781 area.width, area.height);
785 gtk_expander_paint (GtkExpander *expander)
791 widget = GTK_WIDGET (expander);
793 get_expander_bounds (expander, &clip);
795 state = widget->state;
796 if (expander->priv->prelight)
798 state = GTK_STATE_PRELIGHT;
800 gtk_expander_paint_prelight (expander);
803 gtk_paint_expander (widget->style,
809 clip.x + clip.width / 2,
810 clip.y + clip.height / 2,
811 expander->priv->expander_style);
815 gtk_expander_paint_focus (GtkExpander *expander,
819 GtkExpanderPrivate *priv;
821 gint x, y, width, height;
822 gboolean interior_focus;
827 gint expander_spacing;
830 widget = GTK_WIDGET (expander);
831 priv = expander->priv;
833 border_width = GTK_CONTAINER (widget)->border_width;
835 gtk_widget_style_get (widget,
836 "interior-focus", &interior_focus,
837 "focus-line-width", &focus_width,
838 "focus-padding", &focus_pad,
839 "expander-size", &expander_size,
840 "expander-spacing", &expander_spacing,
843 ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
847 if (priv->label_widget)
849 if (gtk_widget_get_visible (priv->label_widget))
851 GtkAllocation label_allocation = priv->label_widget->allocation;
853 width = label_allocation.width;
854 height = label_allocation.height;
857 width += 2 * focus_pad + 2 * focus_width;
858 height += 2 * focus_pad + 2 * focus_width;
860 x = widget->allocation.x + border_width;
861 y = widget->allocation.y + border_width;
866 x += expander_spacing * 2 + expander_size;
870 x += widget->allocation.width - 2 * border_width
871 - expander_spacing * 2 - expander_size - width;
876 width += expander_size + 2 * expander_spacing;
877 height = MAX (height, expander_size + 2 * expander_spacing);
882 get_expander_bounds (expander, &rect);
884 x = rect.x - focus_pad;
885 y = rect.y - focus_pad;
886 width = rect.width + 2 * focus_pad;
887 height = rect.height + 2 * focus_pad;
890 gtk_paint_focus (widget->style, widget->window, gtk_widget_get_state (widget),
891 area, widget, "expander",
892 x, y, width, height);
896 gtk_expander_expose (GtkWidget *widget,
897 GdkEventExpose *event)
899 if (gtk_widget_is_drawable (widget))
901 GtkExpander *expander = GTK_EXPANDER (widget);
903 gtk_expander_paint (expander);
905 if (gtk_widget_has_focus (widget))
906 gtk_expander_paint_focus (expander, &event->area);
908 GTK_WIDGET_CLASS (gtk_expander_parent_class)->expose_event (widget, event);
915 gtk_expander_button_press (GtkWidget *widget,
916 GdkEventButton *event)
918 GtkExpander *expander = GTK_EXPANDER (widget);
920 if (event->button == 1 && event->window == expander->priv->event_window)
922 expander->priv->button_down = TRUE;
930 gtk_expander_button_release (GtkWidget *widget,
931 GdkEventButton *event)
933 GtkExpander *expander = GTK_EXPANDER (widget);
935 if (event->button == 1 && expander->priv->button_down)
937 gtk_widget_activate (widget);
938 expander->priv->button_down = FALSE;
946 gtk_expander_grab_notify (GtkWidget *widget,
947 gboolean was_grabbed)
950 GTK_EXPANDER (widget)->priv->button_down = FALSE;
954 gtk_expander_state_changed (GtkWidget *widget,
955 GtkStateType previous_state)
957 if (!gtk_widget_is_sensitive (widget))
958 GTK_EXPANDER (widget)->priv->button_down = FALSE;
962 gtk_expander_redraw_expander (GtkExpander *expander)
966 widget = GTK_WIDGET (expander);
968 if (gtk_widget_get_realized (widget))
969 gdk_window_invalidate_rect (widget->window, &widget->allocation, FALSE);
973 gtk_expander_enter_notify (GtkWidget *widget,
974 GdkEventCrossing *event)
976 GtkExpander *expander = GTK_EXPANDER (widget);
977 GtkWidget *event_widget;
979 event_widget = gtk_get_event_widget ((GdkEvent *) event);
981 if (event_widget == widget &&
982 event->detail != GDK_NOTIFY_INFERIOR)
984 expander->priv->prelight = TRUE;
986 if (expander->priv->label_widget)
987 gtk_widget_set_state (expander->priv->label_widget, GTK_STATE_PRELIGHT);
989 gtk_expander_redraw_expander (expander);
996 gtk_expander_leave_notify (GtkWidget *widget,
997 GdkEventCrossing *event)
999 GtkExpander *expander = GTK_EXPANDER (widget);
1000 GtkWidget *event_widget;
1002 event_widget = gtk_get_event_widget ((GdkEvent *) event);
1004 if (event_widget == widget &&
1005 event->detail != GDK_NOTIFY_INFERIOR)
1007 expander->priv->prelight = FALSE;
1009 if (expander->priv->label_widget)
1010 gtk_widget_set_state (expander->priv->label_widget, GTK_STATE_NORMAL);
1012 gtk_expander_redraw_expander (expander);
1019 expand_timeout (gpointer data)
1021 GtkExpander *expander = GTK_EXPANDER (data);
1022 GtkExpanderPrivate *priv = expander->priv;
1024 priv->expand_timer = 0;
1025 gtk_expander_set_expanded (expander, TRUE);
1031 gtk_expander_drag_motion (GtkWidget *widget,
1032 GdkDragContext *context,
1037 GtkExpander *expander = GTK_EXPANDER (widget);
1038 GtkExpanderPrivate *priv = expander->priv;
1040 if (!priv->expanded && !priv->expand_timer)
1042 GtkSettings *settings;
1045 settings = gtk_widget_get_settings (widget);
1046 g_object_get (settings, "gtk-timeout-expand", &timeout, NULL);
1048 priv->expand_timer = gdk_threads_add_timeout (timeout, (GSourceFunc) expand_timeout, expander);
1055 gtk_expander_drag_leave (GtkWidget *widget,
1056 GdkDragContext *context,
1059 GtkExpander *expander = GTK_EXPANDER (widget);
1060 GtkExpanderPrivate *priv = expander->priv;
1062 if (priv->expand_timer)
1064 g_source_remove (priv->expand_timer);
1065 priv->expand_timer = 0;
1078 focus_current_site (GtkExpander *expander,
1079 GtkDirectionType direction)
1081 GtkWidget *current_focus;
1083 current_focus = GTK_CONTAINER (expander)->focus_child;
1088 return gtk_widget_child_focus (current_focus, direction);
1092 focus_in_site (GtkExpander *expander,
1094 GtkDirectionType direction)
1099 gtk_widget_grab_focus (GTK_WIDGET (expander));
1102 if (expander->priv->label_widget)
1103 return gtk_widget_child_focus (expander->priv->label_widget, direction);
1108 GtkWidget *child = gtk_bin_get_child (GTK_BIN (expander));
1110 if (child && GTK_WIDGET_CHILD_VISIBLE (child))
1111 return gtk_widget_child_focus (child, direction);
1119 g_assert_not_reached ();
1124 get_next_site (GtkExpander *expander,
1126 GtkDirectionType direction)
1130 ltr = gtk_widget_get_direction (GTK_WIDGET (expander)) != GTK_TEXT_DIR_RTL;
1137 case GTK_DIR_TAB_BACKWARD:
1141 case GTK_DIR_TAB_FORWARD:
1144 return FOCUS_WIDGET;
1149 case GTK_DIR_TAB_BACKWARD:
1153 return ltr ? FOCUS_NONE : FOCUS_LABEL;
1154 case GTK_DIR_TAB_FORWARD:
1158 return ltr ? FOCUS_LABEL : FOCUS_NONE;
1164 case GTK_DIR_TAB_BACKWARD:
1166 return FOCUS_WIDGET;
1168 return ltr ? FOCUS_WIDGET : FOCUS_CHILD;
1169 case GTK_DIR_TAB_FORWARD:
1173 return ltr ? FOCUS_CHILD : FOCUS_WIDGET;
1179 case GTK_DIR_TAB_BACKWARD:
1183 case GTK_DIR_TAB_FORWARD:
1190 g_assert_not_reached ();
1195 gtk_expander_focus (GtkWidget *widget,
1196 GtkDirectionType direction)
1198 GtkExpander *expander = GTK_EXPANDER (widget);
1200 if (!focus_current_site (expander, direction))
1202 GtkWidget *old_focus_child;
1203 gboolean widget_is_focus;
1204 FocusSite site = FOCUS_NONE;
1206 widget_is_focus = gtk_widget_is_focus (widget);
1207 old_focus_child = GTK_CONTAINER (widget)->focus_child;
1209 if (old_focus_child && old_focus_child == expander->priv->label_widget)
1211 else if (old_focus_child)
1213 else if (widget_is_focus)
1214 site = FOCUS_WIDGET;
1216 while ((site = get_next_site (expander, site, direction)) != FOCUS_NONE)
1218 if (focus_in_site (expander, site, direction))
1229 gtk_expander_add (GtkContainer *container,
1232 GTK_CONTAINER_CLASS (gtk_expander_parent_class)->add (container, widget);
1234 gtk_widget_set_child_visible (widget, GTK_EXPANDER (container)->priv->expanded);
1235 gtk_widget_queue_resize (GTK_WIDGET (container));
1239 gtk_expander_remove (GtkContainer *container,
1242 GtkExpander *expander = GTK_EXPANDER (container);
1244 if (GTK_EXPANDER (expander)->priv->label_widget == widget)
1245 gtk_expander_set_label_widget (expander, NULL);
1247 GTK_CONTAINER_CLASS (gtk_expander_parent_class)->remove (container, widget);
1251 gtk_expander_forall (GtkContainer *container,
1252 gboolean include_internals,
1253 GtkCallback callback,
1254 gpointer callback_data)
1256 GtkBin *bin = GTK_BIN (container);
1257 GtkExpanderPrivate *priv = GTK_EXPANDER (container)->priv;
1260 (* callback) (bin->child, callback_data);
1262 if (priv->label_widget)
1263 (* callback) (priv->label_widget, callback_data);
1267 gtk_expander_activate (GtkExpander *expander)
1269 gtk_expander_set_expanded (expander, !expander->priv->expanded);
1274 * @label: the text of the label
1276 * Creates a new expander using @label as the text of the label.
1278 * Return value: a new #GtkExpander widget.
1283 gtk_expander_new (const gchar *label)
1285 return g_object_new (GTK_TYPE_EXPANDER, "label", label, NULL);
1289 * gtk_expander_new_with_mnemonic:
1290 * @label: (allow-none): the text of the label with an underscore in front of the
1291 * mnemonic character
1293 * Creates a new expander using @label as the text of the label.
1294 * If characters in @label are preceded by an underscore, they are underlined.
1295 * If you need a literal underscore character in a label, use '__' (two
1296 * underscores). The first underlined character represents a keyboard
1297 * accelerator called a mnemonic.
1298 * Pressing Alt and that key activates the button.
1300 * Return value: a new #GtkExpander widget.
1305 gtk_expander_new_with_mnemonic (const gchar *label)
1307 return g_object_new (GTK_TYPE_EXPANDER,
1309 "use-underline", TRUE,
1314 gtk_expander_animation_timeout (GtkExpander *expander)
1316 GtkExpanderPrivate *priv = expander->priv;
1318 gboolean finish = FALSE;
1320 if (gtk_widget_get_realized (GTK_WIDGET (expander)))
1322 get_expander_bounds (expander, &area);
1323 gdk_window_invalidate_rect (GTK_WIDGET (expander)->window, &area, TRUE);
1328 if (priv->expander_style == GTK_EXPANDER_COLLAPSED)
1330 priv->expander_style = GTK_EXPANDER_SEMI_EXPANDED;
1334 priv->expander_style = GTK_EXPANDER_EXPANDED;
1340 if (priv->expander_style == GTK_EXPANDER_EXPANDED)
1342 priv->expander_style = GTK_EXPANDER_SEMI_COLLAPSED;
1346 priv->expander_style = GTK_EXPANDER_COLLAPSED;
1353 priv->animation_timeout = 0;
1354 if (GTK_BIN (expander)->child)
1355 gtk_widget_set_child_visible (GTK_BIN (expander)->child, priv->expanded);
1356 gtk_widget_queue_resize (GTK_WIDGET (expander));
1363 gtk_expander_start_animation (GtkExpander *expander)
1365 GtkExpanderPrivate *priv = expander->priv;
1367 if (priv->animation_timeout)
1368 g_source_remove (priv->animation_timeout);
1370 priv->animation_timeout =
1371 gdk_threads_add_timeout (50,
1372 (GSourceFunc) gtk_expander_animation_timeout,
1377 * gtk_expander_set_expanded:
1378 * @expander: a #GtkExpander
1379 * @expanded: whether the child widget is revealed
1381 * Sets the state of the expander. Set to %TRUE, if you want
1382 * the child widget to be revealed, and %FALSE if you want the
1383 * child widget to be hidden.
1388 gtk_expander_set_expanded (GtkExpander *expander,
1391 GtkExpanderPrivate *priv;
1393 g_return_if_fail (GTK_IS_EXPANDER (expander));
1395 priv = expander->priv;
1397 expanded = expanded != FALSE;
1399 if (priv->expanded != expanded)
1401 GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (expander));
1402 gboolean enable_animations;
1404 priv->expanded = expanded;
1406 g_object_get (settings, "gtk-enable-animations", &enable_animations, NULL);
1408 if (enable_animations && gtk_widget_get_realized (GTK_WIDGET (expander)))
1410 gtk_expander_start_animation (expander);
1414 priv->expander_style = expanded ? GTK_EXPANDER_EXPANDED :
1415 GTK_EXPANDER_COLLAPSED;
1417 if (GTK_BIN (expander)->child)
1419 gtk_widget_set_child_visible (GTK_BIN (expander)->child, priv->expanded);
1420 gtk_widget_queue_resize (GTK_WIDGET (expander));
1424 g_object_notify (G_OBJECT (expander), "expanded");
1429 * gtk_expander_get_expanded:
1430 * @expander:a #GtkExpander
1432 * Queries a #GtkExpander and returns its current state. Returns %TRUE
1433 * if the child widget is revealed.
1435 * See gtk_expander_set_expanded().
1437 * Return value: the current state of the expander.
1442 gtk_expander_get_expanded (GtkExpander *expander)
1444 g_return_val_if_fail (GTK_IS_EXPANDER (expander), FALSE);
1446 return expander->priv->expanded;
1450 * gtk_expander_set_spacing:
1451 * @expander: a #GtkExpander
1452 * @spacing: distance between the expander and child in pixels.
1454 * Sets the spacing field of @expander, which is the number of pixels to
1455 * place between expander and the child.
1460 gtk_expander_set_spacing (GtkExpander *expander,
1463 g_return_if_fail (GTK_IS_EXPANDER (expander));
1464 g_return_if_fail (spacing >= 0);
1466 if (expander->priv->spacing != spacing)
1468 expander->priv->spacing = spacing;
1470 gtk_widget_queue_resize (GTK_WIDGET (expander));
1472 g_object_notify (G_OBJECT (expander), "spacing");
1477 * gtk_expander_get_spacing:
1478 * @expander: a #GtkExpander
1480 * Gets the value set by gtk_expander_set_spacing().
1482 * Return value: spacing between the expander and child.
1487 gtk_expander_get_spacing (GtkExpander *expander)
1489 g_return_val_if_fail (GTK_IS_EXPANDER (expander), 0);
1491 return expander->priv->spacing;
1495 * gtk_expander_set_label:
1496 * @expander: a #GtkExpander
1497 * @label: (allow-none): a string
1499 * Sets the text of the label of the expander to @label.
1501 * This will also clear any previously set labels.
1506 gtk_expander_set_label (GtkExpander *expander,
1509 g_return_if_fail (GTK_IS_EXPANDER (expander));
1513 gtk_expander_set_label_widget (expander, NULL);
1519 child = gtk_label_new (label);
1520 gtk_label_set_use_underline (GTK_LABEL (child), expander->priv->use_underline);
1521 gtk_label_set_use_markup (GTK_LABEL (child), expander->priv->use_markup);
1522 gtk_widget_show (child);
1524 gtk_expander_set_label_widget (expander, child);
1527 g_object_notify (G_OBJECT (expander), "label");
1531 * gtk_expander_get_label:
1532 * @expander: a #GtkExpander
1534 * Fetches the text from a label widget including any embedded
1535 * underlines indicating mnemonics and Pango markup, as set by
1536 * gtk_expander_set_label(). If the label text has not been set the
1537 * return value will be %NULL. This will be the case if you create an
1538 * empty button with gtk_button_new() to use as a container.
1540 * Note that this function behaved differently in versions prior to
1541 * 2.14 and used to return the label text stripped of embedded
1542 * underlines indicating mnemonics and Pango markup. This problem can
1543 * be avoided by fetching the label text directly from the label
1546 * Return value: The text of the label widget. This string is owned
1547 * by the widget and must not be modified or freed.
1551 G_CONST_RETURN char *
1552 gtk_expander_get_label (GtkExpander *expander)
1554 GtkExpanderPrivate *priv;
1556 g_return_val_if_fail (GTK_IS_EXPANDER (expander), NULL);
1558 priv = expander->priv;
1560 if (GTK_IS_LABEL (priv->label_widget))
1561 return gtk_label_get_label (GTK_LABEL (priv->label_widget));
1567 * gtk_expander_set_use_underline:
1568 * @expander: a #GtkExpander
1569 * @use_underline: %TRUE if underlines in the text indicate mnemonics
1571 * If true, an underline in the text of the expander label indicates
1572 * the next character should be used for the mnemonic accelerator key.
1577 gtk_expander_set_use_underline (GtkExpander *expander,
1578 gboolean use_underline)
1580 GtkExpanderPrivate *priv;
1582 g_return_if_fail (GTK_IS_EXPANDER (expander));
1584 priv = expander->priv;
1586 use_underline = use_underline != FALSE;
1588 if (priv->use_underline != use_underline)
1590 priv->use_underline = use_underline;
1592 if (GTK_IS_LABEL (priv->label_widget))
1593 gtk_label_set_use_underline (GTK_LABEL (priv->label_widget), use_underline);
1595 g_object_notify (G_OBJECT (expander), "use-underline");
1600 * gtk_expander_get_use_underline:
1601 * @expander: a #GtkExpander
1603 * Returns whether an embedded underline in the expander label indicates a
1604 * mnemonic. See gtk_expander_set_use_underline().
1606 * Return value: %TRUE if an embedded underline in the expander label
1607 * indicates the mnemonic accelerator keys.
1612 gtk_expander_get_use_underline (GtkExpander *expander)
1614 g_return_val_if_fail (GTK_IS_EXPANDER (expander), FALSE);
1616 return expander->priv->use_underline;
1620 * gtk_expander_set_use_markup:
1621 * @expander: a #GtkExpander
1622 * @use_markup: %TRUE if the label's text should be parsed for markup
1624 * Sets whether the text of the label contains markup in <link
1625 * linkend="PangoMarkupFormat">Pango's text markup
1626 * language</link>. See gtk_label_set_markup().
1631 gtk_expander_set_use_markup (GtkExpander *expander,
1632 gboolean use_markup)
1634 GtkExpanderPrivate *priv;
1636 g_return_if_fail (GTK_IS_EXPANDER (expander));
1638 priv = expander->priv;
1640 use_markup = use_markup != FALSE;
1642 if (priv->use_markup != use_markup)
1644 priv->use_markup = use_markup;
1646 if (GTK_IS_LABEL (priv->label_widget))
1647 gtk_label_set_use_markup (GTK_LABEL (priv->label_widget), use_markup);
1649 g_object_notify (G_OBJECT (expander), "use-markup");
1654 * gtk_expander_get_use_markup:
1655 * @expander: a #GtkExpander
1657 * Returns whether the label's text is interpreted as marked up with
1658 * the <link linkend="PangoMarkupFormat">Pango text markup
1659 * language</link>. See gtk_expander_set_use_markup ().
1661 * Return value: %TRUE if the label's text will be parsed for markup
1666 gtk_expander_get_use_markup (GtkExpander *expander)
1668 g_return_val_if_fail (GTK_IS_EXPANDER (expander), FALSE);
1670 return expander->priv->use_markup;
1674 * gtk_expander_set_label_widget:
1675 * @expander: a #GtkExpander
1676 * @label_widget: (allow-none): the new label widget
1678 * Set the label widget for the expander. This is the widget
1679 * that will appear embedded alongside the expander arrow.
1684 gtk_expander_set_label_widget (GtkExpander *expander,
1685 GtkWidget *label_widget)
1687 GtkExpanderPrivate *priv;
1690 g_return_if_fail (GTK_IS_EXPANDER (expander));
1691 g_return_if_fail (label_widget == NULL || GTK_IS_WIDGET (label_widget));
1692 g_return_if_fail (label_widget == NULL || label_widget->parent == NULL);
1694 priv = expander->priv;
1696 if (priv->label_widget == label_widget)
1699 if (priv->label_widget)
1701 gtk_widget_set_state (priv->label_widget, GTK_STATE_NORMAL);
1702 gtk_widget_unparent (priv->label_widget);
1705 priv->label_widget = label_widget;
1706 widget = GTK_WIDGET (expander);
1710 priv->label_widget = label_widget;
1712 gtk_widget_set_parent (label_widget, widget);
1715 gtk_widget_set_state (label_widget, GTK_STATE_PRELIGHT);
1718 if (gtk_widget_get_visible (widget))
1719 gtk_widget_queue_resize (widget);
1721 g_object_freeze_notify (G_OBJECT (expander));
1722 g_object_notify (G_OBJECT (expander), "label-widget");
1723 g_object_notify (G_OBJECT (expander), "label");
1724 g_object_thaw_notify (G_OBJECT (expander));
1728 * gtk_expander_get_label_widget:
1729 * @expander: a #GtkExpander
1731 * Retrieves the label widget for the frame. See
1732 * gtk_expander_set_label_widget().
1734 * Return value: the label widget, or %NULL if there is none.
1739 gtk_expander_get_label_widget (GtkExpander *expander)
1741 g_return_val_if_fail (GTK_IS_EXPANDER (expander), NULL);
1743 return expander->priv->label_widget;