Code

e9bf1633ee7a9cb4db3ae33e38b5a5e51c6dd6d7
[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 <libnr/nr-matrix-fns.h>
36 #include <libnr/nr-matrix-ops.h>
37 #include <libnr/nr-convex-hull.h>
38 #include "preferences.h"
39 #include "inkscape.h"
40 #include "sodipodi-ctrlrect.h"
41 #if ENABLE_LCMS
42 #include "color-profile-fns.h"
43 #endif // ENABLE_LCMS
44 #include "display/rendermode.h"
45 #include "libnr/nr-blit.h"
46 #include "display/inkscape-cairo.h"
47 #include "debug/gdk-event-latency-tracker.h"
48 #include "desktop.h"
49 #include "sp-namedview.h"
51 using Inkscape::Debug::GdkEventLatencyTracker;
53 // GTK_CHECK_VERSION returns false on failure
54 #define HAS_GDK_EVENT_REQUEST_MOTIONS GTK_CHECK_VERSION(2, 12, 0)
56 // gtk_check_version returns non-NULL on failure
57 static bool const HAS_BROKEN_MOTION_HINTS =
58   true || gtk_check_version(2, 12, 0) != NULL || !HAS_GDK_EVENT_REQUEST_MOTIONS;
60 // Define this to visualize the regions to be redrawn
61 //#define DEBUG_REDRAW 1;
63 // Tiles are a way to minimize the number of redraws, eliminating too small redraws.
64 // The canvas stores a 2D array of ints, each representing a TILE_SIZExTILE_SIZE pixels tile.
65 // If any part of it is dirtied, the entire tile is dirtied (its int is nonzero) and repainted.
66 #define TILE_SIZE 16
68 static gint const sp_canvas_update_priority = G_PRIORITY_HIGH_IDLE;
70 #define SP_CANVAS_WINDOW(c) (((GtkWidget *) (c))->window)
72 enum {
73     SP_CANVAS_ITEM_VISIBLE = 1 << 7,
74     SP_CANVAS_ITEM_NEED_UPDATE = 1 << 8,
75     SP_CANVAS_ITEM_NEED_AFFINE = 1 << 9
76 };
78 /**
79  * A group of Items.
80  */
81 struct SPCanvasGroup {
82     SPCanvasItem item;
84     GList *items, *last;
85 };
87 /**
88  * The SPCanvasGroup vtable.
89  */
90 struct SPCanvasGroupClass {
91     SPCanvasItemClass parent_class;
92 };
94 /**
95  * The SPCanvas vtable.
96  */
97 struct SPCanvasClass {
98     GtkWidgetClass parent_class;
99 };
101 static void group_add (SPCanvasGroup *group, SPCanvasItem *item);
102 static void group_remove (SPCanvasGroup *group, SPCanvasItem *item);
104 /* SPCanvasItem */
106 enum {ITEM_EVENT, ITEM_LAST_SIGNAL};
109 static void sp_canvas_request_update (SPCanvas *canvas);
111 static void track_latency(GdkEvent const *event);
112 static void sp_canvas_item_class_init (SPCanvasItemClass *klass);
113 static void sp_canvas_item_init (SPCanvasItem *item);
114 static void sp_canvas_item_dispose (GObject *object);
115 static void sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args);
117 static int emit_event (SPCanvas *canvas, GdkEvent *event);
119 static guint item_signals[ITEM_LAST_SIGNAL] = { 0 };
121 static GtkObjectClass *item_parent_class;
123 /**
124  * Registers the SPCanvasItem class with Glib and returns its type number.
125  */
126 GType
127 sp_canvas_item_get_type (void)
129     static GType type = 0;
130     if (!type) {
131         static GTypeInfo const info = {
132             sizeof (SPCanvasItemClass),
133             NULL, NULL,
134             (GClassInitFunc) sp_canvas_item_class_init,
135             NULL, NULL,
136             sizeof (SPCanvasItem),
137             0,
138             (GInstanceInitFunc) sp_canvas_item_init,
139             NULL
140         };
141         type = g_type_register_static (GTK_TYPE_OBJECT, "SPCanvasItem", &info, (GTypeFlags)0);
142     }
144     return type;
147 /**
148  * Initializes the SPCanvasItem vtable and the "event" signal.
149  */
150 static void
151 sp_canvas_item_class_init (SPCanvasItemClass *klass)
153     GObjectClass *object_class = (GObjectClass *) klass;
155     /* fixme: Derive from GObject */
156     item_parent_class = (GtkObjectClass*)gtk_type_class (GTK_TYPE_OBJECT);
158     item_signals[ITEM_EVENT] = g_signal_new ("event",
159                                              G_TYPE_FROM_CLASS (klass),
160                                              G_SIGNAL_RUN_LAST,
161                                              ((glong)((guint8*)&(klass->event) - (guint8*)klass)),
162                                              NULL, NULL,
163                                              sp_marshal_BOOLEAN__POINTER,
164                                              G_TYPE_BOOLEAN, 1,
165                                              GDK_TYPE_EVENT);
167     object_class->dispose = sp_canvas_item_dispose;
170 /**
171  * Callback for initialization of SPCanvasItem.
172  */
173 static void
174 sp_canvas_item_init (SPCanvasItem *item)
176     item->flags |= SP_CANVAS_ITEM_VISIBLE;
177     item->xform = Geom::Matrix(Geom::identity());
180 /**
181  * Constructs new SPCanvasItem on SPCanvasGroup.
182  */
183 SPCanvasItem *
184 sp_canvas_item_new (SPCanvasGroup *parent, GtkType type, gchar const *first_arg_name, ...)
186     va_list args;
188     g_return_val_if_fail (parent != NULL, NULL);
189     g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL);
190     g_return_val_if_fail (gtk_type_is_a (type, sp_canvas_item_get_type ()), NULL);
192     SPCanvasItem *item = SP_CANVAS_ITEM (gtk_type_new (type));
194     va_start (args, first_arg_name);
195     sp_canvas_item_construct (item, parent, first_arg_name, args);
196     va_end (args);
198     return item;
201 /**
202  * Sets up the newly created SPCanvasItem.
203  *
204  * We make it static for encapsulation reasons since it was nowhere used.
205  */
206 static void
207 sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args)
209     g_return_if_fail (SP_IS_CANVAS_GROUP (parent));
210     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
212     item->parent = SP_CANVAS_ITEM (parent);
213     item->canvas = item->parent->canvas;
215     g_object_set_valist (G_OBJECT (item), first_arg_name, args);
217     group_add (SP_CANVAS_GROUP (item->parent), item);
219     sp_canvas_item_request_update (item);
222 /**
223  * Helper function that requests redraw only if item's visible flag is set.
224  */
225 static void
226 redraw_if_visible (SPCanvasItem *item)
228     if (item->flags & SP_CANVAS_ITEM_VISIBLE) {
229         int x0 = (int)(item->x1);
230         int x1 = (int)(item->x2);
231         int y0 = (int)(item->y1);
232         int y1 = (int)(item->y2);
234         if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
235             sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
236         }
237     }
240 /**
241  * Callback that removes item from all referers and destroys it.
242  */
243 static void
244 sp_canvas_item_dispose (GObject *object)
246     SPCanvasItem *item = SP_CANVAS_ITEM (object);
248     // Hack: if this is a ctrlrect, move it to 0,0;
249     // this redraws only the stroke of the rect to be deleted,
250     // avoiding redraw of the entire area
251     if (SP_IS_CTRLRECT(item)) {
252         SP_CTRLRECT(object)->setRectangle(Geom::Rect(Geom::Point(0,0),Geom::Point(0,0)));
253         SP_CTRLRECT(object)->update(item->xform, 0);
254     } else {
255         redraw_if_visible (item);
256     }
257     item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
259     if (item == item->canvas->current_item) {
260         item->canvas->current_item = NULL;
261         item->canvas->need_repick = TRUE;
262     }
264     if (item == item->canvas->new_current_item) {
265         item->canvas->new_current_item = NULL;
266         item->canvas->need_repick = TRUE;
267     }
269     if (item == item->canvas->grabbed_item) {
270         item->canvas->grabbed_item = NULL;
271         gdk_pointer_ungrab (GDK_CURRENT_TIME);
272     }
274     if (item == item->canvas->focused_item)
275         item->canvas->focused_item = NULL;
277     if (item->parent) {
278         group_remove (SP_CANVAS_GROUP (item->parent), item);
279     }
281     G_OBJECT_CLASS (item_parent_class)->dispose (object);
284 /**
285  * Helper function to update item and its children.
286  *
287  * NB! affine is parent2canvas.
288  */
289 static void
290 sp_canvas_item_invoke_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags)
292     /* Apply the child item's transform */
293     Geom::Matrix child_affine = item->xform * affine;
295     /* apply object flags to child flags */
296     int child_flags = flags & ~SP_CANVAS_UPDATE_REQUESTED;
298     if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
299         child_flags |= SP_CANVAS_UPDATE_REQUESTED;
301     if (item->flags & SP_CANVAS_ITEM_NEED_AFFINE)
302         child_flags |= SP_CANVAS_UPDATE_AFFINE;
304     if (child_flags & (SP_CANVAS_UPDATE_REQUESTED | SP_CANVAS_UPDATE_AFFINE)) {
305         if (SP_CANVAS_ITEM_GET_CLASS (item)->update)
306             SP_CANVAS_ITEM_GET_CLASS (item)->update (item, child_affine, child_flags);
307     }
309     GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_UPDATE);
310     GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_AFFINE);
313 /**
314  * Helper function to invoke the point method of the item.
315  *
316  * The argument x, y should be in the parent's item-relative coordinate
317  * system.  This routine applies the inverse of the item's transform,
318  * maintaining the affine invariant.
319  */
320 static double
321 sp_canvas_item_invoke_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item)
323     if (SP_CANVAS_ITEM_GET_CLASS (item)->point)
324         return SP_CANVAS_ITEM_GET_CLASS (item)->point (item, p, actual_item);
326     return NR_HUGE;
329 /**
330  * Makes the item's affine transformation matrix be equal to the specified
331  * matrix.
332  *
333  * @item: A canvas item.
334  * @affine: An affine transformation matrix.
335  */
336 void
337 sp_canvas_item_affine_absolute (SPCanvasItem *item, Geom::Matrix const &affine)
339     item->xform = affine;
341     if (!(item->flags & SP_CANVAS_ITEM_NEED_AFFINE)) {
342         item->flags |= SP_CANVAS_ITEM_NEED_AFFINE;
343         if (item->parent != NULL) {
344             sp_canvas_item_request_update (item->parent);
345         } else {
346             sp_canvas_request_update (item->canvas);
347         }
348     }
350     item->canvas->need_repick = TRUE;
353 /**
354  * Convenience function to reorder items in a group's child list.
355  *
356  * This puts the specified link after the "before" link.
357  */
358 static void
359 put_item_after (GList *link, GList *before)
361     if (link == before)
362         return;
364     SPCanvasGroup *parent = SP_CANVAS_GROUP (SP_CANVAS_ITEM (link->data)->parent);
366     if (before == NULL) {
367         if (link == parent->items) return;
369         link->prev->next = link->next;
371         if (link->next) {
372             link->next->prev = link->prev;
373         } else {
374             parent->last = link->prev;
375         }
377         link->prev = before;
378         link->next = parent->items;
379         link->next->prev = link;
380         parent->items = link;
381     } else {
382         if ((link == parent->last) && (before == parent->last->prev))
383             return;
385         if (link->next)
386             link->next->prev = link->prev;
388         if (link->prev)
389             link->prev->next = link->next;
390         else {
391             parent->items = link->next;
392             parent->items->prev = NULL;
393         }
395         link->prev = before;
396         link->next = before->next;
398         link->prev->next = link;
400         if (link->next)
401             link->next->prev = link;
402         else
403             parent->last = link;
404     }
408 /**
409  * Raises the item in its parent's stack by the specified number of positions.
410  *
411  * \param item A canvas item.
412  * \param positions Number of steps to raise the item.
413  *
414  * If the number of positions is greater than the distance to the top of the
415  * stack, then the item is put at the top.
416  */
417 void
418 sp_canvas_item_raise (SPCanvasItem *item, int positions)
420     g_return_if_fail (item != NULL);
421     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
422     g_return_if_fail (positions >= 0);
424     if (!item->parent || positions == 0)
425         return;
427     SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
428     GList *link = g_list_find (parent->items, item);
429     g_assert (link != NULL);
431     GList *before;
432     for (before = link; positions && before; positions--)
433         before = before->next;
435     if (!before)
436         before = parent->last;
438     put_item_after (link, before);
440     redraw_if_visible (item);
441     item->canvas->need_repick = TRUE;
445 /**
446  * Lowers the item in its parent's stack by the specified number of positions.
447  *
448  * \param item A canvas item.
449  * \param positions Number of steps to lower the item.
450  *
451  * If the number of positions is greater than the distance to the bottom of the
452  * stack, then the item is put at the bottom.
453  **/
454 void
455 sp_canvas_item_lower (SPCanvasItem *item, int positions)
457     g_return_if_fail (item != NULL);
458     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
459     g_return_if_fail (positions >= 1);
461     if (!item->parent || positions == 0)
462         return;
464     SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
465     GList *link = g_list_find (parent->items, item);
466     g_assert (link != NULL);
468     GList *before;
469     if (link->prev)
470         for (before = link->prev; positions && before; positions--)
471             before = before->prev;
472     else
473         before = NULL;
475     put_item_after (link, before);
477     redraw_if_visible (item);
478     item->canvas->need_repick = TRUE;
481 /**
482  * Sets visible flag on item and requests a redraw.
483  */
484 void
485 sp_canvas_item_show (SPCanvasItem *item)
487     g_return_if_fail (item != NULL);
488     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
490     if (item->flags & SP_CANVAS_ITEM_VISIBLE)
491         return;
493     item->flags |= SP_CANVAS_ITEM_VISIBLE;
495     int x0 = (int)(item->x1);
496     int x1 = (int)(item->x2);
497     int y0 = (int)(item->y1);
498     int y1 = (int)(item->y2);
500     if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
501         sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
502         item->canvas->need_repick = TRUE;
503     }
506 /**
507  * Clears visible flag on item and requests a redraw.
508  */
509 void
510 sp_canvas_item_hide (SPCanvasItem *item)
512     g_return_if_fail (item != NULL);
513     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
515     if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
516         return;
518     item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
520     int x0 = (int)(item->x1);
521     int x1 = (int)(item->x2);
522     int y0 = (int)(item->y1);
523     int y1 = (int)(item->y2);
525     if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
526         sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
527         item->canvas->need_repick = TRUE;
528     }
531 /**
532  * Grab item under cursor.
533  *
534  * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE
535  */
536 int
537 sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
539     g_return_val_if_fail (item != NULL, -1);
540     g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1);
541     g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1);
543     if (item->canvas->grabbed_item)
544         return -1;
546     if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
547         return -1;
549     if (HAS_BROKEN_MOTION_HINTS) {
550         event_mask &= ~GDK_POINTER_MOTION_HINT_MASK;
551     }
553     /* fixme: Top hack (Lauris) */
554     /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
555     /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
556     gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
557                       (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
558                       NULL, cursor, etime);
560     item->canvas->grabbed_item = item;
561     item->canvas->grabbed_event_mask = event_mask;
562     item->canvas->current_item = item; /* So that events go to the grabbed item */
564     return 0;
567 /**
568  * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
569  * mouse.
570  *
571  * \param item A canvas item that holds a grab.
572  * \param etime The timestamp for ungrabbing the mouse.
573  */
574 void
575 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
577     g_return_if_fail (item != NULL);
578     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
580     if (item->canvas->grabbed_item != item)
581         return;
583     item->canvas->grabbed_item = NULL;
585     gdk_pointer_ungrab (etime);
588 /**
589  * Returns the product of all transformation matrices from the root item down
590  * to the item.
591  */
592 Geom::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
594     g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
596     Geom::Matrix affine = Geom::identity();
598     while (item) {
599         affine *= item->xform;
600         item = item->parent;
601     }
602     return affine;
605 /**
606  * Helper that returns true iff item is descendant of parent.
607  */
608 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
610     while (item) {
611         if (item == parent)
612             return true;
613         item = item->parent;
614     }
616     return false;
619 /**
620  * Focus canvas, and item under cursor if it is not already focussed.
621  */
622 void
623 sp_canvas_item_grab_focus (SPCanvasItem *item)
625     g_return_if_fail (item != NULL);
626     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
627     g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
629     SPCanvasItem *focused_item = item->canvas->focused_item;
631     if (focused_item) {
632         GdkEvent ev;
633         ev.focus_change.type = GDK_FOCUS_CHANGE;
634         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
635         ev.focus_change.send_event = FALSE;
636         ev.focus_change.in = FALSE;
638         emit_event (item->canvas, &ev);
639     }
641     item->canvas->focused_item = item;
642     gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
644     if (focused_item) {
645         GdkEvent ev;
646         ev.focus_change.type = GDK_FOCUS_CHANGE;
647         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
648         ev.focus_change.send_event = FALSE;
649         ev.focus_change.in = TRUE;
651         emit_event (item->canvas, &ev);
652     }
655 /**
656  * Requests that the canvas queue an update for the specified item.
657  *
658  * To be used only by item implementations.
659  */
660 void
661 sp_canvas_item_request_update (SPCanvasItem *item)
663     if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
664         return;
666     item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
668     if (item->parent != NULL) {
669         /* Recurse up the tree */
670         sp_canvas_item_request_update (item->parent);
671     } else {
672         /* Have reached the top of the tree, make sure the update call gets scheduled. */
673         sp_canvas_request_update (item->canvas);
674     }
677 /**
678  * Returns position of item in group.
679  */
680 gint sp_canvas_item_order (SPCanvasItem * item)
682     return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
685 /* SPCanvasGroup */
687 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
688 static void sp_canvas_group_init (SPCanvasGroup *group);
689 static void sp_canvas_group_destroy (GtkObject *object);
691 static void sp_canvas_group_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags);
692 static double sp_canvas_group_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item);
693 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
695 static SPCanvasItemClass *group_parent_class;
697 /**
698  * Registers SPCanvasGroup class with Gtk and returns its type number.
699  */
700 GType sp_canvas_group_get_type(void)
702     static GType type = 0;
703     if (!type) {
704         GTypeInfo info = {
705             sizeof(SPCanvasGroupClass),
706             0, // base_init
707             0, // base_finalize
708             (GClassInitFunc)sp_canvas_group_class_init,
709             0, // class_finalize
710             0, // class_data
711             sizeof(SPCanvasGroup),
712             0, // n_preallocs
713             (GInstanceInitFunc)sp_canvas_group_init,
714             0 // value_table
715         };
716         type = g_type_register_static(sp_canvas_item_get_type(), "SPCanvasGroup", &info, static_cast<GTypeFlags>(0));
717     }
718     return type;
721 /**
722  * Class initialization function for SPCanvasGroupClass
723  */
724 static void
725 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
727     GtkObjectClass *object_class = (GtkObjectClass *) klass;
728     SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
730     group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
732     object_class->destroy = sp_canvas_group_destroy;
734     item_class->update = sp_canvas_group_update;
735     item_class->render = sp_canvas_group_render;
736     item_class->point = sp_canvas_group_point;
739 /**
740  * Callback. Empty.
741  */
742 static void
743 sp_canvas_group_init (SPCanvasGroup */*group*/)
745     /* Nothing here */
748 /**
749  * Callback that destroys all items in group and calls group's virtual
750  * destroy() function.
751  */
752 static void
753 sp_canvas_group_destroy (GtkObject *object)
755     g_return_if_fail (object != NULL);
756     g_return_if_fail (SP_IS_CANVAS_GROUP (object));
758     SPCanvasGroup const *group = SP_CANVAS_GROUP (object);
760     GList *list = group->items;
761     while (list) {
762         SPCanvasItem *child = (SPCanvasItem *)list->data;
763         list = list->next;
765         gtk_object_destroy (GTK_OBJECT (child));
766     }
768     if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
769         (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
772 /**
773  * Update handler for canvas groups
774  */
775 static void
776 sp_canvas_group_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags)
778     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
779     Geom::RectHull corners(Geom::Point(0, 0));
780     bool empty=true;
782     for (GList *list = group->items; list; list = list->next) {
783         SPCanvasItem *i = (SPCanvasItem *)list->data;
785         sp_canvas_item_invoke_update (i, affine, flags);
787         if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
788             if (empty) {
789                 corners = Geom::RectHull(Geom::Point(i->x1, i->y1));
790                 empty = false;
791             } else {
792                 corners.add(Geom::Point(i->x1, i->y1));
793             }
794             corners.add(Geom::Point(i->x2, i->y2));
795         }
796     }
798     boost::optional<Geom::Rect> const bounds = corners.bounds();
799     if (bounds) {
800         item->x1 = bounds->min()[Geom::X];
801         item->y1 = bounds->min()[Geom::Y];
802         item->x2 = bounds->max()[Geom::X];
803         item->y2 = bounds->max()[Geom::Y];
804     } else {
805         // FIXME ?
806         item->x1 = item->x2 = item->y1 = item->y2 = 0;
807     }
810 /**
811  * Point handler for canvas groups.
812  */
813 static double
814 sp_canvas_group_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item)
816     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
817     double const x = p[Geom::X];
818     double const y = p[Geom::Y];
819     int x1 = (int)(x - item->canvas->close_enough);
820     int y1 = (int)(y - item->canvas->close_enough);
821     int x2 = (int)(x + item->canvas->close_enough);
822     int y2 = (int)(y + item->canvas->close_enough);
824     double best = 0.0;
825     *actual_item = NULL;
827     double dist = 0.0;
829     for (GList *list = group->items; list; list = list->next) {
830         SPCanvasItem *child = (SPCanvasItem *)list->data;
832         if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
833             SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
835             int has_point;
836             if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
837                 dist = sp_canvas_item_invoke_point (child, p, &point_item);
838                 has_point = TRUE;
839             } else
840                 has_point = FALSE;
842             if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
843                 best = dist;
844                 *actual_item = point_item;
845             }
846         }
847     }
849     return best;
852 /**
853  * Renders all visible canvas group items in buf rectangle.
854  */
855 static void
856 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
858     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
860     for (GList *list = group->items; list; list = list->next) {
861         SPCanvasItem *child = (SPCanvasItem *)list->data;
862         if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
863             if ((child->x1 < buf->rect.x1) &&
864                 (child->y1 < buf->rect.y1) &&
865                 (child->x2 > buf->rect.x0) &&
866                 (child->y2 > buf->rect.y0)) {
867                 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
868                     SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
869             }
870         }
871     }
874 /**
875  * Adds an item to a canvas group.
876  */
877 static void
878 group_add (SPCanvasGroup *group, SPCanvasItem *item)
880     gtk_object_ref (GTK_OBJECT (item));
881     gtk_object_sink (GTK_OBJECT (item));
883     if (!group->items) {
884         group->items = g_list_append (group->items, item);
885         group->last = group->items;
886     } else {
887         group->last = g_list_append (group->last, item)->next;
888     }
890     sp_canvas_item_request_update (item);
893 /**
894  * Removes an item from a canvas group
895  */
896 static void
897 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
899     g_return_if_fail (group != NULL);
900     g_return_if_fail (SP_IS_CANVAS_GROUP (group));
901     g_return_if_fail (item != NULL);
903     for (GList *children = group->items; children; children = children->next) {
904         if (children->data == item) {
906             /* Unparent the child */
908             item->parent = NULL;
909             gtk_object_unref (GTK_OBJECT (item));
911             /* Remove it from the list */
913             if (children == group->last) group->last = children->prev;
915             group->items = g_list_remove_link (group->items, children);
916             g_list_free (children);
917             break;
918         }
919     }
922 /* SPCanvas */
924 static void sp_canvas_class_init (SPCanvasClass *klass);
925 static void sp_canvas_init (SPCanvas *canvas);
926 static void sp_canvas_destroy (GtkObject *object);
928 static void sp_canvas_realize (GtkWidget *widget);
929 static void sp_canvas_unrealize (GtkWidget *widget);
931 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
932 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
934 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
935 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
936 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
937 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
938 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
939 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
940 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
941 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
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;
1055 /**
1056  * Convenience function to remove the idle handler of a canvas.
1057  */
1058 static void
1059 remove_idle (SPCanvas *canvas)
1061     if (canvas->idle_id) {
1062         gtk_idle_remove (canvas->idle_id);
1063         canvas->idle_id = 0;
1064     }
1067 /*
1068  * Removes the transient state of the canvas (idle handler, grabs).
1069  */
1070 static void
1071 shutdown_transients (SPCanvas *canvas)
1073     /* We turn off the need_redraw flag, since if the canvas is mapped again
1074      * it will request a redraw anyways.  We do not turn off the need_update
1075      * flag, though, because updates are not queued when the canvas remaps
1076      * itself.
1077      */
1078     if (canvas->need_redraw) {
1079         canvas->need_redraw = FALSE;
1080     }
1081     if ( canvas->tiles ) g_free(canvas->tiles);
1082     canvas->tiles=NULL;
1083     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1084     canvas->tileH=canvas->tileV=0;
1086     if (canvas->grabbed_item) {
1087         canvas->grabbed_item = NULL;
1088         gdk_pointer_ungrab (GDK_CURRENT_TIME);
1089     }
1091     remove_idle (canvas);
1094 /**
1095  * Destroy handler for SPCanvas.
1096  */
1097 static void
1098 sp_canvas_destroy (GtkObject *object)
1100     SPCanvas *canvas = SP_CANVAS (object);
1102     if (canvas->root) {
1103         gtk_object_unref (GTK_OBJECT (canvas->root));
1104         canvas->root = NULL;
1105     }
1107     shutdown_transients (canvas);
1109     if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1110         (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1113 static void track_latency(GdkEvent const *event) {
1114     GdkEventLatencyTracker &tracker = GdkEventLatencyTracker::default_tracker();
1115     boost::optional<double> latency = tracker.process(event);
1116     if (latency && *latency > 2.0) {
1117         //g_warning("Event latency reached %f sec (%1.4f)", *latency, tracker.getSkew());
1118     }
1121 /**
1122  * Returns new canvas as widget.
1123  */
1124 GtkWidget *
1125 sp_canvas_new_aa (void)
1127     SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1129     return (GtkWidget *) canvas;
1132 /**
1133  * The canvas widget's realize callback.
1134  */
1135 static void
1136 sp_canvas_realize (GtkWidget *widget)
1138     SPCanvas *canvas = SP_CANVAS (widget);
1140     GdkWindowAttr attributes;
1141     attributes.window_type = GDK_WINDOW_CHILD;
1142     attributes.x = widget->allocation.x;
1143     attributes.y = widget->allocation.y;
1144     attributes.width = widget->allocation.width;
1145     attributes.height = widget->allocation.height;
1146     attributes.wclass = GDK_INPUT_OUTPUT;
1147     attributes.visual = gdk_rgb_get_visual ();
1148     attributes.colormap = gdk_rgb_get_cmap ();
1149     attributes.event_mask = (gtk_widget_get_events (widget) |
1150                              GDK_EXPOSURE_MASK |
1151                              GDK_BUTTON_PRESS_MASK |
1152                              GDK_BUTTON_RELEASE_MASK |
1153                              GDK_POINTER_MOTION_MASK |
1154                              ( HAS_BROKEN_MOTION_HINTS ?
1155                                0 : GDK_POINTER_MOTION_HINT_MASK ) |
1156                              GDK_PROXIMITY_IN_MASK |
1157                              GDK_PROXIMITY_OUT_MASK |
1158                              GDK_KEY_PRESS_MASK |
1159                              GDK_KEY_RELEASE_MASK |
1160                              GDK_ENTER_NOTIFY_MASK |
1161                              GDK_LEAVE_NOTIFY_MASK |
1162                              GDK_FOCUS_CHANGE_MASK);
1163     gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1165     widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1166     gdk_window_set_user_data (widget->window, widget);
1168     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1169     if ( prefs->getBool("/options/useextinput/value", true) )
1170         gtk_widget_set_events(widget, attributes.event_mask);
1172     widget->style = gtk_style_attach (widget->style, widget->window);
1174     GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1176     canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1179 /**
1180  * The canvas widget's unrealize callback.
1181  */
1182 static void
1183 sp_canvas_unrealize (GtkWidget *widget)
1185     SPCanvas *canvas = SP_CANVAS (widget);
1187     canvas->current_item = NULL;
1188     canvas->grabbed_item = NULL;
1189     canvas->focused_item = NULL;
1191     shutdown_transients (canvas);
1193     gdk_gc_destroy (canvas->pixmap_gc);
1194     canvas->pixmap_gc = NULL;
1196     if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1197         (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1200 /**
1201  * The canvas widget's size_request callback.
1202  */
1203 static void
1204 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1206     static_cast<void>(SP_CANVAS (widget));
1208     req->width = 256;
1209     req->height = 256;
1212 /**
1213  * The canvas widget's size_allocate callback.
1214  */
1215 static void
1216 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1218     SPCanvas *canvas = SP_CANVAS (widget);
1220     /* Schedule redraw of new region */
1221     sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1222     if (allocation->width > widget->allocation.width) {
1223         sp_canvas_request_redraw (canvas,
1224                                   canvas->x0 + widget->allocation.width,
1225                                   0,
1226                                   canvas->x0 + allocation->width,
1227                                   canvas->y0 + allocation->height);
1228     }
1229     if (allocation->height > widget->allocation.height) {
1230         sp_canvas_request_redraw (canvas,
1231                                   0,
1232                                   canvas->y0 + widget->allocation.height,
1233                                   canvas->x0 + allocation->width,
1234                                   canvas->y0 + allocation->height);
1235     }
1237     widget->allocation = *allocation;
1238     
1239     if (GTK_WIDGET_REALIZED (widget)) {
1240         gdk_window_move_resize (widget->window,
1241                                 widget->allocation.x, widget->allocation.y,
1242                                 widget->allocation.width, widget->allocation.height);
1243     }
1246 /**
1247  * Helper that emits an event for an item in the canvas, be it the current
1248  * item, grabbed item, or focused item, as appropriate.
1249  */
1250 static int
1251 emit_event (SPCanvas *canvas, GdkEvent *event)
1253     guint mask;
1255     if (canvas->grabbed_item) {
1256         switch (event->type) {
1257         case GDK_ENTER_NOTIFY:
1258             mask = GDK_ENTER_NOTIFY_MASK;
1259             break;
1260         case GDK_LEAVE_NOTIFY:
1261             mask = GDK_LEAVE_NOTIFY_MASK;
1262             break;
1263         case GDK_MOTION_NOTIFY:
1264             mask = GDK_POINTER_MOTION_MASK;
1265             break;
1266         case GDK_BUTTON_PRESS:
1267         case GDK_2BUTTON_PRESS:
1268         case GDK_3BUTTON_PRESS:
1269             mask = GDK_BUTTON_PRESS_MASK;
1270             break;
1271         case GDK_BUTTON_RELEASE:
1272             mask = GDK_BUTTON_RELEASE_MASK;
1273             break;
1274         case GDK_KEY_PRESS:
1275             mask = GDK_KEY_PRESS_MASK;
1276             break;
1277         case GDK_KEY_RELEASE:
1278             mask = GDK_KEY_RELEASE_MASK;
1279             break;
1280         case GDK_SCROLL:
1281             mask = GDK_SCROLL;
1282             break;
1283         default:
1284             mask = 0;
1285             break;
1286         }
1288         if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1289     }
1291     /* Convert to world coordinates -- we have two cases because of diferent
1292      * offsets of the fields in the event structures.
1293      */
1295     GdkEvent ev = *event;
1297     switch (ev.type) {
1298     case GDK_ENTER_NOTIFY:
1299     case GDK_LEAVE_NOTIFY:
1300         ev.crossing.x += canvas->x0;
1301         ev.crossing.y += canvas->y0;
1302         break;
1303     case GDK_MOTION_NOTIFY:
1304     case GDK_BUTTON_PRESS:
1305     case GDK_2BUTTON_PRESS:
1306     case GDK_3BUTTON_PRESS:
1307     case GDK_BUTTON_RELEASE:
1308         ev.motion.x += canvas->x0;
1309         ev.motion.y += canvas->y0;
1310         break;
1311     default:
1312         break;
1313     }
1315     /* Choose where we send the event */
1317     /* canvas->current_item becomes NULL in some cases under Win32
1318     ** (e.g. if the pointer leaves the window).  So this is a hack that
1319     ** Lauris applied to SP to get around the problem.
1320     */
1321     SPCanvasItem* item = NULL;
1322     if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1323         item = canvas->grabbed_item;
1324     } else {
1325         item = canvas->current_item;
1326     }
1328     if (canvas->focused_item &&
1329         ((event->type == GDK_KEY_PRESS) ||
1330          (event->type == GDK_KEY_RELEASE) ||
1331          (event->type == GDK_FOCUS_CHANGE))) {
1332         item = canvas->focused_item;
1333     }
1335     /* The event is propagated up the hierarchy (for if someone connected to
1336      * a group instead of a leaf event), and emission is stopped if a
1337      * handler returns TRUE, just like for GtkWidget events.
1338      */
1340     gint finished = FALSE;
1342     while (item && !finished) {
1343         gtk_object_ref (GTK_OBJECT (item));
1344         gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1345         SPCanvasItem *parent = item->parent;
1346         gtk_object_unref (GTK_OBJECT (item));
1347         item = parent;
1348     }
1350     return finished;
1353 /**
1354  * Helper that re-picks the current item in the canvas, based on the event's
1355  * coordinates and emits enter/leave events for items as appropriate.
1356  */
1357 static int
1358 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1360     int button_down = 0;
1361     double x, y;
1363     if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1364         return FALSE;
1366     int retval = FALSE;
1368     if (canvas->gen_all_enter_events == false) {
1369         // If a button is down, we'll perform enter and leave events on the
1370         // current item, but not enter on any other item.  This is more or
1371         // less like X pointer grabbing for canvas items.
1372         //
1373         button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1374                 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1376         if (!button_down) canvas->left_grabbed_item = FALSE;
1377     }
1379     /* Save the event in the canvas.  This is used to synthesize enter and
1380      * leave events in case the current item changes.  It is also used to
1381      * re-pick the current item if the current one gets deleted.  Also,
1382      * synthesize an enter event.
1383      */
1384     if (event != &canvas->pick_event) {
1385         if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1386             /* these fields have the same offsets in both types of events */
1388             canvas->pick_event.crossing.type       = GDK_ENTER_NOTIFY;
1389             canvas->pick_event.crossing.window     = event->motion.window;
1390             canvas->pick_event.crossing.send_event = event->motion.send_event;
1391             canvas->pick_event.crossing.subwindow  = NULL;
1392             canvas->pick_event.crossing.x          = event->motion.x;
1393             canvas->pick_event.crossing.y          = event->motion.y;
1394             canvas->pick_event.crossing.mode       = GDK_CROSSING_NORMAL;
1395             canvas->pick_event.crossing.detail     = GDK_NOTIFY_NONLINEAR;
1396             canvas->pick_event.crossing.focus      = FALSE;
1397             canvas->pick_event.crossing.state      = event->motion.state;
1399             /* these fields don't have the same offsets in both types of events */
1401             if (event->type == GDK_MOTION_NOTIFY) {
1402                 canvas->pick_event.crossing.x_root = event->motion.x_root;
1403                 canvas->pick_event.crossing.y_root = event->motion.y_root;
1404             } else {
1405                 canvas->pick_event.crossing.x_root = event->button.x_root;
1406                 canvas->pick_event.crossing.y_root = event->button.y_root;
1407             }
1408         } else {
1409             canvas->pick_event = *event;
1410         }
1411     }
1413     /* Don't do anything else if this is a recursive call */
1414     if (canvas->in_repick) return retval;
1416     /* LeaveNotify means that there is no current item, so we don't look for one */
1417     if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1418         /* these fields don't have the same offsets in both types of events */
1420         if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1421             x = canvas->pick_event.crossing.x;
1422             y = canvas->pick_event.crossing.y;
1423         } else {
1424             x = canvas->pick_event.motion.x;
1425             y = canvas->pick_event.motion.y;
1426         }
1428         /* world coords */
1429         x += canvas->x0;
1430         y += canvas->y0;
1432         /* find the closest item */
1433         if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1434             sp_canvas_item_invoke_point (canvas->root, Geom::Point(x, y), &canvas->new_current_item);
1435         } else {
1436             canvas->new_current_item = NULL;
1437         }
1438     } else {
1439         canvas->new_current_item = NULL;
1440     }
1442     if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1443         return retval; /* current item did not change */
1444     }
1446     /* Synthesize events for old and new current items */
1448     if ((canvas->new_current_item != canvas->current_item)
1449         && (canvas->current_item != NULL)
1450         && !canvas->left_grabbed_item) {
1451         GdkEvent new_event;
1452         SPCanvasItem *item;
1454         item = canvas->current_item;
1456         new_event = canvas->pick_event;
1457         new_event.type = GDK_LEAVE_NOTIFY;
1459         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1460         new_event.crossing.subwindow = NULL;
1461         canvas->in_repick = TRUE;
1462         retval = emit_event (canvas, &new_event);
1463         canvas->in_repick = FALSE;
1464     }
1466     if (canvas->gen_all_enter_events == false) {
1467         // new_current_item may have been set to NULL during the call to
1468         // emit_event() above
1469         if ((canvas->new_current_item != canvas->current_item) && button_down) {
1470             canvas->left_grabbed_item = TRUE;
1471             return retval;
1472         }
1473     }
1475     /* Handle the rest of cases */
1477     canvas->left_grabbed_item = FALSE;
1478     canvas->current_item = canvas->new_current_item;
1480     if (canvas->current_item != NULL) {
1481         GdkEvent new_event;
1483         new_event = canvas->pick_event;
1484         new_event.type = GDK_ENTER_NOTIFY;
1485         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1486         new_event.crossing.subwindow = NULL;
1487         retval = emit_event (canvas, &new_event);
1488     }
1490     return retval;
1493 /**
1494  * Button event handler for the canvas.
1495  */
1496 static gint
1497 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1499     SPCanvas *canvas = SP_CANVAS (widget);
1500     SPDesktop *dt = SP_ACTIVE_DESKTOP;
1502     int retval = FALSE;
1504     /* dispatch normally regardless of the event's window if an item has
1505        has a pointer grab in effect */
1506     if (!canvas->grabbed_item &&
1507         event->window != SP_CANVAS_WINDOW (canvas))
1508         return retval;
1510     int mask;
1511     switch (event->button) {
1512     case 1:
1513         mask = GDK_BUTTON1_MASK;
1514         break;
1515     case 2:
1516         mask = GDK_BUTTON2_MASK;
1517         break;
1518     case 3:
1519         mask = GDK_BUTTON3_MASK;
1520         break;
1521     case 4:
1522         mask = GDK_BUTTON4_MASK;
1523         break;
1524     case 5:
1525         mask = GDK_BUTTON5_MASK;
1526         break;
1527     default:
1528         mask = 0;
1529     }
1531     switch (event->type) {
1532     case GDK_BUTTON_PRESS:
1533     case GDK_2BUTTON_PRESS:
1534     case GDK_3BUTTON_PRESS:
1535         if (dt) { 
1536                         // Snapping will be on hold if we're moving the mouse at high speeds. When starting
1537                 // drawing a new shape we really should snap though. 
1538                 dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false);
1539                 }
1540         
1541         /* Pick the current item as if the button were not pressed, and
1542          * then process the event.
1543          */
1544         canvas->state = event->state;
1545         pick_current_item (canvas, (GdkEvent *) event);
1546         canvas->state ^= mask;
1547         retval = emit_event (canvas, (GdkEvent *) event);
1548         break;
1550     case GDK_BUTTON_RELEASE:
1551         sp_canvas_snap_watchdog_callback(canvas); // If we have any pending snapping action, then invoke it now 
1552          
1553         /* Process the event as if the button were pressed, then repick
1554          * after the button has been released
1555          */
1556         canvas->state = event->state;
1557         retval = emit_event (canvas, (GdkEvent *) event);
1558         event->state ^= mask;
1559         canvas->state = event->state;
1560         pick_current_item (canvas, (GdkEvent *) event);
1561         event->state ^= mask;
1562         
1563         break;
1565     default:
1566         g_assert_not_reached ();
1567     }
1569     return retval;
1572 /**
1573  * Scroll event handler for the canvas.
1574  *
1575  * \todo FIXME: generate motion events to re-select items.
1576  */
1577 static gint
1578 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1580     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1583 static inline void request_motions(GdkWindow *w, GdkEventMotion *event) {
1584     gdk_window_get_pointer(w, NULL, NULL, NULL);
1585 #if HAS_GDK_EVENT_REQUEST_MOTIONS
1586     gdk_event_request_motions(event);
1587 #endif
1590 /**
1591  * Motion event handler for the canvas.
1592  */
1593 static int
1594 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1596         static guint32 prev_time;
1597         static boost::optional<Geom::Point> prev_pos;
1598         
1599         int status;
1600     SPCanvas *canvas = SP_CANVAS (widget);
1602     track_latency((GdkEvent *)event);
1604     if (event->window != SP_CANVAS_WINDOW (canvas))
1605         return FALSE;
1607     if (canvas->pixmap_gc == NULL) // canvas being deleted
1608         return FALSE;
1609     
1610     SPDesktop *dt = SP_ACTIVE_DESKTOP;
1611     
1612     // Snap when speed drops below e.g. 0.02 px/msec, or when no motion events have occured for some period.
1613         // i.e. snap when we're at stand still. A speed threshold enforces snapping for tablets, which might never
1614         // be fully at stand still and might keep spitting out motion events.
1615     
1616     if (event->type == GDK_MOTION_NOTIFY) {     
1617         Geom::Point event_pos(event->x, event->y);
1618         guint32 event_t = gdk_event_get_time ( (GdkEvent *) event );
1619                 
1620         if (dt) { // put snapping on hold
1621                         dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(true);
1622                 }                               
1623         
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                 // std::cout << "speed = " << speed << " px/msec " << "| time passed = " << delta_t << " msec" << std::endl;
1629                 if (speed > 0.02) { // Jitter threshold, might be needed for tablets 
1630                                 // We're moving fast, so postpone any snapping until the next GDK_MOTION_NOTIFY event. We
1631                         // will keep on postponing the snapping as long as the speed is high.
1632                                 // We must snap at some point in time though, so set a watchdog timer at some time from
1633                                 // now, just in case there's no future motion event that drops under the speed limit (when 
1634                         // stoppping abruptly)
1635                         sp_canvas_snap_watchdog_kill(canvas);
1636                         sp_canvas_snap_watchdog_set(canvas, event); // watchdog is reset, i.e. pushed forward in time
1637                         } else { // Speed is very low, so we're virtually at stand still
1638                                 // But if we're really standing still, then we should snap now. We could use some low-pass filtering, 
1639                                 // otherwise snapping occurs for each jitter movement. For this filtering we'll leave the watchdog to expire,
1640                                 // snap, and set a new watchdog again. 
1641                                 if (canvas->watchdog_id == 0) { // no watchdog has been set 
1642                                         // it might have already expired, so we'll set a new one; the snapping frequency will be limited by this
1643                                         sp_canvas_snap_watchdog_set(canvas, event);
1644                                 } // else: watchdog has been set before and we'll wait for it to expire
1645                         }
1646                 } else {
1647                         // This is the first GDK_MOTION_NOTIFY event, so postpone snapping and set the watchdog
1648                         sp_canvas_snap_watchdog_set(canvas, event);
1649                 }
1650                         
1651         prev_pos = event_pos;
1652         prev_time = event_t;
1653     }
1654     
1655     canvas->state = event->state;
1656     pick_current_item (canvas, (GdkEvent *) event);
1658     status = emit_event (canvas, (GdkEvent *) event);
1660     if (event->is_hint) {
1661         request_motions(widget->window, event);
1662     }
1664     return status;
1667 gboolean sp_canvas_snap_watchdog_callback(gpointer data) 
1669         // Snap NOW! For this the "postponed" flag will be reset and an the last motion event will be repeated  
1670         SPCanvas *canvas = reinterpret_cast<SPCanvas *>(data);
1671         if (!canvas->watchdog_event) {
1672                 return FALSE;
1673         }       
1674         
1675         SPDesktop *dt = SP_ACTIVE_DESKTOP;
1676         if (dt) {
1677                 dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false);
1678         }
1679         
1680         emit_event(canvas, canvas->watchdog_event);
1681         gdk_event_free(canvas->watchdog_event);
1682         canvas->watchdog_event = NULL;
1683         canvas->watchdog_id = 0;
1684     
1685         return FALSE;
1688 void sp_canvas_snap_watchdog_set(SPCanvas *canvas, GdkEventMotion *event) 
1690         g_assert(canvas->watchdog_id == 0);
1691         canvas->watchdog_id = g_timeout_add(400, &sp_canvas_snap_watchdog_callback, canvas);
1692         g_assert(canvas->watchdog_event == NULL);
1693         canvas->watchdog_event = gdk_event_copy( (GdkEvent *) event); 
1696 void sp_canvas_snap_watchdog_kill(SPCanvas *canvas) 
1698         if (canvas->watchdog_id) { 
1699         g_source_remove(canvas->watchdog_id); // Kill the watchdog
1700         canvas->watchdog_id = 0;
1701     }
1702         
1703         if (canvas->watchdog_event) {
1704                 gdk_event_free(canvas->watchdog_event);
1705                 canvas->watchdog_event = NULL;
1706         }
1708         
1709 static void
1710 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)
1712     GtkWidget *widget = GTK_WIDGET (canvas);
1714     SPCanvasBuf buf;
1715     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1716         buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1717     } else {
1718         buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1719     }
1721     // Mark the region clean
1722     sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1724     buf.buf_rowstride = sw * 4; 
1725     buf.rect.x0 = x0;
1726     buf.rect.y0 = y0;
1727     buf.rect.x1 = x1;
1728     buf.rect.y1 = y1;
1729     buf.visible_rect.x0 = draw_x1;
1730     buf.visible_rect.y0 = draw_y1;
1731     buf.visible_rect.x1 = draw_x2;
1732     buf.visible_rect.y1 = draw_y2;
1733     GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1734     buf.bg_color = (((color->red & 0xff00) << 8)
1735                     | (color->green & 0xff00)
1736                     | (color->blue >> 8));
1737     buf.is_empty = true;
1739     buf.ct = nr_create_cairo_context_canvasbuf (&(buf.visible_rect), &buf);
1741     if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1742         SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1743     }
1745 #if ENABLE_LCMS
1746     cmsHTRANSFORM transf = 0;
1747     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1748     bool fromDisplay = prefs->getBool( "/options/displayprofile/from_display");
1749     if ( fromDisplay ) {
1750         transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" );
1751     } else {
1752         transf = Inkscape::colorprofile_get_display_transform();
1753     }
1754 #endif // ENABLE_LCMS
1756     if (buf.is_empty) {
1757 #if ENABLE_LCMS
1758         if ( transf && canvas->enable_cms_display_adj ) {
1759             cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1760         }
1761 #endif // ENABLE_LCMS
1762         gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1763         gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1764                             canvas->pixmap_gc,
1765                             TRUE,
1766                             x0 - canvas->x0, y0 - canvas->y0,
1767                             x1 - x0, y1 - y0);
1768     } else {
1770 #if ENABLE_LCMS
1771         if ( transf && canvas->enable_cms_display_adj ) {
1772             for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1773                 guchar* p = buf.buf + (sw * 3) * yy;
1774                 cmsDoTransform( transf, p, p, (x1 - x0) );
1775             }
1776         }
1777 #endif // ENABLE_LCMS
1779 // Now we only need to output the prepared pixmap to the actual screen, and this define chooses one
1780 // of the two ways to do it. The cairo way is direct and straightforward, but unfortunately
1781 // noticeably slower. I asked Carl Worth but he was unable so far to suggest any specific reason
1782 // for this slowness. So, for now we use the oldish method: squeeze out 32bpp buffer to 24bpp and
1783 // use gdk_draw_rgb_image_dithalign, for unfortunately gdk can only handle 24 bpp, which cairo
1784 // cannot handle at all. Still, this way is currently faster even despite the blit with squeeze.
1786 ///#define CANVAS_OUTPUT_VIA_CAIRO
1788 #ifdef CANVAS_OUTPUT_VIA_CAIRO
1790         buf.cst = cairo_image_surface_create_for_data (
1791             buf.buf,
1792             CAIRO_FORMAT_ARGB32,  // unpacked, i.e. 32 bits! one byte is unused
1793             x1 - x0, y1 - y0,
1794             buf.buf_rowstride
1795             );
1796         cairo_t *window_ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1797         cairo_set_source_surface (window_ct, buf.cst, x0 - canvas->x0, y0 - canvas->y0);
1798         cairo_paint (window_ct);
1799         cairo_destroy (window_ct);
1800         cairo_surface_finish (buf.cst);
1801         cairo_surface_destroy (buf.cst);
1803 #else
1805         NRPixBlock b3;
1806         nr_pixblock_setup_fast (&b3, NR_PIXBLOCK_MODE_R8G8B8, x0, y0, x1, y1, TRUE);
1808         NRPixBlock b4;
1809         nr_pixblock_setup_extern (&b4, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1,
1810                                   buf.buf,
1811                                   buf.buf_rowstride,
1812                                   FALSE, FALSE);
1814         // this does the 32->24 squishing, using an assembler routine:
1815         nr_blit_pixblock_pixblock (&b3, &b4);
1817         gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1818                                       canvas->pixmap_gc,
1819                                       x0 - canvas->x0, y0 - canvas->y0,
1820                                       x1 - x0, y1 - y0,
1821                                       GDK_RGB_DITHER_MAX,
1822                                       NR_PIXBLOCK_PX(&b3),
1823                                       sw * 3,
1824                                       x0 - canvas->x0, y0 - canvas->y0);
1826         nr_pixblock_release (&b3);
1827         nr_pixblock_release (&b4);
1828 #endif
1829     }
1831     cairo_surface_t *cst = cairo_get_target(buf.ct);
1832     cairo_destroy (buf.ct);
1833     cairo_surface_finish (cst);
1834     cairo_surface_destroy (cst);
1836     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1837         nr_pixelstore_256K_free (buf.buf);
1838     } else {
1839         nr_pixelstore_1M_free (buf.buf);
1840     }
1843 struct PaintRectSetup {
1844     SPCanvas* canvas;
1845     NRRectL big_rect;
1846     GTimeVal start_time;
1847     int max_pixels;
1848     Geom::Point mouse_loc;
1849 };
1851 /**
1852  * Paint the given rect, recursively subdividing the region until it is the size of a single
1853  * buffer.
1854  *
1855  * @return true if the drawing completes
1856  */
1857 static int
1858 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1860     GTimeVal now;
1861     g_get_current_time (&now);
1863     glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1864         + (now.tv_usec - setup->start_time.tv_usec);
1866     // Allow only very fast buffers to be run together;
1867     // as soon as the total redraw time exceeds 1ms, cancel;
1868     // this returns control to the idle loop and allows Inkscape to process user input
1869     // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1870     // it will get back and finish painting what remains to paint.
1871     if (elapsed > 1000) {
1873         // Interrupting redraw isn't always good.
1874         // For example, when you drag one node of a big path, only the buffer containing
1875         // the mouse cursor will be redrawn again and again, and the rest of the path
1876         // will remain stale because Inkscape never has enough idle time to redraw all
1877         // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1878         // If this limit is set, and if we have aborted redraw more times than is allowed,
1879         // interrupting is blocked and we're forced to redraw full screen once
1880         // (after which we can again interrupt forced_redraw_limit times).
1881         if (setup->canvas->forced_redraw_limit < 0 ||
1882             setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1884             if (setup->canvas->forced_redraw_limit != -1) {
1885                 setup->canvas->forced_redraw_count++;
1886             }
1888             return false;
1889         }
1890     }
1892     // Find the optimal buffer dimensions
1893     int bw = this_rect.x1 - this_rect.x0;
1894     int bh = this_rect.y1 - this_rect.y0;
1895     if ((bw < 1) || (bh < 1))
1896         return 0;
1898     if (bw * bh < setup->max_pixels) {
1899         // We are small enough
1900         sp_canvas_paint_single_buffer (setup->canvas,
1901                                        this_rect.x0, this_rect.y0,
1902                                        this_rect.x1, this_rect.y1,
1903                                        setup->big_rect.x0, setup->big_rect.y0,
1904                                        setup->big_rect.x1, setup->big_rect.y1, bw);
1905         return 1;
1906     }
1908     NRRectL lo = this_rect;
1909     NRRectL hi = this_rect;
1911 /*
1912 This test determines the redraw strategy:
1914 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1915 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1916 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1917 and seems to be faster for drawings with many smaller objects at zoom-out.
1919 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1920 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1921 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1922 faster.
1924 The default for now is the strips mode.
1925 */
1926     if (bw < bh || bh < 2 * TILE_SIZE) {
1927         // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1928         int mid = ((long long) this_rect.x0 + (long long) this_rect.x1) / 2;
1929         // Make sure that mid lies on a tile boundary
1930         mid = (mid / TILE_SIZE) * TILE_SIZE;
1932         lo.x1 = mid;
1933         hi.x0 = mid;
1935         if (setup->mouse_loc[Geom::X] < mid) {
1936             // Always paint towards the mouse first
1937             return sp_canvas_paint_rect_internal(setup, lo)
1938                 && sp_canvas_paint_rect_internal(setup, hi);
1939         } else {
1940             return sp_canvas_paint_rect_internal(setup, hi)
1941                 && sp_canvas_paint_rect_internal(setup, lo);
1942         }
1943     } else {
1944         // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1945         int mid = ((long long) this_rect.y0 + (long long) this_rect.y1) / 2;
1946         // Make sure that mid lies on a tile boundary
1947         mid = (mid / TILE_SIZE) * TILE_SIZE;
1949         lo.y1 = mid;
1950         hi.y0 = mid;
1952         if (setup->mouse_loc[Geom::Y] < mid) {
1953             // Always paint towards the mouse first
1954             return sp_canvas_paint_rect_internal(setup, lo)
1955                 && sp_canvas_paint_rect_internal(setup, hi);
1956         } else {
1957             return sp_canvas_paint_rect_internal(setup, hi)
1958                 && sp_canvas_paint_rect_internal(setup, lo);
1959         }
1960     }
1964 /**
1965  * Helper that draws a specific rectangular part of the canvas.
1966  *
1967  * @return true if the rectangle painting succeeds.
1968  */
1969 static bool
1970 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1972     g_return_val_if_fail (!canvas->need_update, false);
1974     NRRectL rect;
1975     rect.x0 = xx0;
1976     rect.x1 = xx1;
1977     rect.y0 = yy0;
1978     rect.y1 = yy1;
1980     // Clip rect-to-draw by the current visible area
1981     rect.x0 = MAX (rect.x0, canvas->x0);
1982     rect.y0 = MAX (rect.y0, canvas->y0);
1983     rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1984     rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1986 #ifdef DEBUG_REDRAW
1987     // paint the area to redraw yellow
1988     gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1989     gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1990                         canvas->pixmap_gc,
1991                         TRUE,
1992                         rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1993                         rect.x1 - rect.x0, rect.y1 - rect.y0);
1994 #endif
1996     PaintRectSetup setup;
1998     setup.canvas = canvas;
1999     setup.big_rect = rect;
2001     // Save the mouse location
2002     gint x, y;
2003     gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
2004     setup.mouse_loc = sp_canvas_window_to_world (canvas, Geom::Point(x,y));
2006     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
2007         // use 256K as a compromise to not slow down gradients
2008         // 256K is the cached buffer and we need 4 channels
2009         setup.max_pixels = 65536; // 256K/4
2010     } else {
2011         // paths only, so 1M works faster
2012         // 1M is the cached buffer and we need 4 channels
2013         setup.max_pixels = 262144; 
2014     }
2016     // Start the clock
2017     g_get_current_time(&(setup.start_time));
2019     // Go
2020     return sp_canvas_paint_rect_internal(&setup, rect);
2023 /**
2024  * Force a full redraw after a specified number of interrupted redraws
2025  */
2026 void
2027 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
2028   g_return_if_fail(canvas != NULL);
2030   canvas->forced_redraw_limit = count;
2031   canvas->forced_redraw_count = 0;
2034 /**
2035  * End forced full redraw requests
2036  */
2037 void
2038 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
2039   g_return_if_fail(canvas != NULL);
2041   canvas->forced_redraw_limit = -1;
2044 /**
2045  * The canvas widget's expose callback.
2046  */
2047 static gint
2048 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
2050     SPCanvas *canvas = SP_CANVAS (widget);
2052     if (!GTK_WIDGET_DRAWABLE (widget) ||
2053         (event->window != SP_CANVAS_WINDOW (canvas)))
2054         return FALSE;
2056     int n_rects;
2057     GdkRectangle *rects;
2058     gdk_region_get_rectangles (event->region, &rects, &n_rects);
2060     for (int i = 0; i < n_rects; i++) {
2061         NRRectL rect;
2063         rect.x0 = rects[i].x + canvas->x0;
2064         rect.y0 = rects[i].y + canvas->y0;
2065         rect.x1 = rect.x0 + rects[i].width;
2066         rect.y1 = rect.y0 + rects[i].height;
2068         sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
2069     }
2071     if (n_rects > 0)
2072         g_free (rects);
2074     return FALSE;
2077 /**
2078  * The canvas widget's keypress callback.
2079  */
2080 static gint
2081 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
2083     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
2086 /**
2087  * Crossing event handler for the canvas.
2088  */
2089 static gint
2090 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
2092     SPCanvas *canvas = SP_CANVAS (widget);
2094     if (event->window != SP_CANVAS_WINDOW (canvas))
2095         return FALSE;
2097     canvas->state = event->state;
2098     return pick_current_item (canvas, (GdkEvent *) event);
2101 /**
2102  * Focus in handler for the canvas.
2103  */
2104 static gint
2105 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
2107     GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
2109     SPCanvas *canvas = SP_CANVAS (widget);
2111     if (canvas->focused_item) {
2112         return emit_event (canvas, (GdkEvent *) event);
2113     } else {
2114         return FALSE;
2115     }
2118 /**
2119  * Focus out handler for the canvas.
2120  */
2121 static gint
2122 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
2124     GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2126     SPCanvas *canvas = SP_CANVAS (widget);
2128     if (canvas->focused_item)
2129         return emit_event (canvas, (GdkEvent *) event);
2130     else
2131         return FALSE;
2134 /**
2135  * Helper that repaints the areas in the canvas that need it.
2136  *
2137  * @return true if all the dirty parts have been redrawn
2138  */
2139 static int
2140 paint (SPCanvas *canvas)
2142     if (canvas->need_update) {
2143         sp_canvas_item_invoke_update (canvas->root, Geom::identity(), 0);
2144         canvas->need_update = FALSE;
2145     }
2147     if (!canvas->need_redraw)
2148         return TRUE;
2150     Gdk::Region to_paint;
2152     for (int j=canvas->tTop; j<canvas->tBottom; j++) {
2153         for (int i=canvas->tLeft; i<canvas->tRight; i++) {
2154             int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
2156             if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
2157                 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
2158                                    TILE_SIZE, TILE_SIZE));
2159             }
2161         }
2162     }
2164     if (!to_paint.empty()) {
2165         Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
2166         typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
2167         for (Iter i=rect.begin(); i != rect.end(); ++i) {
2168             int x0 = (*i).get_x();
2169             int y0 = (*i).get_y();
2170             int x1 = x0 + (*i).get_width();
2171             int y1 = y0 + (*i).get_height();
2172             if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
2173                 // Aborted
2174                 return FALSE;
2175             };
2176         }
2177     }
2179     canvas->need_redraw = FALSE;
2181     // we've had a full unaborted redraw, reset the full redraw counter
2182     if (canvas->forced_redraw_limit != -1) {
2183         canvas->forced_redraw_count = 0;
2184     }
2186     return TRUE;
2189 /**
2190  * Helper that invokes update, paint, and repick on canvas.
2191  */
2192 static int
2193 do_update (SPCanvas *canvas)
2195     if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop durring interrupted display!
2196         return TRUE;
2198     /* Cause the update if necessary */
2199     if (canvas->need_update) {
2200         sp_canvas_item_invoke_update (canvas->root, Geom::identity(), 0);
2201         canvas->need_update = FALSE;
2202     }
2204     /* Paint if able to */
2205     if (GTK_WIDGET_DRAWABLE (canvas)) {
2206             return paint (canvas);
2207     }
2209     /* Pick new current item */
2210     while (canvas->need_repick) {
2211         canvas->need_repick = FALSE;
2212         pick_current_item (canvas, &canvas->pick_event);
2213     }
2215     return TRUE;
2218 /**
2219  * Idle handler for the canvas that deals with pending updates and redraws.
2220  */
2221 static gint
2222 idle_handler (gpointer data)
2224     GDK_THREADS_ENTER ();
2226     SPCanvas *canvas = SP_CANVAS (data);
2228     int const ret = do_update (canvas);
2230     if (ret) {
2231         /* Reset idle id */
2232         canvas->idle_id = 0;
2233     }
2235     GDK_THREADS_LEAVE ();
2237     return !ret;
2240 /**
2241  * Convenience function to add an idle handler to a canvas.
2242  */
2243 static void
2244 add_idle (SPCanvas *canvas)
2246     if (canvas->idle_id != 0)
2247         return;
2249     canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2252 /**
2253  * Returns the root group of the specified canvas.
2254  */
2255 SPCanvasGroup *
2256 sp_canvas_root (SPCanvas *canvas)
2258     g_return_val_if_fail (canvas != NULL, NULL);
2259     g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2261     return SP_CANVAS_GROUP (canvas->root);
2264 /**
2265  * Scrolls canvas to specific position (cx and cy are measured in screen pixels)
2266  */
2267 void
2268 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2270     g_return_if_fail (canvas != NULL);
2271     g_return_if_fail (SP_IS_CANVAS (canvas));
2273     int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels)
2274     int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do!
2275     int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the 
2276     int dy = iy - canvas->y0; // canvas w.r.t its previous position
2278     canvas->dx0 = cx; // here the 'd' stands for double, not delta!
2279     canvas->dy0 = cy;
2280     canvas->x0 = ix;
2281     canvas->y0 = iy;
2283     sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2285     if (!clear) {
2286         // scrolling without zoom; redraw only the newly exposed areas
2287         if ((dx != 0) || (dy != 0)) {
2288             canvas->is_scrolling = is_scrolling;
2289             if (GTK_WIDGET_REALIZED (canvas)) {
2290                 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2291             }
2292         }
2293     } else {
2294         // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2295     }
2298 /**
2299  * Updates canvas if necessary.
2300  */
2301 void
2302 sp_canvas_update_now (SPCanvas *canvas)
2304     g_return_if_fail (canvas != NULL);
2305     g_return_if_fail (SP_IS_CANVAS (canvas));
2307     if (!(canvas->need_update ||
2308           canvas->need_redraw))
2309         return;
2311     do_update (canvas);
2314 /**
2315  * Update callback for canvas widget.
2316  */
2317 static void
2318 sp_canvas_request_update (SPCanvas *canvas)
2320     canvas->need_update = TRUE;
2321     add_idle (canvas);
2324 /**
2325  * Forces redraw of rectangular canvas area.
2326  */
2327 void
2328 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2330     NRRectL bbox;
2331     NRRectL visible;
2332     NRRectL clip;
2334     g_return_if_fail (canvas != NULL);
2335     g_return_if_fail (SP_IS_CANVAS (canvas));
2337     if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2338     if ((x0 >= x1) || (y0 >= y1)) return;
2340     bbox.x0 = x0;
2341     bbox.y0 = y0;
2342     bbox.x1 = x1;
2343     bbox.y1 = y1;
2345     visible.x0 = canvas->x0;
2346     visible.y0 = canvas->y0;
2347     visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2348     visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2350     nr_rect_l_intersect (&clip, &bbox, &visible);
2352     sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2353     add_idle (canvas);
2356 /**
2357  * Sets world coordinates from win and canvas.
2358  */
2359 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2361     g_return_if_fail (canvas != NULL);
2362     g_return_if_fail (SP_IS_CANVAS (canvas));
2364     if (worldx) *worldx = canvas->x0 + winx;
2365     if (worldy) *worldy = canvas->y0 + winy;
2368 /**
2369  * Sets win coordinates from world and canvas.
2370  */
2371 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2373     g_return_if_fail (canvas != NULL);
2374     g_return_if_fail (SP_IS_CANVAS (canvas));
2376     if (winx) *winx = worldx - canvas->x0;
2377     if (winy) *winy = worldy - canvas->y0;
2380 /**
2381  * Converts point from win to world coordinates.
2382  */
2383 Geom::Point sp_canvas_window_to_world(SPCanvas const *canvas, Geom::Point const win)
2385     g_assert (canvas != NULL);
2386     g_assert (SP_IS_CANVAS (canvas));
2388     return Geom::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2391 /**
2392  * Converts point from world to win coordinates.
2393  */
2394 Geom::Point sp_canvas_world_to_window(SPCanvas const *canvas, Geom::Point const world)
2396     g_assert (canvas != NULL);
2397     g_assert (SP_IS_CANVAS (canvas));
2399     return Geom::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2402 /**
2403  * Returns true if point given in world coordinates is inside window.
2404  */
2405 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, Geom::Point const &world)
2407     g_assert( canvas != NULL );
2408     g_assert(SP_IS_CANVAS(canvas));
2410     GtkWidget const &w = *GTK_WIDGET(canvas);
2411     return ( ( canvas->x0 <= world[Geom::X] )  &&
2412              ( canvas->y0 <= world[Geom::Y] )  &&
2413              ( world[Geom::X] < canvas->x0 + w.allocation.width )  &&
2414              ( world[Geom::Y] < canvas->y0 + w.allocation.height ) );
2417 /**
2418  * Return canvas window coordinates as Geom::Rect.
2419  */
2420 Geom::Rect SPCanvas::getViewbox() const
2422     GtkWidget const *w = GTK_WIDGET(this);
2423     return Geom::Rect(Geom::Point(dx0, dy0),
2424                       Geom::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2427 /**
2428  * Return canvas window coordinates as IRect (a rectangle defined by integers).
2429  */
2430 NR::IRect SPCanvas::getViewboxIntegers() const
2432     GtkWidget const *w = GTK_WIDGET(this);
2433     return NR::IRect(NR::IPoint(x0, y0),
2434                     NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height));
2437 inline int sp_canvas_tile_floor(int x)
2439     return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2442 inline int sp_canvas_tile_ceil(int x)
2444     return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2447 /**
2448  * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2449  */
2450 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2452     if ( nl >= nr || nt >= nb ) {
2453         if ( canvas->tiles ) g_free(canvas->tiles);
2454         canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2455         canvas->tileH=canvas->tileV=0;
2456         canvas->tiles=NULL;
2457         return;
2458     }
2459     int tl=sp_canvas_tile_floor(nl);
2460     int tt=sp_canvas_tile_floor(nt);
2461     int tr=sp_canvas_tile_ceil(nr);
2462     int tb=sp_canvas_tile_ceil(nb);
2464     int nh = tr-tl, nv = tb-tt;
2465     uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2466     for (int i=tl; i<tr; i++) {
2467         for (int j=tt; j<tb; j++) {
2468             int ind = (i-tl) + (j-tt)*nh;
2469             if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2470                 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2471             } else {
2472                 ntiles[ind]=0; // newly exposed areas get 0
2473             }
2474         }
2475     }
2476     if ( canvas->tiles ) g_free(canvas->tiles);
2477     canvas->tiles=ntiles;
2478     canvas->tLeft=tl;
2479     canvas->tTop=tt;
2480     canvas->tRight=tr;
2481     canvas->tBottom=tb;
2482     canvas->tileH=nh;
2483     canvas->tileV=nv;
2486 /*
2487  * Helper that queues a canvas rectangle for redraw
2488  */
2489 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2490     canvas->need_redraw = TRUE;
2492     sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2495 /**
2496  * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2497  */
2498 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2500     if ( nl >= nr || nt >= nb ) {
2501         return;
2502     }
2503     int tl=sp_canvas_tile_floor(nl);
2504     int tt=sp_canvas_tile_floor(nt);
2505     int tr=sp_canvas_tile_ceil(nr);
2506     int tb=sp_canvas_tile_ceil(nb);
2507     if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2508     if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2509     if ( tr > canvas->tRight ) tr=canvas->tRight;
2510     if ( tt < canvas->tTop ) tt=canvas->tTop;
2511     if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2513     for (int i=tl; i<tr; i++) {
2514         for (int j=tt; j<tb; j++) {
2515             canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2516         }
2517     }
2521 /*
2522   Local Variables:
2523   mode:c++
2524   c-file-style:"stroustrup"
2525   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2526   indent-tabs-mode:nil
2527   fill-column:99
2528   End:
2529 */
2530 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :