]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/win_gtk.c
simplified and cleaned up wxGTK's focus handling
[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/private/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 }
313
314 void
315 gtk_pizza_set_size (GtkPizza *pizza,
316 GtkWidget *widget,
317 gint x,
318 gint y,
319 gint width,
320 gint height)
321 {
322 GtkPizzaChild *child;
323 GList *children;
324
325 g_return_if_fail (pizza != NULL);
326 g_return_if_fail (GTK_IS_PIZZA (pizza));
327 g_return_if_fail (widget != NULL);
328
329 #ifndef WX_WARN_ILLEGAL_SETSIZE
330 /* this really shouldn't happen -- but it does, a lot, right now and we
331 can't pass negative values to gtk_widget_set_size_request() without getting
332 a warning printed out, so filter them out here */
333 if ( width < 0 )
334 width = 0;
335 if ( height < 0 )
336 height = 0;
337 #endif
338
339 children = pizza->children;
340 while (children)
341 {
342 child = children->data;
343 children = children->next;
344
345 if (child->widget == widget)
346 {
347 if (child->x != x || child->y != y)
348 {
349 child->x = x;
350 child->y = y;
351 gtk_widget_queue_resize(widget);
352 }
353
354 gtk_widget_set_size_request (widget, width, height);
355
356 return;
357 }
358 }
359 }
360
361 static void
362 gtk_pizza_map (GtkWidget *widget)
363 {
364 GtkPizza *pizza;
365 GtkPizzaChild *child;
366 GList *children;
367
368 g_return_if_fail (widget != NULL);
369 g_return_if_fail (GTK_IS_PIZZA (widget));
370
371 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
372 pizza = GTK_PIZZA (widget);
373
374 children = pizza->children;
375 while (children)
376 {
377 child = children->data;
378 children = children->next;
379
380 if ( GTK_WIDGET_VISIBLE (child->widget) &&
381 !GTK_WIDGET_MAPPED (child->widget) )
382 {
383 gtk_widget_map (child->widget);
384 }
385 }
386
387 gdk_window_show (widget->window);
388 gdk_window_show (pizza->bin_window);
389 }
390
391 static void
392 gtk_pizza_realize (GtkWidget *widget)
393 {
394 GtkPizza *pizza;
395 GdkWindowAttr attributes;
396 gint attributes_mask;
397 GtkPizzaChild *child;
398 GList *children;
399 int border;
400 int w, h;
401
402 g_return_if_fail (widget != NULL);
403 g_return_if_fail (GTK_IS_PIZZA (widget));
404
405 pizza = GTK_PIZZA (widget);
406 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
407
408 attributes.window_type = GDK_WINDOW_CHILD;
409
410 attributes.x = widget->allocation.x;
411 attributes.y = widget->allocation.y;
412 attributes.width = widget->allocation.width;
413 attributes.height = widget->allocation.height;
414
415 /* minimal size */
416 if (attributes.width < 2) attributes.width = 2;
417 if (attributes.height < 2) attributes.height = 2;
418
419 border = pizza->container.border_width;
420 w = attributes.width - 2 * border;
421 h = attributes.height - 2 * border;
422 if (w < 2) w = 2;
423 if (h < 2) h = 2;
424
425 if (!pizza->m_noscroll)
426 {
427 attributes.x += border;
428 attributes.y += border;
429 attributes.width = w;
430 attributes.height = h;
431 }
432
433 attributes.wclass = GDK_INPUT_OUTPUT;
434 attributes.visual = gtk_widget_get_visual (widget);
435 attributes.colormap = gtk_widget_get_colormap (widget);
436 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
437 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
438
439 widget->window = gdk_window_new(gtk_widget_get_parent_window (widget),
440 &attributes, attributes_mask);
441 gdk_window_set_user_data (widget->window, widget);
442
443 attributes.x = 0;
444 attributes.y = 0;
445 if (pizza->m_noscroll)
446 {
447 attributes.x = border;
448 attributes.y = border;
449 attributes.width = w;
450 attributes.height = h;
451 }
452
453 attributes.event_mask = gtk_widget_get_events (widget);
454 attributes.event_mask |= GDK_EXPOSURE_MASK |
455 GDK_SCROLL_MASK |
456 GDK_POINTER_MOTION_MASK |
457 GDK_POINTER_MOTION_HINT_MASK |
458 GDK_BUTTON_MOTION_MASK |
459 GDK_BUTTON1_MOTION_MASK |
460 GDK_BUTTON2_MOTION_MASK |
461 GDK_BUTTON3_MOTION_MASK |
462 GDK_BUTTON_PRESS_MASK |
463 GDK_BUTTON_RELEASE_MASK |
464 GDK_KEY_PRESS_MASK |
465 GDK_KEY_RELEASE_MASK |
466 GDK_ENTER_NOTIFY_MASK |
467 GDK_LEAVE_NOTIFY_MASK |
468 GDK_FOCUS_CHANGE_MASK;
469
470 pizza->bin_window = gdk_window_new(widget->window,
471 &attributes, attributes_mask);
472 gdk_window_set_user_data (pizza->bin_window, widget);
473
474 widget->style = gtk_style_attach (widget->style, widget->window);
475 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
476 gtk_style_set_background (widget->style, pizza->bin_window, GTK_STATE_NORMAL );
477
478 /*
479 gdk_window_set_back_pixmap( widget->window, NULL, FALSE );
480 gdk_window_set_back_pixmap( pizza->bin_window, NULL, FALSE );
481 */
482
483 /* cannot be done before realisation */
484 children = pizza->children;
485 while (children)
486 {
487 child = children->data;
488 children = children->next;
489
490 gtk_widget_set_parent_window (child->widget, pizza->bin_window);
491 }
492 }
493
494 static void
495 gtk_pizza_unrealize (GtkWidget *widget)
496 {
497 GtkPizza *pizza;
498
499 g_return_if_fail (widget != NULL);
500 g_return_if_fail (GTK_IS_PIZZA (widget));
501
502 pizza = GTK_PIZZA (widget);
503
504 gdk_window_set_user_data (pizza->bin_window, NULL);
505 gdk_window_destroy (pizza->bin_window);
506 pizza->bin_window = NULL;
507
508 if (pizza_parent_class->unrealize)
509 pizza_parent_class->unrealize(widget);
510 }
511
512 static void
513 gtk_pizza_size_request (GtkWidget *widget,
514 GtkRequisition *requisition)
515 {
516 GtkPizza *pizza;
517 GtkPizzaChild *child;
518 GList *children;
519 GtkRequisition child_requisition;
520
521 g_return_if_fail (widget != NULL);
522 g_return_if_fail (GTK_IS_PIZZA (widget));
523 g_return_if_fail (requisition != NULL);
524
525 pizza = GTK_PIZZA (widget);
526
527 children = pizza->children;
528 while (children)
529 {
530 child = children->data;
531 children = children->next;
532
533 if (GTK_WIDGET_VISIBLE (child->widget))
534 {
535 gtk_widget_size_request (child->widget, &child_requisition);
536 }
537 }
538
539 /* request very little, I'm not sure if requesting nothing
540 will always have positive effects on stability... */
541 requisition->width = 2;
542 requisition->height = 2;
543 }
544
545 static void
546 gtk_pizza_size_allocate (GtkWidget *widget,
547 GtkAllocation *allocation)
548 {
549 GtkPizza *pizza;
550 gint border;
551 gint x,y,w,h;
552 GtkPizzaChild *child;
553 GList *children;
554 gboolean only_resize;
555
556 g_return_if_fail (widget != NULL);
557 g_return_if_fail (GTK_IS_PIZZA(widget));
558 g_return_if_fail (allocation != NULL);
559
560 pizza = GTK_PIZZA (widget);
561
562 only_resize = ((widget->allocation.x == allocation->x) &&
563 (widget->allocation.y == allocation->y));
564 widget->allocation = *allocation;
565
566 if (GTK_WIDGET_REALIZED(widget))
567 {
568 border = pizza->container.border_width;
569
570 x = allocation->x + border;
571 y = allocation->y + border;
572 w = allocation->width - border*2;
573 h = allocation->height - border*2;
574 if (w < 0)
575 w = 0;
576 if (h < 0)
577 h = 0;
578
579 if (pizza->m_noscroll)
580 {
581 if (only_resize)
582 gdk_window_resize( widget->window, allocation->width, allocation->height );
583 else
584 gdk_window_move_resize( widget->window, allocation->x, allocation->y,
585 allocation->width, allocation->height );
586
587 gdk_window_move_resize( pizza->bin_window, border, border, w, h );
588 }
589 else
590 {
591 if (only_resize)
592 gdk_window_resize( widget->window, w, h );
593 else
594 gdk_window_move_resize( widget->window, x, y, w, h );
595
596 gdk_window_resize( pizza->bin_window, w, h );
597 }
598 }
599
600 children = pizza->children;
601 while (children)
602 {
603 child = children->data;
604 children = children->next;
605
606 gtk_pizza_allocate_child (pizza, child);
607 }
608 }
609
610 static void
611 gtk_pizza_style_set(GtkWidget *widget, GtkStyle *previous_style)
612 {
613 if (GTK_WIDGET_REALIZED(widget))
614 {
615 gtk_style_set_background(widget->style, widget->window, GTK_STATE_NORMAL);
616 gtk_style_set_background(widget->style, GTK_PIZZA(widget)->bin_window, GTK_STATE_NORMAL );
617 }
618
619 pizza_parent_class->style_set(widget, previous_style);
620 }
621
622 static void
623 gtk_pizza_add (GtkContainer *container,
624 GtkWidget *widget)
625 {
626 g_return_if_fail (container != NULL);
627 g_return_if_fail (GTK_IS_PIZZA (container));
628 g_return_if_fail (widget != NULL);
629
630 gtk_pizza_put (GTK_PIZZA (container), widget, 0, 0, 20, 20 );
631 }
632
633 static void
634 gtk_pizza_remove (GtkContainer *container,
635 GtkWidget *widget)
636 {
637 GtkPizza *pizza;
638 GtkPizzaChild *child;
639 GList *children;
640
641 g_return_if_fail (container != NULL);
642 g_return_if_fail (GTK_IS_PIZZA (container));
643 g_return_if_fail (widget != NULL);
644
645 pizza = GTK_PIZZA (container);
646
647 children = pizza->children;
648 while (children)
649 {
650 child = children->data;
651
652 if (child->widget == widget)
653 {
654 gtk_widget_unparent (widget);
655
656 /* security checks */
657 g_return_if_fail (GTK_IS_WIDGET (widget));
658
659 pizza->children = g_list_remove_link (pizza->children, children);
660 g_list_free (children);
661 g_free (child);
662
663 /* security checks */
664 g_return_if_fail (GTK_IS_WIDGET (widget));
665
666 break;
667 }
668
669 children = children->next;
670 }
671 }
672
673 static void
674 gtk_pizza_forall (GtkContainer *container,
675 gboolean include_internals,
676 GtkCallback callback,
677 gpointer callback_data)
678 {
679 GtkPizza *pizza;
680 GtkPizzaChild *child;
681 GList *children;
682
683 g_return_if_fail (container != NULL);
684 g_return_if_fail (GTK_IS_PIZZA (container));
685 g_return_if_fail (callback != (GtkCallback)NULL);
686
687 pizza = GTK_PIZZA (container);
688
689 children = pizza->children;
690 while (children)
691 {
692 child = children->data;
693 children = children->next;
694
695 (* callback) (child->widget, callback_data);
696 }
697 }
698
699 static void
700 gtk_pizza_allocate_child (GtkPizza *pizza,
701 GtkPizzaChild *child)
702 {
703 GtkAllocation allocation;
704 GtkRequisition requisition;
705
706 allocation.x = child->x - pizza->m_xoffset;
707 allocation.y = child->y - pizza->m_yoffset;
708 gtk_widget_get_child_requisition (child->widget, &requisition);
709 allocation.width = requisition.width;
710 allocation.height = requisition.height;
711
712 if (gtk_widget_get_direction( GTK_WIDGET(pizza) ) == GTK_TEXT_DIR_RTL)
713 {
714 /* reverse horizontal placement */
715 gint offset,border;
716
717 offset = GTK_WIDGET(pizza)->allocation.width;
718 border = pizza->container.border_width;
719 offset -= border*2;
720
721 allocation.x = offset - child->x - allocation.width + pizza->m_xoffset;
722 }
723
724 gtk_widget_size_allocate (child->widget, &allocation);
725 }
726
727 void
728 gtk_pizza_scroll (GtkPizza *pizza, gint dx, gint dy)
729 {
730 GList *tmp_list;
731
732 pizza->m_xoffset += dx;
733 pizza->m_yoffset += dy;
734
735 if (pizza->bin_window)
736 gdk_window_scroll( pizza->bin_window, -dx, -dy );
737
738 for (tmp_list = pizza->children; tmp_list; tmp_list = tmp_list->next)
739 {
740 GtkPizzaChild *child = tmp_list->data;
741 gtk_widget_queue_resize(child->widget);
742 }
743 }
744
745 #ifdef __cplusplus
746 }
747 #endif /* __cplusplus */