Code

Cmake: Corrections for mistakes
[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>
27 #include <gtk/gtkversion.h>
29 #include <gtkmm.h>
31 #include "../helper/sp-marshal.h"
32 #include <helper/recthull.h>
33 #include <display/sp-canvas.h>
34 #include "display-forward.h"
35 #include <2geom/matrix.h>
36 #include <libnr/nr-convex-hull.h>
37 #include "preferences.h"
38 #include "inkscape.h"
39 #include "sodipodi-ctrlrect.h"
40 #if ENABLE_LCMS
41 #include "color-profile-fns.h"
42 #endif // ENABLE_LCMS
43 #include "display/rendermode.h"
44 #include "libnr/nr-blit.h"
45 #include "display/inkscape-cairo.h"
46 #include "debug/gdk-event-latency-tracker.h"
47 #include "desktop.h"
48 #include "sp-namedview.h"
50 using Inkscape::Debug::GdkEventLatencyTracker;
52 // GTK_CHECK_VERSION returns false on failure
53 #define HAS_GDK_EVENT_REQUEST_MOTIONS GTK_CHECK_VERSION(2, 12, 0)
55 // gtk_check_version returns non-NULL on failure
56 static bool const HAS_BROKEN_MOTION_HINTS =
57   true || gtk_check_version(2, 12, 0) != NULL || !HAS_GDK_EVENT_REQUEST_MOTIONS;
59 // Define this to visualize the regions to be redrawn
60 //#define DEBUG_REDRAW 1;
62 // Tiles are a way to minimize the number of redraws, eliminating too small redraws.
63 // The canvas stores a 2D array of ints, each representing a TILE_SIZExTILE_SIZE pixels tile.
64 // If any part of it is dirtied, the entire tile is dirtied (its int is nonzero) and repainted.
65 #define TILE_SIZE 16
67 static gint const sp_canvas_update_priority = G_PRIORITY_HIGH_IDLE;
69 #define SP_CANVAS_WINDOW(c) (((GtkWidget *) (c))->window)
71 enum {
72     SP_CANVAS_ITEM_VISIBLE = 1 << 7,
73     SP_CANVAS_ITEM_NEED_UPDATE = 1 << 8,
74     SP_CANVAS_ITEM_NEED_AFFINE = 1 << 9
75 };
77 /**
78  * A group of Items.
79  */
80 struct SPCanvasGroup {
81     SPCanvasItem item;
83     GList *items, *last;
84 };
86 /**
87  * The SPCanvasGroup vtable.
88  */
89 struct SPCanvasGroupClass {
90     SPCanvasItemClass parent_class;
91 };
93 /**
94  * The SPCanvas vtable.
95  */
96 struct SPCanvasClass {
97     GtkWidgetClass parent_class;
98 };
100 static void group_add (SPCanvasGroup *group, SPCanvasItem *item);
101 static void group_remove (SPCanvasGroup *group, SPCanvasItem *item);
103 /* SPCanvasItem */
105 enum {ITEM_EVENT, ITEM_LAST_SIGNAL};
108 static void sp_canvas_request_update (SPCanvas *canvas);
110 static void track_latency(GdkEvent const *event);
111 static void sp_canvas_item_class_init (SPCanvasItemClass *klass);
112 static void sp_canvas_item_init (SPCanvasItem *item);
113 static void sp_canvas_item_dispose (GObject *object);
114 static void sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args);
116 static int emit_event (SPCanvas *canvas, GdkEvent *event);
118 static guint item_signals[ITEM_LAST_SIGNAL] = { 0 };
120 static GtkObjectClass *item_parent_class;
122 /**
123  * Registers the SPCanvasItem class with Glib and returns its type number.
124  */
125 GType
126 sp_canvas_item_get_type (void)
128     static GType type = 0;
129     if (!type) {
130         static GTypeInfo const info = {
131             sizeof (SPCanvasItemClass),
132             NULL, NULL,
133             (GClassInitFunc) sp_canvas_item_class_init,
134             NULL, NULL,
135             sizeof (SPCanvasItem),
136             0,
137             (GInstanceInitFunc) sp_canvas_item_init,
138             NULL
139         };
140         type = g_type_register_static (GTK_TYPE_OBJECT, "SPCanvasItem", &info, (GTypeFlags)0);
141     }
143     return type;
146 /**
147  * Initializes the SPCanvasItem vtable and the "event" signal.
148  */
149 static void
150 sp_canvas_item_class_init (SPCanvasItemClass *klass)
152     GObjectClass *object_class = (GObjectClass *) klass;
154     /* fixme: Derive from GObject */
155     item_parent_class = (GtkObjectClass*)gtk_type_class (GTK_TYPE_OBJECT);
157     item_signals[ITEM_EVENT] = g_signal_new ("event",
158                                              G_TYPE_FROM_CLASS (klass),
159                                              G_SIGNAL_RUN_LAST,
160                                              ((glong)((guint8*)&(klass->event) - (guint8*)klass)),
161                                              NULL, NULL,
162                                              sp_marshal_BOOLEAN__POINTER,
163                                              G_TYPE_BOOLEAN, 1,
164                                              GDK_TYPE_EVENT);
166     object_class->dispose = sp_canvas_item_dispose;
169 /**
170  * Callback for initialization of SPCanvasItem.
171  */
172 static void
173 sp_canvas_item_init (SPCanvasItem *item)
175     item->flags |= SP_CANVAS_ITEM_VISIBLE;
176     item->xform = Geom::Matrix(Geom::identity());
179 /**
180  * Constructs new SPCanvasItem on SPCanvasGroup.
181  */
182 SPCanvasItem *
183 sp_canvas_item_new (SPCanvasGroup *parent, GtkType type, gchar const *first_arg_name, ...)
185     va_list args;
187     g_return_val_if_fail (parent != NULL, NULL);
188     g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL);
189     g_return_val_if_fail (gtk_type_is_a (type, sp_canvas_item_get_type ()), NULL);
191     SPCanvasItem *item = SP_CANVAS_ITEM (gtk_type_new (type));
193     va_start (args, first_arg_name);
194     sp_canvas_item_construct (item, parent, first_arg_name, args);
195     va_end (args);
197     return item;
200 /**
201  * Sets up the newly created SPCanvasItem.
202  *
203  * We make it static for encapsulation reasons since it was nowhere used.
204  */
205 static void
206 sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args)
208     g_return_if_fail (SP_IS_CANVAS_GROUP (parent));
209     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
211     item->parent = SP_CANVAS_ITEM (parent);
212     item->canvas = item->parent->canvas;
214     g_object_set_valist (G_OBJECT (item), first_arg_name, args);
216     group_add (SP_CANVAS_GROUP (item->parent), item);
218     sp_canvas_item_request_update (item);
221 /**
222  * Helper function that requests redraw only if item's visible flag is set.
223  */
224 static void
225 redraw_if_visible (SPCanvasItem *item)
227     if (item->flags & SP_CANVAS_ITEM_VISIBLE) {
228         int x0 = (int)(item->x1);
229         int x1 = (int)(item->x2);
230         int y0 = (int)(item->y1);
231         int y1 = (int)(item->y2);
233         if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
234             sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
235         }
236     }
239 /**
240  * Callback that removes item from all referers and destroys it.
241  */
242 static void
243 sp_canvas_item_dispose (GObject *object)
245     SPCanvasItem *item = SP_CANVAS_ITEM (object);
247     // Hack: if this is a ctrlrect, move it to 0,0;
248     // this redraws only the stroke of the rect to be deleted,
249     // avoiding redraw of the entire area
250     if (SP_IS_CTRLRECT(item)) {
251         SP_CTRLRECT(object)->setRectangle(Geom::Rect(Geom::Point(0,0),Geom::Point(0,0)));
252         SP_CTRLRECT(object)->update(item->xform, 0);
253     } else {
254         redraw_if_visible (item);
255     }
256     item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
258     if (item == item->canvas->current_item) {
259         item->canvas->current_item = NULL;
260         item->canvas->need_repick = TRUE;
261     }
263     if (item == item->canvas->new_current_item) {
264         item->canvas->new_current_item = NULL;
265         item->canvas->need_repick = TRUE;
266     }
268     if (item == item->canvas->grabbed_item) {
269         item->canvas->grabbed_item = NULL;
270         gdk_pointer_ungrab (GDK_CURRENT_TIME);
271     }
273     if (item == item->canvas->focused_item)
274         item->canvas->focused_item = NULL;
276     if (item->parent) {
277         group_remove (SP_CANVAS_GROUP (item->parent), item);
278     }
280     G_OBJECT_CLASS (item_parent_class)->dispose (object);
283 /**
284  * Helper function to update item and its children.
285  *
286  * NB! affine is parent2canvas.
287  */
288 static void
289 sp_canvas_item_invoke_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags)
291     /* Apply the child item's transform */
292     Geom::Matrix child_affine = item->xform * affine;
294     /* apply object flags to child flags */
295     int child_flags = flags & ~SP_CANVAS_UPDATE_REQUESTED;
297     if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
298         child_flags |= SP_CANVAS_UPDATE_REQUESTED;
300     if (item->flags & SP_CANVAS_ITEM_NEED_AFFINE)
301         child_flags |= SP_CANVAS_UPDATE_AFFINE;
303     if (child_flags & (SP_CANVAS_UPDATE_REQUESTED | SP_CANVAS_UPDATE_AFFINE)) {
304         if (SP_CANVAS_ITEM_GET_CLASS (item)->update)
305             SP_CANVAS_ITEM_GET_CLASS (item)->update (item, child_affine, child_flags);
306     }
308     GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_UPDATE);
309     GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_AFFINE);
312 /**
313  * Helper function to invoke the point method of the item.
314  *
315  * The argument x, y should be in the parent's item-relative coordinate
316  * system.  This routine applies the inverse of the item's transform,
317  * maintaining the affine invariant.
318  */
319 static double
320 sp_canvas_item_invoke_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item)
322     if (SP_CANVAS_ITEM_GET_CLASS (item)->point)
323         return SP_CANVAS_ITEM_GET_CLASS (item)->point (item, p, actual_item);
325     return NR_HUGE;
328 /**
329  * Makes the item's affine transformation matrix be equal to the specified
330  * matrix.
331  *
332  * @item: A canvas item.
333  * @affine: An affine transformation matrix.
334  */
335 void
336 sp_canvas_item_affine_absolute (SPCanvasItem *item, Geom::Matrix const &affine)
338     item->xform = affine;
340     if (!(item->flags & SP_CANVAS_ITEM_NEED_AFFINE)) {
341         item->flags |= SP_CANVAS_ITEM_NEED_AFFINE;
342         if (item->parent != NULL) {
343             sp_canvas_item_request_update (item->parent);
344         } else {
345             sp_canvas_request_update (item->canvas);
346         }
347     }
349     item->canvas->need_repick = TRUE;
352 /**
353  * Convenience function to reorder items in a group's child list.
354  *
355  * This puts the specified link after the "before" link.
356  */
357 static void
358 put_item_after (GList *link, GList *before)
360     if (link == before)
361         return;
363     SPCanvasGroup *parent = SP_CANVAS_GROUP (SP_CANVAS_ITEM (link->data)->parent);
365     if (before == NULL) {
366         if (link == parent->items) return;
368         link->prev->next = link->next;
370         if (link->next) {
371             link->next->prev = link->prev;
372         } else {
373             parent->last = link->prev;
374         }
376         link->prev = before;
377         link->next = parent->items;
378         link->next->prev = link;
379         parent->items = link;
380     } else {
381         if ((link == parent->last) && (before == parent->last->prev))
382             return;
384         if (link->next)
385             link->next->prev = link->prev;
387         if (link->prev)
388             link->prev->next = link->next;
389         else {
390             parent->items = link->next;
391             parent->items->prev = NULL;
392         }
394         link->prev = before;
395         link->next = before->next;
397         link->prev->next = link;
399         if (link->next)
400             link->next->prev = link;
401         else
402             parent->last = link;
403     }
407 /**
408  * Raises the item in its parent's stack by the specified number of positions.
409  *
410  * \param item A canvas item.
411  * \param positions Number of steps to raise the item.
412  *
413  * If the number of positions is greater than the distance to the top of the
414  * stack, then the item is put at the top.
415  */
416 void
417 sp_canvas_item_raise (SPCanvasItem *item, int positions)
419     g_return_if_fail (item != NULL);
420     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
421     g_return_if_fail (positions >= 0);
423     if (!item->parent || positions == 0)
424         return;
426     SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
427     GList *link = g_list_find (parent->items, item);
428     g_assert (link != NULL);
430     GList *before;
431     for (before = link; positions && before; positions--)
432         before = before->next;
434     if (!before)
435         before = parent->last;
437     put_item_after (link, before);
439     redraw_if_visible (item);
440     item->canvas->need_repick = TRUE;
444 /**
445  * Lowers the item in its parent's stack by the specified number of positions.
446  *
447  * \param item A canvas item.
448  * \param positions Number of steps to lower the item.
449  *
450  * If the number of positions is greater than the distance to the bottom of the
451  * stack, then the item is put at the bottom.
452  **/
453 void
454 sp_canvas_item_lower (SPCanvasItem *item, int positions)
456     g_return_if_fail (item != NULL);
457     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
458     g_return_if_fail (positions >= 1);
460     if (!item->parent || positions == 0)
461         return;
463     SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
464     GList *link = g_list_find (parent->items, item);
465     g_assert (link != NULL);
467     GList *before;
468     if (link->prev)
469         for (before = link->prev; positions && before; positions--)
470             before = before->prev;
471     else
472         before = NULL;
474     put_item_after (link, before);
476     redraw_if_visible (item);
477     item->canvas->need_repick = TRUE;
480 /**
481  * Sets visible flag on item and requests a redraw.
482  */
483 void
484 sp_canvas_item_show (SPCanvasItem *item)
486     g_return_if_fail (item != NULL);
487     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
489     if (item->flags & SP_CANVAS_ITEM_VISIBLE)
490         return;
492     item->flags |= SP_CANVAS_ITEM_VISIBLE;
494     int x0 = (int)(item->x1);
495     int x1 = (int)(item->x2);
496     int y0 = (int)(item->y1);
497     int y1 = (int)(item->y2);
499     if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
500         sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
501         item->canvas->need_repick = TRUE;
502     }
505 /**
506  * Clears visible flag on item and requests a redraw.
507  */
508 void
509 sp_canvas_item_hide (SPCanvasItem *item)
511     g_return_if_fail (item != NULL);
512     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
514     if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
515         return;
517     item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
519     int x0 = (int)(item->x1);
520     int x1 = (int)(item->x2);
521     int y0 = (int)(item->y1);
522     int y1 = (int)(item->y2);
524     if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
525         sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
526         item->canvas->need_repick = TRUE;
527     }
530 /**
531  * Grab item under cursor.
532  *
533  * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE
534  */
535 int
536 sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
538     g_return_val_if_fail (item != NULL, -1);
539     g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1);
540     g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1);
542     if (item->canvas->grabbed_item)
543         return -1;
545     if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
546         return -1;
548     if (HAS_BROKEN_MOTION_HINTS) {
549         event_mask &= ~GDK_POINTER_MOTION_HINT_MASK;
550     }
552     /* fixme: Top hack (Lauris) */
553     /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
554     /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
555     gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
556                       (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
557                       NULL, cursor, etime);
559     item->canvas->grabbed_item = item;
560     item->canvas->grabbed_event_mask = event_mask;
561     item->canvas->current_item = item; /* So that events go to the grabbed item */
563     return 0;
566 /**
567  * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
568  * mouse.
569  *
570  * \param item A canvas item that holds a grab.
571  * \param etime The timestamp for ungrabbing the mouse.
572  */
573 void
574 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
576     g_return_if_fail (item != NULL);
577     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
579     if (item->canvas->grabbed_item != item)
580         return;
582     item->canvas->grabbed_item = NULL;
584     gdk_pointer_ungrab (etime);
587 /**
588  * Returns the product of all transformation matrices from the root item down
589  * to the item.
590  */
591 Geom::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
593     g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
595     Geom::Matrix affine = Geom::identity();
597     while (item) {
598         affine *= item->xform;
599         item = item->parent;
600     }
601     return affine;
604 /**
605  * Helper that returns true iff item is descendant of parent.
606  */
607 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
609     while (item) {
610         if (item == parent)
611             return true;
612         item = item->parent;
613     }
615     return false;
618 /**
619  * Focus canvas, and item under cursor if it is not already focussed.
620  */
621 void
622 sp_canvas_item_grab_focus (SPCanvasItem *item)
624     g_return_if_fail (item != NULL);
625     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
626     g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
628     SPCanvasItem *focused_item = item->canvas->focused_item;
630     if (focused_item) {
631         GdkEvent ev;
632         ev.focus_change.type = GDK_FOCUS_CHANGE;
633         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
634         ev.focus_change.send_event = FALSE;
635         ev.focus_change.in = FALSE;
637         emit_event (item->canvas, &ev);
638     }
640     item->canvas->focused_item = item;
641     gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
643     if (focused_item) {
644         GdkEvent ev;
645         ev.focus_change.type = GDK_FOCUS_CHANGE;
646         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
647         ev.focus_change.send_event = FALSE;
648         ev.focus_change.in = TRUE;
650         emit_event (item->canvas, &ev);
651     }
654 /**
655  * Requests that the canvas queue an update for the specified item.
656  *
657  * To be used only by item implementations.
658  */
659 void
660 sp_canvas_item_request_update (SPCanvasItem *item)
662     if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
663         return;
665     item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
667     if (item->parent != NULL) {
668         /* Recurse up the tree */
669         sp_canvas_item_request_update (item->parent);
670     } else {
671         /* Have reached the top of the tree, make sure the update call gets scheduled. */
672         sp_canvas_request_update (item->canvas);
673     }
676 /**
677  * Returns position of item in group.
678  */
679 gint sp_canvas_item_order (SPCanvasItem * item)
681     return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
684 /* SPCanvasGroup */
686 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
687 static void sp_canvas_group_init (SPCanvasGroup *group);
688 static void sp_canvas_group_destroy (GtkObject *object);
690 static void sp_canvas_group_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags);
691 static double sp_canvas_group_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item);
692 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
694 static SPCanvasItemClass *group_parent_class;
696 /**
697  * Registers SPCanvasGroup class with Gtk and returns its type number.
698  */
699 GType sp_canvas_group_get_type(void)
701     static GType type = 0;
702     if (!type) {
703         GTypeInfo info = {
704             sizeof(SPCanvasGroupClass),
705             0, // base_init
706             0, // base_finalize
707             (GClassInitFunc)sp_canvas_group_class_init,
708             0, // class_finalize
709             0, // class_data
710             sizeof(SPCanvasGroup),
711             0, // n_preallocs
712             (GInstanceInitFunc)sp_canvas_group_init,
713             0 // value_table
714         };
715         type = g_type_register_static(sp_canvas_item_get_type(), "SPCanvasGroup", &info, static_cast<GTypeFlags>(0));
716     }
717     return type;
720 /**
721  * Class initialization function for SPCanvasGroupClass
722  */
723 static void
724 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
726     GtkObjectClass *object_class = (GtkObjectClass *) klass;
727     SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
729     group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
731     object_class->destroy = sp_canvas_group_destroy;
733     item_class->update = sp_canvas_group_update;
734     item_class->render = sp_canvas_group_render;
735     item_class->point = sp_canvas_group_point;
738 /**
739  * Callback. Empty.
740  */
741 static void
742 sp_canvas_group_init (SPCanvasGroup */*group*/)
744     /* Nothing here */
747 /**
748  * Callback that destroys all items in group and calls group's virtual
749  * destroy() function.
750  */
751 static void
752 sp_canvas_group_destroy (GtkObject *object)
754     g_return_if_fail (object != NULL);
755     g_return_if_fail (SP_IS_CANVAS_GROUP (object));
757     SPCanvasGroup const *group = SP_CANVAS_GROUP (object);
759     GList *list = group->items;
760     while (list) {
761         SPCanvasItem *child = (SPCanvasItem *)list->data;
762         list = list->next;
764         gtk_object_destroy (GTK_OBJECT (child));
765     }
767     if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
768         (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
771 /**
772  * Update handler for canvas groups
773  */
774 static void
775 sp_canvas_group_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags)
777     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
778     Geom::RectHull corners(Geom::Point(0, 0));
779     bool empty=true;
781     for (GList *list = group->items; list; list = list->next) {
782         SPCanvasItem *i = (SPCanvasItem *)list->data;
784         sp_canvas_item_invoke_update (i, affine, flags);
786         if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
787             if (empty) {
788                 corners = Geom::RectHull(Geom::Point(i->x1, i->y1));
789                 empty = false;
790             } else {
791                 corners.add(Geom::Point(i->x1, i->y1));
792             }
793             corners.add(Geom::Point(i->x2, i->y2));
794         }
795     }
797     Geom::OptRect const bounds = corners.bounds();
798     if (bounds) {
799         item->x1 = bounds->min()[Geom::X];
800         item->y1 = bounds->min()[Geom::Y];
801         item->x2 = bounds->max()[Geom::X];
802         item->y2 = bounds->max()[Geom::Y];
803     } else {
804         // FIXME ?
805         item->x1 = item->x2 = item->y1 = item->y2 = 0;
806     }
809 /**
810  * Point handler for canvas groups.
811  */
812 static double
813 sp_canvas_group_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item)
815     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
816     double const x = p[Geom::X];
817     double const y = p[Geom::Y];
818     int x1 = (int)(x - item->canvas->close_enough);
819     int y1 = (int)(y - item->canvas->close_enough);
820     int x2 = (int)(x + item->canvas->close_enough);
821     int y2 = (int)(y + item->canvas->close_enough);
823     double best = 0.0;
824     *actual_item = NULL;
826     double dist = 0.0;
828     for (GList *list = group->items; list; list = list->next) {
829         SPCanvasItem *child = (SPCanvasItem *)list->data;
831         if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
832             SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
834             int has_point;
835             if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
836                 dist = sp_canvas_item_invoke_point (child, p, &point_item);
837                 has_point = TRUE;
838             } else
839                 has_point = FALSE;
841             if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
842                 best = dist;
843                 *actual_item = point_item;
844             }
845         }
846     }
848     return best;
851 /**
852  * Renders all visible canvas group items in buf rectangle.
853  */
854 static void
855 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
857     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
859     for (GList *list = group->items; list; list = list->next) {
860         SPCanvasItem *child = (SPCanvasItem *)list->data;
861         if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
862             if ((child->x1 < buf->rect.x1) &&
863                 (child->y1 < buf->rect.y1) &&
864                 (child->x2 > buf->rect.x0) &&
865                 (child->y2 > buf->rect.y0)) {
866                 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
867                     SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
868             }
869         }
870     }
873 /**
874  * Adds an item to a canvas group.
875  */
876 static void
877 group_add (SPCanvasGroup *group, SPCanvasItem *item)
879     gtk_object_ref (GTK_OBJECT (item));
880     gtk_object_sink (GTK_OBJECT (item));
882     if (!group->items) {
883         group->items = g_list_append (group->items, item);
884         group->last = group->items;
885     } else {
886         group->last = g_list_append (group->last, item)->next;
887     }
889     sp_canvas_item_request_update (item);
892 /**
893  * Removes an item from a canvas group
894  */
895 static void
896 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
898     g_return_if_fail (group != NULL);
899     g_return_if_fail (SP_IS_CANVAS_GROUP (group));
900     g_return_if_fail (item != NULL);
902     for (GList *children = group->items; children; children = children->next) {
903         if (children->data == item) {
905             /* Unparent the child */
907             item->parent = NULL;
908             gtk_object_unref (GTK_OBJECT (item));
910             /* Remove it from the list */
912             if (children == group->last) group->last = children->prev;
914             group->items = g_list_remove_link (group->items, children);
915             g_list_free (children);
916             break;
917         }
918     }
921 /* SPCanvas */
923 static void sp_canvas_class_init (SPCanvasClass *klass);
924 static void sp_canvas_init (SPCanvas *canvas);
925 static void sp_canvas_destroy (GtkObject *object);
927 static void sp_canvas_realize (GtkWidget *widget);
928 static void sp_canvas_unrealize (GtkWidget *widget);
930 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
931 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
933 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
934 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
935 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
936 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
937 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
938 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
939 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
940 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
942 static GtkWidgetClass *canvas_parent_class;
944 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb);
945 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb);
946 static void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val);
947 static int do_update (SPCanvas *canvas);
949 static gboolean sp_canvas_snap_watchdog_callback(gpointer data); 
950 static void sp_canvas_snap_watchdog_set(SPCanvas *canvas, GdkEventMotion *event); 
951 static void sp_canvas_snap_watchdog_kill(SPCanvas *canvas);
953 /**
954  * Registers the SPCanvas class if necessary, and returns the type ID
955  * associated to it.
956  *
957  * \return The type ID of the SPCanvas class.
958  **/
959 GType sp_canvas_get_type(void)
961     static GType type = 0;
962     if (!type) {
963         GTypeInfo info = {
964             sizeof(SPCanvasClass),
965             0, // base_init
966             0, // base_finalize
967             (GClassInitFunc)sp_canvas_class_init,
968             0, // class_finalize
969             0, // class_data
970             sizeof(SPCanvas),
971             0, // n_preallocs
972             (GInstanceInitFunc)sp_canvas_init,
973             0 // value_table
974         };
975         type = g_type_register_static(GTK_TYPE_WIDGET, "SPCanvas", &info, static_cast<GTypeFlags>(0));
976     }
977     return type;
980 /**
981  * Class initialization function for SPCanvasClass.
982  */
983 static void
984 sp_canvas_class_init (SPCanvasClass *klass)
986     GtkObjectClass *object_class = (GtkObjectClass *) klass;
987     GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
989     canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
991     object_class->destroy = sp_canvas_destroy;
993     widget_class->realize = sp_canvas_realize;
994     widget_class->unrealize = sp_canvas_unrealize;
995     widget_class->size_request = sp_canvas_size_request;
996     widget_class->size_allocate = sp_canvas_size_allocate;
997     widget_class->button_press_event = sp_canvas_button;
998     widget_class->button_release_event = sp_canvas_button;
999     widget_class->motion_notify_event = sp_canvas_motion;
1000     widget_class->scroll_event = sp_canvas_scroll;
1001     widget_class->expose_event = sp_canvas_expose;
1002     widget_class->key_press_event = sp_canvas_key;
1003     widget_class->key_release_event = sp_canvas_key;
1004     widget_class->enter_notify_event = sp_canvas_crossing;
1005     widget_class->leave_notify_event = sp_canvas_crossing;
1006     widget_class->focus_in_event = sp_canvas_focus_in;
1007     widget_class->focus_out_event = sp_canvas_focus_out;
1010 /**
1011  * Callback: object initialization for SPCanvas.
1012  */
1013 static void
1014 sp_canvas_init (SPCanvas *canvas)
1016     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
1017     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
1018     GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
1020     canvas->pick_event.type = GDK_LEAVE_NOTIFY;
1021     canvas->pick_event.crossing.x = 0;
1022     canvas->pick_event.crossing.y = 0;
1024     /* Create the root item as a special case */
1025     canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
1026     canvas->root->canvas = canvas;
1028     gtk_object_ref (GTK_OBJECT (canvas->root));
1029     gtk_object_sink (GTK_OBJECT (canvas->root));
1031     canvas->need_repick = TRUE;
1033     // See comment at in sp-canvas.h.
1034     canvas->gen_all_enter_events = false;
1036     canvas->tiles=NULL;
1037     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1038     canvas->tileH=canvas->tileV=0;
1040     canvas->forced_redraw_count = 0;
1041     canvas->forced_redraw_limit = -1;
1043 #if ENABLE_LCMS
1044     canvas->enable_cms_display_adj = false;
1045     canvas->cms_key = new Glib::ustring("");
1046 #endif // ENABLE_LCMS
1048     canvas->is_scrolling = false;
1050     canvas->watchdog_id = 0;
1051     canvas->watchdog_event = NULL;
1054 /**
1055  * Convenience function to remove the idle handler of a canvas.
1056  */
1057 static void
1058 remove_idle (SPCanvas *canvas)
1060     if (canvas->idle_id) {
1061         gtk_idle_remove (canvas->idle_id);
1062         canvas->idle_id = 0;
1063     }
1066 /*
1067  * Removes the transient state of the canvas (idle handler, grabs).
1068  */
1069 static void
1070 shutdown_transients (SPCanvas *canvas)
1072     /* We turn off the need_redraw flag, since if the canvas is mapped again
1073      * it will request a redraw anyways.  We do not turn off the need_update
1074      * flag, though, because updates are not queued when the canvas remaps
1075      * itself.
1076      */
1077     if (canvas->need_redraw) {
1078         canvas->need_redraw = FALSE;
1079     }
1080     if ( canvas->tiles ) g_free(canvas->tiles);
1081     canvas->tiles=NULL;
1082     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1083     canvas->tileH=canvas->tileV=0;
1085     if (canvas->grabbed_item) {
1086         canvas->grabbed_item = NULL;
1087         gdk_pointer_ungrab (GDK_CURRENT_TIME);
1088     }
1090     remove_idle (canvas);
1093 /**
1094  * Destroy handler for SPCanvas.
1095  */
1096 static void
1097 sp_canvas_destroy (GtkObject *object)
1099     SPCanvas *canvas = SP_CANVAS (object);
1101     if (canvas->root) {
1102         gtk_object_unref (GTK_OBJECT (canvas->root));
1103         canvas->root = NULL;
1104     }
1106     shutdown_transients (canvas);
1108     if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1109         (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1112 static void track_latency(GdkEvent const *event) {
1113     GdkEventLatencyTracker &tracker = GdkEventLatencyTracker::default_tracker();
1114     boost::optional<double> latency = tracker.process(event);
1115     if (latency && *latency > 2.0) {
1116         //g_warning("Event latency reached %f sec (%1.4f)", *latency, tracker.getSkew());
1117     }
1120 /**
1121  * Returns new canvas as widget.
1122  */
1123 GtkWidget *
1124 sp_canvas_new_aa (void)
1126     SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1128     return (GtkWidget *) canvas;
1131 /**
1132  * The canvas widget's realize callback.
1133  */
1134 static void
1135 sp_canvas_realize (GtkWidget *widget)
1137     SPCanvas *canvas = SP_CANVAS (widget);
1139     GdkWindowAttr attributes;
1140     attributes.window_type = GDK_WINDOW_CHILD;
1141     attributes.x = widget->allocation.x;
1142     attributes.y = widget->allocation.y;
1143     attributes.width = widget->allocation.width;
1144     attributes.height = widget->allocation.height;
1145     attributes.wclass = GDK_INPUT_OUTPUT;
1146     attributes.visual = gdk_rgb_get_visual ();
1147     attributes.colormap = gdk_rgb_get_cmap ();
1148     attributes.event_mask = (gtk_widget_get_events (widget) |
1149                              GDK_EXPOSURE_MASK |
1150                              GDK_BUTTON_PRESS_MASK |
1151                              GDK_BUTTON_RELEASE_MASK |
1152                              GDK_POINTER_MOTION_MASK |
1153                              ( HAS_BROKEN_MOTION_HINTS ?
1154                                0 : GDK_POINTER_MOTION_HINT_MASK ) |
1155                              GDK_PROXIMITY_IN_MASK |
1156                              GDK_PROXIMITY_OUT_MASK |
1157                              GDK_KEY_PRESS_MASK |
1158                              GDK_KEY_RELEASE_MASK |
1159                              GDK_ENTER_NOTIFY_MASK |
1160                              GDK_LEAVE_NOTIFY_MASK |
1161                              GDK_FOCUS_CHANGE_MASK);
1162     gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1164     widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1165     gdk_window_set_user_data (widget->window, widget);
1167     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1168     if ( prefs->getBool("/options/useextinput/value", true) )
1169         gtk_widget_set_events(widget, attributes.event_mask);
1171     widget->style = gtk_style_attach (widget->style, widget->window);
1173     GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1175     canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1178 /**
1179  * The canvas widget's unrealize callback.
1180  */
1181 static void
1182 sp_canvas_unrealize (GtkWidget *widget)
1184     SPCanvas *canvas = SP_CANVAS (widget);
1186     canvas->current_item = NULL;
1187     canvas->grabbed_item = NULL;
1188     canvas->focused_item = NULL;
1190     shutdown_transients (canvas);
1192     gdk_gc_destroy (canvas->pixmap_gc);
1193     canvas->pixmap_gc = NULL;
1195     if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1196         (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1199 /**
1200  * The canvas widget's size_request callback.
1201  */
1202 static void
1203 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1205     static_cast<void>(SP_CANVAS (widget));
1207     req->width = 256;
1208     req->height = 256;
1211 /**
1212  * The canvas widget's size_allocate callback.
1213  */
1214 static void
1215 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1217     SPCanvas *canvas = SP_CANVAS (widget);
1219     /* Schedule redraw of new region */
1220     sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1221     if (allocation->width > widget->allocation.width) {
1222         sp_canvas_request_redraw (canvas,
1223                                   canvas->x0 + widget->allocation.width,
1224                                   0,
1225                                   canvas->x0 + allocation->width,
1226                                   canvas->y0 + allocation->height);
1227     }
1228     if (allocation->height > widget->allocation.height) {
1229         sp_canvas_request_redraw (canvas,
1230                                   0,
1231                                   canvas->y0 + widget->allocation.height,
1232                                   canvas->x0 + allocation->width,
1233                                   canvas->y0 + allocation->height);
1234     }
1236     widget->allocation = *allocation;
1237     
1238     if (GTK_WIDGET_REALIZED (widget)) {
1239         gdk_window_move_resize (widget->window,
1240                                 widget->allocation.x, widget->allocation.y,
1241                                 widget->allocation.width, widget->allocation.height);
1242     }
1245 /**
1246  * Helper that emits an event for an item in the canvas, be it the current
1247  * item, grabbed item, or focused item, as appropriate.
1248  */
1249 static int
1250 emit_event (SPCanvas *canvas, GdkEvent *event)
1252     guint mask;
1254     if (canvas->grabbed_item) {
1255         switch (event->type) {
1256         case GDK_ENTER_NOTIFY:
1257             mask = GDK_ENTER_NOTIFY_MASK;
1258             break;
1259         case GDK_LEAVE_NOTIFY:
1260             mask = GDK_LEAVE_NOTIFY_MASK;
1261             break;
1262         case GDK_MOTION_NOTIFY:
1263             mask = GDK_POINTER_MOTION_MASK;
1264             break;
1265         case GDK_BUTTON_PRESS:
1266         case GDK_2BUTTON_PRESS:
1267         case GDK_3BUTTON_PRESS:
1268             mask = GDK_BUTTON_PRESS_MASK;
1269             break;
1270         case GDK_BUTTON_RELEASE:
1271             mask = GDK_BUTTON_RELEASE_MASK;
1272             break;
1273         case GDK_KEY_PRESS:
1274             mask = GDK_KEY_PRESS_MASK;
1275             break;
1276         case GDK_KEY_RELEASE:
1277             mask = GDK_KEY_RELEASE_MASK;
1278             break;
1279         case GDK_SCROLL:
1280             mask = GDK_SCROLL;
1281             break;
1282         default:
1283             mask = 0;
1284             break;
1285         }
1287         if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1288     }
1290     /* Convert to world coordinates -- we have two cases because of diferent
1291      * offsets of the fields in the event structures.
1292      */
1294     GdkEvent ev = *event;
1296     switch (ev.type) {
1297     case GDK_ENTER_NOTIFY:
1298     case GDK_LEAVE_NOTIFY:
1299         ev.crossing.x += canvas->x0;
1300         ev.crossing.y += canvas->y0;
1301         break;
1302     case GDK_MOTION_NOTIFY:
1303     case GDK_BUTTON_PRESS:
1304     case GDK_2BUTTON_PRESS:
1305     case GDK_3BUTTON_PRESS:
1306     case GDK_BUTTON_RELEASE:
1307         ev.motion.x += canvas->x0;
1308         ev.motion.y += canvas->y0;
1309         break;
1310     default:
1311         break;
1312     }
1314     /* Choose where we send the event */
1316     /* canvas->current_item becomes NULL in some cases under Win32
1317     ** (e.g. if the pointer leaves the window).  So this is a hack that
1318     ** Lauris applied to SP to get around the problem.
1319     */
1320     SPCanvasItem* item = NULL;
1321     if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1322         item = canvas->grabbed_item;
1323     } else {
1324         item = canvas->current_item;
1325     }
1327     if (canvas->focused_item &&
1328         ((event->type == GDK_KEY_PRESS) ||
1329          (event->type == GDK_KEY_RELEASE) ||
1330          (event->type == GDK_FOCUS_CHANGE))) {
1331         item = canvas->focused_item;
1332     }
1334     /* The event is propagated up the hierarchy (for if someone connected to
1335      * a group instead of a leaf event), and emission is stopped if a
1336      * handler returns TRUE, just like for GtkWidget events.
1337      */
1339     gint finished = FALSE;
1341     while (item && !finished) {
1342         gtk_object_ref (GTK_OBJECT (item));
1343         gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1344         SPCanvasItem *parent = item->parent;
1345         gtk_object_unref (GTK_OBJECT (item));
1346         item = parent;
1347     }
1349     return finished;
1352 /**
1353  * Helper that re-picks the current item in the canvas, based on the event's
1354  * coordinates and emits enter/leave events for items as appropriate.
1355  */
1356 static int
1357 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1359     int button_down = 0;
1360     double x, y;
1362     if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1363         return FALSE;
1365     int retval = FALSE;
1367     if (canvas->gen_all_enter_events == false) {
1368         // If a button is down, we'll perform enter and leave events on the
1369         // current item, but not enter on any other item.  This is more or
1370         // less like X pointer grabbing for canvas items.
1371         //
1372         button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1373                 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1375         if (!button_down) canvas->left_grabbed_item = FALSE;
1376     }
1378     /* Save the event in the canvas.  This is used to synthesize enter and
1379      * leave events in case the current item changes.  It is also used to
1380      * re-pick the current item if the current one gets deleted.  Also,
1381      * synthesize an enter event.
1382      */
1383     if (event != &canvas->pick_event) {
1384         if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1385             /* these fields have the same offsets in both types of events */
1387             canvas->pick_event.crossing.type       = GDK_ENTER_NOTIFY;
1388             canvas->pick_event.crossing.window     = event->motion.window;
1389             canvas->pick_event.crossing.send_event = event->motion.send_event;
1390             canvas->pick_event.crossing.subwindow  = NULL;
1391             canvas->pick_event.crossing.x          = event->motion.x;
1392             canvas->pick_event.crossing.y          = event->motion.y;
1393             canvas->pick_event.crossing.mode       = GDK_CROSSING_NORMAL;
1394             canvas->pick_event.crossing.detail     = GDK_NOTIFY_NONLINEAR;
1395             canvas->pick_event.crossing.focus      = FALSE;
1396             canvas->pick_event.crossing.state      = event->motion.state;
1398             /* these fields don't have the same offsets in both types of events */
1400             if (event->type == GDK_MOTION_NOTIFY) {
1401                 canvas->pick_event.crossing.x_root = event->motion.x_root;
1402                 canvas->pick_event.crossing.y_root = event->motion.y_root;
1403             } else {
1404                 canvas->pick_event.crossing.x_root = event->button.x_root;
1405                 canvas->pick_event.crossing.y_root = event->button.y_root;
1406             }
1407         } else {
1408             canvas->pick_event = *event;
1409         }
1410     }
1412     /* Don't do anything else if this is a recursive call */
1413     if (canvas->in_repick) return retval;
1415     /* LeaveNotify means that there is no current item, so we don't look for one */
1416     if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1417         /* these fields don't have the same offsets in both types of events */
1419         if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1420             x = canvas->pick_event.crossing.x;
1421             y = canvas->pick_event.crossing.y;
1422         } else {
1423             x = canvas->pick_event.motion.x;
1424             y = canvas->pick_event.motion.y;
1425         }
1427         /* world coords */
1428         x += canvas->x0;
1429         y += canvas->y0;
1431         /* find the closest item */
1432         if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1433             sp_canvas_item_invoke_point (canvas->root, Geom::Point(x, y), &canvas->new_current_item);
1434         } else {
1435             canvas->new_current_item = NULL;
1436         }
1437     } else {
1438         canvas->new_current_item = NULL;
1439     }
1441     if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1442         return retval; /* current item did not change */
1443     }
1445     /* Synthesize events for old and new current items */
1447     if ((canvas->new_current_item != canvas->current_item)
1448         && (canvas->current_item != NULL)
1449         && !canvas->left_grabbed_item) {
1450         GdkEvent new_event;
1451         SPCanvasItem *item;
1453         item = canvas->current_item;
1455         new_event = canvas->pick_event;
1456         new_event.type = GDK_LEAVE_NOTIFY;
1458         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1459         new_event.crossing.subwindow = NULL;
1460         canvas->in_repick = TRUE;
1461         retval = emit_event (canvas, &new_event);
1462         canvas->in_repick = FALSE;
1463     }
1465     if (canvas->gen_all_enter_events == false) {
1466         // new_current_item may have been set to NULL during the call to
1467         // emit_event() above
1468         if ((canvas->new_current_item != canvas->current_item) && button_down) {
1469             canvas->left_grabbed_item = TRUE;
1470             return retval;
1471         }
1472     }
1474     /* Handle the rest of cases */
1476     canvas->left_grabbed_item = FALSE;
1477     canvas->current_item = canvas->new_current_item;
1479     if (canvas->current_item != NULL) {
1480         GdkEvent new_event;
1482         new_event = canvas->pick_event;
1483         new_event.type = GDK_ENTER_NOTIFY;
1484         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1485         new_event.crossing.subwindow = NULL;
1486         retval = emit_event (canvas, &new_event);
1487     }
1489     return retval;
1492 /**
1493  * Button event handler for the canvas.
1494  */
1495 static gint
1496 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1498     SPCanvas *canvas = SP_CANVAS (widget);
1499     SPDesktop *dt = SP_ACTIVE_DESKTOP;
1501     int retval = FALSE;
1503     /* dispatch normally regardless of the event's window if an item has
1504        has a pointer grab in effect */
1505     if (!canvas->grabbed_item &&
1506         event->window != SP_CANVAS_WINDOW (canvas))
1507         return retval;
1509     int mask;
1510     switch (event->button) {
1511     case 1:
1512         mask = GDK_BUTTON1_MASK;
1513         break;
1514     case 2:
1515         mask = GDK_BUTTON2_MASK;
1516         break;
1517     case 3:
1518         mask = GDK_BUTTON3_MASK;
1519         break;
1520     case 4:
1521         mask = GDK_BUTTON4_MASK;
1522         break;
1523     case 5:
1524         mask = GDK_BUTTON5_MASK;
1525         break;
1526     default:
1527         mask = 0;
1528     }
1530     switch (event->type) {
1531     case GDK_BUTTON_PRESS:
1532     case GDK_2BUTTON_PRESS:
1533     case GDK_3BUTTON_PRESS:
1534         if (dt) { 
1535                         // Snapping will be on hold if we're moving the mouse at high speeds. When starting
1536                 // drawing a new shape we really should snap though. 
1537                 dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false);
1538                 }
1539         
1540         /* Pick the current item as if the button were not pressed, and
1541          * then process the event.
1542          */
1543         canvas->state = event->state;
1544         pick_current_item (canvas, (GdkEvent *) event);
1545         canvas->state ^= mask;
1546         retval = emit_event (canvas, (GdkEvent *) event);
1547         break;
1549     case GDK_BUTTON_RELEASE:
1550         sp_canvas_snap_watchdog_callback(canvas); // If we have any pending snapping action, then invoke it now 
1551          
1552         /* Process the event as if the button were pressed, then repick
1553          * after the button has been released
1554          */
1555         canvas->state = event->state;
1556         retval = emit_event (canvas, (GdkEvent *) event);
1557         event->state ^= mask;
1558         canvas->state = event->state;
1559         pick_current_item (canvas, (GdkEvent *) event);
1560         event->state ^= mask;
1561         
1562         break;
1564     default:
1565         g_assert_not_reached ();
1566     }
1568     return retval;
1571 /**
1572  * Scroll event handler for the canvas.
1573  *
1574  * \todo FIXME: generate motion events to re-select items.
1575  */
1576 static gint
1577 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1579     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1582 static inline void request_motions(GdkWindow *w, GdkEventMotion *event) {
1583     gdk_window_get_pointer(w, NULL, NULL, NULL);
1584 #if HAS_GDK_EVENT_REQUEST_MOTIONS
1585     gdk_event_request_motions(event);
1586 #endif
1589 /**
1590  * Motion event handler for the canvas.
1591  */
1592 static int
1593 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1595         static guint32 prev_time;
1596         static boost::optional<Geom::Point> prev_pos;
1597         
1598         int status;
1599     SPCanvas *canvas = SP_CANVAS (widget);
1601     track_latency((GdkEvent *)event);
1603     if (event->window != SP_CANVAS_WINDOW (canvas))
1604         return FALSE;
1606     if (canvas->pixmap_gc == NULL) // canvas being deleted
1607         return FALSE;
1608     
1609     SPDesktop *dt = SP_ACTIVE_DESKTOP;
1610     
1611     // Snap when speed drops below e.g. 0.02 px/msec, or when no motion events have occured for some period.
1612         // i.e. snap when we're at stand still. A speed threshold enforces snapping for tablets, which might never
1613         // be fully at stand still and might keep spitting out motion events.
1614     if (dt) {
1615             bool const c1 = event->type == GDK_MOTION_NOTIFY;
1616             bool const c21 = event->state & GDK_BUTTON1_MASK; // Snapping only occurs when dragging with the left mouse button down
1617             bool const c22 = event->state & GDK_BUTTON2_MASK; // We shouldn't hold back any events when other mouse buttons have been
1618             bool const c23 = event->state & GDK_BUTTON3_MASK; // pressed, e.g. when scrolling with the middle mouse button; if we do then
1619                                                                                                           // Inkscape will get stuck in an unresponsive state
1620             bool const c3 = dt->namedview->snap_manager.snapprefs.getSnapEnabledGlobally();
1621             if (c1 && c21 && (!c22) && (!c23) && c3) {          
1622                 Geom::Point event_pos(event->x, event->y);
1623                 guint32 event_t = gdk_event_get_time ( (GdkEvent *) event );
1624                         
1625                 dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(true); // put snapping on hold
1626                         
1627                 if (prev_pos) {
1628                         Geom::Coord dist = Geom::L2(event_pos - *prev_pos);
1629                         guint32 delta_t = event_t - prev_time;
1630                         gdouble speed = delta_t > 0 ? dist/delta_t : 1000;
1631                         // std::cout << "speed = " << speed << " px/msec " << "| time passed = " << delta_t << " msec" << std::endl;
1632                         if (speed > 0.02) { // Jitter threshold, might be needed for tablets 
1633                                         // We're moving fast, so postpone any snapping until the next GDK_MOTION_NOTIFY event. We
1634                                 // will keep on postponing the snapping as long as the speed is high.
1635                                         // We must snap at some point in time though, so set a watchdog timer at some time from
1636                                         // now, just in case there's no future motion event that drops under the speed limit (when 
1637                                 // stoppping abruptly)
1638                                 sp_canvas_snap_watchdog_kill(canvas);
1639                                 sp_canvas_snap_watchdog_set(canvas, event); // watchdog is reset, i.e. pushed forward in time
1640                                 // If the watchdog expires before a new motion event is received, we will snap (as explained
1641                                 // above). This means however that when the timer is too short, we will always snap and that the
1642                                 // speed threshold is ineffective. In the extreme case the delay is set to zero, and snapping will
1643                                 // be immediate, as it used to be in the old days ;-). 
1644                                 } else { // Speed is very low, so we're virtually at stand still
1645                                         // But if we're really standing still, then we should snap now. We could use some low-pass filtering, 
1646                                         // otherwise snapping occurs for each jitter movement. For this filtering we'll leave the watchdog to expire,
1647                                         // snap, and set a new watchdog again. 
1648                                         if (canvas->watchdog_id == 0) { // no watchdog has been set 
1649                                                 // it might have already expired, so we'll set a new one; the snapping frequency will be limited by this
1650                                                 sp_canvas_snap_watchdog_set(canvas, event);
1651                                         } // else: watchdog has been set before and we'll wait for it to expire
1652                                 }
1653                         } else {
1654                                 // This is the first GDK_MOTION_NOTIFY event, so postpone snapping and set the watchdog
1655                                 sp_canvas_snap_watchdog_set(canvas, event);
1656                         }
1657                                 
1658                 prev_pos = event_pos;
1659                 prev_time = event_t;
1660             }
1661         }
1662     
1663     canvas->state = event->state;
1664     pick_current_item (canvas, (GdkEvent *) event);
1666     status = emit_event (canvas, (GdkEvent *) event);
1668     if (event->is_hint) {
1669         request_motions(widget->window, event);
1670     }
1672     return status;
1675 gboolean sp_canvas_snap_watchdog_callback(gpointer data) 
1677         // Snap NOW! For this the "postponed" flag will be reset and an the last motion event will be repeated  
1678         SPCanvas *canvas = reinterpret_cast<SPCanvas *>(data);
1679         if (!canvas->watchdog_event) {
1680                 return FALSE;
1681         }       
1682         
1683         SPDesktop *dt = SP_ACTIVE_DESKTOP;
1684         if (dt) {
1685                 dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false);
1686         }
1687         
1688         emit_event(canvas, canvas->watchdog_event);
1689         gdk_event_free(canvas->watchdog_event);
1690         canvas->watchdog_event = NULL;
1691         canvas->watchdog_id = 0;
1692     
1693         return FALSE;
1696 void sp_canvas_snap_watchdog_set(SPCanvas *canvas, GdkEventMotion *event) 
1698         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1699         double value = prefs->getDoubleLimited("/options/snapdelay/value", 0, 0, 1000);
1700         g_assert(canvas->watchdog_id == 0);
1701         canvas->watchdog_id = g_timeout_add(value, &sp_canvas_snap_watchdog_callback, canvas);
1702         g_assert(canvas->watchdog_event == NULL);
1703         canvas->watchdog_event = gdk_event_copy( (GdkEvent *) event); 
1706 void sp_canvas_snap_watchdog_kill(SPCanvas *canvas) 
1708         if (canvas->watchdog_id) { 
1709         g_source_remove(canvas->watchdog_id); // Kill the watchdog
1710         canvas->watchdog_id = 0;
1711     }
1712         
1713         if (canvas->watchdog_event) {
1714                 gdk_event_free(canvas->watchdog_event);
1715                 canvas->watchdog_event = NULL;
1716         }
1718         
1719 static void
1720 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)
1722     GtkWidget *widget = GTK_WIDGET (canvas);
1724     SPCanvasBuf buf;
1725     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1726         buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1727     } else {
1728         buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1729     }
1731     // Mark the region clean
1732     sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1734     buf.buf_rowstride = sw * 4; 
1735     buf.rect.x0 = x0;
1736     buf.rect.y0 = y0;
1737     buf.rect.x1 = x1;
1738     buf.rect.y1 = y1;
1739     buf.visible_rect.x0 = draw_x1;
1740     buf.visible_rect.y0 = draw_y1;
1741     buf.visible_rect.x1 = draw_x2;
1742     buf.visible_rect.y1 = draw_y2;
1743     GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1744     buf.bg_color = (((color->red & 0xff00) << 8)
1745                     | (color->green & 0xff00)
1746                     | (color->blue >> 8));
1747     buf.is_empty = true;
1749     buf.ct = nr_create_cairo_context_canvasbuf (&(buf.visible_rect), &buf);
1751     if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1752         SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1753     }
1755 #if ENABLE_LCMS
1756     cmsHTRANSFORM transf = 0;
1757     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1758     bool fromDisplay = prefs->getBool( "/options/displayprofile/from_display");
1759     if ( fromDisplay ) {
1760         transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" );
1761     } else {
1762         transf = Inkscape::colorprofile_get_display_transform();
1763     }
1764 #endif // ENABLE_LCMS
1766     if (buf.is_empty) {
1767 #if ENABLE_LCMS
1768         if ( transf && canvas->enable_cms_display_adj ) {
1769             cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1770         }
1771 #endif // ENABLE_LCMS
1772         gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1773         gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1774                             canvas->pixmap_gc,
1775                             TRUE,
1776                             x0 - canvas->x0, y0 - canvas->y0,
1777                             x1 - x0, y1 - y0);
1778     } else {
1780 #if ENABLE_LCMS
1781         if ( transf && canvas->enable_cms_display_adj ) {
1782             for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1783                 guchar* p = buf.buf + (buf.buf_rowstride * yy);
1784                 cmsDoTransform( transf, p, p, (x1 - x0) );
1785             }
1786         }
1787 #endif // ENABLE_LCMS
1789 // Now we only need to output the prepared pixmap to the actual screen, and this define chooses one
1790 // of the two ways to do it. The cairo way is direct and straightforward, but unfortunately
1791 // noticeably slower. I asked Carl Worth but he was unable so far to suggest any specific reason
1792 // for this slowness. So, for now we use the oldish method: squeeze out 32bpp buffer to 24bpp and
1793 // use gdk_draw_rgb_image_dithalign, for unfortunately gdk can only handle 24 bpp, which cairo
1794 // cannot handle at all. Still, this way is currently faster even despite the blit with squeeze.
1796 ///#define CANVAS_OUTPUT_VIA_CAIRO
1798 #ifdef CANVAS_OUTPUT_VIA_CAIRO
1800         buf.cst = cairo_image_surface_create_for_data (
1801             buf.buf,
1802             CAIRO_FORMAT_ARGB32,  // unpacked, i.e. 32 bits! one byte is unused
1803             x1 - x0, y1 - y0,
1804             buf.buf_rowstride
1805             );
1806         cairo_t *window_ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1807         cairo_set_source_surface (window_ct, buf.cst, x0 - canvas->x0, y0 - canvas->y0);
1808         cairo_paint (window_ct);
1809         cairo_destroy (window_ct);
1810         cairo_surface_finish (buf.cst);
1811         cairo_surface_destroy (buf.cst);
1813 #else
1815         NRPixBlock b3;
1816         nr_pixblock_setup_fast (&b3, NR_PIXBLOCK_MODE_R8G8B8, x0, y0, x1, y1, TRUE);
1818         NRPixBlock b4;
1819         nr_pixblock_setup_extern (&b4, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1,
1820                                   buf.buf,
1821                                   buf.buf_rowstride,
1822                                   FALSE, FALSE);
1824         // this does the 32->24 squishing, using an assembler routine:
1825         nr_blit_pixblock_pixblock (&b3, &b4);
1827         gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1828                                       canvas->pixmap_gc,
1829                                       x0 - canvas->x0, y0 - canvas->y0,
1830                                       x1 - x0, y1 - y0,
1831                                       GDK_RGB_DITHER_MAX,
1832                                       NR_PIXBLOCK_PX(&b3),
1833                                       sw * 3,
1834                                       x0 - canvas->x0, y0 - canvas->y0);
1836         nr_pixblock_release (&b3);
1837         nr_pixblock_release (&b4);
1838 #endif
1839     }
1841     cairo_surface_t *cst = cairo_get_target(buf.ct);
1842     cairo_destroy (buf.ct);
1843     cairo_surface_finish (cst);
1844     cairo_surface_destroy (cst);
1846     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1847         nr_pixelstore_256K_free (buf.buf);
1848     } else {
1849         nr_pixelstore_1M_free (buf.buf);
1850     }
1853 struct PaintRectSetup {
1854     SPCanvas* canvas;
1855     NRRectL big_rect;
1856     GTimeVal start_time;
1857     int max_pixels;
1858     Geom::Point mouse_loc;
1859 };
1861 /**
1862  * Paint the given rect, recursively subdividing the region until it is the size of a single
1863  * buffer.
1864  *
1865  * @return true if the drawing completes
1866  */
1867 static int
1868 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1870     GTimeVal now;
1871     g_get_current_time (&now);
1873     glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1874         + (now.tv_usec - setup->start_time.tv_usec);
1876     // Allow only very fast buffers to be run together;
1877     // as soon as the total redraw time exceeds 1ms, cancel;
1878     // this returns control to the idle loop and allows Inkscape to process user input
1879     // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1880     // it will get back and finish painting what remains to paint.
1881     if (elapsed > 1000) {
1883         // Interrupting redraw isn't always good.
1884         // For example, when you drag one node of a big path, only the buffer containing
1885         // the mouse cursor will be redrawn again and again, and the rest of the path
1886         // will remain stale because Inkscape never has enough idle time to redraw all
1887         // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1888         // If this limit is set, and if we have aborted redraw more times than is allowed,
1889         // interrupting is blocked and we're forced to redraw full screen once
1890         // (after which we can again interrupt forced_redraw_limit times).
1891         if (setup->canvas->forced_redraw_limit < 0 ||
1892             setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1894             if (setup->canvas->forced_redraw_limit != -1) {
1895                 setup->canvas->forced_redraw_count++;
1896             }
1898             return false;
1899         }
1900     }
1902     // Find the optimal buffer dimensions
1903     int bw = this_rect.x1 - this_rect.x0;
1904     int bh = this_rect.y1 - this_rect.y0;
1905     if ((bw < 1) || (bh < 1))
1906         return 0;
1908     if (bw * bh < setup->max_pixels) {
1909         // We are small enough
1910         sp_canvas_paint_single_buffer (setup->canvas,
1911                                        this_rect.x0, this_rect.y0,
1912                                        this_rect.x1, this_rect.y1,
1913                                        setup->big_rect.x0, setup->big_rect.y0,
1914                                        setup->big_rect.x1, setup->big_rect.y1, bw);
1915         return 1;
1916     }
1918     NRRectL lo = this_rect;
1919     NRRectL hi = this_rect;
1921 /*
1922 This test determines the redraw strategy:
1924 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1925 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1926 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1927 and seems to be faster for drawings with many smaller objects at zoom-out.
1929 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1930 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1931 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1932 faster.
1934 The default for now is the strips mode.
1935 */
1936     if (bw < bh || bh < 2 * TILE_SIZE) {
1937         // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1938         int mid = ((long long) this_rect.x0 + (long long) this_rect.x1) / 2;
1939         // Make sure that mid lies on a tile boundary
1940         mid = (mid / TILE_SIZE) * TILE_SIZE;
1942         lo.x1 = mid;
1943         hi.x0 = mid;
1945         if (setup->mouse_loc[Geom::X] < mid) {
1946             // Always paint towards the mouse first
1947             return sp_canvas_paint_rect_internal(setup, lo)
1948                 && sp_canvas_paint_rect_internal(setup, hi);
1949         } else {
1950             return sp_canvas_paint_rect_internal(setup, hi)
1951                 && sp_canvas_paint_rect_internal(setup, lo);
1952         }
1953     } else {
1954         // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1955         int mid = ((long long) this_rect.y0 + (long long) this_rect.y1) / 2;
1956         // Make sure that mid lies on a tile boundary
1957         mid = (mid / TILE_SIZE) * TILE_SIZE;
1959         lo.y1 = mid;
1960         hi.y0 = mid;
1962         if (setup->mouse_loc[Geom::Y] < mid) {
1963             // Always paint towards the mouse first
1964             return sp_canvas_paint_rect_internal(setup, lo)
1965                 && sp_canvas_paint_rect_internal(setup, hi);
1966         } else {
1967             return sp_canvas_paint_rect_internal(setup, hi)
1968                 && sp_canvas_paint_rect_internal(setup, lo);
1969         }
1970     }
1974 /**
1975  * Helper that draws a specific rectangular part of the canvas.
1976  *
1977  * @return true if the rectangle painting succeeds.
1978  */
1979 static bool
1980 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1982     g_return_val_if_fail (!canvas->need_update, false);
1984     NRRectL rect;
1985     rect.x0 = xx0;
1986     rect.x1 = xx1;
1987     rect.y0 = yy0;
1988     rect.y1 = yy1;
1990     // Clip rect-to-draw by the current visible area
1991     rect.x0 = MAX (rect.x0, canvas->x0);
1992     rect.y0 = MAX (rect.y0, canvas->y0);
1993     rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1994     rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1996 #ifdef DEBUG_REDRAW
1997     // paint the area to redraw yellow
1998     gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1999     gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
2000                         canvas->pixmap_gc,
2001                         TRUE,
2002                         rect.x0 - canvas->x0, rect.y0 - canvas->y0,
2003                         rect.x1 - rect.x0, rect.y1 - rect.y0);
2004 #endif
2006     PaintRectSetup setup;
2008     setup.canvas = canvas;
2009     setup.big_rect = rect;
2011     // Save the mouse location
2012     gint x, y;
2013     gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
2014     setup.mouse_loc = sp_canvas_window_to_world (canvas, Geom::Point(x,y));
2016     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
2017         // use 256K as a compromise to not slow down gradients
2018         // 256K is the cached buffer and we need 4 channels
2019         setup.max_pixels = 65536; // 256K/4
2020     } else {
2021         // paths only, so 1M works faster
2022         // 1M is the cached buffer and we need 4 channels
2023         setup.max_pixels = 262144; 
2024     }
2026     // Start the clock
2027     g_get_current_time(&(setup.start_time));
2029     // Go
2030     return sp_canvas_paint_rect_internal(&setup, rect);
2033 /**
2034  * Force a full redraw after a specified number of interrupted redraws
2035  */
2036 void
2037 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
2038   g_return_if_fail(canvas != NULL);
2040   canvas->forced_redraw_limit = count;
2041   canvas->forced_redraw_count = 0;
2044 /**
2045  * End forced full redraw requests
2046  */
2047 void
2048 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
2049   g_return_if_fail(canvas != NULL);
2051   canvas->forced_redraw_limit = -1;
2054 /**
2055  * The canvas widget's expose callback.
2056  */
2057 static gint
2058 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
2060     SPCanvas *canvas = SP_CANVAS (widget);
2062     if (!GTK_WIDGET_DRAWABLE (widget) ||
2063         (event->window != SP_CANVAS_WINDOW (canvas)))
2064         return FALSE;
2066     int n_rects;
2067     GdkRectangle *rects;
2068     gdk_region_get_rectangles (event->region, &rects, &n_rects);
2070     for (int i = 0; i < n_rects; i++) {
2071         NRRectL rect;
2073         rect.x0 = rects[i].x + canvas->x0;
2074         rect.y0 = rects[i].y + canvas->y0;
2075         rect.x1 = rect.x0 + rects[i].width;
2076         rect.y1 = rect.y0 + rects[i].height;
2078         sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
2079     }
2081     if (n_rects > 0)
2082         g_free (rects);
2084     return FALSE;
2087 /**
2088  * The canvas widget's keypress callback.
2089  */
2090 static gint
2091 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
2093     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
2096 /**
2097  * Crossing event handler for the canvas.
2098  */
2099 static gint
2100 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
2102     SPCanvas *canvas = SP_CANVAS (widget);
2104     if (event->window != SP_CANVAS_WINDOW (canvas))
2105         return FALSE;
2107     canvas->state = event->state;
2108     return pick_current_item (canvas, (GdkEvent *) event);
2111 /**
2112  * Focus in handler for the canvas.
2113  */
2114 static gint
2115 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
2117     GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
2119     SPCanvas *canvas = SP_CANVAS (widget);
2121     if (canvas->focused_item) {
2122         return emit_event (canvas, (GdkEvent *) event);
2123     } else {
2124         return FALSE;
2125     }
2128 /**
2129  * Focus out handler for the canvas.
2130  */
2131 static gint
2132 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
2134     GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2136     SPCanvas *canvas = SP_CANVAS (widget);
2138     if (canvas->focused_item)
2139         return emit_event (canvas, (GdkEvent *) event);
2140     else
2141         return FALSE;
2144 /**
2145  * Helper that repaints the areas in the canvas that need it.
2146  *
2147  * @return true if all the dirty parts have been redrawn
2148  */
2149 static int
2150 paint (SPCanvas *canvas)
2152     if (canvas->need_update) {
2153         sp_canvas_item_invoke_update (canvas->root, Geom::identity(), 0);
2154         canvas->need_update = FALSE;
2155     }
2157     if (!canvas->need_redraw)
2158         return TRUE;
2160     Gdk::Region to_paint;
2162     for (int j=canvas->tTop; j<canvas->tBottom; j++) {
2163         for (int i=canvas->tLeft; i<canvas->tRight; i++) {
2164             int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
2166             if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
2167                 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
2168                                    TILE_SIZE, TILE_SIZE));
2169             }
2171         }
2172     }
2174     if (!to_paint.empty()) {
2175         Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
2176         typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
2177         for (Iter i=rect.begin(); i != rect.end(); ++i) {
2178             int x0 = (*i).get_x();
2179             int y0 = (*i).get_y();
2180             int x1 = x0 + (*i).get_width();
2181             int y1 = y0 + (*i).get_height();
2182             if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
2183                 // Aborted
2184                 return FALSE;
2185             };
2186         }
2187     }
2189     canvas->need_redraw = FALSE;
2191     // we've had a full unaborted redraw, reset the full redraw counter
2192     if (canvas->forced_redraw_limit != -1) {
2193         canvas->forced_redraw_count = 0;
2194     }
2196     return TRUE;
2199 /**
2200  * Helper that invokes update, paint, and repick on canvas.
2201  */
2202 static int
2203 do_update (SPCanvas *canvas)
2205     if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop durring interrupted display!
2206         return TRUE;
2208     /* Cause the update if necessary */
2209     if (canvas->need_update) {
2210         sp_canvas_item_invoke_update (canvas->root, Geom::identity(), 0);
2211         canvas->need_update = FALSE;
2212     }
2214     /* Paint if able to */
2215     if (GTK_WIDGET_DRAWABLE (canvas)) {
2216             return paint (canvas);
2217     }
2219     /* Pick new current item */
2220     while (canvas->need_repick) {
2221         canvas->need_repick = FALSE;
2222         pick_current_item (canvas, &canvas->pick_event);
2223     }
2225     return TRUE;
2228 /**
2229  * Idle handler for the canvas that deals with pending updates and redraws.
2230  */
2231 static gint
2232 idle_handler (gpointer data)
2234     GDK_THREADS_ENTER ();
2236     SPCanvas *canvas = SP_CANVAS (data);
2238     int const ret = do_update (canvas);
2240     if (ret) {
2241         /* Reset idle id */
2242         canvas->idle_id = 0;
2243     }
2245     GDK_THREADS_LEAVE ();
2247     return !ret;
2250 /**
2251  * Convenience function to add an idle handler to a canvas.
2252  */
2253 static void
2254 add_idle (SPCanvas *canvas)
2256     if (canvas->idle_id != 0)
2257         return;
2259     canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2262 /**
2263  * Returns the root group of the specified canvas.
2264  */
2265 SPCanvasGroup *
2266 sp_canvas_root (SPCanvas *canvas)
2268     g_return_val_if_fail (canvas != NULL, NULL);
2269     g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2271     return SP_CANVAS_GROUP (canvas->root);
2274 /**
2275  * Scrolls canvas to specific position (cx and cy are measured in screen pixels)
2276  */
2277 void
2278 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2280     g_return_if_fail (canvas != NULL);
2281     g_return_if_fail (SP_IS_CANVAS (canvas));
2283     int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels)
2284     int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do!
2285     int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the 
2286     int dy = iy - canvas->y0; // canvas w.r.t its previous position
2288     canvas->dx0 = cx; // here the 'd' stands for double, not delta!
2289     canvas->dy0 = cy;
2290     canvas->x0 = ix;
2291     canvas->y0 = iy;
2293     sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2295     if (!clear) {
2296         // scrolling without zoom; redraw only the newly exposed areas
2297         if ((dx != 0) || (dy != 0)) {
2298             canvas->is_scrolling = is_scrolling;
2299             if (GTK_WIDGET_REALIZED (canvas)) {
2300                 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2301             }
2302         }
2303     } else {
2304         // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2305     }
2308 /**
2309  * Updates canvas if necessary.
2310  */
2311 void
2312 sp_canvas_update_now (SPCanvas *canvas)
2314     g_return_if_fail (canvas != NULL);
2315     g_return_if_fail (SP_IS_CANVAS (canvas));
2317     if (!(canvas->need_update ||
2318           canvas->need_redraw))
2319         return;
2321     do_update (canvas);
2324 /**
2325  * Update callback for canvas widget.
2326  */
2327 static void
2328 sp_canvas_request_update (SPCanvas *canvas)
2330     canvas->need_update = TRUE;
2331     add_idle (canvas);
2334 /**
2335  * Forces redraw of rectangular canvas area.
2336  */
2337 void
2338 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2340     NRRectL bbox;
2341     NRRectL visible;
2342     NRRectL clip;
2344     g_return_if_fail (canvas != NULL);
2345     g_return_if_fail (SP_IS_CANVAS (canvas));
2347     if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2348     if ((x0 >= x1) || (y0 >= y1)) return;
2350     bbox.x0 = x0;
2351     bbox.y0 = y0;
2352     bbox.x1 = x1;
2353     bbox.y1 = y1;
2355     visible.x0 = canvas->x0;
2356     visible.y0 = canvas->y0;
2357     visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2358     visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2360     nr_rect_l_intersect (&clip, &bbox, &visible);
2362     sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2363     add_idle (canvas);
2366 /**
2367  * Sets world coordinates from win and canvas.
2368  */
2369 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2371     g_return_if_fail (canvas != NULL);
2372     g_return_if_fail (SP_IS_CANVAS (canvas));
2374     if (worldx) *worldx = canvas->x0 + winx;
2375     if (worldy) *worldy = canvas->y0 + winy;
2378 /**
2379  * Sets win coordinates from world and canvas.
2380  */
2381 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2383     g_return_if_fail (canvas != NULL);
2384     g_return_if_fail (SP_IS_CANVAS (canvas));
2386     if (winx) *winx = worldx - canvas->x0;
2387     if (winy) *winy = worldy - canvas->y0;
2390 /**
2391  * Converts point from win to world coordinates.
2392  */
2393 Geom::Point sp_canvas_window_to_world(SPCanvas const *canvas, Geom::Point const win)
2395     g_assert (canvas != NULL);
2396     g_assert (SP_IS_CANVAS (canvas));
2398     return Geom::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2401 /**
2402  * Converts point from world to win coordinates.
2403  */
2404 Geom::Point sp_canvas_world_to_window(SPCanvas const *canvas, Geom::Point const world)
2406     g_assert (canvas != NULL);
2407     g_assert (SP_IS_CANVAS (canvas));
2409     return Geom::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2412 /**
2413  * Returns true if point given in world coordinates is inside window.
2414  */
2415 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, Geom::Point const &world)
2417     g_assert( canvas != NULL );
2418     g_assert(SP_IS_CANVAS(canvas));
2420     GtkWidget const &w = *GTK_WIDGET(canvas);
2421     return ( ( canvas->x0 <= world[Geom::X] )  &&
2422              ( canvas->y0 <= world[Geom::Y] )  &&
2423              ( world[Geom::X] < canvas->x0 + w.allocation.width )  &&
2424              ( world[Geom::Y] < canvas->y0 + w.allocation.height ) );
2427 /**
2428  * Return canvas window coordinates as Geom::Rect.
2429  */
2430 Geom::Rect SPCanvas::getViewbox() const
2432     GtkWidget const *w = GTK_WIDGET(this);
2433     return Geom::Rect(Geom::Point(dx0, dy0),
2434                       Geom::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2437 /**
2438  * Return canvas window coordinates as IRect (a rectangle defined by integers).
2439  */
2440 NR::IRect SPCanvas::getViewboxIntegers() const
2442     GtkWidget const *w = GTK_WIDGET(this);
2443     return NR::IRect(NR::IPoint(x0, y0),
2444                     NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height));
2447 inline int sp_canvas_tile_floor(int x)
2449     return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2452 inline int sp_canvas_tile_ceil(int x)
2454     return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2457 /**
2458  * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2459  */
2460 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2462     if ( nl >= nr || nt >= nb ) {
2463         if ( canvas->tiles ) g_free(canvas->tiles);
2464         canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2465         canvas->tileH=canvas->tileV=0;
2466         canvas->tiles=NULL;
2467         return;
2468     }
2469     int tl=sp_canvas_tile_floor(nl);
2470     int tt=sp_canvas_tile_floor(nt);
2471     int tr=sp_canvas_tile_ceil(nr);
2472     int tb=sp_canvas_tile_ceil(nb);
2474     int nh = tr-tl, nv = tb-tt;
2475     uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2476     for (int i=tl; i<tr; i++) {
2477         for (int j=tt; j<tb; j++) {
2478             int ind = (i-tl) + (j-tt)*nh;
2479             if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2480                 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2481             } else {
2482                 ntiles[ind]=0; // newly exposed areas get 0
2483             }
2484         }
2485     }
2486     if ( canvas->tiles ) g_free(canvas->tiles);
2487     canvas->tiles=ntiles;
2488     canvas->tLeft=tl;
2489     canvas->tTop=tt;
2490     canvas->tRight=tr;
2491     canvas->tBottom=tb;
2492     canvas->tileH=nh;
2493     canvas->tileV=nv;
2496 /*
2497  * Helper that queues a canvas rectangle for redraw
2498  */
2499 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2500     canvas->need_redraw = TRUE;
2502     sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2505 /**
2506  * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2507  */
2508 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2510     if ( nl >= nr || nt >= nb ) {
2511         return;
2512     }
2513     int tl=sp_canvas_tile_floor(nl);
2514     int tt=sp_canvas_tile_floor(nt);
2515     int tr=sp_canvas_tile_ceil(nr);
2516     int tb=sp_canvas_tile_ceil(nb);
2517     if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2518     if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2519     if ( tr > canvas->tRight ) tr=canvas->tRight;
2520     if ( tt < canvas->tTop ) tt=canvas->tTop;
2521     if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2523     for (int i=tl; i<tr; i++) {
2524         for (int j=tt; j<tb; j++) {
2525             canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2526         }
2527     }
2531 /*
2532   Local Variables:
2533   mode:c++
2534   c-file-style:"stroustrup"
2535   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2536   indent-tabs-mode:nil
2537   fill-column:99
2538   End:
2539 */
2540 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :