Code

Merging 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 <libnr/nr-matrix-fns.h>
36 #include <libnr/nr-matrix-ops.h>
37 #include <libnr/nr-convex-hull.h>
38 #include "prefs-utils.h"
39 #include "inkscape.h"
40 #include "sodipodi-ctrlrect.h"
41 #if ENABLE_LCMS
42 #include "color-profile-fns.h"
43 #endif // ENABLE_LCMS
44 #include "display/rendermode.h"
45 #include "libnr/nr-blit.h"
46 #include "display/inkscape-cairo.h"
47 #include "debug/gdk-event-latency-tracker.h"
49 using Inkscape::Debug::GdkEventLatencyTracker;
51 // GTK_CHECK_VERSION returns false on failure
52 #define HAS_GDK_EVENT_REQUEST_MOTIONS GTK_CHECK_VERSION(2, 12, 0)
54 // gtk_check_version returns non-NULL on failure
55 static bool const HAS_BROKEN_MOTION_HINTS =
56   true || gtk_check_version(2, 12, 0) != NULL || !HAS_GDK_EVENT_REQUEST_MOTIONS;
58 // Define this to visualize the regions to be redrawn
59 //#define DEBUG_REDRAW 1;
61 // Tiles are a way to minimize the number of redraws, eliminating too small redraws.
62 // The canvas stores a 2D array of ints, each representing a TILE_SIZExTILE_SIZE pixels tile.
63 // If any part of it is dirtied, the entire tile is dirtied (its int is nonzero) and repainted.
64 #define TILE_SIZE 16
66 static gint const sp_canvas_update_priority = G_PRIORITY_HIGH_IDLE;
68 #define SP_CANVAS_WINDOW(c) (((GtkWidget *) (c))->window)
70 enum {
71     SP_CANVAS_ITEM_VISIBLE = 1 << 7,
72     SP_CANVAS_ITEM_NEED_UPDATE = 1 << 8,
73     SP_CANVAS_ITEM_NEED_AFFINE = 1 << 9
74 };
76 /**
77  * A group of Items.
78  */
79 struct SPCanvasGroup {
80     SPCanvasItem item;
82     GList *items, *last;
83 };
85 /**
86  * The SPCanvasGroup vtable.
87  */
88 struct SPCanvasGroupClass {
89     SPCanvasItemClass parent_class;
90 };
92 /**
93  * The SPCanvas vtable.
94  */
95 struct SPCanvasClass {
96     GtkWidgetClass parent_class;
97 };
99 static void group_add (SPCanvasGroup *group, SPCanvasItem *item);
100 static void group_remove (SPCanvasGroup *group, SPCanvasItem *item);
102 /* SPCanvasItem */
104 enum {ITEM_EVENT, ITEM_LAST_SIGNAL};
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);
117 static guint item_signals[ITEM_LAST_SIGNAL] = { 0 };
119 static GtkObjectClass *item_parent_class;
121 /**
122  * Registers the SPCanvasItem class with Glib and returns its type number.
123  */
124 GType
125 sp_canvas_item_get_type (void)
127     static GType type = 0;
128     if (!type) {
129         static GTypeInfo const info = {
130             sizeof (SPCanvasItemClass),
131             NULL, NULL,
132             (GClassInitFunc) sp_canvas_item_class_init,
133             NULL, NULL,
134             sizeof (SPCanvasItem),
135             0,
136             (GInstanceInitFunc) sp_canvas_item_init,
137             NULL
138         };
139         type = g_type_register_static (GTK_TYPE_OBJECT, "SPCanvasItem", &info, (GTypeFlags)0);
140     }
142     return type;
145 /**
146  * Initializes the SPCanvasItem vtable and the "event" signal.
147  */
148 static void
149 sp_canvas_item_class_init (SPCanvasItemClass *klass)
151     GObjectClass *object_class = (GObjectClass *) klass;
153     /* fixme: Derive from GObject */
154     item_parent_class = (GtkObjectClass*)gtk_type_class (GTK_TYPE_OBJECT);
156     item_signals[ITEM_EVENT] = g_signal_new ("event",
157                                              G_TYPE_FROM_CLASS (klass),
158                                              G_SIGNAL_RUN_LAST,
159                                              ((glong)((guint8*)&(klass->event) - (guint8*)klass)),
160                                              NULL, NULL,
161                                              sp_marshal_BOOLEAN__POINTER,
162                                              G_TYPE_BOOLEAN, 1,
163                                              GDK_TYPE_EVENT);
165     object_class->dispose = sp_canvas_item_dispose;
168 /**
169  * Callback for initialization of SPCanvasItem.
170  */
171 static void
172 sp_canvas_item_init (SPCanvasItem *item)
174     item->flags |= SP_CANVAS_ITEM_VISIBLE;
175     item->xform = Geom::Matrix(Geom::identity());
178 /**
179  * Constructs new SPCanvasItem on SPCanvasGroup.
180  */
181 SPCanvasItem *
182 sp_canvas_item_new (SPCanvasGroup *parent, GtkType type, gchar const *first_arg_name, ...)
184     va_list args;
186     g_return_val_if_fail (parent != NULL, NULL);
187     g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL);
188     g_return_val_if_fail (gtk_type_is_a (type, sp_canvas_item_get_type ()), NULL);
190     SPCanvasItem *item = SP_CANVAS_ITEM (gtk_type_new (type));
192     va_start (args, first_arg_name);
193     sp_canvas_item_construct (item, parent, first_arg_name, args);
194     va_end (args);
196     return item;
199 /**
200  * Sets up the newly created SPCanvasItem.
201  *
202  * We make it static for encapsulation reasons since it was nowhere used.
203  */
204 static void
205 sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args)
207     g_return_if_fail (SP_IS_CANVAS_GROUP (parent));
208     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
210     item->parent = SP_CANVAS_ITEM (parent);
211     item->canvas = item->parent->canvas;
213     g_object_set_valist (G_OBJECT (item), first_arg_name, args);
215     group_add (SP_CANVAS_GROUP (item->parent), item);
217     sp_canvas_item_request_update (item);
220 /**
221  * Helper function that requests redraw only if item's visible flag is set.
222  */
223 static void
224 redraw_if_visible (SPCanvasItem *item)
226     if (item->flags & SP_CANVAS_ITEM_VISIBLE) {
227         int x0 = (int)(item->x1);
228         int x1 = (int)(item->x2);
229         int y0 = (int)(item->y1);
230         int y1 = (int)(item->y2);
232         if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
233             sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
234         }
235     }
238 /**
239  * Callback that removes item from all referers and destroys it.
240  */
241 static void
242 sp_canvas_item_dispose (GObject *object)
244     SPCanvasItem *item = SP_CANVAS_ITEM (object);
246     // Hack: if this is a ctrlrect, move it to 0,0;
247     // this redraws only the stroke of the rect to be deleted,
248     // avoiding redraw of the entire area
249     if (SP_IS_CTRLRECT(item)) {
250         SP_CTRLRECT(object)->setRectangle(Geom::Rect(Geom::Point(0,0),Geom::Point(0,0)));
251         SP_CTRLRECT(object)->update(item->xform, 0);
252     } else {
253         redraw_if_visible (item);
254     }
255     item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
257     if (item == item->canvas->current_item) {
258         item->canvas->current_item = NULL;
259         item->canvas->need_repick = TRUE;
260     }
262     if (item == item->canvas->new_current_item) {
263         item->canvas->new_current_item = NULL;
264         item->canvas->need_repick = TRUE;
265     }
267     if (item == item->canvas->grabbed_item) {
268         item->canvas->grabbed_item = NULL;
269         gdk_pointer_ungrab (GDK_CURRENT_TIME);
270     }
272     if (item == item->canvas->focused_item)
273         item->canvas->focused_item = NULL;
275     if (item->parent) {
276         group_remove (SP_CANVAS_GROUP (item->parent), item);
277     }
279     G_OBJECT_CLASS (item_parent_class)->dispose (object);
282 /**
283  * Helper function to update item and its children.
284  *
285  * NB! affine is parent2canvas.
286  */
287 static void
288 sp_canvas_item_invoke_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags)
290     /* Apply the child item's transform */
291     Geom::Matrix child_affine = item->xform * affine;
293     /* apply object flags to child flags */
294     int child_flags = flags & ~SP_CANVAS_UPDATE_REQUESTED;
296     if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
297         child_flags |= SP_CANVAS_UPDATE_REQUESTED;
299     if (item->flags & SP_CANVAS_ITEM_NEED_AFFINE)
300         child_flags |= SP_CANVAS_UPDATE_AFFINE;
302     if (child_flags & (SP_CANVAS_UPDATE_REQUESTED | SP_CANVAS_UPDATE_AFFINE)) {
303         if (SP_CANVAS_ITEM_GET_CLASS (item)->update)
304             SP_CANVAS_ITEM_GET_CLASS (item)->update (item, child_affine, child_flags);
305     }
307     GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_UPDATE);
308     GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_AFFINE);
311 /**
312  * Helper function to invoke the point method of the item.
313  *
314  * The argument x, y should be in the parent's item-relative coordinate
315  * system.  This routine applies the inverse of the item's transform,
316  * maintaining the affine invariant.
317  */
318 static double
319 sp_canvas_item_invoke_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item)
321     if (SP_CANVAS_ITEM_GET_CLASS (item)->point)
322         return SP_CANVAS_ITEM_GET_CLASS (item)->point (item, p, actual_item);
324     return NR_HUGE;
327 /**
328  * Makes the item's affine transformation matrix be equal to the specified
329  * matrix.
330  *
331  * @item: A canvas item.
332  * @affine: An affine transformation matrix.
333  */
334 void
335 sp_canvas_item_affine_absolute (SPCanvasItem *item, Geom::Matrix const &affine)
337     item->xform = affine;
339     if (!(item->flags & SP_CANVAS_ITEM_NEED_AFFINE)) {
340         item->flags |= SP_CANVAS_ITEM_NEED_AFFINE;
341         if (item->parent != NULL) {
342             sp_canvas_item_request_update (item->parent);
343         } else {
344             sp_canvas_request_update (item->canvas);
345         }
346     }
348     item->canvas->need_repick = TRUE;
351 /**
352  * Convenience function to reorder items in a group's child list.
353  *
354  * This puts the specified link after the "before" link.
355  */
356 static void
357 put_item_after (GList *link, GList *before)
359     if (link == before)
360         return;
362     SPCanvasGroup *parent = SP_CANVAS_GROUP (SP_CANVAS_ITEM (link->data)->parent);
364     if (before == NULL) {
365         if (link == parent->items) return;
367         link->prev->next = link->next;
369         if (link->next) {
370             link->next->prev = link->prev;
371         } else {
372             parent->last = link->prev;
373         }
375         link->prev = before;
376         link->next = parent->items;
377         link->next->prev = link;
378         parent->items = link;
379     } else {
380         if ((link == parent->last) && (before == parent->last->prev))
381             return;
383         if (link->next)
384             link->next->prev = link->prev;
386         if (link->prev)
387             link->prev->next = link->next;
388         else {
389             parent->items = link->next;
390             parent->items->prev = NULL;
391         }
393         link->prev = before;
394         link->next = before->next;
396         link->prev->next = link;
398         if (link->next)
399             link->next->prev = link;
400         else
401             parent->last = link;
402     }
406 /**
407  * Raises the item in its parent's stack by the specified number of positions.
408  *
409  * \param item A canvas item.
410  * \param positions Number of steps to raise the item.
411  *
412  * If the number of positions is greater than the distance to the top of the
413  * stack, then the item is put at the top.
414  */
415 void
416 sp_canvas_item_raise (SPCanvasItem *item, int positions)
418     g_return_if_fail (item != NULL);
419     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
420     g_return_if_fail (positions >= 0);
422     if (!item->parent || positions == 0)
423         return;
425     SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
426     GList *link = g_list_find (parent->items, item);
427     g_assert (link != NULL);
429     GList *before;
430     for (before = link; positions && before; positions--)
431         before = before->next;
433     if (!before)
434         before = parent->last;
436     put_item_after (link, before);
438     redraw_if_visible (item);
439     item->canvas->need_repick = TRUE;
443 /**
444  * Lowers the item in its parent's stack by the specified number of positions.
445  *
446  * \param item A canvas item.
447  * \param positions Number of steps to lower the item.
448  *
449  * If the number of positions is greater than the distance to the bottom of the
450  * stack, then the item is put at the bottom.
451  **/
452 void
453 sp_canvas_item_lower (SPCanvasItem *item, int positions)
455     g_return_if_fail (item != NULL);
456     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
457     g_return_if_fail (positions >= 1);
459     if (!item->parent || positions == 0)
460         return;
462     SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
463     GList *link = g_list_find (parent->items, item);
464     g_assert (link != NULL);
466     GList *before;
467     if (link->prev)
468         for (before = link->prev; positions && before; positions--)
469             before = before->prev;
470     else
471         before = NULL;
473     put_item_after (link, before);
475     redraw_if_visible (item);
476     item->canvas->need_repick = TRUE;
479 /**
480  * Sets visible flag on item and requests a redraw.
481  */
482 void
483 sp_canvas_item_show (SPCanvasItem *item)
485     g_return_if_fail (item != NULL);
486     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
488     if (item->flags & SP_CANVAS_ITEM_VISIBLE)
489         return;
491     item->flags |= SP_CANVAS_ITEM_VISIBLE;
493     int x0 = (int)(item->x1);
494     int x1 = (int)(item->x2);
495     int y0 = (int)(item->y1);
496     int y1 = (int)(item->y2);
498     if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
499         sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
500         item->canvas->need_repick = TRUE;
501     }
504 /**
505  * Clears visible flag on item and requests a redraw.
506  */
507 void
508 sp_canvas_item_hide (SPCanvasItem *item)
510     g_return_if_fail (item != NULL);
511     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
513     if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
514         return;
516     item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
518     int x0 = (int)(item->x1);
519     int x1 = (int)(item->x2);
520     int y0 = (int)(item->y1);
521     int y1 = (int)(item->y2);
523     if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
524         sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
525         item->canvas->need_repick = TRUE;
526     }
529 /**
530  * Grab item under cursor.
531  *
532  * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE
533  */
534 int
535 sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
537     g_return_val_if_fail (item != NULL, -1);
538     g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1);
539     g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1);
541     if (item->canvas->grabbed_item)
542         return -1;
544     if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
545         return -1;
547     if (HAS_BROKEN_MOTION_HINTS) {
548         event_mask &= ~GDK_POINTER_MOTION_HINT_MASK;
549     }
551     /* fixme: Top hack (Lauris) */
552     /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
553     /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
554     gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
555                       (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
556                       NULL, cursor, etime);
558     item->canvas->grabbed_item = item;
559     item->canvas->grabbed_event_mask = event_mask;
560     item->canvas->current_item = item; /* So that events go to the grabbed item */
562     return 0;
565 /**
566  * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
567  * mouse.
568  *
569  * \param item A canvas item that holds a grab.
570  * \param etime The timestamp for ungrabbing the mouse.
571  */
572 void
573 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
575     g_return_if_fail (item != NULL);
576     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
578     if (item->canvas->grabbed_item != item)
579         return;
581     item->canvas->grabbed_item = NULL;
583     gdk_pointer_ungrab (etime);
586 /**
587  * Returns the product of all transformation matrices from the root item down
588  * to the item.
589  */
590 Geom::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
592     g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
594     Geom::Matrix affine = Geom::identity();
596     while (item) {
597         affine *= item->xform;
598         item = item->parent;
599     }
600     return affine;
603 /**
604  * Helper that returns true iff item is descendant of parent.
605  */
606 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
608     while (item) {
609         if (item == parent)
610             return true;
611         item = item->parent;
612     }
614     return false;
617 /**
618  * Focus canvas, and item under cursor if it is not already focussed.
619  */
620 void
621 sp_canvas_item_grab_focus (SPCanvasItem *item)
623     g_return_if_fail (item != NULL);
624     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
625     g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
627     SPCanvasItem *focused_item = item->canvas->focused_item;
629     if (focused_item) {
630         GdkEvent ev;
631         ev.focus_change.type = GDK_FOCUS_CHANGE;
632         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
633         ev.focus_change.send_event = FALSE;
634         ev.focus_change.in = FALSE;
636         emit_event (item->canvas, &ev);
637     }
639     item->canvas->focused_item = item;
640     gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
642     if (focused_item) {
643         GdkEvent ev;
644         ev.focus_change.type = GDK_FOCUS_CHANGE;
645         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
646         ev.focus_change.send_event = FALSE;
647         ev.focus_change.in = TRUE;
649         emit_event (item->canvas, &ev);
650     }
653 /**
654  * Requests that the canvas queue an update for the specified item.
655  *
656  * To be used only by item implementations.
657  */
658 void
659 sp_canvas_item_request_update (SPCanvasItem *item)
661     if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
662         return;
664     item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
666     if (item->parent != NULL) {
667         /* Recurse up the tree */
668         sp_canvas_item_request_update (item->parent);
669     } else {
670         /* Have reached the top of the tree, make sure the update call gets scheduled. */
671         sp_canvas_request_update (item->canvas);
672     }
675 /**
676  * Returns position of item in group.
677  */
678 gint sp_canvas_item_order (SPCanvasItem * item)
680     return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
683 /* SPCanvasGroup */
685 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
686 static void sp_canvas_group_init (SPCanvasGroup *group);
687 static void sp_canvas_group_destroy (GtkObject *object);
689 static void sp_canvas_group_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags);
690 static double sp_canvas_group_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item);
691 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
693 static SPCanvasItemClass *group_parent_class;
695 /**
696  * Registers SPCanvasGroup class with Gtk and returns its type number.
697  */
698 GType sp_canvas_group_get_type(void)
700     static GType type = 0;
701     if (!type) {
702         GTypeInfo info = {
703             sizeof(SPCanvasGroupClass),
704             0, // base_init
705             0, // base_finalize
706             (GClassInitFunc)sp_canvas_group_class_init,
707             0, // class_finalize
708             0, // class_data
709             sizeof(SPCanvasGroup),
710             0, // n_preallocs
711             (GInstanceInitFunc)sp_canvas_group_init,
712             0 // value_table
713         };
714         type = g_type_register_static(sp_canvas_item_get_type(), "SPCanvasGroup", &info, static_cast<GTypeFlags>(0));
715     }
716     return type;
719 /**
720  * Class initialization function for SPCanvasGroupClass
721  */
722 static void
723 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
725     GtkObjectClass *object_class = (GtkObjectClass *) klass;
726     SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
728     group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
730     object_class->destroy = sp_canvas_group_destroy;
732     item_class->update = sp_canvas_group_update;
733     item_class->render = sp_canvas_group_render;
734     item_class->point = sp_canvas_group_point;
737 /**
738  * Callback. Empty.
739  */
740 static void
741 sp_canvas_group_init (SPCanvasGroup */*group*/)
743     /* Nothing here */
746 /**
747  * Callback that destroys all items in group and calls group's virtual
748  * destroy() function.
749  */
750 static void
751 sp_canvas_group_destroy (GtkObject *object)
753     g_return_if_fail (object != NULL);
754     g_return_if_fail (SP_IS_CANVAS_GROUP (object));
756     SPCanvasGroup const *group = SP_CANVAS_GROUP (object);
758     GList *list = group->items;
759     while (list) {
760         SPCanvasItem *child = (SPCanvasItem *)list->data;
761         list = list->next;
763         gtk_object_destroy (GTK_OBJECT (child));
764     }
766     if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
767         (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
770 /**
771  * Update handler for canvas groups
772  */
773 static void
774 sp_canvas_group_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags)
776     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
777     Geom::RectHull corners(Geom::Point(0, 0));
778     bool empty=true;
780     for (GList *list = group->items; list; list = list->next) {
781         SPCanvasItem *i = (SPCanvasItem *)list->data;
783         sp_canvas_item_invoke_update (i, affine, flags);
785         if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
786             if (empty) {
787                 corners = Geom::RectHull(Geom::Point(i->x1, i->y1));
788                 empty = false;
789             } else {
790                 corners.add(Geom::Point(i->x1, i->y1));
791             }
792             corners.add(Geom::Point(i->x2, i->y2));
793         }
794     }
796     boost::optional<Geom::Rect> const bounds = corners.bounds();
797     if (bounds) {
798         item->x1 = bounds->min()[Geom::X];
799         item->y1 = bounds->min()[Geom::Y];
800         item->x2 = bounds->max()[Geom::X];
801         item->y2 = bounds->max()[Geom::Y];
802     } else {
803         // FIXME ?
804         item->x1 = item->x2 = item->y1 = item->y2 = 0;
805     }
808 /**
809  * Point handler for canvas groups.
810  */
811 static double
812 sp_canvas_group_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item)
814     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
815     double const x = p[Geom::X];
816     double const y = p[Geom::Y];
817     int x1 = (int)(x - item->canvas->close_enough);
818     int y1 = (int)(y - item->canvas->close_enough);
819     int x2 = (int)(x + item->canvas->close_enough);
820     int y2 = (int)(y + item->canvas->close_enough);
822     double best = 0.0;
823     *actual_item = NULL;
825     double dist = 0.0;
827     for (GList *list = group->items; list; list = list->next) {
828         SPCanvasItem *child = (SPCanvasItem *)list->data;
830         if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
831             SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
833             int has_point;
834             if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
835                 dist = sp_canvas_item_invoke_point (child, p, &point_item);
836                 has_point = TRUE;
837             } else
838                 has_point = FALSE;
840             if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
841                 best = dist;
842                 *actual_item = point_item;
843             }
844         }
845     }
847     return best;
850 /**
851  * Renders all visible canvas group items in buf rectangle.
852  */
853 static void
854 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
856     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
858     for (GList *list = group->items; list; list = list->next) {
859         SPCanvasItem *child = (SPCanvasItem *)list->data;
860         if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
861             if ((child->x1 < buf->rect.x1) &&
862                 (child->y1 < buf->rect.y1) &&
863                 (child->x2 > buf->rect.x0) &&
864                 (child->y2 > buf->rect.y0)) {
865                 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
866                     SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
867             }
868         }
869     }
872 /**
873  * Adds an item to a canvas group.
874  */
875 static void
876 group_add (SPCanvasGroup *group, SPCanvasItem *item)
878     gtk_object_ref (GTK_OBJECT (item));
879     gtk_object_sink (GTK_OBJECT (item));
881     if (!group->items) {
882         group->items = g_list_append (group->items, item);
883         group->last = group->items;
884     } else {
885         group->last = g_list_append (group->last, item)->next;
886     }
888     sp_canvas_item_request_update (item);
891 /**
892  * Removes an item from a canvas group
893  */
894 static void
895 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
897     g_return_if_fail (group != NULL);
898     g_return_if_fail (SP_IS_CANVAS_GROUP (group));
899     g_return_if_fail (item != NULL);
901     for (GList *children = group->items; children; children = children->next) {
902         if (children->data == item) {
904             /* Unparent the child */
906             item->parent = NULL;
907             gtk_object_unref (GTK_OBJECT (item));
909             /* Remove it from the list */
911             if (children == group->last) group->last = children->prev;
913             group->items = g_list_remove_link (group->items, children);
914             g_list_free (children);
915             break;
916         }
917     }
920 /* SPCanvas */
922 static void sp_canvas_class_init (SPCanvasClass *klass);
923 static void sp_canvas_init (SPCanvas *canvas);
924 static void sp_canvas_destroy (GtkObject *object);
926 static void sp_canvas_realize (GtkWidget *widget);
927 static void sp_canvas_unrealize (GtkWidget *widget);
929 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
930 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
932 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
933 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
934 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
935 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
936 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
937 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
938 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
939 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
941 static GtkWidgetClass *canvas_parent_class;
943 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb);
944 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb);
945 static void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val);
946 static int do_update (SPCanvas *canvas);
948 /**
949  * Registers the SPCanvas class if necessary, and returns the type ID
950  * associated to it.
951  *
952  * \return The type ID of the SPCanvas class.
953  **/
954 GType sp_canvas_get_type(void)
956     static GType type = 0;
957     if (!type) {
958         GTypeInfo info = {
959             sizeof(SPCanvasClass),
960             0, // base_init
961             0, // base_finalize
962             (GClassInitFunc)sp_canvas_class_init,
963             0, // class_finalize
964             0, // class_data
965             sizeof(SPCanvas),
966             0, // n_preallocs
967             (GInstanceInitFunc)sp_canvas_init,
968             0 // value_table
969         };
970         type = g_type_register_static(GTK_TYPE_WIDGET, "SPCanvas", &info, static_cast<GTypeFlags>(0));
971     }
972     return type;
975 /**
976  * Class initialization function for SPCanvasClass.
977  */
978 static void
979 sp_canvas_class_init (SPCanvasClass *klass)
981     GtkObjectClass *object_class = (GtkObjectClass *) klass;
982     GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
984     canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
986     object_class->destroy = sp_canvas_destroy;
988     widget_class->realize = sp_canvas_realize;
989     widget_class->unrealize = sp_canvas_unrealize;
990     widget_class->size_request = sp_canvas_size_request;
991     widget_class->size_allocate = sp_canvas_size_allocate;
992     widget_class->button_press_event = sp_canvas_button;
993     widget_class->button_release_event = sp_canvas_button;
994     widget_class->motion_notify_event = sp_canvas_motion;
995     widget_class->scroll_event = sp_canvas_scroll;
996     widget_class->expose_event = sp_canvas_expose;
997     widget_class->key_press_event = sp_canvas_key;
998     widget_class->key_release_event = sp_canvas_key;
999     widget_class->enter_notify_event = sp_canvas_crossing;
1000     widget_class->leave_notify_event = sp_canvas_crossing;
1001     widget_class->focus_in_event = sp_canvas_focus_in;
1002     widget_class->focus_out_event = sp_canvas_focus_out;
1005 /**
1006  * Callback: object initialization for SPCanvas.
1007  */
1008 static void
1009 sp_canvas_init (SPCanvas *canvas)
1011     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
1012     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
1013     GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
1015     canvas->pick_event.type = GDK_LEAVE_NOTIFY;
1016     canvas->pick_event.crossing.x = 0;
1017     canvas->pick_event.crossing.y = 0;
1019     /* Create the root item as a special case */
1020     canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
1021     canvas->root->canvas = canvas;
1023     gtk_object_ref (GTK_OBJECT (canvas->root));
1024     gtk_object_sink (GTK_OBJECT (canvas->root));
1026     canvas->need_repick = TRUE;
1028     // See comment at in sp-canvas.h.
1029     canvas->gen_all_enter_events = false;
1031     canvas->tiles=NULL;
1032     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1033     canvas->tileH=canvas->tileV=0;
1035     canvas->forced_redraw_count = 0;
1036     canvas->forced_redraw_limit = -1;
1038 #if ENABLE_LCMS
1039     canvas->enable_cms_display_adj = false;
1040     canvas->cms_key = new Glib::ustring("");
1041 #endif // ENABLE_LCMS
1043     canvas->is_scrolling = false;
1047 /**
1048  * Convenience function to remove the idle handler of a canvas.
1049  */
1050 static void
1051 remove_idle (SPCanvas *canvas)
1053     if (canvas->idle_id) {
1054         gtk_idle_remove (canvas->idle_id);
1055         canvas->idle_id = 0;
1056     }
1059 /*
1060  * Removes the transient state of the canvas (idle handler, grabs).
1061  */
1062 static void
1063 shutdown_transients (SPCanvas *canvas)
1065     /* We turn off the need_redraw flag, since if the canvas is mapped again
1066      * it will request a redraw anyways.  We do not turn off the need_update
1067      * flag, though, because updates are not queued when the canvas remaps
1068      * itself.
1069      */
1070     if (canvas->need_redraw) {
1071         canvas->need_redraw = FALSE;
1072     }
1073     if ( canvas->tiles ) g_free(canvas->tiles);
1074     canvas->tiles=NULL;
1075     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1076     canvas->tileH=canvas->tileV=0;
1078     if (canvas->grabbed_item) {
1079         canvas->grabbed_item = NULL;
1080         gdk_pointer_ungrab (GDK_CURRENT_TIME);
1081     }
1083     remove_idle (canvas);
1086 /**
1087  * Destroy handler for SPCanvas.
1088  */
1089 static void
1090 sp_canvas_destroy (GtkObject *object)
1092     SPCanvas *canvas = SP_CANVAS (object);
1094     if (canvas->root) {
1095         gtk_object_unref (GTK_OBJECT (canvas->root));
1096         canvas->root = NULL;
1097     }
1099     shutdown_transients (canvas);
1101     if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1102         (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1105 static void track_latency(GdkEvent const *event) {
1106     GdkEventLatencyTracker &tracker = GdkEventLatencyTracker::default_tracker();
1107     boost::optional<double> latency = tracker.process(event);
1108     if (latency && *latency > 2.0) {
1109         g_warning("Event latency reached %f sec (%1.4f)", *latency, tracker.getSkew());
1110     }
1113 /**
1114  * Returns new canvas as widget.
1115  */
1116 GtkWidget *
1117 sp_canvas_new_aa (void)
1119     SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1121     return (GtkWidget *) canvas;
1124 /**
1125  * The canvas widget's realize callback.
1126  */
1127 static void
1128 sp_canvas_realize (GtkWidget *widget)
1130     SPCanvas *canvas = SP_CANVAS (widget);
1132     GdkWindowAttr attributes;
1133     attributes.window_type = GDK_WINDOW_CHILD;
1134     attributes.x = widget->allocation.x;
1135     attributes.y = widget->allocation.y;
1136     attributes.width = widget->allocation.width;
1137     attributes.height = widget->allocation.height;
1138     attributes.wclass = GDK_INPUT_OUTPUT;
1139     attributes.visual = gdk_rgb_get_visual ();
1140     attributes.colormap = gdk_rgb_get_cmap ();
1141     attributes.event_mask = (gtk_widget_get_events (widget) |
1142                              GDK_EXPOSURE_MASK |
1143                              GDK_BUTTON_PRESS_MASK |
1144                              GDK_BUTTON_RELEASE_MASK |
1145                              GDK_POINTER_MOTION_MASK |
1146                              ( HAS_BROKEN_MOTION_HINTS ?
1147                                0 : GDK_POINTER_MOTION_HINT_MASK ) |
1148                              GDK_PROXIMITY_IN_MASK |
1149                              GDK_PROXIMITY_OUT_MASK |
1150                              GDK_KEY_PRESS_MASK |
1151                              GDK_KEY_RELEASE_MASK |
1152                              GDK_ENTER_NOTIFY_MASK |
1153                              GDK_LEAVE_NOTIFY_MASK |
1154                              GDK_FOCUS_CHANGE_MASK);
1155     gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1157     widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1158     gdk_window_set_user_data (widget->window, widget);
1160     if ( prefs_get_int_attribute ("options.useextinput", "value", 1) )
1161         gtk_widget_set_events(widget, attributes.event_mask);
1163     widget->style = gtk_style_attach (widget->style, widget->window);
1165     GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1167     canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1170 /**
1171  * The canvas widget's unrealize callback.
1172  */
1173 static void
1174 sp_canvas_unrealize (GtkWidget *widget)
1176     SPCanvas *canvas = SP_CANVAS (widget);
1178     canvas->current_item = NULL;
1179     canvas->grabbed_item = NULL;
1180     canvas->focused_item = NULL;
1182     shutdown_transients (canvas);
1184     gdk_gc_destroy (canvas->pixmap_gc);
1185     canvas->pixmap_gc = NULL;
1187     if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1188         (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1191 /**
1192  * The canvas widget's size_request callback.
1193  */
1194 static void
1195 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1197     static_cast<void>(SP_CANVAS (widget));
1199     req->width = 256;
1200     req->height = 256;
1203 /**
1204  * The canvas widget's size_allocate callback.
1205  */
1206 static void
1207 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1209     SPCanvas *canvas = SP_CANVAS (widget);
1211     /* Schedule redraw of new region */
1212     sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1213     if (allocation->width > widget->allocation.width) {
1214         sp_canvas_request_redraw (canvas,
1215                                   canvas->x0 + widget->allocation.width,
1216                                   0,
1217                                   canvas->x0 + allocation->width,
1218                                   canvas->y0 + allocation->height);
1219     }
1220     if (allocation->height > widget->allocation.height) {
1221         sp_canvas_request_redraw (canvas,
1222                                   0,
1223                                   canvas->y0 + widget->allocation.height,
1224                                   canvas->x0 + allocation->width,
1225                                   canvas->y0 + allocation->height);
1226     }
1228     widget->allocation = *allocation;
1229     
1230     if (GTK_WIDGET_REALIZED (widget)) {
1231         gdk_window_move_resize (widget->window,
1232                                 widget->allocation.x, widget->allocation.y,
1233                                 widget->allocation.width, widget->allocation.height);
1234     }
1237 /**
1238  * Helper that emits an event for an item in the canvas, be it the current
1239  * item, grabbed item, or focused item, as appropriate.
1240  */
1241 static int
1242 emit_event (SPCanvas *canvas, GdkEvent *event)
1244     guint mask;
1246     if (canvas->grabbed_item) {
1247         switch (event->type) {
1248         case GDK_ENTER_NOTIFY:
1249             mask = GDK_ENTER_NOTIFY_MASK;
1250             break;
1251         case GDK_LEAVE_NOTIFY:
1252             mask = GDK_LEAVE_NOTIFY_MASK;
1253             break;
1254         case GDK_MOTION_NOTIFY:
1255             mask = GDK_POINTER_MOTION_MASK;
1256             break;
1257         case GDK_BUTTON_PRESS:
1258         case GDK_2BUTTON_PRESS:
1259         case GDK_3BUTTON_PRESS:
1260             mask = GDK_BUTTON_PRESS_MASK;
1261             break;
1262         case GDK_BUTTON_RELEASE:
1263             mask = GDK_BUTTON_RELEASE_MASK;
1264             break;
1265         case GDK_KEY_PRESS:
1266             mask = GDK_KEY_PRESS_MASK;
1267             break;
1268         case GDK_KEY_RELEASE:
1269             mask = GDK_KEY_RELEASE_MASK;
1270             break;
1271         case GDK_SCROLL:
1272             mask = GDK_SCROLL;
1273             break;
1274         default:
1275             mask = 0;
1276             break;
1277         }
1279         if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1280     }
1282     /* Convert to world coordinates -- we have two cases because of diferent
1283      * offsets of the fields in the event structures.
1284      */
1286     GdkEvent ev = *event;
1288     switch (ev.type) {
1289     case GDK_ENTER_NOTIFY:
1290     case GDK_LEAVE_NOTIFY:
1291         ev.crossing.x += canvas->x0;
1292         ev.crossing.y += canvas->y0;
1293         break;
1294     case GDK_MOTION_NOTIFY:
1295     case GDK_BUTTON_PRESS:
1296     case GDK_2BUTTON_PRESS:
1297     case GDK_3BUTTON_PRESS:
1298     case GDK_BUTTON_RELEASE:
1299         ev.motion.x += canvas->x0;
1300         ev.motion.y += canvas->y0;
1301         break;
1302     default:
1303         break;
1304     }
1306     /* Choose where we send the event */
1308     /* canvas->current_item becomes NULL in some cases under Win32
1309     ** (e.g. if the pointer leaves the window).  So this is a hack that
1310     ** Lauris applied to SP to get around the problem.
1311     */
1312     SPCanvasItem* item = NULL;
1313     if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1314         item = canvas->grabbed_item;
1315     } else {
1316         item = canvas->current_item;
1317     }
1319     if (canvas->focused_item &&
1320         ((event->type == GDK_KEY_PRESS) ||
1321          (event->type == GDK_KEY_RELEASE) ||
1322          (event->type == GDK_FOCUS_CHANGE))) {
1323         item = canvas->focused_item;
1324     }
1326     /* The event is propagated up the hierarchy (for if someone connected to
1327      * a group instead of a leaf event), and emission is stopped if a
1328      * handler returns TRUE, just like for GtkWidget events.
1329      */
1331     gint finished = FALSE;
1333     while (item && !finished) {
1334         gtk_object_ref (GTK_OBJECT (item));
1335         gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1336         SPCanvasItem *parent = item->parent;
1337         gtk_object_unref (GTK_OBJECT (item));
1338         item = parent;
1339     }
1341     return finished;
1344 /**
1345  * Helper that re-picks the current item in the canvas, based on the event's
1346  * coordinates and emits enter/leave events for items as appropriate.
1347  */
1348 static int
1349 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1351     int button_down = 0;
1352     double x, y;
1354     if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1355         return FALSE;
1357     int retval = FALSE;
1359     if (canvas->gen_all_enter_events == false) {
1360         // If a button is down, we'll perform enter and leave events on the
1361         // current item, but not enter on any other item.  This is more or
1362         // less like X pointer grabbing for canvas items.
1363         //
1364         button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1365                 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1367         if (!button_down) canvas->left_grabbed_item = FALSE;
1368     }
1370     /* Save the event in the canvas.  This is used to synthesize enter and
1371      * leave events in case the current item changes.  It is also used to
1372      * re-pick the current item if the current one gets deleted.  Also,
1373      * synthesize an enter event.
1374      */
1375     if (event != &canvas->pick_event) {
1376         if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1377             /* these fields have the same offsets in both types of events */
1379             canvas->pick_event.crossing.type       = GDK_ENTER_NOTIFY;
1380             canvas->pick_event.crossing.window     = event->motion.window;
1381             canvas->pick_event.crossing.send_event = event->motion.send_event;
1382             canvas->pick_event.crossing.subwindow  = NULL;
1383             canvas->pick_event.crossing.x          = event->motion.x;
1384             canvas->pick_event.crossing.y          = event->motion.y;
1385             canvas->pick_event.crossing.mode       = GDK_CROSSING_NORMAL;
1386             canvas->pick_event.crossing.detail     = GDK_NOTIFY_NONLINEAR;
1387             canvas->pick_event.crossing.focus      = FALSE;
1388             canvas->pick_event.crossing.state      = event->motion.state;
1390             /* these fields don't have the same offsets in both types of events */
1392             if (event->type == GDK_MOTION_NOTIFY) {
1393                 canvas->pick_event.crossing.x_root = event->motion.x_root;
1394                 canvas->pick_event.crossing.y_root = event->motion.y_root;
1395             } else {
1396                 canvas->pick_event.crossing.x_root = event->button.x_root;
1397                 canvas->pick_event.crossing.y_root = event->button.y_root;
1398             }
1399         } else {
1400             canvas->pick_event = *event;
1401         }
1402     }
1404     /* Don't do anything else if this is a recursive call */
1405     if (canvas->in_repick) return retval;
1407     /* LeaveNotify means that there is no current item, so we don't look for one */
1408     if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1409         /* these fields don't have the same offsets in both types of events */
1411         if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1412             x = canvas->pick_event.crossing.x;
1413             y = canvas->pick_event.crossing.y;
1414         } else {
1415             x = canvas->pick_event.motion.x;
1416             y = canvas->pick_event.motion.y;
1417         }
1419         /* world coords */
1420         x += canvas->x0;
1421         y += canvas->y0;
1423         /* find the closest item */
1424         if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1425             sp_canvas_item_invoke_point (canvas->root, Geom::Point(x, y), &canvas->new_current_item);
1426         } else {
1427             canvas->new_current_item = NULL;
1428         }
1429     } else {
1430         canvas->new_current_item = NULL;
1431     }
1433     if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1434         return retval; /* current item did not change */
1435     }
1437     /* Synthesize events for old and new current items */
1439     if ((canvas->new_current_item != canvas->current_item)
1440         && (canvas->current_item != NULL)
1441         && !canvas->left_grabbed_item) {
1442         GdkEvent new_event;
1443         SPCanvasItem *item;
1445         item = canvas->current_item;
1447         new_event = canvas->pick_event;
1448         new_event.type = GDK_LEAVE_NOTIFY;
1450         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1451         new_event.crossing.subwindow = NULL;
1452         canvas->in_repick = TRUE;
1453         retval = emit_event (canvas, &new_event);
1454         canvas->in_repick = FALSE;
1455     }
1457     if (canvas->gen_all_enter_events == false) {
1458         // new_current_item may have been set to NULL during the call to
1459         // emit_event() above
1460         if ((canvas->new_current_item != canvas->current_item) && button_down) {
1461             canvas->left_grabbed_item = TRUE;
1462             return retval;
1463         }
1464     }
1466     /* Handle the rest of cases */
1468     canvas->left_grabbed_item = FALSE;
1469     canvas->current_item = canvas->new_current_item;
1471     if (canvas->current_item != NULL) {
1472         GdkEvent new_event;
1474         new_event = canvas->pick_event;
1475         new_event.type = GDK_ENTER_NOTIFY;
1476         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1477         new_event.crossing.subwindow = NULL;
1478         retval = emit_event (canvas, &new_event);
1479     }
1481     return retval;
1484 /**
1485  * Button event handler for the canvas.
1486  */
1487 static gint
1488 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1490     SPCanvas *canvas = SP_CANVAS (widget);
1492     int retval = FALSE;
1494     /* dispatch normally regardless of the event's window if an item has
1495        has a pointer grab in effect */
1496     if (!canvas->grabbed_item &&
1497         event->window != SP_CANVAS_WINDOW (canvas))
1498         return retval;
1500     int mask;
1501     switch (event->button) {
1502     case 1:
1503         mask = GDK_BUTTON1_MASK;
1504         break;
1505     case 2:
1506         mask = GDK_BUTTON2_MASK;
1507         break;
1508     case 3:
1509         mask = GDK_BUTTON3_MASK;
1510         break;
1511     case 4:
1512         mask = GDK_BUTTON4_MASK;
1513         break;
1514     case 5:
1515         mask = GDK_BUTTON5_MASK;
1516         break;
1517     default:
1518         mask = 0;
1519     }
1521     switch (event->type) {
1522     case GDK_BUTTON_PRESS:
1523     case GDK_2BUTTON_PRESS:
1524     case GDK_3BUTTON_PRESS:
1525         /* Pick the current item as if the button were not pressed, and
1526          * then process the event.
1527          */
1528         canvas->state = event->state;
1529         pick_current_item (canvas, (GdkEvent *) event);
1530         canvas->state ^= mask;
1531         retval = emit_event (canvas, (GdkEvent *) event);
1532         break;
1534     case GDK_BUTTON_RELEASE:
1535         /* Process the event as if the button were pressed, then repick
1536          * after the button has been released
1537          */
1538         canvas->state = event->state;
1539         retval = emit_event (canvas, (GdkEvent *) event);
1540         event->state ^= mask;
1541         canvas->state = event->state;
1542         pick_current_item (canvas, (GdkEvent *) event);
1543         event->state ^= mask;
1544         break;
1546     default:
1547         g_assert_not_reached ();
1548     }
1550     return retval;
1553 /**
1554  * Scroll event handler for the canvas.
1555  *
1556  * \todo FIXME: generate motion events to re-select items.
1557  */
1558 static gint
1559 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1561     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1564 static inline void request_motions(GdkWindow *w, GdkEventMotion *event) {
1565     gdk_window_get_pointer(w, NULL, NULL, NULL);
1566 #if HAS_GDK_EVENT_REQUEST_MOTIONS
1567     gdk_event_request_motions(event);
1568 #endif
1571 /**
1572  * Motion event handler for the canvas.
1573  */
1574 static int
1575 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1577     int status;
1578     SPCanvas *canvas = SP_CANVAS (widget);
1580     track_latency((GdkEvent *)event);
1582     if (event->window != SP_CANVAS_WINDOW (canvas))
1583         return FALSE;
1585     if (canvas->pixmap_gc == NULL) // canvas being deleted
1586         return FALSE;
1588     canvas->state = event->state;
1589     pick_current_item (canvas, (GdkEvent *) event);
1591     status = emit_event (canvas, (GdkEvent *) event);
1593     if (event->is_hint) {
1594         request_motions(widget->window, event);
1595     }
1597     return status;
1600 static void
1601 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)
1603     GtkWidget *widget = GTK_WIDGET (canvas);
1605     SPCanvasBuf buf;
1606     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1607         buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1608     } else {
1609         buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1610     }
1612     // Mark the region clean
1613     sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1615     buf.buf_rowstride = sw * 4; 
1616     buf.rect.x0 = x0;
1617     buf.rect.y0 = y0;
1618     buf.rect.x1 = x1;
1619     buf.rect.y1 = y1;
1620     buf.visible_rect.x0 = draw_x1;
1621     buf.visible_rect.y0 = draw_y1;
1622     buf.visible_rect.x1 = draw_x2;
1623     buf.visible_rect.y1 = draw_y2;
1624     GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1625     buf.bg_color = (((color->red & 0xff00) << 8)
1626                     | (color->green & 0xff00)
1627                     | (color->blue >> 8));
1628     buf.is_empty = true;
1630     buf.ct = nr_create_cairo_context_canvasbuf (&(buf.visible_rect), &buf);
1632     if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1633         SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1634     }
1636 #if ENABLE_LCMS
1637     cmsHTRANSFORM transf = 0;
1638     long long int fromDisplay = prefs_get_int_attribute_limited( "options.displayprofile", "from_display", 0, 0, 1 );
1639     if ( fromDisplay ) {
1640         transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" );
1641     } else {
1642         transf = Inkscape::colorprofile_get_display_transform();
1643     }
1644 #endif // ENABLE_LCMS
1646     if (buf.is_empty) {
1647 #if ENABLE_LCMS
1648         if ( transf && canvas->enable_cms_display_adj ) {
1649             cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1650         }
1651 #endif // ENABLE_LCMS
1652         gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1653         gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1654                             canvas->pixmap_gc,
1655                             TRUE,
1656                             x0 - canvas->x0, y0 - canvas->y0,
1657                             x1 - x0, y1 - y0);
1658     } else {
1660 #if ENABLE_LCMS
1661         if ( transf && canvas->enable_cms_display_adj ) {
1662             for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1663                 guchar* p = buf.buf + (sw * 3) * yy;
1664                 cmsDoTransform( transf, p, p, (x1 - x0) );
1665             }
1666         }
1667 #endif // ENABLE_LCMS
1669 // Now we only need to output the prepared pixmap to the actual screen, and this define chooses one
1670 // of the two ways to do it. The cairo way is direct and straightforward, but unfortunately
1671 // noticeably slower. I asked Carl Worth but he was unable so far to suggest any specific reason
1672 // for this slowness. So, for now we use the oldish method: squeeze out 32bpp buffer to 24bpp and
1673 // use gdk_draw_rgb_image_dithalign, for unfortunately gdk can only handle 24 bpp, which cairo
1674 // cannot handle at all. Still, this way is currently faster even despite the blit with squeeze.
1676 ///#define CANVAS_OUTPUT_VIA_CAIRO
1678 #ifdef CANVAS_OUTPUT_VIA_CAIRO
1680         buf.cst = cairo_image_surface_create_for_data (
1681             buf.buf,
1682             CAIRO_FORMAT_ARGB32,  // unpacked, i.e. 32 bits! one byte is unused
1683             x1 - x0, y1 - y0,
1684             buf.buf_rowstride
1685             );
1686         cairo_t *window_ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1687         cairo_set_source_surface (window_ct, buf.cst, x0 - canvas->x0, y0 - canvas->y0);
1688         cairo_paint (window_ct);
1689         cairo_destroy (window_ct);
1690         cairo_surface_finish (buf.cst);
1691         cairo_surface_destroy (buf.cst);
1693 #else
1695         NRPixBlock b3;
1696         nr_pixblock_setup_fast (&b3, NR_PIXBLOCK_MODE_R8G8B8, x0, y0, x1, y1, TRUE);
1698         NRPixBlock b4;
1699         nr_pixblock_setup_extern (&b4, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1,
1700                                   buf.buf,
1701                                   buf.buf_rowstride,
1702                                   FALSE, FALSE);
1704         // this does the 32->24 squishing, using an assembler routine:
1705         nr_blit_pixblock_pixblock (&b3, &b4);
1707         gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1708                                       canvas->pixmap_gc,
1709                                       x0 - canvas->x0, y0 - canvas->y0,
1710                                       x1 - x0, y1 - y0,
1711                                       GDK_RGB_DITHER_MAX,
1712                                       NR_PIXBLOCK_PX(&b3),
1713                                       sw * 3,
1714                                       x0 - canvas->x0, y0 - canvas->y0);
1716         nr_pixblock_release (&b3);
1717         nr_pixblock_release (&b4);
1718 #endif
1719     }
1721     cairo_surface_t *cst = cairo_get_target(buf.ct);
1722     cairo_destroy (buf.ct);
1723     cairo_surface_finish (cst);
1724     cairo_surface_destroy (cst);
1726     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1727         nr_pixelstore_256K_free (buf.buf);
1728     } else {
1729         nr_pixelstore_1M_free (buf.buf);
1730     }
1733 struct PaintRectSetup {
1734     SPCanvas* canvas;
1735     NRRectL big_rect;
1736     GTimeVal start_time;
1737     int max_pixels;
1738     Geom::Point mouse_loc;
1739 };
1741 /**
1742  * Paint the given rect, recursively subdividing the region until it is the size of a single
1743  * buffer.
1744  *
1745  * @return true if the drawing completes
1746  */
1747 static int
1748 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1750     GTimeVal now;
1751     g_get_current_time (&now);
1753     glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1754         + (now.tv_usec - setup->start_time.tv_usec);
1756     // Allow only very fast buffers to be run together;
1757     // as soon as the total redraw time exceeds 1ms, cancel;
1758     // this returns control to the idle loop and allows Inkscape to process user input
1759     // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1760     // it will get back and finish painting what remains to paint.
1761     if (elapsed > 1000) {
1763         // Interrupting redraw isn't always good.
1764         // For example, when you drag one node of a big path, only the buffer containing
1765         // the mouse cursor will be redrawn again and again, and the rest of the path
1766         // will remain stale because Inkscape never has enough idle time to redraw all
1767         // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1768         // If this limit is set, and if we have aborted redraw more times than is allowed,
1769         // interrupting is blocked and we're forced to redraw full screen once
1770         // (after which we can again interrupt forced_redraw_limit times).
1771         if (setup->canvas->forced_redraw_limit < 0 ||
1772             setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1774             if (setup->canvas->forced_redraw_limit != -1) {
1775                 setup->canvas->forced_redraw_count++;
1776             }
1778             return false;
1779         }
1780     }
1782     // Find the optimal buffer dimensions
1783     int bw = this_rect.x1 - this_rect.x0;
1784     int bh = this_rect.y1 - this_rect.y0;
1785     if ((bw < 1) || (bh < 1))
1786         return 0;
1788     if (bw * bh < setup->max_pixels) {
1789         // We are small enough
1790         sp_canvas_paint_single_buffer (setup->canvas,
1791                                        this_rect.x0, this_rect.y0,
1792                                        this_rect.x1, this_rect.y1,
1793                                        setup->big_rect.x0, setup->big_rect.y0,
1794                                        setup->big_rect.x1, setup->big_rect.y1, bw);
1795         return 1;
1796     }
1798     NRRectL lo = this_rect;
1799     NRRectL hi = this_rect;
1801 /*
1802 This test determines the redraw strategy:
1804 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1805 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1806 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1807 and seems to be faster for drawings with many smaller objects at zoom-out.
1809 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1810 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1811 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1812 faster.
1814 The default for now is the strips mode.
1815 */
1816     if (bw < bh || bh < 2 * TILE_SIZE) {
1817         // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1818         int mid = ((long long) this_rect.x0 + (long long) this_rect.x1) / 2;
1819         // Make sure that mid lies on a tile boundary
1820         mid = (mid / TILE_SIZE) * TILE_SIZE;
1822         lo.x1 = mid;
1823         hi.x0 = mid;
1825         if (setup->mouse_loc[Geom::X] < mid) {
1826             // Always paint towards the mouse first
1827             return sp_canvas_paint_rect_internal(setup, lo)
1828                 && sp_canvas_paint_rect_internal(setup, hi);
1829         } else {
1830             return sp_canvas_paint_rect_internal(setup, hi)
1831                 && sp_canvas_paint_rect_internal(setup, lo);
1832         }
1833     } else {
1834         // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1835         int mid = ((long long) this_rect.y0 + (long long) this_rect.y1) / 2;
1836         // Make sure that mid lies on a tile boundary
1837         mid = (mid / TILE_SIZE) * TILE_SIZE;
1839         lo.y1 = mid;
1840         hi.y0 = mid;
1842         if (setup->mouse_loc[Geom::Y] < mid) {
1843             // Always paint towards the mouse first
1844             return sp_canvas_paint_rect_internal(setup, lo)
1845                 && sp_canvas_paint_rect_internal(setup, hi);
1846         } else {
1847             return sp_canvas_paint_rect_internal(setup, hi)
1848                 && sp_canvas_paint_rect_internal(setup, lo);
1849         }
1850     }
1854 /**
1855  * Helper that draws a specific rectangular part of the canvas.
1856  *
1857  * @return true if the rectangle painting succeeds.
1858  */
1859 static bool
1860 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1862     g_return_val_if_fail (!canvas->need_update, false);
1864     NRRectL rect;
1865     rect.x0 = xx0;
1866     rect.x1 = xx1;
1867     rect.y0 = yy0;
1868     rect.y1 = yy1;
1870     // Clip rect-to-draw by the current visible area
1871     rect.x0 = MAX (rect.x0, canvas->x0);
1872     rect.y0 = MAX (rect.y0, canvas->y0);
1873     rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1874     rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1876 #ifdef DEBUG_REDRAW
1877     // paint the area to redraw yellow
1878     gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1879     gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1880                         canvas->pixmap_gc,
1881                         TRUE,
1882                         rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1883                         rect.x1 - rect.x0, rect.y1 - rect.y0);
1884 #endif
1886     PaintRectSetup setup;
1888     setup.canvas = canvas;
1889     setup.big_rect = rect;
1891     // Save the mouse location
1892     gint x, y;
1893     gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1894     setup.mouse_loc = sp_canvas_window_to_world (canvas, Geom::Point(x,y));
1896     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1897         // use 256K as a compromise to not slow down gradients
1898         // 256K is the cached buffer and we need 4 channels
1899         setup.max_pixels = 65536; // 256K/4
1900     } else {
1901         // paths only, so 1M works faster
1902         // 1M is the cached buffer and we need 4 channels
1903         setup.max_pixels = 262144; 
1904     }
1906     // Start the clock
1907     g_get_current_time(&(setup.start_time));
1909     // Go
1910     return sp_canvas_paint_rect_internal(&setup, rect);
1913 /**
1914  * Force a full redraw after a specified number of interrupted redraws
1915  */
1916 void
1917 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1918   g_return_if_fail(canvas != NULL);
1920   canvas->forced_redraw_limit = count;
1921   canvas->forced_redraw_count = 0;
1924 /**
1925  * End forced full redraw requests
1926  */
1927 void
1928 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1929   g_return_if_fail(canvas != NULL);
1931   canvas->forced_redraw_limit = -1;
1934 /**
1935  * The canvas widget's expose callback.
1936  */
1937 static gint
1938 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1940     SPCanvas *canvas = SP_CANVAS (widget);
1942     if (!GTK_WIDGET_DRAWABLE (widget) ||
1943         (event->window != SP_CANVAS_WINDOW (canvas)))
1944         return FALSE;
1946     int n_rects;
1947     GdkRectangle *rects;
1948     gdk_region_get_rectangles (event->region, &rects, &n_rects);
1950     for (int i = 0; i < n_rects; i++) {
1951         NRRectL rect;
1953         rect.x0 = rects[i].x + canvas->x0;
1954         rect.y0 = rects[i].y + canvas->y0;
1955         rect.x1 = rect.x0 + rects[i].width;
1956         rect.y1 = rect.y0 + rects[i].height;
1958         sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1959     }
1961     if (n_rects > 0)
1962         g_free (rects);
1964     return FALSE;
1967 /**
1968  * The canvas widget's keypress callback.
1969  */
1970 static gint
1971 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1973     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1976 /**
1977  * Crossing event handler for the canvas.
1978  */
1979 static gint
1980 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1982     SPCanvas *canvas = SP_CANVAS (widget);
1984     if (event->window != SP_CANVAS_WINDOW (canvas))
1985         return FALSE;
1987     canvas->state = event->state;
1988     return pick_current_item (canvas, (GdkEvent *) event);
1991 /**
1992  * Focus in handler for the canvas.
1993  */
1994 static gint
1995 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
1997     GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1999     SPCanvas *canvas = SP_CANVAS (widget);
2001     if (canvas->focused_item) {
2002         return emit_event (canvas, (GdkEvent *) event);
2003     } else {
2004         return FALSE;
2005     }
2008 /**
2009  * Focus out handler for the canvas.
2010  */
2011 static gint
2012 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
2014     GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2016     SPCanvas *canvas = SP_CANVAS (widget);
2018     if (canvas->focused_item)
2019         return emit_event (canvas, (GdkEvent *) event);
2020     else
2021         return FALSE;
2024 /**
2025  * Helper that repaints the areas in the canvas that need it.
2026  *
2027  * @return true if all the dirty parts have been redrawn
2028  */
2029 static int
2030 paint (SPCanvas *canvas)
2032     if (canvas->need_update) {
2033         sp_canvas_item_invoke_update (canvas->root, Geom::identity(), 0);
2034         canvas->need_update = FALSE;
2035     }
2037     if (!canvas->need_redraw)
2038         return TRUE;
2040     Gdk::Region to_paint;
2042     for (int j=canvas->tTop; j<canvas->tBottom; j++) {
2043         for (int i=canvas->tLeft; i<canvas->tRight; i++) {
2044             int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
2046             if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
2047                 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
2048                                    TILE_SIZE, TILE_SIZE));
2049             }
2051         }
2052     }
2054     if (!to_paint.empty()) {
2055         Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
2056         typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
2057         for (Iter i=rect.begin(); i != rect.end(); ++i) {
2058             int x0 = (*i).get_x();
2059             int y0 = (*i).get_y();
2060             int x1 = x0 + (*i).get_width();
2061             int y1 = y0 + (*i).get_height();
2062             if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
2063                 // Aborted
2064                 return FALSE;
2065             };
2066         }
2067     }
2069     canvas->need_redraw = FALSE;
2071     // we've had a full unaborted redraw, reset the full redraw counter
2072     if (canvas->forced_redraw_limit != -1) {
2073         canvas->forced_redraw_count = 0;
2074     }
2076     return TRUE;
2079 /**
2080  * Helper that invokes update, paint, and repick on canvas.
2081  */
2082 static int
2083 do_update (SPCanvas *canvas)
2085     if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop durring interrupted display!
2086         return TRUE;
2088     /* Cause the update if necessary */
2089     if (canvas->need_update) {
2090         sp_canvas_item_invoke_update (canvas->root, Geom::identity(), 0);
2091         canvas->need_update = FALSE;
2092     }
2094     /* Paint if able to */
2095     if (GTK_WIDGET_DRAWABLE (canvas)) {
2096             return paint (canvas);
2097     }
2099     /* Pick new current item */
2100     while (canvas->need_repick) {
2101         canvas->need_repick = FALSE;
2102         pick_current_item (canvas, &canvas->pick_event);
2103     }
2105     return TRUE;
2108 /**
2109  * Idle handler for the canvas that deals with pending updates and redraws.
2110  */
2111 static gint
2112 idle_handler (gpointer data)
2114     GDK_THREADS_ENTER ();
2116     SPCanvas *canvas = SP_CANVAS (data);
2118     int const ret = do_update (canvas);
2120     if (ret) {
2121         /* Reset idle id */
2122         canvas->idle_id = 0;
2123     }
2125     GDK_THREADS_LEAVE ();
2127     return !ret;
2130 /**
2131  * Convenience function to add an idle handler to a canvas.
2132  */
2133 static void
2134 add_idle (SPCanvas *canvas)
2136     if (canvas->idle_id != 0)
2137         return;
2139     canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2142 /**
2143  * Returns the root group of the specified canvas.
2144  */
2145 SPCanvasGroup *
2146 sp_canvas_root (SPCanvas *canvas)
2148     g_return_val_if_fail (canvas != NULL, NULL);
2149     g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2151     return SP_CANVAS_GROUP (canvas->root);
2154 /**
2155  * Scrolls canvas to specific position (cx and cy are measured in screen pixels)
2156  */
2157 void
2158 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2160     g_return_if_fail (canvas != NULL);
2161     g_return_if_fail (SP_IS_CANVAS (canvas));
2163     int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels)
2164     int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do!
2165     int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the 
2166     int dy = iy - canvas->y0; // canvas w.r.t its previous position
2168     canvas->dx0 = cx; // here the 'd' stands for double, not delta!
2169     canvas->dy0 = cy;
2170     canvas->x0 = ix;
2171     canvas->y0 = iy;
2173     sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2175     if (!clear) {
2176         // scrolling without zoom; redraw only the newly exposed areas
2177         if ((dx != 0) || (dy != 0)) {
2178             canvas->is_scrolling = is_scrolling;
2179             if (GTK_WIDGET_REALIZED (canvas)) {
2180                 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2181             }
2182         }
2183     } else {
2184         // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2185     }
2188 /**
2189  * Updates canvas if necessary.
2190  */
2191 void
2192 sp_canvas_update_now (SPCanvas *canvas)
2194     g_return_if_fail (canvas != NULL);
2195     g_return_if_fail (SP_IS_CANVAS (canvas));
2197     if (!(canvas->need_update ||
2198           canvas->need_redraw))
2199         return;
2201     do_update (canvas);
2204 /**
2205  * Update callback for canvas widget.
2206  */
2207 static void
2208 sp_canvas_request_update (SPCanvas *canvas)
2210     canvas->need_update = TRUE;
2211     add_idle (canvas);
2214 /**
2215  * Forces redraw of rectangular canvas area.
2216  */
2217 void
2218 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2220     NRRectL bbox;
2221     NRRectL visible;
2222     NRRectL clip;
2224     g_return_if_fail (canvas != NULL);
2225     g_return_if_fail (SP_IS_CANVAS (canvas));
2227     if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2228     if ((x0 >= x1) || (y0 >= y1)) return;
2230     bbox.x0 = x0;
2231     bbox.y0 = y0;
2232     bbox.x1 = x1;
2233     bbox.y1 = y1;
2235     visible.x0 = canvas->x0;
2236     visible.y0 = canvas->y0;
2237     visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2238     visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2240     nr_rect_l_intersect (&clip, &bbox, &visible);
2242     sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2243     add_idle (canvas);
2246 /**
2247  * Sets world coordinates from win and canvas.
2248  */
2249 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2251     g_return_if_fail (canvas != NULL);
2252     g_return_if_fail (SP_IS_CANVAS (canvas));
2254     if (worldx) *worldx = canvas->x0 + winx;
2255     if (worldy) *worldy = canvas->y0 + winy;
2258 /**
2259  * Sets win coordinates from world and canvas.
2260  */
2261 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2263     g_return_if_fail (canvas != NULL);
2264     g_return_if_fail (SP_IS_CANVAS (canvas));
2266     if (winx) *winx = worldx - canvas->x0;
2267     if (winy) *winy = worldy - canvas->y0;
2270 /**
2271  * Converts point from win to world coordinates.
2272  */
2273 Geom::Point sp_canvas_window_to_world(SPCanvas const *canvas, Geom::Point const win)
2275     g_assert (canvas != NULL);
2276     g_assert (SP_IS_CANVAS (canvas));
2278     return Geom::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2281 /**
2282  * Converts point from world to win coordinates.
2283  */
2284 Geom::Point sp_canvas_world_to_window(SPCanvas const *canvas, Geom::Point const world)
2286     g_assert (canvas != NULL);
2287     g_assert (SP_IS_CANVAS (canvas));
2289     return Geom::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2292 /**
2293  * Returns true if point given in world coordinates is inside window.
2294  */
2295 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, Geom::Point const &world)
2297     g_assert( canvas != NULL );
2298     g_assert(SP_IS_CANVAS(canvas));
2300     GtkWidget const &w = *GTK_WIDGET(canvas);
2301     return ( ( canvas->x0 <= world[Geom::X] )  &&
2302              ( canvas->y0 <= world[Geom::Y] )  &&
2303              ( world[Geom::X] < canvas->x0 + w.allocation.width )  &&
2304              ( world[Geom::Y] < canvas->y0 + w.allocation.height ) );
2307 /**
2308  * Return canvas window coordinates as Geom::Rect.
2309  */
2310 Geom::Rect SPCanvas::getViewbox() const
2312     GtkWidget const *w = GTK_WIDGET(this);
2313     return Geom::Rect(Geom::Point(dx0, dy0),
2314                       Geom::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2317 /**
2318  * Return canvas window coordinates as IRect (a rectangle defined by integers).
2319  */
2320 NR::IRect SPCanvas::getViewboxIntegers() const
2322     GtkWidget const *w = GTK_WIDGET(this);
2323     return NR::IRect(NR::IPoint(x0, y0),
2324                     NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height));
2327 inline int sp_canvas_tile_floor(int x)
2329     return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2332 inline int sp_canvas_tile_ceil(int x)
2334     return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2337 /**
2338  * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2339  */
2340 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2342     if ( nl >= nr || nt >= nb ) {
2343         if ( canvas->tiles ) g_free(canvas->tiles);
2344         canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2345         canvas->tileH=canvas->tileV=0;
2346         canvas->tiles=NULL;
2347         return;
2348     }
2349     int tl=sp_canvas_tile_floor(nl);
2350     int tt=sp_canvas_tile_floor(nt);
2351     int tr=sp_canvas_tile_ceil(nr);
2352     int tb=sp_canvas_tile_ceil(nb);
2354     int nh = tr-tl, nv = tb-tt;
2355     uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2356     for (int i=tl; i<tr; i++) {
2357         for (int j=tt; j<tb; j++) {
2358             int ind = (i-tl) + (j-tt)*nh;
2359             if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2360                 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2361             } else {
2362                 ntiles[ind]=0; // newly exposed areas get 0
2363             }
2364         }
2365     }
2366     if ( canvas->tiles ) g_free(canvas->tiles);
2367     canvas->tiles=ntiles;
2368     canvas->tLeft=tl;
2369     canvas->tTop=tt;
2370     canvas->tRight=tr;
2371     canvas->tBottom=tb;
2372     canvas->tileH=nh;
2373     canvas->tileV=nv;
2376 /*
2377  * Helper that queues a canvas rectangle for redraw
2378  */
2379 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2380     canvas->need_redraw = TRUE;
2382     sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2385 /**
2386  * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2387  */
2388 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2390     if ( nl >= nr || nt >= nb ) {
2391         return;
2392     }
2393     int tl=sp_canvas_tile_floor(nl);
2394     int tt=sp_canvas_tile_floor(nt);
2395     int tr=sp_canvas_tile_ceil(nr);
2396     int tb=sp_canvas_tile_ceil(nb);
2397     if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2398     if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2399     if ( tr > canvas->tRight ) tr=canvas->tRight;
2400     if ( tt < canvas->tTop ) tt=canvas->tTop;
2401     if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2403     for (int i=tl; i<tr; i++) {
2404         for (int j=tt; j<tb; j++) {
2405             canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2406         }
2407     }
2411 /*
2412   Local Variables:
2413   mode:c++
2414   c-file-style:"stroustrup"
2415   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2416   indent-tabs-mode:nil
2417   fill-column:99
2418   End:
2419 */
2420 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :