Code

2319f82f60480da027342d969e3f200db9a9aac4
[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;
1043     
1044     canvas->drawing_disabled = false;
1046     canvas->tiles=NULL;
1047     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1048     canvas->tileH=canvas->tileV=0;
1050     canvas->forced_redraw_count = 0;
1051     canvas->forced_redraw_limit = -1;
1053 #if ENABLE_LCMS
1054     canvas->enable_cms_display_adj = false;
1055     canvas->cms_key = new Glib::ustring("");
1056 #endif // ENABLE_LCMS
1058     canvas->is_scrolling = false;
1061 /**
1062  * Convenience function to remove the idle handler of a canvas.
1063  */
1064 static void
1065 remove_idle (SPCanvas *canvas)
1067     if (canvas->idle_id) {
1068         gtk_idle_remove (canvas->idle_id);
1069         canvas->idle_id = 0;
1070     }
1073 /*
1074  * Removes the transient state of the canvas (idle handler, grabs).
1075  */
1076 static void
1077 shutdown_transients (SPCanvas *canvas)
1079     /* We turn off the need_redraw flag, since if the canvas is mapped again
1080      * it will request a redraw anyways.  We do not turn off the need_update
1081      * flag, though, because updates are not queued when the canvas remaps
1082      * itself.
1083      */
1084     if (canvas->need_redraw) {
1085         canvas->need_redraw = FALSE;
1086     }
1087     if ( canvas->tiles ) g_free(canvas->tiles);
1088     canvas->tiles=NULL;
1089     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1090     canvas->tileH=canvas->tileV=0;
1092     if (canvas->grabbed_item) {
1093         canvas->grabbed_item = NULL;
1094         gdk_pointer_ungrab (GDK_CURRENT_TIME);
1095     }
1097     remove_idle (canvas);
1100 /**
1101  * Destroy handler for SPCanvas.
1102  */
1103 static void
1104 sp_canvas_destroy (GtkObject *object)
1106     SPCanvas *canvas = SP_CANVAS (object);
1108     if (canvas->root) {
1109         gtk_object_unref (GTK_OBJECT (canvas->root));
1110         canvas->root = NULL;
1111     }
1113     shutdown_transients (canvas);
1115     if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1116         (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1119 static void track_latency(GdkEvent const *event) {
1120     GdkEventLatencyTracker &tracker = GdkEventLatencyTracker::default_tracker();
1121     boost::optional<double> latency = tracker.process(event);
1122     if (latency && *latency > 2.0) {
1123         //g_warning("Event latency reached %f sec (%1.4f)", *latency, tracker.getSkew());
1124     }
1127 /**
1128  * Returns new canvas as widget.
1129  */
1130 GtkWidget *
1131 sp_canvas_new_aa (void)
1133     SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1135     return (GtkWidget *) canvas;
1138 /**
1139  * The canvas widget's realize callback.
1140  */
1141 static void
1142 sp_canvas_realize (GtkWidget *widget)
1144     SPCanvas *canvas = SP_CANVAS (widget);
1146     GdkWindowAttr attributes;
1147     attributes.window_type = GDK_WINDOW_CHILD;
1148     attributes.x = widget->allocation.x;
1149     attributes.y = widget->allocation.y;
1150     attributes.width = widget->allocation.width;
1151     attributes.height = widget->allocation.height;
1152     attributes.wclass = GDK_INPUT_OUTPUT;
1153     attributes.visual = gdk_rgb_get_visual ();
1154     attributes.colormap = gdk_rgb_get_cmap ();
1155     attributes.event_mask = (gtk_widget_get_events (widget) |
1156                              GDK_EXPOSURE_MASK |
1157                              GDK_BUTTON_PRESS_MASK |
1158                              GDK_BUTTON_RELEASE_MASK |
1159                              GDK_POINTER_MOTION_MASK |
1160                              ( HAS_BROKEN_MOTION_HINTS ?
1161                                0 : GDK_POINTER_MOTION_HINT_MASK ) |
1162                              GDK_PROXIMITY_IN_MASK |
1163                              GDK_PROXIMITY_OUT_MASK |
1164                              GDK_KEY_PRESS_MASK |
1165                              GDK_KEY_RELEASE_MASK |
1166                              GDK_ENTER_NOTIFY_MASK |
1167                              GDK_LEAVE_NOTIFY_MASK |
1168                              GDK_FOCUS_CHANGE_MASK);
1169     gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1171     widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1172     gdk_window_set_user_data (widget->window, widget);
1174     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1175     if ( prefs->getBool("/options/useextinput/value", true) )
1176         gtk_widget_set_events(widget, attributes.event_mask);
1178     widget->style = gtk_style_attach (widget->style, widget->window);
1180     GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1182     canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1185 /**
1186  * The canvas widget's unrealize callback.
1187  */
1188 static void
1189 sp_canvas_unrealize (GtkWidget *widget)
1191     SPCanvas *canvas = SP_CANVAS (widget);
1193     canvas->current_item = NULL;
1194     canvas->grabbed_item = NULL;
1195     canvas->focused_item = NULL;
1197     shutdown_transients (canvas);
1199     gdk_gc_destroy (canvas->pixmap_gc);
1200     canvas->pixmap_gc = NULL;
1202     if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1203         (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1206 /**
1207  * The canvas widget's size_request callback.
1208  */
1209 static void
1210 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1212     static_cast<void>(SP_CANVAS (widget));
1214     req->width = 256;
1215     req->height = 256;
1218 /**
1219  * The canvas widget's size_allocate callback.
1220  */
1221 static void
1222 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1224     SPCanvas *canvas = SP_CANVAS (widget);
1226     /* Schedule redraw of new region */
1227     sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1228     if (allocation->width > widget->allocation.width) {
1229         sp_canvas_request_redraw (canvas,
1230                                   canvas->x0 + widget->allocation.width,
1231                                   0,
1232                                   canvas->x0 + allocation->width,
1233                                   canvas->y0 + allocation->height);
1234     }
1235     if (allocation->height > widget->allocation.height) {
1236         sp_canvas_request_redraw (canvas,
1237                                   0,
1238                                   canvas->y0 + widget->allocation.height,
1239                                   canvas->x0 + allocation->width,
1240                                   canvas->y0 + allocation->height);
1241     }
1243     widget->allocation = *allocation;
1245     if (GTK_WIDGET_REALIZED (widget)) {
1246         gdk_window_move_resize (widget->window,
1247                                 widget->allocation.x, widget->allocation.y,
1248                                 widget->allocation.width, widget->allocation.height);
1249     }
1252 /**
1253  * Helper that emits an event for an item in the canvas, be it the current
1254  * item, grabbed item, or focused item, as appropriate.
1255  */
1256 static int
1257 emit_event (SPCanvas *canvas, GdkEvent *event)
1259     guint mask;
1261     if (canvas->grabbed_item) {
1262         switch (event->type) {
1263         case GDK_ENTER_NOTIFY:
1264             mask = GDK_ENTER_NOTIFY_MASK;
1265             break;
1266         case GDK_LEAVE_NOTIFY:
1267             mask = GDK_LEAVE_NOTIFY_MASK;
1268             break;
1269         case GDK_MOTION_NOTIFY:
1270             mask = GDK_POINTER_MOTION_MASK;
1271             break;
1272         case GDK_BUTTON_PRESS:
1273         case GDK_2BUTTON_PRESS:
1274         case GDK_3BUTTON_PRESS:
1275             mask = GDK_BUTTON_PRESS_MASK;
1276             break;
1277         case GDK_BUTTON_RELEASE:
1278             mask = GDK_BUTTON_RELEASE_MASK;
1279             break;
1280         case GDK_KEY_PRESS:
1281             mask = GDK_KEY_PRESS_MASK;
1282             break;
1283         case GDK_KEY_RELEASE:
1284             mask = GDK_KEY_RELEASE_MASK;
1285             break;
1286         case GDK_SCROLL:
1287             mask = GDK_SCROLL;
1288             break;
1289         default:
1290             mask = 0;
1291             break;
1292         }
1294         if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1295     }
1297     /* Convert to world coordinates -- we have two cases because of different
1298      * offsets of the fields in the event structures.
1299      */
1301     GdkEvent ev = *event;
1303     switch (ev.type) {
1304     case GDK_ENTER_NOTIFY:
1305     case GDK_LEAVE_NOTIFY:
1306         ev.crossing.x += canvas->x0;
1307         ev.crossing.y += canvas->y0;
1308         break;
1309     case GDK_MOTION_NOTIFY:
1310     case GDK_BUTTON_PRESS:
1311     case GDK_2BUTTON_PRESS:
1312     case GDK_3BUTTON_PRESS:
1313     case GDK_BUTTON_RELEASE:
1314         ev.motion.x += canvas->x0;
1315         ev.motion.y += canvas->y0;
1316         break;
1317     default:
1318         break;
1319     }
1321     /* Choose where we send the event */
1323     /* canvas->current_item becomes NULL in some cases under Win32
1324     ** (e.g. if the pointer leaves the window).  So this is a hack that
1325     ** Lauris applied to SP to get around the problem.
1326     */
1327     SPCanvasItem* item = NULL;
1328     if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1329         item = canvas->grabbed_item;
1330     } else {
1331         // Make sure that current_item is up-to-date. If a snap indicator was just deleted, then
1332         // sp_canvas_item_dispose has been called and there is no current_item specified. We need
1333         // that though because otherwise we don't know where to send this event to, leading to a
1334         // lost event. We can't wait for idle events to have current_item updated, we need it now!
1335         // Otherwise, scrolling when hovering above a pre-snap indicator won't work (for example)
1336         // See this bug report: https://bugs.launchpad.net/inkscape/+bug/522335/comments/8
1337         if (canvas->need_repick && !canvas->in_repick && event->type == GDK_SCROLL) {
1338             // To avoid side effects, we'll only do this for scroll events, because this is the
1339             // only thing we want to fix here. An example of a reported side effect is that
1340             // otherwise selection of nodes in the node editor by dragging a rectangle using a
1341             // tablet will break
1342             canvas->need_repick = FALSE;
1343             pick_current_item (canvas, (GdkEvent *) event);
1344         }
1345         item = canvas->current_item;
1346     }
1348     if (canvas->focused_item &&
1349         ((event->type == GDK_KEY_PRESS) ||
1350          (event->type == GDK_KEY_RELEASE) ||
1351          (event->type == GDK_FOCUS_CHANGE))) {
1352         item = canvas->focused_item;
1353     }
1355     /* The event is propagated up the hierarchy (for if someone connected to
1356      * a group instead of a leaf event), and emission is stopped if a
1357      * handler returns TRUE, just like for GtkWidget events.
1358      */
1360     gint finished = FALSE;
1362     while (item && !finished) {
1363         gtk_object_ref (GTK_OBJECT (item));
1364         gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1365         SPCanvasItem *parent = item->parent;
1366         gtk_object_unref (GTK_OBJECT (item));
1367         item = parent;
1368     }
1370     return finished;
1373 /**
1374  * Helper that re-picks the current item in the canvas, based on the event's
1375  * coordinates and emits enter/leave events for items as appropriate.
1376  */
1377 static int
1378 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1380     int button_down = 0;
1381     double x, y;
1383     if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1384         return FALSE;
1386     int retval = FALSE;
1388     if (canvas->gen_all_enter_events == false) {
1389         // If a button is down, we'll perform enter and leave events on the
1390         // current item, but not enter on any other item.  This is more or
1391         // less like X pointer grabbing for canvas items.
1392         //
1393         button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1394                 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1396         if (!button_down) canvas->left_grabbed_item = FALSE;
1397     }
1399     /* Save the event in the canvas.  This is used to synthesize enter and
1400      * leave events in case the current item changes.  It is also used to
1401      * re-pick the current item if the current one gets deleted.  Also,
1402      * synthesize an enter event.
1403      */
1404     if (event != &canvas->pick_event) {
1405         if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1406             /* these fields have the same offsets in both types of events */
1408             canvas->pick_event.crossing.type       = GDK_ENTER_NOTIFY;
1409             canvas->pick_event.crossing.window     = event->motion.window;
1410             canvas->pick_event.crossing.send_event = event->motion.send_event;
1411             canvas->pick_event.crossing.subwindow  = NULL;
1412             canvas->pick_event.crossing.x          = event->motion.x;
1413             canvas->pick_event.crossing.y          = event->motion.y;
1414             canvas->pick_event.crossing.mode       = GDK_CROSSING_NORMAL;
1415             canvas->pick_event.crossing.detail     = GDK_NOTIFY_NONLINEAR;
1416             canvas->pick_event.crossing.focus      = FALSE;
1417             canvas->pick_event.crossing.state      = event->motion.state;
1419             /* these fields don't have the same offsets in both types of events */
1421             if (event->type == GDK_MOTION_NOTIFY) {
1422                 canvas->pick_event.crossing.x_root = event->motion.x_root;
1423                 canvas->pick_event.crossing.y_root = event->motion.y_root;
1424             } else {
1425                 canvas->pick_event.crossing.x_root = event->button.x_root;
1426                 canvas->pick_event.crossing.y_root = event->button.y_root;
1427             }
1428         } else {
1429             canvas->pick_event = *event;
1430         }
1431     }
1433     /* Don't do anything else if this is a recursive call */
1434     if (canvas->in_repick) return retval;
1436     /* LeaveNotify means that there is no current item, so we don't look for one */
1437     if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1438         /* these fields don't have the same offsets in both types of events */
1440         if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1441             x = canvas->pick_event.crossing.x;
1442             y = canvas->pick_event.crossing.y;
1443         } else {
1444             x = canvas->pick_event.motion.x;
1445             y = canvas->pick_event.motion.y;
1446         }
1448         /* world coords */
1449         x += canvas->x0;
1450         y += canvas->y0;
1452         /* find the closest item */
1453         if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1454             sp_canvas_item_invoke_point (canvas->root, Geom::Point(x, y), &canvas->new_current_item);
1455         } else {
1456             canvas->new_current_item = NULL;
1457         }
1458     } else {
1459         canvas->new_current_item = NULL;
1460     }
1462     if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1463         return retval; /* current item did not change */
1464     }
1466     /* Synthesize events for old and new current items */
1468     if ((canvas->new_current_item != canvas->current_item)
1469         && (canvas->current_item != NULL)
1470         && !canvas->left_grabbed_item) {
1471         GdkEvent new_event;
1472         SPCanvasItem *item;
1474         item = canvas->current_item;
1476         new_event = canvas->pick_event;
1477         new_event.type = GDK_LEAVE_NOTIFY;
1479         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1480         new_event.crossing.subwindow = NULL;
1481         canvas->in_repick = TRUE;
1482         retval = emit_event (canvas, &new_event);
1483         canvas->in_repick = FALSE;
1484     }
1486     if (canvas->gen_all_enter_events == false) {
1487         // new_current_item may have been set to NULL during the call to
1488         // emit_event() above
1489         if ((canvas->new_current_item != canvas->current_item) && button_down) {
1490             canvas->left_grabbed_item = TRUE;
1491             return retval;
1492         }
1493     }
1495     /* Handle the rest of cases */
1497     canvas->left_grabbed_item = FALSE;
1498     canvas->current_item = canvas->new_current_item;
1500     if (canvas->current_item != NULL) {
1501         GdkEvent new_event;
1503         new_event = canvas->pick_event;
1504         new_event.type = GDK_ENTER_NOTIFY;
1505         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1506         new_event.crossing.subwindow = NULL;
1507         retval = emit_event (canvas, &new_event);
1508     }
1512     return retval;
1515 /**
1516  * Button event handler for the canvas.
1517  */
1518 static gint
1519 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1521     SPCanvas *canvas = SP_CANVAS (widget);
1523     int retval = FALSE;
1525     /* dispatch normally regardless of the event's window if an item
1526        has a pointer grab in effect */
1527     if (!canvas->grabbed_item &&
1528         event->window != SP_CANVAS_WINDOW (canvas))
1529         return retval;
1531     int mask;
1532     switch (event->button) {
1533     case 1:
1534         mask = GDK_BUTTON1_MASK;
1535         break;
1536     case 2:
1537         mask = GDK_BUTTON2_MASK;
1538         break;
1539     case 3:
1540         mask = GDK_BUTTON3_MASK;
1541         break;
1542     case 4:
1543         mask = GDK_BUTTON4_MASK;
1544         break;
1545     case 5:
1546         mask = GDK_BUTTON5_MASK;
1547         break;
1548     default:
1549         mask = 0;
1550     }
1552     switch (event->type) {
1553     case GDK_BUTTON_PRESS:
1554     case GDK_2BUTTON_PRESS:
1555     case GDK_3BUTTON_PRESS:
1556         /* Pick the current item as if the button were not pressed, and
1557          * then process the event.
1558          */
1559         canvas->state = event->state;
1560         pick_current_item (canvas, (GdkEvent *) event);
1561         canvas->state ^= mask;
1562         retval = emit_event (canvas, (GdkEvent *) event);
1563         break;
1565     case GDK_BUTTON_RELEASE:
1566         /* Process the event as if the button were pressed, then repick
1567          * after the button has been released
1568          */
1569         canvas->state = event->state;
1570         retval = emit_event (canvas, (GdkEvent *) event);
1571         event->state ^= mask;
1572         canvas->state = event->state;
1573         pick_current_item (canvas, (GdkEvent *) event);
1574         event->state ^= mask;
1576         break;
1578     default:
1579         g_assert_not_reached ();
1580     }
1582     return retval;
1585 /**
1586  * Scroll event handler for the canvas.
1587  *
1588  * \todo FIXME: generate motion events to re-select items.
1589  */
1590 static gint
1591 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1593     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1596 static inline void request_motions(GdkWindow *w, GdkEventMotion *event) {
1597     gdk_window_get_pointer(w, NULL, NULL, NULL);
1598 #if HAS_GDK_EVENT_REQUEST_MOTIONS
1599     gdk_event_request_motions(event);
1600 #endif
1603 /**
1604  * Motion event handler for the canvas.
1605  */
1606 static int
1607 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1609     int status;
1610     SPCanvas *canvas = SP_CANVAS (widget);
1612     track_latency((GdkEvent *)event);
1614     if (event->window != SP_CANVAS_WINDOW (canvas))
1615         return FALSE;
1617     if (canvas->pixmap_gc == NULL) // canvas being deleted
1618         return FALSE;
1620     canvas->state = event->state;
1621     pick_current_item (canvas, (GdkEvent *) event);
1622     status = emit_event (canvas, (GdkEvent *) event);
1623     if (event->is_hint) {
1624         request_motions(widget->window, event);
1625     }
1627     return status;
1630 static void
1631 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)
1633     GtkWidget *widget = GTK_WIDGET (canvas);
1635     SPCanvasBuf buf;
1636     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1637         buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1638     } else {
1639         buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1640     }
1642     // Mark the region clean
1643     sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1645     buf.buf_rowstride = sw * 4;
1646     buf.rect.x0 = x0;
1647     buf.rect.y0 = y0;
1648     buf.rect.x1 = x1;
1649     buf.rect.y1 = y1;
1650     buf.visible_rect.x0 = draw_x1;
1651     buf.visible_rect.y0 = draw_y1;
1652     buf.visible_rect.x1 = draw_x2;
1653     buf.visible_rect.y1 = draw_y2;
1654     GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1655     buf.bg_color = (((color->red & 0xff00) << 8)
1656                     | (color->green & 0xff00)
1657                     | (color->blue >> 8));
1658     buf.is_empty = true;
1660     buf.ct = nr_create_cairo_context_canvasbuf (&(buf.visible_rect), &buf);
1662     if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1663         SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1664     }
1666 #if ENABLE_LCMS
1667     cmsHTRANSFORM transf = 0;
1668     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1669     bool fromDisplay = prefs->getBool( "/options/displayprofile/from_display");
1670     if ( fromDisplay ) {
1671         transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" );
1672     } else {
1673         transf = Inkscape::colorprofile_get_display_transform();
1674     }
1675 #endif // ENABLE_LCMS
1677     if (buf.is_empty) {
1678 #if ENABLE_LCMS
1679         if ( transf && canvas->enable_cms_display_adj ) {
1680             cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1681         }
1682 #endif // ENABLE_LCMS
1683         gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1684         gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1685                             canvas->pixmap_gc,
1686                             TRUE,
1687                             x0 - canvas->x0, y0 - canvas->y0,
1688                             x1 - x0, y1 - y0);
1689     } else {
1691 #if ENABLE_LCMS
1692         if ( transf && canvas->enable_cms_display_adj ) {
1693             for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1694                 guchar* p = buf.buf + (buf.buf_rowstride * yy);
1695                 cmsDoTransform( transf, p, p, (x1 - x0) );
1696             }
1697         }
1698 #endif // ENABLE_LCMS
1700 // Now we only need to output the prepared pixmap to the actual screen, and this define chooses one
1701 // of the two ways to do it. The cairo way is direct and straightforward, but unfortunately
1702 // noticeably slower. I asked Carl Worth but he was unable so far to suggest any specific reason
1703 // for this slowness. So, for now we use the oldish method: squeeze out 32bpp buffer to 24bpp and
1704 // use gdk_draw_rgb_image_dithalign, for unfortunately gdk can only handle 24 bpp, which cairo
1705 // cannot handle at all. Still, this way is currently faster even despite the blit with squeeze.
1707 ///#define CANVAS_OUTPUT_VIA_CAIRO
1709 #ifdef CANVAS_OUTPUT_VIA_CAIRO
1711         buf.cst = cairo_image_surface_create_for_data (
1712             buf.buf,
1713             CAIRO_FORMAT_ARGB32,  // unpacked, i.e. 32 bits! one byte is unused
1714             x1 - x0, y1 - y0,
1715             buf.buf_rowstride
1716             );
1717         cairo_t *window_ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1718         cairo_set_source_surface (window_ct, buf.cst, x0 - canvas->x0, y0 - canvas->y0);
1719         cairo_paint (window_ct);
1720         cairo_destroy (window_ct);
1721         cairo_surface_finish (buf.cst);
1722         cairo_surface_destroy (buf.cst);
1724 #else
1726         NRPixBlock b3;
1727         nr_pixblock_setup_fast (&b3, NR_PIXBLOCK_MODE_R8G8B8, x0, y0, x1, y1, TRUE);
1729         NRPixBlock b4;
1730         nr_pixblock_setup_extern (&b4, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1,
1731                                   buf.buf,
1732                                   buf.buf_rowstride,
1733                                   FALSE, FALSE);
1735         // this does the 32->24 squishing, using an assembler routine:
1736         nr_blit_pixblock_pixblock (&b3, &b4);
1738         gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1739                                       canvas->pixmap_gc,
1740                                       x0 - canvas->x0, y0 - canvas->y0,
1741                                       x1 - x0, y1 - y0,
1742                                       GDK_RGB_DITHER_MAX,
1743                                       NR_PIXBLOCK_PX(&b3),
1744                                       sw * 3,
1745                                       x0 - canvas->x0, y0 - canvas->y0);
1747         nr_pixblock_release (&b3);
1748         nr_pixblock_release (&b4);
1749 #endif
1750     }
1752     cairo_surface_t *cst = cairo_get_target(buf.ct);
1753     cairo_destroy (buf.ct);
1754     cairo_surface_finish (cst);
1755     cairo_surface_destroy (cst);
1757     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1758         nr_pixelstore_256K_free (buf.buf);
1759     } else {
1760         nr_pixelstore_1M_free (buf.buf);
1761     }
1764 struct PaintRectSetup {
1765     SPCanvas* canvas;
1766     NRRectL big_rect;
1767     GTimeVal start_time;
1768     int max_pixels;
1769     Geom::Point mouse_loc;
1770 };
1772 /**
1773  * Paint the given rect, recursively subdividing the region until it is the size of a single
1774  * buffer.
1775  *
1776  * @return true if the drawing completes
1777  */
1778 static int
1779 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1781     GTimeVal now;
1782     g_get_current_time (&now);
1784     glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1785         + (now.tv_usec - setup->start_time.tv_usec);
1787     // Allow only very fast buffers to be run together;
1788     // as soon as the total redraw time exceeds 1ms, cancel;
1789     // this returns control to the idle loop and allows Inkscape to process user input
1790     // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1791     // it will get back and finish painting what remains to paint.
1792     if (elapsed > 1000) {
1794         // Interrupting redraw isn't always good.
1795         // For example, when you drag one node of a big path, only the buffer containing
1796         // the mouse cursor will be redrawn again and again, and the rest of the path
1797         // will remain stale because Inkscape never has enough idle time to redraw all
1798         // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1799         // If this limit is set, and if we have aborted redraw more times than is allowed,
1800         // interrupting is blocked and we're forced to redraw full screen once
1801         // (after which we can again interrupt forced_redraw_limit times).
1802         if (setup->canvas->forced_redraw_limit < 0 ||
1803             setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1805             if (setup->canvas->forced_redraw_limit != -1) {
1806                 setup->canvas->forced_redraw_count++;
1807             }
1809             return false;
1810         }
1811     }
1813     // Find the optimal buffer dimensions
1814     int bw = this_rect.x1 - this_rect.x0;
1815     int bh = this_rect.y1 - this_rect.y0;
1816     if ((bw < 1) || (bh < 1))
1817         return 0;
1819     if (bw * bh < setup->max_pixels) {
1820         // We are small enough
1821         sp_canvas_paint_single_buffer (setup->canvas,
1822                                        this_rect.x0, this_rect.y0,
1823                                        this_rect.x1, this_rect.y1,
1824                                        setup->big_rect.x0, setup->big_rect.y0,
1825                                        setup->big_rect.x1, setup->big_rect.y1, bw);
1826         return 1;
1827     }
1829     NRRectL lo = this_rect;
1830     NRRectL hi = this_rect;
1832 /*
1833 This test determines the redraw strategy:
1835 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1836 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1837 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1838 and seems to be faster for drawings with many smaller objects at zoom-out.
1840 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1841 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1842 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1843 faster.
1845 The default for now is the strips mode.
1846 */
1847     if (bw < bh || bh < 2 * TILE_SIZE) {
1848         // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1849         int mid = ((long long) this_rect.x0 + (long long) this_rect.x1) / 2;
1850         // Make sure that mid lies on a tile boundary
1851         mid = (mid / TILE_SIZE) * TILE_SIZE;
1853         lo.x1 = mid;
1854         hi.x0 = mid;
1856         if (setup->mouse_loc[Geom::X] < mid) {
1857             // Always paint towards the mouse first
1858             return sp_canvas_paint_rect_internal(setup, lo)
1859                 && sp_canvas_paint_rect_internal(setup, hi);
1860         } else {
1861             return sp_canvas_paint_rect_internal(setup, hi)
1862                 && sp_canvas_paint_rect_internal(setup, lo);
1863         }
1864     } else {
1865         // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1866         int mid = ((long long) this_rect.y0 + (long long) this_rect.y1) / 2;
1867         // Make sure that mid lies on a tile boundary
1868         mid = (mid / TILE_SIZE) * TILE_SIZE;
1870         lo.y1 = mid;
1871         hi.y0 = mid;
1873         if (setup->mouse_loc[Geom::Y] < mid) {
1874             // Always paint towards the mouse first
1875             return sp_canvas_paint_rect_internal(setup, lo)
1876                 && sp_canvas_paint_rect_internal(setup, hi);
1877         } else {
1878             return sp_canvas_paint_rect_internal(setup, hi)
1879                 && sp_canvas_paint_rect_internal(setup, lo);
1880         }
1881     }
1885 /**
1886  * Helper that draws a specific rectangular part of the canvas.
1887  *
1888  * @return true if the rectangle painting succeeds.
1889  */
1890 static bool
1891 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1893     g_return_val_if_fail (!canvas->need_update, false);
1895     NRRectL rect;
1896     rect.x0 = xx0;
1897     rect.x1 = xx1;
1898     rect.y0 = yy0;
1899     rect.y1 = yy1;
1901     // Clip rect-to-draw by the current visible area
1902     rect.x0 = MAX (rect.x0, canvas->x0);
1903     rect.y0 = MAX (rect.y0, canvas->y0);
1904     rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1905     rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1907 #ifdef DEBUG_REDRAW
1908     // paint the area to redraw yellow
1909     gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1910     gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1911                         canvas->pixmap_gc,
1912                         TRUE,
1913                         rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1914                         rect.x1 - rect.x0, rect.y1 - rect.y0);
1915 #endif
1917     PaintRectSetup setup;
1919     setup.canvas = canvas;
1920     setup.big_rect = rect;
1922     // Save the mouse location
1923     gint x, y;
1924     gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1925     setup.mouse_loc = sp_canvas_window_to_world (canvas, Geom::Point(x,y));
1927     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1928         // use 256K as a compromise to not slow down gradients
1929         // 256K is the cached buffer and we need 4 channels
1930         setup.max_pixels = 65536; // 256K/4
1931     } else {
1932         // paths only, so 1M works faster
1933         // 1M is the cached buffer and we need 4 channels
1934         setup.max_pixels = 262144;
1935     }
1937     // Start the clock
1938     g_get_current_time(&(setup.start_time));
1940     // Go
1941     return sp_canvas_paint_rect_internal(&setup, rect);
1944 /**
1945  * Force a full redraw after a specified number of interrupted redraws
1946  */
1947 void
1948 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1949   g_return_if_fail(canvas != NULL);
1951   canvas->forced_redraw_limit = count;
1952   canvas->forced_redraw_count = 0;
1955 /**
1956  * End forced full redraw requests
1957  */
1958 void
1959 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1960   g_return_if_fail(canvas != NULL);
1962   canvas->forced_redraw_limit = -1;
1965 /**
1966  * The canvas widget's expose callback.
1967  */
1968 static gint
1969 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1971     SPCanvas *canvas = SP_CANVAS (widget);
1973     if (!GTK_WIDGET_DRAWABLE (widget) ||
1974         (event->window != SP_CANVAS_WINDOW (canvas)))
1975         return FALSE;
1977     int n_rects;
1978     GdkRectangle *rects;
1979     gdk_region_get_rectangles (event->region, &rects, &n_rects);
1981     for (int i = 0; i < n_rects; i++) {
1982         NRRectL rect;
1984         rect.x0 = rects[i].x + canvas->x0;
1985         rect.y0 = rects[i].y + canvas->y0;
1986         rect.x1 = rect.x0 + rects[i].width;
1987         rect.y1 = rect.y0 + rects[i].height;
1989         sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1990     }
1992     if (n_rects > 0)
1993         g_free (rects);
1995     return FALSE;
1998 /**
1999  * The canvas widget's keypress callback.
2000  */
2001 static gint
2002 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
2004     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
2007 /**
2008  * Crossing event handler for the canvas.
2009  */
2010 static gint
2011 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
2013     SPCanvas *canvas = SP_CANVAS (widget);
2015     if (event->window != SP_CANVAS_WINDOW (canvas))
2016         return FALSE;
2018     canvas->state = event->state;
2019     return pick_current_item (canvas, (GdkEvent *) event);
2022 /**
2023  * Focus in handler for the canvas.
2024  */
2025 static gint
2026 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
2028     GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
2030     SPCanvas *canvas = SP_CANVAS (widget);
2032     if (canvas->focused_item) {
2033         return emit_event (canvas, (GdkEvent *) event);
2034     } else {
2035         return FALSE;
2036     }
2039 /**
2040  * Focus out handler for the canvas.
2041  */
2042 static gint
2043 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
2045     GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2047     SPCanvas *canvas = SP_CANVAS (widget);
2049     if (canvas->focused_item)
2050         return emit_event (canvas, (GdkEvent *) event);
2051     else
2052         return FALSE;
2055 /**
2056  * Helper that repaints the areas in the canvas that need it.
2057  *
2058  * @return true if all the dirty parts have been redrawn
2059  */
2060 static int
2061 paint (SPCanvas *canvas)
2063     if (canvas->need_update) {
2064         sp_canvas_item_invoke_update (canvas->root, Geom::identity(), 0);
2065         canvas->need_update = FALSE;
2066     }
2068     if (!canvas->need_redraw)
2069         return TRUE;
2071     Gdk::Region to_paint;
2073     for (int j=canvas->tTop; j<canvas->tBottom; j++) {
2074         for (int i=canvas->tLeft; i<canvas->tRight; i++) {
2075             int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
2077             if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
2078                 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
2079                                    TILE_SIZE, TILE_SIZE));
2080             }
2082         }
2083     }
2085     if (!to_paint.empty()) {
2086         Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
2087         typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
2088         for (Iter i=rect.begin(); i != rect.end(); ++i) {
2089             int x0 = (*i).get_x();
2090             int y0 = (*i).get_y();
2091             int x1 = x0 + (*i).get_width();
2092             int y1 = y0 + (*i).get_height();
2093             if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
2094                 // Aborted
2095                 return FALSE;
2096             };
2097         }
2098     }
2100     canvas->need_redraw = FALSE;
2102     // we've had a full unaborted redraw, reset the full redraw counter
2103     if (canvas->forced_redraw_limit != -1) {
2104         canvas->forced_redraw_count = 0;
2105     }
2107     return TRUE;
2110 /**
2111  * Helper that invokes update, paint, and repick on canvas.
2112  */
2113 static int
2114 do_update (SPCanvas *canvas)
2116     if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop during interrupted display!
2117         return TRUE;
2118         
2119     if (canvas->drawing_disabled)
2120         return TRUE;
2122     /* Cause the update if necessary */
2123     if (canvas->need_update) {
2124         sp_canvas_item_invoke_update (canvas->root, Geom::identity(), 0);
2125         canvas->need_update = FALSE;
2126     }
2128     /* Paint if able to */
2129     if (GTK_WIDGET_DRAWABLE (canvas)) {
2130             return paint (canvas);
2131     }
2133     /* Pick new current item */
2134     while (canvas->need_repick) {
2135         canvas->need_repick = FALSE;
2136         pick_current_item (canvas, &canvas->pick_event);
2137     }
2139     return TRUE;
2142 /**
2143  * Idle handler for the canvas that deals with pending updates and redraws.
2144  */
2145 static gint
2146 idle_handler (gpointer data)
2148     GDK_THREADS_ENTER ();
2150     SPCanvas *canvas = SP_CANVAS (data);
2152     int const ret = do_update (canvas);
2154     if (ret) {
2155         /* Reset idle id */
2156         canvas->idle_id = 0;
2157     }
2159     GDK_THREADS_LEAVE ();
2161     return !ret;
2164 /**
2165  * Convenience function to add an idle handler to a canvas.
2166  */
2167 static void
2168 add_idle (SPCanvas *canvas)
2170     if (canvas->idle_id != 0)
2171         return;
2173     canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2176 /**
2177  * Returns the root group of the specified canvas.
2178  */
2179 SPCanvasGroup *
2180 sp_canvas_root (SPCanvas *canvas)
2182     g_return_val_if_fail (canvas != NULL, NULL);
2183     g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2185     return SP_CANVAS_GROUP (canvas->root);
2188 /**
2189  * Scrolls canvas to specific position (cx and cy are measured in screen pixels)
2190  */
2191 void
2192 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2194     g_return_if_fail (canvas != NULL);
2195     g_return_if_fail (SP_IS_CANVAS (canvas));
2197     int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels)
2198     int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do!
2199     int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the
2200     int dy = iy - canvas->y0; // canvas w.r.t its previous position
2202     canvas->dx0 = cx; // here the 'd' stands for double, not delta!
2203     canvas->dy0 = cy;
2204     canvas->x0 = ix;
2205     canvas->y0 = iy;
2207     sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2209     if (!clear) {
2210         // scrolling without zoom; redraw only the newly exposed areas
2211         if ((dx != 0) || (dy != 0)) {
2212             canvas->is_scrolling = is_scrolling;
2213             if (GTK_WIDGET_REALIZED (canvas)) {
2214                 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2215             }
2216         }
2217     } else {
2218         // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2219     }
2223 /**
2224  * Updates canvas if necessary.
2225  */
2226 void
2227 sp_canvas_update_now (SPCanvas *canvas)
2229     g_return_if_fail (canvas != NULL);
2230     g_return_if_fail (SP_IS_CANVAS (canvas));
2232     if (!(canvas->need_update ||
2233           canvas->need_redraw))
2234         return;
2236     do_update (canvas);
2239 /**
2240  * Update callback for canvas widget.
2241  */
2242 static void
2243 sp_canvas_request_update (SPCanvas *canvas)
2245     canvas->need_update = TRUE;
2246     add_idle (canvas);
2249 /**
2250  * Forces redraw of rectangular canvas area.
2251  */
2252 void
2253 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2255     NRRectL bbox;
2256     NRRectL visible;
2257     NRRectL clip;
2259     g_return_if_fail (canvas != NULL);
2260     g_return_if_fail (SP_IS_CANVAS (canvas));
2262     if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2263     if ((x0 >= x1) || (y0 >= y1)) return;
2265     bbox.x0 = x0;
2266     bbox.y0 = y0;
2267     bbox.x1 = x1;
2268     bbox.y1 = y1;
2270     visible.x0 = canvas->x0;
2271     visible.y0 = canvas->y0;
2272     visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2273     visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2275     nr_rect_l_intersect (&clip, &bbox, &visible);
2277     sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2278     add_idle (canvas);
2281 /**
2282  * Sets world coordinates from win and canvas.
2283  */
2284 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2286     g_return_if_fail (canvas != NULL);
2287     g_return_if_fail (SP_IS_CANVAS (canvas));
2289     if (worldx) *worldx = canvas->x0 + winx;
2290     if (worldy) *worldy = canvas->y0 + winy;
2293 /**
2294  * Sets win coordinates from world and canvas.
2295  */
2296 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2298     g_return_if_fail (canvas != NULL);
2299     g_return_if_fail (SP_IS_CANVAS (canvas));
2301     if (winx) *winx = worldx - canvas->x0;
2302     if (winy) *winy = worldy - canvas->y0;
2305 /**
2306  * Converts point from win to world coordinates.
2307  */
2308 Geom::Point sp_canvas_window_to_world(SPCanvas const *canvas, Geom::Point const win)
2310     g_assert (canvas != NULL);
2311     g_assert (SP_IS_CANVAS (canvas));
2313     return Geom::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2316 /**
2317  * Converts point from world to win coordinates.
2318  */
2319 Geom::Point sp_canvas_world_to_window(SPCanvas const *canvas, Geom::Point const world)
2321     g_assert (canvas != NULL);
2322     g_assert (SP_IS_CANVAS (canvas));
2324     return Geom::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2327 /**
2328  * Returns true if point given in world coordinates is inside window.
2329  */
2330 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, Geom::Point const &world)
2332     g_assert( canvas != NULL );
2333     g_assert(SP_IS_CANVAS(canvas));
2335     GtkWidget const &w = *GTK_WIDGET(canvas);
2336     return ( ( canvas->x0 <= world[Geom::X] )  &&
2337              ( canvas->y0 <= world[Geom::Y] )  &&
2338              ( world[Geom::X] < canvas->x0 + w.allocation.width )  &&
2339              ( world[Geom::Y] < canvas->y0 + w.allocation.height ) );
2342 /**
2343  * Return canvas window coordinates as Geom::Rect.
2344  */
2345 Geom::Rect SPCanvas::getViewbox() const
2347     GtkWidget const *w = GTK_WIDGET(this);
2348     return Geom::Rect(Geom::Point(dx0, dy0),
2349                       Geom::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2352 /**
2353  * Return canvas window coordinates as IRect (a rectangle defined by integers).
2354  */
2355 NR::IRect SPCanvas::getViewboxIntegers() const
2357     GtkWidget const *w = GTK_WIDGET(this);
2358     return NR::IRect(NR::IPoint(x0, y0),
2359                     NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height));
2362 inline int sp_canvas_tile_floor(int x)
2364     return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2367 inline int sp_canvas_tile_ceil(int x)
2369     return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2372 /**
2373  * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2374  */
2375 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2377     if ( nl >= nr || nt >= nb ) {
2378         if ( canvas->tiles ) g_free(canvas->tiles);
2379         canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2380         canvas->tileH=canvas->tileV=0;
2381         canvas->tiles=NULL;
2382         return;
2383     }
2384     int tl=sp_canvas_tile_floor(nl);
2385     int tt=sp_canvas_tile_floor(nt);
2386     int tr=sp_canvas_tile_ceil(nr);
2387     int tb=sp_canvas_tile_ceil(nb);
2389     int nh = tr-tl, nv = tb-tt;
2390     uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2391     for (int i=tl; i<tr; i++) {
2392         for (int j=tt; j<tb; j++) {
2393             int ind = (i-tl) + (j-tt)*nh;
2394             if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2395                 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2396             } else {
2397                 ntiles[ind]=0; // newly exposed areas get 0
2398             }
2399         }
2400     }
2401     if ( canvas->tiles ) g_free(canvas->tiles);
2402     canvas->tiles=ntiles;
2403     canvas->tLeft=tl;
2404     canvas->tTop=tt;
2405     canvas->tRight=tr;
2406     canvas->tBottom=tb;
2407     canvas->tileH=nh;
2408     canvas->tileV=nv;
2411 /*
2412  * Helper that queues a canvas rectangle for redraw
2413  */
2414 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2415     canvas->need_redraw = TRUE;
2417     sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2420 /**
2421  * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2422  */
2423 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2425     if ( nl >= nr || nt >= nb ) {
2426         return;
2427     }
2428     int tl=sp_canvas_tile_floor(nl);
2429     int tt=sp_canvas_tile_floor(nt);
2430     int tr=sp_canvas_tile_ceil(nr);
2431     int tb=sp_canvas_tile_ceil(nb);
2432     if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2433     if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2434     if ( tr > canvas->tRight ) tr=canvas->tRight;
2435     if ( tt < canvas->tTop ) tt=canvas->tTop;
2436     if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2438     for (int i=tl; i<tr; i++) {
2439         for (int j=tt; j<tb; j++) {
2440             canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2441         }
2442     }
2446 /*
2447   Local Variables:
2448   mode:c++
2449   c-file-style:"stroustrup"
2450   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2451   indent-tabs-mode:nil
2452   fill-column:99
2453   End:
2454 */
2455 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :