]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/win_gtk.c
fix XModifierKeymap memory leak (patch 1683137)
[wxWidgets.git] / src / gtk / win_gtk.c
1 /* ///////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/win_gtk.c
3 // Purpose: Native GTK+ widget for wxWidgets, based on GtkLayout and
4 // GtkFixed. It makes use of the gravity window property and
5 // therefore does not work with GTK 1.0.
6 // Author: Robert Roebling
7 // Id: $Id$
8 // Copyright: (c) 1998 Robert Roebling
9 // Licence: wxWidgets licence
10 /////////////////////////////////////////////////////////////////////////// */
11
12 #ifdef VMS
13 #define XCheckIfEvent XCHECKIFEVENT
14 #endif
15
16 #include "wx/platform.h"
17 #include "wx/gtk/win_gtk.h"
18
19 #ifdef __cplusplus
20 extern "C" {
21 #endif /* __cplusplus */
22
23 typedef struct _GtkPizzaChild GtkPizzaChild;
24 typedef struct _GtkPizzaClass GtkPizzaClass;
25 typedef struct _GtkPizzaAdjData GtkPizzaAdjData;
26
27 struct _GtkPizzaClass
28 {
29 GtkContainerClass parent_class;
30
31 void (*set_scroll_adjustments) (GtkPizza *pizza,
32 GtkAdjustment *hadjustment,
33 GtkAdjustment *vadjustment);
34 };
35
36 struct _GtkPizzaChild
37 {
38 GtkWidget *widget;
39 gint x;
40 gint y;
41 };
42
43 struct _GtkPizzaAdjData
44 {
45 gint dx;
46 gint dy;
47 };
48
49 static void gtk_pizza_class_init (GtkPizzaClass *klass);
50 static void gtk_pizza_init (GtkPizza *pizza);
51
52 static void gtk_pizza_realize (GtkWidget *widget);
53 static void gtk_pizza_unrealize (GtkWidget *widget);
54
55 static void gtk_pizza_map (GtkWidget *widget);
56
57 static void gtk_pizza_size_request (GtkWidget *widget,
58 GtkRequisition *requisition);
59 static void gtk_pizza_size_allocate (GtkWidget *widget,
60 GtkAllocation *allocation);
61 static void gtk_pizza_style_set (GtkWidget *widget,
62 GtkStyle *previous_style);
63 static void gtk_pizza_add (GtkContainer *container,
64 GtkWidget *widget);
65 static void gtk_pizza_remove (GtkContainer *container,
66 GtkWidget *widget);
67 static void gtk_pizza_forall (GtkContainer *container,
68 gboolean include_internals,
69 GtkCallback callback,
70 gpointer callback_data);
71
72 static void gtk_pizza_allocate_child (GtkPizza *pizza,
73 GtkPizzaChild *child);
74 static void gtk_pizza_adjust_allocations_recurse (GtkWidget *widget,
75 gpointer cb_data);
76
77 static GtkType gtk_pizza_child_type (GtkContainer *container);
78
79 static void gtk_pizza_scroll_set_adjustments (GtkPizza *pizza,
80 GtkAdjustment *hadj,
81 GtkAdjustment *vadj);
82
83 static GtkWidgetClass* pizza_parent_class;
84
85 GtkType
86 gtk_pizza_get_type ()
87 {
88 static GtkType pizza_type = 0;
89
90 if (!pizza_type)
91 {
92 const GTypeInfo pizza_info =
93 {
94 sizeof (GtkPizzaClass),
95 NULL, /* base_init */
96 NULL, /* base_finalize */
97 (GClassInitFunc) gtk_pizza_class_init,
98 NULL, /* class_finalize */
99 NULL, /* class_data */
100 sizeof (GtkPizza),
101 16, /* n_preallocs */
102 (GInstanceInitFunc) gtk_pizza_init,
103 NULL
104 };
105 pizza_type = g_type_register_static (GTK_TYPE_CONTAINER, "GtkPizza", &pizza_info, (GTypeFlags)0);
106 }
107
108 return pizza_type;
109 }
110
111 /* Marshaller needed for set_scroll_adjustments signal,
112 generated with GLib-2.4.6 glib-genmarshal */
113 #define g_marshal_value_peek_object(v) g_value_get_object (v)
114 static void
115 g_cclosure_user_marshal_VOID__OBJECT_OBJECT (GClosure *closure,
116 GValue *return_value,
117 guint n_param_values,
118 const GValue *param_values,
119 gpointer invocation_hint,
120 gpointer marshal_data)
121 {
122 typedef void (*GMarshalFunc_VOID__OBJECT_OBJECT) (gpointer data1,
123 gpointer arg_1,
124 gpointer arg_2,
125 gpointer data2);
126 register GMarshalFunc_VOID__OBJECT_OBJECT callback;
127 register GCClosure *cc = (GCClosure*) closure;
128 register gpointer data1, data2;
129
130 g_return_if_fail (n_param_values == 3);
131
132 if (G_CCLOSURE_SWAP_DATA (closure))
133 {
134 data1 = closure->data;
135 data2 = g_value_peek_pointer (param_values + 0);
136 }
137 else
138 {
139 data1 = g_value_peek_pointer (param_values + 0);
140 data2 = closure->data;
141 }
142 callback = (GMarshalFunc_VOID__OBJECT_OBJECT) (marshal_data ? marshal_data : cc->callback);
143
144 callback (data1,
145 g_marshal_value_peek_object (param_values + 1),
146 g_marshal_value_peek_object (param_values + 2),
147 data2);
148 }
149
150 static void
151 gtk_pizza_class_init (GtkPizzaClass *klass)
152 {
153 GtkObjectClass *object_class;
154 GtkWidgetClass *widget_class;
155 GtkContainerClass *container_class;
156
157 object_class = (GtkObjectClass*) klass;
158 widget_class = (GtkWidgetClass*) klass;
159 container_class = (GtkContainerClass*) klass;
160 pizza_parent_class = gtk_type_class (GTK_TYPE_CONTAINER);
161
162 widget_class->map = gtk_pizza_map;
163 widget_class->realize = gtk_pizza_realize;
164 widget_class->unrealize = gtk_pizza_unrealize;
165 widget_class->size_request = gtk_pizza_size_request;
166 widget_class->size_allocate = gtk_pizza_size_allocate;
167 widget_class->style_set = gtk_pizza_style_set;
168
169 container_class->add = gtk_pizza_add;
170 container_class->remove = gtk_pizza_remove;
171 container_class->forall = gtk_pizza_forall;
172
173 container_class->child_type = gtk_pizza_child_type;
174
175 klass->set_scroll_adjustments = gtk_pizza_scroll_set_adjustments;
176
177 widget_class->set_scroll_adjustments_signal =
178 g_signal_new(
179 "set_scroll_adjustments",
180 G_TYPE_FROM_CLASS(object_class),
181 G_SIGNAL_RUN_LAST,
182 G_STRUCT_OFFSET(GtkPizzaClass, set_scroll_adjustments),
183 NULL,
184 NULL,
185 g_cclosure_user_marshal_VOID__OBJECT_OBJECT,
186 G_TYPE_NONE,
187 2,
188 GTK_TYPE_ADJUSTMENT,
189 GTK_TYPE_ADJUSTMENT);
190 }
191
192 static GtkType
193 gtk_pizza_child_type (GtkContainer *container)
194 {
195 return GTK_TYPE_WIDGET;
196 }
197
198 static void
199 gtk_pizza_init (GtkPizza *pizza)
200 {
201 GTK_WIDGET_UNSET_FLAGS (pizza, GTK_NO_WINDOW);
202
203 pizza->children = NULL;
204
205 pizza->bin_window = NULL;
206
207 pizza->m_xoffset = 0;
208 pizza->m_yoffset = 0;
209 }
210
211 GtkWidget*
212 gtk_pizza_new ()
213 {
214 GtkPizza *pizza;
215
216 pizza = g_object_new (gtk_pizza_get_type (), NULL);
217
218 return GTK_WIDGET (pizza);
219 }
220
221 gint gtk_pizza_get_xoffset (GtkPizza *pizza)
222 {
223 g_return_val_if_fail ( (pizza != NULL), -1 );
224 g_return_val_if_fail ( (GTK_IS_PIZZA (pizza)), -1 );
225
226 return pizza->m_xoffset;
227 }
228
229 gint gtk_pizza_get_yoffset (GtkPizza *pizza)
230 {
231 g_return_val_if_fail ( (pizza != NULL), -1 );
232 g_return_val_if_fail ( (GTK_IS_PIZZA (pizza)), -1 );
233
234 return pizza->m_yoffset;
235 }
236
237 void gtk_pizza_set_xoffset (GtkPizza *pizza, gint xoffset)
238 {
239 g_return_if_fail (pizza != NULL);
240 g_return_if_fail (GTK_IS_PIZZA (pizza));
241
242 pizza->m_xoffset = xoffset;
243 // do something
244 }
245
246 void gtk_pizza_set_yoffset (GtkPizza *pizza, gint yoffset)
247 {
248 g_return_if_fail (pizza != NULL);
249 g_return_if_fail (GTK_IS_PIZZA (pizza));
250
251 pizza->m_xoffset = yoffset;
252 // do something
253 }
254
255 gint gtk_pizza_get_rtl_offset (GtkPizza *pizza)
256 {
257 gint border;
258
259 g_return_val_if_fail ( (pizza != NULL), 0 );
260 g_return_val_if_fail ( (GTK_IS_PIZZA (pizza)), 0 );
261
262 if (!pizza->bin_window) return 0;
263
264 border = pizza->container.border_width;
265
266 return GTK_WIDGET(pizza)->allocation.width - border*2;
267 }
268
269
270 static void
271 gtk_pizza_scroll_set_adjustments (GtkPizza *pizza,
272 GtkAdjustment *hadj,
273 GtkAdjustment *vadj)
274 {
275 /* We handle scrolling in the wxScrolledWindow, not here. */
276 }
277
278 void
279 gtk_pizza_put (GtkPizza *pizza,
280 GtkWidget *widget,
281 gint x,
282 gint y,
283 gint width,
284 gint height)
285 {
286 GtkPizzaChild *child_info;
287
288 g_return_if_fail (pizza != NULL);
289 g_return_if_fail (GTK_IS_PIZZA (pizza));
290 g_return_if_fail (widget != NULL);
291
292 child_info = g_new (GtkPizzaChild, 1);
293
294 child_info->widget = widget;
295 child_info->x = x;
296 child_info->y = y;
297
298 pizza->children = g_list_append (pizza->children, child_info);
299
300 if (GTK_WIDGET_REALIZED (pizza))
301 gtk_widget_set_parent_window (widget, pizza->bin_window);
302
303 gtk_widget_set_parent (widget, GTK_WIDGET (pizza));
304
305 gtk_widget_set_size_request( widget, width, height );
306 if (GTK_WIDGET_REALIZED (pizza))
307 gtk_pizza_allocate_child (pizza, child_info);
308 }
309
310 void
311 gtk_pizza_set_size (GtkPizza *pizza,
312 GtkWidget *widget,
313 gint x,
314 gint y,
315 gint width,
316 gint height)
317 {
318 GtkPizzaChild *child;
319 GList *children;
320
321 g_return_if_fail (pizza != NULL);
322 g_return_if_fail (GTK_IS_PIZZA (pizza));
323 g_return_if_fail (widget != NULL);
324
325 #ifndef WX_WARN_ILLEGAL_SETSIZE
326 /* this really shouldn't happen -- but it does, a lot, right now and we
327 can't pass negative values to gtk_widget_set_size_request() without getting
328 a warning printed out, so filter them out here */
329 if ( width < 0 )
330 width = 0;
331 if ( height < 0 )
332 height = 0;
333 #endif
334
335 children = pizza->children;
336 while (children)
337 {
338 child = children->data;
339 children = children->next;
340
341 if (child->widget == widget)
342 {
343 if (child->x != x || child->y != y)
344 {
345 child->x = x;
346 child->y = y;
347 gtk_widget_queue_resize(widget);
348 }
349
350 gtk_widget_set_size_request (widget, width, height);
351
352 return;
353 }
354 }
355 }
356
357 static void
358 gtk_pizza_map (GtkWidget *widget)
359 {
360 GtkPizza *pizza;
361 GtkPizzaChild *child;
362 GList *children;
363
364 g_return_if_fail (widget != NULL);
365 g_return_if_fail (GTK_IS_PIZZA (widget));
366
367 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
368 pizza = GTK_PIZZA (widget);
369
370 children = pizza->children;
371 while (children)
372 {
373 child = children->data;
374 children = children->next;
375
376 if ( GTK_WIDGET_VISIBLE (child->widget) &&
377 !GTK_WIDGET_MAPPED (child->widget) )
378 {
379 gtk_widget_map (child->widget);
380 }
381 }
382
383 gdk_window_show (widget->window);
384 gdk_window_show (pizza->bin_window);
385 }
386
387 static void
388 gtk_pizza_realize (GtkWidget *widget)
389 {
390 GtkPizza *pizza;
391 GdkWindowAttr attributes;
392 gint attributes_mask;
393 GtkPizzaChild *child;
394 GList *children;
395 int border;
396
397 g_return_if_fail (widget != NULL);
398 g_return_if_fail (GTK_IS_PIZZA (widget));
399
400 pizza = GTK_PIZZA (widget);
401 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
402
403 attributes.window_type = GDK_WINDOW_CHILD;
404
405 attributes.x = widget->allocation.x;
406 attributes.y = widget->allocation.y;
407 attributes.width = widget->allocation.width;
408 attributes.height = widget->allocation.height;
409
410 border = pizza->container.border_width;
411 attributes.x += border;
412 attributes.y += border;
413 attributes.width -= 2 * border;
414 attributes.height -= 2 * border;
415
416 /* minimal size */
417 if (attributes.width < 2) attributes.width = 2;
418 if (attributes.height < 2) attributes.height = 2;
419
420 attributes.wclass = GDK_INPUT_OUTPUT;
421 attributes.visual = gtk_widget_get_visual (widget);
422 attributes.colormap = gtk_widget_get_colormap (widget);
423 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
424 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
425
426 widget->window = gdk_window_new(gtk_widget_get_parent_window (widget),
427 &attributes, attributes_mask);
428 gdk_window_set_user_data (widget->window, widget);
429
430 attributes.x = 0;
431 attributes.y = 0;
432
433 attributes.event_mask = gtk_widget_get_events (widget);
434 attributes.event_mask |= GDK_EXPOSURE_MASK |
435 GDK_SCROLL_MASK |
436 GDK_POINTER_MOTION_MASK |
437 GDK_POINTER_MOTION_HINT_MASK |
438 GDK_BUTTON_MOTION_MASK |
439 GDK_BUTTON1_MOTION_MASK |
440 GDK_BUTTON2_MOTION_MASK |
441 GDK_BUTTON3_MOTION_MASK |
442 GDK_BUTTON_PRESS_MASK |
443 GDK_BUTTON_RELEASE_MASK |
444 GDK_KEY_PRESS_MASK |
445 GDK_KEY_RELEASE_MASK |
446 GDK_ENTER_NOTIFY_MASK |
447 GDK_LEAVE_NOTIFY_MASK |
448 GDK_FOCUS_CHANGE_MASK;
449
450 pizza->bin_window = gdk_window_new(widget->window,
451 &attributes, attributes_mask);
452 gdk_window_set_user_data (pizza->bin_window, widget);
453
454 widget->style = gtk_style_attach (widget->style, widget->window);
455 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
456 gtk_style_set_background (widget->style, pizza->bin_window, GTK_STATE_NORMAL );
457
458 /*
459 gdk_window_set_back_pixmap( widget->window, NULL, FALSE );
460 gdk_window_set_back_pixmap( pizza->bin_window, NULL, FALSE );
461 */
462
463 /* cannot be done before realisation */
464 children = pizza->children;
465 while (children)
466 {
467 child = children->data;
468 children = children->next;
469
470 gtk_widget_set_parent_window (child->widget, pizza->bin_window);
471 }
472 }
473
474 static void
475 gtk_pizza_unrealize (GtkWidget *widget)
476 {
477 GtkPizza *pizza;
478
479 g_return_if_fail (widget != NULL);
480 g_return_if_fail (GTK_IS_PIZZA (widget));
481
482 pizza = GTK_PIZZA (widget);
483
484 gdk_window_set_user_data (pizza->bin_window, NULL);
485 gdk_window_destroy (pizza->bin_window);
486 pizza->bin_window = NULL;
487
488 if (pizza_parent_class->unrealize)
489 pizza_parent_class->unrealize(widget);
490 }
491
492 static void
493 gtk_pizza_size_request (GtkWidget *widget,
494 GtkRequisition *requisition)
495 {
496 GtkPizza *pizza;
497 GtkPizzaChild *child;
498 GList *children;
499 GtkRequisition child_requisition;
500
501 g_return_if_fail (widget != NULL);
502 g_return_if_fail (GTK_IS_PIZZA (widget));
503 g_return_if_fail (requisition != NULL);
504
505 pizza = GTK_PIZZA (widget);
506
507 children = pizza->children;
508 while (children)
509 {
510 child = children->data;
511 children = children->next;
512
513 if (GTK_WIDGET_VISIBLE (child->widget))
514 {
515 gtk_widget_size_request (child->widget, &child_requisition);
516 }
517 }
518
519 /* request very little, I'm not sure if requesting nothing
520 will always have positive effects on stability... */
521 requisition->width = 2;
522 requisition->height = 2;
523 }
524
525 static void
526 gtk_pizza_size_allocate (GtkWidget *widget,
527 GtkAllocation *allocation)
528 {
529 GtkPizza *pizza;
530 gint border;
531 gint x,y,w,h;
532 GtkPizzaChild *child;
533 GList *children;
534 gboolean only_resize;
535
536 g_return_if_fail (widget != NULL);
537 g_return_if_fail (GTK_IS_PIZZA(widget));
538 g_return_if_fail (allocation != NULL);
539
540 pizza = GTK_PIZZA (widget);
541
542 only_resize = ((widget->allocation.x == allocation->x) &&
543 (widget->allocation.y == allocation->y));
544 widget->allocation = *allocation;
545
546 border = pizza->container.border_width;
547
548 x = allocation->x + border;
549 y = allocation->y + border;
550 w = allocation->width - border*2;
551 h = allocation->height - border*2;
552 if (w < 0)
553 w = 0;
554 if (h < 0)
555 h = 0;
556
557 if (GTK_WIDGET_REALIZED (widget))
558 {
559 if (only_resize)
560 gdk_window_resize( widget->window, w, h );
561 else
562 gdk_window_move_resize( widget->window, x, y, w, h );
563
564 gdk_window_resize( pizza->bin_window, w, h );
565 }
566
567 children = pizza->children;
568 while (children)
569 {
570 child = children->data;
571 children = children->next;
572
573 gtk_pizza_allocate_child (pizza, child);
574 }
575 }
576
577 static void
578 gtk_pizza_style_set(GtkWidget *widget, GtkStyle *previous_style)
579 {
580 if (GTK_WIDGET_REALIZED(widget))
581 {
582 gtk_style_set_background(widget->style, widget->window, GTK_STATE_NORMAL);
583 gtk_style_set_background(widget->style, GTK_PIZZA(widget)->bin_window, GTK_STATE_NORMAL );
584 }
585
586 pizza_parent_class->style_set(widget, previous_style);
587 }
588
589 static void
590 gtk_pizza_add (GtkContainer *container,
591 GtkWidget *widget)
592 {
593 g_return_if_fail (container != NULL);
594 g_return_if_fail (GTK_IS_PIZZA (container));
595 g_return_if_fail (widget != NULL);
596
597 gtk_pizza_put (GTK_PIZZA (container), widget, 0, 0, 20, 20 );
598 }
599
600 static void
601 gtk_pizza_remove (GtkContainer *container,
602 GtkWidget *widget)
603 {
604 GtkPizza *pizza;
605 GtkPizzaChild *child;
606 GList *children;
607
608 g_return_if_fail (container != NULL);
609 g_return_if_fail (GTK_IS_PIZZA (container));
610 g_return_if_fail (widget != NULL);
611
612 pizza = GTK_PIZZA (container);
613
614 children = pizza->children;
615 while (children)
616 {
617 child = children->data;
618
619 if (child->widget == widget)
620 {
621 gtk_widget_unparent (widget);
622
623 /* security checks */
624 g_return_if_fail (GTK_IS_WIDGET (widget));
625
626 pizza->children = g_list_remove_link (pizza->children, children);
627 g_list_free (children);
628 g_free (child);
629
630 /* security checks */
631 g_return_if_fail (GTK_IS_WIDGET (widget));
632
633 break;
634 }
635
636 children = children->next;
637 }
638 }
639
640 static void
641 gtk_pizza_forall (GtkContainer *container,
642 gboolean include_internals,
643 GtkCallback callback,
644 gpointer callback_data)
645 {
646 GtkPizza *pizza;
647 GtkPizzaChild *child;
648 GList *children;
649
650 g_return_if_fail (container != NULL);
651 g_return_if_fail (GTK_IS_PIZZA (container));
652 g_return_if_fail (callback != (GtkCallback)NULL);
653
654 pizza = GTK_PIZZA (container);
655
656 children = pizza->children;
657 while (children)
658 {
659 child = children->data;
660 children = children->next;
661
662 (* callback) (child->widget, callback_data);
663 }
664 }
665
666 static void
667 gtk_pizza_allocate_child (GtkPizza *pizza,
668 GtkPizzaChild *child)
669 {
670 GtkAllocation allocation;
671 GtkRequisition requisition;
672
673 allocation.x = child->x - pizza->m_xoffset;
674 allocation.y = child->y - pizza->m_yoffset;
675 gtk_widget_get_child_requisition (child->widget, &requisition);
676 allocation.width = requisition.width;
677 allocation.height = requisition.height;
678
679 if (gtk_widget_get_direction( GTK_WIDGET(pizza) ) == GTK_TEXT_DIR_RTL)
680 {
681 /* reverse horizontal placement */
682 gint offset,border;
683
684 offset = GTK_WIDGET(pizza)->allocation.width;
685 border = pizza->container.border_width;
686 offset -= border*2;
687
688 allocation.x = offset - child->x - allocation.width + pizza->m_xoffset;
689 }
690
691 gtk_widget_size_allocate (child->widget, &allocation);
692 }
693
694 static void
695 gtk_pizza_adjust_allocations_recurse (GtkWidget *widget,
696 gpointer cb_data)
697 {
698 GtkPizzaAdjData *data = cb_data;
699
700 widget->allocation.x += data->dx;
701 widget->allocation.y += data->dy;
702
703 if (GTK_WIDGET_NO_WINDOW (widget) && GTK_IS_CONTAINER (widget))
704 {
705 gtk_container_forall (GTK_CONTAINER (widget),
706 gtk_pizza_adjust_allocations_recurse,
707 cb_data);
708 }
709 }
710
711 static void
712 gtk_pizza_adjust_allocations (GtkPizza *pizza,
713 gint dx,
714 gint dy)
715 {
716 GList *tmp_list;
717 GtkPizzaAdjData data;
718
719 data.dx = dx;
720 data.dy = dy;
721
722 tmp_list = pizza->children;
723 while (tmp_list)
724 {
725 GtkPizzaChild *child = tmp_list->data;
726 tmp_list = tmp_list->next;
727
728 child->widget->allocation.x += dx;
729 child->widget->allocation.y += dy;
730
731 if (GTK_WIDGET_NO_WINDOW (child->widget) &&
732 GTK_IS_CONTAINER (child->widget))
733 {
734 gtk_container_forall (GTK_CONTAINER (child->widget),
735 gtk_pizza_adjust_allocations_recurse,
736 &data);
737 }
738 }
739 }
740
741 void
742 gtk_pizza_scroll (GtkPizza *pizza, gint dx, gint dy)
743 {
744 pizza->m_xoffset += dx;
745 pizza->m_yoffset += dy;
746
747 gtk_pizza_adjust_allocations (pizza, -dx, -dy);
748
749 if (pizza->bin_window)
750 gdk_window_scroll( pizza->bin_window, -dx, -dy );
751 }
752
753 #ifdef __cplusplus
754 }
755 #endif /* __cplusplus */