Code

lay groundwork for non-filter view mode
[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>
28 #include <gtkmm.h>
30 #include <helper/sp-marshal.h>
31 #include <display/sp-canvas.h>
32 #include "display-forward.h"
33 #include <libnr/nr-matrix-fns.h>
34 #include <libnr/nr-matrix-ops.h>
35 #include <libnr/nr-convex-hull.h>
36 #include "prefs-utils.h"
37 #include "inkscape.h"
38 #include "sodipodi-ctrlrect.h"
39 #if ENABLE_LCMS
40 #include "color-profile-fns.h"
41 #endif // ENABLE_LCMS
42 #include "display/rendermode.h"
44 // Define this to visualize the regions to be redrawn
45 //#define DEBUG_REDRAW 1;
47 // Tiles are a way to minimize the number of redraws, eliminating too small redraws.
48 // The canvas stores a 2D array of ints, each representing a TILE_SIZExTILE_SIZE pixels tile.
49 // If any part of it is dirtied, the entire tile is dirtied (its int is nonzero) and repainted.
50 #define TILE_SIZE 16
52 static gint const sp_canvas_update_priority = G_PRIORITY_HIGH_IDLE;
54 #define SP_CANVAS_WINDOW(c) (((GtkWidget *) (c))->window)
56 enum {
57     SP_CANVAS_ITEM_VISIBLE = 1 << 7,
58     SP_CANVAS_ITEM_NEED_UPDATE = 1 << 8,
59     SP_CANVAS_ITEM_NEED_AFFINE = 1 << 9
60 };
62 /**
63  * A group of Items.
64  */
65 struct SPCanvasGroup {
66     SPCanvasItem item;
68     GList *items, *last;
69 };
71 /**
72  * The SPCanvasGroup vtable.
73  */
74 struct SPCanvasGroupClass {
75     SPCanvasItemClass parent_class;
76 };
78 /**
79  * The SPCanvas vtable.
80  */
81 struct SPCanvasClass {
82     GtkWidgetClass parent_class;
83 };
85 static void group_add (SPCanvasGroup *group, SPCanvasItem *item);
86 static void group_remove (SPCanvasGroup *group, SPCanvasItem *item);
88 /* SPCanvasItem */
90 enum {ITEM_EVENT, ITEM_LAST_SIGNAL};
93 static void sp_canvas_request_update (SPCanvas *canvas);
95 static void sp_canvas_item_class_init (SPCanvasItemClass *klass);
96 static void sp_canvas_item_init (SPCanvasItem *item);
97 static void sp_canvas_item_dispose (GObject *object);
98 static void sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args);
100 static int emit_event (SPCanvas *canvas, GdkEvent *event);
102 static guint item_signals[ITEM_LAST_SIGNAL] = { 0 };
104 static GtkObjectClass *item_parent_class;
106 /**
107  * Registers the SPCanvasItem class with Glib and returns its type number.
108  */
109 GType
110 sp_canvas_item_get_type (void)
112     static GType type = 0;
113     if (!type) {
114         static GTypeInfo const info = {
115             sizeof (SPCanvasItemClass),
116             NULL, NULL,
117             (GClassInitFunc) sp_canvas_item_class_init,
118             NULL, NULL,
119             sizeof (SPCanvasItem),
120             0,
121             (GInstanceInitFunc) sp_canvas_item_init,
122             NULL
123         };
124         type = g_type_register_static (GTK_TYPE_OBJECT, "SPCanvasItem", &info, (GTypeFlags)0);
125     }
127     return type;
130 /**
131  * Initializes the SPCanvasItem vtable and the "event" signal.
132  */
133 static void
134 sp_canvas_item_class_init (SPCanvasItemClass *klass)
136     GObjectClass *object_class = (GObjectClass *) klass;
138     /* fixme: Derive from GObject */
139     item_parent_class = (GtkObjectClass*)gtk_type_class (GTK_TYPE_OBJECT);
141     item_signals[ITEM_EVENT] = g_signal_new ("event",
142                                              G_TYPE_FROM_CLASS (klass),
143                                              G_SIGNAL_RUN_LAST,
144                                              ((glong)((guint8*)&(klass->event) - (guint8*)klass)),
145                                              NULL, NULL,
146                                              sp_marshal_BOOLEAN__POINTER,
147                                              G_TYPE_BOOLEAN, 1,
148                                              GDK_TYPE_EVENT);
150     object_class->dispose = sp_canvas_item_dispose;
153 /**
154  * Callback for initialization of SPCanvasItem.
155  */
156 static void
157 sp_canvas_item_init (SPCanvasItem *item)
159     item->flags |= SP_CANVAS_ITEM_VISIBLE;
160     item->xform = NR::Matrix(NR::identity());
163 /**
164  * Constructs new SPCanvasItem on SPCanvasGroup.
165  */
166 SPCanvasItem *
167 sp_canvas_item_new (SPCanvasGroup *parent, GtkType type, gchar const *first_arg_name, ...)
169     va_list args;
171     g_return_val_if_fail (parent != NULL, NULL);
172     g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL);
173     g_return_val_if_fail (gtk_type_is_a (type, sp_canvas_item_get_type ()), NULL);
175     SPCanvasItem *item = SP_CANVAS_ITEM (gtk_type_new (type));
177     va_start (args, first_arg_name);
178     sp_canvas_item_construct (item, parent, first_arg_name, args);
179     va_end (args);
181     return item;
184 /**
185  * Sets up the newly created SPCanvasItem.
186  *
187  * We make it static for encapsulation reasons since it was nowhere used.
188  */
189 static void
190 sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args)
192     g_return_if_fail (SP_IS_CANVAS_GROUP (parent));
193     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
195     item->parent = SP_CANVAS_ITEM (parent);
196     item->canvas = item->parent->canvas;
198     g_object_set_valist (G_OBJECT (item), first_arg_name, args);
200     group_add (SP_CANVAS_GROUP (item->parent), item);
202     sp_canvas_item_request_update (item);
205 /**
206  * Helper function that requests redraw only if item's visible flag is set.
207  */
208 static void
209 redraw_if_visible (SPCanvasItem *item)
211     if (item->flags & SP_CANVAS_ITEM_VISIBLE) {
212         int x0 = (int)(item->x1);
213         int x1 = (int)(item->x2);
214         int y0 = (int)(item->y1);
215         int y1 = (int)(item->y2);
217         if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
218             sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
219         }
220     }
223 /**
224  * Callback that removes item from all referers and destroys it.
225  */
226 static void
227 sp_canvas_item_dispose (GObject *object)
229     SPCanvasItem *item = SP_CANVAS_ITEM (object);
231     // Hack: if this is a ctrlrect, move it to 0,0;
232     // this redraws only the stroke of the rect to be deleted,
233     // avoiding redraw of the entire area
234     if (SP_IS_CTRLRECT(item)) {
235         SP_CTRLRECT(object)->setRectangle(NR::Rect(NR::Point(0,0),NR::Point(0,0)));
236         SP_CTRLRECT(object)->update(item->xform, 0);
237     } else {
238         redraw_if_visible (item);
239     }
240     item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
242     if (item == item->canvas->current_item) {
243         item->canvas->current_item = NULL;
244         item->canvas->need_repick = TRUE;
245     }
247     if (item == item->canvas->new_current_item) {
248         item->canvas->new_current_item = NULL;
249         item->canvas->need_repick = TRUE;
250     }
252     if (item == item->canvas->grabbed_item) {
253         item->canvas->grabbed_item = NULL;
254         gdk_pointer_ungrab (GDK_CURRENT_TIME);
255     }
257     if (item == item->canvas->focused_item)
258         item->canvas->focused_item = NULL;
260     if (item->parent) {
261         group_remove (SP_CANVAS_GROUP (item->parent), item);
262     }
264     G_OBJECT_CLASS (item_parent_class)->dispose (object);
267 /**
268  * Helper function to update item and its children.
269  *
270  * NB! affine is parent2canvas.
271  */
272 static void
273 sp_canvas_item_invoke_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
275     /* Apply the child item's transform */
276     NR::Matrix child_affine = item->xform * affine;
278     /* apply object flags to child flags */
279     int child_flags = flags & ~SP_CANVAS_UPDATE_REQUESTED;
281     if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
282         child_flags |= SP_CANVAS_UPDATE_REQUESTED;
284     if (item->flags & SP_CANVAS_ITEM_NEED_AFFINE)
285         child_flags |= SP_CANVAS_UPDATE_AFFINE;
287     if (child_flags & (SP_CANVAS_UPDATE_REQUESTED | SP_CANVAS_UPDATE_AFFINE)) {
288         if (SP_CANVAS_ITEM_GET_CLASS (item)->update)
289             SP_CANVAS_ITEM_GET_CLASS (item)->update (item, child_affine, child_flags);
290     }
292     GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_UPDATE);
293     GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_AFFINE);
296 /**
297  * Helper function to invoke the point method of the item.
298  *
299  * The argument x, y should be in the parent's item-relative coordinate
300  * system.  This routine applies the inverse of the item's transform,
301  * maintaining the affine invariant.
302  */
303 static double
304 sp_canvas_item_invoke_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
306     if (SP_CANVAS_ITEM_GET_CLASS (item)->point)
307         return SP_CANVAS_ITEM_GET_CLASS (item)->point (item, p, actual_item);
309     return NR_HUGE;
312 /**
313  * Makes the item's affine transformation matrix be equal to the specified
314  * matrix.
315  *
316  * @item: A canvas item.
317  * @affine: An affine transformation matrix.
318  */
319 void
320 sp_canvas_item_affine_absolute (SPCanvasItem *item, NR::Matrix const &affine)
322     item->xform = affine;
324     if (!(item->flags & SP_CANVAS_ITEM_NEED_AFFINE)) {
325         item->flags |= SP_CANVAS_ITEM_NEED_AFFINE;
326         if (item->parent != NULL) {
327             sp_canvas_item_request_update (item->parent);
328         } else {
329             sp_canvas_request_update (item->canvas);
330         }
331     }
333     item->canvas->need_repick = TRUE;
336 /**
337  * Convenience function to reorder items in a group's child list.
338  *
339  * This puts the specified link after the "before" link.
340  */
341 static void
342 put_item_after (GList *link, GList *before)
344     if (link == before)
345         return;
347     SPCanvasGroup *parent = SP_CANVAS_GROUP (SP_CANVAS_ITEM (link->data)->parent);
349     if (before == NULL) {
350         if (link == parent->items) return;
352         link->prev->next = link->next;
354         if (link->next) {
355             link->next->prev = link->prev;
356         } else {
357             parent->last = link->prev;
358         }
360         link->prev = before;
361         link->next = parent->items;
362         link->next->prev = link;
363         parent->items = link;
364     } else {
365         if ((link == parent->last) && (before == parent->last->prev))
366             return;
368         if (link->next)
369             link->next->prev = link->prev;
371         if (link->prev)
372             link->prev->next = link->next;
373         else {
374             parent->items = link->next;
375             parent->items->prev = NULL;
376         }
378         link->prev = before;
379         link->next = before->next;
381         link->prev->next = link;
383         if (link->next)
384             link->next->prev = link;
385         else
386             parent->last = link;
387     }
391 /**
392  * Raises the item in its parent's stack by the specified number of positions.
393  *
394  * \param item A canvas item.
395  * \param positions Number of steps to raise the item.
396  *
397  * If the number of positions is greater than the distance to the top of the
398  * stack, then the item is put at the top.
399  */
400 void
401 sp_canvas_item_raise (SPCanvasItem *item, int positions)
403     g_return_if_fail (item != NULL);
404     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
405     g_return_if_fail (positions >= 0);
407     if (!item->parent || positions == 0)
408         return;
410     SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
411     GList *link = g_list_find (parent->items, item);
412     g_assert (link != NULL);
414     GList *before;
415     for (before = link; positions && before; positions--)
416         before = before->next;
418     if (!before)
419         before = parent->last;
421     put_item_after (link, before);
423     redraw_if_visible (item);
424     item->canvas->need_repick = TRUE;
428 /**
429  * Lowers the item in its parent's stack by the specified number of positions.
430  *
431  * \param item A canvas item.
432  * \param positions Number of steps to lower the item.
433  *
434  * If the number of positions is greater than the distance to the bottom of the
435  * stack, then the item is put at the bottom.
436  **/
437 void
438 sp_canvas_item_lower (SPCanvasItem *item, int positions)
440     g_return_if_fail (item != NULL);
441     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
442     g_return_if_fail (positions >= 1);
444     if (!item->parent || positions == 0)
445         return;
447     SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
448     GList *link = g_list_find (parent->items, item);
449     g_assert (link != NULL);
451     GList *before;
452     if (link->prev)
453         for (before = link->prev; positions && before; positions--)
454             before = before->prev;
455     else
456         before = NULL;
458     put_item_after (link, before);
460     redraw_if_visible (item);
461     item->canvas->need_repick = TRUE;
464 /**
465  * Sets visible flag on item and requests a redraw.
466  */
467 void
468 sp_canvas_item_show (SPCanvasItem *item)
470     g_return_if_fail (item != NULL);
471     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
473     if (item->flags & SP_CANVAS_ITEM_VISIBLE)
474         return;
476     item->flags |= SP_CANVAS_ITEM_VISIBLE;
478     int x0 = (int)(item->x1);
479     int x1 = (int)(item->x2);
480     int y0 = (int)(item->y1);
481     int y1 = (int)(item->y2);
483     if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
484         sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
485         item->canvas->need_repick = TRUE;
486     }
489 /**
490  * Clears visible flag on item and requests a redraw.
491  */
492 void
493 sp_canvas_item_hide (SPCanvasItem *item)
495     g_return_if_fail (item != NULL);
496     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
498     if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
499         return;
501     item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
503     int x0 = (int)(item->x1);
504     int x1 = (int)(item->x2);
505     int y0 = (int)(item->y1);
506     int y1 = (int)(item->y2);
508     if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
509         sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
510         item->canvas->need_repick = TRUE;
511     }
514 /**
515  * Grab item under cursor.
516  *
517  * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE
518  */
519 int
520 sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
522     g_return_val_if_fail (item != NULL, -1);
523     g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1);
524     g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1);
526     if (item->canvas->grabbed_item)
527         return -1;
529     if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
530         return -1;
532     /* fixme: Top hack (Lauris) */
533     /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
534     /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
535     gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
536                       (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
537                       NULL, cursor, etime);
539     item->canvas->grabbed_item = item;
540     item->canvas->grabbed_event_mask = event_mask;
541     item->canvas->current_item = item; /* So that events go to the grabbed item */
543     return 0;
546 /**
547  * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
548  * mouse.
549  *
550  * \param item A canvas item that holds a grab.
551  * \param etime The timestamp for ungrabbing the mouse.
552  */
553 void
554 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
556     g_return_if_fail (item != NULL);
557     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
559     if (item->canvas->grabbed_item != item)
560         return;
562     item->canvas->grabbed_item = NULL;
564     gdk_pointer_ungrab (etime);
567 /**
568  * Returns the product of all transformation matrices from the root item down
569  * to the item.
570  */
571 NR::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
573     g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
575     NR::Matrix affine = NR::identity();
577     while (item) {
578         affine *= item->xform;
579         item = item->parent;
580     }
581     return affine;
584 /**
585  * Helper that returns true iff item is descendant of parent.
586  */
587 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
589     while (item) {
590         if (item == parent)
591             return true;
592         item = item->parent;
593     }
595     return false;
598 /**
599  * Focus canvas, and item under cursor if it is not already focussed.
600  */
601 void
602 sp_canvas_item_grab_focus (SPCanvasItem *item)
604     g_return_if_fail (item != NULL);
605     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
606     g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
608     SPCanvasItem *focused_item = item->canvas->focused_item;
610     if (focused_item) {
611         GdkEvent ev;
612         ev.focus_change.type = GDK_FOCUS_CHANGE;
613         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
614         ev.focus_change.send_event = FALSE;
615         ev.focus_change.in = FALSE;
617         emit_event (item->canvas, &ev);
618     }
620     item->canvas->focused_item = item;
621     gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
623     if (focused_item) {
624         GdkEvent ev;
625         ev.focus_change.type = GDK_FOCUS_CHANGE;
626         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
627         ev.focus_change.send_event = FALSE;
628         ev.focus_change.in = TRUE;
630         emit_event (item->canvas, &ev);
631     }
634 /**
635  * Requests that the canvas queue an update for the specified item.
636  *
637  * To be used only by item implementations.
638  */
639 void
640 sp_canvas_item_request_update (SPCanvasItem *item)
642     if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
643         return;
645     item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
647     if (item->parent != NULL) {
648         /* Recurse up the tree */
649         sp_canvas_item_request_update (item->parent);
650     } else {
651         /* Have reached the top of the tree, make sure the update call gets scheduled. */
652         sp_canvas_request_update (item->canvas);
653     }
656 /**
657  * Returns position of item in group.
658  */
659 gint sp_canvas_item_order (SPCanvasItem * item)
661     return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
664 /* SPCanvasGroup */
666 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
667 static void sp_canvas_group_init (SPCanvasGroup *group);
668 static void sp_canvas_group_destroy (GtkObject *object);
670 static void sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
671 static double sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item);
672 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
674 static SPCanvasItemClass *group_parent_class;
676 /**
677  * Registers SPCanvasGroup class with Gtk and returns its type number.
678  */
679 GtkType
680 sp_canvas_group_get_type (void)
682     static GtkType group_type = 0;
684     if (!group_type) {
685         static GtkTypeInfo const group_info = {
686             "SPCanvasGroup",
687             sizeof (SPCanvasGroup),
688             sizeof (SPCanvasGroupClass),
689             (GtkClassInitFunc) sp_canvas_group_class_init,
690             (GtkObjectInitFunc) sp_canvas_group_init,
691             NULL, NULL, NULL
692         };
694         group_type = gtk_type_unique (sp_canvas_item_get_type (), &group_info);
695     }
697     return group_type;
700 /**
701  * Class initialization function for SPCanvasGroupClass
702  */
703 static void
704 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
706     GtkObjectClass *object_class = (GtkObjectClass *) klass;
707     SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
709     group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
711     object_class->destroy = sp_canvas_group_destroy;
713     item_class->update = sp_canvas_group_update;
714     item_class->render = sp_canvas_group_render;
715     item_class->point = sp_canvas_group_point;
718 /**
719  * Callback. Empty.
720  */
721 static void
722 sp_canvas_group_init (SPCanvasGroup */*group*/)
724     /* Nothing here */
727 /**
728  * Callback that destroys all items in group and calls group's virtual
729  * destroy() function.
730  */
731 static void
732 sp_canvas_group_destroy (GtkObject *object)
734     g_return_if_fail (object != NULL);
735     g_return_if_fail (SP_IS_CANVAS_GROUP (object));
737     SPCanvasGroup const *group = SP_CANVAS_GROUP (object);
739     GList *list = group->items;
740     while (list) {
741         SPCanvasItem *child = (SPCanvasItem *)list->data;
742         list = list->next;
744         gtk_object_destroy (GTK_OBJECT (child));
745     }
747     if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
748         (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
751 /**
752  * Update handler for canvas groups
753  */
754 static void
755 sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
757     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
758     NR::ConvexHull corners(NR::Point(0, 0));
759     bool empty=true;
761     for (GList *list = group->items; list; list = list->next) {
762         SPCanvasItem *i = (SPCanvasItem *)list->data;
764         sp_canvas_item_invoke_update (i, affine, flags);
766         if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
767             if (empty) {
768                 corners = NR::ConvexHull(NR::Point(i->x1, i->y1));
769                 empty = false;
770             } else {
771                 corners.add(NR::Point(i->x1, i->y1));
772             }
773             corners.add(NR::Point(i->x2, i->y2));
774         }
775     }
777     NR::Maybe<NR::Rect> const bounds = corners.bounds();
778     if (bounds) {
779         item->x1 = bounds->min()[NR::X];
780         item->y1 = bounds->min()[NR::Y];
781         item->x2 = bounds->max()[NR::X];
782         item->y2 = bounds->max()[NR::Y];
783     } else {
784         // FIXME ?
785         item->x1 = item->x2 = item->y1 = item->y2 = 0;
786     }
789 /**
790  * Point handler for canvas groups.
791  */
792 static double
793 sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
795     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
796     double const x = p[NR::X];
797     double const y = p[NR::Y];
798     int x1 = (int)(x - item->canvas->close_enough);
799     int y1 = (int)(y - item->canvas->close_enough);
800     int x2 = (int)(x + item->canvas->close_enough);
801     int y2 = (int)(y + item->canvas->close_enough);
803     double best = 0.0;
804     *actual_item = NULL;
806     double dist = 0.0;
808     for (GList *list = group->items; list; list = list->next) {
809         SPCanvasItem *child = (SPCanvasItem *)list->data;
811         if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
812             SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
814             int has_point;
815             if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
816                 dist = sp_canvas_item_invoke_point (child, p, &point_item);
817                 has_point = TRUE;
818             } else
819                 has_point = FALSE;
821             if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
822                 best = dist;
823                 *actual_item = point_item;
824             }
825         }
826     }
828     return best;
831 /**
832  * Renders all visible canvas group items in buf rectangle.
833  */
834 static void
835 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
837     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
839     for (GList *list = group->items; list; list = list->next) {
840         SPCanvasItem *child = (SPCanvasItem *)list->data;
841         if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
842             if ((child->x1 < buf->rect.x1) &&
843                 (child->y1 < buf->rect.y1) &&
844                 (child->x2 > buf->rect.x0) &&
845                 (child->y2 > buf->rect.y0)) {
846                 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
847                     SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
848             }
849         }
850     }
853 /**
854  * Adds an item to a canvas group.
855  */
856 static void
857 group_add (SPCanvasGroup *group, SPCanvasItem *item)
859     gtk_object_ref (GTK_OBJECT (item));
860     gtk_object_sink (GTK_OBJECT (item));
862     if (!group->items) {
863         group->items = g_list_append (group->items, item);
864         group->last = group->items;
865     } else {
866         group->last = g_list_append (group->last, item)->next;
867     }
869     sp_canvas_item_request_update (item);
872 /**
873  * Removes an item from a canvas group
874  */
875 static void
876 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
878     g_return_if_fail (group != NULL);
879     g_return_if_fail (SP_IS_CANVAS_GROUP (group));
880     g_return_if_fail (item != NULL);
882     for (GList *children = group->items; children; children = children->next) {
883         if (children->data == item) {
885             /* Unparent the child */
887             item->parent = NULL;
888             gtk_object_unref (GTK_OBJECT (item));
890             /* Remove it from the list */
892             if (children == group->last) group->last = children->prev;
894             group->items = g_list_remove_link (group->items, children);
895             g_list_free (children);
896             break;
897         }
898     }
901 /* SPCanvas */
903 static void sp_canvas_class_init (SPCanvasClass *klass);
904 static void sp_canvas_init (SPCanvas *canvas);
905 static void sp_canvas_destroy (GtkObject *object);
907 static void sp_canvas_realize (GtkWidget *widget);
908 static void sp_canvas_unrealize (GtkWidget *widget);
910 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
911 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
913 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
914 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
915 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
916 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
917 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
918 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
919 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
920 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
922 static GtkWidgetClass *canvas_parent_class;
924 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb);
925 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb);
926 static void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val);
927 static int do_update (SPCanvas *canvas);
929 /**
930  * Registers the SPCanvas class if necessary, and returns the type ID
931  * associated to it.
932  *
933  * \return The type ID of the SPCanvas class.
934  **/
935 GtkType
936 sp_canvas_get_type (void)
938     static GtkType canvas_type = 0;
940     if (!canvas_type) {
941         static GtkTypeInfo const canvas_info = {
942             "SPCanvas",
943             sizeof (SPCanvas),
944             sizeof (SPCanvasClass),
945             (GtkClassInitFunc) sp_canvas_class_init,
946             (GtkObjectInitFunc) sp_canvas_init,
947             NULL, NULL, NULL
948         };
950         canvas_type = gtk_type_unique (GTK_TYPE_WIDGET, &canvas_info);
951     }
953     return canvas_type;
956 /**
957  * Class initialization function for SPCanvasClass.
958  */
959 static void
960 sp_canvas_class_init (SPCanvasClass *klass)
962     GtkObjectClass *object_class = (GtkObjectClass *) klass;
963     GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
965     canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
967     object_class->destroy = sp_canvas_destroy;
969     widget_class->realize = sp_canvas_realize;
970     widget_class->unrealize = sp_canvas_unrealize;
971     widget_class->size_request = sp_canvas_size_request;
972     widget_class->size_allocate = sp_canvas_size_allocate;
973     widget_class->button_press_event = sp_canvas_button;
974     widget_class->button_release_event = sp_canvas_button;
975     widget_class->motion_notify_event = sp_canvas_motion;
976     widget_class->scroll_event = sp_canvas_scroll;
977     widget_class->expose_event = sp_canvas_expose;
978     widget_class->key_press_event = sp_canvas_key;
979     widget_class->key_release_event = sp_canvas_key;
980     widget_class->enter_notify_event = sp_canvas_crossing;
981     widget_class->leave_notify_event = sp_canvas_crossing;
982     widget_class->focus_in_event = sp_canvas_focus_in;
983     widget_class->focus_out_event = sp_canvas_focus_out;
986 /**
987  * Callback: object initialization for SPCanvas.
988  */
989 static void
990 sp_canvas_init (SPCanvas *canvas)
992     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
993     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
994     GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
996     canvas->pick_event.type = GDK_LEAVE_NOTIFY;
997     canvas->pick_event.crossing.x = 0;
998     canvas->pick_event.crossing.y = 0;
1000     /* Create the root item as a special case */
1001     canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
1002     canvas->root->canvas = canvas;
1004     gtk_object_ref (GTK_OBJECT (canvas->root));
1005     gtk_object_sink (GTK_OBJECT (canvas->root));
1007     canvas->need_repick = TRUE;
1009     // See comment at in sp-canvas.h.
1010     canvas->gen_all_enter_events = false;
1012     canvas->tiles=NULL;
1013     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1014     canvas->tileH=canvas->tileV=0;
1016     canvas->forced_redraw_count = 0;
1017     canvas->forced_redraw_limit = -1;
1019 #if ENABLE_LCMS
1020     canvas->enable_cms_display_adj = false;
1021     canvas->cms_key = new Glib::ustring("");
1022 #endif // ENABLE_LCMS
1024     canvas->is_scrolling = false;
1028 /**
1029  * Convenience function to remove the idle handler of a canvas.
1030  */
1031 static void
1032 remove_idle (SPCanvas *canvas)
1034     if (canvas->idle_id) {
1035         gtk_idle_remove (canvas->idle_id);
1036         canvas->idle_id = 0;
1037     }
1040 /*
1041  * Removes the transient state of the canvas (idle handler, grabs).
1042  */
1043 static void
1044 shutdown_transients (SPCanvas *canvas)
1046     /* We turn off the need_redraw flag, since if the canvas is mapped again
1047      * it will request a redraw anyways.  We do not turn off the need_update
1048      * flag, though, because updates are not queued when the canvas remaps
1049      * itself.
1050      */
1051     if (canvas->need_redraw) {
1052         canvas->need_redraw = FALSE;
1053     }
1054     if ( canvas->tiles ) g_free(canvas->tiles);
1055     canvas->tiles=NULL;
1056     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1057     canvas->tileH=canvas->tileV=0;
1059     if (canvas->grabbed_item) {
1060         canvas->grabbed_item = NULL;
1061         gdk_pointer_ungrab (GDK_CURRENT_TIME);
1062     }
1064     remove_idle (canvas);
1067 /**
1068  * Destroy handler for SPCanvas.
1069  */
1070 static void
1071 sp_canvas_destroy (GtkObject *object)
1073     SPCanvas *canvas = SP_CANVAS (object);
1075     if (canvas->root) {
1076         gtk_object_unref (GTK_OBJECT (canvas->root));
1077         canvas->root = NULL;
1078     }
1080     shutdown_transients (canvas);
1082     if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1083         (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1086 /**
1087  * Returns new canvas as widget.
1088  */
1089 GtkWidget *
1090 sp_canvas_new_aa (void)
1092     SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1094     return (GtkWidget *) canvas;
1097 /**
1098  * The canvas widget's realize callback.
1099  */
1100 static void
1101 sp_canvas_realize (GtkWidget *widget)
1103     SPCanvas *canvas = SP_CANVAS (widget);
1105     GdkWindowAttr attributes;
1106     attributes.window_type = GDK_WINDOW_CHILD;
1107     attributes.x = widget->allocation.x;
1108     attributes.y = widget->allocation.y;
1109     attributes.width = widget->allocation.width;
1110     attributes.height = widget->allocation.height;
1111     attributes.wclass = GDK_INPUT_OUTPUT;
1112     attributes.visual = gdk_rgb_get_visual ();
1113     attributes.colormap = gdk_rgb_get_cmap ();
1114     attributes.event_mask = (gtk_widget_get_events (widget) |
1115                              GDK_EXPOSURE_MASK |
1116                              GDK_BUTTON_PRESS_MASK |
1117                              GDK_BUTTON_RELEASE_MASK |
1118                              GDK_POINTER_MOTION_MASK |
1119                              GDK_PROXIMITY_IN_MASK |
1120                              GDK_PROXIMITY_OUT_MASK |
1121                              GDK_KEY_PRESS_MASK |
1122                              GDK_KEY_RELEASE_MASK |
1123                              GDK_ENTER_NOTIFY_MASK |
1124                              GDK_LEAVE_NOTIFY_MASK |
1125                              GDK_FOCUS_CHANGE_MASK);
1126     gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1128     widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1129     gdk_window_set_user_data (widget->window, widget);
1131     if ( prefs_get_int_attribute ("options.useextinput", "value", 1) )
1132         gtk_widget_set_events(widget, attributes.event_mask);
1134     widget->style = gtk_style_attach (widget->style, widget->window);
1136     GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1138     canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1141 /**
1142  * The canvas widget's unrealize callback.
1143  */
1144 static void
1145 sp_canvas_unrealize (GtkWidget *widget)
1147     SPCanvas *canvas = SP_CANVAS (widget);
1149     shutdown_transients (canvas);
1151     gdk_gc_destroy (canvas->pixmap_gc);
1152     canvas->pixmap_gc = NULL;
1154     if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1155         (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1158 /**
1159  * The canvas widget's size_request callback.
1160  */
1161 static void
1162 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1164     static_cast<void>(SP_CANVAS (widget));
1166     req->width = 256;
1167     req->height = 256;
1170 /**
1171  * The canvas widget's size_allocate callback.
1172  */
1173 static void
1174 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1176     SPCanvas *canvas = SP_CANVAS (widget);
1178     /* Schedule redraw of new region */
1179     sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1180     if (allocation->width > widget->allocation.width) {
1181         sp_canvas_request_redraw (canvas,
1182                                   canvas->x0 + widget->allocation.width,
1183                                   0,
1184                                   canvas->x0 + allocation->width,
1185                                   canvas->y0 + allocation->height);
1186     }
1187     if (allocation->height > widget->allocation.height) {
1188         sp_canvas_request_redraw (canvas,
1189                                   0,
1190                                   canvas->y0 + widget->allocation.height,
1191                                   canvas->x0 + allocation->width,
1192                                   canvas->y0 + allocation->height);
1193     }
1195     widget->allocation = *allocation;
1196     
1197     if (GTK_WIDGET_REALIZED (widget)) {
1198         gdk_window_move_resize (widget->window,
1199                                 widget->allocation.x, widget->allocation.y,
1200                                 widget->allocation.width, widget->allocation.height);
1201     }
1204 /**
1205  * Helper that emits an event for an item in the canvas, be it the current
1206  * item, grabbed item, or focused item, as appropriate.
1207  */
1208 static int
1209 emit_event (SPCanvas *canvas, GdkEvent *event)
1211     guint mask;
1213     if (canvas->grabbed_item) {
1214         switch (event->type) {
1215         case GDK_ENTER_NOTIFY:
1216             mask = GDK_ENTER_NOTIFY_MASK;
1217             break;
1218         case GDK_LEAVE_NOTIFY:
1219             mask = GDK_LEAVE_NOTIFY_MASK;
1220             break;
1221         case GDK_MOTION_NOTIFY:
1222             mask = GDK_POINTER_MOTION_MASK;
1223             break;
1224         case GDK_BUTTON_PRESS:
1225         case GDK_2BUTTON_PRESS:
1226         case GDK_3BUTTON_PRESS:
1227             mask = GDK_BUTTON_PRESS_MASK;
1228             break;
1229         case GDK_BUTTON_RELEASE:
1230             mask = GDK_BUTTON_RELEASE_MASK;
1231             break;
1232         case GDK_KEY_PRESS:
1233             mask = GDK_KEY_PRESS_MASK;
1234             break;
1235         case GDK_KEY_RELEASE:
1236             mask = GDK_KEY_RELEASE_MASK;
1237             break;
1238         case GDK_SCROLL:
1239             mask = GDK_SCROLL;
1240             break;
1241         default:
1242             mask = 0;
1243             break;
1244         }
1246         if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1247     }
1249     /* Convert to world coordinates -- we have two cases because of diferent
1250      * offsets of the fields in the event structures.
1251      */
1253     GdkEvent ev = *event;
1255     switch (ev.type) {
1256     case GDK_ENTER_NOTIFY:
1257     case GDK_LEAVE_NOTIFY:
1258         ev.crossing.x += canvas->x0;
1259         ev.crossing.y += canvas->y0;
1260         break;
1261     case GDK_MOTION_NOTIFY:
1262     case GDK_BUTTON_PRESS:
1263     case GDK_2BUTTON_PRESS:
1264     case GDK_3BUTTON_PRESS:
1265     case GDK_BUTTON_RELEASE:
1266         ev.motion.x += canvas->x0;
1267         ev.motion.y += canvas->y0;
1268         break;
1269     default:
1270         break;
1271     }
1273     /* Choose where we send the event */
1275     /* canvas->current_item becomes NULL in some cases under Win32
1276     ** (e.g. if the pointer leaves the window).  So this is a hack that
1277     ** Lauris applied to SP to get around the problem.
1278     */
1279     SPCanvasItem* item = NULL;
1280     if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1281         item = canvas->grabbed_item;
1282     } else {
1283         item = canvas->current_item;
1284     }
1286     if (canvas->focused_item &&
1287         ((event->type == GDK_KEY_PRESS) ||
1288          (event->type == GDK_KEY_RELEASE) ||
1289          (event->type == GDK_FOCUS_CHANGE))) {
1290         item = canvas->focused_item;
1291     }
1293     /* The event is propagated up the hierarchy (for if someone connected to
1294      * a group instead of a leaf event), and emission is stopped if a
1295      * handler returns TRUE, just like for GtkWidget events.
1296      */
1298     gint finished = FALSE;
1300     while (item && !finished) {
1301         gtk_object_ref (GTK_OBJECT (item));
1302         gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1303         SPCanvasItem *parent = item->parent;
1304         gtk_object_unref (GTK_OBJECT (item));
1305         item = parent;
1306     }
1308     return finished;
1311 /**
1312  * Helper that re-picks the current item in the canvas, based on the event's
1313  * coordinates and emits enter/leave events for items as appropriate.
1314  */
1315 static int
1316 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1318     int button_down = 0;
1319     double x, y;
1321     if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1322         return FALSE;
1324     int retval = FALSE;
1326     if (canvas->gen_all_enter_events == false) {
1327         // If a button is down, we'll perform enter and leave events on the
1328         // current item, but not enter on any other item.  This is more or
1329         // less like X pointer grabbing for canvas items.
1330         //
1331         button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1332                 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1334         if (!button_down) canvas->left_grabbed_item = FALSE;
1335     }
1337     /* Save the event in the canvas.  This is used to synthesize enter and
1338      * leave events in case the current item changes.  It is also used to
1339      * re-pick the current item if the current one gets deleted.  Also,
1340      * synthesize an enter event.
1341      */
1342     if (event != &canvas->pick_event) {
1343         if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1344             /* these fields have the same offsets in both types of events */
1346             canvas->pick_event.crossing.type       = GDK_ENTER_NOTIFY;
1347             canvas->pick_event.crossing.window     = event->motion.window;
1348             canvas->pick_event.crossing.send_event = event->motion.send_event;
1349             canvas->pick_event.crossing.subwindow  = NULL;
1350             canvas->pick_event.crossing.x          = event->motion.x;
1351             canvas->pick_event.crossing.y          = event->motion.y;
1352             canvas->pick_event.crossing.mode       = GDK_CROSSING_NORMAL;
1353             canvas->pick_event.crossing.detail     = GDK_NOTIFY_NONLINEAR;
1354             canvas->pick_event.crossing.focus      = FALSE;
1355             canvas->pick_event.crossing.state      = event->motion.state;
1357             /* these fields don't have the same offsets in both types of events */
1359             if (event->type == GDK_MOTION_NOTIFY) {
1360                 canvas->pick_event.crossing.x_root = event->motion.x_root;
1361                 canvas->pick_event.crossing.y_root = event->motion.y_root;
1362             } else {
1363                 canvas->pick_event.crossing.x_root = event->button.x_root;
1364                 canvas->pick_event.crossing.y_root = event->button.y_root;
1365             }
1366         } else {
1367             canvas->pick_event = *event;
1368         }
1369     }
1371     /* Don't do anything else if this is a recursive call */
1372     if (canvas->in_repick) return retval;
1374     /* LeaveNotify means that there is no current item, so we don't look for one */
1375     if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1376         /* these fields don't have the same offsets in both types of events */
1378         if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1379             x = canvas->pick_event.crossing.x;
1380             y = canvas->pick_event.crossing.y;
1381         } else {
1382             x = canvas->pick_event.motion.x;
1383             y = canvas->pick_event.motion.y;
1384         }
1386         /* world coords */
1387         x += canvas->x0;
1388         y += canvas->y0;
1390         /* find the closest item */
1391         if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1392             sp_canvas_item_invoke_point (canvas->root, NR::Point(x, y), &canvas->new_current_item);
1393         } else {
1394             canvas->new_current_item = NULL;
1395         }
1396     } else {
1397         canvas->new_current_item = NULL;
1398     }
1400     if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1401         return retval; /* current item did not change */
1402     }
1404     /* Synthesize events for old and new current items */
1406     if ((canvas->new_current_item != canvas->current_item)
1407         && (canvas->current_item != NULL)
1408         && !canvas->left_grabbed_item) {
1409         GdkEvent new_event;
1410         SPCanvasItem *item;
1412         item = canvas->current_item;
1414         new_event = canvas->pick_event;
1415         new_event.type = GDK_LEAVE_NOTIFY;
1417         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1418         new_event.crossing.subwindow = NULL;
1419         canvas->in_repick = TRUE;
1420         retval = emit_event (canvas, &new_event);
1421         canvas->in_repick = FALSE;
1422     }
1424     if (canvas->gen_all_enter_events == false) {
1425         // new_current_item may have been set to NULL during the call to
1426         // emit_event() above
1427         if ((canvas->new_current_item != canvas->current_item) && button_down) {
1428             canvas->left_grabbed_item = TRUE;
1429             return retval;
1430         }
1431     }
1433     /* Handle the rest of cases */
1435     canvas->left_grabbed_item = FALSE;
1436     canvas->current_item = canvas->new_current_item;
1438     if (canvas->current_item != NULL) {
1439         GdkEvent new_event;
1441         new_event = canvas->pick_event;
1442         new_event.type = GDK_ENTER_NOTIFY;
1443         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1444         new_event.crossing.subwindow = NULL;
1445         retval = emit_event (canvas, &new_event);
1446     }
1448     return retval;
1451 /**
1452  * Button event handler for the canvas.
1453  */
1454 static gint
1455 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1457     SPCanvas *canvas = SP_CANVAS (widget);
1459     int retval = FALSE;
1461     /* dispatch normally regardless of the event's window if an item has
1462        has a pointer grab in effect */
1463     if (!canvas->grabbed_item &&
1464         event->window != SP_CANVAS_WINDOW (canvas))
1465         return retval;
1467     int mask;
1468     switch (event->button) {
1469     case 1:
1470         mask = GDK_BUTTON1_MASK;
1471         break;
1472     case 2:
1473         mask = GDK_BUTTON2_MASK;
1474         break;
1475     case 3:
1476         mask = GDK_BUTTON3_MASK;
1477         break;
1478     case 4:
1479         mask = GDK_BUTTON4_MASK;
1480         break;
1481     case 5:
1482         mask = GDK_BUTTON5_MASK;
1483         break;
1484     default:
1485         mask = 0;
1486     }
1488     switch (event->type) {
1489     case GDK_BUTTON_PRESS:
1490     case GDK_2BUTTON_PRESS:
1491     case GDK_3BUTTON_PRESS:
1492         /* Pick the current item as if the button were not pressed, and
1493          * then process the event.
1494          */
1495         canvas->state = event->state;
1496         pick_current_item (canvas, (GdkEvent *) event);
1497         canvas->state ^= mask;
1498         retval = emit_event (canvas, (GdkEvent *) event);
1499         break;
1501     case GDK_BUTTON_RELEASE:
1502         /* Process the event as if the button were pressed, then repick
1503          * after the button has been released
1504          */
1505         canvas->state = event->state;
1506         retval = emit_event (canvas, (GdkEvent *) event);
1507         event->state ^= mask;
1508         canvas->state = event->state;
1509         pick_current_item (canvas, (GdkEvent *) event);
1510         event->state ^= mask;
1511         break;
1513     default:
1514         g_assert_not_reached ();
1515     }
1517     return retval;
1520 /**
1521  * Scroll event handler for the canvas.
1522  *
1523  * \todo FIXME: generate motion events to re-select items.
1524  */
1525 static gint
1526 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1528     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1531 /**
1532  * Motion event handler for the canvas.
1533  */
1534 static int
1535 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1537     SPCanvas *canvas = SP_CANVAS (widget);
1539     if (event->window != SP_CANVAS_WINDOW (canvas))
1540         return FALSE;
1542     if (canvas->grabbed_event_mask & GDK_POINTER_MOTION_HINT_MASK) {
1543         gint x, y;
1544         gdk_window_get_pointer (widget->window, &x, &y, NULL);
1545         event->x = x;
1546         event->y = y;
1547     }
1549     canvas->state = event->state;
1550     pick_current_item (canvas, (GdkEvent *) event);
1552     return emit_event (canvas, (GdkEvent *) event);
1555 static void
1556 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)
1558     GtkWidget *widget = GTK_WIDGET (canvas);
1560     SPCanvasBuf buf;
1561     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1562         buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1563     } else {
1564         buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1565     }
1567     // Mark the region clean
1568     sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1570     buf.buf_rowstride = sw * 3; // CAIRO FIXME: for cairo output, the buffer must be RGB unpacked, i.e. sw * 4
1571     buf.rect.x0 = x0;
1572     buf.rect.y0 = y0;
1573     buf.rect.x1 = x1;
1574     buf.rect.y1 = y1;
1575     buf.visible_rect.x0 = draw_x1;
1576     buf.visible_rect.y0 = draw_y1;
1577     buf.visible_rect.x1 = draw_x2;
1578     buf.visible_rect.y1 = draw_y2;
1579     GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1580     buf.bg_color = (((color->red & 0xff00) << 8)
1581                     | (color->green & 0xff00)
1582                     | (color->blue >> 8));
1583     buf.is_empty = true;
1585     if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1586         SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1587     }
1589 #if ENABLE_LCMS
1590     cmsHTRANSFORM transf = 0;
1591     long long int fromDisplay = prefs_get_int_attribute_limited( "options.displayprofile", "from_display", 0, 0, 1 );
1592     if ( fromDisplay ) {
1593         transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" );
1594     } else {
1595         transf = Inkscape::colorprofile_get_display_transform();
1596     }
1597 #endif // ENABLE_LCMS
1599     if (buf.is_empty) {
1600 #if ENABLE_LCMS
1601         if ( transf && canvas->enable_cms_display_adj ) {
1602             cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1603         }
1604 #endif // ENABLE_LCMS
1605         gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1606         gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1607                             canvas->pixmap_gc,
1608                             TRUE,
1609                             x0 - canvas->x0, y0 - canvas->y0,
1610                             x1 - x0, y1 - y0);
1611     } else {
1612 /*
1613 // CAIRO FIXME: after SPCanvasBuf is made 32bpp throughout, this rgb_draw below can be replaced with the below.
1614 // Why this must not be done currently:
1615 // - all canvas items (handles, nodes etc) paint themselves assuming 24bpp
1616 // - cairo assumes bgra, but we have rgba, so r and b get swapped (until we paint all with cairo too)
1617 // - it does not seem to be any faster; in fact since with 32bpp, buf contains less pixels,
1618 // we need more bufs to paint a given area and as a result it's even a bit slower
1620     cairo_surface_t* cst = cairo_image_surface_create_for_data (
1621         buf.buf,
1622         CAIRO_FORMAT_RGB24,  // unpacked, i.e. 32 bits! one byte is unused
1623         x1 - x0, y1 - y0,
1624         buf.buf_rowstride
1625         );
1626         cairo_t *ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1627         cairo_set_source_surface (ct, cst, x0 - canvas->x0, y0 - canvas->y0);
1628         cairo_paint (ct);
1629     cairo_destroy (ct);
1630     cairo_surface_finish (cst);
1631     cairo_surface_destroy (cst);
1632 */
1634 #if ENABLE_LCMS
1635         if ( transf && canvas->enable_cms_display_adj ) {
1636             for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1637                 guchar* p = buf.buf + (sw * 3) * yy;
1638                 cmsDoTransform( transf, p, p, (x1 - x0) );
1639             }
1640         }
1641 #endif // ENABLE_LCMS
1643         gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1644                                       canvas->pixmap_gc,
1645                                       x0 - canvas->x0, y0 - canvas->y0,
1646                                       x1 - x0, y1 - y0,
1647                                       GDK_RGB_DITHER_MAX,
1648                                       buf.buf,
1649                                       sw * 3,
1650                                       x0 - canvas->x0, y0 - canvas->y0);
1651     }
1653     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1654         nr_pixelstore_256K_free (buf.buf);
1655     } else {
1656         nr_pixelstore_1M_free (buf.buf);
1657     }
1660 struct PaintRectSetup {
1661     SPCanvas* canvas;
1662     NRRectL big_rect;
1663     GTimeVal start_time;
1664     int max_pixels;
1665     NR::Point mouse_loc;
1666 };
1668 /**
1669  * Paint the given rect, recursively subdividing the region until it is the size of a single
1670  * buffer.
1671  *
1672  * @return true if the drawing completes
1673  */
1674 static int
1675 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1677     GTimeVal now;
1678     g_get_current_time (&now);
1680     glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1681         + (now.tv_usec - setup->start_time.tv_usec);
1683     // Allow only very fast buffers to be run together;
1684     // as soon as the total redraw time exceeds 1ms, cancel;
1685     // this returns control to the idle loop and allows Inkscape to process user input
1686     // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1687     // it will get back and finish painting what remains to paint.
1688     if (elapsed > 1000) {
1690         // Interrupting redraw isn't always good.
1691         // For example, when you drag one node of a big path, only the buffer containing
1692         // the mouse cursor will be redrawn again and again, and the rest of the path
1693         // will remain stale because Inkscape never has enough idle time to redraw all
1694         // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1695         // If this limit is set, and if we have aborted redraw more times than is allowed,
1696         // interrupting is blocked and we're forced to redraw full screen once
1697         // (after which we can again interrupt forced_redraw_limit times).
1698         if (setup->canvas->forced_redraw_limit < 0 ||
1699             setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1701             if (setup->canvas->forced_redraw_limit != -1) {
1702                 setup->canvas->forced_redraw_count++;
1703             }
1705             return false;
1706         }
1707     }
1709     // Find the optimal buffer dimensions
1710     int bw = this_rect.x1 - this_rect.x0;
1711     int bh = this_rect.y1 - this_rect.y0;
1712     if ((bw < 1) || (bh < 1))
1713         return 0;
1715     if (bw * bh < setup->max_pixels) {
1716         // We are small enough
1717         sp_canvas_paint_single_buffer (setup->canvas,
1718                                        this_rect.x0, this_rect.y0,
1719                                        this_rect.x1, this_rect.y1,
1720                                        setup->big_rect.x0, setup->big_rect.y0,
1721                                        setup->big_rect.x1, setup->big_rect.y1, bw);
1722         return 1;
1723     }
1725     NRRectL lo = this_rect;
1726     NRRectL hi = this_rect;
1728 /*
1729 This test determines the redraw strategy:
1731 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1732 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1733 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1734 and seems to be faster for drawings with many smaller objects at zoom-out.
1736 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1737 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1738 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1739 faster.
1741 The default for now is the strips mode.
1742 */
1743     if (bw < bh || bh < 2 * TILE_SIZE) {
1744         // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1745         int mid = ((long long) this_rect.x0 + (long long) this_rect.x1) / 2;
1746         // Make sure that mid lies on a tile boundary
1747         mid = (mid / TILE_SIZE) * TILE_SIZE;
1749         lo.x1 = mid;
1750         hi.x0 = mid;
1752         if (setup->mouse_loc[NR::X] < mid) {
1753             // Always paint towards the mouse first
1754             return sp_canvas_paint_rect_internal(setup, lo)
1755                 && sp_canvas_paint_rect_internal(setup, hi);
1756         } else {
1757             return sp_canvas_paint_rect_internal(setup, hi)
1758                 && sp_canvas_paint_rect_internal(setup, lo);
1759         }
1760     } else {
1761         // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1762         int mid = ((long long) this_rect.y0 + (long long) this_rect.y1) / 2;
1763         // Make sure that mid lies on a tile boundary
1764         mid = (mid / TILE_SIZE) * TILE_SIZE;
1766         lo.y1 = mid;
1767         hi.y0 = mid;
1769         if (setup->mouse_loc[NR::Y] < mid) {
1770             // Always paint towards the mouse first
1771             return sp_canvas_paint_rect_internal(setup, lo)
1772                 && sp_canvas_paint_rect_internal(setup, hi);
1773         } else {
1774             return sp_canvas_paint_rect_internal(setup, hi)
1775                 && sp_canvas_paint_rect_internal(setup, lo);
1776         }
1777     }
1781 /**
1782  * Helper that draws a specific rectangular part of the canvas.
1783  *
1784  * @return true if the rectangle painting succeeds.
1785  */
1786 static bool
1787 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1789     g_return_val_if_fail (!canvas->need_update, false);
1791     NRRectL rect;
1792     rect.x0 = xx0;
1793     rect.x1 = xx1;
1794     rect.y0 = yy0;
1795     rect.y1 = yy1;
1797     // Clip rect-to-draw by the current visible area
1798     rect.x0 = MAX (rect.x0, canvas->x0);
1799     rect.y0 = MAX (rect.y0, canvas->y0);
1800     rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1801     rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1803 #ifdef DEBUG_REDRAW
1804     // paint the area to redraw yellow
1805     gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1806     gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1807                         canvas->pixmap_gc,
1808                         TRUE,
1809                         rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1810                         rect.x1 - rect.x0, rect.y1 - rect.y0);
1811 #endif
1813     PaintRectSetup setup;
1815     setup.canvas = canvas;
1816     setup.big_rect = rect;
1818     // Save the mouse location
1819     gint x, y;
1820     gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1821     setup.mouse_loc = sp_canvas_window_to_world (canvas, NR::Point(x,y));
1823     // CAIRO FIXME: the sw/sh calculations below all assume 24bpp, need fixing for 32bpp
1824     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1825         // use 256K as a compromise to not slow down gradients
1826         // 256K is the cached buffer and we need 3 channels
1827         setup.max_pixels = 87381; // 256K/3
1828     } else {
1829         // paths only, so 1M works faster
1830         // 1M is the cached buffer and we need 3 channels
1831         setup.max_pixels = 349525;
1832     }
1834     // Start the clock
1835     g_get_current_time(&(setup.start_time));
1837     // Go
1838     return sp_canvas_paint_rect_internal(&setup, rect);
1841 /**
1842  * Force a full redraw after a specified number of interrupted redraws
1843  */
1844 void
1845 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1846   g_return_if_fail(canvas != NULL);
1848   canvas->forced_redraw_limit = count;
1849   canvas->forced_redraw_count = 0;
1852 /**
1853  * End forced full redraw requests
1854  */
1855 void
1856 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1857   g_return_if_fail(canvas != NULL);
1859   canvas->forced_redraw_limit = -1;
1862 /**
1863  * The canvas widget's expose callback.
1864  */
1865 static gint
1866 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1868     SPCanvas *canvas = SP_CANVAS (widget);
1870     if (!GTK_WIDGET_DRAWABLE (widget) ||
1871         (event->window != SP_CANVAS_WINDOW (canvas)))
1872         return FALSE;
1874     int n_rects;
1875     GdkRectangle *rects;
1876     gdk_region_get_rectangles (event->region, &rects, &n_rects);
1878     for (int i = 0; i < n_rects; i++) {
1879         NRRectL rect;
1881         rect.x0 = rects[i].x + canvas->x0;
1882         rect.y0 = rects[i].y + canvas->y0;
1883         rect.x1 = rect.x0 + rects[i].width;
1884         rect.y1 = rect.y0 + rects[i].height;
1886         sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1887     }
1889     if (n_rects > 0)
1890         g_free (rects);
1892     return FALSE;
1895 /**
1896  * The canvas widget's keypress callback.
1897  */
1898 static gint
1899 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1901     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1904 /**
1905  * Crossing event handler for the canvas.
1906  */
1907 static gint
1908 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1910     SPCanvas *canvas = SP_CANVAS (widget);
1912     if (event->window != SP_CANVAS_WINDOW (canvas))
1913         return FALSE;
1915     canvas->state = event->state;
1916     return pick_current_item (canvas, (GdkEvent *) event);
1919 /**
1920  * Focus in handler for the canvas.
1921  */
1922 static gint
1923 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
1925     GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1927     SPCanvas *canvas = SP_CANVAS (widget);
1929     if (canvas->focused_item) {
1930         return emit_event (canvas, (GdkEvent *) event);
1931     } else {
1932         return FALSE;
1933     }
1936 /**
1937  * Focus out handler for the canvas.
1938  */
1939 static gint
1940 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
1942     GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1944     SPCanvas *canvas = SP_CANVAS (widget);
1946     if (canvas->focused_item)
1947         return emit_event (canvas, (GdkEvent *) event);
1948     else
1949         return FALSE;
1952 /**
1953  * Helper that repaints the areas in the canvas that need it.
1954  *
1955  * @return true if all the dirty parts have been redrawn
1956  */
1957 static int
1958 paint (SPCanvas *canvas)
1960     if (canvas->need_update) {
1961         sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
1962         canvas->need_update = FALSE;
1963     }
1965     if (!canvas->need_redraw)
1966         return TRUE;
1968     Gdk::Region to_paint;
1970     for (int j=canvas->tTop; j<canvas->tBottom; j++) {
1971         for (int i=canvas->tLeft; i<canvas->tRight; i++) {
1972             int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
1974             if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
1975                 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
1976                                    TILE_SIZE, TILE_SIZE));
1977             }
1979         }
1980     }
1982     if (!to_paint.empty()) {
1983         Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
1984         typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
1985         for (Iter i=rect.begin(); i != rect.end(); ++i) {
1986             int x0 = (*i).get_x();
1987             int y0 = (*i).get_y();
1988             int x1 = x0 + (*i).get_width();
1989             int y1 = y0 + (*i).get_height();
1990             if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
1991                 // Aborted
1992                 return FALSE;
1993             };
1994         }
1995     }
1997     canvas->need_redraw = FALSE;
1999     // we've had a full unaborted redraw, reset the full redraw counter
2000     if (canvas->forced_redraw_limit != -1) {
2001         canvas->forced_redraw_count = 0;
2002     }
2004     return TRUE;
2007 /**
2008  * Helper that invokes update, paint, and repick on canvas.
2009  */
2010 static int
2011 do_update (SPCanvas *canvas)
2013     if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
2014         return TRUE;
2016     /* Cause the update if necessary */
2017     if (canvas->need_update) {
2018         sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2019         canvas->need_update = FALSE;
2020     }
2022     /* Paint if able to */
2023     if (GTK_WIDGET_DRAWABLE (canvas)) {
2024             return paint (canvas);
2025     }
2027     /* Pick new current item */
2028     while (canvas->need_repick) {
2029         canvas->need_repick = FALSE;
2030         pick_current_item (canvas, &canvas->pick_event);
2031     }
2033     return TRUE;
2036 /**
2037  * Idle handler for the canvas that deals with pending updates and redraws.
2038  */
2039 static gint
2040 idle_handler (gpointer data)
2042     GDK_THREADS_ENTER ();
2044     SPCanvas *canvas = SP_CANVAS (data);
2046     int const ret = do_update (canvas);
2048     if (ret) {
2049         /* Reset idle id */
2050         canvas->idle_id = 0;
2051     }
2053     GDK_THREADS_LEAVE ();
2055     return !ret;
2058 /**
2059  * Convenience function to add an idle handler to a canvas.
2060  */
2061 static void
2062 add_idle (SPCanvas *canvas)
2064     if (canvas->idle_id != 0)
2065         return;
2067     canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2070 /**
2071  * Returns the root group of the specified canvas.
2072  */
2073 SPCanvasGroup *
2074 sp_canvas_root (SPCanvas *canvas)
2076     g_return_val_if_fail (canvas != NULL, NULL);
2077     g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2079     return SP_CANVAS_GROUP (canvas->root);
2082 /**
2083  * Scrolls canvas to specific position (cx and cy are measured in screen pixels)
2084  */
2085 void
2086 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2088     g_return_if_fail (canvas != NULL);
2089     g_return_if_fail (SP_IS_CANVAS (canvas));
2091     int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels)
2092     int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do!
2093     int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the 
2094     int dy = iy - canvas->y0; // canvas w.r.t its previous position
2096     canvas->dx0 = cx; // here the 'd' stands for double, not delta!
2097     canvas->dy0 = cy;
2098     canvas->x0 = ix;
2099     canvas->y0 = iy;
2101     sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2103     if (!clear) {
2104         // scrolling without zoom; redraw only the newly exposed areas
2105         if ((dx != 0) || (dy != 0)) {
2106             canvas->is_scrolling = is_scrolling;
2107             if (GTK_WIDGET_REALIZED (canvas)) {
2108                 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2109             }
2110         }
2111     } else {
2112         // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2113     }
2116 /**
2117  * Updates canvas if necessary.
2118  */
2119 void
2120 sp_canvas_update_now (SPCanvas *canvas)
2122     g_return_if_fail (canvas != NULL);
2123     g_return_if_fail (SP_IS_CANVAS (canvas));
2125     if (!(canvas->need_update ||
2126           canvas->need_redraw))
2127         return;
2129     do_update (canvas);
2132 /**
2133  * Update callback for canvas widget.
2134  */
2135 static void
2136 sp_canvas_request_update (SPCanvas *canvas)
2138     canvas->need_update = TRUE;
2139     add_idle (canvas);
2142 /**
2143  * Forces redraw of rectangular canvas area.
2144  */
2145 void
2146 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2148     NRRectL bbox;
2149     NRRectL visible;
2150     NRRectL clip;
2152     g_return_if_fail (canvas != NULL);
2153     g_return_if_fail (SP_IS_CANVAS (canvas));
2155     if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2156     if ((x0 >= x1) || (y0 >= y1)) return;
2158     bbox.x0 = x0;
2159     bbox.y0 = y0;
2160     bbox.x1 = x1;
2161     bbox.y1 = y1;
2163     visible.x0 = canvas->x0;
2164     visible.y0 = canvas->y0;
2165     visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2166     visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2168     nr_rect_l_intersect (&clip, &bbox, &visible);
2170     sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2171     add_idle (canvas);
2174 /**
2175  * Sets world coordinates from win and canvas.
2176  */
2177 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2179     g_return_if_fail (canvas != NULL);
2180     g_return_if_fail (SP_IS_CANVAS (canvas));
2182     if (worldx) *worldx = canvas->x0 + winx;
2183     if (worldy) *worldy = canvas->y0 + winy;
2186 /**
2187  * Sets win coordinates from world and canvas.
2188  */
2189 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2191     g_return_if_fail (canvas != NULL);
2192     g_return_if_fail (SP_IS_CANVAS (canvas));
2194     if (winx) *winx = worldx - canvas->x0;
2195     if (winy) *winy = worldy - canvas->y0;
2198 /**
2199  * Converts point from win to world coordinates.
2200  */
2201 NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win)
2203     g_assert (canvas != NULL);
2204     g_assert (SP_IS_CANVAS (canvas));
2206     return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2209 /**
2210  * Converts point from world to win coordinates.
2211  */
2212 NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world)
2214     g_assert (canvas != NULL);
2215     g_assert (SP_IS_CANVAS (canvas));
2217     return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2220 /**
2221  * Returns true if point given in world coordinates is inside window.
2222  */
2223 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world)
2225     g_assert( canvas != NULL );
2226     g_assert(SP_IS_CANVAS(canvas));
2228     using NR::X;
2229     using NR::Y;
2230     GtkWidget const &w = *GTK_WIDGET(canvas);
2231     return ( ( canvas->x0 <= world[X] )  &&
2232              ( canvas->y0 <= world[Y] )  &&
2233              ( world[X] < canvas->x0 + w.allocation.width )  &&
2234              ( world[Y] < canvas->y0 + w.allocation.height ) );
2237 /**
2238  * Return canvas window coordinates as NR::Rect.
2239  */
2240 NR::Rect SPCanvas::getViewbox() const
2242     GtkWidget const *w = GTK_WIDGET(this);
2243     return NR::Rect(NR::Point(dx0, dy0),
2244                     NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2247 /**
2248  * Return canvas window coordinates as IRect (a rectangle defined by integers).
2249  */
2250 NR::IRect SPCanvas::getViewboxIntegers() const
2252     GtkWidget const *w = GTK_WIDGET(this);
2253     return NR::IRect(NR::IPoint(x0, y0),
2254                     NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height));
2257 inline int sp_canvas_tile_floor(int x)
2259     return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2262 inline int sp_canvas_tile_ceil(int x)
2264     return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2267 /**
2268  * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2269  */
2270 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2272     if ( nl >= nr || nt >= nb ) {
2273         if ( canvas->tiles ) g_free(canvas->tiles);
2274         canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2275         canvas->tileH=canvas->tileV=0;
2276         canvas->tiles=NULL;
2277         return;
2278     }
2279     int tl=sp_canvas_tile_floor(nl);
2280     int tt=sp_canvas_tile_floor(nt);
2281     int tr=sp_canvas_tile_ceil(nr);
2282     int tb=sp_canvas_tile_ceil(nb);
2284     int nh = tr-tl, nv = tb-tt;
2285     uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2286     for (int i=tl; i<tr; i++) {
2287         for (int j=tt; j<tb; j++) {
2288             int ind = (i-tl) + (j-tt)*nh;
2289             if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2290                 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2291             } else {
2292                 ntiles[ind]=0; // newly exposed areas get 0
2293             }
2294         }
2295     }
2296     if ( canvas->tiles ) g_free(canvas->tiles);
2297     canvas->tiles=ntiles;
2298     canvas->tLeft=tl;
2299     canvas->tTop=tt;
2300     canvas->tRight=tr;
2301     canvas->tBottom=tb;
2302     canvas->tileH=nh;
2303     canvas->tileV=nv;
2306 /*
2307  * Helper that queues a canvas rectangle for redraw
2308  */
2309 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2310     canvas->need_redraw = TRUE;
2312     sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2315 /**
2316  * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2317  */
2318 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2320     if ( nl >= nr || nt >= nb ) {
2321         return;
2322     }
2323     int tl=sp_canvas_tile_floor(nl);
2324     int tt=sp_canvas_tile_floor(nt);
2325     int tr=sp_canvas_tile_ceil(nr);
2326     int tb=sp_canvas_tile_ceil(nb);
2327     if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2328     if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2329     if ( tr > canvas->tRight ) tr=canvas->tRight;
2330     if ( tt < canvas->tTop ) tt=canvas->tTop;
2331     if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2333     for (int i=tl; i<tr; i++) {
2334         for (int j=tt; j<tb; j++) {
2335             canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2336         }
2337     }
2341 /*
2342   Local Variables:
2343   mode:c++
2344   c-file-style:"stroustrup"
2345   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2346   indent-tabs-mode:nil
2347   fill-column:99
2348   End:
2349 */
2350 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :