]> git.saurik.com Git - wxWidgets.git/blame - src/gtk/win_gtk.cpp
Only use Unix/X11-specific files under Unix in wxGTK.
[wxWidgets.git] / src / gtk / win_gtk.cpp
CommitLineData
e56307d3
PC
1///////////////////////////////////////////////////////////////////////////////
2// Name: src/gtk/win_gtk.cpp
3// Purpose: native GTK+ widget for wxWindow
4// Author: Paul Cornett
5// Id: $Id$
6// Copyright: (c) 2007 Paul Cornett
7// Licence: wxWindows licence
8///////////////////////////////////////////////////////////////////////////////
9
e762ef8f
VZ
10#include "wx/wxprec.h"
11
e56307d3 12#include "wx/defs.h"
e8759560 13#include "wx/gtk/private.h"
bbd92d1d 14#include "wx/gtk/private/win_gtk.h"
e56307d3
PC
15
16/*
17wxPizza is a custom GTK+ widget derived from GtkFixed. A custom widget
18is needed to adapt GTK+ to wxWidgets needs in 3 areas: scrolling, window
19borders, and RTL.
20
21For scrolling, the "set_scroll_adjustments" signal is implemented
22to make wxPizza appear scrollable to GTK+, allowing it to be put in a
23GtkScrolledWindow. Child widget positions are adjusted for the scrolling
375efc1f 24position in size_allocate.
e56307d3 25
75f661bb
PC
26For borders, space is reserved in realize and size_allocate. The border is
27drawn on wxPizza's parent GdkWindow.
e56307d3
PC
28
29For RTL, child widget positions are mirrored in size_allocate.
30*/
31
3b7067a0
PC
32struct wxPizzaChild
33{
34 GtkWidget* widget;
35 int x, y, width, height;
36};
37
e56307d3
PC
38static GtkWidgetClass* parent_class;
39
40extern "C" {
41
42struct wxPizzaClass
43{
44 GtkFixedClass parent;
45 void (*set_scroll_adjustments)(GtkWidget*, GtkAdjustment*, GtkAdjustment*);
46};
47
48static void size_allocate(GtkWidget* widget, GtkAllocation* alloc)
49{
e56307d3
PC
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;
54 if (w < 0) w = 0;
55
030f4112 56 if (gtk_widget_get_realized(widget))
e56307d3
PC
57 {
58 int h = alloc->height - 2 * border_y;
59 if (h < 0) h = 0;
030f4112
PC
60 const int x = alloc->x + border_x;
61 const int y = alloc->y + border_y;
e56307d3 62
030f4112
PC
63 GdkWindow* window = gtk_widget_get_window(widget);
64 int old_x, old_y;
65 gdk_window_get_position(window, &old_x, &old_y);
375efc1f 66
030f4112
PC
67 if (x != old_x || y != old_y ||
68 w != gdk_window_get_width(window) || h != gdk_window_get_height(window))
e56307d3 69 {
030f4112
PC
70 gdk_window_move_resize(window, x, y, w, h);
71
72 if (border_x + border_y)
73 {
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);
79 }
e56307d3
PC
80 }
81 }
7456fe19
PC
82
83 widget->allocation = *alloc;
03647350 84
e56307d3 85 // adjust child positions
3b7067a0 86 for (const GList* p = pizza->m_children; p; p = p->next)
e56307d3 87 {
3b7067a0 88 const wxPizzaChild* child = static_cast<wxPizzaChild*>(p->data);
fc9ab22a 89 if (gtk_widget_get_visible(child->widget))
e56307d3
PC
90 {
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;
3b7067a0
PC
97 child_alloc.width = child->width;
98 child_alloc.height = child->height;
e56307d3 99 if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL)
e56307d3 100 child_alloc.x = w - child_alloc.x - child_alloc.width;
18319143 101 gtk_widget_size_allocate(child->widget, &child_alloc);
e56307d3
PC
102 }
103 }
104}
105
106static void realize(GtkWidget* widget)
107{
108 parent_class->realize(widget);
109
110 wxPizza* pizza = WX_PIZZA(widget);
375efc1f 111 if (pizza->m_border_style)
e56307d3 112 {
e56307d3
PC
113 int border_x, border_y;
114 pizza->get_border_widths(border_x, border_y);
75f661bb
PC
115 int x = widget->allocation.x + border_x;
116 int y = widget->allocation.y + border_y;
e56307d3
PC
117 int w = widget->allocation.width - 2 * border_x;
118 int h = widget->allocation.height - 2 * border_y;
119 if (w < 0) w = 0;
120 if (h < 0) h = 0;
375efc1f 121 gdk_window_move_resize(widget->window, x, y, w, h);
e56307d3 122 }
e56307d3
PC
123}
124
6e7cf3bd
PC
125static void show(GtkWidget* widget)
126{
127 if (widget->parent && WX_PIZZA(widget)->m_border_style)
128 {
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);
132 }
133
134 parent_class->show(widget);
135}
136
137static void hide(GtkWidget* widget)
138{
139 if (widget->parent && WX_PIZZA(widget)->m_border_style)
140 {
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);
144 }
145
146 parent_class->hide(widget);
147}
148
3b7067a0
PC
149static void pizza_add(GtkContainer* container, GtkWidget* widget)
150{
151 WX_PIZZA(container)->put(widget, 0, 0, 1, 1);
152}
153
154static void pizza_remove(GtkContainer* container, GtkWidget* widget)
155{
156 GTK_CONTAINER_CLASS(parent_class)->remove(container, widget);
157
158 wxPizza* pizza = WX_PIZZA(container);
159 for (GList* p = pizza->m_children; p; p = p->next)
160 {
161 wxPizzaChild* child = static_cast<wxPizzaChild*>(p->data);
162 if (child->widget == widget)
163 {
164 pizza->m_children = g_list_delete_link(pizza->m_children, p);
165 delete child;
166 break;
167 }
168 }
169}
170
e56307d3
PC
171// not used, but needs to exist so gtk_widget_set_scroll_adjustments will work
172static void set_scroll_adjustments(GtkWidget*, GtkAdjustment*, GtkAdjustment*)
173{
174}
175
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)
179static void
180g_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)
186{
187 typedef void (*GMarshalFunc_VOID__OBJECT_OBJECT) (gpointer data1,
188 gpointer arg_1,
189 gpointer arg_2,
190 gpointer data2);
191 register GMarshalFunc_VOID__OBJECT_OBJECT callback;
192 register GCClosure *cc = (GCClosure*) closure;
193 register gpointer data1, data2;
194
195 g_return_if_fail (n_param_values == 3);
196
197 if (G_CCLOSURE_SWAP_DATA (closure))
198 {
199 data1 = closure->data;
200 data2 = g_value_peek_pointer (param_values + 0);
201 }
202 else
203 {
204 data1 = g_value_peek_pointer (param_values + 0);
205 data2 = closure->data;
206 }
207 callback = (GMarshalFunc_VOID__OBJECT_OBJECT) (marshal_data ? marshal_data : cc->callback);
208
209 callback (data1,
210 g_marshal_value_peek_object (param_values + 1),
211 g_marshal_value_peek_object (param_values + 2),
212 data2);
213}
214
215static void class_init(void* g_class, void*)
216{
217 GtkWidgetClass* widget_class = (GtkWidgetClass*)g_class;
218 widget_class->size_allocate = size_allocate;
219 widget_class->realize = realize;
6e7cf3bd
PC
220 widget_class->show = show;
221 widget_class->hide = hide;
3b7067a0
PC
222 GtkContainerClass* container_class = (GtkContainerClass*)g_class;
223 container_class->add = pizza_add;
224 container_class->remove = pizza_remove;
e56307d3
PC
225 wxPizzaClass* klass = (wxPizzaClass*)g_class;
226
227 // needed to make widget appear scrollable to GTK+
228 klass->set_scroll_adjustments = set_scroll_adjustments;
229 widget_class->set_scroll_adjustments_signal =
230 g_signal_new(
231 "set_scroll_adjustments",
232 G_TYPE_FROM_CLASS(g_class),
233 G_SIGNAL_RUN_LAST,
234 G_STRUCT_OFFSET(wxPizzaClass, set_scroll_adjustments),
235 NULL, NULL,
236 g_cclosure_user_marshal_VOID__OBJECT_OBJECT,
237 G_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
238
239 parent_class = GTK_WIDGET_CLASS(g_type_class_peek_parent(g_class));
240}
241
242} // extern "C"
243
244GType wxPizza::type()
245{
246 static GtkType type;
247 if (type == 0)
248 {
249 const GTypeInfo info = {
250 sizeof(wxPizzaClass),
251 NULL, NULL,
252 class_init,
253 NULL, NULL,
254 sizeof(wxPizza), 0,
255 NULL, NULL
256 };
257 type = g_type_register_static(
258 GTK_TYPE_FIXED, "wxPizza", &info, GTypeFlags(0));
259 }
260 return type;
261}
262
0f52f610 263GtkWidget* wxPizza::New(long windowStyle)
e56307d3
PC
264{
265 GtkWidget* widget = GTK_WIDGET(g_object_new(type(), NULL));
266 wxPizza* pizza = WX_PIZZA(widget);
3b7067a0 267 pizza->m_children = NULL;
e56307d3
PC
268 pizza->m_scroll_x = 0;
269 pizza->m_scroll_y = 0;
270 pizza->m_is_scrollable = (windowStyle & (wxHSCROLL | wxVSCROLL)) != 0;
59d09ad4
PC
271 // mask off border styles not useable with wxPizza
272 pizza->m_border_style = int(windowStyle & BORDER_STYLES);
5073cadd 273#if GTK_CHECK_VERSION(3,0,0) || defined(GTK_DISABLE_DEPRECATED)
fc9ab22a
VS
274 gtk_widget_set_has_window(widget, true);
275#else
e56307d3 276 gtk_fixed_set_has_window(GTK_FIXED(widget), true);
fc9ab22a 277#endif
e56307d3
PC
278 gtk_widget_add_events(widget,
279 GDK_EXPOSURE_MASK |
280 GDK_SCROLL_MASK |
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 |
289 GDK_KEY_PRESS_MASK |
290 GDK_KEY_RELEASE_MASK |
291 GDK_ENTER_NOTIFY_MASK |
292 GDK_LEAVE_NOTIFY_MASK |
293 GDK_FOCUS_CHANGE_MASK);
e56307d3
PC
294 return widget;
295}
296
3b7067a0 297void wxPizza::move(GtkWidget* widget, int x, int y, int width, int height)
e56307d3 298{
3b7067a0 299 for (const GList* p = m_children; p; p = p->next)
e56307d3 300 {
3b7067a0 301 wxPizzaChild* child = static_cast<wxPizzaChild*>(p->data);
e56307d3
PC
302 if (child->widget == widget)
303 {
3b7067a0
PC
304 child->x = x;
305 child->y = y;
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
e56307d3
PC
310 break;
311 }
312 }
313}
314
3b7067a0 315void wxPizza::put(GtkWidget* widget, int x, int y, int width, int height)
f089940f 316{
3b7067a0
PC
317 gtk_fixed_put(GTK_FIXED(this), widget, 0, 0);
318
319 wxPizzaChild* child = new wxPizzaChild;
320 child->widget = widget;
321 child->x = x;
322 child->y = y;
323 child->width = width;
324 child->height = height;
325 m_children = g_list_append(m_children, child);
f089940f
PC
326}
327
a8997071
PC
328struct AdjustData {
329 GdkWindow* window;
330 int dx, dy;
331};
332
333// Adjust allocations for all widgets using the GdkWindow which was just scrolled
334extern "C" {
335static void scroll_adjust(GtkWidget* widget, void* data)
336{
337 const AdjustData* p = static_cast<AdjustData*>(data);
eb56793e
RR
338 widget->allocation.x += p->dx;
339 widget->allocation.y += p->dy;
03647350 340
a8997071
PC
341 if (widget->window == p->window)
342 {
a8997071
PC
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);
349 }
350}
351}
352
e56307d3
PC
353void wxPizza::scroll(int dx, int dy)
354{
355 GtkWidget* widget = GTK_WIDGET(this);
356 if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL)
357 dx = -dx;
358 m_scroll_x -= dx;
359 m_scroll_y -= dy;
375efc1f 360 if (widget->window)
e56307d3 361 {
375efc1f 362 gdk_window_scroll(widget->window, dx, dy);
a8997071
PC
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.
375efc1f 365 AdjustData data = { widget->window, dx, dy };
a8997071 366 gtk_container_forall(GTK_CONTAINER(widget), scroll_adjust, &data);
e56307d3
PC
367 }
368}
369
370void wxPizza::get_border_widths(int& x, int& y)
371{
372 x = y = 0;
d7b63a01
RR
373 if (m_border_style == 0)
374 return;
03647350 375
75f661bb 376#ifndef __WXUNIVERSAL__
e56307d3
PC
377 if (m_border_style & wxBORDER_SIMPLE)
378 x = y = 1;
ec2d6790 379 else if (m_is_scrollable /* || (m_border_style & wxBORDER_THEME) */)
d7b63a01
RR
380 {
381 GtkWidget *style_widget = wxGTKPrivate::GetTreeWidget();
03647350 382
d7b63a01
RR
383 if (style_widget->style)
384 {
385 x = style_widget->style->xthickness;
386 y = style_widget->style->ythickness;
387 }
388 }
03647350 389 else
e56307d3 390 {
06a42745 391 GtkWidget *style_widget = wxGTKPrivate::GetEntryWidget();
03647350 392
06a42745 393 if (style_widget->style)
e56307d3 394 {
06a42745
RR
395 x = style_widget->style->xthickness;
396 y = style_widget->style->ythickness;
e56307d3
PC
397 }
398 }
75f661bb 399#endif
e56307d3 400}