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