Code

noop: CodingStyle: re-indent a few files that had mixtures of spaces & tabs for inden...
[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                                              G_STRUCT_OFFSET (SPCanvasItemClass, event),
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     canvas->is_scrolling = false;
1029 /**
1030  * Convenience function to remove the idle handler of a canvas.
1031  */
1032 static void
1033 remove_idle (SPCanvas *canvas)
1035     if (canvas->idle_id) {
1036         gtk_idle_remove (canvas->idle_id);
1037         canvas->idle_id = 0;
1038     }
1041 /*
1042  * Removes the transient state of the canvas (idle handler, grabs).
1043  */
1044 static void
1045 shutdown_transients (SPCanvas *canvas)
1047     /* We turn off the need_redraw flag, since if the canvas is mapped again
1048      * it will request a redraw anyways.  We do not turn off the need_update
1049      * flag, though, because updates are not queued when the canvas remaps
1050      * itself.
1051      */
1052     if (canvas->need_redraw) {
1053         canvas->need_redraw = FALSE;
1054     }
1055     if ( canvas->tiles ) g_free(canvas->tiles);
1056     canvas->tiles=NULL;
1057     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1058     canvas->tileH=canvas->tileV=0;
1060     if (canvas->grabbed_item) {
1061         canvas->grabbed_item = NULL;
1062         gdk_pointer_ungrab (GDK_CURRENT_TIME);
1063     }
1065     remove_idle (canvas);
1068 /**
1069  * Destroy handler for SPCanvas.
1070  */
1071 static void
1072 sp_canvas_destroy (GtkObject *object)
1074     SPCanvas *canvas = SP_CANVAS (object);
1076     if (canvas->root) {
1077         gtk_object_unref (GTK_OBJECT (canvas->root));
1078         canvas->root = NULL;
1079     }
1081     shutdown_transients (canvas);
1083     if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1084         (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1087 /**
1088  * Returns new canvas as widget.
1089  */
1090 GtkWidget *
1091 sp_canvas_new_aa (void)
1093     SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1095     return (GtkWidget *) canvas;
1098 /**
1099  * The canvas widget's realize callback.
1100  */
1101 static void
1102 sp_canvas_realize (GtkWidget *widget)
1104     SPCanvas *canvas = SP_CANVAS (widget);
1106     GdkWindowAttr attributes;
1107     attributes.window_type = GDK_WINDOW_CHILD;
1108     attributes.x = widget->allocation.x;
1109     attributes.y = widget->allocation.y;
1110     attributes.width = widget->allocation.width;
1111     attributes.height = widget->allocation.height;
1112     attributes.wclass = GDK_INPUT_OUTPUT;
1113     attributes.visual = gdk_rgb_get_visual ();
1114     attributes.colormap = gdk_rgb_get_cmap ();
1115     attributes.event_mask = (gtk_widget_get_events (widget) |
1116                              GDK_EXPOSURE_MASK |
1117                              GDK_BUTTON_PRESS_MASK |
1118                              GDK_BUTTON_RELEASE_MASK |
1119                              GDK_POINTER_MOTION_MASK |
1120                              GDK_PROXIMITY_IN_MASK |
1121                              GDK_PROXIMITY_OUT_MASK |
1122                              GDK_KEY_PRESS_MASK |
1123                              GDK_KEY_RELEASE_MASK |
1124                              GDK_ENTER_NOTIFY_MASK |
1125                              GDK_LEAVE_NOTIFY_MASK |
1126                              GDK_FOCUS_CHANGE_MASK);
1127     gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1129     widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1130     gdk_window_set_user_data (widget->window, widget);
1132     if ( prefs_get_int_attribute ("options.useextinput", "value", 1) )
1133         gtk_widget_set_events(widget, attributes.event_mask);
1135     widget->style = gtk_style_attach (widget->style, widget->window);
1137     GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1139     canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1142 /**
1143  * The canvas widget's unrealize callback.
1144  */
1145 static void
1146 sp_canvas_unrealize (GtkWidget *widget)
1148     SPCanvas *canvas = SP_CANVAS (widget);
1150     shutdown_transients (canvas);
1152     gdk_gc_destroy (canvas->pixmap_gc);
1153     canvas->pixmap_gc = NULL;
1155     if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1156         (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1159 /**
1160  * The canvas widget's size_request callback.
1161  */
1162 static void
1163 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1165     static_cast<void>(SP_CANVAS (widget));
1167     req->width = 256;
1168     req->height = 256;
1171 /**
1172  * The canvas widget's size_allocate callback.
1173  */
1174 static void
1175 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1177     SPCanvas *canvas = SP_CANVAS (widget);
1179     /* Schedule redraw of new region */
1180     sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1181     if (allocation->width > widget->allocation.width) {
1182         sp_canvas_request_redraw (canvas,
1183                                   canvas->x0 + widget->allocation.width,
1184                                   0,
1185                                   canvas->x0 + allocation->width,
1186                                   canvas->y0 + allocation->height);
1187     }
1188     if (allocation->height > widget->allocation.height) {
1189         sp_canvas_request_redraw (canvas,
1190                                   0,
1191                                   canvas->y0 + widget->allocation.height,
1192                                   canvas->x0 + allocation->width,
1193                                   canvas->y0 + allocation->height);
1194     }
1196     widget->allocation = *allocation;
1198     if (GTK_WIDGET_REALIZED (widget)) {
1199         gdk_window_move_resize (widget->window,
1200                                 widget->allocation.x, widget->allocation.y,
1201                                 widget->allocation.width, widget->allocation.height);
1202     }
1205 /**
1206  * Helper that emits an event for an item in the canvas, be it the current
1207  * item, grabbed item, or focused item, as appropriate.
1208  */
1209 static int
1210 emit_event (SPCanvas *canvas, GdkEvent *event)
1212     guint mask;
1214     if (canvas->grabbed_item) {
1215         switch (event->type) {
1216         case GDK_ENTER_NOTIFY:
1217             mask = GDK_ENTER_NOTIFY_MASK;
1218             break;
1219         case GDK_LEAVE_NOTIFY:
1220             mask = GDK_LEAVE_NOTIFY_MASK;
1221             break;
1222         case GDK_MOTION_NOTIFY:
1223             mask = GDK_POINTER_MOTION_MASK;
1224             break;
1225         case GDK_BUTTON_PRESS:
1226         case GDK_2BUTTON_PRESS:
1227         case GDK_3BUTTON_PRESS:
1228             mask = GDK_BUTTON_PRESS_MASK;
1229             break;
1230         case GDK_BUTTON_RELEASE:
1231             mask = GDK_BUTTON_RELEASE_MASK;
1232             break;
1233         case GDK_KEY_PRESS:
1234             mask = GDK_KEY_PRESS_MASK;
1235             break;
1236         case GDK_KEY_RELEASE:
1237             mask = GDK_KEY_RELEASE_MASK;
1238             break;
1239         case GDK_SCROLL:
1240             mask = GDK_SCROLL;
1241             break;
1242         default:
1243             mask = 0;
1244             break;
1245         }
1247         if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1248     }
1250     /* Convert to world coordinates -- we have two cases because of diferent
1251      * offsets of the fields in the event structures.
1252      */
1254     GdkEvent ev = *event;
1256     switch (ev.type) {
1257     case GDK_ENTER_NOTIFY:
1258     case GDK_LEAVE_NOTIFY:
1259         ev.crossing.x += canvas->x0;
1260         ev.crossing.y += canvas->y0;
1261         break;
1262     case GDK_MOTION_NOTIFY:
1263     case GDK_BUTTON_PRESS:
1264     case GDK_2BUTTON_PRESS:
1265     case GDK_3BUTTON_PRESS:
1266     case GDK_BUTTON_RELEASE:
1267         ev.motion.x += canvas->x0;
1268         ev.motion.y += canvas->y0;
1269         break;
1270     default:
1271         break;
1272     }
1274     /* Choose where we send the event */
1276     /* canvas->current_item becomes NULL in some cases under Win32
1277     ** (e.g. if the pointer leaves the window).  So this is a hack that
1278     ** Lauris applied to SP to get around the problem.
1279     */
1280     SPCanvasItem* item = NULL;
1281     if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1282         item = canvas->grabbed_item;
1283     } else {
1284         item = canvas->current_item;
1285     }
1287     if (canvas->focused_item &&
1288         ((event->type == GDK_KEY_PRESS) ||
1289          (event->type == GDK_KEY_RELEASE) ||
1290          (event->type == GDK_FOCUS_CHANGE))) {
1291         item = canvas->focused_item;
1292     }
1294     /* The event is propagated up the hierarchy (for if someone connected to
1295      * a group instead of a leaf event), and emission is stopped if a
1296      * handler returns TRUE, just like for GtkWidget events.
1297      */
1299     gint finished = FALSE;
1301     while (item && !finished) {
1302         gtk_object_ref (GTK_OBJECT (item));
1303         gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1304         SPCanvasItem *parent = item->parent;
1305         gtk_object_unref (GTK_OBJECT (item));
1306         item = parent;
1307     }
1309     return finished;
1312 /**
1313  * Helper that re-picks the current item in the canvas, based on the event's
1314  * coordinates and emits enter/leave events for items as appropriate.
1315  */
1316 static int
1317 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1319     int button_down = 0;
1320     double x, y;
1322     if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1323         return FALSE;
1325     int retval = FALSE;
1327     if (canvas->gen_all_enter_events == false) {
1328         // If a button is down, we'll perform enter and leave events on the
1329         // current item, but not enter on any other item.  This is more or
1330         // less like X pointer grabbing for canvas items.
1331         //
1332         button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1333                 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1335         if (!button_down) canvas->left_grabbed_item = FALSE;
1336     }
1338     /* Save the event in the canvas.  This is used to synthesize enter and
1339      * leave events in case the current item changes.  It is also used to
1340      * re-pick the current item if the current one gets deleted.  Also,
1341      * synthesize an enter event.
1342      */
1343     if (event != &canvas->pick_event) {
1344         if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1345             /* these fields have the same offsets in both types of events */
1347             canvas->pick_event.crossing.type       = GDK_ENTER_NOTIFY;
1348             canvas->pick_event.crossing.window     = event->motion.window;
1349             canvas->pick_event.crossing.send_event = event->motion.send_event;
1350             canvas->pick_event.crossing.subwindow  = NULL;
1351             canvas->pick_event.crossing.x          = event->motion.x;
1352             canvas->pick_event.crossing.y          = event->motion.y;
1353             canvas->pick_event.crossing.mode       = GDK_CROSSING_NORMAL;
1354             canvas->pick_event.crossing.detail     = GDK_NOTIFY_NONLINEAR;
1355             canvas->pick_event.crossing.focus      = FALSE;
1356             canvas->pick_event.crossing.state      = event->motion.state;
1358             /* these fields don't have the same offsets in both types of events */
1360             if (event->type == GDK_MOTION_NOTIFY) {
1361                 canvas->pick_event.crossing.x_root = event->motion.x_root;
1362                 canvas->pick_event.crossing.y_root = event->motion.y_root;
1363             } else {
1364                 canvas->pick_event.crossing.x_root = event->button.x_root;
1365                 canvas->pick_event.crossing.y_root = event->button.y_root;
1366             }
1367         } else {
1368             canvas->pick_event = *event;
1369         }
1370     }
1372     /* Don't do anything else if this is a recursive call */
1373     if (canvas->in_repick) return retval;
1375     /* LeaveNotify means that there is no current item, so we don't look for one */
1376     if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1377         /* these fields don't have the same offsets in both types of events */
1379         if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1380             x = canvas->pick_event.crossing.x;
1381             y = canvas->pick_event.crossing.y;
1382         } else {
1383             x = canvas->pick_event.motion.x;
1384             y = canvas->pick_event.motion.y;
1385         }
1387         /* world coords */
1388         x += canvas->x0;
1389         y += canvas->y0;
1391         /* find the closest item */
1392         if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1393             sp_canvas_item_invoke_point (canvas->root, NR::Point(x, y), &canvas->new_current_item);
1394         } else {
1395             canvas->new_current_item = NULL;
1396         }
1397     } else {
1398         canvas->new_current_item = NULL;
1399     }
1401     if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1402         return retval; /* current item did not change */
1403     }
1405     /* Synthesize events for old and new current items */
1407     if ((canvas->new_current_item != canvas->current_item)
1408         && (canvas->current_item != NULL)
1409         && !canvas->left_grabbed_item) {
1410         GdkEvent new_event;
1411         SPCanvasItem *item;
1413         item = canvas->current_item;
1415         new_event = canvas->pick_event;
1416         new_event.type = GDK_LEAVE_NOTIFY;
1418         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1419         new_event.crossing.subwindow = NULL;
1420         canvas->in_repick = TRUE;
1421         retval = emit_event (canvas, &new_event);
1422         canvas->in_repick = FALSE;
1423     }
1425     if (canvas->gen_all_enter_events == false) {
1426         // new_current_item may have been set to NULL during the call to
1427         // emit_event() above
1428         if ((canvas->new_current_item != canvas->current_item) && button_down) {
1429             canvas->left_grabbed_item = TRUE;
1430             return retval;
1431         }
1432     }
1434     /* Handle the rest of cases */
1436     canvas->left_grabbed_item = FALSE;
1437     canvas->current_item = canvas->new_current_item;
1439     if (canvas->current_item != NULL) {
1440         GdkEvent new_event;
1442         new_event = canvas->pick_event;
1443         new_event.type = GDK_ENTER_NOTIFY;
1444         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1445         new_event.crossing.subwindow = NULL;
1446         retval = emit_event (canvas, &new_event);
1447     }
1449     return retval;
1452 /**
1453  * Button event handler for the canvas.
1454  */
1455 static gint
1456 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1458     SPCanvas *canvas = SP_CANVAS (widget);
1460     int retval = FALSE;
1462     /* dispatch normally regardless of the event's window if an item has
1463        has a pointer grab in effect */
1464     if (!canvas->grabbed_item &&
1465         event->window != SP_CANVAS_WINDOW (canvas))
1466         return retval;
1468     int mask;
1469     switch (event->button) {
1470     case 1:
1471         mask = GDK_BUTTON1_MASK;
1472         break;
1473     case 2:
1474         mask = GDK_BUTTON2_MASK;
1475         break;
1476     case 3:
1477         mask = GDK_BUTTON3_MASK;
1478         break;
1479     case 4:
1480         mask = GDK_BUTTON4_MASK;
1481         break;
1482     case 5:
1483         mask = GDK_BUTTON5_MASK;
1484         break;
1485     default:
1486         mask = 0;
1487     }
1489     switch (event->type) {
1490     case GDK_BUTTON_PRESS:
1491     case GDK_2BUTTON_PRESS:
1492     case GDK_3BUTTON_PRESS:
1493         /* Pick the current item as if the button were not pressed, and
1494          * then process the event.
1495          */
1496         canvas->state = event->state;
1497         pick_current_item (canvas, (GdkEvent *) event);
1498         canvas->state ^= mask;
1499         retval = emit_event (canvas, (GdkEvent *) event);
1500         break;
1502     case GDK_BUTTON_RELEASE:
1503         /* Process the event as if the button were pressed, then repick
1504          * after the button has been released
1505          */
1506         canvas->state = event->state;
1507         retval = emit_event (canvas, (GdkEvent *) event);
1508         event->state ^= mask;
1509         canvas->state = event->state;
1510         pick_current_item (canvas, (GdkEvent *) event);
1511         event->state ^= mask;
1512         break;
1514     default:
1515         g_assert_not_reached ();
1516     }
1518     return retval;
1521 /**
1522  * Scroll event handler for the canvas.
1523  *
1524  * \todo FIXME: generate motion events to re-select items.
1525  */
1526 static gint
1527 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1529     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1532 /**
1533  * Motion event handler for the canvas.
1534  */
1535 static int
1536 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1538     SPCanvas *canvas = SP_CANVAS (widget);
1540     if (event->window != SP_CANVAS_WINDOW (canvas))
1541         return FALSE;
1543     if (canvas->grabbed_event_mask & GDK_POINTER_MOTION_HINT_MASK) {
1544         gint x, y;
1545         gdk_window_get_pointer (widget->window, &x, &y, NULL);
1546         event->x = x;
1547         event->y = y;
1548     }
1550     canvas->state = event->state;
1551     pick_current_item (canvas, (GdkEvent *) event);
1553     return emit_event (canvas, (GdkEvent *) event);
1556 static void
1557 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)
1559     GtkWidget *widget = GTK_WIDGET (canvas);
1561     SPCanvasBuf buf;
1562     if (canvas->rendermode != RENDERMODE_OUTLINE) {
1563         buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1564     } else {
1565         buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1566     }
1568     // Mark the region clean
1569     sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1571     buf.buf_rowstride = sw * 3; // CAIRO FIXME: for cairo output, the buffer must be RGB unpacked, i.e. sw * 4
1572     buf.rect.x0 = x0;
1573     buf.rect.y0 = y0;
1574     buf.rect.x1 = x1;
1575     buf.rect.y1 = y1;
1576     buf.visible_rect.x0 = draw_x1;
1577     buf.visible_rect.y0 = draw_y1;
1578     buf.visible_rect.x1 = draw_x2;
1579     buf.visible_rect.y1 = draw_y2;
1580     GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1581     buf.bg_color = (((color->red & 0xff00) << 8)
1582                     | (color->green & 0xff00)
1583                     | (color->blue >> 8));
1584     buf.is_empty = true;
1586     if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1587         SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1588     }
1590 #if ENABLE_LCMS
1591         cmsHPROFILE hprof = Inkscape::colorprofile_get_system_profile_handle();
1592         cmsHPROFILE srcprof = hprof ? cmsCreate_sRGBProfile() : 0;
1593         cmsHTRANSFORM transf = hprof ? cmsCreateTransform( srcprof, TYPE_RGB_8, hprof, TYPE_RGB_8, INTENT_PERCEPTUAL, 0 ) : 0;
1594 #endif // ENABLE_LCMS
1596     if (buf.is_empty) {
1597 #if ENABLE_LCMS
1598         if ( transf ) {
1599             cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1600         }
1601 #endif // ENABLE_LCMS
1602         gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1603         gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1604                             canvas->pixmap_gc,
1605                             TRUE,
1606                             x0 - canvas->x0, y0 - canvas->y0,
1607                             x1 - x0, y1 - y0);
1608     } else {
1609 /*
1610 // CAIRO FIXME: after SPCanvasBuf is made 32bpp throughout, this rgb_draw below can be replaced with the below.
1611 // Why this must not be done currently:
1612 // - all canvas items (handles, nodes etc) paint themselves assuming 24bpp
1613 // - cairo assumes bgra, but we have rgba, so r and b get swapped (until we paint all with cairo too)
1614 // - it does not seem to be any faster; in fact since with 32bpp, buf contains less pixels,
1615 // we need more bufs to paint a given area and as a result it's even a bit slower
1617     cairo_surface_t* cst = cairo_image_surface_create_for_data (
1618         buf.buf,
1619         CAIRO_FORMAT_RGB24,  // unpacked, i.e. 32 bits! one byte is unused
1620         x1 - x0, y1 - y0,
1621         buf.buf_rowstride
1622         );
1623         cairo_t *ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1624         cairo_set_source_surface (ct, cst, x0 - canvas->x0, y0 - canvas->y0);
1625         cairo_paint (ct);
1626     cairo_destroy (ct);
1627     cairo_surface_finish (cst);
1628     cairo_surface_destroy (cst);
1629 */
1631 #if ENABLE_LCMS
1632         if ( transf ) {
1633             for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1634                 guchar* p = buf.buf + (sw * 3) * yy;
1635                 cmsDoTransform( transf, p, p, (x1 - x0) );
1636             }
1637         }
1638 #endif // ENABLE_LCMS
1640         gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1641                                       canvas->pixmap_gc,
1642                                       x0 - canvas->x0, y0 - canvas->y0,
1643                                       x1 - x0, y1 - y0,
1644                                       GDK_RGB_DITHER_MAX,
1645                                       buf.buf,
1646                                       sw * 3,
1647                                       x0 - canvas->x0, y0 - canvas->y0);
1648     }
1650 #if ENABLE_LCMS
1651     if ( transf ) {
1652         cmsDeleteTransform( transf );
1653         transf = 0;
1654     }
1655 #endif // ENABLE_LCMS
1658     if (canvas->rendermode != RENDERMODE_OUTLINE) {
1659         nr_pixelstore_256K_free (buf.buf);
1660     } else {
1661         nr_pixelstore_1M_free (buf.buf);
1662     }
1665 struct PaintRectSetup {
1666     SPCanvas* canvas;
1667     NRRectL big_rect;
1668     GTimeVal start_time;
1669     int max_pixels;
1670     NR::Point mouse_loc;
1671 };
1673 /**
1674  * Paint the given rect, recursively subdividing the region until it is the size of a single
1675  * buffer.
1676  *
1677  * @return true if the drawing completes
1678  */
1679 static int
1680 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1682     GTimeVal now;
1683     g_get_current_time (&now);
1685     glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1686         + (now.tv_usec - setup->start_time.tv_usec);
1688     // Allow only very fast buffers to be run together;
1689     // as soon as the total redraw time exceeds 1ms, cancel;
1690     // this returns control to the idle loop and allows Inkscape to process user input
1691     // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1692     // it will get back and finish painting what remains to paint.
1693     if (elapsed > 1000) {
1695         // Interrupting redraw isn't always good.
1696         // For example, when you drag one node of a big path, only the buffer containing
1697         // the mouse cursor will be redrawn again and again, and the rest of the path
1698         // will remain stale because Inkscape never has enough idle time to redraw all
1699         // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1700         // If this limit is set, and if we have aborted redraw more times than is allowed,
1701         // interrupting is blocked and we're forced to redraw full screen once
1702         // (after which we can again interrupt forced_redraw_limit times).
1703         if (setup->canvas->forced_redraw_limit < 0 ||
1704             setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1706             if (setup->canvas->forced_redraw_limit != -1) {
1707                 setup->canvas->forced_redraw_count++;
1708             }
1710             return false;
1711         }
1712     }
1714     // Find the optimal buffer dimensions
1715     int bw = this_rect.x1 - this_rect.x0;
1716     int bh = this_rect.y1 - this_rect.y0;
1717     if ((bw < 1) || (bh < 1))
1718         return 0;
1720     if (bw * bh < setup->max_pixels) {
1721         // We are small enough
1722         sp_canvas_paint_single_buffer (setup->canvas,
1723                                        this_rect.x0, this_rect.y0,
1724                                        this_rect.x1, this_rect.y1,
1725                                        setup->big_rect.x0, setup->big_rect.y0,
1726                                        setup->big_rect.x1, setup->big_rect.y1, bw);
1727         return 1;
1728     }
1730     NRRectL lo = this_rect;
1731     NRRectL hi = this_rect;
1733 /*
1734 This test determines the redraw strategy:
1736 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1737 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1738 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1739 and seems to be faster for drawings with many smaller objects at zoom-out.
1741 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1742 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1743 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1744 faster.
1746 The default for now is the strips mode.
1747 */
1748     if (bw < bh || bh < 2 * TILE_SIZE) {
1749         int mid = (this_rect.x0 + this_rect.x1) / 2;
1750         // Make sure that mid lies on a tile boundary
1751         mid = (mid / TILE_SIZE) * TILE_SIZE;
1753         lo.x1 = mid;
1754         hi.x0 = mid;
1756         if (setup->mouse_loc[NR::X] < mid) {
1757             // Always paint towards the mouse first
1758             return sp_canvas_paint_rect_internal(setup, lo)
1759                 && sp_canvas_paint_rect_internal(setup, hi);
1760         } else {
1761             return sp_canvas_paint_rect_internal(setup, hi)
1762                 && sp_canvas_paint_rect_internal(setup, lo);
1763         }
1764     } else {
1765         int mid = (this_rect.y0 + this_rect.y1) / 2;
1766         // Make sure that mid lies on a tile boundary
1767         mid = (mid / TILE_SIZE) * TILE_SIZE;
1769         lo.y1 = mid;
1770         hi.y0 = mid;
1772         if (setup->mouse_loc[NR::Y] < mid) {
1773             // Always paint towards the mouse first
1774             return sp_canvas_paint_rect_internal(setup, lo)
1775                 && sp_canvas_paint_rect_internal(setup, hi);
1776         } else {
1777             return sp_canvas_paint_rect_internal(setup, hi)
1778                 && sp_canvas_paint_rect_internal(setup, lo);
1779         }
1780     }
1784 /**
1785  * Helper that draws a specific rectangular part of the canvas.
1786  *
1787  * @return true if the rectangle painting succeeds.
1788  */
1789 static bool
1790 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1792     g_return_val_if_fail (!canvas->need_update, false);
1794     NRRectL rect;
1795     rect.x0 = xx0;
1796     rect.x1 = xx1;
1797     rect.y0 = yy0;
1798     rect.y1 = yy1;
1800     // Clip rect-to-draw by the current visible area
1801     rect.x0 = MAX (rect.x0, canvas->x0);
1802     rect.y0 = MAX (rect.y0, canvas->y0);
1803     rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1804     rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1806 #ifdef DEBUG_REDRAW
1807     // paint the area to redraw yellow
1808     gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1809     gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1810                         canvas->pixmap_gc,
1811                         TRUE,
1812                         rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1813                         rect.x1 - rect.x0, rect.y1 - rect.y0);
1814 #endif
1816     PaintRectSetup setup;
1818     setup.canvas = canvas;
1819     setup.big_rect = rect;
1821     // Save the mouse location
1822     gint x, y;
1823     gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1824     setup.mouse_loc = sp_canvas_window_to_world (canvas, NR::Point(x,y));
1826     // CAIRO FIXME: the sw/sh calculations below all assume 24bpp, need fixing for 32bpp
1827     if (canvas->rendermode != RENDERMODE_OUTLINE) {
1828         // use 256K as a compromise to not slow down gradients
1829         // 256K is the cached buffer and we need 3 channels
1830         setup.max_pixels = 87381; // 256K/3
1831     } else {
1832         // paths only, so 1M works faster
1833         // 1M is the cached buffer and we need 3 channels
1834         setup.max_pixels = 349525;
1835     }
1837     // Start the clock
1838     g_get_current_time(&(setup.start_time));
1840     // Go
1841     return sp_canvas_paint_rect_internal(&setup, rect);
1844 /**
1845  * Force a full redraw after a specified number of interrupted redraws
1846  */
1847 void
1848 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1849   g_return_if_fail(canvas != NULL);
1851   canvas->forced_redraw_limit = count;
1852   canvas->forced_redraw_count = 0;
1855 /**
1856  * End forced full redraw requests
1857  */
1858 void
1859 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1860   g_return_if_fail(canvas != NULL);
1862   canvas->forced_redraw_limit = -1;
1865 /**
1866  * The canvas widget's expose callback.
1867  */
1868 static gint
1869 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1871     SPCanvas *canvas = SP_CANVAS (widget);
1873     if (!GTK_WIDGET_DRAWABLE (widget) ||
1874         (event->window != SP_CANVAS_WINDOW (canvas)))
1875         return FALSE;
1877     int n_rects;
1878     GdkRectangle *rects;
1879     gdk_region_get_rectangles (event->region, &rects, &n_rects);
1881     for (int i = 0; i < n_rects; i++) {
1882         NRRectL rect;
1884         rect.x0 = rects[i].x + canvas->x0;
1885         rect.y0 = rects[i].y + canvas->y0;
1886         rect.x1 = rect.x0 + rects[i].width;
1887         rect.y1 = rect.y0 + rects[i].height;
1889         sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1890     }
1892     if (n_rects > 0)
1893         g_free (rects);
1895     return FALSE;
1898 /**
1899  * The canvas widget's keypress callback.
1900  */
1901 static gint
1902 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1904     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1907 /**
1908  * Crossing event handler for the canvas.
1909  */
1910 static gint
1911 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1913     SPCanvas *canvas = SP_CANVAS (widget);
1915     if (event->window != SP_CANVAS_WINDOW (canvas))
1916         return FALSE;
1918     canvas->state = event->state;
1919     return pick_current_item (canvas, (GdkEvent *) event);
1922 /**
1923  * Focus in handler for the canvas.
1924  */
1925 static gint
1926 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
1928     GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1930     SPCanvas *canvas = SP_CANVAS (widget);
1932     if (canvas->focused_item) {
1933         return emit_event (canvas, (GdkEvent *) event);
1934     } else {
1935         return FALSE;
1936     }
1939 /**
1940  * Focus out handler for the canvas.
1941  */
1942 static gint
1943 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
1945     GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1947     SPCanvas *canvas = SP_CANVAS (widget);
1949     if (canvas->focused_item)
1950         return emit_event (canvas, (GdkEvent *) event);
1951     else
1952         return FALSE;
1955 /**
1956  * Helper that repaints the areas in the canvas that need it.
1957  *
1958  * @return true if all the dirty parts have been redrawn
1959  */
1960 static int
1961 paint (SPCanvas *canvas)
1963     if (canvas->need_update) {
1964         sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
1965         canvas->need_update = FALSE;
1966     }
1968     if (!canvas->need_redraw)
1969         return TRUE;
1971     Gdk::Region to_paint;
1973     for (int j=canvas->tTop; j<canvas->tBottom; j++) {
1974         for (int i=canvas->tLeft; i<canvas->tRight; i++) {
1975             int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
1977             if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
1978                 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
1979                                    TILE_SIZE, TILE_SIZE));
1980             }
1982         }
1983     }
1985     if (!to_paint.empty()) {
1986         Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
1987         typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
1988         for (Iter i=rect.begin(); i != rect.end(); ++i) {
1989             int x0 = (*i).get_x();
1990             int y0 = (*i).get_y();
1991             int x1 = x0 + (*i).get_width();
1992             int y1 = y0 + (*i).get_height();
1993             if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
1994                 // Aborted
1995                 return FALSE;
1996             };
1997         }
1998     }
2000     canvas->need_redraw = FALSE;
2002     // we've had a full unaborted redraw, reset the full redraw counter
2003     if (canvas->forced_redraw_limit != -1) {
2004         canvas->forced_redraw_count = 0;
2005     }
2007     return TRUE;
2010 /**
2011  * Helper that invokes update, paint, and repick on canvas.
2012  */
2013 static int
2014 do_update (SPCanvas *canvas)
2016     if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
2017         return TRUE;
2019     /* Cause the update if necessary */
2020     if (canvas->need_update) {
2021         sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2022         canvas->need_update = FALSE;
2023     }
2025     /* Paint if able to */
2026     if (GTK_WIDGET_DRAWABLE (canvas)) {
2027             return paint (canvas);
2028     }
2030     /* Pick new current item */
2031     while (canvas->need_repick) {
2032         canvas->need_repick = FALSE;
2033         pick_current_item (canvas, &canvas->pick_event);
2034     }
2036     return TRUE;
2039 /**
2040  * Idle handler for the canvas that deals with pending updates and redraws.
2041  */
2042 static gint
2043 idle_handler (gpointer data)
2045     GDK_THREADS_ENTER ();
2047     SPCanvas *canvas = SP_CANVAS (data);
2049     int const ret = do_update (canvas);
2051     if (ret) {
2052         /* Reset idle id */
2053         canvas->idle_id = 0;
2054     }
2056     GDK_THREADS_LEAVE ();
2058     return !ret;
2061 /**
2062  * Convenience function to add an idle handler to a canvas.
2063  */
2064 static void
2065 add_idle (SPCanvas *canvas)
2067     if (canvas->idle_id != 0)
2068         return;
2070     canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2073 /**
2074  * Returns the root group of the specified canvas.
2075  */
2076 SPCanvasGroup *
2077 sp_canvas_root (SPCanvas *canvas)
2079     g_return_val_if_fail (canvas != NULL, NULL);
2080     g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2082     return SP_CANVAS_GROUP (canvas->root);
2085 /**
2086  * Scrolls canvas to specific position.
2087  */
2088 void
2089 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2091     g_return_if_fail (canvas != NULL);
2092     g_return_if_fail (SP_IS_CANVAS (canvas));
2094     int ix = (int) (cx + 0.5);
2095     int iy = (int) (cy + 0.5);
2096     int dx = ix - canvas->x0;
2097     int dy = iy - canvas->y0;
2099     canvas->dx0 = cx;
2100     canvas->dy0 = cy;
2101     canvas->x0 = ix;
2102     canvas->y0 = iy;
2104     sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2106     if (!clear) {
2107         // scrolling without zoom; redraw only the newly exposed areas
2108         if ((dx != 0) || (dy != 0)) {
2109             canvas->is_scrolling = is_scrolling;
2110             if (GTK_WIDGET_REALIZED (canvas)) {
2111                 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2112             }
2113         }
2114     } else {
2115         // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2116     }
2118     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
2119     SPEventContext *ec = inkscape_active_event_context();
2120     if (SP_IS_3DBOX_CONTEXT (ec)) {
2121         // We could avoid redraw during panning by checking the status of is_scrolling, but this is
2122         // neither faster nor does it get rid of artefacts, so we update PLs unconditionally
2123         SP3DBoxContext *bc = SP_3DBOX_CONTEXT (ec);
2124         bc->_vpdrag->updateLines();
2125     }
2128 /**
2129  * Updates canvas if necessary.
2130  */
2131 void
2132 sp_canvas_update_now (SPCanvas *canvas)
2134     g_return_if_fail (canvas != NULL);
2135     g_return_if_fail (SP_IS_CANVAS (canvas));
2137     if (!(canvas->need_update ||
2138           canvas->need_redraw))
2139         return;
2141     do_update (canvas);
2144 /**
2145  * Update callback for canvas widget.
2146  */
2147 static void
2148 sp_canvas_request_update (SPCanvas *canvas)
2150     canvas->need_update = TRUE;
2151     add_idle (canvas);
2154 /**
2155  * Forces redraw of rectangular canvas area.
2156  */
2157 void
2158 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2160     NRRectL bbox;
2161     NRRectL visible;
2162     NRRectL clip;
2164     g_return_if_fail (canvas != NULL);
2165     g_return_if_fail (SP_IS_CANVAS (canvas));
2167     if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2168     if ((x0 >= x1) || (y0 >= y1)) return;
2170     bbox.x0 = x0;
2171     bbox.y0 = y0;
2172     bbox.x1 = x1;
2173     bbox.y1 = y1;
2175     visible.x0 = canvas->x0;
2176     visible.y0 = canvas->y0;
2177     visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2178     visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2180     nr_rect_l_intersect (&clip, &bbox, &visible);
2182     sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2183     add_idle (canvas);
2186 /**
2187  * Sets world coordinates from win and canvas.
2188  */
2189 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2191     g_return_if_fail (canvas != NULL);
2192     g_return_if_fail (SP_IS_CANVAS (canvas));
2194     if (worldx) *worldx = canvas->x0 + winx;
2195     if (worldy) *worldy = canvas->y0 + winy;
2198 /**
2199  * Sets win coordinates from world and canvas.
2200  */
2201 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2203     g_return_if_fail (canvas != NULL);
2204     g_return_if_fail (SP_IS_CANVAS (canvas));
2206     if (winx) *winx = worldx - canvas->x0;
2207     if (winy) *winy = worldy - canvas->y0;
2210 /**
2211  * Converts point from win to world coordinates.
2212  */
2213 NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win)
2215     g_assert (canvas != NULL);
2216     g_assert (SP_IS_CANVAS (canvas));
2218     return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2221 /**
2222  * Converts point from world to win coordinates.
2223  */
2224 NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world)
2226     g_assert (canvas != NULL);
2227     g_assert (SP_IS_CANVAS (canvas));
2229     return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2232 /**
2233  * Returns true if point given in world coordinates is inside window.
2234  */
2235 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world)
2237     g_assert( canvas != NULL );
2238     g_assert(SP_IS_CANVAS(canvas));
2240     using NR::X;
2241     using NR::Y;
2242     GtkWidget const &w = *GTK_WIDGET(canvas);
2243     return ( ( canvas->x0 <= world[X] )  &&
2244              ( canvas->y0 <= world[Y] )  &&
2245              ( world[X] < canvas->x0 + w.allocation.width )  &&
2246              ( world[Y] < canvas->y0 + w.allocation.height ) );
2249 /**
2250  * Return canvas window coordinates as NR::Rect.
2251  */
2252 NR::Rect SPCanvas::getViewbox() const
2254     GtkWidget const *w = GTK_WIDGET(this);
2256     return NR::Rect(NR::Point(dx0, dy0),
2257                     NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2260 inline int sp_canvas_tile_floor(int x)
2262     return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2265 inline int sp_canvas_tile_ceil(int x)
2267     return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2270 /**
2271  * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2272  */
2273 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2275     if ( nl >= nr || nt >= nb ) {
2276         if ( canvas->tiles ) g_free(canvas->tiles);
2277         canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2278         canvas->tileH=canvas->tileV=0;
2279         canvas->tiles=NULL;
2280         return;
2281     }
2282     int tl=sp_canvas_tile_floor(nl);
2283     int tt=sp_canvas_tile_floor(nt);
2284     int tr=sp_canvas_tile_ceil(nr);
2285     int tb=sp_canvas_tile_ceil(nb);
2287     int nh = tr-tl, nv = tb-tt;
2288     uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2289     for (int i=tl; i<tr; i++) {
2290         for (int j=tt; j<tb; j++) {
2291             int ind = (i-tl) + (j-tt)*nh;
2292             if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2293                 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2294             } else {
2295                 ntiles[ind]=0; // newly exposed areas get 0
2296             }
2297         }
2298     }
2299     if ( canvas->tiles ) g_free(canvas->tiles);
2300     canvas->tiles=ntiles;
2301     canvas->tLeft=tl;
2302     canvas->tTop=tt;
2303     canvas->tRight=tr;
2304     canvas->tBottom=tb;
2305     canvas->tileH=nh;
2306     canvas->tileV=nv;
2309 /*
2310  * Helper that queues a canvas rectangle for redraw
2311  */
2312 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2313     canvas->need_redraw = TRUE;
2315     sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2318 /**
2319  * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2320  */
2321 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2323     if ( nl >= nr || nt >= nb ) {
2324         return;
2325     }
2326     int tl=sp_canvas_tile_floor(nl);
2327     int tt=sp_canvas_tile_floor(nt);
2328     int tr=sp_canvas_tile_ceil(nr);
2329     int tb=sp_canvas_tile_ceil(nb);
2330     if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2331     if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2332     if ( tr > canvas->tRight ) tr=canvas->tRight;
2333     if ( tt < canvas->tTop ) tt=canvas->tTop;
2334     if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2336     for (int i=tl; i<tr; i++) {
2337         for (int j=tt; j<tb; j++) {
2338             canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2339         }
2340     }
2344 /*
2345   Local Variables:
2346   mode:c++
2347   c-file-style:"stroustrup"
2348   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2349   indent-tabs-mode:nil
2350   fill-column:99
2351   End:
2352 */
2353 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :