Code

86b1e9eabbbb75580a0063238e705e55754a2aed
[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
43 // Define this to visualize the regions to be redrawn
44 //#define DEBUG_REDRAW 1;
46 // Tiles are a way to minimize the number of redraws, eliminating too small redraws.
47 // The canvas stores a 2D array of ints, each representing a TILE_SIZExTILE_SIZE pixels tile.
48 // If any part of it is dirtied, the entire tile is dirtied (its int is nonzero) and repainted.
49 #define TILE_SIZE 16
51 enum {
52     RENDERMODE_NORMAL,
53     RENDERMODE_NOAA,
54     RENDERMODE_OUTLINE
55 };
57 static gint const sp_canvas_update_priority = G_PRIORITY_HIGH_IDLE;
59 #define SP_CANVAS_WINDOW(c) (((GtkWidget *) (c))->window)
61 enum {
62     SP_CANVAS_ITEM_VISIBLE = 1 << 7,
63     SP_CANVAS_ITEM_NEED_UPDATE = 1 << 8,
64     SP_CANVAS_ITEM_NEED_AFFINE = 1 << 9
65 };
67 /**
68  * A group of Items.
69  */
70 struct SPCanvasGroup {
71     SPCanvasItem item;
73     GList *items, *last;
74 };
76 /**
77  * The SPCanvasGroup vtable.
78  */
79 struct SPCanvasGroupClass {
80     SPCanvasItemClass parent_class;
81 };
83 /**
84  * The SPCanvas vtable.
85  */
86 struct SPCanvasClass {
87     GtkWidgetClass parent_class;
88 };
90 static void group_add (SPCanvasGroup *group, SPCanvasItem *item);
91 static void group_remove (SPCanvasGroup *group, SPCanvasItem *item);
93 /* SPCanvasItem */
95 enum {ITEM_EVENT, ITEM_LAST_SIGNAL};
98 static void sp_canvas_request_update (SPCanvas *canvas);
100 static void sp_canvas_item_class_init (SPCanvasItemClass *klass);
101 static void sp_canvas_item_init (SPCanvasItem *item);
102 static void sp_canvas_item_dispose (GObject *object);
103 static void sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args);
105 static int emit_event (SPCanvas *canvas, GdkEvent *event);
107 static guint item_signals[ITEM_LAST_SIGNAL] = { 0 };
109 static GtkObjectClass *item_parent_class;
111 /**
112  * Registers the SPCanvasItem class with Glib and returns its type number.
113  */
114 GType
115 sp_canvas_item_get_type (void)
117     static GType type = 0;
118     if (!type) {
119         static GTypeInfo const info = {
120             sizeof (SPCanvasItemClass),
121             NULL, NULL,
122             (GClassInitFunc) sp_canvas_item_class_init,
123             NULL, NULL,
124             sizeof (SPCanvasItem),
125             0,
126             (GInstanceInitFunc) sp_canvas_item_init,
127             NULL
128         };
129         type = g_type_register_static (GTK_TYPE_OBJECT, "SPCanvasItem", &info, (GTypeFlags)0);
130     }
132     return type;
135 /**
136  * Initializes the SPCanvasItem vtable and the "event" signal.
137  */
138 static void
139 sp_canvas_item_class_init (SPCanvasItemClass *klass)
141     GObjectClass *object_class = (GObjectClass *) klass;
143     /* fixme: Derive from GObject */
144     item_parent_class = (GtkObjectClass*)gtk_type_class (GTK_TYPE_OBJECT);
146     item_signals[ITEM_EVENT] = g_signal_new ("event",
147                                              G_TYPE_FROM_CLASS (klass),
148                                              G_SIGNAL_RUN_LAST,
149                                              ((glong)((guint8*)&(klass->event) - (guint8*)klass)),
150                                              NULL, NULL,
151                                              sp_marshal_BOOLEAN__POINTER,
152                                              G_TYPE_BOOLEAN, 1,
153                                              GDK_TYPE_EVENT);
155     object_class->dispose = sp_canvas_item_dispose;
158 /**
159  * Callback for initialization of SPCanvasItem.
160  */
161 static void
162 sp_canvas_item_init (SPCanvasItem *item)
164     item->flags |= SP_CANVAS_ITEM_VISIBLE;
165     item->xform = NR::Matrix(NR::identity());
168 /**
169  * Constructs new SPCanvasItem on SPCanvasGroup.
170  */
171 SPCanvasItem *
172 sp_canvas_item_new (SPCanvasGroup *parent, GtkType type, gchar const *first_arg_name, ...)
174     va_list args;
176     g_return_val_if_fail (parent != NULL, NULL);
177     g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL);
178     g_return_val_if_fail (gtk_type_is_a (type, sp_canvas_item_get_type ()), NULL);
180     SPCanvasItem *item = SP_CANVAS_ITEM (gtk_type_new (type));
182     va_start (args, first_arg_name);
183     sp_canvas_item_construct (item, parent, first_arg_name, args);
184     va_end (args);
186     return item;
189 /**
190  * Sets up the newly created SPCanvasItem.
191  *
192  * We make it static for encapsulation reasons since it was nowhere used.
193  */
194 static void
195 sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args)
197     g_return_if_fail (SP_IS_CANVAS_GROUP (parent));
198     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
200     item->parent = SP_CANVAS_ITEM (parent);
201     item->canvas = item->parent->canvas;
203     g_object_set_valist (G_OBJECT (item), first_arg_name, args);
205     group_add (SP_CANVAS_GROUP (item->parent), item);
207     sp_canvas_item_request_update (item);
210 /**
211  * Helper function that requests redraw only if item's visible flag is set.
212  */
213 static void
214 redraw_if_visible (SPCanvasItem *item)
216     if (item->flags & SP_CANVAS_ITEM_VISIBLE) {
217         int x0 = (int)(item->x1);
218         int x1 = (int)(item->x2);
219         int y0 = (int)(item->y1);
220         int y1 = (int)(item->y2);
222         if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
223             sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
224         }
225     }
228 /**
229  * Callback that removes item from all referers and destroys it.
230  */
231 static void
232 sp_canvas_item_dispose (GObject *object)
234     SPCanvasItem *item = SP_CANVAS_ITEM (object);
236     // Hack: if this is a ctrlrect, move it to 0,0;
237     // this redraws only the stroke of the rect to be deleted,
238     // avoiding redraw of the entire area
239     if (SP_IS_CTRLRECT(item)) {
240         SP_CTRLRECT(object)->setRectangle(NR::Rect(NR::Point(0,0),NR::Point(0,0)));
241         SP_CTRLRECT(object)->update(item->xform, 0);
242     } else {
243         redraw_if_visible (item);
244     }
245     item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
247     if (item == item->canvas->current_item) {
248         item->canvas->current_item = NULL;
249         item->canvas->need_repick = TRUE;
250     }
252     if (item == item->canvas->new_current_item) {
253         item->canvas->new_current_item = NULL;
254         item->canvas->need_repick = TRUE;
255     }
257     if (item == item->canvas->grabbed_item) {
258         item->canvas->grabbed_item = NULL;
259         gdk_pointer_ungrab (GDK_CURRENT_TIME);
260     }
262     if (item == item->canvas->focused_item)
263         item->canvas->focused_item = NULL;
265     if (item->parent) {
266         group_remove (SP_CANVAS_GROUP (item->parent), item);
267     }
269     G_OBJECT_CLASS (item_parent_class)->dispose (object);
272 /**
273  * Helper function to update item and its children.
274  *
275  * NB! affine is parent2canvas.
276  */
277 static void
278 sp_canvas_item_invoke_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
280     /* Apply the child item's transform */
281     NR::Matrix child_affine = item->xform * affine;
283     /* apply object flags to child flags */
284     int child_flags = flags & ~SP_CANVAS_UPDATE_REQUESTED;
286     if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
287         child_flags |= SP_CANVAS_UPDATE_REQUESTED;
289     if (item->flags & SP_CANVAS_ITEM_NEED_AFFINE)
290         child_flags |= SP_CANVAS_UPDATE_AFFINE;
292     if (child_flags & (SP_CANVAS_UPDATE_REQUESTED | SP_CANVAS_UPDATE_AFFINE)) {
293         if (SP_CANVAS_ITEM_GET_CLASS (item)->update)
294             SP_CANVAS_ITEM_GET_CLASS (item)->update (item, child_affine, child_flags);
295     }
297     GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_UPDATE);
298     GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_AFFINE);
301 /**
302  * Helper function to invoke the point method of the item.
303  *
304  * The argument x, y should be in the parent's item-relative coordinate
305  * system.  This routine applies the inverse of the item's transform,
306  * maintaining the affine invariant.
307  */
308 static double
309 sp_canvas_item_invoke_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
311     if (SP_CANVAS_ITEM_GET_CLASS (item)->point)
312         return SP_CANVAS_ITEM_GET_CLASS (item)->point (item, p, actual_item);
314     return NR_HUGE;
317 /**
318  * Makes the item's affine transformation matrix be equal to the specified
319  * matrix.
320  *
321  * @item: A canvas item.
322  * @affine: An affine transformation matrix.
323  */
324 void
325 sp_canvas_item_affine_absolute (SPCanvasItem *item, NR::Matrix const &affine)
327     item->xform = affine;
329     if (!(item->flags & SP_CANVAS_ITEM_NEED_AFFINE)) {
330         item->flags |= SP_CANVAS_ITEM_NEED_AFFINE;
331         if (item->parent != NULL) {
332             sp_canvas_item_request_update (item->parent);
333         } else {
334             sp_canvas_request_update (item->canvas);
335         }
336     }
338     item->canvas->need_repick = TRUE;
341 /**
342  * Convenience function to reorder items in a group's child list.
343  *
344  * This puts the specified link after the "before" link.
345  */
346 static void
347 put_item_after (GList *link, GList *before)
349     if (link == before)
350         return;
352     SPCanvasGroup *parent = SP_CANVAS_GROUP (SP_CANVAS_ITEM (link->data)->parent);
354     if (before == NULL) {
355         if (link == parent->items) return;
357         link->prev->next = link->next;
359         if (link->next) {
360             link->next->prev = link->prev;
361         } else {
362             parent->last = link->prev;
363         }
365         link->prev = before;
366         link->next = parent->items;
367         link->next->prev = link;
368         parent->items = link;
369     } else {
370         if ((link == parent->last) && (before == parent->last->prev))
371             return;
373         if (link->next)
374             link->next->prev = link->prev;
376         if (link->prev)
377             link->prev->next = link->next;
378         else {
379             parent->items = link->next;
380             parent->items->prev = NULL;
381         }
383         link->prev = before;
384         link->next = before->next;
386         link->prev->next = link;
388         if (link->next)
389             link->next->prev = link;
390         else
391             parent->last = link;
392     }
396 /**
397  * Raises the item in its parent's stack by the specified number of positions.
398  *
399  * \param item A canvas item.
400  * \param positions Number of steps to raise the item.
401  *
402  * If the number of positions is greater than the distance to the top of the
403  * stack, then the item is put at the top.
404  */
405 void
406 sp_canvas_item_raise (SPCanvasItem *item, int positions)
408     g_return_if_fail (item != NULL);
409     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
410     g_return_if_fail (positions >= 0);
412     if (!item->parent || positions == 0)
413         return;
415     SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
416     GList *link = g_list_find (parent->items, item);
417     g_assert (link != NULL);
419     GList *before;
420     for (before = link; positions && before; positions--)
421         before = before->next;
423     if (!before)
424         before = parent->last;
426     put_item_after (link, before);
428     redraw_if_visible (item);
429     item->canvas->need_repick = TRUE;
433 /**
434  * Lowers the item in its parent's stack by the specified number of positions.
435  *
436  * \param item A canvas item.
437  * \param positions Number of steps to lower the item.
438  *
439  * If the number of positions is greater than the distance to the bottom of the
440  * stack, then the item is put at the bottom.
441  **/
442 void
443 sp_canvas_item_lower (SPCanvasItem *item, int positions)
445     g_return_if_fail (item != NULL);
446     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
447     g_return_if_fail (positions >= 1);
449     if (!item->parent || positions == 0)
450         return;
452     SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
453     GList *link = g_list_find (parent->items, item);
454     g_assert (link != NULL);
456     GList *before;
457     if (link->prev)
458         for (before = link->prev; positions && before; positions--)
459             before = before->prev;
460     else
461         before = NULL;
463     put_item_after (link, before);
465     redraw_if_visible (item);
466     item->canvas->need_repick = TRUE;
469 /**
470  * Sets visible flag on item and requests a redraw.
471  */
472 void
473 sp_canvas_item_show (SPCanvasItem *item)
475     g_return_if_fail (item != NULL);
476     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
478     if (item->flags & SP_CANVAS_ITEM_VISIBLE)
479         return;
481     item->flags |= SP_CANVAS_ITEM_VISIBLE;
483     int x0 = (int)(item->x1);
484     int x1 = (int)(item->x2);
485     int y0 = (int)(item->y1);
486     int y1 = (int)(item->y2);
488     if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
489         sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
490         item->canvas->need_repick = TRUE;
491     }
494 /**
495  * Clears visible flag on item and requests a redraw.
496  */
497 void
498 sp_canvas_item_hide (SPCanvasItem *item)
500     g_return_if_fail (item != NULL);
501     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
503     if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
504         return;
506     item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
508     int x0 = (int)(item->x1);
509     int x1 = (int)(item->x2);
510     int y0 = (int)(item->y1);
511     int y1 = (int)(item->y2);
513     if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
514         sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
515         item->canvas->need_repick = TRUE;
516     }
519 /**
520  * Grab item under cursor.
521  *
522  * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE
523  */
524 int
525 sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
527     g_return_val_if_fail (item != NULL, -1);
528     g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1);
529     g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1);
531     if (item->canvas->grabbed_item)
532         return -1;
534     if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
535         return -1;
537     /* fixme: Top hack (Lauris) */
538     /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
539     /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
540     gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
541                       (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
542                       NULL, cursor, etime);
544     item->canvas->grabbed_item = item;
545     item->canvas->grabbed_event_mask = event_mask;
546     item->canvas->current_item = item; /* So that events go to the grabbed item */
548     return 0;
551 /**
552  * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
553  * mouse.
554  *
555  * \param item A canvas item that holds a grab.
556  * \param etime The timestamp for ungrabbing the mouse.
557  */
558 void
559 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
561     g_return_if_fail (item != NULL);
562     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
564     if (item->canvas->grabbed_item != item)
565         return;
567     item->canvas->grabbed_item = NULL;
569     gdk_pointer_ungrab (etime);
572 /**
573  * Returns the product of all transformation matrices from the root item down
574  * to the item.
575  */
576 NR::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
578     g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
580     NR::Matrix affine = NR::identity();
582     while (item) {
583         affine *= item->xform;
584         item = item->parent;
585     }
586     return affine;
589 /**
590  * Helper that returns true iff item is descendant of parent.
591  */
592 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
594     while (item) {
595         if (item == parent)
596             return true;
597         item = item->parent;
598     }
600     return false;
603 /**
604  * Focus canvas, and item under cursor if it is not already focussed.
605  */
606 void
607 sp_canvas_item_grab_focus (SPCanvasItem *item)
609     g_return_if_fail (item != NULL);
610     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
611     g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
613     SPCanvasItem *focused_item = item->canvas->focused_item;
615     if (focused_item) {
616         GdkEvent ev;
617         ev.focus_change.type = GDK_FOCUS_CHANGE;
618         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
619         ev.focus_change.send_event = FALSE;
620         ev.focus_change.in = FALSE;
622         emit_event (item->canvas, &ev);
623     }
625     item->canvas->focused_item = item;
626     gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
628     if (focused_item) {
629         GdkEvent ev;
630         ev.focus_change.type = GDK_FOCUS_CHANGE;
631         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
632         ev.focus_change.send_event = FALSE;
633         ev.focus_change.in = TRUE;
635         emit_event (item->canvas, &ev);
636     }
639 /**
640  * Requests that the canvas queue an update for the specified item.
641  *
642  * To be used only by item implementations.
643  */
644 void
645 sp_canvas_item_request_update (SPCanvasItem *item)
647     if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
648         return;
650     item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
652     if (item->parent != NULL) {
653         /* Recurse up the tree */
654         sp_canvas_item_request_update (item->parent);
655     } else {
656         /* Have reached the top of the tree, make sure the update call gets scheduled. */
657         sp_canvas_request_update (item->canvas);
658     }
661 /**
662  * Returns position of item in group.
663  */
664 gint sp_canvas_item_order (SPCanvasItem * item)
666     return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
669 /* SPCanvasGroup */
671 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
672 static void sp_canvas_group_init (SPCanvasGroup *group);
673 static void sp_canvas_group_destroy (GtkObject *object);
675 static void sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
676 static double sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item);
677 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
679 static SPCanvasItemClass *group_parent_class;
681 /**
682  * Registers SPCanvasGroup class with Gtk and returns its type number.
683  */
684 GtkType
685 sp_canvas_group_get_type (void)
687     static GtkType group_type = 0;
689     if (!group_type) {
690         static GtkTypeInfo const group_info = {
691             "SPCanvasGroup",
692             sizeof (SPCanvasGroup),
693             sizeof (SPCanvasGroupClass),
694             (GtkClassInitFunc) sp_canvas_group_class_init,
695             (GtkObjectInitFunc) sp_canvas_group_init,
696             NULL, NULL, NULL
697         };
699         group_type = gtk_type_unique (sp_canvas_item_get_type (), &group_info);
700     }
702     return group_type;
705 /**
706  * Class initialization function for SPCanvasGroupClass
707  */
708 static void
709 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
711     GtkObjectClass *object_class = (GtkObjectClass *) klass;
712     SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
714     group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
716     object_class->destroy = sp_canvas_group_destroy;
718     item_class->update = sp_canvas_group_update;
719     item_class->render = sp_canvas_group_render;
720     item_class->point = sp_canvas_group_point;
723 /**
724  * Callback. Empty.
725  */
726 static void
727 sp_canvas_group_init (SPCanvasGroup */*group*/)
729     /* Nothing here */
732 /**
733  * Callback that destroys all items in group and calls group's virtual
734  * destroy() function.
735  */
736 static void
737 sp_canvas_group_destroy (GtkObject *object)
739     g_return_if_fail (object != NULL);
740     g_return_if_fail (SP_IS_CANVAS_GROUP (object));
742     SPCanvasGroup const *group = SP_CANVAS_GROUP (object);
744     GList *list = group->items;
745     while (list) {
746         SPCanvasItem *child = (SPCanvasItem *)list->data;
747         list = list->next;
749         gtk_object_destroy (GTK_OBJECT (child));
750     }
752     if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
753         (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
756 /**
757  * Update handler for canvas groups
758  */
759 static void
760 sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
762     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
763     NR::ConvexHull corners(NR::Point(0, 0));
764     bool empty=true;
766     for (GList *list = group->items; list; list = list->next) {
767         SPCanvasItem *i = (SPCanvasItem *)list->data;
769         sp_canvas_item_invoke_update (i, affine, flags);
771         if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
772             if (empty) {
773                 corners = NR::ConvexHull(NR::Point(i->x1, i->y1));
774                 empty = false;
775             } else {
776                 corners.add(NR::Point(i->x1, i->y1));
777             }
778             corners.add(NR::Point(i->x2, i->y2));
779         }
780     }
782     NR::Maybe<NR::Rect> const bounds = corners.bounds();
783     if (bounds) {
784         item->x1 = bounds->min()[NR::X];
785         item->y1 = bounds->min()[NR::Y];
786         item->x2 = bounds->max()[NR::X];
787         item->y2 = bounds->max()[NR::Y];
788     } else {
789         // FIXME ?
790         item->x1 = item->x2 = item->y1 = item->y2 = 0;
791     }
794 /**
795  * Point handler for canvas groups.
796  */
797 static double
798 sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
800     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
801     double const x = p[NR::X];
802     double const y = p[NR::Y];
803     int x1 = (int)(x - item->canvas->close_enough);
804     int y1 = (int)(y - item->canvas->close_enough);
805     int x2 = (int)(x + item->canvas->close_enough);
806     int y2 = (int)(y + item->canvas->close_enough);
808     double best = 0.0;
809     *actual_item = NULL;
811     double dist = 0.0;
813     for (GList *list = group->items; list; list = list->next) {
814         SPCanvasItem *child = (SPCanvasItem *)list->data;
816         if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
817             SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
819             int has_point;
820             if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
821                 dist = sp_canvas_item_invoke_point (child, p, &point_item);
822                 has_point = TRUE;
823             } else
824                 has_point = FALSE;
826             if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
827                 best = dist;
828                 *actual_item = point_item;
829             }
830         }
831     }
833     return best;
836 /**
837  * Renders all visible canvas group items in buf rectangle.
838  */
839 static void
840 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
842     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
844     for (GList *list = group->items; list; list = list->next) {
845         SPCanvasItem *child = (SPCanvasItem *)list->data;
846         if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
847             if ((child->x1 < buf->rect.x1) &&
848                 (child->y1 < buf->rect.y1) &&
849                 (child->x2 > buf->rect.x0) &&
850                 (child->y2 > buf->rect.y0)) {
851                 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
852                     SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
853             }
854         }
855     }
858 /**
859  * Adds an item to a canvas group.
860  */
861 static void
862 group_add (SPCanvasGroup *group, SPCanvasItem *item)
864     gtk_object_ref (GTK_OBJECT (item));
865     gtk_object_sink (GTK_OBJECT (item));
867     if (!group->items) {
868         group->items = g_list_append (group->items, item);
869         group->last = group->items;
870     } else {
871         group->last = g_list_append (group->last, item)->next;
872     }
874     sp_canvas_item_request_update (item);
877 /**
878  * Removes an item from a canvas group
879  */
880 static void
881 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
883     g_return_if_fail (group != NULL);
884     g_return_if_fail (SP_IS_CANVAS_GROUP (group));
885     g_return_if_fail (item != NULL);
887     for (GList *children = group->items; children; children = children->next) {
888         if (children->data == item) {
890             /* Unparent the child */
892             item->parent = NULL;
893             gtk_object_unref (GTK_OBJECT (item));
895             /* Remove it from the list */
897             if (children == group->last) group->last = children->prev;
899             group->items = g_list_remove_link (group->items, children);
900             g_list_free (children);
901             break;
902         }
903     }
906 /* SPCanvas */
908 static void sp_canvas_class_init (SPCanvasClass *klass);
909 static void sp_canvas_init (SPCanvas *canvas);
910 static void sp_canvas_destroy (GtkObject *object);
912 static void sp_canvas_realize (GtkWidget *widget);
913 static void sp_canvas_unrealize (GtkWidget *widget);
915 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
916 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
918 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
919 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
920 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
921 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
922 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
923 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
924 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
925 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
927 static GtkWidgetClass *canvas_parent_class;
929 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb);
930 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb);
931 static void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val);
932 static int do_update (SPCanvas *canvas);
934 /**
935  * Registers the SPCanvas class if necessary, and returns the type ID
936  * associated to it.
937  *
938  * \return The type ID of the SPCanvas class.
939  **/
940 GtkType
941 sp_canvas_get_type (void)
943     static GtkType canvas_type = 0;
945     if (!canvas_type) {
946         static GtkTypeInfo const canvas_info = {
947             "SPCanvas",
948             sizeof (SPCanvas),
949             sizeof (SPCanvasClass),
950             (GtkClassInitFunc) sp_canvas_class_init,
951             (GtkObjectInitFunc) sp_canvas_init,
952             NULL, NULL, NULL
953         };
955         canvas_type = gtk_type_unique (GTK_TYPE_WIDGET, &canvas_info);
956     }
958     return canvas_type;
961 /**
962  * Class initialization function for SPCanvasClass.
963  */
964 static void
965 sp_canvas_class_init (SPCanvasClass *klass)
967     GtkObjectClass *object_class = (GtkObjectClass *) klass;
968     GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
970     canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
972     object_class->destroy = sp_canvas_destroy;
974     widget_class->realize = sp_canvas_realize;
975     widget_class->unrealize = sp_canvas_unrealize;
976     widget_class->size_request = sp_canvas_size_request;
977     widget_class->size_allocate = sp_canvas_size_allocate;
978     widget_class->button_press_event = sp_canvas_button;
979     widget_class->button_release_event = sp_canvas_button;
980     widget_class->motion_notify_event = sp_canvas_motion;
981     widget_class->scroll_event = sp_canvas_scroll;
982     widget_class->expose_event = sp_canvas_expose;
983     widget_class->key_press_event = sp_canvas_key;
984     widget_class->key_release_event = sp_canvas_key;
985     widget_class->enter_notify_event = sp_canvas_crossing;
986     widget_class->leave_notify_event = sp_canvas_crossing;
987     widget_class->focus_in_event = sp_canvas_focus_in;
988     widget_class->focus_out_event = sp_canvas_focus_out;
991 /**
992  * Callback: object initialization for SPCanvas.
993  */
994 static void
995 sp_canvas_init (SPCanvas *canvas)
997     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
998     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
999     GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
1001     canvas->pick_event.type = GDK_LEAVE_NOTIFY;
1002     canvas->pick_event.crossing.x = 0;
1003     canvas->pick_event.crossing.y = 0;
1005     /* Create the root item as a special case */
1006     canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
1007     canvas->root->canvas = canvas;
1009     gtk_object_ref (GTK_OBJECT (canvas->root));
1010     gtk_object_sink (GTK_OBJECT (canvas->root));
1012     canvas->need_repick = TRUE;
1014     // See comment at in sp-canvas.h.
1015     canvas->gen_all_enter_events = false;
1017     canvas->tiles=NULL;
1018     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1019     canvas->tileH=canvas->tileV=0;
1021     canvas->forced_redraw_count = 0;
1022     canvas->forced_redraw_limit = -1;
1024 #if ENABLE_LCMS
1025     canvas->enable_cms_display_adj = false;
1026     canvas->cms_key = new Glib::ustring("");
1027 #endif // ENABLE_LCMS
1029     canvas->is_scrolling = false;
1033 /**
1034  * Convenience function to remove the idle handler of a canvas.
1035  */
1036 static void
1037 remove_idle (SPCanvas *canvas)
1039     if (canvas->idle_id) {
1040         gtk_idle_remove (canvas->idle_id);
1041         canvas->idle_id = 0;
1042     }
1045 /*
1046  * Removes the transient state of the canvas (idle handler, grabs).
1047  */
1048 static void
1049 shutdown_transients (SPCanvas *canvas)
1051     /* We turn off the need_redraw flag, since if the canvas is mapped again
1052      * it will request a redraw anyways.  We do not turn off the need_update
1053      * flag, though, because updates are not queued when the canvas remaps
1054      * itself.
1055      */
1056     if (canvas->need_redraw) {
1057         canvas->need_redraw = FALSE;
1058     }
1059     if ( canvas->tiles ) g_free(canvas->tiles);
1060     canvas->tiles=NULL;
1061     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1062     canvas->tileH=canvas->tileV=0;
1064     if (canvas->grabbed_item) {
1065         canvas->grabbed_item = NULL;
1066         gdk_pointer_ungrab (GDK_CURRENT_TIME);
1067     }
1069     remove_idle (canvas);
1072 /**
1073  * Destroy handler for SPCanvas.
1074  */
1075 static void
1076 sp_canvas_destroy (GtkObject *object)
1078     SPCanvas *canvas = SP_CANVAS (object);
1080     if (canvas->root) {
1081         gtk_object_unref (GTK_OBJECT (canvas->root));
1082         canvas->root = NULL;
1083     }
1085     shutdown_transients (canvas);
1087     if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1088         (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1091 /**
1092  * Returns new canvas as widget.
1093  */
1094 GtkWidget *
1095 sp_canvas_new_aa (void)
1097     SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1099     return (GtkWidget *) canvas;
1102 /**
1103  * The canvas widget's realize callback.
1104  */
1105 static void
1106 sp_canvas_realize (GtkWidget *widget)
1108     SPCanvas *canvas = SP_CANVAS (widget);
1110     GdkWindowAttr attributes;
1111     attributes.window_type = GDK_WINDOW_CHILD;
1112     attributes.x = widget->allocation.x;
1113     attributes.y = widget->allocation.y;
1114     attributes.width = widget->allocation.width;
1115     attributes.height = widget->allocation.height;
1116     attributes.wclass = GDK_INPUT_OUTPUT;
1117     attributes.visual = gdk_rgb_get_visual ();
1118     attributes.colormap = gdk_rgb_get_cmap ();
1119     attributes.event_mask = (gtk_widget_get_events (widget) |
1120                              GDK_EXPOSURE_MASK |
1121                              GDK_BUTTON_PRESS_MASK |
1122                              GDK_BUTTON_RELEASE_MASK |
1123                              GDK_POINTER_MOTION_MASK |
1124                              GDK_PROXIMITY_IN_MASK |
1125                              GDK_PROXIMITY_OUT_MASK |
1126                              GDK_KEY_PRESS_MASK |
1127                              GDK_KEY_RELEASE_MASK |
1128                              GDK_ENTER_NOTIFY_MASK |
1129                              GDK_LEAVE_NOTIFY_MASK |
1130                              GDK_FOCUS_CHANGE_MASK);
1131     gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1133     widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1134     gdk_window_set_user_data (widget->window, widget);
1136     if ( prefs_get_int_attribute ("options.useextinput", "value", 1) )
1137         gtk_widget_set_events(widget, attributes.event_mask);
1139     widget->style = gtk_style_attach (widget->style, widget->window);
1141     GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1143     canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1146 /**
1147  * The canvas widget's unrealize callback.
1148  */
1149 static void
1150 sp_canvas_unrealize (GtkWidget *widget)
1152     SPCanvas *canvas = SP_CANVAS (widget);
1154     shutdown_transients (canvas);
1156     gdk_gc_destroy (canvas->pixmap_gc);
1157     canvas->pixmap_gc = NULL;
1159     if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1160         (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1163 /**
1164  * The canvas widget's size_request callback.
1165  */
1166 static void
1167 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1169     static_cast<void>(SP_CANVAS (widget));
1171     req->width = 256;
1172     req->height = 256;
1175 /**
1176  * The canvas widget's size_allocate callback.
1177  */
1178 static void
1179 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1181     SPCanvas *canvas = SP_CANVAS (widget);
1183     /* Schedule redraw of new region */
1184     sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1185     if (allocation->width > widget->allocation.width) {
1186         sp_canvas_request_redraw (canvas,
1187                                   canvas->x0 + widget->allocation.width,
1188                                   0,
1189                                   canvas->x0 + allocation->width,
1190                                   canvas->y0 + allocation->height);
1191     }
1192     if (allocation->height > widget->allocation.height) {
1193         sp_canvas_request_redraw (canvas,
1194                                   0,
1195                                   canvas->y0 + widget->allocation.height,
1196                                   canvas->x0 + allocation->width,
1197                                   canvas->y0 + allocation->height);
1198     }
1200     widget->allocation = *allocation;
1201     
1202     if (GTK_WIDGET_REALIZED (widget)) {
1203         gdk_window_move_resize (widget->window,
1204                                 widget->allocation.x, widget->allocation.y,
1205                                 widget->allocation.width, widget->allocation.height);
1206     }
1209 /**
1210  * Helper that emits an event for an item in the canvas, be it the current
1211  * item, grabbed item, or focused item, as appropriate.
1212  */
1213 static int
1214 emit_event (SPCanvas *canvas, GdkEvent *event)
1216     guint mask;
1218     if (canvas->grabbed_item) {
1219         switch (event->type) {
1220         case GDK_ENTER_NOTIFY:
1221             mask = GDK_ENTER_NOTIFY_MASK;
1222             break;
1223         case GDK_LEAVE_NOTIFY:
1224             mask = GDK_LEAVE_NOTIFY_MASK;
1225             break;
1226         case GDK_MOTION_NOTIFY:
1227             mask = GDK_POINTER_MOTION_MASK;
1228             break;
1229         case GDK_BUTTON_PRESS:
1230         case GDK_2BUTTON_PRESS:
1231         case GDK_3BUTTON_PRESS:
1232             mask = GDK_BUTTON_PRESS_MASK;
1233             break;
1234         case GDK_BUTTON_RELEASE:
1235             mask = GDK_BUTTON_RELEASE_MASK;
1236             break;
1237         case GDK_KEY_PRESS:
1238             mask = GDK_KEY_PRESS_MASK;
1239             break;
1240         case GDK_KEY_RELEASE:
1241             mask = GDK_KEY_RELEASE_MASK;
1242             break;
1243         case GDK_SCROLL:
1244             mask = GDK_SCROLL;
1245             break;
1246         default:
1247             mask = 0;
1248             break;
1249         }
1251         if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1252     }
1254     /* Convert to world coordinates -- we have two cases because of diferent
1255      * offsets of the fields in the event structures.
1256      */
1258     GdkEvent ev = *event;
1260     switch (ev.type) {
1261     case GDK_ENTER_NOTIFY:
1262     case GDK_LEAVE_NOTIFY:
1263         ev.crossing.x += canvas->x0;
1264         ev.crossing.y += canvas->y0;
1265         break;
1266     case GDK_MOTION_NOTIFY:
1267     case GDK_BUTTON_PRESS:
1268     case GDK_2BUTTON_PRESS:
1269     case GDK_3BUTTON_PRESS:
1270     case GDK_BUTTON_RELEASE:
1271         ev.motion.x += canvas->x0;
1272         ev.motion.y += canvas->y0;
1273         break;
1274     default:
1275         break;
1276     }
1278     /* Choose where we send the event */
1280     /* canvas->current_item becomes NULL in some cases under Win32
1281     ** (e.g. if the pointer leaves the window).  So this is a hack that
1282     ** Lauris applied to SP to get around the problem.
1283     */
1284     SPCanvasItem* item = NULL;
1285     if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1286         item = canvas->grabbed_item;
1287     } else {
1288         item = canvas->current_item;
1289     }
1291     if (canvas->focused_item &&
1292         ((event->type == GDK_KEY_PRESS) ||
1293          (event->type == GDK_KEY_RELEASE) ||
1294          (event->type == GDK_FOCUS_CHANGE))) {
1295         item = canvas->focused_item;
1296     }
1298     /* The event is propagated up the hierarchy (for if someone connected to
1299      * a group instead of a leaf event), and emission is stopped if a
1300      * handler returns TRUE, just like for GtkWidget events.
1301      */
1303     gint finished = FALSE;
1305     while (item && !finished) {
1306         gtk_object_ref (GTK_OBJECT (item));
1307         gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1308         SPCanvasItem *parent = item->parent;
1309         gtk_object_unref (GTK_OBJECT (item));
1310         item = parent;
1311     }
1313     return finished;
1316 /**
1317  * Helper that re-picks the current item in the canvas, based on the event's
1318  * coordinates and emits enter/leave events for items as appropriate.
1319  */
1320 static int
1321 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1323     int button_down = 0;
1324     double x, y;
1326     if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1327         return FALSE;
1329     int retval = FALSE;
1331     if (canvas->gen_all_enter_events == false) {
1332         // If a button is down, we'll perform enter and leave events on the
1333         // current item, but not enter on any other item.  This is more or
1334         // less like X pointer grabbing for canvas items.
1335         //
1336         button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1337                 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1339         if (!button_down) canvas->left_grabbed_item = FALSE;
1340     }
1342     /* Save the event in the canvas.  This is used to synthesize enter and
1343      * leave events in case the current item changes.  It is also used to
1344      * re-pick the current item if the current one gets deleted.  Also,
1345      * synthesize an enter event.
1346      */
1347     if (event != &canvas->pick_event) {
1348         if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1349             /* these fields have the same offsets in both types of events */
1351             canvas->pick_event.crossing.type       = GDK_ENTER_NOTIFY;
1352             canvas->pick_event.crossing.window     = event->motion.window;
1353             canvas->pick_event.crossing.send_event = event->motion.send_event;
1354             canvas->pick_event.crossing.subwindow  = NULL;
1355             canvas->pick_event.crossing.x          = event->motion.x;
1356             canvas->pick_event.crossing.y          = event->motion.y;
1357             canvas->pick_event.crossing.mode       = GDK_CROSSING_NORMAL;
1358             canvas->pick_event.crossing.detail     = GDK_NOTIFY_NONLINEAR;
1359             canvas->pick_event.crossing.focus      = FALSE;
1360             canvas->pick_event.crossing.state      = event->motion.state;
1362             /* these fields don't have the same offsets in both types of events */
1364             if (event->type == GDK_MOTION_NOTIFY) {
1365                 canvas->pick_event.crossing.x_root = event->motion.x_root;
1366                 canvas->pick_event.crossing.y_root = event->motion.y_root;
1367             } else {
1368                 canvas->pick_event.crossing.x_root = event->button.x_root;
1369                 canvas->pick_event.crossing.y_root = event->button.y_root;
1370             }
1371         } else {
1372             canvas->pick_event = *event;
1373         }
1374     }
1376     /* Don't do anything else if this is a recursive call */
1377     if (canvas->in_repick) return retval;
1379     /* LeaveNotify means that there is no current item, so we don't look for one */
1380     if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1381         /* these fields don't have the same offsets in both types of events */
1383         if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1384             x = canvas->pick_event.crossing.x;
1385             y = canvas->pick_event.crossing.y;
1386         } else {
1387             x = canvas->pick_event.motion.x;
1388             y = canvas->pick_event.motion.y;
1389         }
1391         /* world coords */
1392         x += canvas->x0;
1393         y += canvas->y0;
1395         /* find the closest item */
1396         if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1397             sp_canvas_item_invoke_point (canvas->root, NR::Point(x, y), &canvas->new_current_item);
1398         } else {
1399             canvas->new_current_item = NULL;
1400         }
1401     } else {
1402         canvas->new_current_item = NULL;
1403     }
1405     if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1406         return retval; /* current item did not change */
1407     }
1409     /* Synthesize events for old and new current items */
1411     if ((canvas->new_current_item != canvas->current_item)
1412         && (canvas->current_item != NULL)
1413         && !canvas->left_grabbed_item) {
1414         GdkEvent new_event;
1415         SPCanvasItem *item;
1417         item = canvas->current_item;
1419         new_event = canvas->pick_event;
1420         new_event.type = GDK_LEAVE_NOTIFY;
1422         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1423         new_event.crossing.subwindow = NULL;
1424         canvas->in_repick = TRUE;
1425         retval = emit_event (canvas, &new_event);
1426         canvas->in_repick = FALSE;
1427     }
1429     if (canvas->gen_all_enter_events == false) {
1430         // new_current_item may have been set to NULL during the call to
1431         // emit_event() above
1432         if ((canvas->new_current_item != canvas->current_item) && button_down) {
1433             canvas->left_grabbed_item = TRUE;
1434             return retval;
1435         }
1436     }
1438     /* Handle the rest of cases */
1440     canvas->left_grabbed_item = FALSE;
1441     canvas->current_item = canvas->new_current_item;
1443     if (canvas->current_item != NULL) {
1444         GdkEvent new_event;
1446         new_event = canvas->pick_event;
1447         new_event.type = GDK_ENTER_NOTIFY;
1448         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1449         new_event.crossing.subwindow = NULL;
1450         retval = emit_event (canvas, &new_event);
1451     }
1453     return retval;
1456 /**
1457  * Button event handler for the canvas.
1458  */
1459 static gint
1460 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1462     SPCanvas *canvas = SP_CANVAS (widget);
1464     int retval = FALSE;
1466     /* dispatch normally regardless of the event's window if an item has
1467        has a pointer grab in effect */
1468     if (!canvas->grabbed_item &&
1469         event->window != SP_CANVAS_WINDOW (canvas))
1470         return retval;
1472     int mask;
1473     switch (event->button) {
1474     case 1:
1475         mask = GDK_BUTTON1_MASK;
1476         break;
1477     case 2:
1478         mask = GDK_BUTTON2_MASK;
1479         break;
1480     case 3:
1481         mask = GDK_BUTTON3_MASK;
1482         break;
1483     case 4:
1484         mask = GDK_BUTTON4_MASK;
1485         break;
1486     case 5:
1487         mask = GDK_BUTTON5_MASK;
1488         break;
1489     default:
1490         mask = 0;
1491     }
1493     switch (event->type) {
1494     case GDK_BUTTON_PRESS:
1495     case GDK_2BUTTON_PRESS:
1496     case GDK_3BUTTON_PRESS:
1497         /* Pick the current item as if the button were not pressed, and
1498          * then process the event.
1499          */
1500         canvas->state = event->state;
1501         pick_current_item (canvas, (GdkEvent *) event);
1502         canvas->state ^= mask;
1503         retval = emit_event (canvas, (GdkEvent *) event);
1504         break;
1506     case GDK_BUTTON_RELEASE:
1507         /* Process the event as if the button were pressed, then repick
1508          * after the button has been released
1509          */
1510         canvas->state = event->state;
1511         retval = emit_event (canvas, (GdkEvent *) event);
1512         event->state ^= mask;
1513         canvas->state = event->state;
1514         pick_current_item (canvas, (GdkEvent *) event);
1515         event->state ^= mask;
1516         break;
1518     default:
1519         g_assert_not_reached ();
1520     }
1522     return retval;
1525 /**
1526  * Scroll event handler for the canvas.
1527  *
1528  * \todo FIXME: generate motion events to re-select items.
1529  */
1530 static gint
1531 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1533     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1536 /**
1537  * Motion event handler for the canvas.
1538  */
1539 static int
1540 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1542     SPCanvas *canvas = SP_CANVAS (widget);
1544     if (event->window != SP_CANVAS_WINDOW (canvas))
1545         return FALSE;
1547     if (canvas->grabbed_event_mask & GDK_POINTER_MOTION_HINT_MASK) {
1548         gint x, y;
1549         gdk_window_get_pointer (widget->window, &x, &y, NULL);
1550         event->x = x;
1551         event->y = y;
1552     }
1554     canvas->state = event->state;
1555     pick_current_item (canvas, (GdkEvent *) event);
1557     return emit_event (canvas, (GdkEvent *) event);
1560 static void
1561 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)
1563     GtkWidget *widget = GTK_WIDGET (canvas);
1565     SPCanvasBuf buf;
1566     if (canvas->rendermode != RENDERMODE_OUTLINE) {
1567         buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1568     } else {
1569         buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1570     }
1572     // Mark the region clean
1573     sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1575     buf.buf_rowstride = sw * 3; // CAIRO FIXME: for cairo output, the buffer must be RGB unpacked, i.e. sw * 4
1576     buf.rect.x0 = x0;
1577     buf.rect.y0 = y0;
1578     buf.rect.x1 = x1;
1579     buf.rect.y1 = y1;
1580     buf.visible_rect.x0 = draw_x1;
1581     buf.visible_rect.y0 = draw_y1;
1582     buf.visible_rect.x1 = draw_x2;
1583     buf.visible_rect.y1 = draw_y2;
1584     GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1585     buf.bg_color = (((color->red & 0xff00) << 8)
1586                     | (color->green & 0xff00)
1587                     | (color->blue >> 8));
1588     buf.is_empty = true;
1590     if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1591         SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1592     }
1594 #if ENABLE_LCMS
1595     cmsHTRANSFORM transf = 0;
1596     long long int fromDisplay = prefs_get_int_attribute_limited( "options.displayprofile", "from_display", 0, 0, 1 );
1597     if ( fromDisplay ) {
1598         transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" );
1599     } else {
1600         transf = Inkscape::colorprofile_get_display_transform();
1601     }
1602 #endif // ENABLE_LCMS
1604     if (buf.is_empty) {
1605 #if ENABLE_LCMS
1606         if ( transf && canvas->enable_cms_display_adj ) {
1607             cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1608         }
1609 #endif // ENABLE_LCMS
1610         gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1611         gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1612                             canvas->pixmap_gc,
1613                             TRUE,
1614                             x0 - canvas->x0, y0 - canvas->y0,
1615                             x1 - x0, y1 - y0);
1616     } else {
1617 /*
1618 // CAIRO FIXME: after SPCanvasBuf is made 32bpp throughout, this rgb_draw below can be replaced with the below.
1619 // Why this must not be done currently:
1620 // - all canvas items (handles, nodes etc) paint themselves assuming 24bpp
1621 // - cairo assumes bgra, but we have rgba, so r and b get swapped (until we paint all with cairo too)
1622 // - it does not seem to be any faster; in fact since with 32bpp, buf contains less pixels,
1623 // we need more bufs to paint a given area and as a result it's even a bit slower
1625     cairo_surface_t* cst = cairo_image_surface_create_for_data (
1626         buf.buf,
1627         CAIRO_FORMAT_RGB24,  // unpacked, i.e. 32 bits! one byte is unused
1628         x1 - x0, y1 - y0,
1629         buf.buf_rowstride
1630         );
1631         cairo_t *ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1632         cairo_set_source_surface (ct, cst, x0 - canvas->x0, y0 - canvas->y0);
1633         cairo_paint (ct);
1634     cairo_destroy (ct);
1635     cairo_surface_finish (cst);
1636     cairo_surface_destroy (cst);
1637 */
1639 #if ENABLE_LCMS
1640         if ( transf && canvas->enable_cms_display_adj ) {
1641             for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1642                 guchar* p = buf.buf + (sw * 3) * yy;
1643                 cmsDoTransform( transf, p, p, (x1 - x0) );
1644             }
1645         }
1646 #endif // ENABLE_LCMS
1648         gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1649                                       canvas->pixmap_gc,
1650                                       x0 - canvas->x0, y0 - canvas->y0,
1651                                       x1 - x0, y1 - y0,
1652                                       GDK_RGB_DITHER_MAX,
1653                                       buf.buf,
1654                                       sw * 3,
1655                                       x0 - canvas->x0, y0 - canvas->y0);
1656     }
1658     if (canvas->rendermode != RENDERMODE_OUTLINE) {
1659         nr_pixelstore_256K_free (buf.buf);
1660     } else {
1661         nr_pixelstore_1M_free (buf.buf);
1662     }
1665 struct PaintRectSetup {
1666     SPCanvas* canvas;
1667     NRRectL big_rect;
1668     GTimeVal start_time;
1669     int max_pixels;
1670     NR::Point mouse_loc;
1671 };
1673 /**
1674  * Paint the given rect, recursively subdividing the region until it is the size of a single
1675  * buffer.
1676  *
1677  * @return true if the drawing completes
1678  */
1679 static int
1680 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1682     GTimeVal now;
1683     g_get_current_time (&now);
1685     glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1686         + (now.tv_usec - setup->start_time.tv_usec);
1688     // Allow only very fast buffers to be run together;
1689     // as soon as the total redraw time exceeds 1ms, cancel;
1690     // this returns control to the idle loop and allows Inkscape to process user input
1691     // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1692     // it will get back and finish painting what remains to paint.
1693     if (elapsed > 1000) {
1695         // Interrupting redraw isn't always good.
1696         // For example, when you drag one node of a big path, only the buffer containing
1697         // the mouse cursor will be redrawn again and again, and the rest of the path
1698         // will remain stale because Inkscape never has enough idle time to redraw all
1699         // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1700         // If this limit is set, and if we have aborted redraw more times than is allowed,
1701         // interrupting is blocked and we're forced to redraw full screen once
1702         // (after which we can again interrupt forced_redraw_limit times).
1703         if (setup->canvas->forced_redraw_limit < 0 ||
1704             setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1706             if (setup->canvas->forced_redraw_limit != -1) {
1707                 setup->canvas->forced_redraw_count++;
1708             }
1710             return false;
1711         }
1712     }
1714     // Find the optimal buffer dimensions
1715     int bw = this_rect.x1 - this_rect.x0;
1716     int bh = this_rect.y1 - this_rect.y0;
1717     if ((bw < 1) || (bh < 1))
1718         return 0;
1720     if (bw * bh < setup->max_pixels) {
1721         // We are small enough
1722         sp_canvas_paint_single_buffer (setup->canvas,
1723                                        this_rect.x0, this_rect.y0,
1724                                        this_rect.x1, this_rect.y1,
1725                                        setup->big_rect.x0, setup->big_rect.y0,
1726                                        setup->big_rect.x1, setup->big_rect.y1, bw);
1727         return 1;
1728     }
1730     NRRectL lo = this_rect;
1731     NRRectL hi = this_rect;
1733 /*
1734 This test determines the redraw strategy:
1736 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1737 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1738 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1739 and seems to be faster for drawings with many smaller objects at zoom-out.
1741 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1742 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1743 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1744 faster.
1746 The default for now is the strips mode.
1747 */
1748     if (bw < bh || bh < 2 * TILE_SIZE) {
1749         // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1750         int mid = ((long long) this_rect.x0 + (long long) this_rect.x1) / 2;
1751         // Make sure that mid lies on a tile boundary
1752         mid = (mid / TILE_SIZE) * TILE_SIZE;
1754         lo.x1 = mid;
1755         hi.x0 = mid;
1757         if (setup->mouse_loc[NR::X] < mid) {
1758             // Always paint towards the mouse first
1759             return sp_canvas_paint_rect_internal(setup, lo)
1760                 && sp_canvas_paint_rect_internal(setup, hi);
1761         } else {
1762             return sp_canvas_paint_rect_internal(setup, hi)
1763                 && sp_canvas_paint_rect_internal(setup, lo);
1764         }
1765     } else {
1766         // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1767         int mid = ((long long) this_rect.y0 + (long long) this_rect.y1) / 2;
1768         // Make sure that mid lies on a tile boundary
1769         mid = (mid / TILE_SIZE) * TILE_SIZE;
1771         lo.y1 = mid;
1772         hi.y0 = mid;
1774         if (setup->mouse_loc[NR::Y] < mid) {
1775             // Always paint towards the mouse first
1776             return sp_canvas_paint_rect_internal(setup, lo)
1777                 && sp_canvas_paint_rect_internal(setup, hi);
1778         } else {
1779             return sp_canvas_paint_rect_internal(setup, hi)
1780                 && sp_canvas_paint_rect_internal(setup, lo);
1781         }
1782     }
1786 /**
1787  * Helper that draws a specific rectangular part of the canvas.
1788  *
1789  * @return true if the rectangle painting succeeds.
1790  */
1791 static bool
1792 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1794     g_return_val_if_fail (!canvas->need_update, false);
1796     NRRectL rect;
1797     rect.x0 = xx0;
1798     rect.x1 = xx1;
1799     rect.y0 = yy0;
1800     rect.y1 = yy1;
1802     // Clip rect-to-draw by the current visible area
1803     rect.x0 = MAX (rect.x0, canvas->x0);
1804     rect.y0 = MAX (rect.y0, canvas->y0);
1805     rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1806     rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1808 #ifdef DEBUG_REDRAW
1809     // paint the area to redraw yellow
1810     gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1811     gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1812                         canvas->pixmap_gc,
1813                         TRUE,
1814                         rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1815                         rect.x1 - rect.x0, rect.y1 - rect.y0);
1816 #endif
1818     PaintRectSetup setup;
1820     setup.canvas = canvas;
1821     setup.big_rect = rect;
1823     // Save the mouse location
1824     gint x, y;
1825     gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1826     setup.mouse_loc = sp_canvas_window_to_world (canvas, NR::Point(x,y));
1828     // CAIRO FIXME: the sw/sh calculations below all assume 24bpp, need fixing for 32bpp
1829     if (canvas->rendermode != RENDERMODE_OUTLINE) {
1830         // use 256K as a compromise to not slow down gradients
1831         // 256K is the cached buffer and we need 3 channels
1832         setup.max_pixels = 87381; // 256K/3
1833     } else {
1834         // paths only, so 1M works faster
1835         // 1M is the cached buffer and we need 3 channels
1836         setup.max_pixels = 349525;
1837     }
1839     // Start the clock
1840     g_get_current_time(&(setup.start_time));
1842     // Go
1843     return sp_canvas_paint_rect_internal(&setup, rect);
1846 /**
1847  * Force a full redraw after a specified number of interrupted redraws
1848  */
1849 void
1850 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1851   g_return_if_fail(canvas != NULL);
1853   canvas->forced_redraw_limit = count;
1854   canvas->forced_redraw_count = 0;
1857 /**
1858  * End forced full redraw requests
1859  */
1860 void
1861 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1862   g_return_if_fail(canvas != NULL);
1864   canvas->forced_redraw_limit = -1;
1867 /**
1868  * The canvas widget's expose callback.
1869  */
1870 static gint
1871 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1873     SPCanvas *canvas = SP_CANVAS (widget);
1875     if (!GTK_WIDGET_DRAWABLE (widget) ||
1876         (event->window != SP_CANVAS_WINDOW (canvas)))
1877         return FALSE;
1879     int n_rects;
1880     GdkRectangle *rects;
1881     gdk_region_get_rectangles (event->region, &rects, &n_rects);
1883     for (int i = 0; i < n_rects; i++) {
1884         NRRectL rect;
1886         rect.x0 = rects[i].x + canvas->x0;
1887         rect.y0 = rects[i].y + canvas->y0;
1888         rect.x1 = rect.x0 + rects[i].width;
1889         rect.y1 = rect.y0 + rects[i].height;
1891         sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1892     }
1894     if (n_rects > 0)
1895         g_free (rects);
1897     return FALSE;
1900 /**
1901  * The canvas widget's keypress callback.
1902  */
1903 static gint
1904 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1906     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1909 /**
1910  * Crossing event handler for the canvas.
1911  */
1912 static gint
1913 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1915     SPCanvas *canvas = SP_CANVAS (widget);
1917     if (event->window != SP_CANVAS_WINDOW (canvas))
1918         return FALSE;
1920     canvas->state = event->state;
1921     return pick_current_item (canvas, (GdkEvent *) event);
1924 /**
1925  * Focus in handler for the canvas.
1926  */
1927 static gint
1928 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
1930     GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1932     SPCanvas *canvas = SP_CANVAS (widget);
1934     if (canvas->focused_item) {
1935         return emit_event (canvas, (GdkEvent *) event);
1936     } else {
1937         return FALSE;
1938     }
1941 /**
1942  * Focus out handler for the canvas.
1943  */
1944 static gint
1945 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
1947     GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1949     SPCanvas *canvas = SP_CANVAS (widget);
1951     if (canvas->focused_item)
1952         return emit_event (canvas, (GdkEvent *) event);
1953     else
1954         return FALSE;
1957 /**
1958  * Helper that repaints the areas in the canvas that need it.
1959  *
1960  * @return true if all the dirty parts have been redrawn
1961  */
1962 static int
1963 paint (SPCanvas *canvas)
1965     if (canvas->need_update) {
1966         sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
1967         canvas->need_update = FALSE;
1968     }
1970     if (!canvas->need_redraw)
1971         return TRUE;
1973     Gdk::Region to_paint;
1975     for (int j=canvas->tTop; j<canvas->tBottom; j++) {
1976         for (int i=canvas->tLeft; i<canvas->tRight; i++) {
1977             int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
1979             if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
1980                 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
1981                                    TILE_SIZE, TILE_SIZE));
1982             }
1984         }
1985     }
1987     if (!to_paint.empty()) {
1988         Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
1989         typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
1990         for (Iter i=rect.begin(); i != rect.end(); ++i) {
1991             int x0 = (*i).get_x();
1992             int y0 = (*i).get_y();
1993             int x1 = x0 + (*i).get_width();
1994             int y1 = y0 + (*i).get_height();
1995             if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
1996                 // Aborted
1997                 return FALSE;
1998             };
1999         }
2000     }
2002     canvas->need_redraw = FALSE;
2004     // we've had a full unaborted redraw, reset the full redraw counter
2005     if (canvas->forced_redraw_limit != -1) {
2006         canvas->forced_redraw_count = 0;
2007     }
2009     return TRUE;
2012 /**
2013  * Helper that invokes update, paint, and repick on canvas.
2014  */
2015 static int
2016 do_update (SPCanvas *canvas)
2018     if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
2019         return TRUE;
2021     /* Cause the update if necessary */
2022     if (canvas->need_update) {
2023         sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2024         canvas->need_update = FALSE;
2025     }
2027     /* Paint if able to */
2028     if (GTK_WIDGET_DRAWABLE (canvas)) {
2029             return paint (canvas);
2030     }
2032     /* Pick new current item */
2033     while (canvas->need_repick) {
2034         canvas->need_repick = FALSE;
2035         pick_current_item (canvas, &canvas->pick_event);
2036     }
2038     return TRUE;
2041 /**
2042  * Idle handler for the canvas that deals with pending updates and redraws.
2043  */
2044 static gint
2045 idle_handler (gpointer data)
2047     GDK_THREADS_ENTER ();
2049     SPCanvas *canvas = SP_CANVAS (data);
2051     int const ret = do_update (canvas);
2053     if (ret) {
2054         /* Reset idle id */
2055         canvas->idle_id = 0;
2056     }
2058     GDK_THREADS_LEAVE ();
2060     return !ret;
2063 /**
2064  * Convenience function to add an idle handler to a canvas.
2065  */
2066 static void
2067 add_idle (SPCanvas *canvas)
2069     if (canvas->idle_id != 0)
2070         return;
2072     canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2075 /**
2076  * Returns the root group of the specified canvas.
2077  */
2078 SPCanvasGroup *
2079 sp_canvas_root (SPCanvas *canvas)
2081     g_return_val_if_fail (canvas != NULL, NULL);
2082     g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2084     return SP_CANVAS_GROUP (canvas->root);
2087 /**
2088  * Scrolls canvas to specific position (cx and cy are measured in screen pixels)
2089  */
2090 void
2091 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2093     g_return_if_fail (canvas != NULL);
2094     g_return_if_fail (SP_IS_CANVAS (canvas));
2096     int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels)
2097     int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do!
2098     int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the 
2099     int dy = iy - canvas->y0; // canvas w.r.t its previous position
2101     canvas->dx0 = cx; // here the 'd' stands for double, not delta!
2102     canvas->dy0 = cy;
2103     canvas->x0 = ix;
2104     canvas->y0 = iy;
2106     sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2108     if (!clear) {
2109         // scrolling without zoom; redraw only the newly exposed areas
2110         if ((dx != 0) || (dy != 0)) {
2111             canvas->is_scrolling = is_scrolling;
2112             if (GTK_WIDGET_REALIZED (canvas)) {
2113                 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2114             }
2115         }
2116     } else {
2117         // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2118     }
2121 /**
2122  * Updates canvas if necessary.
2123  */
2124 void
2125 sp_canvas_update_now (SPCanvas *canvas)
2127     g_return_if_fail (canvas != NULL);
2128     g_return_if_fail (SP_IS_CANVAS (canvas));
2130     if (!(canvas->need_update ||
2131           canvas->need_redraw))
2132         return;
2134     do_update (canvas);
2137 /**
2138  * Update callback for canvas widget.
2139  */
2140 static void
2141 sp_canvas_request_update (SPCanvas *canvas)
2143     canvas->need_update = TRUE;
2144     add_idle (canvas);
2147 /**
2148  * Forces redraw of rectangular canvas area.
2149  */
2150 void
2151 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2153     NRRectL bbox;
2154     NRRectL visible;
2155     NRRectL clip;
2157     g_return_if_fail (canvas != NULL);
2158     g_return_if_fail (SP_IS_CANVAS (canvas));
2160     if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2161     if ((x0 >= x1) || (y0 >= y1)) return;
2163     bbox.x0 = x0;
2164     bbox.y0 = y0;
2165     bbox.x1 = x1;
2166     bbox.y1 = y1;
2168     visible.x0 = canvas->x0;
2169     visible.y0 = canvas->y0;
2170     visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2171     visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2173     nr_rect_l_intersect (&clip, &bbox, &visible);
2175     sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2176     add_idle (canvas);
2179 /**
2180  * Sets world coordinates from win and canvas.
2181  */
2182 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2184     g_return_if_fail (canvas != NULL);
2185     g_return_if_fail (SP_IS_CANVAS (canvas));
2187     if (worldx) *worldx = canvas->x0 + winx;
2188     if (worldy) *worldy = canvas->y0 + winy;
2191 /**
2192  * Sets win coordinates from world and canvas.
2193  */
2194 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2196     g_return_if_fail (canvas != NULL);
2197     g_return_if_fail (SP_IS_CANVAS (canvas));
2199     if (winx) *winx = worldx - canvas->x0;
2200     if (winy) *winy = worldy - canvas->y0;
2203 /**
2204  * Converts point from win to world coordinates.
2205  */
2206 NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win)
2208     g_assert (canvas != NULL);
2209     g_assert (SP_IS_CANVAS (canvas));
2211     return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2214 /**
2215  * Converts point from world to win coordinates.
2216  */
2217 NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world)
2219     g_assert (canvas != NULL);
2220     g_assert (SP_IS_CANVAS (canvas));
2222     return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2225 /**
2226  * Returns true if point given in world coordinates is inside window.
2227  */
2228 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world)
2230     g_assert( canvas != NULL );
2231     g_assert(SP_IS_CANVAS(canvas));
2233     using NR::X;
2234     using NR::Y;
2235     GtkWidget const &w = *GTK_WIDGET(canvas);
2236     return ( ( canvas->x0 <= world[X] )  &&
2237              ( canvas->y0 <= world[Y] )  &&
2238              ( world[X] < canvas->x0 + w.allocation.width )  &&
2239              ( world[Y] < canvas->y0 + w.allocation.height ) );
2242 /**
2243  * Return canvas window coordinates as NR::Rect.
2244  */
2245 NR::Rect SPCanvas::getViewbox() const
2247     GtkWidget const *w = GTK_WIDGET(this);
2248     return NR::Rect(NR::Point(dx0, dy0),
2249                     NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2252 /**
2253  * Return canvas window coordinates as IRect (a rectangle defined by integers).
2254  */
2255 NR::IRect SPCanvas::getViewboxIntegers() const
2257     GtkWidget const *w = GTK_WIDGET(this);
2258     return NR::IRect(NR::IPoint(x0, y0),
2259                     NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height));
2262 inline int sp_canvas_tile_floor(int x)
2264     return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2267 inline int sp_canvas_tile_ceil(int x)
2269     return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2272 /**
2273  * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2274  */
2275 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2277     if ( nl >= nr || nt >= nb ) {
2278         if ( canvas->tiles ) g_free(canvas->tiles);
2279         canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2280         canvas->tileH=canvas->tileV=0;
2281         canvas->tiles=NULL;
2282         return;
2283     }
2284     int tl=sp_canvas_tile_floor(nl);
2285     int tt=sp_canvas_tile_floor(nt);
2286     int tr=sp_canvas_tile_ceil(nr);
2287     int tb=sp_canvas_tile_ceil(nb);
2289     int nh = tr-tl, nv = tb-tt;
2290     uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2291     for (int i=tl; i<tr; i++) {
2292         for (int j=tt; j<tb; j++) {
2293             int ind = (i-tl) + (j-tt)*nh;
2294             if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2295                 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2296             } else {
2297                 ntiles[ind]=0; // newly exposed areas get 0
2298             }
2299         }
2300     }
2301     if ( canvas->tiles ) g_free(canvas->tiles);
2302     canvas->tiles=ntiles;
2303     canvas->tLeft=tl;
2304     canvas->tTop=tt;
2305     canvas->tRight=tr;
2306     canvas->tBottom=tb;
2307     canvas->tileH=nh;
2308     canvas->tileV=nv;
2311 /*
2312  * Helper that queues a canvas rectangle for redraw
2313  */
2314 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2315     canvas->need_redraw = TRUE;
2317     sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2320 /**
2321  * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2322  */
2323 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2325     if ( nl >= nr || nt >= nb ) {
2326         return;
2327     }
2328     int tl=sp_canvas_tile_floor(nl);
2329     int tt=sp_canvas_tile_floor(nt);
2330     int tr=sp_canvas_tile_ceil(nr);
2331     int tb=sp_canvas_tile_ceil(nb);
2332     if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2333     if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2334     if ( tr > canvas->tRight ) tr=canvas->tRight;
2335     if ( tt < canvas->tTop ) tt=canvas->tTop;
2336     if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2338     for (int i=tl; i<tr; i++) {
2339         for (int j=tt; j<tb; j++) {
2340             canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2341         }
2342     }
2346 /*
2347   Local Variables:
2348   mode:c++
2349   c-file-style:"stroustrup"
2350   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2351   indent-tabs-mode:nil
2352   fill-column:99
2353   End:
2354 */
2355 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :