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 ///////////////////////////////////////////////////////////////////////////////
11 #include "wx/gtk/private.h"
12 #include "wx/gtk/private/win_gtk.h"
15 wxPizza is a custom GTK+ widget derived from GtkFixed. A custom widget
16 is needed to adapt GTK+ to wxWidgets needs in 3 areas: scrolling, window
19 For scrolling, the "set_scroll_adjustments" signal is implemented
20 to make wxPizza appear scrollable to GTK+, allowing it to be put in a
21 GtkScrolledWindow. Child widget positions are adjusted for the scrolling
22 position in size_allocate.
24 For borders, space is reserved in realize and size_allocate. The border is
25 drawn on wxPizza's parent GdkWindow.
27 For RTL, child widget positions are mirrored in size_allocate.
30 static GtkWidgetClass
* parent_class
;
37 void (*set_scroll_adjustments
)(GtkWidget
*, GtkAdjustment
*, GtkAdjustment
*);
40 static void size_allocate(GtkWidget
* widget
, GtkAllocation
* alloc
)
42 const bool is_resize
=
43 widget
->allocation
.width
!= alloc
->width
||
44 widget
->allocation
.height
!= alloc
->height
;
46 widget
->allocation
.x
!= alloc
->x
||
47 widget
->allocation
.y
!= alloc
->y
;
49 wxPizza
* pizza
= WX_PIZZA(widget
);
50 int border_x
, border_y
;
51 pizza
->get_border_widths(border_x
, border_y
);
52 int w
= alloc
->width
- 2 * border_x
;
55 if (gtk_widget_get_realized(widget
) && (is_move
|| is_resize
))
57 int h
= alloc
->height
- 2 * border_y
;
60 gdk_window_move_resize(widget
->window
,
61 alloc
->x
+ border_x
, alloc
->y
+ border_y
, w
, h
);
63 if (is_resize
&& (border_x
|| border_y
))
65 // old and new border areas need to be invalidated,
66 // otherwise they will not be erased/redrawn properly
67 GdkWindow
* parent
= gtk_widget_get_parent_window(widget
);
68 gdk_window_invalidate_rect(parent
, &widget
->allocation
, false);
69 gdk_window_invalidate_rect(parent
, alloc
, false);
73 widget
->allocation
= *alloc
;
75 // adjust child positions
76 for (const GList
* list
= pizza
->m_fixed
.children
; list
; list
= list
->next
)
78 const GtkFixedChild
* child
= static_cast<GtkFixedChild
*>(list
->data
);
79 if (gtk_widget_get_visible(child
->widget
))
81 GtkAllocation child_alloc
;
82 // note that child positions do not take border into
83 // account, they need to be relative to widget->window,
84 // which has already been adjusted
85 child_alloc
.x
= child
->x
- pizza
->m_scroll_x
;
86 child_alloc
.y
= child
->y
- pizza
->m_scroll_y
;
88 gtk_widget_get_child_requisition(child
->widget
, &req
);
89 child_alloc
.width
= req
.width
;
90 child_alloc
.height
= req
.height
;
91 if (gtk_widget_get_direction(widget
) == GTK_TEXT_DIR_RTL
)
92 child_alloc
.x
= w
- child_alloc
.x
- child_alloc
.width
;
93 gtk_widget_size_allocate(child
->widget
, &child_alloc
);
98 static void realize(GtkWidget
* widget
)
100 parent_class
->realize(widget
);
102 wxPizza
* pizza
= WX_PIZZA(widget
);
103 if (pizza
->m_border_style
)
105 int border_x
, border_y
;
106 pizza
->get_border_widths(border_x
, border_y
);
107 int x
= widget
->allocation
.x
+ border_x
;
108 int y
= widget
->allocation
.y
+ border_y
;
109 int w
= widget
->allocation
.width
- 2 * border_x
;
110 int h
= widget
->allocation
.height
- 2 * border_y
;
113 gdk_window_move_resize(widget
->window
, x
, y
, w
, h
);
117 static void show(GtkWidget
* widget
)
119 if (widget
->parent
&& WX_PIZZA(widget
)->m_border_style
)
121 // invalidate whole allocation so borders will be drawn properly
122 const GtkAllocation
& a
= widget
->allocation
;
123 gtk_widget_queue_draw_area(widget
->parent
, a
.x
, a
.y
, a
.width
, a
.height
);
126 parent_class
->show(widget
);
129 static void hide(GtkWidget
* widget
)
131 if (widget
->parent
&& WX_PIZZA(widget
)->m_border_style
)
133 // invalidate whole allocation so borders will be erased properly
134 const GtkAllocation
& a
= widget
->allocation
;
135 gtk_widget_queue_draw_area(widget
->parent
, a
.x
, a
.y
, a
.width
, a
.height
);
138 parent_class
->hide(widget
);
141 // not used, but needs to exist so gtk_widget_set_scroll_adjustments will work
142 static void set_scroll_adjustments(GtkWidget
*, GtkAdjustment
*, GtkAdjustment
*)
146 // Marshaller needed for set_scroll_adjustments signal,
147 // generated with GLib-2.4.6 glib-genmarshal
148 #define g_marshal_value_peek_object(v) g_value_get_object (v)
150 g_cclosure_user_marshal_VOID__OBJECT_OBJECT (GClosure
*closure
,
151 GValue
* /*return_value*/,
152 guint n_param_values
,
153 const GValue
*param_values
,
154 gpointer
/*invocation_hint*/,
155 gpointer marshal_data
)
157 typedef void (*GMarshalFunc_VOID__OBJECT_OBJECT
) (gpointer data1
,
161 register GMarshalFunc_VOID__OBJECT_OBJECT callback
;
162 register GCClosure
*cc
= (GCClosure
*) closure
;
163 register gpointer data1
, data2
;
165 g_return_if_fail (n_param_values
== 3);
167 if (G_CCLOSURE_SWAP_DATA (closure
))
169 data1
= closure
->data
;
170 data2
= g_value_peek_pointer (param_values
+ 0);
174 data1
= g_value_peek_pointer (param_values
+ 0);
175 data2
= closure
->data
;
177 callback
= (GMarshalFunc_VOID__OBJECT_OBJECT
) (marshal_data
? marshal_data
: cc
->callback
);
180 g_marshal_value_peek_object (param_values
+ 1),
181 g_marshal_value_peek_object (param_values
+ 2),
185 static void class_init(void* g_class
, void*)
187 GtkWidgetClass
* widget_class
= (GtkWidgetClass
*)g_class
;
188 widget_class
->size_allocate
= size_allocate
;
189 widget_class
->realize
= realize
;
190 widget_class
->show
= show
;
191 widget_class
->hide
= hide
;
192 wxPizzaClass
* klass
= (wxPizzaClass
*)g_class
;
194 // needed to make widget appear scrollable to GTK+
195 klass
->set_scroll_adjustments
= set_scroll_adjustments
;
196 widget_class
->set_scroll_adjustments_signal
=
198 "set_scroll_adjustments",
199 G_TYPE_FROM_CLASS(g_class
),
201 G_STRUCT_OFFSET(wxPizzaClass
, set_scroll_adjustments
),
203 g_cclosure_user_marshal_VOID__OBJECT_OBJECT
,
204 G_TYPE_NONE
, 2, GTK_TYPE_ADJUSTMENT
, GTK_TYPE_ADJUSTMENT
);
206 parent_class
= GTK_WIDGET_CLASS(g_type_class_peek_parent(g_class
));
211 GType
wxPizza::type()
216 const GTypeInfo info
= {
217 sizeof(wxPizzaClass
),
224 type
= g_type_register_static(
225 GTK_TYPE_FIXED
, "wxPizza", &info
, GTypeFlags(0));
230 GtkWidget
* wxPizza::New(long windowStyle
)
232 GtkWidget
* widget
= GTK_WIDGET(g_object_new(type(), NULL
));
233 wxPizza
* pizza
= WX_PIZZA(widget
);
234 pizza
->m_scroll_x
= 0;
235 pizza
->m_scroll_y
= 0;
236 pizza
->m_is_scrollable
= (windowStyle
& (wxHSCROLL
| wxVSCROLL
)) != 0;
237 // mask off border styles not useable with wxPizza
238 pizza
->m_border_style
= int(windowStyle
& BORDER_STYLES
);
239 #if GTK_CHECK_VERSION(3,0,0) || defined(GTK_DISABLE_DEPRECATED)
240 gtk_widget_set_has_window(widget
, true);
242 gtk_fixed_set_has_window(GTK_FIXED(widget
), true);
244 gtk_widget_add_events(widget
,
247 GDK_POINTER_MOTION_MASK
|
248 GDK_POINTER_MOTION_HINT_MASK
|
249 GDK_BUTTON_MOTION_MASK
|
250 GDK_BUTTON1_MOTION_MASK
|
251 GDK_BUTTON2_MOTION_MASK
|
252 GDK_BUTTON3_MOTION_MASK
|
253 GDK_BUTTON_PRESS_MASK
|
254 GDK_BUTTON_RELEASE_MASK
|
256 GDK_KEY_RELEASE_MASK
|
257 GDK_ENTER_NOTIFY_MASK
|
258 GDK_LEAVE_NOTIFY_MASK
|
259 GDK_FOCUS_CHANGE_MASK
);
263 // gtk_fixed_move does not check for a change before issuing a queue_resize,
264 // we need to avoid that to prevent endless sizing loops, so check first
265 void wxPizza::move(GtkWidget
* widget
, int x
, int y
)
267 GtkFixed
* fixed
= &m_fixed
;
268 for (const GList
* list
= fixed
->children
; list
; list
= list
->next
)
270 const GtkFixedChild
* child
= static_cast<GtkFixedChild
*>(list
->data
);
271 if (child
->widget
== widget
)
273 if (child
->x
!= x
|| child
->y
!= y
)
274 gtk_fixed_move(fixed
, widget
, x
, y
);
280 void wxPizza::put(GtkWidget
* widget
, int x
, int y
)
282 gtk_fixed_put(&m_fixed
, widget
, x
, y
);
290 // Adjust allocations for all widgets using the GdkWindow which was just scrolled
292 static void scroll_adjust(GtkWidget
* widget
, void* data
)
294 const AdjustData
* p
= static_cast<AdjustData
*>(data
);
295 widget
->allocation
.x
+= p
->dx
;
296 widget
->allocation
.y
+= p
->dy
;
298 if (widget
->window
== p
->window
)
300 // GtkFrame requires a queue_resize, otherwise parts of
301 // the frame newly exposed by the scroll are not drawn.
302 // To be safe, do it for all widgets.
303 gtk_widget_queue_resize_no_redraw(widget
);
304 if (GTK_IS_CONTAINER(widget
))
305 gtk_container_forall(GTK_CONTAINER(widget
), scroll_adjust
, data
);
310 void wxPizza::scroll(int dx
, int dy
)
312 GtkWidget
* widget
= GTK_WIDGET(this);
313 if (gtk_widget_get_direction(widget
) == GTK_TEXT_DIR_RTL
)
319 gdk_window_scroll(widget
->window
, dx
, dy
);
320 // Adjust child allocations. Doing a queue_resize on the children is not
321 // enough, sometimes they redraw in the wrong place during fast scrolling.
322 AdjustData data
= { widget
->window
, dx
, dy
};
323 gtk_container_forall(GTK_CONTAINER(widget
), scroll_adjust
, &data
);
327 void wxPizza::get_border_widths(int& x
, int& y
)
330 if (m_border_style
== 0)
333 #ifndef __WXUNIVERSAL__
334 if (m_border_style
& wxBORDER_SIMPLE
)
336 else if (m_is_scrollable
/* || (m_border_style & wxBORDER_THEME) */)
338 GtkWidget
*style_widget
= wxGTKPrivate::GetTreeWidget();
340 if (style_widget
->style
)
342 x
= style_widget
->style
->xthickness
;
343 y
= style_widget
->style
->ythickness
;
348 GtkWidget
*style_widget
= wxGTKPrivate::GetEntryWidget();
350 if (style_widget
->style
)
352 x
= style_widget
->style
->xthickness
;
353 y
= style_widget
->style
->ythickness
;