source: trunk/third/gtk/gtk/gtklayout.c @ 14482

Revision 14482, 31.6 KB checked in by ghudson, 25 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r14481, which included commits to RCS files with non-trunk default branches.
Line 
1/* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 *
19 * GtkLayout: Widget for scrolling of arbitrary-sized areas.
20 *
21 * Copyright Owen Taylor, 1998
22 */
23
24/*
25 * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
26 * file for a list of people on the GTK+ Team.  See the ChangeLog
27 * files for a list of changes.  These files are distributed with
28 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
29 */
30
31#include "gtklayout.h"
32#include "gtksignal.h"
33#include "gtkprivate.h"
34#include "gdk/gdkx.h"
35
36typedef struct _GtkLayoutAdjData GtkLayoutAdjData;
37typedef struct _GtkLayoutChild   GtkLayoutChild;
38
39struct _GtkLayoutAdjData {
40  gint dx;
41  gint dy;
42};
43
44struct _GtkLayoutChild {
45  GtkWidget *widget;
46  gint x;
47  gint y;
48};
49
50#define IS_ONSCREEN(x,y) ((x >= G_MINSHORT) && (x <= G_MAXSHORT) && \
51                          (y >= G_MINSHORT) && (y <= G_MAXSHORT))
52
53static void     gtk_layout_class_init         (GtkLayoutClass *class);
54static void     gtk_layout_init               (GtkLayout      *layout);
55
56static void     gtk_layout_finalize           (GtkObject      *object);
57static void     gtk_layout_realize            (GtkWidget      *widget);
58static void     gtk_layout_unrealize          (GtkWidget      *widget);
59static void     gtk_layout_map                (GtkWidget      *widget);
60static void     gtk_layout_size_request       (GtkWidget      *widget,
61                                               GtkRequisition *requisition);
62static void     gtk_layout_size_allocate      (GtkWidget      *widget,
63                                               GtkAllocation  *allocation);
64static void     gtk_layout_draw               (GtkWidget      *widget,
65                                               GdkRectangle   *area);
66static gint     gtk_layout_expose             (GtkWidget      *widget,
67                                               GdkEventExpose *event);
68
69static void     gtk_layout_remove             (GtkContainer *container,
70                                               GtkWidget    *widget);
71static void     gtk_layout_forall             (GtkContainer *container,
72                                               gboolean      include_internals,
73                                               GtkCallback   callback,
74                                               gpointer      callback_data);
75static void     gtk_layout_set_adjustments    (GtkLayout     *layout,
76                                               GtkAdjustment *hadj,
77                                               GtkAdjustment *vadj);
78
79static void     gtk_layout_position_child     (GtkLayout      *layout,
80                                               GtkLayoutChild *child);
81static void     gtk_layout_allocate_child     (GtkLayout      *layout,
82                                               GtkLayoutChild *child);
83static void     gtk_layout_position_children  (GtkLayout      *layout);
84
85static void     gtk_layout_adjust_allocations_recurse (GtkWidget *widget,
86                                                       gpointer   cb_data);
87static void     gtk_layout_adjust_allocations         (GtkLayout *layout,
88                                                       gint       dx,
89                                                       gint       dy);
90
91
92
93static void     gtk_layout_expose_area        (GtkLayout      *layout,
94                                               gint            x,
95                                               gint            y,
96                                               gint            width,
97                                               gint            height);
98static void     gtk_layout_adjustment_changed (GtkAdjustment  *adjustment,
99                                               GtkLayout      *layout);
100static GdkFilterReturn gtk_layout_main_filter (GdkXEvent      *gdk_xevent,
101                                               GdkEvent       *event,
102                                               gpointer        data);
103
104static GtkWidgetClass *parent_class = NULL;
105static gboolean gravity_works;
106
107/* Public interface
108 */
109 
110GtkWidget*   
111gtk_layout_new (GtkAdjustment *hadjustment,
112                GtkAdjustment *vadjustment)
113{
114  GtkLayout *layout;
115
116  layout = gtk_type_new (GTK_TYPE_LAYOUT);
117
118  gtk_layout_set_adjustments (layout, hadjustment, vadjustment);
119
120  return GTK_WIDGET (layout);
121}
122
123GtkAdjustment*
124gtk_layout_get_hadjustment (GtkLayout     *layout)
125{
126  g_return_val_if_fail (layout != NULL, NULL);
127  g_return_val_if_fail (GTK_IS_LAYOUT (layout), NULL);
128
129  return layout->hadjustment;
130}
131GtkAdjustment*
132gtk_layout_get_vadjustment (GtkLayout     *layout)
133{
134  g_return_val_if_fail (layout != NULL, NULL);
135  g_return_val_if_fail (GTK_IS_LAYOUT (layout), NULL);
136
137  return layout->vadjustment;
138}
139
140static void           
141gtk_layout_set_adjustments (GtkLayout     *layout,
142                            GtkAdjustment *hadj,
143                            GtkAdjustment *vadj)
144{
145  gboolean need_adjust = FALSE;
146
147  g_return_if_fail (layout != NULL);
148  g_return_if_fail (GTK_IS_LAYOUT (layout));
149
150  if (hadj)
151    g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
152  else
153    hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
154  if (vadj)
155    g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
156  else
157    vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
158 
159  if (layout->hadjustment && (layout->hadjustment != hadj))
160    {
161      gtk_signal_disconnect_by_data (GTK_OBJECT (layout->hadjustment), layout);
162      gtk_object_unref (GTK_OBJECT (layout->hadjustment));
163    }
164 
165  if (layout->vadjustment && (layout->vadjustment != vadj))
166    {
167      gtk_signal_disconnect_by_data (GTK_OBJECT (layout->vadjustment), layout);
168      gtk_object_unref (GTK_OBJECT (layout->vadjustment));
169    }
170 
171  if (layout->hadjustment != hadj)
172    {
173      layout->hadjustment = hadj;
174      gtk_object_ref (GTK_OBJECT (layout->hadjustment));
175      gtk_object_sink (GTK_OBJECT (layout->hadjustment));
176     
177      gtk_signal_connect (GTK_OBJECT (layout->hadjustment), "value_changed",
178                          (GtkSignalFunc) gtk_layout_adjustment_changed,
179                          layout);
180      need_adjust = TRUE;
181    }
182 
183  if (layout->vadjustment != vadj)
184    {
185      layout->vadjustment = vadj;
186      gtk_object_ref (GTK_OBJECT (layout->vadjustment));
187      gtk_object_sink (GTK_OBJECT (layout->vadjustment));
188     
189      gtk_signal_connect (GTK_OBJECT (layout->vadjustment), "value_changed",
190                          (GtkSignalFunc) gtk_layout_adjustment_changed,
191                          layout);
192      need_adjust = TRUE;
193    }
194
195  if (need_adjust)
196    gtk_layout_adjustment_changed (NULL, layout);
197}
198
199static void
200gtk_layout_finalize (GtkObject *object)
201{
202  GtkLayout *layout = GTK_LAYOUT (object);
203
204  gtk_object_unref (GTK_OBJECT (layout->hadjustment));
205  gtk_object_unref (GTK_OBJECT (layout->vadjustment));
206
207  GTK_OBJECT_CLASS (parent_class)->finalize (object);
208}
209
210void           
211gtk_layout_set_hadjustment (GtkLayout     *layout,
212                            GtkAdjustment *adjustment)
213{
214  g_return_if_fail (layout != NULL);
215  g_return_if_fail (GTK_IS_LAYOUT (layout));
216
217  gtk_layout_set_adjustments (layout, adjustment, layout->vadjustment);
218}
219 
220
221void           
222gtk_layout_set_vadjustment (GtkLayout     *layout,
223                            GtkAdjustment *adjustment)
224{
225  g_return_if_fail (layout != NULL);
226  g_return_if_fail (GTK_IS_LAYOUT (layout));
227 
228  gtk_layout_set_adjustments (layout, layout->hadjustment, adjustment);
229}
230
231
232void           
233gtk_layout_put (GtkLayout     *layout,
234                GtkWidget     *child_widget,
235                gint           x,
236                gint           y)
237{
238  GtkLayoutChild *child;
239
240  g_return_if_fail (layout != NULL);
241  g_return_if_fail (GTK_IS_LAYOUT (layout));
242  g_return_if_fail (child_widget != NULL);
243  g_return_if_fail (GTK_IS_WIDGET (child_widget));
244 
245  child = g_new (GtkLayoutChild, 1);
246
247  child->widget = child_widget;
248  child->x = x;
249  child->y = y;
250
251  layout->children = g_list_append (layout->children, child);
252 
253  gtk_widget_set_parent (child_widget, GTK_WIDGET (layout));
254  if (GTK_WIDGET_REALIZED (layout))
255    gtk_widget_set_parent_window (child->widget, layout->bin_window);
256
257  if (!IS_ONSCREEN (x, y))
258    GTK_PRIVATE_SET_FLAG (child_widget, GTK_IS_OFFSCREEN);
259
260  if (GTK_WIDGET_REALIZED (layout))
261    gtk_widget_realize (child_widget);
262   
263  if (GTK_WIDGET_VISIBLE (layout) && GTK_WIDGET_VISIBLE (child_widget))
264    {
265      if (GTK_WIDGET_MAPPED (layout))
266        gtk_widget_map (child_widget);
267
268      gtk_widget_queue_resize (child_widget);
269    }
270}
271
272void           
273gtk_layout_move (GtkLayout     *layout,
274                 GtkWidget     *child_widget,
275                 gint           x,
276                 gint           y)
277{
278  GList *tmp_list;
279  GtkLayoutChild *child;
280 
281  g_return_if_fail (layout != NULL);
282  g_return_if_fail (GTK_IS_LAYOUT (layout));
283
284  tmp_list = layout->children;
285  while (tmp_list)
286    {
287      child = tmp_list->data;
288      tmp_list = tmp_list->next;
289
290      if (child->widget == child_widget)
291        {
292          child->x = x;
293          child->y = y;
294
295          if (GTK_WIDGET_VISIBLE (child_widget) && GTK_WIDGET_VISIBLE (layout))
296            gtk_widget_queue_resize (child_widget);
297
298          return;
299        }
300    }
301}
302
303void
304gtk_layout_set_size (GtkLayout     *layout,
305                     guint          width,
306                     guint          height)
307{
308  g_return_if_fail (layout != NULL);
309  g_return_if_fail (GTK_IS_LAYOUT (layout));
310
311  layout->width = width;
312  layout->height = height;
313
314  layout->hadjustment->upper = layout->width;
315  gtk_signal_emit_by_name (GTK_OBJECT (layout->hadjustment), "changed");
316
317  layout->vadjustment->upper = layout->height;
318  gtk_signal_emit_by_name (GTK_OBJECT (layout->vadjustment), "changed");
319}
320
321void
322gtk_layout_freeze (GtkLayout *layout)
323{
324  g_return_if_fail (layout != NULL);
325  g_return_if_fail (GTK_IS_LAYOUT (layout));
326
327  layout->freeze_count++;
328}
329
330void
331gtk_layout_thaw (GtkLayout *layout)
332{
333  g_return_if_fail (layout != NULL);
334  g_return_if_fail (GTK_IS_LAYOUT (layout));
335
336  if (layout->freeze_count)
337    if (!(--layout->freeze_count))
338      {
339        gtk_layout_position_children (layout);
340        gtk_widget_draw (GTK_WIDGET (layout), NULL);
341      }
342}
343
344/* Basic Object handling procedures
345 */
346GtkType
347gtk_layout_get_type (void)
348{
349  static GtkType layout_type = 0;
350
351  if (!layout_type)
352    {
353      static const GtkTypeInfo layout_info =
354      {
355        "GtkLayout",
356        sizeof (GtkLayout),
357        sizeof (GtkLayoutClass),
358        (GtkClassInitFunc) gtk_layout_class_init,
359        (GtkObjectInitFunc) gtk_layout_init,
360        (GtkArgSetFunc) NULL,
361        (GtkArgGetFunc) NULL,
362      };
363
364      layout_type = gtk_type_unique (GTK_TYPE_CONTAINER, &layout_info);
365    }
366
367  return layout_type;
368}
369
370static void
371gtk_layout_class_init (GtkLayoutClass *class)
372{
373  GtkObjectClass *object_class;
374  GtkWidgetClass *widget_class;
375  GtkContainerClass *container_class;
376
377  object_class = (GtkObjectClass*) class;
378  widget_class = (GtkWidgetClass*) class;
379  container_class = (GtkContainerClass*) class;
380
381  parent_class = gtk_type_class (GTK_TYPE_CONTAINER);
382
383  object_class->finalize = gtk_layout_finalize;
384
385  widget_class->realize = gtk_layout_realize;
386  widget_class->unrealize = gtk_layout_unrealize;
387  widget_class->map = gtk_layout_map;
388  widget_class->size_request = gtk_layout_size_request;
389  widget_class->size_allocate = gtk_layout_size_allocate;
390  widget_class->draw = gtk_layout_draw;
391  widget_class->expose_event = gtk_layout_expose;
392
393  container_class->remove = gtk_layout_remove;
394  container_class->forall = gtk_layout_forall;
395
396  class->set_scroll_adjustments = gtk_layout_set_adjustments;
397
398  widget_class->set_scroll_adjustments_signal =
399    gtk_signal_new ("set_scroll_adjustments",
400                    GTK_RUN_LAST,
401                    object_class->type,
402                    GTK_SIGNAL_OFFSET (GtkLayoutClass, set_scroll_adjustments),
403                    gtk_marshal_NONE__POINTER_POINTER,
404                    GTK_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
405}
406
407static void
408gtk_layout_init (GtkLayout *layout)
409{
410  layout->children = NULL;
411
412  layout->width = 100;
413  layout->height = 100;
414
415  layout->hadjustment = NULL;
416  layout->vadjustment = NULL;
417
418  layout->bin_window = NULL;
419
420  layout->configure_serial = 0;
421  layout->scroll_x = 0;
422  layout->scroll_y = 0;
423  layout->visibility = GDK_VISIBILITY_PARTIAL;
424
425  layout->freeze_count = 0;
426}
427
428/* Widget methods
429 */
430
431static void
432gtk_layout_realize (GtkWidget *widget)
433{
434  GList *tmp_list;
435  GtkLayout *layout;
436  GdkWindowAttr attributes;
437  gint attributes_mask;
438
439  g_return_if_fail (widget != NULL);
440  g_return_if_fail (GTK_IS_LAYOUT (widget));
441
442  layout = GTK_LAYOUT (widget);
443  GTK_WIDGET_SET_FLAGS (layout, GTK_REALIZED);
444
445  attributes.window_type = GDK_WINDOW_CHILD;
446  attributes.x = widget->allocation.x;
447  attributes.y = widget->allocation.y;
448  attributes.width = widget->allocation.width;
449  attributes.height = widget->allocation.height;
450  attributes.wclass = GDK_INPUT_OUTPUT;
451  attributes.visual = gtk_widget_get_visual (widget);
452  attributes.colormap = gtk_widget_get_colormap (widget);
453  attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
454
455  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
456
457  widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
458                                   &attributes, attributes_mask);
459  gdk_window_set_user_data (widget->window, widget);
460
461  attributes.x = 0;
462  attributes.y = 0;
463  attributes.event_mask = GDK_EXPOSURE_MASK |
464                          gtk_widget_get_events (widget);
465
466  layout->bin_window = gdk_window_new (widget->window,
467                                        &attributes, attributes_mask);
468  gdk_window_set_user_data (layout->bin_window, widget);
469
470  widget->style = gtk_style_attach (widget->style, widget->window);
471  gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
472  gtk_style_set_background (widget->style, layout->bin_window, GTK_STATE_NORMAL);
473
474  gdk_window_add_filter (widget->window, gtk_layout_main_filter, layout);
475  /*   gdk_window_add_filter (layout->bin_window, gtk_layout_filter, layout);*/
476
477  /* XXX: If we ever get multiple displays for GTK+, then gravity_works
478   *      will have to become a widget member. Right now we just
479   *      keep it as a global
480   */
481  gravity_works = gdk_window_set_static_gravities (layout->bin_window, TRUE);
482
483  tmp_list = layout->children;
484  while (tmp_list)
485    {
486      GtkLayoutChild *child = tmp_list->data;
487      tmp_list = tmp_list->next;
488
489      gtk_widget_set_parent_window (child->widget, layout->bin_window);
490    }
491}
492
493static void
494gtk_layout_map (GtkWidget *widget)
495{
496  GList *tmp_list;
497  GtkLayout *layout;
498
499  g_return_if_fail (widget != NULL);
500  g_return_if_fail (GTK_IS_LAYOUT (widget));
501
502  layout = GTK_LAYOUT (widget);
503
504  GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
505
506  tmp_list = layout->children;
507  while (tmp_list)
508    {
509      GtkLayoutChild *child = tmp_list->data;
510      tmp_list = tmp_list->next;
511
512      if (GTK_WIDGET_VISIBLE (child->widget))
513        {
514          if (!GTK_WIDGET_MAPPED (child->widget) &&
515              !GTK_WIDGET_IS_OFFSCREEN (child->widget))
516            gtk_widget_map (child->widget);
517        }
518    }
519
520  gdk_window_show (layout->bin_window);
521  gdk_window_show (widget->window);
522}
523
524static void
525gtk_layout_unrealize (GtkWidget *widget)
526{
527  GtkLayout *layout;
528
529  g_return_if_fail (widget != NULL);
530  g_return_if_fail (GTK_IS_LAYOUT (widget));
531
532  layout = GTK_LAYOUT (widget);
533
534  gdk_window_set_user_data (layout->bin_window, NULL);
535  gdk_window_destroy (layout->bin_window);
536  layout->bin_window = NULL;
537
538  if (GTK_WIDGET_CLASS (parent_class)->unrealize)
539    (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
540}
541
542static void     
543gtk_layout_size_request (GtkWidget     *widget,
544                         GtkRequisition *requisition)
545{
546  GList *tmp_list;
547  GtkLayout *layout;
548
549  g_return_if_fail (widget != NULL);
550  g_return_if_fail (GTK_IS_LAYOUT (widget));
551
552  layout = GTK_LAYOUT (widget);
553
554  requisition->width = 0;
555  requisition->height = 0;
556
557  tmp_list = layout->children;
558
559  while (tmp_list)
560    {
561      GtkLayoutChild *child = tmp_list->data;
562      GtkRequisition child_requisition;
563     
564      tmp_list = tmp_list->next;
565
566      gtk_widget_size_request (child->widget, &child_requisition);
567    }
568}
569
570static void     
571gtk_layout_size_allocate (GtkWidget     *widget,
572                          GtkAllocation *allocation)
573{
574  GList *tmp_list;
575  GtkLayout *layout;
576
577  g_return_if_fail (widget != NULL);
578  g_return_if_fail (GTK_IS_LAYOUT (widget));
579
580  widget->allocation = *allocation;
581 
582  layout = GTK_LAYOUT (widget);
583
584  tmp_list = layout->children;
585
586  while (tmp_list)
587    {
588      GtkLayoutChild *child = tmp_list->data;
589      tmp_list = tmp_list->next;
590
591      gtk_layout_position_child (layout, child);
592      gtk_layout_allocate_child (layout, child);
593    }
594
595  if (GTK_WIDGET_REALIZED (widget))
596    {
597      gdk_window_move_resize (widget->window,
598                              allocation->x, allocation->y,
599                              allocation->width, allocation->height);
600      gdk_window_move_resize (GTK_LAYOUT(widget)->bin_window,
601                              0, 0,
602                              allocation->width, allocation->height);
603    }
604
605  layout->hadjustment->page_size = allocation->width;
606  layout->hadjustment->page_increment = allocation->width / 2;
607  layout->hadjustment->lower = 0;
608  layout->hadjustment->upper = layout->width;
609  gtk_signal_emit_by_name (GTK_OBJECT (layout->hadjustment), "changed");
610
611  layout->vadjustment->page_size = allocation->height;
612  layout->vadjustment->page_increment = allocation->height / 2;
613  layout->vadjustment->lower = 0;
614  layout->vadjustment->upper = layout->height;
615  gtk_signal_emit_by_name (GTK_OBJECT (layout->vadjustment), "changed");
616}
617
618static void
619gtk_layout_draw (GtkWidget *widget, GdkRectangle *area)
620{
621  GList *tmp_list;
622  GtkLayout *layout;
623  GdkRectangle child_area;
624
625  g_return_if_fail (widget != NULL);
626  g_return_if_fail (GTK_IS_LAYOUT (widget));
627
628  layout = GTK_LAYOUT (widget);
629
630  /* We don't have any way of telling themes about this properly,
631   * so we just assume a background pixmap
632   */
633  if (!GTK_WIDGET_APP_PAINTABLE (widget))
634    gdk_window_clear_area (layout->bin_window,
635                           area->x, area->y, area->width, area->height);
636 
637  tmp_list = layout->children;
638  while (tmp_list)
639    {
640      GtkLayoutChild *child = tmp_list->data;
641      tmp_list = tmp_list->next;
642
643      if (gtk_widget_intersect (child->widget, area, &child_area))
644        gtk_widget_draw (child->widget, &child_area);
645    }
646}
647
648static gint
649gtk_layout_expose (GtkWidget *widget, GdkEventExpose *event)
650{
651  GList *tmp_list;
652  GtkLayout *layout;
653  GdkEventExpose child_event;
654
655  g_return_val_if_fail (widget != NULL, FALSE);
656  g_return_val_if_fail (GTK_IS_LAYOUT (widget), FALSE);
657
658  layout = GTK_LAYOUT (widget);
659
660  if (event->window != layout->bin_window)
661    return FALSE;
662 
663  tmp_list = layout->children;
664  while (tmp_list)
665    {
666      GtkLayoutChild *child = tmp_list->data;
667      tmp_list = tmp_list->next;
668
669      child_event = *event;
670      if (GTK_WIDGET_DRAWABLE (child->widget) &&
671          GTK_WIDGET_NO_WINDOW (child->widget) &&
672          gtk_widget_intersect (child->widget, &event->area, &child_event.area))
673        gtk_widget_event (child->widget, (GdkEvent*) &child_event);
674    }
675
676  return FALSE;
677}
678
679/* Container method
680 */
681static void
682gtk_layout_remove (GtkContainer *container,
683                   GtkWidget    *widget)
684{
685  GList *tmp_list;
686  GtkLayout *layout;
687  GtkLayoutChild *child = NULL;
688 
689  g_return_if_fail (container != NULL);
690  g_return_if_fail (GTK_IS_LAYOUT (container));
691 
692  layout = GTK_LAYOUT (container);
693
694  tmp_list = layout->children;
695  while (tmp_list)
696    {
697      child = tmp_list->data;
698      if (child->widget == widget)
699        break;
700      tmp_list = tmp_list->next;
701    }
702
703  if (tmp_list)
704    {
705      GTK_PRIVATE_UNSET_FLAG (widget, GTK_IS_OFFSCREEN);
706
707      gtk_widget_unparent (widget);
708
709      layout->children = g_list_remove_link (layout->children, tmp_list);
710      g_list_free_1 (tmp_list);
711      g_free (child);
712    }
713}
714
715static void
716gtk_layout_forall (GtkContainer *container,
717                   gboolean      include_internals,
718                   GtkCallback   callback,
719                   gpointer      callback_data)
720{
721  GtkLayout *layout;
722  GtkLayoutChild *child;
723  GList *tmp_list;
724
725  g_return_if_fail (container != NULL);
726  g_return_if_fail (GTK_IS_LAYOUT (container));
727  g_return_if_fail (callback != NULL);
728
729  layout = GTK_LAYOUT (container);
730
731  tmp_list = layout->children;
732  while (tmp_list)
733    {
734      child = tmp_list->data;
735      tmp_list = tmp_list->next;
736
737      (* callback) (child->widget, callback_data);
738    }
739}
740
741/* Operations on children
742 */
743
744static void
745gtk_layout_position_child (GtkLayout      *layout,
746                           GtkLayoutChild *child)
747{
748  gint x;
749  gint y;
750
751  x = child->x - layout->xoffset;
752  y = child->y - layout->yoffset;
753
754  if (IS_ONSCREEN (x,y))
755    {
756      if (GTK_WIDGET_MAPPED (layout) &&
757          GTK_WIDGET_VISIBLE (child->widget))
758        {
759          if (!GTK_WIDGET_MAPPED (child->widget))
760            gtk_widget_map (child->widget);
761        }
762
763      if (GTK_WIDGET_IS_OFFSCREEN (child->widget))
764        GTK_PRIVATE_UNSET_FLAG (child->widget, GTK_IS_OFFSCREEN);
765    }
766  else
767    {
768      if (!GTK_WIDGET_IS_OFFSCREEN (child->widget))
769        GTK_PRIVATE_SET_FLAG (child->widget, GTK_IS_OFFSCREEN);
770
771      if (GTK_WIDGET_MAPPED (child->widget))
772        gtk_widget_unmap (child->widget);
773    }
774}
775
776static void
777gtk_layout_allocate_child (GtkLayout      *layout,
778                           GtkLayoutChild *child)
779{
780  GtkAllocation allocation;
781  GtkRequisition requisition;
782
783  allocation.x = child->x - layout->xoffset;
784  allocation.y = child->y - layout->yoffset;
785  gtk_widget_get_child_requisition (child->widget, &requisition);
786  allocation.width = requisition.width;
787  allocation.height = requisition.height;
788 
789  gtk_widget_size_allocate (child->widget, &allocation);
790}
791
792static void
793gtk_layout_position_children (GtkLayout *layout)
794{
795  GList *tmp_list;
796
797  tmp_list = layout->children;
798  while (tmp_list)
799    {
800      GtkLayoutChild *child = tmp_list->data;
801      tmp_list = tmp_list->next;
802     
803      gtk_layout_position_child (layout, child);
804    }
805}
806
807static void
808gtk_layout_adjust_allocations_recurse (GtkWidget *widget,
809                                       gpointer   cb_data)
810{
811  GtkLayoutAdjData *data = cb_data;
812
813  widget->allocation.x += data->dx;
814  widget->allocation.y += data->dy;
815
816  if (GTK_WIDGET_NO_WINDOW (widget) &&
817      GTK_IS_CONTAINER (widget))
818    gtk_container_forall (GTK_CONTAINER (widget),
819                          gtk_layout_adjust_allocations_recurse,
820                          cb_data);
821}
822
823static void
824gtk_layout_adjust_allocations (GtkLayout *layout,
825                               gint       dx,
826                               gint       dy)
827{
828  GList *tmp_list;
829  GtkLayoutAdjData data;
830
831  data.dx = dx;
832  data.dy = dy;
833
834  tmp_list = layout->children;
835  while (tmp_list)
836    {
837      GtkLayoutChild *child = tmp_list->data;
838      tmp_list = tmp_list->next;
839     
840      child->widget->allocation.x += dx;
841      child->widget->allocation.y += dy;
842
843      if (GTK_WIDGET_NO_WINDOW (child->widget) &&
844          GTK_IS_CONTAINER (child->widget))
845        gtk_container_forall (GTK_CONTAINER (child->widget),
846                              gtk_layout_adjust_allocations_recurse,
847                              &data);
848    }
849}
850 
851/* Callbacks */
852
853/* Send a synthetic expose event to the widget
854 */
855static void
856gtk_layout_expose_area (GtkLayout    *layout,
857                        gint x, gint y, gint width, gint height)
858{
859  if (layout->visibility == GDK_VISIBILITY_UNOBSCURED)
860    {
861      GdkEventExpose event;
862     
863      event.type = GDK_EXPOSE;
864      event.send_event = TRUE;
865      event.window = layout->bin_window;
866      event.count = 0;
867     
868      event.area.x = x;
869      event.area.y = y;
870      event.area.width = width;
871      event.area.height = height;
872     
873      gdk_window_ref (event.window);
874      gtk_widget_event (GTK_WIDGET (layout), (GdkEvent *)&event);
875      gdk_window_unref (event.window);
876    }
877}
878
879/* This function is used to find events to process while scrolling
880 */
881
882static Bool
883gtk_layout_expose_predicate (Display *display,
884                  XEvent  *xevent,
885                  XPointer arg)
886{
887  if ((xevent->type == Expose) ||
888      ((xevent->xany.window == *(Window *)arg) &&
889       (xevent->type == ConfigureNotify)))
890    return True;
891  else
892    return False;
893}
894
895/* This is the main routine to do the scrolling. Scrolling is
896 * done by "Guffaw" scrolling, as in the Mozilla XFE, with
897 * a few modifications.
898 *
899 * The main improvement is that we keep track of whether we
900 * are obscured or not. If not, we ignore the generated expose
901 * events and instead do the exposes ourself, without having
902 * to wait for a roundtrip to the server. This also provides
903 * a limited form of expose-event compression, since we do
904 * the affected area as one big chunk.
905 *
906 * Real expose event compression, as in the XFE, could be added
907 * here. It would help opaque drags over the region, and the
908 * obscured case.
909 *
910 * Code needs to be added here to do the scrolling on machines
911 * that don't have working WindowGravity. That could be done
912 *
913 *  - XCopyArea and move the windows, and accept trailing the
914 *    background color. (Since it is only a fallback method)
915 *  - XmHTML style. As above, but turn off expose events when
916 *    not obscured and do the exposures ourself.
917 *  - gzilla-style. Move the window continuously, and reset
918 *    every 32768 pixels
919 */
920
921static void
922gtk_layout_adjustment_changed (GtkAdjustment *adjustment,
923                               GtkLayout     *layout)
924{
925  GtkWidget *widget;
926  XEvent xevent;
927 
928  gint dx, dy;
929
930  widget = GTK_WIDGET (layout);
931
932  dx = (gint)layout->hadjustment->value - layout->xoffset;
933  dy = (gint)layout->vadjustment->value - layout->yoffset;
934
935  layout->xoffset = (gint)layout->hadjustment->value;
936  layout->yoffset = (gint)layout->vadjustment->value;
937
938  if (layout->freeze_count)
939    return;
940
941  if (!GTK_WIDGET_MAPPED (layout))
942    {
943      gtk_layout_position_children (layout);
944      return;
945    }
946
947  gtk_layout_adjust_allocations (layout, -dx, -dy);
948
949  if (dx > 0)
950    {
951      if (gravity_works)
952        {
953          gdk_window_resize (layout->bin_window,
954                             widget->allocation.width + dx,
955                             widget->allocation.height);
956          gdk_window_move   (layout->bin_window, -dx, 0);
957          gdk_window_move_resize (layout->bin_window,
958                                  0, 0,
959                                  widget->allocation.width,
960                                  widget->allocation.height);
961        }
962      else
963        {
964          /* FIXME */
965        }
966
967      gtk_layout_expose_area (layout,
968                              MAX ((gint)widget->allocation.width - dx, 0),
969                              0,
970                              MIN (dx, widget->allocation.width),
971                              widget->allocation.height);
972    }
973  else if (dx < 0)
974    {
975      if (gravity_works)
976        {
977          gdk_window_move_resize (layout->bin_window,
978                                  dx, 0,
979                                  widget->allocation.width - dx,
980                                  widget->allocation.height);
981          gdk_window_move   (layout->bin_window, 0, 0);
982          gdk_window_resize (layout->bin_window,
983                             widget->allocation.width,
984                             widget->allocation.height);
985        }
986      else
987        {
988          /* FIXME */
989        }
990
991      gtk_layout_expose_area (layout,
992                              0,
993                              0,
994                              MIN (-dx, widget->allocation.width),
995                              widget->allocation.height);
996    }
997
998  if (dy > 0)
999    {
1000      if (gravity_works)
1001        {
1002          gdk_window_resize (layout->bin_window,
1003                             widget->allocation.width,
1004                             widget->allocation.height + dy);
1005          gdk_window_move   (layout->bin_window, 0, -dy);
1006          gdk_window_move_resize (layout->bin_window,
1007                                  0, 0,
1008                                  widget->allocation.width,
1009                                  widget->allocation.height);
1010        }
1011      else
1012        {
1013          /* FIXME */
1014        }
1015
1016      gtk_layout_expose_area (layout,
1017                              0,
1018                              MAX ((gint)widget->allocation.height - dy, 0),
1019                              widget->allocation.width,
1020                              MIN (dy, widget->allocation.height));
1021    }
1022  else if (dy < 0)
1023    {
1024      if (gravity_works)
1025        {
1026          gdk_window_move_resize (layout->bin_window,
1027                                  0, dy,
1028                                  widget->allocation.width,
1029                                  widget->allocation.height - dy);
1030          gdk_window_move   (layout->bin_window, 0, 0);
1031          gdk_window_resize (layout->bin_window,
1032                             widget->allocation.width,
1033                             widget->allocation.height);
1034        }
1035      else
1036        {
1037          /* FIXME */
1038        }
1039      gtk_layout_expose_area (layout,
1040                              0,
1041                              0,
1042                              widget->allocation.width,
1043                              MIN (-dy, (gint)widget->allocation.height));
1044    }
1045
1046  gtk_layout_position_children (layout);
1047
1048  /* We have to make sure that all exposes from this scroll get
1049   * processed before we scroll again, or the expose events will
1050   * have invalid coordinates.
1051   *
1052   * We also do expose events for other windows, since otherwise
1053   * their updating will fall behind the scrolling
1054   *
1055   * This also avoids a problem in pre-1.0 GTK where filters don't
1056   * have access to configure events that were compressed.
1057   */
1058
1059  gdk_flush();
1060  while (XCheckIfEvent(GDK_WINDOW_XDISPLAY (layout->bin_window),
1061                       &xevent,
1062                       gtk_layout_expose_predicate,
1063                       (XPointer)&GDK_WINDOW_XWINDOW (layout->bin_window)))
1064    {
1065      GdkEvent event;
1066      GtkWidget *event_widget;
1067
1068      switch (xevent.type)
1069        {
1070        case Expose:
1071
1072          if (xevent.xany.window == GDK_WINDOW_XWINDOW (layout->bin_window))
1073            {
1074              /* If the window is unobscured, then we've exposed the
1075               * regions with the following serials already, so we
1076               * can throw out the expose events.
1077               */
1078              if (layout->visibility == GDK_VISIBILITY_UNOBSCURED &&
1079                  (((dx > 0 || dy > 0) &&
1080                    xevent.xexpose.serial == layout->configure_serial) ||
1081                   ((dx < 0 || dy < 0) &&
1082                    xevent.xexpose.serial == layout->configure_serial + 1)))
1083                continue;
1084              /* The following expose was generated while the origin was
1085               * different from the current origin, so we need to offset it.
1086               */
1087              else if (xevent.xexpose.serial == layout->configure_serial)
1088                {
1089                  xevent.xexpose.x += layout->scroll_x;
1090                  xevent.xexpose.y += layout->scroll_y;
1091                }
1092              event.expose.window = layout->bin_window;
1093              event_widget = widget;
1094            }
1095          else
1096            {
1097              event.expose.window = gdk_window_lookup (xevent.xany.window);
1098              gdk_window_get_user_data (event.expose.window,
1099                                        (gpointer *)&event_widget);
1100            }
1101
1102          if (event_widget)
1103            {
1104              event.expose.type = GDK_EXPOSE;
1105              event.expose.area.x = xevent.xexpose.x;
1106              event.expose.area.y = xevent.xexpose.y;
1107              event.expose.area.width = xevent.xexpose.width;
1108              event.expose.area.height = xevent.xexpose.height;
1109              event.expose.count = xevent.xexpose.count;
1110             
1111              gdk_window_ref (event.expose.window);
1112              gtk_widget_event (event_widget, &event);
1113              gdk_window_unref (event.expose.window);
1114            }
1115          break;
1116     
1117        case ConfigureNotify:
1118          if (xevent.xany.window == GDK_WINDOW_XWINDOW (layout->bin_window) &&
1119              (xevent.xconfigure.x != 0 || xevent.xconfigure.y != 0))
1120            {
1121              layout->configure_serial = xevent.xconfigure.serial;
1122              layout->scroll_x = xevent.xconfigure.x;
1123              layout->scroll_y = xevent.xconfigure.y;
1124            }
1125          break;
1126        }
1127    }
1128}
1129
1130#if 0
1131/* The main event filter. Actually, we probably don't really need
1132 * thisfilter at all, since we are calling it directly above in the
1133 * expose-handling hack. But in case scrollbars
1134 * are fixed up in some manner...
1135 *
1136 * This routine identifies expose events that are generated when
1137 * we've temporarily moved the bin_window_origin, and translates
1138 * them or discards them, depending on whether we are obscured
1139 * or not.
1140 */
1141static GdkFilterReturn
1142gtk_layout_filter (GdkXEvent *gdk_xevent,
1143                   GdkEvent  *event,
1144                   gpointer   data)
1145{
1146  XEvent *xevent;
1147  GtkLayout *layout;
1148
1149  xevent = (XEvent *)gdk_xevent;
1150  layout = GTK_LAYOUT (data);
1151
1152  switch (xevent->type)
1153    {
1154    case Expose:
1155      if (xevent->xexpose.serial == layout->configure_serial)
1156        {
1157          if (layout->visibility == GDK_VISIBILITY_UNOBSCURED)
1158            return GDK_FILTER_REMOVE;
1159          else
1160            {
1161              xevent->xexpose.x += layout->scroll_x;
1162              xevent->xexpose.y += layout->scroll_y;
1163             
1164              break;
1165            }
1166        }
1167      break;
1168     
1169    case ConfigureNotify:
1170       if ((xevent->xconfigure.x != 0) || (xevent->xconfigure.y != 0))
1171        {
1172          layout->configure_serial = xevent->xconfigure.serial;
1173          layout->scroll_x = xevent->xconfigure.x;
1174          layout->scroll_y = xevent->xconfigure.y;
1175        }
1176      break;
1177    }
1178 
1179  return GDK_FILTER_CONTINUE;
1180}
1181#endif 0
1182
1183/* Although GDK does have a GDK_VISIBILITY_NOTIFY event,
1184 * there is no corresponding event in GTK, so we have
1185 * to get the events from a filter
1186 */
1187static GdkFilterReturn
1188gtk_layout_main_filter (GdkXEvent *gdk_xevent,
1189                        GdkEvent  *event,
1190                        gpointer   data)
1191{
1192  XEvent *xevent;
1193  GtkLayout *layout;
1194
1195  xevent = (XEvent *)gdk_xevent;
1196  layout = GTK_LAYOUT (data);
1197
1198  if (xevent->type == VisibilityNotify)
1199    {
1200      switch (xevent->xvisibility.state)
1201        {
1202        case VisibilityFullyObscured:
1203          layout->visibility = GDK_VISIBILITY_FULLY_OBSCURED;
1204          break;
1205
1206        case VisibilityPartiallyObscured:
1207          layout->visibility = GDK_VISIBILITY_PARTIAL;
1208          break;
1209
1210        case VisibilityUnobscured:
1211          layout->visibility = GDK_VISIBILITY_UNOBSCURED;
1212          break;
1213        }
1214
1215      return GDK_FILTER_REMOVE;
1216    }
1217
1218 
1219  return GDK_FILTER_CONTINUE;
1220}
1221
Note: See TracBrowser for help on using the repository browser.