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"
13 #include "wx/gtk/private.h"
14 #include "wx/gtk/private/win_gtk.h"
17 wxPizza is a custom GTK+ widget derived from GtkFixed. A custom widget
18 is needed to adapt GTK+ to wxWidgets needs in 3 areas: scrolling, window
21 For scrolling, the "set_scroll_adjustments" signal is implemented
22 to make wxPizza appear scrollable to GTK+, allowing it to be put in a
23 GtkScrolledWindow. Child widget positions are adjusted for the scrolling
24 position in size_allocate.
26 For borders, space is reserved in realize and size_allocate. The border is
27 drawn on wxPizza's parent GdkWindow.
29 For RTL, child widget positions are mirrored in size_allocate.
35 int x
, y
, width
, height
;
38 static GtkWidgetClass
* parent_class
;
45 void (*set_scroll_adjustments
)(GtkWidget
*, GtkAdjustment
*, GtkAdjustment
*);
48 static void size_allocate(GtkWidget
* widget
, GtkAllocation
* alloc
)
50 wxPizza
* pizza
= WX_PIZZA(widget
);
51 int border_x
, border_y
;
52 pizza
->get_border_widths(border_x
, border_y
);
53 int w
= alloc
->width
- 2 * border_x
;
56 if (gtk_widget_get_realized(widget
))
58 int h
= alloc
->height
- 2 * border_y
;
60 const int x
= alloc
->x
+ border_x
;
61 const int y
= alloc
->y
+ border_y
;
63 GdkWindow
* window
= gtk_widget_get_window(widget
);
65 gdk_window_get_position(window
, &old_x
, &old_y
);
67 if (x
!= old_x
|| y
!= old_y
||
68 w
!= gdk_window_get_width(window
) || h
!= gdk_window_get_height(window
))
70 gdk_window_move_resize(window
, x
, y
, w
, h
);
72 if (border_x
+ border_y
)
74 // old and new border areas need to be invalidated,
75 // otherwise they will not be erased/redrawn properly
76 GdkWindow
* parent
= gtk_widget_get_parent_window(widget
);
77 gdk_window_invalidate_rect(parent
, &widget
->allocation
, false);
78 gdk_window_invalidate_rect(parent
, alloc
, false);
83 widget
->allocation
= *alloc
;
85 // adjust child positions
86 for (const GList
* p
= pizza
->m_children
; p
; p
= p
->next
)
88 const wxPizzaChild
* child
= static_cast<wxPizzaChild
*>(p
->data
);
89 if (gtk_widget_get_visible(child
->widget
))
91 GtkAllocation child_alloc
;
92 // note that child positions do not take border into
93 // account, they need to be relative to widget->window,
94 // which has already been adjusted
95 child_alloc
.x
= child
->x
- pizza
->m_scroll_x
;
96 child_alloc
.y
= child
->y
- pizza
->m_scroll_y
;
97 child_alloc
.width
= child
->width
;
98 child_alloc
.height
= child
->height
;
99 if (gtk_widget_get_direction(widget
) == GTK_TEXT_DIR_RTL
)
100 child_alloc
.x
= w
- child_alloc
.x
- child_alloc
.width
;
101 gtk_widget_size_allocate(child
->widget
, &child_alloc
);
106 static void realize(GtkWidget
* widget
)
108 parent_class
->realize(widget
);
110 wxPizza
* pizza
= WX_PIZZA(widget
);
111 if (pizza
->m_border_style
)
113 int border_x
, border_y
;
114 pizza
->get_border_widths(border_x
, border_y
);
115 int x
= widget
->allocation
.x
+ border_x
;
116 int y
= widget
->allocation
.y
+ border_y
;
117 int w
= widget
->allocation
.width
- 2 * border_x
;
118 int h
= widget
->allocation
.height
- 2 * border_y
;
121 gdk_window_move_resize(widget
->window
, x
, y
, w
, h
);
125 static void show(GtkWidget
* widget
)
127 if (widget
->parent
&& WX_PIZZA(widget
)->m_border_style
)
129 // invalidate whole allocation so borders will be drawn properly
130 const GtkAllocation
& a
= widget
->allocation
;
131 gtk_widget_queue_draw_area(widget
->parent
, a
.x
, a
.y
, a
.width
, a
.height
);
134 parent_class
->show(widget
);
137 static void hide(GtkWidget
* widget
)
139 if (widget
->parent
&& WX_PIZZA(widget
)->m_border_style
)
141 // invalidate whole allocation so borders will be erased properly
142 const GtkAllocation
& a
= widget
->allocation
;
143 gtk_widget_queue_draw_area(widget
->parent
, a
.x
, a
.y
, a
.width
, a
.height
);
146 parent_class
->hide(widget
);
149 static void pizza_add(GtkContainer
* container
, GtkWidget
* widget
)
151 WX_PIZZA(container
)->put(widget
, 0, 0, 1, 1);
154 static void pizza_remove(GtkContainer
* container
, GtkWidget
* widget
)
156 GTK_CONTAINER_CLASS(parent_class
)->remove(container
, widget
);
158 wxPizza
* pizza
= WX_PIZZA(container
);
159 for (GList
* p
= pizza
->m_children
; p
; p
= p
->next
)
161 wxPizzaChild
* child
= static_cast<wxPizzaChild
*>(p
->data
);
162 if (child
->widget
== widget
)
164 pizza
->m_children
= g_list_delete_link(pizza
->m_children
, p
);
171 // not used, but needs to exist so gtk_widget_set_scroll_adjustments will work
172 static void set_scroll_adjustments(GtkWidget
*, GtkAdjustment
*, GtkAdjustment
*)
176 // Marshaller needed for set_scroll_adjustments signal,
177 // generated with GLib-2.4.6 glib-genmarshal
178 #define g_marshal_value_peek_object(v) g_value_get_object (v)
180 g_cclosure_user_marshal_VOID__OBJECT_OBJECT (GClosure
*closure
,
181 GValue
* /*return_value*/,
182 guint n_param_values
,
183 const GValue
*param_values
,
184 gpointer
/*invocation_hint*/,
185 gpointer marshal_data
)
187 typedef void (*GMarshalFunc_VOID__OBJECT_OBJECT
) (gpointer data1
,
191 register GMarshalFunc_VOID__OBJECT_OBJECT callback
;
192 register GCClosure
*cc
= (GCClosure
*) closure
;
193 register gpointer data1
, data2
;
195 g_return_if_fail (n_param_values
== 3);
197 if (G_CCLOSURE_SWAP_DATA (closure
))
199 data1
= closure
->data
;
200 data2
= g_value_peek_pointer (param_values
+ 0);
204 data1
= g_value_peek_pointer (param_values
+ 0);
205 data2
= closure
->data
;
207 callback
= (GMarshalFunc_VOID__OBJECT_OBJECT
) (marshal_data
? marshal_data
: cc
->callback
);
210 g_marshal_value_peek_object (param_values
+ 1),
211 g_marshal_value_peek_object (param_values
+ 2),
215 static void class_init(void* g_class
, void*)
217 GtkWidgetClass
* widget_class
= (GtkWidgetClass
*)g_class
;
218 widget_class
->size_allocate
= size_allocate
;
219 widget_class
->realize
= realize
;
220 widget_class
->show
= show
;
221 widget_class
->hide
= hide
;
222 GtkContainerClass
* container_class
= (GtkContainerClass
*)g_class
;
223 container_class
->add
= pizza_add
;
224 container_class
->remove
= pizza_remove
;
225 wxPizzaClass
* klass
= (wxPizzaClass
*)g_class
;
227 // needed to make widget appear scrollable to GTK+
228 klass
->set_scroll_adjustments
= set_scroll_adjustments
;
229 widget_class
->set_scroll_adjustments_signal
=
231 "set_scroll_adjustments",
232 G_TYPE_FROM_CLASS(g_class
),
234 G_STRUCT_OFFSET(wxPizzaClass
, set_scroll_adjustments
),
236 g_cclosure_user_marshal_VOID__OBJECT_OBJECT
,
237 G_TYPE_NONE
, 2, GTK_TYPE_ADJUSTMENT
, GTK_TYPE_ADJUSTMENT
);
239 parent_class
= GTK_WIDGET_CLASS(g_type_class_peek_parent(g_class
));
244 GType
wxPizza::type()
249 const GTypeInfo info
= {
250 sizeof(wxPizzaClass
),
257 type
= g_type_register_static(
258 GTK_TYPE_FIXED
, "wxPizza", &info
, GTypeFlags(0));
263 GtkWidget
* wxPizza::New(long windowStyle
)
265 GtkWidget
* widget
= GTK_WIDGET(g_object_new(type(), NULL
));
266 wxPizza
* pizza
= WX_PIZZA(widget
);
267 pizza
->m_children
= NULL
;
268 pizza
->m_scroll_x
= 0;
269 pizza
->m_scroll_y
= 0;
270 pizza
->m_is_scrollable
= (windowStyle
& (wxHSCROLL
| wxVSCROLL
)) != 0;
271 // mask off border styles not useable with wxPizza
272 pizza
->m_border_style
= int(windowStyle
& BORDER_STYLES
);
273 #if GTK_CHECK_VERSION(3,0,0) || defined(GTK_DISABLE_DEPRECATED)
274 gtk_widget_set_has_window(widget
, true);
276 gtk_fixed_set_has_window(GTK_FIXED(widget
), true);
278 gtk_widget_add_events(widget
,
281 GDK_POINTER_MOTION_MASK
|
282 GDK_POINTER_MOTION_HINT_MASK
|
283 GDK_BUTTON_MOTION_MASK
|
284 GDK_BUTTON1_MOTION_MASK
|
285 GDK_BUTTON2_MOTION_MASK
|
286 GDK_BUTTON3_MOTION_MASK
|
287 GDK_BUTTON_PRESS_MASK
|
288 GDK_BUTTON_RELEASE_MASK
|
290 GDK_KEY_RELEASE_MASK
|
291 GDK_ENTER_NOTIFY_MASK
|
292 GDK_LEAVE_NOTIFY_MASK
|
293 GDK_FOCUS_CHANGE_MASK
);
297 void wxPizza::move(GtkWidget
* widget
, int x
, int y
, int width
, int height
)
299 for (const GList
* p
= m_children
; p
; p
= p
->next
)
301 wxPizzaChild
* child
= static_cast<wxPizzaChild
*>(p
->data
);
302 if (child
->widget
== widget
)
306 child
->width
= width
;
307 child
->height
= height
;
308 // normally a queue-resize would be needed here, but we know
309 // wxWindowGTK::DoMoveWindow() will take care of it
315 void wxPizza::put(GtkWidget
* widget
, int x
, int y
, int width
, int height
)
317 gtk_fixed_put(GTK_FIXED(this), widget
, 0, 0);
319 wxPizzaChild
* child
= new wxPizzaChild
;
320 child
->widget
= widget
;
323 child
->width
= width
;
324 child
->height
= height
;
325 m_children
= g_list_append(m_children
, child
);
333 // Adjust allocations for all widgets using the GdkWindow which was just scrolled
335 static void scroll_adjust(GtkWidget
* widget
, void* data
)
337 const AdjustData
* p
= static_cast<AdjustData
*>(data
);
338 widget
->allocation
.x
+= p
->dx
;
339 widget
->allocation
.y
+= p
->dy
;
341 if (widget
->window
== p
->window
)
343 // GtkFrame requires a queue_resize, otherwise parts of
344 // the frame newly exposed by the scroll are not drawn.
345 // To be safe, do it for all widgets.
346 gtk_widget_queue_resize_no_redraw(widget
);
347 if (GTK_IS_CONTAINER(widget
))
348 gtk_container_forall(GTK_CONTAINER(widget
), scroll_adjust
, data
);
353 void wxPizza::scroll(int dx
, int dy
)
355 GtkWidget
* widget
= GTK_WIDGET(this);
356 if (gtk_widget_get_direction(widget
) == GTK_TEXT_DIR_RTL
)
362 gdk_window_scroll(widget
->window
, dx
, dy
);
363 // Adjust child allocations. Doing a queue_resize on the children is not
364 // enough, sometimes they redraw in the wrong place during fast scrolling.
365 AdjustData data
= { widget
->window
, dx
, dy
};
366 gtk_container_forall(GTK_CONTAINER(widget
), scroll_adjust
, &data
);
370 void wxPizza::get_border_widths(int& x
, int& y
)
373 if (m_border_style
== 0)
376 #ifndef __WXUNIVERSAL__
377 if (m_border_style
& wxBORDER_SIMPLE
)
379 else if (m_is_scrollable
/* || (m_border_style & wxBORDER_THEME) */)
381 GtkWidget
*style_widget
= wxGTKPrivate::GetTreeWidget();
383 if (style_widget
->style
)
385 x
= style_widget
->style
->xthickness
;
386 y
= style_widget
->style
->ythickness
;
391 GtkWidget
*style_widget
= wxGTKPrivate::GetEntryWidget();
393 if (style_widget
->style
)
395 x
= style_widget
->style
->xthickness
;
396 y
= style_widget
->style
->ythickness
;