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