Code

After snapping, show a tooltip together with the snap indicator
[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);
941 static gboolean sp_canvas_query_tooltip (GtkWidget *widget, gint x, gint y, gboolean keyboard_mode, GtkTooltip *tooltip);
943 static GtkWidgetClass *canvas_parent_class;
945 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb);
946 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb);
947 static void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val);
948 static int do_update (SPCanvas *canvas);
950 static gboolean sp_canvas_snap_watchdog_callback(gpointer data);
951 static void sp_canvas_snap_watchdog_set(SPCanvas *canvas, GdkEventMotion *event);
952 static void sp_canvas_snap_watchdog_kill(SPCanvas *canvas);
954 /**
955  * Registers the SPCanvas class if necessary, and returns the type ID
956  * associated to it.
957  *
958  * \return The type ID of the SPCanvas class.
959  **/
960 GType sp_canvas_get_type(void)
962     static GType type = 0;
963     if (!type) {
964         GTypeInfo info = {
965             sizeof(SPCanvasClass),
966             0, // base_init
967             0, // base_finalize
968             (GClassInitFunc)sp_canvas_class_init,
969             0, // class_finalize
970             0, // class_data
971             sizeof(SPCanvas),
972             0, // n_preallocs
973             (GInstanceInitFunc)sp_canvas_init,
974             0 // value_table
975         };
976         type = g_type_register_static(GTK_TYPE_WIDGET, "SPCanvas", &info, static_cast<GTypeFlags>(0));
977     }
978     return type;
981 /**
982  * Class initialization function for SPCanvasClass.
983  */
984 static void
985 sp_canvas_class_init (SPCanvasClass *klass)
987     GtkObjectClass *object_class = (GtkObjectClass *) klass;
988     GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
990     canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
992     object_class->destroy = sp_canvas_destroy;
994     widget_class->realize = sp_canvas_realize;
995     widget_class->unrealize = sp_canvas_unrealize;
996     widget_class->size_request = sp_canvas_size_request;
997     widget_class->size_allocate = sp_canvas_size_allocate;
998     widget_class->button_press_event = sp_canvas_button;
999     widget_class->button_release_event = sp_canvas_button;
1000     widget_class->motion_notify_event = sp_canvas_motion;
1001     widget_class->scroll_event = sp_canvas_scroll;
1002     widget_class->expose_event = sp_canvas_expose;
1003     widget_class->key_press_event = sp_canvas_key;
1004     widget_class->key_release_event = sp_canvas_key;
1005     widget_class->enter_notify_event = sp_canvas_crossing;
1006     widget_class->leave_notify_event = sp_canvas_crossing;
1007     widget_class->focus_in_event = sp_canvas_focus_in;
1008     widget_class->focus_out_event = sp_canvas_focus_out;
1011 /**
1012  * Callback: object initialization for SPCanvas.
1013  */
1014 static void
1015 sp_canvas_init (SPCanvas *canvas)
1017     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
1018     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
1019     GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
1021     canvas->pick_event.type = GDK_LEAVE_NOTIFY;
1022     canvas->pick_event.crossing.x = 0;
1023     canvas->pick_event.crossing.y = 0;
1025     /* Create the root item as a special case */
1026     canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
1027     canvas->root->canvas = canvas;
1029     gtk_object_ref (GTK_OBJECT (canvas->root));
1030     gtk_object_sink (GTK_OBJECT (canvas->root));
1032     canvas->need_repick = TRUE;
1034     // See comment at in sp-canvas.h.
1035     canvas->gen_all_enter_events = false;
1037     canvas->tiles=NULL;
1038     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1039     canvas->tileH=canvas->tileV=0;
1041     canvas->forced_redraw_count = 0;
1042     canvas->forced_redraw_limit = -1;
1044 #if ENABLE_LCMS
1045     canvas->enable_cms_display_adj = false;
1046     canvas->cms_key = new Glib::ustring("");
1047 #endif // ENABLE_LCMS
1049     canvas->is_scrolling = false;
1051     canvas->watchdog_id = 0;
1052     canvas->watchdog_event = NULL;
1053     canvas->context_snap_delay_active = false;
1055     g_signal_connect(&(canvas->widget), "query-tooltip", G_CALLBACK (sp_canvas_query_tooltip), NULL);
1058 /**
1059  * Convenience function to remove the idle handler of a canvas.
1060  */
1061 static void
1062 remove_idle (SPCanvas *canvas)
1064     if (canvas->idle_id) {
1065         gtk_idle_remove (canvas->idle_id);
1066         canvas->idle_id = 0;
1067     }
1070 /*
1071  * Removes the transient state of the canvas (idle handler, grabs).
1072  */
1073 static void
1074 shutdown_transients (SPCanvas *canvas)
1076     /* We turn off the need_redraw flag, since if the canvas is mapped again
1077      * it will request a redraw anyways.  We do not turn off the need_update
1078      * flag, though, because updates are not queued when the canvas remaps
1079      * itself.
1080      */
1081     if (canvas->need_redraw) {
1082         canvas->need_redraw = FALSE;
1083     }
1084     if ( canvas->tiles ) g_free(canvas->tiles);
1085     canvas->tiles=NULL;
1086     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1087     canvas->tileH=canvas->tileV=0;
1089     if (canvas->grabbed_item) {
1090         canvas->grabbed_item = NULL;
1091         gdk_pointer_ungrab (GDK_CURRENT_TIME);
1092     }
1094     remove_idle (canvas);
1097 /**
1098  * Destroy handler for SPCanvas.
1099  */
1100 static void
1101 sp_canvas_destroy (GtkObject *object)
1103     SPCanvas *canvas = SP_CANVAS (object);
1105     if (canvas->root) {
1106         gtk_object_unref (GTK_OBJECT (canvas->root));
1107         canvas->root = NULL;
1108     }
1110     shutdown_transients (canvas);
1112     if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1113         (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1116 static void track_latency(GdkEvent const *event) {
1117     GdkEventLatencyTracker &tracker = GdkEventLatencyTracker::default_tracker();
1118     boost::optional<double> latency = tracker.process(event);
1119     if (latency && *latency > 2.0) {
1120         //g_warning("Event latency reached %f sec (%1.4f)", *latency, tracker.getSkew());
1121     }
1124 /**
1125  * Returns new canvas as widget.
1126  */
1127 GtkWidget *
1128 sp_canvas_new_aa (void)
1130     SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1132     return (GtkWidget *) canvas;
1135 /**
1136  * The canvas widget's realize callback.
1137  */
1138 static void
1139 sp_canvas_realize (GtkWidget *widget)
1141     SPCanvas *canvas = SP_CANVAS (widget);
1143     GdkWindowAttr attributes;
1144     attributes.window_type = GDK_WINDOW_CHILD;
1145     attributes.x = widget->allocation.x;
1146     attributes.y = widget->allocation.y;
1147     attributes.width = widget->allocation.width;
1148     attributes.height = widget->allocation.height;
1149     attributes.wclass = GDK_INPUT_OUTPUT;
1150     attributes.visual = gdk_rgb_get_visual ();
1151     attributes.colormap = gdk_rgb_get_cmap ();
1152     attributes.event_mask = (gtk_widget_get_events (widget) |
1153                              GDK_EXPOSURE_MASK |
1154                              GDK_BUTTON_PRESS_MASK |
1155                              GDK_BUTTON_RELEASE_MASK |
1156                              GDK_POINTER_MOTION_MASK |
1157                              ( HAS_BROKEN_MOTION_HINTS ?
1158                                0 : GDK_POINTER_MOTION_HINT_MASK ) |
1159                              GDK_PROXIMITY_IN_MASK |
1160                              GDK_PROXIMITY_OUT_MASK |
1161                              GDK_KEY_PRESS_MASK |
1162                              GDK_KEY_RELEASE_MASK |
1163                              GDK_ENTER_NOTIFY_MASK |
1164                              GDK_LEAVE_NOTIFY_MASK |
1165                              GDK_FOCUS_CHANGE_MASK);
1166     gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1168     widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1169     gdk_window_set_user_data (widget->window, widget);
1171     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1172     if ( prefs->getBool("/options/useextinput/value", true) )
1173         gtk_widget_set_events(widget, attributes.event_mask);
1175     widget->style = gtk_style_attach (widget->style, widget->window);
1177     GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1179     canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1182 /**
1183  * The canvas widget's unrealize callback.
1184  */
1185 static void
1186 sp_canvas_unrealize (GtkWidget *widget)
1188     SPCanvas *canvas = SP_CANVAS (widget);
1190     canvas->current_item = NULL;
1191     canvas->grabbed_item = NULL;
1192     canvas->focused_item = NULL;
1194     shutdown_transients (canvas);
1196     gdk_gc_destroy (canvas->pixmap_gc);
1197     canvas->pixmap_gc = NULL;
1199     if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1200         (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1203 /**
1204  * The canvas widget's size_request callback.
1205  */
1206 static void
1207 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1209     static_cast<void>(SP_CANVAS (widget));
1211     req->width = 256;
1212     req->height = 256;
1215 /**
1216  * The canvas widget's size_allocate callback.
1217  */
1218 static void
1219 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1221     SPCanvas *canvas = SP_CANVAS (widget);
1223     /* Schedule redraw of new region */
1224     sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1225     if (allocation->width > widget->allocation.width) {
1226         sp_canvas_request_redraw (canvas,
1227                                   canvas->x0 + widget->allocation.width,
1228                                   0,
1229                                   canvas->x0 + allocation->width,
1230                                   canvas->y0 + allocation->height);
1231     }
1232     if (allocation->height > widget->allocation.height) {
1233         sp_canvas_request_redraw (canvas,
1234                                   0,
1235                                   canvas->y0 + widget->allocation.height,
1236                                   canvas->x0 + allocation->width,
1237                                   canvas->y0 + allocation->height);
1238     }
1240     widget->allocation = *allocation;
1242     if (GTK_WIDGET_REALIZED (widget)) {
1243         gdk_window_move_resize (widget->window,
1244                                 widget->allocation.x, widget->allocation.y,
1245                                 widget->allocation.width, widget->allocation.height);
1246     }
1249 /**
1250  * Helper that emits an event for an item in the canvas, be it the current
1251  * item, grabbed item, or focused item, as appropriate.
1252  */
1253 static int
1254 emit_event (SPCanvas *canvas, GdkEvent *event)
1256     guint mask;
1258     if (canvas->grabbed_item) {
1259         switch (event->type) {
1260         case GDK_ENTER_NOTIFY:
1261             mask = GDK_ENTER_NOTIFY_MASK;
1262             break;
1263         case GDK_LEAVE_NOTIFY:
1264             mask = GDK_LEAVE_NOTIFY_MASK;
1265             break;
1266         case GDK_MOTION_NOTIFY:
1267             mask = GDK_POINTER_MOTION_MASK;
1268             break;
1269         case GDK_BUTTON_PRESS:
1270         case GDK_2BUTTON_PRESS:
1271         case GDK_3BUTTON_PRESS:
1272             mask = GDK_BUTTON_PRESS_MASK;
1273             break;
1274         case GDK_BUTTON_RELEASE:
1275             mask = GDK_BUTTON_RELEASE_MASK;
1276             break;
1277         case GDK_KEY_PRESS:
1278             mask = GDK_KEY_PRESS_MASK;
1279             break;
1280         case GDK_KEY_RELEASE:
1281             mask = GDK_KEY_RELEASE_MASK;
1282             break;
1283         case GDK_SCROLL:
1284             mask = GDK_SCROLL;
1285             break;
1286         default:
1287             mask = 0;
1288             break;
1289         }
1291         if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1292     }
1294     /* Convert to world coordinates -- we have two cases because of diferent
1295      * offsets of the fields in the event structures.
1296      */
1298     GdkEvent ev = *event;
1300     switch (ev.type) {
1301     case GDK_ENTER_NOTIFY:
1302     case GDK_LEAVE_NOTIFY:
1303         ev.crossing.x += canvas->x0;
1304         ev.crossing.y += canvas->y0;
1305         break;
1306     case GDK_MOTION_NOTIFY:
1307     case GDK_BUTTON_PRESS:
1308     case GDK_2BUTTON_PRESS:
1309     case GDK_3BUTTON_PRESS:
1310     case GDK_BUTTON_RELEASE:
1311         ev.motion.x += canvas->x0;
1312         ev.motion.y += canvas->y0;
1313         break;
1314     default:
1315         break;
1316     }
1318     /* Choose where we send the event */
1320     /* canvas->current_item becomes NULL in some cases under Win32
1321     ** (e.g. if the pointer leaves the window).  So this is a hack that
1322     ** Lauris applied to SP to get around the problem.
1323     */
1324     SPCanvasItem* item = NULL;
1325     if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1326         item = canvas->grabbed_item;
1327     } else {
1328         item = canvas->current_item;
1329     }
1331     if (canvas->focused_item &&
1332         ((event->type == GDK_KEY_PRESS) ||
1333          (event->type == GDK_KEY_RELEASE) ||
1334          (event->type == GDK_FOCUS_CHANGE))) {
1335         item = canvas->focused_item;
1336     }
1338     /* The event is propagated up the hierarchy (for if someone connected to
1339      * a group instead of a leaf event), and emission is stopped if a
1340      * handler returns TRUE, just like for GtkWidget events.
1341      */
1343     gint finished = FALSE;
1345     while (item && !finished) {
1346         gtk_object_ref (GTK_OBJECT (item));
1347         gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1348         SPCanvasItem *parent = item->parent;
1349         gtk_object_unref (GTK_OBJECT (item));
1350         item = parent;
1351     }
1353     return finished;
1356 /**
1357  * Helper that re-picks the current item in the canvas, based on the event's
1358  * coordinates and emits enter/leave events for items as appropriate.
1359  */
1360 static int
1361 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1363     int button_down = 0;
1364     double x, y;
1366     if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1367         return FALSE;
1369     int retval = FALSE;
1371     if (canvas->gen_all_enter_events == false) {
1372         // If a button is down, we'll perform enter and leave events on the
1373         // current item, but not enter on any other item.  This is more or
1374         // less like X pointer grabbing for canvas items.
1375         //
1376         button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1377                 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1379         if (!button_down) canvas->left_grabbed_item = FALSE;
1380     }
1382     /* Save the event in the canvas.  This is used to synthesize enter and
1383      * leave events in case the current item changes.  It is also used to
1384      * re-pick the current item if the current one gets deleted.  Also,
1385      * synthesize an enter event.
1386      */
1387     if (event != &canvas->pick_event) {
1388         if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1389             /* these fields have the same offsets in both types of events */
1391             canvas->pick_event.crossing.type       = GDK_ENTER_NOTIFY;
1392             canvas->pick_event.crossing.window     = event->motion.window;
1393             canvas->pick_event.crossing.send_event = event->motion.send_event;
1394             canvas->pick_event.crossing.subwindow  = NULL;
1395             canvas->pick_event.crossing.x          = event->motion.x;
1396             canvas->pick_event.crossing.y          = event->motion.y;
1397             canvas->pick_event.crossing.mode       = GDK_CROSSING_NORMAL;
1398             canvas->pick_event.crossing.detail     = GDK_NOTIFY_NONLINEAR;
1399             canvas->pick_event.crossing.focus      = FALSE;
1400             canvas->pick_event.crossing.state      = event->motion.state;
1402             /* these fields don't have the same offsets in both types of events */
1404             if (event->type == GDK_MOTION_NOTIFY) {
1405                 canvas->pick_event.crossing.x_root = event->motion.x_root;
1406                 canvas->pick_event.crossing.y_root = event->motion.y_root;
1407             } else {
1408                 canvas->pick_event.crossing.x_root = event->button.x_root;
1409                 canvas->pick_event.crossing.y_root = event->button.y_root;
1410             }
1411         } else {
1412             canvas->pick_event = *event;
1413         }
1414     }
1416     /* Don't do anything else if this is a recursive call */
1417     if (canvas->in_repick) return retval;
1419     /* LeaveNotify means that there is no current item, so we don't look for one */
1420     if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1421         /* these fields don't have the same offsets in both types of events */
1423         if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1424             x = canvas->pick_event.crossing.x;
1425             y = canvas->pick_event.crossing.y;
1426         } else {
1427             x = canvas->pick_event.motion.x;
1428             y = canvas->pick_event.motion.y;
1429         }
1431         /* world coords */
1432         x += canvas->x0;
1433         y += canvas->y0;
1435         /* find the closest item */
1436         if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1437             sp_canvas_item_invoke_point (canvas->root, Geom::Point(x, y), &canvas->new_current_item);
1438         } else {
1439             canvas->new_current_item = NULL;
1440         }
1441     } else {
1442         canvas->new_current_item = NULL;
1443     }
1445     if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1446         return retval; /* current item did not change */
1447     }
1449     /* Synthesize events for old and new current items */
1451     if ((canvas->new_current_item != canvas->current_item)
1452         && (canvas->current_item != NULL)
1453         && !canvas->left_grabbed_item) {
1454         GdkEvent new_event;
1455         SPCanvasItem *item;
1457         item = canvas->current_item;
1459         new_event = canvas->pick_event;
1460         new_event.type = GDK_LEAVE_NOTIFY;
1462         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1463         new_event.crossing.subwindow = NULL;
1464         canvas->in_repick = TRUE;
1465         retval = emit_event (canvas, &new_event);
1466         canvas->in_repick = FALSE;
1467     }
1469     if (canvas->gen_all_enter_events == false) {
1470         // new_current_item may have been set to NULL during the call to
1471         // emit_event() above
1472         if ((canvas->new_current_item != canvas->current_item) && button_down) {
1473             canvas->left_grabbed_item = TRUE;
1474             return retval;
1475         }
1476     }
1478     /* Handle the rest of cases */
1480     canvas->left_grabbed_item = FALSE;
1481     canvas->current_item = canvas->new_current_item;
1483     if (canvas->current_item != NULL) {
1484         GdkEvent new_event;
1486         new_event = canvas->pick_event;
1487         new_event.type = GDK_ENTER_NOTIFY;
1488         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1489         new_event.crossing.subwindow = NULL;
1490         retval = emit_event (canvas, &new_event);
1491     }
1493     return retval;
1496 /**
1497  * Button event handler for the canvas.
1498  */
1499 static gint
1500 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1502     SPCanvas *canvas = SP_CANVAS (widget);
1503     SPDesktop *dt = SP_ACTIVE_DESKTOP;
1505     int retval = FALSE;
1507     /* dispatch normally regardless of the event's window if an item has
1508        has a pointer grab in effect */
1509     if (!canvas->grabbed_item &&
1510         event->window != SP_CANVAS_WINDOW (canvas))
1511         return retval;
1513     int mask;
1514     switch (event->button) {
1515     case 1:
1516         mask = GDK_BUTTON1_MASK;
1517         break;
1518     case 2:
1519         mask = GDK_BUTTON2_MASK;
1520         break;
1521     case 3:
1522         mask = GDK_BUTTON3_MASK;
1523         break;
1524     case 4:
1525         mask = GDK_BUTTON4_MASK;
1526         break;
1527     case 5:
1528         mask = GDK_BUTTON5_MASK;
1529         break;
1530     default:
1531         mask = 0;
1532     }
1534     switch (event->type) {
1535     case GDK_BUTTON_PRESS:
1536     case GDK_2BUTTON_PRESS:
1537     case GDK_3BUTTON_PRESS:
1538         if (dt) {
1539                         // Snapping will be on hold if we're moving the mouse at high speeds. When starting
1540                 // drawing a new shape we really should snap though.
1541                 dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false);
1542                 }
1544         /* Pick the current item as if the button were not pressed, and
1545          * then process the event.
1546          */
1547         canvas->state = event->state;
1548         pick_current_item (canvas, (GdkEvent *) event);
1549         canvas->state ^= mask;
1550         retval = emit_event (canvas, (GdkEvent *) event);
1551         break;
1553     case GDK_BUTTON_RELEASE:
1554         sp_canvas_snap_watchdog_callback(canvas); // If we have any pending snapping action, then invoke it now
1556         /* Process the event as if the button were pressed, then repick
1557          * after the button has been released
1558          */
1559         canvas->state = event->state;
1560         retval = emit_event (canvas, (GdkEvent *) event);
1561         event->state ^= mask;
1562         canvas->state = event->state;
1563         pick_current_item (canvas, (GdkEvent *) event);
1564         event->state ^= mask;
1566         break;
1568     default:
1569         g_assert_not_reached ();
1570     }
1572     return retval;
1575 /**
1576  * Scroll event handler for the canvas.
1577  *
1578  * \todo FIXME: generate motion events to re-select items.
1579  */
1580 static gint
1581 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1583     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1586 static inline void request_motions(GdkWindow *w, GdkEventMotion *event) {
1587     gdk_window_get_pointer(w, NULL, NULL, NULL);
1588 #if HAS_GDK_EVENT_REQUEST_MOTIONS
1589     gdk_event_request_motions(event);
1590 #endif
1593 /**
1594  * Motion event handler for the canvas.
1595  */
1596 static int
1597 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1599         static guint32 prev_time;
1600         static boost::optional<Geom::Point> prev_pos;
1602         int status;
1603     SPCanvas *canvas = SP_CANVAS (widget);
1605     track_latency((GdkEvent *)event);
1607     if (event->window != SP_CANVAS_WINDOW (canvas))
1608         return FALSE;
1610     if (canvas->pixmap_gc == NULL) // canvas being deleted
1611         return FALSE;
1613     SPDesktop *dt = SP_ACTIVE_DESKTOP;
1615     if (canvas->context_snap_delay_active && dt && dt->namedview->snap_manager.snapprefs.getSnapEnabledGlobally()) {
1616         // Snap when speed drops below e.g. 0.02 px/msec, or when no motion events have occurred for some period.
1617                 // i.e. snap when we're at stand still. A speed threshold enforces snapping for tablets, which might never
1618                 // be fully at stand still and might keep spitting out motion events.
1619                 dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(true); // put snapping on hold
1621         Geom::Point event_pos(event->x, event->y);
1622                 guint32 event_t = gdk_event_get_time ( (GdkEvent *) event );
1624                 if (prev_pos) {
1625                         Geom::Coord dist = Geom::L2(event_pos - *prev_pos);
1626                         guint32 delta_t = event_t - prev_time;
1627                         gdouble speed = delta_t > 0 ? dist/delta_t : 1000;
1628                         if (speed > 0.02) { // Jitter threshold, might be needed for tablets
1629                                 // We're moving fast, so postpone any snapping until the next GDK_MOTION_NOTIFY event. We
1630                                 // will keep on postponing the snapping as long as the speed is high.
1631                                 // We must snap at some point in time though, so set a watchdog timer at some time from
1632                                 // now, just in case there's no future motion event that drops under the speed limit (when
1633                                 // stopping abruptly)
1634                                 sp_canvas_snap_watchdog_kill(canvas);
1635                                 sp_canvas_snap_watchdog_set(canvas, event); // watchdog is reset, i.e. pushed forward in time
1636                                 // If the watchdog expires before a new motion event is received, we will snap (as explained
1637                                 // above). This means however that when the timer is too short, we will always snap and that the
1638                                 // speed threshold is ineffective. In the extreme case the delay is set to zero, and snapping will
1639                                 // be immediate, as it used to be in the old days ;-).
1640                         } else { // Speed is very low, so we're virtually at stand still
1641                                 // But if we're really standing still, then we should snap now. We could use some low-pass filtering,
1642                                 // otherwise snapping occurs for each jitter movement. For this filtering we'll leave the watchdog to expire,
1643                                 // snap, and set a new watchdog again.
1644                                 if (canvas->watchdog_id == 0) { // no watchdog has been set
1645                                         // it might have already expired, so we'll set a new one; the snapping frequency will be limited by this
1646                                         sp_canvas_snap_watchdog_set(canvas, event);
1647                                 } // else: watchdog has been set before and we'll wait for it to expire
1648                         }
1649                 } else {
1650                         // This is the first GDK_MOTION_NOTIFY event, so postpone snapping and set the watchdog
1651                         sp_canvas_snap_watchdog_set(canvas, event);
1652                 }
1654                 prev_pos = event_pos;
1655                 prev_time = event_t;
1656         }
1658     canvas->state = event->state;
1659         pick_current_item (canvas, (GdkEvent *) event);
1660         status = emit_event (canvas, (GdkEvent *) event);
1661         if (event->is_hint) {
1662                 request_motions(widget->window, event);
1663         }
1665     return status;
1668 gboolean sp_canvas_snap_watchdog_callback(gpointer data)
1670         // Snap NOW! For this the "postponed" flag will be reset and an the last motion event will be repeated
1671         SPCanvas *canvas = reinterpret_cast<SPCanvas *>(data);
1672         if (!canvas->watchdog_event) {
1673                 // This might occur when this method is called directly, i.e. not through the timer
1674                 return FALSE;
1675         }
1677         SPDesktop *dt = SP_ACTIVE_DESKTOP;
1678         if (dt) {
1679                 dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false);
1680         }
1682         emit_event(canvas, canvas->watchdog_event);
1683         gdk_event_free(canvas->watchdog_event);
1684         canvas->watchdog_event = NULL;
1685         canvas->watchdog_id = 0;
1687         return FALSE;
1690 void sp_canvas_snap_watchdog_set(SPCanvas *canvas, GdkEventMotion *event)
1692         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1693         double value = prefs->getDoubleLimited("/options/snapdelay/value", 0, 0, 1000);
1694         g_assert(canvas->watchdog_id == 0);
1695         canvas->watchdog_id = g_timeout_add(value, &sp_canvas_snap_watchdog_callback, canvas);
1696         g_assert(canvas->watchdog_event == NULL);
1697         canvas->watchdog_event = gdk_event_copy( (GdkEvent *) event);
1700 void sp_canvas_snap_watchdog_kill(SPCanvas *canvas)
1702         if (canvas->watchdog_id) {
1703         g_source_remove(canvas->watchdog_id); // Kill the watchdog
1704         canvas->watchdog_id = 0;
1705     }
1707         if (canvas->watchdog_event) {
1708                 gdk_event_free(canvas->watchdog_event);
1709                 canvas->watchdog_event = NULL;
1710         }
1713 void sp_canvas_set_snap_delay_active(SPCanvas *canvas, bool snapping)
1715         // Only when canvas->context_snap_delay_active has been set, Inkscape will know that snapping is active
1716         // and will delay any snapping events (but only when asked to through the preferences)
1718         // When snapping is being delayed, then that will also mean that at some point the last event
1719         // might be re-triggered. This should only occur when Inkscape is still in the same tool or context,
1720         // and even more specifically, the tool should even be in the same state. If for example snapping is being delayed while
1721         // creating a rectangle, then the rect-context will be active and it will be in the "dragging" state
1722         // (see the static boolean variable "dragging" in the sp_rect_context_root_handler). The procedure is
1723         // as follows: call sp_canvas_set_snap_delay_active(*, TRUE) when entering the "dragging" state, which will delay
1724         // snapping from that moment on, and call sp_canvas_set_snap_delay_active(*, FALSE) when leaving the "dragging"
1725         // state. This last call will also make sure that any pending snap events will be canceled.
1727         if (!canvas) {
1728                 g_warning("sp_canvas_set_snap_delay_active() has been called without providing a canvas!");
1729                 return;
1730         }
1732         if (canvas->context_snap_delay_active == snapping) {
1733                 g_warning("Snapping was already allowed or disallowed! This is a bug, please report it.");
1734         }
1736         canvas->context_snap_delay_active = snapping;
1738         if (snapping == false) {
1739                 sp_canvas_snap_watchdog_kill(canvas); // kill any pending snapping events
1740         }
1743 static void
1744 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)
1746     GtkWidget *widget = GTK_WIDGET (canvas);
1748     SPCanvasBuf buf;
1749     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1750         buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1751     } else {
1752         buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1753     }
1755     // Mark the region clean
1756     sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1758     buf.buf_rowstride = sw * 4;
1759     buf.rect.x0 = x0;
1760     buf.rect.y0 = y0;
1761     buf.rect.x1 = x1;
1762     buf.rect.y1 = y1;
1763     buf.visible_rect.x0 = draw_x1;
1764     buf.visible_rect.y0 = draw_y1;
1765     buf.visible_rect.x1 = draw_x2;
1766     buf.visible_rect.y1 = draw_y2;
1767     GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1768     buf.bg_color = (((color->red & 0xff00) << 8)
1769                     | (color->green & 0xff00)
1770                     | (color->blue >> 8));
1771     buf.is_empty = true;
1773     buf.ct = nr_create_cairo_context_canvasbuf (&(buf.visible_rect), &buf);
1775     if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1776         SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1777     }
1779 #if ENABLE_LCMS
1780     cmsHTRANSFORM transf = 0;
1781     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1782     bool fromDisplay = prefs->getBool( "/options/displayprofile/from_display");
1783     if ( fromDisplay ) {
1784         transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" );
1785     } else {
1786         transf = Inkscape::colorprofile_get_display_transform();
1787     }
1788 #endif // ENABLE_LCMS
1790     if (buf.is_empty) {
1791 #if ENABLE_LCMS
1792         if ( transf && canvas->enable_cms_display_adj ) {
1793             cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1794         }
1795 #endif // ENABLE_LCMS
1796         gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1797         gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1798                             canvas->pixmap_gc,
1799                             TRUE,
1800                             x0 - canvas->x0, y0 - canvas->y0,
1801                             x1 - x0, y1 - y0);
1802     } else {
1804 #if ENABLE_LCMS
1805         if ( transf && canvas->enable_cms_display_adj ) {
1806             for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1807                 guchar* p = buf.buf + (buf.buf_rowstride * yy);
1808                 cmsDoTransform( transf, p, p, (x1 - x0) );
1809             }
1810         }
1811 #endif // ENABLE_LCMS
1813 // Now we only need to output the prepared pixmap to the actual screen, and this define chooses one
1814 // of the two ways to do it. The cairo way is direct and straightforward, but unfortunately
1815 // noticeably slower. I asked Carl Worth but he was unable so far to suggest any specific reason
1816 // for this slowness. So, for now we use the oldish method: squeeze out 32bpp buffer to 24bpp and
1817 // use gdk_draw_rgb_image_dithalign, for unfortunately gdk can only handle 24 bpp, which cairo
1818 // cannot handle at all. Still, this way is currently faster even despite the blit with squeeze.
1820 ///#define CANVAS_OUTPUT_VIA_CAIRO
1822 #ifdef CANVAS_OUTPUT_VIA_CAIRO
1824         buf.cst = cairo_image_surface_create_for_data (
1825             buf.buf,
1826             CAIRO_FORMAT_ARGB32,  // unpacked, i.e. 32 bits! one byte is unused
1827             x1 - x0, y1 - y0,
1828             buf.buf_rowstride
1829             );
1830         cairo_t *window_ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1831         cairo_set_source_surface (window_ct, buf.cst, x0 - canvas->x0, y0 - canvas->y0);
1832         cairo_paint (window_ct);
1833         cairo_destroy (window_ct);
1834         cairo_surface_finish (buf.cst);
1835         cairo_surface_destroy (buf.cst);
1837 #else
1839         NRPixBlock b3;
1840         nr_pixblock_setup_fast (&b3, NR_PIXBLOCK_MODE_R8G8B8, x0, y0, x1, y1, TRUE);
1842         NRPixBlock b4;
1843         nr_pixblock_setup_extern (&b4, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1,
1844                                   buf.buf,
1845                                   buf.buf_rowstride,
1846                                   FALSE, FALSE);
1848         // this does the 32->24 squishing, using an assembler routine:
1849         nr_blit_pixblock_pixblock (&b3, &b4);
1851         gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1852                                       canvas->pixmap_gc,
1853                                       x0 - canvas->x0, y0 - canvas->y0,
1854                                       x1 - x0, y1 - y0,
1855                                       GDK_RGB_DITHER_MAX,
1856                                       NR_PIXBLOCK_PX(&b3),
1857                                       sw * 3,
1858                                       x0 - canvas->x0, y0 - canvas->y0);
1860         nr_pixblock_release (&b3);
1861         nr_pixblock_release (&b4);
1862 #endif
1863     }
1865     cairo_surface_t *cst = cairo_get_target(buf.ct);
1866     cairo_destroy (buf.ct);
1867     cairo_surface_finish (cst);
1868     cairo_surface_destroy (cst);
1870     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1871         nr_pixelstore_256K_free (buf.buf);
1872     } else {
1873         nr_pixelstore_1M_free (buf.buf);
1874     }
1877 struct PaintRectSetup {
1878     SPCanvas* canvas;
1879     NRRectL big_rect;
1880     GTimeVal start_time;
1881     int max_pixels;
1882     Geom::Point mouse_loc;
1883 };
1885 /**
1886  * Paint the given rect, recursively subdividing the region until it is the size of a single
1887  * buffer.
1888  *
1889  * @return true if the drawing completes
1890  */
1891 static int
1892 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1894     GTimeVal now;
1895     g_get_current_time (&now);
1897     glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1898         + (now.tv_usec - setup->start_time.tv_usec);
1900     // Allow only very fast buffers to be run together;
1901     // as soon as the total redraw time exceeds 1ms, cancel;
1902     // this returns control to the idle loop and allows Inkscape to process user input
1903     // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1904     // it will get back and finish painting what remains to paint.
1905     if (elapsed > 1000) {
1907         // Interrupting redraw isn't always good.
1908         // For example, when you drag one node of a big path, only the buffer containing
1909         // the mouse cursor will be redrawn again and again, and the rest of the path
1910         // will remain stale because Inkscape never has enough idle time to redraw all
1911         // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1912         // If this limit is set, and if we have aborted redraw more times than is allowed,
1913         // interrupting is blocked and we're forced to redraw full screen once
1914         // (after which we can again interrupt forced_redraw_limit times).
1915         if (setup->canvas->forced_redraw_limit < 0 ||
1916             setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1918             if (setup->canvas->forced_redraw_limit != -1) {
1919                 setup->canvas->forced_redraw_count++;
1920             }
1922             return false;
1923         }
1924     }
1926     // Find the optimal buffer dimensions
1927     int bw = this_rect.x1 - this_rect.x0;
1928     int bh = this_rect.y1 - this_rect.y0;
1929     if ((bw < 1) || (bh < 1))
1930         return 0;
1932     if (bw * bh < setup->max_pixels) {
1933         // We are small enough
1934         sp_canvas_paint_single_buffer (setup->canvas,
1935                                        this_rect.x0, this_rect.y0,
1936                                        this_rect.x1, this_rect.y1,
1937                                        setup->big_rect.x0, setup->big_rect.y0,
1938                                        setup->big_rect.x1, setup->big_rect.y1, bw);
1939         return 1;
1940     }
1942     NRRectL lo = this_rect;
1943     NRRectL hi = this_rect;
1945 /*
1946 This test determines the redraw strategy:
1948 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1949 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1950 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1951 and seems to be faster for drawings with many smaller objects at zoom-out.
1953 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1954 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1955 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1956 faster.
1958 The default for now is the strips mode.
1959 */
1960     if (bw < bh || bh < 2 * TILE_SIZE) {
1961         // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1962         int mid = ((long long) this_rect.x0 + (long long) this_rect.x1) / 2;
1963         // Make sure that mid lies on a tile boundary
1964         mid = (mid / TILE_SIZE) * TILE_SIZE;
1966         lo.x1 = mid;
1967         hi.x0 = mid;
1969         if (setup->mouse_loc[Geom::X] < mid) {
1970             // Always paint towards the mouse first
1971             return sp_canvas_paint_rect_internal(setup, lo)
1972                 && sp_canvas_paint_rect_internal(setup, hi);
1973         } else {
1974             return sp_canvas_paint_rect_internal(setup, hi)
1975                 && sp_canvas_paint_rect_internal(setup, lo);
1976         }
1977     } else {
1978         // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1979         int mid = ((long long) this_rect.y0 + (long long) this_rect.y1) / 2;
1980         // Make sure that mid lies on a tile boundary
1981         mid = (mid / TILE_SIZE) * TILE_SIZE;
1983         lo.y1 = mid;
1984         hi.y0 = mid;
1986         if (setup->mouse_loc[Geom::Y] < mid) {
1987             // Always paint towards the mouse first
1988             return sp_canvas_paint_rect_internal(setup, lo)
1989                 && sp_canvas_paint_rect_internal(setup, hi);
1990         } else {
1991             return sp_canvas_paint_rect_internal(setup, hi)
1992                 && sp_canvas_paint_rect_internal(setup, lo);
1993         }
1994     }
1998 /**
1999  * Helper that draws a specific rectangular part of the canvas.
2000  *
2001  * @return true if the rectangle painting succeeds.
2002  */
2003 static bool
2004 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
2006     g_return_val_if_fail (!canvas->need_update, false);
2008     NRRectL rect;
2009     rect.x0 = xx0;
2010     rect.x1 = xx1;
2011     rect.y0 = yy0;
2012     rect.y1 = yy1;
2014     // Clip rect-to-draw by the current visible area
2015     rect.x0 = MAX (rect.x0, canvas->x0);
2016     rect.y0 = MAX (rect.y0, canvas->y0);
2017     rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
2018     rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
2020 #ifdef DEBUG_REDRAW
2021     // paint the area to redraw yellow
2022     gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
2023     gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
2024                         canvas->pixmap_gc,
2025                         TRUE,
2026                         rect.x0 - canvas->x0, rect.y0 - canvas->y0,
2027                         rect.x1 - rect.x0, rect.y1 - rect.y0);
2028 #endif
2030     PaintRectSetup setup;
2032     setup.canvas = canvas;
2033     setup.big_rect = rect;
2035     // Save the mouse location
2036     gint x, y;
2037     gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
2038     setup.mouse_loc = sp_canvas_window_to_world (canvas, Geom::Point(x,y));
2040     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
2041         // use 256K as a compromise to not slow down gradients
2042         // 256K is the cached buffer and we need 4 channels
2043         setup.max_pixels = 65536; // 256K/4
2044     } else {
2045         // paths only, so 1M works faster
2046         // 1M is the cached buffer and we need 4 channels
2047         setup.max_pixels = 262144;
2048     }
2050     // Start the clock
2051     g_get_current_time(&(setup.start_time));
2053     // Go
2054     return sp_canvas_paint_rect_internal(&setup, rect);
2057 /**
2058  * Force a full redraw after a specified number of interrupted redraws
2059  */
2060 void
2061 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
2062   g_return_if_fail(canvas != NULL);
2064   canvas->forced_redraw_limit = count;
2065   canvas->forced_redraw_count = 0;
2068 /**
2069  * End forced full redraw requests
2070  */
2071 void
2072 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
2073   g_return_if_fail(canvas != NULL);
2075   canvas->forced_redraw_limit = -1;
2078 /**
2079  * The canvas widget's expose callback.
2080  */
2081 static gint
2082 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
2084     SPCanvas *canvas = SP_CANVAS (widget);
2086     if (!GTK_WIDGET_DRAWABLE (widget) ||
2087         (event->window != SP_CANVAS_WINDOW (canvas)))
2088         return FALSE;
2090     int n_rects;
2091     GdkRectangle *rects;
2092     gdk_region_get_rectangles (event->region, &rects, &n_rects);
2094     for (int i = 0; i < n_rects; i++) {
2095         NRRectL rect;
2097         rect.x0 = rects[i].x + canvas->x0;
2098         rect.y0 = rects[i].y + canvas->y0;
2099         rect.x1 = rect.x0 + rects[i].width;
2100         rect.y1 = rect.y0 + rects[i].height;
2102         sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
2103     }
2105     if (n_rects > 0)
2106         g_free (rects);
2108     return FALSE;
2111 /**
2112  * The canvas widget's keypress callback.
2113  */
2114 static gint
2115 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
2117     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
2120 /**
2121  * Crossing event handler for the canvas.
2122  */
2123 static gint
2124 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
2126     SPCanvas *canvas = SP_CANVAS (widget);
2128     if (event->window != SP_CANVAS_WINDOW (canvas))
2129         return FALSE;
2131     canvas->state = event->state;
2132     return pick_current_item (canvas, (GdkEvent *) event);
2135 /**
2136  * Focus in handler for the canvas.
2137  */
2138 static gint
2139 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
2141     GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
2143     SPCanvas *canvas = SP_CANVAS (widget);
2145     if (canvas->focused_item) {
2146         return emit_event (canvas, (GdkEvent *) event);
2147     } else {
2148         return FALSE;
2149     }
2152 /**
2153  * Focus out handler for the canvas.
2154  */
2155 static gint
2156 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
2158     GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2160     SPCanvas *canvas = SP_CANVAS (widget);
2162     if (canvas->focused_item)
2163         return emit_event (canvas, (GdkEvent *) event);
2164     else
2165         return FALSE;
2169 static gboolean sp_canvas_query_tooltip (GtkWidget  *widget,
2170                   gint        x,
2171                   gint        y,
2172                   gboolean        keyboard_mode,
2173                   GtkTooltip *tooltip)
2175         // We're not really doing anything special here, so we might just as well remove sp_canvas_query_tooltip
2176         // all together (and stop listening to the query_tooltip signal. We might make a custom tooltip however
2177         // someday, for example to display icons instead of just plain text. In that case we will need this call
2178         // so that's why I'm leaving it here for the time being.
2180         if (canvas_parent_class->query_tooltip) {
2181                 return canvas_parent_class->query_tooltip (widget, x, y, keyboard_mode, tooltip);
2182         }
2184         return false;
2188 /**
2189  * Helper that repaints the areas in the canvas that need it.
2190  *
2191  * @return true if all the dirty parts have been redrawn
2192  */
2193 static int
2194 paint (SPCanvas *canvas)
2196     if (canvas->need_update) {
2197         sp_canvas_item_invoke_update (canvas->root, Geom::identity(), 0);
2198         canvas->need_update = FALSE;
2199     }
2201     if (!canvas->need_redraw)
2202         return TRUE;
2204     Gdk::Region to_paint;
2206     for (int j=canvas->tTop; j<canvas->tBottom; j++) {
2207         for (int i=canvas->tLeft; i<canvas->tRight; i++) {
2208             int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
2210             if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
2211                 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
2212                                    TILE_SIZE, TILE_SIZE));
2213             }
2215         }
2216     }
2218     if (!to_paint.empty()) {
2219         Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
2220         typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
2221         for (Iter i=rect.begin(); i != rect.end(); ++i) {
2222             int x0 = (*i).get_x();
2223             int y0 = (*i).get_y();
2224             int x1 = x0 + (*i).get_width();
2225             int y1 = y0 + (*i).get_height();
2226             if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
2227                 // Aborted
2228                 return FALSE;
2229             };
2230         }
2231     }
2233     canvas->need_redraw = FALSE;
2235     // we've had a full unaborted redraw, reset the full redraw counter
2236     if (canvas->forced_redraw_limit != -1) {
2237         canvas->forced_redraw_count = 0;
2238     }
2240     return TRUE;
2243 /**
2244  * Helper that invokes update, paint, and repick on canvas.
2245  */
2246 static int
2247 do_update (SPCanvas *canvas)
2249     if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop durring interrupted display!
2250         return TRUE;
2252     /* Cause the update if necessary */
2253     if (canvas->need_update) {
2254         sp_canvas_item_invoke_update (canvas->root, Geom::identity(), 0);
2255         canvas->need_update = FALSE;
2256     }
2258     /* Paint if able to */
2259     if (GTK_WIDGET_DRAWABLE (canvas)) {
2260             return paint (canvas);
2261     }
2263     /* Pick new current item */
2264     while (canvas->need_repick) {
2265         canvas->need_repick = FALSE;
2266         pick_current_item (canvas, &canvas->pick_event);
2267     }
2269     return TRUE;
2272 /**
2273  * Idle handler for the canvas that deals with pending updates and redraws.
2274  */
2275 static gint
2276 idle_handler (gpointer data)
2278     GDK_THREADS_ENTER ();
2280     SPCanvas *canvas = SP_CANVAS (data);
2282     int const ret = do_update (canvas);
2284     if (ret) {
2285         /* Reset idle id */
2286         canvas->idle_id = 0;
2287     }
2289     GDK_THREADS_LEAVE ();
2291     return !ret;
2294 /**
2295  * Convenience function to add an idle handler to a canvas.
2296  */
2297 static void
2298 add_idle (SPCanvas *canvas)
2300     if (canvas->idle_id != 0)
2301         return;
2303     canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2306 /**
2307  * Returns the root group of the specified canvas.
2308  */
2309 SPCanvasGroup *
2310 sp_canvas_root (SPCanvas *canvas)
2312     g_return_val_if_fail (canvas != NULL, NULL);
2313     g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2315     return SP_CANVAS_GROUP (canvas->root);
2318 /**
2319  * Scrolls canvas to specific position (cx and cy are measured in screen pixels)
2320  */
2321 void
2322 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2324     g_return_if_fail (canvas != NULL);
2325     g_return_if_fail (SP_IS_CANVAS (canvas));
2327     int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels)
2328     int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do!
2329     int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the
2330     int dy = iy - canvas->y0; // canvas w.r.t its previous position
2332     canvas->dx0 = cx; // here the 'd' stands for double, not delta!
2333     canvas->dy0 = cy;
2334     canvas->x0 = ix;
2335     canvas->y0 = iy;
2337     sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2339     if (!clear) {
2340         // scrolling without zoom; redraw only the newly exposed areas
2341         if ((dx != 0) || (dy != 0)) {
2342             canvas->is_scrolling = is_scrolling;
2343             if (GTK_WIDGET_REALIZED (canvas)) {
2344                 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2345             }
2346         }
2347     } else {
2348         // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2349     }
2352 /**
2353  * Updates canvas if necessary.
2354  */
2355 void
2356 sp_canvas_update_now (SPCanvas *canvas)
2358     g_return_if_fail (canvas != NULL);
2359     g_return_if_fail (SP_IS_CANVAS (canvas));
2361     if (!(canvas->need_update ||
2362           canvas->need_redraw))
2363         return;
2365     do_update (canvas);
2368 /**
2369  * Update callback for canvas widget.
2370  */
2371 static void
2372 sp_canvas_request_update (SPCanvas *canvas)
2374     canvas->need_update = TRUE;
2375     add_idle (canvas);
2378 /**
2379  * Forces redraw of rectangular canvas area.
2380  */
2381 void
2382 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2384     NRRectL bbox;
2385     NRRectL visible;
2386     NRRectL clip;
2388     g_return_if_fail (canvas != NULL);
2389     g_return_if_fail (SP_IS_CANVAS (canvas));
2391     if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2392     if ((x0 >= x1) || (y0 >= y1)) return;
2394     bbox.x0 = x0;
2395     bbox.y0 = y0;
2396     bbox.x1 = x1;
2397     bbox.y1 = y1;
2399     visible.x0 = canvas->x0;
2400     visible.y0 = canvas->y0;
2401     visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2402     visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2404     nr_rect_l_intersect (&clip, &bbox, &visible);
2406     sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2407     add_idle (canvas);
2410 /**
2411  * Sets world coordinates from win and canvas.
2412  */
2413 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2415     g_return_if_fail (canvas != NULL);
2416     g_return_if_fail (SP_IS_CANVAS (canvas));
2418     if (worldx) *worldx = canvas->x0 + winx;
2419     if (worldy) *worldy = canvas->y0 + winy;
2422 /**
2423  * Sets win coordinates from world and canvas.
2424  */
2425 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2427     g_return_if_fail (canvas != NULL);
2428     g_return_if_fail (SP_IS_CANVAS (canvas));
2430     if (winx) *winx = worldx - canvas->x0;
2431     if (winy) *winy = worldy - canvas->y0;
2434 /**
2435  * Converts point from win to world coordinates.
2436  */
2437 Geom::Point sp_canvas_window_to_world(SPCanvas const *canvas, Geom::Point const win)
2439     g_assert (canvas != NULL);
2440     g_assert (SP_IS_CANVAS (canvas));
2442     return Geom::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2445 /**
2446  * Converts point from world to win coordinates.
2447  */
2448 Geom::Point sp_canvas_world_to_window(SPCanvas const *canvas, Geom::Point const world)
2450     g_assert (canvas != NULL);
2451     g_assert (SP_IS_CANVAS (canvas));
2453     return Geom::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2456 /**
2457  * Returns true if point given in world coordinates is inside window.
2458  */
2459 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, Geom::Point const &world)
2461     g_assert( canvas != NULL );
2462     g_assert(SP_IS_CANVAS(canvas));
2464     GtkWidget const &w = *GTK_WIDGET(canvas);
2465     return ( ( canvas->x0 <= world[Geom::X] )  &&
2466              ( canvas->y0 <= world[Geom::Y] )  &&
2467              ( world[Geom::X] < canvas->x0 + w.allocation.width )  &&
2468              ( world[Geom::Y] < canvas->y0 + w.allocation.height ) );
2471 /**
2472  * Return canvas window coordinates as Geom::Rect.
2473  */
2474 Geom::Rect SPCanvas::getViewbox() const
2476     GtkWidget const *w = GTK_WIDGET(this);
2477     return Geom::Rect(Geom::Point(dx0, dy0),
2478                       Geom::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2481 /**
2482  * Return canvas window coordinates as IRect (a rectangle defined by integers).
2483  */
2484 NR::IRect SPCanvas::getViewboxIntegers() const
2486     GtkWidget const *w = GTK_WIDGET(this);
2487     return NR::IRect(NR::IPoint(x0, y0),
2488                     NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height));
2491 inline int sp_canvas_tile_floor(int x)
2493     return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2496 inline int sp_canvas_tile_ceil(int x)
2498     return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2501 /**
2502  * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2503  */
2504 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2506     if ( nl >= nr || nt >= nb ) {
2507         if ( canvas->tiles ) g_free(canvas->tiles);
2508         canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2509         canvas->tileH=canvas->tileV=0;
2510         canvas->tiles=NULL;
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);
2518     int nh = tr-tl, nv = tb-tt;
2519     uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2520     for (int i=tl; i<tr; i++) {
2521         for (int j=tt; j<tb; j++) {
2522             int ind = (i-tl) + (j-tt)*nh;
2523             if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2524                 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2525             } else {
2526                 ntiles[ind]=0; // newly exposed areas get 0
2527             }
2528         }
2529     }
2530     if ( canvas->tiles ) g_free(canvas->tiles);
2531     canvas->tiles=ntiles;
2532     canvas->tLeft=tl;
2533     canvas->tTop=tt;
2534     canvas->tRight=tr;
2535     canvas->tBottom=tb;
2536     canvas->tileH=nh;
2537     canvas->tileV=nv;
2540 /*
2541  * Helper that queues a canvas rectangle for redraw
2542  */
2543 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2544     canvas->need_redraw = TRUE;
2546     sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2549 /**
2550  * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2551  */
2552 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2554     if ( nl >= nr || nt >= nb ) {
2555         return;
2556     }
2557     int tl=sp_canvas_tile_floor(nl);
2558     int tt=sp_canvas_tile_floor(nt);
2559     int tr=sp_canvas_tile_ceil(nr);
2560     int tb=sp_canvas_tile_ceil(nb);
2561     if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2562     if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2563     if ( tr > canvas->tRight ) tr=canvas->tRight;
2564     if ( tt < canvas->tTop ) tt=canvas->tTop;
2565     if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2567     for (int i=tl; i<tr; i++) {
2568         for (int j=tt; j<tb; j++) {
2569             canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2570         }
2571     }
2575 /*
2576   Local Variables:
2577   mode:c++
2578   c-file-style:"stroustrup"
2579   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2580   indent-tabs-mode:nil
2581   fill-column:99
2582   End:
2583 */
2584 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :