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);
1501 int retval = FALSE;
1503 /* dispatch normally regardless of the event's window if an item has
1504 has a pointer grab in effect */
1505 if (!canvas->grabbed_item &&
1506 event->window != SP_CANVAS_WINDOW (canvas))
1507 return retval;
1509 int mask;
1510 switch (event->button) {
1511 case 1:
1512 mask = GDK_BUTTON1_MASK;
1513 break;
1514 case 2:
1515 mask = GDK_BUTTON2_MASK;
1516 break;
1517 case 3:
1518 mask = GDK_BUTTON3_MASK;
1519 break;
1520 case 4:
1521 mask = GDK_BUTTON4_MASK;
1522 break;
1523 case 5:
1524 mask = GDK_BUTTON5_MASK;
1525 break;
1526 default:
1527 mask = 0;
1528 }
1530 switch (event->type) {
1531 case GDK_BUTTON_PRESS:
1532 case GDK_2BUTTON_PRESS:
1533 case GDK_3BUTTON_PRESS:
1534 /* Pick the current item as if the button were not pressed, and
1535 * then process the event.
1536 */
1537 canvas->state = event->state;
1538 pick_current_item (canvas, (GdkEvent *) event);
1539 canvas->state ^= mask;
1540 retval = emit_event (canvas, (GdkEvent *) event);
1541 break;
1543 case GDK_BUTTON_RELEASE:
1544 /* Process the event as if the button were pressed, then repick
1545 * after the button has been released
1546 */
1547 canvas->state = event->state;
1548 retval = emit_event (canvas, (GdkEvent *) event);
1549 event->state ^= mask;
1550 canvas->state = event->state;
1551 pick_current_item (canvas, (GdkEvent *) event);
1552 event->state ^= mask;
1553 break;
1555 default:
1556 g_assert_not_reached ();
1557 }
1559 return retval;
1560 }
1562 /**
1563 * Scroll event handler for the canvas.
1564 *
1565 * \todo FIXME: generate motion events to re-select items.
1566 */
1567 static gint
1568 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1569 {
1570 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1571 }
1573 static inline void request_motions(GdkWindow *w, GdkEventMotion *event) {
1574 gdk_window_get_pointer(w, NULL, NULL, NULL);
1575 #if HAS_GDK_EVENT_REQUEST_MOTIONS
1576 gdk_event_request_motions(event);
1577 #endif
1578 }
1580 /**
1581 * Motion event handler for the canvas.
1582 */
1583 static int
1584 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1585 {
1586 static guint32 prev_time;
1587 static boost::optional<Geom::Point> prev_pos;
1589 int status;
1590 SPCanvas *canvas = SP_CANVAS (widget);
1592 track_latency((GdkEvent *)event);
1594 if (event->window != SP_CANVAS_WINDOW (canvas))
1595 return FALSE;
1597 if (canvas->pixmap_gc == NULL) // canvas being deleted
1598 return FALSE;
1600 // Snap when speed drops below e.g. 0.1 px/msec, or when no motion events have occured for 100 msec.
1601 // i.e. snap when we're at stand still. The speed threshold enforces snapping for tablets, which will never
1602 // be full at stand still and might keep spitting out motion events.
1604 // When moving at speeds around the speed limit, Inkscape might snap for one motion event but not for the
1605 // next, which will make the object that's being dragged jump from the snapped position to the mouse
1606 // position and back again. That could be annoying, but I don't see an easy way around this.
1608 if (event->type == GDK_MOTION_NOTIFY) {
1609 Geom::Point event_pos(event->x, event->y);
1610 guint32 event_t = gdk_event_get_time ( (GdkEvent *) event );
1612 sp_canvas_snap_watchdog_kill(canvas);
1613 SPDesktop *dt = SP_ACTIVE_DESKTOP;
1615 if (prev_pos) {
1616 Geom::Coord dist = Geom::L2(event_pos - *prev_pos);
1617 guint32 delta_t = event_t - prev_time;
1618 gdouble speed = delta_t > 0 ? dist/delta_t : 1000;
1619 // std::cout << "speed = " << speed << " px/msec " << "| time passed = " << delta_t << " msec" << std::endl;
1620 if (speed < 0.1) {
1621 if (dt) {
1622 dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false);
1623 }
1624 } else {
1625 // We're moving fast, so postpone any snapping until the next GDK_MOTION_NOTIFY event.
1626 if (dt) {
1627 dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(true);
1628 }
1629 // We must snap at some point in time though, so set a watchdog timer at 100 msec from
1630 // now, just in case there's no future motion event that's under the speed limit.
1631 sp_canvas_snap_watchdog_set(canvas, event);
1632 }
1633 } else {
1634 // This is the first GDK_MOTION_NOTIFY event, so postpone snapping and set the watchdog
1635 if (dt) {
1636 dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(true);
1637 }
1638 sp_canvas_snap_watchdog_set(canvas, event);
1639 }
1641 prev_pos = event_pos;
1642 prev_time = event_t;
1643 }
1645 canvas->state = event->state;
1646 pick_current_item (canvas, (GdkEvent *) event);
1648 status = emit_event (canvas, (GdkEvent *) event);
1650 if (event->is_hint) {
1651 request_motions(widget->window, event);
1652 }
1654 return status;
1655 }
1657 gboolean sp_canvas_snap_watchdog_callback(gpointer data)
1658 {
1659 SPDesktop *dt = SP_ACTIVE_DESKTOP;
1660 if (dt) {
1661 dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false);
1662 }
1664 SPCanvas *canvas = reinterpret_cast<SPCanvas *>(data);
1665 emit_event(canvas, canvas->watchdog_event);
1666 gdk_event_free(canvas->watchdog_event);
1667 canvas->watchdog_event = NULL;
1668 canvas->watchdog_id = 0;
1670 return FALSE;
1671 }
1673 void sp_canvas_snap_watchdog_set(SPCanvas *canvas, GdkEventMotion *event)
1674 {
1675 g_assert(canvas->watchdog_id == 0);
1676 canvas->watchdog_id = g_timeout_add(100, &sp_canvas_snap_watchdog_callback, canvas);
1677 g_assert(canvas->watchdog_event == NULL);
1678 canvas->watchdog_event = gdk_event_copy( (GdkEvent *) event);
1679 }
1681 void sp_canvas_snap_watchdog_kill(SPCanvas *canvas)
1682 {
1683 if (canvas->watchdog_id) {
1684 g_source_remove(canvas->watchdog_id); // Kill the watchdog
1685 canvas->watchdog_id = 0;
1686 }
1688 if (canvas->watchdog_event) {
1689 gdk_event_free(canvas->watchdog_event);
1690 canvas->watchdog_event = NULL;
1691 }
1692 }
1694 static void
1695 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)
1696 {
1697 GtkWidget *widget = GTK_WIDGET (canvas);
1699 SPCanvasBuf buf;
1700 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1701 buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1702 } else {
1703 buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1704 }
1706 // Mark the region clean
1707 sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1709 buf.buf_rowstride = sw * 4;
1710 buf.rect.x0 = x0;
1711 buf.rect.y0 = y0;
1712 buf.rect.x1 = x1;
1713 buf.rect.y1 = y1;
1714 buf.visible_rect.x0 = draw_x1;
1715 buf.visible_rect.y0 = draw_y1;
1716 buf.visible_rect.x1 = draw_x2;
1717 buf.visible_rect.y1 = draw_y2;
1718 GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1719 buf.bg_color = (((color->red & 0xff00) << 8)
1720 | (color->green & 0xff00)
1721 | (color->blue >> 8));
1722 buf.is_empty = true;
1724 buf.ct = nr_create_cairo_context_canvasbuf (&(buf.visible_rect), &buf);
1726 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1727 SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1728 }
1730 #if ENABLE_LCMS
1731 cmsHTRANSFORM transf = 0;
1732 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1733 bool fromDisplay = prefs->getBool( "/options/displayprofile/from_display");
1734 if ( fromDisplay ) {
1735 transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" );
1736 } else {
1737 transf = Inkscape::colorprofile_get_display_transform();
1738 }
1739 #endif // ENABLE_LCMS
1741 if (buf.is_empty) {
1742 #if ENABLE_LCMS
1743 if ( transf && canvas->enable_cms_display_adj ) {
1744 cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1745 }
1746 #endif // ENABLE_LCMS
1747 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1748 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1749 canvas->pixmap_gc,
1750 TRUE,
1751 x0 - canvas->x0, y0 - canvas->y0,
1752 x1 - x0, y1 - y0);
1753 } else {
1755 #if ENABLE_LCMS
1756 if ( transf && canvas->enable_cms_display_adj ) {
1757 for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1758 guchar* p = buf.buf + (sw * 3) * yy;
1759 cmsDoTransform( transf, p, p, (x1 - x0) );
1760 }
1761 }
1762 #endif // ENABLE_LCMS
1764 // Now we only need to output the prepared pixmap to the actual screen, and this define chooses one
1765 // of the two ways to do it. The cairo way is direct and straightforward, but unfortunately
1766 // noticeably slower. I asked Carl Worth but he was unable so far to suggest any specific reason
1767 // for this slowness. So, for now we use the oldish method: squeeze out 32bpp buffer to 24bpp and
1768 // use gdk_draw_rgb_image_dithalign, for unfortunately gdk can only handle 24 bpp, which cairo
1769 // cannot handle at all. Still, this way is currently faster even despite the blit with squeeze.
1771 ///#define CANVAS_OUTPUT_VIA_CAIRO
1773 #ifdef CANVAS_OUTPUT_VIA_CAIRO
1775 buf.cst = cairo_image_surface_create_for_data (
1776 buf.buf,
1777 CAIRO_FORMAT_ARGB32, // unpacked, i.e. 32 bits! one byte is unused
1778 x1 - x0, y1 - y0,
1779 buf.buf_rowstride
1780 );
1781 cairo_t *window_ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1782 cairo_set_source_surface (window_ct, buf.cst, x0 - canvas->x0, y0 - canvas->y0);
1783 cairo_paint (window_ct);
1784 cairo_destroy (window_ct);
1785 cairo_surface_finish (buf.cst);
1786 cairo_surface_destroy (buf.cst);
1788 #else
1790 NRPixBlock b3;
1791 nr_pixblock_setup_fast (&b3, NR_PIXBLOCK_MODE_R8G8B8, x0, y0, x1, y1, TRUE);
1793 NRPixBlock b4;
1794 nr_pixblock_setup_extern (&b4, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1,
1795 buf.buf,
1796 buf.buf_rowstride,
1797 FALSE, FALSE);
1799 // this does the 32->24 squishing, using an assembler routine:
1800 nr_blit_pixblock_pixblock (&b3, &b4);
1802 gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1803 canvas->pixmap_gc,
1804 x0 - canvas->x0, y0 - canvas->y0,
1805 x1 - x0, y1 - y0,
1806 GDK_RGB_DITHER_MAX,
1807 NR_PIXBLOCK_PX(&b3),
1808 sw * 3,
1809 x0 - canvas->x0, y0 - canvas->y0);
1811 nr_pixblock_release (&b3);
1812 nr_pixblock_release (&b4);
1813 #endif
1814 }
1816 cairo_surface_t *cst = cairo_get_target(buf.ct);
1817 cairo_destroy (buf.ct);
1818 cairo_surface_finish (cst);
1819 cairo_surface_destroy (cst);
1821 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1822 nr_pixelstore_256K_free (buf.buf);
1823 } else {
1824 nr_pixelstore_1M_free (buf.buf);
1825 }
1826 }
1828 struct PaintRectSetup {
1829 SPCanvas* canvas;
1830 NRRectL big_rect;
1831 GTimeVal start_time;
1832 int max_pixels;
1833 Geom::Point mouse_loc;
1834 };
1836 /**
1837 * Paint the given rect, recursively subdividing the region until it is the size of a single
1838 * buffer.
1839 *
1840 * @return true if the drawing completes
1841 */
1842 static int
1843 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1844 {
1845 GTimeVal now;
1846 g_get_current_time (&now);
1848 glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1849 + (now.tv_usec - setup->start_time.tv_usec);
1851 // Allow only very fast buffers to be run together;
1852 // as soon as the total redraw time exceeds 1ms, cancel;
1853 // this returns control to the idle loop and allows Inkscape to process user input
1854 // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1855 // it will get back and finish painting what remains to paint.
1856 if (elapsed > 1000) {
1858 // Interrupting redraw isn't always good.
1859 // For example, when you drag one node of a big path, only the buffer containing
1860 // the mouse cursor will be redrawn again and again, and the rest of the path
1861 // will remain stale because Inkscape never has enough idle time to redraw all
1862 // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1863 // If this limit is set, and if we have aborted redraw more times than is allowed,
1864 // interrupting is blocked and we're forced to redraw full screen once
1865 // (after which we can again interrupt forced_redraw_limit times).
1866 if (setup->canvas->forced_redraw_limit < 0 ||
1867 setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1869 if (setup->canvas->forced_redraw_limit != -1) {
1870 setup->canvas->forced_redraw_count++;
1871 }
1873 return false;
1874 }
1875 }
1877 // Find the optimal buffer dimensions
1878 int bw = this_rect.x1 - this_rect.x0;
1879 int bh = this_rect.y1 - this_rect.y0;
1880 if ((bw < 1) || (bh < 1))
1881 return 0;
1883 if (bw * bh < setup->max_pixels) {
1884 // We are small enough
1885 sp_canvas_paint_single_buffer (setup->canvas,
1886 this_rect.x0, this_rect.y0,
1887 this_rect.x1, this_rect.y1,
1888 setup->big_rect.x0, setup->big_rect.y0,
1889 setup->big_rect.x1, setup->big_rect.y1, bw);
1890 return 1;
1891 }
1893 NRRectL lo = this_rect;
1894 NRRectL hi = this_rect;
1896 /*
1897 This test determines the redraw strategy:
1899 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1900 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1901 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1902 and seems to be faster for drawings with many smaller objects at zoom-out.
1904 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1905 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1906 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1907 faster.
1909 The default for now is the strips mode.
1910 */
1911 if (bw < bh || bh < 2 * TILE_SIZE) {
1912 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1913 int mid = ((long long) this_rect.x0 + (long long) this_rect.x1) / 2;
1914 // Make sure that mid lies on a tile boundary
1915 mid = (mid / TILE_SIZE) * TILE_SIZE;
1917 lo.x1 = mid;
1918 hi.x0 = mid;
1920 if (setup->mouse_loc[Geom::X] < mid) {
1921 // Always paint towards the mouse first
1922 return sp_canvas_paint_rect_internal(setup, lo)
1923 && sp_canvas_paint_rect_internal(setup, hi);
1924 } else {
1925 return sp_canvas_paint_rect_internal(setup, hi)
1926 && sp_canvas_paint_rect_internal(setup, lo);
1927 }
1928 } else {
1929 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1930 int mid = ((long long) this_rect.y0 + (long long) this_rect.y1) / 2;
1931 // Make sure that mid lies on a tile boundary
1932 mid = (mid / TILE_SIZE) * TILE_SIZE;
1934 lo.y1 = mid;
1935 hi.y0 = mid;
1937 if (setup->mouse_loc[Geom::Y] < mid) {
1938 // Always paint towards the mouse first
1939 return sp_canvas_paint_rect_internal(setup, lo)
1940 && sp_canvas_paint_rect_internal(setup, hi);
1941 } else {
1942 return sp_canvas_paint_rect_internal(setup, hi)
1943 && sp_canvas_paint_rect_internal(setup, lo);
1944 }
1945 }
1946 }
1949 /**
1950 * Helper that draws a specific rectangular part of the canvas.
1951 *
1952 * @return true if the rectangle painting succeeds.
1953 */
1954 static bool
1955 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1956 {
1957 g_return_val_if_fail (!canvas->need_update, false);
1959 NRRectL rect;
1960 rect.x0 = xx0;
1961 rect.x1 = xx1;
1962 rect.y0 = yy0;
1963 rect.y1 = yy1;
1965 // Clip rect-to-draw by the current visible area
1966 rect.x0 = MAX (rect.x0, canvas->x0);
1967 rect.y0 = MAX (rect.y0, canvas->y0);
1968 rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1969 rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1971 #ifdef DEBUG_REDRAW
1972 // paint the area to redraw yellow
1973 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1974 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1975 canvas->pixmap_gc,
1976 TRUE,
1977 rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1978 rect.x1 - rect.x0, rect.y1 - rect.y0);
1979 #endif
1981 PaintRectSetup setup;
1983 setup.canvas = canvas;
1984 setup.big_rect = rect;
1986 // Save the mouse location
1987 gint x, y;
1988 gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1989 setup.mouse_loc = sp_canvas_window_to_world (canvas, Geom::Point(x,y));
1991 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1992 // use 256K as a compromise to not slow down gradients
1993 // 256K is the cached buffer and we need 4 channels
1994 setup.max_pixels = 65536; // 256K/4
1995 } else {
1996 // paths only, so 1M works faster
1997 // 1M is the cached buffer and we need 4 channels
1998 setup.max_pixels = 262144;
1999 }
2001 // Start the clock
2002 g_get_current_time(&(setup.start_time));
2004 // Go
2005 return sp_canvas_paint_rect_internal(&setup, rect);
2006 }
2008 /**
2009 * Force a full redraw after a specified number of interrupted redraws
2010 */
2011 void
2012 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
2013 g_return_if_fail(canvas != NULL);
2015 canvas->forced_redraw_limit = count;
2016 canvas->forced_redraw_count = 0;
2017 }
2019 /**
2020 * End forced full redraw requests
2021 */
2022 void
2023 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
2024 g_return_if_fail(canvas != NULL);
2026 canvas->forced_redraw_limit = -1;
2027 }
2029 /**
2030 * The canvas widget's expose callback.
2031 */
2032 static gint
2033 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
2034 {
2035 SPCanvas *canvas = SP_CANVAS (widget);
2037 if (!GTK_WIDGET_DRAWABLE (widget) ||
2038 (event->window != SP_CANVAS_WINDOW (canvas)))
2039 return FALSE;
2041 int n_rects;
2042 GdkRectangle *rects;
2043 gdk_region_get_rectangles (event->region, &rects, &n_rects);
2045 for (int i = 0; i < n_rects; i++) {
2046 NRRectL rect;
2048 rect.x0 = rects[i].x + canvas->x0;
2049 rect.y0 = rects[i].y + canvas->y0;
2050 rect.x1 = rect.x0 + rects[i].width;
2051 rect.y1 = rect.y0 + rects[i].height;
2053 sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
2054 }
2056 if (n_rects > 0)
2057 g_free (rects);
2059 return FALSE;
2060 }
2062 /**
2063 * The canvas widget's keypress callback.
2064 */
2065 static gint
2066 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
2067 {
2068 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
2069 }
2071 /**
2072 * Crossing event handler for the canvas.
2073 */
2074 static gint
2075 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
2076 {
2077 SPCanvas *canvas = SP_CANVAS (widget);
2079 if (event->window != SP_CANVAS_WINDOW (canvas))
2080 return FALSE;
2082 canvas->state = event->state;
2083 return pick_current_item (canvas, (GdkEvent *) event);
2084 }
2086 /**
2087 * Focus in handler for the canvas.
2088 */
2089 static gint
2090 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
2091 {
2092 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
2094 SPCanvas *canvas = SP_CANVAS (widget);
2096 if (canvas->focused_item) {
2097 return emit_event (canvas, (GdkEvent *) event);
2098 } else {
2099 return FALSE;
2100 }
2101 }
2103 /**
2104 * Focus out handler for the canvas.
2105 */
2106 static gint
2107 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
2108 {
2109 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2111 SPCanvas *canvas = SP_CANVAS (widget);
2113 if (canvas->focused_item)
2114 return emit_event (canvas, (GdkEvent *) event);
2115 else
2116 return FALSE;
2117 }
2119 /**
2120 * Helper that repaints the areas in the canvas that need it.
2121 *
2122 * @return true if all the dirty parts have been redrawn
2123 */
2124 static int
2125 paint (SPCanvas *canvas)
2126 {
2127 if (canvas->need_update) {
2128 sp_canvas_item_invoke_update (canvas->root, Geom::identity(), 0);
2129 canvas->need_update = FALSE;
2130 }
2132 if (!canvas->need_redraw)
2133 return TRUE;
2135 Gdk::Region to_paint;
2137 for (int j=canvas->tTop; j<canvas->tBottom; j++) {
2138 for (int i=canvas->tLeft; i<canvas->tRight; i++) {
2139 int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
2141 if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
2142 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
2143 TILE_SIZE, TILE_SIZE));
2144 }
2146 }
2147 }
2149 if (!to_paint.empty()) {
2150 Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
2151 typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
2152 for (Iter i=rect.begin(); i != rect.end(); ++i) {
2153 int x0 = (*i).get_x();
2154 int y0 = (*i).get_y();
2155 int x1 = x0 + (*i).get_width();
2156 int y1 = y0 + (*i).get_height();
2157 if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
2158 // Aborted
2159 return FALSE;
2160 };
2161 }
2162 }
2164 canvas->need_redraw = FALSE;
2166 // we've had a full unaborted redraw, reset the full redraw counter
2167 if (canvas->forced_redraw_limit != -1) {
2168 canvas->forced_redraw_count = 0;
2169 }
2171 return TRUE;
2172 }
2174 /**
2175 * Helper that invokes update, paint, and repick on canvas.
2176 */
2177 static int
2178 do_update (SPCanvas *canvas)
2179 {
2180 if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop durring interrupted display!
2181 return TRUE;
2183 /* Cause the update if necessary */
2184 if (canvas->need_update) {
2185 sp_canvas_item_invoke_update (canvas->root, Geom::identity(), 0);
2186 canvas->need_update = FALSE;
2187 }
2189 /* Paint if able to */
2190 if (GTK_WIDGET_DRAWABLE (canvas)) {
2191 return paint (canvas);
2192 }
2194 /* Pick new current item */
2195 while (canvas->need_repick) {
2196 canvas->need_repick = FALSE;
2197 pick_current_item (canvas, &canvas->pick_event);
2198 }
2200 return TRUE;
2201 }
2203 /**
2204 * Idle handler for the canvas that deals with pending updates and redraws.
2205 */
2206 static gint
2207 idle_handler (gpointer data)
2208 {
2209 GDK_THREADS_ENTER ();
2211 SPCanvas *canvas = SP_CANVAS (data);
2213 int const ret = do_update (canvas);
2215 if (ret) {
2216 /* Reset idle id */
2217 canvas->idle_id = 0;
2218 }
2220 GDK_THREADS_LEAVE ();
2222 return !ret;
2223 }
2225 /**
2226 * Convenience function to add an idle handler to a canvas.
2227 */
2228 static void
2229 add_idle (SPCanvas *canvas)
2230 {
2231 if (canvas->idle_id != 0)
2232 return;
2234 canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2235 }
2237 /**
2238 * Returns the root group of the specified canvas.
2239 */
2240 SPCanvasGroup *
2241 sp_canvas_root (SPCanvas *canvas)
2242 {
2243 g_return_val_if_fail (canvas != NULL, NULL);
2244 g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2246 return SP_CANVAS_GROUP (canvas->root);
2247 }
2249 /**
2250 * Scrolls canvas to specific position (cx and cy are measured in screen pixels)
2251 */
2252 void
2253 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2254 {
2255 g_return_if_fail (canvas != NULL);
2256 g_return_if_fail (SP_IS_CANVAS (canvas));
2258 int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels)
2259 int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do!
2260 int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the
2261 int dy = iy - canvas->y0; // canvas w.r.t its previous position
2263 canvas->dx0 = cx; // here the 'd' stands for double, not delta!
2264 canvas->dy0 = cy;
2265 canvas->x0 = ix;
2266 canvas->y0 = iy;
2268 sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2270 if (!clear) {
2271 // scrolling without zoom; redraw only the newly exposed areas
2272 if ((dx != 0) || (dy != 0)) {
2273 canvas->is_scrolling = is_scrolling;
2274 if (GTK_WIDGET_REALIZED (canvas)) {
2275 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2276 }
2277 }
2278 } else {
2279 // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2280 }
2281 }
2283 /**
2284 * Updates canvas if necessary.
2285 */
2286 void
2287 sp_canvas_update_now (SPCanvas *canvas)
2288 {
2289 g_return_if_fail (canvas != NULL);
2290 g_return_if_fail (SP_IS_CANVAS (canvas));
2292 if (!(canvas->need_update ||
2293 canvas->need_redraw))
2294 return;
2296 do_update (canvas);
2297 }
2299 /**
2300 * Update callback for canvas widget.
2301 */
2302 static void
2303 sp_canvas_request_update (SPCanvas *canvas)
2304 {
2305 canvas->need_update = TRUE;
2306 add_idle (canvas);
2307 }
2309 /**
2310 * Forces redraw of rectangular canvas area.
2311 */
2312 void
2313 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2314 {
2315 NRRectL bbox;
2316 NRRectL visible;
2317 NRRectL clip;
2319 g_return_if_fail (canvas != NULL);
2320 g_return_if_fail (SP_IS_CANVAS (canvas));
2322 if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2323 if ((x0 >= x1) || (y0 >= y1)) return;
2325 bbox.x0 = x0;
2326 bbox.y0 = y0;
2327 bbox.x1 = x1;
2328 bbox.y1 = y1;
2330 visible.x0 = canvas->x0;
2331 visible.y0 = canvas->y0;
2332 visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2333 visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2335 nr_rect_l_intersect (&clip, &bbox, &visible);
2337 sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2338 add_idle (canvas);
2339 }
2341 /**
2342 * Sets world coordinates from win and canvas.
2343 */
2344 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2345 {
2346 g_return_if_fail (canvas != NULL);
2347 g_return_if_fail (SP_IS_CANVAS (canvas));
2349 if (worldx) *worldx = canvas->x0 + winx;
2350 if (worldy) *worldy = canvas->y0 + winy;
2351 }
2353 /**
2354 * Sets win coordinates from world and canvas.
2355 */
2356 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2357 {
2358 g_return_if_fail (canvas != NULL);
2359 g_return_if_fail (SP_IS_CANVAS (canvas));
2361 if (winx) *winx = worldx - canvas->x0;
2362 if (winy) *winy = worldy - canvas->y0;
2363 }
2365 /**
2366 * Converts point from win to world coordinates.
2367 */
2368 Geom::Point sp_canvas_window_to_world(SPCanvas const *canvas, Geom::Point const win)
2369 {
2370 g_assert (canvas != NULL);
2371 g_assert (SP_IS_CANVAS (canvas));
2373 return Geom::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2374 }
2376 /**
2377 * Converts point from world to win coordinates.
2378 */
2379 Geom::Point sp_canvas_world_to_window(SPCanvas const *canvas, Geom::Point const world)
2380 {
2381 g_assert (canvas != NULL);
2382 g_assert (SP_IS_CANVAS (canvas));
2384 return Geom::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2385 }
2387 /**
2388 * Returns true if point given in world coordinates is inside window.
2389 */
2390 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, Geom::Point const &world)
2391 {
2392 g_assert( canvas != NULL );
2393 g_assert(SP_IS_CANVAS(canvas));
2395 GtkWidget const &w = *GTK_WIDGET(canvas);
2396 return ( ( canvas->x0 <= world[Geom::X] ) &&
2397 ( canvas->y0 <= world[Geom::Y] ) &&
2398 ( world[Geom::X] < canvas->x0 + w.allocation.width ) &&
2399 ( world[Geom::Y] < canvas->y0 + w.allocation.height ) );
2400 }
2402 /**
2403 * Return canvas window coordinates as Geom::Rect.
2404 */
2405 Geom::Rect SPCanvas::getViewbox() const
2406 {
2407 GtkWidget const *w = GTK_WIDGET(this);
2408 return Geom::Rect(Geom::Point(dx0, dy0),
2409 Geom::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2410 }
2412 /**
2413 * Return canvas window coordinates as IRect (a rectangle defined by integers).
2414 */
2415 NR::IRect SPCanvas::getViewboxIntegers() const
2416 {
2417 GtkWidget const *w = GTK_WIDGET(this);
2418 return NR::IRect(NR::IPoint(x0, y0),
2419 NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height));
2420 }
2422 inline int sp_canvas_tile_floor(int x)
2423 {
2424 return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2425 }
2427 inline int sp_canvas_tile_ceil(int x)
2428 {
2429 return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2430 }
2432 /**
2433 * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2434 */
2435 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2436 {
2437 if ( nl >= nr || nt >= nb ) {
2438 if ( canvas->tiles ) g_free(canvas->tiles);
2439 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2440 canvas->tileH=canvas->tileV=0;
2441 canvas->tiles=NULL;
2442 return;
2443 }
2444 int tl=sp_canvas_tile_floor(nl);
2445 int tt=sp_canvas_tile_floor(nt);
2446 int tr=sp_canvas_tile_ceil(nr);
2447 int tb=sp_canvas_tile_ceil(nb);
2449 int nh = tr-tl, nv = tb-tt;
2450 uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2451 for (int i=tl; i<tr; i++) {
2452 for (int j=tt; j<tb; j++) {
2453 int ind = (i-tl) + (j-tt)*nh;
2454 if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2455 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2456 } else {
2457 ntiles[ind]=0; // newly exposed areas get 0
2458 }
2459 }
2460 }
2461 if ( canvas->tiles ) g_free(canvas->tiles);
2462 canvas->tiles=ntiles;
2463 canvas->tLeft=tl;
2464 canvas->tTop=tt;
2465 canvas->tRight=tr;
2466 canvas->tBottom=tb;
2467 canvas->tileH=nh;
2468 canvas->tileV=nv;
2469 }
2471 /*
2472 * Helper that queues a canvas rectangle for redraw
2473 */
2474 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2475 canvas->need_redraw = TRUE;
2477 sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2478 }
2480 /**
2481 * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2482 */
2483 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2484 {
2485 if ( nl >= nr || nt >= nb ) {
2486 return;
2487 }
2488 int tl=sp_canvas_tile_floor(nl);
2489 int tt=sp_canvas_tile_floor(nt);
2490 int tr=sp_canvas_tile_ceil(nr);
2491 int tb=sp_canvas_tile_ceil(nb);
2492 if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2493 if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2494 if ( tr > canvas->tRight ) tr=canvas->tRight;
2495 if ( tt < canvas->tTop ) tt=canvas->tTop;
2496 if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2498 for (int i=tl; i<tr; i++) {
2499 for (int j=tt; j<tb; j++) {
2500 canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2501 }
2502 }
2503 }
2506 /*
2507 Local Variables:
2508 mode:c++
2509 c-file-style:"stroustrup"
2510 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2511 indent-tabs-mode:nil
2512 fill-column:99
2513 End:
2514 */
2515 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :