1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/win_gtk.cpp
3 // Purpose: native GTK+ widget for wxWindow
4 // Author: Paul Cornett
6 // Copyright: (c) 2007 Paul Cornett
7 // Licence: wxWindows licence
8 ///////////////////////////////////////////////////////////////////////////////
10 #include "wx/wxprec.h"
15 #include "wx/gtk/private/win_gtk.h"
17 #include "wx/gtk/private.h"
18 #include "wx/gtk/private/gtk2-compat.h"
21 wxPizza is a custom GTK+ widget derived from GtkFixed. A custom widget
22 is needed to adapt GTK+ to wxWidgets needs in 3 areas: scrolling, window
25 For scrolling, the "set_scroll_adjustments" signal is implemented
26 to make wxPizza appear scrollable to GTK+, allowing it to be put in a
27 GtkScrolledWindow. Child widget positions are adjusted for the scrolling
28 position in size_allocate.
30 For borders, space is reserved in realize and size_allocate. The border is
31 drawn on wxPizza's parent GdkWindow.
33 For RTL, child widget positions are mirrored in size_allocate.
39 int x
, y
, width
, height
;
42 static GtkWidgetClass
* parent_class
;
60 void (*set_scroll_adjustments
)(GtkWidget
*, GtkAdjustment
*, GtkAdjustment
*);
64 static void pizza_size_allocate(GtkWidget
* widget
, GtkAllocation
* alloc
)
66 wxPizza
* pizza
= WX_PIZZA(widget
);
68 pizza
->get_border(border
);
69 int w
= alloc
->width
- border
.left
- border
.right
;
72 if (gtk_widget_get_realized(widget
))
74 int h
= alloc
->height
- border
.top
- border
.bottom
;
76 const int x
= alloc
->x
+ border
.left
;
77 const int y
= alloc
->y
+ border
.top
;
79 GdkWindow
* window
= gtk_widget_get_window(widget
);
81 gdk_window_get_position(window
, &old_x
, &old_y
);
83 if (x
!= old_x
|| y
!= old_y
||
84 w
!= gdk_window_get_width(window
) || h
!= gdk_window_get_height(window
))
86 gdk_window_move_resize(window
, x
, y
, w
, h
);
88 if (border
.left
+ border
.right
+ border
.top
+ border
.bottom
)
90 // old and new border areas need to be invalidated,
91 // otherwise they will not be erased/redrawn properly
92 GtkAllocation old_alloc
;
93 gtk_widget_get_allocation(widget
, &old_alloc
);
94 GdkWindow
* parent
= gtk_widget_get_parent_window(widget
);
95 gdk_window_invalidate_rect(parent
, &old_alloc
, false);
96 gdk_window_invalidate_rect(parent
, alloc
, false);
101 gtk_widget_set_allocation(widget
, alloc
);
103 // adjust child positions
104 for (const GList
* p
= pizza
->m_children
; p
; p
= p
->next
)
106 const wxPizzaChild
* child
= static_cast<wxPizzaChild
*>(p
->data
);
107 if (gtk_widget_get_visible(child
->widget
))
109 GtkAllocation child_alloc
;
110 // note that child positions do not take border into
111 // account, they need to be relative to widget->window,
112 // which has already been adjusted
113 child_alloc
.x
= child
->x
- pizza
->m_scroll_x
;
114 child_alloc
.y
= child
->y
- pizza
->m_scroll_y
;
115 child_alloc
.width
= child
->width
;
116 child_alloc
.height
= child
->height
;
117 if (gtk_widget_get_direction(widget
) == GTK_TEXT_DIR_RTL
)
118 child_alloc
.x
= w
- child_alloc
.x
- child_alloc
.width
;
119 gtk_widget_size_allocate(child
->widget
, &child_alloc
);
124 static void pizza_realize(GtkWidget
* widget
)
126 parent_class
->realize(widget
);
128 wxPizza
* pizza
= WX_PIZZA(widget
);
129 if (pizza
->m_windowStyle
& wxPizza::BORDER_STYLES
)
132 pizza
->get_border(border
);
134 gtk_widget_get_allocation(widget
, &a
);
135 int x
= a
.x
+ border
.left
;
136 int y
= a
.y
+ border
.top
;
137 int w
= a
.width
- border
.left
- border
.right
;
138 int h
= a
.height
- border
.top
- border
.bottom
;
141 gdk_window_move_resize(gtk_widget_get_window(widget
), x
, y
, w
, h
);
145 static void pizza_show(GtkWidget
* widget
)
147 GtkWidget
* parent
= gtk_widget_get_parent(widget
);
148 if (parent
&& (WX_PIZZA(widget
)->m_windowStyle
& wxPizza::BORDER_STYLES
))
150 // invalidate whole allocation so borders will be drawn properly
152 gtk_widget_get_allocation(widget
, &a
);
153 gtk_widget_queue_draw_area(parent
, a
.x
, a
.y
, a
.width
, a
.height
);
156 parent_class
->show(widget
);
159 static void pizza_hide(GtkWidget
* widget
)
161 GtkWidget
* parent
= gtk_widget_get_parent(widget
);
162 if (parent
&& (WX_PIZZA(widget
)->m_windowStyle
& wxPizza::BORDER_STYLES
))
164 // invalidate whole allocation so borders will be erased properly
166 gtk_widget_get_allocation(widget
, &a
);
167 gtk_widget_queue_draw_area(parent
, a
.x
, a
.y
, a
.width
, a
.height
);
170 parent_class
->hide(widget
);
173 static void pizza_add(GtkContainer
* container
, GtkWidget
* widget
)
175 WX_PIZZA(container
)->put(widget
, 0, 0, 1, 1);
178 static void pizza_remove(GtkContainer
* container
, GtkWidget
* widget
)
180 GTK_CONTAINER_CLASS(parent_class
)->remove(container
, widget
);
182 wxPizza
* pizza
= WX_PIZZA(container
);
183 for (GList
* p
= pizza
->m_children
; p
; p
= p
->next
)
185 wxPizzaChild
* child
= static_cast<wxPizzaChild
*>(p
->data
);
186 if (child
->widget
== widget
)
188 pizza
->m_children
= g_list_delete_link(pizza
->m_children
, p
);
196 static void pizza_get_preferred_width(GtkWidget
* widget
, int* minimum
, int* natural
)
199 gtk_widget_get_size_request(widget
, natural
, NULL
);
204 static void pizza_get_preferred_height(GtkWidget
* widget
, int* minimum
, int* natural
)
207 gtk_widget_get_size_request(widget
, NULL
, natural
);
212 // Needed to implement GtkScrollable interface, but we don't care about the
213 // properties. wxWindowGTK handles the adjustments and scroll policy.
214 static void pizza_get_property(GObject
*, guint
, GValue
*, GParamSpec
*)
218 static void pizza_set_property(GObject
*, guint
, const GValue
*, GParamSpec
*)
222 // not used, but needs to exist so gtk_widget_set_scroll_adjustments will work
223 static void pizza_set_scroll_adjustments(GtkWidget
*, GtkAdjustment
*, GtkAdjustment
*)
227 // Marshaller needed for set_scroll_adjustments signal,
228 // generated with GLib-2.4.6 glib-genmarshal
229 #define g_marshal_value_peek_object(v) g_value_get_object (v)
231 g_cclosure_user_marshal_VOID__OBJECT_OBJECT (GClosure
*closure
,
232 GValue
* /*return_value*/,
233 guint n_param_values
,
234 const GValue
*param_values
,
235 gpointer
/*invocation_hint*/,
236 gpointer marshal_data
)
238 typedef void (*GMarshalFunc_VOID__OBJECT_OBJECT
) (gpointer data1
,
242 register GMarshalFunc_VOID__OBJECT_OBJECT callback
;
243 register GCClosure
*cc
= (GCClosure
*) closure
;
244 register gpointer data1
, data2
;
246 g_return_if_fail (n_param_values
== 3);
248 if (G_CCLOSURE_SWAP_DATA (closure
))
250 data1
= closure
->data
;
251 data2
= g_value_peek_pointer (param_values
+ 0);
255 data1
= g_value_peek_pointer (param_values
+ 0);
256 data2
= closure
->data
;
258 callback
= (GMarshalFunc_VOID__OBJECT_OBJECT
) (marshal_data
? marshal_data
: cc
->callback
);
261 g_marshal_value_peek_object (param_values
+ 1),
262 g_marshal_value_peek_object (param_values
+ 2),
267 static void class_init(void* g_class
, void*)
269 GtkWidgetClass
* widget_class
= (GtkWidgetClass
*)g_class
;
270 widget_class
->size_allocate
= pizza_size_allocate
;
271 widget_class
->realize
= pizza_realize
;
272 widget_class
->show
= pizza_show
;
273 widget_class
->hide
= pizza_hide
;
274 GtkContainerClass
* container_class
= (GtkContainerClass
*)g_class
;
275 container_class
->add
= pizza_add
;
276 container_class
->remove
= pizza_remove
;
279 widget_class
->get_preferred_width
= pizza_get_preferred_width
;
280 widget_class
->get_preferred_height
= pizza_get_preferred_height
;
281 GObjectClass
*gobject_class
= G_OBJECT_CLASS(g_class
);
282 gobject_class
->set_property
= pizza_set_property
;
283 gobject_class
->get_property
= pizza_get_property
;
284 g_object_class_override_property(gobject_class
, PROP_HADJUSTMENT
, "hadjustment");
285 g_object_class_override_property(gobject_class
, PROP_VADJUSTMENT
, "vadjustment");
286 g_object_class_override_property(gobject_class
, PROP_HSCROLL_POLICY
, "hscroll-policy");
287 g_object_class_override_property(gobject_class
, PROP_VSCROLL_POLICY
, "vscroll-policy");
289 wxPizzaClass
* klass
= static_cast<wxPizzaClass
*>(g_class
);
290 // needed to make widget appear scrollable to GTK+
291 klass
->set_scroll_adjustments
= pizza_set_scroll_adjustments
;
292 widget_class
->set_scroll_adjustments_signal
=
294 "set_scroll_adjustments",
295 G_TYPE_FROM_CLASS(g_class
),
297 G_STRUCT_OFFSET(wxPizzaClass
, set_scroll_adjustments
),
299 g_cclosure_user_marshal_VOID__OBJECT_OBJECT
,
300 G_TYPE_NONE
, 2, GTK_TYPE_ADJUSTMENT
, GTK_TYPE_ADJUSTMENT
);
302 parent_class
= GTK_WIDGET_CLASS(g_type_class_peek_parent(g_class
));
307 GType
wxPizza::type()
312 const GTypeInfo info
= {
313 sizeof(wxPizzaClass
),
320 type
= g_type_register_static(
321 GTK_TYPE_FIXED
, "wxPizza", &info
, GTypeFlags(0));
323 const GInterfaceInfo interface_info
= { NULL
, NULL
, NULL
};
324 g_type_add_interface_static(type
, GTK_TYPE_SCROLLABLE
, &interface_info
);
330 GtkWidget
* wxPizza::New(long windowStyle
)
332 GtkWidget
* widget
= GTK_WIDGET(g_object_new(type(), NULL
));
333 wxPizza
* pizza
= WX_PIZZA(widget
);
334 pizza
->m_children
= NULL
;
335 pizza
->m_scroll_x
= 0;
336 pizza
->m_scroll_y
= 0;
337 pizza
->m_windowStyle
= windowStyle
;
339 gtk_widget_set_has_window(widget
, true);
341 gtk_fixed_set_has_window(GTK_FIXED(widget
), true);
343 gtk_widget_add_events(widget
,
346 GDK_POINTER_MOTION_MASK
|
347 GDK_POINTER_MOTION_HINT_MASK
|
348 GDK_BUTTON_MOTION_MASK
|
349 GDK_BUTTON1_MOTION_MASK
|
350 GDK_BUTTON2_MOTION_MASK
|
351 GDK_BUTTON3_MOTION_MASK
|
352 GDK_BUTTON_PRESS_MASK
|
353 GDK_BUTTON_RELEASE_MASK
|
355 GDK_KEY_RELEASE_MASK
|
356 GDK_ENTER_NOTIFY_MASK
|
357 GDK_LEAVE_NOTIFY_MASK
|
358 GDK_FOCUS_CHANGE_MASK
);
362 void wxPizza::move(GtkWidget
* widget
, int x
, int y
, int width
, int height
)
364 for (const GList
* p
= m_children
; p
; p
= p
->next
)
366 wxPizzaChild
* child
= static_cast<wxPizzaChild
*>(p
->data
);
367 if (child
->widget
== widget
)
371 child
->width
= width
;
372 child
->height
= height
;
373 // normally a queue-resize would be needed here, but we know
374 // wxWindowGTK::DoMoveWindow() will take care of it
380 void wxPizza::put(GtkWidget
* widget
, int x
, int y
, int width
, int height
)
382 // Re-parenting a TLW under a child window is possible at wx level but
383 // using a TLW as child at GTK+ level results in problems, so don't do it.
384 #if GTK_CHECK_VERSION(2,19,3)
385 if (!gtk_widget_is_toplevel(GTK_WIDGET(widget
)))
387 if (!GTK_WIDGET_TOPLEVEL(GTK_WIDGET(widget
)))
389 gtk_fixed_put(GTK_FIXED(this), widget
, 0, 0);
391 wxPizzaChild
* child
= new wxPizzaChild
;
392 child
->widget
= widget
;
395 child
->width
= width
;
396 child
->height
= height
;
397 m_children
= g_list_append(m_children
, child
);
405 // Adjust allocations for all widgets using the GdkWindow which was just scrolled
407 static void scroll_adjust(GtkWidget
* widget
, void* data
)
409 const AdjustData
* p
= static_cast<AdjustData
*>(data
);
411 gtk_widget_get_allocation(widget
, &a
);
414 gtk_widget_set_allocation(widget
, &a
);
416 if (gtk_widget_get_window(widget
) == p
->window
)
418 // GtkFrame requires a queue_resize, otherwise parts of
419 // the frame newly exposed by the scroll are not drawn.
420 // To be safe, do it for all widgets.
421 gtk_widget_queue_resize_no_redraw(widget
);
422 if (GTK_IS_CONTAINER(widget
))
423 gtk_container_forall(GTK_CONTAINER(widget
), scroll_adjust
, data
);
428 void wxPizza::scroll(int dx
, int dy
)
430 GtkWidget
* widget
= GTK_WIDGET(this);
431 if (gtk_widget_get_direction(widget
) == GTK_TEXT_DIR_RTL
)
435 GdkWindow
* window
= gtk_widget_get_window(widget
);
438 gdk_window_scroll(window
, dx
, dy
);
439 // Adjust child allocations. Doing a queue_resize on the children is not
440 // enough, sometimes they redraw in the wrong place during fast scrolling.
441 AdjustData data
= { window
, dx
, dy
};
442 gtk_container_forall(GTK_CONTAINER(widget
), scroll_adjust
, &data
);
446 void wxPizza::get_border(GtkBorder
& border
)
448 #ifndef __WXUNIVERSAL__
449 if (m_windowStyle
& wxBORDER_SIMPLE
)
450 border
.left
= border
.right
= border
.top
= border
.bottom
= 1;
451 else if (m_windowStyle
& (wxBORDER_RAISED
| wxBORDER_SUNKEN
| wxBORDER_THEME
))
455 if (m_windowStyle
& (wxHSCROLL
| wxVSCROLL
))
456 sc
= gtk_widget_get_style_context(wxGTKPrivate::GetTreeWidget());
458 sc
= gtk_widget_get_style_context(wxGTKPrivate::GetEntryWidget());
460 gtk_style_context_get_border(sc
, GTK_STATE_FLAG_NORMAL
, &border
);
463 if (m_windowStyle
& (wxHSCROLL
| wxVSCROLL
))
464 style
= gtk_widget_get_style(wxGTKPrivate::GetTreeWidget());
466 style
= gtk_widget_get_style(wxGTKPrivate::GetEntryWidget());
468 border
.left
= border
.right
= style
->xthickness
;
469 border
.top
= border
.bottom
= style
->ythickness
;
470 #endif // !__WXGTK3__
473 #endif // !__WXUNIVERSAL__
475 border
.left
= border
.right
= border
.top
= border
.bottom
= 0;