* Patch to test the composited windows, it requires a patch in gdkwindow
[hildon] / hildon / hildon-viewport.c
1 /*
2  * This file is a part of hildon
3  *
4  * Copyright (C) 2009 Nokia Corporation, all rights reserved.
5  *
6  * Contact: Rodrigo Novo <rodrigo.novo@nokia.com>
7  *
8  * This widget is based on GtkViewport from gtk+
9  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh
10  * MacDonald
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU Lesser Public License as published by
14  * the Free Software Foundation; version 2 of the license.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU Lesser Public License for more details.
20  *
21  */
22
23 #include <config.h>
24 #include "hildon-viewport.h"
25 #include "hildon-pannable-area.h"
26 #include "hildon-marshalers.h"
27 #include "hildon-enum-types.h"
28 #include <gtk/gtk.h>
29
30 enum {
31   PROP_0,
32   PROP_HADJUSTMENT,
33   PROP_VADJUSTMENT,
34   PROP_SHADOW_TYPE
35 };
36
37
38 static void hildon_viewport_finalize                 (GObject          *object);
39 static void hildon_viewport_destroy                  (GtkObject        *object);
40 static void hildon_viewport_set_property             (GObject          *object,
41                                                       guint             prop_id,
42                                                       const GValue     *value,
43                                                       GParamSpec       *pspec);
44 static void hildon_viewport_get_property             (GObject          *object,
45                                                       guint             prop_id,
46                                                       GValue           *value,
47                                                       GParamSpec       *pspec);
48 static void hildon_viewport_set_scroll_adjustments   (HildonViewport   *viewport,
49                                                       GtkAdjustment    *hadjustment,
50                                                       GtkAdjustment    *vadjustment);
51 static void hildon_viewport_realize                  (GtkWidget        *widget);
52 static void hildon_viewport_unrealize                (GtkWidget        *widget);
53 static void hildon_viewport_paint                    (GtkWidget        *widget,
54                                                       GdkRectangle     *area);
55 static gint hildon_viewport_expose                   (GtkWidget        *widget,
56                                                       GdkEventExpose   *event);
57 static void hildon_viewport_add                      (GtkContainer     *container,
58                                                       GtkWidget        *widget);
59 static void hildon_viewport_size_request             (GtkWidget        *widget,
60                                                       GtkRequisition   *requisition);
61 static void hildon_viewport_size_allocate            (GtkWidget        *widget,
62                                                       GtkAllocation    *allocation);
63 static void hildon_viewport_adjustment_value_changed (GtkAdjustment    *adjustment,
64                                                       gpointer          data);
65 static void hildon_viewport_style_set                (GtkWidget        *widget,
66                                                       GtkStyle         *previous_style);
67 static void hildon_viewport_paint_offscreen_children (cairo_t          *cr,
68                                                       GdkWindow        *window,
69                                                       GdkEventExpose   *event,
70                                                       gint              orig_x,
71                                                       gint              orig_y);
72 static void hildon_viewport_expose_window            (GdkWindow        *window);
73
74
75 G_DEFINE_TYPE (HildonViewport, hildon_viewport, GTK_TYPE_BIN)
76
77 #define HILDON_VIEWPORT_PRIVATE(o)                                \
78   (G_TYPE_INSTANCE_GET_PRIVATE ((o), HILDON_TYPE_VIEWPORT, \
79                                 HildonViewportPrivate))
80
81 struct _HildonViewportPrivate {
82   GdkPixmap *pixmap;
83   gint old_x;
84   gint old_y;
85 };
86
87 static void
88 hildon_viewport_class_init (HildonViewportClass *class)
89 {
90   GtkObjectClass *object_class;
91   GObjectClass   *gobject_class;
92   GtkWidgetClass *widget_class;
93   GtkContainerClass *container_class;
94
95   g_type_class_add_private (class, sizeof (HildonViewportPrivate));
96
97   object_class = (GtkObjectClass*) class;
98   gobject_class = G_OBJECT_CLASS (class);
99   widget_class = (GtkWidgetClass*) class;
100   container_class = (GtkContainerClass*) class;
101
102   gobject_class->finalize = hildon_viewport_finalize;
103   gobject_class->set_property = hildon_viewport_set_property;
104   gobject_class->get_property = hildon_viewport_get_property;
105   object_class->destroy = hildon_viewport_destroy;
106
107   widget_class->realize = hildon_viewport_realize;
108   widget_class->unrealize = hildon_viewport_unrealize;
109   widget_class->expose_event = hildon_viewport_expose;
110   widget_class->size_request = hildon_viewport_size_request;
111   widget_class->size_allocate = hildon_viewport_size_allocate;
112   widget_class->style_set = hildon_viewport_style_set;
113
114   container_class->add = hildon_viewport_add;
115
116   class->set_scroll_adjustments = hildon_viewport_set_scroll_adjustments;
117
118   g_object_class_install_property (gobject_class,
119                                    PROP_HADJUSTMENT,
120                                    g_param_spec_object ("hadjustment",
121                                                         "Horizontal adjustment",
122                                                         "The GtkAdjustment that determines the values of the horizontal position for this viewport",
123                                                         GTK_TYPE_ADJUSTMENT,
124                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
125
126   g_object_class_install_property (gobject_class,
127                                    PROP_VADJUSTMENT,
128                                    g_param_spec_object ("vadjustment",
129                                                         "Vertical adjustment",
130                                                         "The GtkAdjustment that determines the values of the vertical position for this viewport",
131                                                         GTK_TYPE_ADJUSTMENT,
132                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
133
134   g_object_class_install_property (gobject_class,
135                                    PROP_SHADOW_TYPE,
136                                    g_param_spec_enum ("shadow-type",
137                                                       "Shadow type",
138                                                       "Determines how the shadowed box around the viewport is drawn",
139                                                       GTK_TYPE_SHADOW_TYPE,
140                                                       GTK_SHADOW_IN,
141                                                       G_PARAM_READWRITE));
142
143   widget_class->set_scroll_adjustments_signal =
144     g_signal_new ("set_scroll_adjustments",
145                   G_OBJECT_CLASS_TYPE (gobject_class),
146                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
147                   G_STRUCT_OFFSET (HildonViewportClass, set_scroll_adjustments),
148                   NULL, NULL,
149                   _hildon_marshal_VOID__OBJECT_OBJECT,
150                   G_TYPE_NONE, 2,
151                   GTK_TYPE_ADJUSTMENT,
152                   GTK_TYPE_ADJUSTMENT);
153 }
154
155 static void
156 hildon_viewport_set_property (GObject         *object,
157                               guint            prop_id,
158                               const GValue    *value,
159                               GParamSpec      *pspec)
160 {
161   HildonViewport *viewport;
162
163   viewport = HILDON_VIEWPORT (object);
164
165   switch (prop_id)
166     {
167     case PROP_HADJUSTMENT:
168       hildon_viewport_set_hadjustment (viewport, g_value_get_object (value));
169       break;
170     case PROP_VADJUSTMENT:
171       hildon_viewport_set_vadjustment (viewport, g_value_get_object (value));
172       break;
173     case PROP_SHADOW_TYPE:
174       hildon_viewport_set_shadow_type (viewport, g_value_get_enum (value));
175       break;
176     default:
177       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
178       break;
179     }
180 }
181
182 static void
183 hildon_viewport_get_property (GObject         *object,
184                               guint            prop_id,
185                               GValue          *value,
186                               GParamSpec      *pspec)
187 {
188   HildonViewport *viewport;
189
190   viewport = HILDON_VIEWPORT (object);
191
192   switch (prop_id)
193     {
194     case PROP_HADJUSTMENT:
195       g_value_set_object (value, viewport->hadjustment);
196       break;
197     case PROP_VADJUSTMENT:
198       g_value_set_object (value, viewport->vadjustment);
199       break;
200     case PROP_SHADOW_TYPE:
201       g_value_set_enum (value, viewport->shadow_type);
202       break;
203     default:
204       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
205       break;
206     }
207 }
208
209 static void
210 hildon_viewport_init (HildonViewport *viewport)
211 {
212   HildonViewportPrivate *priv = HILDON_VIEWPORT_PRIVATE (viewport);
213
214   GTK_WIDGET_UNSET_FLAGS (viewport, GTK_NO_WINDOW);
215
216   gtk_widget_set_redraw_on_allocate (GTK_WIDGET (viewport), FALSE);
217   gtk_container_set_resize_mode (GTK_CONTAINER (viewport), GTK_RESIZE_QUEUE);
218
219   viewport->priv = priv;
220   viewport->shadow_type = GTK_SHADOW_IN;
221   viewport->view_window = NULL;
222   viewport->bin_window = NULL;
223   viewport->hadjustment = NULL;
224   viewport->vadjustment = NULL;
225   priv->pixmap = NULL;
226   priv->old_x = 0;
227   priv->old_y = 0;
228 }
229
230 /**
231  * hildon_viewport_new:
232  * @hadjustment: horizontal adjustment.
233  * @vadjustment: vertical adjustment.
234  * @returns: a new #HildonViewport.
235  *
236  * Creates a new #HildonViewport with the given adjustments.
237  *
238  **/
239 GtkWidget*
240 hildon_viewport_new (GtkAdjustment *hadjustment,
241                      GtkAdjustment *vadjustment)
242 {
243   GtkWidget *viewport;
244
245   viewport = gtk_widget_new (HILDON_TYPE_VIEWPORT,
246                              "hadjustment", hadjustment,
247                              "vadjustment", vadjustment,
248                              NULL);
249
250   return viewport;
251 }
252
253 #define ADJUSTMENT_POINTER(viewport, orientation)       \
254   (((orientation) == GTK_ORIENTATION_HORIZONTAL) ?      \
255    &(viewport)->hadjustment : &(viewport)->vadjustment)
256
257 static void
258 viewport_disconnect_adjustment (HildonViewport    *viewport,
259                                 GtkOrientation  orientation)
260 {
261   GtkAdjustment **adjustmentp = ADJUSTMENT_POINTER (viewport, orientation);
262
263   if (*adjustmentp)
264     {
265       g_signal_handlers_disconnect_by_func (*adjustmentp,
266                                             hildon_viewport_adjustment_value_changed,
267                                             viewport);
268       g_object_unref (*adjustmentp);
269       *adjustmentp = NULL;
270     }
271 }
272
273 static void
274 hildon_viewport_finalize (GObject *object)
275 {
276   HildonViewport *viewport = HILDON_VIEWPORT (object);
277
278   viewport_disconnect_adjustment (viewport, GTK_ORIENTATION_HORIZONTAL);
279   viewport_disconnect_adjustment (viewport, GTK_ORIENTATION_VERTICAL);
280
281   G_OBJECT_CLASS (hildon_viewport_parent_class)->finalize (object);
282 }
283
284 static void
285 hildon_viewport_destroy (GtkObject *object)
286 {
287   HildonViewport *viewport = HILDON_VIEWPORT (object);
288
289   viewport_disconnect_adjustment (viewport, GTK_ORIENTATION_HORIZONTAL);
290   viewport_disconnect_adjustment (viewport, GTK_ORIENTATION_VERTICAL);
291
292   GTK_OBJECT_CLASS (hildon_viewport_parent_class)->destroy (object);
293 }
294
295 /**
296  * hildon_viewport_get_hadjustment:
297  * @viewport: a #HildonViewport.
298  *
299  * Returns the horizontal adjustment of the viewport.
300  *
301  * Return value: the horizontal adjustment of @viewport.
302  **/
303 GtkAdjustment*
304 hildon_viewport_get_hadjustment (HildonViewport *viewport)
305 {
306   g_return_val_if_fail (HILDON_IS_VIEWPORT (viewport), NULL);
307
308   if (!viewport->hadjustment)
309     hildon_viewport_set_hadjustment (viewport, NULL);
310
311   return viewport->hadjustment;
312 }
313
314 /**
315  * hildon_viewport_get_vadjustment:
316  * @viewport: a #HildonViewport.
317  *
318  * Returns the vertical adjustment of the viewport.
319  *
320  * Return value: the vertical adjustment of @viewport.
321  **/
322 GtkAdjustment*
323 hildon_viewport_get_vadjustment (HildonViewport *viewport)
324 {
325   g_return_val_if_fail (HILDON_IS_VIEWPORT (viewport), NULL);
326
327   if (!viewport->vadjustment)
328     hildon_viewport_set_vadjustment (viewport, NULL);
329
330   return viewport->vadjustment;
331 }
332
333 static void
334 viewport_get_view_allocation (HildonViewport   *viewport,
335                               GtkAllocation *view_allocation)
336 {
337   GtkWidget *widget = GTK_WIDGET (viewport);
338   GtkAllocation *allocation = &widget->allocation;
339   gint border_width = GTK_CONTAINER (viewport)->border_width;
340
341   view_allocation->x = 0;
342   view_allocation->y = 0;
343
344   if (viewport->shadow_type != GTK_SHADOW_NONE)
345     {
346       view_allocation->x = widget->style->xthickness;
347       view_allocation->y = widget->style->ythickness;
348     }
349
350   view_allocation->width = MAX (1, allocation->width - view_allocation->x * 2 - border_width * 2);
351   view_allocation->height = MAX (1, allocation->height - view_allocation->y * 2 - border_width * 2);
352 }
353
354 static void
355 viewport_reclamp_adjustment (GtkAdjustment *adjustment,
356                              gboolean      *value_changed)
357 {
358   gdouble value = adjustment->value;
359
360   value = CLAMP (value, 0, adjustment->upper - adjustment->page_size);
361   if (value != adjustment->value)
362     {
363       adjustment->value = value;
364       if (value_changed)
365         *value_changed = TRUE;
366     }
367   else if (value_changed)
368     *value_changed = FALSE;
369 }
370
371 static void
372 viewport_set_hadjustment_values (HildonViewport *viewport,
373                                  gboolean    *value_changed)
374 {
375   GtkBin *bin = GTK_BIN (viewport);
376   GtkAllocation view_allocation;
377   GtkAdjustment *hadjustment = hildon_viewport_get_hadjustment (viewport);
378   gdouble old_page_size;
379   gdouble old_upper;
380   gdouble old_value;
381
382   viewport_get_view_allocation (viewport, &view_allocation);
383
384   old_page_size = hadjustment->page_size;
385   old_upper = hadjustment->upper;
386   old_value = hadjustment->value;
387   hadjustment->page_size = view_allocation.width;
388   hadjustment->step_increment = view_allocation.width * 0.1;
389   hadjustment->page_increment = view_allocation.width * 0.9;
390
391   hadjustment->lower = 0;
392
393   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
394     {
395       GtkRequisition child_requisition;
396
397       gtk_widget_get_child_requisition (bin->child, &child_requisition);
398       hadjustment->upper = MAX (child_requisition.width, view_allocation.width);
399     }
400   else
401     hadjustment->upper = view_allocation.width;
402
403   if (gtk_widget_get_direction (GTK_WIDGET (viewport)) == GTK_TEXT_DIR_RTL)
404     {
405       gdouble dist = old_upper - (old_value + old_page_size);
406       hadjustment->value = hadjustment->upper - dist - hadjustment->page_size;
407       viewport_reclamp_adjustment (hadjustment, value_changed);
408       *value_changed = (old_value != hadjustment->value);
409     }
410   else
411     viewport_reclamp_adjustment (hadjustment, value_changed);
412 }
413
414 static void
415 viewport_set_vadjustment_values (HildonViewport *viewport,
416                                  gboolean    *value_changed)
417 {
418   GtkBin *bin = GTK_BIN (viewport);
419   GtkAllocation view_allocation;
420   GtkAdjustment *vadjustment = hildon_viewport_get_vadjustment (viewport);
421
422   viewport_get_view_allocation (viewport, &view_allocation);
423
424   vadjustment->page_size = view_allocation.height;
425   vadjustment->step_increment = view_allocation.height * 0.1;
426   vadjustment->page_increment = view_allocation.height * 0.9;
427
428   vadjustment->lower = 0;
429
430   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
431     {
432       GtkRequisition child_requisition;
433
434       gtk_widget_get_child_requisition (bin->child, &child_requisition);
435       vadjustment->upper = MAX (child_requisition.height, view_allocation.height);
436     }
437   else
438     vadjustment->upper = view_allocation.height;
439
440   viewport_reclamp_adjustment (vadjustment, value_changed);
441 }
442
443 static void
444 viewport_set_adjustment (HildonViewport    *viewport,
445                          GtkOrientation  orientation,
446                          GtkAdjustment  *adjustment)
447 {
448   GtkAdjustment **adjustmentp = ADJUSTMENT_POINTER (viewport, orientation);
449   gboolean value_changed;
450
451   if (adjustment && adjustment == *adjustmentp)
452     return;
453
454   if (!adjustment)
455     adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0,
456                                                      0.0, 0.0, 0.0));
457   viewport_disconnect_adjustment (viewport, orientation);
458   *adjustmentp = adjustment;
459   g_object_ref_sink (adjustment);
460
461   if (orientation == GTK_ORIENTATION_HORIZONTAL)
462     viewport_set_hadjustment_values (viewport, &value_changed);
463   else
464     viewport_set_vadjustment_values (viewport, &value_changed);
465
466   g_signal_connect (adjustment, "value_changed",
467                     G_CALLBACK (hildon_viewport_adjustment_value_changed),
468                     viewport);
469
470   gtk_adjustment_changed (adjustment);
471
472   if (value_changed)
473     gtk_adjustment_value_changed (adjustment);
474   else
475     hildon_viewport_adjustment_value_changed (adjustment, viewport);
476 }
477
478 /**
479  * hildon_viewport_set_hadjustment:
480  * @viewport: a #HildonViewport.
481  * @adjustment: a #GtkAdjustment.
482  *
483  * Sets the horizontal adjustment of the viewport.
484  **/
485 void
486 hildon_viewport_set_hadjustment (HildonViewport   *viewport,
487                                  GtkAdjustment *adjustment)
488 {
489   g_return_if_fail (HILDON_IS_VIEWPORT (viewport));
490   if (adjustment)
491     g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
492
493   viewport_set_adjustment (viewport, GTK_ORIENTATION_HORIZONTAL, adjustment);
494
495   g_object_notify (G_OBJECT (viewport), "hadjustment");
496 }
497
498 /**
499  * hildon_viewport_set_vadjustment:
500  * @viewport: a #HildonViewport.
501  * @adjustment: a #GtkAdjustment.
502  *
503  * Sets the vertical adjustment of the viewport.
504  **/
505 void
506 hildon_viewport_set_vadjustment (HildonViewport   *viewport,
507                                  GtkAdjustment *adjustment)
508 {
509   g_return_if_fail (HILDON_IS_VIEWPORT (viewport));
510   if (adjustment)
511     g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
512
513   viewport_set_adjustment (viewport, GTK_ORIENTATION_VERTICAL, adjustment);
514
515   g_object_notify (G_OBJECT (viewport), "vadjustment");
516 }
517
518 static void
519 hildon_viewport_set_scroll_adjustments (HildonViewport      *viewport,
520                                         GtkAdjustment    *hadjustment,
521                                         GtkAdjustment    *vadjustment)
522 {
523   hildon_viewport_set_hadjustment (viewport, hadjustment);
524   hildon_viewport_set_vadjustment (viewport, vadjustment);
525 }
526
527 /**
528  * hildon_viewport_set_shadow_type:
529  * @viewport: a #HildonViewport.
530  * @type: the new shadow type.
531  *
532  * Sets the shadow type of the viewport.
533  **/
534 void
535 hildon_viewport_set_shadow_type (HildonViewport   *viewport,
536                                  GtkShadowType  type)
537 {
538   g_return_if_fail (HILDON_IS_VIEWPORT (viewport));
539
540   if ((GtkShadowType) viewport->shadow_type != type)
541     {
542       viewport->shadow_type = type;
543
544       if (GTK_WIDGET_VISIBLE (viewport))
545         {
546           gtk_widget_size_allocate (GTK_WIDGET (viewport), &(GTK_WIDGET (viewport)->allocation));
547           gtk_widget_queue_draw (GTK_WIDGET (viewport));
548         }
549
550       g_object_notify (G_OBJECT (viewport), "shadow-type");
551     }
552 }
553
554 /**
555  * hildon_viewport_get_shadow_type:
556  * @viewport: a #HildonViewport
557  *
558  * Gets the shadow type of the #HildonViewport. See
559  * hildon_viewport_set_shadow_type().
560
561  * Return value: the shadow type
562  **/
563 GtkShadowType
564 hildon_viewport_get_shadow_type (HildonViewport *viewport)
565 {
566   g_return_val_if_fail (HILDON_IS_VIEWPORT (viewport), GTK_SHADOW_NONE);
567
568   return viewport->shadow_type;
569 }
570
571 static void
572 hildon_viewport_expose_window (GdkWindow *window)
573 {
574   GList *l, *children;
575   gpointer user_data;
576   gboolean is_double_buffered;
577   GdkEvent event;
578
579   gdk_window_get_user_data (window, &user_data);
580
581   event.expose.type = GDK_EXPOSE;
582   event.expose.window = g_object_ref (window);
583   event.expose.send_event = FALSE;
584   event.expose.count = 0;
585   event.expose.area.x = 0;
586   event.expose.area.y = 0;
587   gdk_drawable_get_size (GDK_DRAWABLE (window),
588                          &event.expose.area.width,
589                          &event.expose.area.height);
590   event.expose.region = gdk_region_rectangle (&event.expose.area);
591
592   if (user_data)
593     is_double_buffered = GTK_WIDGET_DOUBLE_BUFFERED (GTK_WIDGET (user_data));
594   else
595     is_double_buffered = FALSE;
596
597   /* If this is not double buffered, force a double buffer so that
598      redirection works. */
599   if (!is_double_buffered)
600     gdk_window_begin_paint_region (window, event.expose.region);
601
602   gtk_main_do_event (&event);
603
604   if (!is_double_buffered)
605     gdk_window_end_paint (window);
606
607   children = gdk_window_peek_children (window);
608   for (l = children; l != NULL; l = l->next)
609     {
610       GdkWindow *child = l->data;
611
612       /* Don't expose input-only windows */
613       if (gdk_drawable_get_depth (GDK_DRAWABLE (child)) != 0)
614         hildon_viewport_expose_window (l->data);
615     }
616 }
617
618 void
619 hildon_viewport_freeze (HildonViewport *viewport)
620 {
621   HildonViewportPrivate *priv = viewport->priv;
622
623   GtkBin *bin;
624   GtkWidget *child;
625   GtkWidget *widget;
626   gint w, h;
627   cairo_t *cr;
628   GdkEvent event;
629
630   g_return_if_fail (GTK_WIDGET_REALIZED (viewport));
631   g_return_if_fail (priv->pixmap == NULL);
632
633   widget = GTK_WIDGET (viewport);
634   bin = GTK_BIN (viewport);
635   child = gtk_bin_get_child (bin);
636
637   gdk_drawable_get_size (viewport->bin_window, &w, &h);
638
639   event.expose.type = GDK_EXPOSE;
640   event.expose.window = g_object_ref (viewport->bin_window);
641   event.expose.send_event = FALSE;
642   event.expose.count = 0;
643   event.expose.area.x = 0;
644   event.expose.area.y = 0;
645   event.expose.area.width = w;
646   event.expose.area.height = h;
647   event.expose.region = gdk_region_rectangle (&event.expose.area);
648
649   /* ensure the children are drawn in their offscreen windows */
650   hildon_viewport_expose_window (viewport->bin_window);
651
652   cr = gdk_cairo_create (viewport->bin_window);
653
654   /* blit the children to the bin window */
655   hildon_viewport_paint_offscreen_children (cr,
656                                             viewport->bin_window,
657                                             (GdkEventExpose *)&event,
658                                             0, 0);
659
660   g_object_unref (viewport->bin_window);
661
662   cairo_destroy (cr);
663
664   priv->pixmap = gdk_pixmap_new (viewport->bin_window,
665                                  w, h, -1);
666
667   /* copy the bin window to a pixmap, consider XCompositeNameWindowPixmap */
668   gdk_draw_drawable (priv->pixmap,
669                      widget->style->bg_gc[GTK_STATE_NORMAL],
670                      viewport->bin_window,
671                      0, 0, 0, 0, w, h);
672
673   gdk_window_hide (viewport->bin_window);
674   gtk_widget_queue_draw (widget);
675 }
676
677 void
678 hildon_viewport_thaw (HildonViewport *viewport)
679 {
680   HildonViewportPrivate *priv = viewport->priv;
681   GtkAdjustment *hadjustment = hildon_viewport_get_hadjustment (viewport);
682   GtkAdjustment *vadjustment = hildon_viewport_get_vadjustment (viewport);
683   gint new_x, new_y;
684   GtkWidget *widget;
685
686   widget = GTK_WIDGET (viewport);
687
688   if (priv->pixmap) {
689     g_object_unref (priv->pixmap);
690
691     priv->pixmap = NULL;
692
693     new_x = - hadjustment->value;
694     new_y = - vadjustment->value;
695
696     gdk_window_show (viewport->bin_window);
697     gdk_window_move (viewport->bin_window, new_x, new_y);
698   }
699 }
700
701 static void
702 hildon_viewport_realize (GtkWidget *widget)
703 {
704   HildonViewport *viewport = HILDON_VIEWPORT (widget);
705   GtkBin *bin = GTK_BIN (widget);
706   GtkAdjustment *hadjustment = hildon_viewport_get_hadjustment (viewport);
707   GtkAdjustment *vadjustment = hildon_viewport_get_vadjustment (viewport);
708   gint border_width = GTK_CONTAINER (widget)->border_width;
709
710   GtkAllocation view_allocation;
711   GdkWindowAttr attributes;
712   gint attributes_mask;
713   gint event_mask;
714
715   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
716
717   attributes.x = widget->allocation.x + border_width;
718   attributes.y = widget->allocation.y + border_width;
719   attributes.width = widget->allocation.width - border_width * 2;
720   attributes.height = widget->allocation.height - border_width * 2;
721   attributes.window_type = GDK_WINDOW_CHILD;
722   attributes.wclass = GDK_INPUT_OUTPUT;
723   attributes.visual = gtk_widget_get_visual (widget);
724   attributes.colormap = gtk_widget_get_colormap (widget);
725
726   event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
727   /* We select on button_press_mask so that button 4-5 scrolls are trapped.
728    */
729   attributes.event_mask = event_mask | GDK_BUTTON_PRESS_MASK;
730
731   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
732
733   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
734                                    &attributes, attributes_mask);
735   gdk_window_set_user_data (widget->window, viewport);
736
737   viewport_get_view_allocation (viewport, &view_allocation);
738
739   attributes.x = view_allocation.x;
740   attributes.y = view_allocation.y;
741   attributes.width = view_allocation.width;
742   attributes.height = view_allocation.height;
743   attributes.event_mask = event_mask;
744
745   viewport->view_window = gdk_window_new (widget->window, &attributes, attributes_mask);
746   gdk_window_set_user_data (viewport->view_window, viewport);
747
748   attributes.x = - hadjustment->value;
749   attributes.y = - vadjustment->value;
750   attributes.width = hadjustment->upper;
751   attributes.height = vadjustment->upper;
752
753   viewport->bin_window = gdk_window_new (viewport->view_window, &attributes, attributes_mask);
754   gdk_window_set_user_data (viewport->bin_window, viewport);
755
756   if (bin->child)
757     gtk_widget_set_parent_window (bin->child, viewport->bin_window);
758
759   widget->style = gtk_style_attach (widget->style, widget->window);
760   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
761   gtk_style_set_background (widget->style, viewport->view_window, GTK_STATE_NORMAL);
762   gtk_style_set_background (widget->style, viewport->bin_window, GTK_STATE_NORMAL);
763
764   /* Call paint here to allow a theme to set the background without flashing
765    */
766   gtk_paint_flat_box(widget->style, viewport->bin_window, GTK_STATE_NORMAL,
767                      GTK_SHADOW_NONE,
768                      NULL, widget, "viewportbin",
769                      0, 0, -1, -1);
770
771   gdk_window_set_composited (viewport->bin_window, TRUE);
772
773   gdk_window_show (viewport->bin_window);
774   gdk_window_show (viewport->view_window);
775 }
776
777 static void
778 hildon_viewport_unrealize (GtkWidget *widget)
779 {
780   HildonViewport *viewport = HILDON_VIEWPORT (widget);
781
782   gdk_window_set_user_data (viewport->view_window, NULL);
783   gdk_window_destroy (viewport->view_window);
784   viewport->view_window = NULL;
785
786   gdk_window_set_user_data (viewport->bin_window, NULL);
787   gdk_window_destroy (viewport->bin_window);
788   viewport->bin_window = NULL;
789
790   if (GTK_WIDGET_CLASS (hildon_viewport_parent_class)->unrealize)
791     (* GTK_WIDGET_CLASS (hildon_viewport_parent_class)->unrealize) (widget);
792 }
793
794 static void
795 hildon_viewport_paint (GtkWidget    *widget,
796                        GdkRectangle *area)
797 {
798   HildonViewport *viewport;
799
800   g_return_if_fail (HILDON_IS_VIEWPORT (widget));
801   g_return_if_fail (area != NULL);
802
803   if (GTK_WIDGET_DRAWABLE (widget))
804     {
805       viewport = HILDON_VIEWPORT (widget);
806
807       gtk_paint_shadow (widget->style, widget->window,
808                         GTK_STATE_NORMAL, viewport->shadow_type,
809                         area, widget, "viewport",
810                         0, 0, -1, -1);
811     }
812 }
813
814 static void
815 hildon_viewport_paint_offscreen_children (cairo_t        *cr,
816                                           GdkWindow      *window,
817                                           GdkEventExpose *event,
818                                           gint            orig_x,
819                                           gint            orig_y)
820 {
821   GList *subwindow;
822
823   for (subwindow = gdk_window_peek_children (window);
824        subwindow;
825        subwindow = subwindow->next)
826     {
827       /* avoid input-only windows */
828       if (gdk_drawable_get_depth (GDK_DRAWABLE (subwindow->data)) != 0)
829         {
830           gint x, y, w, h;
831
832           gdk_window_get_position (subwindow->data,
833                                    &x, &y);
834
835           x += orig_x;
836           y += orig_y;
837
838           gdk_drawable_get_size (subwindow->data, &w, &h);
839
840           /* do not draw the widgets outside the event */
841           if ((x+w < event->area.x) ||
842               (y+h < event->area.y) ||
843               (x > event->area.x+event->area.width) ||
844               (y > event->area.y+event->area.height))
845             continue;
846
847           gdk_cairo_set_source_pixmap (cr, subwindow->data, x, y);
848
849           cairo_rectangle (cr, x, y, w, h);
850
851           cairo_fill (cr);
852
853
854           hildon_viewport_paint_offscreen_children (cr,
855                                                     subwindow->data,
856                                                     event,
857                                                     x, y);
858         }
859     }
860 }
861
862 static gint
863 hildon_viewport_expose (GtkWidget      *widget,
864                         GdkEventExpose *event)
865 {
866   HildonViewport *viewport = HILDON_VIEWPORT (widget);
867   HildonViewportPrivate *priv = viewport->priv;
868
869   if (GTK_WIDGET_DRAWABLE (widget))
870     {
871       if (event->window == widget->window)
872         hildon_viewport_paint (widget, &event->area);
873       else if ((event->window == viewport->bin_window))
874         {
875           cairo_t *cr;
876
877           gtk_paint_flat_box(widget->style,
878                              viewport->bin_window,
879                              GTK_STATE_NORMAL, GTK_SHADOW_NONE,
880                              &event->area, widget, "viewportbin",
881                              0, 0, -1, -1);
882
883           (* GTK_WIDGET_CLASS (hildon_viewport_parent_class)->expose_event) (widget, event);
884
885           cr = gdk_cairo_create (viewport->bin_window);
886
887           hildon_viewport_paint_offscreen_children (cr,
888                                                     viewport->bin_window,
889                                                     event, 0, 0);
890
891           cairo_destroy (cr);
892         }
893       else if (event->window == viewport->view_window)
894         {
895           GtkAdjustment *vadjustment = hildon_viewport_get_vadjustment (viewport);
896           GtkAdjustment *hadjustment = hildon_viewport_get_hadjustment (viewport);
897
898           gdk_draw_drawable (viewport->view_window,
899                              widget->style->bg_gc[GTK_STATE_NORMAL],
900                              priv->pixmap ? priv->pixmap : viewport->bin_window,
901                              hadjustment->value + event->area.x,
902                              vadjustment->value + event->area.y,
903                              event->area.x, event->area.y,
904                              event->area.width, event->area.height);
905         }
906     }
907
908   return FALSE;
909 }
910
911 static void
912 hildon_viewport_add (GtkContainer *container,
913                      GtkWidget    *child)
914 {
915   GtkBin *bin;
916
917   g_return_if_fail (GTK_IS_WIDGET (child));
918
919   bin = GTK_BIN (container);
920   g_return_if_fail (bin->child == NULL);
921
922   gtk_widget_set_parent_window (child, HILDON_VIEWPORT (bin)->bin_window);
923
924   GTK_CONTAINER_CLASS (hildon_viewport_parent_class)->add (container, child);
925 }
926
927 static void
928 hildon_viewport_size_request (GtkWidget      *widget,
929                               GtkRequisition *requisition)
930 {
931   GtkBin *bin;
932   GtkRequisition child_requisition;
933
934   bin = GTK_BIN (widget);
935
936   requisition->width = GTK_CONTAINER (widget)->border_width;
937
938   requisition->height = GTK_CONTAINER (widget)->border_width;
939
940   if (HILDON_VIEWPORT (widget)->shadow_type != GTK_SHADOW_NONE)
941     {
942       requisition->width += 2 * widget->style->xthickness;
943       requisition->height += 2 * widget->style->ythickness;
944     }
945
946   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
947     {
948       gtk_widget_size_request (bin->child, &child_requisition);
949       requisition->width += child_requisition.width;
950       requisition->height += child_requisition.height;
951     }
952 }
953
954 static void
955 hildon_viewport_size_allocate (GtkWidget     *widget,
956                                GtkAllocation *allocation)
957 {
958   HildonViewport *viewport = HILDON_VIEWPORT (widget);
959   GtkBin *bin = GTK_BIN (widget);
960   gint border_width = GTK_CONTAINER (widget)->border_width;
961   gboolean hadjustment_value_changed, vadjustment_value_changed;
962   GtkAdjustment *hadjustment = hildon_viewport_get_hadjustment (viewport);
963   GtkAdjustment *vadjustment = hildon_viewport_get_vadjustment (viewport);
964   GtkAllocation child_allocation;
965
966   /* If our size changed, and we have a shadow, queue a redraw on widget->window to
967    * redraw the shadow correctly.
968    */
969   if (GTK_WIDGET_MAPPED (widget) &&
970       viewport->shadow_type != GTK_SHADOW_NONE &&
971       (widget->allocation.width != allocation->width ||
972        widget->allocation.height != allocation->height))
973     gdk_window_invalidate_rect (widget->window, NULL, FALSE);
974
975   widget->allocation = *allocation;
976
977   viewport_set_hadjustment_values (viewport, &hadjustment_value_changed);
978   viewport_set_vadjustment_values (viewport, &vadjustment_value_changed);
979
980   child_allocation.x = 0;
981   child_allocation.y = 0;
982   child_allocation.width = hadjustment->upper;
983   child_allocation.height = vadjustment->upper;
984   if (GTK_WIDGET_REALIZED (widget))
985     {
986       GtkAllocation view_allocation;
987
988       gdk_window_move_resize (widget->window,
989                               allocation->x + border_width,
990                               allocation->y + border_width,
991                               allocation->width - border_width * 2,
992                               allocation->height - border_width * 2);
993
994       viewport_get_view_allocation (viewport, &view_allocation);
995       gdk_window_move_resize (viewport->view_window,
996                               view_allocation.x,
997                               view_allocation.y,
998                               view_allocation.width,
999                               view_allocation.height);
1000       gdk_window_move_resize (viewport->bin_window,
1001                               - hadjustment->value,
1002                               - vadjustment->value,
1003                               child_allocation.width,
1004                               child_allocation.height);
1005     }
1006   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
1007     gtk_widget_size_allocate (bin->child, &child_allocation);
1008
1009   gtk_adjustment_changed (hadjustment);
1010   gtk_adjustment_changed (vadjustment);
1011   if (hadjustment_value_changed)
1012     gtk_adjustment_value_changed (hadjustment);
1013   if (vadjustment_value_changed)
1014     gtk_adjustment_value_changed (vadjustment);
1015 }
1016
1017 static void
1018 hildon_viewport_adjustment_value_changed (GtkAdjustment *adjustment,
1019                                           gpointer       data)
1020 {
1021   GtkWidget *widget;
1022   HildonViewport *viewport;
1023   GtkBin *bin;
1024   HildonViewportPrivate *priv;
1025
1026   g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
1027   g_return_if_fail (HILDON_IS_VIEWPORT (data));
1028
1029   viewport = HILDON_VIEWPORT (data);
1030   widget = GTK_WIDGET (viewport);
1031   bin = GTK_BIN (data);
1032
1033   priv = viewport->priv;
1034
1035   if (bin->child && GTK_WIDGET_VISIBLE (bin->child)  &&
1036       GTK_WIDGET_REALIZED (viewport))
1037     {
1038       GtkAdjustment *hadjustment = hildon_viewport_get_hadjustment (viewport);
1039       GtkAdjustment *vadjustment = hildon_viewport_get_vadjustment (viewport);
1040       gint new_x, new_y;
1041
1042       new_x = - hadjustment->value;
1043       new_y = - vadjustment->value;
1044
1045       if (new_x != priv->old_x || new_y != priv->old_y)
1046         {
1047           if (priv->pixmap) {
1048             gdk_window_invalidate_rect (viewport->view_window, NULL, FALSE);
1049           } else {
1050             gdk_window_move (viewport->bin_window, new_x, new_y);
1051           }
1052
1053           gdk_window_process_updates (viewport->view_window, TRUE);
1054
1055           priv->old_x = new_x;
1056           priv->old_y = new_y;
1057         }
1058     }
1059 }
1060
1061 static void
1062 hildon_viewport_style_set (GtkWidget *widget,
1063                            GtkStyle  *previous_style)
1064 {
1065   HildonViewport *viewport;
1066
1067   if (GTK_WIDGET_REALIZED (widget) &&
1068       !GTK_WIDGET_NO_WINDOW (widget))
1069     {
1070       HildonViewportPrivate *priv;
1071       viewport = HILDON_VIEWPORT (widget);
1072
1073       priv = viewport->priv;
1074
1075       gtk_style_set_background (widget->style, viewport->bin_window, GTK_STATE_NORMAL);
1076       gtk_style_set_background (widget->style, viewport->view_window, GTK_STATE_NORMAL);
1077       gtk_style_set_background (widget->style, widget->window, widget->state);
1078     }
1079 }
1080
1081 #define __HILDON_VIEWPORT_C__