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