]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/gtk/win_gtk.cpp
use const arrays for wxDC array parameters, closes #10712
[wxWidgets.git] / src / gtk / win_gtk.cpp
... / ...
CommitLineData
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
10#include "wx/wxprec.h"
11
12#include "wx/defs.h"
13
14#include <gtk/gtk.h>
15#include "wx/gtk/private/win_gtk.h"
16
17#include "wx/gtk/private.h"
18#include "wx/gtk/private/gtk2-compat.h"
19
20/*
21wxPizza is a custom GTK+ widget derived from GtkFixed. A custom widget
22is needed to adapt GTK+ to wxWidgets needs in 3 areas: scrolling, window
23borders, and RTL.
24
25For scrolling, the "set_scroll_adjustments" signal is implemented
26to make wxPizza appear scrollable to GTK+, allowing it to be put in a
27GtkScrolledWindow. Child widget positions are adjusted for the scrolling
28position in size_allocate.
29
30For borders, space is reserved in realize and size_allocate. The border is
31drawn on wxPizza's parent GdkWindow.
32
33For RTL, child widget positions are mirrored in size_allocate.
34*/
35
36struct wxPizzaChild
37{
38 GtkWidget* widget;
39 int x, y, width, height;
40};
41
42static GtkWidgetClass* parent_class;
43
44#ifdef __WXGTK3__
45enum {
46 PROP_0,
47 PROP_HADJUSTMENT,
48 PROP_VADJUSTMENT,
49 PROP_HSCROLL_POLICY,
50 PROP_VSCROLL_POLICY
51};
52#endif
53
54extern "C" {
55
56struct wxPizzaClass
57{
58 GtkFixedClass parent;
59#ifndef __WXGTK3__
60 void (*set_scroll_adjustments)(GtkWidget*, GtkAdjustment*, GtkAdjustment*);
61#endif
62};
63
64static void pizza_size_allocate(GtkWidget* widget, GtkAllocation* alloc)
65{
66 wxPizza* pizza = WX_PIZZA(widget);
67 GtkBorder border;
68 pizza->get_border(border);
69 int w = alloc->width - border.left - border.right;
70 if (w < 0) w = 0;
71
72 if (gtk_widget_get_realized(widget))
73 {
74 int h = alloc->height - border.top - border.bottom;
75 if (h < 0) h = 0;
76 const int x = alloc->x + border.left;
77 const int y = alloc->y + border.top;
78
79 GdkWindow* window = gtk_widget_get_window(widget);
80 int old_x, old_y;
81 gdk_window_get_position(window, &old_x, &old_y);
82
83 if (x != old_x || y != old_y ||
84 w != gdk_window_get_width(window) || h != gdk_window_get_height(window))
85 {
86 gdk_window_move_resize(window, x, y, w, h);
87
88 if (border.left + border.right + border.top + border.bottom)
89 {
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);
97 }
98 }
99 }
100
101 gtk_widget_set_allocation(widget, alloc);
102
103 // adjust child positions
104 for (const GList* p = pizza->m_children; p; p = p->next)
105 {
106 const wxPizzaChild* child = static_cast<wxPizzaChild*>(p->data);
107 if (gtk_widget_get_visible(child->widget))
108 {
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);
120 }
121 }
122}
123
124static void pizza_realize(GtkWidget* widget)
125{
126 parent_class->realize(widget);
127
128 wxPizza* pizza = WX_PIZZA(widget);
129 if (pizza->m_windowStyle & wxPizza::BORDER_STYLES)
130 {
131 GtkBorder border;
132 pizza->get_border(border);
133 GtkAllocation a;
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;
139 if (w < 0) w = 0;
140 if (h < 0) h = 0;
141 gdk_window_move_resize(gtk_widget_get_window(widget), x, y, w, h);
142 }
143}
144
145static void pizza_show(GtkWidget* widget)
146{
147 GtkWidget* parent = gtk_widget_get_parent(widget);
148 if (parent && (WX_PIZZA(widget)->m_windowStyle & wxPizza::BORDER_STYLES))
149 {
150 // invalidate whole allocation so borders will be drawn properly
151 GtkAllocation a;
152 gtk_widget_get_allocation(widget, &a);
153 gtk_widget_queue_draw_area(parent, a.x, a.y, a.width, a.height);
154 }
155
156 parent_class->show(widget);
157}
158
159static void pizza_hide(GtkWidget* widget)
160{
161 GtkWidget* parent = gtk_widget_get_parent(widget);
162 if (parent && (WX_PIZZA(widget)->m_windowStyle & wxPizza::BORDER_STYLES))
163 {
164 // invalidate whole allocation so borders will be erased properly
165 GtkAllocation a;
166 gtk_widget_get_allocation(widget, &a);
167 gtk_widget_queue_draw_area(parent, a.x, a.y, a.width, a.height);
168 }
169
170 parent_class->hide(widget);
171}
172
173static void pizza_add(GtkContainer* container, GtkWidget* widget)
174{
175 WX_PIZZA(container)->put(widget, 0, 0, 1, 1);
176}
177
178static void pizza_remove(GtkContainer* container, GtkWidget* widget)
179{
180 GTK_CONTAINER_CLASS(parent_class)->remove(container, widget);
181
182 wxPizza* pizza = WX_PIZZA(container);
183 for (GList* p = pizza->m_children; p; p = p->next)
184 {
185 wxPizzaChild* child = static_cast<wxPizzaChild*>(p->data);
186 if (child->widget == widget)
187 {
188 pizza->m_children = g_list_delete_link(pizza->m_children, p);
189 delete child;
190 break;
191 }
192 }
193}
194
195#ifdef __WXGTK3__
196static void pizza_get_preferred_width(GtkWidget* widget, int* minimum, int* natural)
197{
198 *minimum = 0;
199 gtk_widget_get_size_request(widget, natural, NULL);
200 if (*natural < 0)
201 *natural = 0;
202}
203
204static void pizza_get_preferred_height(GtkWidget* widget, int* minimum, int* natural)
205{
206 *minimum = 0;
207 gtk_widget_get_size_request(widget, NULL, natural);
208 if (*natural < 0)
209 *natural = 0;
210}
211
212// Needed to implement GtkScrollable interface, but we don't care about the
213// properties. wxWindowGTK handles the adjustments and scroll policy.
214static void pizza_get_property(GObject*, guint, GValue*, GParamSpec*)
215{
216}
217
218static void pizza_set_property(GObject*, guint, const GValue*, GParamSpec*)
219{
220}
221#else
222// not used, but needs to exist so gtk_widget_set_scroll_adjustments will work
223static void pizza_set_scroll_adjustments(GtkWidget*, GtkAdjustment*, GtkAdjustment*)
224{
225}
226
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)
230static void
231g_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)
237{
238 typedef void (*GMarshalFunc_VOID__OBJECT_OBJECT) (gpointer data1,
239 gpointer arg_1,
240 gpointer arg_2,
241 gpointer data2);
242 register GMarshalFunc_VOID__OBJECT_OBJECT callback;
243 register GCClosure *cc = (GCClosure*) closure;
244 register gpointer data1, data2;
245
246 g_return_if_fail (n_param_values == 3);
247
248 if (G_CCLOSURE_SWAP_DATA (closure))
249 {
250 data1 = closure->data;
251 data2 = g_value_peek_pointer (param_values + 0);
252 }
253 else
254 {
255 data1 = g_value_peek_pointer (param_values + 0);
256 data2 = closure->data;
257 }
258 callback = (GMarshalFunc_VOID__OBJECT_OBJECT) (marshal_data ? marshal_data : cc->callback);
259
260 callback (data1,
261 g_marshal_value_peek_object (param_values + 1),
262 g_marshal_value_peek_object (param_values + 2),
263 data2);
264}
265#endif
266
267static void class_init(void* g_class, void*)
268{
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;
277
278#ifdef __WXGTK3__
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");
288#else
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 =
293 g_signal_new(
294 "set_scroll_adjustments",
295 G_TYPE_FROM_CLASS(g_class),
296 G_SIGNAL_RUN_LAST,
297 G_STRUCT_OFFSET(wxPizzaClass, set_scroll_adjustments),
298 NULL, NULL,
299 g_cclosure_user_marshal_VOID__OBJECT_OBJECT,
300 G_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
301#endif
302 parent_class = GTK_WIDGET_CLASS(g_type_class_peek_parent(g_class));
303}
304
305} // extern "C"
306
307GType wxPizza::type()
308{
309 static GType type;
310 if (type == 0)
311 {
312 const GTypeInfo info = {
313 sizeof(wxPizzaClass),
314 NULL, NULL,
315 class_init,
316 NULL, NULL,
317 sizeof(wxPizza), 0,
318 NULL, NULL
319 };
320 type = g_type_register_static(
321 GTK_TYPE_FIXED, "wxPizza", &info, GTypeFlags(0));
322#ifdef __WXGTK3__
323 const GInterfaceInfo interface_info = { NULL, NULL, NULL };
324 g_type_add_interface_static(type, GTK_TYPE_SCROLLABLE, &interface_info);
325#endif
326 }
327 return type;
328}
329
330GtkWidget* wxPizza::New(long windowStyle)
331{
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;
338#ifdef __WXGTK3__
339 gtk_widget_set_has_window(widget, true);
340#else
341 gtk_fixed_set_has_window(GTK_FIXED(widget), true);
342#endif
343 gtk_widget_add_events(widget,
344 GDK_EXPOSURE_MASK |
345 GDK_SCROLL_MASK |
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 |
354 GDK_KEY_PRESS_MASK |
355 GDK_KEY_RELEASE_MASK |
356 GDK_ENTER_NOTIFY_MASK |
357 GDK_LEAVE_NOTIFY_MASK |
358 GDK_FOCUS_CHANGE_MASK);
359 return widget;
360}
361
362void wxPizza::move(GtkWidget* widget, int x, int y, int width, int height)
363{
364 for (const GList* p = m_children; p; p = p->next)
365 {
366 wxPizzaChild* child = static_cast<wxPizzaChild*>(p->data);
367 if (child->widget == widget)
368 {
369 child->x = x;
370 child->y = y;
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
375 break;
376 }
377 }
378}
379
380void wxPizza::put(GtkWidget* widget, int x, int y, int width, int height)
381{
382 gtk_fixed_put(GTK_FIXED(this), widget, 0, 0);
383
384 wxPizzaChild* child = new wxPizzaChild;
385 child->widget = widget;
386 child->x = x;
387 child->y = y;
388 child->width = width;
389 child->height = height;
390 m_children = g_list_append(m_children, child);
391}
392
393struct AdjustData {
394 GdkWindow* window;
395 int dx, dy;
396};
397
398// Adjust allocations for all widgets using the GdkWindow which was just scrolled
399extern "C" {
400static void scroll_adjust(GtkWidget* widget, void* data)
401{
402 const AdjustData* p = static_cast<AdjustData*>(data);
403 GtkAllocation a;
404 gtk_widget_get_allocation(widget, &a);
405 a.x += p->dx;
406 a.y += p->dy;
407 gtk_widget_set_allocation(widget, &a);
408
409 if (gtk_widget_get_window(widget) == p->window)
410 {
411 // GtkFrame requires a queue_resize, otherwise parts of
412 // the frame newly exposed by the scroll are not drawn.
413 // To be safe, do it for all widgets.
414 gtk_widget_queue_resize_no_redraw(widget);
415 if (GTK_IS_CONTAINER(widget))
416 gtk_container_forall(GTK_CONTAINER(widget), scroll_adjust, data);
417 }
418}
419}
420
421void wxPizza::scroll(int dx, int dy)
422{
423 GtkWidget* widget = GTK_WIDGET(this);
424 if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL)
425 dx = -dx;
426 m_scroll_x -= dx;
427 m_scroll_y -= dy;
428 GdkWindow* window = gtk_widget_get_window(widget);
429 if (window)
430 {
431 gdk_window_scroll(window, dx, dy);
432 // Adjust child allocations. Doing a queue_resize on the children is not
433 // enough, sometimes they redraw in the wrong place during fast scrolling.
434 AdjustData data = { window, dx, dy };
435 gtk_container_forall(GTK_CONTAINER(widget), scroll_adjust, &data);
436 }
437}
438
439void wxPizza::get_border(GtkBorder& border)
440{
441#ifndef __WXUNIVERSAL__
442 if (m_windowStyle & wxBORDER_SIMPLE)
443 border.left = border.right = border.top = border.bottom = 1;
444 else if (m_windowStyle & (wxBORDER_RAISED | wxBORDER_SUNKEN | wxBORDER_THEME))
445 {
446#ifdef __WXGTK3__
447 GtkStyleContext* sc;
448 if (m_windowStyle & (wxHSCROLL | wxVSCROLL))
449 sc = gtk_widget_get_style_context(wxGTKPrivate::GetTreeWidget());
450 else
451 sc = gtk_widget_get_style_context(wxGTKPrivate::GetEntryWidget());
452
453 gtk_style_context_get_border(sc, GTK_STATE_FLAG_NORMAL, &border);
454#else // !__WXGTK3__
455 GtkStyle* style;
456 if (m_windowStyle & (wxHSCROLL | wxVSCROLL))
457 style = gtk_widget_get_style(wxGTKPrivate::GetTreeWidget());
458 else
459 style = gtk_widget_get_style(wxGTKPrivate::GetEntryWidget());
460
461 border.left = border.right = style->xthickness;
462 border.top = border.bottom = style->ythickness;
463#endif // !__WXGTK3__
464 }
465 else
466#endif // !__WXUNIVERSAL__
467 {
468 border.left = border.right = border.top = border.bottom = 0;
469 }
470}