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 /* this really shouldn't happen -- but it does, a lot, right now and we
398 can't pass negative values to gtk_widget_set_size_request(), so filter
405 children
= pizza
->children
;
408 child
= children
->data
;
409 children
= children
->next
;
411 if (child
->widget
== widget
)
413 if ((child
->x
== x
) &&
415 (child
->width
== width
) &&
416 (child
->height
== height
)) return;
420 child
->width
= width
;
421 child
->height
= height
;
423 gtk_widget_set_size_request (widget
, width
, height
);
425 if (GTK_WIDGET_VISIBLE (widget
) && GTK_WIDGET_VISIBLE (pizza
))
426 gtk_widget_queue_resize (widget
);
434 gtk_pizza_child_resized (GtkPizza
*pizza
,
437 GtkPizzaChild
*child
;
440 g_return_val_if_fail (pizza
!= NULL
, FALSE
);
441 g_return_val_if_fail (GTK_IS_PIZZA (pizza
), FALSE
);
442 g_return_val_if_fail (widget
!= NULL
, FALSE
);
444 children
= pizza
->children
;
447 child
= children
->data
;
448 children
= children
->next
;
450 if (child
->widget
== widget
)
452 return ((child
->width
== widget
->allocation
.width
) &&
453 (child
->height
== widget
->allocation
.height
));
461 gtk_pizza_map (GtkWidget
*widget
)
464 GtkPizzaChild
*child
;
467 g_return_if_fail (widget
!= NULL
);
468 g_return_if_fail (GTK_IS_PIZZA (widget
));
470 GTK_WIDGET_SET_FLAGS (widget
, GTK_MAPPED
);
471 pizza
= GTK_PIZZA (widget
);
473 children
= pizza
->children
;
476 child
= children
->data
;
477 children
= children
->next
;
479 if ( GTK_WIDGET_VISIBLE (child
->widget
) &&
480 !GTK_WIDGET_MAPPED (child
->widget
) &&
483 gtk_widget_map (child
->widget
);
487 gdk_window_show (widget
->window
);
488 gdk_window_show (pizza
->bin_window
);
492 gtk_pizza_realize (GtkWidget
*widget
)
495 GdkWindowAttr attributes
;
496 gint attributes_mask
;
497 GtkPizzaChild
*child
;
500 g_return_if_fail (widget
!= NULL
);
501 g_return_if_fail (GTK_IS_PIZZA (widget
));
503 pizza
= GTK_PIZZA (widget
);
504 GTK_WIDGET_SET_FLAGS (widget
, GTK_REALIZED
);
506 attributes
.window_type
= GDK_WINDOW_CHILD
;
508 attributes
.x
= widget
->allocation
.x
;
509 attributes
.y
= widget
->allocation
.y
;
510 attributes
.width
= widget
->allocation
.width
;
511 attributes
.height
= widget
->allocation
.height
;
513 #ifndef __WXUNIVERSAL__
514 if (pizza
->shadow_type
== GTK_MYSHADOW_NONE
)
516 /* no border, no changes to sizes */
518 else if (pizza
->shadow_type
== GTK_MYSHADOW_THIN
)
520 /* GTK_MYSHADOW_THIN == wxSIMPLE_BORDER */
523 attributes
.width
-= 2;
524 attributes
.height
-= 2;
528 /* GTK_MYSHADOW_IN == wxSUNKEN_BORDER */
529 /* GTK_MYSHADOW_OUT == wxRAISED_BORDER */
532 attributes
.width
-= 4;
533 attributes
.height
-= 4;
535 #endif /* __WXUNIVERSAL__ */
538 if (attributes
.width
< 2) attributes
.width
= 2;
539 if (attributes
.height
< 2) attributes
.height
= 2;
541 attributes
.wclass
= GDK_INPUT_OUTPUT
;
542 attributes
.visual
= gtk_widget_get_visual (widget
);
543 attributes
.colormap
= gtk_widget_get_colormap (widget
);
544 attributes
.event_mask
= GDK_VISIBILITY_NOTIFY_MASK
;
545 attributes_mask
= GDK_WA_X
| GDK_WA_Y
| GDK_WA_VISUAL
| GDK_WA_COLORMAP
;
547 widget
->window
= gdk_window_new(gtk_widget_get_parent_window (widget
),
548 &attributes
, attributes_mask
);
549 gdk_window_set_user_data (widget
->window
, widget
);
554 attributes
.event_mask
= gtk_widget_get_events (widget
);
555 attributes
.event_mask
|= GDK_EXPOSURE_MASK
|
557 GDK_POINTER_MOTION_MASK
|
558 GDK_POINTER_MOTION_HINT_MASK
|
559 GDK_BUTTON_MOTION_MASK
|
560 GDK_BUTTON1_MOTION_MASK
|
561 GDK_BUTTON2_MOTION_MASK
|
562 GDK_BUTTON3_MOTION_MASK
|
563 GDK_BUTTON_PRESS_MASK
|
564 GDK_BUTTON_RELEASE_MASK
|
566 GDK_KEY_RELEASE_MASK
|
567 GDK_ENTER_NOTIFY_MASK
|
568 GDK_LEAVE_NOTIFY_MASK
|
569 GDK_FOCUS_CHANGE_MASK
;
571 pizza
->bin_window
= gdk_window_new(widget
->window
,
572 &attributes
, attributes_mask
);
573 gdk_window_set_user_data (pizza
->bin_window
, widget
);
575 widget
->style
= gtk_style_attach (widget
->style
, widget
->window
);
576 gtk_style_set_background (widget
->style
, widget
->window
, GTK_STATE_NORMAL
);
577 gtk_style_set_background (widget
->style
, pizza
->bin_window
, GTK_STATE_NORMAL
);
580 gdk_window_set_back_pixmap( widget->window, NULL, FALSE );
581 gdk_window_set_back_pixmap( pizza->bin_window, NULL, FALSE );
584 /* cannot be done before realisation */
585 children
= pizza
->children
;
588 child
= children
->data
;
589 children
= children
->next
;
591 gtk_widget_set_parent_window (child
->widget
, pizza
->bin_window
);
596 gtk_pizza_unrealize (GtkWidget
*widget
)
600 g_return_if_fail (widget
!= NULL
);
601 g_return_if_fail (GTK_IS_PIZZA (widget
));
603 pizza
= GTK_PIZZA (widget
);
605 gdk_window_set_user_data (pizza
->bin_window
, NULL
);
606 gdk_window_destroy (pizza
->bin_window
);
607 pizza
->bin_window
= NULL
;
609 if (GTK_WIDGET_CLASS (pizza_parent_class
)->unrealize
)
610 (* GTK_WIDGET_CLASS (pizza_parent_class
)->unrealize
) (widget
);
614 gtk_pizza_size_request (GtkWidget
*widget
,
615 GtkRequisition
*requisition
)
618 GtkPizzaChild
*child
;
620 GtkRequisition child_requisition
;
622 g_return_if_fail (widget
!= NULL
);
623 g_return_if_fail (GTK_IS_PIZZA (widget
));
624 g_return_if_fail (requisition
!= NULL
);
626 pizza
= GTK_PIZZA (widget
);
628 children
= pizza
->children
;
631 child
= children
->data
;
632 children
= children
->next
;
634 if (GTK_WIDGET_VISIBLE (child
->widget
))
636 gtk_widget_size_request (child
->widget
, &child_requisition
);
640 /* request very little, I'm not sure if requesting nothing
641 will always have positive effects on stability... */
642 requisition
->width
= 2;
643 requisition
->height
= 2;
647 gtk_pizza_size_allocate (GtkWidget
*widget
,
648 GtkAllocation
*allocation
)
653 GtkPizzaChild
*child
;
656 g_return_if_fail (widget
!= NULL
);
657 g_return_if_fail (GTK_IS_PIZZA(widget
));
658 g_return_if_fail (allocation
!= NULL
);
660 pizza
= GTK_PIZZA (widget
);
662 widget
->allocation
= *allocation
;
664 if (pizza
->shadow_type
== GTK_MYSHADOW_NONE
)
667 if (pizza
->shadow_type
== GTK_MYSHADOW_THIN
)
672 x
= allocation
->x
+ border
;
673 y
= allocation
->y
+ border
;
674 w
= allocation
->width
- border
*2;
675 h
= allocation
->height
- border
*2;
677 if (GTK_WIDGET_REALIZED (widget
))
679 gdk_window_move_resize( widget
->window
, x
, y
, w
, h
);
680 gdk_window_move_resize( pizza
->bin_window
, 0, 0, w
, h
);
683 children
= pizza
->children
;
686 child
= children
->data
;
687 children
= children
->next
;
689 gtk_pizza_allocate_child (pizza
, child
);
694 gtk_pizza_expose (GtkWidget
*widget
,
695 GdkEventExpose
*event
)
699 g_return_val_if_fail (widget
!= NULL
, FALSE
);
700 g_return_val_if_fail (GTK_IS_PIZZA (widget
), FALSE
);
701 g_return_val_if_fail (event
!= NULL
, FALSE
);
703 pizza
= GTK_PIZZA (widget
);
705 if (event
->window
!= pizza
->bin_window
)
708 /* We handle all expose events in window.cpp now. */
709 if (pizza
->external_expose
)
712 (* GTK_WIDGET_CLASS (pizza_parent_class
)->expose_event
) (widget
, event
);
718 gtk_pizza_style_set(GtkWidget
*widget
, GtkStyle
*previous_style
)
720 if (GTK_WIDGET_REALIZED(widget
))
722 gtk_style_set_background(widget
->style
, widget
->window
, GTK_STATE_NORMAL
);
723 gtk_style_set_background(widget
->style
, GTK_PIZZA(widget
)->bin_window
, GTK_STATE_NORMAL
);
726 (* GTK_WIDGET_CLASS (pizza_parent_class
)->style_set
) (widget
, previous_style
);
730 gtk_pizza_add (GtkContainer
*container
,
733 g_return_if_fail (container
!= NULL
);
734 g_return_if_fail (GTK_IS_PIZZA (container
));
735 g_return_if_fail (widget
!= NULL
);
737 gtk_pizza_put (GTK_PIZZA (container
), widget
, 0, 0, 20, 20 );
741 gtk_pizza_remove (GtkContainer
*container
,
745 GtkPizzaChild
*child
;
748 g_return_if_fail (container
!= NULL
);
749 g_return_if_fail (GTK_IS_PIZZA (container
));
750 g_return_if_fail (widget
!= NULL
);
752 pizza
= GTK_PIZZA (container
);
754 children
= pizza
->children
;
757 child
= children
->data
;
759 if (child
->widget
== widget
)
761 gtk_widget_unparent (widget
);
763 /* security checks */
764 g_return_if_fail (GTK_IS_WIDGET (widget
));
766 pizza
->children
= g_list_remove_link (pizza
->children
, children
);
767 g_list_free (children
);
770 /* security checks */
771 g_return_if_fail (GTK_IS_WIDGET (widget
));
776 children
= children
->next
;
781 gtk_pizza_forall (GtkContainer
*container
,
782 gboolean include_internals
,
783 GtkCallback callback
,
784 gpointer callback_data
)
787 GtkPizzaChild
*child
;
790 g_return_if_fail (container
!= NULL
);
791 g_return_if_fail (GTK_IS_PIZZA (container
));
792 g_return_if_fail (callback
!= (GtkCallback
)NULL
);
794 pizza
= GTK_PIZZA (container
);
796 children
= pizza
->children
;
799 child
= children
->data
;
800 children
= children
->next
;
802 (* callback
) (child
->widget
, callback_data
);
807 gtk_pizza_allocate_child (GtkPizza
*pizza
,
808 GtkPizzaChild
*child
)
810 GtkAllocation allocation
;
811 GtkRequisition requisition
;
813 allocation
.x
= child
->x
- pizza
->xoffset
;
814 allocation
.y
= child
->y
- pizza
->yoffset
;
815 gtk_widget_get_child_requisition (child
->widget
, &requisition
);
816 allocation
.width
= requisition
.width
;
817 allocation
.height
= requisition
.height
;
819 gtk_widget_size_allocate (child
->widget
, &allocation
);
823 gtk_pizza_adjust_allocations_recurse (GtkWidget
*widget
,
826 GtkPizzaAdjData
*data
= cb_data
;
828 widget
->allocation
.x
+= data
->dx
;
829 widget
->allocation
.y
+= data
->dy
;
831 if (GTK_WIDGET_NO_WINDOW (widget
) && GTK_IS_CONTAINER (widget
))
833 gtk_container_forall (GTK_CONTAINER (widget
),
834 gtk_pizza_adjust_allocations_recurse
,
840 gtk_pizza_adjust_allocations (GtkPizza
*pizza
,
845 GtkPizzaAdjData data
;
850 tmp_list
= pizza
->children
;
853 GtkPizzaChild
*child
= tmp_list
->data
;
854 tmp_list
= tmp_list
->next
;
856 child
->widget
->allocation
.x
+= dx
;
857 child
->widget
->allocation
.y
+= dy
;
859 if (GTK_WIDGET_NO_WINDOW (child
->widget
) &&
860 GTK_IS_CONTAINER (child
->widget
))
862 gtk_container_forall (GTK_CONTAINER (child
->widget
),
863 gtk_pizza_adjust_allocations_recurse
,
870 /* This is the main routine to do the scrolling. Scrolling is
871 * done by "Guffaw" scrolling, as in the Mozilla XFE, with
872 * a few modifications.
874 * The main improvement is that we keep track of whether we
875 * are obscured or not. If not, we ignore the generated expose
876 * events and instead do the exposes ourself, without having
877 * to wait for a roundtrip to the server. This also provides
878 * a limited form of expose-event compression, since we do
879 * the affected area as one big chunk.
883 gtk_pizza_scroll (GtkPizza
*pizza
, gint dx
, gint dy
)
885 pizza
->xoffset
+= dx
;
886 pizza
->yoffset
+= dy
;
888 gtk_pizza_adjust_allocations (pizza
, -dx
, -dy
);
890 if (pizza
->bin_window
)
891 gdk_window_scroll( pizza
->bin_window
, -dx
, -dy
);
896 #endif /* __cplusplus */