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_usize (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_usize (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 children
= pizza
->children
;
400 child
= children
->data
;
401 children
= children
->next
;
403 if (child
->widget
== widget
)
405 if ((child
->x
== x
) &&
407 (child
->width
== width
) &&
408 (child
->height
== height
)) return;
412 child
->width
= width
;
413 child
->height
= height
;
415 gtk_widget_set_usize (widget
, width
, height
);
417 if (GTK_WIDGET_VISIBLE (widget
) && GTK_WIDGET_VISIBLE (pizza
))
418 gtk_widget_queue_resize (widget
);
426 gtk_pizza_child_resized (GtkPizza
*pizza
,
429 GtkPizzaChild
*child
;
432 g_return_val_if_fail (pizza
!= NULL
, FALSE
);
433 g_return_val_if_fail (GTK_IS_PIZZA (pizza
), FALSE
);
434 g_return_val_if_fail (widget
!= NULL
, FALSE
);
436 children
= pizza
->children
;
439 child
= children
->data
;
440 children
= children
->next
;
442 if (child
->widget
== widget
)
444 return ((child
->width
== widget
->allocation
.width
) &&
445 (child
->height
== widget
->allocation
.height
));
453 gtk_pizza_map (GtkWidget
*widget
)
456 GtkPizzaChild
*child
;
459 g_return_if_fail (widget
!= NULL
);
460 g_return_if_fail (GTK_IS_PIZZA (widget
));
462 GTK_WIDGET_SET_FLAGS (widget
, GTK_MAPPED
);
463 pizza
= GTK_PIZZA (widget
);
465 children
= pizza
->children
;
468 child
= children
->data
;
469 children
= children
->next
;
471 if ( GTK_WIDGET_VISIBLE (child
->widget
) &&
472 !GTK_WIDGET_MAPPED (child
->widget
) &&
475 gtk_widget_map (child
->widget
);
479 gdk_window_show (widget
->window
);
480 gdk_window_show (pizza
->bin_window
);
484 gtk_pizza_realize (GtkWidget
*widget
)
487 GdkWindowAttr attributes
;
488 gint attributes_mask
;
489 GtkPizzaChild
*child
;
492 g_return_if_fail (widget
!= NULL
);
493 g_return_if_fail (GTK_IS_PIZZA (widget
));
495 pizza
= GTK_PIZZA (widget
);
496 GTK_WIDGET_SET_FLAGS (widget
, GTK_REALIZED
);
498 attributes
.window_type
= GDK_WINDOW_CHILD
;
500 attributes
.x
= widget
->allocation
.x
;
501 attributes
.y
= widget
->allocation
.y
;
502 attributes
.width
= widget
->allocation
.width
;
503 attributes
.height
= widget
->allocation
.height
;
505 #ifndef __WXUNIVERSAL__
506 if (pizza
->shadow_type
== GTK_MYSHADOW_NONE
)
508 /* no border, no changes to sizes */
510 else if (pizza
->shadow_type
== GTK_MYSHADOW_THIN
)
512 /* GTK_MYSHADOW_THIN == wxSIMPLE_BORDER */
515 attributes
.width
-= 2;
516 attributes
.height
-= 2;
520 /* GTK_MYSHADOW_IN == wxSUNKEN_BORDER */
521 /* GTK_MYSHADOW_OUT == wxRAISED_BORDER */
524 attributes
.width
-= 4;
525 attributes
.height
-= 4;
527 #endif /* __WXUNIVERSAL__ */
530 if (attributes
.width
< 2) attributes
.width
= 2;
531 if (attributes
.height
< 2) attributes
.height
= 2;
533 attributes
.wclass
= GDK_INPUT_OUTPUT
;
534 attributes
.visual
= gtk_widget_get_visual (widget
);
535 attributes
.colormap
= gtk_widget_get_colormap (widget
);
536 attributes
.event_mask
= GDK_VISIBILITY_NOTIFY_MASK
;
537 attributes_mask
= GDK_WA_X
| GDK_WA_Y
| GDK_WA_VISUAL
| GDK_WA_COLORMAP
;
539 widget
->window
= gdk_window_new(gtk_widget_get_parent_window (widget
),
540 &attributes
, attributes_mask
);
541 gdk_window_set_user_data (widget
->window
, widget
);
546 attributes
.event_mask
= gtk_widget_get_events (widget
);
547 attributes
.event_mask
|= GDK_EXPOSURE_MASK
|
549 GDK_POINTER_MOTION_MASK
|
550 GDK_POINTER_MOTION_HINT_MASK
|
551 GDK_BUTTON_MOTION_MASK
|
552 GDK_BUTTON1_MOTION_MASK
|
553 GDK_BUTTON2_MOTION_MASK
|
554 GDK_BUTTON3_MOTION_MASK
|
555 GDK_BUTTON_PRESS_MASK
|
556 GDK_BUTTON_RELEASE_MASK
|
558 GDK_KEY_RELEASE_MASK
|
559 GDK_ENTER_NOTIFY_MASK
|
560 GDK_LEAVE_NOTIFY_MASK
|
561 GDK_FOCUS_CHANGE_MASK
;
563 pizza
->bin_window
= gdk_window_new(widget
->window
,
564 &attributes
, attributes_mask
);
565 gdk_window_set_user_data (pizza
->bin_window
, widget
);
567 widget
->style
= gtk_style_attach (widget
->style
, widget
->window
);
568 gtk_style_set_background (widget
->style
, widget
->window
, GTK_STATE_NORMAL
);
569 gtk_style_set_background (widget
->style
, pizza
->bin_window
, GTK_STATE_NORMAL
);
572 gdk_window_set_back_pixmap( widget->window, NULL, FALSE );
573 gdk_window_set_back_pixmap( pizza->bin_window, NULL, FALSE );
576 /* cannot be done before realisation */
577 children
= pizza
->children
;
580 child
= children
->data
;
581 children
= children
->next
;
583 gtk_widget_set_parent_window (child
->widget
, pizza
->bin_window
);
588 gtk_pizza_unrealize (GtkWidget
*widget
)
592 g_return_if_fail (widget
!= NULL
);
593 g_return_if_fail (GTK_IS_PIZZA (widget
));
595 pizza
= GTK_PIZZA (widget
);
597 gdk_window_set_user_data (pizza
->bin_window
, NULL
);
598 gdk_window_destroy (pizza
->bin_window
);
599 pizza
->bin_window
= NULL
;
601 if (GTK_WIDGET_CLASS (pizza_parent_class
)->unrealize
)
602 (* GTK_WIDGET_CLASS (pizza_parent_class
)->unrealize
) (widget
);
606 gtk_pizza_size_request (GtkWidget
*widget
,
607 GtkRequisition
*requisition
)
610 GtkPizzaChild
*child
;
612 GtkRequisition child_requisition
;
614 g_return_if_fail (widget
!= NULL
);
615 g_return_if_fail (GTK_IS_PIZZA (widget
));
616 g_return_if_fail (requisition
!= NULL
);
618 pizza
= GTK_PIZZA (widget
);
620 children
= pizza
->children
;
623 child
= children
->data
;
624 children
= children
->next
;
626 if (GTK_WIDGET_VISIBLE (child
->widget
))
628 gtk_widget_size_request (child
->widget
, &child_requisition
);
632 /* request very little, I'm not sure if requesting nothing
633 will always have positive effects on stability... */
634 requisition
->width
= 2;
635 requisition
->height
= 2;
639 gtk_pizza_size_allocate (GtkWidget
*widget
,
640 GtkAllocation
*allocation
)
645 GtkPizzaChild
*child
;
648 g_return_if_fail (widget
!= NULL
);
649 g_return_if_fail (GTK_IS_PIZZA(widget
));
650 g_return_if_fail (allocation
!= NULL
);
652 pizza
= GTK_PIZZA (widget
);
654 widget
->allocation
= *allocation
;
656 if (pizza
->shadow_type
== GTK_MYSHADOW_NONE
)
659 if (pizza
->shadow_type
== GTK_MYSHADOW_THIN
)
664 x
= allocation
->x
+ border
;
665 y
= allocation
->y
+ border
;
666 w
= allocation
->width
- border
*2;
667 h
= allocation
->height
- border
*2;
669 if (GTK_WIDGET_REALIZED (widget
))
671 gdk_window_move_resize( widget
->window
, x
, y
, w
, h
);
672 gdk_window_move_resize( pizza
->bin_window
, 0, 0, w
, h
);
675 children
= pizza
->children
;
678 child
= children
->data
;
679 children
= children
->next
;
681 gtk_pizza_allocate_child (pizza
, child
);
686 gtk_pizza_expose (GtkWidget
*widget
,
687 GdkEventExpose
*event
)
691 g_return_val_if_fail (widget
!= NULL
, FALSE
);
692 g_return_val_if_fail (GTK_IS_PIZZA (widget
), FALSE
);
693 g_return_val_if_fail (event
!= NULL
, FALSE
);
695 pizza
= GTK_PIZZA (widget
);
697 if (event
->window
!= pizza
->bin_window
)
700 /* We handle all expose events in window.cpp now. */
701 if (pizza
->external_expose
)
704 (* GTK_WIDGET_CLASS (pizza_parent_class
)->expose_event
) (widget
, event
);
710 gtk_pizza_style_set(GtkWidget
*widget
, GtkStyle
*previous_style
)
712 if (GTK_WIDGET_REALIZED(widget
))
714 gtk_style_set_background(widget
->style
, widget
->window
, GTK_STATE_NORMAL
);
715 gtk_style_set_background(widget
->style
, GTK_PIZZA(widget
)->bin_window
, GTK_STATE_NORMAL
);
718 (* GTK_WIDGET_CLASS (pizza_parent_class
)->style_set
) (widget
, previous_style
);
722 gtk_pizza_add (GtkContainer
*container
,
725 g_return_if_fail (container
!= NULL
);
726 g_return_if_fail (GTK_IS_PIZZA (container
));
727 g_return_if_fail (widget
!= NULL
);
729 gtk_pizza_put (GTK_PIZZA (container
), widget
, 0, 0, 20, 20 );
733 gtk_pizza_remove (GtkContainer
*container
,
737 GtkPizzaChild
*child
;
740 g_return_if_fail (container
!= NULL
);
741 g_return_if_fail (GTK_IS_PIZZA (container
));
742 g_return_if_fail (widget
!= NULL
);
744 pizza
= GTK_PIZZA (container
);
746 children
= pizza
->children
;
749 child
= children
->data
;
751 if (child
->widget
== widget
)
753 gtk_widget_unparent (widget
);
755 /* security checks */
756 g_return_if_fail (GTK_IS_WIDGET (widget
));
758 pizza
->children
= g_list_remove_link (pizza
->children
, children
);
759 g_list_free (children
);
762 /* security checks */
763 g_return_if_fail (GTK_IS_WIDGET (widget
));
768 children
= children
->next
;
773 gtk_pizza_forall (GtkContainer
*container
,
774 gboolean include_internals
,
775 GtkCallback callback
,
776 gpointer callback_data
)
779 GtkPizzaChild
*child
;
782 g_return_if_fail (container
!= NULL
);
783 g_return_if_fail (GTK_IS_PIZZA (container
));
784 g_return_if_fail (callback
!= (GtkCallback
)NULL
);
786 pizza
= GTK_PIZZA (container
);
788 children
= pizza
->children
;
791 child
= children
->data
;
792 children
= children
->next
;
794 (* callback
) (child
->widget
, callback_data
);
799 gtk_pizza_allocate_child (GtkPizza
*pizza
,
800 GtkPizzaChild
*child
)
802 GtkAllocation allocation
;
803 GtkRequisition requisition
;
805 allocation
.x
= child
->x
- pizza
->xoffset
;
806 allocation
.y
= child
->y
- pizza
->yoffset
;
807 gtk_widget_get_child_requisition (child
->widget
, &requisition
);
808 allocation
.width
= requisition
.width
;
809 allocation
.height
= requisition
.height
;
811 gtk_widget_size_allocate (child
->widget
, &allocation
);
815 gtk_pizza_adjust_allocations_recurse (GtkWidget
*widget
,
818 GtkPizzaAdjData
*data
= cb_data
;
820 widget
->allocation
.x
+= data
->dx
;
821 widget
->allocation
.y
+= data
->dy
;
823 if (GTK_WIDGET_NO_WINDOW (widget
) && GTK_IS_CONTAINER (widget
))
825 gtk_container_forall (GTK_CONTAINER (widget
),
826 gtk_pizza_adjust_allocations_recurse
,
832 gtk_pizza_adjust_allocations (GtkPizza
*pizza
,
837 GtkPizzaAdjData data
;
842 tmp_list
= pizza
->children
;
845 GtkPizzaChild
*child
= tmp_list
->data
;
846 tmp_list
= tmp_list
->next
;
848 child
->widget
->allocation
.x
+= dx
;
849 child
->widget
->allocation
.y
+= dy
;
851 if (GTK_WIDGET_NO_WINDOW (child
->widget
) &&
852 GTK_IS_CONTAINER (child
->widget
))
854 gtk_container_forall (GTK_CONTAINER (child
->widget
),
855 gtk_pizza_adjust_allocations_recurse
,
862 /* This is the main routine to do the scrolling. Scrolling is
863 * done by "Guffaw" scrolling, as in the Mozilla XFE, with
864 * a few modifications.
866 * The main improvement is that we keep track of whether we
867 * are obscured or not. If not, we ignore the generated expose
868 * events and instead do the exposes ourself, without having
869 * to wait for a roundtrip to the server. This also provides
870 * a limited form of expose-event compression, since we do
871 * the affected area as one big chunk.
875 gtk_pizza_scroll (GtkPizza
*pizza
, gint dx
, gint dy
)
877 pizza
->xoffset
+= dx
;
878 pizza
->yoffset
+= dy
;
880 gtk_pizza_adjust_allocations (pizza
, -dx
, -dy
);
882 if (pizza
->bin_window
)
883 gdk_window_scroll( pizza
->bin_window
, -dx
, -dy
);
888 #endif /* __cplusplus */