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);
942 static GtkWidgetClass *canvas_parent_class;
944 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb);
945 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb);
946 static void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val);
947 static int do_update (SPCanvas *canvas);
949 static gboolean sp_canvas_snap_watchdog_callback(gpointer data);
950 static void sp_canvas_snap_watchdog_set(SPCanvas *canvas, GdkEventMotion *event);
951 static void sp_canvas_snap_watchdog_kill(SPCanvas *canvas);
953 /**
954 * Registers the SPCanvas class if necessary, and returns the type ID
955 * associated to it.
956 *
957 * \return The type ID of the SPCanvas class.
958 **/
959 GType sp_canvas_get_type(void)
960 {
961 static GType type = 0;
962 if (!type) {
963 GTypeInfo info = {
964 sizeof(SPCanvasClass),
965 0, // base_init
966 0, // base_finalize
967 (GClassInitFunc)sp_canvas_class_init,
968 0, // class_finalize
969 0, // class_data
970 sizeof(SPCanvas),
971 0, // n_preallocs
972 (GInstanceInitFunc)sp_canvas_init,
973 0 // value_table
974 };
975 type = g_type_register_static(GTK_TYPE_WIDGET, "SPCanvas", &info, static_cast<GTypeFlags>(0));
976 }
977 return type;
978 }
980 /**
981 * Class initialization function for SPCanvasClass.
982 */
983 static void
984 sp_canvas_class_init (SPCanvasClass *klass)
985 {
986 GtkObjectClass *object_class = (GtkObjectClass *) klass;
987 GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
989 canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
991 object_class->destroy = sp_canvas_destroy;
993 widget_class->realize = sp_canvas_realize;
994 widget_class->unrealize = sp_canvas_unrealize;
995 widget_class->size_request = sp_canvas_size_request;
996 widget_class->size_allocate = sp_canvas_size_allocate;
997 widget_class->button_press_event = sp_canvas_button;
998 widget_class->button_release_event = sp_canvas_button;
999 widget_class->motion_notify_event = sp_canvas_motion;
1000 widget_class->scroll_event = sp_canvas_scroll;
1001 widget_class->expose_event = sp_canvas_expose;
1002 widget_class->key_press_event = sp_canvas_key;
1003 widget_class->key_release_event = sp_canvas_key;
1004 widget_class->enter_notify_event = sp_canvas_crossing;
1005 widget_class->leave_notify_event = sp_canvas_crossing;
1006 widget_class->focus_in_event = sp_canvas_focus_in;
1007 widget_class->focus_out_event = sp_canvas_focus_out;
1008 }
1010 /**
1011 * Callback: object initialization for SPCanvas.
1012 */
1013 static void
1014 sp_canvas_init (SPCanvas *canvas)
1015 {
1016 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
1017 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
1018 GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
1020 canvas->pick_event.type = GDK_LEAVE_NOTIFY;
1021 canvas->pick_event.crossing.x = 0;
1022 canvas->pick_event.crossing.y = 0;
1024 /* Create the root item as a special case */
1025 canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
1026 canvas->root->canvas = canvas;
1028 gtk_object_ref (GTK_OBJECT (canvas->root));
1029 gtk_object_sink (GTK_OBJECT (canvas->root));
1031 canvas->need_repick = TRUE;
1033 // See comment at in sp-canvas.h.
1034 canvas->gen_all_enter_events = false;
1036 canvas->tiles=NULL;
1037 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1038 canvas->tileH=canvas->tileV=0;
1040 canvas->forced_redraw_count = 0;
1041 canvas->forced_redraw_limit = -1;
1043 #if ENABLE_LCMS
1044 canvas->enable_cms_display_adj = false;
1045 canvas->cms_key = new Glib::ustring("");
1046 #endif // ENABLE_LCMS
1048 canvas->is_scrolling = false;
1050 canvas->watchdog_id = 0;
1051 canvas->watchdog_event = NULL;
1052 }
1054 /**
1055 * Convenience function to remove the idle handler of a canvas.
1056 */
1057 static void
1058 remove_idle (SPCanvas *canvas)
1059 {
1060 if (canvas->idle_id) {
1061 gtk_idle_remove (canvas->idle_id);
1062 canvas->idle_id = 0;
1063 }
1064 }
1066 /*
1067 * Removes the transient state of the canvas (idle handler, grabs).
1068 */
1069 static void
1070 shutdown_transients (SPCanvas *canvas)
1071 {
1072 /* We turn off the need_redraw flag, since if the canvas is mapped again
1073 * it will request a redraw anyways. We do not turn off the need_update
1074 * flag, though, because updates are not queued when the canvas remaps
1075 * itself.
1076 */
1077 if (canvas->need_redraw) {
1078 canvas->need_redraw = FALSE;
1079 }
1080 if ( canvas->tiles ) g_free(canvas->tiles);
1081 canvas->tiles=NULL;
1082 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1083 canvas->tileH=canvas->tileV=0;
1085 if (canvas->grabbed_item) {
1086 canvas->grabbed_item = NULL;
1087 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1088 }
1090 remove_idle (canvas);
1091 }
1093 /**
1094 * Destroy handler for SPCanvas.
1095 */
1096 static void
1097 sp_canvas_destroy (GtkObject *object)
1098 {
1099 SPCanvas *canvas = SP_CANVAS (object);
1101 if (canvas->root) {
1102 gtk_object_unref (GTK_OBJECT (canvas->root));
1103 canvas->root = NULL;
1104 }
1106 shutdown_transients (canvas);
1108 if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1109 (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1110 }
1112 static void track_latency(GdkEvent const *event) {
1113 GdkEventLatencyTracker &tracker = GdkEventLatencyTracker::default_tracker();
1114 boost::optional<double> latency = tracker.process(event);
1115 if (latency && *latency > 2.0) {
1116 //g_warning("Event latency reached %f sec (%1.4f)", *latency, tracker.getSkew());
1117 }
1118 }
1120 /**
1121 * Returns new canvas as widget.
1122 */
1123 GtkWidget *
1124 sp_canvas_new_aa (void)
1125 {
1126 SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1128 return (GtkWidget *) canvas;
1129 }
1131 /**
1132 * The canvas widget's realize callback.
1133 */
1134 static void
1135 sp_canvas_realize (GtkWidget *widget)
1136 {
1137 SPCanvas *canvas = SP_CANVAS (widget);
1139 GdkWindowAttr attributes;
1140 attributes.window_type = GDK_WINDOW_CHILD;
1141 attributes.x = widget->allocation.x;
1142 attributes.y = widget->allocation.y;
1143 attributes.width = widget->allocation.width;
1144 attributes.height = widget->allocation.height;
1145 attributes.wclass = GDK_INPUT_OUTPUT;
1146 attributes.visual = gdk_rgb_get_visual ();
1147 attributes.colormap = gdk_rgb_get_cmap ();
1148 attributes.event_mask = (gtk_widget_get_events (widget) |
1149 GDK_EXPOSURE_MASK |
1150 GDK_BUTTON_PRESS_MASK |
1151 GDK_BUTTON_RELEASE_MASK |
1152 GDK_POINTER_MOTION_MASK |
1153 ( HAS_BROKEN_MOTION_HINTS ?
1154 0 : GDK_POINTER_MOTION_HINT_MASK ) |
1155 GDK_PROXIMITY_IN_MASK |
1156 GDK_PROXIMITY_OUT_MASK |
1157 GDK_KEY_PRESS_MASK |
1158 GDK_KEY_RELEASE_MASK |
1159 GDK_ENTER_NOTIFY_MASK |
1160 GDK_LEAVE_NOTIFY_MASK |
1161 GDK_FOCUS_CHANGE_MASK);
1162 gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1164 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1165 gdk_window_set_user_data (widget->window, widget);
1167 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1168 if ( prefs->getBool("/options/useextinput/value", true) )
1169 gtk_widget_set_events(widget, attributes.event_mask);
1171 widget->style = gtk_style_attach (widget->style, widget->window);
1173 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1175 canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1176 }
1178 /**
1179 * The canvas widget's unrealize callback.
1180 */
1181 static void
1182 sp_canvas_unrealize (GtkWidget *widget)
1183 {
1184 SPCanvas *canvas = SP_CANVAS (widget);
1186 canvas->current_item = NULL;
1187 canvas->grabbed_item = NULL;
1188 canvas->focused_item = NULL;
1190 shutdown_transients (canvas);
1192 gdk_gc_destroy (canvas->pixmap_gc);
1193 canvas->pixmap_gc = NULL;
1195 if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1196 (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1197 }
1199 /**
1200 * The canvas widget's size_request callback.
1201 */
1202 static void
1203 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1204 {
1205 static_cast<void>(SP_CANVAS (widget));
1207 req->width = 256;
1208 req->height = 256;
1209 }
1211 /**
1212 * The canvas widget's size_allocate callback.
1213 */
1214 static void
1215 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1216 {
1217 SPCanvas *canvas = SP_CANVAS (widget);
1219 /* Schedule redraw of new region */
1220 sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1221 if (allocation->width > widget->allocation.width) {
1222 sp_canvas_request_redraw (canvas,
1223 canvas->x0 + widget->allocation.width,
1224 0,
1225 canvas->x0 + allocation->width,
1226 canvas->y0 + allocation->height);
1227 }
1228 if (allocation->height > widget->allocation.height) {
1229 sp_canvas_request_redraw (canvas,
1230 0,
1231 canvas->y0 + widget->allocation.height,
1232 canvas->x0 + allocation->width,
1233 canvas->y0 + allocation->height);
1234 }
1236 widget->allocation = *allocation;
1238 if (GTK_WIDGET_REALIZED (widget)) {
1239 gdk_window_move_resize (widget->window,
1240 widget->allocation.x, widget->allocation.y,
1241 widget->allocation.width, widget->allocation.height);
1242 }
1243 }
1245 /**
1246 * Helper that emits an event for an item in the canvas, be it the current
1247 * item, grabbed item, or focused item, as appropriate.
1248 */
1249 static int
1250 emit_event (SPCanvas *canvas, GdkEvent *event)
1251 {
1252 guint mask;
1254 if (canvas->grabbed_item) {
1255 switch (event->type) {
1256 case GDK_ENTER_NOTIFY:
1257 mask = GDK_ENTER_NOTIFY_MASK;
1258 break;
1259 case GDK_LEAVE_NOTIFY:
1260 mask = GDK_LEAVE_NOTIFY_MASK;
1261 break;
1262 case GDK_MOTION_NOTIFY:
1263 mask = GDK_POINTER_MOTION_MASK;
1264 break;
1265 case GDK_BUTTON_PRESS:
1266 case GDK_2BUTTON_PRESS:
1267 case GDK_3BUTTON_PRESS:
1268 mask = GDK_BUTTON_PRESS_MASK;
1269 break;
1270 case GDK_BUTTON_RELEASE:
1271 mask = GDK_BUTTON_RELEASE_MASK;
1272 break;
1273 case GDK_KEY_PRESS:
1274 mask = GDK_KEY_PRESS_MASK;
1275 break;
1276 case GDK_KEY_RELEASE:
1277 mask = GDK_KEY_RELEASE_MASK;
1278 break;
1279 case GDK_SCROLL:
1280 mask = GDK_SCROLL;
1281 break;
1282 default:
1283 mask = 0;
1284 break;
1285 }
1287 if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1288 }
1290 /* Convert to world coordinates -- we have two cases because of diferent
1291 * offsets of the fields in the event structures.
1292 */
1294 GdkEvent ev = *event;
1296 switch (ev.type) {
1297 case GDK_ENTER_NOTIFY:
1298 case GDK_LEAVE_NOTIFY:
1299 ev.crossing.x += canvas->x0;
1300 ev.crossing.y += canvas->y0;
1301 break;
1302 case GDK_MOTION_NOTIFY:
1303 case GDK_BUTTON_PRESS:
1304 case GDK_2BUTTON_PRESS:
1305 case GDK_3BUTTON_PRESS:
1306 case GDK_BUTTON_RELEASE:
1307 ev.motion.x += canvas->x0;
1308 ev.motion.y += canvas->y0;
1309 break;
1310 default:
1311 break;
1312 }
1314 /* Choose where we send the event */
1316 /* canvas->current_item becomes NULL in some cases under Win32
1317 ** (e.g. if the pointer leaves the window). So this is a hack that
1318 ** Lauris applied to SP to get around the problem.
1319 */
1320 SPCanvasItem* item = NULL;
1321 if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1322 item = canvas->grabbed_item;
1323 } else {
1324 item = canvas->current_item;
1325 }
1327 if (canvas->focused_item &&
1328 ((event->type == GDK_KEY_PRESS) ||
1329 (event->type == GDK_KEY_RELEASE) ||
1330 (event->type == GDK_FOCUS_CHANGE))) {
1331 item = canvas->focused_item;
1332 }
1334 /* The event is propagated up the hierarchy (for if someone connected to
1335 * a group instead of a leaf event), and emission is stopped if a
1336 * handler returns TRUE, just like for GtkWidget events.
1337 */
1339 gint finished = FALSE;
1341 while (item && !finished) {
1342 gtk_object_ref (GTK_OBJECT (item));
1343 gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1344 SPCanvasItem *parent = item->parent;
1345 gtk_object_unref (GTK_OBJECT (item));
1346 item = parent;
1347 }
1349 return finished;
1350 }
1352 /**
1353 * Helper that re-picks the current item in the canvas, based on the event's
1354 * coordinates and emits enter/leave events for items as appropriate.
1355 */
1356 static int
1357 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1358 {
1359 int button_down = 0;
1360 double x, y;
1362 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1363 return FALSE;
1365 int retval = FALSE;
1367 if (canvas->gen_all_enter_events == false) {
1368 // If a button is down, we'll perform enter and leave events on the
1369 // current item, but not enter on any other item. This is more or
1370 // less like X pointer grabbing for canvas items.
1371 //
1372 button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1373 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1375 if (!button_down) canvas->left_grabbed_item = FALSE;
1376 }
1378 /* Save the event in the canvas. This is used to synthesize enter and
1379 * leave events in case the current item changes. It is also used to
1380 * re-pick the current item if the current one gets deleted. Also,
1381 * synthesize an enter event.
1382 */
1383 if (event != &canvas->pick_event) {
1384 if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1385 /* these fields have the same offsets in both types of events */
1387 canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY;
1388 canvas->pick_event.crossing.window = event->motion.window;
1389 canvas->pick_event.crossing.send_event = event->motion.send_event;
1390 canvas->pick_event.crossing.subwindow = NULL;
1391 canvas->pick_event.crossing.x = event->motion.x;
1392 canvas->pick_event.crossing.y = event->motion.y;
1393 canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL;
1394 canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR;
1395 canvas->pick_event.crossing.focus = FALSE;
1396 canvas->pick_event.crossing.state = event->motion.state;
1398 /* these fields don't have the same offsets in both types of events */
1400 if (event->type == GDK_MOTION_NOTIFY) {
1401 canvas->pick_event.crossing.x_root = event->motion.x_root;
1402 canvas->pick_event.crossing.y_root = event->motion.y_root;
1403 } else {
1404 canvas->pick_event.crossing.x_root = event->button.x_root;
1405 canvas->pick_event.crossing.y_root = event->button.y_root;
1406 }
1407 } else {
1408 canvas->pick_event = *event;
1409 }
1410 }
1412 /* Don't do anything else if this is a recursive call */
1413 if (canvas->in_repick) return retval;
1415 /* LeaveNotify means that there is no current item, so we don't look for one */
1416 if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1417 /* these fields don't have the same offsets in both types of events */
1419 if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1420 x = canvas->pick_event.crossing.x;
1421 y = canvas->pick_event.crossing.y;
1422 } else {
1423 x = canvas->pick_event.motion.x;
1424 y = canvas->pick_event.motion.y;
1425 }
1427 /* world coords */
1428 x += canvas->x0;
1429 y += canvas->y0;
1431 /* find the closest item */
1432 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1433 sp_canvas_item_invoke_point (canvas->root, Geom::Point(x, y), &canvas->new_current_item);
1434 } else {
1435 canvas->new_current_item = NULL;
1436 }
1437 } else {
1438 canvas->new_current_item = NULL;
1439 }
1441 if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1442 return retval; /* current item did not change */
1443 }
1445 /* Synthesize events for old and new current items */
1447 if ((canvas->new_current_item != canvas->current_item)
1448 && (canvas->current_item != NULL)
1449 && !canvas->left_grabbed_item) {
1450 GdkEvent new_event;
1451 SPCanvasItem *item;
1453 item = canvas->current_item;
1455 new_event = canvas->pick_event;
1456 new_event.type = GDK_LEAVE_NOTIFY;
1458 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1459 new_event.crossing.subwindow = NULL;
1460 canvas->in_repick = TRUE;
1461 retval = emit_event (canvas, &new_event);
1462 canvas->in_repick = FALSE;
1463 }
1465 if (canvas->gen_all_enter_events == false) {
1466 // new_current_item may have been set to NULL during the call to
1467 // emit_event() above
1468 if ((canvas->new_current_item != canvas->current_item) && button_down) {
1469 canvas->left_grabbed_item = TRUE;
1470 return retval;
1471 }
1472 }
1474 /* Handle the rest of cases */
1476 canvas->left_grabbed_item = FALSE;
1477 canvas->current_item = canvas->new_current_item;
1479 if (canvas->current_item != NULL) {
1480 GdkEvent new_event;
1482 new_event = canvas->pick_event;
1483 new_event.type = GDK_ENTER_NOTIFY;
1484 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1485 new_event.crossing.subwindow = NULL;
1486 retval = emit_event (canvas, &new_event);
1487 }
1489 return retval;
1490 }
1492 /**
1493 * Button event handler for the canvas.
1494 */
1495 static gint
1496 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1497 {
1498 SPCanvas *canvas = SP_CANVAS (widget);
1499 SPDesktop *dt = SP_ACTIVE_DESKTOP;
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 if (dt) {
1535 // Snapping will be on hold if we're moving the mouse at high speeds. When starting
1536 // drawing a new shape we really should snap though.
1537 dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false);
1538 }
1540 /* Pick the current item as if the button were not pressed, and
1541 * then process the event.
1542 */
1543 canvas->state = event->state;
1544 pick_current_item (canvas, (GdkEvent *) event);
1545 canvas->state ^= mask;
1546 retval = emit_event (canvas, (GdkEvent *) event);
1547 break;
1549 case GDK_BUTTON_RELEASE:
1550 sp_canvas_snap_watchdog_callback(canvas); // If we have any pending snapping action, then invoke it now
1552 /* Process the event as if the button were pressed, then repick
1553 * after the button has been released
1554 */
1555 canvas->state = event->state;
1556 retval = emit_event (canvas, (GdkEvent *) event);
1557 event->state ^= mask;
1558 canvas->state = event->state;
1559 pick_current_item (canvas, (GdkEvent *) event);
1560 event->state ^= mask;
1562 break;
1564 default:
1565 g_assert_not_reached ();
1566 }
1568 return retval;
1569 }
1571 /**
1572 * Scroll event handler for the canvas.
1573 *
1574 * \todo FIXME: generate motion events to re-select items.
1575 */
1576 static gint
1577 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1578 {
1579 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1580 }
1582 static inline void request_motions(GdkWindow *w, GdkEventMotion *event) {
1583 gdk_window_get_pointer(w, NULL, NULL, NULL);
1584 #if HAS_GDK_EVENT_REQUEST_MOTIONS
1585 gdk_event_request_motions(event);
1586 #endif
1587 }
1589 /**
1590 * Motion event handler for the canvas.
1591 */
1592 static int
1593 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1594 {
1595 static guint32 prev_time;
1596 static boost::optional<Geom::Point> prev_pos;
1598 int status;
1599 SPCanvas *canvas = SP_CANVAS (widget);
1601 track_latency((GdkEvent *)event);
1603 if (event->window != SP_CANVAS_WINDOW (canvas))
1604 return FALSE;
1606 if (canvas->pixmap_gc == NULL) // canvas being deleted
1607 return FALSE;
1609 SPDesktop *dt = SP_ACTIVE_DESKTOP;
1611 // Snap when speed drops below e.g. 0.02 px/msec, or when no motion events have occured for some period.
1612 // i.e. snap when we're at stand still. A speed threshold enforces snapping for tablets, which might never
1613 // be fully at stand still and might keep spitting out motion events.
1614 if (dt) {
1615 bool const c1 = event->type == GDK_MOTION_NOTIFY;
1616 bool const c21 = event->state & GDK_BUTTON1_MASK; // Snapping only occurs when dragging with the left mouse button down
1617 bool const c22 = event->state & GDK_BUTTON2_MASK; // We shouldn't hold back any events when other mouse buttons have been
1618 bool const c23 = event->state & GDK_BUTTON3_MASK; // pressed, e.g. when scrolling with the middle mouse button; if we do then
1619 // Inkscape will get stuck in an unresponsive state
1620 bool const c3 = dt->namedview->snap_manager.snapprefs.getSnapEnabledGlobally();
1621 if (c1 && c21 && (!c22) && (!c23) && c3) {
1622 Geom::Point event_pos(event->x, event->y);
1623 guint32 event_t = gdk_event_get_time ( (GdkEvent *) event );
1625 dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(true); // put snapping on hold
1627 if (prev_pos) {
1628 Geom::Coord dist = Geom::L2(event_pos - *prev_pos);
1629 guint32 delta_t = event_t - prev_time;
1630 gdouble speed = delta_t > 0 ? dist/delta_t : 1000;
1631 // std::cout << "speed = " << speed << " px/msec " << "| time passed = " << delta_t << " msec" << std::endl;
1632 if (speed > 0.02) { // Jitter threshold, might be needed for tablets
1633 // We're moving fast, so postpone any snapping until the next GDK_MOTION_NOTIFY event. We
1634 // will keep on postponing the snapping as long as the speed is high.
1635 // We must snap at some point in time though, so set a watchdog timer at some time from
1636 // now, just in case there's no future motion event that drops under the speed limit (when
1637 // stoppping abruptly)
1638 sp_canvas_snap_watchdog_kill(canvas);
1639 sp_canvas_snap_watchdog_set(canvas, event); // watchdog is reset, i.e. pushed forward in time
1640 // If the watchdog expires before a new motion event is received, we will snap (as explained
1641 // above). This means however that when the timer is too short, we will always snap and that the
1642 // speed threshold is ineffective. In the extreme case the delay is set to zero, and snapping will
1643 // be immediate, as it used to be in the old days ;-).
1644 } else { // Speed is very low, so we're virtually at stand still
1645 // But if we're really standing still, then we should snap now. We could use some low-pass filtering,
1646 // otherwise snapping occurs for each jitter movement. For this filtering we'll leave the watchdog to expire,
1647 // snap, and set a new watchdog again.
1648 if (canvas->watchdog_id == 0) { // no watchdog has been set
1649 // it might have already expired, so we'll set a new one; the snapping frequency will be limited by this
1650 sp_canvas_snap_watchdog_set(canvas, event);
1651 } // else: watchdog has been set before and we'll wait for it to expire
1652 }
1653 } else {
1654 // This is the first GDK_MOTION_NOTIFY event, so postpone snapping and set the watchdog
1655 sp_canvas_snap_watchdog_set(canvas, event);
1656 }
1658 prev_pos = event_pos;
1659 prev_time = event_t;
1660 }
1661 }
1663 canvas->state = event->state;
1664 pick_current_item (canvas, (GdkEvent *) event);
1666 status = emit_event (canvas, (GdkEvent *) event);
1668 if (event->is_hint) {
1669 request_motions(widget->window, event);
1670 }
1672 return status;
1673 }
1675 gboolean sp_canvas_snap_watchdog_callback(gpointer data)
1676 {
1677 // Snap NOW! For this the "postponed" flag will be reset and an the last motion event will be repeated
1678 SPCanvas *canvas = reinterpret_cast<SPCanvas *>(data);
1679 if (!canvas->watchdog_event) {
1680 return FALSE;
1681 }
1683 SPDesktop *dt = SP_ACTIVE_DESKTOP;
1684 if (dt) {
1685 dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false);
1686 }
1688 emit_event(canvas, canvas->watchdog_event);
1689 gdk_event_free(canvas->watchdog_event);
1690 canvas->watchdog_event = NULL;
1691 canvas->watchdog_id = 0;
1693 return FALSE;
1694 }
1696 void sp_canvas_snap_watchdog_set(SPCanvas *canvas, GdkEventMotion *event)
1697 {
1698 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1699 double value = prefs->getDoubleLimited("/options/snapdelay/value", 0, 0, 1000);
1700 g_assert(canvas->watchdog_id == 0);
1701 canvas->watchdog_id = g_timeout_add(value, &sp_canvas_snap_watchdog_callback, canvas);
1702 g_assert(canvas->watchdog_event == NULL);
1703 canvas->watchdog_event = gdk_event_copy( (GdkEvent *) event);
1704 }
1706 void sp_canvas_snap_watchdog_kill(SPCanvas *canvas)
1707 {
1708 if (canvas->watchdog_id) {
1709 g_source_remove(canvas->watchdog_id); // Kill the watchdog
1710 canvas->watchdog_id = 0;
1711 }
1713 if (canvas->watchdog_event) {
1714 gdk_event_free(canvas->watchdog_event);
1715 canvas->watchdog_event = NULL;
1716 }
1717 }
1719 static void
1720 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)
1721 {
1722 GtkWidget *widget = GTK_WIDGET (canvas);
1724 SPCanvasBuf buf;
1725 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1726 buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1727 } else {
1728 buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1729 }
1731 // Mark the region clean
1732 sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1734 buf.buf_rowstride = sw * 4;
1735 buf.rect.x0 = x0;
1736 buf.rect.y0 = y0;
1737 buf.rect.x1 = x1;
1738 buf.rect.y1 = y1;
1739 buf.visible_rect.x0 = draw_x1;
1740 buf.visible_rect.y0 = draw_y1;
1741 buf.visible_rect.x1 = draw_x2;
1742 buf.visible_rect.y1 = draw_y2;
1743 GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1744 buf.bg_color = (((color->red & 0xff00) << 8)
1745 | (color->green & 0xff00)
1746 | (color->blue >> 8));
1747 buf.is_empty = true;
1749 buf.ct = nr_create_cairo_context_canvasbuf (&(buf.visible_rect), &buf);
1751 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1752 SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1753 }
1755 #if ENABLE_LCMS
1756 cmsHTRANSFORM transf = 0;
1757 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1758 bool fromDisplay = prefs->getBool( "/options/displayprofile/from_display");
1759 if ( fromDisplay ) {
1760 transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" );
1761 } else {
1762 transf = Inkscape::colorprofile_get_display_transform();
1763 }
1764 #endif // ENABLE_LCMS
1766 if (buf.is_empty) {
1767 #if ENABLE_LCMS
1768 if ( transf && canvas->enable_cms_display_adj ) {
1769 cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1770 }
1771 #endif // ENABLE_LCMS
1772 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1773 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1774 canvas->pixmap_gc,
1775 TRUE,
1776 x0 - canvas->x0, y0 - canvas->y0,
1777 x1 - x0, y1 - y0);
1778 } else {
1780 #if ENABLE_LCMS
1781 if ( transf && canvas->enable_cms_display_adj ) {
1782 for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1783 guchar* p = buf.buf + (buf.buf_rowstride * yy);
1784 cmsDoTransform( transf, p, p, (x1 - x0) );
1785 }
1786 }
1787 #endif // ENABLE_LCMS
1789 // Now we only need to output the prepared pixmap to the actual screen, and this define chooses one
1790 // of the two ways to do it. The cairo way is direct and straightforward, but unfortunately
1791 // noticeably slower. I asked Carl Worth but he was unable so far to suggest any specific reason
1792 // for this slowness. So, for now we use the oldish method: squeeze out 32bpp buffer to 24bpp and
1793 // use gdk_draw_rgb_image_dithalign, for unfortunately gdk can only handle 24 bpp, which cairo
1794 // cannot handle at all. Still, this way is currently faster even despite the blit with squeeze.
1796 ///#define CANVAS_OUTPUT_VIA_CAIRO
1798 #ifdef CANVAS_OUTPUT_VIA_CAIRO
1800 buf.cst = cairo_image_surface_create_for_data (
1801 buf.buf,
1802 CAIRO_FORMAT_ARGB32, // unpacked, i.e. 32 bits! one byte is unused
1803 x1 - x0, y1 - y0,
1804 buf.buf_rowstride
1805 );
1806 cairo_t *window_ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1807 cairo_set_source_surface (window_ct, buf.cst, x0 - canvas->x0, y0 - canvas->y0);
1808 cairo_paint (window_ct);
1809 cairo_destroy (window_ct);
1810 cairo_surface_finish (buf.cst);
1811 cairo_surface_destroy (buf.cst);
1813 #else
1815 NRPixBlock b3;
1816 nr_pixblock_setup_fast (&b3, NR_PIXBLOCK_MODE_R8G8B8, x0, y0, x1, y1, TRUE);
1818 NRPixBlock b4;
1819 nr_pixblock_setup_extern (&b4, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1,
1820 buf.buf,
1821 buf.buf_rowstride,
1822 FALSE, FALSE);
1824 // this does the 32->24 squishing, using an assembler routine:
1825 nr_blit_pixblock_pixblock (&b3, &b4);
1827 gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1828 canvas->pixmap_gc,
1829 x0 - canvas->x0, y0 - canvas->y0,
1830 x1 - x0, y1 - y0,
1831 GDK_RGB_DITHER_MAX,
1832 NR_PIXBLOCK_PX(&b3),
1833 sw * 3,
1834 x0 - canvas->x0, y0 - canvas->y0);
1836 nr_pixblock_release (&b3);
1837 nr_pixblock_release (&b4);
1838 #endif
1839 }
1841 cairo_surface_t *cst = cairo_get_target(buf.ct);
1842 cairo_destroy (buf.ct);
1843 cairo_surface_finish (cst);
1844 cairo_surface_destroy (cst);
1846 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1847 nr_pixelstore_256K_free (buf.buf);
1848 } else {
1849 nr_pixelstore_1M_free (buf.buf);
1850 }
1851 }
1853 struct PaintRectSetup {
1854 SPCanvas* canvas;
1855 NRRectL big_rect;
1856 GTimeVal start_time;
1857 int max_pixels;
1858 Geom::Point mouse_loc;
1859 };
1861 /**
1862 * Paint the given rect, recursively subdividing the region until it is the size of a single
1863 * buffer.
1864 *
1865 * @return true if the drawing completes
1866 */
1867 static int
1868 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1869 {
1870 GTimeVal now;
1871 g_get_current_time (&now);
1873 glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1874 + (now.tv_usec - setup->start_time.tv_usec);
1876 // Allow only very fast buffers to be run together;
1877 // as soon as the total redraw time exceeds 1ms, cancel;
1878 // this returns control to the idle loop and allows Inkscape to process user input
1879 // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1880 // it will get back and finish painting what remains to paint.
1881 if (elapsed > 1000) {
1883 // Interrupting redraw isn't always good.
1884 // For example, when you drag one node of a big path, only the buffer containing
1885 // the mouse cursor will be redrawn again and again, and the rest of the path
1886 // will remain stale because Inkscape never has enough idle time to redraw all
1887 // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1888 // If this limit is set, and if we have aborted redraw more times than is allowed,
1889 // interrupting is blocked and we're forced to redraw full screen once
1890 // (after which we can again interrupt forced_redraw_limit times).
1891 if (setup->canvas->forced_redraw_limit < 0 ||
1892 setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1894 if (setup->canvas->forced_redraw_limit != -1) {
1895 setup->canvas->forced_redraw_count++;
1896 }
1898 return false;
1899 }
1900 }
1902 // Find the optimal buffer dimensions
1903 int bw = this_rect.x1 - this_rect.x0;
1904 int bh = this_rect.y1 - this_rect.y0;
1905 if ((bw < 1) || (bh < 1))
1906 return 0;
1908 if (bw * bh < setup->max_pixels) {
1909 // We are small enough
1910 sp_canvas_paint_single_buffer (setup->canvas,
1911 this_rect.x0, this_rect.y0,
1912 this_rect.x1, this_rect.y1,
1913 setup->big_rect.x0, setup->big_rect.y0,
1914 setup->big_rect.x1, setup->big_rect.y1, bw);
1915 return 1;
1916 }
1918 NRRectL lo = this_rect;
1919 NRRectL hi = this_rect;
1921 /*
1922 This test determines the redraw strategy:
1924 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1925 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1926 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1927 and seems to be faster for drawings with many smaller objects at zoom-out.
1929 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1930 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1931 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1932 faster.
1934 The default for now is the strips mode.
1935 */
1936 if (bw < bh || bh < 2 * TILE_SIZE) {
1937 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1938 int mid = ((long long) this_rect.x0 + (long long) this_rect.x1) / 2;
1939 // Make sure that mid lies on a tile boundary
1940 mid = (mid / TILE_SIZE) * TILE_SIZE;
1942 lo.x1 = mid;
1943 hi.x0 = mid;
1945 if (setup->mouse_loc[Geom::X] < mid) {
1946 // Always paint towards the mouse first
1947 return sp_canvas_paint_rect_internal(setup, lo)
1948 && sp_canvas_paint_rect_internal(setup, hi);
1949 } else {
1950 return sp_canvas_paint_rect_internal(setup, hi)
1951 && sp_canvas_paint_rect_internal(setup, lo);
1952 }
1953 } else {
1954 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1955 int mid = ((long long) this_rect.y0 + (long long) this_rect.y1) / 2;
1956 // Make sure that mid lies on a tile boundary
1957 mid = (mid / TILE_SIZE) * TILE_SIZE;
1959 lo.y1 = mid;
1960 hi.y0 = mid;
1962 if (setup->mouse_loc[Geom::Y] < mid) {
1963 // Always paint towards the mouse first
1964 return sp_canvas_paint_rect_internal(setup, lo)
1965 && sp_canvas_paint_rect_internal(setup, hi);
1966 } else {
1967 return sp_canvas_paint_rect_internal(setup, hi)
1968 && sp_canvas_paint_rect_internal(setup, lo);
1969 }
1970 }
1971 }
1974 /**
1975 * Helper that draws a specific rectangular part of the canvas.
1976 *
1977 * @return true if the rectangle painting succeeds.
1978 */
1979 static bool
1980 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1981 {
1982 g_return_val_if_fail (!canvas->need_update, false);
1984 NRRectL rect;
1985 rect.x0 = xx0;
1986 rect.x1 = xx1;
1987 rect.y0 = yy0;
1988 rect.y1 = yy1;
1990 // Clip rect-to-draw by the current visible area
1991 rect.x0 = MAX (rect.x0, canvas->x0);
1992 rect.y0 = MAX (rect.y0, canvas->y0);
1993 rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1994 rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1996 #ifdef DEBUG_REDRAW
1997 // paint the area to redraw yellow
1998 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1999 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
2000 canvas->pixmap_gc,
2001 TRUE,
2002 rect.x0 - canvas->x0, rect.y0 - canvas->y0,
2003 rect.x1 - rect.x0, rect.y1 - rect.y0);
2004 #endif
2006 PaintRectSetup setup;
2008 setup.canvas = canvas;
2009 setup.big_rect = rect;
2011 // Save the mouse location
2012 gint x, y;
2013 gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
2014 setup.mouse_loc = sp_canvas_window_to_world (canvas, Geom::Point(x,y));
2016 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
2017 // use 256K as a compromise to not slow down gradients
2018 // 256K is the cached buffer and we need 4 channels
2019 setup.max_pixels = 65536; // 256K/4
2020 } else {
2021 // paths only, so 1M works faster
2022 // 1M is the cached buffer and we need 4 channels
2023 setup.max_pixels = 262144;
2024 }
2026 // Start the clock
2027 g_get_current_time(&(setup.start_time));
2029 // Go
2030 return sp_canvas_paint_rect_internal(&setup, rect);
2031 }
2033 /**
2034 * Force a full redraw after a specified number of interrupted redraws
2035 */
2036 void
2037 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
2038 g_return_if_fail(canvas != NULL);
2040 canvas->forced_redraw_limit = count;
2041 canvas->forced_redraw_count = 0;
2042 }
2044 /**
2045 * End forced full redraw requests
2046 */
2047 void
2048 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
2049 g_return_if_fail(canvas != NULL);
2051 canvas->forced_redraw_limit = -1;
2052 }
2054 /**
2055 * The canvas widget's expose callback.
2056 */
2057 static gint
2058 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
2059 {
2060 SPCanvas *canvas = SP_CANVAS (widget);
2062 if (!GTK_WIDGET_DRAWABLE (widget) ||
2063 (event->window != SP_CANVAS_WINDOW (canvas)))
2064 return FALSE;
2066 int n_rects;
2067 GdkRectangle *rects;
2068 gdk_region_get_rectangles (event->region, &rects, &n_rects);
2070 for (int i = 0; i < n_rects; i++) {
2071 NRRectL rect;
2073 rect.x0 = rects[i].x + canvas->x0;
2074 rect.y0 = rects[i].y + canvas->y0;
2075 rect.x1 = rect.x0 + rects[i].width;
2076 rect.y1 = rect.y0 + rects[i].height;
2078 sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
2079 }
2081 if (n_rects > 0)
2082 g_free (rects);
2084 return FALSE;
2085 }
2087 /**
2088 * The canvas widget's keypress callback.
2089 */
2090 static gint
2091 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
2092 {
2093 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
2094 }
2096 /**
2097 * Crossing event handler for the canvas.
2098 */
2099 static gint
2100 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
2101 {
2102 SPCanvas *canvas = SP_CANVAS (widget);
2104 if (event->window != SP_CANVAS_WINDOW (canvas))
2105 return FALSE;
2107 canvas->state = event->state;
2108 return pick_current_item (canvas, (GdkEvent *) event);
2109 }
2111 /**
2112 * Focus in handler for the canvas.
2113 */
2114 static gint
2115 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
2116 {
2117 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
2119 SPCanvas *canvas = SP_CANVAS (widget);
2121 if (canvas->focused_item) {
2122 return emit_event (canvas, (GdkEvent *) event);
2123 } else {
2124 return FALSE;
2125 }
2126 }
2128 /**
2129 * Focus out handler for the canvas.
2130 */
2131 static gint
2132 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
2133 {
2134 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2136 SPCanvas *canvas = SP_CANVAS (widget);
2138 if (canvas->focused_item)
2139 return emit_event (canvas, (GdkEvent *) event);
2140 else
2141 return FALSE;
2142 }
2144 /**
2145 * Helper that repaints the areas in the canvas that need it.
2146 *
2147 * @return true if all the dirty parts have been redrawn
2148 */
2149 static int
2150 paint (SPCanvas *canvas)
2151 {
2152 if (canvas->need_update) {
2153 sp_canvas_item_invoke_update (canvas->root, Geom::identity(), 0);
2154 canvas->need_update = FALSE;
2155 }
2157 if (!canvas->need_redraw)
2158 return TRUE;
2160 Gdk::Region to_paint;
2162 for (int j=canvas->tTop; j<canvas->tBottom; j++) {
2163 for (int i=canvas->tLeft; i<canvas->tRight; i++) {
2164 int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
2166 if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
2167 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
2168 TILE_SIZE, TILE_SIZE));
2169 }
2171 }
2172 }
2174 if (!to_paint.empty()) {
2175 Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
2176 typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
2177 for (Iter i=rect.begin(); i != rect.end(); ++i) {
2178 int x0 = (*i).get_x();
2179 int y0 = (*i).get_y();
2180 int x1 = x0 + (*i).get_width();
2181 int y1 = y0 + (*i).get_height();
2182 if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
2183 // Aborted
2184 return FALSE;
2185 };
2186 }
2187 }
2189 canvas->need_redraw = FALSE;
2191 // we've had a full unaborted redraw, reset the full redraw counter
2192 if (canvas->forced_redraw_limit != -1) {
2193 canvas->forced_redraw_count = 0;
2194 }
2196 return TRUE;
2197 }
2199 /**
2200 * Helper that invokes update, paint, and repick on canvas.
2201 */
2202 static int
2203 do_update (SPCanvas *canvas)
2204 {
2205 if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop durring interrupted display!
2206 return TRUE;
2208 /* Cause the update if necessary */
2209 if (canvas->need_update) {
2210 sp_canvas_item_invoke_update (canvas->root, Geom::identity(), 0);
2211 canvas->need_update = FALSE;
2212 }
2214 /* Paint if able to */
2215 if (GTK_WIDGET_DRAWABLE (canvas)) {
2216 return paint (canvas);
2217 }
2219 /* Pick new current item */
2220 while (canvas->need_repick) {
2221 canvas->need_repick = FALSE;
2222 pick_current_item (canvas, &canvas->pick_event);
2223 }
2225 return TRUE;
2226 }
2228 /**
2229 * Idle handler for the canvas that deals with pending updates and redraws.
2230 */
2231 static gint
2232 idle_handler (gpointer data)
2233 {
2234 GDK_THREADS_ENTER ();
2236 SPCanvas *canvas = SP_CANVAS (data);
2238 int const ret = do_update (canvas);
2240 if (ret) {
2241 /* Reset idle id */
2242 canvas->idle_id = 0;
2243 }
2245 GDK_THREADS_LEAVE ();
2247 return !ret;
2248 }
2250 /**
2251 * Convenience function to add an idle handler to a canvas.
2252 */
2253 static void
2254 add_idle (SPCanvas *canvas)
2255 {
2256 if (canvas->idle_id != 0)
2257 return;
2259 canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2260 }
2262 /**
2263 * Returns the root group of the specified canvas.
2264 */
2265 SPCanvasGroup *
2266 sp_canvas_root (SPCanvas *canvas)
2267 {
2268 g_return_val_if_fail (canvas != NULL, NULL);
2269 g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2271 return SP_CANVAS_GROUP (canvas->root);
2272 }
2274 /**
2275 * Scrolls canvas to specific position (cx and cy are measured in screen pixels)
2276 */
2277 void
2278 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2279 {
2280 g_return_if_fail (canvas != NULL);
2281 g_return_if_fail (SP_IS_CANVAS (canvas));
2283 int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels)
2284 int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do!
2285 int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the
2286 int dy = iy - canvas->y0; // canvas w.r.t its previous position
2288 canvas->dx0 = cx; // here the 'd' stands for double, not delta!
2289 canvas->dy0 = cy;
2290 canvas->x0 = ix;
2291 canvas->y0 = iy;
2293 sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2295 if (!clear) {
2296 // scrolling without zoom; redraw only the newly exposed areas
2297 if ((dx != 0) || (dy != 0)) {
2298 canvas->is_scrolling = is_scrolling;
2299 if (GTK_WIDGET_REALIZED (canvas)) {
2300 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2301 }
2302 }
2303 } else {
2304 // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2305 }
2306 }
2308 /**
2309 * Updates canvas if necessary.
2310 */
2311 void
2312 sp_canvas_update_now (SPCanvas *canvas)
2313 {
2314 g_return_if_fail (canvas != NULL);
2315 g_return_if_fail (SP_IS_CANVAS (canvas));
2317 if (!(canvas->need_update ||
2318 canvas->need_redraw))
2319 return;
2321 do_update (canvas);
2322 }
2324 /**
2325 * Update callback for canvas widget.
2326 */
2327 static void
2328 sp_canvas_request_update (SPCanvas *canvas)
2329 {
2330 canvas->need_update = TRUE;
2331 add_idle (canvas);
2332 }
2334 /**
2335 * Forces redraw of rectangular canvas area.
2336 */
2337 void
2338 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2339 {
2340 NRRectL bbox;
2341 NRRectL visible;
2342 NRRectL clip;
2344 g_return_if_fail (canvas != NULL);
2345 g_return_if_fail (SP_IS_CANVAS (canvas));
2347 if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2348 if ((x0 >= x1) || (y0 >= y1)) return;
2350 bbox.x0 = x0;
2351 bbox.y0 = y0;
2352 bbox.x1 = x1;
2353 bbox.y1 = y1;
2355 visible.x0 = canvas->x0;
2356 visible.y0 = canvas->y0;
2357 visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2358 visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2360 nr_rect_l_intersect (&clip, &bbox, &visible);
2362 sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2363 add_idle (canvas);
2364 }
2366 /**
2367 * Sets world coordinates from win and canvas.
2368 */
2369 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2370 {
2371 g_return_if_fail (canvas != NULL);
2372 g_return_if_fail (SP_IS_CANVAS (canvas));
2374 if (worldx) *worldx = canvas->x0 + winx;
2375 if (worldy) *worldy = canvas->y0 + winy;
2376 }
2378 /**
2379 * Sets win coordinates from world and canvas.
2380 */
2381 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2382 {
2383 g_return_if_fail (canvas != NULL);
2384 g_return_if_fail (SP_IS_CANVAS (canvas));
2386 if (winx) *winx = worldx - canvas->x0;
2387 if (winy) *winy = worldy - canvas->y0;
2388 }
2390 /**
2391 * Converts point from win to world coordinates.
2392 */
2393 Geom::Point sp_canvas_window_to_world(SPCanvas const *canvas, Geom::Point const win)
2394 {
2395 g_assert (canvas != NULL);
2396 g_assert (SP_IS_CANVAS (canvas));
2398 return Geom::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2399 }
2401 /**
2402 * Converts point from world to win coordinates.
2403 */
2404 Geom::Point sp_canvas_world_to_window(SPCanvas const *canvas, Geom::Point const world)
2405 {
2406 g_assert (canvas != NULL);
2407 g_assert (SP_IS_CANVAS (canvas));
2409 return Geom::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2410 }
2412 /**
2413 * Returns true if point given in world coordinates is inside window.
2414 */
2415 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, Geom::Point const &world)
2416 {
2417 g_assert( canvas != NULL );
2418 g_assert(SP_IS_CANVAS(canvas));
2420 GtkWidget const &w = *GTK_WIDGET(canvas);
2421 return ( ( canvas->x0 <= world[Geom::X] ) &&
2422 ( canvas->y0 <= world[Geom::Y] ) &&
2423 ( world[Geom::X] < canvas->x0 + w.allocation.width ) &&
2424 ( world[Geom::Y] < canvas->y0 + w.allocation.height ) );
2425 }
2427 /**
2428 * Return canvas window coordinates as Geom::Rect.
2429 */
2430 Geom::Rect SPCanvas::getViewbox() const
2431 {
2432 GtkWidget const *w = GTK_WIDGET(this);
2433 return Geom::Rect(Geom::Point(dx0, dy0),
2434 Geom::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2435 }
2437 /**
2438 * Return canvas window coordinates as IRect (a rectangle defined by integers).
2439 */
2440 NR::IRect SPCanvas::getViewboxIntegers() const
2441 {
2442 GtkWidget const *w = GTK_WIDGET(this);
2443 return NR::IRect(NR::IPoint(x0, y0),
2444 NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height));
2445 }
2447 inline int sp_canvas_tile_floor(int x)
2448 {
2449 return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2450 }
2452 inline int sp_canvas_tile_ceil(int x)
2453 {
2454 return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2455 }
2457 /**
2458 * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2459 */
2460 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2461 {
2462 if ( nl >= nr || nt >= nb ) {
2463 if ( canvas->tiles ) g_free(canvas->tiles);
2464 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2465 canvas->tileH=canvas->tileV=0;
2466 canvas->tiles=NULL;
2467 return;
2468 }
2469 int tl=sp_canvas_tile_floor(nl);
2470 int tt=sp_canvas_tile_floor(nt);
2471 int tr=sp_canvas_tile_ceil(nr);
2472 int tb=sp_canvas_tile_ceil(nb);
2474 int nh = tr-tl, nv = tb-tt;
2475 uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2476 for (int i=tl; i<tr; i++) {
2477 for (int j=tt; j<tb; j++) {
2478 int ind = (i-tl) + (j-tt)*nh;
2479 if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2480 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2481 } else {
2482 ntiles[ind]=0; // newly exposed areas get 0
2483 }
2484 }
2485 }
2486 if ( canvas->tiles ) g_free(canvas->tiles);
2487 canvas->tiles=ntiles;
2488 canvas->tLeft=tl;
2489 canvas->tTop=tt;
2490 canvas->tRight=tr;
2491 canvas->tBottom=tb;
2492 canvas->tileH=nh;
2493 canvas->tileV=nv;
2494 }
2496 /*
2497 * Helper that queues a canvas rectangle for redraw
2498 */
2499 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2500 canvas->need_redraw = TRUE;
2502 sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2503 }
2505 /**
2506 * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2507 */
2508 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2509 {
2510 if ( nl >= nr || nt >= nb ) {
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);
2517 if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2518 if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2519 if ( tr > canvas->tRight ) tr=canvas->tRight;
2520 if ( tt < canvas->tTop ) tt=canvas->tTop;
2521 if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2523 for (int i=tl; i<tr; i++) {
2524 for (int j=tt; j<tb; j++) {
2525 canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2526 }
2527 }
2528 }
2531 /*
2532 Local Variables:
2533 mode:c++
2534 c-file-style:"stroustrup"
2535 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2536 indent-tabs-mode:nil
2537 fill-column:99
2538 End:
2539 */
2540 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :