e9bf1633ee7a9cb4db3ae33e38b5a5e51c6dd6d7
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 <helper/recthull.h>
33 #include <display/sp-canvas.h>
34 #include "display-forward.h"
35 #include <libnr/nr-matrix-fns.h>
36 #include <libnr/nr-matrix-ops.h>
37 #include <libnr/nr-convex-hull.h>
38 #include "preferences.h"
39 #include "inkscape.h"
40 #include "sodipodi-ctrlrect.h"
41 #if ENABLE_LCMS
42 #include "color-profile-fns.h"
43 #endif // ENABLE_LCMS
44 #include "display/rendermode.h"
45 #include "libnr/nr-blit.h"
46 #include "display/inkscape-cairo.h"
47 #include "debug/gdk-event-latency-tracker.h"
48 #include "desktop.h"
49 #include "sp-namedview.h"
51 using Inkscape::Debug::GdkEventLatencyTracker;
53 // GTK_CHECK_VERSION returns false on failure
54 #define HAS_GDK_EVENT_REQUEST_MOTIONS GTK_CHECK_VERSION(2, 12, 0)
56 // gtk_check_version returns non-NULL on failure
57 static bool const HAS_BROKEN_MOTION_HINTS =
58 true || gtk_check_version(2, 12, 0) != NULL || !HAS_GDK_EVENT_REQUEST_MOTIONS;
60 // Define this to visualize the regions to be redrawn
61 //#define DEBUG_REDRAW 1;
63 // Tiles are a way to minimize the number of redraws, eliminating too small redraws.
64 // The canvas stores a 2D array of ints, each representing a TILE_SIZExTILE_SIZE pixels tile.
65 // If any part of it is dirtied, the entire tile is dirtied (its int is nonzero) and repainted.
66 #define TILE_SIZE 16
68 static gint const sp_canvas_update_priority = G_PRIORITY_HIGH_IDLE;
70 #define SP_CANVAS_WINDOW(c) (((GtkWidget *) (c))->window)
72 enum {
73 SP_CANVAS_ITEM_VISIBLE = 1 << 7,
74 SP_CANVAS_ITEM_NEED_UPDATE = 1 << 8,
75 SP_CANVAS_ITEM_NEED_AFFINE = 1 << 9
76 };
78 /**
79 * A group of Items.
80 */
81 struct SPCanvasGroup {
82 SPCanvasItem item;
84 GList *items, *last;
85 };
87 /**
88 * The SPCanvasGroup vtable.
89 */
90 struct SPCanvasGroupClass {
91 SPCanvasItemClass parent_class;
92 };
94 /**
95 * The SPCanvas vtable.
96 */
97 struct SPCanvasClass {
98 GtkWidgetClass parent_class;
99 };
101 static void group_add (SPCanvasGroup *group, SPCanvasItem *item);
102 static void group_remove (SPCanvasGroup *group, SPCanvasItem *item);
104 /* SPCanvasItem */
106 enum {ITEM_EVENT, ITEM_LAST_SIGNAL};
109 static void sp_canvas_request_update (SPCanvas *canvas);
111 static void track_latency(GdkEvent const *event);
112 static void sp_canvas_item_class_init (SPCanvasItemClass *klass);
113 static void sp_canvas_item_init (SPCanvasItem *item);
114 static void sp_canvas_item_dispose (GObject *object);
115 static void sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args);
117 static int emit_event (SPCanvas *canvas, GdkEvent *event);
119 static guint item_signals[ITEM_LAST_SIGNAL] = { 0 };
121 static GtkObjectClass *item_parent_class;
123 /**
124 * Registers the SPCanvasItem class with Glib and returns its type number.
125 */
126 GType
127 sp_canvas_item_get_type (void)
128 {
129 static GType type = 0;
130 if (!type) {
131 static GTypeInfo const info = {
132 sizeof (SPCanvasItemClass),
133 NULL, NULL,
134 (GClassInitFunc) sp_canvas_item_class_init,
135 NULL, NULL,
136 sizeof (SPCanvasItem),
137 0,
138 (GInstanceInitFunc) sp_canvas_item_init,
139 NULL
140 };
141 type = g_type_register_static (GTK_TYPE_OBJECT, "SPCanvasItem", &info, (GTypeFlags)0);
142 }
144 return type;
145 }
147 /**
148 * Initializes the SPCanvasItem vtable and the "event" signal.
149 */
150 static void
151 sp_canvas_item_class_init (SPCanvasItemClass *klass)
152 {
153 GObjectClass *object_class = (GObjectClass *) klass;
155 /* fixme: Derive from GObject */
156 item_parent_class = (GtkObjectClass*)gtk_type_class (GTK_TYPE_OBJECT);
158 item_signals[ITEM_EVENT] = g_signal_new ("event",
159 G_TYPE_FROM_CLASS (klass),
160 G_SIGNAL_RUN_LAST,
161 ((glong)((guint8*)&(klass->event) - (guint8*)klass)),
162 NULL, NULL,
163 sp_marshal_BOOLEAN__POINTER,
164 G_TYPE_BOOLEAN, 1,
165 GDK_TYPE_EVENT);
167 object_class->dispose = sp_canvas_item_dispose;
168 }
170 /**
171 * Callback for initialization of SPCanvasItem.
172 */
173 static void
174 sp_canvas_item_init (SPCanvasItem *item)
175 {
176 item->flags |= SP_CANVAS_ITEM_VISIBLE;
177 item->xform = Geom::Matrix(Geom::identity());
178 }
180 /**
181 * Constructs new SPCanvasItem on SPCanvasGroup.
182 */
183 SPCanvasItem *
184 sp_canvas_item_new (SPCanvasGroup *parent, GtkType type, gchar const *first_arg_name, ...)
185 {
186 va_list args;
188 g_return_val_if_fail (parent != NULL, NULL);
189 g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL);
190 g_return_val_if_fail (gtk_type_is_a (type, sp_canvas_item_get_type ()), NULL);
192 SPCanvasItem *item = SP_CANVAS_ITEM (gtk_type_new (type));
194 va_start (args, first_arg_name);
195 sp_canvas_item_construct (item, parent, first_arg_name, args);
196 va_end (args);
198 return item;
199 }
201 /**
202 * Sets up the newly created SPCanvasItem.
203 *
204 * We make it static for encapsulation reasons since it was nowhere used.
205 */
206 static void
207 sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args)
208 {
209 g_return_if_fail (SP_IS_CANVAS_GROUP (parent));
210 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
212 item->parent = SP_CANVAS_ITEM (parent);
213 item->canvas = item->parent->canvas;
215 g_object_set_valist (G_OBJECT (item), first_arg_name, args);
217 group_add (SP_CANVAS_GROUP (item->parent), item);
219 sp_canvas_item_request_update (item);
220 }
222 /**
223 * Helper function that requests redraw only if item's visible flag is set.
224 */
225 static void
226 redraw_if_visible (SPCanvasItem *item)
227 {
228 if (item->flags & SP_CANVAS_ITEM_VISIBLE) {
229 int x0 = (int)(item->x1);
230 int x1 = (int)(item->x2);
231 int y0 = (int)(item->y1);
232 int y1 = (int)(item->y2);
234 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
235 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
236 }
237 }
238 }
240 /**
241 * Callback that removes item from all referers and destroys it.
242 */
243 static void
244 sp_canvas_item_dispose (GObject *object)
245 {
246 SPCanvasItem *item = SP_CANVAS_ITEM (object);
248 // Hack: if this is a ctrlrect, move it to 0,0;
249 // this redraws only the stroke of the rect to be deleted,
250 // avoiding redraw of the entire area
251 if (SP_IS_CTRLRECT(item)) {
252 SP_CTRLRECT(object)->setRectangle(Geom::Rect(Geom::Point(0,0),Geom::Point(0,0)));
253 SP_CTRLRECT(object)->update(item->xform, 0);
254 } else {
255 redraw_if_visible (item);
256 }
257 item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
259 if (item == item->canvas->current_item) {
260 item->canvas->current_item = NULL;
261 item->canvas->need_repick = TRUE;
262 }
264 if (item == item->canvas->new_current_item) {
265 item->canvas->new_current_item = NULL;
266 item->canvas->need_repick = TRUE;
267 }
269 if (item == item->canvas->grabbed_item) {
270 item->canvas->grabbed_item = NULL;
271 gdk_pointer_ungrab (GDK_CURRENT_TIME);
272 }
274 if (item == item->canvas->focused_item)
275 item->canvas->focused_item = NULL;
277 if (item->parent) {
278 group_remove (SP_CANVAS_GROUP (item->parent), item);
279 }
281 G_OBJECT_CLASS (item_parent_class)->dispose (object);
282 }
284 /**
285 * Helper function to update item and its children.
286 *
287 * NB! affine is parent2canvas.
288 */
289 static void
290 sp_canvas_item_invoke_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags)
291 {
292 /* Apply the child item's transform */
293 Geom::Matrix child_affine = item->xform * affine;
295 /* apply object flags to child flags */
296 int child_flags = flags & ~SP_CANVAS_UPDATE_REQUESTED;
298 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
299 child_flags |= SP_CANVAS_UPDATE_REQUESTED;
301 if (item->flags & SP_CANVAS_ITEM_NEED_AFFINE)
302 child_flags |= SP_CANVAS_UPDATE_AFFINE;
304 if (child_flags & (SP_CANVAS_UPDATE_REQUESTED | SP_CANVAS_UPDATE_AFFINE)) {
305 if (SP_CANVAS_ITEM_GET_CLASS (item)->update)
306 SP_CANVAS_ITEM_GET_CLASS (item)->update (item, child_affine, child_flags);
307 }
309 GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_UPDATE);
310 GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_AFFINE);
311 }
313 /**
314 * Helper function to invoke the point method of the item.
315 *
316 * The argument x, y should be in the parent's item-relative coordinate
317 * system. This routine applies the inverse of the item's transform,
318 * maintaining the affine invariant.
319 */
320 static double
321 sp_canvas_item_invoke_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item)
322 {
323 if (SP_CANVAS_ITEM_GET_CLASS (item)->point)
324 return SP_CANVAS_ITEM_GET_CLASS (item)->point (item, p, actual_item);
326 return NR_HUGE;
327 }
329 /**
330 * Makes the item's affine transformation matrix be equal to the specified
331 * matrix.
332 *
333 * @item: A canvas item.
334 * @affine: An affine transformation matrix.
335 */
336 void
337 sp_canvas_item_affine_absolute (SPCanvasItem *item, Geom::Matrix const &affine)
338 {
339 item->xform = affine;
341 if (!(item->flags & SP_CANVAS_ITEM_NEED_AFFINE)) {
342 item->flags |= SP_CANVAS_ITEM_NEED_AFFINE;
343 if (item->parent != NULL) {
344 sp_canvas_item_request_update (item->parent);
345 } else {
346 sp_canvas_request_update (item->canvas);
347 }
348 }
350 item->canvas->need_repick = TRUE;
351 }
353 /**
354 * Convenience function to reorder items in a group's child list.
355 *
356 * This puts the specified link after the "before" link.
357 */
358 static void
359 put_item_after (GList *link, GList *before)
360 {
361 if (link == before)
362 return;
364 SPCanvasGroup *parent = SP_CANVAS_GROUP (SP_CANVAS_ITEM (link->data)->parent);
366 if (before == NULL) {
367 if (link == parent->items) return;
369 link->prev->next = link->next;
371 if (link->next) {
372 link->next->prev = link->prev;
373 } else {
374 parent->last = link->prev;
375 }
377 link->prev = before;
378 link->next = parent->items;
379 link->next->prev = link;
380 parent->items = link;
381 } else {
382 if ((link == parent->last) && (before == parent->last->prev))
383 return;
385 if (link->next)
386 link->next->prev = link->prev;
388 if (link->prev)
389 link->prev->next = link->next;
390 else {
391 parent->items = link->next;
392 parent->items->prev = NULL;
393 }
395 link->prev = before;
396 link->next = before->next;
398 link->prev->next = link;
400 if (link->next)
401 link->next->prev = link;
402 else
403 parent->last = link;
404 }
405 }
408 /**
409 * Raises the item in its parent's stack by the specified number of positions.
410 *
411 * \param item A canvas item.
412 * \param positions Number of steps to raise the item.
413 *
414 * If the number of positions is greater than the distance to the top of the
415 * stack, then the item is put at the top.
416 */
417 void
418 sp_canvas_item_raise (SPCanvasItem *item, int positions)
419 {
420 g_return_if_fail (item != NULL);
421 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
422 g_return_if_fail (positions >= 0);
424 if (!item->parent || positions == 0)
425 return;
427 SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
428 GList *link = g_list_find (parent->items, item);
429 g_assert (link != NULL);
431 GList *before;
432 for (before = link; positions && before; positions--)
433 before = before->next;
435 if (!before)
436 before = parent->last;
438 put_item_after (link, before);
440 redraw_if_visible (item);
441 item->canvas->need_repick = TRUE;
442 }
445 /**
446 * Lowers the item in its parent's stack by the specified number of positions.
447 *
448 * \param item A canvas item.
449 * \param positions Number of steps to lower the item.
450 *
451 * If the number of positions is greater than the distance to the bottom of the
452 * stack, then the item is put at the bottom.
453 **/
454 void
455 sp_canvas_item_lower (SPCanvasItem *item, int positions)
456 {
457 g_return_if_fail (item != NULL);
458 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
459 g_return_if_fail (positions >= 1);
461 if (!item->parent || positions == 0)
462 return;
464 SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
465 GList *link = g_list_find (parent->items, item);
466 g_assert (link != NULL);
468 GList *before;
469 if (link->prev)
470 for (before = link->prev; positions && before; positions--)
471 before = before->prev;
472 else
473 before = NULL;
475 put_item_after (link, before);
477 redraw_if_visible (item);
478 item->canvas->need_repick = TRUE;
479 }
481 /**
482 * Sets visible flag on item and requests a redraw.
483 */
484 void
485 sp_canvas_item_show (SPCanvasItem *item)
486 {
487 g_return_if_fail (item != NULL);
488 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
490 if (item->flags & SP_CANVAS_ITEM_VISIBLE)
491 return;
493 item->flags |= SP_CANVAS_ITEM_VISIBLE;
495 int x0 = (int)(item->x1);
496 int x1 = (int)(item->x2);
497 int y0 = (int)(item->y1);
498 int y1 = (int)(item->y2);
500 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
501 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
502 item->canvas->need_repick = TRUE;
503 }
504 }
506 /**
507 * Clears visible flag on item and requests a redraw.
508 */
509 void
510 sp_canvas_item_hide (SPCanvasItem *item)
511 {
512 g_return_if_fail (item != NULL);
513 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
515 if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
516 return;
518 item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
520 int x0 = (int)(item->x1);
521 int x1 = (int)(item->x2);
522 int y0 = (int)(item->y1);
523 int y1 = (int)(item->y2);
525 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
526 sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
527 item->canvas->need_repick = TRUE;
528 }
529 }
531 /**
532 * Grab item under cursor.
533 *
534 * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE
535 */
536 int
537 sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
538 {
539 g_return_val_if_fail (item != NULL, -1);
540 g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1);
541 g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1);
543 if (item->canvas->grabbed_item)
544 return -1;
546 if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
547 return -1;
549 if (HAS_BROKEN_MOTION_HINTS) {
550 event_mask &= ~GDK_POINTER_MOTION_HINT_MASK;
551 }
553 /* fixme: Top hack (Lauris) */
554 /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
555 /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
556 gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
557 (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
558 NULL, cursor, etime);
560 item->canvas->grabbed_item = item;
561 item->canvas->grabbed_event_mask = event_mask;
562 item->canvas->current_item = item; /* So that events go to the grabbed item */
564 return 0;
565 }
567 /**
568 * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
569 * mouse.
570 *
571 * \param item A canvas item that holds a grab.
572 * \param etime The timestamp for ungrabbing the mouse.
573 */
574 void
575 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
576 {
577 g_return_if_fail (item != NULL);
578 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
580 if (item->canvas->grabbed_item != item)
581 return;
583 item->canvas->grabbed_item = NULL;
585 gdk_pointer_ungrab (etime);
586 }
588 /**
589 * Returns the product of all transformation matrices from the root item down
590 * to the item.
591 */
592 Geom::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
593 {
594 g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
596 Geom::Matrix affine = Geom::identity();
598 while (item) {
599 affine *= item->xform;
600 item = item->parent;
601 }
602 return affine;
603 }
605 /**
606 * Helper that returns true iff item is descendant of parent.
607 */
608 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
609 {
610 while (item) {
611 if (item == parent)
612 return true;
613 item = item->parent;
614 }
616 return false;
617 }
619 /**
620 * Focus canvas, and item under cursor if it is not already focussed.
621 */
622 void
623 sp_canvas_item_grab_focus (SPCanvasItem *item)
624 {
625 g_return_if_fail (item != NULL);
626 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
627 g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
629 SPCanvasItem *focused_item = item->canvas->focused_item;
631 if (focused_item) {
632 GdkEvent ev;
633 ev.focus_change.type = GDK_FOCUS_CHANGE;
634 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
635 ev.focus_change.send_event = FALSE;
636 ev.focus_change.in = FALSE;
638 emit_event (item->canvas, &ev);
639 }
641 item->canvas->focused_item = item;
642 gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
644 if (focused_item) {
645 GdkEvent ev;
646 ev.focus_change.type = GDK_FOCUS_CHANGE;
647 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
648 ev.focus_change.send_event = FALSE;
649 ev.focus_change.in = TRUE;
651 emit_event (item->canvas, &ev);
652 }
653 }
655 /**
656 * Requests that the canvas queue an update for the specified item.
657 *
658 * To be used only by item implementations.
659 */
660 void
661 sp_canvas_item_request_update (SPCanvasItem *item)
662 {
663 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
664 return;
666 item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
668 if (item->parent != NULL) {
669 /* Recurse up the tree */
670 sp_canvas_item_request_update (item->parent);
671 } else {
672 /* Have reached the top of the tree, make sure the update call gets scheduled. */
673 sp_canvas_request_update (item->canvas);
674 }
675 }
677 /**
678 * Returns position of item in group.
679 */
680 gint sp_canvas_item_order (SPCanvasItem * item)
681 {
682 return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
683 }
685 /* SPCanvasGroup */
687 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
688 static void sp_canvas_group_init (SPCanvasGroup *group);
689 static void sp_canvas_group_destroy (GtkObject *object);
691 static void sp_canvas_group_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags);
692 static double sp_canvas_group_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item);
693 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
695 static SPCanvasItemClass *group_parent_class;
697 /**
698 * Registers SPCanvasGroup class with Gtk and returns its type number.
699 */
700 GType sp_canvas_group_get_type(void)
701 {
702 static GType type = 0;
703 if (!type) {
704 GTypeInfo info = {
705 sizeof(SPCanvasGroupClass),
706 0, // base_init
707 0, // base_finalize
708 (GClassInitFunc)sp_canvas_group_class_init,
709 0, // class_finalize
710 0, // class_data
711 sizeof(SPCanvasGroup),
712 0, // n_preallocs
713 (GInstanceInitFunc)sp_canvas_group_init,
714 0 // value_table
715 };
716 type = g_type_register_static(sp_canvas_item_get_type(), "SPCanvasGroup", &info, static_cast<GTypeFlags>(0));
717 }
718 return type;
719 }
721 /**
722 * Class initialization function for SPCanvasGroupClass
723 */
724 static void
725 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
726 {
727 GtkObjectClass *object_class = (GtkObjectClass *) klass;
728 SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
730 group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
732 object_class->destroy = sp_canvas_group_destroy;
734 item_class->update = sp_canvas_group_update;
735 item_class->render = sp_canvas_group_render;
736 item_class->point = sp_canvas_group_point;
737 }
739 /**
740 * Callback. Empty.
741 */
742 static void
743 sp_canvas_group_init (SPCanvasGroup */*group*/)
744 {
745 /* Nothing here */
746 }
748 /**
749 * Callback that destroys all items in group and calls group's virtual
750 * destroy() function.
751 */
752 static void
753 sp_canvas_group_destroy (GtkObject *object)
754 {
755 g_return_if_fail (object != NULL);
756 g_return_if_fail (SP_IS_CANVAS_GROUP (object));
758 SPCanvasGroup const *group = SP_CANVAS_GROUP (object);
760 GList *list = group->items;
761 while (list) {
762 SPCanvasItem *child = (SPCanvasItem *)list->data;
763 list = list->next;
765 gtk_object_destroy (GTK_OBJECT (child));
766 }
768 if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
769 (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
770 }
772 /**
773 * Update handler for canvas groups
774 */
775 static void
776 sp_canvas_group_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags)
777 {
778 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
779 Geom::RectHull corners(Geom::Point(0, 0));
780 bool empty=true;
782 for (GList *list = group->items; list; list = list->next) {
783 SPCanvasItem *i = (SPCanvasItem *)list->data;
785 sp_canvas_item_invoke_update (i, affine, flags);
787 if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
788 if (empty) {
789 corners = Geom::RectHull(Geom::Point(i->x1, i->y1));
790 empty = false;
791 } else {
792 corners.add(Geom::Point(i->x1, i->y1));
793 }
794 corners.add(Geom::Point(i->x2, i->y2));
795 }
796 }
798 boost::optional<Geom::Rect> const bounds = corners.bounds();
799 if (bounds) {
800 item->x1 = bounds->min()[Geom::X];
801 item->y1 = bounds->min()[Geom::Y];
802 item->x2 = bounds->max()[Geom::X];
803 item->y2 = bounds->max()[Geom::Y];
804 } else {
805 // FIXME ?
806 item->x1 = item->x2 = item->y1 = item->y2 = 0;
807 }
808 }
810 /**
811 * Point handler for canvas groups.
812 */
813 static double
814 sp_canvas_group_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item)
815 {
816 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
817 double const x = p[Geom::X];
818 double const y = p[Geom::Y];
819 int x1 = (int)(x - item->canvas->close_enough);
820 int y1 = (int)(y - item->canvas->close_enough);
821 int x2 = (int)(x + item->canvas->close_enough);
822 int y2 = (int)(y + item->canvas->close_enough);
824 double best = 0.0;
825 *actual_item = NULL;
827 double dist = 0.0;
829 for (GList *list = group->items; list; list = list->next) {
830 SPCanvasItem *child = (SPCanvasItem *)list->data;
832 if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
833 SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
835 int has_point;
836 if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
837 dist = sp_canvas_item_invoke_point (child, p, &point_item);
838 has_point = TRUE;
839 } else
840 has_point = FALSE;
842 if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
843 best = dist;
844 *actual_item = point_item;
845 }
846 }
847 }
849 return best;
850 }
852 /**
853 * Renders all visible canvas group items in buf rectangle.
854 */
855 static void
856 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
857 {
858 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
860 for (GList *list = group->items; list; list = list->next) {
861 SPCanvasItem *child = (SPCanvasItem *)list->data;
862 if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
863 if ((child->x1 < buf->rect.x1) &&
864 (child->y1 < buf->rect.y1) &&
865 (child->x2 > buf->rect.x0) &&
866 (child->y2 > buf->rect.y0)) {
867 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
868 SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
869 }
870 }
871 }
872 }
874 /**
875 * Adds an item to a canvas group.
876 */
877 static void
878 group_add (SPCanvasGroup *group, SPCanvasItem *item)
879 {
880 gtk_object_ref (GTK_OBJECT (item));
881 gtk_object_sink (GTK_OBJECT (item));
883 if (!group->items) {
884 group->items = g_list_append (group->items, item);
885 group->last = group->items;
886 } else {
887 group->last = g_list_append (group->last, item)->next;
888 }
890 sp_canvas_item_request_update (item);
891 }
893 /**
894 * Removes an item from a canvas group
895 */
896 static void
897 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
898 {
899 g_return_if_fail (group != NULL);
900 g_return_if_fail (SP_IS_CANVAS_GROUP (group));
901 g_return_if_fail (item != NULL);
903 for (GList *children = group->items; children; children = children->next) {
904 if (children->data == item) {
906 /* Unparent the child */
908 item->parent = NULL;
909 gtk_object_unref (GTK_OBJECT (item));
911 /* Remove it from the list */
913 if (children == group->last) group->last = children->prev;
915 group->items = g_list_remove_link (group->items, children);
916 g_list_free (children);
917 break;
918 }
919 }
920 }
922 /* SPCanvas */
924 static void sp_canvas_class_init (SPCanvasClass *klass);
925 static void sp_canvas_init (SPCanvas *canvas);
926 static void sp_canvas_destroy (GtkObject *object);
928 static void sp_canvas_realize (GtkWidget *widget);
929 static void sp_canvas_unrealize (GtkWidget *widget);
931 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
932 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
934 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
935 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
936 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
937 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
938 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
939 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
940 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
941 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
943 static GtkWidgetClass *canvas_parent_class;
945 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb);
946 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb);
947 static void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val);
948 static int do_update (SPCanvas *canvas);
950 static gboolean sp_canvas_snap_watchdog_callback(gpointer data);
951 static void sp_canvas_snap_watchdog_set(SPCanvas *canvas, GdkEventMotion *event);
952 static void sp_canvas_snap_watchdog_kill(SPCanvas *canvas);
954 /**
955 * Registers the SPCanvas class if necessary, and returns the type ID
956 * associated to it.
957 *
958 * \return The type ID of the SPCanvas class.
959 **/
960 GType sp_canvas_get_type(void)
961 {
962 static GType type = 0;
963 if (!type) {
964 GTypeInfo info = {
965 sizeof(SPCanvasClass),
966 0, // base_init
967 0, // base_finalize
968 (GClassInitFunc)sp_canvas_class_init,
969 0, // class_finalize
970 0, // class_data
971 sizeof(SPCanvas),
972 0, // n_preallocs
973 (GInstanceInitFunc)sp_canvas_init,
974 0 // value_table
975 };
976 type = g_type_register_static(GTK_TYPE_WIDGET, "SPCanvas", &info, static_cast<GTypeFlags>(0));
977 }
978 return type;
979 }
981 /**
982 * Class initialization function for SPCanvasClass.
983 */
984 static void
985 sp_canvas_class_init (SPCanvasClass *klass)
986 {
987 GtkObjectClass *object_class = (GtkObjectClass *) klass;
988 GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
990 canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
992 object_class->destroy = sp_canvas_destroy;
994 widget_class->realize = sp_canvas_realize;
995 widget_class->unrealize = sp_canvas_unrealize;
996 widget_class->size_request = sp_canvas_size_request;
997 widget_class->size_allocate = sp_canvas_size_allocate;
998 widget_class->button_press_event = sp_canvas_button;
999 widget_class->button_release_event = sp_canvas_button;
1000 widget_class->motion_notify_event = sp_canvas_motion;
1001 widget_class->scroll_event = sp_canvas_scroll;
1002 widget_class->expose_event = sp_canvas_expose;
1003 widget_class->key_press_event = sp_canvas_key;
1004 widget_class->key_release_event = sp_canvas_key;
1005 widget_class->enter_notify_event = sp_canvas_crossing;
1006 widget_class->leave_notify_event = sp_canvas_crossing;
1007 widget_class->focus_in_event = sp_canvas_focus_in;
1008 widget_class->focus_out_event = sp_canvas_focus_out;
1009 }
1011 /**
1012 * Callback: object initialization for SPCanvas.
1013 */
1014 static void
1015 sp_canvas_init (SPCanvas *canvas)
1016 {
1017 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
1018 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
1019 GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
1021 canvas->pick_event.type = GDK_LEAVE_NOTIFY;
1022 canvas->pick_event.crossing.x = 0;
1023 canvas->pick_event.crossing.y = 0;
1025 /* Create the root item as a special case */
1026 canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
1027 canvas->root->canvas = canvas;
1029 gtk_object_ref (GTK_OBJECT (canvas->root));
1030 gtk_object_sink (GTK_OBJECT (canvas->root));
1032 canvas->need_repick = TRUE;
1034 // See comment at in sp-canvas.h.
1035 canvas->gen_all_enter_events = false;
1037 canvas->tiles=NULL;
1038 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1039 canvas->tileH=canvas->tileV=0;
1041 canvas->forced_redraw_count = 0;
1042 canvas->forced_redraw_limit = -1;
1044 #if ENABLE_LCMS
1045 canvas->enable_cms_display_adj = false;
1046 canvas->cms_key = new Glib::ustring("");
1047 #endif // ENABLE_LCMS
1049 canvas->is_scrolling = false;
1051 canvas->watchdog_id = 0;
1052 canvas->watchdog_event = NULL;
1053 }
1055 /**
1056 * Convenience function to remove the idle handler of a canvas.
1057 */
1058 static void
1059 remove_idle (SPCanvas *canvas)
1060 {
1061 if (canvas->idle_id) {
1062 gtk_idle_remove (canvas->idle_id);
1063 canvas->idle_id = 0;
1064 }
1065 }
1067 /*
1068 * Removes the transient state of the canvas (idle handler, grabs).
1069 */
1070 static void
1071 shutdown_transients (SPCanvas *canvas)
1072 {
1073 /* We turn off the need_redraw flag, since if the canvas is mapped again
1074 * it will request a redraw anyways. We do not turn off the need_update
1075 * flag, though, because updates are not queued when the canvas remaps
1076 * itself.
1077 */
1078 if (canvas->need_redraw) {
1079 canvas->need_redraw = FALSE;
1080 }
1081 if ( canvas->tiles ) g_free(canvas->tiles);
1082 canvas->tiles=NULL;
1083 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1084 canvas->tileH=canvas->tileV=0;
1086 if (canvas->grabbed_item) {
1087 canvas->grabbed_item = NULL;
1088 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1089 }
1091 remove_idle (canvas);
1092 }
1094 /**
1095 * Destroy handler for SPCanvas.
1096 */
1097 static void
1098 sp_canvas_destroy (GtkObject *object)
1099 {
1100 SPCanvas *canvas = SP_CANVAS (object);
1102 if (canvas->root) {
1103 gtk_object_unref (GTK_OBJECT (canvas->root));
1104 canvas->root = NULL;
1105 }
1107 shutdown_transients (canvas);
1109 if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1110 (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1111 }
1113 static void track_latency(GdkEvent const *event) {
1114 GdkEventLatencyTracker &tracker = GdkEventLatencyTracker::default_tracker();
1115 boost::optional<double> latency = tracker.process(event);
1116 if (latency && *latency > 2.0) {
1117 //g_warning("Event latency reached %f sec (%1.4f)", *latency, tracker.getSkew());
1118 }
1119 }
1121 /**
1122 * Returns new canvas as widget.
1123 */
1124 GtkWidget *
1125 sp_canvas_new_aa (void)
1126 {
1127 SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1129 return (GtkWidget *) canvas;
1130 }
1132 /**
1133 * The canvas widget's realize callback.
1134 */
1135 static void
1136 sp_canvas_realize (GtkWidget *widget)
1137 {
1138 SPCanvas *canvas = SP_CANVAS (widget);
1140 GdkWindowAttr attributes;
1141 attributes.window_type = GDK_WINDOW_CHILD;
1142 attributes.x = widget->allocation.x;
1143 attributes.y = widget->allocation.y;
1144 attributes.width = widget->allocation.width;
1145 attributes.height = widget->allocation.height;
1146 attributes.wclass = GDK_INPUT_OUTPUT;
1147 attributes.visual = gdk_rgb_get_visual ();
1148 attributes.colormap = gdk_rgb_get_cmap ();
1149 attributes.event_mask = (gtk_widget_get_events (widget) |
1150 GDK_EXPOSURE_MASK |
1151 GDK_BUTTON_PRESS_MASK |
1152 GDK_BUTTON_RELEASE_MASK |
1153 GDK_POINTER_MOTION_MASK |
1154 ( HAS_BROKEN_MOTION_HINTS ?
1155 0 : GDK_POINTER_MOTION_HINT_MASK ) |
1156 GDK_PROXIMITY_IN_MASK |
1157 GDK_PROXIMITY_OUT_MASK |
1158 GDK_KEY_PRESS_MASK |
1159 GDK_KEY_RELEASE_MASK |
1160 GDK_ENTER_NOTIFY_MASK |
1161 GDK_LEAVE_NOTIFY_MASK |
1162 GDK_FOCUS_CHANGE_MASK);
1163 gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1165 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1166 gdk_window_set_user_data (widget->window, widget);
1168 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1169 if ( prefs->getBool("/options/useextinput/value", true) )
1170 gtk_widget_set_events(widget, attributes.event_mask);
1172 widget->style = gtk_style_attach (widget->style, widget->window);
1174 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1176 canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1177 }
1179 /**
1180 * The canvas widget's unrealize callback.
1181 */
1182 static void
1183 sp_canvas_unrealize (GtkWidget *widget)
1184 {
1185 SPCanvas *canvas = SP_CANVAS (widget);
1187 canvas->current_item = NULL;
1188 canvas->grabbed_item = NULL;
1189 canvas->focused_item = NULL;
1191 shutdown_transients (canvas);
1193 gdk_gc_destroy (canvas->pixmap_gc);
1194 canvas->pixmap_gc = NULL;
1196 if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1197 (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1198 }
1200 /**
1201 * The canvas widget's size_request callback.
1202 */
1203 static void
1204 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1205 {
1206 static_cast<void>(SP_CANVAS (widget));
1208 req->width = 256;
1209 req->height = 256;
1210 }
1212 /**
1213 * The canvas widget's size_allocate callback.
1214 */
1215 static void
1216 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1217 {
1218 SPCanvas *canvas = SP_CANVAS (widget);
1220 /* Schedule redraw of new region */
1221 sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1222 if (allocation->width > widget->allocation.width) {
1223 sp_canvas_request_redraw (canvas,
1224 canvas->x0 + widget->allocation.width,
1225 0,
1226 canvas->x0 + allocation->width,
1227 canvas->y0 + allocation->height);
1228 }
1229 if (allocation->height > widget->allocation.height) {
1230 sp_canvas_request_redraw (canvas,
1231 0,
1232 canvas->y0 + widget->allocation.height,
1233 canvas->x0 + allocation->width,
1234 canvas->y0 + allocation->height);
1235 }
1237 widget->allocation = *allocation;
1239 if (GTK_WIDGET_REALIZED (widget)) {
1240 gdk_window_move_resize (widget->window,
1241 widget->allocation.x, widget->allocation.y,
1242 widget->allocation.width, widget->allocation.height);
1243 }
1244 }
1246 /**
1247 * Helper that emits an event for an item in the canvas, be it the current
1248 * item, grabbed item, or focused item, as appropriate.
1249 */
1250 static int
1251 emit_event (SPCanvas *canvas, GdkEvent *event)
1252 {
1253 guint mask;
1255 if (canvas->grabbed_item) {
1256 switch (event->type) {
1257 case GDK_ENTER_NOTIFY:
1258 mask = GDK_ENTER_NOTIFY_MASK;
1259 break;
1260 case GDK_LEAVE_NOTIFY:
1261 mask = GDK_LEAVE_NOTIFY_MASK;
1262 break;
1263 case GDK_MOTION_NOTIFY:
1264 mask = GDK_POINTER_MOTION_MASK;
1265 break;
1266 case GDK_BUTTON_PRESS:
1267 case GDK_2BUTTON_PRESS:
1268 case GDK_3BUTTON_PRESS:
1269 mask = GDK_BUTTON_PRESS_MASK;
1270 break;
1271 case GDK_BUTTON_RELEASE:
1272 mask = GDK_BUTTON_RELEASE_MASK;
1273 break;
1274 case GDK_KEY_PRESS:
1275 mask = GDK_KEY_PRESS_MASK;
1276 break;
1277 case GDK_KEY_RELEASE:
1278 mask = GDK_KEY_RELEASE_MASK;
1279 break;
1280 case GDK_SCROLL:
1281 mask = GDK_SCROLL;
1282 break;
1283 default:
1284 mask = 0;
1285 break;
1286 }
1288 if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1289 }
1291 /* Convert to world coordinates -- we have two cases because of diferent
1292 * offsets of the fields in the event structures.
1293 */
1295 GdkEvent ev = *event;
1297 switch (ev.type) {
1298 case GDK_ENTER_NOTIFY:
1299 case GDK_LEAVE_NOTIFY:
1300 ev.crossing.x += canvas->x0;
1301 ev.crossing.y += canvas->y0;
1302 break;
1303 case GDK_MOTION_NOTIFY:
1304 case GDK_BUTTON_PRESS:
1305 case GDK_2BUTTON_PRESS:
1306 case GDK_3BUTTON_PRESS:
1307 case GDK_BUTTON_RELEASE:
1308 ev.motion.x += canvas->x0;
1309 ev.motion.y += canvas->y0;
1310 break;
1311 default:
1312 break;
1313 }
1315 /* Choose where we send the event */
1317 /* canvas->current_item becomes NULL in some cases under Win32
1318 ** (e.g. if the pointer leaves the window). So this is a hack that
1319 ** Lauris applied to SP to get around the problem.
1320 */
1321 SPCanvasItem* item = NULL;
1322 if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1323 item = canvas->grabbed_item;
1324 } else {
1325 item = canvas->current_item;
1326 }
1328 if (canvas->focused_item &&
1329 ((event->type == GDK_KEY_PRESS) ||
1330 (event->type == GDK_KEY_RELEASE) ||
1331 (event->type == GDK_FOCUS_CHANGE))) {
1332 item = canvas->focused_item;
1333 }
1335 /* The event is propagated up the hierarchy (for if someone connected to
1336 * a group instead of a leaf event), and emission is stopped if a
1337 * handler returns TRUE, just like for GtkWidget events.
1338 */
1340 gint finished = FALSE;
1342 while (item && !finished) {
1343 gtk_object_ref (GTK_OBJECT (item));
1344 gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1345 SPCanvasItem *parent = item->parent;
1346 gtk_object_unref (GTK_OBJECT (item));
1347 item = parent;
1348 }
1350 return finished;
1351 }
1353 /**
1354 * Helper that re-picks the current item in the canvas, based on the event's
1355 * coordinates and emits enter/leave events for items as appropriate.
1356 */
1357 static int
1358 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1359 {
1360 int button_down = 0;
1361 double x, y;
1363 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1364 return FALSE;
1366 int retval = FALSE;
1368 if (canvas->gen_all_enter_events == false) {
1369 // If a button is down, we'll perform enter and leave events on the
1370 // current item, but not enter on any other item. This is more or
1371 // less like X pointer grabbing for canvas items.
1372 //
1373 button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1374 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1376 if (!button_down) canvas->left_grabbed_item = FALSE;
1377 }
1379 /* Save the event in the canvas. This is used to synthesize enter and
1380 * leave events in case the current item changes. It is also used to
1381 * re-pick the current item if the current one gets deleted. Also,
1382 * synthesize an enter event.
1383 */
1384 if (event != &canvas->pick_event) {
1385 if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1386 /* these fields have the same offsets in both types of events */
1388 canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY;
1389 canvas->pick_event.crossing.window = event->motion.window;
1390 canvas->pick_event.crossing.send_event = event->motion.send_event;
1391 canvas->pick_event.crossing.subwindow = NULL;
1392 canvas->pick_event.crossing.x = event->motion.x;
1393 canvas->pick_event.crossing.y = event->motion.y;
1394 canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL;
1395 canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR;
1396 canvas->pick_event.crossing.focus = FALSE;
1397 canvas->pick_event.crossing.state = event->motion.state;
1399 /* these fields don't have the same offsets in both types of events */
1401 if (event->type == GDK_MOTION_NOTIFY) {
1402 canvas->pick_event.crossing.x_root = event->motion.x_root;
1403 canvas->pick_event.crossing.y_root = event->motion.y_root;
1404 } else {
1405 canvas->pick_event.crossing.x_root = event->button.x_root;
1406 canvas->pick_event.crossing.y_root = event->button.y_root;
1407 }
1408 } else {
1409 canvas->pick_event = *event;
1410 }
1411 }
1413 /* Don't do anything else if this is a recursive call */
1414 if (canvas->in_repick) return retval;
1416 /* LeaveNotify means that there is no current item, so we don't look for one */
1417 if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1418 /* these fields don't have the same offsets in both types of events */
1420 if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1421 x = canvas->pick_event.crossing.x;
1422 y = canvas->pick_event.crossing.y;
1423 } else {
1424 x = canvas->pick_event.motion.x;
1425 y = canvas->pick_event.motion.y;
1426 }
1428 /* world coords */
1429 x += canvas->x0;
1430 y += canvas->y0;
1432 /* find the closest item */
1433 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1434 sp_canvas_item_invoke_point (canvas->root, Geom::Point(x, y), &canvas->new_current_item);
1435 } else {
1436 canvas->new_current_item = NULL;
1437 }
1438 } else {
1439 canvas->new_current_item = NULL;
1440 }
1442 if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1443 return retval; /* current item did not change */
1444 }
1446 /* Synthesize events for old and new current items */
1448 if ((canvas->new_current_item != canvas->current_item)
1449 && (canvas->current_item != NULL)
1450 && !canvas->left_grabbed_item) {
1451 GdkEvent new_event;
1452 SPCanvasItem *item;
1454 item = canvas->current_item;
1456 new_event = canvas->pick_event;
1457 new_event.type = GDK_LEAVE_NOTIFY;
1459 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1460 new_event.crossing.subwindow = NULL;
1461 canvas->in_repick = TRUE;
1462 retval = emit_event (canvas, &new_event);
1463 canvas->in_repick = FALSE;
1464 }
1466 if (canvas->gen_all_enter_events == false) {
1467 // new_current_item may have been set to NULL during the call to
1468 // emit_event() above
1469 if ((canvas->new_current_item != canvas->current_item) && button_down) {
1470 canvas->left_grabbed_item = TRUE;
1471 return retval;
1472 }
1473 }
1475 /* Handle the rest of cases */
1477 canvas->left_grabbed_item = FALSE;
1478 canvas->current_item = canvas->new_current_item;
1480 if (canvas->current_item != NULL) {
1481 GdkEvent new_event;
1483 new_event = canvas->pick_event;
1484 new_event.type = GDK_ENTER_NOTIFY;
1485 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1486 new_event.crossing.subwindow = NULL;
1487 retval = emit_event (canvas, &new_event);
1488 }
1490 return retval;
1491 }
1493 /**
1494 * Button event handler for the canvas.
1495 */
1496 static gint
1497 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1498 {
1499 SPCanvas *canvas = SP_CANVAS (widget);
1500 SPDesktop *dt = SP_ACTIVE_DESKTOP;
1502 int retval = FALSE;
1504 /* dispatch normally regardless of the event's window if an item has
1505 has a pointer grab in effect */
1506 if (!canvas->grabbed_item &&
1507 event->window != SP_CANVAS_WINDOW (canvas))
1508 return retval;
1510 int mask;
1511 switch (event->button) {
1512 case 1:
1513 mask = GDK_BUTTON1_MASK;
1514 break;
1515 case 2:
1516 mask = GDK_BUTTON2_MASK;
1517 break;
1518 case 3:
1519 mask = GDK_BUTTON3_MASK;
1520 break;
1521 case 4:
1522 mask = GDK_BUTTON4_MASK;
1523 break;
1524 case 5:
1525 mask = GDK_BUTTON5_MASK;
1526 break;
1527 default:
1528 mask = 0;
1529 }
1531 switch (event->type) {
1532 case GDK_BUTTON_PRESS:
1533 case GDK_2BUTTON_PRESS:
1534 case GDK_3BUTTON_PRESS:
1535 if (dt) {
1536 // Snapping will be on hold if we're moving the mouse at high speeds. When starting
1537 // drawing a new shape we really should snap though.
1538 dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false);
1539 }
1541 /* Pick the current item as if the button were not pressed, and
1542 * then process the event.
1543 */
1544 canvas->state = event->state;
1545 pick_current_item (canvas, (GdkEvent *) event);
1546 canvas->state ^= mask;
1547 retval = emit_event (canvas, (GdkEvent *) event);
1548 break;
1550 case GDK_BUTTON_RELEASE:
1551 sp_canvas_snap_watchdog_callback(canvas); // If we have any pending snapping action, then invoke it now
1553 /* Process the event as if the button were pressed, then repick
1554 * after the button has been released
1555 */
1556 canvas->state = event->state;
1557 retval = emit_event (canvas, (GdkEvent *) event);
1558 event->state ^= mask;
1559 canvas->state = event->state;
1560 pick_current_item (canvas, (GdkEvent *) event);
1561 event->state ^= mask;
1563 break;
1565 default:
1566 g_assert_not_reached ();
1567 }
1569 return retval;
1570 }
1572 /**
1573 * Scroll event handler for the canvas.
1574 *
1575 * \todo FIXME: generate motion events to re-select items.
1576 */
1577 static gint
1578 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1579 {
1580 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1581 }
1583 static inline void request_motions(GdkWindow *w, GdkEventMotion *event) {
1584 gdk_window_get_pointer(w, NULL, NULL, NULL);
1585 #if HAS_GDK_EVENT_REQUEST_MOTIONS
1586 gdk_event_request_motions(event);
1587 #endif
1588 }
1590 /**
1591 * Motion event handler for the canvas.
1592 */
1593 static int
1594 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1595 {
1596 static guint32 prev_time;
1597 static boost::optional<Geom::Point> prev_pos;
1599 int status;
1600 SPCanvas *canvas = SP_CANVAS (widget);
1602 track_latency((GdkEvent *)event);
1604 if (event->window != SP_CANVAS_WINDOW (canvas))
1605 return FALSE;
1607 if (canvas->pixmap_gc == NULL) // canvas being deleted
1608 return FALSE;
1610 SPDesktop *dt = SP_ACTIVE_DESKTOP;
1612 // Snap when speed drops below e.g. 0.02 px/msec, or when no motion events have occured for some period.
1613 // i.e. snap when we're at stand still. A speed threshold enforces snapping for tablets, which might never
1614 // be fully at stand still and might keep spitting out motion events.
1616 if (event->type == GDK_MOTION_NOTIFY) {
1617 Geom::Point event_pos(event->x, event->y);
1618 guint32 event_t = gdk_event_get_time ( (GdkEvent *) event );
1620 if (dt) { // put snapping on hold
1621 dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(true);
1622 }
1624 if (prev_pos) {
1625 Geom::Coord dist = Geom::L2(event_pos - *prev_pos);
1626 guint32 delta_t = event_t - prev_time;
1627 gdouble speed = delta_t > 0 ? dist/delta_t : 1000;
1628 // std::cout << "speed = " << speed << " px/msec " << "| time passed = " << delta_t << " msec" << std::endl;
1629 if (speed > 0.02) { // Jitter threshold, might be needed for tablets
1630 // We're moving fast, so postpone any snapping until the next GDK_MOTION_NOTIFY event. We
1631 // will keep on postponing the snapping as long as the speed is high.
1632 // We must snap at some point in time though, so set a watchdog timer at some time from
1633 // now, just in case there's no future motion event that drops under the speed limit (when
1634 // stoppping abruptly)
1635 sp_canvas_snap_watchdog_kill(canvas);
1636 sp_canvas_snap_watchdog_set(canvas, event); // watchdog is reset, i.e. pushed forward in time
1637 } else { // Speed is very low, so we're virtually at stand still
1638 // But if we're really standing still, then we should snap now. We could use some low-pass filtering,
1639 // otherwise snapping occurs for each jitter movement. For this filtering we'll leave the watchdog to expire,
1640 // snap, and set a new watchdog again.
1641 if (canvas->watchdog_id == 0) { // no watchdog has been set
1642 // it might have already expired, so we'll set a new one; the snapping frequency will be limited by this
1643 sp_canvas_snap_watchdog_set(canvas, event);
1644 } // else: watchdog has been set before and we'll wait for it to expire
1645 }
1646 } else {
1647 // This is the first GDK_MOTION_NOTIFY event, so postpone snapping and set the watchdog
1648 sp_canvas_snap_watchdog_set(canvas, event);
1649 }
1651 prev_pos = event_pos;
1652 prev_time = event_t;
1653 }
1655 canvas->state = event->state;
1656 pick_current_item (canvas, (GdkEvent *) event);
1658 status = emit_event (canvas, (GdkEvent *) event);
1660 if (event->is_hint) {
1661 request_motions(widget->window, event);
1662 }
1664 return status;
1665 }
1667 gboolean sp_canvas_snap_watchdog_callback(gpointer data)
1668 {
1669 // Snap NOW! For this the "postponed" flag will be reset and an the last motion event will be repeated
1670 SPCanvas *canvas = reinterpret_cast<SPCanvas *>(data);
1671 if (!canvas->watchdog_event) {
1672 return FALSE;
1673 }
1675 SPDesktop *dt = SP_ACTIVE_DESKTOP;
1676 if (dt) {
1677 dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false);
1678 }
1680 emit_event(canvas, canvas->watchdog_event);
1681 gdk_event_free(canvas->watchdog_event);
1682 canvas->watchdog_event = NULL;
1683 canvas->watchdog_id = 0;
1685 return FALSE;
1686 }
1688 void sp_canvas_snap_watchdog_set(SPCanvas *canvas, GdkEventMotion *event)
1689 {
1690 g_assert(canvas->watchdog_id == 0);
1691 canvas->watchdog_id = g_timeout_add(400, &sp_canvas_snap_watchdog_callback, canvas);
1692 g_assert(canvas->watchdog_event == NULL);
1693 canvas->watchdog_event = gdk_event_copy( (GdkEvent *) event);
1694 }
1696 void sp_canvas_snap_watchdog_kill(SPCanvas *canvas)
1697 {
1698 if (canvas->watchdog_id) {
1699 g_source_remove(canvas->watchdog_id); // Kill the watchdog
1700 canvas->watchdog_id = 0;
1701 }
1703 if (canvas->watchdog_event) {
1704 gdk_event_free(canvas->watchdog_event);
1705 canvas->watchdog_event = NULL;
1706 }
1707 }
1709 static void
1710 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)
1711 {
1712 GtkWidget *widget = GTK_WIDGET (canvas);
1714 SPCanvasBuf buf;
1715 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1716 buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1717 } else {
1718 buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1719 }
1721 // Mark the region clean
1722 sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1724 buf.buf_rowstride = sw * 4;
1725 buf.rect.x0 = x0;
1726 buf.rect.y0 = y0;
1727 buf.rect.x1 = x1;
1728 buf.rect.y1 = y1;
1729 buf.visible_rect.x0 = draw_x1;
1730 buf.visible_rect.y0 = draw_y1;
1731 buf.visible_rect.x1 = draw_x2;
1732 buf.visible_rect.y1 = draw_y2;
1733 GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1734 buf.bg_color = (((color->red & 0xff00) << 8)
1735 | (color->green & 0xff00)
1736 | (color->blue >> 8));
1737 buf.is_empty = true;
1739 buf.ct = nr_create_cairo_context_canvasbuf (&(buf.visible_rect), &buf);
1741 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1742 SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1743 }
1745 #if ENABLE_LCMS
1746 cmsHTRANSFORM transf = 0;
1747 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1748 bool fromDisplay = prefs->getBool( "/options/displayprofile/from_display");
1749 if ( fromDisplay ) {
1750 transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" );
1751 } else {
1752 transf = Inkscape::colorprofile_get_display_transform();
1753 }
1754 #endif // ENABLE_LCMS
1756 if (buf.is_empty) {
1757 #if ENABLE_LCMS
1758 if ( transf && canvas->enable_cms_display_adj ) {
1759 cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1760 }
1761 #endif // ENABLE_LCMS
1762 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1763 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1764 canvas->pixmap_gc,
1765 TRUE,
1766 x0 - canvas->x0, y0 - canvas->y0,
1767 x1 - x0, y1 - y0);
1768 } else {
1770 #if ENABLE_LCMS
1771 if ( transf && canvas->enable_cms_display_adj ) {
1772 for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1773 guchar* p = buf.buf + (sw * 3) * yy;
1774 cmsDoTransform( transf, p, p, (x1 - x0) );
1775 }
1776 }
1777 #endif // ENABLE_LCMS
1779 // Now we only need to output the prepared pixmap to the actual screen, and this define chooses one
1780 // of the two ways to do it. The cairo way is direct and straightforward, but unfortunately
1781 // noticeably slower. I asked Carl Worth but he was unable so far to suggest any specific reason
1782 // for this slowness. So, for now we use the oldish method: squeeze out 32bpp buffer to 24bpp and
1783 // use gdk_draw_rgb_image_dithalign, for unfortunately gdk can only handle 24 bpp, which cairo
1784 // cannot handle at all. Still, this way is currently faster even despite the blit with squeeze.
1786 ///#define CANVAS_OUTPUT_VIA_CAIRO
1788 #ifdef CANVAS_OUTPUT_VIA_CAIRO
1790 buf.cst = cairo_image_surface_create_for_data (
1791 buf.buf,
1792 CAIRO_FORMAT_ARGB32, // unpacked, i.e. 32 bits! one byte is unused
1793 x1 - x0, y1 - y0,
1794 buf.buf_rowstride
1795 );
1796 cairo_t *window_ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1797 cairo_set_source_surface (window_ct, buf.cst, x0 - canvas->x0, y0 - canvas->y0);
1798 cairo_paint (window_ct);
1799 cairo_destroy (window_ct);
1800 cairo_surface_finish (buf.cst);
1801 cairo_surface_destroy (buf.cst);
1803 #else
1805 NRPixBlock b3;
1806 nr_pixblock_setup_fast (&b3, NR_PIXBLOCK_MODE_R8G8B8, x0, y0, x1, y1, TRUE);
1808 NRPixBlock b4;
1809 nr_pixblock_setup_extern (&b4, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1,
1810 buf.buf,
1811 buf.buf_rowstride,
1812 FALSE, FALSE);
1814 // this does the 32->24 squishing, using an assembler routine:
1815 nr_blit_pixblock_pixblock (&b3, &b4);
1817 gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1818 canvas->pixmap_gc,
1819 x0 - canvas->x0, y0 - canvas->y0,
1820 x1 - x0, y1 - y0,
1821 GDK_RGB_DITHER_MAX,
1822 NR_PIXBLOCK_PX(&b3),
1823 sw * 3,
1824 x0 - canvas->x0, y0 - canvas->y0);
1826 nr_pixblock_release (&b3);
1827 nr_pixblock_release (&b4);
1828 #endif
1829 }
1831 cairo_surface_t *cst = cairo_get_target(buf.ct);
1832 cairo_destroy (buf.ct);
1833 cairo_surface_finish (cst);
1834 cairo_surface_destroy (cst);
1836 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1837 nr_pixelstore_256K_free (buf.buf);
1838 } else {
1839 nr_pixelstore_1M_free (buf.buf);
1840 }
1841 }
1843 struct PaintRectSetup {
1844 SPCanvas* canvas;
1845 NRRectL big_rect;
1846 GTimeVal start_time;
1847 int max_pixels;
1848 Geom::Point mouse_loc;
1849 };
1851 /**
1852 * Paint the given rect, recursively subdividing the region until it is the size of a single
1853 * buffer.
1854 *
1855 * @return true if the drawing completes
1856 */
1857 static int
1858 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1859 {
1860 GTimeVal now;
1861 g_get_current_time (&now);
1863 glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1864 + (now.tv_usec - setup->start_time.tv_usec);
1866 // Allow only very fast buffers to be run together;
1867 // as soon as the total redraw time exceeds 1ms, cancel;
1868 // this returns control to the idle loop and allows Inkscape to process user input
1869 // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1870 // it will get back and finish painting what remains to paint.
1871 if (elapsed > 1000) {
1873 // Interrupting redraw isn't always good.
1874 // For example, when you drag one node of a big path, only the buffer containing
1875 // the mouse cursor will be redrawn again and again, and the rest of the path
1876 // will remain stale because Inkscape never has enough idle time to redraw all
1877 // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1878 // If this limit is set, and if we have aborted redraw more times than is allowed,
1879 // interrupting is blocked and we're forced to redraw full screen once
1880 // (after which we can again interrupt forced_redraw_limit times).
1881 if (setup->canvas->forced_redraw_limit < 0 ||
1882 setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1884 if (setup->canvas->forced_redraw_limit != -1) {
1885 setup->canvas->forced_redraw_count++;
1886 }
1888 return false;
1889 }
1890 }
1892 // Find the optimal buffer dimensions
1893 int bw = this_rect.x1 - this_rect.x0;
1894 int bh = this_rect.y1 - this_rect.y0;
1895 if ((bw < 1) || (bh < 1))
1896 return 0;
1898 if (bw * bh < setup->max_pixels) {
1899 // We are small enough
1900 sp_canvas_paint_single_buffer (setup->canvas,
1901 this_rect.x0, this_rect.y0,
1902 this_rect.x1, this_rect.y1,
1903 setup->big_rect.x0, setup->big_rect.y0,
1904 setup->big_rect.x1, setup->big_rect.y1, bw);
1905 return 1;
1906 }
1908 NRRectL lo = this_rect;
1909 NRRectL hi = this_rect;
1911 /*
1912 This test determines the redraw strategy:
1914 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1915 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1916 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1917 and seems to be faster for drawings with many smaller objects at zoom-out.
1919 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1920 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1921 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1922 faster.
1924 The default for now is the strips mode.
1925 */
1926 if (bw < bh || bh < 2 * TILE_SIZE) {
1927 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1928 int mid = ((long long) this_rect.x0 + (long long) this_rect.x1) / 2;
1929 // Make sure that mid lies on a tile boundary
1930 mid = (mid / TILE_SIZE) * TILE_SIZE;
1932 lo.x1 = mid;
1933 hi.x0 = mid;
1935 if (setup->mouse_loc[Geom::X] < mid) {
1936 // Always paint towards the mouse first
1937 return sp_canvas_paint_rect_internal(setup, lo)
1938 && sp_canvas_paint_rect_internal(setup, hi);
1939 } else {
1940 return sp_canvas_paint_rect_internal(setup, hi)
1941 && sp_canvas_paint_rect_internal(setup, lo);
1942 }
1943 } else {
1944 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1945 int mid = ((long long) this_rect.y0 + (long long) this_rect.y1) / 2;
1946 // Make sure that mid lies on a tile boundary
1947 mid = (mid / TILE_SIZE) * TILE_SIZE;
1949 lo.y1 = mid;
1950 hi.y0 = mid;
1952 if (setup->mouse_loc[Geom::Y] < mid) {
1953 // Always paint towards the mouse first
1954 return sp_canvas_paint_rect_internal(setup, lo)
1955 && sp_canvas_paint_rect_internal(setup, hi);
1956 } else {
1957 return sp_canvas_paint_rect_internal(setup, hi)
1958 && sp_canvas_paint_rect_internal(setup, lo);
1959 }
1960 }
1961 }
1964 /**
1965 * Helper that draws a specific rectangular part of the canvas.
1966 *
1967 * @return true if the rectangle painting succeeds.
1968 */
1969 static bool
1970 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1971 {
1972 g_return_val_if_fail (!canvas->need_update, false);
1974 NRRectL rect;
1975 rect.x0 = xx0;
1976 rect.x1 = xx1;
1977 rect.y0 = yy0;
1978 rect.y1 = yy1;
1980 // Clip rect-to-draw by the current visible area
1981 rect.x0 = MAX (rect.x0, canvas->x0);
1982 rect.y0 = MAX (rect.y0, canvas->y0);
1983 rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1984 rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1986 #ifdef DEBUG_REDRAW
1987 // paint the area to redraw yellow
1988 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1989 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1990 canvas->pixmap_gc,
1991 TRUE,
1992 rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1993 rect.x1 - rect.x0, rect.y1 - rect.y0);
1994 #endif
1996 PaintRectSetup setup;
1998 setup.canvas = canvas;
1999 setup.big_rect = rect;
2001 // Save the mouse location
2002 gint x, y;
2003 gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
2004 setup.mouse_loc = sp_canvas_window_to_world (canvas, Geom::Point(x,y));
2006 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
2007 // use 256K as a compromise to not slow down gradients
2008 // 256K is the cached buffer and we need 4 channels
2009 setup.max_pixels = 65536; // 256K/4
2010 } else {
2011 // paths only, so 1M works faster
2012 // 1M is the cached buffer and we need 4 channels
2013 setup.max_pixels = 262144;
2014 }
2016 // Start the clock
2017 g_get_current_time(&(setup.start_time));
2019 // Go
2020 return sp_canvas_paint_rect_internal(&setup, rect);
2021 }
2023 /**
2024 * Force a full redraw after a specified number of interrupted redraws
2025 */
2026 void
2027 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
2028 g_return_if_fail(canvas != NULL);
2030 canvas->forced_redraw_limit = count;
2031 canvas->forced_redraw_count = 0;
2032 }
2034 /**
2035 * End forced full redraw requests
2036 */
2037 void
2038 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
2039 g_return_if_fail(canvas != NULL);
2041 canvas->forced_redraw_limit = -1;
2042 }
2044 /**
2045 * The canvas widget's expose callback.
2046 */
2047 static gint
2048 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
2049 {
2050 SPCanvas *canvas = SP_CANVAS (widget);
2052 if (!GTK_WIDGET_DRAWABLE (widget) ||
2053 (event->window != SP_CANVAS_WINDOW (canvas)))
2054 return FALSE;
2056 int n_rects;
2057 GdkRectangle *rects;
2058 gdk_region_get_rectangles (event->region, &rects, &n_rects);
2060 for (int i = 0; i < n_rects; i++) {
2061 NRRectL rect;
2063 rect.x0 = rects[i].x + canvas->x0;
2064 rect.y0 = rects[i].y + canvas->y0;
2065 rect.x1 = rect.x0 + rects[i].width;
2066 rect.y1 = rect.y0 + rects[i].height;
2068 sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
2069 }
2071 if (n_rects > 0)
2072 g_free (rects);
2074 return FALSE;
2075 }
2077 /**
2078 * The canvas widget's keypress callback.
2079 */
2080 static gint
2081 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
2082 {
2083 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
2084 }
2086 /**
2087 * Crossing event handler for the canvas.
2088 */
2089 static gint
2090 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
2091 {
2092 SPCanvas *canvas = SP_CANVAS (widget);
2094 if (event->window != SP_CANVAS_WINDOW (canvas))
2095 return FALSE;
2097 canvas->state = event->state;
2098 return pick_current_item (canvas, (GdkEvent *) event);
2099 }
2101 /**
2102 * Focus in handler for the canvas.
2103 */
2104 static gint
2105 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
2106 {
2107 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
2109 SPCanvas *canvas = SP_CANVAS (widget);
2111 if (canvas->focused_item) {
2112 return emit_event (canvas, (GdkEvent *) event);
2113 } else {
2114 return FALSE;
2115 }
2116 }
2118 /**
2119 * Focus out handler for the canvas.
2120 */
2121 static gint
2122 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
2123 {
2124 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2126 SPCanvas *canvas = SP_CANVAS (widget);
2128 if (canvas->focused_item)
2129 return emit_event (canvas, (GdkEvent *) event);
2130 else
2131 return FALSE;
2132 }
2134 /**
2135 * Helper that repaints the areas in the canvas that need it.
2136 *
2137 * @return true if all the dirty parts have been redrawn
2138 */
2139 static int
2140 paint (SPCanvas *canvas)
2141 {
2142 if (canvas->need_update) {
2143 sp_canvas_item_invoke_update (canvas->root, Geom::identity(), 0);
2144 canvas->need_update = FALSE;
2145 }
2147 if (!canvas->need_redraw)
2148 return TRUE;
2150 Gdk::Region to_paint;
2152 for (int j=canvas->tTop; j<canvas->tBottom; j++) {
2153 for (int i=canvas->tLeft; i<canvas->tRight; i++) {
2154 int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
2156 if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
2157 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
2158 TILE_SIZE, TILE_SIZE));
2159 }
2161 }
2162 }
2164 if (!to_paint.empty()) {
2165 Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
2166 typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
2167 for (Iter i=rect.begin(); i != rect.end(); ++i) {
2168 int x0 = (*i).get_x();
2169 int y0 = (*i).get_y();
2170 int x1 = x0 + (*i).get_width();
2171 int y1 = y0 + (*i).get_height();
2172 if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
2173 // Aborted
2174 return FALSE;
2175 };
2176 }
2177 }
2179 canvas->need_redraw = FALSE;
2181 // we've had a full unaborted redraw, reset the full redraw counter
2182 if (canvas->forced_redraw_limit != -1) {
2183 canvas->forced_redraw_count = 0;
2184 }
2186 return TRUE;
2187 }
2189 /**
2190 * Helper that invokes update, paint, and repick on canvas.
2191 */
2192 static int
2193 do_update (SPCanvas *canvas)
2194 {
2195 if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop durring interrupted display!
2196 return TRUE;
2198 /* Cause the update if necessary */
2199 if (canvas->need_update) {
2200 sp_canvas_item_invoke_update (canvas->root, Geom::identity(), 0);
2201 canvas->need_update = FALSE;
2202 }
2204 /* Paint if able to */
2205 if (GTK_WIDGET_DRAWABLE (canvas)) {
2206 return paint (canvas);
2207 }
2209 /* Pick new current item */
2210 while (canvas->need_repick) {
2211 canvas->need_repick = FALSE;
2212 pick_current_item (canvas, &canvas->pick_event);
2213 }
2215 return TRUE;
2216 }
2218 /**
2219 * Idle handler for the canvas that deals with pending updates and redraws.
2220 */
2221 static gint
2222 idle_handler (gpointer data)
2223 {
2224 GDK_THREADS_ENTER ();
2226 SPCanvas *canvas = SP_CANVAS (data);
2228 int const ret = do_update (canvas);
2230 if (ret) {
2231 /* Reset idle id */
2232 canvas->idle_id = 0;
2233 }
2235 GDK_THREADS_LEAVE ();
2237 return !ret;
2238 }
2240 /**
2241 * Convenience function to add an idle handler to a canvas.
2242 */
2243 static void
2244 add_idle (SPCanvas *canvas)
2245 {
2246 if (canvas->idle_id != 0)
2247 return;
2249 canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2250 }
2252 /**
2253 * Returns the root group of the specified canvas.
2254 */
2255 SPCanvasGroup *
2256 sp_canvas_root (SPCanvas *canvas)
2257 {
2258 g_return_val_if_fail (canvas != NULL, NULL);
2259 g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2261 return SP_CANVAS_GROUP (canvas->root);
2262 }
2264 /**
2265 * Scrolls canvas to specific position (cx and cy are measured in screen pixels)
2266 */
2267 void
2268 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2269 {
2270 g_return_if_fail (canvas != NULL);
2271 g_return_if_fail (SP_IS_CANVAS (canvas));
2273 int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels)
2274 int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do!
2275 int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the
2276 int dy = iy - canvas->y0; // canvas w.r.t its previous position
2278 canvas->dx0 = cx; // here the 'd' stands for double, not delta!
2279 canvas->dy0 = cy;
2280 canvas->x0 = ix;
2281 canvas->y0 = iy;
2283 sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2285 if (!clear) {
2286 // scrolling without zoom; redraw only the newly exposed areas
2287 if ((dx != 0) || (dy != 0)) {
2288 canvas->is_scrolling = is_scrolling;
2289 if (GTK_WIDGET_REALIZED (canvas)) {
2290 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2291 }
2292 }
2293 } else {
2294 // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2295 }
2296 }
2298 /**
2299 * Updates canvas if necessary.
2300 */
2301 void
2302 sp_canvas_update_now (SPCanvas *canvas)
2303 {
2304 g_return_if_fail (canvas != NULL);
2305 g_return_if_fail (SP_IS_CANVAS (canvas));
2307 if (!(canvas->need_update ||
2308 canvas->need_redraw))
2309 return;
2311 do_update (canvas);
2312 }
2314 /**
2315 * Update callback for canvas widget.
2316 */
2317 static void
2318 sp_canvas_request_update (SPCanvas *canvas)
2319 {
2320 canvas->need_update = TRUE;
2321 add_idle (canvas);
2322 }
2324 /**
2325 * Forces redraw of rectangular canvas area.
2326 */
2327 void
2328 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2329 {
2330 NRRectL bbox;
2331 NRRectL visible;
2332 NRRectL clip;
2334 g_return_if_fail (canvas != NULL);
2335 g_return_if_fail (SP_IS_CANVAS (canvas));
2337 if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2338 if ((x0 >= x1) || (y0 >= y1)) return;
2340 bbox.x0 = x0;
2341 bbox.y0 = y0;
2342 bbox.x1 = x1;
2343 bbox.y1 = y1;
2345 visible.x0 = canvas->x0;
2346 visible.y0 = canvas->y0;
2347 visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2348 visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2350 nr_rect_l_intersect (&clip, &bbox, &visible);
2352 sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2353 add_idle (canvas);
2354 }
2356 /**
2357 * Sets world coordinates from win and canvas.
2358 */
2359 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2360 {
2361 g_return_if_fail (canvas != NULL);
2362 g_return_if_fail (SP_IS_CANVAS (canvas));
2364 if (worldx) *worldx = canvas->x0 + winx;
2365 if (worldy) *worldy = canvas->y0 + winy;
2366 }
2368 /**
2369 * Sets win coordinates from world and canvas.
2370 */
2371 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2372 {
2373 g_return_if_fail (canvas != NULL);
2374 g_return_if_fail (SP_IS_CANVAS (canvas));
2376 if (winx) *winx = worldx - canvas->x0;
2377 if (winy) *winy = worldy - canvas->y0;
2378 }
2380 /**
2381 * Converts point from win to world coordinates.
2382 */
2383 Geom::Point sp_canvas_window_to_world(SPCanvas const *canvas, Geom::Point const win)
2384 {
2385 g_assert (canvas != NULL);
2386 g_assert (SP_IS_CANVAS (canvas));
2388 return Geom::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2389 }
2391 /**
2392 * Converts point from world to win coordinates.
2393 */
2394 Geom::Point sp_canvas_world_to_window(SPCanvas const *canvas, Geom::Point const world)
2395 {
2396 g_assert (canvas != NULL);
2397 g_assert (SP_IS_CANVAS (canvas));
2399 return Geom::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2400 }
2402 /**
2403 * Returns true if point given in world coordinates is inside window.
2404 */
2405 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, Geom::Point const &world)
2406 {
2407 g_assert( canvas != NULL );
2408 g_assert(SP_IS_CANVAS(canvas));
2410 GtkWidget const &w = *GTK_WIDGET(canvas);
2411 return ( ( canvas->x0 <= world[Geom::X] ) &&
2412 ( canvas->y0 <= world[Geom::Y] ) &&
2413 ( world[Geom::X] < canvas->x0 + w.allocation.width ) &&
2414 ( world[Geom::Y] < canvas->y0 + w.allocation.height ) );
2415 }
2417 /**
2418 * Return canvas window coordinates as Geom::Rect.
2419 */
2420 Geom::Rect SPCanvas::getViewbox() const
2421 {
2422 GtkWidget const *w = GTK_WIDGET(this);
2423 return Geom::Rect(Geom::Point(dx0, dy0),
2424 Geom::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2425 }
2427 /**
2428 * Return canvas window coordinates as IRect (a rectangle defined by integers).
2429 */
2430 NR::IRect SPCanvas::getViewboxIntegers() const
2431 {
2432 GtkWidget const *w = GTK_WIDGET(this);
2433 return NR::IRect(NR::IPoint(x0, y0),
2434 NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height));
2435 }
2437 inline int sp_canvas_tile_floor(int x)
2438 {
2439 return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2440 }
2442 inline int sp_canvas_tile_ceil(int x)
2443 {
2444 return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2445 }
2447 /**
2448 * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2449 */
2450 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2451 {
2452 if ( nl >= nr || nt >= nb ) {
2453 if ( canvas->tiles ) g_free(canvas->tiles);
2454 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2455 canvas->tileH=canvas->tileV=0;
2456 canvas->tiles=NULL;
2457 return;
2458 }
2459 int tl=sp_canvas_tile_floor(nl);
2460 int tt=sp_canvas_tile_floor(nt);
2461 int tr=sp_canvas_tile_ceil(nr);
2462 int tb=sp_canvas_tile_ceil(nb);
2464 int nh = tr-tl, nv = tb-tt;
2465 uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2466 for (int i=tl; i<tr; i++) {
2467 for (int j=tt; j<tb; j++) {
2468 int ind = (i-tl) + (j-tt)*nh;
2469 if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2470 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2471 } else {
2472 ntiles[ind]=0; // newly exposed areas get 0
2473 }
2474 }
2475 }
2476 if ( canvas->tiles ) g_free(canvas->tiles);
2477 canvas->tiles=ntiles;
2478 canvas->tLeft=tl;
2479 canvas->tTop=tt;
2480 canvas->tRight=tr;
2481 canvas->tBottom=tb;
2482 canvas->tileH=nh;
2483 canvas->tileV=nv;
2484 }
2486 /*
2487 * Helper that queues a canvas rectangle for redraw
2488 */
2489 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2490 canvas->need_redraw = TRUE;
2492 sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2493 }
2495 /**
2496 * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2497 */
2498 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2499 {
2500 if ( nl >= nr || nt >= nb ) {
2501 return;
2502 }
2503 int tl=sp_canvas_tile_floor(nl);
2504 int tt=sp_canvas_tile_floor(nt);
2505 int tr=sp_canvas_tile_ceil(nr);
2506 int tb=sp_canvas_tile_ceil(nb);
2507 if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2508 if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2509 if ( tr > canvas->tRight ) tr=canvas->tRight;
2510 if ( tt < canvas->tTop ) tt=canvas->tTop;
2511 if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2513 for (int i=tl; i<tr; i++) {
2514 for (int j=tt; j<tb; j++) {
2515 canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2516 }
2517 }
2518 }
2521 /*
2522 Local Variables:
2523 mode:c++
2524 c-file-style:"stroustrup"
2525 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2526 indent-tabs-mode:nil
2527 fill-column:99
2528 End:
2529 */
2530 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :