1 /* ///////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/win_gtk.c
3 // Purpose: Native GTK+ widget for wxWidgets, based on GtkLayout and
4 // GtkFixed. It makes use of the gravity window property and
5 // therefore does not work with GTK 1.0.
6 // Author: Robert Roebling
8 // Copyright: (c) 1998 Robert Roebling
9 // Licence: wxWidgets licence
10 /////////////////////////////////////////////////////////////////////////// */
13 #define XCheckIfEvent XCHECKIFEVENT
16 #include "wx/platform.h"
17 #include "wx/gtk/win_gtk.h"
18 #include "gtk/gtksignal.h"
19 #include "gtk/gtkprivate.h"
24 #endif /* __cplusplus */
26 typedef struct _GtkPizzaAdjData GtkPizzaAdjData
;
28 struct _GtkPizzaAdjData
34 static void gtk_pizza_class_init (GtkPizzaClass
*klass
);
35 static void gtk_pizza_init (GtkPizza
*pizza
);
37 static void gtk_pizza_realize (GtkWidget
*widget
);
38 static void gtk_pizza_unrealize (GtkWidget
*widget
);
40 static void gtk_pizza_map (GtkWidget
*widget
);
42 static void gtk_pizza_size_request (GtkWidget
*widget
,
43 GtkRequisition
*requisition
);
44 static void gtk_pizza_size_allocate (GtkWidget
*widget
,
45 GtkAllocation
*allocation
);
46 static gint
gtk_pizza_expose (GtkWidget
*widget
,
47 GdkEventExpose
*event
);
48 static void gtk_pizza_style_set (GtkWidget
*widget
,
49 GtkStyle
*previous_style
);
50 static void gtk_pizza_add (GtkContainer
*container
,
52 static void gtk_pizza_remove (GtkContainer
*container
,
54 static void gtk_pizza_forall (GtkContainer
*container
,
55 gboolean include_internals
,
57 gpointer callback_data
);
59 static void gtk_pizza_allocate_child (GtkPizza
*pizza
,
60 GtkPizzaChild
*child
);
61 static void gtk_pizza_adjust_allocations_recurse (GtkWidget
*widget
,
64 static GtkType
gtk_pizza_child_type (GtkContainer
*container
);
66 static void gtk_pizza_scroll_set_adjustments (GtkPizza
*pizza
,
71 GtkContainerClass
*pizza_parent_class
= NULL
;
76 static GtkType pizza_type
= 0;
80 static const GTypeInfo pizza_info
=
82 sizeof (GtkPizzaClass
),
84 NULL
, /* base_finalize */
85 (GClassInitFunc
) gtk_pizza_class_init
,
86 NULL
, /* class_finalize */
87 NULL
, /* class_data */
90 (GInstanceInitFunc
) gtk_pizza_init
,
92 pizza_type
= g_type_register_static (GTK_TYPE_CONTAINER
, "GtkPizza", &pizza_info
, (GTypeFlags
)0);
98 /* Marshaller needed for set_scroll_adjustments signal,
99 generated with GLib-2.4.6 glib-genmarshal */
100 #define g_marshal_value_peek_object(v) g_value_get_object (v)
102 g_cclosure_user_marshal_VOID__OBJECT_OBJECT (GClosure
*closure
,
103 GValue
*return_value
,
104 guint n_param_values
,
105 const GValue
*param_values
,
106 gpointer invocation_hint
,
107 gpointer marshal_data
)
109 typedef void (*GMarshalFunc_VOID__OBJECT_OBJECT
) (gpointer data1
,
113 register GMarshalFunc_VOID__OBJECT_OBJECT callback
;
114 register GCClosure
*cc
= (GCClosure
*) closure
;
115 register gpointer data1
, data2
;
117 g_return_if_fail (n_param_values
== 3);
119 if (G_CCLOSURE_SWAP_DATA (closure
))
121 data1
= closure
->data
;
122 data2
= g_value_peek_pointer (param_values
+ 0);
126 data1
= g_value_peek_pointer (param_values
+ 0);
127 data2
= closure
->data
;
129 callback
= (GMarshalFunc_VOID__OBJECT_OBJECT
) (marshal_data
? marshal_data
: cc
->callback
);
132 g_marshal_value_peek_object (param_values
+ 1),
133 g_marshal_value_peek_object (param_values
+ 2),
138 gtk_pizza_class_init (GtkPizzaClass
*klass
)
140 GtkObjectClass
*object_class
;
141 GtkWidgetClass
*widget_class
;
142 GtkContainerClass
*container_class
;
144 object_class
= (GtkObjectClass
*) klass
;
145 widget_class
= (GtkWidgetClass
*) klass
;
146 container_class
= (GtkContainerClass
*) klass
;
147 pizza_parent_class
= gtk_type_class (GTK_TYPE_CONTAINER
);
149 widget_class
->map
= gtk_pizza_map
;
150 widget_class
->realize
= gtk_pizza_realize
;
151 widget_class
->unrealize
= gtk_pizza_unrealize
;
152 widget_class
->size_request
= gtk_pizza_size_request
;
153 widget_class
->size_allocate
= gtk_pizza_size_allocate
;
154 widget_class
->expose_event
= gtk_pizza_expose
;
155 widget_class
->style_set
= gtk_pizza_style_set
;
157 container_class
->add
= gtk_pizza_add
;
158 container_class
->remove
= gtk_pizza_remove
;
159 container_class
->forall
= gtk_pizza_forall
;
161 container_class
->child_type
= gtk_pizza_child_type
;
163 klass
->set_scroll_adjustments
= gtk_pizza_scroll_set_adjustments
;
165 widget_class
->set_scroll_adjustments_signal
=
167 "set_scroll_adjustments",
168 G_TYPE_FROM_CLASS(object_class
),
170 G_STRUCT_OFFSET(GtkPizzaClass
, set_scroll_adjustments
),
173 g_cclosure_user_marshal_VOID__OBJECT_OBJECT
,
177 GTK_TYPE_ADJUSTMENT
);
181 gtk_pizza_child_type (GtkContainer
*container
)
183 return GTK_TYPE_WIDGET
;
187 gtk_pizza_init (GtkPizza
*pizza
)
189 GTK_WIDGET_UNSET_FLAGS (pizza
, GTK_NO_WINDOW
);
191 pizza
->shadow_type
= GTK_MYSHADOW_NONE
;
193 pizza
->children
= NULL
;
198 pizza
->bin_window
= NULL
;
203 pizza
->configure_serial
= 0;
206 pizza
->visibility
= GDK_VISIBILITY_PARTIAL
;
208 pizza
->clear_on_draw
= TRUE
;
209 pizza
->use_filter
= TRUE
;
210 pizza
->external_expose
= FALSE
;
218 pizza
= gtk_type_new (gtk_pizza_get_type ());
220 return GTK_WIDGET (pizza
);
224 gtk_pizza_scroll_set_adjustments (GtkPizza
*pizza
,
228 /* We handle scrolling in the wxScrolledWindow, not here. */
232 gtk_pizza_set_shadow_type (GtkPizza
*pizza
,
233 GtkMyShadowType type
)
235 g_return_if_fail (pizza
!= NULL
);
236 g_return_if_fail (GTK_IS_PIZZA (pizza
));
238 if ((GtkMyShadowType
) pizza
->shadow_type
!= type
)
240 pizza
->shadow_type
= type
;
242 if (GTK_WIDGET_VISIBLE (pizza
))
244 gtk_widget_size_allocate (GTK_WIDGET (pizza
), &(GTK_WIDGET (pizza
)->allocation
));
245 gtk_widget_queue_draw (GTK_WIDGET (pizza
));
251 gtk_pizza_set_clear (GtkPizza
*pizza
,
254 g_return_if_fail (pizza
!= NULL
);
255 g_return_if_fail (GTK_IS_PIZZA (pizza
));
257 pizza
->clear_on_draw
= clear
;
261 gtk_pizza_set_filter (GtkPizza
*pizza
,
264 g_return_if_fail (pizza
!= NULL
);
265 g_return_if_fail (GTK_IS_PIZZA (pizza
));
267 pizza
->use_filter
= use
;
271 gtk_pizza_set_external (GtkPizza
*pizza
,
274 g_return_if_fail (pizza
!= NULL
);
275 g_return_if_fail (GTK_IS_PIZZA (pizza
));
277 pizza
->external_expose
= expose
;
281 gtk_pizza_put (GtkPizza
*pizza
,
288 GtkPizzaChild
*child_info
;
290 g_return_if_fail (pizza
!= NULL
);
291 g_return_if_fail (GTK_IS_PIZZA (pizza
));
292 g_return_if_fail (widget
!= NULL
);
294 child_info
= g_new (GtkPizzaChild
, 1);
296 child_info
->widget
= widget
;
299 child_info
->width
= width
;
300 child_info
->height
= height
;
302 pizza
->children
= g_list_append (pizza
->children
, child_info
);
304 if (GTK_WIDGET_REALIZED (pizza
))
305 gtk_widget_set_parent_window (widget
, pizza
->bin_window
);
307 gtk_widget_set_parent (widget
, GTK_WIDGET (pizza
));
309 gtk_widget_set_size_request (widget
, width
, height
);
313 gtk_pizza_move (GtkPizza
*pizza
,
318 GtkPizzaChild
*child
;
321 g_return_if_fail (pizza
!= NULL
);
322 g_return_if_fail (GTK_IS_PIZZA (pizza
));
323 g_return_if_fail (widget
!= NULL
);
325 children
= pizza
->children
;
328 child
= children
->data
;
329 children
= children
->next
;
331 if (child
->widget
== widget
)
333 if ((child
->x
== x
) && (child
->y
== y
))
339 if (GTK_WIDGET_VISIBLE (widget
) && GTK_WIDGET_VISIBLE (pizza
))
340 gtk_widget_queue_resize (widget
);
347 gtk_pizza_resize (GtkPizza
*pizza
,
352 GtkPizzaChild
*child
;
355 g_return_if_fail (pizza
!= NULL
);
356 g_return_if_fail (GTK_IS_PIZZA (pizza
));
357 g_return_if_fail (widget
!= NULL
);
359 children
= pizza
->children
;
362 child
= children
->data
;
363 children
= children
->next
;
365 if (child
->widget
== widget
)
367 if ((child
->width
== width
) && (child
->height
== height
))
370 child
->width
= width
;
371 child
->height
= height
;
373 gtk_widget_set_size_request (widget
, width
, height
);
375 if (GTK_WIDGET_VISIBLE (widget
) && GTK_WIDGET_VISIBLE (pizza
))
376 gtk_widget_queue_resize (widget
);
383 gtk_pizza_set_size (GtkPizza
*pizza
,
390 GtkPizzaChild
*child
;
393 g_return_if_fail (pizza
!= NULL
);
394 g_return_if_fail (GTK_IS_PIZZA (pizza
));
395 g_return_if_fail (widget
!= NULL
);
397 #ifndef WX_WARN_ILLEGAL_SETSIZE
398 /* this really shouldn't happen -- but it does, a lot, right now and we
399 can't pass negative values to gtk_widget_set_size_request() without getting
400 a warning printed out, so filter them out here */
407 children
= pizza
->children
;
410 child
= children
->data
;
411 children
= children
->next
;
413 if (child
->widget
== widget
)
415 if ((child
->x
== x
) &&
417 (child
->width
== width
) &&
418 (child
->height
== height
)) return;
422 child
->width
= width
;
423 child
->height
= height
;
425 gtk_widget_set_size_request (widget
, width
, height
);
427 if (GTK_WIDGET_VISIBLE (widget
) && GTK_WIDGET_VISIBLE (pizza
))
428 gtk_widget_queue_resize (widget
);
436 gtk_pizza_child_resized (GtkPizza
*pizza
,
439 GtkPizzaChild
*child
;
442 g_return_val_if_fail (pizza
!= NULL
, FALSE
);
443 g_return_val_if_fail (GTK_IS_PIZZA (pizza
), FALSE
);
444 g_return_val_if_fail (widget
!= NULL
, FALSE
);
446 children
= pizza
->children
;
449 child
= children
->data
;
450 children
= children
->next
;
452 if (child
->widget
== widget
)
454 return ((child
->width
== widget
->allocation
.width
) &&
455 (child
->height
== widget
->allocation
.height
));
463 gtk_pizza_map (GtkWidget
*widget
)
466 GtkPizzaChild
*child
;
469 g_return_if_fail (widget
!= NULL
);
470 g_return_if_fail (GTK_IS_PIZZA (widget
));
472 GTK_WIDGET_SET_FLAGS (widget
, GTK_MAPPED
);
473 pizza
= GTK_PIZZA (widget
);
475 children
= pizza
->children
;
478 child
= children
->data
;
479 children
= children
->next
;
481 if ( GTK_WIDGET_VISIBLE (child
->widget
) &&
482 !GTK_WIDGET_MAPPED (child
->widget
) &&
485 gtk_widget_map (child
->widget
);
489 gdk_window_show (widget
->window
);
490 gdk_window_show (pizza
->bin_window
);
494 gtk_pizza_realize (GtkWidget
*widget
)
497 GdkWindowAttr attributes
;
498 gint attributes_mask
;
499 GtkPizzaChild
*child
;
502 g_return_if_fail (widget
!= NULL
);
503 g_return_if_fail (GTK_IS_PIZZA (widget
));
505 pizza
= GTK_PIZZA (widget
);
506 GTK_WIDGET_SET_FLAGS (widget
, GTK_REALIZED
);
508 attributes
.window_type
= GDK_WINDOW_CHILD
;
510 attributes
.x
= widget
->allocation
.x
;
511 attributes
.y
= widget
->allocation
.y
;
512 attributes
.width
= widget
->allocation
.width
;
513 attributes
.height
= widget
->allocation
.height
;
515 #ifndef __WXUNIVERSAL__
516 if (pizza
->shadow_type
== GTK_MYSHADOW_NONE
)
518 /* no border, no changes to sizes */
520 else if (pizza
->shadow_type
== GTK_MYSHADOW_THIN
)
522 /* GTK_MYSHADOW_THIN == wxSIMPLE_BORDER */
525 attributes
.width
-= 2;
526 attributes
.height
-= 2;
530 /* GTK_MYSHADOW_IN == wxSUNKEN_BORDER */
531 /* GTK_MYSHADOW_OUT == wxRAISED_BORDER */
534 attributes
.width
-= 4;
535 attributes
.height
-= 4;
537 #endif /* __WXUNIVERSAL__ */
540 if (attributes
.width
< 2) attributes
.width
= 2;
541 if (attributes
.height
< 2) attributes
.height
= 2;
543 attributes
.wclass
= GDK_INPUT_OUTPUT
;
544 attributes
.visual
= gtk_widget_get_visual (widget
);
545 attributes
.colormap
= gtk_widget_get_colormap (widget
);
546 attributes
.event_mask
= GDK_VISIBILITY_NOTIFY_MASK
;
547 attributes_mask
= GDK_WA_X
| GDK_WA_Y
| GDK_WA_VISUAL
| GDK_WA_COLORMAP
;
549 widget
->window
= gdk_window_new(gtk_widget_get_parent_window (widget
),
550 &attributes
, attributes_mask
);
551 gdk_window_set_user_data (widget
->window
, widget
);
556 attributes
.event_mask
= gtk_widget_get_events (widget
);
557 attributes
.event_mask
|= GDK_EXPOSURE_MASK
|
559 GDK_POINTER_MOTION_MASK
|
560 GDK_POINTER_MOTION_HINT_MASK
|
561 GDK_BUTTON_MOTION_MASK
|
562 GDK_BUTTON1_MOTION_MASK
|
563 GDK_BUTTON2_MOTION_MASK
|
564 GDK_BUTTON3_MOTION_MASK
|
565 GDK_BUTTON_PRESS_MASK
|
566 GDK_BUTTON_RELEASE_MASK
|
568 GDK_KEY_RELEASE_MASK
|
569 GDK_ENTER_NOTIFY_MASK
|
570 GDK_LEAVE_NOTIFY_MASK
|
571 GDK_FOCUS_CHANGE_MASK
;
573 pizza
->bin_window
= gdk_window_new(widget
->window
,
574 &attributes
, attributes_mask
);
575 gdk_window_set_user_data (pizza
->bin_window
, widget
);
577 widget
->style
= gtk_style_attach (widget
->style
, widget
->window
);
578 gtk_style_set_background (widget
->style
, widget
->window
, GTK_STATE_NORMAL
);
579 gtk_style_set_background (widget
->style
, pizza
->bin_window
, GTK_STATE_NORMAL
);
582 gdk_window_set_back_pixmap( widget->window, NULL, FALSE );
583 gdk_window_set_back_pixmap( pizza->bin_window, NULL, FALSE );
586 /* cannot be done before realisation */
587 children
= pizza
->children
;
590 child
= children
->data
;
591 children
= children
->next
;
593 gtk_widget_set_parent_window (child
->widget
, pizza
->bin_window
);
598 gtk_pizza_unrealize (GtkWidget
*widget
)
602 g_return_if_fail (widget
!= NULL
);
603 g_return_if_fail (GTK_IS_PIZZA (widget
));
605 pizza
= GTK_PIZZA (widget
);
607 gdk_window_set_user_data (pizza
->bin_window
, NULL
);
608 gdk_window_destroy (pizza
->bin_window
);
609 pizza
->bin_window
= NULL
;
611 if (GTK_WIDGET_CLASS (pizza_parent_class
)->unrealize
)
612 (* GTK_WIDGET_CLASS (pizza_parent_class
)->unrealize
) (widget
);
616 gtk_pizza_size_request (GtkWidget
*widget
,
617 GtkRequisition
*requisition
)
620 GtkPizzaChild
*child
;
622 GtkRequisition child_requisition
;
624 g_return_if_fail (widget
!= NULL
);
625 g_return_if_fail (GTK_IS_PIZZA (widget
));
626 g_return_if_fail (requisition
!= NULL
);
628 pizza
= GTK_PIZZA (widget
);
630 children
= pizza
->children
;
633 child
= children
->data
;
634 children
= children
->next
;
636 if (GTK_WIDGET_VISIBLE (child
->widget
))
638 gtk_widget_size_request (child
->widget
, &child_requisition
);
642 /* request very little, I'm not sure if requesting nothing
643 will always have positive effects on stability... */
644 requisition
->width
= 2;
645 requisition
->height
= 2;
649 gtk_pizza_size_allocate (GtkWidget
*widget
,
650 GtkAllocation
*allocation
)
655 GtkPizzaChild
*child
;
658 g_return_if_fail (widget
!= NULL
);
659 g_return_if_fail (GTK_IS_PIZZA(widget
));
660 g_return_if_fail (allocation
!= NULL
);
662 pizza
= GTK_PIZZA (widget
);
664 widget
->allocation
= *allocation
;
666 if (pizza
->shadow_type
== GTK_MYSHADOW_NONE
)
669 if (pizza
->shadow_type
== GTK_MYSHADOW_THIN
)
674 x
= allocation
->x
+ border
;
675 y
= allocation
->y
+ border
;
676 w
= allocation
->width
- border
*2;
677 h
= allocation
->height
- border
*2;
679 if (GTK_WIDGET_REALIZED (widget
))
681 gdk_window_move_resize( widget
->window
, x
, y
, w
, h
);
682 gdk_window_move_resize( pizza
->bin_window
, 0, 0, w
, h
);
685 children
= pizza
->children
;
688 child
= children
->data
;
689 children
= children
->next
;
691 gtk_pizza_allocate_child (pizza
, child
);
696 gtk_pizza_expose (GtkWidget
*widget
,
697 GdkEventExpose
*event
)
701 g_return_val_if_fail (widget
!= NULL
, FALSE
);
702 g_return_val_if_fail (GTK_IS_PIZZA (widget
), FALSE
);
703 g_return_val_if_fail (event
!= NULL
, FALSE
);
705 pizza
= GTK_PIZZA (widget
);
707 if (event
->window
!= pizza
->bin_window
)
710 /* We handle all expose events in window.cpp now. */
711 if (pizza
->external_expose
)
714 (* GTK_WIDGET_CLASS (pizza_parent_class
)->expose_event
) (widget
, event
);
720 gtk_pizza_style_set(GtkWidget
*widget
, GtkStyle
*previous_style
)
722 if (GTK_WIDGET_REALIZED(widget
))
724 gtk_style_set_background(widget
->style
, widget
->window
, GTK_STATE_NORMAL
);
725 gtk_style_set_background(widget
->style
, GTK_PIZZA(widget
)->bin_window
, GTK_STATE_NORMAL
);
728 (* GTK_WIDGET_CLASS (pizza_parent_class
)->style_set
) (widget
, previous_style
);
732 gtk_pizza_add (GtkContainer
*container
,
735 g_return_if_fail (container
!= NULL
);
736 g_return_if_fail (GTK_IS_PIZZA (container
));
737 g_return_if_fail (widget
!= NULL
);
739 gtk_pizza_put (GTK_PIZZA (container
), widget
, 0, 0, 20, 20 );
743 gtk_pizza_remove (GtkContainer
*container
,
747 GtkPizzaChild
*child
;
750 g_return_if_fail (container
!= NULL
);
751 g_return_if_fail (GTK_IS_PIZZA (container
));
752 g_return_if_fail (widget
!= NULL
);
754 pizza
= GTK_PIZZA (container
);
756 children
= pizza
->children
;
759 child
= children
->data
;
761 if (child
->widget
== widget
)
763 gtk_widget_unparent (widget
);
765 /* security checks */
766 g_return_if_fail (GTK_IS_WIDGET (widget
));
768 pizza
->children
= g_list_remove_link (pizza
->children
, children
);
769 g_list_free (children
);
772 /* security checks */
773 g_return_if_fail (GTK_IS_WIDGET (widget
));
778 children
= children
->next
;
783 gtk_pizza_forall (GtkContainer
*container
,
784 gboolean include_internals
,
785 GtkCallback callback
,
786 gpointer callback_data
)
789 GtkPizzaChild
*child
;
792 g_return_if_fail (container
!= NULL
);
793 g_return_if_fail (GTK_IS_PIZZA (container
));
794 g_return_if_fail (callback
!= (GtkCallback
)NULL
);
796 pizza
= GTK_PIZZA (container
);
798 children
= pizza
->children
;
801 child
= children
->data
;
802 children
= children
->next
;
804 (* callback
) (child
->widget
, callback_data
);
809 gtk_pizza_allocate_child (GtkPizza
*pizza
,
810 GtkPizzaChild
*child
)
812 GtkAllocation allocation
;
813 GtkRequisition requisition
;
815 allocation
.x
= child
->x
- pizza
->xoffset
;
816 allocation
.y
= child
->y
- pizza
->yoffset
;
817 gtk_widget_get_child_requisition (child
->widget
, &requisition
);
818 allocation
.width
= requisition
.width
;
819 allocation
.height
= requisition
.height
;
821 gtk_widget_size_allocate (child
->widget
, &allocation
);
825 gtk_pizza_adjust_allocations_recurse (GtkWidget
*widget
,
828 GtkPizzaAdjData
*data
= cb_data
;
830 widget
->allocation
.x
+= data
->dx
;
831 widget
->allocation
.y
+= data
->dy
;
833 if (GTK_WIDGET_NO_WINDOW (widget
) && GTK_IS_CONTAINER (widget
))
835 gtk_container_forall (GTK_CONTAINER (widget
),
836 gtk_pizza_adjust_allocations_recurse
,
842 gtk_pizza_adjust_allocations (GtkPizza
*pizza
,
847 GtkPizzaAdjData data
;
852 tmp_list
= pizza
->children
;
855 GtkPizzaChild
*child
= tmp_list
->data
;
856 tmp_list
= tmp_list
->next
;
858 child
->widget
->allocation
.x
+= dx
;
859 child
->widget
->allocation
.y
+= dy
;
861 if (GTK_WIDGET_NO_WINDOW (child
->widget
) &&
862 GTK_IS_CONTAINER (child
->widget
))
864 gtk_container_forall (GTK_CONTAINER (child
->widget
),
865 gtk_pizza_adjust_allocations_recurse
,
872 /* This is the main routine to do the scrolling. Scrolling is
873 * done by "Guffaw" scrolling, as in the Mozilla XFE, with
874 * a few modifications.
876 * The main improvement is that we keep track of whether we
877 * are obscured or not. If not, we ignore the generated expose
878 * events and instead do the exposes ourself, without having
879 * to wait for a roundtrip to the server. This also provides
880 * a limited form of expose-event compression, since we do
881 * the affected area as one big chunk.
885 gtk_pizza_scroll (GtkPizza
*pizza
, gint dx
, gint dy
)
887 pizza
->xoffset
+= dx
;
888 pizza
->yoffset
+= dy
;
890 gtk_pizza_adjust_allocations (pizza
, -dx
, -dy
);
892 if (pizza
->bin_window
)
893 gdk_window_scroll( pizza
->bin_window
, -dx
, -dy
);
898 #endif /* __cplusplus */