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 <2geom/matrix.h>
36 #include <libnr/nr-convex-hull.h>
37 #include "preferences.h"
38 #include "inkscape.h"
39 #include "sodipodi-ctrlrect.h"
40 #if ENABLE_LCMS
41 #include "color-profile-fns.h"
42 #endif // ENABLE_LCMS
43 #include "display/rendermode.h"
44 #include "libnr/nr-blit.h"
45 #include "display/inkscape-cairo.h"
46 #include "debug/gdk-event-latency-tracker.h"
47 #include "desktop.h"
48 #include "sp-namedview.h"
50 using Inkscape::Debug::GdkEventLatencyTracker;
52 // GTK_CHECK_VERSION returns false on failure
53 #define HAS_GDK_EVENT_REQUEST_MOTIONS GTK_CHECK_VERSION(2, 12, 0)
55 // gtk_check_version returns non-NULL on failure
56 static bool const HAS_BROKEN_MOTION_HINTS =
57 true || gtk_check_version(2, 12, 0) != NULL || !HAS_GDK_EVENT_REQUEST_MOTIONS;
59 // Define this to visualize the regions to be redrawn
60 //#define DEBUG_REDRAW 1;
62 // Tiles are a way to minimize the number of redraws, eliminating too small redraws.
63 // The canvas stores a 2D array of ints, each representing a TILE_SIZExTILE_SIZE pixels tile.
64 // If any part of it is dirtied, the entire tile is dirtied (its int is nonzero) and repainted.
65 #define TILE_SIZE 16
67 static gint const sp_canvas_update_priority = G_PRIORITY_HIGH_IDLE;
69 #define SP_CANVAS_WINDOW(c) (((GtkWidget *) (c))->window)
71 enum {
72 SP_CANVAS_ITEM_VISIBLE = 1 << 7,
73 SP_CANVAS_ITEM_NEED_UPDATE = 1 << 8,
74 SP_CANVAS_ITEM_NEED_AFFINE = 1 << 9
75 };
77 /**
78 * A group of Items.
79 */
80 struct SPCanvasGroup {
81 SPCanvasItem item;
83 GList *items, *last;
84 };
86 /**
87 * The SPCanvasGroup vtable.
88 */
89 struct SPCanvasGroupClass {
90 SPCanvasItemClass parent_class;
91 };
93 /**
94 * The SPCanvas vtable.
95 */
96 struct SPCanvasClass {
97 GtkWidgetClass parent_class;
98 };
100 static void group_add (SPCanvasGroup *group, SPCanvasItem *item);
101 static void group_remove (SPCanvasGroup *group, SPCanvasItem *item);
103 /* SPCanvasItem */
105 enum {ITEM_EVENT, ITEM_LAST_SIGNAL};
108 static void sp_canvas_request_update (SPCanvas *canvas);
110 static void track_latency(GdkEvent const *event);
111 static void sp_canvas_item_class_init (SPCanvasItemClass *klass);
112 static void sp_canvas_item_init (SPCanvasItem *item);
113 static void sp_canvas_item_dispose (GObject *object);
114 static void sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args);
116 static int emit_event (SPCanvas *canvas, GdkEvent *event);
118 static guint item_signals[ITEM_LAST_SIGNAL] = { 0 };
120 static GtkObjectClass *item_parent_class;
122 /**
123 * Registers the SPCanvasItem class with Glib and returns its type number.
124 */
125 GType
126 sp_canvas_item_get_type (void)
127 {
128 static GType type = 0;
129 if (!type) {
130 static GTypeInfo const info = {
131 sizeof (SPCanvasItemClass),
132 NULL, NULL,
133 (GClassInitFunc) sp_canvas_item_class_init,
134 NULL, NULL,
135 sizeof (SPCanvasItem),
136 0,
137 (GInstanceInitFunc) sp_canvas_item_init,
138 NULL
139 };
140 type = g_type_register_static (GTK_TYPE_OBJECT, "SPCanvasItem", &info, (GTypeFlags)0);
141 }
143 return type;
144 }
146 /**
147 * Initializes the SPCanvasItem vtable and the "event" signal.
148 */
149 static void
150 sp_canvas_item_class_init (SPCanvasItemClass *klass)
151 {
152 GObjectClass *object_class = (GObjectClass *) klass;
154 /* fixme: Derive from GObject */
155 item_parent_class = (GtkObjectClass*)gtk_type_class (GTK_TYPE_OBJECT);
157 item_signals[ITEM_EVENT] = g_signal_new ("event",
158 G_TYPE_FROM_CLASS (klass),
159 G_SIGNAL_RUN_LAST,
160 ((glong)((guint8*)&(klass->event) - (guint8*)klass)),
161 NULL, NULL,
162 sp_marshal_BOOLEAN__POINTER,
163 G_TYPE_BOOLEAN, 1,
164 GDK_TYPE_EVENT);
166 object_class->dispose = sp_canvas_item_dispose;
167 }
169 /**
170 * Callback for initialization of SPCanvasItem.
171 */
172 static void
173 sp_canvas_item_init (SPCanvasItem *item)
174 {
175 item->flags |= SP_CANVAS_ITEM_VISIBLE;
176 item->xform = Geom::Matrix(Geom::identity());
177 }
179 /**
180 * Constructs new SPCanvasItem on SPCanvasGroup.
181 */
182 SPCanvasItem *
183 sp_canvas_item_new (SPCanvasGroup *parent, GtkType type, gchar const *first_arg_name, ...)
184 {
185 va_list args;
187 g_return_val_if_fail (parent != NULL, NULL);
188 g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL);
189 g_return_val_if_fail (gtk_type_is_a (type, sp_canvas_item_get_type ()), NULL);
191 SPCanvasItem *item = SP_CANVAS_ITEM (gtk_type_new (type));
193 va_start (args, first_arg_name);
194 sp_canvas_item_construct (item, parent, first_arg_name, args);
195 va_end (args);
197 return item;
198 }
200 /**
201 * Sets up the newly created SPCanvasItem.
202 *
203 * We make it static for encapsulation reasons since it was nowhere used.
204 */
205 static void
206 sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args)
207 {
208 g_return_if_fail (SP_IS_CANVAS_GROUP (parent));
209 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
211 item->parent = SP_CANVAS_ITEM (parent);
212 item->canvas = item->parent->canvas;
214 g_object_set_valist (G_OBJECT (item), first_arg_name, args);
216 group_add (SP_CANVAS_GROUP (item->parent), item);
218 sp_canvas_item_request_update (item);
219 }
221 /**
222 * Helper function that requests redraw only if item's visible flag is set.
223 */
224 static void
225 redraw_if_visible (SPCanvasItem *item)
226 {
227 if (item->flags & SP_CANVAS_ITEM_VISIBLE) {
228 int x0 = (int)(item->x1);
229 int x1 = (int)(item->x2);
230 int y0 = (int)(item->y1);
231 int y1 = (int)(item->y2);
233 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
234 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
235 }
236 }
237 }
239 /**
240 * Callback that removes item from all referers and destroys it.
241 */
242 static void
243 sp_canvas_item_dispose (GObject *object)
244 {
245 SPCanvasItem *item = SP_CANVAS_ITEM (object);
247 // Hack: if this is a ctrlrect, move it to 0,0;
248 // this redraws only the stroke of the rect to be deleted,
249 // avoiding redraw of the entire area
250 if (SP_IS_CTRLRECT(item)) {
251 SP_CTRLRECT(object)->setRectangle(Geom::Rect(Geom::Point(0,0),Geom::Point(0,0)));
252 SP_CTRLRECT(object)->update(item->xform, 0);
253 } else {
254 redraw_if_visible (item);
255 }
256 item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
258 if (item == item->canvas->current_item) {
259 item->canvas->current_item = NULL;
260 item->canvas->need_repick = TRUE;
261 }
263 if (item == item->canvas->new_current_item) {
264 item->canvas->new_current_item = NULL;
265 item->canvas->need_repick = TRUE;
266 }
268 if (item == item->canvas->grabbed_item) {
269 item->canvas->grabbed_item = NULL;
270 gdk_pointer_ungrab (GDK_CURRENT_TIME);
271 }
273 if (item == item->canvas->focused_item)
274 item->canvas->focused_item = NULL;
276 if (item->parent) {
277 group_remove (SP_CANVAS_GROUP (item->parent), item);
278 }
280 G_OBJECT_CLASS (item_parent_class)->dispose (object);
281 }
283 /**
284 * Helper function to update item and its children.
285 *
286 * NB! affine is parent2canvas.
287 */
288 static void
289 sp_canvas_item_invoke_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags)
290 {
291 /* Apply the child item's transform */
292 Geom::Matrix child_affine = item->xform * affine;
294 /* apply object flags to child flags */
295 int child_flags = flags & ~SP_CANVAS_UPDATE_REQUESTED;
297 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
298 child_flags |= SP_CANVAS_UPDATE_REQUESTED;
300 if (item->flags & SP_CANVAS_ITEM_NEED_AFFINE)
301 child_flags |= SP_CANVAS_UPDATE_AFFINE;
303 if (child_flags & (SP_CANVAS_UPDATE_REQUESTED | SP_CANVAS_UPDATE_AFFINE)) {
304 if (SP_CANVAS_ITEM_GET_CLASS (item)->update)
305 SP_CANVAS_ITEM_GET_CLASS (item)->update (item, child_affine, child_flags);
306 }
308 GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_UPDATE);
309 GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_AFFINE);
310 }
312 /**
313 * Helper function to invoke the point method of the item.
314 *
315 * The argument x, y should be in the parent's item-relative coordinate
316 * system. This routine applies the inverse of the item's transform,
317 * maintaining the affine invariant.
318 */
319 static double
320 sp_canvas_item_invoke_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item)
321 {
322 if (SP_CANVAS_ITEM_GET_CLASS (item)->point)
323 return SP_CANVAS_ITEM_GET_CLASS (item)->point (item, p, actual_item);
325 return NR_HUGE;
326 }
328 /**
329 * Makes the item's affine transformation matrix be equal to the specified
330 * matrix.
331 *
332 * @item: A canvas item.
333 * @affine: An affine transformation matrix.
334 */
335 void
336 sp_canvas_item_affine_absolute (SPCanvasItem *item, Geom::Matrix const &affine)
337 {
338 item->xform = affine;
340 if (!(item->flags & SP_CANVAS_ITEM_NEED_AFFINE)) {
341 item->flags |= SP_CANVAS_ITEM_NEED_AFFINE;
342 if (item->parent != NULL) {
343 sp_canvas_item_request_update (item->parent);
344 } else {
345 sp_canvas_request_update (item->canvas);
346 }
347 }
349 item->canvas->need_repick = TRUE;
350 }
352 /**
353 * Convenience function to reorder items in a group's child list.
354 *
355 * This puts the specified link after the "before" link.
356 */
357 static void
358 put_item_after (GList *link, GList *before)
359 {
360 if (link == before)
361 return;
363 SPCanvasGroup *parent = SP_CANVAS_GROUP (SP_CANVAS_ITEM (link->data)->parent);
365 if (before == NULL) {
366 if (link == parent->items) return;
368 link->prev->next = link->next;
370 if (link->next) {
371 link->next->prev = link->prev;
372 } else {
373 parent->last = link->prev;
374 }
376 link->prev = before;
377 link->next = parent->items;
378 link->next->prev = link;
379 parent->items = link;
380 } else {
381 if ((link == parent->last) && (before == parent->last->prev))
382 return;
384 if (link->next)
385 link->next->prev = link->prev;
387 if (link->prev)
388 link->prev->next = link->next;
389 else {
390 parent->items = link->next;
391 parent->items->prev = NULL;
392 }
394 link->prev = before;
395 link->next = before->next;
397 link->prev->next = link;
399 if (link->next)
400 link->next->prev = link;
401 else
402 parent->last = link;
403 }
404 }
407 /**
408 * Raises the item in its parent's stack by the specified number of positions.
409 *
410 * \param item A canvas item.
411 * \param positions Number of steps to raise the item.
412 *
413 * If the number of positions is greater than the distance to the top of the
414 * stack, then the item is put at the top.
415 */
416 void
417 sp_canvas_item_raise (SPCanvasItem *item, int positions)
418 {
419 g_return_if_fail (item != NULL);
420 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
421 g_return_if_fail (positions >= 0);
423 if (!item->parent || positions == 0)
424 return;
426 SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
427 GList *link = g_list_find (parent->items, item);
428 g_assert (link != NULL);
430 GList *before;
431 for (before = link; positions && before; positions--)
432 before = before->next;
434 if (!before)
435 before = parent->last;
437 put_item_after (link, before);
439 redraw_if_visible (item);
440 item->canvas->need_repick = TRUE;
441 }
444 /**
445 * Lowers the item in its parent's stack by the specified number of positions.
446 *
447 * \param item A canvas item.
448 * \param positions Number of steps to lower the item.
449 *
450 * If the number of positions is greater than the distance to the bottom of the
451 * stack, then the item is put at the bottom.
452 **/
453 void
454 sp_canvas_item_lower (SPCanvasItem *item, int positions)
455 {
456 g_return_if_fail (item != NULL);
457 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
458 g_return_if_fail (positions >= 1);
460 if (!item->parent || positions == 0)
461 return;
463 SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
464 GList *link = g_list_find (parent->items, item);
465 g_assert (link != NULL);
467 GList *before;
468 if (link->prev)
469 for (before = link->prev; positions && before; positions--)
470 before = before->prev;
471 else
472 before = NULL;
474 put_item_after (link, before);
476 redraw_if_visible (item);
477 item->canvas->need_repick = TRUE;
478 }
480 /**
481 * Sets visible flag on item and requests a redraw.
482 */
483 void
484 sp_canvas_item_show (SPCanvasItem *item)
485 {
486 g_return_if_fail (item != NULL);
487 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
489 if (item->flags & SP_CANVAS_ITEM_VISIBLE)
490 return;
492 item->flags |= SP_CANVAS_ITEM_VISIBLE;
494 int x0 = (int)(item->x1);
495 int x1 = (int)(item->x2);
496 int y0 = (int)(item->y1);
497 int y1 = (int)(item->y2);
499 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
500 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
501 item->canvas->need_repick = TRUE;
502 }
503 }
505 /**
506 * Clears visible flag on item and requests a redraw.
507 */
508 void
509 sp_canvas_item_hide (SPCanvasItem *item)
510 {
511 g_return_if_fail (item != NULL);
512 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
514 if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
515 return;
517 item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
519 int x0 = (int)(item->x1);
520 int x1 = (int)(item->x2);
521 int y0 = (int)(item->y1);
522 int y1 = (int)(item->y2);
524 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
525 sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
526 item->canvas->need_repick = TRUE;
527 }
528 }
530 /**
531 * Grab item under cursor.
532 *
533 * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE
534 */
535 int
536 sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
537 {
538 g_return_val_if_fail (item != NULL, -1);
539 g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1);
540 g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1);
542 if (item->canvas->grabbed_item)
543 return -1;
545 if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
546 return -1;
548 if (HAS_BROKEN_MOTION_HINTS) {
549 event_mask &= ~GDK_POINTER_MOTION_HINT_MASK;
550 }
552 /* fixme: Top hack (Lauris) */
553 /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
554 /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
555 gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
556 (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
557 NULL, cursor, etime);
559 item->canvas->grabbed_item = item;
560 item->canvas->grabbed_event_mask = event_mask;
561 item->canvas->current_item = item; /* So that events go to the grabbed item */
563 return 0;
564 }
566 /**
567 * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
568 * mouse.
569 *
570 * \param item A canvas item that holds a grab.
571 * \param etime The timestamp for ungrabbing the mouse.
572 */
573 void
574 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
575 {
576 g_return_if_fail (item != NULL);
577 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
579 if (item->canvas->grabbed_item != item)
580 return;
582 item->canvas->grabbed_item = NULL;
584 gdk_pointer_ungrab (etime);
585 }
587 /**
588 * Returns the product of all transformation matrices from the root item down
589 * to the item.
590 */
591 Geom::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
592 {
593 g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
595 Geom::Matrix affine = Geom::identity();
597 while (item) {
598 affine *= item->xform;
599 item = item->parent;
600 }
601 return affine;
602 }
604 /**
605 * Helper that returns true iff item is descendant of parent.
606 */
607 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
608 {
609 while (item) {
610 if (item == parent)
611 return true;
612 item = item->parent;
613 }
615 return false;
616 }
618 /**
619 * Focus canvas, and item under cursor if it is not already focussed.
620 */
621 void
622 sp_canvas_item_grab_focus (SPCanvasItem *item)
623 {
624 g_return_if_fail (item != NULL);
625 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
626 g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
628 SPCanvasItem *focused_item = item->canvas->focused_item;
630 if (focused_item) {
631 GdkEvent ev;
632 ev.focus_change.type = GDK_FOCUS_CHANGE;
633 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
634 ev.focus_change.send_event = FALSE;
635 ev.focus_change.in = FALSE;
637 emit_event (item->canvas, &ev);
638 }
640 item->canvas->focused_item = item;
641 gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
643 if (focused_item) {
644 GdkEvent ev;
645 ev.focus_change.type = GDK_FOCUS_CHANGE;
646 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
647 ev.focus_change.send_event = FALSE;
648 ev.focus_change.in = TRUE;
650 emit_event (item->canvas, &ev);
651 }
652 }
654 /**
655 * Requests that the canvas queue an update for the specified item.
656 *
657 * To be used only by item implementations.
658 */
659 void
660 sp_canvas_item_request_update (SPCanvasItem *item)
661 {
662 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
663 return;
665 item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
667 if (item->parent != NULL) {
668 /* Recurse up the tree */
669 sp_canvas_item_request_update (item->parent);
670 } else {
671 /* Have reached the top of the tree, make sure the update call gets scheduled. */
672 sp_canvas_request_update (item->canvas);
673 }
674 }
676 /**
677 * Returns position of item in group.
678 */
679 gint sp_canvas_item_order (SPCanvasItem * item)
680 {
681 return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
682 }
684 /* SPCanvasGroup */
686 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
687 static void sp_canvas_group_init (SPCanvasGroup *group);
688 static void sp_canvas_group_destroy (GtkObject *object);
690 static void sp_canvas_group_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags);
691 static double sp_canvas_group_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item);
692 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
694 static SPCanvasItemClass *group_parent_class;
696 /**
697 * Registers SPCanvasGroup class with Gtk and returns its type number.
698 */
699 GType sp_canvas_group_get_type(void)
700 {
701 static GType type = 0;
702 if (!type) {
703 GTypeInfo info = {
704 sizeof(SPCanvasGroupClass),
705 0, // base_init
706 0, // base_finalize
707 (GClassInitFunc)sp_canvas_group_class_init,
708 0, // class_finalize
709 0, // class_data
710 sizeof(SPCanvasGroup),
711 0, // n_preallocs
712 (GInstanceInitFunc)sp_canvas_group_init,
713 0 // value_table
714 };
715 type = g_type_register_static(sp_canvas_item_get_type(), "SPCanvasGroup", &info, static_cast<GTypeFlags>(0));
716 }
717 return type;
718 }
720 /**
721 * Class initialization function for SPCanvasGroupClass
722 */
723 static void
724 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
725 {
726 GtkObjectClass *object_class = (GtkObjectClass *) klass;
727 SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
729 group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
731 object_class->destroy = sp_canvas_group_destroy;
733 item_class->update = sp_canvas_group_update;
734 item_class->render = sp_canvas_group_render;
735 item_class->point = sp_canvas_group_point;
736 }
738 /**
739 * Callback. Empty.
740 */
741 static void
742 sp_canvas_group_init (SPCanvasGroup */*group*/)
743 {
744 /* Nothing here */
745 }
747 /**
748 * Callback that destroys all items in group and calls group's virtual
749 * destroy() function.
750 */
751 static void
752 sp_canvas_group_destroy (GtkObject *object)
753 {
754 g_return_if_fail (object != NULL);
755 g_return_if_fail (SP_IS_CANVAS_GROUP (object));
757 SPCanvasGroup const *group = SP_CANVAS_GROUP (object);
759 GList *list = group->items;
760 while (list) {
761 SPCanvasItem *child = (SPCanvasItem *)list->data;
762 list = list->next;
764 gtk_object_destroy (GTK_OBJECT (child));
765 }
767 if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
768 (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
769 }
771 /**
772 * Update handler for canvas groups
773 */
774 static void
775 sp_canvas_group_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags)
776 {
777 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
778 Geom::RectHull corners(Geom::Point(0, 0));
779 bool empty=true;
781 for (GList *list = group->items; list; list = list->next) {
782 SPCanvasItem *i = (SPCanvasItem *)list->data;
784 sp_canvas_item_invoke_update (i, affine, flags);
786 if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
787 if (empty) {
788 corners = Geom::RectHull(Geom::Point(i->x1, i->y1));
789 empty = false;
790 } else {
791 corners.add(Geom::Point(i->x1, i->y1));
792 }
793 corners.add(Geom::Point(i->x2, i->y2));
794 }
795 }
797 Geom::OptRect const bounds = corners.bounds();
798 if (bounds) {
799 item->x1 = bounds->min()[Geom::X];
800 item->y1 = bounds->min()[Geom::Y];
801 item->x2 = bounds->max()[Geom::X];
802 item->y2 = bounds->max()[Geom::Y];
803 } else {
804 // FIXME ?
805 item->x1 = item->x2 = item->y1 = item->y2 = 0;
806 }
807 }
809 /**
810 * Point handler for canvas groups.
811 */
812 static double
813 sp_canvas_group_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item)
814 {
815 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
816 double const x = p[Geom::X];
817 double const y = p[Geom::Y];
818 int x1 = (int)(x - item->canvas->close_enough);
819 int y1 = (int)(y - item->canvas->close_enough);
820 int x2 = (int)(x + item->canvas->close_enough);
821 int y2 = (int)(y + item->canvas->close_enough);
823 double best = 0.0;
824 *actual_item = NULL;
826 double dist = 0.0;
828 for (GList *list = group->items; list; list = list->next) {
829 SPCanvasItem *child = (SPCanvasItem *)list->data;
831 if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
832 SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
834 int has_point;
835 if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
836 dist = sp_canvas_item_invoke_point (child, p, &point_item);
837 has_point = TRUE;
838 } else
839 has_point = FALSE;
841 if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
842 best = dist;
843 *actual_item = point_item;
844 }
845 }
846 }
848 return best;
849 }
851 /**
852 * Renders all visible canvas group items in buf rectangle.
853 */
854 static void
855 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
856 {
857 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
859 for (GList *list = group->items; list; list = list->next) {
860 SPCanvasItem *child = (SPCanvasItem *)list->data;
861 if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
862 if ((child->x1 < buf->rect.x1) &&
863 (child->y1 < buf->rect.y1) &&
864 (child->x2 > buf->rect.x0) &&
865 (child->y2 > buf->rect.y0)) {
866 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
867 SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
868 }
869 }
870 }
871 }
873 /**
874 * Adds an item to a canvas group.
875 */
876 static void
877 group_add (SPCanvasGroup *group, SPCanvasItem *item)
878 {
879 gtk_object_ref (GTK_OBJECT (item));
880 gtk_object_sink (GTK_OBJECT (item));
882 if (!group->items) {
883 group->items = g_list_append (group->items, item);
884 group->last = group->items;
885 } else {
886 group->last = g_list_append (group->last, item)->next;
887 }
889 sp_canvas_item_request_update (item);
890 }
892 /**
893 * Removes an item from a canvas group
894 */
895 static void
896 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
897 {
898 g_return_if_fail (group != NULL);
899 g_return_if_fail (SP_IS_CANVAS_GROUP (group));
900 g_return_if_fail (item != NULL);
902 for (GList *children = group->items; children; children = children->next) {
903 if (children->data == item) {
905 /* Unparent the child */
907 item->parent = NULL;
908 gtk_object_unref (GTK_OBJECT (item));
910 /* Remove it from the list */
912 if (children == group->last) group->last = children->prev;
914 group->items = g_list_remove_link (group->items, children);
915 g_list_free (children);
916 break;
917 }
918 }
919 }
921 /* SPCanvas */
923 static void sp_canvas_class_init (SPCanvasClass *klass);
924 static void sp_canvas_init (SPCanvas *canvas);
925 static void sp_canvas_destroy (GtkObject *object);
927 static void sp_canvas_realize (GtkWidget *widget);
928 static void sp_canvas_unrealize (GtkWidget *widget);
930 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
931 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
933 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
934 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
935 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
936 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
937 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
938 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
939 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
940 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
941 static gboolean sp_canvas_query_tooltip (GtkWidget *widget, gint x, gint y, gboolean keyboard_mode, GtkTooltip *tooltip);
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 canvas->context_snap_delay_active = false;
1055 g_signal_connect(&(canvas->widget), "query-tooltip", G_CALLBACK (sp_canvas_query_tooltip), NULL);
1056 }
1058 /**
1059 * Convenience function to remove the idle handler of a canvas.
1060 */
1061 static void
1062 remove_idle (SPCanvas *canvas)
1063 {
1064 if (canvas->idle_id) {
1065 gtk_idle_remove (canvas->idle_id);
1066 canvas->idle_id = 0;
1067 }
1068 }
1070 /*
1071 * Removes the transient state of the canvas (idle handler, grabs).
1072 */
1073 static void
1074 shutdown_transients (SPCanvas *canvas)
1075 {
1076 /* We turn off the need_redraw flag, since if the canvas is mapped again
1077 * it will request a redraw anyways. We do not turn off the need_update
1078 * flag, though, because updates are not queued when the canvas remaps
1079 * itself.
1080 */
1081 if (canvas->need_redraw) {
1082 canvas->need_redraw = FALSE;
1083 }
1084 if ( canvas->tiles ) g_free(canvas->tiles);
1085 canvas->tiles=NULL;
1086 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1087 canvas->tileH=canvas->tileV=0;
1089 if (canvas->grabbed_item) {
1090 canvas->grabbed_item = NULL;
1091 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1092 }
1094 remove_idle (canvas);
1095 }
1097 /**
1098 * Destroy handler for SPCanvas.
1099 */
1100 static void
1101 sp_canvas_destroy (GtkObject *object)
1102 {
1103 SPCanvas *canvas = SP_CANVAS (object);
1105 if (canvas->root) {
1106 gtk_object_unref (GTK_OBJECT (canvas->root));
1107 canvas->root = NULL;
1108 }
1110 shutdown_transients (canvas);
1112 if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1113 (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1114 }
1116 static void track_latency(GdkEvent const *event) {
1117 GdkEventLatencyTracker &tracker = GdkEventLatencyTracker::default_tracker();
1118 boost::optional<double> latency = tracker.process(event);
1119 if (latency && *latency > 2.0) {
1120 //g_warning("Event latency reached %f sec (%1.4f)", *latency, tracker.getSkew());
1121 }
1122 }
1124 /**
1125 * Returns new canvas as widget.
1126 */
1127 GtkWidget *
1128 sp_canvas_new_aa (void)
1129 {
1130 SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1132 return (GtkWidget *) canvas;
1133 }
1135 /**
1136 * The canvas widget's realize callback.
1137 */
1138 static void
1139 sp_canvas_realize (GtkWidget *widget)
1140 {
1141 SPCanvas *canvas = SP_CANVAS (widget);
1143 GdkWindowAttr attributes;
1144 attributes.window_type = GDK_WINDOW_CHILD;
1145 attributes.x = widget->allocation.x;
1146 attributes.y = widget->allocation.y;
1147 attributes.width = widget->allocation.width;
1148 attributes.height = widget->allocation.height;
1149 attributes.wclass = GDK_INPUT_OUTPUT;
1150 attributes.visual = gdk_rgb_get_visual ();
1151 attributes.colormap = gdk_rgb_get_cmap ();
1152 attributes.event_mask = (gtk_widget_get_events (widget) |
1153 GDK_EXPOSURE_MASK |
1154 GDK_BUTTON_PRESS_MASK |
1155 GDK_BUTTON_RELEASE_MASK |
1156 GDK_POINTER_MOTION_MASK |
1157 ( HAS_BROKEN_MOTION_HINTS ?
1158 0 : GDK_POINTER_MOTION_HINT_MASK ) |
1159 GDK_PROXIMITY_IN_MASK |
1160 GDK_PROXIMITY_OUT_MASK |
1161 GDK_KEY_PRESS_MASK |
1162 GDK_KEY_RELEASE_MASK |
1163 GDK_ENTER_NOTIFY_MASK |
1164 GDK_LEAVE_NOTIFY_MASK |
1165 GDK_FOCUS_CHANGE_MASK);
1166 gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1168 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1169 gdk_window_set_user_data (widget->window, widget);
1171 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1172 if ( prefs->getBool("/options/useextinput/value", true) )
1173 gtk_widget_set_events(widget, attributes.event_mask);
1175 widget->style = gtk_style_attach (widget->style, widget->window);
1177 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1179 canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1180 }
1182 /**
1183 * The canvas widget's unrealize callback.
1184 */
1185 static void
1186 sp_canvas_unrealize (GtkWidget *widget)
1187 {
1188 SPCanvas *canvas = SP_CANVAS (widget);
1190 canvas->current_item = NULL;
1191 canvas->grabbed_item = NULL;
1192 canvas->focused_item = NULL;
1194 shutdown_transients (canvas);
1196 gdk_gc_destroy (canvas->pixmap_gc);
1197 canvas->pixmap_gc = NULL;
1199 if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1200 (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1201 }
1203 /**
1204 * The canvas widget's size_request callback.
1205 */
1206 static void
1207 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1208 {
1209 static_cast<void>(SP_CANVAS (widget));
1211 req->width = 256;
1212 req->height = 256;
1213 }
1215 /**
1216 * The canvas widget's size_allocate callback.
1217 */
1218 static void
1219 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1220 {
1221 SPCanvas *canvas = SP_CANVAS (widget);
1223 /* Schedule redraw of new region */
1224 sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1225 if (allocation->width > widget->allocation.width) {
1226 sp_canvas_request_redraw (canvas,
1227 canvas->x0 + widget->allocation.width,
1228 0,
1229 canvas->x0 + allocation->width,
1230 canvas->y0 + allocation->height);
1231 }
1232 if (allocation->height > widget->allocation.height) {
1233 sp_canvas_request_redraw (canvas,
1234 0,
1235 canvas->y0 + widget->allocation.height,
1236 canvas->x0 + allocation->width,
1237 canvas->y0 + allocation->height);
1238 }
1240 widget->allocation = *allocation;
1242 if (GTK_WIDGET_REALIZED (widget)) {
1243 gdk_window_move_resize (widget->window,
1244 widget->allocation.x, widget->allocation.y,
1245 widget->allocation.width, widget->allocation.height);
1246 }
1247 }
1249 /**
1250 * Helper that emits an event for an item in the canvas, be it the current
1251 * item, grabbed item, or focused item, as appropriate.
1252 */
1253 static int
1254 emit_event (SPCanvas *canvas, GdkEvent *event)
1255 {
1256 guint mask;
1258 if (canvas->grabbed_item) {
1259 switch (event->type) {
1260 case GDK_ENTER_NOTIFY:
1261 mask = GDK_ENTER_NOTIFY_MASK;
1262 break;
1263 case GDK_LEAVE_NOTIFY:
1264 mask = GDK_LEAVE_NOTIFY_MASK;
1265 break;
1266 case GDK_MOTION_NOTIFY:
1267 mask = GDK_POINTER_MOTION_MASK;
1268 break;
1269 case GDK_BUTTON_PRESS:
1270 case GDK_2BUTTON_PRESS:
1271 case GDK_3BUTTON_PRESS:
1272 mask = GDK_BUTTON_PRESS_MASK;
1273 break;
1274 case GDK_BUTTON_RELEASE:
1275 mask = GDK_BUTTON_RELEASE_MASK;
1276 break;
1277 case GDK_KEY_PRESS:
1278 mask = GDK_KEY_PRESS_MASK;
1279 break;
1280 case GDK_KEY_RELEASE:
1281 mask = GDK_KEY_RELEASE_MASK;
1282 break;
1283 case GDK_SCROLL:
1284 mask = GDK_SCROLL;
1285 break;
1286 default:
1287 mask = 0;
1288 break;
1289 }
1291 if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1292 }
1294 /* Convert to world coordinates -- we have two cases because of diferent
1295 * offsets of the fields in the event structures.
1296 */
1298 GdkEvent ev = *event;
1300 switch (ev.type) {
1301 case GDK_ENTER_NOTIFY:
1302 case GDK_LEAVE_NOTIFY:
1303 ev.crossing.x += canvas->x0;
1304 ev.crossing.y += canvas->y0;
1305 break;
1306 case GDK_MOTION_NOTIFY:
1307 case GDK_BUTTON_PRESS:
1308 case GDK_2BUTTON_PRESS:
1309 case GDK_3BUTTON_PRESS:
1310 case GDK_BUTTON_RELEASE:
1311 ev.motion.x += canvas->x0;
1312 ev.motion.y += canvas->y0;
1313 break;
1314 default:
1315 break;
1316 }
1318 /* Choose where we send the event */
1320 /* canvas->current_item becomes NULL in some cases under Win32
1321 ** (e.g. if the pointer leaves the window). So this is a hack that
1322 ** Lauris applied to SP to get around the problem.
1323 */
1324 SPCanvasItem* item = NULL;
1325 if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1326 item = canvas->grabbed_item;
1327 } else {
1328 item = canvas->current_item;
1329 }
1331 if (canvas->focused_item &&
1332 ((event->type == GDK_KEY_PRESS) ||
1333 (event->type == GDK_KEY_RELEASE) ||
1334 (event->type == GDK_FOCUS_CHANGE))) {
1335 item = canvas->focused_item;
1336 }
1338 /* The event is propagated up the hierarchy (for if someone connected to
1339 * a group instead of a leaf event), and emission is stopped if a
1340 * handler returns TRUE, just like for GtkWidget events.
1341 */
1343 gint finished = FALSE;
1345 while (item && !finished) {
1346 gtk_object_ref (GTK_OBJECT (item));
1347 gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1348 SPCanvasItem *parent = item->parent;
1349 gtk_object_unref (GTK_OBJECT (item));
1350 item = parent;
1351 }
1353 return finished;
1354 }
1356 /**
1357 * Helper that re-picks the current item in the canvas, based on the event's
1358 * coordinates and emits enter/leave events for items as appropriate.
1359 */
1360 static int
1361 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1362 {
1363 int button_down = 0;
1364 double x, y;
1366 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1367 return FALSE;
1369 int retval = FALSE;
1371 if (canvas->gen_all_enter_events == false) {
1372 // If a button is down, we'll perform enter and leave events on the
1373 // current item, but not enter on any other item. This is more or
1374 // less like X pointer grabbing for canvas items.
1375 //
1376 button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1377 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1379 if (!button_down) canvas->left_grabbed_item = FALSE;
1380 }
1382 /* Save the event in the canvas. This is used to synthesize enter and
1383 * leave events in case the current item changes. It is also used to
1384 * re-pick the current item if the current one gets deleted. Also,
1385 * synthesize an enter event.
1386 */
1387 if (event != &canvas->pick_event) {
1388 if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1389 /* these fields have the same offsets in both types of events */
1391 canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY;
1392 canvas->pick_event.crossing.window = event->motion.window;
1393 canvas->pick_event.crossing.send_event = event->motion.send_event;
1394 canvas->pick_event.crossing.subwindow = NULL;
1395 canvas->pick_event.crossing.x = event->motion.x;
1396 canvas->pick_event.crossing.y = event->motion.y;
1397 canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL;
1398 canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR;
1399 canvas->pick_event.crossing.focus = FALSE;
1400 canvas->pick_event.crossing.state = event->motion.state;
1402 /* these fields don't have the same offsets in both types of events */
1404 if (event->type == GDK_MOTION_NOTIFY) {
1405 canvas->pick_event.crossing.x_root = event->motion.x_root;
1406 canvas->pick_event.crossing.y_root = event->motion.y_root;
1407 } else {
1408 canvas->pick_event.crossing.x_root = event->button.x_root;
1409 canvas->pick_event.crossing.y_root = event->button.y_root;
1410 }
1411 } else {
1412 canvas->pick_event = *event;
1413 }
1414 }
1416 /* Don't do anything else if this is a recursive call */
1417 if (canvas->in_repick) return retval;
1419 /* LeaveNotify means that there is no current item, so we don't look for one */
1420 if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1421 /* these fields don't have the same offsets in both types of events */
1423 if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1424 x = canvas->pick_event.crossing.x;
1425 y = canvas->pick_event.crossing.y;
1426 } else {
1427 x = canvas->pick_event.motion.x;
1428 y = canvas->pick_event.motion.y;
1429 }
1431 /* world coords */
1432 x += canvas->x0;
1433 y += canvas->y0;
1435 /* find the closest item */
1436 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1437 sp_canvas_item_invoke_point (canvas->root, Geom::Point(x, y), &canvas->new_current_item);
1438 } else {
1439 canvas->new_current_item = NULL;
1440 }
1441 } else {
1442 canvas->new_current_item = NULL;
1443 }
1445 if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1446 return retval; /* current item did not change */
1447 }
1449 /* Synthesize events for old and new current items */
1451 if ((canvas->new_current_item != canvas->current_item)
1452 && (canvas->current_item != NULL)
1453 && !canvas->left_grabbed_item) {
1454 GdkEvent new_event;
1455 SPCanvasItem *item;
1457 item = canvas->current_item;
1459 new_event = canvas->pick_event;
1460 new_event.type = GDK_LEAVE_NOTIFY;
1462 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1463 new_event.crossing.subwindow = NULL;
1464 canvas->in_repick = TRUE;
1465 retval = emit_event (canvas, &new_event);
1466 canvas->in_repick = FALSE;
1467 }
1469 if (canvas->gen_all_enter_events == false) {
1470 // new_current_item may have been set to NULL during the call to
1471 // emit_event() above
1472 if ((canvas->new_current_item != canvas->current_item) && button_down) {
1473 canvas->left_grabbed_item = TRUE;
1474 return retval;
1475 }
1476 }
1478 /* Handle the rest of cases */
1480 canvas->left_grabbed_item = FALSE;
1481 canvas->current_item = canvas->new_current_item;
1483 if (canvas->current_item != NULL) {
1484 GdkEvent new_event;
1486 new_event = canvas->pick_event;
1487 new_event.type = GDK_ENTER_NOTIFY;
1488 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1489 new_event.crossing.subwindow = NULL;
1490 retval = emit_event (canvas, &new_event);
1491 }
1493 return retval;
1494 }
1496 /**
1497 * Button event handler for the canvas.
1498 */
1499 static gint
1500 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1501 {
1502 SPCanvas *canvas = SP_CANVAS (widget);
1503 SPDesktop *dt = SP_ACTIVE_DESKTOP;
1505 int retval = FALSE;
1507 /* dispatch normally regardless of the event's window if an item has
1508 has a pointer grab in effect */
1509 if (!canvas->grabbed_item &&
1510 event->window != SP_CANVAS_WINDOW (canvas))
1511 return retval;
1513 int mask;
1514 switch (event->button) {
1515 case 1:
1516 mask = GDK_BUTTON1_MASK;
1517 break;
1518 case 2:
1519 mask = GDK_BUTTON2_MASK;
1520 break;
1521 case 3:
1522 mask = GDK_BUTTON3_MASK;
1523 break;
1524 case 4:
1525 mask = GDK_BUTTON4_MASK;
1526 break;
1527 case 5:
1528 mask = GDK_BUTTON5_MASK;
1529 break;
1530 default:
1531 mask = 0;
1532 }
1534 switch (event->type) {
1535 case GDK_BUTTON_PRESS:
1536 case GDK_2BUTTON_PRESS:
1537 case GDK_3BUTTON_PRESS:
1538 if (dt) {
1539 // Snapping will be on hold if we're moving the mouse at high speeds. When starting
1540 // drawing a new shape we really should snap though.
1541 dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false);
1542 }
1544 /* Pick the current item as if the button were not pressed, and
1545 * then process the event.
1546 */
1547 canvas->state = event->state;
1548 pick_current_item (canvas, (GdkEvent *) event);
1549 canvas->state ^= mask;
1550 retval = emit_event (canvas, (GdkEvent *) event);
1551 break;
1553 case GDK_BUTTON_RELEASE:
1554 sp_canvas_snap_watchdog_callback(canvas); // If we have any pending snapping action, then invoke it now
1556 /* Process the event as if the button were pressed, then repick
1557 * after the button has been released
1558 */
1559 canvas->state = event->state;
1560 retval = emit_event (canvas, (GdkEvent *) event);
1561 event->state ^= mask;
1562 canvas->state = event->state;
1563 pick_current_item (canvas, (GdkEvent *) event);
1564 event->state ^= mask;
1566 break;
1568 default:
1569 g_assert_not_reached ();
1570 }
1572 return retval;
1573 }
1575 /**
1576 * Scroll event handler for the canvas.
1577 *
1578 * \todo FIXME: generate motion events to re-select items.
1579 */
1580 static gint
1581 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1582 {
1583 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1584 }
1586 static inline void request_motions(GdkWindow *w, GdkEventMotion *event) {
1587 gdk_window_get_pointer(w, NULL, NULL, NULL);
1588 #if HAS_GDK_EVENT_REQUEST_MOTIONS
1589 gdk_event_request_motions(event);
1590 #endif
1591 }
1593 /**
1594 * Motion event handler for the canvas.
1595 */
1596 static int
1597 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1598 {
1599 static guint32 prev_time;
1600 static boost::optional<Geom::Point> prev_pos;
1602 int status;
1603 SPCanvas *canvas = SP_CANVAS (widget);
1605 track_latency((GdkEvent *)event);
1607 if (event->window != SP_CANVAS_WINDOW (canvas))
1608 return FALSE;
1610 if (canvas->pixmap_gc == NULL) // canvas being deleted
1611 return FALSE;
1613 SPDesktop *dt = SP_ACTIVE_DESKTOP;
1615 if (canvas->context_snap_delay_active && dt && dt->namedview->snap_manager.snapprefs.getSnapEnabledGlobally()) {
1616 // Snap when speed drops below e.g. 0.02 px/msec, or when no motion events have occurred for some period.
1617 // i.e. snap when we're at stand still. A speed threshold enforces snapping for tablets, which might never
1618 // be fully at stand still and might keep spitting out motion events.
1619 dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(true); // put snapping on hold
1621 Geom::Point event_pos(event->x, event->y);
1622 guint32 event_t = gdk_event_get_time ( (GdkEvent *) event );
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 if (speed > 0.02) { // Jitter threshold, might be needed for tablets
1629 // We're moving fast, so postpone any snapping until the next GDK_MOTION_NOTIFY event. We
1630 // will keep on postponing the snapping as long as the speed is high.
1631 // We must snap at some point in time though, so set a watchdog timer at some time from
1632 // now, just in case there's no future motion event that drops under the speed limit (when
1633 // stopping abruptly)
1634 sp_canvas_snap_watchdog_kill(canvas);
1635 sp_canvas_snap_watchdog_set(canvas, event); // watchdog is reset, i.e. pushed forward in time
1636 // If the watchdog expires before a new motion event is received, we will snap (as explained
1637 // above). This means however that when the timer is too short, we will always snap and that the
1638 // speed threshold is ineffective. In the extreme case the delay is set to zero, and snapping will
1639 // be immediate, as it used to be in the old days ;-).
1640 } else { // Speed is very low, so we're virtually at stand still
1641 // But if we're really standing still, then we should snap now. We could use some low-pass filtering,
1642 // otherwise snapping occurs for each jitter movement. For this filtering we'll leave the watchdog to expire,
1643 // snap, and set a new watchdog again.
1644 if (canvas->watchdog_id == 0) { // no watchdog has been set
1645 // it might have already expired, so we'll set a new one; the snapping frequency will be limited by this
1646 sp_canvas_snap_watchdog_set(canvas, event);
1647 } // else: watchdog has been set before and we'll wait for it to expire
1648 }
1649 } else {
1650 // This is the first GDK_MOTION_NOTIFY event, so postpone snapping and set the watchdog
1651 sp_canvas_snap_watchdog_set(canvas, event);
1652 }
1654 prev_pos = event_pos;
1655 prev_time = event_t;
1656 }
1658 canvas->state = event->state;
1659 pick_current_item (canvas, (GdkEvent *) event);
1660 status = emit_event (canvas, (GdkEvent *) event);
1661 if (event->is_hint) {
1662 request_motions(widget->window, event);
1663 }
1665 return status;
1666 }
1668 gboolean sp_canvas_snap_watchdog_callback(gpointer data)
1669 {
1670 // Snap NOW! For this the "postponed" flag will be reset and an the last motion event will be repeated
1671 SPCanvas *canvas = reinterpret_cast<SPCanvas *>(data);
1672 if (!canvas->watchdog_event) {
1673 // This might occur when this method is called directly, i.e. not through the timer
1674 return FALSE;
1675 }
1677 SPDesktop *dt = SP_ACTIVE_DESKTOP;
1678 if (dt) {
1679 dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false);
1680 }
1682 emit_event(canvas, canvas->watchdog_event);
1683 gdk_event_free(canvas->watchdog_event);
1684 canvas->watchdog_event = NULL;
1685 canvas->watchdog_id = 0;
1687 return FALSE;
1688 }
1690 void sp_canvas_snap_watchdog_set(SPCanvas *canvas, GdkEventMotion *event)
1691 {
1692 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1693 double value = prefs->getDoubleLimited("/options/snapdelay/value", 0, 0, 1000);
1694 g_assert(canvas->watchdog_id == 0);
1695 canvas->watchdog_id = g_timeout_add(value, &sp_canvas_snap_watchdog_callback, canvas);
1696 g_assert(canvas->watchdog_event == NULL);
1697 canvas->watchdog_event = gdk_event_copy( (GdkEvent *) event);
1698 }
1700 void sp_canvas_snap_watchdog_kill(SPCanvas *canvas)
1701 {
1702 if (canvas->watchdog_id) {
1703 g_source_remove(canvas->watchdog_id); // Kill the watchdog
1704 canvas->watchdog_id = 0;
1705 }
1707 if (canvas->watchdog_event) {
1708 gdk_event_free(canvas->watchdog_event);
1709 canvas->watchdog_event = NULL;
1710 }
1711 }
1713 void sp_canvas_set_snap_delay_active(SPCanvas *canvas, bool snapping)
1714 {
1715 // Only when canvas->context_snap_delay_active has been set, Inkscape will know that snapping is active
1716 // and will delay any snapping events (but only when asked to through the preferences)
1718 // When snapping is being delayed, then that will also mean that at some point the last event
1719 // might be re-triggered. This should only occur when Inkscape is still in the same tool or context,
1720 // and even more specifically, the tool should even be in the same state. If for example snapping is being delayed while
1721 // creating a rectangle, then the rect-context will be active and it will be in the "dragging" state
1722 // (see the static boolean variable "dragging" in the sp_rect_context_root_handler). The procedure is
1723 // as follows: call sp_canvas_set_snap_delay_active(*, TRUE) when entering the "dragging" state, which will delay
1724 // snapping from that moment on, and call sp_canvas_set_snap_delay_active(*, FALSE) when leaving the "dragging"
1725 // state. This last call will also make sure that any pending snap events will be canceled.
1727 if (!canvas) {
1728 g_warning("sp_canvas_set_snap_delay_active() has been called without providing a canvas!");
1729 return;
1730 }
1732 if (canvas->context_snap_delay_active == snapping) {
1733 g_warning("Snapping was already allowed or disallowed! This is a bug, please report it.");
1734 }
1736 canvas->context_snap_delay_active = snapping;
1738 if (snapping == false) {
1739 sp_canvas_snap_watchdog_kill(canvas); // kill any pending snapping events
1740 }
1741 }
1743 static void
1744 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)
1745 {
1746 GtkWidget *widget = GTK_WIDGET (canvas);
1748 SPCanvasBuf buf;
1749 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1750 buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1751 } else {
1752 buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1753 }
1755 // Mark the region clean
1756 sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1758 buf.buf_rowstride = sw * 4;
1759 buf.rect.x0 = x0;
1760 buf.rect.y0 = y0;
1761 buf.rect.x1 = x1;
1762 buf.rect.y1 = y1;
1763 buf.visible_rect.x0 = draw_x1;
1764 buf.visible_rect.y0 = draw_y1;
1765 buf.visible_rect.x1 = draw_x2;
1766 buf.visible_rect.y1 = draw_y2;
1767 GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1768 buf.bg_color = (((color->red & 0xff00) << 8)
1769 | (color->green & 0xff00)
1770 | (color->blue >> 8));
1771 buf.is_empty = true;
1773 buf.ct = nr_create_cairo_context_canvasbuf (&(buf.visible_rect), &buf);
1775 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1776 SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1777 }
1779 #if ENABLE_LCMS
1780 cmsHTRANSFORM transf = 0;
1781 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1782 bool fromDisplay = prefs->getBool( "/options/displayprofile/from_display");
1783 if ( fromDisplay ) {
1784 transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" );
1785 } else {
1786 transf = Inkscape::colorprofile_get_display_transform();
1787 }
1788 #endif // ENABLE_LCMS
1790 if (buf.is_empty) {
1791 #if ENABLE_LCMS
1792 if ( transf && canvas->enable_cms_display_adj ) {
1793 cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1794 }
1795 #endif // ENABLE_LCMS
1796 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1797 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1798 canvas->pixmap_gc,
1799 TRUE,
1800 x0 - canvas->x0, y0 - canvas->y0,
1801 x1 - x0, y1 - y0);
1802 } else {
1804 #if ENABLE_LCMS
1805 if ( transf && canvas->enable_cms_display_adj ) {
1806 for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1807 guchar* p = buf.buf + (buf.buf_rowstride * yy);
1808 cmsDoTransform( transf, p, p, (x1 - x0) );
1809 }
1810 }
1811 #endif // ENABLE_LCMS
1813 // Now we only need to output the prepared pixmap to the actual screen, and this define chooses one
1814 // of the two ways to do it. The cairo way is direct and straightforward, but unfortunately
1815 // noticeably slower. I asked Carl Worth but he was unable so far to suggest any specific reason
1816 // for this slowness. So, for now we use the oldish method: squeeze out 32bpp buffer to 24bpp and
1817 // use gdk_draw_rgb_image_dithalign, for unfortunately gdk can only handle 24 bpp, which cairo
1818 // cannot handle at all. Still, this way is currently faster even despite the blit with squeeze.
1820 ///#define CANVAS_OUTPUT_VIA_CAIRO
1822 #ifdef CANVAS_OUTPUT_VIA_CAIRO
1824 buf.cst = cairo_image_surface_create_for_data (
1825 buf.buf,
1826 CAIRO_FORMAT_ARGB32, // unpacked, i.e. 32 bits! one byte is unused
1827 x1 - x0, y1 - y0,
1828 buf.buf_rowstride
1829 );
1830 cairo_t *window_ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1831 cairo_set_source_surface (window_ct, buf.cst, x0 - canvas->x0, y0 - canvas->y0);
1832 cairo_paint (window_ct);
1833 cairo_destroy (window_ct);
1834 cairo_surface_finish (buf.cst);
1835 cairo_surface_destroy (buf.cst);
1837 #else
1839 NRPixBlock b3;
1840 nr_pixblock_setup_fast (&b3, NR_PIXBLOCK_MODE_R8G8B8, x0, y0, x1, y1, TRUE);
1842 NRPixBlock b4;
1843 nr_pixblock_setup_extern (&b4, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1,
1844 buf.buf,
1845 buf.buf_rowstride,
1846 FALSE, FALSE);
1848 // this does the 32->24 squishing, using an assembler routine:
1849 nr_blit_pixblock_pixblock (&b3, &b4);
1851 gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1852 canvas->pixmap_gc,
1853 x0 - canvas->x0, y0 - canvas->y0,
1854 x1 - x0, y1 - y0,
1855 GDK_RGB_DITHER_MAX,
1856 NR_PIXBLOCK_PX(&b3),
1857 sw * 3,
1858 x0 - canvas->x0, y0 - canvas->y0);
1860 nr_pixblock_release (&b3);
1861 nr_pixblock_release (&b4);
1862 #endif
1863 }
1865 cairo_surface_t *cst = cairo_get_target(buf.ct);
1866 cairo_destroy (buf.ct);
1867 cairo_surface_finish (cst);
1868 cairo_surface_destroy (cst);
1870 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1871 nr_pixelstore_256K_free (buf.buf);
1872 } else {
1873 nr_pixelstore_1M_free (buf.buf);
1874 }
1875 }
1877 struct PaintRectSetup {
1878 SPCanvas* canvas;
1879 NRRectL big_rect;
1880 GTimeVal start_time;
1881 int max_pixels;
1882 Geom::Point mouse_loc;
1883 };
1885 /**
1886 * Paint the given rect, recursively subdividing the region until it is the size of a single
1887 * buffer.
1888 *
1889 * @return true if the drawing completes
1890 */
1891 static int
1892 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1893 {
1894 GTimeVal now;
1895 g_get_current_time (&now);
1897 glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1898 + (now.tv_usec - setup->start_time.tv_usec);
1900 // Allow only very fast buffers to be run together;
1901 // as soon as the total redraw time exceeds 1ms, cancel;
1902 // this returns control to the idle loop and allows Inkscape to process user input
1903 // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1904 // it will get back and finish painting what remains to paint.
1905 if (elapsed > 1000) {
1907 // Interrupting redraw isn't always good.
1908 // For example, when you drag one node of a big path, only the buffer containing
1909 // the mouse cursor will be redrawn again and again, and the rest of the path
1910 // will remain stale because Inkscape never has enough idle time to redraw all
1911 // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1912 // If this limit is set, and if we have aborted redraw more times than is allowed,
1913 // interrupting is blocked and we're forced to redraw full screen once
1914 // (after which we can again interrupt forced_redraw_limit times).
1915 if (setup->canvas->forced_redraw_limit < 0 ||
1916 setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1918 if (setup->canvas->forced_redraw_limit != -1) {
1919 setup->canvas->forced_redraw_count++;
1920 }
1922 return false;
1923 }
1924 }
1926 // Find the optimal buffer dimensions
1927 int bw = this_rect.x1 - this_rect.x0;
1928 int bh = this_rect.y1 - this_rect.y0;
1929 if ((bw < 1) || (bh < 1))
1930 return 0;
1932 if (bw * bh < setup->max_pixels) {
1933 // We are small enough
1934 sp_canvas_paint_single_buffer (setup->canvas,
1935 this_rect.x0, this_rect.y0,
1936 this_rect.x1, this_rect.y1,
1937 setup->big_rect.x0, setup->big_rect.y0,
1938 setup->big_rect.x1, setup->big_rect.y1, bw);
1939 return 1;
1940 }
1942 NRRectL lo = this_rect;
1943 NRRectL hi = this_rect;
1945 /*
1946 This test determines the redraw strategy:
1948 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1949 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1950 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1951 and seems to be faster for drawings with many smaller objects at zoom-out.
1953 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1954 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1955 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1956 faster.
1958 The default for now is the strips mode.
1959 */
1960 if (bw < bh || bh < 2 * TILE_SIZE) {
1961 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1962 int mid = ((long long) this_rect.x0 + (long long) this_rect.x1) / 2;
1963 // Make sure that mid lies on a tile boundary
1964 mid = (mid / TILE_SIZE) * TILE_SIZE;
1966 lo.x1 = mid;
1967 hi.x0 = mid;
1969 if (setup->mouse_loc[Geom::X] < mid) {
1970 // Always paint towards the mouse first
1971 return sp_canvas_paint_rect_internal(setup, lo)
1972 && sp_canvas_paint_rect_internal(setup, hi);
1973 } else {
1974 return sp_canvas_paint_rect_internal(setup, hi)
1975 && sp_canvas_paint_rect_internal(setup, lo);
1976 }
1977 } else {
1978 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1979 int mid = ((long long) this_rect.y0 + (long long) this_rect.y1) / 2;
1980 // Make sure that mid lies on a tile boundary
1981 mid = (mid / TILE_SIZE) * TILE_SIZE;
1983 lo.y1 = mid;
1984 hi.y0 = mid;
1986 if (setup->mouse_loc[Geom::Y] < mid) {
1987 // Always paint towards the mouse first
1988 return sp_canvas_paint_rect_internal(setup, lo)
1989 && sp_canvas_paint_rect_internal(setup, hi);
1990 } else {
1991 return sp_canvas_paint_rect_internal(setup, hi)
1992 && sp_canvas_paint_rect_internal(setup, lo);
1993 }
1994 }
1995 }
1998 /**
1999 * Helper that draws a specific rectangular part of the canvas.
2000 *
2001 * @return true if the rectangle painting succeeds.
2002 */
2003 static bool
2004 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
2005 {
2006 g_return_val_if_fail (!canvas->need_update, false);
2008 NRRectL rect;
2009 rect.x0 = xx0;
2010 rect.x1 = xx1;
2011 rect.y0 = yy0;
2012 rect.y1 = yy1;
2014 // Clip rect-to-draw by the current visible area
2015 rect.x0 = MAX (rect.x0, canvas->x0);
2016 rect.y0 = MAX (rect.y0, canvas->y0);
2017 rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
2018 rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
2020 #ifdef DEBUG_REDRAW
2021 // paint the area to redraw yellow
2022 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
2023 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
2024 canvas->pixmap_gc,
2025 TRUE,
2026 rect.x0 - canvas->x0, rect.y0 - canvas->y0,
2027 rect.x1 - rect.x0, rect.y1 - rect.y0);
2028 #endif
2030 PaintRectSetup setup;
2032 setup.canvas = canvas;
2033 setup.big_rect = rect;
2035 // Save the mouse location
2036 gint x, y;
2037 gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
2038 setup.mouse_loc = sp_canvas_window_to_world (canvas, Geom::Point(x,y));
2040 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
2041 // use 256K as a compromise to not slow down gradients
2042 // 256K is the cached buffer and we need 4 channels
2043 setup.max_pixels = 65536; // 256K/4
2044 } else {
2045 // paths only, so 1M works faster
2046 // 1M is the cached buffer and we need 4 channels
2047 setup.max_pixels = 262144;
2048 }
2050 // Start the clock
2051 g_get_current_time(&(setup.start_time));
2053 // Go
2054 return sp_canvas_paint_rect_internal(&setup, rect);
2055 }
2057 /**
2058 * Force a full redraw after a specified number of interrupted redraws
2059 */
2060 void
2061 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
2062 g_return_if_fail(canvas != NULL);
2064 canvas->forced_redraw_limit = count;
2065 canvas->forced_redraw_count = 0;
2066 }
2068 /**
2069 * End forced full redraw requests
2070 */
2071 void
2072 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
2073 g_return_if_fail(canvas != NULL);
2075 canvas->forced_redraw_limit = -1;
2076 }
2078 /**
2079 * The canvas widget's expose callback.
2080 */
2081 static gint
2082 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
2083 {
2084 SPCanvas *canvas = SP_CANVAS (widget);
2086 if (!GTK_WIDGET_DRAWABLE (widget) ||
2087 (event->window != SP_CANVAS_WINDOW (canvas)))
2088 return FALSE;
2090 int n_rects;
2091 GdkRectangle *rects;
2092 gdk_region_get_rectangles (event->region, &rects, &n_rects);
2094 for (int i = 0; i < n_rects; i++) {
2095 NRRectL rect;
2097 rect.x0 = rects[i].x + canvas->x0;
2098 rect.y0 = rects[i].y + canvas->y0;
2099 rect.x1 = rect.x0 + rects[i].width;
2100 rect.y1 = rect.y0 + rects[i].height;
2102 sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
2103 }
2105 if (n_rects > 0)
2106 g_free (rects);
2108 return FALSE;
2109 }
2111 /**
2112 * The canvas widget's keypress callback.
2113 */
2114 static gint
2115 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
2116 {
2117 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
2118 }
2120 /**
2121 * Crossing event handler for the canvas.
2122 */
2123 static gint
2124 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
2125 {
2126 SPCanvas *canvas = SP_CANVAS (widget);
2128 if (event->window != SP_CANVAS_WINDOW (canvas))
2129 return FALSE;
2131 canvas->state = event->state;
2132 return pick_current_item (canvas, (GdkEvent *) event);
2133 }
2135 /**
2136 * Focus in handler for the canvas.
2137 */
2138 static gint
2139 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
2140 {
2141 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
2143 SPCanvas *canvas = SP_CANVAS (widget);
2145 if (canvas->focused_item) {
2146 return emit_event (canvas, (GdkEvent *) event);
2147 } else {
2148 return FALSE;
2149 }
2150 }
2152 /**
2153 * Focus out handler for the canvas.
2154 */
2155 static gint
2156 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
2157 {
2158 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2160 SPCanvas *canvas = SP_CANVAS (widget);
2162 if (canvas->focused_item)
2163 return emit_event (canvas, (GdkEvent *) event);
2164 else
2165 return FALSE;
2166 }
2169 static gboolean sp_canvas_query_tooltip (GtkWidget *widget,
2170 gint x,
2171 gint y,
2172 gboolean keyboard_mode,
2173 GtkTooltip *tooltip)
2174 {
2175 // We're not really doing anything special here, so we might just as well remove sp_canvas_query_tooltip
2176 // all together (and stop listening to the query_tooltip signal. We might make a custom tooltip however
2177 // someday, for example to display icons instead of just plain text. In that case we will need this call
2178 // so that's why I'm leaving it here for the time being.
2180 if (canvas_parent_class->query_tooltip) {
2181 return canvas_parent_class->query_tooltip (widget, x, y, keyboard_mode, tooltip);
2182 }
2184 return false;
2185 }
2188 /**
2189 * Helper that repaints the areas in the canvas that need it.
2190 *
2191 * @return true if all the dirty parts have been redrawn
2192 */
2193 static int
2194 paint (SPCanvas *canvas)
2195 {
2196 if (canvas->need_update) {
2197 sp_canvas_item_invoke_update (canvas->root, Geom::identity(), 0);
2198 canvas->need_update = FALSE;
2199 }
2201 if (!canvas->need_redraw)
2202 return TRUE;
2204 Gdk::Region to_paint;
2206 for (int j=canvas->tTop; j<canvas->tBottom; j++) {
2207 for (int i=canvas->tLeft; i<canvas->tRight; i++) {
2208 int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
2210 if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
2211 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
2212 TILE_SIZE, TILE_SIZE));
2213 }
2215 }
2216 }
2218 if (!to_paint.empty()) {
2219 Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
2220 typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
2221 for (Iter i=rect.begin(); i != rect.end(); ++i) {
2222 int x0 = (*i).get_x();
2223 int y0 = (*i).get_y();
2224 int x1 = x0 + (*i).get_width();
2225 int y1 = y0 + (*i).get_height();
2226 if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
2227 // Aborted
2228 return FALSE;
2229 };
2230 }
2231 }
2233 canvas->need_redraw = FALSE;
2235 // we've had a full unaborted redraw, reset the full redraw counter
2236 if (canvas->forced_redraw_limit != -1) {
2237 canvas->forced_redraw_count = 0;
2238 }
2240 return TRUE;
2241 }
2243 /**
2244 * Helper that invokes update, paint, and repick on canvas.
2245 */
2246 static int
2247 do_update (SPCanvas *canvas)
2248 {
2249 if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop durring interrupted display!
2250 return TRUE;
2252 /* Cause the update if necessary */
2253 if (canvas->need_update) {
2254 sp_canvas_item_invoke_update (canvas->root, Geom::identity(), 0);
2255 canvas->need_update = FALSE;
2256 }
2258 /* Paint if able to */
2259 if (GTK_WIDGET_DRAWABLE (canvas)) {
2260 return paint (canvas);
2261 }
2263 /* Pick new current item */
2264 while (canvas->need_repick) {
2265 canvas->need_repick = FALSE;
2266 pick_current_item (canvas, &canvas->pick_event);
2267 }
2269 return TRUE;
2270 }
2272 /**
2273 * Idle handler for the canvas that deals with pending updates and redraws.
2274 */
2275 static gint
2276 idle_handler (gpointer data)
2277 {
2278 GDK_THREADS_ENTER ();
2280 SPCanvas *canvas = SP_CANVAS (data);
2282 int const ret = do_update (canvas);
2284 if (ret) {
2285 /* Reset idle id */
2286 canvas->idle_id = 0;
2287 }
2289 GDK_THREADS_LEAVE ();
2291 return !ret;
2292 }
2294 /**
2295 * Convenience function to add an idle handler to a canvas.
2296 */
2297 static void
2298 add_idle (SPCanvas *canvas)
2299 {
2300 if (canvas->idle_id != 0)
2301 return;
2303 canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2304 }
2306 /**
2307 * Returns the root group of the specified canvas.
2308 */
2309 SPCanvasGroup *
2310 sp_canvas_root (SPCanvas *canvas)
2311 {
2312 g_return_val_if_fail (canvas != NULL, NULL);
2313 g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2315 return SP_CANVAS_GROUP (canvas->root);
2316 }
2318 /**
2319 * Scrolls canvas to specific position (cx and cy are measured in screen pixels)
2320 */
2321 void
2322 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2323 {
2324 g_return_if_fail (canvas != NULL);
2325 g_return_if_fail (SP_IS_CANVAS (canvas));
2327 int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels)
2328 int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do!
2329 int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the
2330 int dy = iy - canvas->y0; // canvas w.r.t its previous position
2332 canvas->dx0 = cx; // here the 'd' stands for double, not delta!
2333 canvas->dy0 = cy;
2334 canvas->x0 = ix;
2335 canvas->y0 = iy;
2337 sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2339 if (!clear) {
2340 // scrolling without zoom; redraw only the newly exposed areas
2341 if ((dx != 0) || (dy != 0)) {
2342 canvas->is_scrolling = is_scrolling;
2343 if (GTK_WIDGET_REALIZED (canvas)) {
2344 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2345 }
2346 }
2347 } else {
2348 // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2349 }
2350 }
2352 /**
2353 * Updates canvas if necessary.
2354 */
2355 void
2356 sp_canvas_update_now (SPCanvas *canvas)
2357 {
2358 g_return_if_fail (canvas != NULL);
2359 g_return_if_fail (SP_IS_CANVAS (canvas));
2361 if (!(canvas->need_update ||
2362 canvas->need_redraw))
2363 return;
2365 do_update (canvas);
2366 }
2368 /**
2369 * Update callback for canvas widget.
2370 */
2371 static void
2372 sp_canvas_request_update (SPCanvas *canvas)
2373 {
2374 canvas->need_update = TRUE;
2375 add_idle (canvas);
2376 }
2378 /**
2379 * Forces redraw of rectangular canvas area.
2380 */
2381 void
2382 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2383 {
2384 NRRectL bbox;
2385 NRRectL visible;
2386 NRRectL clip;
2388 g_return_if_fail (canvas != NULL);
2389 g_return_if_fail (SP_IS_CANVAS (canvas));
2391 if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2392 if ((x0 >= x1) || (y0 >= y1)) return;
2394 bbox.x0 = x0;
2395 bbox.y0 = y0;
2396 bbox.x1 = x1;
2397 bbox.y1 = y1;
2399 visible.x0 = canvas->x0;
2400 visible.y0 = canvas->y0;
2401 visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2402 visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2404 nr_rect_l_intersect (&clip, &bbox, &visible);
2406 sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2407 add_idle (canvas);
2408 }
2410 /**
2411 * Sets world coordinates from win and canvas.
2412 */
2413 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2414 {
2415 g_return_if_fail (canvas != NULL);
2416 g_return_if_fail (SP_IS_CANVAS (canvas));
2418 if (worldx) *worldx = canvas->x0 + winx;
2419 if (worldy) *worldy = canvas->y0 + winy;
2420 }
2422 /**
2423 * Sets win coordinates from world and canvas.
2424 */
2425 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2426 {
2427 g_return_if_fail (canvas != NULL);
2428 g_return_if_fail (SP_IS_CANVAS (canvas));
2430 if (winx) *winx = worldx - canvas->x0;
2431 if (winy) *winy = worldy - canvas->y0;
2432 }
2434 /**
2435 * Converts point from win to world coordinates.
2436 */
2437 Geom::Point sp_canvas_window_to_world(SPCanvas const *canvas, Geom::Point const win)
2438 {
2439 g_assert (canvas != NULL);
2440 g_assert (SP_IS_CANVAS (canvas));
2442 return Geom::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2443 }
2445 /**
2446 * Converts point from world to win coordinates.
2447 */
2448 Geom::Point sp_canvas_world_to_window(SPCanvas const *canvas, Geom::Point const world)
2449 {
2450 g_assert (canvas != NULL);
2451 g_assert (SP_IS_CANVAS (canvas));
2453 return Geom::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2454 }
2456 /**
2457 * Returns true if point given in world coordinates is inside window.
2458 */
2459 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, Geom::Point const &world)
2460 {
2461 g_assert( canvas != NULL );
2462 g_assert(SP_IS_CANVAS(canvas));
2464 GtkWidget const &w = *GTK_WIDGET(canvas);
2465 return ( ( canvas->x0 <= world[Geom::X] ) &&
2466 ( canvas->y0 <= world[Geom::Y] ) &&
2467 ( world[Geom::X] < canvas->x0 + w.allocation.width ) &&
2468 ( world[Geom::Y] < canvas->y0 + w.allocation.height ) );
2469 }
2471 /**
2472 * Return canvas window coordinates as Geom::Rect.
2473 */
2474 Geom::Rect SPCanvas::getViewbox() const
2475 {
2476 GtkWidget const *w = GTK_WIDGET(this);
2477 return Geom::Rect(Geom::Point(dx0, dy0),
2478 Geom::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2479 }
2481 /**
2482 * Return canvas window coordinates as IRect (a rectangle defined by integers).
2483 */
2484 NR::IRect SPCanvas::getViewboxIntegers() const
2485 {
2486 GtkWidget const *w = GTK_WIDGET(this);
2487 return NR::IRect(NR::IPoint(x0, y0),
2488 NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height));
2489 }
2491 inline int sp_canvas_tile_floor(int x)
2492 {
2493 return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2494 }
2496 inline int sp_canvas_tile_ceil(int x)
2497 {
2498 return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2499 }
2501 /**
2502 * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2503 */
2504 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2505 {
2506 if ( nl >= nr || nt >= nb ) {
2507 if ( canvas->tiles ) g_free(canvas->tiles);
2508 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2509 canvas->tileH=canvas->tileV=0;
2510 canvas->tiles=NULL;
2511 return;
2512 }
2513 int tl=sp_canvas_tile_floor(nl);
2514 int tt=sp_canvas_tile_floor(nt);
2515 int tr=sp_canvas_tile_ceil(nr);
2516 int tb=sp_canvas_tile_ceil(nb);
2518 int nh = tr-tl, nv = tb-tt;
2519 uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2520 for (int i=tl; i<tr; i++) {
2521 for (int j=tt; j<tb; j++) {
2522 int ind = (i-tl) + (j-tt)*nh;
2523 if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2524 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2525 } else {
2526 ntiles[ind]=0; // newly exposed areas get 0
2527 }
2528 }
2529 }
2530 if ( canvas->tiles ) g_free(canvas->tiles);
2531 canvas->tiles=ntiles;
2532 canvas->tLeft=tl;
2533 canvas->tTop=tt;
2534 canvas->tRight=tr;
2535 canvas->tBottom=tb;
2536 canvas->tileH=nh;
2537 canvas->tileV=nv;
2538 }
2540 /*
2541 * Helper that queues a canvas rectangle for redraw
2542 */
2543 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2544 canvas->need_redraw = TRUE;
2546 sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2547 }
2549 /**
2550 * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2551 */
2552 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2553 {
2554 if ( nl >= nr || nt >= nb ) {
2555 return;
2556 }
2557 int tl=sp_canvas_tile_floor(nl);
2558 int tt=sp_canvas_tile_floor(nt);
2559 int tr=sp_canvas_tile_ceil(nr);
2560 int tb=sp_canvas_tile_ceil(nb);
2561 if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2562 if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2563 if ( tr > canvas->tRight ) tr=canvas->tRight;
2564 if ( tt < canvas->tTop ) tt=canvas->tTop;
2565 if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2567 for (int i=tl; i<tr; i++) {
2568 for (int j=tt; j<tb; j++) {
2569 canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2570 }
2571 }
2572 }
2575 /*
2576 Local Variables:
2577 mode:c++
2578 c-file-style:"stroustrup"
2579 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2580 indent-tabs-mode:nil
2581 fill-column:99
2582 End:
2583 */
2584 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :