Code

Fix regression caused by rev. #9158 (selection of nodes in the node editor by draggin...
[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};
106 enum {PROP_0, PROP_VISIBLE};
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);
118 static int pick_current_item (SPCanvas *canvas, GdkEvent *event);
120 static guint item_signals[ITEM_LAST_SIGNAL] = { 0 };
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     item_signals[ITEM_EVENT] = g_signal_new ("event",
155                                              G_TYPE_FROM_CLASS (klass),
156                                              G_SIGNAL_RUN_LAST,
157                                              ((glong)((guint8*)&(klass->event) - (guint8*)klass)),
158                                              NULL, NULL,
159                                              sp_marshal_BOOLEAN__POINTER,
160                                              G_TYPE_BOOLEAN, 1,
161                                              GDK_TYPE_EVENT);
163     object_class->dispose = sp_canvas_item_dispose;
166 /**
167  * Callback for initialization of SPCanvasItem.
168  */
169 static void
170 sp_canvas_item_init (SPCanvasItem *item)
172     // TODO items should not be visible on creation - this causes kludges with items
173     // that should be initially invisible; examples of such items: node handles, the CtrlRect
174     // used for rubberbanding, path outline, etc.
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 (g_type_class_peek(g_type_parent(sp_canvas_item_get_type())))->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 bool
481 sp_canvas_item_is_visible (SPCanvasItem *item)
483     return item->flags & SP_CANVAS_ITEM_VISIBLE;
487 /**
488  * Sets visible flag on item and requests a redraw.
489  */
490 void
491 sp_canvas_item_show (SPCanvasItem *item)
493     g_return_if_fail (item != NULL);
494     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
496     if (item->flags & SP_CANVAS_ITEM_VISIBLE)
497         return;
499     item->flags |= SP_CANVAS_ITEM_VISIBLE;
501     int x0 = (int)(item->x1);
502     int x1 = (int)(item->x2);
503     int y0 = (int)(item->y1);
504     int y1 = (int)(item->y2);
506     if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
507         sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
508         item->canvas->need_repick = TRUE;
509     }
512 /**
513  * Clears visible flag on item and requests a redraw.
514  */
515 void
516 sp_canvas_item_hide (SPCanvasItem *item)
518     g_return_if_fail (item != NULL);
519     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
521     if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
522         return;
524     item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
526     int x0 = (int)(item->x1);
527     int x1 = (int)(item->x2);
528     int y0 = (int)(item->y1);
529     int y1 = (int)(item->y2);
531     if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
532         sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
533         item->canvas->need_repick = TRUE;
534     }
537 /**
538  * Grab item under cursor.
539  *
540  * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE
541  */
542 int
543 sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
545     g_return_val_if_fail (item != NULL, -1);
546     g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1);
547     g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1);
549     if (item->canvas->grabbed_item)
550         return -1;
552     // This test disallows grabbing events by an invisible item, which may be useful
553     // sometimes. An example is the hidden control point used for the selector component,
554     // where it is used for object selection and rubberbanding. There seems to be nothing
555     // preventing this except this test, so I removed it.
556     // -- Krzysztof KosiÅ„ski, 2009.08.12
557     //if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
558     //    return -1;
560     if (HAS_BROKEN_MOTION_HINTS) {
561         event_mask &= ~GDK_POINTER_MOTION_HINT_MASK;
562     }
564     /* fixme: Top hack (Lauris) */
565     /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
566     /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
567     gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
568                       (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
569                       NULL, cursor, etime);
571     item->canvas->grabbed_item = item;
572     item->canvas->grabbed_event_mask = event_mask;
573     item->canvas->current_item = item; /* So that events go to the grabbed item */
575     return 0;
578 /**
579  * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
580  * mouse.
581  *
582  * \param item A canvas item that holds a grab.
583  * \param etime The timestamp for ungrabbing the mouse.
584  */
585 void
586 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
588     g_return_if_fail (item != NULL);
589     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
591     if (item->canvas->grabbed_item != item)
592         return;
594     item->canvas->grabbed_item = NULL;
596     gdk_pointer_ungrab (etime);
599 /**
600  * Returns the product of all transformation matrices from the root item down
601  * to the item.
602  */
603 Geom::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
605     g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
607     Geom::Matrix affine = Geom::identity();
609     while (item) {
610         affine *= item->xform;
611         item = item->parent;
612     }
613     return affine;
616 /**
617  * Helper that returns true iff item is descendant of parent.
618  */
619 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
621     while (item) {
622         if (item == parent)
623             return true;
624         item = item->parent;
625     }
627     return false;
630 /**
631  * Focus canvas, and item under cursor if it is not already focussed.
632  */
633 void
634 sp_canvas_item_grab_focus (SPCanvasItem *item)
636     g_return_if_fail (item != NULL);
637     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
638     g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
640     SPCanvasItem *focused_item = item->canvas->focused_item;
642     if (focused_item) {
643         GdkEvent ev;
644         ev.focus_change.type = GDK_FOCUS_CHANGE;
645         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
646         ev.focus_change.send_event = FALSE;
647         ev.focus_change.in = FALSE;
649         emit_event (item->canvas, &ev);
650     }
652     item->canvas->focused_item = item;
653     gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
655     if (focused_item) {
656         GdkEvent ev;
657         ev.focus_change.type = GDK_FOCUS_CHANGE;
658         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
659         ev.focus_change.send_event = FALSE;
660         ev.focus_change.in = TRUE;
662         emit_event (item->canvas, &ev);
663     }
666 /**
667  * Requests that the canvas queue an update for the specified item.
668  *
669  * To be used only by item implementations.
670  */
671 void
672 sp_canvas_item_request_update (SPCanvasItem *item)
674     if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
675         return;
677     item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
679     if (item->parent != NULL) {
680         /* Recurse up the tree */
681         sp_canvas_item_request_update (item->parent);
682     } else {
683         /* Have reached the top of the tree, make sure the update call gets scheduled. */
684         sp_canvas_request_update (item->canvas);
685     }
688 /**
689  * Returns position of item in group.
690  */
691 gint sp_canvas_item_order (SPCanvasItem * item)
693     return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
696 /* SPCanvasGroup */
698 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
699 static void sp_canvas_group_init (SPCanvasGroup *group);
700 static void sp_canvas_group_destroy (GtkObject *object);
702 static void sp_canvas_group_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags);
703 static double sp_canvas_group_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item);
704 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
706 static SPCanvasItemClass *group_parent_class;
708 /**
709  * Registers SPCanvasGroup class with Gtk and returns its type number.
710  */
711 GType sp_canvas_group_get_type(void)
713     static GType type = 0;
714     if (!type) {
715         GTypeInfo info = {
716             sizeof(SPCanvasGroupClass),
717             0, // base_init
718             0, // base_finalize
719             (GClassInitFunc)sp_canvas_group_class_init,
720             0, // class_finalize
721             0, // class_data
722             sizeof(SPCanvasGroup),
723             0, // n_preallocs
724             (GInstanceInitFunc)sp_canvas_group_init,
725             0 // value_table
726         };
727         type = g_type_register_static(sp_canvas_item_get_type(), "SPCanvasGroup", &info, static_cast<GTypeFlags>(0));
728     }
729     return type;
732 /**
733  * Class initialization function for SPCanvasGroupClass
734  */
735 static void
736 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
738     GtkObjectClass *object_class = (GtkObjectClass *) klass;
739     SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
741     group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
743     object_class->destroy = sp_canvas_group_destroy;
745     item_class->update = sp_canvas_group_update;
746     item_class->render = sp_canvas_group_render;
747     item_class->point = sp_canvas_group_point;
750 /**
751  * Callback. Empty.
752  */
753 static void
754 sp_canvas_group_init (SPCanvasGroup */*group*/)
756     /* Nothing here */
759 /**
760  * Callback that destroys all items in group and calls group's virtual
761  * destroy() function.
762  */
763 static void
764 sp_canvas_group_destroy (GtkObject *object)
766     g_return_if_fail (object != NULL);
767     g_return_if_fail (SP_IS_CANVAS_GROUP (object));
769     SPCanvasGroup const *group = SP_CANVAS_GROUP (object);
771     GList *list = group->items;
772     while (list) {
773         SPCanvasItem *child = (SPCanvasItem *)list->data;
774         list = list->next;
776         gtk_object_destroy (GTK_OBJECT (child));
777     }
779     if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
780         (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
783 /**
784  * Update handler for canvas groups
785  */
786 static void
787 sp_canvas_group_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags)
789     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
790     Geom::RectHull corners(Geom::Point(0, 0));
791     bool empty=true;
793     for (GList *list = group->items; list; list = list->next) {
794         SPCanvasItem *i = (SPCanvasItem *)list->data;
796         sp_canvas_item_invoke_update (i, affine, flags);
798         if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
799             if (empty) {
800                 corners = Geom::RectHull(Geom::Point(i->x1, i->y1));
801                 empty = false;
802             } else {
803                 corners.add(Geom::Point(i->x1, i->y1));
804             }
805             corners.add(Geom::Point(i->x2, i->y2));
806         }
807     }
809     Geom::OptRect const bounds = corners.bounds();
810     if (bounds) {
811         item->x1 = bounds->min()[Geom::X];
812         item->y1 = bounds->min()[Geom::Y];
813         item->x2 = bounds->max()[Geom::X];
814         item->y2 = bounds->max()[Geom::Y];
815     } else {
816         // FIXME ?
817         item->x1 = item->x2 = item->y1 = item->y2 = 0;
818     }
821 /**
822  * Point handler for canvas groups.
823  */
824 static double
825 sp_canvas_group_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item)
827     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
828     double const x = p[Geom::X];
829     double const y = p[Geom::Y];
830     int x1 = (int)(x - item->canvas->close_enough);
831     int y1 = (int)(y - item->canvas->close_enough);
832     int x2 = (int)(x + item->canvas->close_enough);
833     int y2 = (int)(y + item->canvas->close_enough);
835     double best = 0.0;
836     *actual_item = NULL;
838     double dist = 0.0;
840     for (GList *list = group->items; list; list = list->next) {
841         SPCanvasItem *child = (SPCanvasItem *)list->data;
843         if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
844             SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
846             int has_point;
847             if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
848                 dist = sp_canvas_item_invoke_point (child, p, &point_item);
849                 has_point = TRUE;
850             } else
851                 has_point = FALSE;
853             if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
854                 best = dist;
855                 *actual_item = point_item;
856             }
857         }
858     }
860     return best;
863 /**
864  * Renders all visible canvas group items in buf rectangle.
865  */
866 static void
867 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
869     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
871     for (GList *list = group->items; list; list = list->next) {
872         SPCanvasItem *child = (SPCanvasItem *)list->data;
873         if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
874             if ((child->x1 < buf->rect.x1) &&
875                 (child->y1 < buf->rect.y1) &&
876                 (child->x2 > buf->rect.x0) &&
877                 (child->y2 > buf->rect.y0)) {
878                 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
879                     SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
880             }
881         }
882     }
885 /**
886  * Adds an item to a canvas group.
887  */
888 static void
889 group_add (SPCanvasGroup *group, SPCanvasItem *item)
891     gtk_object_ref (GTK_OBJECT (item));
892     gtk_object_sink (GTK_OBJECT (item));
894     if (!group->items) {
895         group->items = g_list_append (group->items, item);
896         group->last = group->items;
897     } else {
898         group->last = g_list_append (group->last, item)->next;
899     }
901     sp_canvas_item_request_update (item);
904 /**
905  * Removes an item from a canvas group
906  */
907 static void
908 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
910     g_return_if_fail (group != NULL);
911     g_return_if_fail (SP_IS_CANVAS_GROUP (group));
912     g_return_if_fail (item != NULL);
914     for (GList *children = group->items; children; children = children->next) {
915         if (children->data == item) {
917             /* Unparent the child */
919             item->parent = NULL;
920             gtk_object_unref (GTK_OBJECT (item));
922             /* Remove it from the list */
924             if (children == group->last) group->last = children->prev;
926             group->items = g_list_remove_link (group->items, children);
927             g_list_free (children);
928             break;
929         }
930     }
933 /* SPCanvas */
935 static void sp_canvas_class_init (SPCanvasClass *klass);
936 static void sp_canvas_init (SPCanvas *canvas);
937 static void sp_canvas_destroy (GtkObject *object);
939 static void sp_canvas_realize (GtkWidget *widget);
940 static void sp_canvas_unrealize (GtkWidget *widget);
942 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
943 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
945 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
946 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
947 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
948 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
949 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
950 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
951 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
952 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
954 static GtkWidgetClass *canvas_parent_class;
956 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb);
957 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb);
958 static void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val);
959 static int do_update (SPCanvas *canvas);
961 /**
962  * Registers the SPCanvas class if necessary, and returns the type ID
963  * associated to it.
964  *
965  * \return The type ID of the SPCanvas class.
966  **/
967 GType sp_canvas_get_type(void)
969     static GType type = 0;
970     if (!type) {
971         GTypeInfo info = {
972             sizeof(SPCanvasClass),
973             0, // base_init
974             0, // base_finalize
975             (GClassInitFunc)sp_canvas_class_init,
976             0, // class_finalize
977             0, // class_data
978             sizeof(SPCanvas),
979             0, // n_preallocs
980             (GInstanceInitFunc)sp_canvas_init,
981             0 // value_table
982         };
983         type = g_type_register_static(GTK_TYPE_WIDGET, "SPCanvas", &info, static_cast<GTypeFlags>(0));
984     }
985     return type;
988 /**
989  * Class initialization function for SPCanvasClass.
990  */
991 static void
992 sp_canvas_class_init (SPCanvasClass *klass)
994     GtkObjectClass *object_class = (GtkObjectClass *) klass;
995     GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
997     canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
999     object_class->destroy = sp_canvas_destroy;
1001     widget_class->realize = sp_canvas_realize;
1002     widget_class->unrealize = sp_canvas_unrealize;
1003     widget_class->size_request = sp_canvas_size_request;
1004     widget_class->size_allocate = sp_canvas_size_allocate;
1005     widget_class->button_press_event = sp_canvas_button;
1006     widget_class->button_release_event = sp_canvas_button;
1007     widget_class->motion_notify_event = sp_canvas_motion;
1008     widget_class->scroll_event = sp_canvas_scroll;
1009     widget_class->expose_event = sp_canvas_expose;
1010     widget_class->key_press_event = sp_canvas_key;
1011     widget_class->key_release_event = sp_canvas_key;
1012     widget_class->enter_notify_event = sp_canvas_crossing;
1013     widget_class->leave_notify_event = sp_canvas_crossing;
1014     widget_class->focus_in_event = sp_canvas_focus_in;
1015     widget_class->focus_out_event = sp_canvas_focus_out;
1018 /**
1019  * Callback: object initialization for SPCanvas.
1020  */
1021 static void
1022 sp_canvas_init (SPCanvas *canvas)
1024     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
1025     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
1026     GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
1028     canvas->pick_event.type = GDK_LEAVE_NOTIFY;
1029     canvas->pick_event.crossing.x = 0;
1030     canvas->pick_event.crossing.y = 0;
1032     /* Create the root item as a special case */
1033     canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
1034     canvas->root->canvas = canvas;
1036     gtk_object_ref (GTK_OBJECT (canvas->root));
1037     gtk_object_sink (GTK_OBJECT (canvas->root));
1039     canvas->need_repick = TRUE;
1041     // See comment at in sp-canvas.h.
1042     canvas->gen_all_enter_events = false;
1044     canvas->tiles=NULL;
1045     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1046     canvas->tileH=canvas->tileV=0;
1048     canvas->forced_redraw_count = 0;
1049     canvas->forced_redraw_limit = -1;
1051 #if ENABLE_LCMS
1052     canvas->enable_cms_display_adj = false;
1053     canvas->cms_key = new Glib::ustring("");
1054 #endif // ENABLE_LCMS
1056     canvas->is_scrolling = false;
1059 /**
1060  * Convenience function to remove the idle handler of a canvas.
1061  */
1062 static void
1063 remove_idle (SPCanvas *canvas)
1065     if (canvas->idle_id) {
1066         gtk_idle_remove (canvas->idle_id);
1067         canvas->idle_id = 0;
1068     }
1071 /*
1072  * Removes the transient state of the canvas (idle handler, grabs).
1073  */
1074 static void
1075 shutdown_transients (SPCanvas *canvas)
1077     /* We turn off the need_redraw flag, since if the canvas is mapped again
1078      * it will request a redraw anyways.  We do not turn off the need_update
1079      * flag, though, because updates are not queued when the canvas remaps
1080      * itself.
1081      */
1082     if (canvas->need_redraw) {
1083         canvas->need_redraw = FALSE;
1084     }
1085     if ( canvas->tiles ) g_free(canvas->tiles);
1086     canvas->tiles=NULL;
1087     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1088     canvas->tileH=canvas->tileV=0;
1090     if (canvas->grabbed_item) {
1091         canvas->grabbed_item = NULL;
1092         gdk_pointer_ungrab (GDK_CURRENT_TIME);
1093     }
1095     remove_idle (canvas);
1098 /**
1099  * Destroy handler for SPCanvas.
1100  */
1101 static void
1102 sp_canvas_destroy (GtkObject *object)
1104     SPCanvas *canvas = SP_CANVAS (object);
1106     if (canvas->root) {
1107         gtk_object_unref (GTK_OBJECT (canvas->root));
1108         canvas->root = NULL;
1109     }
1111     shutdown_transients (canvas);
1113     if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1114         (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1117 static void track_latency(GdkEvent const *event) {
1118     GdkEventLatencyTracker &tracker = GdkEventLatencyTracker::default_tracker();
1119     boost::optional<double> latency = tracker.process(event);
1120     if (latency && *latency > 2.0) {
1121         //g_warning("Event latency reached %f sec (%1.4f)", *latency, tracker.getSkew());
1122     }
1125 /**
1126  * Returns new canvas as widget.
1127  */
1128 GtkWidget *
1129 sp_canvas_new_aa (void)
1131     SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1133     return (GtkWidget *) canvas;
1136 /**
1137  * The canvas widget's realize callback.
1138  */
1139 static void
1140 sp_canvas_realize (GtkWidget *widget)
1142     SPCanvas *canvas = SP_CANVAS (widget);
1144     GdkWindowAttr attributes;
1145     attributes.window_type = GDK_WINDOW_CHILD;
1146     attributes.x = widget->allocation.x;
1147     attributes.y = widget->allocation.y;
1148     attributes.width = widget->allocation.width;
1149     attributes.height = widget->allocation.height;
1150     attributes.wclass = GDK_INPUT_OUTPUT;
1151     attributes.visual = gdk_rgb_get_visual ();
1152     attributes.colormap = gdk_rgb_get_cmap ();
1153     attributes.event_mask = (gtk_widget_get_events (widget) |
1154                              GDK_EXPOSURE_MASK |
1155                              GDK_BUTTON_PRESS_MASK |
1156                              GDK_BUTTON_RELEASE_MASK |
1157                              GDK_POINTER_MOTION_MASK |
1158                              ( HAS_BROKEN_MOTION_HINTS ?
1159                                0 : GDK_POINTER_MOTION_HINT_MASK ) |
1160                              GDK_PROXIMITY_IN_MASK |
1161                              GDK_PROXIMITY_OUT_MASK |
1162                              GDK_KEY_PRESS_MASK |
1163                              GDK_KEY_RELEASE_MASK |
1164                              GDK_ENTER_NOTIFY_MASK |
1165                              GDK_LEAVE_NOTIFY_MASK |
1166                              GDK_FOCUS_CHANGE_MASK);
1167     gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1169     widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1170     gdk_window_set_user_data (widget->window, widget);
1172     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1173     if ( prefs->getBool("/options/useextinput/value", true) )
1174         gtk_widget_set_events(widget, attributes.event_mask);
1176     widget->style = gtk_style_attach (widget->style, widget->window);
1178     GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1180     canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1183 /**
1184  * The canvas widget's unrealize callback.
1185  */
1186 static void
1187 sp_canvas_unrealize (GtkWidget *widget)
1189     SPCanvas *canvas = SP_CANVAS (widget);
1191     canvas->current_item = NULL;
1192     canvas->grabbed_item = NULL;
1193     canvas->focused_item = NULL;
1195     shutdown_transients (canvas);
1197     gdk_gc_destroy (canvas->pixmap_gc);
1198     canvas->pixmap_gc = NULL;
1200     if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1201         (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1204 /**
1205  * The canvas widget's size_request callback.
1206  */
1207 static void
1208 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1210     static_cast<void>(SP_CANVAS (widget));
1212     req->width = 256;
1213     req->height = 256;
1216 /**
1217  * The canvas widget's size_allocate callback.
1218  */
1219 static void
1220 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1222     SPCanvas *canvas = SP_CANVAS (widget);
1224     /* Schedule redraw of new region */
1225     sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1226     if (allocation->width > widget->allocation.width) {
1227         sp_canvas_request_redraw (canvas,
1228                                   canvas->x0 + widget->allocation.width,
1229                                   0,
1230                                   canvas->x0 + allocation->width,
1231                                   canvas->y0 + allocation->height);
1232     }
1233     if (allocation->height > widget->allocation.height) {
1234         sp_canvas_request_redraw (canvas,
1235                                   0,
1236                                   canvas->y0 + widget->allocation.height,
1237                                   canvas->x0 + allocation->width,
1238                                   canvas->y0 + allocation->height);
1239     }
1241     widget->allocation = *allocation;
1243     if (GTK_WIDGET_REALIZED (widget)) {
1244         gdk_window_move_resize (widget->window,
1245                                 widget->allocation.x, widget->allocation.y,
1246                                 widget->allocation.width, widget->allocation.height);
1247     }
1250 /**
1251  * Helper that emits an event for an item in the canvas, be it the current
1252  * item, grabbed item, or focused item, as appropriate.
1253  */
1254 static int
1255 emit_event (SPCanvas *canvas, GdkEvent *event)
1257     guint mask;
1259     if (canvas->grabbed_item) {
1260         switch (event->type) {
1261         case GDK_ENTER_NOTIFY:
1262             mask = GDK_ENTER_NOTIFY_MASK;
1263             break;
1264         case GDK_LEAVE_NOTIFY:
1265             mask = GDK_LEAVE_NOTIFY_MASK;
1266             break;
1267         case GDK_MOTION_NOTIFY:
1268             mask = GDK_POINTER_MOTION_MASK;
1269             break;
1270         case GDK_BUTTON_PRESS:
1271         case GDK_2BUTTON_PRESS:
1272         case GDK_3BUTTON_PRESS:
1273             mask = GDK_BUTTON_PRESS_MASK;
1274             break;
1275         case GDK_BUTTON_RELEASE:
1276             mask = GDK_BUTTON_RELEASE_MASK;
1277             break;
1278         case GDK_KEY_PRESS:
1279             mask = GDK_KEY_PRESS_MASK;
1280             break;
1281         case GDK_KEY_RELEASE:
1282             mask = GDK_KEY_RELEASE_MASK;
1283             break;
1284         case GDK_SCROLL:
1285             mask = GDK_SCROLL;
1286             break;
1287         default:
1288             mask = 0;
1289             break;
1290         }
1292         if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1293     }
1295     /* Convert to world coordinates -- we have two cases because of different
1296      * offsets of the fields in the event structures.
1297      */
1299     GdkEvent ev = *event;
1301     switch (ev.type) {
1302     case GDK_ENTER_NOTIFY:
1303     case GDK_LEAVE_NOTIFY:
1304         ev.crossing.x += canvas->x0;
1305         ev.crossing.y += canvas->y0;
1306         break;
1307     case GDK_MOTION_NOTIFY:
1308     case GDK_BUTTON_PRESS:
1309     case GDK_2BUTTON_PRESS:
1310     case GDK_3BUTTON_PRESS:
1311     case GDK_BUTTON_RELEASE:
1312         ev.motion.x += canvas->x0;
1313         ev.motion.y += canvas->y0;
1314         break;
1315     default:
1316         break;
1317     }
1319     /* Choose where we send the event */
1321     /* canvas->current_item becomes NULL in some cases under Win32
1322     ** (e.g. if the pointer leaves the window).  So this is a hack that
1323     ** Lauris applied to SP to get around the problem.
1324     */
1325     SPCanvasItem* item = NULL;
1326     if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1327         item = canvas->grabbed_item;
1328     } else {
1329         // Make sure that current_item is up-to-date. If a snap indicator was just deleted, then
1330         // sp_canvas_item_dispose has been called and there is no current_item specified. We need
1331         // that though because otherwise we don't know where to send this event to, leading to a
1332         // lost event. We can't wait for idle events to have current_item updated, we need it now!
1333         // Otherwise, scrolling when hovering above a pre-snap indicator won't work (for example)
1334         // See this bug report: https://bugs.launchpad.net/inkscape/+bug/522335/comments/8
1335         if (canvas->need_repick && !canvas->in_repick && event->type == GDK_SCROLL) {
1336             // To avoid side effects, we'll only do this for scroll events, because this is the
1337             // only thing we want to fix here. An example of a reported side effect is that
1338             // otherwise selection of nodes in the node editor by dragging a rectangle using a
1339             // tablet will break
1340             canvas->need_repick = FALSE;
1341             pick_current_item (canvas, (GdkEvent *) event);
1342         }
1343         item = canvas->current_item;
1344     }
1346     if (canvas->focused_item &&
1347         ((event->type == GDK_KEY_PRESS) ||
1348          (event->type == GDK_KEY_RELEASE) ||
1349          (event->type == GDK_FOCUS_CHANGE))) {
1350         item = canvas->focused_item;
1351     }
1353     /* The event is propagated up the hierarchy (for if someone connected to
1354      * a group instead of a leaf event), and emission is stopped if a
1355      * handler returns TRUE, just like for GtkWidget events.
1356      */
1358     gint finished = FALSE;
1360     while (item && !finished) {
1361         gtk_object_ref (GTK_OBJECT (item));
1362         gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1363         SPCanvasItem *parent = item->parent;
1364         gtk_object_unref (GTK_OBJECT (item));
1365         item = parent;
1366     }
1368     return finished;
1371 /**
1372  * Helper that re-picks the current item in the canvas, based on the event's
1373  * coordinates and emits enter/leave events for items as appropriate.
1374  */
1375 static int
1376 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1378     int button_down = 0;
1379     double x, y;
1381     if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1382         return FALSE;
1384     int retval = FALSE;
1386     if (canvas->gen_all_enter_events == false) {
1387         // If a button is down, we'll perform enter and leave events on the
1388         // current item, but not enter on any other item.  This is more or
1389         // less like X pointer grabbing for canvas items.
1390         //
1391         button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1392                 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1394         if (!button_down) canvas->left_grabbed_item = FALSE;
1395     }
1397     /* Save the event in the canvas.  This is used to synthesize enter and
1398      * leave events in case the current item changes.  It is also used to
1399      * re-pick the current item if the current one gets deleted.  Also,
1400      * synthesize an enter event.
1401      */
1402     if (event != &canvas->pick_event) {
1403         if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1404             /* these fields have the same offsets in both types of events */
1406             canvas->pick_event.crossing.type       = GDK_ENTER_NOTIFY;
1407             canvas->pick_event.crossing.window     = event->motion.window;
1408             canvas->pick_event.crossing.send_event = event->motion.send_event;
1409             canvas->pick_event.crossing.subwindow  = NULL;
1410             canvas->pick_event.crossing.x          = event->motion.x;
1411             canvas->pick_event.crossing.y          = event->motion.y;
1412             canvas->pick_event.crossing.mode       = GDK_CROSSING_NORMAL;
1413             canvas->pick_event.crossing.detail     = GDK_NOTIFY_NONLINEAR;
1414             canvas->pick_event.crossing.focus      = FALSE;
1415             canvas->pick_event.crossing.state      = event->motion.state;
1417             /* these fields don't have the same offsets in both types of events */
1419             if (event->type == GDK_MOTION_NOTIFY) {
1420                 canvas->pick_event.crossing.x_root = event->motion.x_root;
1421                 canvas->pick_event.crossing.y_root = event->motion.y_root;
1422             } else {
1423                 canvas->pick_event.crossing.x_root = event->button.x_root;
1424                 canvas->pick_event.crossing.y_root = event->button.y_root;
1425             }
1426         } else {
1427             canvas->pick_event = *event;
1428         }
1429     }
1431     /* Don't do anything else if this is a recursive call */
1432     if (canvas->in_repick) return retval;
1434     /* LeaveNotify means that there is no current item, so we don't look for one */
1435     if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1436         /* these fields don't have the same offsets in both types of events */
1438         if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1439             x = canvas->pick_event.crossing.x;
1440             y = canvas->pick_event.crossing.y;
1441         } else {
1442             x = canvas->pick_event.motion.x;
1443             y = canvas->pick_event.motion.y;
1444         }
1446         /* world coords */
1447         x += canvas->x0;
1448         y += canvas->y0;
1450         /* find the closest item */
1451         if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1452             sp_canvas_item_invoke_point (canvas->root, Geom::Point(x, y), &canvas->new_current_item);
1453         } else {
1454             canvas->new_current_item = NULL;
1455         }
1456     } else {
1457         canvas->new_current_item = NULL;
1458     }
1460     if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1461         return retval; /* current item did not change */
1462     }
1464     /* Synthesize events for old and new current items */
1466     if ((canvas->new_current_item != canvas->current_item)
1467         && (canvas->current_item != NULL)
1468         && !canvas->left_grabbed_item) {
1469         GdkEvent new_event;
1470         SPCanvasItem *item;
1472         item = canvas->current_item;
1474         new_event = canvas->pick_event;
1475         new_event.type = GDK_LEAVE_NOTIFY;
1477         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1478         new_event.crossing.subwindow = NULL;
1479         canvas->in_repick = TRUE;
1480         retval = emit_event (canvas, &new_event);
1481         canvas->in_repick = FALSE;
1482     }
1484     if (canvas->gen_all_enter_events == false) {
1485         // new_current_item may have been set to NULL during the call to
1486         // emit_event() above
1487         if ((canvas->new_current_item != canvas->current_item) && button_down) {
1488             canvas->left_grabbed_item = TRUE;
1489             return retval;
1490         }
1491     }
1493     /* Handle the rest of cases */
1495     canvas->left_grabbed_item = FALSE;
1496     canvas->current_item = canvas->new_current_item;
1498     if (canvas->current_item != NULL) {
1499         GdkEvent new_event;
1501         new_event = canvas->pick_event;
1502         new_event.type = GDK_ENTER_NOTIFY;
1503         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1504         new_event.crossing.subwindow = NULL;
1505         retval = emit_event (canvas, &new_event);
1506     }
1510     return retval;
1513 /**
1514  * Button event handler for the canvas.
1515  */
1516 static gint
1517 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1519     SPCanvas *canvas = SP_CANVAS (widget);
1521     int retval = FALSE;
1523     /* dispatch normally regardless of the event's window if an item
1524        has a pointer grab in effect */
1525     if (!canvas->grabbed_item &&
1526         event->window != SP_CANVAS_WINDOW (canvas))
1527         return retval;
1529     int mask;
1530     switch (event->button) {
1531     case 1:
1532         mask = GDK_BUTTON1_MASK;
1533         break;
1534     case 2:
1535         mask = GDK_BUTTON2_MASK;
1536         break;
1537     case 3:
1538         mask = GDK_BUTTON3_MASK;
1539         break;
1540     case 4:
1541         mask = GDK_BUTTON4_MASK;
1542         break;
1543     case 5:
1544         mask = GDK_BUTTON5_MASK;
1545         break;
1546     default:
1547         mask = 0;
1548     }
1550     switch (event->type) {
1551     case GDK_BUTTON_PRESS:
1552     case GDK_2BUTTON_PRESS:
1553     case GDK_3BUTTON_PRESS:
1554         /* Pick the current item as if the button were not pressed, and
1555          * then process the event.
1556          */
1557         canvas->state = event->state;
1558         pick_current_item (canvas, (GdkEvent *) event);
1559         canvas->state ^= mask;
1560         retval = emit_event (canvas, (GdkEvent *) event);
1561         break;
1563     case GDK_BUTTON_RELEASE:
1564         /* Process the event as if the button were pressed, then repick
1565          * after the button has been released
1566          */
1567         canvas->state = event->state;
1568         retval = emit_event (canvas, (GdkEvent *) event);
1569         event->state ^= mask;
1570         canvas->state = event->state;
1571         pick_current_item (canvas, (GdkEvent *) event);
1572         event->state ^= mask;
1574         break;
1576     default:
1577         g_assert_not_reached ();
1578     }
1580     return retval;
1583 /**
1584  * Scroll event handler for the canvas.
1585  *
1586  * \todo FIXME: generate motion events to re-select items.
1587  */
1588 static gint
1589 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1591     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1594 static inline void request_motions(GdkWindow *w, GdkEventMotion *event) {
1595     gdk_window_get_pointer(w, NULL, NULL, NULL);
1596 #if HAS_GDK_EVENT_REQUEST_MOTIONS
1597     gdk_event_request_motions(event);
1598 #endif
1601 /**
1602  * Motion event handler for the canvas.
1603  */
1604 static int
1605 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1607     int status;
1608     SPCanvas *canvas = SP_CANVAS (widget);
1610     track_latency((GdkEvent *)event);
1612     if (event->window != SP_CANVAS_WINDOW (canvas))
1613         return FALSE;
1615     if (canvas->pixmap_gc == NULL) // canvas being deleted
1616         return FALSE;
1618     canvas->state = event->state;
1619     pick_current_item (canvas, (GdkEvent *) event);
1620     status = emit_event (canvas, (GdkEvent *) event);
1621     if (event->is_hint) {
1622         request_motions(widget->window, event);
1623     }
1625     return status;
1628 static void
1629 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)
1631     GtkWidget *widget = GTK_WIDGET (canvas);
1633     SPCanvasBuf buf;
1634     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1635         buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1636     } else {
1637         buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1638     }
1640     // Mark the region clean
1641     sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1643     buf.buf_rowstride = sw * 4;
1644     buf.rect.x0 = x0;
1645     buf.rect.y0 = y0;
1646     buf.rect.x1 = x1;
1647     buf.rect.y1 = y1;
1648     buf.visible_rect.x0 = draw_x1;
1649     buf.visible_rect.y0 = draw_y1;
1650     buf.visible_rect.x1 = draw_x2;
1651     buf.visible_rect.y1 = draw_y2;
1652     GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1653     buf.bg_color = (((color->red & 0xff00) << 8)
1654                     | (color->green & 0xff00)
1655                     | (color->blue >> 8));
1656     buf.is_empty = true;
1658     buf.ct = nr_create_cairo_context_canvasbuf (&(buf.visible_rect), &buf);
1660     if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1661         SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1662     }
1664 #if ENABLE_LCMS
1665     cmsHTRANSFORM transf = 0;
1666     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1667     bool fromDisplay = prefs->getBool( "/options/displayprofile/from_display");
1668     if ( fromDisplay ) {
1669         transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" );
1670     } else {
1671         transf = Inkscape::colorprofile_get_display_transform();
1672     }
1673 #endif // ENABLE_LCMS
1675     if (buf.is_empty) {
1676 #if ENABLE_LCMS
1677         if ( transf && canvas->enable_cms_display_adj ) {
1678             cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1679         }
1680 #endif // ENABLE_LCMS
1681         gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1682         gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1683                             canvas->pixmap_gc,
1684                             TRUE,
1685                             x0 - canvas->x0, y0 - canvas->y0,
1686                             x1 - x0, y1 - y0);
1687     } else {
1689 #if ENABLE_LCMS
1690         if ( transf && canvas->enable_cms_display_adj ) {
1691             for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1692                 guchar* p = buf.buf + (buf.buf_rowstride * yy);
1693                 cmsDoTransform( transf, p, p, (x1 - x0) );
1694             }
1695         }
1696 #endif // ENABLE_LCMS
1698 // Now we only need to output the prepared pixmap to the actual screen, and this define chooses one
1699 // of the two ways to do it. The cairo way is direct and straightforward, but unfortunately
1700 // noticeably slower. I asked Carl Worth but he was unable so far to suggest any specific reason
1701 // for this slowness. So, for now we use the oldish method: squeeze out 32bpp buffer to 24bpp and
1702 // use gdk_draw_rgb_image_dithalign, for unfortunately gdk can only handle 24 bpp, which cairo
1703 // cannot handle at all. Still, this way is currently faster even despite the blit with squeeze.
1705 ///#define CANVAS_OUTPUT_VIA_CAIRO
1707 #ifdef CANVAS_OUTPUT_VIA_CAIRO
1709         buf.cst = cairo_image_surface_create_for_data (
1710             buf.buf,
1711             CAIRO_FORMAT_ARGB32,  // unpacked, i.e. 32 bits! one byte is unused
1712             x1 - x0, y1 - y0,
1713             buf.buf_rowstride
1714             );
1715         cairo_t *window_ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1716         cairo_set_source_surface (window_ct, buf.cst, x0 - canvas->x0, y0 - canvas->y0);
1717         cairo_paint (window_ct);
1718         cairo_destroy (window_ct);
1719         cairo_surface_finish (buf.cst);
1720         cairo_surface_destroy (buf.cst);
1722 #else
1724         NRPixBlock b3;
1725         nr_pixblock_setup_fast (&b3, NR_PIXBLOCK_MODE_R8G8B8, x0, y0, x1, y1, TRUE);
1727         NRPixBlock b4;
1728         nr_pixblock_setup_extern (&b4, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1,
1729                                   buf.buf,
1730                                   buf.buf_rowstride,
1731                                   FALSE, FALSE);
1733         // this does the 32->24 squishing, using an assembler routine:
1734         nr_blit_pixblock_pixblock (&b3, &b4);
1736         gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1737                                       canvas->pixmap_gc,
1738                                       x0 - canvas->x0, y0 - canvas->y0,
1739                                       x1 - x0, y1 - y0,
1740                                       GDK_RGB_DITHER_MAX,
1741                                       NR_PIXBLOCK_PX(&b3),
1742                                       sw * 3,
1743                                       x0 - canvas->x0, y0 - canvas->y0);
1745         nr_pixblock_release (&b3);
1746         nr_pixblock_release (&b4);
1747 #endif
1748     }
1750     cairo_surface_t *cst = cairo_get_target(buf.ct);
1751     cairo_destroy (buf.ct);
1752     cairo_surface_finish (cst);
1753     cairo_surface_destroy (cst);
1755     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1756         nr_pixelstore_256K_free (buf.buf);
1757     } else {
1758         nr_pixelstore_1M_free (buf.buf);
1759     }
1762 struct PaintRectSetup {
1763     SPCanvas* canvas;
1764     NRRectL big_rect;
1765     GTimeVal start_time;
1766     int max_pixels;
1767     Geom::Point mouse_loc;
1768 };
1770 /**
1771  * Paint the given rect, recursively subdividing the region until it is the size of a single
1772  * buffer.
1773  *
1774  * @return true if the drawing completes
1775  */
1776 static int
1777 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1779     GTimeVal now;
1780     g_get_current_time (&now);
1782     glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1783         + (now.tv_usec - setup->start_time.tv_usec);
1785     // Allow only very fast buffers to be run together;
1786     // as soon as the total redraw time exceeds 1ms, cancel;
1787     // this returns control to the idle loop and allows Inkscape to process user input
1788     // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1789     // it will get back and finish painting what remains to paint.
1790     if (elapsed > 1000) {
1792         // Interrupting redraw isn't always good.
1793         // For example, when you drag one node of a big path, only the buffer containing
1794         // the mouse cursor will be redrawn again and again, and the rest of the path
1795         // will remain stale because Inkscape never has enough idle time to redraw all
1796         // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1797         // If this limit is set, and if we have aborted redraw more times than is allowed,
1798         // interrupting is blocked and we're forced to redraw full screen once
1799         // (after which we can again interrupt forced_redraw_limit times).
1800         if (setup->canvas->forced_redraw_limit < 0 ||
1801             setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1803             if (setup->canvas->forced_redraw_limit != -1) {
1804                 setup->canvas->forced_redraw_count++;
1805             }
1807             return false;
1808         }
1809     }
1811     // Find the optimal buffer dimensions
1812     int bw = this_rect.x1 - this_rect.x0;
1813     int bh = this_rect.y1 - this_rect.y0;
1814     if ((bw < 1) || (bh < 1))
1815         return 0;
1817     if (bw * bh < setup->max_pixels) {
1818         // We are small enough
1819         sp_canvas_paint_single_buffer (setup->canvas,
1820                                        this_rect.x0, this_rect.y0,
1821                                        this_rect.x1, this_rect.y1,
1822                                        setup->big_rect.x0, setup->big_rect.y0,
1823                                        setup->big_rect.x1, setup->big_rect.y1, bw);
1824         return 1;
1825     }
1827     NRRectL lo = this_rect;
1828     NRRectL hi = this_rect;
1830 /*
1831 This test determines the redraw strategy:
1833 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1834 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1835 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1836 and seems to be faster for drawings with many smaller objects at zoom-out.
1838 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1839 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1840 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1841 faster.
1843 The default for now is the strips mode.
1844 */
1845     if (bw < bh || bh < 2 * TILE_SIZE) {
1846         // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1847         int mid = ((long long) this_rect.x0 + (long long) this_rect.x1) / 2;
1848         // Make sure that mid lies on a tile boundary
1849         mid = (mid / TILE_SIZE) * TILE_SIZE;
1851         lo.x1 = mid;
1852         hi.x0 = mid;
1854         if (setup->mouse_loc[Geom::X] < mid) {
1855             // Always paint towards the mouse first
1856             return sp_canvas_paint_rect_internal(setup, lo)
1857                 && sp_canvas_paint_rect_internal(setup, hi);
1858         } else {
1859             return sp_canvas_paint_rect_internal(setup, hi)
1860                 && sp_canvas_paint_rect_internal(setup, lo);
1861         }
1862     } else {
1863         // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1864         int mid = ((long long) this_rect.y0 + (long long) this_rect.y1) / 2;
1865         // Make sure that mid lies on a tile boundary
1866         mid = (mid / TILE_SIZE) * TILE_SIZE;
1868         lo.y1 = mid;
1869         hi.y0 = mid;
1871         if (setup->mouse_loc[Geom::Y] < mid) {
1872             // Always paint towards the mouse first
1873             return sp_canvas_paint_rect_internal(setup, lo)
1874                 && sp_canvas_paint_rect_internal(setup, hi);
1875         } else {
1876             return sp_canvas_paint_rect_internal(setup, hi)
1877                 && sp_canvas_paint_rect_internal(setup, lo);
1878         }
1879     }
1883 /**
1884  * Helper that draws a specific rectangular part of the canvas.
1885  *
1886  * @return true if the rectangle painting succeeds.
1887  */
1888 static bool
1889 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1891     g_return_val_if_fail (!canvas->need_update, false);
1893     NRRectL rect;
1894     rect.x0 = xx0;
1895     rect.x1 = xx1;
1896     rect.y0 = yy0;
1897     rect.y1 = yy1;
1899     // Clip rect-to-draw by the current visible area
1900     rect.x0 = MAX (rect.x0, canvas->x0);
1901     rect.y0 = MAX (rect.y0, canvas->y0);
1902     rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1903     rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1905 #ifdef DEBUG_REDRAW
1906     // paint the area to redraw yellow
1907     gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1908     gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1909                         canvas->pixmap_gc,
1910                         TRUE,
1911                         rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1912                         rect.x1 - rect.x0, rect.y1 - rect.y0);
1913 #endif
1915     PaintRectSetup setup;
1917     setup.canvas = canvas;
1918     setup.big_rect = rect;
1920     // Save the mouse location
1921     gint x, y;
1922     gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1923     setup.mouse_loc = sp_canvas_window_to_world (canvas, Geom::Point(x,y));
1925     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1926         // use 256K as a compromise to not slow down gradients
1927         // 256K is the cached buffer and we need 4 channels
1928         setup.max_pixels = 65536; // 256K/4
1929     } else {
1930         // paths only, so 1M works faster
1931         // 1M is the cached buffer and we need 4 channels
1932         setup.max_pixels = 262144;
1933     }
1935     // Start the clock
1936     g_get_current_time(&(setup.start_time));
1938     // Go
1939     return sp_canvas_paint_rect_internal(&setup, rect);
1942 /**
1943  * Force a full redraw after a specified number of interrupted redraws
1944  */
1945 void
1946 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1947   g_return_if_fail(canvas != NULL);
1949   canvas->forced_redraw_limit = count;
1950   canvas->forced_redraw_count = 0;
1953 /**
1954  * End forced full redraw requests
1955  */
1956 void
1957 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1958   g_return_if_fail(canvas != NULL);
1960   canvas->forced_redraw_limit = -1;
1963 /**
1964  * The canvas widget's expose callback.
1965  */
1966 static gint
1967 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1969     SPCanvas *canvas = SP_CANVAS (widget);
1971     if (!GTK_WIDGET_DRAWABLE (widget) ||
1972         (event->window != SP_CANVAS_WINDOW (canvas)))
1973         return FALSE;
1975     int n_rects;
1976     GdkRectangle *rects;
1977     gdk_region_get_rectangles (event->region, &rects, &n_rects);
1979     for (int i = 0; i < n_rects; i++) {
1980         NRRectL rect;
1982         rect.x0 = rects[i].x + canvas->x0;
1983         rect.y0 = rects[i].y + canvas->y0;
1984         rect.x1 = rect.x0 + rects[i].width;
1985         rect.y1 = rect.y0 + rects[i].height;
1987         sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1988     }
1990     if (n_rects > 0)
1991         g_free (rects);
1993     return FALSE;
1996 /**
1997  * The canvas widget's keypress callback.
1998  */
1999 static gint
2000 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
2002     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
2005 /**
2006  * Crossing event handler for the canvas.
2007  */
2008 static gint
2009 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
2011     SPCanvas *canvas = SP_CANVAS (widget);
2013     if (event->window != SP_CANVAS_WINDOW (canvas))
2014         return FALSE;
2016     canvas->state = event->state;
2017     return pick_current_item (canvas, (GdkEvent *) event);
2020 /**
2021  * Focus in handler for the canvas.
2022  */
2023 static gint
2024 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
2026     GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
2028     SPCanvas *canvas = SP_CANVAS (widget);
2030     if (canvas->focused_item) {
2031         return emit_event (canvas, (GdkEvent *) event);
2032     } else {
2033         return FALSE;
2034     }
2037 /**
2038  * Focus out handler for the canvas.
2039  */
2040 static gint
2041 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
2043     GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2045     SPCanvas *canvas = SP_CANVAS (widget);
2047     if (canvas->focused_item)
2048         return emit_event (canvas, (GdkEvent *) event);
2049     else
2050         return FALSE;
2053 /**
2054  * Helper that repaints the areas in the canvas that need it.
2055  *
2056  * @return true if all the dirty parts have been redrawn
2057  */
2058 static int
2059 paint (SPCanvas *canvas)
2061     if (canvas->need_update) {
2062         sp_canvas_item_invoke_update (canvas->root, Geom::identity(), 0);
2063         canvas->need_update = FALSE;
2064     }
2066     if (!canvas->need_redraw)
2067         return TRUE;
2069     Gdk::Region to_paint;
2071     for (int j=canvas->tTop; j<canvas->tBottom; j++) {
2072         for (int i=canvas->tLeft; i<canvas->tRight; i++) {
2073             int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
2075             if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
2076                 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
2077                                    TILE_SIZE, TILE_SIZE));
2078             }
2080         }
2081     }
2083     if (!to_paint.empty()) {
2084         Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
2085         typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
2086         for (Iter i=rect.begin(); i != rect.end(); ++i) {
2087             int x0 = (*i).get_x();
2088             int y0 = (*i).get_y();
2089             int x1 = x0 + (*i).get_width();
2090             int y1 = y0 + (*i).get_height();
2091             if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
2092                 // Aborted
2093                 return FALSE;
2094             };
2095         }
2096     }
2098     canvas->need_redraw = FALSE;
2100     // we've had a full unaborted redraw, reset the full redraw counter
2101     if (canvas->forced_redraw_limit != -1) {
2102         canvas->forced_redraw_count = 0;
2103     }
2105     return TRUE;
2108 /**
2109  * Helper that invokes update, paint, and repick on canvas.
2110  */
2111 static int
2112 do_update (SPCanvas *canvas)
2114     if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop during interrupted display!
2115         return TRUE;
2117     /* Cause the update if necessary */
2118     if (canvas->need_update) {
2119         sp_canvas_item_invoke_update (canvas->root, Geom::identity(), 0);
2120         canvas->need_update = FALSE;
2121     }
2123     /* Paint if able to */
2124     if (GTK_WIDGET_DRAWABLE (canvas)) {
2125             return paint (canvas);
2126     }
2128     /* Pick new current item */
2129     while (canvas->need_repick) {
2130         canvas->need_repick = FALSE;
2131         pick_current_item (canvas, &canvas->pick_event);
2132     }
2134     return TRUE;
2137 /**
2138  * Idle handler for the canvas that deals with pending updates and redraws.
2139  */
2140 static gint
2141 idle_handler (gpointer data)
2143     GDK_THREADS_ENTER ();
2145     SPCanvas *canvas = SP_CANVAS (data);
2147     int const ret = do_update (canvas);
2149     if (ret) {
2150         /* Reset idle id */
2151         canvas->idle_id = 0;
2152     }
2154     GDK_THREADS_LEAVE ();
2156     return !ret;
2159 /**
2160  * Convenience function to add an idle handler to a canvas.
2161  */
2162 static void
2163 add_idle (SPCanvas *canvas)
2165     if (canvas->idle_id != 0)
2166         return;
2168     canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2171 /**
2172  * Returns the root group of the specified canvas.
2173  */
2174 SPCanvasGroup *
2175 sp_canvas_root (SPCanvas *canvas)
2177     g_return_val_if_fail (canvas != NULL, NULL);
2178     g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2180     return SP_CANVAS_GROUP (canvas->root);
2183 /**
2184  * Scrolls canvas to specific position (cx and cy are measured in screen pixels)
2185  */
2186 void
2187 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2189     g_return_if_fail (canvas != NULL);
2190     g_return_if_fail (SP_IS_CANVAS (canvas));
2192     int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels)
2193     int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do!
2194     int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the
2195     int dy = iy - canvas->y0; // canvas w.r.t its previous position
2197     canvas->dx0 = cx; // here the 'd' stands for double, not delta!
2198     canvas->dy0 = cy;
2199     canvas->x0 = ix;
2200     canvas->y0 = iy;
2202     sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2204     if (!clear) {
2205         // scrolling without zoom; redraw only the newly exposed areas
2206         if ((dx != 0) || (dy != 0)) {
2207             canvas->is_scrolling = is_scrolling;
2208             if (GTK_WIDGET_REALIZED (canvas)) {
2209                 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2210             }
2211         }
2212     } else {
2213         // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2214     }
2218 /**
2219  * Updates canvas if necessary.
2220  */
2221 void
2222 sp_canvas_update_now (SPCanvas *canvas)
2224     g_return_if_fail (canvas != NULL);
2225     g_return_if_fail (SP_IS_CANVAS (canvas));
2227     if (!(canvas->need_update ||
2228           canvas->need_redraw))
2229         return;
2231     do_update (canvas);
2234 /**
2235  * Update callback for canvas widget.
2236  */
2237 static void
2238 sp_canvas_request_update (SPCanvas *canvas)
2240     canvas->need_update = TRUE;
2241     add_idle (canvas);
2244 /**
2245  * Forces redraw of rectangular canvas area.
2246  */
2247 void
2248 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2250     NRRectL bbox;
2251     NRRectL visible;
2252     NRRectL clip;
2254     g_return_if_fail (canvas != NULL);
2255     g_return_if_fail (SP_IS_CANVAS (canvas));
2257     if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2258     if ((x0 >= x1) || (y0 >= y1)) return;
2260     bbox.x0 = x0;
2261     bbox.y0 = y0;
2262     bbox.x1 = x1;
2263     bbox.y1 = y1;
2265     visible.x0 = canvas->x0;
2266     visible.y0 = canvas->y0;
2267     visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2268     visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2270     nr_rect_l_intersect (&clip, &bbox, &visible);
2272     sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2273     add_idle (canvas);
2276 /**
2277  * Sets world coordinates from win and canvas.
2278  */
2279 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2281     g_return_if_fail (canvas != NULL);
2282     g_return_if_fail (SP_IS_CANVAS (canvas));
2284     if (worldx) *worldx = canvas->x0 + winx;
2285     if (worldy) *worldy = canvas->y0 + winy;
2288 /**
2289  * Sets win coordinates from world and canvas.
2290  */
2291 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2293     g_return_if_fail (canvas != NULL);
2294     g_return_if_fail (SP_IS_CANVAS (canvas));
2296     if (winx) *winx = worldx - canvas->x0;
2297     if (winy) *winy = worldy - canvas->y0;
2300 /**
2301  * Converts point from win to world coordinates.
2302  */
2303 Geom::Point sp_canvas_window_to_world(SPCanvas const *canvas, Geom::Point const win)
2305     g_assert (canvas != NULL);
2306     g_assert (SP_IS_CANVAS (canvas));
2308     return Geom::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2311 /**
2312  * Converts point from world to win coordinates.
2313  */
2314 Geom::Point sp_canvas_world_to_window(SPCanvas const *canvas, Geom::Point const world)
2316     g_assert (canvas != NULL);
2317     g_assert (SP_IS_CANVAS (canvas));
2319     return Geom::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2322 /**
2323  * Returns true if point given in world coordinates is inside window.
2324  */
2325 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, Geom::Point const &world)
2327     g_assert( canvas != NULL );
2328     g_assert(SP_IS_CANVAS(canvas));
2330     GtkWidget const &w = *GTK_WIDGET(canvas);
2331     return ( ( canvas->x0 <= world[Geom::X] )  &&
2332              ( canvas->y0 <= world[Geom::Y] )  &&
2333              ( world[Geom::X] < canvas->x0 + w.allocation.width )  &&
2334              ( world[Geom::Y] < canvas->y0 + w.allocation.height ) );
2337 /**
2338  * Return canvas window coordinates as Geom::Rect.
2339  */
2340 Geom::Rect SPCanvas::getViewbox() const
2342     GtkWidget const *w = GTK_WIDGET(this);
2343     return Geom::Rect(Geom::Point(dx0, dy0),
2344                       Geom::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2347 /**
2348  * Return canvas window coordinates as IRect (a rectangle defined by integers).
2349  */
2350 NR::IRect SPCanvas::getViewboxIntegers() const
2352     GtkWidget const *w = GTK_WIDGET(this);
2353     return NR::IRect(NR::IPoint(x0, y0),
2354                     NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height));
2357 inline int sp_canvas_tile_floor(int x)
2359     return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2362 inline int sp_canvas_tile_ceil(int x)
2364     return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2367 /**
2368  * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2369  */
2370 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2372     if ( nl >= nr || nt >= nb ) {
2373         if ( canvas->tiles ) g_free(canvas->tiles);
2374         canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2375         canvas->tileH=canvas->tileV=0;
2376         canvas->tiles=NULL;
2377         return;
2378     }
2379     int tl=sp_canvas_tile_floor(nl);
2380     int tt=sp_canvas_tile_floor(nt);
2381     int tr=sp_canvas_tile_ceil(nr);
2382     int tb=sp_canvas_tile_ceil(nb);
2384     int nh = tr-tl, nv = tb-tt;
2385     uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2386     for (int i=tl; i<tr; i++) {
2387         for (int j=tt; j<tb; j++) {
2388             int ind = (i-tl) + (j-tt)*nh;
2389             if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2390                 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2391             } else {
2392                 ntiles[ind]=0; // newly exposed areas get 0
2393             }
2394         }
2395     }
2396     if ( canvas->tiles ) g_free(canvas->tiles);
2397     canvas->tiles=ntiles;
2398     canvas->tLeft=tl;
2399     canvas->tTop=tt;
2400     canvas->tRight=tr;
2401     canvas->tBottom=tb;
2402     canvas->tileH=nh;
2403     canvas->tileV=nv;
2406 /*
2407  * Helper that queues a canvas rectangle for redraw
2408  */
2409 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2410     canvas->need_redraw = TRUE;
2412     sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2415 /**
2416  * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2417  */
2418 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2420     if ( nl >= nr || nt >= nb ) {
2421         return;
2422     }
2423     int tl=sp_canvas_tile_floor(nl);
2424     int tt=sp_canvas_tile_floor(nt);
2425     int tr=sp_canvas_tile_ceil(nr);
2426     int tb=sp_canvas_tile_ceil(nb);
2427     if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2428     if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2429     if ( tr > canvas->tRight ) tr=canvas->tRight;
2430     if ( tt < canvas->tTop ) tt=canvas->tTop;
2431     if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2433     for (int i=tl; i<tr; i++) {
2434         for (int j=tt; j<tb; j++) {
2435             canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2436         }
2437     }
2441 /*
2442   Local Variables:
2443   mode:c++
2444   c-file-style:"stroustrup"
2445   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2446   indent-tabs-mode:nil
2447   fill-column:99
2448   End:
2449 */
2450 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :