1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/win_gtk.cpp
3 // Purpose: native GTK+ widget for wxWindow
4 // Author: Paul Cornett
5 // Copyright: (c) 2007 Paul Cornett
6 // Licence: wxWindows licence
7 ///////////////////////////////////////////////////////////////////////////////
14 #include "wx/gtk/private/win_gtk.h"
16 #include "wx/gtk/private.h"
17 #include "wx/gtk/private/gtk2-compat.h"
20 wxPizza is a custom GTK+ widget derived from GtkFixed. A custom widget
21 is needed to adapt GTK+ to wxWidgets needs in 3 areas: scrolling, window
24 For scrolling, the "set_scroll_adjustments" signal is implemented
25 to make wxPizza appear scrollable to GTK+, allowing it to be put in a
26 GtkScrolledWindow. Child widget positions are adjusted for the scrolling
27 position in size_allocate.
29 For borders, space is reserved in realize and size_allocate. The border is
30 drawn on wxPizza's parent GdkWindow.
32 For RTL, child widget positions are mirrored in size_allocate.
38 int x
, y
, width
, height
;
41 static GtkWidgetClass
* parent_class
;
59 void (*set_scroll_adjustments
)(GtkWidget
*, GtkAdjustment
*, GtkAdjustment
*);
63 static void pizza_size_allocate(GtkWidget
* widget
, GtkAllocation
* alloc
)
65 wxPizza
* pizza
= WX_PIZZA(widget
);
67 pizza
->get_border(border
);
68 int w
= alloc
->width
- border
.left
- border
.right
;
71 if (gtk_widget_get_realized(widget
))
73 int h
= alloc
->height
- border
.top
- border
.bottom
;
75 const int x
= alloc
->x
+ border
.left
;
76 const int y
= alloc
->y
+ border
.top
;
78 GdkWindow
* window
= gtk_widget_get_window(widget
);
80 gdk_window_get_position(window
, &old_x
, &old_y
);
82 if (x
!= old_x
|| y
!= old_y
||
83 w
!= gdk_window_get_width(window
) || h
!= gdk_window_get_height(window
))
85 gdk_window_move_resize(window
, x
, y
, w
, h
);
87 if (border
.left
+ border
.right
+ border
.top
+ border
.bottom
)
89 // old and new border areas need to be invalidated,
90 // otherwise they will not be erased/redrawn properly
91 GtkAllocation old_alloc
;
92 gtk_widget_get_allocation(widget
, &old_alloc
);
93 GdkWindow
* parent
= gtk_widget_get_parent_window(widget
);
94 gdk_window_invalidate_rect(parent
, &old_alloc
, false);
95 gdk_window_invalidate_rect(parent
, alloc
, false);
100 gtk_widget_set_allocation(widget
, alloc
);
102 // adjust child positions
103 for (const GList
* p
= pizza
->m_children
; p
; p
= p
->next
)
105 const wxPizzaChild
* child
= static_cast<wxPizzaChild
*>(p
->data
);
106 if (gtk_widget_get_visible(child
->widget
))
108 GtkAllocation child_alloc
;
109 // note that child positions do not take border into
110 // account, they need to be relative to widget->window,
111 // which has already been adjusted
112 child_alloc
.x
= child
->x
- pizza
->m_scroll_x
;
113 child_alloc
.y
= child
->y
- pizza
->m_scroll_y
;
114 child_alloc
.width
= child
->width
;
115 child_alloc
.height
= child
->height
;
116 if (gtk_widget_get_direction(widget
) == GTK_TEXT_DIR_RTL
)
117 child_alloc
.x
= w
- child_alloc
.x
- child_alloc
.width
;
118 gtk_widget_size_allocate(child
->widget
, &child_alloc
);
123 static void pizza_realize(GtkWidget
* widget
)
125 parent_class
->realize(widget
);
127 wxPizza
* pizza
= WX_PIZZA(widget
);
128 if (pizza
->m_windowStyle
& wxPizza::BORDER_STYLES
)
131 pizza
->get_border(border
);
133 gtk_widget_get_allocation(widget
, &a
);
134 int x
= a
.x
+ border
.left
;
135 int y
= a
.y
+ border
.top
;
136 int w
= a
.width
- border
.left
- border
.right
;
137 int h
= a
.height
- border
.top
- border
.bottom
;
140 gdk_window_move_resize(gtk_widget_get_window(widget
), x
, y
, w
, h
);
144 static void pizza_show(GtkWidget
* widget
)
146 GtkWidget
* parent
= gtk_widget_get_parent(widget
);
147 if (parent
&& (WX_PIZZA(widget
)->m_windowStyle
& wxPizza::BORDER_STYLES
))
149 // invalidate whole allocation so borders will be drawn properly
151 gtk_widget_get_allocation(widget
, &a
);
152 gtk_widget_queue_draw_area(parent
, a
.x
, a
.y
, a
.width
, a
.height
);
155 parent_class
->show(widget
);
158 static void pizza_hide(GtkWidget
* widget
)
160 GtkWidget
* parent
= gtk_widget_get_parent(widget
);
161 if (parent
&& (WX_PIZZA(widget
)->m_windowStyle
& wxPizza::BORDER_STYLES
))
163 // invalidate whole allocation so borders will be erased properly
165 gtk_widget_get_allocation(widget
, &a
);
166 gtk_widget_queue_draw_area(parent
, a
.x
, a
.y
, a
.width
, a
.height
);
169 parent_class
->hide(widget
);
172 static void pizza_add(GtkContainer
* container
, GtkWidget
* widget
)
174 WX_PIZZA(container
)->put(widget
, 0, 0, 1, 1);
177 static void pizza_remove(GtkContainer
* container
, GtkWidget
* widget
)
179 GTK_CONTAINER_CLASS(parent_class
)->remove(container
, widget
);
181 wxPizza
* pizza
= WX_PIZZA(container
);
182 for (GList
* p
= pizza
->m_children
; p
; p
= p
->next
)
184 wxPizzaChild
* child
= static_cast<wxPizzaChild
*>(p
->data
);
185 if (child
->widget
== widget
)
187 pizza
->m_children
= g_list_delete_link(pizza
->m_children
, p
);
195 static void pizza_get_preferred_width(GtkWidget
* widget
, int* minimum
, int* natural
)
198 gtk_widget_get_size_request(widget
, natural
, NULL
);
203 static void pizza_get_preferred_height(GtkWidget
* widget
, int* minimum
, int* natural
)
206 gtk_widget_get_size_request(widget
, NULL
, natural
);
211 // Needed to implement GtkScrollable interface, but we don't care about the
212 // properties. wxWindowGTK handles the adjustments and scroll policy.
213 static void pizza_get_property(GObject
*, guint
, GValue
*, GParamSpec
*)
217 static void pizza_set_property(GObject
*, guint
, const GValue
*, GParamSpec
*)
221 // not used, but needs to exist so gtk_widget_set_scroll_adjustments will work
222 static void pizza_set_scroll_adjustments(GtkWidget
*, GtkAdjustment
*, GtkAdjustment
*)
226 // Marshaller needed for set_scroll_adjustments signal,
227 // generated with GLib-2.4.6 glib-genmarshal
228 #define g_marshal_value_peek_object(v) g_value_get_object (v)
230 g_cclosure_user_marshal_VOID__OBJECT_OBJECT (GClosure
*closure
,
231 GValue
* /*return_value*/,
232 guint n_param_values
,
233 const GValue
*param_values
,
234 gpointer
/*invocation_hint*/,
235 gpointer marshal_data
)
237 typedef void (*GMarshalFunc_VOID__OBJECT_OBJECT
) (gpointer data1
,
241 register GMarshalFunc_VOID__OBJECT_OBJECT callback
;
242 register GCClosure
*cc
= (GCClosure
*) closure
;
243 register gpointer data1
, data2
;
245 g_return_if_fail (n_param_values
== 3);
247 if (G_CCLOSURE_SWAP_DATA (closure
))
249 data1
= closure
->data
;
250 data2
= g_value_peek_pointer (param_values
+ 0);
254 data1
= g_value_peek_pointer (param_values
+ 0);
255 data2
= closure
->data
;
257 callback
= (GMarshalFunc_VOID__OBJECT_OBJECT
) (marshal_data
? marshal_data
: cc
->callback
);
260 g_marshal_value_peek_object (param_values
+ 1),
261 g_marshal_value_peek_object (param_values
+ 2),
266 static void class_init(void* g_class
, void*)
268 GtkWidgetClass
* widget_class
= (GtkWidgetClass
*)g_class
;
269 widget_class
->size_allocate
= pizza_size_allocate
;
270 widget_class
->realize
= pizza_realize
;
271 widget_class
->show
= pizza_show
;
272 widget_class
->hide
= pizza_hide
;
273 GtkContainerClass
* container_class
= (GtkContainerClass
*)g_class
;
274 container_class
->add
= pizza_add
;
275 container_class
->remove
= pizza_remove
;
278 widget_class
->get_preferred_width
= pizza_get_preferred_width
;
279 widget_class
->get_preferred_height
= pizza_get_preferred_height
;
280 GObjectClass
*gobject_class
= G_OBJECT_CLASS(g_class
);
281 gobject_class
->set_property
= pizza_set_property
;
282 gobject_class
->get_property
= pizza_get_property
;
283 g_object_class_override_property(gobject_class
, PROP_HADJUSTMENT
, "hadjustment");
284 g_object_class_override_property(gobject_class
, PROP_VADJUSTMENT
, "vadjustment");
285 g_object_class_override_property(gobject_class
, PROP_HSCROLL_POLICY
, "hscroll-policy");
286 g_object_class_override_property(gobject_class
, PROP_VSCROLL_POLICY
, "vscroll-policy");
288 wxPizzaClass
* klass
= static_cast<wxPizzaClass
*>(g_class
);
289 // needed to make widget appear scrollable to GTK+
290 klass
->set_scroll_adjustments
= pizza_set_scroll_adjustments
;
291 widget_class
->set_scroll_adjustments_signal
=
293 "set_scroll_adjustments",
294 G_TYPE_FROM_CLASS(g_class
),
296 G_STRUCT_OFFSET(wxPizzaClass
, set_scroll_adjustments
),
298 g_cclosure_user_marshal_VOID__OBJECT_OBJECT
,
299 G_TYPE_NONE
, 2, GTK_TYPE_ADJUSTMENT
, GTK_TYPE_ADJUSTMENT
);
301 parent_class
= GTK_WIDGET_CLASS(g_type_class_peek_parent(g_class
));
306 GType
wxPizza::type()
311 const GTypeInfo info
= {
312 sizeof(wxPizzaClass
),
319 type
= g_type_register_static(
320 GTK_TYPE_FIXED
, "wxPizza", &info
, GTypeFlags(0));
322 const GInterfaceInfo interface_info
= { NULL
, NULL
, NULL
};
323 g_type_add_interface_static(type
, GTK_TYPE_SCROLLABLE
, &interface_info
);
329 GtkWidget
* wxPizza::New(long windowStyle
)
331 GtkWidget
* widget
= GTK_WIDGET(g_object_new(type(), NULL
));
332 wxPizza
* pizza
= WX_PIZZA(widget
);
333 pizza
->m_children
= NULL
;
334 pizza
->m_scroll_x
= 0;
335 pizza
->m_scroll_y
= 0;
336 pizza
->m_windowStyle
= windowStyle
;
338 gtk_widget_set_has_window(widget
, true);
340 gtk_fixed_set_has_window(GTK_FIXED(widget
), true);
342 gtk_widget_add_events(widget
,
345 GDK_POINTER_MOTION_MASK
|
346 GDK_POINTER_MOTION_HINT_MASK
|
347 GDK_BUTTON_MOTION_MASK
|
348 GDK_BUTTON1_MOTION_MASK
|
349 GDK_BUTTON2_MOTION_MASK
|
350 GDK_BUTTON3_MOTION_MASK
|
351 GDK_BUTTON_PRESS_MASK
|
352 GDK_BUTTON_RELEASE_MASK
|
354 GDK_KEY_RELEASE_MASK
|
355 GDK_ENTER_NOTIFY_MASK
|
356 GDK_LEAVE_NOTIFY_MASK
|
357 GDK_FOCUS_CHANGE_MASK
);
361 void wxPizza::move(GtkWidget
* widget
, int x
, int y
, int width
, int height
)
363 for (const GList
* p
= m_children
; p
; p
= p
->next
)
365 wxPizzaChild
* child
= static_cast<wxPizzaChild
*>(p
->data
);
366 if (child
->widget
== widget
)
370 child
->width
= width
;
371 child
->height
= height
;
372 // normally a queue-resize would be needed here, but we know
373 // wxWindowGTK::DoMoveWindow() will take care of it
379 void wxPizza::put(GtkWidget
* widget
, int x
, int y
, int width
, int height
)
381 // Re-parenting a TLW under a child window is possible at wx level but
382 // using a TLW as child at GTK+ level results in problems, so don't do it.
383 #if GTK_CHECK_VERSION(2,19,3)
384 if (!gtk_widget_is_toplevel(GTK_WIDGET(widget
)))
386 if (!GTK_WIDGET_TOPLEVEL(GTK_WIDGET(widget
)))
388 gtk_fixed_put(GTK_FIXED(this), widget
, 0, 0);
390 wxPizzaChild
* child
= new wxPizzaChild
;
391 child
->widget
= widget
;
394 child
->width
= width
;
395 child
->height
= height
;
396 m_children
= g_list_append(m_children
, child
);
404 // Adjust allocations for all widgets using the GdkWindow which was just scrolled
406 static void scroll_adjust(GtkWidget
* widget
, void* data
)
408 const AdjustData
* p
= static_cast<AdjustData
*>(data
);
410 gtk_widget_get_allocation(widget
, &a
);
413 gtk_widget_set_allocation(widget
, &a
);
415 if (gtk_widget_get_window(widget
) == p
->window
)
417 // GtkFrame requires a queue_resize, otherwise parts of
418 // the frame newly exposed by the scroll are not drawn.
419 // To be safe, do it for all widgets.
420 gtk_widget_queue_resize_no_redraw(widget
);
421 if (GTK_IS_CONTAINER(widget
))
422 gtk_container_forall(GTK_CONTAINER(widget
), scroll_adjust
, data
);
427 void wxPizza::scroll(int dx
, int dy
)
429 GtkWidget
* widget
= GTK_WIDGET(this);
430 if (gtk_widget_get_direction(widget
) == GTK_TEXT_DIR_RTL
)
434 GdkWindow
* window
= gtk_widget_get_window(widget
);
437 gdk_window_scroll(window
, dx
, dy
);
438 // Adjust child allocations. Doing a queue_resize on the children is not
439 // enough, sometimes they redraw in the wrong place during fast scrolling.
440 AdjustData data
= { window
, dx
, dy
};
441 gtk_container_forall(GTK_CONTAINER(widget
), scroll_adjust
, &data
);
445 void wxPizza::get_border(GtkBorder
& border
)
447 #ifndef __WXUNIVERSAL__
448 if (m_windowStyle
& wxBORDER_SIMPLE
)
449 border
.left
= border
.right
= border
.top
= border
.bottom
= 1;
450 else if (m_windowStyle
& (wxBORDER_RAISED
| wxBORDER_SUNKEN
| wxBORDER_THEME
))
454 if (m_windowStyle
& (wxHSCROLL
| wxVSCROLL
))
455 sc
= gtk_widget_get_style_context(wxGTKPrivate::GetTreeWidget());
457 sc
= gtk_widget_get_style_context(wxGTKPrivate::GetEntryWidget());
459 gtk_style_context_get_border(sc
, GTK_STATE_FLAG_NORMAL
, &border
);
462 if (m_windowStyle
& (wxHSCROLL
| wxVSCROLL
))
463 style
= gtk_widget_get_style(wxGTKPrivate::GetTreeWidget());
465 style
= gtk_widget_get_style(wxGTKPrivate::GetEntryWidget());
467 border
.left
= border
.right
= style
->xthickness
;
468 border
.top
= border
.bottom
= style
->ythickness
;
469 #endif // !__WXGTK3__
472 #endif // !__WXUNIVERSAL__
474 border
.left
= border
.right
= border
.top
= border
.bottom
= 0;