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

Revision 15781, 31.6 KB checked in by ghudson, 24 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r15780, which included commits to RCS files with non-trunk default branches.
RevLine 
[14481]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,
[15780]360        /* reserved_1 */ NULL,
361        /* reserved_2 */ NULL,
362        (GtkClassInitFunc) NULL,
[14481]363      };
364
365      layout_type = gtk_type_unique (GTK_TYPE_CONTAINER, &layout_info);
366    }
367
368  return layout_type;
369}
370
371static void
372gtk_layout_class_init (GtkLayoutClass *class)
373{
374  GtkObjectClass *object_class;
375  GtkWidgetClass *widget_class;
376  GtkContainerClass *container_class;
377
378  object_class = (GtkObjectClass*) class;
379  widget_class = (GtkWidgetClass*) class;
380  container_class = (GtkContainerClass*) class;
381
382  parent_class = gtk_type_class (GTK_TYPE_CONTAINER);
383
384  object_class->finalize = gtk_layout_finalize;
385
386  widget_class->realize = gtk_layout_realize;
387  widget_class->unrealize = gtk_layout_unrealize;
388  widget_class->map = gtk_layout_map;
389  widget_class->size_request = gtk_layout_size_request;
390  widget_class->size_allocate = gtk_layout_size_allocate;
391  widget_class->draw = gtk_layout_draw;
392  widget_class->expose_event = gtk_layout_expose;
393
394  container_class->remove = gtk_layout_remove;
395  container_class->forall = gtk_layout_forall;
396
397  class->set_scroll_adjustments = gtk_layout_set_adjustments;
398
399  widget_class->set_scroll_adjustments_signal =
400    gtk_signal_new ("set_scroll_adjustments",
401                    GTK_RUN_LAST,
402                    object_class->type,
403                    GTK_SIGNAL_OFFSET (GtkLayoutClass, set_scroll_adjustments),
404                    gtk_marshal_NONE__POINTER_POINTER,
405                    GTK_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
406}
407
408static void
409gtk_layout_init (GtkLayout *layout)
410{
411  layout->children = NULL;
412
413  layout->width = 100;
414  layout->height = 100;
415
416  layout->hadjustment = NULL;
417  layout->vadjustment = NULL;
418
419  layout->bin_window = NULL;
420
421  layout->configure_serial = 0;
422  layout->scroll_x = 0;
423  layout->scroll_y = 0;
424  layout->visibility = GDK_VISIBILITY_PARTIAL;
425
426  layout->freeze_count = 0;
427}
428
429/* Widget methods
430 */
431
432static void
433gtk_layout_realize (GtkWidget *widget)
434{
435  GList *tmp_list;
436  GtkLayout *layout;
437  GdkWindowAttr attributes;
438  gint attributes_mask;
439
440  g_return_if_fail (widget != NULL);
441  g_return_if_fail (GTK_IS_LAYOUT (widget));
442
443  layout = GTK_LAYOUT (widget);
444  GTK_WIDGET_SET_FLAGS (layout, GTK_REALIZED);
445
446  attributes.window_type = GDK_WINDOW_CHILD;
447  attributes.x = widget->allocation.x;
448  attributes.y = widget->allocation.y;
449  attributes.width = widget->allocation.width;
450  attributes.height = widget->allocation.height;
451  attributes.wclass = GDK_INPUT_OUTPUT;
452  attributes.visual = gtk_widget_get_visual (widget);
453  attributes.colormap = gtk_widget_get_colormap (widget);
454  attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
455
456  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
457
458  widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
459                                   &attributes, attributes_mask);
460  gdk_window_set_user_data (widget->window, widget);
461
462  attributes.x = 0;
463  attributes.y = 0;
464  attributes.event_mask = GDK_EXPOSURE_MASK |
465                          gtk_widget_get_events (widget);
466
467  layout->bin_window = gdk_window_new (widget->window,
468                                        &attributes, attributes_mask);
469  gdk_window_set_user_data (layout->bin_window, widget);
470
471  widget->style = gtk_style_attach (widget->style, widget->window);
472  gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
473  gtk_style_set_background (widget->style, layout->bin_window, GTK_STATE_NORMAL);
474
475  gdk_window_add_filter (widget->window, gtk_layout_main_filter, layout);
476  /*   gdk_window_add_filter (layout->bin_window, gtk_layout_filter, layout);*/
477
478  /* XXX: If we ever get multiple displays for GTK+, then gravity_works
479   *      will have to become a widget member. Right now we just
480   *      keep it as a global
481   */
482  gravity_works = gdk_window_set_static_gravities (layout->bin_window, TRUE);
483
484  tmp_list = layout->children;
485  while (tmp_list)
486    {
487      GtkLayoutChild *child = tmp_list->data;
488      tmp_list = tmp_list->next;
489
490      gtk_widget_set_parent_window (child->widget, layout->bin_window);
491    }
492}
493
494static void
495gtk_layout_map (GtkWidget *widget)
496{
497  GList *tmp_list;
498  GtkLayout *layout;
499
500  g_return_if_fail (widget != NULL);
501  g_return_if_fail (GTK_IS_LAYOUT (widget));
502
503  layout = GTK_LAYOUT (widget);
504
505  GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
506
507  tmp_list = layout->children;
508  while (tmp_list)
509    {
510      GtkLayoutChild *child = tmp_list->data;
511      tmp_list = tmp_list->next;
512
513      if (GTK_WIDGET_VISIBLE (child->widget))
514        {
515          if (!GTK_WIDGET_MAPPED (child->widget) &&
516              !GTK_WIDGET_IS_OFFSCREEN (child->widget))
517            gtk_widget_map (child->widget);
518        }
519    }
520
521  gdk_window_show (layout->bin_window);
522  gdk_window_show (widget->window);
523}
524
525static void
526gtk_layout_unrealize (GtkWidget *widget)
527{
528  GtkLayout *layout;
529
530  g_return_if_fail (widget != NULL);
531  g_return_if_fail (GTK_IS_LAYOUT (widget));
532
533  layout = GTK_LAYOUT (widget);
534
535  gdk_window_set_user_data (layout->bin_window, NULL);
536  gdk_window_destroy (layout->bin_window);
537  layout->bin_window = NULL;
538
539  if (GTK_WIDGET_CLASS (parent_class)->unrealize)
540    (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
541}
542
543static void     
544gtk_layout_size_request (GtkWidget     *widget,
545                         GtkRequisition *requisition)
546{
547  GList *tmp_list;
548  GtkLayout *layout;
549
550  g_return_if_fail (widget != NULL);
551  g_return_if_fail (GTK_IS_LAYOUT (widget));
552
553  layout = GTK_LAYOUT (widget);
554
555  requisition->width = 0;
556  requisition->height = 0;
557
558  tmp_list = layout->children;
559
560  while (tmp_list)
561    {
562      GtkLayoutChild *child = tmp_list->data;
563      GtkRequisition child_requisition;
564     
565      tmp_list = tmp_list->next;
566
567      gtk_widget_size_request (child->widget, &child_requisition);
568    }
569}
570
571static void     
572gtk_layout_size_allocate (GtkWidget     *widget,
573                          GtkAllocation *allocation)
574{
575  GList *tmp_list;
576  GtkLayout *layout;
577
578  g_return_if_fail (widget != NULL);
579  g_return_if_fail (GTK_IS_LAYOUT (widget));
580
581  widget->allocation = *allocation;
582 
583  layout = GTK_LAYOUT (widget);
584
585  tmp_list = layout->children;
586
587  while (tmp_list)
588    {
589      GtkLayoutChild *child = tmp_list->data;
590      tmp_list = tmp_list->next;
591
592      gtk_layout_position_child (layout, child);
593      gtk_layout_allocate_child (layout, child);
594    }
595
596  if (GTK_WIDGET_REALIZED (widget))
597    {
598      gdk_window_move_resize (widget->window,
599                              allocation->x, allocation->y,
600                              allocation->width, allocation->height);
601      gdk_window_move_resize (GTK_LAYOUT(widget)->bin_window,
602                              0, 0,
603                              allocation->width, allocation->height);
604    }
605
606  layout->hadjustment->page_size = allocation->width;
607  layout->hadjustment->page_increment = allocation->width / 2;
608  layout->hadjustment->lower = 0;
609  layout->hadjustment->upper = layout->width;
610  gtk_signal_emit_by_name (GTK_OBJECT (layout->hadjustment), "changed");
611
612  layout->vadjustment->page_size = allocation->height;
613  layout->vadjustment->page_increment = allocation->height / 2;
614  layout->vadjustment->lower = 0;
615  layout->vadjustment->upper = layout->height;
616  gtk_signal_emit_by_name (GTK_OBJECT (layout->vadjustment), "changed");
617}
618
619static void
620gtk_layout_draw (GtkWidget *widget, GdkRectangle *area)
621{
622  GList *tmp_list;
623  GtkLayout *layout;
624  GdkRectangle child_area;
625
626  g_return_if_fail (widget != NULL);
627  g_return_if_fail (GTK_IS_LAYOUT (widget));
628
629  layout = GTK_LAYOUT (widget);
630
631  /* We don't have any way of telling themes about this properly,
632   * so we just assume a background pixmap
633   */
634  if (!GTK_WIDGET_APP_PAINTABLE (widget))
635    gdk_window_clear_area (layout->bin_window,
636                           area->x, area->y, area->width, area->height);
637 
638  tmp_list = layout->children;
639  while (tmp_list)
640    {
641      GtkLayoutChild *child = tmp_list->data;
642      tmp_list = tmp_list->next;
643
644      if (gtk_widget_intersect (child->widget, area, &child_area))
645        gtk_widget_draw (child->widget, &child_area);
646    }
647}
648
649static gint
650gtk_layout_expose (GtkWidget *widget, GdkEventExpose *event)
651{
652  GList *tmp_list;
653  GtkLayout *layout;
654  GdkEventExpose child_event;
655
656  g_return_val_if_fail (widget != NULL, FALSE);
657  g_return_val_if_fail (GTK_IS_LAYOUT (widget), FALSE);
658
659  layout = GTK_LAYOUT (widget);
660
661  if (event->window != layout->bin_window)
662    return FALSE;
663 
664  tmp_list = layout->children;
665  while (tmp_list)
666    {
667      GtkLayoutChild *child = tmp_list->data;
668      tmp_list = tmp_list->next;
669
670      child_event = *event;
671      if (GTK_WIDGET_DRAWABLE (child->widget) &&
672          GTK_WIDGET_NO_WINDOW (child->widget) &&
673          gtk_widget_intersect (child->widget, &event->area, &child_event.area))
674        gtk_widget_event (child->widget, (GdkEvent*) &child_event);
675    }
676
677  return FALSE;
678}
679
680/* Container method
681 */
682static void
683gtk_layout_remove (GtkContainer *container,
684                   GtkWidget    *widget)
685{
686  GList *tmp_list;
687  GtkLayout *layout;
688  GtkLayoutChild *child = NULL;
689 
690  g_return_if_fail (container != NULL);
691  g_return_if_fail (GTK_IS_LAYOUT (container));
692 
693  layout = GTK_LAYOUT (container);
694
695  tmp_list = layout->children;
696  while (tmp_list)
697    {
698      child = tmp_list->data;
699      if (child->widget == widget)
700        break;
701      tmp_list = tmp_list->next;
702    }
703
704  if (tmp_list)
705    {
706      GTK_PRIVATE_UNSET_FLAG (widget, GTK_IS_OFFSCREEN);
707
708      gtk_widget_unparent (widget);
709
710      layout->children = g_list_remove_link (layout->children, tmp_list);
711      g_list_free_1 (tmp_list);
712      g_free (child);
713    }
714}
715
716static void
717gtk_layout_forall (GtkContainer *container,
718                   gboolean      include_internals,
719                   GtkCallback   callback,
720                   gpointer      callback_data)
721{
722  GtkLayout *layout;
723  GtkLayoutChild *child;
724  GList *tmp_list;
725
726  g_return_if_fail (container != NULL);
727  g_return_if_fail (GTK_IS_LAYOUT (container));
728  g_return_if_fail (callback != NULL);
729
730  layout = GTK_LAYOUT (container);
731
732  tmp_list = layout->children;
733  while (tmp_list)
734    {
735      child = tmp_list->data;
736      tmp_list = tmp_list->next;
737
738      (* callback) (child->widget, callback_data);
739    }
740}
741
742/* Operations on children
743 */
744
745static void
746gtk_layout_position_child (GtkLayout      *layout,
747                           GtkLayoutChild *child)
748{
749  gint x;
750  gint y;
751
752  x = child->x - layout->xoffset;
753  y = child->y - layout->yoffset;
754
755  if (IS_ONSCREEN (x,y))
756    {
757      if (GTK_WIDGET_MAPPED (layout) &&
758          GTK_WIDGET_VISIBLE (child->widget))
759        {
760          if (!GTK_WIDGET_MAPPED (child->widget))
761            gtk_widget_map (child->widget);
762        }
763
764      if (GTK_WIDGET_IS_OFFSCREEN (child->widget))
765        GTK_PRIVATE_UNSET_FLAG (child->widget, GTK_IS_OFFSCREEN);
766    }
767  else
768    {
769      if (!GTK_WIDGET_IS_OFFSCREEN (child->widget))
770        GTK_PRIVATE_SET_FLAG (child->widget, GTK_IS_OFFSCREEN);
771
772      if (GTK_WIDGET_MAPPED (child->widget))
773        gtk_widget_unmap (child->widget);
774    }
775}
776
777static void
778gtk_layout_allocate_child (GtkLayout      *layout,
779                           GtkLayoutChild *child)
780{
781  GtkAllocation allocation;
782  GtkRequisition requisition;
783
784  allocation.x = child->x - layout->xoffset;
785  allocation.y = child->y - layout->yoffset;
786  gtk_widget_get_child_requisition (child->widget, &requisition);
787  allocation.width = requisition.width;
788  allocation.height = requisition.height;
789 
790  gtk_widget_size_allocate (child->widget, &allocation);
791}
792
793static void
794gtk_layout_position_children (GtkLayout *layout)
795{
796  GList *tmp_list;
797
798  tmp_list = layout->children;
799  while (tmp_list)
800    {
801      GtkLayoutChild *child = tmp_list->data;
802      tmp_list = tmp_list->next;
803     
804      gtk_layout_position_child (layout, child);
805    }
806}
807
808static void
809gtk_layout_adjust_allocations_recurse (GtkWidget *widget,
810                                       gpointer   cb_data)
811{
812  GtkLayoutAdjData *data = cb_data;
813
814  widget->allocation.x += data->dx;
815  widget->allocation.y += data->dy;
816
817  if (GTK_WIDGET_NO_WINDOW (widget) &&
818      GTK_IS_CONTAINER (widget))
819    gtk_container_forall (GTK_CONTAINER (widget),
820                          gtk_layout_adjust_allocations_recurse,
821                          cb_data);
822}
823
824static void
825gtk_layout_adjust_allocations (GtkLayout *layout,
826                               gint       dx,
827                               gint       dy)
828{
829  GList *tmp_list;
830  GtkLayoutAdjData data;
831
832  data.dx = dx;
833  data.dy = dy;
834
835  tmp_list = layout->children;
836  while (tmp_list)
837    {
838      GtkLayoutChild *child = tmp_list->data;
839      tmp_list = tmp_list->next;
840     
841      child->widget->allocation.x += dx;
842      child->widget->allocation.y += dy;
843
844      if (GTK_WIDGET_NO_WINDOW (child->widget) &&
845          GTK_IS_CONTAINER (child->widget))
846        gtk_container_forall (GTK_CONTAINER (child->widget),
847                              gtk_layout_adjust_allocations_recurse,
848                              &data);
849    }
850}
851 
852/* Callbacks */
853
854/* Send a synthetic expose event to the widget
855 */
856static void
857gtk_layout_expose_area (GtkLayout    *layout,
858                        gint x, gint y, gint width, gint height)
859{
860  if (layout->visibility == GDK_VISIBILITY_UNOBSCURED)
861    {
862      GdkEventExpose event;
863     
864      event.type = GDK_EXPOSE;
865      event.send_event = TRUE;
866      event.window = layout->bin_window;
867      event.count = 0;
868     
869      event.area.x = x;
870      event.area.y = y;
871      event.area.width = width;
872      event.area.height = height;
873     
874      gdk_window_ref (event.window);
875      gtk_widget_event (GTK_WIDGET (layout), (GdkEvent *)&event);
876      gdk_window_unref (event.window);
877    }
878}
879
880/* This function is used to find events to process while scrolling
881 */
882
883static Bool
884gtk_layout_expose_predicate (Display *display,
885                  XEvent  *xevent,
886                  XPointer arg)
887{
888  if ((xevent->type == Expose) ||
889      ((xevent->xany.window == *(Window *)arg) &&
890       (xevent->type == ConfigureNotify)))
891    return True;
892  else
893    return False;
894}
895
896/* This is the main routine to do the scrolling. Scrolling is
897 * done by "Guffaw" scrolling, as in the Mozilla XFE, with
898 * a few modifications.
899 *
900 * The main improvement is that we keep track of whether we
901 * are obscured or not. If not, we ignore the generated expose
902 * events and instead do the exposes ourself, without having
903 * to wait for a roundtrip to the server. This also provides
904 * a limited form of expose-event compression, since we do
905 * the affected area as one big chunk.
906 *
907 * Real expose event compression, as in the XFE, could be added
908 * here. It would help opaque drags over the region, and the
909 * obscured case.
910 *
911 * Code needs to be added here to do the scrolling on machines
912 * that don't have working WindowGravity. That could be done
913 *
914 *  - XCopyArea and move the windows, and accept trailing the
915 *    background color. (Since it is only a fallback method)
916 *  - XmHTML style. As above, but turn off expose events when
917 *    not obscured and do the exposures ourself.
918 *  - gzilla-style. Move the window continuously, and reset
919 *    every 32768 pixels
920 */
921
922static void
923gtk_layout_adjustment_changed (GtkAdjustment *adjustment,
924                               GtkLayout     *layout)
925{
926  GtkWidget *widget;
927  XEvent xevent;
928 
929  gint dx, dy;
930
931  widget = GTK_WIDGET (layout);
932
933  dx = (gint)layout->hadjustment->value - layout->xoffset;
934  dy = (gint)layout->vadjustment->value - layout->yoffset;
935
936  layout->xoffset = (gint)layout->hadjustment->value;
937  layout->yoffset = (gint)layout->vadjustment->value;
938
939  if (layout->freeze_count)
940    return;
941
942  if (!GTK_WIDGET_MAPPED (layout))
943    {
944      gtk_layout_position_children (layout);
945      return;
946    }
947
948  gtk_layout_adjust_allocations (layout, -dx, -dy);
949
950  if (dx > 0)
951    {
952      if (gravity_works)
953        {
954          gdk_window_resize (layout->bin_window,
955                             widget->allocation.width + dx,
956                             widget->allocation.height);
957          gdk_window_move   (layout->bin_window, -dx, 0);
958          gdk_window_move_resize (layout->bin_window,
959                                  0, 0,
960                                  widget->allocation.width,
961                                  widget->allocation.height);
962        }
963      else
964        {
965          /* FIXME */
966        }
967
968      gtk_layout_expose_area (layout,
969                              MAX ((gint)widget->allocation.width - dx, 0),
970                              0,
971                              MIN (dx, widget->allocation.width),
972                              widget->allocation.height);
973    }
974  else if (dx < 0)
975    {
976      if (gravity_works)
977        {
978          gdk_window_move_resize (layout->bin_window,
979                                  dx, 0,
980                                  widget->allocation.width - dx,
981                                  widget->allocation.height);
982          gdk_window_move   (layout->bin_window, 0, 0);
983          gdk_window_resize (layout->bin_window,
984                             widget->allocation.width,
985                             widget->allocation.height);
986        }
987      else
988        {
989          /* FIXME */
990        }
991
992      gtk_layout_expose_area (layout,
993                              0,
994                              0,
995                              MIN (-dx, widget->allocation.width),
996                              widget->allocation.height);
997    }
998
999  if (dy > 0)
1000    {
1001      if (gravity_works)
1002        {
1003          gdk_window_resize (layout->bin_window,
1004                             widget->allocation.width,
1005                             widget->allocation.height + dy);
1006          gdk_window_move   (layout->bin_window, 0, -dy);
1007          gdk_window_move_resize (layout->bin_window,
1008                                  0, 0,
1009                                  widget->allocation.width,
1010                                  widget->allocation.height);
1011        }
1012      else
1013        {
1014          /* FIXME */
1015        }
1016
1017      gtk_layout_expose_area (layout,
1018                              0,
1019                              MAX ((gint)widget->allocation.height - dy, 0),
1020                              widget->allocation.width,
1021                              MIN (dy, widget->allocation.height));
1022    }
1023  else if (dy < 0)
1024    {
1025      if (gravity_works)
1026        {
1027          gdk_window_move_resize (layout->bin_window,
1028                                  0, dy,
1029                                  widget->allocation.width,
1030                                  widget->allocation.height - dy);
1031          gdk_window_move   (layout->bin_window, 0, 0);
1032          gdk_window_resize (layout->bin_window,
1033                             widget->allocation.width,
1034                             widget->allocation.height);
1035        }
1036      else
1037        {
1038          /* FIXME */
1039        }
1040      gtk_layout_expose_area (layout,
1041                              0,
1042                              0,
1043                              widget->allocation.width,
1044                              MIN (-dy, (gint)widget->allocation.height));
1045    }
1046
1047  gtk_layout_position_children (layout);
1048
1049  /* We have to make sure that all exposes from this scroll get
1050   * processed before we scroll again, or the expose events will
1051   * have invalid coordinates.
1052   *
1053   * We also do expose events for other windows, since otherwise
1054   * their updating will fall behind the scrolling
1055   *
1056   * This also avoids a problem in pre-1.0 GTK where filters don't
1057   * have access to configure events that were compressed.
1058   */
1059
1060  gdk_flush();
1061  while (XCheckIfEvent(GDK_WINDOW_XDISPLAY (layout->bin_window),
1062                       &xevent,
1063                       gtk_layout_expose_predicate,
1064                       (XPointer)&GDK_WINDOW_XWINDOW (layout->bin_window)))
1065    {
1066      GdkEvent event;
1067      GtkWidget *event_widget;
1068
1069      switch (xevent.type)
1070        {
1071        case Expose:
1072
1073          if (xevent.xany.window == GDK_WINDOW_XWINDOW (layout->bin_window))
1074            {
1075              /* If the window is unobscured, then we've exposed the
1076               * regions with the following serials already, so we
1077               * can throw out the expose events.
1078               */
1079              if (layout->visibility == GDK_VISIBILITY_UNOBSCURED &&
1080                  (((dx > 0 || dy > 0) &&
1081                    xevent.xexpose.serial == layout->configure_serial) ||
1082                   ((dx < 0 || dy < 0) &&
1083                    xevent.xexpose.serial == layout->configure_serial + 1)))
1084                continue;
1085              /* The following expose was generated while the origin was
1086               * different from the current origin, so we need to offset it.
1087               */
1088              else if (xevent.xexpose.serial == layout->configure_serial)
1089                {
1090                  xevent.xexpose.x += layout->scroll_x;
1091                  xevent.xexpose.y += layout->scroll_y;
1092                }
1093              event.expose.window = layout->bin_window;
1094              event_widget = widget;
1095            }
1096          else
1097            {
1098              event.expose.window = gdk_window_lookup (xevent.xany.window);
1099              gdk_window_get_user_data (event.expose.window,
1100                                        (gpointer *)&event_widget);
1101            }
1102
1103          if (event_widget)
1104            {
1105              event.expose.type = GDK_EXPOSE;
1106              event.expose.area.x = xevent.xexpose.x;
1107              event.expose.area.y = xevent.xexpose.y;
1108              event.expose.area.width = xevent.xexpose.width;
1109              event.expose.area.height = xevent.xexpose.height;
1110              event.expose.count = xevent.xexpose.count;
1111             
1112              gdk_window_ref (event.expose.window);
1113              gtk_widget_event (event_widget, &event);
1114              gdk_window_unref (event.expose.window);
1115            }
1116          break;
1117     
1118        case ConfigureNotify:
1119          if (xevent.xany.window == GDK_WINDOW_XWINDOW (layout->bin_window) &&
1120              (xevent.xconfigure.x != 0 || xevent.xconfigure.y != 0))
1121            {
1122              layout->configure_serial = xevent.xconfigure.serial;
1123              layout->scroll_x = xevent.xconfigure.x;
1124              layout->scroll_y = xevent.xconfigure.y;
1125            }
1126          break;
1127        }
1128    }
1129}
1130
1131#if 0
1132/* The main event filter. Actually, we probably don't really need
1133 * thisfilter at all, since we are calling it directly above in the
1134 * expose-handling hack. But in case scrollbars
1135 * are fixed up in some manner...
1136 *
1137 * This routine identifies expose events that are generated when
1138 * we've temporarily moved the bin_window_origin, and translates
1139 * them or discards them, depending on whether we are obscured
1140 * or not.
1141 */
1142static GdkFilterReturn
1143gtk_layout_filter (GdkXEvent *gdk_xevent,
1144                   GdkEvent  *event,
1145                   gpointer   data)
1146{
1147  XEvent *xevent;
1148  GtkLayout *layout;
1149
1150  xevent = (XEvent *)gdk_xevent;
1151  layout = GTK_LAYOUT (data);
1152
1153  switch (xevent->type)
1154    {
1155    case Expose:
1156      if (xevent->xexpose.serial == layout->configure_serial)
1157        {
1158          if (layout->visibility == GDK_VISIBILITY_UNOBSCURED)
1159            return GDK_FILTER_REMOVE;
1160          else
1161            {
1162              xevent->xexpose.x += layout->scroll_x;
1163              xevent->xexpose.y += layout->scroll_y;
1164             
1165              break;
1166            }
1167        }
1168      break;
1169     
1170    case ConfigureNotify:
1171       if ((xevent->xconfigure.x != 0) || (xevent->xconfigure.y != 0))
1172        {
1173          layout->configure_serial = xevent->xconfigure.serial;
1174          layout->scroll_x = xevent->xconfigure.x;
1175          layout->scroll_y = xevent->xconfigure.y;
1176        }
1177      break;
1178    }
1179 
1180  return GDK_FILTER_CONTINUE;
1181}
[15780]1182#endif
[14481]1183
1184/* Although GDK does have a GDK_VISIBILITY_NOTIFY event,
1185 * there is no corresponding event in GTK, so we have
1186 * to get the events from a filter
1187 */
1188static GdkFilterReturn
1189gtk_layout_main_filter (GdkXEvent *gdk_xevent,
1190                        GdkEvent  *event,
1191                        gpointer   data)
1192{
1193  XEvent *xevent;
1194  GtkLayout *layout;
1195
1196  xevent = (XEvent *)gdk_xevent;
1197  layout = GTK_LAYOUT (data);
1198
1199  if (xevent->type == VisibilityNotify)
1200    {
1201      switch (xevent->xvisibility.state)
1202        {
1203        case VisibilityFullyObscured:
1204          layout->visibility = GDK_VISIBILITY_FULLY_OBSCURED;
1205          break;
1206
1207        case VisibilityPartiallyObscured:
1208          layout->visibility = GDK_VISIBILITY_PARTIAL;
1209          break;
1210
1211        case VisibilityUnobscured:
1212          layout->visibility = GDK_VISIBILITY_UNOBSCURED;
1213          break;
1214        }
1215
1216      return GDK_FILTER_REMOVE;
1217    }
1218
1219 
1220  return GDK_FILTER_CONTINUE;
1221}
1222
Note: See TracBrowser for help on using the repository browser.