Code

suppress warnings when closing a desktop but not program
[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     canvas->current_item = NULL;
1150     canvas->grabbed_item = NULL;
1151     canvas->focused_item = NULL;
1153     shutdown_transients (canvas);
1155     gdk_gc_destroy (canvas->pixmap_gc);
1156     canvas->pixmap_gc = NULL;
1158     if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1159         (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1162 /**
1163  * The canvas widget's size_request callback.
1164  */
1165 static void
1166 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1168     static_cast<void>(SP_CANVAS (widget));
1170     req->width = 256;
1171     req->height = 256;
1174 /**
1175  * The canvas widget's size_allocate callback.
1176  */
1177 static void
1178 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1180     SPCanvas *canvas = SP_CANVAS (widget);
1182     /* Schedule redraw of new region */
1183     sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1184     if (allocation->width > widget->allocation.width) {
1185         sp_canvas_request_redraw (canvas,
1186                                   canvas->x0 + widget->allocation.width,
1187                                   0,
1188                                   canvas->x0 + allocation->width,
1189                                   canvas->y0 + allocation->height);
1190     }
1191     if (allocation->height > widget->allocation.height) {
1192         sp_canvas_request_redraw (canvas,
1193                                   0,
1194                                   canvas->y0 + widget->allocation.height,
1195                                   canvas->x0 + allocation->width,
1196                                   canvas->y0 + allocation->height);
1197     }
1199     widget->allocation = *allocation;
1200     
1201     if (GTK_WIDGET_REALIZED (widget)) {
1202         gdk_window_move_resize (widget->window,
1203                                 widget->allocation.x, widget->allocation.y,
1204                                 widget->allocation.width, widget->allocation.height);
1205     }
1208 /**
1209  * Helper that emits an event for an item in the canvas, be it the current
1210  * item, grabbed item, or focused item, as appropriate.
1211  */
1212 static int
1213 emit_event (SPCanvas *canvas, GdkEvent *event)
1215     guint mask;
1217     if (canvas->grabbed_item) {
1218         switch (event->type) {
1219         case GDK_ENTER_NOTIFY:
1220             mask = GDK_ENTER_NOTIFY_MASK;
1221             break;
1222         case GDK_LEAVE_NOTIFY:
1223             mask = GDK_LEAVE_NOTIFY_MASK;
1224             break;
1225         case GDK_MOTION_NOTIFY:
1226             mask = GDK_POINTER_MOTION_MASK;
1227             break;
1228         case GDK_BUTTON_PRESS:
1229         case GDK_2BUTTON_PRESS:
1230         case GDK_3BUTTON_PRESS:
1231             mask = GDK_BUTTON_PRESS_MASK;
1232             break;
1233         case GDK_BUTTON_RELEASE:
1234             mask = GDK_BUTTON_RELEASE_MASK;
1235             break;
1236         case GDK_KEY_PRESS:
1237             mask = GDK_KEY_PRESS_MASK;
1238             break;
1239         case GDK_KEY_RELEASE:
1240             mask = GDK_KEY_RELEASE_MASK;
1241             break;
1242         case GDK_SCROLL:
1243             mask = GDK_SCROLL;
1244             break;
1245         default:
1246             mask = 0;
1247             break;
1248         }
1250         if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1251     }
1253     /* Convert to world coordinates -- we have two cases because of diferent
1254      * offsets of the fields in the event structures.
1255      */
1257     GdkEvent ev = *event;
1259     switch (ev.type) {
1260     case GDK_ENTER_NOTIFY:
1261     case GDK_LEAVE_NOTIFY:
1262         ev.crossing.x += canvas->x0;
1263         ev.crossing.y += canvas->y0;
1264         break;
1265     case GDK_MOTION_NOTIFY:
1266     case GDK_BUTTON_PRESS:
1267     case GDK_2BUTTON_PRESS:
1268     case GDK_3BUTTON_PRESS:
1269     case GDK_BUTTON_RELEASE:
1270         ev.motion.x += canvas->x0;
1271         ev.motion.y += canvas->y0;
1272         break;
1273     default:
1274         break;
1275     }
1277     /* Choose where we send the event */
1279     /* canvas->current_item becomes NULL in some cases under Win32
1280     ** (e.g. if the pointer leaves the window).  So this is a hack that
1281     ** Lauris applied to SP to get around the problem.
1282     */
1283     SPCanvasItem* item = NULL;
1284     if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1285         item = canvas->grabbed_item;
1286     } else {
1287         item = canvas->current_item;
1288     }
1290     if (canvas->focused_item &&
1291         ((event->type == GDK_KEY_PRESS) ||
1292          (event->type == GDK_KEY_RELEASE) ||
1293          (event->type == GDK_FOCUS_CHANGE))) {
1294         item = canvas->focused_item;
1295     }
1297     /* The event is propagated up the hierarchy (for if someone connected to
1298      * a group instead of a leaf event), and emission is stopped if a
1299      * handler returns TRUE, just like for GtkWidget events.
1300      */
1302     gint finished = FALSE;
1304     while (item && !finished) {
1305         gtk_object_ref (GTK_OBJECT (item));
1306         gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1307         SPCanvasItem *parent = item->parent;
1308         gtk_object_unref (GTK_OBJECT (item));
1309         item = parent;
1310     }
1312     return finished;
1315 /**
1316  * Helper that re-picks the current item in the canvas, based on the event's
1317  * coordinates and emits enter/leave events for items as appropriate.
1318  */
1319 static int
1320 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1322     int button_down = 0;
1323     double x, y;
1325     if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1326         return FALSE;
1328     int retval = FALSE;
1330     if (canvas->gen_all_enter_events == false) {
1331         // If a button is down, we'll perform enter and leave events on the
1332         // current item, but not enter on any other item.  This is more or
1333         // less like X pointer grabbing for canvas items.
1334         //
1335         button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1336                 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1338         if (!button_down) canvas->left_grabbed_item = FALSE;
1339     }
1341     /* Save the event in the canvas.  This is used to synthesize enter and
1342      * leave events in case the current item changes.  It is also used to
1343      * re-pick the current item if the current one gets deleted.  Also,
1344      * synthesize an enter event.
1345      */
1346     if (event != &canvas->pick_event) {
1347         if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1348             /* these fields have the same offsets in both types of events */
1350             canvas->pick_event.crossing.type       = GDK_ENTER_NOTIFY;
1351             canvas->pick_event.crossing.window     = event->motion.window;
1352             canvas->pick_event.crossing.send_event = event->motion.send_event;
1353             canvas->pick_event.crossing.subwindow  = NULL;
1354             canvas->pick_event.crossing.x          = event->motion.x;
1355             canvas->pick_event.crossing.y          = event->motion.y;
1356             canvas->pick_event.crossing.mode       = GDK_CROSSING_NORMAL;
1357             canvas->pick_event.crossing.detail     = GDK_NOTIFY_NONLINEAR;
1358             canvas->pick_event.crossing.focus      = FALSE;
1359             canvas->pick_event.crossing.state      = event->motion.state;
1361             /* these fields don't have the same offsets in both types of events */
1363             if (event->type == GDK_MOTION_NOTIFY) {
1364                 canvas->pick_event.crossing.x_root = event->motion.x_root;
1365                 canvas->pick_event.crossing.y_root = event->motion.y_root;
1366             } else {
1367                 canvas->pick_event.crossing.x_root = event->button.x_root;
1368                 canvas->pick_event.crossing.y_root = event->button.y_root;
1369             }
1370         } else {
1371             canvas->pick_event = *event;
1372         }
1373     }
1375     /* Don't do anything else if this is a recursive call */
1376     if (canvas->in_repick) return retval;
1378     /* LeaveNotify means that there is no current item, so we don't look for one */
1379     if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1380         /* these fields don't have the same offsets in both types of events */
1382         if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1383             x = canvas->pick_event.crossing.x;
1384             y = canvas->pick_event.crossing.y;
1385         } else {
1386             x = canvas->pick_event.motion.x;
1387             y = canvas->pick_event.motion.y;
1388         }
1390         /* world coords */
1391         x += canvas->x0;
1392         y += canvas->y0;
1394         /* find the closest item */
1395         if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1396             sp_canvas_item_invoke_point (canvas->root, NR::Point(x, y), &canvas->new_current_item);
1397         } else {
1398             canvas->new_current_item = NULL;
1399         }
1400     } else {
1401         canvas->new_current_item = NULL;
1402     }
1404     if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1405         return retval; /* current item did not change */
1406     }
1408     /* Synthesize events for old and new current items */
1410     if ((canvas->new_current_item != canvas->current_item)
1411         && (canvas->current_item != NULL)
1412         && !canvas->left_grabbed_item) {
1413         GdkEvent new_event;
1414         SPCanvasItem *item;
1416         item = canvas->current_item;
1418         new_event = canvas->pick_event;
1419         new_event.type = GDK_LEAVE_NOTIFY;
1421         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1422         new_event.crossing.subwindow = NULL;
1423         canvas->in_repick = TRUE;
1424         retval = emit_event (canvas, &new_event);
1425         canvas->in_repick = FALSE;
1426     }
1428     if (canvas->gen_all_enter_events == false) {
1429         // new_current_item may have been set to NULL during the call to
1430         // emit_event() above
1431         if ((canvas->new_current_item != canvas->current_item) && button_down) {
1432             canvas->left_grabbed_item = TRUE;
1433             return retval;
1434         }
1435     }
1437     /* Handle the rest of cases */
1439     canvas->left_grabbed_item = FALSE;
1440     canvas->current_item = canvas->new_current_item;
1442     if (canvas->current_item != NULL) {
1443         GdkEvent new_event;
1445         new_event = canvas->pick_event;
1446         new_event.type = GDK_ENTER_NOTIFY;
1447         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1448         new_event.crossing.subwindow = NULL;
1449         retval = emit_event (canvas, &new_event);
1450     }
1452     return retval;
1455 /**
1456  * Button event handler for the canvas.
1457  */
1458 static gint
1459 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1461     SPCanvas *canvas = SP_CANVAS (widget);
1463     int retval = FALSE;
1465     /* dispatch normally regardless of the event's window if an item has
1466        has a pointer grab in effect */
1467     if (!canvas->grabbed_item &&
1468         event->window != SP_CANVAS_WINDOW (canvas))
1469         return retval;
1471     int mask;
1472     switch (event->button) {
1473     case 1:
1474         mask = GDK_BUTTON1_MASK;
1475         break;
1476     case 2:
1477         mask = GDK_BUTTON2_MASK;
1478         break;
1479     case 3:
1480         mask = GDK_BUTTON3_MASK;
1481         break;
1482     case 4:
1483         mask = GDK_BUTTON4_MASK;
1484         break;
1485     case 5:
1486         mask = GDK_BUTTON5_MASK;
1487         break;
1488     default:
1489         mask = 0;
1490     }
1492     switch (event->type) {
1493     case GDK_BUTTON_PRESS:
1494     case GDK_2BUTTON_PRESS:
1495     case GDK_3BUTTON_PRESS:
1496         /* Pick the current item as if the button were not pressed, and
1497          * then process the event.
1498          */
1499         canvas->state = event->state;
1500         pick_current_item (canvas, (GdkEvent *) event);
1501         canvas->state ^= mask;
1502         retval = emit_event (canvas, (GdkEvent *) event);
1503         break;
1505     case GDK_BUTTON_RELEASE:
1506         /* Process the event as if the button were pressed, then repick
1507          * after the button has been released
1508          */
1509         canvas->state = event->state;
1510         retval = emit_event (canvas, (GdkEvent *) event);
1511         event->state ^= mask;
1512         canvas->state = event->state;
1513         pick_current_item (canvas, (GdkEvent *) event);
1514         event->state ^= mask;
1515         break;
1517     default:
1518         g_assert_not_reached ();
1519     }
1521     return retval;
1524 /**
1525  * Scroll event handler for the canvas.
1526  *
1527  * \todo FIXME: generate motion events to re-select items.
1528  */
1529 static gint
1530 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1532     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1535 /**
1536  * Motion event handler for the canvas.
1537  */
1538 static int
1539 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1541     SPCanvas *canvas = SP_CANVAS (widget);
1543     if (event->window != SP_CANVAS_WINDOW (canvas))
1544         return FALSE;
1546     if (canvas->pixmap_gc == NULL) // canvas being deleted
1547         return FALSE;
1549     if (canvas->grabbed_event_mask & GDK_POINTER_MOTION_HINT_MASK) {
1550         gint x, y;
1551         gdk_window_get_pointer (widget->window, &x, &y, NULL);
1552         event->x = x;
1553         event->y = y;
1554     }
1556     canvas->state = event->state;
1557     pick_current_item (canvas, (GdkEvent *) event);
1559     return emit_event (canvas, (GdkEvent *) event);
1562 static void
1563 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)
1565     GtkWidget *widget = GTK_WIDGET (canvas);
1567     SPCanvasBuf buf;
1568     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1569         buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1570     } else {
1571         buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1572     }
1574     // Mark the region clean
1575     sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1577     buf.buf_rowstride = sw * 3; // CAIRO FIXME: for cairo output, the buffer must be RGB unpacked, i.e. sw * 4
1578     buf.rect.x0 = x0;
1579     buf.rect.y0 = y0;
1580     buf.rect.x1 = x1;
1581     buf.rect.y1 = y1;
1582     buf.visible_rect.x0 = draw_x1;
1583     buf.visible_rect.y0 = draw_y1;
1584     buf.visible_rect.x1 = draw_x2;
1585     buf.visible_rect.y1 = draw_y2;
1586     GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1587     buf.bg_color = (((color->red & 0xff00) << 8)
1588                     | (color->green & 0xff00)
1589                     | (color->blue >> 8));
1590     buf.is_empty = true;
1592     if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1593         SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1594     }
1596 #if ENABLE_LCMS
1597     cmsHTRANSFORM transf = 0;
1598     long long int fromDisplay = prefs_get_int_attribute_limited( "options.displayprofile", "from_display", 0, 0, 1 );
1599     if ( fromDisplay ) {
1600         transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" );
1601     } else {
1602         transf = Inkscape::colorprofile_get_display_transform();
1603     }
1604 #endif // ENABLE_LCMS
1606     if (buf.is_empty) {
1607 #if ENABLE_LCMS
1608         if ( transf && canvas->enable_cms_display_adj ) {
1609             cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1610         }
1611 #endif // ENABLE_LCMS
1612         gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1613         gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1614                             canvas->pixmap_gc,
1615                             TRUE,
1616                             x0 - canvas->x0, y0 - canvas->y0,
1617                             x1 - x0, y1 - y0);
1618     } else {
1619 /*
1620 // CAIRO FIXME: after SPCanvasBuf is made 32bpp throughout, this rgb_draw below can be replaced with the below.
1621 // Why this must not be done currently:
1622 // - all canvas items (handles, nodes etc) paint themselves assuming 24bpp
1623 // - cairo assumes bgra, but we have rgba, so r and b get swapped (until we paint all with cairo too)
1624 // - it does not seem to be any faster; in fact since with 32bpp, buf contains less pixels,
1625 // we need more bufs to paint a given area and as a result it's even a bit slower
1627     cairo_surface_t* cst = cairo_image_surface_create_for_data (
1628         buf.buf,
1629         CAIRO_FORMAT_RGB24,  // unpacked, i.e. 32 bits! one byte is unused
1630         x1 - x0, y1 - y0,
1631         buf.buf_rowstride
1632         );
1633         cairo_t *ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1634         cairo_set_source_surface (ct, cst, x0 - canvas->x0, y0 - canvas->y0);
1635         cairo_paint (ct);
1636     cairo_destroy (ct);
1637     cairo_surface_finish (cst);
1638     cairo_surface_destroy (cst);
1639 */
1641 #if ENABLE_LCMS
1642         if ( transf && canvas->enable_cms_display_adj ) {
1643             for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1644                 guchar* p = buf.buf + (sw * 3) * yy;
1645                 cmsDoTransform( transf, p, p, (x1 - x0) );
1646             }
1647         }
1648 #endif // ENABLE_LCMS
1650         gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1651                                       canvas->pixmap_gc,
1652                                       x0 - canvas->x0, y0 - canvas->y0,
1653                                       x1 - x0, y1 - y0,
1654                                       GDK_RGB_DITHER_MAX,
1655                                       buf.buf,
1656                                       sw * 3,
1657                                       x0 - canvas->x0, y0 - canvas->y0);
1658     }
1660     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1661         nr_pixelstore_256K_free (buf.buf);
1662     } else {
1663         nr_pixelstore_1M_free (buf.buf);
1664     }
1667 struct PaintRectSetup {
1668     SPCanvas* canvas;
1669     NRRectL big_rect;
1670     GTimeVal start_time;
1671     int max_pixels;
1672     NR::Point mouse_loc;
1673 };
1675 /**
1676  * Paint the given rect, recursively subdividing the region until it is the size of a single
1677  * buffer.
1678  *
1679  * @return true if the drawing completes
1680  */
1681 static int
1682 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1684     GTimeVal now;
1685     g_get_current_time (&now);
1687     glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1688         + (now.tv_usec - setup->start_time.tv_usec);
1690     // Allow only very fast buffers to be run together;
1691     // as soon as the total redraw time exceeds 1ms, cancel;
1692     // this returns control to the idle loop and allows Inkscape to process user input
1693     // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1694     // it will get back and finish painting what remains to paint.
1695     if (elapsed > 1000) {
1697         // Interrupting redraw isn't always good.
1698         // For example, when you drag one node of a big path, only the buffer containing
1699         // the mouse cursor will be redrawn again and again, and the rest of the path
1700         // will remain stale because Inkscape never has enough idle time to redraw all
1701         // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1702         // If this limit is set, and if we have aborted redraw more times than is allowed,
1703         // interrupting is blocked and we're forced to redraw full screen once
1704         // (after which we can again interrupt forced_redraw_limit times).
1705         if (setup->canvas->forced_redraw_limit < 0 ||
1706             setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1708             if (setup->canvas->forced_redraw_limit != -1) {
1709                 setup->canvas->forced_redraw_count++;
1710             }
1712             return false;
1713         }
1714     }
1716     // Find the optimal buffer dimensions
1717     int bw = this_rect.x1 - this_rect.x0;
1718     int bh = this_rect.y1 - this_rect.y0;
1719     if ((bw < 1) || (bh < 1))
1720         return 0;
1722     if (bw * bh < setup->max_pixels) {
1723         // We are small enough
1724         sp_canvas_paint_single_buffer (setup->canvas,
1725                                        this_rect.x0, this_rect.y0,
1726                                        this_rect.x1, this_rect.y1,
1727                                        setup->big_rect.x0, setup->big_rect.y0,
1728                                        setup->big_rect.x1, setup->big_rect.y1, bw);
1729         return 1;
1730     }
1732     NRRectL lo = this_rect;
1733     NRRectL hi = this_rect;
1735 /*
1736 This test determines the redraw strategy:
1738 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1739 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1740 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1741 and seems to be faster for drawings with many smaller objects at zoom-out.
1743 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1744 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1745 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1746 faster.
1748 The default for now is the strips mode.
1749 */
1750     if (bw < bh || bh < 2 * TILE_SIZE) {
1751         // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1752         int mid = ((long long) this_rect.x0 + (long long) this_rect.x1) / 2;
1753         // Make sure that mid lies on a tile boundary
1754         mid = (mid / TILE_SIZE) * TILE_SIZE;
1756         lo.x1 = mid;
1757         hi.x0 = mid;
1759         if (setup->mouse_loc[NR::X] < mid) {
1760             // Always paint towards the mouse first
1761             return sp_canvas_paint_rect_internal(setup, lo)
1762                 && sp_canvas_paint_rect_internal(setup, hi);
1763         } else {
1764             return sp_canvas_paint_rect_internal(setup, hi)
1765                 && sp_canvas_paint_rect_internal(setup, lo);
1766         }
1767     } else {
1768         // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1769         int mid = ((long long) this_rect.y0 + (long long) this_rect.y1) / 2;
1770         // Make sure that mid lies on a tile boundary
1771         mid = (mid / TILE_SIZE) * TILE_SIZE;
1773         lo.y1 = mid;
1774         hi.y0 = mid;
1776         if (setup->mouse_loc[NR::Y] < mid) {
1777             // Always paint towards the mouse first
1778             return sp_canvas_paint_rect_internal(setup, lo)
1779                 && sp_canvas_paint_rect_internal(setup, hi);
1780         } else {
1781             return sp_canvas_paint_rect_internal(setup, hi)
1782                 && sp_canvas_paint_rect_internal(setup, lo);
1783         }
1784     }
1788 /**
1789  * Helper that draws a specific rectangular part of the canvas.
1790  *
1791  * @return true if the rectangle painting succeeds.
1792  */
1793 static bool
1794 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1796     g_return_val_if_fail (!canvas->need_update, false);
1798     NRRectL rect;
1799     rect.x0 = xx0;
1800     rect.x1 = xx1;
1801     rect.y0 = yy0;
1802     rect.y1 = yy1;
1804     // Clip rect-to-draw by the current visible area
1805     rect.x0 = MAX (rect.x0, canvas->x0);
1806     rect.y0 = MAX (rect.y0, canvas->y0);
1807     rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1808     rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1810 #ifdef DEBUG_REDRAW
1811     // paint the area to redraw yellow
1812     gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1813     gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1814                         canvas->pixmap_gc,
1815                         TRUE,
1816                         rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1817                         rect.x1 - rect.x0, rect.y1 - rect.y0);
1818 #endif
1820     PaintRectSetup setup;
1822     setup.canvas = canvas;
1823     setup.big_rect = rect;
1825     // Save the mouse location
1826     gint x, y;
1827     gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1828     setup.mouse_loc = sp_canvas_window_to_world (canvas, NR::Point(x,y));
1830     // CAIRO FIXME: the sw/sh calculations below all assume 24bpp, need fixing for 32bpp
1831     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1832         // use 256K as a compromise to not slow down gradients
1833         // 256K is the cached buffer and we need 3 channels
1834         setup.max_pixels = 87381; // 256K/3
1835     } else {
1836         // paths only, so 1M works faster
1837         // 1M is the cached buffer and we need 3 channels
1838         setup.max_pixels = 349525;
1839     }
1841     // Start the clock
1842     g_get_current_time(&(setup.start_time));
1844     // Go
1845     return sp_canvas_paint_rect_internal(&setup, rect);
1848 /**
1849  * Force a full redraw after a specified number of interrupted redraws
1850  */
1851 void
1852 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1853   g_return_if_fail(canvas != NULL);
1855   canvas->forced_redraw_limit = count;
1856   canvas->forced_redraw_count = 0;
1859 /**
1860  * End forced full redraw requests
1861  */
1862 void
1863 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1864   g_return_if_fail(canvas != NULL);
1866   canvas->forced_redraw_limit = -1;
1869 /**
1870  * The canvas widget's expose callback.
1871  */
1872 static gint
1873 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1875     SPCanvas *canvas = SP_CANVAS (widget);
1877     if (!GTK_WIDGET_DRAWABLE (widget) ||
1878         (event->window != SP_CANVAS_WINDOW (canvas)))
1879         return FALSE;
1881     int n_rects;
1882     GdkRectangle *rects;
1883     gdk_region_get_rectangles (event->region, &rects, &n_rects);
1885     for (int i = 0; i < n_rects; i++) {
1886         NRRectL rect;
1888         rect.x0 = rects[i].x + canvas->x0;
1889         rect.y0 = rects[i].y + canvas->y0;
1890         rect.x1 = rect.x0 + rects[i].width;
1891         rect.y1 = rect.y0 + rects[i].height;
1893         sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1894     }
1896     if (n_rects > 0)
1897         g_free (rects);
1899     return FALSE;
1902 /**
1903  * The canvas widget's keypress callback.
1904  */
1905 static gint
1906 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1908     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1911 /**
1912  * Crossing event handler for the canvas.
1913  */
1914 static gint
1915 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1917     SPCanvas *canvas = SP_CANVAS (widget);
1919     if (event->window != SP_CANVAS_WINDOW (canvas))
1920         return FALSE;
1922     canvas->state = event->state;
1923     return pick_current_item (canvas, (GdkEvent *) event);
1926 /**
1927  * Focus in handler for the canvas.
1928  */
1929 static gint
1930 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
1932     GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1934     SPCanvas *canvas = SP_CANVAS (widget);
1936     if (canvas->focused_item) {
1937         return emit_event (canvas, (GdkEvent *) event);
1938     } else {
1939         return FALSE;
1940     }
1943 /**
1944  * Focus out handler for the canvas.
1945  */
1946 static gint
1947 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
1949     GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1951     SPCanvas *canvas = SP_CANVAS (widget);
1953     if (canvas->focused_item)
1954         return emit_event (canvas, (GdkEvent *) event);
1955     else
1956         return FALSE;
1959 /**
1960  * Helper that repaints the areas in the canvas that need it.
1961  *
1962  * @return true if all the dirty parts have been redrawn
1963  */
1964 static int
1965 paint (SPCanvas *canvas)
1967     if (canvas->need_update) {
1968         sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
1969         canvas->need_update = FALSE;
1970     }
1972     if (!canvas->need_redraw)
1973         return TRUE;
1975     Gdk::Region to_paint;
1977     for (int j=canvas->tTop; j<canvas->tBottom; j++) {
1978         for (int i=canvas->tLeft; i<canvas->tRight; i++) {
1979             int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
1981             if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
1982                 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
1983                                    TILE_SIZE, TILE_SIZE));
1984             }
1986         }
1987     }
1989     if (!to_paint.empty()) {
1990         Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
1991         typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
1992         for (Iter i=rect.begin(); i != rect.end(); ++i) {
1993             int x0 = (*i).get_x();
1994             int y0 = (*i).get_y();
1995             int x1 = x0 + (*i).get_width();
1996             int y1 = y0 + (*i).get_height();
1997             if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
1998                 // Aborted
1999                 return FALSE;
2000             };
2001         }
2002     }
2004     canvas->need_redraw = FALSE;
2006     // we've had a full unaborted redraw, reset the full redraw counter
2007     if (canvas->forced_redraw_limit != -1) {
2008         canvas->forced_redraw_count = 0;
2009     }
2011     return TRUE;
2014 /**
2015  * Helper that invokes update, paint, and repick on canvas.
2016  */
2017 static int
2018 do_update (SPCanvas *canvas)
2020     if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop durring interrupted display!
2021         return TRUE;
2023     /* Cause the update if necessary */
2024     if (canvas->need_update) {
2025         sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2026         canvas->need_update = FALSE;
2027     }
2029     /* Paint if able to */
2030     if (GTK_WIDGET_DRAWABLE (canvas)) {
2031             return paint (canvas);
2032     }
2034     /* Pick new current item */
2035     while (canvas->need_repick) {
2036         canvas->need_repick = FALSE;
2037         pick_current_item (canvas, &canvas->pick_event);
2038     }
2040     return TRUE;
2043 /**
2044  * Idle handler for the canvas that deals with pending updates and redraws.
2045  */
2046 static gint
2047 idle_handler (gpointer data)
2049     GDK_THREADS_ENTER ();
2051     SPCanvas *canvas = SP_CANVAS (data);
2053     int const ret = do_update (canvas);
2055     if (ret) {
2056         /* Reset idle id */
2057         canvas->idle_id = 0;
2058     }
2060     GDK_THREADS_LEAVE ();
2062     return !ret;
2065 /**
2066  * Convenience function to add an idle handler to a canvas.
2067  */
2068 static void
2069 add_idle (SPCanvas *canvas)
2071     if (canvas->idle_id != 0)
2072         return;
2074     canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2077 /**
2078  * Returns the root group of the specified canvas.
2079  */
2080 SPCanvasGroup *
2081 sp_canvas_root (SPCanvas *canvas)
2083     g_return_val_if_fail (canvas != NULL, NULL);
2084     g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2086     return SP_CANVAS_GROUP (canvas->root);
2089 /**
2090  * Scrolls canvas to specific position (cx and cy are measured in screen pixels)
2091  */
2092 void
2093 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2095     g_return_if_fail (canvas != NULL);
2096     g_return_if_fail (SP_IS_CANVAS (canvas));
2098     int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels)
2099     int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do!
2100     int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the 
2101     int dy = iy - canvas->y0; // canvas w.r.t its previous position
2103     canvas->dx0 = cx; // here the 'd' stands for double, not delta!
2104     canvas->dy0 = cy;
2105     canvas->x0 = ix;
2106     canvas->y0 = iy;
2108     sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2110     if (!clear) {
2111         // scrolling without zoom; redraw only the newly exposed areas
2112         if ((dx != 0) || (dy != 0)) {
2113             canvas->is_scrolling = is_scrolling;
2114             if (GTK_WIDGET_REALIZED (canvas)) {
2115                 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2116             }
2117         }
2118     } else {
2119         // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2120     }
2123 /**
2124  * Updates canvas if necessary.
2125  */
2126 void
2127 sp_canvas_update_now (SPCanvas *canvas)
2129     g_return_if_fail (canvas != NULL);
2130     g_return_if_fail (SP_IS_CANVAS (canvas));
2132     if (!(canvas->need_update ||
2133           canvas->need_redraw))
2134         return;
2136     do_update (canvas);
2139 /**
2140  * Update callback for canvas widget.
2141  */
2142 static void
2143 sp_canvas_request_update (SPCanvas *canvas)
2145     canvas->need_update = TRUE;
2146     add_idle (canvas);
2149 /**
2150  * Forces redraw of rectangular canvas area.
2151  */
2152 void
2153 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2155     NRRectL bbox;
2156     NRRectL visible;
2157     NRRectL clip;
2159     g_return_if_fail (canvas != NULL);
2160     g_return_if_fail (SP_IS_CANVAS (canvas));
2162     if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2163     if ((x0 >= x1) || (y0 >= y1)) return;
2165     bbox.x0 = x0;
2166     bbox.y0 = y0;
2167     bbox.x1 = x1;
2168     bbox.y1 = y1;
2170     visible.x0 = canvas->x0;
2171     visible.y0 = canvas->y0;
2172     visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2173     visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2175     nr_rect_l_intersect (&clip, &bbox, &visible);
2177     sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2178     add_idle (canvas);
2181 /**
2182  * Sets world coordinates from win and canvas.
2183  */
2184 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2186     g_return_if_fail (canvas != NULL);
2187     g_return_if_fail (SP_IS_CANVAS (canvas));
2189     if (worldx) *worldx = canvas->x0 + winx;
2190     if (worldy) *worldy = canvas->y0 + winy;
2193 /**
2194  * Sets win coordinates from world and canvas.
2195  */
2196 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2198     g_return_if_fail (canvas != NULL);
2199     g_return_if_fail (SP_IS_CANVAS (canvas));
2201     if (winx) *winx = worldx - canvas->x0;
2202     if (winy) *winy = worldy - canvas->y0;
2205 /**
2206  * Converts point from win to world coordinates.
2207  */
2208 NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win)
2210     g_assert (canvas != NULL);
2211     g_assert (SP_IS_CANVAS (canvas));
2213     return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2216 /**
2217  * Converts point from world to win coordinates.
2218  */
2219 NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world)
2221     g_assert (canvas != NULL);
2222     g_assert (SP_IS_CANVAS (canvas));
2224     return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2227 /**
2228  * Returns true if point given in world coordinates is inside window.
2229  */
2230 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world)
2232     g_assert( canvas != NULL );
2233     g_assert(SP_IS_CANVAS(canvas));
2235     using NR::X;
2236     using NR::Y;
2237     GtkWidget const &w = *GTK_WIDGET(canvas);
2238     return ( ( canvas->x0 <= world[X] )  &&
2239              ( canvas->y0 <= world[Y] )  &&
2240              ( world[X] < canvas->x0 + w.allocation.width )  &&
2241              ( world[Y] < canvas->y0 + w.allocation.height ) );
2244 /**
2245  * Return canvas window coordinates as NR::Rect.
2246  */
2247 NR::Rect SPCanvas::getViewbox() const
2249     GtkWidget const *w = GTK_WIDGET(this);
2250     return NR::Rect(NR::Point(dx0, dy0),
2251                     NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2254 /**
2255  * Return canvas window coordinates as IRect (a rectangle defined by integers).
2256  */
2257 NR::IRect SPCanvas::getViewboxIntegers() const
2259     GtkWidget const *w = GTK_WIDGET(this);
2260     return NR::IRect(NR::IPoint(x0, y0),
2261                     NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height));
2264 inline int sp_canvas_tile_floor(int x)
2266     return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2269 inline int sp_canvas_tile_ceil(int x)
2271     return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2274 /**
2275  * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2276  */
2277 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2279     if ( nl >= nr || nt >= nb ) {
2280         if ( canvas->tiles ) g_free(canvas->tiles);
2281         canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2282         canvas->tileH=canvas->tileV=0;
2283         canvas->tiles=NULL;
2284         return;
2285     }
2286     int tl=sp_canvas_tile_floor(nl);
2287     int tt=sp_canvas_tile_floor(nt);
2288     int tr=sp_canvas_tile_ceil(nr);
2289     int tb=sp_canvas_tile_ceil(nb);
2291     int nh = tr-tl, nv = tb-tt;
2292     uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2293     for (int i=tl; i<tr; i++) {
2294         for (int j=tt; j<tb; j++) {
2295             int ind = (i-tl) + (j-tt)*nh;
2296             if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2297                 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2298             } else {
2299                 ntiles[ind]=0; // newly exposed areas get 0
2300             }
2301         }
2302     }
2303     if ( canvas->tiles ) g_free(canvas->tiles);
2304     canvas->tiles=ntiles;
2305     canvas->tLeft=tl;
2306     canvas->tTop=tt;
2307     canvas->tRight=tr;
2308     canvas->tBottom=tb;
2309     canvas->tileH=nh;
2310     canvas->tileV=nv;
2313 /*
2314  * Helper that queues a canvas rectangle for redraw
2315  */
2316 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2317     canvas->need_redraw = TRUE;
2319     sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2322 /**
2323  * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2324  */
2325 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2327     if ( nl >= nr || nt >= nb ) {
2328         return;
2329     }
2330     int tl=sp_canvas_tile_floor(nl);
2331     int tt=sp_canvas_tile_floor(nt);
2332     int tr=sp_canvas_tile_ceil(nr);
2333     int tb=sp_canvas_tile_ceil(nb);
2334     if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2335     if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2336     if ( tr > canvas->tRight ) tr=canvas->tRight;
2337     if ( tt < canvas->tTop ) tt=canvas->tTop;
2338     if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2340     for (int i=tl; i<tr; i++) {
2341         for (int j=tt; j<tb; j++) {
2342             canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2343         }
2344     }
2348 /*
2349   Local Variables:
2350   mode:c++
2351   c-file-style:"stroustrup"
2352   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2353   indent-tabs-mode:nil
2354   fill-column:99
2355   End:
2356 */
2357 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :