Code

Split SPCanvasItem and SPCanvasGroup to individual .h files. Removed forward header.
[inkscape.git] / src / display / sp-canvas.cpp
1 /** \file
2  * Port of GnomeCanvas for Inkscape needs
3  *
4  * Authors:
5  *   Federico Mena <federico@nuclecu.unam.mx>
6  *   Raph Levien <raph@gimp.org>
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   fred
9  *   bbyak
10  *
11  * Copyright (C) 1998 The Free Software Foundation
12  * Copyright (C) 2002-2006 authors
13  *
14  * Released under GNU GPL, read the file 'COPYING' for more information
15  */
17 #ifdef HAVE_CONFIG_H
18 # include "config.h"
19 #endif
21 #include <libnr/nr-pixblock.h>
23 #include <gtk/gtkmain.h>
24 #include <gtk/gtksignal.h>
25 #include <gtk/gtkversion.h>
27 #include <gtkmm.h>
29 #include "helper/sp-marshal.h"
30 #include <helper/recthull.h>
31 #include "display/sp-canvas.h"
32 #include "display/sp-canvas-group.h"
33 #include <2geom/matrix.h>
34 #include "libnr/nr-convex-hull.h"
35 #include "preferences.h"
36 #include "inkscape.h"
37 #include "sodipodi-ctrlrect.h"
38 #if ENABLE_LCMS
39 #include "color-profile-fns.h"
40 #endif // ENABLE_LCMS
41 #include "display/rendermode.h"
42 #include "libnr/nr-blit.h"
43 #include "display/inkscape-cairo.h"
44 #include "debug/gdk-event-latency-tracker.h"
45 #include "desktop.h"
46 #include "sp-namedview.h"
48 using Inkscape::Debug::GdkEventLatencyTracker;
50 // GTK_CHECK_VERSION returns false on failure
51 #define HAS_GDK_EVENT_REQUEST_MOTIONS GTK_CHECK_VERSION(2, 12, 0)
53 // gtk_check_version returns non-NULL on failure
54 static bool const HAS_BROKEN_MOTION_HINTS =
55   true || gtk_check_version(2, 12, 0) != NULL || !HAS_GDK_EVENT_REQUEST_MOTIONS;
57 // Define this to visualize the regions to be redrawn
58 //#define DEBUG_REDRAW 1;
60 // Tiles are a way to minimize the number of redraws, eliminating too small redraws.
61 // The canvas stores a 2D array of ints, each representing a TILE_SIZExTILE_SIZE pixels tile.
62 // If any part of it is dirtied, the entire tile is dirtied (its int is nonzero) and repainted.
63 #define TILE_SIZE 16
65 static gint const sp_canvas_update_priority = G_PRIORITY_HIGH_IDLE;
67 #define SP_CANVAS_WINDOW(c) (((GtkWidget *) (c))->window)
69 enum {
70     SP_CANVAS_ITEM_VISIBLE = 1 << 7,
71     SP_CANVAS_ITEM_NEED_UPDATE = 1 << 8,
72     SP_CANVAS_ITEM_NEED_AFFINE = 1 << 9
73 };
75 /**
76  * A group of Items.
77  */
78 struct SPCanvasGroup {
79     SPCanvasItem item;
81     GList *items, *last;
82 };
84 /**
85  * The SPCanvasGroup vtable.
86  */
87 struct SPCanvasGroupClass {
88     SPCanvasItemClass parent_class;
89 };
91 /**
92  * The SPCanvas vtable.
93  */
94 struct SPCanvasClass {
95     GtkWidgetClass parent_class;
96 };
98 static void group_add (SPCanvasGroup *group, SPCanvasItem *item);
99 static void group_remove (SPCanvasGroup *group, SPCanvasItem *item);
101 /* SPCanvasItem */
103 enum {ITEM_EVENT, ITEM_LAST_SIGNAL};
104 enum {PROP_0, PROP_VISIBLE};
107 static void sp_canvas_request_update (SPCanvas *canvas);
109 static void track_latency(GdkEvent const *event);
110 static void sp_canvas_item_class_init (SPCanvasItemClass *klass);
111 static void sp_canvas_item_init (SPCanvasItem *item);
112 static void sp_canvas_item_dispose (GObject *object);
113 static void sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args);
115 static int emit_event (SPCanvas *canvas, GdkEvent *event);
116 static int pick_current_item (SPCanvas *canvas, GdkEvent *event);
118 static guint item_signals[ITEM_LAST_SIGNAL] = { 0 };
120 /**
121  * Registers the SPCanvasItem class with Glib and returns its type number.
122  */
123 GType
124 sp_canvas_item_get_type (void)
126     static GType type = 0;
127     if (!type) {
128         static GTypeInfo const info = {
129             sizeof (SPCanvasItemClass),
130             NULL, NULL,
131             (GClassInitFunc) sp_canvas_item_class_init,
132             NULL, NULL,
133             sizeof (SPCanvasItem),
134             0,
135             (GInstanceInitFunc) sp_canvas_item_init,
136             NULL
137         };
138         type = g_type_register_static (GTK_TYPE_OBJECT, "SPCanvasItem", &info, (GTypeFlags)0);
139     }
141     return type;
144 /**
145  * Initializes the SPCanvasItem vtable and the "event" signal.
146  */
147 static void
148 sp_canvas_item_class_init (SPCanvasItemClass *klass)
150     GObjectClass *object_class = (GObjectClass *) klass;
152     item_signals[ITEM_EVENT] = g_signal_new ("event",
153                                              G_TYPE_FROM_CLASS (klass),
154                                              G_SIGNAL_RUN_LAST,
155                                              ((glong)((guint8*)&(klass->event) - (guint8*)klass)),
156                                              NULL, NULL,
157                                              sp_marshal_BOOLEAN__POINTER,
158                                              G_TYPE_BOOLEAN, 1,
159                                              GDK_TYPE_EVENT);
161     object_class->dispose = sp_canvas_item_dispose;
164 /**
165  * Callback for initialization of SPCanvasItem.
166  */
167 static void
168 sp_canvas_item_init (SPCanvasItem *item)
170     // TODO items should not be visible on creation - this causes kludges with items
171     // that should be initially invisible; examples of such items: node handles, the CtrlRect
172     // used for rubberbanding, path outline, etc.
173     item->flags |= SP_CANVAS_ITEM_VISIBLE;
174     item->xform = Geom::Matrix(Geom::identity());
177 /**
178  * Constructs new SPCanvasItem on SPCanvasGroup.
179  */
180 SPCanvasItem *
181 sp_canvas_item_new (SPCanvasGroup *parent, GtkType type, gchar const *first_arg_name, ...)
183     va_list args;
185     g_return_val_if_fail (parent != NULL, NULL);
186     g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL);
187     g_return_val_if_fail (gtk_type_is_a (type, sp_canvas_item_get_type ()), NULL);
189     SPCanvasItem *item = SP_CANVAS_ITEM (gtk_type_new (type));
191     va_start (args, first_arg_name);
192     sp_canvas_item_construct (item, parent, first_arg_name, args);
193     va_end (args);
195     return item;
198 /**
199  * Sets up the newly created SPCanvasItem.
200  *
201  * We make it static for encapsulation reasons since it was nowhere used.
202  */
203 static void
204 sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args)
206     g_return_if_fail (SP_IS_CANVAS_GROUP (parent));
207     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
209     item->parent = SP_CANVAS_ITEM (parent);
210     item->canvas = item->parent->canvas;
212     g_object_set_valist (G_OBJECT (item), first_arg_name, args);
214     group_add (SP_CANVAS_GROUP (item->parent), item);
216     sp_canvas_item_request_update (item);
219 /**
220  * Helper function that requests redraw only if item's visible flag is set.
221  */
222 static void
223 redraw_if_visible (SPCanvasItem *item)
225     if (item->flags & SP_CANVAS_ITEM_VISIBLE) {
226         int x0 = (int)(item->x1);
227         int x1 = (int)(item->x2);
228         int y0 = (int)(item->y1);
229         int y1 = (int)(item->y2);
231         if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
232             sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
233         }
234     }
237 /**
238  * Callback that removes item from all referers and destroys it.
239  */
240 static void
241 sp_canvas_item_dispose (GObject *object)
243     SPCanvasItem *item = SP_CANVAS_ITEM (object);
245     // Hack: if this is a ctrlrect, move it to 0,0;
246     // this redraws only the stroke of the rect to be deleted,
247     // avoiding redraw of the entire area
248     if (SP_IS_CTRLRECT(item)) {
249         SP_CTRLRECT(object)->setRectangle(Geom::Rect(Geom::Point(0,0),Geom::Point(0,0)));
250         SP_CTRLRECT(object)->update(item->xform, 0);
251     } else {
252         redraw_if_visible (item);
253     }
254     item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
256     if (item == item->canvas->current_item) {
257         item->canvas->current_item = NULL;
258         item->canvas->need_repick = TRUE;
259     }
261     if (item == item->canvas->new_current_item) {
262         item->canvas->new_current_item = NULL;
263         item->canvas->need_repick = TRUE;
264     }
266     if (item == item->canvas->grabbed_item) {
267         item->canvas->grabbed_item = NULL;
268         gdk_pointer_ungrab (GDK_CURRENT_TIME);
269     }
271     if (item == item->canvas->focused_item)
272         item->canvas->focused_item = NULL;
274     if (item->parent) {
275         group_remove (SP_CANVAS_GROUP (item->parent), item);
276     }
278     G_OBJECT_CLASS (g_type_class_peek(g_type_parent(sp_canvas_item_get_type())))->dispose (object);
281 /**
282  * Helper function to update item and its children.
283  *
284  * NB! affine is parent2canvas.
285  */
286 static void
287 sp_canvas_item_invoke_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags)
289     /* Apply the child item's transform */
290     Geom::Matrix child_affine = item->xform * affine;
292     /* apply object flags to child flags */
293     int child_flags = flags & ~SP_CANVAS_UPDATE_REQUESTED;
295     if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
296         child_flags |= SP_CANVAS_UPDATE_REQUESTED;
298     if (item->flags & SP_CANVAS_ITEM_NEED_AFFINE)
299         child_flags |= SP_CANVAS_UPDATE_AFFINE;
301     if (child_flags & (SP_CANVAS_UPDATE_REQUESTED | SP_CANVAS_UPDATE_AFFINE)) {
302         if (SP_CANVAS_ITEM_GET_CLASS (item)->update)
303             SP_CANVAS_ITEM_GET_CLASS (item)->update (item, child_affine, child_flags);
304     }
306     GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_UPDATE);
307     GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_AFFINE);
310 /**
311  * Helper function to invoke the point method of the item.
312  *
313  * The argument x, y should be in the parent's item-relative coordinate
314  * system.  This routine applies the inverse of the item's transform,
315  * maintaining the affine invariant.
316  */
317 static double
318 sp_canvas_item_invoke_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item)
320     if (SP_CANVAS_ITEM_GET_CLASS (item)->point)
321         return SP_CANVAS_ITEM_GET_CLASS (item)->point (item, p, actual_item);
323     return NR_HUGE;
326 /**
327  * Makes the item's affine transformation matrix be equal to the specified
328  * matrix.
329  *
330  * @item: A canvas item.
331  * @affine: An affine transformation matrix.
332  */
333 void
334 sp_canvas_item_affine_absolute (SPCanvasItem *item, Geom::Matrix const &affine)
336     item->xform = affine;
338     if (!(item->flags & SP_CANVAS_ITEM_NEED_AFFINE)) {
339         item->flags |= SP_CANVAS_ITEM_NEED_AFFINE;
340         if (item->parent != NULL) {
341             sp_canvas_item_request_update (item->parent);
342         } else {
343             sp_canvas_request_update (item->canvas);
344         }
345     }
347     item->canvas->need_repick = TRUE;
350 /**
351  * Convenience function to reorder items in a group's child list.
352  *
353  * This puts the specified link after the "before" link.
354  */
355 static void
356 put_item_after (GList *link, GList *before)
358     if (link == before)
359         return;
361     SPCanvasGroup *parent = SP_CANVAS_GROUP (SP_CANVAS_ITEM (link->data)->parent);
363     if (before == NULL) {
364         if (link == parent->items) return;
366         link->prev->next = link->next;
368         if (link->next) {
369             link->next->prev = link->prev;
370         } else {
371             parent->last = link->prev;
372         }
374         link->prev = before;
375         link->next = parent->items;
376         link->next->prev = link;
377         parent->items = link;
378     } else {
379         if ((link == parent->last) && (before == parent->last->prev))
380             return;
382         if (link->next)
383             link->next->prev = link->prev;
385         if (link->prev)
386             link->prev->next = link->next;
387         else {
388             parent->items = link->next;
389             parent->items->prev = NULL;
390         }
392         link->prev = before;
393         link->next = before->next;
395         link->prev->next = link;
397         if (link->next)
398             link->next->prev = link;
399         else
400             parent->last = link;
401     }
405 /**
406  * Raises the item in its parent's stack by the specified number of positions.
407  *
408  * \param item A canvas item.
409  * \param positions Number of steps to raise the item.
410  *
411  * If the number of positions is greater than the distance to the top of the
412  * stack, then the item is put at the top.
413  */
414 void
415 sp_canvas_item_raise (SPCanvasItem *item, int positions)
417     g_return_if_fail (item != NULL);
418     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
419     g_return_if_fail (positions >= 0);
421     if (!item->parent || positions == 0)
422         return;
424     SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
425     GList *link = g_list_find (parent->items, item);
426     g_assert (link != NULL);
428     GList *before;
429     for (before = link; positions && before; positions--)
430         before = before->next;
432     if (!before)
433         before = parent->last;
435     put_item_after (link, before);
437     redraw_if_visible (item);
438     item->canvas->need_repick = TRUE;
442 /**
443  * Lowers the item in its parent's stack by the specified number of positions.
444  *
445  * \param item A canvas item.
446  * \param positions Number of steps to lower the item.
447  *
448  * If the number of positions is greater than the distance to the bottom of the
449  * stack, then the item is put at the bottom.
450  **/
451 void
452 sp_canvas_item_lower (SPCanvasItem *item, int positions)
454     g_return_if_fail (item != NULL);
455     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
456     g_return_if_fail (positions >= 1);
458     if (!item->parent || positions == 0)
459         return;
461     SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
462     GList *link = g_list_find (parent->items, item);
463     g_assert (link != NULL);
465     GList *before;
466     if (link->prev)
467         for (before = link->prev; positions && before; positions--)
468             before = before->prev;
469     else
470         before = NULL;
472     put_item_after (link, before);
474     redraw_if_visible (item);
475     item->canvas->need_repick = TRUE;
478 bool
479 sp_canvas_item_is_visible (SPCanvasItem *item)
481     return item->flags & SP_CANVAS_ITEM_VISIBLE;
485 /**
486  * Sets visible flag on item and requests a redraw.
487  */
488 void
489 sp_canvas_item_show (SPCanvasItem *item)
491     g_return_if_fail (item != NULL);
492     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
494     if (item->flags & SP_CANVAS_ITEM_VISIBLE)
495         return;
497     item->flags |= SP_CANVAS_ITEM_VISIBLE;
499     int x0 = (int)(item->x1);
500     int x1 = (int)(item->x2);
501     int y0 = (int)(item->y1);
502     int y1 = (int)(item->y2);
504     if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
505         sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
506         item->canvas->need_repick = TRUE;
507     }
510 /**
511  * Clears visible flag on item and requests a redraw.
512  */
513 void
514 sp_canvas_item_hide (SPCanvasItem *item)
516     g_return_if_fail (item != NULL);
517     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
519     if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
520         return;
522     item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
524     int x0 = (int)(item->x1);
525     int x1 = (int)(item->x2);
526     int y0 = (int)(item->y1);
527     int y1 = (int)(item->y2);
529     if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
530         sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
531         item->canvas->need_repick = TRUE;
532     }
535 /**
536  * Grab item under cursor.
537  *
538  * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE
539  */
540 int
541 sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
543     g_return_val_if_fail (item != NULL, -1);
544     g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1);
545     g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1);
547     if (item->canvas->grabbed_item)
548         return -1;
550     // This test disallows grabbing events by an invisible item, which may be useful
551     // sometimes. An example is the hidden control point used for the selector component,
552     // where it is used for object selection and rubberbanding. There seems to be nothing
553     // preventing this except this test, so I removed it.
554     // -- Krzysztof KosiÅ„ski, 2009.08.12
555     //if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
556     //    return -1;
558     if (HAS_BROKEN_MOTION_HINTS) {
559         event_mask &= ~GDK_POINTER_MOTION_HINT_MASK;
560     }
562     /* fixme: Top hack (Lauris) */
563     /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
564     /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
565     gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
566                       (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
567                       NULL, cursor, etime);
569     item->canvas->grabbed_item = item;
570     item->canvas->grabbed_event_mask = event_mask;
571     item->canvas->current_item = item; /* So that events go to the grabbed item */
573     return 0;
576 /**
577  * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
578  * mouse.
579  *
580  * \param item A canvas item that holds a grab.
581  * \param etime The timestamp for ungrabbing the mouse.
582  */
583 void
584 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
586     g_return_if_fail (item != NULL);
587     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
589     if (item->canvas->grabbed_item != item)
590         return;
592     item->canvas->grabbed_item = NULL;
594     gdk_pointer_ungrab (etime);
597 /**
598  * Returns the product of all transformation matrices from the root item down
599  * to the item.
600  */
601 Geom::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
603     g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
605     Geom::Matrix affine = Geom::identity();
607     while (item) {
608         affine *= item->xform;
609         item = item->parent;
610     }
611     return affine;
614 /**
615  * Helper that returns true iff item is descendant of parent.
616  */
617 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
619     while (item) {
620         if (item == parent)
621             return true;
622         item = item->parent;
623     }
625     return false;
628 /**
629  * Focus canvas, and item under cursor if it is not already focussed.
630  */
631 void
632 sp_canvas_item_grab_focus (SPCanvasItem *item)
634     g_return_if_fail (item != NULL);
635     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
636     g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
638     SPCanvasItem *focused_item = item->canvas->focused_item;
640     if (focused_item) {
641         GdkEvent ev;
642         ev.focus_change.type = GDK_FOCUS_CHANGE;
643         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
644         ev.focus_change.send_event = FALSE;
645         ev.focus_change.in = FALSE;
647         emit_event (item->canvas, &ev);
648     }
650     item->canvas->focused_item = item;
651     gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
653     if (focused_item) {
654         GdkEvent ev;
655         ev.focus_change.type = GDK_FOCUS_CHANGE;
656         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
657         ev.focus_change.send_event = FALSE;
658         ev.focus_change.in = TRUE;
660         emit_event (item->canvas, &ev);
661     }
664 /**
665  * Requests that the canvas queue an update for the specified item.
666  *
667  * To be used only by item implementations.
668  */
669 void
670 sp_canvas_item_request_update (SPCanvasItem *item)
672     if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
673         return;
675     item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
677     if (item->parent != NULL) {
678         /* Recurse up the tree */
679         sp_canvas_item_request_update (item->parent);
680     } else {
681         /* Have reached the top of the tree, make sure the update call gets scheduled. */
682         sp_canvas_request_update (item->canvas);
683     }
686 /**
687  * Returns position of item in group.
688  */
689 gint sp_canvas_item_order (SPCanvasItem * item)
691     return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
694 /* SPCanvasGroup */
696 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
697 static void sp_canvas_group_init (SPCanvasGroup *group);
698 static void sp_canvas_group_destroy (GtkObject *object);
700 static void sp_canvas_group_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags);
701 static double sp_canvas_group_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item);
702 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
704 static SPCanvasItemClass *group_parent_class;
706 /**
707  * Registers SPCanvasGroup class with Gtk and returns its type number.
708  */
709 GType sp_canvas_group_get_type(void)
711     static GType type = 0;
712     if (!type) {
713         GTypeInfo info = {
714             sizeof(SPCanvasGroupClass),
715             0, // base_init
716             0, // base_finalize
717             (GClassInitFunc)sp_canvas_group_class_init,
718             0, // class_finalize
719             0, // class_data
720             sizeof(SPCanvasGroup),
721             0, // n_preallocs
722             (GInstanceInitFunc)sp_canvas_group_init,
723             0 // value_table
724         };
725         type = g_type_register_static(sp_canvas_item_get_type(), "SPCanvasGroup", &info, static_cast<GTypeFlags>(0));
726     }
727     return type;
730 /**
731  * Class initialization function for SPCanvasGroupClass
732  */
733 static void
734 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
736     GtkObjectClass *object_class = (GtkObjectClass *) klass;
737     SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
739     group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
741     object_class->destroy = sp_canvas_group_destroy;
743     item_class->update = sp_canvas_group_update;
744     item_class->render = sp_canvas_group_render;
745     item_class->point = sp_canvas_group_point;
748 /**
749  * Callback. Empty.
750  */
751 static void
752 sp_canvas_group_init (SPCanvasGroup */*group*/)
754     /* Nothing here */
757 /**
758  * Callback that destroys all items in group and calls group's virtual
759  * destroy() function.
760  */
761 static void
762 sp_canvas_group_destroy (GtkObject *object)
764     g_return_if_fail (object != NULL);
765     g_return_if_fail (SP_IS_CANVAS_GROUP (object));
767     SPCanvasGroup const *group = SP_CANVAS_GROUP (object);
769     GList *list = group->items;
770     while (list) {
771         SPCanvasItem *child = (SPCanvasItem *)list->data;
772         list = list->next;
774         gtk_object_destroy (GTK_OBJECT (child));
775     }
777     if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
778         (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
781 /**
782  * Update handler for canvas groups
783  */
784 static void
785 sp_canvas_group_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags)
787     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
788     Geom::RectHull corners(Geom::Point(0, 0));
789     bool empty=true;
791     for (GList *list = group->items; list; list = list->next) {
792         SPCanvasItem *i = (SPCanvasItem *)list->data;
794         sp_canvas_item_invoke_update (i, affine, flags);
796         if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
797             if (empty) {
798                 corners = Geom::RectHull(Geom::Point(i->x1, i->y1));
799                 empty = false;
800             } else {
801                 corners.add(Geom::Point(i->x1, i->y1));
802             }
803             corners.add(Geom::Point(i->x2, i->y2));
804         }
805     }
807     Geom::OptRect const bounds = corners.bounds();
808     if (bounds) {
809         item->x1 = bounds->min()[Geom::X];
810         item->y1 = bounds->min()[Geom::Y];
811         item->x2 = bounds->max()[Geom::X];
812         item->y2 = bounds->max()[Geom::Y];
813     } else {
814         // FIXME ?
815         item->x1 = item->x2 = item->y1 = item->y2 = 0;
816     }
819 /**
820  * Point handler for canvas groups.
821  */
822 static double
823 sp_canvas_group_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item)
825     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
826     double const x = p[Geom::X];
827     double const y = p[Geom::Y];
828     int x1 = (int)(x - item->canvas->close_enough);
829     int y1 = (int)(y - item->canvas->close_enough);
830     int x2 = (int)(x + item->canvas->close_enough);
831     int y2 = (int)(y + item->canvas->close_enough);
833     double best = 0.0;
834     *actual_item = NULL;
836     double dist = 0.0;
838     for (GList *list = group->items; list; list = list->next) {
839         SPCanvasItem *child = (SPCanvasItem *)list->data;
841         if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
842             SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
844             int has_point;
845             if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
846                 dist = sp_canvas_item_invoke_point (child, p, &point_item);
847                 has_point = TRUE;
848             } else
849                 has_point = FALSE;
851             if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
852                 best = dist;
853                 *actual_item = point_item;
854             }
855         }
856     }
858     return best;
861 /**
862  * Renders all visible canvas group items in buf rectangle.
863  */
864 static void
865 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
867     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
869     for (GList *list = group->items; list; list = list->next) {
870         SPCanvasItem *child = (SPCanvasItem *)list->data;
871         if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
872             if ((child->x1 < buf->rect.x1) &&
873                 (child->y1 < buf->rect.y1) &&
874                 (child->x2 > buf->rect.x0) &&
875                 (child->y2 > buf->rect.y0)) {
876                 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
877                     SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
878             }
879         }
880     }
883 /**
884  * Adds an item to a canvas group.
885  */
886 static void
887 group_add (SPCanvasGroup *group, SPCanvasItem *item)
889     gtk_object_ref (GTK_OBJECT (item));
890     gtk_object_sink (GTK_OBJECT (item));
892     if (!group->items) {
893         group->items = g_list_append (group->items, item);
894         group->last = group->items;
895     } else {
896         group->last = g_list_append (group->last, item)->next;
897     }
899     sp_canvas_item_request_update (item);
902 /**
903  * Removes an item from a canvas group
904  */
905 static void
906 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
908     g_return_if_fail (group != NULL);
909     g_return_if_fail (SP_IS_CANVAS_GROUP (group));
910     g_return_if_fail (item != NULL);
912     for (GList *children = group->items; children; children = children->next) {
913         if (children->data == item) {
915             /* Unparent the child */
917             item->parent = NULL;
918             gtk_object_unref (GTK_OBJECT (item));
920             /* Remove it from the list */
922             if (children == group->last) group->last = children->prev;
924             group->items = g_list_remove_link (group->items, children);
925             g_list_free (children);
926             break;
927         }
928     }
931 /* SPCanvas */
933 static void sp_canvas_class_init (SPCanvasClass *klass);
934 static void sp_canvas_init (SPCanvas *canvas);
935 static void sp_canvas_destroy (GtkObject *object);
937 static void sp_canvas_realize (GtkWidget *widget);
938 static void sp_canvas_unrealize (GtkWidget *widget);
940 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
941 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
943 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
944 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
945 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
946 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
947 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
948 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
949 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
950 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
952 static GtkWidgetClass *canvas_parent_class;
954 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb);
955 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb);
956 static void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val);
957 static int do_update (SPCanvas *canvas);
959 /**
960  * Registers the SPCanvas class if necessary, and returns the type ID
961  * associated to it.
962  *
963  * \return The type ID of the SPCanvas class.
964  **/
965 GType sp_canvas_get_type(void)
967     static GType type = 0;
968     if (!type) {
969         GTypeInfo info = {
970             sizeof(SPCanvasClass),
971             0, // base_init
972             0, // base_finalize
973             (GClassInitFunc)sp_canvas_class_init,
974             0, // class_finalize
975             0, // class_data
976             sizeof(SPCanvas),
977             0, // n_preallocs
978             (GInstanceInitFunc)sp_canvas_init,
979             0 // value_table
980         };
981         type = g_type_register_static(GTK_TYPE_WIDGET, "SPCanvas", &info, static_cast<GTypeFlags>(0));
982     }
983     return type;
986 /**
987  * Class initialization function for SPCanvasClass.
988  */
989 static void
990 sp_canvas_class_init (SPCanvasClass *klass)
992     GtkObjectClass *object_class = (GtkObjectClass *) klass;
993     GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
995     canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
997     object_class->destroy = sp_canvas_destroy;
999     widget_class->realize = sp_canvas_realize;
1000     widget_class->unrealize = sp_canvas_unrealize;
1001     widget_class->size_request = sp_canvas_size_request;
1002     widget_class->size_allocate = sp_canvas_size_allocate;
1003     widget_class->button_press_event = sp_canvas_button;
1004     widget_class->button_release_event = sp_canvas_button;
1005     widget_class->motion_notify_event = sp_canvas_motion;
1006     widget_class->scroll_event = sp_canvas_scroll;
1007     widget_class->expose_event = sp_canvas_expose;
1008     widget_class->key_press_event = sp_canvas_key;
1009     widget_class->key_release_event = sp_canvas_key;
1010     widget_class->enter_notify_event = sp_canvas_crossing;
1011     widget_class->leave_notify_event = sp_canvas_crossing;
1012     widget_class->focus_in_event = sp_canvas_focus_in;
1013     widget_class->focus_out_event = sp_canvas_focus_out;
1016 /**
1017  * Callback: object initialization for SPCanvas.
1018  */
1019 static void
1020 sp_canvas_init (SPCanvas *canvas)
1022     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
1023     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
1024     GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
1026     canvas->pick_event.type = GDK_LEAVE_NOTIFY;
1027     canvas->pick_event.crossing.x = 0;
1028     canvas->pick_event.crossing.y = 0;
1030     /* Create the root item as a special case */
1031     canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
1032     canvas->root->canvas = canvas;
1034     gtk_object_ref (GTK_OBJECT (canvas->root));
1035     gtk_object_sink (GTK_OBJECT (canvas->root));
1037     canvas->need_repick = TRUE;
1039     // See comment at in sp-canvas.h.
1040     canvas->gen_all_enter_events = false;
1041     
1042     canvas->drawing_disabled = 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;
2116         
2117     if (canvas->drawing_disabled)
2118         return TRUE;
2120     /* Cause the update if necessary */
2121     if (canvas->need_update) {
2122         sp_canvas_item_invoke_update (canvas->root, Geom::identity(), 0);
2123         canvas->need_update = FALSE;
2124     }
2126     /* Paint if able to */
2127     if (GTK_WIDGET_DRAWABLE (canvas)) {
2128             return paint (canvas);
2129     }
2131     /* Pick new current item */
2132     while (canvas->need_repick) {
2133         canvas->need_repick = FALSE;
2134         pick_current_item (canvas, &canvas->pick_event);
2135     }
2137     return TRUE;
2140 /**
2141  * Idle handler for the canvas that deals with pending updates and redraws.
2142  */
2143 static gint
2144 idle_handler (gpointer data)
2146     GDK_THREADS_ENTER ();
2148     SPCanvas *canvas = SP_CANVAS (data);
2150     int const ret = do_update (canvas);
2152     if (ret) {
2153         /* Reset idle id */
2154         canvas->idle_id = 0;
2155     }
2157     GDK_THREADS_LEAVE ();
2159     return !ret;
2162 /**
2163  * Convenience function to add an idle handler to a canvas.
2164  */
2165 static void
2166 add_idle (SPCanvas *canvas)
2168     if (canvas->idle_id != 0)
2169         return;
2171     canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2174 /**
2175  * Returns the root group of the specified canvas.
2176  */
2177 SPCanvasGroup *
2178 sp_canvas_root (SPCanvas *canvas)
2180     g_return_val_if_fail (canvas != NULL, NULL);
2181     g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2183     return SP_CANVAS_GROUP (canvas->root);
2186 /**
2187  * Scrolls canvas to specific position (cx and cy are measured in screen pixels)
2188  */
2189 void
2190 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2192     g_return_if_fail (canvas != NULL);
2193     g_return_if_fail (SP_IS_CANVAS (canvas));
2195     int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels)
2196     int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do!
2197     int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the
2198     int dy = iy - canvas->y0; // canvas w.r.t its previous position
2200     canvas->dx0 = cx; // here the 'd' stands for double, not delta!
2201     canvas->dy0 = cy;
2202     canvas->x0 = ix;
2203     canvas->y0 = iy;
2205     sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2207     if (!clear) {
2208         // scrolling without zoom; redraw only the newly exposed areas
2209         if ((dx != 0) || (dy != 0)) {
2210             canvas->is_scrolling = is_scrolling;
2211             if (GTK_WIDGET_REALIZED (canvas)) {
2212                 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2213             }
2214         }
2215     } else {
2216         // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2217     }
2221 /**
2222  * Updates canvas if necessary.
2223  */
2224 void
2225 sp_canvas_update_now (SPCanvas *canvas)
2227     g_return_if_fail (canvas != NULL);
2228     g_return_if_fail (SP_IS_CANVAS (canvas));
2230     if (!(canvas->need_update ||
2231           canvas->need_redraw))
2232         return;
2234     do_update (canvas);
2237 /**
2238  * Update callback for canvas widget.
2239  */
2240 static void
2241 sp_canvas_request_update (SPCanvas *canvas)
2243     canvas->need_update = TRUE;
2244     add_idle (canvas);
2247 /**
2248  * Forces redraw of rectangular canvas area.
2249  */
2250 void
2251 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2253     NRRectL bbox;
2254     NRRectL visible;
2255     NRRectL clip;
2257     g_return_if_fail (canvas != NULL);
2258     g_return_if_fail (SP_IS_CANVAS (canvas));
2260     if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2261     if ((x0 >= x1) || (y0 >= y1)) return;
2263     bbox.x0 = x0;
2264     bbox.y0 = y0;
2265     bbox.x1 = x1;
2266     bbox.y1 = y1;
2268     visible.x0 = canvas->x0;
2269     visible.y0 = canvas->y0;
2270     visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2271     visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2273     nr_rect_l_intersect (&clip, &bbox, &visible);
2275     sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2276     add_idle (canvas);
2279 /**
2280  * Sets world coordinates from win and canvas.
2281  */
2282 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2284     g_return_if_fail (canvas != NULL);
2285     g_return_if_fail (SP_IS_CANVAS (canvas));
2287     if (worldx) *worldx = canvas->x0 + winx;
2288     if (worldy) *worldy = canvas->y0 + winy;
2291 /**
2292  * Sets win coordinates from world and canvas.
2293  */
2294 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2296     g_return_if_fail (canvas != NULL);
2297     g_return_if_fail (SP_IS_CANVAS (canvas));
2299     if (winx) *winx = worldx - canvas->x0;
2300     if (winy) *winy = worldy - canvas->y0;
2303 /**
2304  * Converts point from win to world coordinates.
2305  */
2306 Geom::Point sp_canvas_window_to_world(SPCanvas const *canvas, Geom::Point const win)
2308     g_assert (canvas != NULL);
2309     g_assert (SP_IS_CANVAS (canvas));
2311     return Geom::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2314 /**
2315  * Converts point from world to win coordinates.
2316  */
2317 Geom::Point sp_canvas_world_to_window(SPCanvas const *canvas, Geom::Point const world)
2319     g_assert (canvas != NULL);
2320     g_assert (SP_IS_CANVAS (canvas));
2322     return Geom::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2325 /**
2326  * Returns true if point given in world coordinates is inside window.
2327  */
2328 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, Geom::Point const &world)
2330     g_assert( canvas != NULL );
2331     g_assert(SP_IS_CANVAS(canvas));
2333     GtkWidget const &w = *GTK_WIDGET(canvas);
2334     return ( ( canvas->x0 <= world[Geom::X] )  &&
2335              ( canvas->y0 <= world[Geom::Y] )  &&
2336              ( world[Geom::X] < canvas->x0 + w.allocation.width )  &&
2337              ( world[Geom::Y] < canvas->y0 + w.allocation.height ) );
2340 /**
2341  * Return canvas window coordinates as Geom::Rect.
2342  */
2343 Geom::Rect SPCanvas::getViewbox() const
2345     GtkWidget const *w = GTK_WIDGET(this);
2346     return Geom::Rect(Geom::Point(dx0, dy0),
2347                       Geom::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2350 /**
2351  * Return canvas window coordinates as IRect (a rectangle defined by integers).
2352  */
2353 NR::IRect SPCanvas::getViewboxIntegers() const
2355     GtkWidget const *w = GTK_WIDGET(this);
2356     return NR::IRect(NR::IPoint(x0, y0),
2357                     NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height));
2360 inline int sp_canvas_tile_floor(int x)
2362     return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2365 inline int sp_canvas_tile_ceil(int x)
2367     return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2370 /**
2371  * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2372  */
2373 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2375     if ( nl >= nr || nt >= nb ) {
2376         if ( canvas->tiles ) g_free(canvas->tiles);
2377         canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2378         canvas->tileH=canvas->tileV=0;
2379         canvas->tiles=NULL;
2380         return;
2381     }
2382     int tl=sp_canvas_tile_floor(nl);
2383     int tt=sp_canvas_tile_floor(nt);
2384     int tr=sp_canvas_tile_ceil(nr);
2385     int tb=sp_canvas_tile_ceil(nb);
2387     int nh = tr-tl, nv = tb-tt;
2388     uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2389     for (int i=tl; i<tr; i++) {
2390         for (int j=tt; j<tb; j++) {
2391             int ind = (i-tl) + (j-tt)*nh;
2392             if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2393                 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2394             } else {
2395                 ntiles[ind]=0; // newly exposed areas get 0
2396             }
2397         }
2398     }
2399     if ( canvas->tiles ) g_free(canvas->tiles);
2400     canvas->tiles=ntiles;
2401     canvas->tLeft=tl;
2402     canvas->tTop=tt;
2403     canvas->tRight=tr;
2404     canvas->tBottom=tb;
2405     canvas->tileH=nh;
2406     canvas->tileV=nv;
2409 /*
2410  * Helper that queues a canvas rectangle for redraw
2411  */
2412 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2413     canvas->need_redraw = TRUE;
2415     sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2418 /**
2419  * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2420  */
2421 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2423     if ( nl >= nr || nt >= nb ) {
2424         return;
2425     }
2426     int tl=sp_canvas_tile_floor(nl);
2427     int tt=sp_canvas_tile_floor(nt);
2428     int tr=sp_canvas_tile_ceil(nr);
2429     int tb=sp_canvas_tile_ceil(nb);
2430     if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2431     if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2432     if ( tr > canvas->tRight ) tr=canvas->tRight;
2433     if ( tt < canvas->tTop ) tt=canvas->tTop;
2434     if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2436     for (int i=tl; i<tr; i++) {
2437         for (int j=tt; j<tb; j++) {
2438             canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2439         }
2440     }
2444 /*
2445   Local Variables:
2446   mode:c++
2447   c-file-style:"stroustrup"
2448   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2449   indent-tabs-mode:nil
2450   fill-column:99
2451   End:
2452 */
2453 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :