Code

Merging in from trunk
[inkscape.git] / src / display / sp-canvas.cpp
1 #define __SP_CANVAS_C__
3 /** \file
4  * Port of GnomeCanvas for Inkscape needs
5  *
6  * Authors:
7  *   Federico Mena <federico@nuclecu.unam.mx>
8  *   Raph Levien <raph@gimp.org>
9  *   Lauris Kaplinski <lauris@kaplinski.com>
10  *   fred
11  *   bbyak
12  *
13  * Copyright (C) 1998 The Free Software Foundation
14  * Copyright (C) 2002-2006 authors
15  *
16  * Released under GNU GPL, read the file 'COPYING' for more information
17  */
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
23 #include <libnr/nr-pixblock.h>
25 #include <gtk/gtkmain.h>
26 #include <gtk/gtksignal.h>
27 #include <gtk/gtkversion.h>
29 #include <gtkmm.h>
31 #include "helper/sp-marshal.h"
32 #include <helper/recthull.h>
33 #include <display/sp-canvas.h>
34 #include "display-forward.h"
35 #include <2geom/matrix.h>
36 #include <libnr/nr-convex-hull.h>
37 #include "preferences.h"
38 #include "inkscape.h"
39 #include "sodipodi-ctrlrect.h"
40 #if ENABLE_LCMS
41 #include "color-profile-fns.h"
42 #endif // ENABLE_LCMS
43 #include "display/rendermode.h"
44 #include "libnr/nr-blit.h"
45 #include "display/inkscape-cairo.h"
46 #include "debug/gdk-event-latency-tracker.h"
47 #include "desktop.h"
48 #include "sp-namedview.h"
50 using Inkscape::Debug::GdkEventLatencyTracker;
52 // GTK_CHECK_VERSION returns false on failure
53 #define HAS_GDK_EVENT_REQUEST_MOTIONS GTK_CHECK_VERSION(2, 12, 0)
55 // gtk_check_version returns non-NULL on failure
56 static bool const HAS_BROKEN_MOTION_HINTS =
57   true || gtk_check_version(2, 12, 0) != NULL || !HAS_GDK_EVENT_REQUEST_MOTIONS;
59 // Define this to visualize the regions to be redrawn
60 //#define DEBUG_REDRAW 1;
62 // Tiles are a way to minimize the number of redraws, eliminating too small redraws.
63 // The canvas stores a 2D array of ints, each representing a TILE_SIZExTILE_SIZE pixels tile.
64 // If any part of it is dirtied, the entire tile is dirtied (its int is nonzero) and repainted.
65 #define TILE_SIZE 16
67 static gint const sp_canvas_update_priority = G_PRIORITY_HIGH_IDLE;
69 #define SP_CANVAS_WINDOW(c) (((GtkWidget *) (c))->window)
71 enum {
72     SP_CANVAS_ITEM_VISIBLE = 1 << 7,
73     SP_CANVAS_ITEM_NEED_UPDATE = 1 << 8,
74     SP_CANVAS_ITEM_NEED_AFFINE = 1 << 9
75 };
77 /**
78  * A group of Items.
79  */
80 struct SPCanvasGroup {
81     SPCanvasItem item;
83     GList *items, *last;
84 };
86 /**
87  * The SPCanvasGroup vtable.
88  */
89 struct SPCanvasGroupClass {
90     SPCanvasItemClass parent_class;
91 };
93 /**
94  * The SPCanvas vtable.
95  */
96 struct SPCanvasClass {
97     GtkWidgetClass parent_class;
98 };
100 static void group_add (SPCanvasGroup *group, SPCanvasItem *item);
101 static void group_remove (SPCanvasGroup *group, SPCanvasItem *item);
103 /* SPCanvasItem */
105 enum {ITEM_EVENT, ITEM_LAST_SIGNAL};
108 static void sp_canvas_request_update (SPCanvas *canvas);
110 static void track_latency(GdkEvent const *event);
111 static void sp_canvas_item_class_init (SPCanvasItemClass *klass);
112 static void sp_canvas_item_init (SPCanvasItem *item);
113 static void sp_canvas_item_dispose (GObject *object);
114 static void sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args);
116 static int emit_event (SPCanvas *canvas, GdkEvent *event);
118 static guint item_signals[ITEM_LAST_SIGNAL] = { 0 };
120 static GtkObjectClass *item_parent_class;
122 /**
123  * Registers the SPCanvasItem class with Glib and returns its type number.
124  */
125 GType
126 sp_canvas_item_get_type (void)
128     static GType type = 0;
129     if (!type) {
130         static GTypeInfo const info = {
131             sizeof (SPCanvasItemClass),
132             NULL, NULL,
133             (GClassInitFunc) sp_canvas_item_class_init,
134             NULL, NULL,
135             sizeof (SPCanvasItem),
136             0,
137             (GInstanceInitFunc) sp_canvas_item_init,
138             NULL
139         };
140         type = g_type_register_static (GTK_TYPE_OBJECT, "SPCanvasItem", &info, (GTypeFlags)0);
141     }
143     return type;
146 /**
147  * Initializes the SPCanvasItem vtable and the "event" signal.
148  */
149 static void
150 sp_canvas_item_class_init (SPCanvasItemClass *klass)
152     GObjectClass *object_class = (GObjectClass *) klass;
154     /* fixme: Derive from GObject */
155     item_parent_class = (GtkObjectClass*)gtk_type_class (GTK_TYPE_OBJECT);
157     item_signals[ITEM_EVENT] = g_signal_new ("event",
158                                              G_TYPE_FROM_CLASS (klass),
159                                              G_SIGNAL_RUN_LAST,
160                                              ((glong)((guint8*)&(klass->event) - (guint8*)klass)),
161                                              NULL, NULL,
162                                              sp_marshal_BOOLEAN__POINTER,
163                                              G_TYPE_BOOLEAN, 1,
164                                              GDK_TYPE_EVENT);
166     object_class->dispose = sp_canvas_item_dispose;
169 /**
170  * Callback for initialization of SPCanvasItem.
171  */
172 static void
173 sp_canvas_item_init (SPCanvasItem *item)
175     item->flags |= SP_CANVAS_ITEM_VISIBLE;
176     item->xform = Geom::Matrix(Geom::identity());
179 /**
180  * Constructs new SPCanvasItem on SPCanvasGroup.
181  */
182 SPCanvasItem *
183 sp_canvas_item_new (SPCanvasGroup *parent, GtkType type, gchar const *first_arg_name, ...)
185     va_list args;
187     g_return_val_if_fail (parent != NULL, NULL);
188     g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL);
189     g_return_val_if_fail (gtk_type_is_a (type, sp_canvas_item_get_type ()), NULL);
191     SPCanvasItem *item = SP_CANVAS_ITEM (gtk_type_new (type));
193     va_start (args, first_arg_name);
194     sp_canvas_item_construct (item, parent, first_arg_name, args);
195     va_end (args);
197     return item;
200 /**
201  * Sets up the newly created SPCanvasItem.
202  *
203  * We make it static for encapsulation reasons since it was nowhere used.
204  */
205 static void
206 sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args)
208     g_return_if_fail (SP_IS_CANVAS_GROUP (parent));
209     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
211     item->parent = SP_CANVAS_ITEM (parent);
212     item->canvas = item->parent->canvas;
214     g_object_set_valist (G_OBJECT (item), first_arg_name, args);
216     group_add (SP_CANVAS_GROUP (item->parent), item);
218     sp_canvas_item_request_update (item);
221 /**
222  * Helper function that requests redraw only if item's visible flag is set.
223  */
224 static void
225 redraw_if_visible (SPCanvasItem *item)
227     if (item->flags & SP_CANVAS_ITEM_VISIBLE) {
228         int x0 = (int)(item->x1);
229         int x1 = (int)(item->x2);
230         int y0 = (int)(item->y1);
231         int y1 = (int)(item->y2);
233         if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
234             sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
235         }
236     }
239 /**
240  * Callback that removes item from all referers and destroys it.
241  */
242 static void
243 sp_canvas_item_dispose (GObject *object)
245     SPCanvasItem *item = SP_CANVAS_ITEM (object);
247     // Hack: if this is a ctrlrect, move it to 0,0;
248     // this redraws only the stroke of the rect to be deleted,
249     // avoiding redraw of the entire area
250     if (SP_IS_CTRLRECT(item)) {
251         SP_CTRLRECT(object)->setRectangle(Geom::Rect(Geom::Point(0,0),Geom::Point(0,0)));
252         SP_CTRLRECT(object)->update(item->xform, 0);
253     } else {
254         redraw_if_visible (item);
255     }
256     item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
258     if (item == item->canvas->current_item) {
259         item->canvas->current_item = NULL;
260         item->canvas->need_repick = TRUE;
261     }
263     if (item == item->canvas->new_current_item) {
264         item->canvas->new_current_item = NULL;
265         item->canvas->need_repick = TRUE;
266     }
268     if (item == item->canvas->grabbed_item) {
269         item->canvas->grabbed_item = NULL;
270         gdk_pointer_ungrab (GDK_CURRENT_TIME);
271     }
273     if (item == item->canvas->focused_item)
274         item->canvas->focused_item = NULL;
276     if (item->parent) {
277         group_remove (SP_CANVAS_GROUP (item->parent), item);
278     }
280     G_OBJECT_CLASS (item_parent_class)->dispose (object);
283 /**
284  * Helper function to update item and its children.
285  *
286  * NB! affine is parent2canvas.
287  */
288 static void
289 sp_canvas_item_invoke_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags)
291     /* Apply the child item's transform */
292     Geom::Matrix child_affine = item->xform * affine;
294     /* apply object flags to child flags */
295     int child_flags = flags & ~SP_CANVAS_UPDATE_REQUESTED;
297     if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
298         child_flags |= SP_CANVAS_UPDATE_REQUESTED;
300     if (item->flags & SP_CANVAS_ITEM_NEED_AFFINE)
301         child_flags |= SP_CANVAS_UPDATE_AFFINE;
303     if (child_flags & (SP_CANVAS_UPDATE_REQUESTED | SP_CANVAS_UPDATE_AFFINE)) {
304         if (SP_CANVAS_ITEM_GET_CLASS (item)->update)
305             SP_CANVAS_ITEM_GET_CLASS (item)->update (item, child_affine, child_flags);
306     }
308     GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_UPDATE);
309     GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_AFFINE);
312 /**
313  * Helper function to invoke the point method of the item.
314  *
315  * The argument x, y should be in the parent's item-relative coordinate
316  * system.  This routine applies the inverse of the item's transform,
317  * maintaining the affine invariant.
318  */
319 static double
320 sp_canvas_item_invoke_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item)
322     if (SP_CANVAS_ITEM_GET_CLASS (item)->point)
323         return SP_CANVAS_ITEM_GET_CLASS (item)->point (item, p, actual_item);
325     return NR_HUGE;
328 /**
329  * Makes the item's affine transformation matrix be equal to the specified
330  * matrix.
331  *
332  * @item: A canvas item.
333  * @affine: An affine transformation matrix.
334  */
335 void
336 sp_canvas_item_affine_absolute (SPCanvasItem *item, Geom::Matrix const &affine)
338     item->xform = affine;
340     if (!(item->flags & SP_CANVAS_ITEM_NEED_AFFINE)) {
341         item->flags |= SP_CANVAS_ITEM_NEED_AFFINE;
342         if (item->parent != NULL) {
343             sp_canvas_item_request_update (item->parent);
344         } else {
345             sp_canvas_request_update (item->canvas);
346         }
347     }
349     item->canvas->need_repick = TRUE;
352 /**
353  * Convenience function to reorder items in a group's child list.
354  *
355  * This puts the specified link after the "before" link.
356  */
357 static void
358 put_item_after (GList *link, GList *before)
360     if (link == before)
361         return;
363     SPCanvasGroup *parent = SP_CANVAS_GROUP (SP_CANVAS_ITEM (link->data)->parent);
365     if (before == NULL) {
366         if (link == parent->items) return;
368         link->prev->next = link->next;
370         if (link->next) {
371             link->next->prev = link->prev;
372         } else {
373             parent->last = link->prev;
374         }
376         link->prev = before;
377         link->next = parent->items;
378         link->next->prev = link;
379         parent->items = link;
380     } else {
381         if ((link == parent->last) && (before == parent->last->prev))
382             return;
384         if (link->next)
385             link->next->prev = link->prev;
387         if (link->prev)
388             link->prev->next = link->next;
389         else {
390             parent->items = link->next;
391             parent->items->prev = NULL;
392         }
394         link->prev = before;
395         link->next = before->next;
397         link->prev->next = link;
399         if (link->next)
400             link->next->prev = link;
401         else
402             parent->last = link;
403     }
407 /**
408  * Raises the item in its parent's stack by the specified number of positions.
409  *
410  * \param item A canvas item.
411  * \param positions Number of steps to raise the item.
412  *
413  * If the number of positions is greater than the distance to the top of the
414  * stack, then the item is put at the top.
415  */
416 void
417 sp_canvas_item_raise (SPCanvasItem *item, int positions)
419     g_return_if_fail (item != NULL);
420     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
421     g_return_if_fail (positions >= 0);
423     if (!item->parent || positions == 0)
424         return;
426     SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
427     GList *link = g_list_find (parent->items, item);
428     g_assert (link != NULL);
430     GList *before;
431     for (before = link; positions && before; positions--)
432         before = before->next;
434     if (!before)
435         before = parent->last;
437     put_item_after (link, before);
439     redraw_if_visible (item);
440     item->canvas->need_repick = TRUE;
444 /**
445  * Lowers the item in its parent's stack by the specified number of positions.
446  *
447  * \param item A canvas item.
448  * \param positions Number of steps to lower the item.
449  *
450  * If the number of positions is greater than the distance to the bottom of the
451  * stack, then the item is put at the bottom.
452  **/
453 void
454 sp_canvas_item_lower (SPCanvasItem *item, int positions)
456     g_return_if_fail (item != NULL);
457     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
458     g_return_if_fail (positions >= 1);
460     if (!item->parent || positions == 0)
461         return;
463     SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
464     GList *link = g_list_find (parent->items, item);
465     g_assert (link != NULL);
467     GList *before;
468     if (link->prev)
469         for (before = link->prev; positions && before; positions--)
470             before = before->prev;
471     else
472         before = NULL;
474     put_item_after (link, before);
476     redraw_if_visible (item);
477     item->canvas->need_repick = TRUE;
480 /**
481  * Sets visible flag on item and requests a redraw.
482  */
483 void
484 sp_canvas_item_show (SPCanvasItem *item)
486     g_return_if_fail (item != NULL);
487     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
489     if (item->flags & SP_CANVAS_ITEM_VISIBLE)
490         return;
492     item->flags |= SP_CANVAS_ITEM_VISIBLE;
494     int x0 = (int)(item->x1);
495     int x1 = (int)(item->x2);
496     int y0 = (int)(item->y1);
497     int y1 = (int)(item->y2);
499     if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
500         sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
501         item->canvas->need_repick = TRUE;
502     }
505 /**
506  * Clears visible flag on item and requests a redraw.
507  */
508 void
509 sp_canvas_item_hide (SPCanvasItem *item)
511     g_return_if_fail (item != NULL);
512     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
514     if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
515         return;
517     item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
519     int x0 = (int)(item->x1);
520     int x1 = (int)(item->x2);
521     int y0 = (int)(item->y1);
522     int y1 = (int)(item->y2);
524     if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
525         sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
526         item->canvas->need_repick = TRUE;
527     }
530 /**
531  * Grab item under cursor.
532  *
533  * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE
534  */
535 int
536 sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
538     g_return_val_if_fail (item != NULL, -1);
539     g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1);
540     g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1);
542     if (item->canvas->grabbed_item)
543         return -1;
545     if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
546         return -1;
548     if (HAS_BROKEN_MOTION_HINTS) {
549         event_mask &= ~GDK_POINTER_MOTION_HINT_MASK;
550     }
552     /* fixme: Top hack (Lauris) */
553     /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
554     /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
555     gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
556                       (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
557                       NULL, cursor, etime);
559     item->canvas->grabbed_item = item;
560     item->canvas->grabbed_event_mask = event_mask;
561     item->canvas->current_item = item; /* So that events go to the grabbed item */
563     return 0;
566 /**
567  * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
568  * mouse.
569  *
570  * \param item A canvas item that holds a grab.
571  * \param etime The timestamp for ungrabbing the mouse.
572  */
573 void
574 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
576     g_return_if_fail (item != NULL);
577     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
579     if (item->canvas->grabbed_item != item)
580         return;
582     item->canvas->grabbed_item = NULL;
584     gdk_pointer_ungrab (etime);
587 /**
588  * Returns the product of all transformation matrices from the root item down
589  * to the item.
590  */
591 Geom::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
593     g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
595     Geom::Matrix affine = Geom::identity();
597     while (item) {
598         affine *= item->xform;
599         item = item->parent;
600     }
601     return affine;
604 /**
605  * Helper that returns true iff item is descendant of parent.
606  */
607 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
609     while (item) {
610         if (item == parent)
611             return true;
612         item = item->parent;
613     }
615     return false;
618 /**
619  * Focus canvas, and item under cursor if it is not already focussed.
620  */
621 void
622 sp_canvas_item_grab_focus (SPCanvasItem *item)
624     g_return_if_fail (item != NULL);
625     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
626     g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
628     SPCanvasItem *focused_item = item->canvas->focused_item;
630     if (focused_item) {
631         GdkEvent ev;
632         ev.focus_change.type = GDK_FOCUS_CHANGE;
633         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
634         ev.focus_change.send_event = FALSE;
635         ev.focus_change.in = FALSE;
637         emit_event (item->canvas, &ev);
638     }
640     item->canvas->focused_item = item;
641     gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
643     if (focused_item) {
644         GdkEvent ev;
645         ev.focus_change.type = GDK_FOCUS_CHANGE;
646         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
647         ev.focus_change.send_event = FALSE;
648         ev.focus_change.in = TRUE;
650         emit_event (item->canvas, &ev);
651     }
654 /**
655  * Requests that the canvas queue an update for the specified item.
656  *
657  * To be used only by item implementations.
658  */
659 void
660 sp_canvas_item_request_update (SPCanvasItem *item)
662     if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
663         return;
665     item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
667     if (item->parent != NULL) {
668         /* Recurse up the tree */
669         sp_canvas_item_request_update (item->parent);
670     } else {
671         /* Have reached the top of the tree, make sure the update call gets scheduled. */
672         sp_canvas_request_update (item->canvas);
673     }
676 /**
677  * Returns position of item in group.
678  */
679 gint sp_canvas_item_order (SPCanvasItem * item)
681     return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
684 /* SPCanvasGroup */
686 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
687 static void sp_canvas_group_init (SPCanvasGroup *group);
688 static void sp_canvas_group_destroy (GtkObject *object);
690 static void sp_canvas_group_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags);
691 static double sp_canvas_group_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item);
692 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
694 static SPCanvasItemClass *group_parent_class;
696 /**
697  * Registers SPCanvasGroup class with Gtk and returns its type number.
698  */
699 GType sp_canvas_group_get_type(void)
701     static GType type = 0;
702     if (!type) {
703         GTypeInfo info = {
704             sizeof(SPCanvasGroupClass),
705             0, // base_init
706             0, // base_finalize
707             (GClassInitFunc)sp_canvas_group_class_init,
708             0, // class_finalize
709             0, // class_data
710             sizeof(SPCanvasGroup),
711             0, // n_preallocs
712             (GInstanceInitFunc)sp_canvas_group_init,
713             0 // value_table
714         };
715         type = g_type_register_static(sp_canvas_item_get_type(), "SPCanvasGroup", &info, static_cast<GTypeFlags>(0));
716     }
717     return type;
720 /**
721  * Class initialization function for SPCanvasGroupClass
722  */
723 static void
724 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
726     GtkObjectClass *object_class = (GtkObjectClass *) klass;
727     SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
729     group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
731     object_class->destroy = sp_canvas_group_destroy;
733     item_class->update = sp_canvas_group_update;
734     item_class->render = sp_canvas_group_render;
735     item_class->point = sp_canvas_group_point;
738 /**
739  * Callback. Empty.
740  */
741 static void
742 sp_canvas_group_init (SPCanvasGroup */*group*/)
744     /* Nothing here */
747 /**
748  * Callback that destroys all items in group and calls group's virtual
749  * destroy() function.
750  */
751 static void
752 sp_canvas_group_destroy (GtkObject *object)
754     g_return_if_fail (object != NULL);
755     g_return_if_fail (SP_IS_CANVAS_GROUP (object));
757     SPCanvasGroup const *group = SP_CANVAS_GROUP (object);
759     GList *list = group->items;
760     while (list) {
761         SPCanvasItem *child = (SPCanvasItem *)list->data;
762         list = list->next;
764         gtk_object_destroy (GTK_OBJECT (child));
765     }
767     if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
768         (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
771 /**
772  * Update handler for canvas groups
773  */
774 static void
775 sp_canvas_group_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags)
777     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
778     Geom::RectHull corners(Geom::Point(0, 0));
779     bool empty=true;
781     for (GList *list = group->items; list; list = list->next) {
782         SPCanvasItem *i = (SPCanvasItem *)list->data;
784         sp_canvas_item_invoke_update (i, affine, flags);
786         if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
787             if (empty) {
788                 corners = Geom::RectHull(Geom::Point(i->x1, i->y1));
789                 empty = false;
790             } else {
791                 corners.add(Geom::Point(i->x1, i->y1));
792             }
793             corners.add(Geom::Point(i->x2, i->y2));
794         }
795     }
797     Geom::OptRect const bounds = corners.bounds();
798     if (bounds) {
799         item->x1 = bounds->min()[Geom::X];
800         item->y1 = bounds->min()[Geom::Y];
801         item->x2 = bounds->max()[Geom::X];
802         item->y2 = bounds->max()[Geom::Y];
803     } else {
804         // FIXME ?
805         item->x1 = item->x2 = item->y1 = item->y2 = 0;
806     }
809 /**
810  * Point handler for canvas groups.
811  */
812 static double
813 sp_canvas_group_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item)
815     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
816     double const x = p[Geom::X];
817     double const y = p[Geom::Y];
818     int x1 = (int)(x - item->canvas->close_enough);
819     int y1 = (int)(y - item->canvas->close_enough);
820     int x2 = (int)(x + item->canvas->close_enough);
821     int y2 = (int)(y + item->canvas->close_enough);
823     double best = 0.0;
824     *actual_item = NULL;
826     double dist = 0.0;
828     for (GList *list = group->items; list; list = list->next) {
829         SPCanvasItem *child = (SPCanvasItem *)list->data;
831         if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
832             SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
834             int has_point;
835             if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
836                 dist = sp_canvas_item_invoke_point (child, p, &point_item);
837                 has_point = TRUE;
838             } else
839                 has_point = FALSE;
841             if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
842                 best = dist;
843                 *actual_item = point_item;
844             }
845         }
846     }
848     return best;
851 /**
852  * Renders all visible canvas group items in buf rectangle.
853  */
854 static void
855 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
857     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
859     for (GList *list = group->items; list; list = list->next) {
860         SPCanvasItem *child = (SPCanvasItem *)list->data;
861         if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
862             if ((child->x1 < buf->rect.x1) &&
863                 (child->y1 < buf->rect.y1) &&
864                 (child->x2 > buf->rect.x0) &&
865                 (child->y2 > buf->rect.y0)) {
866                 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
867                     SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
868             }
869         }
870     }
873 /**
874  * Adds an item to a canvas group.
875  */
876 static void
877 group_add (SPCanvasGroup *group, SPCanvasItem *item)
879     gtk_object_ref (GTK_OBJECT (item));
880     gtk_object_sink (GTK_OBJECT (item));
882     if (!group->items) {
883         group->items = g_list_append (group->items, item);
884         group->last = group->items;
885     } else {
886         group->last = g_list_append (group->last, item)->next;
887     }
889     sp_canvas_item_request_update (item);
892 /**
893  * Removes an item from a canvas group
894  */
895 static void
896 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
898     g_return_if_fail (group != NULL);
899     g_return_if_fail (SP_IS_CANVAS_GROUP (group));
900     g_return_if_fail (item != NULL);
902     for (GList *children = group->items; children; children = children->next) {
903         if (children->data == item) {
905             /* Unparent the child */
907             item->parent = NULL;
908             gtk_object_unref (GTK_OBJECT (item));
910             /* Remove it from the list */
912             if (children == group->last) group->last = children->prev;
914             group->items = g_list_remove_link (group->items, children);
915             g_list_free (children);
916             break;
917         }
918     }
921 /* SPCanvas */
923 static void sp_canvas_class_init (SPCanvasClass *klass);
924 static void sp_canvas_init (SPCanvas *canvas);
925 static void sp_canvas_destroy (GtkObject *object);
927 static void sp_canvas_realize (GtkWidget *widget);
928 static void sp_canvas_unrealize (GtkWidget *widget);
930 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
931 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
933 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
934 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
935 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
936 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
937 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
938 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
939 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
940 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
942 static GtkWidgetClass *canvas_parent_class;
944 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb);
945 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb);
946 static void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val);
947 static int do_update (SPCanvas *canvas);
949 /**
950  * Registers the SPCanvas class if necessary, and returns the type ID
951  * associated to it.
952  *
953  * \return The type ID of the SPCanvas class.
954  **/
955 GType sp_canvas_get_type(void)
957     static GType type = 0;
958     if (!type) {
959         GTypeInfo info = {
960             sizeof(SPCanvasClass),
961             0, // base_init
962             0, // base_finalize
963             (GClassInitFunc)sp_canvas_class_init,
964             0, // class_finalize
965             0, // class_data
966             sizeof(SPCanvas),
967             0, // n_preallocs
968             (GInstanceInitFunc)sp_canvas_init,
969             0 // value_table
970         };
971         type = g_type_register_static(GTK_TYPE_WIDGET, "SPCanvas", &info, static_cast<GTypeFlags>(0));
972     }
973     return type;
976 /**
977  * Class initialization function for SPCanvasClass.
978  */
979 static void
980 sp_canvas_class_init (SPCanvasClass *klass)
982     GtkObjectClass *object_class = (GtkObjectClass *) klass;
983     GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
985     canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
987     object_class->destroy = sp_canvas_destroy;
989     widget_class->realize = sp_canvas_realize;
990     widget_class->unrealize = sp_canvas_unrealize;
991     widget_class->size_request = sp_canvas_size_request;
992     widget_class->size_allocate = sp_canvas_size_allocate;
993     widget_class->button_press_event = sp_canvas_button;
994     widget_class->button_release_event = sp_canvas_button;
995     widget_class->motion_notify_event = sp_canvas_motion;
996     widget_class->scroll_event = sp_canvas_scroll;
997     widget_class->expose_event = sp_canvas_expose;
998     widget_class->key_press_event = sp_canvas_key;
999     widget_class->key_release_event = sp_canvas_key;
1000     widget_class->enter_notify_event = sp_canvas_crossing;
1001     widget_class->leave_notify_event = sp_canvas_crossing;
1002     widget_class->focus_in_event = sp_canvas_focus_in;
1003     widget_class->focus_out_event = sp_canvas_focus_out;
1006 /**
1007  * Callback: object initialization for SPCanvas.
1008  */
1009 static void
1010 sp_canvas_init (SPCanvas *canvas)
1012     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
1013     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
1014     GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
1016     canvas->pick_event.type = GDK_LEAVE_NOTIFY;
1017     canvas->pick_event.crossing.x = 0;
1018     canvas->pick_event.crossing.y = 0;
1020     /* Create the root item as a special case */
1021     canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
1022     canvas->root->canvas = canvas;
1024     gtk_object_ref (GTK_OBJECT (canvas->root));
1025     gtk_object_sink (GTK_OBJECT (canvas->root));
1027     canvas->need_repick = TRUE;
1029     // See comment at in sp-canvas.h.
1030     canvas->gen_all_enter_events = false;
1031     
1032     canvas->drawing_disabled = false;
1034     canvas->tiles=NULL;
1035     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1036     canvas->tileH=canvas->tileV=0;
1038     canvas->forced_redraw_count = 0;
1039     canvas->forced_redraw_limit = -1;
1041 #if ENABLE_LCMS
1042     canvas->enable_cms_display_adj = false;
1043     canvas->cms_key = new Glib::ustring("");
1044 #endif // ENABLE_LCMS
1046     canvas->is_scrolling = false;
1049 /**
1050  * Convenience function to remove the idle handler of a canvas.
1051  */
1052 static void
1053 remove_idle (SPCanvas *canvas)
1055     if (canvas->idle_id) {
1056         gtk_idle_remove (canvas->idle_id);
1057         canvas->idle_id = 0;
1058     }
1061 /*
1062  * Removes the transient state of the canvas (idle handler, grabs).
1063  */
1064 static void
1065 shutdown_transients (SPCanvas *canvas)
1067     /* We turn off the need_redraw flag, since if the canvas is mapped again
1068      * it will request a redraw anyways.  We do not turn off the need_update
1069      * flag, though, because updates are not queued when the canvas remaps
1070      * itself.
1071      */
1072     if (canvas->need_redraw) {
1073         canvas->need_redraw = FALSE;
1074     }
1075     if ( canvas->tiles ) g_free(canvas->tiles);
1076     canvas->tiles=NULL;
1077     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1078     canvas->tileH=canvas->tileV=0;
1080     if (canvas->grabbed_item) {
1081         canvas->grabbed_item = NULL;
1082         gdk_pointer_ungrab (GDK_CURRENT_TIME);
1083     }
1085     remove_idle (canvas);
1088 /**
1089  * Destroy handler for SPCanvas.
1090  */
1091 static void
1092 sp_canvas_destroy (GtkObject *object)
1094     SPCanvas *canvas = SP_CANVAS (object);
1096     if (canvas->root) {
1097         gtk_object_unref (GTK_OBJECT (canvas->root));
1098         canvas->root = NULL;
1099     }
1101     shutdown_transients (canvas);
1103     if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1104         (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1107 static void track_latency(GdkEvent const *event) {
1108     GdkEventLatencyTracker &tracker = GdkEventLatencyTracker::default_tracker();
1109     boost::optional<double> latency = tracker.process(event);
1110     if (latency && *latency > 2.0) {
1111         //g_warning("Event latency reached %f sec (%1.4f)", *latency, tracker.getSkew());
1112     }
1115 /**
1116  * Returns new canvas as widget.
1117  */
1118 GtkWidget *
1119 sp_canvas_new_aa (void)
1121     SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1123     return (GtkWidget *) canvas;
1126 /**
1127  * The canvas widget's realize callback.
1128  */
1129 static void
1130 sp_canvas_realize (GtkWidget *widget)
1132     SPCanvas *canvas = SP_CANVAS (widget);
1134     GdkWindowAttr attributes;
1135     attributes.window_type = GDK_WINDOW_CHILD;
1136     attributes.x = widget->allocation.x;
1137     attributes.y = widget->allocation.y;
1138     attributes.width = widget->allocation.width;
1139     attributes.height = widget->allocation.height;
1140     attributes.wclass = GDK_INPUT_OUTPUT;
1141     attributes.visual = gdk_rgb_get_visual ();
1142     attributes.colormap = gdk_rgb_get_cmap ();
1143     attributes.event_mask = (gtk_widget_get_events (widget) |
1144                              GDK_EXPOSURE_MASK |
1145                              GDK_BUTTON_PRESS_MASK |
1146                              GDK_BUTTON_RELEASE_MASK |
1147                              GDK_POINTER_MOTION_MASK |
1148                              ( HAS_BROKEN_MOTION_HINTS ?
1149                                0 : GDK_POINTER_MOTION_HINT_MASK ) |
1150                              GDK_PROXIMITY_IN_MASK |
1151                              GDK_PROXIMITY_OUT_MASK |
1152                              GDK_KEY_PRESS_MASK |
1153                              GDK_KEY_RELEASE_MASK |
1154                              GDK_ENTER_NOTIFY_MASK |
1155                              GDK_LEAVE_NOTIFY_MASK |
1156                              GDK_FOCUS_CHANGE_MASK);
1157     gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1159     widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1160     gdk_window_set_user_data (widget->window, widget);
1162     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1163     if ( prefs->getBool("/options/useextinput/value", true) )
1164         gtk_widget_set_events(widget, attributes.event_mask);
1166     widget->style = gtk_style_attach (widget->style, widget->window);
1168     GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1170     canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1173 /**
1174  * The canvas widget's unrealize callback.
1175  */
1176 static void
1177 sp_canvas_unrealize (GtkWidget *widget)
1179     SPCanvas *canvas = SP_CANVAS (widget);
1181     canvas->current_item = NULL;
1182     canvas->grabbed_item = NULL;
1183     canvas->focused_item = NULL;
1185     shutdown_transients (canvas);
1187     gdk_gc_destroy (canvas->pixmap_gc);
1188     canvas->pixmap_gc = NULL;
1190     if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1191         (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1194 /**
1195  * The canvas widget's size_request callback.
1196  */
1197 static void
1198 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1200     static_cast<void>(SP_CANVAS (widget));
1202     req->width = 256;
1203     req->height = 256;
1206 /**
1207  * The canvas widget's size_allocate callback.
1208  */
1209 static void
1210 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1212     SPCanvas *canvas = SP_CANVAS (widget);
1214     /* Schedule redraw of new region */
1215     sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1216     if (allocation->width > widget->allocation.width) {
1217         sp_canvas_request_redraw (canvas,
1218                                   canvas->x0 + widget->allocation.width,
1219                                   0,
1220                                   canvas->x0 + allocation->width,
1221                                   canvas->y0 + allocation->height);
1222     }
1223     if (allocation->height > widget->allocation.height) {
1224         sp_canvas_request_redraw (canvas,
1225                                   0,
1226                                   canvas->y0 + widget->allocation.height,
1227                                   canvas->x0 + allocation->width,
1228                                   canvas->y0 + allocation->height);
1229     }
1231     widget->allocation = *allocation;
1233     if (GTK_WIDGET_REALIZED (widget)) {
1234         gdk_window_move_resize (widget->window,
1235                                 widget->allocation.x, widget->allocation.y,
1236                                 widget->allocation.width, widget->allocation.height);
1237     }
1240 /**
1241  * Helper that emits an event for an item in the canvas, be it the current
1242  * item, grabbed item, or focused item, as appropriate.
1243  */
1244 static int
1245 emit_event (SPCanvas *canvas, GdkEvent *event)
1247     guint mask;
1249     if (canvas->grabbed_item) {
1250         switch (event->type) {
1251         case GDK_ENTER_NOTIFY:
1252             mask = GDK_ENTER_NOTIFY_MASK;
1253             break;
1254         case GDK_LEAVE_NOTIFY:
1255             mask = GDK_LEAVE_NOTIFY_MASK;
1256             break;
1257         case GDK_MOTION_NOTIFY:
1258             mask = GDK_POINTER_MOTION_MASK;
1259             break;
1260         case GDK_BUTTON_PRESS:
1261         case GDK_2BUTTON_PRESS:
1262         case GDK_3BUTTON_PRESS:
1263             mask = GDK_BUTTON_PRESS_MASK;
1264             break;
1265         case GDK_BUTTON_RELEASE:
1266             mask = GDK_BUTTON_RELEASE_MASK;
1267             break;
1268         case GDK_KEY_PRESS:
1269             mask = GDK_KEY_PRESS_MASK;
1270             break;
1271         case GDK_KEY_RELEASE:
1272             mask = GDK_KEY_RELEASE_MASK;
1273             break;
1274         case GDK_SCROLL:
1275             mask = GDK_SCROLL;
1276             break;
1277         default:
1278             mask = 0;
1279             break;
1280         }
1282         if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1283     }
1285     /* Convert to world coordinates -- we have two cases because of different
1286      * offsets of the fields in the event structures.
1287      */
1289     GdkEvent ev = *event;
1291     switch (ev.type) {
1292     case GDK_ENTER_NOTIFY:
1293     case GDK_LEAVE_NOTIFY:
1294         ev.crossing.x += canvas->x0;
1295         ev.crossing.y += canvas->y0;
1296         break;
1297     case GDK_MOTION_NOTIFY:
1298     case GDK_BUTTON_PRESS:
1299     case GDK_2BUTTON_PRESS:
1300     case GDK_3BUTTON_PRESS:
1301     case GDK_BUTTON_RELEASE:
1302         ev.motion.x += canvas->x0;
1303         ev.motion.y += canvas->y0;
1304         break;
1305     default:
1306         break;
1307     }
1309     /* Choose where we send the event */
1311     /* canvas->current_item becomes NULL in some cases under Win32
1312     ** (e.g. if the pointer leaves the window).  So this is a hack that
1313     ** Lauris applied to SP to get around the problem.
1314     */
1315     SPCanvasItem* item = NULL;
1316     if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1317         item = canvas->grabbed_item;
1318     } else {
1319         item = canvas->current_item;
1320     }
1322     if (canvas->focused_item &&
1323         ((event->type == GDK_KEY_PRESS) ||
1324          (event->type == GDK_KEY_RELEASE) ||
1325          (event->type == GDK_FOCUS_CHANGE))) {
1326         item = canvas->focused_item;
1327     }
1329     /* The event is propagated up the hierarchy (for if someone connected to
1330      * a group instead of a leaf event), and emission is stopped if a
1331      * handler returns TRUE, just like for GtkWidget events.
1332      */
1334     gint finished = FALSE;
1336     while (item && !finished) {
1337         gtk_object_ref (GTK_OBJECT (item));
1338         gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1339         SPCanvasItem *parent = item->parent;
1340         gtk_object_unref (GTK_OBJECT (item));
1341         item = parent;
1342     }
1344     return finished;
1347 /**
1348  * Helper that re-picks the current item in the canvas, based on the event's
1349  * coordinates and emits enter/leave events for items as appropriate.
1350  */
1351 static int
1352 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1354     int button_down = 0;
1355     double x, y;
1357     if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1358         return FALSE;
1360     int retval = FALSE;
1362     if (canvas->gen_all_enter_events == false) {
1363         // If a button is down, we'll perform enter and leave events on the
1364         // current item, but not enter on any other item.  This is more or
1365         // less like X pointer grabbing for canvas items.
1366         //
1367         button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1368                 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1370         if (!button_down) canvas->left_grabbed_item = FALSE;
1371     }
1373     /* Save the event in the canvas.  This is used to synthesize enter and
1374      * leave events in case the current item changes.  It is also used to
1375      * re-pick the current item if the current one gets deleted.  Also,
1376      * synthesize an enter event.
1377      */
1378     if (event != &canvas->pick_event) {
1379         if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1380             /* these fields have the same offsets in both types of events */
1382             canvas->pick_event.crossing.type       = GDK_ENTER_NOTIFY;
1383             canvas->pick_event.crossing.window     = event->motion.window;
1384             canvas->pick_event.crossing.send_event = event->motion.send_event;
1385             canvas->pick_event.crossing.subwindow  = NULL;
1386             canvas->pick_event.crossing.x          = event->motion.x;
1387             canvas->pick_event.crossing.y          = event->motion.y;
1388             canvas->pick_event.crossing.mode       = GDK_CROSSING_NORMAL;
1389             canvas->pick_event.crossing.detail     = GDK_NOTIFY_NONLINEAR;
1390             canvas->pick_event.crossing.focus      = FALSE;
1391             canvas->pick_event.crossing.state      = event->motion.state;
1393             /* these fields don't have the same offsets in both types of events */
1395             if (event->type == GDK_MOTION_NOTIFY) {
1396                 canvas->pick_event.crossing.x_root = event->motion.x_root;
1397                 canvas->pick_event.crossing.y_root = event->motion.y_root;
1398             } else {
1399                 canvas->pick_event.crossing.x_root = event->button.x_root;
1400                 canvas->pick_event.crossing.y_root = event->button.y_root;
1401             }
1402         } else {
1403             canvas->pick_event = *event;
1404         }
1405     }
1407     /* Don't do anything else if this is a recursive call */
1408     if (canvas->in_repick) return retval;
1410     /* LeaveNotify means that there is no current item, so we don't look for one */
1411     if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1412         /* these fields don't have the same offsets in both types of events */
1414         if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1415             x = canvas->pick_event.crossing.x;
1416             y = canvas->pick_event.crossing.y;
1417         } else {
1418             x = canvas->pick_event.motion.x;
1419             y = canvas->pick_event.motion.y;
1420         }
1422         /* world coords */
1423         x += canvas->x0;
1424         y += canvas->y0;
1426         /* find the closest item */
1427         if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1428             sp_canvas_item_invoke_point (canvas->root, Geom::Point(x, y), &canvas->new_current_item);
1429         } else {
1430             canvas->new_current_item = NULL;
1431         }
1432     } else {
1433         canvas->new_current_item = NULL;
1434     }
1436     if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1437         return retval; /* current item did not change */
1438     }
1440     /* Synthesize events for old and new current items */
1442     if ((canvas->new_current_item != canvas->current_item)
1443         && (canvas->current_item != NULL)
1444         && !canvas->left_grabbed_item) {
1445         GdkEvent new_event;
1446         SPCanvasItem *item;
1448         item = canvas->current_item;
1450         new_event = canvas->pick_event;
1451         new_event.type = GDK_LEAVE_NOTIFY;
1453         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1454         new_event.crossing.subwindow = NULL;
1455         canvas->in_repick = TRUE;
1456         retval = emit_event (canvas, &new_event);
1457         canvas->in_repick = FALSE;
1458     }
1460     if (canvas->gen_all_enter_events == false) {
1461         // new_current_item may have been set to NULL during the call to
1462         // emit_event() above
1463         if ((canvas->new_current_item != canvas->current_item) && button_down) {
1464             canvas->left_grabbed_item = TRUE;
1465             return retval;
1466         }
1467     }
1469     /* Handle the rest of cases */
1471     canvas->left_grabbed_item = FALSE;
1472     canvas->current_item = canvas->new_current_item;
1474     if (canvas->current_item != NULL) {
1475         GdkEvent new_event;
1477         new_event = canvas->pick_event;
1478         new_event.type = GDK_ENTER_NOTIFY;
1479         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1480         new_event.crossing.subwindow = NULL;
1481         retval = emit_event (canvas, &new_event);
1482     }
1484     return retval;
1487 /**
1488  * Button event handler for the canvas.
1489  */
1490 static gint
1491 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1493     SPCanvas *canvas = SP_CANVAS (widget);
1495     int retval = FALSE;
1497     /* dispatch normally regardless of the event's window if an item
1498        has a pointer grab in effect */
1499     if (!canvas->grabbed_item &&
1500         event->window != SP_CANVAS_WINDOW (canvas))
1501         return retval;
1503     int mask;
1504     switch (event->button) {
1505     case 1:
1506         mask = GDK_BUTTON1_MASK;
1507         break;
1508     case 2:
1509         mask = GDK_BUTTON2_MASK;
1510         break;
1511     case 3:
1512         mask = GDK_BUTTON3_MASK;
1513         break;
1514     case 4:
1515         mask = GDK_BUTTON4_MASK;
1516         break;
1517     case 5:
1518         mask = GDK_BUTTON5_MASK;
1519         break;
1520     default:
1521         mask = 0;
1522     }
1524     switch (event->type) {
1525     case GDK_BUTTON_PRESS:
1526     case GDK_2BUTTON_PRESS:
1527     case GDK_3BUTTON_PRESS:
1528         /* Pick the current item as if the button were not pressed, and
1529          * then process the event.
1530          */
1531         canvas->state = event->state;
1532         pick_current_item (canvas, (GdkEvent *) event);
1533         canvas->state ^= mask;
1534         retval = emit_event (canvas, (GdkEvent *) event);
1535         break;
1537     case GDK_BUTTON_RELEASE:
1538         /* Process the event as if the button were pressed, then repick
1539          * after the button has been released
1540          */
1541         canvas->state = event->state;
1542         retval = emit_event (canvas, (GdkEvent *) event);
1543         event->state ^= mask;
1544         canvas->state = event->state;
1545         pick_current_item (canvas, (GdkEvent *) event);
1546         event->state ^= mask;
1548         break;
1550     default:
1551         g_assert_not_reached ();
1552     }
1554     return retval;
1557 /**
1558  * Scroll event handler for the canvas.
1559  *
1560  * \todo FIXME: generate motion events to re-select items.
1561  */
1562 static gint
1563 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1565     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1568 static inline void request_motions(GdkWindow *w, GdkEventMotion *event) {
1569     gdk_window_get_pointer(w, NULL, NULL, NULL);
1570 #if HAS_GDK_EVENT_REQUEST_MOTIONS
1571     gdk_event_request_motions(event);
1572 #endif
1575 /**
1576  * Motion event handler for the canvas.
1577  */
1578 static int
1579 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1581         int status;
1582     SPCanvas *canvas = SP_CANVAS (widget);
1584     track_latency((GdkEvent *)event);
1586     if (event->window != SP_CANVAS_WINDOW (canvas))
1587         return FALSE;
1589     if (canvas->pixmap_gc == NULL) // canvas being deleted
1590         return FALSE;
1592     canvas->state = event->state;
1593         pick_current_item (canvas, (GdkEvent *) event);
1594         status = emit_event (canvas, (GdkEvent *) event);
1595         if (event->is_hint) {
1596                 request_motions(widget->window, event);
1597         }
1599     return status;
1602 static void
1603 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)
1605     GtkWidget *widget = GTK_WIDGET (canvas);
1607     SPCanvasBuf buf;
1608     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1609         buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1610     } else {
1611         buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1612     }
1614     // Mark the region clean
1615     sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1617     buf.buf_rowstride = sw * 4;
1618     buf.rect.x0 = x0;
1619     buf.rect.y0 = y0;
1620     buf.rect.x1 = x1;
1621     buf.rect.y1 = y1;
1622     buf.visible_rect.x0 = draw_x1;
1623     buf.visible_rect.y0 = draw_y1;
1624     buf.visible_rect.x1 = draw_x2;
1625     buf.visible_rect.y1 = draw_y2;
1626     GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1627     buf.bg_color = (((color->red & 0xff00) << 8)
1628                     | (color->green & 0xff00)
1629                     | (color->blue >> 8));
1630     buf.is_empty = true;
1632     buf.ct = nr_create_cairo_context_canvasbuf (&(buf.visible_rect), &buf);
1634     if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1635         SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1636     }
1638 #if ENABLE_LCMS
1639     cmsHTRANSFORM transf = 0;
1640     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1641     bool fromDisplay = prefs->getBool( "/options/displayprofile/from_display");
1642     if ( fromDisplay ) {
1643         transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" );
1644     } else {
1645         transf = Inkscape::colorprofile_get_display_transform();
1646     }
1647 #endif // ENABLE_LCMS
1649     if (buf.is_empty) {
1650 #if ENABLE_LCMS
1651         if ( transf && canvas->enable_cms_display_adj ) {
1652             cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1653         }
1654 #endif // ENABLE_LCMS
1655         gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1656         gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1657                             canvas->pixmap_gc,
1658                             TRUE,
1659                             x0 - canvas->x0, y0 - canvas->y0,
1660                             x1 - x0, y1 - y0);
1661     } else {
1663 #if ENABLE_LCMS
1664         if ( transf && canvas->enable_cms_display_adj ) {
1665             for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1666                 guchar* p = buf.buf + (buf.buf_rowstride * yy);
1667                 cmsDoTransform( transf, p, p, (x1 - x0) );
1668             }
1669         }
1670 #endif // ENABLE_LCMS
1672 // Now we only need to output the prepared pixmap to the actual screen, and this define chooses one
1673 // of the two ways to do it. The cairo way is direct and straightforward, but unfortunately
1674 // noticeably slower. I asked Carl Worth but he was unable so far to suggest any specific reason
1675 // for this slowness. So, for now we use the oldish method: squeeze out 32bpp buffer to 24bpp and
1676 // use gdk_draw_rgb_image_dithalign, for unfortunately gdk can only handle 24 bpp, which cairo
1677 // cannot handle at all. Still, this way is currently faster even despite the blit with squeeze.
1679 ///#define CANVAS_OUTPUT_VIA_CAIRO
1681 #ifdef CANVAS_OUTPUT_VIA_CAIRO
1683         buf.cst = cairo_image_surface_create_for_data (
1684             buf.buf,
1685             CAIRO_FORMAT_ARGB32,  // unpacked, i.e. 32 bits! one byte is unused
1686             x1 - x0, y1 - y0,
1687             buf.buf_rowstride
1688             );
1689         cairo_t *window_ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1690         cairo_set_source_surface (window_ct, buf.cst, x0 - canvas->x0, y0 - canvas->y0);
1691         cairo_paint (window_ct);
1692         cairo_destroy (window_ct);
1693         cairo_surface_finish (buf.cst);
1694         cairo_surface_destroy (buf.cst);
1696 #else
1698         NRPixBlock b3;
1699         nr_pixblock_setup_fast (&b3, NR_PIXBLOCK_MODE_R8G8B8, x0, y0, x1, y1, TRUE);
1701         NRPixBlock b4;
1702         nr_pixblock_setup_extern (&b4, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1,
1703                                   buf.buf,
1704                                   buf.buf_rowstride,
1705                                   FALSE, FALSE);
1707         // this does the 32->24 squishing, using an assembler routine:
1708         nr_blit_pixblock_pixblock (&b3, &b4);
1710         gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1711                                       canvas->pixmap_gc,
1712                                       x0 - canvas->x0, y0 - canvas->y0,
1713                                       x1 - x0, y1 - y0,
1714                                       GDK_RGB_DITHER_MAX,
1715                                       NR_PIXBLOCK_PX(&b3),
1716                                       sw * 3,
1717                                       x0 - canvas->x0, y0 - canvas->y0);
1719         nr_pixblock_release (&b3);
1720         nr_pixblock_release (&b4);
1721 #endif
1722     }
1724     cairo_surface_t *cst = cairo_get_target(buf.ct);
1725     cairo_destroy (buf.ct);
1726     cairo_surface_finish (cst);
1727     cairo_surface_destroy (cst);
1729     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1730         nr_pixelstore_256K_free (buf.buf);
1731     } else {
1732         nr_pixelstore_1M_free (buf.buf);
1733     }
1736 struct PaintRectSetup {
1737     SPCanvas* canvas;
1738     NRRectL big_rect;
1739     GTimeVal start_time;
1740     int max_pixels;
1741     Geom::Point mouse_loc;
1742 };
1744 /**
1745  * Paint the given rect, recursively subdividing the region until it is the size of a single
1746  * buffer.
1747  *
1748  * @return true if the drawing completes
1749  */
1750 static int
1751 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1753     GTimeVal now;
1754     g_get_current_time (&now);
1756     glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1757         + (now.tv_usec - setup->start_time.tv_usec);
1759     // Allow only very fast buffers to be run together;
1760     // as soon as the total redraw time exceeds 1ms, cancel;
1761     // this returns control to the idle loop and allows Inkscape to process user input
1762     // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1763     // it will get back and finish painting what remains to paint.
1764     if (elapsed > 1000) {
1766         // Interrupting redraw isn't always good.
1767         // For example, when you drag one node of a big path, only the buffer containing
1768         // the mouse cursor will be redrawn again and again, and the rest of the path
1769         // will remain stale because Inkscape never has enough idle time to redraw all
1770         // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1771         // If this limit is set, and if we have aborted redraw more times than is allowed,
1772         // interrupting is blocked and we're forced to redraw full screen once
1773         // (after which we can again interrupt forced_redraw_limit times).
1774         if (setup->canvas->forced_redraw_limit < 0 ||
1775             setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1777             if (setup->canvas->forced_redraw_limit != -1) {
1778                 setup->canvas->forced_redraw_count++;
1779             }
1781             return false;
1782         }
1783     }
1785     // Find the optimal buffer dimensions
1786     int bw = this_rect.x1 - this_rect.x0;
1787     int bh = this_rect.y1 - this_rect.y0;
1788     if ((bw < 1) || (bh < 1))
1789         return 0;
1791     if (bw * bh < setup->max_pixels) {
1792         // We are small enough
1793         sp_canvas_paint_single_buffer (setup->canvas,
1794                                        this_rect.x0, this_rect.y0,
1795                                        this_rect.x1, this_rect.y1,
1796                                        setup->big_rect.x0, setup->big_rect.y0,
1797                                        setup->big_rect.x1, setup->big_rect.y1, bw);
1798         return 1;
1799     }
1801     NRRectL lo = this_rect;
1802     NRRectL hi = this_rect;
1804 /*
1805 This test determines the redraw strategy:
1807 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1808 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1809 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1810 and seems to be faster for drawings with many smaller objects at zoom-out.
1812 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1813 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1814 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1815 faster.
1817 The default for now is the strips mode.
1818 */
1819     if (bw < bh || bh < 2 * TILE_SIZE) {
1820         // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1821         int mid = ((long long) this_rect.x0 + (long long) this_rect.x1) / 2;
1822         // Make sure that mid lies on a tile boundary
1823         mid = (mid / TILE_SIZE) * TILE_SIZE;
1825         lo.x1 = mid;
1826         hi.x0 = mid;
1828         if (setup->mouse_loc[Geom::X] < mid) {
1829             // Always paint towards the mouse first
1830             return sp_canvas_paint_rect_internal(setup, lo)
1831                 && sp_canvas_paint_rect_internal(setup, hi);
1832         } else {
1833             return sp_canvas_paint_rect_internal(setup, hi)
1834                 && sp_canvas_paint_rect_internal(setup, lo);
1835         }
1836     } else {
1837         // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1838         int mid = ((long long) this_rect.y0 + (long long) this_rect.y1) / 2;
1839         // Make sure that mid lies on a tile boundary
1840         mid = (mid / TILE_SIZE) * TILE_SIZE;
1842         lo.y1 = mid;
1843         hi.y0 = mid;
1845         if (setup->mouse_loc[Geom::Y] < mid) {
1846             // Always paint towards the mouse first
1847             return sp_canvas_paint_rect_internal(setup, lo)
1848                 && sp_canvas_paint_rect_internal(setup, hi);
1849         } else {
1850             return sp_canvas_paint_rect_internal(setup, hi)
1851                 && sp_canvas_paint_rect_internal(setup, lo);
1852         }
1853     }
1857 /**
1858  * Helper that draws a specific rectangular part of the canvas.
1859  *
1860  * @return true if the rectangle painting succeeds.
1861  */
1862 static bool
1863 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1865     g_return_val_if_fail (!canvas->need_update, false);
1867     NRRectL rect;
1868     rect.x0 = xx0;
1869     rect.x1 = xx1;
1870     rect.y0 = yy0;
1871     rect.y1 = yy1;
1873     // Clip rect-to-draw by the current visible area
1874     rect.x0 = MAX (rect.x0, canvas->x0);
1875     rect.y0 = MAX (rect.y0, canvas->y0);
1876     rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1877     rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1879 #ifdef DEBUG_REDRAW
1880     // paint the area to redraw yellow
1881     gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1882     gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1883                         canvas->pixmap_gc,
1884                         TRUE,
1885                         rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1886                         rect.x1 - rect.x0, rect.y1 - rect.y0);
1887 #endif
1889     PaintRectSetup setup;
1891     setup.canvas = canvas;
1892     setup.big_rect = rect;
1894     // Save the mouse location
1895     gint x, y;
1896     gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1897     setup.mouse_loc = sp_canvas_window_to_world (canvas, Geom::Point(x,y));
1899     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1900         // use 256K as a compromise to not slow down gradients
1901         // 256K is the cached buffer and we need 4 channels
1902         setup.max_pixels = 65536; // 256K/4
1903     } else {
1904         // paths only, so 1M works faster
1905         // 1M is the cached buffer and we need 4 channels
1906         setup.max_pixels = 262144;
1907     }
1909     // Start the clock
1910     g_get_current_time(&(setup.start_time));
1912     // Go
1913     return sp_canvas_paint_rect_internal(&setup, rect);
1916 /**
1917  * Force a full redraw after a specified number of interrupted redraws
1918  */
1919 void
1920 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1921   g_return_if_fail(canvas != NULL);
1923   canvas->forced_redraw_limit = count;
1924   canvas->forced_redraw_count = 0;
1927 /**
1928  * End forced full redraw requests
1929  */
1930 void
1931 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1932   g_return_if_fail(canvas != NULL);
1934   canvas->forced_redraw_limit = -1;
1937 /**
1938  * The canvas widget's expose callback.
1939  */
1940 static gint
1941 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1943     SPCanvas *canvas = SP_CANVAS (widget);
1945     if (!GTK_WIDGET_DRAWABLE (widget) ||
1946         (event->window != SP_CANVAS_WINDOW (canvas)))
1947         return FALSE;
1949     int n_rects;
1950     GdkRectangle *rects;
1951     gdk_region_get_rectangles (event->region, &rects, &n_rects);
1953     for (int i = 0; i < n_rects; i++) {
1954         NRRectL rect;
1956         rect.x0 = rects[i].x + canvas->x0;
1957         rect.y0 = rects[i].y + canvas->y0;
1958         rect.x1 = rect.x0 + rects[i].width;
1959         rect.y1 = rect.y0 + rects[i].height;
1961         sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1962     }
1964     if (n_rects > 0)
1965         g_free (rects);
1967     return FALSE;
1970 /**
1971  * The canvas widget's keypress callback.
1972  */
1973 static gint
1974 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1976     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1979 /**
1980  * Crossing event handler for the canvas.
1981  */
1982 static gint
1983 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1985     SPCanvas *canvas = SP_CANVAS (widget);
1987     if (event->window != SP_CANVAS_WINDOW (canvas))
1988         return FALSE;
1990     canvas->state = event->state;
1991     return pick_current_item (canvas, (GdkEvent *) event);
1994 /**
1995  * Focus in handler for the canvas.
1996  */
1997 static gint
1998 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
2000     GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
2002     SPCanvas *canvas = SP_CANVAS (widget);
2004     if (canvas->focused_item) {
2005         return emit_event (canvas, (GdkEvent *) event);
2006     } else {
2007         return FALSE;
2008     }
2011 /**
2012  * Focus out handler for the canvas.
2013  */
2014 static gint
2015 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
2017     GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2019     SPCanvas *canvas = SP_CANVAS (widget);
2021     if (canvas->focused_item)
2022         return emit_event (canvas, (GdkEvent *) event);
2023     else
2024         return FALSE;
2027 /**
2028  * Helper that repaints the areas in the canvas that need it.
2029  *
2030  * @return true if all the dirty parts have been redrawn
2031  */
2032 static int
2033 paint (SPCanvas *canvas)
2035     if (canvas->need_update) {
2036         sp_canvas_item_invoke_update (canvas->root, Geom::identity(), 0);
2037         canvas->need_update = FALSE;
2038     }
2040     if (!canvas->need_redraw)
2041         return TRUE;
2043     Gdk::Region to_paint;
2045     for (int j=canvas->tTop; j<canvas->tBottom; j++) {
2046         for (int i=canvas->tLeft; i<canvas->tRight; i++) {
2047             int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
2049             if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
2050                 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
2051                                    TILE_SIZE, TILE_SIZE));
2052             }
2054         }
2055     }
2057     if (!to_paint.empty()) {
2058         Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
2059         typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
2060         for (Iter i=rect.begin(); i != rect.end(); ++i) {
2061             int x0 = (*i).get_x();
2062             int y0 = (*i).get_y();
2063             int x1 = x0 + (*i).get_width();
2064             int y1 = y0 + (*i).get_height();
2065             if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
2066                 // Aborted
2067                 return FALSE;
2068             };
2069         }
2070     }
2072     canvas->need_redraw = FALSE;
2074     // we've had a full unaborted redraw, reset the full redraw counter
2075     if (canvas->forced_redraw_limit != -1) {
2076         canvas->forced_redraw_count = 0;
2077     }
2079     return TRUE;
2082 /**
2083  * Helper that invokes update, paint, and repick on canvas.
2084  */
2085 static int
2086 do_update (SPCanvas *canvas)
2088     if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop durring interrupted display!
2089         return TRUE;
2090         
2091     if (canvas->drawing_disabled)
2092         return TRUE;
2094     /* Cause the update if necessary */
2095     if (canvas->need_update) {
2096         sp_canvas_item_invoke_update (canvas->root, Geom::identity(), 0);
2097         canvas->need_update = FALSE;
2098     }
2100     /* Paint if able to */
2101     if (GTK_WIDGET_DRAWABLE (canvas)) {
2102             return paint (canvas);
2103     }
2105     /* Pick new current item */
2106     while (canvas->need_repick) {
2107         canvas->need_repick = FALSE;
2108         pick_current_item (canvas, &canvas->pick_event);
2109     }
2111     return TRUE;
2114 /**
2115  * Idle handler for the canvas that deals with pending updates and redraws.
2116  */
2117 static gint
2118 idle_handler (gpointer data)
2120     GDK_THREADS_ENTER ();
2122     SPCanvas *canvas = SP_CANVAS (data);
2124     int const ret = do_update (canvas);
2126     if (ret) {
2127         /* Reset idle id */
2128         canvas->idle_id = 0;
2129     }
2131     GDK_THREADS_LEAVE ();
2133     return !ret;
2136 /**
2137  * Convenience function to add an idle handler to a canvas.
2138  */
2139 static void
2140 add_idle (SPCanvas *canvas)
2142     if (canvas->idle_id != 0)
2143         return;
2145     canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2148 /**
2149  * Returns the root group of the specified canvas.
2150  */
2151 SPCanvasGroup *
2152 sp_canvas_root (SPCanvas *canvas)
2154     g_return_val_if_fail (canvas != NULL, NULL);
2155     g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2157     return SP_CANVAS_GROUP (canvas->root);
2160 /**
2161  * Scrolls canvas to specific position (cx and cy are measured in screen pixels)
2162  */
2163 void
2164 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2166     g_return_if_fail (canvas != NULL);
2167     g_return_if_fail (SP_IS_CANVAS (canvas));
2169     int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels)
2170     int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do!
2171     int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the
2172     int dy = iy - canvas->y0; // canvas w.r.t its previous position
2174     canvas->dx0 = cx; // here the 'd' stands for double, not delta!
2175     canvas->dy0 = cy;
2176     canvas->x0 = ix;
2177     canvas->y0 = iy;
2179     sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2181     if (!clear) {
2182         // scrolling without zoom; redraw only the newly exposed areas
2183         if ((dx != 0) || (dy != 0)) {
2184             canvas->is_scrolling = is_scrolling;
2185             if (GTK_WIDGET_REALIZED (canvas)) {
2186                 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2187             }
2188         }
2189     } else {
2190         // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2191     }
2194 /**
2195  * Updates canvas if necessary.
2196  */
2197 void
2198 sp_canvas_update_now (SPCanvas *canvas)
2200     g_return_if_fail (canvas != NULL);
2201     g_return_if_fail (SP_IS_CANVAS (canvas));
2203     if (!(canvas->need_update ||
2204           canvas->need_redraw))
2205         return;
2207     do_update (canvas);
2210 /**
2211  * Update callback for canvas widget.
2212  */
2213 static void
2214 sp_canvas_request_update (SPCanvas *canvas)
2216     canvas->need_update = TRUE;
2217     add_idle (canvas);
2220 /**
2221  * Forces redraw of rectangular canvas area.
2222  */
2223 void
2224 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2226     NRRectL bbox;
2227     NRRectL visible;
2228     NRRectL clip;
2230     g_return_if_fail (canvas != NULL);
2231     g_return_if_fail (SP_IS_CANVAS (canvas));
2233     if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2234     if ((x0 >= x1) || (y0 >= y1)) return;
2236     bbox.x0 = x0;
2237     bbox.y0 = y0;
2238     bbox.x1 = x1;
2239     bbox.y1 = y1;
2241     visible.x0 = canvas->x0;
2242     visible.y0 = canvas->y0;
2243     visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2244     visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2246     nr_rect_l_intersect (&clip, &bbox, &visible);
2248     sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2249     add_idle (canvas);
2252 /**
2253  * Sets world coordinates from win and canvas.
2254  */
2255 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2257     g_return_if_fail (canvas != NULL);
2258     g_return_if_fail (SP_IS_CANVAS (canvas));
2260     if (worldx) *worldx = canvas->x0 + winx;
2261     if (worldy) *worldy = canvas->y0 + winy;
2264 /**
2265  * Sets win coordinates from world and canvas.
2266  */
2267 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2269     g_return_if_fail (canvas != NULL);
2270     g_return_if_fail (SP_IS_CANVAS (canvas));
2272     if (winx) *winx = worldx - canvas->x0;
2273     if (winy) *winy = worldy - canvas->y0;
2276 /**
2277  * Converts point from win to world coordinates.
2278  */
2279 Geom::Point sp_canvas_window_to_world(SPCanvas const *canvas, Geom::Point const win)
2281     g_assert (canvas != NULL);
2282     g_assert (SP_IS_CANVAS (canvas));
2284     return Geom::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2287 /**
2288  * Converts point from world to win coordinates.
2289  */
2290 Geom::Point sp_canvas_world_to_window(SPCanvas const *canvas, Geom::Point const world)
2292     g_assert (canvas != NULL);
2293     g_assert (SP_IS_CANVAS (canvas));
2295     return Geom::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2298 /**
2299  * Returns true if point given in world coordinates is inside window.
2300  */
2301 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, Geom::Point const &world)
2303     g_assert( canvas != NULL );
2304     g_assert(SP_IS_CANVAS(canvas));
2306     GtkWidget const &w = *GTK_WIDGET(canvas);
2307     return ( ( canvas->x0 <= world[Geom::X] )  &&
2308              ( canvas->y0 <= world[Geom::Y] )  &&
2309              ( world[Geom::X] < canvas->x0 + w.allocation.width )  &&
2310              ( world[Geom::Y] < canvas->y0 + w.allocation.height ) );
2313 /**
2314  * Return canvas window coordinates as Geom::Rect.
2315  */
2316 Geom::Rect SPCanvas::getViewbox() const
2318     GtkWidget const *w = GTK_WIDGET(this);
2319     return Geom::Rect(Geom::Point(dx0, dy0),
2320                       Geom::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2323 /**
2324  * Return canvas window coordinates as IRect (a rectangle defined by integers).
2325  */
2326 NR::IRect SPCanvas::getViewboxIntegers() const
2328     GtkWidget const *w = GTK_WIDGET(this);
2329     return NR::IRect(NR::IPoint(x0, y0),
2330                     NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height));
2333 inline int sp_canvas_tile_floor(int x)
2335     return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2338 inline int sp_canvas_tile_ceil(int x)
2340     return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2343 /**
2344  * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2345  */
2346 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2348     if ( nl >= nr || nt >= nb ) {
2349         if ( canvas->tiles ) g_free(canvas->tiles);
2350         canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2351         canvas->tileH=canvas->tileV=0;
2352         canvas->tiles=NULL;
2353         return;
2354     }
2355     int tl=sp_canvas_tile_floor(nl);
2356     int tt=sp_canvas_tile_floor(nt);
2357     int tr=sp_canvas_tile_ceil(nr);
2358     int tb=sp_canvas_tile_ceil(nb);
2360     int nh = tr-tl, nv = tb-tt;
2361     uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2362     for (int i=tl; i<tr; i++) {
2363         for (int j=tt; j<tb; j++) {
2364             int ind = (i-tl) + (j-tt)*nh;
2365             if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2366                 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2367             } else {
2368                 ntiles[ind]=0; // newly exposed areas get 0
2369             }
2370         }
2371     }
2372     if ( canvas->tiles ) g_free(canvas->tiles);
2373     canvas->tiles=ntiles;
2374     canvas->tLeft=tl;
2375     canvas->tTop=tt;
2376     canvas->tRight=tr;
2377     canvas->tBottom=tb;
2378     canvas->tileH=nh;
2379     canvas->tileV=nv;
2382 /*
2383  * Helper that queues a canvas rectangle for redraw
2384  */
2385 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2386     canvas->need_redraw = TRUE;
2388     sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2391 /**
2392  * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2393  */
2394 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2396     if ( nl >= nr || nt >= nb ) {
2397         return;
2398     }
2399     int tl=sp_canvas_tile_floor(nl);
2400     int tt=sp_canvas_tile_floor(nt);
2401     int tr=sp_canvas_tile_ceil(nr);
2402     int tb=sp_canvas_tile_ceil(nb);
2403     if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2404     if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2405     if ( tr > canvas->tRight ) tr=canvas->tRight;
2406     if ( tt < canvas->tTop ) tt=canvas->tTop;
2407     if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2409     for (int i=tl; i<tr; i++) {
2410         for (int j=tt; j<tb; j++) {
2411             canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2412         }
2413     }
2417 /*
2418   Local Variables:
2419   mode:c++
2420   c-file-style:"stroustrup"
2421   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2422   indent-tabs-mode:nil
2423   fill-column:99
2424   End:
2425 */
2426 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :