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