~kroq-gar78/ubuntu/precise/gnome-control-center/fix-885947

« back to all changes in this revision

Viewing changes to capplets/display/scrollarea.c

  • Committer: Bazaar Package Importer
  • Author(s): Rodrigo Moya
  • Date: 2011-05-17 10:47:27 UTC
  • mfrom: (0.1.11 experimental) (1.1.45 upstream)
  • Revision ID: james.westby@ubuntu.com-20110517104727-lqel6m8vhfw5jby1
Tags: 1:3.0.1.1-1ubuntu1
* Rebase on Debian, remaining Ubuntu changes:
* debian/control:
  - Build-Depend on hardening-wrapper, dpkg-dev and dh-autoreconf
  - Add dependency on ubuntu-system-service
  - Remove dependency on gnome-icon-theme-symbolic
  - Move dependency on apg, gnome-icon-theme-symbolic and accountsservice to
    be a Recommends: until we get them in main
* debian/rules:
  - Use autoreconf
  - Add binary-post-install rule for gnome-control-center-data
  - Run dh-autoreconf
* debian/gnome-control-center.dirs:
* debian/gnome-control-center.links:
  - Add a link to the control center shell for indicators
* debian/patches/00_disable-nm.patch:
  - Temporary patch to disable building with NetworkManager until we get
    the new one in the archive
* debian/patches/01_git_remove_gettext_calls.patch:
  - Remove calls to AM_GNU_GETTEXT, IT_PROG_INTLTOOL should be enough
* debian/patches/01_git_kill_warning.patch:
  - Kill warning
* debian/patches/50_ubuntu_systemwide_prefs.patch:
  - Ubuntu specific proxy preferences
* debian/patches/51_ubuntu_system_keyboard.patch:
  - Implement the global keyboard spec at https://wiki.ubuntu.com/DefaultKeyboardSettings

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* Copyright 2006, 2007, 2008, Soren Sandmann <sandmann@daimi.au.dk>
2
 
 *
3
 
 * This library is free software; you can redistribute it and/or
4
 
 * modify it under the terms of the GNU Lesser General Public
5
 
 * License as published by the Free Software Foundation; either
6
 
 * version 2 of the License, or (at your option) any later version.
7
 
 *
8
 
 * This library is distributed in the hope that it will be useful,
9
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11
 
 * Lesser General Public License for more details.
12
 
 *
13
 
 * You should have received a copy of the GNU Lesser General Public
14
 
 * License along with this library; if not, write to the
15
 
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16
 
 * Boston, MA 02111-1307, USA.
17
 
 */
18
 
 
19
 
#include <gdk/gdkprivate.h> /* For GDK_PARENT_RELATIVE_BG */
20
 
#include "scrollarea.h"
21
 
#include "foo-marshal.h"
22
 
 
23
 
G_DEFINE_TYPE (FooScrollArea, foo_scroll_area, GTK_TYPE_CONTAINER);
24
 
 
25
 
static GtkWidgetClass *parent_class;
26
 
 
27
 
typedef struct BackingStore BackingStore;
28
 
 
29
 
typedef void (* ExposeFunc) (cairo_t *cr, GdkRegion *region, gpointer data);
30
 
 
31
 
#if 0
32
 
static void          backing_store_draw (BackingStore *store, 
33
 
                                         GdkDrawable *dest,
34
 
                                         GdkRegion *clip,
35
 
                                         int dest_x,
36
 
                                         int dest_y);
37
 
static void          backing_store_scroll (BackingStore *store,
38
 
                                           int dx, int dy);
39
 
static void          backing_store_invalidate_rect (BackingStore *store,
40
 
                                                    GdkRectangle *rect);
41
 
static void          backing_store_invalidate_region (BackingStore *store,
42
 
                                                      GdkRegion *region);
43
 
static void          backing_store_invalidate_all (BackingStore *store);
44
 
static BackingStore *backing_store_new (GdkWindow *window,
45
 
                                        int width, int height);
46
 
static void          backing_store_resize (BackingStore *store,
47
 
                                           int width, int height);
48
 
static void          backing_store_process_updates (BackingStore *store,
49
 
                                                    ExposeFunc func,
50
 
                                                    gpointer data);
51
 
static void          backing_store_free (BackingStore *store);
52
 
#endif
53
 
 
54
 
typedef struct InputPath InputPath;
55
 
typedef struct InputRegion InputRegion;
56
 
typedef struct AutoScrollInfo AutoScrollInfo;
57
 
 
58
 
struct InputPath
59
 
{
60
 
    gboolean                    is_stroke;
61
 
    cairo_fill_rule_t           fill_rule;
62
 
    double                      line_width;
63
 
    cairo_path_t               *path;           /* In canvas coordinates */
64
 
 
65
 
    FooScrollAreaEventFunc      func;
66
 
    gpointer                    data;
67
 
 
68
 
    InputPath                  *next;
69
 
};
70
 
 
71
 
/* InputRegions are mutually disjoint */
72
 
struct InputRegion
73
 
{
74
 
    GdkRegion *region;          /* the boundary of this area in canvas coordinates */
75
 
    InputPath *paths;
76
 
};
77
 
 
78
 
struct AutoScrollInfo
79
 
{
80
 
    int                         dx;
81
 
    int                         dy;
82
 
    int                         timeout_id;
83
 
    int                         begin_x;
84
 
    int                         begin_y;
85
 
    double                      res_x;
86
 
    double                      res_y;
87
 
    GTimer                     *timer;
88
 
};
89
 
 
90
 
struct FooScrollAreaPrivate
91
 
{
92
 
    GdkWindow                  *input_window;
93
 
    
94
 
    int                         width;
95
 
    int                         height;
96
 
    
97
 
    GtkAdjustment              *hadj;
98
 
    GtkAdjustment              *vadj;
99
 
    int                         x_offset;
100
 
    int                         y_offset;
101
 
    
102
 
    int                         min_width;
103
 
    int                         min_height;
104
 
 
105
 
    GPtrArray                  *input_regions;
106
 
    
107
 
    AutoScrollInfo             *auto_scroll_info;
108
 
    
109
 
    /* During expose, this region is set to the region
110
 
     * being exposed. At other times, it is NULL
111
 
     *
112
 
     * It is used for clipping of input areas
113
 
     */
114
 
    GdkRegion                  *expose_region;
115
 
    InputRegion                *current_input;
116
 
    
117
 
    gboolean                    grabbed;
118
 
    FooScrollAreaEventFunc      grab_func;
119
 
    gpointer                    grab_data;
120
 
 
121
 
    GdkPixmap                  *pixmap;
122
 
    GdkRegion                  *update_region;          /* In canvas coordinates */
123
 
};
124
 
 
125
 
enum
126
 
{
127
 
    VIEWPORT_CHANGED,
128
 
    PAINT,
129
 
    INPUT,
130
 
    LAST_SIGNAL,
131
 
};
132
 
 
133
 
static guint signals [LAST_SIGNAL] = { 0 };
134
 
 
135
 
static void foo_scroll_area_size_request (GtkWidget *widget,
136
 
                                          GtkRequisition *requisition);
137
 
static gboolean foo_scroll_area_expose (GtkWidget *widget,
138
 
                                        GdkEventExpose *expose);
139
 
static void foo_scroll_area_size_allocate (GtkWidget *widget,
140
 
                                           GtkAllocation *allocation);
141
 
static void foo_scroll_area_set_scroll_adjustments (FooScrollArea *scroll_area,
142
 
                                                    GtkAdjustment    *hadjustment,
143
 
                                                    GtkAdjustment    *vadjustment);
144
 
static void foo_scroll_area_realize (GtkWidget *widget);
145
 
static void foo_scroll_area_unrealize (GtkWidget *widget);
146
 
static void foo_scroll_area_map (GtkWidget *widget);
147
 
static void foo_scroll_area_unmap (GtkWidget *widget);
148
 
static gboolean foo_scroll_area_button_press (GtkWidget *widget,
149
 
                                              GdkEventButton *event);
150
 
static gboolean foo_scroll_area_button_release (GtkWidget *widget,
151
 
                                                GdkEventButton *event);
152
 
static gboolean foo_scroll_area_motion (GtkWidget *widget,
153
 
                                        GdkEventMotion *event);
154
 
 
155
 
static void
156
 
foo_scroll_area_map (GtkWidget *widget)
157
 
{
158
 
    FooScrollArea *area = FOO_SCROLL_AREA (widget);
159
 
    
160
 
    GTK_WIDGET_CLASS (parent_class)->map (widget);
161
 
    
162
 
    if (area->priv->input_window)
163
 
        gdk_window_show (area->priv->input_window);
164
 
}
165
 
 
166
 
static void
167
 
foo_scroll_area_unmap (GtkWidget *widget)
168
 
{
169
 
    FooScrollArea *area = FOO_SCROLL_AREA (widget);
170
 
    
171
 
    if (area->priv->input_window)
172
 
        gdk_window_hide (area->priv->input_window);
173
 
    
174
 
    GTK_WIDGET_CLASS (parent_class)->unmap (widget);
175
 
}
176
 
 
177
 
static void
178
 
foo_scroll_area_finalize (GObject *object)
179
 
{
180
 
    FooScrollArea *scroll_area = FOO_SCROLL_AREA (object);
181
 
    
182
 
    g_object_unref (scroll_area->priv->hadj);
183
 
    g_object_unref (scroll_area->priv->vadj);
184
 
    
185
 
    g_ptr_array_free (scroll_area->priv->input_regions, TRUE);
186
 
    
187
 
    g_free (scroll_area->priv);
188
 
 
189
 
    G_OBJECT_CLASS (foo_scroll_area_parent_class)->finalize (object);
190
 
}
191
 
 
192
 
static void
193
 
foo_scroll_area_class_init (FooScrollAreaClass *class)
194
 
{
195
 
    GObjectClass *object_class = G_OBJECT_CLASS (class);
196
 
    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
197
 
    
198
 
    object_class->finalize = foo_scroll_area_finalize;
199
 
    widget_class->size_request = foo_scroll_area_size_request;
200
 
    widget_class->expose_event = foo_scroll_area_expose;
201
 
    widget_class->size_allocate = foo_scroll_area_size_allocate;
202
 
    widget_class->realize = foo_scroll_area_realize;
203
 
    widget_class->unrealize = foo_scroll_area_unrealize;
204
 
    widget_class->button_press_event = foo_scroll_area_button_press;
205
 
    widget_class->button_release_event = foo_scroll_area_button_release;
206
 
    widget_class->motion_notify_event = foo_scroll_area_motion;
207
 
    widget_class->map = foo_scroll_area_map;
208
 
    widget_class->unmap = foo_scroll_area_unmap;
209
 
    
210
 
    class->set_scroll_adjustments = foo_scroll_area_set_scroll_adjustments;
211
 
    
212
 
    parent_class = g_type_class_peek_parent (class);
213
 
    
214
 
    signals[VIEWPORT_CHANGED] =
215
 
        g_signal_new ("viewport_changed",
216
 
                      G_OBJECT_CLASS_TYPE (object_class),
217
 
                      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
218
 
                      G_STRUCT_OFFSET (FooScrollAreaClass,
219
 
                                       viewport_changed),
220
 
                      NULL, NULL,
221
 
                      foo_marshal_VOID__BOXED_BOXED,
222
 
                      G_TYPE_NONE, 2,
223
 
                      GDK_TYPE_RECTANGLE,
224
 
                      GDK_TYPE_RECTANGLE);
225
 
    
226
 
    signals[PAINT] =
227
 
        g_signal_new ("paint",
228
 
                      G_OBJECT_CLASS_TYPE (object_class),
229
 
                      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
230
 
                      G_STRUCT_OFFSET (FooScrollAreaClass,
231
 
                                       paint),
232
 
                      NULL, NULL,
233
 
                      foo_marshal_VOID__POINTER_BOXED_POINTER,
234
 
                      G_TYPE_NONE, 3,
235
 
                      G_TYPE_POINTER,
236
 
                      GDK_TYPE_RECTANGLE, 
237
 
                      G_TYPE_POINTER);
238
 
    
239
 
    widget_class->set_scroll_adjustments_signal =
240
 
        g_signal_new ("set_scroll_adjustments",
241
 
                      G_OBJECT_CLASS_TYPE (object_class),
242
 
                      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
243
 
                      G_STRUCT_OFFSET (FooScrollAreaClass,
244
 
                                       set_scroll_adjustments),
245
 
                      NULL, NULL,
246
 
                      foo_marshal_VOID__OBJECT_OBJECT,
247
 
                      G_TYPE_NONE, 2,
248
 
                      GTK_TYPE_ADJUSTMENT,
249
 
                      GTK_TYPE_ADJUSTMENT);
250
 
}
251
 
 
252
 
static GtkAdjustment *
253
 
new_adjustment (void)
254
 
{
255
 
    return GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
256
 
}
257
 
 
258
 
static void
259
 
foo_scroll_area_init (FooScrollArea *scroll_area)
260
 
{
261
 
    GtkWidget *widget;
262
 
 
263
 
    widget = GTK_WIDGET (scroll_area);
264
 
 
265
 
    gtk_widget_set_has_window (widget, FALSE);
266
 
    gtk_widget_set_redraw_on_allocate (widget, FALSE);
267
 
    
268
 
    scroll_area->priv = g_new0 (FooScrollAreaPrivate, 1);
269
 
    scroll_area->priv->width = 0;
270
 
    scroll_area->priv->height = 0;
271
 
    scroll_area->priv->hadj = g_object_ref_sink (new_adjustment());
272
 
    scroll_area->priv->vadj = g_object_ref_sink (new_adjustment());
273
 
    scroll_area->priv->x_offset = 0.0;
274
 
    scroll_area->priv->y_offset = 0.0;
275
 
    scroll_area->priv->min_width = -1;
276
 
    scroll_area->priv->min_height = -1;
277
 
    scroll_area->priv->auto_scroll_info = NULL;
278
 
    scroll_area->priv->input_regions = g_ptr_array_new ();
279
 
    scroll_area->priv->pixmap = NULL;
280
 
    scroll_area->priv->update_region = gdk_region_new ();
281
 
 
282
 
    gtk_widget_set_double_buffered (widget, FALSE);
283
 
}
284
 
 
285
 
static void
286
 
translate_cairo_device (cairo_t       *cr,
287
 
                        int            x_offset,
288
 
                        int            y_offset)
289
 
{
290
 
    cairo_surface_t *surface = cairo_get_target (cr);
291
 
    double dev_x;
292
 
    double dev_y;
293
 
    
294
 
    cairo_surface_get_device_offset (surface, &dev_x, &dev_y);
295
 
    dev_x += x_offset;
296
 
    dev_y += y_offset;
297
 
    cairo_surface_set_device_offset (surface, dev_x, dev_y);
298
 
}
299
 
 
300
 
#if 0
301
 
static void
302
 
print_region (const char *header, GdkRegion *region)
303
 
{
304
 
    GdkRectangle *rects;
305
 
    int n_rects;
306
 
    int i;
307
 
    
308
 
    g_print ("%s\n", header);
309
 
    
310
 
    gdk_region_get_rectangles (region, &rects, &n_rects);
311
 
    for (i = 0; i < n_rects; ++i)
312
 
    {
313
 
        GdkRectangle *rect = &(rects[i]);
314
 
        g_print ("  %d %d %d %d\n",
315
 
                 rect->x, rect->y, rect->width, rect->height);
316
 
    }
317
 
}
318
 
#endif
319
 
 
320
 
typedef void (* PathForeachFunc) (double  *x,
321
 
                                  double  *y,
322
 
                                  gpointer data);
323
 
 
324
 
static void
325
 
path_foreach_point (cairo_path_t     *path,
326
 
                    PathForeachFunc   func,
327
 
                    gpointer          user_data)
328
 
{
329
 
    int i;
330
 
    
331
 
    for (i = 0; i < path->num_data; i += path->data[i].header.length)
332
 
    {
333
 
        cairo_path_data_t *data = &(path->data[i]);
334
 
        
335
 
        switch (data->header.type)
336
 
        {
337
 
        case CAIRO_PATH_MOVE_TO:
338
 
        case CAIRO_PATH_LINE_TO:
339
 
            func (&(data[1].point.x), &(data[1].point.y), user_data);
340
 
            break;
341
 
            
342
 
        case CAIRO_PATH_CURVE_TO:
343
 
            func (&(data[1].point.x), &(data[1].point.y), user_data);
344
 
            func (&(data[2].point.x), &(data[2].point.y), user_data);
345
 
            func (&(data[3].point.x), &(data[3].point.y), user_data);
346
 
            break;
347
 
            
348
 
        case CAIRO_PATH_CLOSE_PATH:
349
 
            break;
350
 
        }
351
 
    }
352
 
}
353
 
 
354
 
typedef struct
355
 
{
356
 
    double x1, y1, x2, y2;
357
 
} Box;
358
 
 
359
 
#if 0
360
 
static void
361
 
update_box (double *x, double *y, gpointer data)
362
 
{
363
 
    Box *box = data;
364
 
    
365
 
    if (*x < box->x1)
366
 
        box->x1 = *x;
367
 
 
368
 
    if (*y < box->y1)
369
 
        box->y1 = *y;
370
 
 
371
 
    if (*y > box->y2)
372
 
        box->y2 = *y;
373
 
    
374
 
    if (*x > box->x2)
375
 
        box->x2 = *x;
376
 
}
377
 
#endif
378
 
 
379
 
#if 0
380
 
static void
381
 
path_compute_extents (cairo_path_t *path,
382
 
                      GdkRectangle *rect)
383
 
{
384
 
    if (rect)
385
 
    {
386
 
        Box box = { G_MAXDOUBLE, G_MAXDOUBLE, G_MINDOUBLE, G_MINDOUBLE };
387
 
 
388
 
        path_foreach_point (path, update_box, &box);
389
 
 
390
 
        rect->x = box.x1;
391
 
        rect->y = box.y1;
392
 
        rect->width = box.x2 - box.x1;
393
 
        rect->height = box.y2 - box.y1;
394
 
    }
395
 
}
396
 
#endif
397
 
 
398
 
static void
399
 
input_path_free_list (InputPath *paths)
400
 
{
401
 
    if (!paths)
402
 
        return;
403
 
 
404
 
    input_path_free_list (paths->next);
405
 
    cairo_path_destroy (paths->path);
406
 
    g_free (paths);
407
 
}
408
 
 
409
 
static void
410
 
input_region_free (InputRegion *region)
411
 
{
412
 
    input_path_free_list (region->paths);
413
 
    gdk_region_destroy (region->region);
414
 
 
415
 
    g_free (region);
416
 
}
417
 
 
418
 
static void
419
 
get_viewport (FooScrollArea *scroll_area,
420
 
              GdkRectangle  *viewport)
421
 
{
422
 
    GtkAllocation allocation;
423
 
    GtkWidget *widget = GTK_WIDGET (scroll_area);
424
 
 
425
 
    gtk_widget_get_allocation (widget, &allocation);
426
 
 
427
 
    viewport->x = scroll_area->priv->x_offset;
428
 
    viewport->y = scroll_area->priv->y_offset;
429
 
    viewport->width = allocation.width;
430
 
    viewport->height = allocation.height;
431
 
}
432
 
 
433
 
static void
434
 
allocation_to_canvas (FooScrollArea *area,
435
 
                      int           *x,
436
 
                      int           *y)
437
 
{
438
 
    *x += area->priv->x_offset;
439
 
    *y += area->priv->y_offset;
440
 
}
441
 
 
442
 
static void
443
 
clear_exposed_input_region (FooScrollArea *area,
444
 
                            GdkRegion *exposed) /* in canvas coordinates */
445
 
{
446
 
    int i;
447
 
    GdkRegion *viewport;
448
 
    GdkRectangle allocation;
449
 
 
450
 
    gtk_widget_get_allocation (GTK_WIDGET (area), &allocation);
451
 
    allocation.x = 0;
452
 
    allocation.y = 0;
453
 
    allocation_to_canvas (area, &allocation.x, &allocation.y);
454
 
    viewport = gdk_region_rectangle (&allocation);
455
 
    gdk_region_subtract (viewport, exposed);
456
 
    
457
 
    for (i = 0; i < area->priv->input_regions->len; ++i)
458
 
    {
459
 
        InputRegion *region = area->priv->input_regions->pdata[i];
460
 
 
461
 
        gdk_region_intersect (region->region, viewport);
462
 
 
463
 
        if (gdk_region_empty (region->region))
464
 
        {
465
 
            input_region_free (region);
466
 
            g_ptr_array_remove_index_fast (area->priv->input_regions, i--);
467
 
        }
468
 
    }
469
 
 
470
 
    gdk_region_destroy (viewport);
471
 
    
472
 
#if 0
473
 
        path = region->paths;
474
 
        while (path != NULL)
475
 
        {
476
 
            GdkRectangle rect;
477
 
 
478
 
            path_compute_extents (path->path, &rect);
479
 
 
480
 
            if (gdk_region_rect_in (area->priv->expose_region, &rect) == GDK_OVERLAP_RECTANGLE_IN)
481
 
                g_print ("we would have deleted it\n");
482
 
#if 0
483
 
            else
484
 
                g_print ("nope (%d %d %d %d)\n", );
485
 
#endif
486
 
            
487
 
            path = path->next;
488
 
        }
489
 
        
490
 
        /* FIXME: we should also delete paths (and path segments)
491
 
         * completely contained in the expose_region
492
 
         */
493
 
    }
494
 
#endif
495
 
}
496
 
 
497
 
static void
498
 
setup_background_cr (GdkWindow *window,
499
 
                     cairo_t   *cr,
500
 
                     int        x_offset,
501
 
                     int        y_offset)
502
 
{
503
 
    GdkWindowObject *private = (GdkWindowObject *)window;
504
 
    
505
 
    if (private->bg_pixmap == GDK_PARENT_RELATIVE_BG && private->parent)
506
 
    {
507
 
        x_offset += private->x;
508
 
        y_offset += private->y;
509
 
        
510
 
        setup_background_cr (GDK_WINDOW (private->parent), cr, x_offset, y_offset);
511
 
    }
512
 
    else if (private->bg_pixmap &&
513
 
             private->bg_pixmap != GDK_PARENT_RELATIVE_BG &&
514
 
             private->bg_pixmap != GDK_NO_BG)
515
 
    {
516
 
        gdk_cairo_set_source_pixmap (cr, private->bg_pixmap, -x_offset, -y_offset);
517
 
    }
518
 
    else
519
 
    {
520
 
        gdk_cairo_set_source_color (cr, &private->bg_color);
521
 
    }
522
 
}
523
 
 
524
 
static void
525
 
initialize_background (GtkWidget *widget,
526
 
                       cairo_t   *cr)
527
 
{
528
 
    setup_background_cr (gtk_widget_get_window (widget), cr, 0, 0);
529
 
 
530
 
    cairo_paint (cr);
531
 
}
532
 
 
533
 
static void
534
 
clip_to_region (cairo_t *cr, GdkRegion *region)
535
 
{
536
 
    int n_rects;
537
 
    GdkRectangle *rects;
538
 
 
539
 
    gdk_region_get_rectangles (region, &rects, &n_rects);
540
 
 
541
 
    cairo_new_path (cr);
542
 
    while (n_rects--)
543
 
    {
544
 
        GdkRectangle *rect = &(rects[n_rects]);
545
 
 
546
 
        cairo_rectangle (cr, rect->x, rect->y, rect->width, rect->height);
547
 
    }
548
 
    cairo_clip (cr);
549
 
 
550
 
    g_free (rects);
551
 
}
552
 
 
553
 
static void
554
 
simple_draw_drawable (GdkDrawable *dst,
555
 
                      GdkDrawable *src,
556
 
                      int          src_x,
557
 
                      int          src_y,
558
 
                      int          dst_x,
559
 
                      int          dst_y,
560
 
                      int          width,
561
 
                      int          height)
562
 
{
563
 
    GdkGC *gc = gdk_gc_new (dst);
564
 
 
565
 
    gdk_draw_drawable (dst, gc, src, src_x, src_y, dst_x, dst_y, width, height);
566
 
 
567
 
    g_object_unref (gc);
568
 
}
569
 
 
570
 
static gboolean
571
 
foo_scroll_area_expose (GtkWidget *widget,
572
 
                        GdkEventExpose *expose)
573
 
{
574
 
    FooScrollArea *scroll_area = FOO_SCROLL_AREA (widget);
575
 
    cairo_t *cr;
576
 
    GdkRectangle extents;
577
 
    GdkRegion *region;
578
 
    int x_offset, y_offset;
579
 
    GdkGC *gc;
580
 
    GtkAllocation widget_allocation;
581
 
    GdkWindow *window = gtk_widget_get_window (widget);
582
 
 
583
 
    /* I don't think expose can ever recurse for the same area */
584
 
    g_assert (!scroll_area->priv->expose_region);
585
 
    
586
 
    /* Note that this function can be called at a time
587
 
     * where the adj->value is different from x_offset. 
588
 
     * Ie., the GtkScrolledWindow changed the adj->value
589
 
     * without emitting the value_changed signal. 
590
 
     *
591
 
     * Hence we must always use the value we got 
592
 
     * the last time the signal was emitted, ie.,
593
 
     * priv->{x,y}_offset.
594
 
     */
595
 
    
596
 
    x_offset = scroll_area->priv->x_offset;
597
 
    y_offset = scroll_area->priv->y_offset;
598
 
    
599
 
    scroll_area->priv->expose_region = expose->region;
600
 
 
601
 
    /* Setup input areas */
602
 
    clear_exposed_input_region (scroll_area, scroll_area->priv->update_region);
603
 
    
604
 
    scroll_area->priv->current_input = g_new0 (InputRegion, 1);
605
 
    scroll_area->priv->current_input->region = gdk_region_copy (scroll_area->priv->update_region);
606
 
    scroll_area->priv->current_input->paths = NULL;
607
 
    g_ptr_array_add (scroll_area->priv->input_regions,
608
 
                     scroll_area->priv->current_input);
609
 
 
610
 
    region = scroll_area->priv->update_region;
611
 
    scroll_area->priv->update_region = gdk_region_new ();
612
 
    
613
 
    /* Create cairo context */
614
 
    cr = gdk_cairo_create (scroll_area->priv->pixmap);
615
 
    translate_cairo_device (cr, -x_offset, -y_offset);
616
 
    clip_to_region (cr, region);
617
 
    initialize_background (widget, cr);
618
 
 
619
 
    /* Create regions */
620
 
    gdk_region_get_clipbox (region, &extents);
621
 
 
622
 
    g_signal_emit (widget, signals[PAINT], 0, cr, &extents, region);
623
 
 
624
 
    /* Destroy stuff */
625
 
    cairo_destroy (cr);
626
 
    
627
 
    scroll_area->priv->expose_region = NULL;
628
 
    scroll_area->priv->current_input = NULL;
629
 
 
630
 
    /* Finally draw the backing pixmap */
631
 
    gc = gdk_gc_new (window);
632
 
 
633
 
    gdk_gc_set_clip_region (gc, expose->region);
634
 
 
635
 
    gtk_widget_get_allocation (widget, &widget_allocation);
636
 
    gdk_draw_drawable (window, gc, scroll_area->priv->pixmap,
637
 
                       0, 0, widget_allocation.x, widget_allocation.y,
638
 
                       widget_allocation.width, widget_allocation.height);
639
 
 
640
 
    g_object_unref (gc);
641
 
    gdk_region_destroy (region);
642
 
    
643
 
    return TRUE;
644
 
}
645
 
 
646
 
void
647
 
foo_scroll_area_get_viewport (FooScrollArea *scroll_area,
648
 
                              GdkRectangle  *viewport)
649
 
{
650
 
    g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area));
651
 
    
652
 
    if (!viewport)
653
 
        return;
654
 
    
655
 
    get_viewport (scroll_area, viewport);
656
 
}
657
 
 
658
 
static void
659
 
process_event (FooScrollArea           *scroll_area,
660
 
               FooScrollAreaEventType   input_type,
661
 
               int                      x,
662
 
               int                      y);
663
 
 
664
 
static void
665
 
emit_viewport_changed (FooScrollArea *scroll_area,
666
 
                       GdkRectangle  *new_viewport,
667
 
                       GdkRectangle  *old_viewport)
668
 
{
669
 
    int px, py;
670
 
    g_signal_emit (scroll_area, signals[VIEWPORT_CHANGED], 0, 
671
 
                   new_viewport, old_viewport);
672
 
    
673
 
    gdk_window_get_pointer (scroll_area->priv->input_window, &px, &py, NULL);
674
 
    
675
 
#if 0
676
 
    g_print ("procc\n");
677
 
#endif
678
 
    
679
 
    process_event (scroll_area, FOO_MOTION, px, py);
680
 
}
681
 
 
682
 
static void
683
 
clamp_adjustment (GtkAdjustment *adj)
684
 
{
685
 
    if (gtk_adjustment_get_upper (adj) >= gtk_adjustment_get_page_size (adj))
686
 
        gtk_adjustment_set_value (adj, CLAMP (gtk_adjustment_get_value (adj), 0.0,
687
 
                                              gtk_adjustment_get_upper (adj)
688
 
                                               - gtk_adjustment_get_page_size (adj)));
689
 
    else
690
 
        gtk_adjustment_set_value (adj, 0.0);
691
 
    
692
 
    gtk_adjustment_changed (adj);
693
 
}
694
 
 
695
 
static gboolean
696
 
set_adjustment_values (FooScrollArea *scroll_area)
697
 
{
698
 
    GtkAllocation allocation;
699
 
 
700
 
    GtkAdjustment *hadj = scroll_area->priv->hadj;
701
 
    GtkAdjustment *vadj = scroll_area->priv->vadj;
702
 
    
703
 
    /* Horizontal */
704
 
    gtk_widget_get_allocation (GTK_WIDGET (scroll_area), &allocation);
705
 
    g_object_freeze_notify (G_OBJECT (hadj));
706
 
    gtk_adjustment_set_page_size (hadj, allocation.width);
707
 
    gtk_adjustment_set_step_increment (hadj, 0.1 * allocation.width);
708
 
    gtk_adjustment_set_page_increment (hadj, 0.9 * allocation.width);
709
 
    gtk_adjustment_set_lower (hadj, 0.0);
710
 
    gtk_adjustment_set_upper (hadj, scroll_area->priv->width);
711
 
    g_object_thaw_notify (G_OBJECT (hadj));
712
 
    
713
 
    /* Vertical */
714
 
    g_object_freeze_notify (G_OBJECT (vadj));
715
 
    gtk_adjustment_set_page_size (vadj, allocation.height);
716
 
    gtk_adjustment_set_step_increment (vadj, 0.1 * allocation.height);
717
 
    gtk_adjustment_set_page_increment (vadj, 0.9 * allocation.height);
718
 
    gtk_adjustment_set_lower (vadj, 0.0);
719
 
    gtk_adjustment_set_upper (vadj, scroll_area->priv->height);
720
 
    g_object_thaw_notify (G_OBJECT (vadj));
721
 
 
722
 
    clamp_adjustment (hadj);
723
 
    clamp_adjustment (vadj);
724
 
    
725
 
    return TRUE;
726
 
}
727
 
 
728
 
static void
729
 
foo_scroll_area_realize (GtkWidget *widget)
730
 
{
731
 
    FooScrollArea *area = FOO_SCROLL_AREA (widget);
732
 
    GdkWindowAttr attributes;
733
 
    GtkAllocation widget_allocation;
734
 
    GdkWindow *window;
735
 
    gint attributes_mask;
736
 
 
737
 
    gtk_widget_get_allocation (widget, &widget_allocation);
738
 
    gtk_widget_set_realized (widget, TRUE);
739
 
    
740
 
    attributes.window_type = GDK_WINDOW_CHILD;
741
 
    attributes.x = widget_allocation.x;
742
 
    attributes.y = widget_allocation.y;
743
 
    attributes.width = widget_allocation.width;
744
 
    attributes.height = widget_allocation.height;
745
 
    attributes.wclass = GDK_INPUT_ONLY;
746
 
    attributes.event_mask = gtk_widget_get_events (widget);
747
 
    attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
748
 
                              GDK_BUTTON_RELEASE_MASK |
749
 
                              GDK_BUTTON1_MOTION_MASK |
750
 
                              GDK_BUTTON2_MOTION_MASK |
751
 
                              GDK_BUTTON3_MOTION_MASK |
752
 
                              GDK_POINTER_MOTION_MASK |
753
 
                              GDK_ENTER_NOTIFY_MASK |
754
 
                              GDK_LEAVE_NOTIFY_MASK);
755
 
    
756
 
    attributes_mask = GDK_WA_X | GDK_WA_Y;
757
 
 
758
 
    window = gtk_widget_get_parent_window (widget);
759
 
    gtk_widget_set_window (widget, window);
760
 
    g_object_ref (window);
761
 
    
762
 
    area->priv->input_window = gdk_window_new (window,
763
 
                                               &attributes, attributes_mask);
764
 
    area->priv->pixmap = gdk_pixmap_new (window,
765
 
                                         widget_allocation.width,
766
 
                                         widget_allocation.height,
767
 
                                         -1);
768
 
    gdk_window_set_user_data (area->priv->input_window, area);
769
 
    
770
 
    gtk_widget_style_attach (widget);
771
 
}
772
 
 
773
 
static void
774
 
foo_scroll_area_unrealize (GtkWidget *widget)
775
 
{
776
 
    FooScrollArea *area = FOO_SCROLL_AREA (widget);
777
 
    
778
 
    if (area->priv->input_window)
779
 
    {
780
 
        gdk_window_set_user_data (area->priv->input_window, NULL);
781
 
        gdk_window_destroy (area->priv->input_window);
782
 
        area->priv->input_window = NULL;
783
 
    }
784
 
    
785
 
    GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
786
 
}
787
 
 
788
 
static GdkPixmap *
789
 
create_new_pixmap (GtkWidget *widget,
790
 
                   GdkPixmap *old)
791
 
{
792
 
    GtkAllocation widget_allocation;
793
 
    GdkPixmap *new;
794
 
 
795
 
    gtk_widget_get_allocation (widget, &widget_allocation);
796
 
    new = gdk_pixmap_new (gtk_widget_get_window (widget),
797
 
                          widget_allocation.width,
798
 
                          widget_allocation.height,
799
 
                          -1);
800
 
 
801
 
    /* Unfortunately we don't know in which direction we were resized,
802
 
     * so we just assume we were dragged from the south-east corner.
803
 
     *
804
 
     * Although, maybe we could get the root coordinates of the input-window?
805
 
     * That might just work, actually. We need to make sure metacity uses
806
 
     * static gravity for the window before this will be useful.
807
 
     */
808
 
    simple_draw_drawable (new, old, 0, 0, 0, 0, -1, -1);
809
 
 
810
 
    return new;
811
 
}
812
 
                   
813
 
static void
814
 
allocation_to_canvas_region (FooScrollArea *area,
815
 
                             GdkRegion *region)
816
 
{
817
 
    gdk_region_offset (region, area->priv->x_offset, area->priv->y_offset);
818
 
}
819
 
                             
820
 
 
821
 
static void
822
 
foo_scroll_area_size_allocate (GtkWidget     *widget,
823
 
                               GtkAllocation *allocation)
824
 
{
825
 
    FooScrollArea *scroll_area = FOO_SCROLL_AREA (widget);
826
 
    GdkRectangle new_viewport;
827
 
    GdkRectangle old_viewport;
828
 
    GdkRegion *old_allocation;
829
 
    GdkRegion *invalid;
830
 
    GtkAllocation widget_allocation;
831
 
 
832
 
    get_viewport (scroll_area, &old_viewport);
833
 
 
834
 
    gtk_widget_get_allocation (widget, &widget_allocation);
835
 
    old_allocation = gdk_region_rectangle (&widget_allocation);
836
 
    gdk_region_offset (old_allocation,
837
 
                       -widget_allocation.x, -widget_allocation.y);
838
 
    invalid = gdk_region_rectangle (allocation);
839
 
    gdk_region_offset (invalid, -allocation->x, -allocation->y);
840
 
    gdk_region_xor (invalid, old_allocation);
841
 
    allocation_to_canvas_region (scroll_area, invalid);
842
 
    foo_scroll_area_invalidate_region (scroll_area, invalid);
843
 
    gdk_region_destroy (old_allocation);
844
 
    gdk_region_destroy (invalid);
845
 
 
846
 
    gtk_widget_set_allocation (widget, allocation);
847
 
    
848
 
    if (scroll_area->priv->input_window)
849
 
    {
850
 
        GdkPixmap *new_pixmap;
851
 
        
852
 
        gdk_window_move_resize (scroll_area->priv->input_window,
853
 
                                allocation->x, allocation->y,
854
 
                                allocation->width, allocation->height);
855
 
 
856
 
        new_pixmap = create_new_pixmap (widget, scroll_area->priv->pixmap);
857
 
 
858
 
        g_object_unref (scroll_area->priv->pixmap);
859
 
 
860
 
        scroll_area->priv->pixmap = new_pixmap;
861
 
    }
862
 
    
863
 
    get_viewport (scroll_area, &new_viewport);
864
 
    
865
 
    emit_viewport_changed (scroll_area, &new_viewport, &old_viewport);
866
 
}
867
 
 
868
 
static void
869
 
emit_input (FooScrollArea *scroll_area,
870
 
            FooScrollAreaEventType type,
871
 
            int                    x,
872
 
            int                    y,
873
 
            FooScrollAreaEventFunc func,
874
 
            gpointer            data)
875
 
{
876
 
    FooScrollAreaEvent event;
877
 
    
878
 
    if (!func)
879
 
        return;
880
 
 
881
 
    if (type != FOO_MOTION)
882
 
        emit_input (scroll_area, FOO_MOTION, x, y, func, data);
883
 
    
884
 
#if 0
885
 
    x += scroll_area->priv->x_offset;
886
 
    y += scroll_area->priv->y_offset;
887
 
#endif
888
 
    
889
 
    event.type = type;
890
 
    event.x = x;
891
 
    event.y = y;
892
 
    
893
 
    func (scroll_area, &event, data);
894
 
}
895
 
 
896
 
#if 0
897
 
static void
898
 
print_path (const char *header,
899
 
            cairo_path_t *path)
900
 
{
901
 
    int i;
902
 
 
903
 
    g_print ("%s\n", header);
904
 
 
905
 
    for (i=0; i < path->num_data; i += path->data[i].header.length)
906
 
    {
907
 
        cairo_path_data_t *data = &(path->data[i]);
908
 
        
909
 
        switch (data->header.type)
910
 
        {
911
 
        case CAIRO_PATH_MOVE_TO:
912
 
            g_print ("move to:    %f, %f\n", data[1].point.x, data[1].point.y);
913
 
            break;
914
 
            
915
 
        case CAIRO_PATH_LINE_TO:
916
 
            g_print ("line to:    %f, %f\n", data[1].point.x, data[1].point.y);
917
 
            break;
918
 
            
919
 
        case CAIRO_PATH_CURVE_TO:
920
 
            g_print ("curve to:   %f, %f\n", data[1].point.x, data[1].point.y);
921
 
            g_print ("            %f, %f\n", data[1].point.x, data[1].point.y);
922
 
            g_print ("            %f, %f\n", data[1].point.x, data[1].point.y);
923
 
            break;
924
 
            
925
 
        case CAIRO_PATH_CLOSE_PATH:
926
 
            break;
927
 
        }
928
 
    }
929
 
}
930
 
#endif
931
 
 
932
 
static void
933
 
process_event (FooScrollArea           *scroll_area,
934
 
               FooScrollAreaEventType   input_type,
935
 
               int                      x,
936
 
               int                      y)
937
 
{
938
 
    GtkWidget *widget = GTK_WIDGET (scroll_area);
939
 
    int i;
940
 
 
941
 
    allocation_to_canvas (scroll_area, &x, &y);
942
 
    
943
 
    if (scroll_area->priv->grabbed)
944
 
    {
945
 
        emit_input (scroll_area, input_type, x, y,
946
 
                    scroll_area->priv->grab_func,
947
 
                    scroll_area->priv->grab_data);
948
 
        return;
949
 
    }
950
 
 
951
 
    
952
 
#if 0
953
 
    x += widget->allocation.x;
954
 
    y += widget->allocation.y;
955
 
#endif
956
 
 
957
 
#if 0
958
 
    g_print ("number of input regions: %d\n", scroll_area->priv->input_regions->len);
959
 
#endif
960
 
    
961
 
    for (i = 0; i < scroll_area->priv->input_regions->len; ++i)
962
 
    {
963
 
        InputRegion *region = scroll_area->priv->input_regions->pdata[i];
964
 
 
965
 
#if 0
966
 
        g_print ("%d ", i);
967
 
        print_region ("region:", region->region);
968
 
#endif
969
 
        
970
 
        if (gdk_region_point_in (region->region, x, y))
971
 
        {
972
 
            InputPath *path;
973
 
 
974
 
            path = region->paths;
975
 
            while (path)
976
 
            {
977
 
                cairo_t *cr;
978
 
                gboolean inside;
979
 
 
980
 
                cr = gdk_cairo_create (gtk_widget_get_window (widget));
981
 
                cairo_set_fill_rule (cr, path->fill_rule);
982
 
                cairo_set_line_width (cr, path->line_width);
983
 
                cairo_append_path (cr, path->path);
984
 
 
985
 
                if (path->is_stroke)
986
 
                    inside = cairo_in_stroke (cr, x, y);
987
 
                else
988
 
                    inside = cairo_in_fill (cr, x, y);
989
 
 
990
 
                cairo_destroy (cr);
991
 
                
992
 
                if (inside)
993
 
                {
994
 
                    emit_input (scroll_area, input_type,
995
 
                                x, y,
996
 
                                path->func,
997
 
                                path->data);
998
 
                    return;
999
 
                }
1000
 
                
1001
 
                path = path->next;
1002
 
            }
1003
 
 
1004
 
            /* Since the regions are all disjoint, no other region
1005
 
             * can match. Of course we could be clever and try and
1006
 
             * sort the regions, but so far I have been unable to
1007
 
             * make this loop show up on a profile.
1008
 
             */
1009
 
            return;
1010
 
        }
1011
 
    }
1012
 
}
1013
 
 
1014
 
static void
1015
 
process_gdk_event (FooScrollArea *scroll_area,
1016
 
                   int            x,
1017
 
                   int            y,
1018
 
                   GdkEvent      *event)
1019
 
{
1020
 
    FooScrollAreaEventType input_type;
1021
 
    
1022
 
    if (event->type == GDK_BUTTON_PRESS)
1023
 
        input_type = FOO_BUTTON_PRESS;
1024
 
    else if (event->type == GDK_BUTTON_RELEASE)
1025
 
        input_type = FOO_BUTTON_RELEASE;
1026
 
    else if (event->type == GDK_MOTION_NOTIFY)
1027
 
        input_type = FOO_MOTION;
1028
 
    else
1029
 
        return;
1030
 
    
1031
 
    process_event (scroll_area, input_type, x, y);
1032
 
}
1033
 
 
1034
 
static gboolean
1035
 
foo_scroll_area_button_press (GtkWidget *widget,
1036
 
                              GdkEventButton *event)
1037
 
{
1038
 
    FooScrollArea *area = FOO_SCROLL_AREA (widget);
1039
 
    
1040
 
    process_gdk_event (area, event->x, event->y, (GdkEvent *)event);
1041
 
    
1042
 
    return TRUE;
1043
 
}
1044
 
 
1045
 
static gboolean
1046
 
foo_scroll_area_button_release (GtkWidget *widget,
1047
 
                                GdkEventButton *event)
1048
 
{
1049
 
    FooScrollArea *area = FOO_SCROLL_AREA (widget);
1050
 
    
1051
 
    process_gdk_event (area, event->x, event->y, (GdkEvent *)event);
1052
 
    
1053
 
    return FALSE;
1054
 
}
1055
 
 
1056
 
static gboolean
1057
 
foo_scroll_area_motion (GtkWidget *widget,
1058
 
                        GdkEventMotion *event)
1059
 
{
1060
 
    FooScrollArea *area = FOO_SCROLL_AREA (widget);
1061
 
    
1062
 
    process_gdk_event (area, event->x, event->y, (GdkEvent *)event);
1063
 
    return TRUE;
1064
 
}
1065
 
 
1066
 
void
1067
 
foo_scroll_area_set_size_fixed_y (FooScrollArea        *scroll_area,
1068
 
                                  int                   width,
1069
 
                                  int                   height,
1070
 
                                  int                   old_y,
1071
 
                                  int                   new_y)
1072
 
{
1073
 
    scroll_area->priv->width = width;
1074
 
    scroll_area->priv->height = height;
1075
 
    
1076
 
#if 0
1077
 
    g_print ("diff: %d\n", new_y - old_y);
1078
 
#endif
1079
 
    g_object_thaw_notify (G_OBJECT (scroll_area->priv->vadj));
1080
 
    gtk_adjustment_set_value (scroll_area->priv->vadj, new_y);
1081
 
    
1082
 
    set_adjustment_values (scroll_area);
1083
 
    g_object_thaw_notify (G_OBJECT (scroll_area->priv->vadj));
1084
 
}
1085
 
 
1086
 
void
1087
 
foo_scroll_area_set_size (FooScrollArea        *scroll_area,
1088
 
                          int                   width,
1089
 
                          int                   height)
1090
 
{
1091
 
    g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area));
1092
 
    
1093
 
    /* FIXME: Default scroll algorithm should probably be to
1094
 
     * keep the same *area* outside the screen as before.
1095
 
     *
1096
 
     * For wrapper widgets that will do something roughly
1097
 
     * right. For widgets that don't change size, it
1098
 
     * will do the right thing. Except for idle-layouting
1099
 
     * widgets.
1100
 
     *
1101
 
     * Maybe there should be some generic support for those
1102
 
     * widgets. Can that even be done?
1103
 
     *
1104
 
     * Should we have a version of this function using 
1105
 
     * fixed points?
1106
 
     */
1107
 
    
1108
 
    scroll_area->priv->width = width;
1109
 
    scroll_area->priv->height = height;
1110
 
    
1111
 
    set_adjustment_values (scroll_area);
1112
 
}
1113
 
 
1114
 
static void
1115
 
foo_scroll_area_size_request (GtkWidget      *widget,
1116
 
                              GtkRequisition *requisition)
1117
 
{
1118
 
    FooScrollArea *scroll_area = FOO_SCROLL_AREA (widget);
1119
 
    
1120
 
    requisition->width = scroll_area->priv->min_width;
1121
 
    requisition->height = scroll_area->priv->min_height;
1122
 
    
1123
 
#if 0
1124
 
    g_print ("request %d %d\n", requisition->width, requisition->height);
1125
 
#endif
1126
 
}
1127
 
 
1128
 
#if 0
1129
 
static void
1130
 
translate_point (double *x, double *y, gpointer data)
1131
 
{
1132
 
    int *translation = data;
1133
 
 
1134
 
    *x += translation[0];
1135
 
    *y += translation[1];
1136
 
}
1137
 
#endif
1138
 
 
1139
 
#if 0
1140
 
static void
1141
 
path_translate (cairo_path_t  *path,
1142
 
                int            dx,
1143
 
                int            dy)
1144
 
{
1145
 
    int translation[2] = {dx, dy};
1146
 
    
1147
 
    path_foreach_point (path, translate_point, translation);
1148
 
}
1149
 
#endif
1150
 
 
1151
 
static void
1152
 
translate_input_regions (FooScrollArea *scroll_area,
1153
 
                         int            dx,
1154
 
                         int            dy)
1155
 
{
1156
 
#if 0
1157
 
    int i;
1158
 
 
1159
 
    for (i = 0; i < scroll_area->priv->input_regions->len; ++i)
1160
 
    {
1161
 
        InputRegion *region = scroll_area->priv->input_regions->pdata[i];
1162
 
        InputPath *path;
1163
 
        
1164
 
        gdk_region_offset (region->region, dx, dy);
1165
 
 
1166
 
        path = region->paths;
1167
 
        while (path != NULL)
1168
 
        {
1169
 
            path_translate (path->path, dx, dy);
1170
 
            path = path->next;
1171
 
        }
1172
 
    }
1173
 
#endif
1174
 
}
1175
 
 
1176
 
#if 0
1177
 
static void
1178
 
paint_region (FooScrollArea *area, GdkRegion *region)
1179
 
{
1180
 
    int n_rects;
1181
 
    GdkRectangle *rects;
1182
 
    region = gdk_region_copy (region);
1183
 
    
1184
 
    gdk_region_get_rectangles (region, &rects, &n_rects);
1185
 
 
1186
 
    gdk_region_offset (region,
1187
 
                       GTK_WIDGET (area)->allocation.x,
1188
 
                       GTK_WIDGET (area)->allocation.y);
1189
 
 
1190
 
    GdkGC *gc = gdk_gc_new (GTK_WIDGET (area)->window);
1191
 
    gdk_gc_set_clip_region (gc, region);
1192
 
    gdk_draw_rectangle (GTK_WIDGET (area)->window, gc, TRUE, 0, 0, -1, -1);
1193
 
    g_object_unref (gc);
1194
 
    g_free (rects);
1195
 
}
1196
 
#endif
1197
 
 
1198
 
static void
1199
 
foo_scroll_area_scroll (FooScrollArea *area,
1200
 
                        gint dx, 
1201
 
                        gint dy)
1202
 
{
1203
 
    GdkRectangle allocation;
1204
 
    GdkRectangle src_area;
1205
 
    GdkRectangle move_area;
1206
 
    GdkRegion *invalid_region;
1207
 
 
1208
 
    gtk_widget_get_allocation (GTK_WIDGET (area), &allocation);
1209
 
    allocation.x = 0;
1210
 
    allocation.y = 0;
1211
 
 
1212
 
    src_area = allocation;
1213
 
    src_area.x -= dx;
1214
 
    src_area.y -= dy;
1215
 
 
1216
 
    invalid_region = gdk_region_rectangle (&allocation);
1217
 
    
1218
 
    if (gdk_rectangle_intersect (&allocation, &src_area, &move_area))
1219
 
    {
1220
 
        GdkRegion *move_region;
1221
 
 
1222
 
#if 0
1223
 
        g_print ("scrolling %d %d %d %d (%d %d)\n",
1224
 
                 move_area.x, move_area.y,
1225
 
                 move_area.width, move_area.height,
1226
 
                 dx, dy);
1227
 
#endif
1228
 
        
1229
 
        simple_draw_drawable (area->priv->pixmap, area->priv->pixmap,
1230
 
                              move_area.x, move_area.y,
1231
 
                              move_area.x + dx, move_area.y + dy,
1232
 
                              move_area.width, move_area.height);
1233
 
        gtk_widget_queue_draw (GTK_WIDGET (area));
1234
 
        
1235
 
        move_region = gdk_region_rectangle (&move_area);
1236
 
        gdk_region_offset (move_region, dx, dy);
1237
 
        gdk_region_subtract (invalid_region, move_region);
1238
 
        gdk_region_destroy (move_region);
1239
 
    }
1240
 
 
1241
 
#if 0
1242
 
    paint_region (area, invalid_region);
1243
 
#endif
1244
 
    
1245
 
    allocation_to_canvas_region (area, invalid_region);
1246
 
 
1247
 
    foo_scroll_area_invalidate_region (area, invalid_region);
1248
 
    
1249
 
    gdk_region_destroy (invalid_region);
1250
 
}
1251
 
 
1252
 
static void
1253
 
foo_scrollbar_adjustment_changed (GtkAdjustment *adj,
1254
 
                                  FooScrollArea *scroll_area)
1255
 
{
1256
 
    GtkWidget *widget = GTK_WIDGET (scroll_area);
1257
 
    gint dx = 0;
1258
 
    gint dy = 0;
1259
 
    GdkRectangle old_viewport, new_viewport;
1260
 
    
1261
 
    get_viewport (scroll_area, &old_viewport);
1262
 
    
1263
 
    if (adj == scroll_area->priv->hadj)
1264
 
    {
1265
 
        /* FIXME: do we treat the offset as int or double, and,
1266
 
         * if int, how do we round?
1267
 
         */
1268
 
        dx = (int)gtk_adjustment_get_value (adj) - scroll_area->priv->x_offset;
1269
 
        scroll_area->priv->x_offset = gtk_adjustment_get_value (adj);
1270
 
    }
1271
 
    else if (adj == scroll_area->priv->vadj)
1272
 
    {
1273
 
        dy = (int)gtk_adjustment_get_value (adj) - scroll_area->priv->y_offset;
1274
 
        scroll_area->priv->y_offset = gtk_adjustment_get_value (adj);
1275
 
    }
1276
 
    else
1277
 
    {
1278
 
        g_assert_not_reached ();
1279
 
    }
1280
 
    
1281
 
    if (gtk_widget_get_realized (widget))
1282
 
    {
1283
 
        foo_scroll_area_scroll (scroll_area, -dx, -dy);
1284
 
    
1285
 
#if 0
1286
 
        window_scroll_area (widget->window, &widget->allocation, -dx, -dy);
1287
 
#endif
1288
 
        translate_input_regions (scroll_area, -dx, -dy);
1289
 
 
1290
 
#if 0
1291
 
        gdk_window_process_updates (widget->window, TRUE);
1292
 
#endif
1293
 
    }
1294
 
    
1295
 
    get_viewport (scroll_area, &new_viewport);
1296
 
    
1297
 
    emit_viewport_changed (scroll_area, &new_viewport, &old_viewport);
1298
 
}
1299
 
 
1300
 
static void
1301
 
set_one_adjustment (FooScrollArea *scroll_area,
1302
 
                    GtkAdjustment *adjustment,
1303
 
                    GtkAdjustment **location)
1304
 
{
1305
 
    g_return_if_fail (location != NULL);
1306
 
    
1307
 
    if (adjustment == *location)
1308
 
        return;
1309
 
    
1310
 
    if (!adjustment)
1311
 
        adjustment = new_adjustment ();
1312
 
    
1313
 
    g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
1314
 
    
1315
 
    if (*location)
1316
 
    {
1317
 
        g_signal_handlers_disconnect_by_func (
1318
 
            *location, foo_scrollbar_adjustment_changed, scroll_area);
1319
 
        
1320
 
        g_object_unref (*location);
1321
 
    }
1322
 
    
1323
 
    *location = adjustment;
1324
 
    
1325
 
    g_object_ref_sink (*location);
1326
 
    
1327
 
    g_signal_connect (*location, "value_changed",
1328
 
                      G_CALLBACK (foo_scrollbar_adjustment_changed),
1329
 
                      scroll_area);
1330
 
}
1331
 
 
1332
 
static void
1333
 
foo_scroll_area_set_scroll_adjustments (FooScrollArea *scroll_area,
1334
 
                                        GtkAdjustment *hadjustment,
1335
 
                                        GtkAdjustment *vadjustment)
1336
 
{
1337
 
    set_one_adjustment (scroll_area, hadjustment, &scroll_area->priv->hadj);
1338
 
    set_one_adjustment (scroll_area, vadjustment, &scroll_area->priv->vadj);
1339
 
    
1340
 
    set_adjustment_values (scroll_area);
1341
 
}
1342
 
 
1343
 
FooScrollArea *
1344
 
foo_scroll_area_new (void)
1345
 
{
1346
 
    return g_object_new (FOO_TYPE_SCROLL_AREA, NULL);
1347
 
}
1348
 
 
1349
 
void
1350
 
foo_scroll_area_set_min_size (FooScrollArea *scroll_area,
1351
 
                              int                  min_width,
1352
 
                              int            min_height)
1353
 
{
1354
 
    scroll_area->priv->min_width = min_width;
1355
 
    scroll_area->priv->min_height = min_height;
1356
 
    
1357
 
    /* FIXME: think through invalidation.
1358
 
     *
1359
 
     * Goals: - no repainting everything on size_allocate(),
1360
 
     *        - make sure input boxes are invalidated when
1361
 
     *          needed
1362
 
     */
1363
 
    gtk_widget_queue_resize (GTK_WIDGET (scroll_area));
1364
 
}
1365
 
 
1366
 
#if 0
1367
 
static void
1368
 
warn_about_adding_input_outside_expose (const char *func)
1369
 
{
1370
 
    static gboolean warned = FALSE;
1371
 
    
1372
 
    if (!warned)
1373
 
    {
1374
 
        g_warning ("%s() can only be called "
1375
 
                   "from the paint handler for the FooScrollArea\n", func);
1376
 
        
1377
 
        warned = TRUE;
1378
 
    }
1379
 
}
1380
 
#endif
1381
 
 
1382
 
static void
1383
 
user_to_device (double *x, double *y,
1384
 
                gpointer data)
1385
 
{
1386
 
    cairo_t *cr = data;
1387
 
    
1388
 
    cairo_user_to_device (cr, x, y);
1389
 
}
1390
 
 
1391
 
static InputPath *
1392
 
make_path (FooScrollArea *area,
1393
 
           cairo_t *cr,
1394
 
           gboolean is_stroke,
1395
 
           FooScrollAreaEventFunc func,
1396
 
           gpointer data)
1397
 
{
1398
 
    InputPath *path = g_new0 (InputPath, 1);
1399
 
 
1400
 
    path->is_stroke = is_stroke;
1401
 
    path->fill_rule = cairo_get_fill_rule (cr);
1402
 
    path->line_width = cairo_get_line_width (cr);
1403
 
    path->path = cairo_copy_path (cr);
1404
 
    path_foreach_point (path->path, user_to_device, cr);
1405
 
    path->func = func;
1406
 
    path->data = data;
1407
 
    path->next = area->priv->current_input->paths;
1408
 
    area->priv->current_input->paths = path;
1409
 
    return path;
1410
 
}
1411
 
 
1412
 
/* FIXME: we probably really want a
1413
 
 *
1414
 
 *      foo_scroll_area_add_input_from_fill (area, cr, ...);
1415
 
 * and
1416
 
 *      foo_scroll_area_add_input_from_stroke (area, cr, ...);
1417
 
 * as well.
1418
 
 */
1419
 
void
1420
 
foo_scroll_area_add_input_from_fill (FooScrollArea           *scroll_area,
1421
 
                                     cairo_t                 *cr,
1422
 
                                     FooScrollAreaEventFunc   func,
1423
 
                                     gpointer                 data)
1424
 
{
1425
 
    g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area));
1426
 
    g_return_if_fail (cr != NULL);
1427
 
    g_return_if_fail (scroll_area->priv->current_input);
1428
 
 
1429
 
    make_path (scroll_area, cr, FALSE, func, data);
1430
 
}
1431
 
 
1432
 
void
1433
 
foo_scroll_area_add_input_from_stroke (FooScrollArea           *scroll_area,
1434
 
                                       cairo_t                  *cr,
1435
 
                                       FooScrollAreaEventFunc   func,
1436
 
                                       gpointer                 data)
1437
 
{
1438
 
    g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area));
1439
 
    g_return_if_fail (cr != NULL);
1440
 
    g_return_if_fail (scroll_area->priv->current_input);
1441
 
 
1442
 
    make_path (scroll_area, cr, TRUE, func, data);
1443
 
}
1444
 
 
1445
 
void
1446
 
foo_scroll_area_invalidate (FooScrollArea *scroll_area)
1447
 
{
1448
 
    GtkAllocation allocation;
1449
 
    GtkWidget *widget = GTK_WIDGET (scroll_area);
1450
 
 
1451
 
    gtk_widget_get_allocation (widget, &allocation);
1452
 
    foo_scroll_area_invalidate_rect (scroll_area,
1453
 
                                     scroll_area->priv->x_offset, scroll_area->priv->y_offset,
1454
 
                                     allocation.width,
1455
 
                                     allocation.height);
1456
 
}
1457
 
 
1458
 
static void
1459
 
canvas_to_window (FooScrollArea *area,
1460
 
                  GdkRegion *region)
1461
 
{
1462
 
    GtkAllocation allocation;
1463
 
    GtkWidget *widget = GTK_WIDGET (area);
1464
 
    
1465
 
    gtk_widget_get_allocation (widget, &allocation);
1466
 
    gdk_region_offset (region,
1467
 
                       -area->priv->x_offset + allocation.x,
1468
 
                       -area->priv->y_offset + allocation.y);
1469
 
}
1470
 
 
1471
 
static void
1472
 
window_to_canvas (FooScrollArea *area,
1473
 
                  GdkRegion *region)
1474
 
{
1475
 
    GtkAllocation allocation;
1476
 
    GtkWidget *widget = GTK_WIDGET (area);
1477
 
 
1478
 
    gtk_widget_get_allocation (widget, &allocation);
1479
 
    gdk_region_offset (region,
1480
 
                       area->priv->x_offset - allocation.x,
1481
 
                       area->priv->y_offset - allocation.y);
1482
 
}
1483
 
 
1484
 
void
1485
 
foo_scroll_area_invalidate_region (FooScrollArea *area,
1486
 
                                   GdkRegion     *region)
1487
 
{
1488
 
    GtkWidget *widget;
1489
 
 
1490
 
    g_return_if_fail (FOO_IS_SCROLL_AREA (area));
1491
 
 
1492
 
    widget = GTK_WIDGET (area);
1493
 
 
1494
 
    gdk_region_union (area->priv->update_region, region);
1495
 
 
1496
 
    if (gtk_widget_get_realized (widget))
1497
 
    {
1498
 
        canvas_to_window (area, region);
1499
 
        
1500
 
        gdk_window_invalidate_region (gtk_widget_get_window (widget),
1501
 
                                      region, TRUE);
1502
 
        
1503
 
        window_to_canvas (area, region);
1504
 
    }
1505
 
}
1506
 
 
1507
 
void
1508
 
foo_scroll_area_invalidate_rect (FooScrollArea *scroll_area,
1509
 
                                 int            x,
1510
 
                                 int            y,
1511
 
                                 int            width,
1512
 
                                 int            height)
1513
 
{
1514
 
    GdkRectangle rect = { x, y, width, height };
1515
 
    GdkRegion *region;
1516
 
    
1517
 
    g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area));
1518
 
 
1519
 
    region = gdk_region_rectangle (&rect);
1520
 
 
1521
 
    foo_scroll_area_invalidate_region (scroll_area, region);
1522
 
 
1523
 
    gdk_region_destroy (region);
1524
 
}
1525
 
 
1526
 
void
1527
 
foo_scroll_area_begin_grab (FooScrollArea *scroll_area,
1528
 
                            FooScrollAreaEventFunc func,
1529
 
                            gpointer       input_data)
1530
 
{
1531
 
    g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area));
1532
 
    g_return_if_fail (!scroll_area->priv->grabbed);
1533
 
    
1534
 
    scroll_area->priv->grabbed = TRUE;
1535
 
    scroll_area->priv->grab_func = func;
1536
 
    scroll_area->priv->grab_data = input_data;
1537
 
    
1538
 
    /* FIXME: we should probably take a server grab */
1539
 
    /* Also, maybe there should be support for setting the grab cursor */
1540
 
}
1541
 
 
1542
 
void
1543
 
foo_scroll_area_end_grab (FooScrollArea *scroll_area)
1544
 
{
1545
 
    g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area));
1546
 
    
1547
 
    scroll_area->priv->grabbed = FALSE;
1548
 
    scroll_area->priv->grab_func = NULL;
1549
 
    scroll_area->priv->grab_data = NULL;
1550
 
}
1551
 
 
1552
 
gboolean
1553
 
foo_scroll_area_is_grabbed (FooScrollArea *scroll_area)
1554
 
{
1555
 
    return scroll_area->priv->grabbed;
1556
 
}
1557
 
 
1558
 
void
1559
 
foo_scroll_area_set_viewport_pos (FooScrollArea  *scroll_area,
1560
 
                                  int             x,
1561
 
                                  int             y)
1562
 
{
1563
 
    g_object_freeze_notify (G_OBJECT (scroll_area->priv->hadj));
1564
 
    g_object_freeze_notify (G_OBJECT (scroll_area->priv->vadj));
1565
 
    gtk_adjustment_set_value (scroll_area->priv->hadj, x);
1566
 
    gtk_adjustment_set_value (scroll_area->priv->vadj, y);
1567
 
 
1568
 
    set_adjustment_values (scroll_area);
1569
 
    g_object_thaw_notify (G_OBJECT (scroll_area->priv->hadj));
1570
 
    g_object_thaw_notify (G_OBJECT (scroll_area->priv->vadj));
1571
 
}
1572
 
 
1573
 
static gboolean
1574
 
rect_contains (const GdkRectangle *rect, int x, int y)
1575
 
{
1576
 
    return (x >= rect->x                &&
1577
 
            y >= rect->y                &&
1578
 
            x  < rect->x + rect->width  &&
1579
 
            y  < rect->y + rect->height);
1580
 
}
1581
 
 
1582
 
static void
1583
 
stop_scrolling (FooScrollArea *area)
1584
 
{
1585
 
#if 0
1586
 
    g_print ("stop scrolling\n");
1587
 
#endif
1588
 
    if (area->priv->auto_scroll_info)
1589
 
    {
1590
 
        g_source_remove (area->priv->auto_scroll_info->timeout_id);
1591
 
        g_timer_destroy (area->priv->auto_scroll_info->timer);
1592
 
        g_free (area->priv->auto_scroll_info);
1593
 
        
1594
 
        area->priv->auto_scroll_info = NULL;
1595
 
    }
1596
 
}
1597
 
 
1598
 
static gboolean
1599
 
scroll_idle (gpointer data)
1600
 
{
1601
 
    GdkRectangle viewport, new_viewport;
1602
 
    FooScrollArea *area = data;
1603
 
    AutoScrollInfo *info = area->priv->auto_scroll_info;
1604
 
#if 0
1605
 
    int dx, dy;
1606
 
#endif
1607
 
    int new_x, new_y;
1608
 
    double elapsed;
1609
 
    
1610
 
    get_viewport (area, &viewport);
1611
 
    
1612
 
#if 0
1613
 
    g_print ("old info: %d %d\n", info->dx, info->dy);
1614
 
    
1615
 
    g_print ("timeout (%d %d)\n", dx, dy);
1616
 
#endif
1617
 
    
1618
 
#if 0
1619
 
    viewport.x += info->dx;
1620
 
    viewport.y += info->dy;
1621
 
#endif
1622
 
    
1623
 
#if 0
1624
 
    g_print ("new info %d %d\n", info->dx, info->dy);
1625
 
#endif
1626
 
 
1627
 
    elapsed = g_timer_elapsed (info->timer, NULL);
1628
 
 
1629
 
    info->res_x = elapsed * info->dx / 0.2;
1630
 
    info->res_y = elapsed * info->dy / 0.2;
1631
 
 
1632
 
#if 0
1633
 
    g_print ("%f %f\n", info->res_x, info->res_y);
1634
 
#endif
1635
 
    
1636
 
    new_x = viewport.x + info->res_x;
1637
 
    new_y = viewport.y + info->res_y;
1638
 
 
1639
 
#if 0
1640
 
    g_print ("%f\n", elapsed * (info->dx / 0.2));
1641
 
#endif
1642
 
    
1643
 
#if 0
1644
 
    g_print ("new_x, new_y\n: %d %d\n", new_x, new_y);
1645
 
#endif
1646
 
    
1647
 
    foo_scroll_area_set_viewport_pos (area, new_x, new_y);
1648
 
#if 0
1649
 
                                      viewport.x + info->dx,
1650
 
                                      viewport.y + info->dy);
1651
 
#endif
1652
 
 
1653
 
    get_viewport (area, &new_viewport);
1654
 
 
1655
 
    if (viewport.x == new_viewport.x            &&
1656
 
        viewport.y == new_viewport.y            &&
1657
 
        (info->res_x > 1.0                      ||
1658
 
         info->res_y > 1.0                      ||
1659
 
         info->res_x < -1.0                     ||
1660
 
         info->res_y < -1.0))
1661
 
    {
1662
 
        stop_scrolling (area);
1663
 
        
1664
 
        /* stop scrolling if it didn't have an effect */
1665
 
        return FALSE;
1666
 
    }
1667
 
    
1668
 
    return TRUE;
1669
 
}
1670
 
 
1671
 
static void
1672
 
ensure_scrolling (FooScrollArea *area,
1673
 
                  int            dx,
1674
 
                  int            dy)
1675
 
{
1676
 
    if (!area->priv->auto_scroll_info)
1677
 
    {
1678
 
#if 0
1679
 
        g_print ("start scrolling\n");
1680
 
#endif
1681
 
        area->priv->auto_scroll_info = g_new0 (AutoScrollInfo, 1);
1682
 
        area->priv->auto_scroll_info->timeout_id =
1683
 
            g_idle_add (scroll_idle, area);
1684
 
        area->priv->auto_scroll_info->timer = g_timer_new ();
1685
 
    }
1686
 
    
1687
 
#if 0
1688
 
    g_print ("setting scrolling to %d %d\n", dx, dy);
1689
 
#endif
1690
 
 
1691
 
#if 0
1692
 
    g_print ("dx, dy: %d %d\n", dx, dy);
1693
 
#endif
1694
 
    
1695
 
    area->priv->auto_scroll_info->dx = dx;
1696
 
    area->priv->auto_scroll_info->dy = dy;
1697
 
}
1698
 
 
1699
 
void
1700
 
foo_scroll_area_auto_scroll (FooScrollArea *scroll_area,
1701
 
                             FooScrollAreaEvent *event)
1702
 
{
1703
 
    GdkRectangle viewport;
1704
 
    
1705
 
    get_viewport (scroll_area, &viewport);
1706
 
    
1707
 
    if (rect_contains (&viewport, event->x, event->y))
1708
 
    {
1709
 
        stop_scrolling (scroll_area);
1710
 
    }
1711
 
    else
1712
 
    {
1713
 
        int dx, dy;
1714
 
        
1715
 
        dx = dy = 0;
1716
 
        
1717
 
        if (event->y < viewport.y)
1718
 
        {
1719
 
            dy = event->y - viewport.y;
1720
 
            dy = MIN (dy + 2, 0);
1721
 
        }
1722
 
        else if (event->y >= viewport.y + viewport.height)
1723
 
        {
1724
 
            dy = event->y - (viewport.y + viewport.height - 1);
1725
 
            dy = MAX (dy - 2, 0);
1726
 
        }
1727
 
        
1728
 
        if (event->x < viewport.x)
1729
 
        {
1730
 
            dx = event->x - viewport.x;
1731
 
            dx = MIN (dx + 2, 0);
1732
 
        }
1733
 
        else if (event->x >= viewport.x + viewport.width)
1734
 
        {
1735
 
            dx = event->x - (viewport.x + viewport.width - 1);
1736
 
            dx = MAX (dx - 2, 0);
1737
 
        }
1738
 
 
1739
 
#if 0
1740
 
        g_print ("dx, dy: %d %d\n", dx, dy);
1741
 
#endif
1742
 
        
1743
 
        ensure_scrolling (scroll_area, dx, dy);
1744
 
    }
1745
 
}
1746
 
 
1747
 
void
1748
 
foo_scroll_area_begin_auto_scroll (FooScrollArea *scroll_area)
1749
 
{
1750
 
    /* noop  for now */
1751
 
}
1752
 
 
1753
 
void
1754
 
foo_scroll_area_end_auto_scroll (FooScrollArea *scroll_area)
1755
 
{
1756
 
    stop_scrolling (scroll_area);
1757
 
}
1758
 
 
1759
 
 
1760
 
 
1761
 
#if 0
1762
 
/*
1763
 
 * Backing Store
1764
 
 */
1765
 
struct BackingStore
1766
 
{
1767
 
    GdkPixmap *pixmap;
1768
 
    GdkRegion *update_region;
1769
 
    int width;
1770
 
    int height;
1771
 
};
1772
 
 
1773
 
static BackingStore *
1774
 
backing_store_new (GdkWindow *window,
1775
 
                   int width, int height)
1776
 
{
1777
 
    BackingStore *store = g_new0 (BackingStore, 1);
1778
 
    GdkRectangle rect = { 0, 0, width, height };
1779
 
    
1780
 
    store->pixmap = gdk_pixmap_new (window, width, height, -1);
1781
 
    store->update_region = gdk_region_rectangle (&rect);
1782
 
    store->width = width;
1783
 
    store->height = height;
1784
 
 
1785
 
    return store;
1786
 
}
1787
 
 
1788
 
static void
1789
 
backing_store_free (BackingStore *store)
1790
 
{
1791
 
    g_object_unref (store->pixmap);
1792
 
    gdk_region_destroy (store->update_region);
1793
 
    g_free (store);
1794
 
}
1795
 
 
1796
 
static void
1797
 
backing_store_draw (BackingStore  *store, 
1798
 
                    GdkDrawable   *dest,
1799
 
                    GdkRegion     *clip,
1800
 
                    int            x,
1801
 
                    int            y)
1802
 
{
1803
 
    GdkGC *gc = gdk_gc_new (dest);
1804
 
    
1805
 
    gdk_gc_set_clip_region (gc, clip);
1806
 
 
1807
 
    gdk_draw_drawable (dest, gc, store->pixmap,
1808
 
                       0, 0, x, y, store->width, store->height);
1809
 
 
1810
 
    g_object_unref (gc);
1811
 
}
1812
 
 
1813
 
static void
1814
 
backing_store_scroll (BackingStore *store,
1815
 
                      int           dx,
1816
 
                      int           dy)
1817
 
{
1818
 
    GdkGC *gc = gdk_gc_new (store->pixmap);
1819
 
    GdkRectangle rect;
1820
 
    
1821
 
    gdk_draw_drawable (store->pixmap, gc, store->pixmap,
1822
 
                       0, 0, dx, dy,
1823
 
                       store->width, store->height);
1824
 
 
1825
 
    /* Invalidate vertically */
1826
 
    rect.x = 0;
1827
 
    rect.width = store->width;
1828
 
 
1829
 
    if (dy > 0)
1830
 
    {
1831
 
        rect.y = 0;
1832
 
        rect.height = dy;
1833
 
    }
1834
 
    else
1835
 
    {
1836
 
        rect.y = store->height + dy;
1837
 
        rect.y = -dy;
1838
 
    }
1839
 
 
1840
 
    gdk_region_union_with_rect (store->update_region, &rect);
1841
 
    
1842
 
    /* Invalidate horizontally */
1843
 
    rect.y = 0;
1844
 
    rect.height = store->height;
1845
 
 
1846
 
    if (dx > 0)
1847
 
    {
1848
 
        rect.x = 0;
1849
 
        rect.width = dx;
1850
 
    }
1851
 
    else
1852
 
    {
1853
 
        rect.x = store->width + dx;
1854
 
        rect.width = -dx;
1855
 
    }
1856
 
 
1857
 
    gdk_region_union_with_rect (store->update_region, &rect);
1858
 
}
1859
 
 
1860
 
static void
1861
 
backing_store_invalidate_rect (BackingStore *store,
1862
 
                               GdkRectangle *rect)
1863
 
{
1864
 
    gdk_region_union_with_rect (store->update_region, rect);
1865
 
}
1866
 
 
1867
 
static void
1868
 
backing_store_invalidate_region (BackingStore *store,
1869
 
                                 GdkRegion *region)
1870
 
{
1871
 
    gdk_region_union (store->update_region, region);
1872
 
}
1873
 
 
1874
 
static void
1875
 
backing_store_invalidate_all (BackingStore *store)
1876
 
{
1877
 
    GdkRectangle rect = { 0, 0, store->width, store->height };
1878
 
    gdk_region_destroy (store->update_region);
1879
 
    store->update_region = gdk_region_rectangle (&rect);
1880
 
}
1881
 
 
1882
 
static void
1883
 
backing_store_resize (BackingStore *store,
1884
 
                      int           width,
1885
 
                      int           height)
1886
 
{
1887
 
    GdkPixmap *pixmap = gdk_pixmap_new (store->pixmap, width, height, -1);
1888
 
 
1889
 
    /* Unfortunately we don't know in which direction we were resized,
1890
 
     * so we just assume we were dragged from the south-east corner.
1891
 
     *
1892
 
     * Although, maybe we could get the root coordinates of the input-window?
1893
 
     * That might just work, actually. We need to make sure metacity uses
1894
 
     * static gravity for the window before this will be useful.
1895
 
     */
1896
 
    simple_draw_drawable (pixmap, store->pixmap, 0, 0, 0, 0, -1, -1);
1897
 
 
1898
 
    g_object_unref (store->pixmap);
1899
 
 
1900
 
    store->pixmap = pixmap;
1901
 
 
1902
 
    /* FIXME: invalidate uncovered strip only */
1903
 
 
1904
 
    backing_store_invalidate_all (store);
1905
 
}
1906
 
 
1907
 
static void
1908
 
cclip_to_region (cairo_t *cr, GdkRegion *region)
1909
 
{
1910
 
    int n_rects;
1911
 
    GdkRectangle *rects;
1912
 
 
1913
 
    gdk_region_get_rectangles (region, &rects, &n_rects);
1914
 
 
1915
 
    cairo_new_path (cr);
1916
 
    while (n_rects--)
1917
 
    {
1918
 
        GdkRectangle *rect = &(rects[n_rects]);
1919
 
 
1920
 
        cairo_rectangle (cr, rect->x, rect->y, rect->width, rect->height);
1921
 
    }
1922
 
    cairo_clip (cr);
1923
 
 
1924
 
    g_free (rects);
1925
 
}
1926
 
 
1927
 
static void
1928
 
backing_store_process_updates (BackingStore *store,
1929
 
                               ExposeFunc    func,
1930
 
                               gpointer      data)
1931
 
{
1932
 
    cairo_t *cr = gdk_cairo_create (store->pixmap);
1933
 
    GdkRegion *region = store->update_region;
1934
 
    store->update_region = gdk_region_new ();
1935
 
 
1936
 
    cclip_to_region (cr, store->update_region);
1937
 
 
1938
 
    func (cr, store->update_region, data);
1939
 
 
1940
 
    gdk_region_destroy (region);
1941
 
    cairo_destroy (cr);
1942
 
}
1943
 
 
1944
 
#endif