]> git.saurik.com Git - wxWidgets.git/blame - src/gtk/win_gtk.c
wxChoice/wxComboBox background colour change (to get back to where we were);
[wxWidgets.git] / src / gtk / win_gtk.c
CommitLineData
c67daf87 1/* ///////////////////////////////////////////////////////////////////////////
c67d8618 2// Name: win_gtk.c
ed673c6a
RR
3// Purpose: Native GTK+ widget for wxWindows, based on GtkLayout and
4// GtkFixed. It makes use of the gravity window property and
5// therefore does not work with GTK 1.0.
c801d85f 6// Author: Robert Roebling
c67d8618 7// Id: $Id$
01111366 8// Copyright: (c) 1998 Robert Roebling
c801d85f 9// Licence: wxWindows licence
c67daf87 10/////////////////////////////////////////////////////////////////////////// */
c801d85f
KB
11
12#include "wx/gtk/win_gtk.h"
034be888 13#include "gtk/gtksignal.h"
ed673c6a
RR
14#include "gtk/gtkprivate.h"
15#include "gdk/gdkx.h"
38c7b3d3 16
c801d85f
KB
17#ifdef __cplusplus
18extern "C" {
19#endif /* __cplusplus */
20
ed673c6a
RR
21#define IS_ONSCREEN(x,y) ((x >= G_MINSHORT) && (x <= G_MAXSHORT) && \
22 (y >= G_MINSHORT) && (y <= G_MAXSHORT))
23
da048e3d
RR
24typedef struct _GtkPizzaAdjData GtkPizzaAdjData;
25typedef struct _GtkPizzaChild GtkPizzaChild;
ed673c6a 26
da048e3d 27struct _GtkPizzaAdjData
ed673c6a
RR
28{
29 gint dx;
30 gint dy;
31};
32
da048e3d 33struct _GtkPizzaChild
ed673c6a
RR
34{
35 GtkWidget *widget;
36 gint x;
37 gint y;
38 gint width;
39 gint height;
40};
41
da048e3d
RR
42static void gtk_pizza_class_init (GtkPizzaClass *klass);
43static void gtk_pizza_init (GtkPizza *pizza);
ed673c6a 44
da048e3d
RR
45static void gtk_pizza_realize (GtkWidget *widget);
46static void gtk_pizza_unrealize (GtkWidget *widget);
ed673c6a 47
da048e3d 48static void gtk_pizza_map (GtkWidget *widget);
ed673c6a 49
da048e3d 50static void gtk_pizza_size_request (GtkWidget *widget,
c801d85f 51 GtkRequisition *requisition);
da048e3d 52static void gtk_pizza_size_allocate (GtkWidget *widget,
c801d85f 53 GtkAllocation *allocation);
da048e3d 54static void gtk_pizza_draw (GtkWidget *widget,
c801d85f 55 GdkRectangle *area);
da048e3d 56static gint gtk_pizza_expose (GtkWidget *widget,
c801d85f 57 GdkEventExpose *event);
da048e3d 58static void gtk_pizza_add (GtkContainer *container,
c801d85f 59 GtkWidget *widget);
da048e3d 60static void gtk_pizza_remove (GtkContainer *container,
c801d85f 61 GtkWidget *widget);
da048e3d 62static void gtk_pizza_forall (GtkContainer *container,
75ed1d15 63 gboolean include_internals,
c801d85f
KB
64 GtkCallback callback,
65 gpointer callback_data);
ed673c6a 66
da048e3d
RR
67static void gtk_pizza_position_child (GtkPizza *pizza,
68 GtkPizzaChild *child);
69static void gtk_pizza_allocate_child (GtkPizza *pizza,
70 GtkPizzaChild *child);
71static void gtk_pizza_position_children (GtkPizza *pizza);
ed673c6a 72
da048e3d 73static void gtk_pizza_adjust_allocations_recurse (GtkWidget *widget,
ed673c6a 74 gpointer cb_data);
da048e3d 75static void gtk_pizza_adjust_allocations (GtkPizza *pizza,
ed673c6a
RR
76 gint dx,
77 gint dy);
78
79
da048e3d 80static void gtk_pizza_expose_area (GtkPizza *pizza,
ed673c6a
RR
81 gint x,
82 gint y,
83 gint width,
84 gint height);
da048e3d
RR
85static void gtk_pizza_adjustment_changed (GtkAdjustment *adjustment,
86 GtkPizza *pizza);
87static GdkFilterReturn gtk_pizza_filter (GdkXEvent *gdk_xevent,
ed673c6a
RR
88 GdkEvent *event,
89 gpointer data);
da048e3d 90static GdkFilterReturn gtk_pizza_main_filter (GdkXEvent *gdk_xevent,
ed673c6a
RR
91 GdkEvent *event,
92 gpointer data);
93
94
da048e3d 95static GtkType gtk_pizza_child_type (GtkContainer *container);
c801d85f 96
da048e3d 97static void gtk_pizza_scroll_set_adjustments (GtkPizza *pizza,
034be888
RR
98 GtkAdjustment *hadj,
99 GtkAdjustment *vadj);
c801d85f
KB
100
101
034be888 102static GtkContainerClass *parent_class = NULL;
ed673c6a 103static gboolean gravity_works;
034be888 104
c801d85f 105guint
da048e3d 106gtk_pizza_get_type ()
c801d85f 107{
da048e3d 108 static guint pizza_type = 0;
c801d85f 109
da048e3d 110 if (!pizza_type)
c801d85f 111 {
da048e3d 112 GtkTypeInfo pizza_info =
053f9cc1 113 {
da048e3d
RR
114 "GtkPizza",
115 sizeof (GtkPizza),
116 sizeof (GtkPizzaClass),
117 (GtkClassInitFunc) gtk_pizza_class_init,
118 (GtkObjectInitFunc) gtk_pizza_init,
053f9cc1
RR
119 /* reserved_1 */ NULL,
120 /* reserved_2 */ NULL,
121 (GtkClassInitFunc) NULL,
053f9cc1 122 };
da048e3d 123 pizza_type = gtk_type_unique (gtk_container_get_type (), &pizza_info);
c801d85f 124 }
053f9cc1 125
da048e3d 126 return pizza_type;
c801d85f
KB
127}
128
129static void
da048e3d 130gtk_pizza_class_init (GtkPizzaClass *klass)
c801d85f 131{
053f9cc1
RR
132 GtkObjectClass *object_class;
133 GtkWidgetClass *widget_class;
134 GtkContainerClass *container_class;
c801d85f 135
053f9cc1
RR
136 object_class = (GtkObjectClass*) klass;
137 widget_class = (GtkWidgetClass*) klass;
138 container_class = (GtkContainerClass*) klass;
053f9cc1 139 parent_class = gtk_type_class (GTK_TYPE_CONTAINER);
c801d85f 140
da048e3d
RR
141 widget_class->map = gtk_pizza_map;
142 widget_class->realize = gtk_pizza_realize;
143 widget_class->unrealize = gtk_pizza_unrealize;
144 widget_class->size_request = gtk_pizza_size_request;
145 widget_class->size_allocate = gtk_pizza_size_allocate;
146 widget_class->draw = gtk_pizza_draw;
147 widget_class->expose_event = gtk_pizza_expose;
053f9cc1 148
da048e3d
RR
149 container_class->add = gtk_pizza_add;
150 container_class->remove = gtk_pizza_remove;
151 container_class->forall = gtk_pizza_forall;
38c7b3d3 152
da048e3d 153 container_class->child_type = gtk_pizza_child_type;
034be888 154
da048e3d 155 klass->set_scroll_adjustments = gtk_pizza_scroll_set_adjustments;
034be888 156
053f9cc1 157 widget_class->set_scroll_adjustments_signal =
034be888
RR
158 gtk_signal_new ("set_scroll_adjustments",
159 GTK_RUN_LAST,
160 object_class->type,
da048e3d 161 GTK_SIGNAL_OFFSET (GtkPizzaClass, set_scroll_adjustments),
034be888
RR
162 gtk_marshal_NONE__POINTER_POINTER,
163 GTK_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
38c7b3d3
RR
164}
165
38c7b3d3 166static GtkType
da048e3d 167gtk_pizza_child_type (GtkContainer *container)
38c7b3d3 168{
053f9cc1 169 return GTK_TYPE_WIDGET;
c801d85f
KB
170}
171
172static void
da048e3d 173gtk_pizza_init (GtkPizza *pizza)
c801d85f 174{
da048e3d 175 GTK_WIDGET_UNSET_FLAGS (pizza, GTK_NO_WINDOW);
38c7b3d3 176
da048e3d 177 pizza->shadow_type = GTK_MYSHADOW_NONE;
034be888 178
da048e3d 179 pizza->children = NULL;
ed673c6a 180
da048e3d
RR
181 pizza->width = 20;
182 pizza->height = 20;
ed673c6a 183
da048e3d 184 pizza->bin_window = NULL;
ed673c6a 185
da048e3d
RR
186 pizza->configure_serial = 0;
187 pizza->scroll_x = 0;
188 pizza->scroll_y = 0;
189 pizza->visibility = GDK_VISIBILITY_PARTIAL;
147bc491 190
da048e3d 191 pizza->clear_on_draw = TRUE;
c801d85f
KB
192}
193
194GtkWidget*
da048e3d 195gtk_pizza_new ()
c801d85f 196{
da048e3d 197 GtkPizza *pizza;
c801d85f 198
da048e3d 199 pizza = gtk_type_new (gtk_pizza_get_type ());
c801d85f 200
da048e3d 201 return GTK_WIDGET (pizza);
c801d85f
KB
202}
203
7941ba11 204static void
da048e3d 205gtk_pizza_scroll_set_adjustments (GtkPizza *pizza,
053f9cc1
RR
206 GtkAdjustment *hadj,
207 GtkAdjustment *vadj)
034be888 208{
ed673c6a 209 /* We handle scrolling in the wxScrolledWindow, not here. */
034be888
RR
210}
211
212void
da048e3d 213gtk_pizza_set_shadow_type (GtkPizza *pizza,
5e014a0c 214 GtkMyShadowType type)
034be888 215{
da048e3d
RR
216 g_return_if_fail (pizza != NULL);
217 g_return_if_fail (GTK_IS_PIZZA (pizza));
034be888 218
da048e3d 219 if ((GtkMyShadowType) pizza->shadow_type != type)
034be888 220 {
da048e3d 221 pizza->shadow_type = type;
034be888 222
da048e3d 223 if (GTK_WIDGET_VISIBLE (pizza))
034be888 224 {
da048e3d
RR
225 gtk_widget_size_allocate (GTK_WIDGET (pizza), &(GTK_WIDGET (pizza)->allocation));
226 gtk_widget_queue_draw (GTK_WIDGET (pizza));
034be888
RR
227 }
228 }
229}
034be888 230
147bc491 231void
da048e3d 232gtk_pizza_set_clear (GtkPizza *pizza,
147bc491
RR
233 gboolean clear)
234{
da048e3d
RR
235 g_return_if_fail (pizza != NULL);
236 g_return_if_fail (GTK_IS_PIZZA (pizza));
147bc491 237
da048e3d 238 pizza->clear_on_draw = clear;
147bc491
RR
239}
240
c801d85f 241void
da048e3d 242gtk_pizza_put (GtkPizza *pizza,
053f9cc1 243 GtkWidget *widget,
ed673c6a
RR
244 gint x,
245 gint y,
246 gint width,
247 gint height)
c801d85f 248{
da048e3d 249 GtkPizzaChild *child_info;
053f9cc1 250
da048e3d
RR
251 g_return_if_fail (pizza != NULL);
252 g_return_if_fail (GTK_IS_PIZZA (pizza));
053f9cc1
RR
253 g_return_if_fail (widget != NULL);
254
da048e3d 255 child_info = g_new (GtkPizzaChild, 1);
ed673c6a 256
053f9cc1
RR
257 child_info->widget = widget;
258 child_info->x = x;
259 child_info->y = y;
260 child_info->width = width;
261 child_info->height = height;
d4c99d6f 262
da048e3d 263 pizza->children = g_list_append (pizza->children, child_info);
ed673c6a 264
da048e3d 265 gtk_widget_set_parent (widget, GTK_WIDGET (pizza));
c801d85f 266
da048e3d
RR
267 if (GTK_WIDGET_REALIZED (pizza))
268 gtk_widget_set_parent_window (widget, pizza->bin_window);
ed673c6a
RR
269
270 if (!IS_ONSCREEN (x, y))
271 GTK_PRIVATE_SET_FLAG (widget, GTK_IS_OFFSCREEN);
c801d85f 272
da048e3d 273 if (GTK_WIDGET_REALIZED (pizza))
053f9cc1 274 gtk_widget_realize (widget);
c801d85f 275
ed673c6a
RR
276 gtk_widget_set_usize (widget, width, height);
277
da048e3d 278 if (GTK_WIDGET_VISIBLE (pizza) && GTK_WIDGET_VISIBLE (widget))
326f9654 279 {
da048e3d 280 if (GTK_WIDGET_MAPPED (pizza))
053f9cc1 281 gtk_widget_map (widget);
326f9654 282
ed673c6a 283 gtk_widget_queue_resize (widget);
326f9654 284 }
c801d85f
KB
285}
286
287void
da048e3d 288gtk_pizza_move (GtkPizza *pizza,
053f9cc1 289 GtkWidget *widget,
ed673c6a
RR
290 gint x,
291 gint y)
c801d85f 292{
da048e3d 293 GtkPizzaChild *child;
053f9cc1 294 GList *children;
c801d85f 295
da048e3d
RR
296 g_return_if_fail (pizza != NULL);
297 g_return_if_fail (GTK_IS_PIZZA (pizza));
053f9cc1 298 g_return_if_fail (widget != NULL);
c801d85f 299
da048e3d 300 children = pizza->children;
053f9cc1 301 while (children)
fdd3ed7a 302 {
053f9cc1
RR
303 child = children->data;
304 children = children->next;
fdd3ed7a 305
053f9cc1 306 if (child->widget == widget)
fdd3ed7a 307 {
ed673c6a
RR
308 if ((child->x == x) && (child->y == y))
309 break;
310
311 child->x = x;
312 child->y = y;
313
da048e3d 314 if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_VISIBLE (pizza))
ed673c6a 315 gtk_widget_queue_resize (widget);
053f9cc1 316 break;
fdd3ed7a
RR
317 }
318 }
319}
320
321void
da048e3d 322gtk_pizza_resize (GtkPizza *pizza,
fdd3ed7a 323 GtkWidget *widget,
ed673c6a
RR
324 gint width,
325 gint height)
fdd3ed7a 326{
da048e3d 327 GtkPizzaChild *child;
053f9cc1 328 GList *children;
fdd3ed7a 329
da048e3d
RR
330 g_return_if_fail (pizza != NULL);
331 g_return_if_fail (GTK_IS_PIZZA (pizza));
053f9cc1 332 g_return_if_fail (widget != NULL);
fdd3ed7a 333
da048e3d 334 children = pizza->children;
053f9cc1 335 while (children)
fdd3ed7a 336 {
053f9cc1
RR
337 child = children->data;
338 children = children->next;
fdd3ed7a 339
053f9cc1 340 if (child->widget == widget)
fdd3ed7a 341 {
ed673c6a
RR
342 if ((child->width == width) && (child->height == height))
343 break;
344
345 child->width = width;
346 child->height = height;
347
348 gtk_widget_set_usize (widget, width, height);
349
da048e3d 350 if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_VISIBLE (pizza))
ed673c6a
RR
351 gtk_widget_queue_resize (widget);
352 break;
fdd3ed7a
RR
353 }
354 }
355}
356
357void
da048e3d 358gtk_pizza_set_size (GtkPizza *pizza,
053f9cc1 359 GtkWidget *widget,
ed673c6a
RR
360 gint x,
361 gint y,
362 gint width,
363 gint height)
fdd3ed7a 364{
da048e3d 365 GtkPizzaChild *child;
053f9cc1
RR
366 GList *children;
367 GtkAllocation child_allocation;
fdd3ed7a 368
da048e3d
RR
369 g_return_if_fail (pizza != NULL);
370 g_return_if_fail (GTK_IS_PIZZA (pizza));
053f9cc1 371 g_return_if_fail (widget != NULL);
fdd3ed7a 372
da048e3d 373 children = pizza->children;
053f9cc1 374 while (children)
c801d85f 375 {
053f9cc1
RR
376 child = children->data;
377 children = children->next;
c801d85f 378
053f9cc1 379 if (child->widget == widget)
c801d85f 380 {
053f9cc1
RR
381 if ((child->x == x) &&
382 (child->y == y) &&
383 (child->width == width) &&
384 (child->height == height)) return;
fb1585ae 385
053f9cc1
RR
386 child->x = x;
387 child->y = y;
388 child->width = width;
389 child->height = height;
ed673c6a
RR
390
391 gtk_widget_set_usize (widget, width, height);
392
da048e3d 393 if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_VISIBLE (pizza))
ed673c6a
RR
394 gtk_widget_queue_resize (widget);
395
6d693bb4 396 return;
c801d85f
KB
397 }
398 }
399}
400
401static void
da048e3d 402gtk_pizza_map (GtkWidget *widget)
c801d85f 403{
da048e3d
RR
404 GtkPizza *pizza;
405 GtkPizzaChild *child;
053f9cc1 406 GList *children;
c801d85f 407
053f9cc1 408 g_return_if_fail (widget != NULL);
da048e3d 409 g_return_if_fail (GTK_IS_PIZZA (widget));
c801d85f 410
053f9cc1 411 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
da048e3d 412 pizza = GTK_PIZZA (widget);
c801d85f 413
da048e3d 414 children = pizza->children;
053f9cc1 415 while (children)
c801d85f 416 {
053f9cc1
RR
417 child = children->data;
418 children = children->next;
c801d85f 419
ed673c6a
RR
420 if ( GTK_WIDGET_VISIBLE (child->widget) &&
421 !GTK_WIDGET_MAPPED (child->widget) &&
422 !GTK_WIDGET_IS_OFFSCREEN (child->widget))
423 {
053f9cc1 424 gtk_widget_map (child->widget);
ed673c6a 425 }
c801d85f 426 }
d872b8a9 427
053f9cc1 428 gdk_window_show (widget->window);
da048e3d 429 gdk_window_show (pizza->bin_window);
c801d85f
KB
430}
431
c801d85f 432static void
da048e3d 433gtk_pizza_realize (GtkWidget *widget)
c801d85f 434{
da048e3d 435 GtkPizza *pizza;
053f9cc1
RR
436 GdkWindowAttr attributes;
437 gint attributes_mask;
da048e3d 438 GtkPizzaChild *child;
ed673c6a 439 GList *children;
c801d85f 440
053f9cc1 441 g_return_if_fail (widget != NULL);
da048e3d 442 g_return_if_fail (GTK_IS_PIZZA (widget));
c801d85f 443
da048e3d 444 pizza = GTK_PIZZA (widget);
d4c99d6f 445
053f9cc1 446 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
c801d85f 447
053f9cc1 448 attributes.window_type = GDK_WINDOW_CHILD;
034be888 449
053f9cc1
RR
450 attributes.x = widget->allocation.x;
451 attributes.y = widget->allocation.y;
452 attributes.width = widget->allocation.width;
453 attributes.height = widget->allocation.height;
454
da048e3d 455 if (pizza->shadow_type == GTK_MYSHADOW_NONE)
053f9cc1 456 {
5e014a0c
RR
457 /* no border, no changes to sizes */
458 } else
da048e3d 459 if (pizza->shadow_type == GTK_MYSHADOW_THIN)
5e014a0c
RR
460 {
461 /* GTK_MYSHADOW_THIN == wxSIMPLE_BORDER */
462 attributes.x += 1;
463 attributes.y += 1;
464 attributes.width -= 2;
465 attributes.height -= 2;
466 } else
467 {
468 /* GTK_MYSHADOW_IN == wxSUNKEN_BORDER */
469 /* GTK_MYSHADOW_OUT == wxRAISED_BORDER */
053f9cc1
RR
470 attributes.x += 2;
471 attributes.y += 2;
472 attributes.width -= 4;
473 attributes.height -= 4;
474 }
58dea4b0 475
ed673c6a 476 /* minimal size */
053f9cc1
RR
477 if (attributes.width < 2) attributes.width = 2;
478 if (attributes.height < 2) attributes.height = 2;
ed673c6a 479
053f9cc1
RR
480 attributes.wclass = GDK_INPUT_OUTPUT;
481 attributes.visual = gtk_widget_get_visual (widget);
482 attributes.colormap = gtk_widget_get_colormap (widget);
ed673c6a
RR
483 attributes.event_mask =
484 GDK_VISIBILITY_NOTIFY_MASK;
485 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
486
487 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
488 &attributes, attributes_mask);
489 gdk_window_set_user_data (widget->window, widget);
490
491 attributes.x = 0;
492 attributes.y = 0;
493
053f9cc1
RR
494 attributes.event_mask = gtk_widget_get_events (widget);
495 attributes.event_mask |=
496 GDK_EXPOSURE_MASK |
497 GDK_POINTER_MOTION_MASK |
498 GDK_POINTER_MOTION_HINT_MASK |
499 GDK_BUTTON_MOTION_MASK |
500 GDK_BUTTON1_MOTION_MASK |
501 GDK_BUTTON2_MOTION_MASK |
502 GDK_BUTTON3_MOTION_MASK |
503 GDK_BUTTON_PRESS_MASK |
504 GDK_BUTTON_RELEASE_MASK |
505 GDK_KEY_PRESS_MASK |
506 GDK_KEY_RELEASE_MASK |
507 GDK_ENTER_NOTIFY_MASK |
508 GDK_LEAVE_NOTIFY_MASK |
509 GDK_FOCUS_CHANGE_MASK;
053f9cc1 510
da048e3d 511 pizza->bin_window = gdk_window_new (widget->window,
ed673c6a 512 &attributes, attributes_mask);
da048e3d 513 gdk_window_set_user_data (pizza->bin_window, widget);
c801d85f 514
053f9cc1
RR
515 widget->style = gtk_style_attach (widget->style, widget->window);
516 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
da048e3d 517 gtk_style_set_background (widget->style, pizza->bin_window, GTK_STATE_NORMAL);
ed673c6a
RR
518
519 /* add filters for intercepting visibility and expose events */
da048e3d
RR
520 gdk_window_add_filter (widget->window, gtk_pizza_main_filter, pizza);
521 gdk_window_add_filter (pizza->bin_window, gtk_pizza_filter, pizza);
ed673c6a
RR
522
523 /* we NEED gravity or we'll give up */
da048e3d 524 gravity_works = gdk_window_set_static_gravities (pizza->bin_window, TRUE);
ed673c6a
RR
525
526 /* cannot be done before realisation */
da048e3d 527 children = pizza->children;
ed673c6a
RR
528 while (children)
529 {
530 child = children->data;
531 children = children->next;
532
da048e3d 533 gtk_widget_set_parent_window (child->widget, pizza->bin_window);
ed673c6a
RR
534 }
535}
536
537static void
da048e3d 538gtk_pizza_unrealize (GtkWidget *widget)
ed673c6a 539{
da048e3d 540 GtkPizza *pizza;
ed673c6a
RR
541
542 g_return_if_fail (widget != NULL);
da048e3d 543 g_return_if_fail (GTK_IS_PIZZA (widget));
ed673c6a 544
da048e3d 545 pizza = GTK_PIZZA (widget);
ed673c6a 546
da048e3d
RR
547 gdk_window_set_user_data (pizza->bin_window, NULL);
548 gdk_window_destroy (pizza->bin_window);
549 pizza->bin_window = NULL;
ed673c6a
RR
550
551 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
552 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
c801d85f
KB
553}
554
555static void
da048e3d 556gtk_pizza_size_request (GtkWidget *widget,
053f9cc1 557 GtkRequisition *requisition)
c801d85f 558{
da048e3d
RR
559 GtkPizza *pizza;
560 GtkPizzaChild *child;
053f9cc1
RR
561 GList *children;
562 GtkRequisition child_requisition;
c801d85f 563
053f9cc1 564 g_return_if_fail (widget != NULL);
da048e3d 565 g_return_if_fail (GTK_IS_PIZZA (widget));
053f9cc1 566 g_return_if_fail (requisition != NULL);
c801d85f 567
da048e3d 568 pizza = GTK_PIZZA (widget);
c801d85f 569
da048e3d 570 children = pizza->children;
053f9cc1 571 while (children)
c801d85f 572 {
053f9cc1
RR
573 child = children->data;
574 children = children->next;
c801d85f 575
053f9cc1 576 if (GTK_WIDGET_VISIBLE (child->widget))
c801d85f 577 {
053f9cc1 578 gtk_widget_size_request (child->widget, &child_requisition);
c801d85f
KB
579 }
580 }
d872b8a9 581
053f9cc1
RR
582 /* request very little, I'm not sure if requesting nothing
583 will always have positive effects on stability... */
584 requisition->width = 2;
585 requisition->height = 2;
c801d85f
KB
586}
587
588static void
da048e3d 589gtk_pizza_size_allocate (GtkWidget *widget,
fdd3ed7a 590 GtkAllocation *allocation)
c801d85f 591{
da048e3d 592 GtkPizza *pizza;
053f9cc1 593 gint border;
ed673c6a 594 gint x,y,w,h;
da048e3d 595 GtkPizzaChild *child;
053f9cc1 596 GtkAllocation child_allocation;
ed673c6a 597 GList *children;
c801d85f 598
053f9cc1 599 g_return_if_fail (widget != NULL);
da048e3d 600 g_return_if_fail (GTK_IS_PIZZA(widget));
053f9cc1 601 g_return_if_fail (allocation != NULL);
c801d85f 602
da048e3d 603 pizza = GTK_PIZZA (widget);
e208b369 604
227e5e99 605 widget->allocation = *allocation;
ed673c6a 606
da048e3d 607 if (pizza->shadow_type == GTK_MYSHADOW_NONE)
053f9cc1 608 border = 0;
5e014a0c 609 else
da048e3d 610 if (pizza->shadow_type == GTK_MYSHADOW_THIN)
5e014a0c 611 border = 1;
053f9cc1
RR
612 else
613 border = 2;
ed673c6a
RR
614
615 x = allocation->x + border;
616 y = allocation->y + border;
617 w = allocation->width - border*2;
618 h = allocation->height - border*2;
034be888 619
053f9cc1
RR
620 if (GTK_WIDGET_REALIZED (widget))
621 {
ed673c6a 622 gdk_window_move_resize( widget->window, x, y, w, h );
da048e3d 623 gdk_window_move_resize( pizza->bin_window, 0, 0, w, h );
053f9cc1 624 }
6d693bb4 625
da048e3d 626 children = pizza->children;
6d693bb4
RR
627 while (children)
628 {
629 child = children->data;
630 children = children->next;
631
da048e3d
RR
632 gtk_pizza_position_child (pizza, child);
633 gtk_pizza_allocate_child (pizza, child);
6d693bb4 634 }
c801d85f
KB
635}
636
637static void
da048e3d 638gtk_pizza_draw (GtkWidget *widget,
ed673c6a 639 GdkRectangle *area)
c801d85f 640{
da048e3d
RR
641 GtkPizza *pizza;
642 GtkPizzaChild *child;
ed673c6a
RR
643 GdkRectangle child_area;
644 GList *children;
645
053f9cc1 646 g_return_if_fail (widget != NULL);
da048e3d 647 g_return_if_fail (GTK_IS_PIZZA (widget));
053f9cc1 648
da048e3d 649 pizza = GTK_PIZZA (widget);
ed673c6a 650
da048e3d 651 children = pizza->children;
147bc491 652 if ( !(GTK_WIDGET_APP_PAINTABLE (widget)) &&
da048e3d 653 (pizza->clear_on_draw))
ed673c6a 654 {
da048e3d 655 gdk_window_clear_area( pizza->bin_window,
ed673c6a
RR
656 area->x, area->y, area->width, area->height);
657 }
658
659 while (children)
660 {
661 child = children->data;
662 children = children->next;
663
664 if (gtk_widget_intersect (child->widget, area, &child_area))
665 gtk_widget_draw (child->widget, &child_area);
666 }
c801d85f
KB
667}
668
c801d85f 669static gint
da048e3d 670gtk_pizza_expose (GtkWidget *widget,
fdd3ed7a 671 GdkEventExpose *event)
c801d85f 672{
da048e3d
RR
673 GtkPizza *pizza;
674 GtkPizzaChild *child;
ed673c6a
RR
675 GdkEventExpose child_event;
676 GList *children;
c801d85f 677
ed673c6a 678 g_return_val_if_fail (widget != NULL, FALSE);
da048e3d 679 g_return_val_if_fail (GTK_IS_PIZZA (widget), FALSE);
ed673c6a
RR
680 g_return_val_if_fail (event != NULL, FALSE);
681
da048e3d 682 pizza = GTK_PIZZA (widget);
c801d85f 683
ed673c6a
RR
684/*
685 if (event->window == widget->window)
c801d85f 686 {
da048e3d 687 gtk_pizza_draw_border( pizza );
ed673c6a
RR
688 return FALSE;
689 }
690*/
c801d85f 691
da048e3d 692 if (event->window != pizza->bin_window)
ed673c6a 693 return FALSE;
c801d85f 694
da048e3d 695 children = pizza->children;
ed673c6a
RR
696 while (children)
697 {
698 child = children->data;
699 children = children->next;
c801d85f 700
ed673c6a
RR
701 child_event = *event;
702
703 if (GTK_WIDGET_NO_WINDOW (child->widget) &&
704 GTK_WIDGET_DRAWABLE (child->widget) &&
705 gtk_widget_intersect (child->widget, &event->area, &child_event.area))
706 {
c801d85f
KB
707 gtk_widget_event (child->widget, (GdkEvent*) &child_event);
708 }
709 }
710
ed673c6a 711 return FALSE;
c801d85f
KB
712}
713
714static void
da048e3d 715gtk_pizza_add (GtkContainer *container,
c801d85f
KB
716 GtkWidget *widget)
717{
ed673c6a 718 g_return_if_fail (container != NULL);
da048e3d 719 g_return_if_fail (GTK_IS_PIZZA (container));
ed673c6a 720 g_return_if_fail (widget != NULL);
c801d85f 721
da048e3d 722 gtk_pizza_put (GTK_PIZZA (container), widget, 0, 0, 20, 20 );
c801d85f
KB
723}
724
725static void
da048e3d 726gtk_pizza_remove (GtkContainer *container,
ed673c6a 727 GtkWidget *widget)
c801d85f 728{
da048e3d
RR
729 GtkPizza *pizza;
730 GtkPizzaChild *child;
ed673c6a 731 GList *children;
c801d85f 732
ed673c6a 733 g_return_if_fail (container != NULL);
da048e3d 734 g_return_if_fail (GTK_IS_PIZZA (container));
ed673c6a 735 g_return_if_fail (widget != NULL);
c801d85f 736
da048e3d 737 pizza = GTK_PIZZA (container);
c801d85f 738
da048e3d 739 children = pizza->children;
ed673c6a 740 while (children)
c801d85f 741 {
ed673c6a 742 child = children->data;
c801d85f 743
ed673c6a 744 if (child->widget == widget)
c801d85f 745 {
ed673c6a 746 gtk_widget_unparent (widget);
c801d85f 747
ed673c6a
RR
748 /* security checks */
749 g_return_if_fail (GTK_IS_WIDGET (widget));
5fd11f09 750
da048e3d 751 pizza->children = g_list_remove_link (pizza->children, children);
ed673c6a
RR
752 g_list_free (children);
753 g_free (child);
c801d85f 754
ed673c6a
RR
755 /* security checks */
756 g_return_if_fail (GTK_IS_WIDGET (widget));
757
758 GTK_PRIVATE_UNSET_FLAG (widget, GTK_IS_OFFSCREEN);
759
760 break;
c801d85f
KB
761 }
762
ed673c6a 763 children = children->next;
c801d85f
KB
764 }
765}
766
767static void
da048e3d 768gtk_pizza_forall (GtkContainer *container,
75ed1d15 769 gboolean include_internals,
c801d85f
KB
770 GtkCallback callback,
771 gpointer callback_data)
772{
da048e3d
RR
773 GtkPizza *pizza;
774 GtkPizzaChild *child;
6d693bb4 775 GList *children;
c801d85f 776
6d693bb4 777 g_return_if_fail (container != NULL);
da048e3d 778 g_return_if_fail (GTK_IS_PIZZA (container));
6d693bb4 779 g_return_if_fail (callback != NULL);
c801d85f 780
da048e3d 781 pizza = GTK_PIZZA (container);
c801d85f 782
da048e3d 783 children = pizza->children;
6d693bb4 784 while (children)
c801d85f 785 {
6d693bb4
RR
786 child = children->data;
787 children = children->next;
c801d85f 788
6d693bb4 789 (* callback) (child->widget, callback_data);
c801d85f
KB
790 }
791}
792
793
ed673c6a
RR
794/* Operations on children
795 */
796
797static void
da048e3d
RR
798gtk_pizza_position_child (GtkPizza *pizza,
799 GtkPizzaChild *child)
ed673c6a 800{
6d693bb4
RR
801 gint x;
802 gint y;
ed673c6a 803
da048e3d
RR
804 x = child->x - pizza->xoffset;
805 y = child->y - pizza->yoffset;
ed673c6a 806
6d693bb4 807 if (IS_ONSCREEN (x,y))
ed673c6a 808 {
da048e3d 809 if (GTK_WIDGET_MAPPED (pizza) &&
ed673c6a
RR
810 GTK_WIDGET_VISIBLE (child->widget))
811 {
6d693bb4
RR
812 if (!GTK_WIDGET_MAPPED (child->widget))
813 gtk_widget_map (child->widget);
ed673c6a
RR
814 }
815
6d693bb4
RR
816 if (GTK_WIDGET_IS_OFFSCREEN (child->widget))
817 GTK_PRIVATE_UNSET_FLAG (child->widget, GTK_IS_OFFSCREEN);
ed673c6a 818 }
6d693bb4 819 else
ed673c6a 820 {
6d693bb4
RR
821 if (!GTK_WIDGET_IS_OFFSCREEN (child->widget))
822 GTK_PRIVATE_SET_FLAG (child->widget, GTK_IS_OFFSCREEN);
ed673c6a 823
6d693bb4
RR
824 if (GTK_WIDGET_MAPPED (child->widget))
825 gtk_widget_unmap (child->widget);
ed673c6a
RR
826 }
827}
828
829static void
da048e3d
RR
830gtk_pizza_allocate_child (GtkPizza *pizza,
831 GtkPizzaChild *child)
ed673c6a 832{
6d693bb4
RR
833 GtkAllocation allocation;
834 GtkRequisition requisition;
835
da048e3d
RR
836 allocation.x = child->x - pizza->xoffset;
837 allocation.y = child->y - pizza->yoffset;
6d693bb4
RR
838 gtk_widget_get_child_requisition (child->widget, &requisition);
839 allocation.width = requisition.width;
840 allocation.height = requisition.height;
ed673c6a 841
6d693bb4 842 gtk_widget_size_allocate (child->widget, &allocation);
ed673c6a
RR
843}
844
845static void
da048e3d 846gtk_pizza_position_children (GtkPizza *pizza)
ed673c6a 847{
6d693bb4 848 GList *tmp_list;
ed673c6a 849
da048e3d 850 tmp_list = pizza->children;
6d693bb4 851 while (tmp_list)
ed673c6a 852 {
da048e3d 853 GtkPizzaChild *child = tmp_list->data;
6d693bb4 854 tmp_list = tmp_list->next;
ed673c6a 855
da048e3d 856 gtk_pizza_position_child (pizza, child);
ed673c6a
RR
857 }
858}
859
860static void
da048e3d 861gtk_pizza_adjust_allocations_recurse (GtkWidget *widget,
ed673c6a
RR
862 gpointer cb_data)
863{
da048e3d 864 GtkPizzaAdjData *data = cb_data;
ed673c6a 865
6d693bb4
RR
866 widget->allocation.x += data->dx;
867 widget->allocation.y += data->dy;
ed673c6a 868
6d693bb4
RR
869 if (GTK_WIDGET_NO_WINDOW (widget) && GTK_IS_CONTAINER (widget))
870 {
871 gtk_container_forall (GTK_CONTAINER (widget),
da048e3d 872 gtk_pizza_adjust_allocations_recurse,
ed673c6a 873 cb_data);
6d693bb4 874 }
ed673c6a
RR
875}
876
877static void
da048e3d 878gtk_pizza_adjust_allocations (GtkPizza *pizza,
ed673c6a
RR
879 gint dx,
880 gint dy)
881{
882 GList *tmp_list;
da048e3d 883 GtkPizzaAdjData data;
ed673c6a
RR
884
885 data.dx = dx;
886 data.dy = dy;
887
da048e3d 888 tmp_list = pizza->children;
ed673c6a
RR
889 while (tmp_list)
890 {
da048e3d 891 GtkPizzaChild *child = tmp_list->data;
ed673c6a
RR
892 tmp_list = tmp_list->next;
893
894 child->widget->allocation.x += dx;
895 child->widget->allocation.y += dy;
896
897 if (GTK_WIDGET_NO_WINDOW (child->widget) &&
898 GTK_IS_CONTAINER (child->widget))
899 gtk_container_forall (GTK_CONTAINER (child->widget),
da048e3d 900 gtk_pizza_adjust_allocations_recurse,
ed673c6a
RR
901 &data);
902 }
903}
904
905/* Callbacks */
906
907/* Send a synthetic expose event to the widget
908 */
909static void
da048e3d 910gtk_pizza_expose_area (GtkPizza *pizza,
ed673c6a
RR
911 gint x, gint y, gint width, gint height)
912{
da048e3d 913 if (pizza->visibility == GDK_VISIBILITY_UNOBSCURED)
ed673c6a
RR
914 {
915 GdkEventExpose event;
916
917 event.type = GDK_EXPOSE;
918 event.send_event = TRUE;
da048e3d 919 event.window = pizza->bin_window;
ed673c6a
RR
920 event.count = 0;
921
922 event.area.x = x;
923 event.area.y = y;
924 event.area.width = width;
925 event.area.height = height;
926
927 gdk_window_ref (event.window);
da048e3d 928 gtk_widget_event (GTK_WIDGET (pizza), (GdkEvent *)&event);
ed673c6a
RR
929 gdk_window_unref (event.window);
930 }
931}
932
933/* This function is used to find events to process while scrolling
934 */
935
936static Bool
da048e3d 937gtk_pizza_expose_predicate (Display *display,
ed673c6a
RR
938 XEvent *xevent,
939 XPointer arg)
940{
941 if ((xevent->type == Expose) ||
942 ((xevent->xany.window == *(Window *)arg) &&
943 (xevent->type == ConfigureNotify)))
944 return True;
945 else
946 return False;
947}
948
949/* This is the main routine to do the scrolling. Scrolling is
950 * done by "Guffaw" scrolling, as in the Mozilla XFE, with
951 * a few modifications.
952 *
953 * The main improvement is that we keep track of whether we
954 * are obscured or not. If not, we ignore the generated expose
955 * events and instead do the exposes ourself, without having
956 * to wait for a roundtrip to the server. This also provides
957 * a limited form of expose-event compression, since we do
958 * the affected area as one big chunk.
959 */
960
961void
da048e3d 962gtk_pizza_scroll (GtkPizza *pizza, gint dx, gint dy)
ed673c6a
RR
963{
964 GtkWidget *widget;
965 XEvent xevent;
966
967 gint x,y,w,h,border;
968
da048e3d 969 widget = GTK_WIDGET (pizza);
ed673c6a 970
da048e3d
RR
971 pizza->xoffset += dx;
972 pizza->yoffset += dy;
ed673c6a 973
da048e3d 974 if (!GTK_WIDGET_MAPPED (pizza))
ed673c6a 975 {
da048e3d 976 gtk_pizza_position_children (pizza);
ed673c6a
RR
977 return;
978 }
979
da048e3d 980 gtk_pizza_adjust_allocations (pizza, -dx, -dy);
ed673c6a 981
da048e3d 982 if (pizza->shadow_type == GTK_MYSHADOW_NONE)
ed673c6a
RR
983 border = 0;
984 else
da048e3d 985 if (pizza->shadow_type == GTK_MYSHADOW_THIN)
ed673c6a
RR
986 border = 1;
987 else
988 border = 2;
989
990 x = 0;
991 y = 0;
992 w = widget->allocation.width - 2*border;
993 h = widget->allocation.height - 2*border;
994
995 if (dx > 0)
996 {
997 if (gravity_works)
998 {
da048e3d 999 gdk_window_resize (pizza->bin_window,
ed673c6a
RR
1000 w + dx,
1001 h);
da048e3d
RR
1002 gdk_window_move (pizza->bin_window, x-dx, y);
1003 gdk_window_move_resize (pizza->bin_window, x, y, w, h );
ed673c6a
RR
1004 }
1005 else
1006 {
1007 /* FIXME */
1008 }
1009
da048e3d 1010 gtk_pizza_expose_area (pizza,
ed673c6a
RR
1011 MAX ((gint)w - dx, 0),
1012 0,
1013 MIN (dx, w),
1014 h);
1015 }
1016 else if (dx < 0)
1017 {
1018 if (gravity_works)
1019 {
da048e3d 1020 gdk_window_move_resize (pizza->bin_window,
ed673c6a
RR
1021 x + dx,
1022 y,
1023 w - dx,
1024 h);
da048e3d
RR
1025 gdk_window_move (pizza->bin_window, x, y);
1026 gdk_window_resize (pizza->bin_window, w, h );
ed673c6a
RR
1027 }
1028 else
1029 {
1030 /* FIXME */
1031 }
1032
da048e3d 1033 gtk_pizza_expose_area (pizza,
ed673c6a
RR
1034 0,
1035 0,
1036 MIN (-dx, w),
1037 h);
1038 }
1039
1040 if (dy > 0)
1041 {
1042 if (gravity_works)
1043 {
da048e3d
RR
1044 gdk_window_resize (pizza->bin_window, w, h + dy);
1045 gdk_window_move (pizza->bin_window, x, y-dy);
1046 gdk_window_move_resize (pizza->bin_window,
ed673c6a
RR
1047 x, y, w, h );
1048 }
1049 else
1050 {
1051 /* FIXME */
1052 }
1053
da048e3d 1054 gtk_pizza_expose_area (pizza,
ed673c6a
RR
1055 0,
1056 MAX ((gint)h - dy, 0),
1057 w,
1058 MIN (dy, h));
1059 }
1060 else if (dy < 0)
1061 {
1062 if (gravity_works)
1063 {
da048e3d 1064 gdk_window_move_resize (pizza->bin_window,
ed673c6a 1065 x, y+dy, w, h - dy );
da048e3d
RR
1066 gdk_window_move (pizza->bin_window, x, y);
1067 gdk_window_resize (pizza->bin_window, w, h );
ed673c6a
RR
1068 }
1069 else
1070 {
1071 /* FIXME */
1072 }
da048e3d 1073 gtk_pizza_expose_area (pizza,
ed673c6a
RR
1074 0,
1075 0,
1076 w,
1077 MIN (-dy, (gint)h));
1078 }
1079
da048e3d 1080 gtk_pizza_position_children (pizza);
ed673c6a
RR
1081
1082 /* We have to make sure that all exposes from this scroll get
1083 * processed before we scroll again, or the expose events will
1084 * have invalid coordinates.
1085 *
1086 * We also do expose events for other windows, since otherwise
1087 * their updating will fall behind the scrolling
1088 *
1089 * This also avoids a problem in pre-1.0 GTK where filters don't
1090 * have access to configure events that were compressed.
1091 */
1092
1093 gdk_flush();
da048e3d 1094 while (XCheckIfEvent(GDK_WINDOW_XDISPLAY (pizza->bin_window),
ed673c6a 1095 &xevent,
da048e3d
RR
1096 gtk_pizza_expose_predicate,
1097 (XPointer)&GDK_WINDOW_XWINDOW (pizza->bin_window)))
ed673c6a
RR
1098 {
1099 GdkEvent event;
1100 GtkWidget *event_widget;
1101
da048e3d
RR
1102 if ((xevent.xany.window == GDK_WINDOW_XWINDOW (pizza->bin_window)) &&
1103 (gtk_pizza_filter (&xevent, &event, pizza) == GDK_FILTER_REMOVE))
ed673c6a
RR
1104 continue;
1105
1106 if (xevent.type == Expose)
1107 {
1108 event.expose.window = gdk_window_lookup (xevent.xany.window);
1109 gdk_window_get_user_data (event.expose.window,
1110 (gpointer *)&event_widget);
1111
1112 if (event_widget)
1113 {
1114 event.expose.type = GDK_EXPOSE;
1115 event.expose.area.x = xevent.xexpose.x;
1116 event.expose.area.y = xevent.xexpose.y;
1117 event.expose.area.width = xevent.xexpose.width;
1118 event.expose.area.height = xevent.xexpose.height;
1119 event.expose.count = xevent.xexpose.count;
1120
1121 gdk_window_ref (event.expose.window);
1122 gtk_widget_event (event_widget, &event);
1123 gdk_window_unref (event.expose.window);
1124 }
1125 }
1126 }
1127}
1128
1129/* The main event filter. Actually, we probably don't really need
1130 * to install this as a filter at all, since we are calling it
1131 * directly above in the expose-handling hack. But in case scrollbars
1132 * are fixed up in some manner...
1133 *
1134 * This routine identifies expose events that are generated when
1135 * we've temporarily moved the bin_window_origin, and translates
1136 * them or discards them, depending on whether we are obscured
1137 * or not.
1138 */
1139static GdkFilterReturn
da048e3d 1140gtk_pizza_filter (GdkXEvent *gdk_xevent,
ed673c6a
RR
1141 GdkEvent *event,
1142 gpointer data)
1143{
1144 XEvent *xevent;
da048e3d 1145 GtkPizza *pizza;
ed673c6a
RR
1146
1147 xevent = (XEvent *)gdk_xevent;
da048e3d 1148 pizza = GTK_PIZZA (data);
ed673c6a
RR
1149
1150 switch (xevent->type)
1151 {
1152 case Expose:
da048e3d 1153 if (xevent->xexpose.serial == pizza->configure_serial)
ed673c6a 1154 {
da048e3d 1155 if (pizza->visibility == GDK_VISIBILITY_UNOBSCURED)
ed673c6a
RR
1156 return GDK_FILTER_REMOVE;
1157 else
1158 {
da048e3d
RR
1159 xevent->xexpose.x += pizza->scroll_x;
1160 xevent->xexpose.y += pizza->scroll_y;
ed673c6a
RR
1161
1162 break;
1163 }
1164 }
1165 break;
1166
1167 case ConfigureNotify:
1168 if ((xevent->xconfigure.x != 0) || (xevent->xconfigure.y != 0))
1169 {
da048e3d
RR
1170 pizza->configure_serial = xevent->xconfigure.serial;
1171 pizza->scroll_x = xevent->xconfigure.x;
1172 pizza->scroll_y = xevent->xconfigure.y;
ed673c6a
RR
1173 }
1174 break;
1175 }
1176
1177 return GDK_FILTER_CONTINUE;
1178}
1179
1180/* Although GDK does have a GDK_VISIBILITY_NOTIFY event,
1181 * there is no corresponding event in GTK, so we have
1182 * to get the events from a filter
1183 */
1184static GdkFilterReturn
da048e3d 1185gtk_pizza_main_filter (GdkXEvent *gdk_xevent,
ed673c6a
RR
1186 GdkEvent *event,
1187 gpointer data)
1188{
1189 XEvent *xevent;
da048e3d 1190 GtkPizza *pizza;
ed673c6a
RR
1191
1192 xevent = (XEvent *)gdk_xevent;
da048e3d 1193 pizza = GTK_PIZZA (data);
ed673c6a
RR
1194
1195 if (xevent->type == VisibilityNotify)
1196 {
1197 switch (xevent->xvisibility.state)
1198 {
1199 case VisibilityFullyObscured:
da048e3d 1200 pizza->visibility = GDK_VISIBILITY_FULLY_OBSCURED;
ed673c6a
RR
1201 break;
1202
1203 case VisibilityPartiallyObscured:
da048e3d 1204 pizza->visibility = GDK_VISIBILITY_PARTIAL;
ed673c6a
RR
1205 break;
1206
1207 case VisibilityUnobscured:
da048e3d 1208 pizza->visibility = GDK_VISIBILITY_UNOBSCURED;
ed673c6a
RR
1209 break;
1210 }
1211
1212 return GDK_FILTER_REMOVE;
1213 }
1214
1215
1216 return GDK_FILTER_CONTINUE;
1217}
1218
1219
1220
1221
c801d85f
KB
1222#ifdef __cplusplus
1223}
1224#endif /* __cplusplus */
1225