Code

whitespace
[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"
43 #include "libnr/nr-blit.h"
44 #include "display/inkscape-cairo.h"
46 // Define this to visualize the regions to be redrawn
47 //#define DEBUG_REDRAW 1;
49 // Tiles are a way to minimize the number of redraws, eliminating too small redraws.
50 // The canvas stores a 2D array of ints, each representing a TILE_SIZExTILE_SIZE pixels tile.
51 // If any part of it is dirtied, the entire tile is dirtied (its int is nonzero) and repainted.
52 #define TILE_SIZE 16
54 static gint const sp_canvas_update_priority = G_PRIORITY_HIGH_IDLE;
56 #define SP_CANVAS_WINDOW(c) (((GtkWidget *) (c))->window)
58 enum {
59     SP_CANVAS_ITEM_VISIBLE = 1 << 7,
60     SP_CANVAS_ITEM_NEED_UPDATE = 1 << 8,
61     SP_CANVAS_ITEM_NEED_AFFINE = 1 << 9
62 };
64 /**
65  * A group of Items.
66  */
67 struct SPCanvasGroup {
68     SPCanvasItem item;
70     GList *items, *last;
71 };
73 /**
74  * The SPCanvasGroup vtable.
75  */
76 struct SPCanvasGroupClass {
77     SPCanvasItemClass parent_class;
78 };
80 /**
81  * The SPCanvas vtable.
82  */
83 struct SPCanvasClass {
84     GtkWidgetClass parent_class;
85 };
87 static void group_add (SPCanvasGroup *group, SPCanvasItem *item);
88 static void group_remove (SPCanvasGroup *group, SPCanvasItem *item);
90 /* SPCanvasItem */
92 enum {ITEM_EVENT, ITEM_LAST_SIGNAL};
95 static void sp_canvas_request_update (SPCanvas *canvas);
97 static void sp_canvas_item_class_init (SPCanvasItemClass *klass);
98 static void sp_canvas_item_init (SPCanvasItem *item);
99 static void sp_canvas_item_dispose (GObject *object);
100 static void sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args);
102 static int emit_event (SPCanvas *canvas, GdkEvent *event);
104 static guint item_signals[ITEM_LAST_SIGNAL] = { 0 };
106 static GtkObjectClass *item_parent_class;
108 /**
109  * Registers the SPCanvasItem class with Glib and returns its type number.
110  */
111 GType
112 sp_canvas_item_get_type (void)
114     static GType type = 0;
115     if (!type) {
116         static GTypeInfo const info = {
117             sizeof (SPCanvasItemClass),
118             NULL, NULL,
119             (GClassInitFunc) sp_canvas_item_class_init,
120             NULL, NULL,
121             sizeof (SPCanvasItem),
122             0,
123             (GInstanceInitFunc) sp_canvas_item_init,
124             NULL
125         };
126         type = g_type_register_static (GTK_TYPE_OBJECT, "SPCanvasItem", &info, (GTypeFlags)0);
127     }
129     return type;
132 /**
133  * Initializes the SPCanvasItem vtable and the "event" signal.
134  */
135 static void
136 sp_canvas_item_class_init (SPCanvasItemClass *klass)
138     GObjectClass *object_class = (GObjectClass *) klass;
140     /* fixme: Derive from GObject */
141     item_parent_class = (GtkObjectClass*)gtk_type_class (GTK_TYPE_OBJECT);
143     item_signals[ITEM_EVENT] = g_signal_new ("event",
144                                              G_TYPE_FROM_CLASS (klass),
145                                              G_SIGNAL_RUN_LAST,
146                                              ((glong)((guint8*)&(klass->event) - (guint8*)klass)),
147                                              NULL, NULL,
148                                              sp_marshal_BOOLEAN__POINTER,
149                                              G_TYPE_BOOLEAN, 1,
150                                              GDK_TYPE_EVENT);
152     object_class->dispose = sp_canvas_item_dispose;
155 /**
156  * Callback for initialization of SPCanvasItem.
157  */
158 static void
159 sp_canvas_item_init (SPCanvasItem *item)
161     item->flags |= SP_CANVAS_ITEM_VISIBLE;
162     item->xform = NR::Matrix(NR::identity());
165 /**
166  * Constructs new SPCanvasItem on SPCanvasGroup.
167  */
168 SPCanvasItem *
169 sp_canvas_item_new (SPCanvasGroup *parent, GtkType type, gchar const *first_arg_name, ...)
171     va_list args;
173     g_return_val_if_fail (parent != NULL, NULL);
174     g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL);
175     g_return_val_if_fail (gtk_type_is_a (type, sp_canvas_item_get_type ()), NULL);
177     SPCanvasItem *item = SP_CANVAS_ITEM (gtk_type_new (type));
179     va_start (args, first_arg_name);
180     sp_canvas_item_construct (item, parent, first_arg_name, args);
181     va_end (args);
183     return item;
186 /**
187  * Sets up the newly created SPCanvasItem.
188  *
189  * We make it static for encapsulation reasons since it was nowhere used.
190  */
191 static void
192 sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args)
194     g_return_if_fail (SP_IS_CANVAS_GROUP (parent));
195     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
197     item->parent = SP_CANVAS_ITEM (parent);
198     item->canvas = item->parent->canvas;
200     g_object_set_valist (G_OBJECT (item), first_arg_name, args);
202     group_add (SP_CANVAS_GROUP (item->parent), item);
204     sp_canvas_item_request_update (item);
207 /**
208  * Helper function that requests redraw only if item's visible flag is set.
209  */
210 static void
211 redraw_if_visible (SPCanvasItem *item)
213     if (item->flags & SP_CANVAS_ITEM_VISIBLE) {
214         int x0 = (int)(item->x1);
215         int x1 = (int)(item->x2);
216         int y0 = (int)(item->y1);
217         int y1 = (int)(item->y2);
219         if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
220             sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
221         }
222     }
225 /**
226  * Callback that removes item from all referers and destroys it.
227  */
228 static void
229 sp_canvas_item_dispose (GObject *object)
231     SPCanvasItem *item = SP_CANVAS_ITEM (object);
233     // Hack: if this is a ctrlrect, move it to 0,0;
234     // this redraws only the stroke of the rect to be deleted,
235     // avoiding redraw of the entire area
236     if (SP_IS_CTRLRECT(item)) {
237         SP_CTRLRECT(object)->setRectangle(NR::Rect(NR::Point(0,0),NR::Point(0,0)));
238         SP_CTRLRECT(object)->update(item->xform, 0);
239     } else {
240         redraw_if_visible (item);
241     }
242     item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
244     if (item == item->canvas->current_item) {
245         item->canvas->current_item = NULL;
246         item->canvas->need_repick = TRUE;
247     }
249     if (item == item->canvas->new_current_item) {
250         item->canvas->new_current_item = NULL;
251         item->canvas->need_repick = TRUE;
252     }
254     if (item == item->canvas->grabbed_item) {
255         item->canvas->grabbed_item = NULL;
256         gdk_pointer_ungrab (GDK_CURRENT_TIME);
257     }
259     if (item == item->canvas->focused_item)
260         item->canvas->focused_item = NULL;
262     if (item->parent) {
263         group_remove (SP_CANVAS_GROUP (item->parent), item);
264     }
266     G_OBJECT_CLASS (item_parent_class)->dispose (object);
269 /**
270  * Helper function to update item and its children.
271  *
272  * NB! affine is parent2canvas.
273  */
274 static void
275 sp_canvas_item_invoke_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
277     /* Apply the child item's transform */
278     NR::Matrix child_affine = item->xform * affine;
280     /* apply object flags to child flags */
281     int child_flags = flags & ~SP_CANVAS_UPDATE_REQUESTED;
283     if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
284         child_flags |= SP_CANVAS_UPDATE_REQUESTED;
286     if (item->flags & SP_CANVAS_ITEM_NEED_AFFINE)
287         child_flags |= SP_CANVAS_UPDATE_AFFINE;
289     if (child_flags & (SP_CANVAS_UPDATE_REQUESTED | SP_CANVAS_UPDATE_AFFINE)) {
290         if (SP_CANVAS_ITEM_GET_CLASS (item)->update)
291             SP_CANVAS_ITEM_GET_CLASS (item)->update (item, child_affine, child_flags);
292     }
294     GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_UPDATE);
295     GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_AFFINE);
298 /**
299  * Helper function to invoke the point method of the item.
300  *
301  * The argument x, y should be in the parent's item-relative coordinate
302  * system.  This routine applies the inverse of the item's transform,
303  * maintaining the affine invariant.
304  */
305 static double
306 sp_canvas_item_invoke_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
308     if (SP_CANVAS_ITEM_GET_CLASS (item)->point)
309         return SP_CANVAS_ITEM_GET_CLASS (item)->point (item, p, actual_item);
311     return NR_HUGE;
314 /**
315  * Makes the item's affine transformation matrix be equal to the specified
316  * matrix.
317  *
318  * @item: A canvas item.
319  * @affine: An affine transformation matrix.
320  */
321 void
322 sp_canvas_item_affine_absolute (SPCanvasItem *item, NR::Matrix const &affine)
324     item->xform = affine;
326     if (!(item->flags & SP_CANVAS_ITEM_NEED_AFFINE)) {
327         item->flags |= SP_CANVAS_ITEM_NEED_AFFINE;
328         if (item->parent != NULL) {
329             sp_canvas_item_request_update (item->parent);
330         } else {
331             sp_canvas_request_update (item->canvas);
332         }
333     }
335     item->canvas->need_repick = TRUE;
338 /**
339  * Convenience function to reorder items in a group's child list.
340  *
341  * This puts the specified link after the "before" link.
342  */
343 static void
344 put_item_after (GList *link, GList *before)
346     if (link == before)
347         return;
349     SPCanvasGroup *parent = SP_CANVAS_GROUP (SP_CANVAS_ITEM (link->data)->parent);
351     if (before == NULL) {
352         if (link == parent->items) return;
354         link->prev->next = link->next;
356         if (link->next) {
357             link->next->prev = link->prev;
358         } else {
359             parent->last = link->prev;
360         }
362         link->prev = before;
363         link->next = parent->items;
364         link->next->prev = link;
365         parent->items = link;
366     } else {
367         if ((link == parent->last) && (before == parent->last->prev))
368             return;
370         if (link->next)
371             link->next->prev = link->prev;
373         if (link->prev)
374             link->prev->next = link->next;
375         else {
376             parent->items = link->next;
377             parent->items->prev = NULL;
378         }
380         link->prev = before;
381         link->next = before->next;
383         link->prev->next = link;
385         if (link->next)
386             link->next->prev = link;
387         else
388             parent->last = link;
389     }
393 /**
394  * Raises the item in its parent's stack by the specified number of positions.
395  *
396  * \param item A canvas item.
397  * \param positions Number of steps to raise the item.
398  *
399  * If the number of positions is greater than the distance to the top of the
400  * stack, then the item is put at the top.
401  */
402 void
403 sp_canvas_item_raise (SPCanvasItem *item, int positions)
405     g_return_if_fail (item != NULL);
406     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
407     g_return_if_fail (positions >= 0);
409     if (!item->parent || positions == 0)
410         return;
412     SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
413     GList *link = g_list_find (parent->items, item);
414     g_assert (link != NULL);
416     GList *before;
417     for (before = link; positions && before; positions--)
418         before = before->next;
420     if (!before)
421         before = parent->last;
423     put_item_after (link, before);
425     redraw_if_visible (item);
426     item->canvas->need_repick = TRUE;
430 /**
431  * Lowers the item in its parent's stack by the specified number of positions.
432  *
433  * \param item A canvas item.
434  * \param positions Number of steps to lower the item.
435  *
436  * If the number of positions is greater than the distance to the bottom of the
437  * stack, then the item is put at the bottom.
438  **/
439 void
440 sp_canvas_item_lower (SPCanvasItem *item, int positions)
442     g_return_if_fail (item != NULL);
443     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
444     g_return_if_fail (positions >= 1);
446     if (!item->parent || positions == 0)
447         return;
449     SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
450     GList *link = g_list_find (parent->items, item);
451     g_assert (link != NULL);
453     GList *before;
454     if (link->prev)
455         for (before = link->prev; positions && before; positions--)
456             before = before->prev;
457     else
458         before = NULL;
460     put_item_after (link, before);
462     redraw_if_visible (item);
463     item->canvas->need_repick = TRUE;
466 /**
467  * Sets visible flag on item and requests a redraw.
468  */
469 void
470 sp_canvas_item_show (SPCanvasItem *item)
472     g_return_if_fail (item != NULL);
473     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
475     if (item->flags & SP_CANVAS_ITEM_VISIBLE)
476         return;
478     item->flags |= SP_CANVAS_ITEM_VISIBLE;
480     int x0 = (int)(item->x1);
481     int x1 = (int)(item->x2);
482     int y0 = (int)(item->y1);
483     int y1 = (int)(item->y2);
485     if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
486         sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
487         item->canvas->need_repick = TRUE;
488     }
491 /**
492  * Clears visible flag on item and requests a redraw.
493  */
494 void
495 sp_canvas_item_hide (SPCanvasItem *item)
497     g_return_if_fail (item != NULL);
498     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
500     if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
501         return;
503     item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
505     int x0 = (int)(item->x1);
506     int x1 = (int)(item->x2);
507     int y0 = (int)(item->y1);
508     int y1 = (int)(item->y2);
510     if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
511         sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
512         item->canvas->need_repick = TRUE;
513     }
516 /**
517  * Grab item under cursor.
518  *
519  * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE
520  */
521 int
522 sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
524     g_return_val_if_fail (item != NULL, -1);
525     g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1);
526     g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1);
528     if (item->canvas->grabbed_item)
529         return -1;
531     if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
532         return -1;
534     /* fixme: Top hack (Lauris) */
535     /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
536     /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
537     gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
538                       (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
539                       NULL, cursor, etime);
541     item->canvas->grabbed_item = item;
542     item->canvas->grabbed_event_mask = event_mask;
543     item->canvas->current_item = item; /* So that events go to the grabbed item */
545     return 0;
548 /**
549  * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
550  * mouse.
551  *
552  * \param item A canvas item that holds a grab.
553  * \param etime The timestamp for ungrabbing the mouse.
554  */
555 void
556 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
558     g_return_if_fail (item != NULL);
559     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
561     if (item->canvas->grabbed_item != item)
562         return;
564     item->canvas->grabbed_item = NULL;
566     gdk_pointer_ungrab (etime);
569 /**
570  * Returns the product of all transformation matrices from the root item down
571  * to the item.
572  */
573 NR::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
575     g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
577     NR::Matrix affine = NR::identity();
579     while (item) {
580         affine *= item->xform;
581         item = item->parent;
582     }
583     return affine;
586 /**
587  * Helper that returns true iff item is descendant of parent.
588  */
589 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
591     while (item) {
592         if (item == parent)
593             return true;
594         item = item->parent;
595     }
597     return false;
600 /**
601  * Focus canvas, and item under cursor if it is not already focussed.
602  */
603 void
604 sp_canvas_item_grab_focus (SPCanvasItem *item)
606     g_return_if_fail (item != NULL);
607     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
608     g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
610     SPCanvasItem *focused_item = item->canvas->focused_item;
612     if (focused_item) {
613         GdkEvent ev;
614         ev.focus_change.type = GDK_FOCUS_CHANGE;
615         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
616         ev.focus_change.send_event = FALSE;
617         ev.focus_change.in = FALSE;
619         emit_event (item->canvas, &ev);
620     }
622     item->canvas->focused_item = item;
623     gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
625     if (focused_item) {
626         GdkEvent ev;
627         ev.focus_change.type = GDK_FOCUS_CHANGE;
628         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
629         ev.focus_change.send_event = FALSE;
630         ev.focus_change.in = TRUE;
632         emit_event (item->canvas, &ev);
633     }
636 /**
637  * Requests that the canvas queue an update for the specified item.
638  *
639  * To be used only by item implementations.
640  */
641 void
642 sp_canvas_item_request_update (SPCanvasItem *item)
644     if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
645         return;
647     item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
649     if (item->parent != NULL) {
650         /* Recurse up the tree */
651         sp_canvas_item_request_update (item->parent);
652     } else {
653         /* Have reached the top of the tree, make sure the update call gets scheduled. */
654         sp_canvas_request_update (item->canvas);
655     }
658 /**
659  * Returns position of item in group.
660  */
661 gint sp_canvas_item_order (SPCanvasItem * item)
663     return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
666 /* SPCanvasGroup */
668 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
669 static void sp_canvas_group_init (SPCanvasGroup *group);
670 static void sp_canvas_group_destroy (GtkObject *object);
672 static void sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
673 static double sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item);
674 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
676 static SPCanvasItemClass *group_parent_class;
678 /**
679  * Registers SPCanvasGroup class with Gtk and returns its type number.
680  */
681 GType sp_canvas_group_get_type(void)
683     static GType type = 0;
684     if (!type) {
685         GTypeInfo info = {
686             sizeof(SPCanvasGroupClass),
687             0, // base_init
688             0, // base_finalize
689             (GClassInitFunc)sp_canvas_group_class_init,
690             0, // class_finalize
691             0, // class_data
692             sizeof(SPCanvasGroup),
693             0, // n_preallocs
694             (GInstanceInitFunc)sp_canvas_group_init,
695             0 // value_table
696         };
697         type = g_type_register_static(sp_canvas_item_get_type(), "SPCanvasGroup", &info, static_cast<GTypeFlags>(0));
698     }
699     return type;
702 /**
703  * Class initialization function for SPCanvasGroupClass
704  */
705 static void
706 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
708     GtkObjectClass *object_class = (GtkObjectClass *) klass;
709     SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
711     group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
713     object_class->destroy = sp_canvas_group_destroy;
715     item_class->update = sp_canvas_group_update;
716     item_class->render = sp_canvas_group_render;
717     item_class->point = sp_canvas_group_point;
720 /**
721  * Callback. Empty.
722  */
723 static void
724 sp_canvas_group_init (SPCanvasGroup */*group*/)
726     /* Nothing here */
729 /**
730  * Callback that destroys all items in group and calls group's virtual
731  * destroy() function.
732  */
733 static void
734 sp_canvas_group_destroy (GtkObject *object)
736     g_return_if_fail (object != NULL);
737     g_return_if_fail (SP_IS_CANVAS_GROUP (object));
739     SPCanvasGroup const *group = SP_CANVAS_GROUP (object);
741     GList *list = group->items;
742     while (list) {
743         SPCanvasItem *child = (SPCanvasItem *)list->data;
744         list = list->next;
746         gtk_object_destroy (GTK_OBJECT (child));
747     }
749     if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
750         (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
753 /**
754  * Update handler for canvas groups
755  */
756 static void
757 sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
759     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
760     NR::ConvexHull corners(NR::Point(0, 0));
761     bool empty=true;
763     for (GList *list = group->items; list; list = list->next) {
764         SPCanvasItem *i = (SPCanvasItem *)list->data;
766         sp_canvas_item_invoke_update (i, affine, flags);
768         if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
769             if (empty) {
770                 corners = NR::ConvexHull(NR::Point(i->x1, i->y1));
771                 empty = false;
772             } else {
773                 corners.add(NR::Point(i->x1, i->y1));
774             }
775             corners.add(NR::Point(i->x2, i->y2));
776         }
777     }
779     NR::Maybe<NR::Rect> const bounds = corners.bounds();
780     if (bounds) {
781         item->x1 = bounds->min()[NR::X];
782         item->y1 = bounds->min()[NR::Y];
783         item->x2 = bounds->max()[NR::X];
784         item->y2 = bounds->max()[NR::Y];
785     } else {
786         // FIXME ?
787         item->x1 = item->x2 = item->y1 = item->y2 = 0;
788     }
791 /**
792  * Point handler for canvas groups.
793  */
794 static double
795 sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
797     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
798     double const x = p[NR::X];
799     double const y = p[NR::Y];
800     int x1 = (int)(x - item->canvas->close_enough);
801     int y1 = (int)(y - item->canvas->close_enough);
802     int x2 = (int)(x + item->canvas->close_enough);
803     int y2 = (int)(y + item->canvas->close_enough);
805     double best = 0.0;
806     *actual_item = NULL;
808     double dist = 0.0;
810     for (GList *list = group->items; list; list = list->next) {
811         SPCanvasItem *child = (SPCanvasItem *)list->data;
813         if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
814             SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
816             int has_point;
817             if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
818                 dist = sp_canvas_item_invoke_point (child, p, &point_item);
819                 has_point = TRUE;
820             } else
821                 has_point = FALSE;
823             if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
824                 best = dist;
825                 *actual_item = point_item;
826             }
827         }
828     }
830     return best;
833 /**
834  * Renders all visible canvas group items in buf rectangle.
835  */
836 static void
837 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
839     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
841     for (GList *list = group->items; list; list = list->next) {
842         SPCanvasItem *child = (SPCanvasItem *)list->data;
843         if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
844             if ((child->x1 < buf->rect.x1) &&
845                 (child->y1 < buf->rect.y1) &&
846                 (child->x2 > buf->rect.x0) &&
847                 (child->y2 > buf->rect.y0)) {
848                 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
849                     SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
850             }
851         }
852     }
855 /**
856  * Adds an item to a canvas group.
857  */
858 static void
859 group_add (SPCanvasGroup *group, SPCanvasItem *item)
861     gtk_object_ref (GTK_OBJECT (item));
862     gtk_object_sink (GTK_OBJECT (item));
864     if (!group->items) {
865         group->items = g_list_append (group->items, item);
866         group->last = group->items;
867     } else {
868         group->last = g_list_append (group->last, item)->next;
869     }
871     sp_canvas_item_request_update (item);
874 /**
875  * Removes an item from a canvas group
876  */
877 static void
878 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
880     g_return_if_fail (group != NULL);
881     g_return_if_fail (SP_IS_CANVAS_GROUP (group));
882     g_return_if_fail (item != NULL);
884     for (GList *children = group->items; children; children = children->next) {
885         if (children->data == item) {
887             /* Unparent the child */
889             item->parent = NULL;
890             gtk_object_unref (GTK_OBJECT (item));
892             /* Remove it from the list */
894             if (children == group->last) group->last = children->prev;
896             group->items = g_list_remove_link (group->items, children);
897             g_list_free (children);
898             break;
899         }
900     }
903 /* SPCanvas */
905 static void sp_canvas_class_init (SPCanvasClass *klass);
906 static void sp_canvas_init (SPCanvas *canvas);
907 static void sp_canvas_destroy (GtkObject *object);
909 static void sp_canvas_realize (GtkWidget *widget);
910 static void sp_canvas_unrealize (GtkWidget *widget);
912 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
913 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
915 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
916 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
917 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
918 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
919 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
920 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
921 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
922 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
924 static GtkWidgetClass *canvas_parent_class;
926 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb);
927 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb);
928 static void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val);
929 static int do_update (SPCanvas *canvas);
931 /**
932  * Registers the SPCanvas class if necessary, and returns the type ID
933  * associated to it.
934  *
935  * \return The type ID of the SPCanvas class.
936  **/
937 GType sp_canvas_get_type(void)
939     static GType type = 0;
940     if (!type) {
941         GTypeInfo info = {
942             sizeof(SPCanvasClass),
943             0, // base_init
944             0, // base_finalize
945             (GClassInitFunc)sp_canvas_class_init,
946             0, // class_finalize
947             0, // class_data
948             sizeof(SPCanvas),
949             0, // n_preallocs
950             (GInstanceInitFunc)sp_canvas_init,
951             0 // value_table
952         };
953         type = g_type_register_static(GTK_TYPE_WIDGET, "SPCanvas", &info, static_cast<GTypeFlags>(0));
954     }
955     return type;
958 /**
959  * Class initialization function for SPCanvasClass.
960  */
961 static void
962 sp_canvas_class_init (SPCanvasClass *klass)
964     GtkObjectClass *object_class = (GtkObjectClass *) klass;
965     GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
967     canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
969     object_class->destroy = sp_canvas_destroy;
971     widget_class->realize = sp_canvas_realize;
972     widget_class->unrealize = sp_canvas_unrealize;
973     widget_class->size_request = sp_canvas_size_request;
974     widget_class->size_allocate = sp_canvas_size_allocate;
975     widget_class->button_press_event = sp_canvas_button;
976     widget_class->button_release_event = sp_canvas_button;
977     widget_class->motion_notify_event = sp_canvas_motion;
978     widget_class->scroll_event = sp_canvas_scroll;
979     widget_class->expose_event = sp_canvas_expose;
980     widget_class->key_press_event = sp_canvas_key;
981     widget_class->key_release_event = sp_canvas_key;
982     widget_class->enter_notify_event = sp_canvas_crossing;
983     widget_class->leave_notify_event = sp_canvas_crossing;
984     widget_class->focus_in_event = sp_canvas_focus_in;
985     widget_class->focus_out_event = sp_canvas_focus_out;
988 /**
989  * Callback: object initialization for SPCanvas.
990  */
991 static void
992 sp_canvas_init (SPCanvas *canvas)
994     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
995     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
996     GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
998     canvas->pick_event.type = GDK_LEAVE_NOTIFY;
999     canvas->pick_event.crossing.x = 0;
1000     canvas->pick_event.crossing.y = 0;
1002     /* Create the root item as a special case */
1003     canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
1004     canvas->root->canvas = canvas;
1006     gtk_object_ref (GTK_OBJECT (canvas->root));
1007     gtk_object_sink (GTK_OBJECT (canvas->root));
1009     canvas->need_repick = TRUE;
1011     // See comment at in sp-canvas.h.
1012     canvas->gen_all_enter_events = false;
1014     canvas->tiles=NULL;
1015     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1016     canvas->tileH=canvas->tileV=0;
1018     canvas->forced_redraw_count = 0;
1019     canvas->forced_redraw_limit = -1;
1021 #if ENABLE_LCMS
1022     canvas->enable_cms_display_adj = false;
1023     canvas->cms_key = new Glib::ustring("");
1024 #endif // ENABLE_LCMS
1026     canvas->is_scrolling = false;
1030 /**
1031  * Convenience function to remove the idle handler of a canvas.
1032  */
1033 static void
1034 remove_idle (SPCanvas *canvas)
1036     if (canvas->idle_id) {
1037         gtk_idle_remove (canvas->idle_id);
1038         canvas->idle_id = 0;
1039     }
1042 /*
1043  * Removes the transient state of the canvas (idle handler, grabs).
1044  */
1045 static void
1046 shutdown_transients (SPCanvas *canvas)
1048     /* We turn off the need_redraw flag, since if the canvas is mapped again
1049      * it will request a redraw anyways.  We do not turn off the need_update
1050      * flag, though, because updates are not queued when the canvas remaps
1051      * itself.
1052      */
1053     if (canvas->need_redraw) {
1054         canvas->need_redraw = FALSE;
1055     }
1056     if ( canvas->tiles ) g_free(canvas->tiles);
1057     canvas->tiles=NULL;
1058     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1059     canvas->tileH=canvas->tileV=0;
1061     if (canvas->grabbed_item) {
1062         canvas->grabbed_item = NULL;
1063         gdk_pointer_ungrab (GDK_CURRENT_TIME);
1064     }
1066     remove_idle (canvas);
1069 /**
1070  * Destroy handler for SPCanvas.
1071  */
1072 static void
1073 sp_canvas_destroy (GtkObject *object)
1075     SPCanvas *canvas = SP_CANVAS (object);
1077     if (canvas->root) {
1078         gtk_object_unref (GTK_OBJECT (canvas->root));
1079         canvas->root = NULL;
1080     }
1082     shutdown_transients (canvas);
1084     if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1085         (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1088 /**
1089  * Returns new canvas as widget.
1090  */
1091 GtkWidget *
1092 sp_canvas_new_aa (void)
1094     SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1096     return (GtkWidget *) canvas;
1099 /**
1100  * The canvas widget's realize callback.
1101  */
1102 static void
1103 sp_canvas_realize (GtkWidget *widget)
1105     SPCanvas *canvas = SP_CANVAS (widget);
1107     GdkWindowAttr attributes;
1108     attributes.window_type = GDK_WINDOW_CHILD;
1109     attributes.x = widget->allocation.x;
1110     attributes.y = widget->allocation.y;
1111     attributes.width = widget->allocation.width;
1112     attributes.height = widget->allocation.height;
1113     attributes.wclass = GDK_INPUT_OUTPUT;
1114     attributes.visual = gdk_rgb_get_visual ();
1115     attributes.colormap = gdk_rgb_get_cmap ();
1116     attributes.event_mask = (gtk_widget_get_events (widget) |
1117                              GDK_EXPOSURE_MASK |
1118                              GDK_BUTTON_PRESS_MASK |
1119                              GDK_BUTTON_RELEASE_MASK |
1120                              GDK_POINTER_MOTION_MASK |
1121                              GDK_PROXIMITY_IN_MASK |
1122                              GDK_PROXIMITY_OUT_MASK |
1123                              GDK_KEY_PRESS_MASK |
1124                              GDK_KEY_RELEASE_MASK |
1125                              GDK_ENTER_NOTIFY_MASK |
1126                              GDK_LEAVE_NOTIFY_MASK |
1127                              GDK_FOCUS_CHANGE_MASK);
1128     gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1130     widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1131     gdk_window_set_user_data (widget->window, widget);
1133     if ( prefs_get_int_attribute ("options.useextinput", "value", 1) )
1134         gtk_widget_set_events(widget, attributes.event_mask);
1136     widget->style = gtk_style_attach (widget->style, widget->window);
1138     GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1140     canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1143 /**
1144  * The canvas widget's unrealize callback.
1145  */
1146 static void
1147 sp_canvas_unrealize (GtkWidget *widget)
1149     SPCanvas *canvas = SP_CANVAS (widget);
1151     canvas->current_item = NULL;
1152     canvas->grabbed_item = NULL;
1153     canvas->focused_item = NULL;
1155     shutdown_transients (canvas);
1157     gdk_gc_destroy (canvas->pixmap_gc);
1158     canvas->pixmap_gc = NULL;
1160     if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1161         (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1164 /**
1165  * The canvas widget's size_request callback.
1166  */
1167 static void
1168 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1170     static_cast<void>(SP_CANVAS (widget));
1172     req->width = 256;
1173     req->height = 256;
1176 /**
1177  * The canvas widget's size_allocate callback.
1178  */
1179 static void
1180 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1182     SPCanvas *canvas = SP_CANVAS (widget);
1184     /* Schedule redraw of new region */
1185     sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1186     if (allocation->width > widget->allocation.width) {
1187         sp_canvas_request_redraw (canvas,
1188                                   canvas->x0 + widget->allocation.width,
1189                                   0,
1190                                   canvas->x0 + allocation->width,
1191                                   canvas->y0 + allocation->height);
1192     }
1193     if (allocation->height > widget->allocation.height) {
1194         sp_canvas_request_redraw (canvas,
1195                                   0,
1196                                   canvas->y0 + widget->allocation.height,
1197                                   canvas->x0 + allocation->width,
1198                                   canvas->y0 + allocation->height);
1199     }
1201     widget->allocation = *allocation;
1202     
1203     if (GTK_WIDGET_REALIZED (widget)) {
1204         gdk_window_move_resize (widget->window,
1205                                 widget->allocation.x, widget->allocation.y,
1206                                 widget->allocation.width, widget->allocation.height);
1207     }
1210 /**
1211  * Helper that emits an event for an item in the canvas, be it the current
1212  * item, grabbed item, or focused item, as appropriate.
1213  */
1214 static int
1215 emit_event (SPCanvas *canvas, GdkEvent *event)
1217     guint mask;
1219     if (canvas->grabbed_item) {
1220         switch (event->type) {
1221         case GDK_ENTER_NOTIFY:
1222             mask = GDK_ENTER_NOTIFY_MASK;
1223             break;
1224         case GDK_LEAVE_NOTIFY:
1225             mask = GDK_LEAVE_NOTIFY_MASK;
1226             break;
1227         case GDK_MOTION_NOTIFY:
1228             mask = GDK_POINTER_MOTION_MASK;
1229             break;
1230         case GDK_BUTTON_PRESS:
1231         case GDK_2BUTTON_PRESS:
1232         case GDK_3BUTTON_PRESS:
1233             mask = GDK_BUTTON_PRESS_MASK;
1234             break;
1235         case GDK_BUTTON_RELEASE:
1236             mask = GDK_BUTTON_RELEASE_MASK;
1237             break;
1238         case GDK_KEY_PRESS:
1239             mask = GDK_KEY_PRESS_MASK;
1240             break;
1241         case GDK_KEY_RELEASE:
1242             mask = GDK_KEY_RELEASE_MASK;
1243             break;
1244         case GDK_SCROLL:
1245             mask = GDK_SCROLL;
1246             break;
1247         default:
1248             mask = 0;
1249             break;
1250         }
1252         if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1253     }
1255     /* Convert to world coordinates -- we have two cases because of diferent
1256      * offsets of the fields in the event structures.
1257      */
1259     GdkEvent ev = *event;
1261     switch (ev.type) {
1262     case GDK_ENTER_NOTIFY:
1263     case GDK_LEAVE_NOTIFY:
1264         ev.crossing.x += canvas->x0;
1265         ev.crossing.y += canvas->y0;
1266         break;
1267     case GDK_MOTION_NOTIFY:
1268     case GDK_BUTTON_PRESS:
1269     case GDK_2BUTTON_PRESS:
1270     case GDK_3BUTTON_PRESS:
1271     case GDK_BUTTON_RELEASE:
1272         ev.motion.x += canvas->x0;
1273         ev.motion.y += canvas->y0;
1274         break;
1275     default:
1276         break;
1277     }
1279     /* Choose where we send the event */
1281     /* canvas->current_item becomes NULL in some cases under Win32
1282     ** (e.g. if the pointer leaves the window).  So this is a hack that
1283     ** Lauris applied to SP to get around the problem.
1284     */
1285     SPCanvasItem* item = NULL;
1286     if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1287         item = canvas->grabbed_item;
1288     } else {
1289         item = canvas->current_item;
1290     }
1292     if (canvas->focused_item &&
1293         ((event->type == GDK_KEY_PRESS) ||
1294          (event->type == GDK_KEY_RELEASE) ||
1295          (event->type == GDK_FOCUS_CHANGE))) {
1296         item = canvas->focused_item;
1297     }
1299     /* The event is propagated up the hierarchy (for if someone connected to
1300      * a group instead of a leaf event), and emission is stopped if a
1301      * handler returns TRUE, just like for GtkWidget events.
1302      */
1304     gint finished = FALSE;
1306     while (item && !finished) {
1307         gtk_object_ref (GTK_OBJECT (item));
1308         gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1309         SPCanvasItem *parent = item->parent;
1310         gtk_object_unref (GTK_OBJECT (item));
1311         item = parent;
1312     }
1314     return finished;
1317 /**
1318  * Helper that re-picks the current item in the canvas, based on the event's
1319  * coordinates and emits enter/leave events for items as appropriate.
1320  */
1321 static int
1322 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1324     int button_down = 0;
1325     double x, y;
1327     if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1328         return FALSE;
1330     int retval = FALSE;
1332     if (canvas->gen_all_enter_events == false) {
1333         // If a button is down, we'll perform enter and leave events on the
1334         // current item, but not enter on any other item.  This is more or
1335         // less like X pointer grabbing for canvas items.
1336         //
1337         button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1338                 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1340         if (!button_down) canvas->left_grabbed_item = FALSE;
1341     }
1343     /* Save the event in the canvas.  This is used to synthesize enter and
1344      * leave events in case the current item changes.  It is also used to
1345      * re-pick the current item if the current one gets deleted.  Also,
1346      * synthesize an enter event.
1347      */
1348     if (event != &canvas->pick_event) {
1349         if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1350             /* these fields have the same offsets in both types of events */
1352             canvas->pick_event.crossing.type       = GDK_ENTER_NOTIFY;
1353             canvas->pick_event.crossing.window     = event->motion.window;
1354             canvas->pick_event.crossing.send_event = event->motion.send_event;
1355             canvas->pick_event.crossing.subwindow  = NULL;
1356             canvas->pick_event.crossing.x          = event->motion.x;
1357             canvas->pick_event.crossing.y          = event->motion.y;
1358             canvas->pick_event.crossing.mode       = GDK_CROSSING_NORMAL;
1359             canvas->pick_event.crossing.detail     = GDK_NOTIFY_NONLINEAR;
1360             canvas->pick_event.crossing.focus      = FALSE;
1361             canvas->pick_event.crossing.state      = event->motion.state;
1363             /* these fields don't have the same offsets in both types of events */
1365             if (event->type == GDK_MOTION_NOTIFY) {
1366                 canvas->pick_event.crossing.x_root = event->motion.x_root;
1367                 canvas->pick_event.crossing.y_root = event->motion.y_root;
1368             } else {
1369                 canvas->pick_event.crossing.x_root = event->button.x_root;
1370                 canvas->pick_event.crossing.y_root = event->button.y_root;
1371             }
1372         } else {
1373             canvas->pick_event = *event;
1374         }
1375     }
1377     /* Don't do anything else if this is a recursive call */
1378     if (canvas->in_repick) return retval;
1380     /* LeaveNotify means that there is no current item, so we don't look for one */
1381     if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1382         /* these fields don't have the same offsets in both types of events */
1384         if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1385             x = canvas->pick_event.crossing.x;
1386             y = canvas->pick_event.crossing.y;
1387         } else {
1388             x = canvas->pick_event.motion.x;
1389             y = canvas->pick_event.motion.y;
1390         }
1392         /* world coords */
1393         x += canvas->x0;
1394         y += canvas->y0;
1396         /* find the closest item */
1397         if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1398             sp_canvas_item_invoke_point (canvas->root, NR::Point(x, y), &canvas->new_current_item);
1399         } else {
1400             canvas->new_current_item = NULL;
1401         }
1402     } else {
1403         canvas->new_current_item = NULL;
1404     }
1406     if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1407         return retval; /* current item did not change */
1408     }
1410     /* Synthesize events for old and new current items */
1412     if ((canvas->new_current_item != canvas->current_item)
1413         && (canvas->current_item != NULL)
1414         && !canvas->left_grabbed_item) {
1415         GdkEvent new_event;
1416         SPCanvasItem *item;
1418         item = canvas->current_item;
1420         new_event = canvas->pick_event;
1421         new_event.type = GDK_LEAVE_NOTIFY;
1423         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1424         new_event.crossing.subwindow = NULL;
1425         canvas->in_repick = TRUE;
1426         retval = emit_event (canvas, &new_event);
1427         canvas->in_repick = FALSE;
1428     }
1430     if (canvas->gen_all_enter_events == false) {
1431         // new_current_item may have been set to NULL during the call to
1432         // emit_event() above
1433         if ((canvas->new_current_item != canvas->current_item) && button_down) {
1434             canvas->left_grabbed_item = TRUE;
1435             return retval;
1436         }
1437     }
1439     /* Handle the rest of cases */
1441     canvas->left_grabbed_item = FALSE;
1442     canvas->current_item = canvas->new_current_item;
1444     if (canvas->current_item != NULL) {
1445         GdkEvent new_event;
1447         new_event = canvas->pick_event;
1448         new_event.type = GDK_ENTER_NOTIFY;
1449         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1450         new_event.crossing.subwindow = NULL;
1451         retval = emit_event (canvas, &new_event);
1452     }
1454     return retval;
1457 /**
1458  * Button event handler for the canvas.
1459  */
1460 static gint
1461 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1463     SPCanvas *canvas = SP_CANVAS (widget);
1465     int retval = FALSE;
1467     /* dispatch normally regardless of the event's window if an item has
1468        has a pointer grab in effect */
1469     if (!canvas->grabbed_item &&
1470         event->window != SP_CANVAS_WINDOW (canvas))
1471         return retval;
1473     int mask;
1474     switch (event->button) {
1475     case 1:
1476         mask = GDK_BUTTON1_MASK;
1477         break;
1478     case 2:
1479         mask = GDK_BUTTON2_MASK;
1480         break;
1481     case 3:
1482         mask = GDK_BUTTON3_MASK;
1483         break;
1484     case 4:
1485         mask = GDK_BUTTON4_MASK;
1486         break;
1487     case 5:
1488         mask = GDK_BUTTON5_MASK;
1489         break;
1490     default:
1491         mask = 0;
1492     }
1494     switch (event->type) {
1495     case GDK_BUTTON_PRESS:
1496     case GDK_2BUTTON_PRESS:
1497     case GDK_3BUTTON_PRESS:
1498         /* Pick the current item as if the button were not pressed, and
1499          * then process the event.
1500          */
1501         canvas->state = event->state;
1502         pick_current_item (canvas, (GdkEvent *) event);
1503         canvas->state ^= mask;
1504         retval = emit_event (canvas, (GdkEvent *) event);
1505         break;
1507     case GDK_BUTTON_RELEASE:
1508         /* Process the event as if the button were pressed, then repick
1509          * after the button has been released
1510          */
1511         canvas->state = event->state;
1512         retval = emit_event (canvas, (GdkEvent *) event);
1513         event->state ^= mask;
1514         canvas->state = event->state;
1515         pick_current_item (canvas, (GdkEvent *) event);
1516         event->state ^= mask;
1517         break;
1519     default:
1520         g_assert_not_reached ();
1521     }
1523     return retval;
1526 /**
1527  * Scroll event handler for the canvas.
1528  *
1529  * \todo FIXME: generate motion events to re-select items.
1530  */
1531 static gint
1532 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1534     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1537 /**
1538  * Motion event handler for the canvas.
1539  */
1540 static int
1541 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1543     SPCanvas *canvas = SP_CANVAS (widget);
1545     if (event->window != SP_CANVAS_WINDOW (canvas))
1546         return FALSE;
1548     if (canvas->pixmap_gc == NULL) // canvas being deleted
1549         return FALSE;
1551     if (canvas->grabbed_event_mask & GDK_POINTER_MOTION_HINT_MASK) {
1552         gint x, y;
1553         gdk_window_get_pointer (widget->window, &x, &y, NULL);
1554         event->x = x;
1555         event->y = y;
1556     }
1558     canvas->state = event->state;
1559     pick_current_item (canvas, (GdkEvent *) event);
1561     return emit_event (canvas, (GdkEvent *) event);
1564 static void
1565 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)
1567     GtkWidget *widget = GTK_WIDGET (canvas);
1569     SPCanvasBuf buf;
1570     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1571         buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1572     } else {
1573         buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1574     }
1576     // Mark the region clean
1577     sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1579     buf.buf_rowstride = sw * 4; 
1580     buf.rect.x0 = x0;
1581     buf.rect.y0 = y0;
1582     buf.rect.x1 = x1;
1583     buf.rect.y1 = y1;
1584     buf.visible_rect.x0 = draw_x1;
1585     buf.visible_rect.y0 = draw_y1;
1586     buf.visible_rect.x1 = draw_x2;
1587     buf.visible_rect.y1 = draw_y2;
1588     GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1589     buf.bg_color = (((color->red & 0xff00) << 8)
1590                     | (color->green & 0xff00)
1591                     | (color->blue >> 8));
1592     buf.is_empty = true;
1594     buf.ct = nr_create_cairo_context_canvasbuf (&(buf.visible_rect), &buf);
1596     if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1597         SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1598     }
1600 #if ENABLE_LCMS
1601     cmsHTRANSFORM transf = 0;
1602     long long int fromDisplay = prefs_get_int_attribute_limited( "options.displayprofile", "from_display", 0, 0, 1 );
1603     if ( fromDisplay ) {
1604         transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" );
1605     } else {
1606         transf = Inkscape::colorprofile_get_display_transform();
1607     }
1608 #endif // ENABLE_LCMS
1610     if (buf.is_empty) {
1611 #if ENABLE_LCMS
1612         if ( transf && canvas->enable_cms_display_adj ) {
1613             cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1614         }
1615 #endif // ENABLE_LCMS
1616         gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1617         gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1618                             canvas->pixmap_gc,
1619                             TRUE,
1620                             x0 - canvas->x0, y0 - canvas->y0,
1621                             x1 - x0, y1 - y0);
1622     } else {
1624 #if ENABLE_LCMS
1625         if ( transf && canvas->enable_cms_display_adj ) {
1626             for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1627                 guchar* p = buf.buf + (sw * 3) * yy;
1628                 cmsDoTransform( transf, p, p, (x1 - x0) );
1629             }
1630         }
1631 #endif // ENABLE_LCMS
1633 // Now we only need to output the prepared pixmap to the actual screen, and this define chooses one
1634 // of the two ways to do it. The cairo way is direct and straightforward, but unfortunately
1635 // noticeably slower. I asked Carl Worth but he was unable so far to suggest any specific reason
1636 // for this slowness. So, for now we use the oldish method: squeeze out 32bpp buffer to 24bpp and
1637 // use gdk_draw_rgb_image_dithalign, for unfortunately gdk can only handle 24 bpp, which cairo
1638 // cannot handle at all. Still, this way is currently faster even despite the blit with squeeze.
1640 ///#define CANVAS_OUTPUT_VIA_CAIRO
1642 #ifdef CANVAS_OUTPUT_VIA_CAIRO
1644         buf.cst = cairo_image_surface_create_for_data (
1645             buf.buf,
1646             CAIRO_FORMAT_ARGB32,  // unpacked, i.e. 32 bits! one byte is unused
1647             x1 - x0, y1 - y0,
1648             buf.buf_rowstride
1649             );
1650         cairo_t *window_ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1651         cairo_set_source_surface (window_ct, buf.cst, x0 - canvas->x0, y0 - canvas->y0);
1652         cairo_paint (window_ct);
1653         cairo_destroy (window_ct);
1654         cairo_surface_finish (buf.cst);
1655         cairo_surface_destroy (buf.cst);
1657 #else
1659         NRPixBlock b3;
1660         nr_pixblock_setup_fast (&b3, NR_PIXBLOCK_MODE_R8G8B8, x0, y0, x1, y1, TRUE);
1662         NRPixBlock b4;
1663         nr_pixblock_setup_extern (&b4, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1,
1664                                   buf.buf,
1665                                   buf.buf_rowstride,
1666                                   FALSE, FALSE);
1668         // this does the 32->24 squishing, using an assembler routine:
1669         nr_blit_pixblock_pixblock (&b3, &b4);
1671         gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1672                                       canvas->pixmap_gc,
1673                                       x0 - canvas->x0, y0 - canvas->y0,
1674                                       x1 - x0, y1 - y0,
1675                                       GDK_RGB_DITHER_MAX,
1676                                       b3.data.px,
1677                                       sw * 3,
1678                                       x0 - canvas->x0, y0 - canvas->y0);
1680         nr_pixblock_release (&b3);
1681         nr_pixblock_release (&b4);
1682 #endif
1683     }
1685     cairo_surface_t *cst = cairo_get_target(buf.ct);
1686     cairo_destroy (buf.ct);
1687     cairo_surface_finish (cst);
1688     cairo_surface_destroy (cst);
1690     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1691         nr_pixelstore_256K_free (buf.buf);
1692     } else {
1693         nr_pixelstore_1M_free (buf.buf);
1694     }
1697 struct PaintRectSetup {
1698     SPCanvas* canvas;
1699     NRRectL big_rect;
1700     GTimeVal start_time;
1701     int max_pixels;
1702     NR::Point mouse_loc;
1703 };
1705 /**
1706  * Paint the given rect, recursively subdividing the region until it is the size of a single
1707  * buffer.
1708  *
1709  * @return true if the drawing completes
1710  */
1711 static int
1712 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1714     GTimeVal now;
1715     g_get_current_time (&now);
1717     glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1718         + (now.tv_usec - setup->start_time.tv_usec);
1720     // Allow only very fast buffers to be run together;
1721     // as soon as the total redraw time exceeds 1ms, cancel;
1722     // this returns control to the idle loop and allows Inkscape to process user input
1723     // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1724     // it will get back and finish painting what remains to paint.
1725     if (elapsed > 1000) {
1727         // Interrupting redraw isn't always good.
1728         // For example, when you drag one node of a big path, only the buffer containing
1729         // the mouse cursor will be redrawn again and again, and the rest of the path
1730         // will remain stale because Inkscape never has enough idle time to redraw all
1731         // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1732         // If this limit is set, and if we have aborted redraw more times than is allowed,
1733         // interrupting is blocked and we're forced to redraw full screen once
1734         // (after which we can again interrupt forced_redraw_limit times).
1735         if (setup->canvas->forced_redraw_limit < 0 ||
1736             setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1738             if (setup->canvas->forced_redraw_limit != -1) {
1739                 setup->canvas->forced_redraw_count++;
1740             }
1742             return false;
1743         }
1744     }
1746     // Find the optimal buffer dimensions
1747     int bw = this_rect.x1 - this_rect.x0;
1748     int bh = this_rect.y1 - this_rect.y0;
1749     if ((bw < 1) || (bh < 1))
1750         return 0;
1752     if (bw * bh < setup->max_pixels) {
1753         // We are small enough
1754         sp_canvas_paint_single_buffer (setup->canvas,
1755                                        this_rect.x0, this_rect.y0,
1756                                        this_rect.x1, this_rect.y1,
1757                                        setup->big_rect.x0, setup->big_rect.y0,
1758                                        setup->big_rect.x1, setup->big_rect.y1, bw);
1759         return 1;
1760     }
1762     NRRectL lo = this_rect;
1763     NRRectL hi = this_rect;
1765 /*
1766 This test determines the redraw strategy:
1768 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1769 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1770 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1771 and seems to be faster for drawings with many smaller objects at zoom-out.
1773 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1774 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1775 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1776 faster.
1778 The default for now is the strips mode.
1779 */
1780     if (bw < bh || bh < 2 * TILE_SIZE) {
1781         // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1782         int mid = ((long long) this_rect.x0 + (long long) this_rect.x1) / 2;
1783         // Make sure that mid lies on a tile boundary
1784         mid = (mid / TILE_SIZE) * TILE_SIZE;
1786         lo.x1 = mid;
1787         hi.x0 = mid;
1789         if (setup->mouse_loc[NR::X] < mid) {
1790             // Always paint towards the mouse first
1791             return sp_canvas_paint_rect_internal(setup, lo)
1792                 && sp_canvas_paint_rect_internal(setup, hi);
1793         } else {
1794             return sp_canvas_paint_rect_internal(setup, hi)
1795                 && sp_canvas_paint_rect_internal(setup, lo);
1796         }
1797     } else {
1798         // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1799         int mid = ((long long) this_rect.y0 + (long long) this_rect.y1) / 2;
1800         // Make sure that mid lies on a tile boundary
1801         mid = (mid / TILE_SIZE) * TILE_SIZE;
1803         lo.y1 = mid;
1804         hi.y0 = mid;
1806         if (setup->mouse_loc[NR::Y] < mid) {
1807             // Always paint towards the mouse first
1808             return sp_canvas_paint_rect_internal(setup, lo)
1809                 && sp_canvas_paint_rect_internal(setup, hi);
1810         } else {
1811             return sp_canvas_paint_rect_internal(setup, hi)
1812                 && sp_canvas_paint_rect_internal(setup, lo);
1813         }
1814     }
1818 /**
1819  * Helper that draws a specific rectangular part of the canvas.
1820  *
1821  * @return true if the rectangle painting succeeds.
1822  */
1823 static bool
1824 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1826     g_return_val_if_fail (!canvas->need_update, false);
1828     NRRectL rect;
1829     rect.x0 = xx0;
1830     rect.x1 = xx1;
1831     rect.y0 = yy0;
1832     rect.y1 = yy1;
1834     // Clip rect-to-draw by the current visible area
1835     rect.x0 = MAX (rect.x0, canvas->x0);
1836     rect.y0 = MAX (rect.y0, canvas->y0);
1837     rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1838     rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1840 #ifdef DEBUG_REDRAW
1841     // paint the area to redraw yellow
1842     gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1843     gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1844                         canvas->pixmap_gc,
1845                         TRUE,
1846                         rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1847                         rect.x1 - rect.x0, rect.y1 - rect.y0);
1848 #endif
1850     PaintRectSetup setup;
1852     setup.canvas = canvas;
1853     setup.big_rect = rect;
1855     // Save the mouse location
1856     gint x, y;
1857     gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1858     setup.mouse_loc = sp_canvas_window_to_world (canvas, NR::Point(x,y));
1860     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1861         // use 256K as a compromise to not slow down gradients
1862         // 256K is the cached buffer and we need 4 channels
1863         setup.max_pixels = 65536; // 256K/4
1864     } else {
1865         // paths only, so 1M works faster
1866         // 1M is the cached buffer and we need 4 channels
1867         setup.max_pixels = 262144; 
1868     }
1870     // Start the clock
1871     g_get_current_time(&(setup.start_time));
1873     // Go
1874     return sp_canvas_paint_rect_internal(&setup, rect);
1877 /**
1878  * Force a full redraw after a specified number of interrupted redraws
1879  */
1880 void
1881 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1882   g_return_if_fail(canvas != NULL);
1884   canvas->forced_redraw_limit = count;
1885   canvas->forced_redraw_count = 0;
1888 /**
1889  * End forced full redraw requests
1890  */
1891 void
1892 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1893   g_return_if_fail(canvas != NULL);
1895   canvas->forced_redraw_limit = -1;
1898 /**
1899  * The canvas widget's expose callback.
1900  */
1901 static gint
1902 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1904     SPCanvas *canvas = SP_CANVAS (widget);
1906     if (!GTK_WIDGET_DRAWABLE (widget) ||
1907         (event->window != SP_CANVAS_WINDOW (canvas)))
1908         return FALSE;
1910     int n_rects;
1911     GdkRectangle *rects;
1912     gdk_region_get_rectangles (event->region, &rects, &n_rects);
1914     for (int i = 0; i < n_rects; i++) {
1915         NRRectL rect;
1917         rect.x0 = rects[i].x + canvas->x0;
1918         rect.y0 = rects[i].y + canvas->y0;
1919         rect.x1 = rect.x0 + rects[i].width;
1920         rect.y1 = rect.y0 + rects[i].height;
1922         sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1923     }
1925     if (n_rects > 0)
1926         g_free (rects);
1928     return FALSE;
1931 /**
1932  * The canvas widget's keypress callback.
1933  */
1934 static gint
1935 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1937     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1940 /**
1941  * Crossing event handler for the canvas.
1942  */
1943 static gint
1944 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1946     SPCanvas *canvas = SP_CANVAS (widget);
1948     if (event->window != SP_CANVAS_WINDOW (canvas))
1949         return FALSE;
1951     canvas->state = event->state;
1952     return pick_current_item (canvas, (GdkEvent *) event);
1955 /**
1956  * Focus in handler for the canvas.
1957  */
1958 static gint
1959 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
1961     GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1963     SPCanvas *canvas = SP_CANVAS (widget);
1965     if (canvas->focused_item) {
1966         return emit_event (canvas, (GdkEvent *) event);
1967     } else {
1968         return FALSE;
1969     }
1972 /**
1973  * Focus out handler for the canvas.
1974  */
1975 static gint
1976 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
1978     GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1980     SPCanvas *canvas = SP_CANVAS (widget);
1982     if (canvas->focused_item)
1983         return emit_event (canvas, (GdkEvent *) event);
1984     else
1985         return FALSE;
1988 /**
1989  * Helper that repaints the areas in the canvas that need it.
1990  *
1991  * @return true if all the dirty parts have been redrawn
1992  */
1993 static int
1994 paint (SPCanvas *canvas)
1996     if (canvas->need_update) {
1997         sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
1998         canvas->need_update = FALSE;
1999     }
2001     if (!canvas->need_redraw)
2002         return TRUE;
2004     Gdk::Region to_paint;
2006     for (int j=canvas->tTop; j<canvas->tBottom; j++) {
2007         for (int i=canvas->tLeft; i<canvas->tRight; i++) {
2008             int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
2010             if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
2011                 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
2012                                    TILE_SIZE, TILE_SIZE));
2013             }
2015         }
2016     }
2018     if (!to_paint.empty()) {
2019         Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
2020         typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
2021         for (Iter i=rect.begin(); i != rect.end(); ++i) {
2022             int x0 = (*i).get_x();
2023             int y0 = (*i).get_y();
2024             int x1 = x0 + (*i).get_width();
2025             int y1 = y0 + (*i).get_height();
2026             if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
2027                 // Aborted
2028                 return FALSE;
2029             };
2030         }
2031     }
2033     canvas->need_redraw = FALSE;
2035     // we've had a full unaborted redraw, reset the full redraw counter
2036     if (canvas->forced_redraw_limit != -1) {
2037         canvas->forced_redraw_count = 0;
2038     }
2040     return TRUE;
2043 /**
2044  * Helper that invokes update, paint, and repick on canvas.
2045  */
2046 static int
2047 do_update (SPCanvas *canvas)
2049     if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop durring interrupted display!
2050         return TRUE;
2052     /* Cause the update if necessary */
2053     if (canvas->need_update) {
2054         sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2055         canvas->need_update = FALSE;
2056     }
2058     /* Paint if able to */
2059     if (GTK_WIDGET_DRAWABLE (canvas)) {
2060             return paint (canvas);
2061     }
2063     /* Pick new current item */
2064     while (canvas->need_repick) {
2065         canvas->need_repick = FALSE;
2066         pick_current_item (canvas, &canvas->pick_event);
2067     }
2069     return TRUE;
2072 /**
2073  * Idle handler for the canvas that deals with pending updates and redraws.
2074  */
2075 static gint
2076 idle_handler (gpointer data)
2078     GDK_THREADS_ENTER ();
2080     SPCanvas *canvas = SP_CANVAS (data);
2082     int const ret = do_update (canvas);
2084     if (ret) {
2085         /* Reset idle id */
2086         canvas->idle_id = 0;
2087     }
2089     GDK_THREADS_LEAVE ();
2091     return !ret;
2094 /**
2095  * Convenience function to add an idle handler to a canvas.
2096  */
2097 static void
2098 add_idle (SPCanvas *canvas)
2100     if (canvas->idle_id != 0)
2101         return;
2103     canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2106 /**
2107  * Returns the root group of the specified canvas.
2108  */
2109 SPCanvasGroup *
2110 sp_canvas_root (SPCanvas *canvas)
2112     g_return_val_if_fail (canvas != NULL, NULL);
2113     g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2115     return SP_CANVAS_GROUP (canvas->root);
2118 /**
2119  * Scrolls canvas to specific position (cx and cy are measured in screen pixels)
2120  */
2121 void
2122 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2124     g_return_if_fail (canvas != NULL);
2125     g_return_if_fail (SP_IS_CANVAS (canvas));
2127     int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels)
2128     int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do!
2129     int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the 
2130     int dy = iy - canvas->y0; // canvas w.r.t its previous position
2132     canvas->dx0 = cx; // here the 'd' stands for double, not delta!
2133     canvas->dy0 = cy;
2134     canvas->x0 = ix;
2135     canvas->y0 = iy;
2137     sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2139     if (!clear) {
2140         // scrolling without zoom; redraw only the newly exposed areas
2141         if ((dx != 0) || (dy != 0)) {
2142             canvas->is_scrolling = is_scrolling;
2143             if (GTK_WIDGET_REALIZED (canvas)) {
2144                 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2145             }
2146         }
2147     } else {
2148         // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2149     }
2152 /**
2153  * Updates canvas if necessary.
2154  */
2155 void
2156 sp_canvas_update_now (SPCanvas *canvas)
2158     g_return_if_fail (canvas != NULL);
2159     g_return_if_fail (SP_IS_CANVAS (canvas));
2161     if (!(canvas->need_update ||
2162           canvas->need_redraw))
2163         return;
2165     do_update (canvas);
2168 /**
2169  * Update callback for canvas widget.
2170  */
2171 static void
2172 sp_canvas_request_update (SPCanvas *canvas)
2174     canvas->need_update = TRUE;
2175     add_idle (canvas);
2178 /**
2179  * Forces redraw of rectangular canvas area.
2180  */
2181 void
2182 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2184     NRRectL bbox;
2185     NRRectL visible;
2186     NRRectL clip;
2188     g_return_if_fail (canvas != NULL);
2189     g_return_if_fail (SP_IS_CANVAS (canvas));
2191     if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2192     if ((x0 >= x1) || (y0 >= y1)) return;
2194     bbox.x0 = x0;
2195     bbox.y0 = y0;
2196     bbox.x1 = x1;
2197     bbox.y1 = y1;
2199     visible.x0 = canvas->x0;
2200     visible.y0 = canvas->y0;
2201     visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2202     visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2204     nr_rect_l_intersect (&clip, &bbox, &visible);
2206     sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2207     add_idle (canvas);
2210 /**
2211  * Sets world coordinates from win and canvas.
2212  */
2213 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2215     g_return_if_fail (canvas != NULL);
2216     g_return_if_fail (SP_IS_CANVAS (canvas));
2218     if (worldx) *worldx = canvas->x0 + winx;
2219     if (worldy) *worldy = canvas->y0 + winy;
2222 /**
2223  * Sets win coordinates from world and canvas.
2224  */
2225 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2227     g_return_if_fail (canvas != NULL);
2228     g_return_if_fail (SP_IS_CANVAS (canvas));
2230     if (winx) *winx = worldx - canvas->x0;
2231     if (winy) *winy = worldy - canvas->y0;
2234 /**
2235  * Converts point from win to world coordinates.
2236  */
2237 NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win)
2239     g_assert (canvas != NULL);
2240     g_assert (SP_IS_CANVAS (canvas));
2242     return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2245 /**
2246  * Converts point from world to win coordinates.
2247  */
2248 NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world)
2250     g_assert (canvas != NULL);
2251     g_assert (SP_IS_CANVAS (canvas));
2253     return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2256 /**
2257  * Returns true if point given in world coordinates is inside window.
2258  */
2259 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world)
2261     g_assert( canvas != NULL );
2262     g_assert(SP_IS_CANVAS(canvas));
2264     using NR::X;
2265     using NR::Y;
2266     GtkWidget const &w = *GTK_WIDGET(canvas);
2267     return ( ( canvas->x0 <= world[X] )  &&
2268              ( canvas->y0 <= world[Y] )  &&
2269              ( world[X] < canvas->x0 + w.allocation.width )  &&
2270              ( world[Y] < canvas->y0 + w.allocation.height ) );
2273 /**
2274  * Return canvas window coordinates as NR::Rect.
2275  */
2276 NR::Rect SPCanvas::getViewbox() const
2278     GtkWidget const *w = GTK_WIDGET(this);
2279     return NR::Rect(NR::Point(dx0, dy0),
2280                     NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2283 /**
2284  * Return canvas window coordinates as IRect (a rectangle defined by integers).
2285  */
2286 NR::IRect SPCanvas::getViewboxIntegers() const
2288     GtkWidget const *w = GTK_WIDGET(this);
2289     return NR::IRect(NR::IPoint(x0, y0),
2290                     NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height));
2293 inline int sp_canvas_tile_floor(int x)
2295     return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2298 inline int sp_canvas_tile_ceil(int x)
2300     return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2303 /**
2304  * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2305  */
2306 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2308     if ( nl >= nr || nt >= nb ) {
2309         if ( canvas->tiles ) g_free(canvas->tiles);
2310         canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2311         canvas->tileH=canvas->tileV=0;
2312         canvas->tiles=NULL;
2313         return;
2314     }
2315     int tl=sp_canvas_tile_floor(nl);
2316     int tt=sp_canvas_tile_floor(nt);
2317     int tr=sp_canvas_tile_ceil(nr);
2318     int tb=sp_canvas_tile_ceil(nb);
2320     int nh = tr-tl, nv = tb-tt;
2321     uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2322     for (int i=tl; i<tr; i++) {
2323         for (int j=tt; j<tb; j++) {
2324             int ind = (i-tl) + (j-tt)*nh;
2325             if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2326                 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2327             } else {
2328                 ntiles[ind]=0; // newly exposed areas get 0
2329             }
2330         }
2331     }
2332     if ( canvas->tiles ) g_free(canvas->tiles);
2333     canvas->tiles=ntiles;
2334     canvas->tLeft=tl;
2335     canvas->tTop=tt;
2336     canvas->tRight=tr;
2337     canvas->tBottom=tb;
2338     canvas->tileH=nh;
2339     canvas->tileV=nv;
2342 /*
2343  * Helper that queues a canvas rectangle for redraw
2344  */
2345 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2346     canvas->need_redraw = TRUE;
2348     sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2351 /**
2352  * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2353  */
2354 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2356     if ( nl >= nr || nt >= nb ) {
2357         return;
2358     }
2359     int tl=sp_canvas_tile_floor(nl);
2360     int tt=sp_canvas_tile_floor(nt);
2361     int tr=sp_canvas_tile_ceil(nr);
2362     int tb=sp_canvas_tile_ceil(nb);
2363     if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2364     if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2365     if ( tr > canvas->tRight ) tr=canvas->tRight;
2366     if ( tt < canvas->tTop ) tt=canvas->tTop;
2367     if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2369     for (int i=tl; i<tr; i++) {
2370         for (int j=tt; j<tb; j++) {
2371             canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2372         }
2373     }
2377 /*
2378   Local Variables:
2379   mode:c++
2380   c-file-style:"stroustrup"
2381   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2382   indent-tabs-mode:nil
2383   fill-column:99
2384   End:
2385 */
2386 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :