Code

False alarm; re-enable them
[inkscape.git] / src / display / sp-canvas.cpp
1 #define __SP_CANVAS_C__
3 /** \file
4  * Port of GnomeCanvas for Inkscape needs
5  *
6  * Authors:
7  *   Federico Mena <federico@nuclecu.unam.mx>
8  *   Raph Levien <raph@gimp.org>
9  *   Lauris Kaplinski <lauris@kaplinski.com>
10  *   fred
11  *   bbyak
12  *
13  * Copyright (C) 1998 The Free Software Foundation
14  * Copyright (C) 2002-2006 authors
15  *
16  * Released under GNU GPL, read the file 'COPYING' for more information
17  */
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
23 #include <libnr/nr-pixblock.h>
25 #include <gtk/gtkmain.h>
26 #include <gtk/gtksignal.h>
27 #include <gtk/gtkversion.h>
29 #include <gtkmm.h>
31 #include <helper/sp-marshal.h>
32 #include <display/sp-canvas.h>
33 #include "display-forward.h"
34 #include <libnr/nr-matrix-fns.h>
35 #include <libnr/nr-matrix-ops.h>
36 #include <libnr/nr-convex-hull.h>
37 #include "prefs-utils.h"
38 #include "inkscape.h"
39 #include "sodipodi-ctrlrect.h"
40 #if ENABLE_LCMS
41 #include "color-profile-fns.h"
42 #endif // ENABLE_LCMS
43 #include "display/rendermode.h"
44 #include "libnr/nr-blit.h"
45 #include "display/inkscape-cairo.h"
47 // GTK_CHECK_VERSION returns false on failure
48 #define HAS_GDK_EVENT_REQUEST_MOTIONS GTK_CHECK_VERSION(2, 12, 0)
50 // gtk_check_version returns non-NULL on failure
51 static bool const HAS_BROKEN_MOTION_HINTS =
52   gtk_check_version(2, 12, 0) != NULL || !HAS_GDK_EVENT_REQUEST_MOTIONS;
54 // Define this to visualize the regions to be redrawn
55 //#define DEBUG_REDRAW 1;
57 // Tiles are a way to minimize the number of redraws, eliminating too small redraws.
58 // The canvas stores a 2D array of ints, each representing a TILE_SIZExTILE_SIZE pixels tile.
59 // If any part of it is dirtied, the entire tile is dirtied (its int is nonzero) and repainted.
60 #define TILE_SIZE 16
62 static gint const sp_canvas_update_priority = G_PRIORITY_HIGH_IDLE;
64 #define SP_CANVAS_WINDOW(c) (((GtkWidget *) (c))->window)
66 enum {
67     SP_CANVAS_ITEM_VISIBLE = 1 << 7,
68     SP_CANVAS_ITEM_NEED_UPDATE = 1 << 8,
69     SP_CANVAS_ITEM_NEED_AFFINE = 1 << 9
70 };
72 /**
73  * A group of Items.
74  */
75 struct SPCanvasGroup {
76     SPCanvasItem item;
78     GList *items, *last;
79 };
81 /**
82  * The SPCanvasGroup vtable.
83  */
84 struct SPCanvasGroupClass {
85     SPCanvasItemClass parent_class;
86 };
88 /**
89  * The SPCanvas vtable.
90  */
91 struct SPCanvasClass {
92     GtkWidgetClass parent_class;
93 };
95 static void group_add (SPCanvasGroup *group, SPCanvasItem *item);
96 static void group_remove (SPCanvasGroup *group, SPCanvasItem *item);
98 /* SPCanvasItem */
100 enum {ITEM_EVENT, ITEM_LAST_SIGNAL};
103 static void sp_canvas_request_update (SPCanvas *canvas);
105 static void sp_canvas_item_class_init (SPCanvasItemClass *klass);
106 static void sp_canvas_item_init (SPCanvasItem *item);
107 static void sp_canvas_item_dispose (GObject *object);
108 static void sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args);
110 static int emit_event (SPCanvas *canvas, GdkEvent *event);
112 static guint item_signals[ITEM_LAST_SIGNAL] = { 0 };
114 static GtkObjectClass *item_parent_class;
116 /**
117  * Registers the SPCanvasItem class with Glib and returns its type number.
118  */
119 GType
120 sp_canvas_item_get_type (void)
122     static GType type = 0;
123     if (!type) {
124         static GTypeInfo const info = {
125             sizeof (SPCanvasItemClass),
126             NULL, NULL,
127             (GClassInitFunc) sp_canvas_item_class_init,
128             NULL, NULL,
129             sizeof (SPCanvasItem),
130             0,
131             (GInstanceInitFunc) sp_canvas_item_init,
132             NULL
133         };
134         type = g_type_register_static (GTK_TYPE_OBJECT, "SPCanvasItem", &info, (GTypeFlags)0);
135     }
137     return type;
140 /**
141  * Initializes the SPCanvasItem vtable and the "event" signal.
142  */
143 static void
144 sp_canvas_item_class_init (SPCanvasItemClass *klass)
146     GObjectClass *object_class = (GObjectClass *) klass;
148     /* fixme: Derive from GObject */
149     item_parent_class = (GtkObjectClass*)gtk_type_class (GTK_TYPE_OBJECT);
151     item_signals[ITEM_EVENT] = g_signal_new ("event",
152                                              G_TYPE_FROM_CLASS (klass),
153                                              G_SIGNAL_RUN_LAST,
154                                              ((glong)((guint8*)&(klass->event) - (guint8*)klass)),
155                                              NULL, NULL,
156                                              sp_marshal_BOOLEAN__POINTER,
157                                              G_TYPE_BOOLEAN, 1,
158                                              GDK_TYPE_EVENT);
160     object_class->dispose = sp_canvas_item_dispose;
163 /**
164  * Callback for initialization of SPCanvasItem.
165  */
166 static void
167 sp_canvas_item_init (SPCanvasItem *item)
169     item->flags |= SP_CANVAS_ITEM_VISIBLE;
170     item->xform = NR::Matrix(NR::identity());
173 /**
174  * Constructs new SPCanvasItem on SPCanvasGroup.
175  */
176 SPCanvasItem *
177 sp_canvas_item_new (SPCanvasGroup *parent, GtkType type, gchar const *first_arg_name, ...)
179     va_list args;
181     g_return_val_if_fail (parent != NULL, NULL);
182     g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL);
183     g_return_val_if_fail (gtk_type_is_a (type, sp_canvas_item_get_type ()), NULL);
185     SPCanvasItem *item = SP_CANVAS_ITEM (gtk_type_new (type));
187     va_start (args, first_arg_name);
188     sp_canvas_item_construct (item, parent, first_arg_name, args);
189     va_end (args);
191     return item;
194 /**
195  * Sets up the newly created SPCanvasItem.
196  *
197  * We make it static for encapsulation reasons since it was nowhere used.
198  */
199 static void
200 sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args)
202     g_return_if_fail (SP_IS_CANVAS_GROUP (parent));
203     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
205     item->parent = SP_CANVAS_ITEM (parent);
206     item->canvas = item->parent->canvas;
208     g_object_set_valist (G_OBJECT (item), first_arg_name, args);
210     group_add (SP_CANVAS_GROUP (item->parent), item);
212     sp_canvas_item_request_update (item);
215 /**
216  * Helper function that requests redraw only if item's visible flag is set.
217  */
218 static void
219 redraw_if_visible (SPCanvasItem *item)
221     if (item->flags & SP_CANVAS_ITEM_VISIBLE) {
222         int x0 = (int)(item->x1);
223         int x1 = (int)(item->x2);
224         int y0 = (int)(item->y1);
225         int y1 = (int)(item->y2);
227         if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
228             sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
229         }
230     }
233 /**
234  * Callback that removes item from all referers and destroys it.
235  */
236 static void
237 sp_canvas_item_dispose (GObject *object)
239     SPCanvasItem *item = SP_CANVAS_ITEM (object);
241     // Hack: if this is a ctrlrect, move it to 0,0;
242     // this redraws only the stroke of the rect to be deleted,
243     // avoiding redraw of the entire area
244     if (SP_IS_CTRLRECT(item)) {
245         SP_CTRLRECT(object)->setRectangle(NR::Rect(NR::Point(0,0),NR::Point(0,0)));
246         SP_CTRLRECT(object)->update(item->xform, 0);
247     } else {
248         redraw_if_visible (item);
249     }
250     item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
252     if (item == item->canvas->current_item) {
253         item->canvas->current_item = NULL;
254         item->canvas->need_repick = TRUE;
255     }
257     if (item == item->canvas->new_current_item) {
258         item->canvas->new_current_item = NULL;
259         item->canvas->need_repick = TRUE;
260     }
262     if (item == item->canvas->grabbed_item) {
263         item->canvas->grabbed_item = NULL;
264         gdk_pointer_ungrab (GDK_CURRENT_TIME);
265     }
267     if (item == item->canvas->focused_item)
268         item->canvas->focused_item = NULL;
270     if (item->parent) {
271         group_remove (SP_CANVAS_GROUP (item->parent), item);
272     }
274     G_OBJECT_CLASS (item_parent_class)->dispose (object);
277 /**
278  * Helper function to update item and its children.
279  *
280  * NB! affine is parent2canvas.
281  */
282 static void
283 sp_canvas_item_invoke_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
285     /* Apply the child item's transform */
286     NR::Matrix child_affine = item->xform * affine;
288     /* apply object flags to child flags */
289     int child_flags = flags & ~SP_CANVAS_UPDATE_REQUESTED;
291     if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
292         child_flags |= SP_CANVAS_UPDATE_REQUESTED;
294     if (item->flags & SP_CANVAS_ITEM_NEED_AFFINE)
295         child_flags |= SP_CANVAS_UPDATE_AFFINE;
297     if (child_flags & (SP_CANVAS_UPDATE_REQUESTED | SP_CANVAS_UPDATE_AFFINE)) {
298         if (SP_CANVAS_ITEM_GET_CLASS (item)->update)
299             SP_CANVAS_ITEM_GET_CLASS (item)->update (item, child_affine, child_flags);
300     }
302     GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_UPDATE);
303     GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_AFFINE);
306 /**
307  * Helper function to invoke the point method of the item.
308  *
309  * The argument x, y should be in the parent's item-relative coordinate
310  * system.  This routine applies the inverse of the item's transform,
311  * maintaining the affine invariant.
312  */
313 static double
314 sp_canvas_item_invoke_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
316     if (SP_CANVAS_ITEM_GET_CLASS (item)->point)
317         return SP_CANVAS_ITEM_GET_CLASS (item)->point (item, p, actual_item);
319     return NR_HUGE;
322 /**
323  * Makes the item's affine transformation matrix be equal to the specified
324  * matrix.
325  *
326  * @item: A canvas item.
327  * @affine: An affine transformation matrix.
328  */
329 void
330 sp_canvas_item_affine_absolute (SPCanvasItem *item, NR::Matrix const &affine)
332     item->xform = affine;
334     if (!(item->flags & SP_CANVAS_ITEM_NEED_AFFINE)) {
335         item->flags |= SP_CANVAS_ITEM_NEED_AFFINE;
336         if (item->parent != NULL) {
337             sp_canvas_item_request_update (item->parent);
338         } else {
339             sp_canvas_request_update (item->canvas);
340         }
341     }
343     item->canvas->need_repick = TRUE;
346 /**
347  * Convenience function to reorder items in a group's child list.
348  *
349  * This puts the specified link after the "before" link.
350  */
351 static void
352 put_item_after (GList *link, GList *before)
354     if (link == before)
355         return;
357     SPCanvasGroup *parent = SP_CANVAS_GROUP (SP_CANVAS_ITEM (link->data)->parent);
359     if (before == NULL) {
360         if (link == parent->items) return;
362         link->prev->next = link->next;
364         if (link->next) {
365             link->next->prev = link->prev;
366         } else {
367             parent->last = link->prev;
368         }
370         link->prev = before;
371         link->next = parent->items;
372         link->next->prev = link;
373         parent->items = link;
374     } else {
375         if ((link == parent->last) && (before == parent->last->prev))
376             return;
378         if (link->next)
379             link->next->prev = link->prev;
381         if (link->prev)
382             link->prev->next = link->next;
383         else {
384             parent->items = link->next;
385             parent->items->prev = NULL;
386         }
388         link->prev = before;
389         link->next = before->next;
391         link->prev->next = link;
393         if (link->next)
394             link->next->prev = link;
395         else
396             parent->last = link;
397     }
401 /**
402  * Raises the item in its parent's stack by the specified number of positions.
403  *
404  * \param item A canvas item.
405  * \param positions Number of steps to raise the item.
406  *
407  * If the number of positions is greater than the distance to the top of the
408  * stack, then the item is put at the top.
409  */
410 void
411 sp_canvas_item_raise (SPCanvasItem *item, int positions)
413     g_return_if_fail (item != NULL);
414     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
415     g_return_if_fail (positions >= 0);
417     if (!item->parent || positions == 0)
418         return;
420     SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
421     GList *link = g_list_find (parent->items, item);
422     g_assert (link != NULL);
424     GList *before;
425     for (before = link; positions && before; positions--)
426         before = before->next;
428     if (!before)
429         before = parent->last;
431     put_item_after (link, before);
433     redraw_if_visible (item);
434     item->canvas->need_repick = TRUE;
438 /**
439  * Lowers the item in its parent's stack by the specified number of positions.
440  *
441  * \param item A canvas item.
442  * \param positions Number of steps to lower the item.
443  *
444  * If the number of positions is greater than the distance to the bottom of the
445  * stack, then the item is put at the bottom.
446  **/
447 void
448 sp_canvas_item_lower (SPCanvasItem *item, int positions)
450     g_return_if_fail (item != NULL);
451     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
452     g_return_if_fail (positions >= 1);
454     if (!item->parent || positions == 0)
455         return;
457     SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
458     GList *link = g_list_find (parent->items, item);
459     g_assert (link != NULL);
461     GList *before;
462     if (link->prev)
463         for (before = link->prev; positions && before; positions--)
464             before = before->prev;
465     else
466         before = NULL;
468     put_item_after (link, before);
470     redraw_if_visible (item);
471     item->canvas->need_repick = TRUE;
474 /**
475  * Sets visible flag on item and requests a redraw.
476  */
477 void
478 sp_canvas_item_show (SPCanvasItem *item)
480     g_return_if_fail (item != NULL);
481     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
483     if (item->flags & SP_CANVAS_ITEM_VISIBLE)
484         return;
486     item->flags |= SP_CANVAS_ITEM_VISIBLE;
488     int x0 = (int)(item->x1);
489     int x1 = (int)(item->x2);
490     int y0 = (int)(item->y1);
491     int y1 = (int)(item->y2);
493     if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
494         sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
495         item->canvas->need_repick = TRUE;
496     }
499 /**
500  * Clears visible flag on item and requests a redraw.
501  */
502 void
503 sp_canvas_item_hide (SPCanvasItem *item)
505     g_return_if_fail (item != NULL);
506     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
508     if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
509         return;
511     item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
513     int x0 = (int)(item->x1);
514     int x1 = (int)(item->x2);
515     int y0 = (int)(item->y1);
516     int y1 = (int)(item->y2);
518     if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
519         sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
520         item->canvas->need_repick = TRUE;
521     }
524 /**
525  * Grab item under cursor.
526  *
527  * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE
528  */
529 int
530 sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
532     g_return_val_if_fail (item != NULL, -1);
533     g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1);
534     g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1);
536     if (item->canvas->grabbed_item)
537         return -1;
539     if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
540         return -1;
542     if (HAS_BROKEN_MOTION_HINTS) {
543         event_mask &= ~GDK_POINTER_MOTION_HINT_MASK;
544     }
546     /* fixme: Top hack (Lauris) */
547     /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
548     /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
549     gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
550                       (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
551                       NULL, cursor, etime);
553     item->canvas->grabbed_item = item;
554     item->canvas->grabbed_event_mask = event_mask;
555     item->canvas->current_item = item; /* So that events go to the grabbed item */
557     return 0;
560 /**
561  * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
562  * mouse.
563  *
564  * \param item A canvas item that holds a grab.
565  * \param etime The timestamp for ungrabbing the mouse.
566  */
567 void
568 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
570     g_return_if_fail (item != NULL);
571     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
573     if (item->canvas->grabbed_item != item)
574         return;
576     item->canvas->grabbed_item = NULL;
578     gdk_pointer_ungrab (etime);
581 /**
582  * Returns the product of all transformation matrices from the root item down
583  * to the item.
584  */
585 NR::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
587     g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
589     NR::Matrix affine = NR::identity();
591     while (item) {
592         affine *= item->xform;
593         item = item->parent;
594     }
595     return affine;
598 /**
599  * Helper that returns true iff item is descendant of parent.
600  */
601 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
603     while (item) {
604         if (item == parent)
605             return true;
606         item = item->parent;
607     }
609     return false;
612 /**
613  * Focus canvas, and item under cursor if it is not already focussed.
614  */
615 void
616 sp_canvas_item_grab_focus (SPCanvasItem *item)
618     g_return_if_fail (item != NULL);
619     g_return_if_fail (SP_IS_CANVAS_ITEM (item));
620     g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
622     SPCanvasItem *focused_item = item->canvas->focused_item;
624     if (focused_item) {
625         GdkEvent ev;
626         ev.focus_change.type = GDK_FOCUS_CHANGE;
627         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
628         ev.focus_change.send_event = FALSE;
629         ev.focus_change.in = FALSE;
631         emit_event (item->canvas, &ev);
632     }
634     item->canvas->focused_item = item;
635     gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
637     if (focused_item) {
638         GdkEvent ev;
639         ev.focus_change.type = GDK_FOCUS_CHANGE;
640         ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
641         ev.focus_change.send_event = FALSE;
642         ev.focus_change.in = TRUE;
644         emit_event (item->canvas, &ev);
645     }
648 /**
649  * Requests that the canvas queue an update for the specified item.
650  *
651  * To be used only by item implementations.
652  */
653 void
654 sp_canvas_item_request_update (SPCanvasItem *item)
656     if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
657         return;
659     item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
661     if (item->parent != NULL) {
662         /* Recurse up the tree */
663         sp_canvas_item_request_update (item->parent);
664     } else {
665         /* Have reached the top of the tree, make sure the update call gets scheduled. */
666         sp_canvas_request_update (item->canvas);
667     }
670 /**
671  * Returns position of item in group.
672  */
673 gint sp_canvas_item_order (SPCanvasItem * item)
675     return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
678 /* SPCanvasGroup */
680 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
681 static void sp_canvas_group_init (SPCanvasGroup *group);
682 static void sp_canvas_group_destroy (GtkObject *object);
684 static void sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
685 static double sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item);
686 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
688 static SPCanvasItemClass *group_parent_class;
690 /**
691  * Registers SPCanvasGroup class with Gtk and returns its type number.
692  */
693 GType sp_canvas_group_get_type(void)
695     static GType type = 0;
696     if (!type) {
697         GTypeInfo info = {
698             sizeof(SPCanvasGroupClass),
699             0, // base_init
700             0, // base_finalize
701             (GClassInitFunc)sp_canvas_group_class_init,
702             0, // class_finalize
703             0, // class_data
704             sizeof(SPCanvasGroup),
705             0, // n_preallocs
706             (GInstanceInitFunc)sp_canvas_group_init,
707             0 // value_table
708         };
709         type = g_type_register_static(sp_canvas_item_get_type(), "SPCanvasGroup", &info, static_cast<GTypeFlags>(0));
710     }
711     return type;
714 /**
715  * Class initialization function for SPCanvasGroupClass
716  */
717 static void
718 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
720     GtkObjectClass *object_class = (GtkObjectClass *) klass;
721     SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
723     group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
725     object_class->destroy = sp_canvas_group_destroy;
727     item_class->update = sp_canvas_group_update;
728     item_class->render = sp_canvas_group_render;
729     item_class->point = sp_canvas_group_point;
732 /**
733  * Callback. Empty.
734  */
735 static void
736 sp_canvas_group_init (SPCanvasGroup */*group*/)
738     /* Nothing here */
741 /**
742  * Callback that destroys all items in group and calls group's virtual
743  * destroy() function.
744  */
745 static void
746 sp_canvas_group_destroy (GtkObject *object)
748     g_return_if_fail (object != NULL);
749     g_return_if_fail (SP_IS_CANVAS_GROUP (object));
751     SPCanvasGroup const *group = SP_CANVAS_GROUP (object);
753     GList *list = group->items;
754     while (list) {
755         SPCanvasItem *child = (SPCanvasItem *)list->data;
756         list = list->next;
758         gtk_object_destroy (GTK_OBJECT (child));
759     }
761     if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
762         (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
765 /**
766  * Update handler for canvas groups
767  */
768 static void
769 sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
771     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
772     NR::ConvexHull corners(NR::Point(0, 0));
773     bool empty=true;
775     for (GList *list = group->items; list; list = list->next) {
776         SPCanvasItem *i = (SPCanvasItem *)list->data;
778         sp_canvas_item_invoke_update (i, affine, flags);
780         if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
781             if (empty) {
782                 corners = NR::ConvexHull(NR::Point(i->x1, i->y1));
783                 empty = false;
784             } else {
785                 corners.add(NR::Point(i->x1, i->y1));
786             }
787             corners.add(NR::Point(i->x2, i->y2));
788         }
789     }
791     NR::Maybe<NR::Rect> const bounds = corners.bounds();
792     if (bounds) {
793         item->x1 = bounds->min()[NR::X];
794         item->y1 = bounds->min()[NR::Y];
795         item->x2 = bounds->max()[NR::X];
796         item->y2 = bounds->max()[NR::Y];
797     } else {
798         // FIXME ?
799         item->x1 = item->x2 = item->y1 = item->y2 = 0;
800     }
803 /**
804  * Point handler for canvas groups.
805  */
806 static double
807 sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
809     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
810     double const x = p[NR::X];
811     double const y = p[NR::Y];
812     int x1 = (int)(x - item->canvas->close_enough);
813     int y1 = (int)(y - item->canvas->close_enough);
814     int x2 = (int)(x + item->canvas->close_enough);
815     int y2 = (int)(y + item->canvas->close_enough);
817     double best = 0.0;
818     *actual_item = NULL;
820     double dist = 0.0;
822     for (GList *list = group->items; list; list = list->next) {
823         SPCanvasItem *child = (SPCanvasItem *)list->data;
825         if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
826             SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
828             int has_point;
829             if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
830                 dist = sp_canvas_item_invoke_point (child, p, &point_item);
831                 has_point = TRUE;
832             } else
833                 has_point = FALSE;
835             if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
836                 best = dist;
837                 *actual_item = point_item;
838             }
839         }
840     }
842     return best;
845 /**
846  * Renders all visible canvas group items in buf rectangle.
847  */
848 static void
849 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
851     SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
853     for (GList *list = group->items; list; list = list->next) {
854         SPCanvasItem *child = (SPCanvasItem *)list->data;
855         if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
856             if ((child->x1 < buf->rect.x1) &&
857                 (child->y1 < buf->rect.y1) &&
858                 (child->x2 > buf->rect.x0) &&
859                 (child->y2 > buf->rect.y0)) {
860                 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
861                     SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
862             }
863         }
864     }
867 /**
868  * Adds an item to a canvas group.
869  */
870 static void
871 group_add (SPCanvasGroup *group, SPCanvasItem *item)
873     gtk_object_ref (GTK_OBJECT (item));
874     gtk_object_sink (GTK_OBJECT (item));
876     if (!group->items) {
877         group->items = g_list_append (group->items, item);
878         group->last = group->items;
879     } else {
880         group->last = g_list_append (group->last, item)->next;
881     }
883     sp_canvas_item_request_update (item);
886 /**
887  * Removes an item from a canvas group
888  */
889 static void
890 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
892     g_return_if_fail (group != NULL);
893     g_return_if_fail (SP_IS_CANVAS_GROUP (group));
894     g_return_if_fail (item != NULL);
896     for (GList *children = group->items; children; children = children->next) {
897         if (children->data == item) {
899             /* Unparent the child */
901             item->parent = NULL;
902             gtk_object_unref (GTK_OBJECT (item));
904             /* Remove it from the list */
906             if (children == group->last) group->last = children->prev;
908             group->items = g_list_remove_link (group->items, children);
909             g_list_free (children);
910             break;
911         }
912     }
915 /* SPCanvas */
917 static void sp_canvas_class_init (SPCanvasClass *klass);
918 static void sp_canvas_init (SPCanvas *canvas);
919 static void sp_canvas_destroy (GtkObject *object);
921 static void sp_canvas_realize (GtkWidget *widget);
922 static void sp_canvas_unrealize (GtkWidget *widget);
924 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
925 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
927 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
928 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
929 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
930 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
931 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
932 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
933 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
934 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
936 static GtkWidgetClass *canvas_parent_class;
938 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb);
939 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb);
940 static void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val);
941 static int do_update (SPCanvas *canvas);
943 /**
944  * Registers the SPCanvas class if necessary, and returns the type ID
945  * associated to it.
946  *
947  * \return The type ID of the SPCanvas class.
948  **/
949 GType sp_canvas_get_type(void)
951     static GType type = 0;
952     if (!type) {
953         GTypeInfo info = {
954             sizeof(SPCanvasClass),
955             0, // base_init
956             0, // base_finalize
957             (GClassInitFunc)sp_canvas_class_init,
958             0, // class_finalize
959             0, // class_data
960             sizeof(SPCanvas),
961             0, // n_preallocs
962             (GInstanceInitFunc)sp_canvas_init,
963             0 // value_table
964         };
965         type = g_type_register_static(GTK_TYPE_WIDGET, "SPCanvas", &info, static_cast<GTypeFlags>(0));
966     }
967     return type;
970 /**
971  * Class initialization function for SPCanvasClass.
972  */
973 static void
974 sp_canvas_class_init (SPCanvasClass *klass)
976     GtkObjectClass *object_class = (GtkObjectClass *) klass;
977     GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
979     canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
981     object_class->destroy = sp_canvas_destroy;
983     widget_class->realize = sp_canvas_realize;
984     widget_class->unrealize = sp_canvas_unrealize;
985     widget_class->size_request = sp_canvas_size_request;
986     widget_class->size_allocate = sp_canvas_size_allocate;
987     widget_class->button_press_event = sp_canvas_button;
988     widget_class->button_release_event = sp_canvas_button;
989     widget_class->motion_notify_event = sp_canvas_motion;
990     widget_class->scroll_event = sp_canvas_scroll;
991     widget_class->expose_event = sp_canvas_expose;
992     widget_class->key_press_event = sp_canvas_key;
993     widget_class->key_release_event = sp_canvas_key;
994     widget_class->enter_notify_event = sp_canvas_crossing;
995     widget_class->leave_notify_event = sp_canvas_crossing;
996     widget_class->focus_in_event = sp_canvas_focus_in;
997     widget_class->focus_out_event = sp_canvas_focus_out;
1000 /**
1001  * Callback: object initialization for SPCanvas.
1002  */
1003 static void
1004 sp_canvas_init (SPCanvas *canvas)
1006     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
1007     GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
1008     GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
1010     canvas->pick_event.type = GDK_LEAVE_NOTIFY;
1011     canvas->pick_event.crossing.x = 0;
1012     canvas->pick_event.crossing.y = 0;
1014     /* Create the root item as a special case */
1015     canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
1016     canvas->root->canvas = canvas;
1018     gtk_object_ref (GTK_OBJECT (canvas->root));
1019     gtk_object_sink (GTK_OBJECT (canvas->root));
1021     canvas->need_repick = TRUE;
1023     // See comment at in sp-canvas.h.
1024     canvas->gen_all_enter_events = false;
1026     canvas->tiles=NULL;
1027     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1028     canvas->tileH=canvas->tileV=0;
1030     canvas->forced_redraw_count = 0;
1031     canvas->forced_redraw_limit = -1;
1033 #if ENABLE_LCMS
1034     canvas->enable_cms_display_adj = false;
1035     canvas->cms_key = new Glib::ustring("");
1036 #endif // ENABLE_LCMS
1038     canvas->is_scrolling = false;
1042 /**
1043  * Convenience function to remove the idle handler of a canvas.
1044  */
1045 static void
1046 remove_idle (SPCanvas *canvas)
1048     if (canvas->idle_id) {
1049         gtk_idle_remove (canvas->idle_id);
1050         canvas->idle_id = 0;
1051     }
1054 /*
1055  * Removes the transient state of the canvas (idle handler, grabs).
1056  */
1057 static void
1058 shutdown_transients (SPCanvas *canvas)
1060     /* We turn off the need_redraw flag, since if the canvas is mapped again
1061      * it will request a redraw anyways.  We do not turn off the need_update
1062      * flag, though, because updates are not queued when the canvas remaps
1063      * itself.
1064      */
1065     if (canvas->need_redraw) {
1066         canvas->need_redraw = FALSE;
1067     }
1068     if ( canvas->tiles ) g_free(canvas->tiles);
1069     canvas->tiles=NULL;
1070     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1071     canvas->tileH=canvas->tileV=0;
1073     if (canvas->grabbed_item) {
1074         canvas->grabbed_item = NULL;
1075         gdk_pointer_ungrab (GDK_CURRENT_TIME);
1076     }
1078     remove_idle (canvas);
1081 /**
1082  * Destroy handler for SPCanvas.
1083  */
1084 static void
1085 sp_canvas_destroy (GtkObject *object)
1087     SPCanvas *canvas = SP_CANVAS (object);
1089     if (canvas->root) {
1090         gtk_object_unref (GTK_OBJECT (canvas->root));
1091         canvas->root = NULL;
1092     }
1094     shutdown_transients (canvas);
1096     if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1097         (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1100 /**
1101  * Returns new canvas as widget.
1102  */
1103 GtkWidget *
1104 sp_canvas_new_aa (void)
1106     SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1108     return (GtkWidget *) canvas;
1111 /**
1112  * The canvas widget's realize callback.
1113  */
1114 static void
1115 sp_canvas_realize (GtkWidget *widget)
1117     SPCanvas *canvas = SP_CANVAS (widget);
1119     GdkWindowAttr attributes;
1120     attributes.window_type = GDK_WINDOW_CHILD;
1121     attributes.x = widget->allocation.x;
1122     attributes.y = widget->allocation.y;
1123     attributes.width = widget->allocation.width;
1124     attributes.height = widget->allocation.height;
1125     attributes.wclass = GDK_INPUT_OUTPUT;
1126     attributes.visual = gdk_rgb_get_visual ();
1127     attributes.colormap = gdk_rgb_get_cmap ();
1128     attributes.event_mask = (gtk_widget_get_events (widget) |
1129                              GDK_EXPOSURE_MASK |
1130                              GDK_BUTTON_PRESS_MASK |
1131                              GDK_BUTTON_RELEASE_MASK |
1132                              GDK_POINTER_MOTION_MASK |
1133                              ( HAS_BROKEN_MOTION_HINTS ?
1134                                0 : GDK_POINTER_MOTION_HINT_MASK ) |
1135                              GDK_PROXIMITY_IN_MASK |
1136                              GDK_PROXIMITY_OUT_MASK |
1137                              GDK_KEY_PRESS_MASK |
1138                              GDK_KEY_RELEASE_MASK |
1139                              GDK_ENTER_NOTIFY_MASK |
1140                              GDK_LEAVE_NOTIFY_MASK |
1141                              GDK_FOCUS_CHANGE_MASK);
1142     gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1144     widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1145     gdk_window_set_user_data (widget->window, widget);
1147     if ( prefs_get_int_attribute ("options.useextinput", "value", 1) )
1148         gtk_widget_set_events(widget, attributes.event_mask);
1150     widget->style = gtk_style_attach (widget->style, widget->window);
1152     GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1154     canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1157 /**
1158  * The canvas widget's unrealize callback.
1159  */
1160 static void
1161 sp_canvas_unrealize (GtkWidget *widget)
1163     SPCanvas *canvas = SP_CANVAS (widget);
1165     canvas->current_item = NULL;
1166     canvas->grabbed_item = NULL;
1167     canvas->focused_item = NULL;
1169     shutdown_transients (canvas);
1171     gdk_gc_destroy (canvas->pixmap_gc);
1172     canvas->pixmap_gc = NULL;
1174     if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1175         (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1178 /**
1179  * The canvas widget's size_request callback.
1180  */
1181 static void
1182 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1184     static_cast<void>(SP_CANVAS (widget));
1186     req->width = 256;
1187     req->height = 256;
1190 /**
1191  * The canvas widget's size_allocate callback.
1192  */
1193 static void
1194 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1196     SPCanvas *canvas = SP_CANVAS (widget);
1198     /* Schedule redraw of new region */
1199     sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1200     if (allocation->width > widget->allocation.width) {
1201         sp_canvas_request_redraw (canvas,
1202                                   canvas->x0 + widget->allocation.width,
1203                                   0,
1204                                   canvas->x0 + allocation->width,
1205                                   canvas->y0 + allocation->height);
1206     }
1207     if (allocation->height > widget->allocation.height) {
1208         sp_canvas_request_redraw (canvas,
1209                                   0,
1210                                   canvas->y0 + widget->allocation.height,
1211                                   canvas->x0 + allocation->width,
1212                                   canvas->y0 + allocation->height);
1213     }
1215     widget->allocation = *allocation;
1216     
1217     if (GTK_WIDGET_REALIZED (widget)) {
1218         gdk_window_move_resize (widget->window,
1219                                 widget->allocation.x, widget->allocation.y,
1220                                 widget->allocation.width, widget->allocation.height);
1221     }
1224 /**
1225  * Helper that emits an event for an item in the canvas, be it the current
1226  * item, grabbed item, or focused item, as appropriate.
1227  */
1228 static int
1229 emit_event (SPCanvas *canvas, GdkEvent *event)
1231     guint mask;
1233     if (canvas->grabbed_item) {
1234         switch (event->type) {
1235         case GDK_ENTER_NOTIFY:
1236             mask = GDK_ENTER_NOTIFY_MASK;
1237             break;
1238         case GDK_LEAVE_NOTIFY:
1239             mask = GDK_LEAVE_NOTIFY_MASK;
1240             break;
1241         case GDK_MOTION_NOTIFY:
1242             mask = GDK_POINTER_MOTION_MASK;
1243             break;
1244         case GDK_BUTTON_PRESS:
1245         case GDK_2BUTTON_PRESS:
1246         case GDK_3BUTTON_PRESS:
1247             mask = GDK_BUTTON_PRESS_MASK;
1248             break;
1249         case GDK_BUTTON_RELEASE:
1250             mask = GDK_BUTTON_RELEASE_MASK;
1251             break;
1252         case GDK_KEY_PRESS:
1253             mask = GDK_KEY_PRESS_MASK;
1254             break;
1255         case GDK_KEY_RELEASE:
1256             mask = GDK_KEY_RELEASE_MASK;
1257             break;
1258         case GDK_SCROLL:
1259             mask = GDK_SCROLL;
1260             break;
1261         default:
1262             mask = 0;
1263             break;
1264         }
1266         if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1267     }
1269     /* Convert to world coordinates -- we have two cases because of diferent
1270      * offsets of the fields in the event structures.
1271      */
1273     GdkEvent ev = *event;
1275     switch (ev.type) {
1276     case GDK_ENTER_NOTIFY:
1277     case GDK_LEAVE_NOTIFY:
1278         ev.crossing.x += canvas->x0;
1279         ev.crossing.y += canvas->y0;
1280         break;
1281     case GDK_MOTION_NOTIFY:
1282     case GDK_BUTTON_PRESS:
1283     case GDK_2BUTTON_PRESS:
1284     case GDK_3BUTTON_PRESS:
1285     case GDK_BUTTON_RELEASE:
1286         ev.motion.x += canvas->x0;
1287         ev.motion.y += canvas->y0;
1288         break;
1289     default:
1290         break;
1291     }
1293     /* Choose where we send the event */
1295     /* canvas->current_item becomes NULL in some cases under Win32
1296     ** (e.g. if the pointer leaves the window).  So this is a hack that
1297     ** Lauris applied to SP to get around the problem.
1298     */
1299     SPCanvasItem* item = NULL;
1300     if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1301         item = canvas->grabbed_item;
1302     } else {
1303         item = canvas->current_item;
1304     }
1306     if (canvas->focused_item &&
1307         ((event->type == GDK_KEY_PRESS) ||
1308          (event->type == GDK_KEY_RELEASE) ||
1309          (event->type == GDK_FOCUS_CHANGE))) {
1310         item = canvas->focused_item;
1311     }
1313     /* The event is propagated up the hierarchy (for if someone connected to
1314      * a group instead of a leaf event), and emission is stopped if a
1315      * handler returns TRUE, just like for GtkWidget events.
1316      */
1318     gint finished = FALSE;
1320     while (item && !finished) {
1321         gtk_object_ref (GTK_OBJECT (item));
1322         gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1323         SPCanvasItem *parent = item->parent;
1324         gtk_object_unref (GTK_OBJECT (item));
1325         item = parent;
1326     }
1328     return finished;
1331 /**
1332  * Helper that re-picks the current item in the canvas, based on the event's
1333  * coordinates and emits enter/leave events for items as appropriate.
1334  */
1335 static int
1336 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1338     int button_down = 0;
1339     double x, y;
1341     if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1342         return FALSE;
1344     int retval = FALSE;
1346     if (canvas->gen_all_enter_events == false) {
1347         // If a button is down, we'll perform enter and leave events on the
1348         // current item, but not enter on any other item.  This is more or
1349         // less like X pointer grabbing for canvas items.
1350         //
1351         button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1352                 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1354         if (!button_down) canvas->left_grabbed_item = FALSE;
1355     }
1357     /* Save the event in the canvas.  This is used to synthesize enter and
1358      * leave events in case the current item changes.  It is also used to
1359      * re-pick the current item if the current one gets deleted.  Also,
1360      * synthesize an enter event.
1361      */
1362     if (event != &canvas->pick_event) {
1363         if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1364             /* these fields have the same offsets in both types of events */
1366             canvas->pick_event.crossing.type       = GDK_ENTER_NOTIFY;
1367             canvas->pick_event.crossing.window     = event->motion.window;
1368             canvas->pick_event.crossing.send_event = event->motion.send_event;
1369             canvas->pick_event.crossing.subwindow  = NULL;
1370             canvas->pick_event.crossing.x          = event->motion.x;
1371             canvas->pick_event.crossing.y          = event->motion.y;
1372             canvas->pick_event.crossing.mode       = GDK_CROSSING_NORMAL;
1373             canvas->pick_event.crossing.detail     = GDK_NOTIFY_NONLINEAR;
1374             canvas->pick_event.crossing.focus      = FALSE;
1375             canvas->pick_event.crossing.state      = event->motion.state;
1377             /* these fields don't have the same offsets in both types of events */
1379             if (event->type == GDK_MOTION_NOTIFY) {
1380                 canvas->pick_event.crossing.x_root = event->motion.x_root;
1381                 canvas->pick_event.crossing.y_root = event->motion.y_root;
1382             } else {
1383                 canvas->pick_event.crossing.x_root = event->button.x_root;
1384                 canvas->pick_event.crossing.y_root = event->button.y_root;
1385             }
1386         } else {
1387             canvas->pick_event = *event;
1388         }
1389     }
1391     /* Don't do anything else if this is a recursive call */
1392     if (canvas->in_repick) return retval;
1394     /* LeaveNotify means that there is no current item, so we don't look for one */
1395     if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1396         /* these fields don't have the same offsets in both types of events */
1398         if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1399             x = canvas->pick_event.crossing.x;
1400             y = canvas->pick_event.crossing.y;
1401         } else {
1402             x = canvas->pick_event.motion.x;
1403             y = canvas->pick_event.motion.y;
1404         }
1406         /* world coords */
1407         x += canvas->x0;
1408         y += canvas->y0;
1410         /* find the closest item */
1411         if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1412             sp_canvas_item_invoke_point (canvas->root, NR::Point(x, y), &canvas->new_current_item);
1413         } else {
1414             canvas->new_current_item = NULL;
1415         }
1416     } else {
1417         canvas->new_current_item = NULL;
1418     }
1420     if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1421         return retval; /* current item did not change */
1422     }
1424     /* Synthesize events for old and new current items */
1426     if ((canvas->new_current_item != canvas->current_item)
1427         && (canvas->current_item != NULL)
1428         && !canvas->left_grabbed_item) {
1429         GdkEvent new_event;
1430         SPCanvasItem *item;
1432         item = canvas->current_item;
1434         new_event = canvas->pick_event;
1435         new_event.type = GDK_LEAVE_NOTIFY;
1437         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1438         new_event.crossing.subwindow = NULL;
1439         canvas->in_repick = TRUE;
1440         retval = emit_event (canvas, &new_event);
1441         canvas->in_repick = FALSE;
1442     }
1444     if (canvas->gen_all_enter_events == false) {
1445         // new_current_item may have been set to NULL during the call to
1446         // emit_event() above
1447         if ((canvas->new_current_item != canvas->current_item) && button_down) {
1448             canvas->left_grabbed_item = TRUE;
1449             return retval;
1450         }
1451     }
1453     /* Handle the rest of cases */
1455     canvas->left_grabbed_item = FALSE;
1456     canvas->current_item = canvas->new_current_item;
1458     if (canvas->current_item != NULL) {
1459         GdkEvent new_event;
1461         new_event = canvas->pick_event;
1462         new_event.type = GDK_ENTER_NOTIFY;
1463         new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1464         new_event.crossing.subwindow = NULL;
1465         retval = emit_event (canvas, &new_event);
1466     }
1468     return retval;
1471 /**
1472  * Button event handler for the canvas.
1473  */
1474 static gint
1475 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1477     SPCanvas *canvas = SP_CANVAS (widget);
1479     int retval = FALSE;
1481     /* dispatch normally regardless of the event's window if an item has
1482        has a pointer grab in effect */
1483     if (!canvas->grabbed_item &&
1484         event->window != SP_CANVAS_WINDOW (canvas))
1485         return retval;
1487     int mask;
1488     switch (event->button) {
1489     case 1:
1490         mask = GDK_BUTTON1_MASK;
1491         break;
1492     case 2:
1493         mask = GDK_BUTTON2_MASK;
1494         break;
1495     case 3:
1496         mask = GDK_BUTTON3_MASK;
1497         break;
1498     case 4:
1499         mask = GDK_BUTTON4_MASK;
1500         break;
1501     case 5:
1502         mask = GDK_BUTTON5_MASK;
1503         break;
1504     default:
1505         mask = 0;
1506     }
1508     switch (event->type) {
1509     case GDK_BUTTON_PRESS:
1510     case GDK_2BUTTON_PRESS:
1511     case GDK_3BUTTON_PRESS:
1512         /* Pick the current item as if the button were not pressed, and
1513          * then process the event.
1514          */
1515         canvas->state = event->state;
1516         pick_current_item (canvas, (GdkEvent *) event);
1517         canvas->state ^= mask;
1518         retval = emit_event (canvas, (GdkEvent *) event);
1519         break;
1521     case GDK_BUTTON_RELEASE:
1522         /* Process the event as if the button were pressed, then repick
1523          * after the button has been released
1524          */
1525         canvas->state = event->state;
1526         retval = emit_event (canvas, (GdkEvent *) event);
1527         event->state ^= mask;
1528         canvas->state = event->state;
1529         pick_current_item (canvas, (GdkEvent *) event);
1530         event->state ^= mask;
1531         break;
1533     default:
1534         g_assert_not_reached ();
1535     }
1537     return retval;
1540 /**
1541  * Scroll event handler for the canvas.
1542  *
1543  * \todo FIXME: generate motion events to re-select items.
1544  */
1545 static gint
1546 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1548     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1551 #if HAS_GDK_EVENT_REQUEST_MOTIONS
1552 static inline void request_motions(GdkWindow *, GdkEventMotion *event) {
1553     gdk_event_request_motions(event);
1555 #else
1556 static inline void request_motions(GdkWindow *w, GdkEventMotion *) {
1557     gdk_window_get_pointer(w, NULL, NULL, NULL);
1559 #endif
1561 /**
1562  * Motion event handler for the canvas.
1563  */
1564 static int
1565 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1567     int status;
1568     SPCanvas *canvas = SP_CANVAS (widget);
1570     if (event->window != SP_CANVAS_WINDOW (canvas))
1571         return FALSE;
1573     if (canvas->pixmap_gc == NULL) // canvas being deleted
1574         return FALSE;
1576     canvas->state = event->state;
1577     pick_current_item (canvas, (GdkEvent *) event);
1579     status = emit_event (canvas, (GdkEvent *) event);
1581     if (event->is_hint) {
1582         request_motions(widget->window, event);
1583     }
1585     return status;
1588 static void
1589 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)
1591     GtkWidget *widget = GTK_WIDGET (canvas);
1593     SPCanvasBuf buf;
1594     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1595         buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1596     } else {
1597         buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1598     }
1600     // Mark the region clean
1601     sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1603     buf.buf_rowstride = sw * 4; 
1604     buf.rect.x0 = x0;
1605     buf.rect.y0 = y0;
1606     buf.rect.x1 = x1;
1607     buf.rect.y1 = y1;
1608     buf.visible_rect.x0 = draw_x1;
1609     buf.visible_rect.y0 = draw_y1;
1610     buf.visible_rect.x1 = draw_x2;
1611     buf.visible_rect.y1 = draw_y2;
1612     GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1613     buf.bg_color = (((color->red & 0xff00) << 8)
1614                     | (color->green & 0xff00)
1615                     | (color->blue >> 8));
1616     buf.is_empty = true;
1618     buf.ct = nr_create_cairo_context_canvasbuf (&(buf.visible_rect), &buf);
1620     if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1621         SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1622     }
1624 #if ENABLE_LCMS
1625     cmsHTRANSFORM transf = 0;
1626     long long int fromDisplay = prefs_get_int_attribute_limited( "options.displayprofile", "from_display", 0, 0, 1 );
1627     if ( fromDisplay ) {
1628         transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" );
1629     } else {
1630         transf = Inkscape::colorprofile_get_display_transform();
1631     }
1632 #endif // ENABLE_LCMS
1634     if (buf.is_empty) {
1635 #if ENABLE_LCMS
1636         if ( transf && canvas->enable_cms_display_adj ) {
1637             cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1638         }
1639 #endif // ENABLE_LCMS
1640         gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1641         gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1642                             canvas->pixmap_gc,
1643                             TRUE,
1644                             x0 - canvas->x0, y0 - canvas->y0,
1645                             x1 - x0, y1 - y0);
1646     } else {
1648 #if ENABLE_LCMS
1649         if ( transf && canvas->enable_cms_display_adj ) {
1650             for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1651                 guchar* p = buf.buf + (sw * 3) * yy;
1652                 cmsDoTransform( transf, p, p, (x1 - x0) );
1653             }
1654         }
1655 #endif // ENABLE_LCMS
1657 // Now we only need to output the prepared pixmap to the actual screen, and this define chooses one
1658 // of the two ways to do it. The cairo way is direct and straightforward, but unfortunately
1659 // noticeably slower. I asked Carl Worth but he was unable so far to suggest any specific reason
1660 // for this slowness. So, for now we use the oldish method: squeeze out 32bpp buffer to 24bpp and
1661 // use gdk_draw_rgb_image_dithalign, for unfortunately gdk can only handle 24 bpp, which cairo
1662 // cannot handle at all. Still, this way is currently faster even despite the blit with squeeze.
1664 ///#define CANVAS_OUTPUT_VIA_CAIRO
1666 #ifdef CANVAS_OUTPUT_VIA_CAIRO
1668         buf.cst = cairo_image_surface_create_for_data (
1669             buf.buf,
1670             CAIRO_FORMAT_ARGB32,  // unpacked, i.e. 32 bits! one byte is unused
1671             x1 - x0, y1 - y0,
1672             buf.buf_rowstride
1673             );
1674         cairo_t *window_ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1675         cairo_set_source_surface (window_ct, buf.cst, x0 - canvas->x0, y0 - canvas->y0);
1676         cairo_paint (window_ct);
1677         cairo_destroy (window_ct);
1678         cairo_surface_finish (buf.cst);
1679         cairo_surface_destroy (buf.cst);
1681 #else
1683         NRPixBlock b3;
1684         nr_pixblock_setup_fast (&b3, NR_PIXBLOCK_MODE_R8G8B8, x0, y0, x1, y1, TRUE);
1686         NRPixBlock b4;
1687         nr_pixblock_setup_extern (&b4, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1,
1688                                   buf.buf,
1689                                   buf.buf_rowstride,
1690                                   FALSE, FALSE);
1692         // this does the 32->24 squishing, using an assembler routine:
1693         nr_blit_pixblock_pixblock (&b3, &b4);
1695         gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1696                                       canvas->pixmap_gc,
1697                                       x0 - canvas->x0, y0 - canvas->y0,
1698                                       x1 - x0, y1 - y0,
1699                                       GDK_RGB_DITHER_MAX,
1700                                       b3.data.px,
1701                                       sw * 3,
1702                                       x0 - canvas->x0, y0 - canvas->y0);
1704         nr_pixblock_release (&b3);
1705         nr_pixblock_release (&b4);
1706 #endif
1707     }
1709     cairo_surface_t *cst = cairo_get_target(buf.ct);
1710     cairo_destroy (buf.ct);
1711     cairo_surface_finish (cst);
1712     cairo_surface_destroy (cst);
1714     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1715         nr_pixelstore_256K_free (buf.buf);
1716     } else {
1717         nr_pixelstore_1M_free (buf.buf);
1718     }
1721 struct PaintRectSetup {
1722     SPCanvas* canvas;
1723     NRRectL big_rect;
1724     GTimeVal start_time;
1725     int max_pixels;
1726     NR::Point mouse_loc;
1727 };
1729 /**
1730  * Paint the given rect, recursively subdividing the region until it is the size of a single
1731  * buffer.
1732  *
1733  * @return true if the drawing completes
1734  */
1735 static int
1736 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1738     GTimeVal now;
1739     g_get_current_time (&now);
1741     glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1742         + (now.tv_usec - setup->start_time.tv_usec);
1744     // Allow only very fast buffers to be run together;
1745     // as soon as the total redraw time exceeds 1ms, cancel;
1746     // this returns control to the idle loop and allows Inkscape to process user input
1747     // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1748     // it will get back and finish painting what remains to paint.
1749     if (elapsed > 1000) {
1751         // Interrupting redraw isn't always good.
1752         // For example, when you drag one node of a big path, only the buffer containing
1753         // the mouse cursor will be redrawn again and again, and the rest of the path
1754         // will remain stale because Inkscape never has enough idle time to redraw all
1755         // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1756         // If this limit is set, and if we have aborted redraw more times than is allowed,
1757         // interrupting is blocked and we're forced to redraw full screen once
1758         // (after which we can again interrupt forced_redraw_limit times).
1759         if (setup->canvas->forced_redraw_limit < 0 ||
1760             setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1762             if (setup->canvas->forced_redraw_limit != -1) {
1763                 setup->canvas->forced_redraw_count++;
1764             }
1766             return false;
1767         }
1768     }
1770     // Find the optimal buffer dimensions
1771     int bw = this_rect.x1 - this_rect.x0;
1772     int bh = this_rect.y1 - this_rect.y0;
1773     if ((bw < 1) || (bh < 1))
1774         return 0;
1776     if (bw * bh < setup->max_pixels) {
1777         // We are small enough
1778         sp_canvas_paint_single_buffer (setup->canvas,
1779                                        this_rect.x0, this_rect.y0,
1780                                        this_rect.x1, this_rect.y1,
1781                                        setup->big_rect.x0, setup->big_rect.y0,
1782                                        setup->big_rect.x1, setup->big_rect.y1, bw);
1783         return 1;
1784     }
1786     NRRectL lo = this_rect;
1787     NRRectL hi = this_rect;
1789 /*
1790 This test determines the redraw strategy:
1792 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1793 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1794 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1795 and seems to be faster for drawings with many smaller objects at zoom-out.
1797 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1798 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1799 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1800 faster.
1802 The default for now is the strips mode.
1803 */
1804     if (bw < bh || bh < 2 * TILE_SIZE) {
1805         // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1806         int mid = ((long long) this_rect.x0 + (long long) this_rect.x1) / 2;
1807         // Make sure that mid lies on a tile boundary
1808         mid = (mid / TILE_SIZE) * TILE_SIZE;
1810         lo.x1 = mid;
1811         hi.x0 = mid;
1813         if (setup->mouse_loc[NR::X] < mid) {
1814             // Always paint towards the mouse first
1815             return sp_canvas_paint_rect_internal(setup, lo)
1816                 && sp_canvas_paint_rect_internal(setup, hi);
1817         } else {
1818             return sp_canvas_paint_rect_internal(setup, hi)
1819                 && sp_canvas_paint_rect_internal(setup, lo);
1820         }
1821     } else {
1822         // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1823         int mid = ((long long) this_rect.y0 + (long long) this_rect.y1) / 2;
1824         // Make sure that mid lies on a tile boundary
1825         mid = (mid / TILE_SIZE) * TILE_SIZE;
1827         lo.y1 = mid;
1828         hi.y0 = mid;
1830         if (setup->mouse_loc[NR::Y] < mid) {
1831             // Always paint towards the mouse first
1832             return sp_canvas_paint_rect_internal(setup, lo)
1833                 && sp_canvas_paint_rect_internal(setup, hi);
1834         } else {
1835             return sp_canvas_paint_rect_internal(setup, hi)
1836                 && sp_canvas_paint_rect_internal(setup, lo);
1837         }
1838     }
1842 /**
1843  * Helper that draws a specific rectangular part of the canvas.
1844  *
1845  * @return true if the rectangle painting succeeds.
1846  */
1847 static bool
1848 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1850     g_return_val_if_fail (!canvas->need_update, false);
1852     NRRectL rect;
1853     rect.x0 = xx0;
1854     rect.x1 = xx1;
1855     rect.y0 = yy0;
1856     rect.y1 = yy1;
1858     // Clip rect-to-draw by the current visible area
1859     rect.x0 = MAX (rect.x0, canvas->x0);
1860     rect.y0 = MAX (rect.y0, canvas->y0);
1861     rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1862     rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1864 #ifdef DEBUG_REDRAW
1865     // paint the area to redraw yellow
1866     gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1867     gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1868                         canvas->pixmap_gc,
1869                         TRUE,
1870                         rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1871                         rect.x1 - rect.x0, rect.y1 - rect.y0);
1872 #endif
1874     PaintRectSetup setup;
1876     setup.canvas = canvas;
1877     setup.big_rect = rect;
1879     // Save the mouse location
1880     gint x, y;
1881     gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1882     setup.mouse_loc = sp_canvas_window_to_world (canvas, NR::Point(x,y));
1884     if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1885         // use 256K as a compromise to not slow down gradients
1886         // 256K is the cached buffer and we need 4 channels
1887         setup.max_pixels = 65536; // 256K/4
1888     } else {
1889         // paths only, so 1M works faster
1890         // 1M is the cached buffer and we need 4 channels
1891         setup.max_pixels = 262144; 
1892     }
1894     // Start the clock
1895     g_get_current_time(&(setup.start_time));
1897     // Go
1898     return sp_canvas_paint_rect_internal(&setup, rect);
1901 /**
1902  * Force a full redraw after a specified number of interrupted redraws
1903  */
1904 void
1905 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1906   g_return_if_fail(canvas != NULL);
1908   canvas->forced_redraw_limit = count;
1909   canvas->forced_redraw_count = 0;
1912 /**
1913  * End forced full redraw requests
1914  */
1915 void
1916 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1917   g_return_if_fail(canvas != NULL);
1919   canvas->forced_redraw_limit = -1;
1922 /**
1923  * The canvas widget's expose callback.
1924  */
1925 static gint
1926 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1928     SPCanvas *canvas = SP_CANVAS (widget);
1930     if (!GTK_WIDGET_DRAWABLE (widget) ||
1931         (event->window != SP_CANVAS_WINDOW (canvas)))
1932         return FALSE;
1934     int n_rects;
1935     GdkRectangle *rects;
1936     gdk_region_get_rectangles (event->region, &rects, &n_rects);
1938     for (int i = 0; i < n_rects; i++) {
1939         NRRectL rect;
1941         rect.x0 = rects[i].x + canvas->x0;
1942         rect.y0 = rects[i].y + canvas->y0;
1943         rect.x1 = rect.x0 + rects[i].width;
1944         rect.y1 = rect.y0 + rects[i].height;
1946         sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1947     }
1949     if (n_rects > 0)
1950         g_free (rects);
1952     return FALSE;
1955 /**
1956  * The canvas widget's keypress callback.
1957  */
1958 static gint
1959 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1961     return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1964 /**
1965  * Crossing event handler for the canvas.
1966  */
1967 static gint
1968 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1970     SPCanvas *canvas = SP_CANVAS (widget);
1972     if (event->window != SP_CANVAS_WINDOW (canvas))
1973         return FALSE;
1975     canvas->state = event->state;
1976     return pick_current_item (canvas, (GdkEvent *) event);
1979 /**
1980  * Focus in handler for the canvas.
1981  */
1982 static gint
1983 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
1985     GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1987     SPCanvas *canvas = SP_CANVAS (widget);
1989     if (canvas->focused_item) {
1990         return emit_event (canvas, (GdkEvent *) event);
1991     } else {
1992         return FALSE;
1993     }
1996 /**
1997  * Focus out handler for the canvas.
1998  */
1999 static gint
2000 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
2002     GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2004     SPCanvas *canvas = SP_CANVAS (widget);
2006     if (canvas->focused_item)
2007         return emit_event (canvas, (GdkEvent *) event);
2008     else
2009         return FALSE;
2012 /**
2013  * Helper that repaints the areas in the canvas that need it.
2014  *
2015  * @return true if all the dirty parts have been redrawn
2016  */
2017 static int
2018 paint (SPCanvas *canvas)
2020     if (canvas->need_update) {
2021         sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2022         canvas->need_update = FALSE;
2023     }
2025     if (!canvas->need_redraw)
2026         return TRUE;
2028     Gdk::Region to_paint;
2030     for (int j=canvas->tTop; j<canvas->tBottom; j++) {
2031         for (int i=canvas->tLeft; i<canvas->tRight; i++) {
2032             int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
2034             if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
2035                 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
2036                                    TILE_SIZE, TILE_SIZE));
2037             }
2039         }
2040     }
2042     if (!to_paint.empty()) {
2043         Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
2044         typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
2045         for (Iter i=rect.begin(); i != rect.end(); ++i) {
2046             int x0 = (*i).get_x();
2047             int y0 = (*i).get_y();
2048             int x1 = x0 + (*i).get_width();
2049             int y1 = y0 + (*i).get_height();
2050             if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
2051                 // Aborted
2052                 return FALSE;
2053             };
2054         }
2055     }
2057     canvas->need_redraw = FALSE;
2059     // we've had a full unaborted redraw, reset the full redraw counter
2060     if (canvas->forced_redraw_limit != -1) {
2061         canvas->forced_redraw_count = 0;
2062     }
2064     return TRUE;
2067 /**
2068  * Helper that invokes update, paint, and repick on canvas.
2069  */
2070 static int
2071 do_update (SPCanvas *canvas)
2073     if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop durring interrupted display!
2074         return TRUE;
2076     /* Cause the update if necessary */
2077     if (canvas->need_update) {
2078         sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2079         canvas->need_update = FALSE;
2080     }
2082     /* Paint if able to */
2083     if (GTK_WIDGET_DRAWABLE (canvas)) {
2084             return paint (canvas);
2085     }
2087     /* Pick new current item */
2088     while (canvas->need_repick) {
2089         canvas->need_repick = FALSE;
2090         pick_current_item (canvas, &canvas->pick_event);
2091     }
2093     return TRUE;
2096 /**
2097  * Idle handler for the canvas that deals with pending updates and redraws.
2098  */
2099 static gint
2100 idle_handler (gpointer data)
2102     GDK_THREADS_ENTER ();
2104     SPCanvas *canvas = SP_CANVAS (data);
2106     int const ret = do_update (canvas);
2108     if (ret) {
2109         /* Reset idle id */
2110         canvas->idle_id = 0;
2111     }
2113     GDK_THREADS_LEAVE ();
2115     return !ret;
2118 /**
2119  * Convenience function to add an idle handler to a canvas.
2120  */
2121 static void
2122 add_idle (SPCanvas *canvas)
2124     if (canvas->idle_id != 0)
2125         return;
2127     canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2130 /**
2131  * Returns the root group of the specified canvas.
2132  */
2133 SPCanvasGroup *
2134 sp_canvas_root (SPCanvas *canvas)
2136     g_return_val_if_fail (canvas != NULL, NULL);
2137     g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2139     return SP_CANVAS_GROUP (canvas->root);
2142 /**
2143  * Scrolls canvas to specific position (cx and cy are measured in screen pixels)
2144  */
2145 void
2146 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2148     g_return_if_fail (canvas != NULL);
2149     g_return_if_fail (SP_IS_CANVAS (canvas));
2151     int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels)
2152     int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do!
2153     int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the 
2154     int dy = iy - canvas->y0; // canvas w.r.t its previous position
2156     canvas->dx0 = cx; // here the 'd' stands for double, not delta!
2157     canvas->dy0 = cy;
2158     canvas->x0 = ix;
2159     canvas->y0 = iy;
2161     sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2163     if (!clear) {
2164         // scrolling without zoom; redraw only the newly exposed areas
2165         if ((dx != 0) || (dy != 0)) {
2166             canvas->is_scrolling = is_scrolling;
2167             if (GTK_WIDGET_REALIZED (canvas)) {
2168                 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2169             }
2170         }
2171     } else {
2172         // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2173     }
2176 /**
2177  * Updates canvas if necessary.
2178  */
2179 void
2180 sp_canvas_update_now (SPCanvas *canvas)
2182     g_return_if_fail (canvas != NULL);
2183     g_return_if_fail (SP_IS_CANVAS (canvas));
2185     if (!(canvas->need_update ||
2186           canvas->need_redraw))
2187         return;
2189     do_update (canvas);
2192 /**
2193  * Update callback for canvas widget.
2194  */
2195 static void
2196 sp_canvas_request_update (SPCanvas *canvas)
2198     canvas->need_update = TRUE;
2199     add_idle (canvas);
2202 /**
2203  * Forces redraw of rectangular canvas area.
2204  */
2205 void
2206 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2208     NRRectL bbox;
2209     NRRectL visible;
2210     NRRectL clip;
2212     g_return_if_fail (canvas != NULL);
2213     g_return_if_fail (SP_IS_CANVAS (canvas));
2215     if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2216     if ((x0 >= x1) || (y0 >= y1)) return;
2218     bbox.x0 = x0;
2219     bbox.y0 = y0;
2220     bbox.x1 = x1;
2221     bbox.y1 = y1;
2223     visible.x0 = canvas->x0;
2224     visible.y0 = canvas->y0;
2225     visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2226     visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2228     nr_rect_l_intersect (&clip, &bbox, &visible);
2230     sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2231     add_idle (canvas);
2234 /**
2235  * Sets world coordinates from win and canvas.
2236  */
2237 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2239     g_return_if_fail (canvas != NULL);
2240     g_return_if_fail (SP_IS_CANVAS (canvas));
2242     if (worldx) *worldx = canvas->x0 + winx;
2243     if (worldy) *worldy = canvas->y0 + winy;
2246 /**
2247  * Sets win coordinates from world and canvas.
2248  */
2249 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2251     g_return_if_fail (canvas != NULL);
2252     g_return_if_fail (SP_IS_CANVAS (canvas));
2254     if (winx) *winx = worldx - canvas->x0;
2255     if (winy) *winy = worldy - canvas->y0;
2258 /**
2259  * Converts point from win to world coordinates.
2260  */
2261 NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win)
2263     g_assert (canvas != NULL);
2264     g_assert (SP_IS_CANVAS (canvas));
2266     return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2269 /**
2270  * Converts point from world to win coordinates.
2271  */
2272 NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world)
2274     g_assert (canvas != NULL);
2275     g_assert (SP_IS_CANVAS (canvas));
2277     return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2280 /**
2281  * Returns true if point given in world coordinates is inside window.
2282  */
2283 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world)
2285     g_assert( canvas != NULL );
2286     g_assert(SP_IS_CANVAS(canvas));
2288     using NR::X;
2289     using NR::Y;
2290     GtkWidget const &w = *GTK_WIDGET(canvas);
2291     return ( ( canvas->x0 <= world[X] )  &&
2292              ( canvas->y0 <= world[Y] )  &&
2293              ( world[X] < canvas->x0 + w.allocation.width )  &&
2294              ( world[Y] < canvas->y0 + w.allocation.height ) );
2297 /**
2298  * Return canvas window coordinates as NR::Rect.
2299  */
2300 NR::Rect SPCanvas::getViewbox() const
2302     GtkWidget const *w = GTK_WIDGET(this);
2303     return NR::Rect(NR::Point(dx0, dy0),
2304                     NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2307 /**
2308  * Return canvas window coordinates as IRect (a rectangle defined by integers).
2309  */
2310 NR::IRect SPCanvas::getViewboxIntegers() const
2312     GtkWidget const *w = GTK_WIDGET(this);
2313     return NR::IRect(NR::IPoint(x0, y0),
2314                     NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height));
2317 inline int sp_canvas_tile_floor(int x)
2319     return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2322 inline int sp_canvas_tile_ceil(int x)
2324     return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2327 /**
2328  * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2329  */
2330 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2332     if ( nl >= nr || nt >= nb ) {
2333         if ( canvas->tiles ) g_free(canvas->tiles);
2334         canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2335         canvas->tileH=canvas->tileV=0;
2336         canvas->tiles=NULL;
2337         return;
2338     }
2339     int tl=sp_canvas_tile_floor(nl);
2340     int tt=sp_canvas_tile_floor(nt);
2341     int tr=sp_canvas_tile_ceil(nr);
2342     int tb=sp_canvas_tile_ceil(nb);
2344     int nh = tr-tl, nv = tb-tt;
2345     uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2346     for (int i=tl; i<tr; i++) {
2347         for (int j=tt; j<tb; j++) {
2348             int ind = (i-tl) + (j-tt)*nh;
2349             if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2350                 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2351             } else {
2352                 ntiles[ind]=0; // newly exposed areas get 0
2353             }
2354         }
2355     }
2356     if ( canvas->tiles ) g_free(canvas->tiles);
2357     canvas->tiles=ntiles;
2358     canvas->tLeft=tl;
2359     canvas->tTop=tt;
2360     canvas->tRight=tr;
2361     canvas->tBottom=tb;
2362     canvas->tileH=nh;
2363     canvas->tileV=nv;
2366 /*
2367  * Helper that queues a canvas rectangle for redraw
2368  */
2369 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2370     canvas->need_redraw = TRUE;
2372     sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2375 /**
2376  * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2377  */
2378 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2380     if ( nl >= nr || nt >= nb ) {
2381         return;
2382     }
2383     int tl=sp_canvas_tile_floor(nl);
2384     int tt=sp_canvas_tile_floor(nt);
2385     int tr=sp_canvas_tile_ceil(nr);
2386     int tb=sp_canvas_tile_ceil(nb);
2387     if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2388     if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2389     if ( tr > canvas->tRight ) tr=canvas->tRight;
2390     if ( tt < canvas->tTop ) tt=canvas->tTop;
2391     if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2393     for (int i=tl; i<tr; i++) {
2394         for (int j=tt; j<tb; j++) {
2395             canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2396         }
2397     }
2401 /*
2402   Local Variables:
2403   mode:c++
2404   c-file-style:"stroustrup"
2405   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2406   indent-tabs-mode:nil
2407   fill-column:99
2408   End:
2409 */
2410 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :