Code

Warning cleanup (including OFFSET_OF warning)
[inkscape.git] / src / display / sp-canvas.cpp
1 #define __SP_CANVAS_C__
3 /** \file
4  * Port of GnomeCanvas for Inkscape needs
5  *
6  * Authors:
7  *   Federico Mena <federico@nuclecu.unam.mx>
8  *   Raph Levien <raph@gimp.org>
9  *   Lauris Kaplinski <lauris@kaplinski.com>
10  *   fred
11  *   bbyak
12  *
13  * Copyright (C) 1998 The Free Software Foundation
14  * Copyright (C) 2002-2006 authors
15  *
16  * Released under GNU GPL, read the file 'COPYING' for more information
17  */
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
23 #include <libnr/nr-pixblock.h>
25 #include <gtk/gtkmain.h>
26 #include <gtk/gtksignal.h>
28 #include <gtkmm.h>
30 #include <helper/sp-marshal.h>
31 #include <display/sp-canvas.h>
32 #include "display-forward.h"
33 #include <libnr/nr-matrix-fns.h>
34 #include <libnr/nr-matrix-ops.h>
35 #include <libnr/nr-convex-hull.h>
36 #include "prefs-utils.h"
37 #include "box3d-context.h"
38 #include "inkscape.h"
39 #include "sodipodi-ctrlrect.h"
40 #if ENABLE_LCMS
41 #include "color-profile-fns.h"
42 #endif // ENABLE_LCMS
44 // Define this to visualize the regions to be redrawn
45 //#define DEBUG_REDRAW 1;
47 // Tiles are a way to minimize the number of redraws, eliminating too small redraws.
48 // The canvas stores a 2D array of ints, each representing a TILE_SIZExTILE_SIZE pixels tile.
49 // If any part of it is dirtied, the entire tile is dirtied (its int is nonzero) and repainted.
50 #define TILE_SIZE 16
52 enum {
53     RENDERMODE_NORMAL,
54     RENDERMODE_NOAA,
55     RENDERMODE_OUTLINE
56 };
58 static gint const sp_canvas_update_priority = G_PRIORITY_HIGH_IDLE;
60 #define SP_CANVAS_WINDOW(c) (((GtkWidget *) (c))->window)
62 enum {
63     SP_CANVAS_ITEM_VISIBLE = 1 << 7,
64     SP_CANVAS_ITEM_NEED_UPDATE = 1 << 8,
65     SP_CANVAS_ITEM_NEED_AFFINE = 1 << 9
66 };
68 /**
69  * A group of Items.
70  */
71 struct SPCanvasGroup {
72     SPCanvasItem item;
74     GList *items, *last;
75 };
77 /**
78  * The SPCanvasGroup vtable.
79  */
80 struct SPCanvasGroupClass {
81     SPCanvasItemClass parent_class;
82 };
84 /**
85  * The SPCanvas vtable.
86  */
87 struct SPCanvasClass {
88     GtkWidgetClass parent_class;
89 };
91 static void group_add (SPCanvasGroup *group, SPCanvasItem *item);
92 static void group_remove (SPCanvasGroup *group, SPCanvasItem *item);
94 /* SPCanvasItem */
96 enum {ITEM_EVENT, ITEM_LAST_SIGNAL};
99 static void sp_canvas_request_update (SPCanvas *canvas);
101 static void sp_canvas_item_class_init (SPCanvasItemClass *klass);
102 static void sp_canvas_item_init (SPCanvasItem *item);
103 static void sp_canvas_item_dispose (GObject *object);
104 static void sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args);
106 static int emit_event (SPCanvas *canvas, GdkEvent *event);
108 static guint item_signals[ITEM_LAST_SIGNAL] = { 0 };
110 static GtkObjectClass *item_parent_class;
112 /**
113  * Registers the SPCanvasItem class with Glib and returns its type number.
114  */
115 GType
116 sp_canvas_item_get_type (void)
118     static GType type = 0;
119     if (!type) {
120         static GTypeInfo const info = {
121             sizeof (SPCanvasItemClass),
122             NULL, NULL,
123             (GClassInitFunc) sp_canvas_item_class_init,
124             NULL, NULL,
125             sizeof (SPCanvasItem),
126             0,
127             (GInstanceInitFunc) sp_canvas_item_init,
128             NULL
129         };
130         type = g_type_register_static (GTK_TYPE_OBJECT, "SPCanvasItem", &info, (GTypeFlags)0);
131     }
133     return type;
136 /**
137  * Initializes the SPCanvasItem vtable and the "event" signal.
138  */
139 static void
140 sp_canvas_item_class_init (SPCanvasItemClass *klass)
142     GObjectClass *object_class = (GObjectClass *) klass;
144     /* fixme: Derive from GObject */
145     item_parent_class = (GtkObjectClass*)gtk_type_class (GTK_TYPE_OBJECT);
147     item_signals[ITEM_EVENT] = g_signal_new ("event",
148                                              G_TYPE_FROM_CLASS (klass),
149                                              G_SIGNAL_RUN_LAST,
150                                              ((glong)((guint8*)&(klass->event) - (guint8*)klass)),
151                                              NULL, NULL,
152                                              sp_marshal_BOOLEAN__POINTER,
153                                              G_TYPE_BOOLEAN, 1,
154                                              GDK_TYPE_EVENT);
156     object_class->dispose = sp_canvas_item_dispose;
159 /**
160  * Callback for initialization of SPCanvasItem.
161  */
162 static void
163 sp_canvas_item_init (SPCanvasItem *item)
165     item->flags |= SP_CANVAS_ITEM_VISIBLE;
166     item->xform = NR::Matrix(NR::identity());
169 /**
170  * Constructs new SPCanvasItem on SPCanvasGroup.
171  */
172 SPCanvasItem *
173 sp_canvas_item_new (SPCanvasGroup *parent, GtkType type, gchar const *first_arg_name, ...)
175     va_list args;
177     g_return_val_if_fail (parent != NULL, NULL);
178     g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL);
179     g_return_val_if_fail (gtk_type_is_a (type, sp_canvas_item_get_type ()), NULL);
181     SPCanvasItem *item = SP_CANVAS_ITEM (gtk_type_new (type));
183     va_start (args, first_arg_name);
184     sp_canvas_item_construct (item, parent, first_arg_name, args);
185     va_end (args);
187     return item;
190 /**
191  * Sets up the newly created SPCanvasItem.
192  *
193  * We make it static for encapsulation reasons since it was nowhere used.
194  */
195 static void
196 sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args)
198     g_return_if_fail (SP_IS_CANVAS_GROUP (parent));
199     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
201     item->parent = SP_CANVAS_ITEM (parent);
202     item->canvas = item->parent->canvas;
204     g_object_set_valist (G_OBJECT (item), first_arg_name, args);
206     group_add (SP_CANVAS_GROUP (item->parent), item);
208     sp_canvas_item_request_update (item);
211 /**
212  * Helper function that requests redraw only if item's visible flag is set.
213  */
214 static void
215 redraw_if_visible (SPCanvasItem *item)
217     if (item->flags & SP_CANVAS_ITEM_VISIBLE) {
218         int x0 = (int)(item->x1);
219         int x1 = (int)(item->x2);
220         int y0 = (int)(item->y1);
221         int y1 = (int)(item->y2);
223         if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
224             sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
225         }
226     }
229 /**
230  * Callback that removes item from all referers and destroys it.
231  */
232 static void
233 sp_canvas_item_dispose (GObject *object)
235     SPCanvasItem *item = SP_CANVAS_ITEM (object);
237     // Hack: if this is a ctrlrect, move it to 0,0;
238     // this redraws only the stroke of the rect to be deleted,
239     // avoiding redraw of the entire area
240     if (SP_IS_CTRLRECT(item)) {
241         SP_CTRLRECT(object)->setRectangle(NR::Rect(NR::Point(0,0),NR::Point(0,0)));
242         SP_CTRLRECT(object)->update(item->xform, 0);
243     } else {
244         redraw_if_visible (item);
245     }
246     item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
248     if (item == item->canvas->current_item) {
249         item->canvas->current_item = NULL;
250         item->canvas->need_repick = TRUE;
251     }
253     if (item == item->canvas->new_current_item) {
254         item->canvas->new_current_item = NULL;
255         item->canvas->need_repick = TRUE;
256     }
258     if (item == item->canvas->grabbed_item) {
259         item->canvas->grabbed_item = NULL;
260         gdk_pointer_ungrab (GDK_CURRENT_TIME);
261     }
263     if (item == item->canvas->focused_item)
264         item->canvas->focused_item = NULL;
266     if (item->parent) {
267         group_remove (SP_CANVAS_GROUP (item->parent), item);
268     }
270     G_OBJECT_CLASS (item_parent_class)->dispose (object);
273 /**
274  * Helper function to update item and its children.
275  *
276  * NB! affine is parent2canvas.
277  */
278 static void
279 sp_canvas_item_invoke_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
281     /* Apply the child item's transform */
282     NR::Matrix child_affine = item->xform * affine;
284     /* apply object flags to child flags */
285     int child_flags = flags & ~SP_CANVAS_UPDATE_REQUESTED;
287     if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
288         child_flags |= SP_CANVAS_UPDATE_REQUESTED;
290     if (item->flags & SP_CANVAS_ITEM_NEED_AFFINE)
291         child_flags |= SP_CANVAS_UPDATE_AFFINE;
293     if (child_flags & (SP_CANVAS_UPDATE_REQUESTED | SP_CANVAS_UPDATE_AFFINE)) {
294         if (SP_CANVAS_ITEM_GET_CLASS (item)->update)
295             SP_CANVAS_ITEM_GET_CLASS (item)->update (item, child_affine, child_flags);
296     }
298     GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_UPDATE);
299     GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_AFFINE);
302 /**
303  * Helper function to invoke the point method of the item.
304  *
305  * The argument x, y should be in the parent's item-relative coordinate
306  * system.  This routine applies the inverse of the item's transform,
307  * maintaining the affine invariant.
308  */
309 static double
310 sp_canvas_item_invoke_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
312     if (SP_CANVAS_ITEM_GET_CLASS (item)->point)
313         return SP_CANVAS_ITEM_GET_CLASS (item)->point (item, p, actual_item);
315     return NR_HUGE;
318 /**
319  * Makes the item's affine transformation matrix be equal to the specified
320  * matrix.
321  *
322  * @item: A canvas item.
323  * @affine: An affine transformation matrix.
324  */
325 void
326 sp_canvas_item_affine_absolute (SPCanvasItem *item, NR::Matrix const &affine)
328     item->xform = affine;
330     if (!(item->flags & SP_CANVAS_ITEM_NEED_AFFINE)) {
331         item->flags |= SP_CANVAS_ITEM_NEED_AFFINE;
332         if (item->parent != NULL) {
333             sp_canvas_item_request_update (item->parent);
334         } else {
335             sp_canvas_request_update (item->canvas);
336         }
337     }
339     item->canvas->need_repick = TRUE;
342 /**
343  * Convenience function to reorder items in a group's child list.
344  *
345  * This puts the specified link after the "before" link.
346  */
347 static void
348 put_item_after (GList *link, GList *before)
350     if (link == before)
351         return;
353     SPCanvasGroup *parent = SP_CANVAS_GROUP (SP_CANVAS_ITEM (link->data)->parent);
355     if (before == NULL) {
356         if (link == parent->items) return;
358         link->prev->next = link->next;
360         if (link->next) {
361             link->next->prev = link->prev;
362         } else {
363             parent->last = link->prev;
364         }
366         link->prev = before;
367         link->next = parent->items;
368         link->next->prev = link;
369         parent->items = link;
370     } else {
371         if ((link == parent->last) && (before == parent->last->prev))
372             return;
374         if (link->next)
375             link->next->prev = link->prev;
377         if (link->prev)
378             link->prev->next = link->next;
379         else {
380             parent->items = link->next;
381             parent->items->prev = NULL;
382         }
384         link->prev = before;
385         link->next = before->next;
387         link->prev->next = link;
389         if (link->next)
390             link->next->prev = link;
391         else
392             parent->last = link;
393     }
397 /**
398  * Raises the item in its parent's stack by the specified number of positions.
399  *
400  * \param item A canvas item.
401  * \param positions Number of steps to raise the item.
402  *
403  * If the number of positions is greater than the distance to the top of the
404  * stack, then the item is put at the top.
405  */
406 void
407 sp_canvas_item_raise (SPCanvasItem *item, int positions)
409     g_return_if_fail (item != NULL);
410     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
411     g_return_if_fail (positions >= 0);
413     if (!item->parent || positions == 0)
414         return;
416     SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
417     GList *link = g_list_find (parent->items, item);
418     g_assert (link != NULL);
420     GList *before;
421     for (before = link; positions && before; positions--)
422         before = before->next;
424     if (!before)
425         before = parent->last;
427     put_item_after (link, before);
429     redraw_if_visible (item);
430     item->canvas->need_repick = TRUE;
434 /**
435  * Lowers the item in its parent's stack by the specified number of positions.
436  *
437  * \param item A canvas item.
438  * \param positions Number of steps to lower the item.
439  *
440  * If the number of positions is greater than the distance to the bottom of the
441  * stack, then the item is put at the bottom.
442  **/
443 void
444 sp_canvas_item_lower (SPCanvasItem *item, int positions)
446     g_return_if_fail (item != NULL);
447     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
448     g_return_if_fail (positions >= 1);
450     if (!item->parent || positions == 0)
451         return;
453     SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
454     GList *link = g_list_find (parent->items, item);
455     g_assert (link != NULL);
457     GList *before;
458     if (link->prev)
459         for (before = link->prev; positions && before; positions--)
460             before = before->prev;
461     else
462         before = NULL;
464     put_item_after (link, before);
466     redraw_if_visible (item);
467     item->canvas->need_repick = TRUE;
470 /**
471  * Sets visible flag on item and requests a redraw.
472  */
473 void
474 sp_canvas_item_show (SPCanvasItem *item)
476     g_return_if_fail (item != NULL);
477     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
479     if (item->flags & SP_CANVAS_ITEM_VISIBLE)
480         return;
482     item->flags |= SP_CANVAS_ITEM_VISIBLE;
484     int x0 = (int)(item->x1);
485     int x1 = (int)(item->x2);
486     int y0 = (int)(item->y1);
487     int y1 = (int)(item->y2);
489     if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
490         sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
491         item->canvas->need_repick = TRUE;
492     }
495 /**
496  * Clears visible flag on item and requests a redraw.
497  */
498 void
499 sp_canvas_item_hide (SPCanvasItem *item)
501     g_return_if_fail (item != NULL);
502     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
504     if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
505         return;
507     item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
509     int x0 = (int)(item->x1);
510     int x1 = (int)(item->x2);
511     int y0 = (int)(item->y1);
512     int y1 = (int)(item->y2);
514     if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
515         sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
516         item->canvas->need_repick = TRUE;
517     }
520 /**
521  * Grab item under cursor.
522  *
523  * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE
524  */
525 int
526 sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
528     g_return_val_if_fail (item != NULL, -1);
529     g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1);
530     g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1);
532     if (item->canvas->grabbed_item)
533         return -1;
535     if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
536         return -1;
538     /* fixme: Top hack (Lauris) */
539     /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
540     /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
541     gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
542                       (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
543                       NULL, cursor, etime);
545     item->canvas->grabbed_item = item;
546     item->canvas->grabbed_event_mask = event_mask;
547     item->canvas->current_item = item; /* So that events go to the grabbed item */
549     return 0;
552 /**
553  * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
554  * mouse.
555  *
556  * \param item A canvas item that holds a grab.
557  * \param etime The timestamp for ungrabbing the mouse.
558  */
559 void
560 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
562     g_return_if_fail (item != NULL);
563     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
565     if (item->canvas->grabbed_item != item)
566         return;
568     item->canvas->grabbed_item = NULL;
570     gdk_pointer_ungrab (etime);
573 /**
574  * Returns the product of all transformation matrices from the root item down
575  * to the item.
576  */
577 NR::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
579     g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
581     NR::Matrix affine = NR::identity();
583     while (item) {
584         affine *= item->xform;
585         item = item->parent;
586     }
587     return affine;
590 /**
591  * Helper that returns true iff item is descendant of parent.
592  */
593 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
595     while (item) {
596         if (item == parent)
597             return true;
598         item = item->parent;
599     }
601     return false;
604 /**
605  * Focus canvas, and item under cursor if it is not already focussed.
606  */
607 void
608 sp_canvas_item_grab_focus (SPCanvasItem *item)
610     g_return_if_fail (item != NULL);
611     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
612     g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
614     SPCanvasItem *focused_item = item->canvas->focused_item;
616     if (focused_item) {
617         GdkEvent ev;
618         ev.focus_change.type = GDK_FOCUS_CHANGE;
619         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
620         ev.focus_change.send_event = FALSE;
621         ev.focus_change.in = FALSE;
623         emit_event (item->canvas, &ev);
624     }
626     item->canvas->focused_item = item;
627     gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
629     if (focused_item) {
630         GdkEvent ev;
631         ev.focus_change.type = GDK_FOCUS_CHANGE;
632         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
633         ev.focus_change.send_event = FALSE;
634         ev.focus_change.in = TRUE;
636         emit_event (item->canvas, &ev);
637     }
640 /**
641  * Requests that the canvas queue an update for the specified item.
642  *
643  * To be used only by item implementations.
644  */
645 void
646 sp_canvas_item_request_update (SPCanvasItem *item)
648     if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
649         return;
651     item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
653     if (item->parent != NULL) {
654         /* Recurse up the tree */
655         sp_canvas_item_request_update (item->parent);
656     } else {
657         /* Have reached the top of the tree, make sure the update call gets scheduled. */
658         sp_canvas_request_update (item->canvas);
659     }
662 /**
663  * Returns position of item in group.
664  */
665 gint sp_canvas_item_order (SPCanvasItem * item)
667     return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
670 /* SPCanvasGroup */
672 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
673 static void sp_canvas_group_init (SPCanvasGroup *group);
674 static void sp_canvas_group_destroy (GtkObject *object);
676 static void sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
677 static double sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item);
678 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
680 static SPCanvasItemClass *group_parent_class;
682 /**
683  * Registers SPCanvasGroup class with Gtk and returns its type number.
684  */
685 GtkType
686 sp_canvas_group_get_type (void)
688     static GtkType group_type = 0;
690     if (!group_type) {
691         static GtkTypeInfo const group_info = {
692             "SPCanvasGroup",
693             sizeof (SPCanvasGroup),
694             sizeof (SPCanvasGroupClass),
695             (GtkClassInitFunc) sp_canvas_group_class_init,
696             (GtkObjectInitFunc) sp_canvas_group_init,
697             NULL, NULL, NULL
698         };
700         group_type = gtk_type_unique (sp_canvas_item_get_type (), &group_info);
701     }
703     return group_type;
706 /**
707  * Class initialization function for SPCanvasGroupClass
708  */
709 static void
710 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
712     GtkObjectClass *object_class = (GtkObjectClass *) klass;
713     SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
715     group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
717     object_class->destroy = sp_canvas_group_destroy;
719     item_class->update = sp_canvas_group_update;
720     item_class->render = sp_canvas_group_render;
721     item_class->point = sp_canvas_group_point;
724 /**
725  * Callback. Empty.
726  */
727 static void
728 sp_canvas_group_init (SPCanvasGroup */*group*/)
730     /* Nothing here */
733 /**
734  * Callback that destroys all items in group and calls group's virtual
735  * destroy() function.
736  */
737 static void
738 sp_canvas_group_destroy (GtkObject *object)
740     g_return_if_fail (object != NULL);
741     g_return_if_fail (SP_IS_CANVAS_GROUP (object));
743     SPCanvasGroup const *group = SP_CANVAS_GROUP (object);
745     GList *list = group->items;
746     while (list) {
747         SPCanvasItem *child = (SPCanvasItem *)list->data;
748         list = list->next;
750         gtk_object_destroy (GTK_OBJECT (child));
751     }
753     if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
754         (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
757 /**
758  * Update handler for canvas groups
759  */
760 static void
761 sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
763     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
764     NR::ConvexHull corners(NR::Point(0, 0));
765     bool empty=true;
767     for (GList *list = group->items; list; list = list->next) {
768         SPCanvasItem *i = (SPCanvasItem *)list->data;
770         sp_canvas_item_invoke_update (i, affine, flags);
772         if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
773             if (empty) {
774                 corners = NR::ConvexHull(NR::Point(i->x1, i->y1));
775                 empty = false;
776             } else {
777                 corners.add(NR::Point(i->x1, i->y1));
778             }
779             corners.add(NR::Point(i->x2, i->y2));
780         }
781     }
783     NR::Maybe<NR::Rect> const bounds = corners.bounds();
784     if (bounds) {
785         item->x1 = bounds->min()[NR::X];
786         item->y1 = bounds->min()[NR::Y];
787         item->x2 = bounds->max()[NR::X];
788         item->y2 = bounds->max()[NR::Y];
789     } else {
790         // FIXME ?
791         item->x1 = item->x2 = item->y1 = item->y2 = 0;
792     }
795 /**
796  * Point handler for canvas groups.
797  */
798 static double
799 sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
801     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
802     double const x = p[NR::X];
803     double const y = p[NR::Y];
804     int x1 = (int)(x - item->canvas->close_enough);
805     int y1 = (int)(y - item->canvas->close_enough);
806     int x2 = (int)(x + item->canvas->close_enough);
807     int y2 = (int)(y + item->canvas->close_enough);
809     double best = 0.0;
810     *actual_item = NULL;
812     double dist = 0.0;
814     for (GList *list = group->items; list; list = list->next) {
815         SPCanvasItem *child = (SPCanvasItem *)list->data;
817         if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
818             SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
820             int has_point;
821             if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
822                 dist = sp_canvas_item_invoke_point (child, p, &point_item);
823                 has_point = TRUE;
824             } else
825                 has_point = FALSE;
827             if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
828                 best = dist;
829                 *actual_item = point_item;
830             }
831         }
832     }
834     return best;
837 /**
838  * Renders all visible canvas group items in buf rectangle.
839  */
840 static void
841 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
843     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
845     for (GList *list = group->items; list; list = list->next) {
846         SPCanvasItem *child = (SPCanvasItem *)list->data;
847         if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
848             if ((child->x1 < buf->rect.x1) &&
849                 (child->y1 < buf->rect.y1) &&
850                 (child->x2 > buf->rect.x0) &&
851                 (child->y2 > buf->rect.y0)) {
852                 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
853                     SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
854             }
855         }
856     }
859 /**
860  * Adds an item to a canvas group.
861  */
862 static void
863 group_add (SPCanvasGroup *group, SPCanvasItem *item)
865     gtk_object_ref (GTK_OBJECT (item));
866     gtk_object_sink (GTK_OBJECT (item));
868     if (!group->items) {
869         group->items = g_list_append (group->items, item);
870         group->last = group->items;
871     } else {
872         group->last = g_list_append (group->last, item)->next;
873     }
875     sp_canvas_item_request_update (item);
878 /**
879  * Removes an item from a canvas group
880  */
881 static void
882 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
884     g_return_if_fail (group != NULL);
885     g_return_if_fail (SP_IS_CANVAS_GROUP (group));
886     g_return_if_fail (item != NULL);
888     for (GList *children = group->items; children; children = children->next) {
889         if (children->data == item) {
891             /* Unparent the child */
893             item->parent = NULL;
894             gtk_object_unref (GTK_OBJECT (item));
896             /* Remove it from the list */
898             if (children == group->last) group->last = children->prev;
900             group->items = g_list_remove_link (group->items, children);
901             g_list_free (children);
902             break;
903         }
904     }
907 /* SPCanvas */
909 static void sp_canvas_class_init (SPCanvasClass *klass);
910 static void sp_canvas_init (SPCanvas *canvas);
911 static void sp_canvas_destroy (GtkObject *object);
913 static void sp_canvas_realize (GtkWidget *widget);
914 static void sp_canvas_unrealize (GtkWidget *widget);
916 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
917 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
919 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
920 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
921 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
922 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
923 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
924 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
925 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
926 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
928 static GtkWidgetClass *canvas_parent_class;
930 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb);
931 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb);
932 static void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val);
933 static int do_update (SPCanvas *canvas);
935 /**
936  * Registers the SPCanvas class if necessary, and returns the type ID
937  * associated to it.
938  *
939  * \return The type ID of the SPCanvas class.
940  **/
941 GtkType
942 sp_canvas_get_type (void)
944     static GtkType canvas_type = 0;
946     if (!canvas_type) {
947         static GtkTypeInfo const canvas_info = {
948             "SPCanvas",
949             sizeof (SPCanvas),
950             sizeof (SPCanvasClass),
951             (GtkClassInitFunc) sp_canvas_class_init,
952             (GtkObjectInitFunc) sp_canvas_init,
953             NULL, NULL, NULL
954         };
956         canvas_type = gtk_type_unique (GTK_TYPE_WIDGET, &canvas_info);
957     }
959     return canvas_type;
962 /**
963  * Class initialization function for SPCanvasClass.
964  */
965 static void
966 sp_canvas_class_init (SPCanvasClass *klass)
968     GtkObjectClass *object_class = (GtkObjectClass *) klass;
969     GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
971     canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
973     object_class->destroy = sp_canvas_destroy;
975     widget_class->realize = sp_canvas_realize;
976     widget_class->unrealize = sp_canvas_unrealize;
977     widget_class->size_request = sp_canvas_size_request;
978     widget_class->size_allocate = sp_canvas_size_allocate;
979     widget_class->button_press_event = sp_canvas_button;
980     widget_class->button_release_event = sp_canvas_button;
981     widget_class->motion_notify_event = sp_canvas_motion;
982     widget_class->scroll_event = sp_canvas_scroll;
983     widget_class->expose_event = sp_canvas_expose;
984     widget_class->key_press_event = sp_canvas_key;
985     widget_class->key_release_event = sp_canvas_key;
986     widget_class->enter_notify_event = sp_canvas_crossing;
987     widget_class->leave_notify_event = sp_canvas_crossing;
988     widget_class->focus_in_event = sp_canvas_focus_in;
989     widget_class->focus_out_event = sp_canvas_focus_out;
992 /**
993  * Callback: object initialization for SPCanvas.
994  */
995 static void
996 sp_canvas_init (SPCanvas *canvas)
998     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
999     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
1000     GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
1002     canvas->pick_event.type = GDK_LEAVE_NOTIFY;
1003     canvas->pick_event.crossing.x = 0;
1004     canvas->pick_event.crossing.y = 0;
1006     /* Create the root item as a special case */
1007     canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
1008     canvas->root->canvas = canvas;
1010     gtk_object_ref (GTK_OBJECT (canvas->root));
1011     gtk_object_sink (GTK_OBJECT (canvas->root));
1013     canvas->need_repick = TRUE;
1015     // See comment at in sp-canvas.h.
1016     canvas->gen_all_enter_events = false;
1018     canvas->tiles=NULL;
1019     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1020     canvas->tileH=canvas->tileV=0;
1022     canvas->forced_redraw_count = 0;
1023     canvas->forced_redraw_limit = -1;
1025 #if ENABLE_LCMS
1026     canvas->enable_cms_display_adj = false;
1027     canvas->cms_key = new Glib::ustring("");
1028 #endif // ENABLE_LCMS
1030     canvas->is_scrolling = false;
1034 /**
1035  * Convenience function to remove the idle handler of a canvas.
1036  */
1037 static void
1038 remove_idle (SPCanvas *canvas)
1040     if (canvas->idle_id) {
1041         gtk_idle_remove (canvas->idle_id);
1042         canvas->idle_id = 0;
1043     }
1046 /*
1047  * Removes the transient state of the canvas (idle handler, grabs).
1048  */
1049 static void
1050 shutdown_transients (SPCanvas *canvas)
1052     /* We turn off the need_redraw flag, since if the canvas is mapped again
1053      * it will request a redraw anyways.  We do not turn off the need_update
1054      * flag, though, because updates are not queued when the canvas remaps
1055      * itself.
1056      */
1057     if (canvas->need_redraw) {
1058         canvas->need_redraw = FALSE;
1059     }
1060     if ( canvas->tiles ) g_free(canvas->tiles);
1061     canvas->tiles=NULL;
1062     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1063     canvas->tileH=canvas->tileV=0;
1065     if (canvas->grabbed_item) {
1066         canvas->grabbed_item = NULL;
1067         gdk_pointer_ungrab (GDK_CURRENT_TIME);
1068     }
1070     remove_idle (canvas);
1073 /**
1074  * Destroy handler for SPCanvas.
1075  */
1076 static void
1077 sp_canvas_destroy (GtkObject *object)
1079     SPCanvas *canvas = SP_CANVAS (object);
1081     if (canvas->root) {
1082         gtk_object_unref (GTK_OBJECT (canvas->root));
1083         canvas->root = NULL;
1084     }
1086     shutdown_transients (canvas);
1088     if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1089         (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1092 /**
1093  * Returns new canvas as widget.
1094  */
1095 GtkWidget *
1096 sp_canvas_new_aa (void)
1098     SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1100     return (GtkWidget *) canvas;
1103 /**
1104  * The canvas widget's realize callback.
1105  */
1106 static void
1107 sp_canvas_realize (GtkWidget *widget)
1109     SPCanvas *canvas = SP_CANVAS (widget);
1111     GdkWindowAttr attributes;
1112     attributes.window_type = GDK_WINDOW_CHILD;
1113     attributes.x = widget->allocation.x;
1114     attributes.y = widget->allocation.y;
1115     attributes.width = widget->allocation.width;
1116     attributes.height = widget->allocation.height;
1117     attributes.wclass = GDK_INPUT_OUTPUT;
1118     attributes.visual = gdk_rgb_get_visual ();
1119     attributes.colormap = gdk_rgb_get_cmap ();
1120     attributes.event_mask = (gtk_widget_get_events (widget) |
1121                              GDK_EXPOSURE_MASK |
1122                              GDK_BUTTON_PRESS_MASK |
1123                              GDK_BUTTON_RELEASE_MASK |
1124                              GDK_POINTER_MOTION_MASK |
1125                              GDK_PROXIMITY_IN_MASK |
1126                              GDK_PROXIMITY_OUT_MASK |
1127                              GDK_KEY_PRESS_MASK |
1128                              GDK_KEY_RELEASE_MASK |
1129                              GDK_ENTER_NOTIFY_MASK |
1130                              GDK_LEAVE_NOTIFY_MASK |
1131                              GDK_FOCUS_CHANGE_MASK);
1132     gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1134     widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1135     gdk_window_set_user_data (widget->window, widget);
1137     if ( prefs_get_int_attribute ("options.useextinput", "value", 1) )
1138         gtk_widget_set_events(widget, attributes.event_mask);
1140     widget->style = gtk_style_attach (widget->style, widget->window);
1142     GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1144     canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1147 /**
1148  * The canvas widget's unrealize callback.
1149  */
1150 static void
1151 sp_canvas_unrealize (GtkWidget *widget)
1153     SPCanvas *canvas = SP_CANVAS (widget);
1155     shutdown_transients (canvas);
1157     gdk_gc_destroy (canvas->pixmap_gc);
1158     canvas->pixmap_gc = NULL;
1160     if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1161         (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1164 /**
1165  * The canvas widget's size_request callback.
1166  */
1167 static void
1168 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1170     static_cast<void>(SP_CANVAS (widget));
1172     req->width = 256;
1173     req->height = 256;
1176 /**
1177  * The canvas widget's size_allocate callback.
1178  */
1179 static void
1180 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1182     SPCanvas *canvas = SP_CANVAS (widget);
1184     /* Schedule redraw of new region */
1185     sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1186     if (allocation->width > widget->allocation.width) {
1187         sp_canvas_request_redraw (canvas,
1188                                   canvas->x0 + widget->allocation.width,
1189                                   0,
1190                                   canvas->x0 + allocation->width,
1191                                   canvas->y0 + allocation->height);
1192     }
1193     if (allocation->height > widget->allocation.height) {
1194         sp_canvas_request_redraw (canvas,
1195                                   0,
1196                                   canvas->y0 + widget->allocation.height,
1197                                   canvas->x0 + allocation->width,
1198                                   canvas->y0 + allocation->height);
1199     }
1201     widget->allocation = *allocation;
1203     if (GTK_WIDGET_REALIZED (widget)) {
1204         gdk_window_move_resize (widget->window,
1205                                 widget->allocation.x, widget->allocation.y,
1206                                 widget->allocation.width, widget->allocation.height);
1207     }
1210 /**
1211  * Helper that emits an event for an item in the canvas, be it the current
1212  * item, grabbed item, or focused item, as appropriate.
1213  */
1214 static int
1215 emit_event (SPCanvas *canvas, GdkEvent *event)
1217     guint mask;
1219     if (canvas->grabbed_item) {
1220         switch (event->type) {
1221         case GDK_ENTER_NOTIFY:
1222             mask = GDK_ENTER_NOTIFY_MASK;
1223             break;
1224         case GDK_LEAVE_NOTIFY:
1225             mask = GDK_LEAVE_NOTIFY_MASK;
1226             break;
1227         case GDK_MOTION_NOTIFY:
1228             mask = GDK_POINTER_MOTION_MASK;
1229             break;
1230         case GDK_BUTTON_PRESS:
1231         case GDK_2BUTTON_PRESS:
1232         case GDK_3BUTTON_PRESS:
1233             mask = GDK_BUTTON_PRESS_MASK;
1234             break;
1235         case GDK_BUTTON_RELEASE:
1236             mask = GDK_BUTTON_RELEASE_MASK;
1237             break;
1238         case GDK_KEY_PRESS:
1239             mask = GDK_KEY_PRESS_MASK;
1240             break;
1241         case GDK_KEY_RELEASE:
1242             mask = GDK_KEY_RELEASE_MASK;
1243             break;
1244         case GDK_SCROLL:
1245             mask = GDK_SCROLL;
1246             break;
1247         default:
1248             mask = 0;
1249             break;
1250         }
1252         if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1253     }
1255     /* Convert to world coordinates -- we have two cases because of diferent
1256      * offsets of the fields in the event structures.
1257      */
1259     GdkEvent ev = *event;
1261     switch (ev.type) {
1262     case GDK_ENTER_NOTIFY:
1263     case GDK_LEAVE_NOTIFY:
1264         ev.crossing.x += canvas->x0;
1265         ev.crossing.y += canvas->y0;
1266         break;
1267     case GDK_MOTION_NOTIFY:
1268     case GDK_BUTTON_PRESS:
1269     case GDK_2BUTTON_PRESS:
1270     case GDK_3BUTTON_PRESS:
1271     case GDK_BUTTON_RELEASE:
1272         ev.motion.x += canvas->x0;
1273         ev.motion.y += canvas->y0;
1274         break;
1275     default:
1276         break;
1277     }
1279     /* Choose where we send the event */
1281     /* canvas->current_item becomes NULL in some cases under Win32
1282     ** (e.g. if the pointer leaves the window).  So this is a hack that
1283     ** Lauris applied to SP to get around the problem.
1284     */
1285     SPCanvasItem* item = NULL;
1286     if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1287         item = canvas->grabbed_item;
1288     } else {
1289         item = canvas->current_item;
1290     }
1292     if (canvas->focused_item &&
1293         ((event->type == GDK_KEY_PRESS) ||
1294          (event->type == GDK_KEY_RELEASE) ||
1295          (event->type == GDK_FOCUS_CHANGE))) {
1296         item = canvas->focused_item;
1297     }
1299     /* The event is propagated up the hierarchy (for if someone connected to
1300      * a group instead of a leaf event), and emission is stopped if a
1301      * handler returns TRUE, just like for GtkWidget events.
1302      */
1304     gint finished = FALSE;
1306     while (item && !finished) {
1307         gtk_object_ref (GTK_OBJECT (item));
1308         gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1309         SPCanvasItem *parent = item->parent;
1310         gtk_object_unref (GTK_OBJECT (item));
1311         item = parent;
1312     }
1314     return finished;
1317 /**
1318  * Helper that re-picks the current item in the canvas, based on the event's
1319  * coordinates and emits enter/leave events for items as appropriate.
1320  */
1321 static int
1322 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1324     int button_down = 0;
1325     double x, y;
1327     if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1328         return FALSE;
1330     int retval = FALSE;
1332     if (canvas->gen_all_enter_events == false) {
1333         // If a button is down, we'll perform enter and leave events on the
1334         // current item, but not enter on any other item.  This is more or
1335         // less like X pointer grabbing for canvas items.
1336         //
1337         button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1338                 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1340         if (!button_down) canvas->left_grabbed_item = FALSE;
1341     }
1343     /* Save the event in the canvas.  This is used to synthesize enter and
1344      * leave events in case the current item changes.  It is also used to
1345      * re-pick the current item if the current one gets deleted.  Also,
1346      * synthesize an enter event.
1347      */
1348     if (event != &canvas->pick_event) {
1349         if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1350             /* these fields have the same offsets in both types of events */
1352             canvas->pick_event.crossing.type       = GDK_ENTER_NOTIFY;
1353             canvas->pick_event.crossing.window     = event->motion.window;
1354             canvas->pick_event.crossing.send_event = event->motion.send_event;
1355             canvas->pick_event.crossing.subwindow  = NULL;
1356             canvas->pick_event.crossing.x          = event->motion.x;
1357             canvas->pick_event.crossing.y          = event->motion.y;
1358             canvas->pick_event.crossing.mode       = GDK_CROSSING_NORMAL;
1359             canvas->pick_event.crossing.detail     = GDK_NOTIFY_NONLINEAR;
1360             canvas->pick_event.crossing.focus      = FALSE;
1361             canvas->pick_event.crossing.state      = event->motion.state;
1363             /* these fields don't have the same offsets in both types of events */
1365             if (event->type == GDK_MOTION_NOTIFY) {
1366                 canvas->pick_event.crossing.x_root = event->motion.x_root;
1367                 canvas->pick_event.crossing.y_root = event->motion.y_root;
1368             } else {
1369                 canvas->pick_event.crossing.x_root = event->button.x_root;
1370                 canvas->pick_event.crossing.y_root = event->button.y_root;
1371             }
1372         } else {
1373             canvas->pick_event = *event;
1374         }
1375     }
1377     /* Don't do anything else if this is a recursive call */
1378     if (canvas->in_repick) return retval;
1380     /* LeaveNotify means that there is no current item, so we don't look for one */
1381     if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1382         /* these fields don't have the same offsets in both types of events */
1384         if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1385             x = canvas->pick_event.crossing.x;
1386             y = canvas->pick_event.crossing.y;
1387         } else {
1388             x = canvas->pick_event.motion.x;
1389             y = canvas->pick_event.motion.y;
1390         }
1392         /* world coords */
1393         x += canvas->x0;
1394         y += canvas->y0;
1396         /* find the closest item */
1397         if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1398             sp_canvas_item_invoke_point (canvas->root, NR::Point(x, y), &canvas->new_current_item);
1399         } else {
1400             canvas->new_current_item = NULL;
1401         }
1402     } else {
1403         canvas->new_current_item = NULL;
1404     }
1406     if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1407         return retval; /* current item did not change */
1408     }
1410     /* Synthesize events for old and new current items */
1412     if ((canvas->new_current_item != canvas->current_item)
1413         && (canvas->current_item != NULL)
1414         && !canvas->left_grabbed_item) {
1415         GdkEvent new_event;
1416         SPCanvasItem *item;
1418         item = canvas->current_item;
1420         new_event = canvas->pick_event;
1421         new_event.type = GDK_LEAVE_NOTIFY;
1423         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1424         new_event.crossing.subwindow = NULL;
1425         canvas->in_repick = TRUE;
1426         retval = emit_event (canvas, &new_event);
1427         canvas->in_repick = FALSE;
1428     }
1430     if (canvas->gen_all_enter_events == false) {
1431         // new_current_item may have been set to NULL during the call to
1432         // emit_event() above
1433         if ((canvas->new_current_item != canvas->current_item) && button_down) {
1434             canvas->left_grabbed_item = TRUE;
1435             return retval;
1436         }
1437     }
1439     /* Handle the rest of cases */
1441     canvas->left_grabbed_item = FALSE;
1442     canvas->current_item = canvas->new_current_item;
1444     if (canvas->current_item != NULL) {
1445         GdkEvent new_event;
1447         new_event = canvas->pick_event;
1448         new_event.type = GDK_ENTER_NOTIFY;
1449         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1450         new_event.crossing.subwindow = NULL;
1451         retval = emit_event (canvas, &new_event);
1452     }
1454     return retval;
1457 /**
1458  * Button event handler for the canvas.
1459  */
1460 static gint
1461 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1463     SPCanvas *canvas = SP_CANVAS (widget);
1465     int retval = FALSE;
1467     /* dispatch normally regardless of the event's window if an item has
1468        has a pointer grab in effect */
1469     if (!canvas->grabbed_item &&
1470         event->window != SP_CANVAS_WINDOW (canvas))
1471         return retval;
1473     int mask;
1474     switch (event->button) {
1475     case 1:
1476         mask = GDK_BUTTON1_MASK;
1477         break;
1478     case 2:
1479         mask = GDK_BUTTON2_MASK;
1480         break;
1481     case 3:
1482         mask = GDK_BUTTON3_MASK;
1483         break;
1484     case 4:
1485         mask = GDK_BUTTON4_MASK;
1486         break;
1487     case 5:
1488         mask = GDK_BUTTON5_MASK;
1489         break;
1490     default:
1491         mask = 0;
1492     }
1494     switch (event->type) {
1495     case GDK_BUTTON_PRESS:
1496     case GDK_2BUTTON_PRESS:
1497     case GDK_3BUTTON_PRESS:
1498         /* Pick the current item as if the button were not pressed, and
1499          * then process the event.
1500          */
1501         canvas->state = event->state;
1502         pick_current_item (canvas, (GdkEvent *) event);
1503         canvas->state ^= mask;
1504         retval = emit_event (canvas, (GdkEvent *) event);
1505         break;
1507     case GDK_BUTTON_RELEASE:
1508         /* Process the event as if the button were pressed, then repick
1509          * after the button has been released
1510          */
1511         canvas->state = event->state;
1512         retval = emit_event (canvas, (GdkEvent *) event);
1513         event->state ^= mask;
1514         canvas->state = event->state;
1515         pick_current_item (canvas, (GdkEvent *) event);
1516         event->state ^= mask;
1517         break;
1519     default:
1520         g_assert_not_reached ();
1521     }
1523     return retval;
1526 /**
1527  * Scroll event handler for the canvas.
1528  *
1529  * \todo FIXME: generate motion events to re-select items.
1530  */
1531 static gint
1532 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1534     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1537 /**
1538  * Motion event handler for the canvas.
1539  */
1540 static int
1541 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1543     SPCanvas *canvas = SP_CANVAS (widget);
1545     if (event->window != SP_CANVAS_WINDOW (canvas))
1546         return FALSE;
1548     if (canvas->grabbed_event_mask & GDK_POINTER_MOTION_HINT_MASK) {
1549         gint x, y;
1550         gdk_window_get_pointer (widget->window, &x, &y, NULL);
1551         event->x = x;
1552         event->y = y;
1553     }
1555     canvas->state = event->state;
1556     pick_current_item (canvas, (GdkEvent *) event);
1558     return emit_event (canvas, (GdkEvent *) event);
1561 static void
1562 sp_canvas_paint_single_buffer (SPCanvas *canvas, int x0, int y0, int x1, int y1, int draw_x1, int draw_y1, int draw_x2, int draw_y2, int sw)
1564     GtkWidget *widget = GTK_WIDGET (canvas);
1566     SPCanvasBuf buf;
1567     if (canvas->rendermode != RENDERMODE_OUTLINE) {
1568         buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1569     } else {
1570         buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1571     }
1573     // Mark the region clean
1574     sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1576     buf.buf_rowstride = sw * 3; // CAIRO FIXME: for cairo output, the buffer must be RGB unpacked, i.e. sw * 4
1577     buf.rect.x0 = x0;
1578     buf.rect.y0 = y0;
1579     buf.rect.x1 = x1;
1580     buf.rect.y1 = y1;
1581     buf.visible_rect.x0 = draw_x1;
1582     buf.visible_rect.y0 = draw_y1;
1583     buf.visible_rect.x1 = draw_x2;
1584     buf.visible_rect.y1 = draw_y2;
1585     GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1586     buf.bg_color = (((color->red & 0xff00) << 8)
1587                     | (color->green & 0xff00)
1588                     | (color->blue >> 8));
1589     buf.is_empty = true;
1591     if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1592         SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1593     }
1595 #if ENABLE_LCMS
1596     cmsHTRANSFORM transf = 0;
1597     long long int fromDisplay = prefs_get_int_attribute_limited( "options.displayprofile", "from_display", 0, 0, 1 );
1598     if ( fromDisplay ) {
1599         transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" );
1600     } else {
1601         transf = Inkscape::colorprofile_get_display_transform();
1602     }
1603 #endif // ENABLE_LCMS
1605     if (buf.is_empty) {
1606 #if ENABLE_LCMS
1607         if ( transf && canvas->enable_cms_display_adj ) {
1608             cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1609         }
1610 #endif // ENABLE_LCMS
1611         gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1612         gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1613                             canvas->pixmap_gc,
1614                             TRUE,
1615                             x0 - canvas->x0, y0 - canvas->y0,
1616                             x1 - x0, y1 - y0);
1617     } else {
1618 /*
1619 // CAIRO FIXME: after SPCanvasBuf is made 32bpp throughout, this rgb_draw below can be replaced with the below.
1620 // Why this must not be done currently:
1621 // - all canvas items (handles, nodes etc) paint themselves assuming 24bpp
1622 // - cairo assumes bgra, but we have rgba, so r and b get swapped (until we paint all with cairo too)
1623 // - it does not seem to be any faster; in fact since with 32bpp, buf contains less pixels,
1624 // we need more bufs to paint a given area and as a result it's even a bit slower
1626     cairo_surface_t* cst = cairo_image_surface_create_for_data (
1627         buf.buf,
1628         CAIRO_FORMAT_RGB24,  // unpacked, i.e. 32 bits! one byte is unused
1629         x1 - x0, y1 - y0,
1630         buf.buf_rowstride
1631         );
1632         cairo_t *ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1633         cairo_set_source_surface (ct, cst, x0 - canvas->x0, y0 - canvas->y0);
1634         cairo_paint (ct);
1635     cairo_destroy (ct);
1636     cairo_surface_finish (cst);
1637     cairo_surface_destroy (cst);
1638 */
1640 #if ENABLE_LCMS
1641         if ( transf && canvas->enable_cms_display_adj ) {
1642             for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1643                 guchar* p = buf.buf + (sw * 3) * yy;
1644                 cmsDoTransform( transf, p, p, (x1 - x0) );
1645             }
1646         }
1647 #endif // ENABLE_LCMS
1649         gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1650                                       canvas->pixmap_gc,
1651                                       x0 - canvas->x0, y0 - canvas->y0,
1652                                       x1 - x0, y1 - y0,
1653                                       GDK_RGB_DITHER_MAX,
1654                                       buf.buf,
1655                                       sw * 3,
1656                                       x0 - canvas->x0, y0 - canvas->y0);
1657     }
1659     if (canvas->rendermode != RENDERMODE_OUTLINE) {
1660         nr_pixelstore_256K_free (buf.buf);
1661     } else {
1662         nr_pixelstore_1M_free (buf.buf);
1663     }
1666 struct PaintRectSetup {
1667     SPCanvas* canvas;
1668     NRRectL big_rect;
1669     GTimeVal start_time;
1670     int max_pixels;
1671     NR::Point mouse_loc;
1672 };
1674 /**
1675  * Paint the given rect, recursively subdividing the region until it is the size of a single
1676  * buffer.
1677  *
1678  * @return true if the drawing completes
1679  */
1680 static int
1681 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1683     GTimeVal now;
1684     g_get_current_time (&now);
1686     glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1687         + (now.tv_usec - setup->start_time.tv_usec);
1689     // Allow only very fast buffers to be run together;
1690     // as soon as the total redraw time exceeds 1ms, cancel;
1691     // this returns control to the idle loop and allows Inkscape to process user input
1692     // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1693     // it will get back and finish painting what remains to paint.
1694     if (elapsed > 1000) {
1696         // Interrupting redraw isn't always good.
1697         // For example, when you drag one node of a big path, only the buffer containing
1698         // the mouse cursor will be redrawn again and again, and the rest of the path
1699         // will remain stale because Inkscape never has enough idle time to redraw all
1700         // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1701         // If this limit is set, and if we have aborted redraw more times than is allowed,
1702         // interrupting is blocked and we're forced to redraw full screen once
1703         // (after which we can again interrupt forced_redraw_limit times).
1704         if (setup->canvas->forced_redraw_limit < 0 ||
1705             setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1707             if (setup->canvas->forced_redraw_limit != -1) {
1708                 setup->canvas->forced_redraw_count++;
1709             }
1711             return false;
1712         }
1713     }
1715     // Find the optimal buffer dimensions
1716     int bw = this_rect.x1 - this_rect.x0;
1717     int bh = this_rect.y1 - this_rect.y0;
1718     if ((bw < 1) || (bh < 1))
1719         return 0;
1721     if (bw * bh < setup->max_pixels) {
1722         // We are small enough
1723         sp_canvas_paint_single_buffer (setup->canvas,
1724                                        this_rect.x0, this_rect.y0,
1725                                        this_rect.x1, this_rect.y1,
1726                                        setup->big_rect.x0, setup->big_rect.y0,
1727                                        setup->big_rect.x1, setup->big_rect.y1, bw);
1728         return 1;
1729     }
1731     NRRectL lo = this_rect;
1732     NRRectL hi = this_rect;
1734 /*
1735 This test determines the redraw strategy:
1737 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1738 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1739 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1740 and seems to be faster for drawings with many smaller objects at zoom-out.
1742 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1743 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1744 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1745 faster.
1747 The default for now is the strips mode.
1748 */
1749     if (bw < bh || bh < 2 * TILE_SIZE) {
1750         int mid = (this_rect.x0 + this_rect.x1) / 2;
1751         // Make sure that mid lies on a tile boundary
1752         mid = (mid / TILE_SIZE) * TILE_SIZE;
1754         lo.x1 = mid;
1755         hi.x0 = mid;
1757         if (setup->mouse_loc[NR::X] < mid) {
1758             // Always paint towards the mouse first
1759             return sp_canvas_paint_rect_internal(setup, lo)
1760                 && sp_canvas_paint_rect_internal(setup, hi);
1761         } else {
1762             return sp_canvas_paint_rect_internal(setup, hi)
1763                 && sp_canvas_paint_rect_internal(setup, lo);
1764         }
1765     } else {
1766         int mid = (this_rect.y0 + this_rect.y1) / 2;
1767         // Make sure that mid lies on a tile boundary
1768         mid = (mid / TILE_SIZE) * TILE_SIZE;
1770         lo.y1 = mid;
1771         hi.y0 = mid;
1773         if (setup->mouse_loc[NR::Y] < mid) {
1774             // Always paint towards the mouse first
1775             return sp_canvas_paint_rect_internal(setup, lo)
1776                 && sp_canvas_paint_rect_internal(setup, hi);
1777         } else {
1778             return sp_canvas_paint_rect_internal(setup, hi)
1779                 && sp_canvas_paint_rect_internal(setup, lo);
1780         }
1781     }
1785 /**
1786  * Helper that draws a specific rectangular part of the canvas.
1787  *
1788  * @return true if the rectangle painting succeeds.
1789  */
1790 static bool
1791 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1793     g_return_val_if_fail (!canvas->need_update, false);
1795     NRRectL rect;
1796     rect.x0 = xx0;
1797     rect.x1 = xx1;
1798     rect.y0 = yy0;
1799     rect.y1 = yy1;
1801     // Clip rect-to-draw by the current visible area
1802     rect.x0 = MAX (rect.x0, canvas->x0);
1803     rect.y0 = MAX (rect.y0, canvas->y0);
1804     rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1805     rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1807 #ifdef DEBUG_REDRAW
1808     // paint the area to redraw yellow
1809     gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1810     gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1811                         canvas->pixmap_gc,
1812                         TRUE,
1813                         rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1814                         rect.x1 - rect.x0, rect.y1 - rect.y0);
1815 #endif
1817     PaintRectSetup setup;
1819     setup.canvas = canvas;
1820     setup.big_rect = rect;
1822     // Save the mouse location
1823     gint x, y;
1824     gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1825     setup.mouse_loc = sp_canvas_window_to_world (canvas, NR::Point(x,y));
1827     // CAIRO FIXME: the sw/sh calculations below all assume 24bpp, need fixing for 32bpp
1828     if (canvas->rendermode != RENDERMODE_OUTLINE) {
1829         // use 256K as a compromise to not slow down gradients
1830         // 256K is the cached buffer and we need 3 channels
1831         setup.max_pixels = 87381; // 256K/3
1832     } else {
1833         // paths only, so 1M works faster
1834         // 1M is the cached buffer and we need 3 channels
1835         setup.max_pixels = 349525;
1836     }
1838     // Start the clock
1839     g_get_current_time(&(setup.start_time));
1841     // Go
1842     return sp_canvas_paint_rect_internal(&setup, rect);
1845 /**
1846  * Force a full redraw after a specified number of interrupted redraws
1847  */
1848 void
1849 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1850   g_return_if_fail(canvas != NULL);
1852   canvas->forced_redraw_limit = count;
1853   canvas->forced_redraw_count = 0;
1856 /**
1857  * End forced full redraw requests
1858  */
1859 void
1860 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1861   g_return_if_fail(canvas != NULL);
1863   canvas->forced_redraw_limit = -1;
1866 /**
1867  * The canvas widget's expose callback.
1868  */
1869 static gint
1870 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1872     SPCanvas *canvas = SP_CANVAS (widget);
1874     if (!GTK_WIDGET_DRAWABLE (widget) ||
1875         (event->window != SP_CANVAS_WINDOW (canvas)))
1876         return FALSE;
1878     int n_rects;
1879     GdkRectangle *rects;
1880     gdk_region_get_rectangles (event->region, &rects, &n_rects);
1882     for (int i = 0; i < n_rects; i++) {
1883         NRRectL rect;
1885         rect.x0 = rects[i].x + canvas->x0;
1886         rect.y0 = rects[i].y + canvas->y0;
1887         rect.x1 = rect.x0 + rects[i].width;
1888         rect.y1 = rect.y0 + rects[i].height;
1890         sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1891     }
1893     if (n_rects > 0)
1894         g_free (rects);
1896     return FALSE;
1899 /**
1900  * The canvas widget's keypress callback.
1901  */
1902 static gint
1903 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1905     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1908 /**
1909  * Crossing event handler for the canvas.
1910  */
1911 static gint
1912 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1914     SPCanvas *canvas = SP_CANVAS (widget);
1916     if (event->window != SP_CANVAS_WINDOW (canvas))
1917         return FALSE;
1919     canvas->state = event->state;
1920     return pick_current_item (canvas, (GdkEvent *) event);
1923 /**
1924  * Focus in handler for the canvas.
1925  */
1926 static gint
1927 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
1929     GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1931     SPCanvas *canvas = SP_CANVAS (widget);
1933     if (canvas->focused_item) {
1934         return emit_event (canvas, (GdkEvent *) event);
1935     } else {
1936         return FALSE;
1937     }
1940 /**
1941  * Focus out handler for the canvas.
1942  */
1943 static gint
1944 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
1946     GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1948     SPCanvas *canvas = SP_CANVAS (widget);
1950     if (canvas->focused_item)
1951         return emit_event (canvas, (GdkEvent *) event);
1952     else
1953         return FALSE;
1956 /**
1957  * Helper that repaints the areas in the canvas that need it.
1958  *
1959  * @return true if all the dirty parts have been redrawn
1960  */
1961 static int
1962 paint (SPCanvas *canvas)
1964     if (canvas->need_update) {
1965         sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
1966         canvas->need_update = FALSE;
1967     }
1969     if (!canvas->need_redraw)
1970         return TRUE;
1972     Gdk::Region to_paint;
1974     for (int j=canvas->tTop; j<canvas->tBottom; j++) {
1975         for (int i=canvas->tLeft; i<canvas->tRight; i++) {
1976             int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
1978             if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
1979                 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
1980                                    TILE_SIZE, TILE_SIZE));
1981             }
1983         }
1984     }
1986     if (!to_paint.empty()) {
1987         Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
1988         typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
1989         for (Iter i=rect.begin(); i != rect.end(); ++i) {
1990             int x0 = (*i).get_x();
1991             int y0 = (*i).get_y();
1992             int x1 = x0 + (*i).get_width();
1993             int y1 = y0 + (*i).get_height();
1994             if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
1995                 // Aborted
1996                 return FALSE;
1997             };
1998         }
1999     }
2001     canvas->need_redraw = FALSE;
2003     // we've had a full unaborted redraw, reset the full redraw counter
2004     if (canvas->forced_redraw_limit != -1) {
2005         canvas->forced_redraw_count = 0;
2006     }
2008     return TRUE;
2011 /**
2012  * Helper that invokes update, paint, and repick on canvas.
2013  */
2014 static int
2015 do_update (SPCanvas *canvas)
2017     if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
2018         return TRUE;
2020     /* Cause the update if necessary */
2021     if (canvas->need_update) {
2022         sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2023         canvas->need_update = FALSE;
2024     }
2026     /* Paint if able to */
2027     if (GTK_WIDGET_DRAWABLE (canvas)) {
2028             return paint (canvas);
2029     }
2031     /* Pick new current item */
2032     while (canvas->need_repick) {
2033         canvas->need_repick = FALSE;
2034         pick_current_item (canvas, &canvas->pick_event);
2035     }
2037     return TRUE;
2040 /**
2041  * Idle handler for the canvas that deals with pending updates and redraws.
2042  */
2043 static gint
2044 idle_handler (gpointer data)
2046     GDK_THREADS_ENTER ();
2048     SPCanvas *canvas = SP_CANVAS (data);
2050     int const ret = do_update (canvas);
2052     if (ret) {
2053         /* Reset idle id */
2054         canvas->idle_id = 0;
2055     }
2057     GDK_THREADS_LEAVE ();
2059     return !ret;
2062 /**
2063  * Convenience function to add an idle handler to a canvas.
2064  */
2065 static void
2066 add_idle (SPCanvas *canvas)
2068     if (canvas->idle_id != 0)
2069         return;
2071     canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2074 /**
2075  * Returns the root group of the specified canvas.
2076  */
2077 SPCanvasGroup *
2078 sp_canvas_root (SPCanvas *canvas)
2080     g_return_val_if_fail (canvas != NULL, NULL);
2081     g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2083     return SP_CANVAS_GROUP (canvas->root);
2086 /**
2087  * Scrolls canvas to specific position.
2088  */
2089 void
2090 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2092     g_return_if_fail (canvas != NULL);
2093     g_return_if_fail (SP_IS_CANVAS (canvas));
2095     int ix = (int) (cx + 0.5);
2096     int iy = (int) (cy + 0.5);
2097     int dx = ix - canvas->x0;
2098     int dy = iy - canvas->y0;
2100     canvas->dx0 = cx;
2101     canvas->dy0 = cy;
2102     canvas->x0 = ix;
2103     canvas->y0 = iy;
2105     sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2107     if (!clear) {
2108         // scrolling without zoom; redraw only the newly exposed areas
2109         if ((dx != 0) || (dy != 0)) {
2110             canvas->is_scrolling = is_scrolling;
2111             if (GTK_WIDGET_REALIZED (canvas)) {
2112                 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2113             }
2114         }
2115     } else {
2116         // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2117     }
2119     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
2120     SPEventContext *ec = inkscape_active_event_context();
2121     if (SP_IS_3DBOX_CONTEXT (ec)) {
2122         // We could avoid redraw during panning by checking the status of is_scrolling, but this is
2123         // neither faster nor does it get rid of artefacts, so we update PLs unconditionally
2124         SP3DBoxContext *bc = SP_3DBOX_CONTEXT (ec);
2125         bc->_vpdrag->updateLines();
2126     }
2129 /**
2130  * Updates canvas if necessary.
2131  */
2132 void
2133 sp_canvas_update_now (SPCanvas *canvas)
2135     g_return_if_fail (canvas != NULL);
2136     g_return_if_fail (SP_IS_CANVAS (canvas));
2138     if (!(canvas->need_update ||
2139           canvas->need_redraw))
2140         return;
2142     do_update (canvas);
2145 /**
2146  * Update callback for canvas widget.
2147  */
2148 static void
2149 sp_canvas_request_update (SPCanvas *canvas)
2151     canvas->need_update = TRUE;
2152     add_idle (canvas);
2155 /**
2156  * Forces redraw of rectangular canvas area.
2157  */
2158 void
2159 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2161     NRRectL bbox;
2162     NRRectL visible;
2163     NRRectL clip;
2165     g_return_if_fail (canvas != NULL);
2166     g_return_if_fail (SP_IS_CANVAS (canvas));
2168     if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2169     if ((x0 >= x1) || (y0 >= y1)) return;
2171     bbox.x0 = x0;
2172     bbox.y0 = y0;
2173     bbox.x1 = x1;
2174     bbox.y1 = y1;
2176     visible.x0 = canvas->x0;
2177     visible.y0 = canvas->y0;
2178     visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2179     visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2181     nr_rect_l_intersect (&clip, &bbox, &visible);
2183     sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2184     add_idle (canvas);
2187 /**
2188  * Sets world coordinates from win and canvas.
2189  */
2190 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2192     g_return_if_fail (canvas != NULL);
2193     g_return_if_fail (SP_IS_CANVAS (canvas));
2195     if (worldx) *worldx = canvas->x0 + winx;
2196     if (worldy) *worldy = canvas->y0 + winy;
2199 /**
2200  * Sets win coordinates from world and canvas.
2201  */
2202 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2204     g_return_if_fail (canvas != NULL);
2205     g_return_if_fail (SP_IS_CANVAS (canvas));
2207     if (winx) *winx = worldx - canvas->x0;
2208     if (winy) *winy = worldy - canvas->y0;
2211 /**
2212  * Converts point from win to world coordinates.
2213  */
2214 NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win)
2216     g_assert (canvas != NULL);
2217     g_assert (SP_IS_CANVAS (canvas));
2219     return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2222 /**
2223  * Converts point from world to win coordinates.
2224  */
2225 NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world)
2227     g_assert (canvas != NULL);
2228     g_assert (SP_IS_CANVAS (canvas));
2230     return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2233 /**
2234  * Returns true if point given in world coordinates is inside window.
2235  */
2236 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world)
2238     g_assert( canvas != NULL );
2239     g_assert(SP_IS_CANVAS(canvas));
2241     using NR::X;
2242     using NR::Y;
2243     GtkWidget const &w = *GTK_WIDGET(canvas);
2244     return ( ( canvas->x0 <= world[X] )  &&
2245              ( canvas->y0 <= world[Y] )  &&
2246              ( world[X] < canvas->x0 + w.allocation.width )  &&
2247              ( world[Y] < canvas->y0 + w.allocation.height ) );
2250 /**
2251  * Return canvas window coordinates as NR::Rect.
2252  */
2253 NR::Rect SPCanvas::getViewbox() const
2255     GtkWidget const *w = GTK_WIDGET(this);
2257     return NR::Rect(NR::Point(dx0, dy0),
2258                     NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2261 inline int sp_canvas_tile_floor(int x)
2263     return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2266 inline int sp_canvas_tile_ceil(int x)
2268     return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2271 /**
2272  * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2273  */
2274 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2276     if ( nl >= nr || nt >= nb ) {
2277         if ( canvas->tiles ) g_free(canvas->tiles);
2278         canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2279         canvas->tileH=canvas->tileV=0;
2280         canvas->tiles=NULL;
2281         return;
2282     }
2283     int tl=sp_canvas_tile_floor(nl);
2284     int tt=sp_canvas_tile_floor(nt);
2285     int tr=sp_canvas_tile_ceil(nr);
2286     int tb=sp_canvas_tile_ceil(nb);
2288     int nh = tr-tl, nv = tb-tt;
2289     uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2290     for (int i=tl; i<tr; i++) {
2291         for (int j=tt; j<tb; j++) {
2292             int ind = (i-tl) + (j-tt)*nh;
2293             if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2294                 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2295             } else {
2296                 ntiles[ind]=0; // newly exposed areas get 0
2297             }
2298         }
2299     }
2300     if ( canvas->tiles ) g_free(canvas->tiles);
2301     canvas->tiles=ntiles;
2302     canvas->tLeft=tl;
2303     canvas->tTop=tt;
2304     canvas->tRight=tr;
2305     canvas->tBottom=tb;
2306     canvas->tileH=nh;
2307     canvas->tileV=nv;
2310 /*
2311  * Helper that queues a canvas rectangle for redraw
2312  */
2313 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2314     canvas->need_redraw = TRUE;
2316     sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2319 /**
2320  * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2321  */
2322 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2324     if ( nl >= nr || nt >= nb ) {
2325         return;
2326     }
2327     int tl=sp_canvas_tile_floor(nl);
2328     int tt=sp_canvas_tile_floor(nt);
2329     int tr=sp_canvas_tile_ceil(nr);
2330     int tb=sp_canvas_tile_ceil(nb);
2331     if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2332     if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2333     if ( tr > canvas->tRight ) tr=canvas->tRight;
2334     if ( tt < canvas->tTop ) tt=canvas->tTop;
2335     if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2337     for (int i=tl; i<tr; i++) {
2338         for (int j=tt; j<tb; j++) {
2339             canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2340         }
2341     }
2345 /*
2346   Local Variables:
2347   mode:c++
2348   c-file-style:"stroustrup"
2349   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2350   indent-tabs-mode:nil
2351   fill-column:99
2352   End:
2353 */
2354 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :