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 /**
950 * Registers the SPCanvas class if necessary, and returns the type ID
951 * associated to it.
952 *
953 * \return The type ID of the SPCanvas class.
954 **/
955 GType sp_canvas_get_type(void)
956 {
957 static GType type = 0;
958 if (!type) {
959 GTypeInfo info = {
960 sizeof(SPCanvasClass),
961 0, // base_init
962 0, // base_finalize
963 (GClassInitFunc)sp_canvas_class_init,
964 0, // class_finalize
965 0, // class_data
966 sizeof(SPCanvas),
967 0, // n_preallocs
968 (GInstanceInitFunc)sp_canvas_init,
969 0 // value_table
970 };
971 type = g_type_register_static(GTK_TYPE_WIDGET, "SPCanvas", &info, static_cast<GTypeFlags>(0));
972 }
973 return type;
974 }
976 /**
977 * Class initialization function for SPCanvasClass.
978 */
979 static void
980 sp_canvas_class_init (SPCanvasClass *klass)
981 {
982 GtkObjectClass *object_class = (GtkObjectClass *) klass;
983 GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
985 canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
987 object_class->destroy = sp_canvas_destroy;
989 widget_class->realize = sp_canvas_realize;
990 widget_class->unrealize = sp_canvas_unrealize;
991 widget_class->size_request = sp_canvas_size_request;
992 widget_class->size_allocate = sp_canvas_size_allocate;
993 widget_class->button_press_event = sp_canvas_button;
994 widget_class->button_release_event = sp_canvas_button;
995 widget_class->motion_notify_event = sp_canvas_motion;
996 widget_class->scroll_event = sp_canvas_scroll;
997 widget_class->expose_event = sp_canvas_expose;
998 widget_class->key_press_event = sp_canvas_key;
999 widget_class->key_release_event = sp_canvas_key;
1000 widget_class->enter_notify_event = sp_canvas_crossing;
1001 widget_class->leave_notify_event = sp_canvas_crossing;
1002 widget_class->focus_in_event = sp_canvas_focus_in;
1003 widget_class->focus_out_event = sp_canvas_focus_out;
1004 }
1006 /**
1007 * Callback: object initialization for SPCanvas.
1008 */
1009 static void
1010 sp_canvas_init (SPCanvas *canvas)
1011 {
1012 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
1013 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
1014 GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
1016 canvas->pick_event.type = GDK_LEAVE_NOTIFY;
1017 canvas->pick_event.crossing.x = 0;
1018 canvas->pick_event.crossing.y = 0;
1020 /* Create the root item as a special case */
1021 canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
1022 canvas->root->canvas = canvas;
1024 gtk_object_ref (GTK_OBJECT (canvas->root));
1025 gtk_object_sink (GTK_OBJECT (canvas->root));
1027 canvas->need_repick = TRUE;
1029 // See comment at in sp-canvas.h.
1030 canvas->gen_all_enter_events = false;
1032 canvas->drawing_disabled = false;
1034 canvas->tiles=NULL;
1035 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1036 canvas->tileH=canvas->tileV=0;
1038 canvas->forced_redraw_count = 0;
1039 canvas->forced_redraw_limit = -1;
1041 #if ENABLE_LCMS
1042 canvas->enable_cms_display_adj = false;
1043 canvas->cms_key = new Glib::ustring("");
1044 #endif // ENABLE_LCMS
1046 canvas->is_scrolling = false;
1047 }
1049 /**
1050 * Convenience function to remove the idle handler of a canvas.
1051 */
1052 static void
1053 remove_idle (SPCanvas *canvas)
1054 {
1055 if (canvas->idle_id) {
1056 gtk_idle_remove (canvas->idle_id);
1057 canvas->idle_id = 0;
1058 }
1059 }
1061 /*
1062 * Removes the transient state of the canvas (idle handler, grabs).
1063 */
1064 static void
1065 shutdown_transients (SPCanvas *canvas)
1066 {
1067 /* We turn off the need_redraw flag, since if the canvas is mapped again
1068 * it will request a redraw anyways. We do not turn off the need_update
1069 * flag, though, because updates are not queued when the canvas remaps
1070 * itself.
1071 */
1072 if (canvas->need_redraw) {
1073 canvas->need_redraw = FALSE;
1074 }
1075 if ( canvas->tiles ) g_free(canvas->tiles);
1076 canvas->tiles=NULL;
1077 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1078 canvas->tileH=canvas->tileV=0;
1080 if (canvas->grabbed_item) {
1081 canvas->grabbed_item = NULL;
1082 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1083 }
1085 remove_idle (canvas);
1086 }
1088 /**
1089 * Destroy handler for SPCanvas.
1090 */
1091 static void
1092 sp_canvas_destroy (GtkObject *object)
1093 {
1094 SPCanvas *canvas = SP_CANVAS (object);
1096 if (canvas->root) {
1097 gtk_object_unref (GTK_OBJECT (canvas->root));
1098 canvas->root = NULL;
1099 }
1101 shutdown_transients (canvas);
1103 if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1104 (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1105 }
1107 static void track_latency(GdkEvent const *event) {
1108 GdkEventLatencyTracker &tracker = GdkEventLatencyTracker::default_tracker();
1109 boost::optional<double> latency = tracker.process(event);
1110 if (latency && *latency > 2.0) {
1111 //g_warning("Event latency reached %f sec (%1.4f)", *latency, tracker.getSkew());
1112 }
1113 }
1115 /**
1116 * Returns new canvas as widget.
1117 */
1118 GtkWidget *
1119 sp_canvas_new_aa (void)
1120 {
1121 SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1123 return (GtkWidget *) canvas;
1124 }
1126 /**
1127 * The canvas widget's realize callback.
1128 */
1129 static void
1130 sp_canvas_realize (GtkWidget *widget)
1131 {
1132 SPCanvas *canvas = SP_CANVAS (widget);
1134 GdkWindowAttr attributes;
1135 attributes.window_type = GDK_WINDOW_CHILD;
1136 attributes.x = widget->allocation.x;
1137 attributes.y = widget->allocation.y;
1138 attributes.width = widget->allocation.width;
1139 attributes.height = widget->allocation.height;
1140 attributes.wclass = GDK_INPUT_OUTPUT;
1141 attributes.visual = gdk_rgb_get_visual ();
1142 attributes.colormap = gdk_rgb_get_cmap ();
1143 attributes.event_mask = (gtk_widget_get_events (widget) |
1144 GDK_EXPOSURE_MASK |
1145 GDK_BUTTON_PRESS_MASK |
1146 GDK_BUTTON_RELEASE_MASK |
1147 GDK_POINTER_MOTION_MASK |
1148 ( HAS_BROKEN_MOTION_HINTS ?
1149 0 : GDK_POINTER_MOTION_HINT_MASK ) |
1150 GDK_PROXIMITY_IN_MASK |
1151 GDK_PROXIMITY_OUT_MASK |
1152 GDK_KEY_PRESS_MASK |
1153 GDK_KEY_RELEASE_MASK |
1154 GDK_ENTER_NOTIFY_MASK |
1155 GDK_LEAVE_NOTIFY_MASK |
1156 GDK_FOCUS_CHANGE_MASK);
1157 gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1159 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1160 gdk_window_set_user_data (widget->window, widget);
1162 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1163 if ( prefs->getBool("/options/useextinput/value", true) )
1164 gtk_widget_set_events(widget, attributes.event_mask);
1166 widget->style = gtk_style_attach (widget->style, widget->window);
1168 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1170 canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1171 }
1173 /**
1174 * The canvas widget's unrealize callback.
1175 */
1176 static void
1177 sp_canvas_unrealize (GtkWidget *widget)
1178 {
1179 SPCanvas *canvas = SP_CANVAS (widget);
1181 canvas->current_item = NULL;
1182 canvas->grabbed_item = NULL;
1183 canvas->focused_item = NULL;
1185 shutdown_transients (canvas);
1187 gdk_gc_destroy (canvas->pixmap_gc);
1188 canvas->pixmap_gc = NULL;
1190 if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1191 (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1192 }
1194 /**
1195 * The canvas widget's size_request callback.
1196 */
1197 static void
1198 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1199 {
1200 static_cast<void>(SP_CANVAS (widget));
1202 req->width = 256;
1203 req->height = 256;
1204 }
1206 /**
1207 * The canvas widget's size_allocate callback.
1208 */
1209 static void
1210 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1211 {
1212 SPCanvas *canvas = SP_CANVAS (widget);
1214 /* Schedule redraw of new region */
1215 sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1216 if (allocation->width > widget->allocation.width) {
1217 sp_canvas_request_redraw (canvas,
1218 canvas->x0 + widget->allocation.width,
1219 0,
1220 canvas->x0 + allocation->width,
1221 canvas->y0 + allocation->height);
1222 }
1223 if (allocation->height > widget->allocation.height) {
1224 sp_canvas_request_redraw (canvas,
1225 0,
1226 canvas->y0 + widget->allocation.height,
1227 canvas->x0 + allocation->width,
1228 canvas->y0 + allocation->height);
1229 }
1231 widget->allocation = *allocation;
1233 if (GTK_WIDGET_REALIZED (widget)) {
1234 gdk_window_move_resize (widget->window,
1235 widget->allocation.x, widget->allocation.y,
1236 widget->allocation.width, widget->allocation.height);
1237 }
1238 }
1240 /**
1241 * Helper that emits an event for an item in the canvas, be it the current
1242 * item, grabbed item, or focused item, as appropriate.
1243 */
1244 static int
1245 emit_event (SPCanvas *canvas, GdkEvent *event)
1246 {
1247 guint mask;
1249 if (canvas->grabbed_item) {
1250 switch (event->type) {
1251 case GDK_ENTER_NOTIFY:
1252 mask = GDK_ENTER_NOTIFY_MASK;
1253 break;
1254 case GDK_LEAVE_NOTIFY:
1255 mask = GDK_LEAVE_NOTIFY_MASK;
1256 break;
1257 case GDK_MOTION_NOTIFY:
1258 mask = GDK_POINTER_MOTION_MASK;
1259 break;
1260 case GDK_BUTTON_PRESS:
1261 case GDK_2BUTTON_PRESS:
1262 case GDK_3BUTTON_PRESS:
1263 mask = GDK_BUTTON_PRESS_MASK;
1264 break;
1265 case GDK_BUTTON_RELEASE:
1266 mask = GDK_BUTTON_RELEASE_MASK;
1267 break;
1268 case GDK_KEY_PRESS:
1269 mask = GDK_KEY_PRESS_MASK;
1270 break;
1271 case GDK_KEY_RELEASE:
1272 mask = GDK_KEY_RELEASE_MASK;
1273 break;
1274 case GDK_SCROLL:
1275 mask = GDK_SCROLL;
1276 break;
1277 default:
1278 mask = 0;
1279 break;
1280 }
1282 if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1283 }
1285 /* Convert to world coordinates -- we have two cases because of different
1286 * offsets of the fields in the event structures.
1287 */
1289 GdkEvent ev = *event;
1291 switch (ev.type) {
1292 case GDK_ENTER_NOTIFY:
1293 case GDK_LEAVE_NOTIFY:
1294 ev.crossing.x += canvas->x0;
1295 ev.crossing.y += canvas->y0;
1296 break;
1297 case GDK_MOTION_NOTIFY:
1298 case GDK_BUTTON_PRESS:
1299 case GDK_2BUTTON_PRESS:
1300 case GDK_3BUTTON_PRESS:
1301 case GDK_BUTTON_RELEASE:
1302 ev.motion.x += canvas->x0;
1303 ev.motion.y += canvas->y0;
1304 break;
1305 default:
1306 break;
1307 }
1309 /* Choose where we send the event */
1311 /* canvas->current_item becomes NULL in some cases under Win32
1312 ** (e.g. if the pointer leaves the window). So this is a hack that
1313 ** Lauris applied to SP to get around the problem.
1314 */
1315 SPCanvasItem* item = NULL;
1316 if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1317 item = canvas->grabbed_item;
1318 } else {
1319 item = canvas->current_item;
1320 }
1322 if (canvas->focused_item &&
1323 ((event->type == GDK_KEY_PRESS) ||
1324 (event->type == GDK_KEY_RELEASE) ||
1325 (event->type == GDK_FOCUS_CHANGE))) {
1326 item = canvas->focused_item;
1327 }
1329 /* The event is propagated up the hierarchy (for if someone connected to
1330 * a group instead of a leaf event), and emission is stopped if a
1331 * handler returns TRUE, just like for GtkWidget events.
1332 */
1334 gint finished = FALSE;
1336 while (item && !finished) {
1337 gtk_object_ref (GTK_OBJECT (item));
1338 gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1339 SPCanvasItem *parent = item->parent;
1340 gtk_object_unref (GTK_OBJECT (item));
1341 item = parent;
1342 }
1344 return finished;
1345 }
1347 /**
1348 * Helper that re-picks the current item in the canvas, based on the event's
1349 * coordinates and emits enter/leave events for items as appropriate.
1350 */
1351 static int
1352 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1353 {
1354 int button_down = 0;
1355 double x, y;
1357 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1358 return FALSE;
1360 int retval = FALSE;
1362 if (canvas->gen_all_enter_events == false) {
1363 // If a button is down, we'll perform enter and leave events on the
1364 // current item, but not enter on any other item. This is more or
1365 // less like X pointer grabbing for canvas items.
1366 //
1367 button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1368 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1370 if (!button_down) canvas->left_grabbed_item = FALSE;
1371 }
1373 /* Save the event in the canvas. This is used to synthesize enter and
1374 * leave events in case the current item changes. It is also used to
1375 * re-pick the current item if the current one gets deleted. Also,
1376 * synthesize an enter event.
1377 */
1378 if (event != &canvas->pick_event) {
1379 if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1380 /* these fields have the same offsets in both types of events */
1382 canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY;
1383 canvas->pick_event.crossing.window = event->motion.window;
1384 canvas->pick_event.crossing.send_event = event->motion.send_event;
1385 canvas->pick_event.crossing.subwindow = NULL;
1386 canvas->pick_event.crossing.x = event->motion.x;
1387 canvas->pick_event.crossing.y = event->motion.y;
1388 canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL;
1389 canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR;
1390 canvas->pick_event.crossing.focus = FALSE;
1391 canvas->pick_event.crossing.state = event->motion.state;
1393 /* these fields don't have the same offsets in both types of events */
1395 if (event->type == GDK_MOTION_NOTIFY) {
1396 canvas->pick_event.crossing.x_root = event->motion.x_root;
1397 canvas->pick_event.crossing.y_root = event->motion.y_root;
1398 } else {
1399 canvas->pick_event.crossing.x_root = event->button.x_root;
1400 canvas->pick_event.crossing.y_root = event->button.y_root;
1401 }
1402 } else {
1403 canvas->pick_event = *event;
1404 }
1405 }
1407 /* Don't do anything else if this is a recursive call */
1408 if (canvas->in_repick) return retval;
1410 /* LeaveNotify means that there is no current item, so we don't look for one */
1411 if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1412 /* these fields don't have the same offsets in both types of events */
1414 if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1415 x = canvas->pick_event.crossing.x;
1416 y = canvas->pick_event.crossing.y;
1417 } else {
1418 x = canvas->pick_event.motion.x;
1419 y = canvas->pick_event.motion.y;
1420 }
1422 /* world coords */
1423 x += canvas->x0;
1424 y += canvas->y0;
1426 /* find the closest item */
1427 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1428 sp_canvas_item_invoke_point (canvas->root, Geom::Point(x, y), &canvas->new_current_item);
1429 } else {
1430 canvas->new_current_item = NULL;
1431 }
1432 } else {
1433 canvas->new_current_item = NULL;
1434 }
1436 if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1437 return retval; /* current item did not change */
1438 }
1440 /* Synthesize events for old and new current items */
1442 if ((canvas->new_current_item != canvas->current_item)
1443 && (canvas->current_item != NULL)
1444 && !canvas->left_grabbed_item) {
1445 GdkEvent new_event;
1446 SPCanvasItem *item;
1448 item = canvas->current_item;
1450 new_event = canvas->pick_event;
1451 new_event.type = GDK_LEAVE_NOTIFY;
1453 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1454 new_event.crossing.subwindow = NULL;
1455 canvas->in_repick = TRUE;
1456 retval = emit_event (canvas, &new_event);
1457 canvas->in_repick = FALSE;
1458 }
1460 if (canvas->gen_all_enter_events == false) {
1461 // new_current_item may have been set to NULL during the call to
1462 // emit_event() above
1463 if ((canvas->new_current_item != canvas->current_item) && button_down) {
1464 canvas->left_grabbed_item = TRUE;
1465 return retval;
1466 }
1467 }
1469 /* Handle the rest of cases */
1471 canvas->left_grabbed_item = FALSE;
1472 canvas->current_item = canvas->new_current_item;
1474 if (canvas->current_item != NULL) {
1475 GdkEvent new_event;
1477 new_event = canvas->pick_event;
1478 new_event.type = GDK_ENTER_NOTIFY;
1479 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1480 new_event.crossing.subwindow = NULL;
1481 retval = emit_event (canvas, &new_event);
1482 }
1484 return retval;
1485 }
1487 /**
1488 * Button event handler for the canvas.
1489 */
1490 static gint
1491 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1492 {
1493 SPCanvas *canvas = SP_CANVAS (widget);
1495 int retval = FALSE;
1497 /* dispatch normally regardless of the event's window if an item has
1498 has a pointer grab in effect */
1499 if (!canvas->grabbed_item &&
1500 event->window != SP_CANVAS_WINDOW (canvas))
1501 return retval;
1503 int mask;
1504 switch (event->button) {
1505 case 1:
1506 mask = GDK_BUTTON1_MASK;
1507 break;
1508 case 2:
1509 mask = GDK_BUTTON2_MASK;
1510 break;
1511 case 3:
1512 mask = GDK_BUTTON3_MASK;
1513 break;
1514 case 4:
1515 mask = GDK_BUTTON4_MASK;
1516 break;
1517 case 5:
1518 mask = GDK_BUTTON5_MASK;
1519 break;
1520 default:
1521 mask = 0;
1522 }
1524 switch (event->type) {
1525 case GDK_BUTTON_PRESS:
1526 case GDK_2BUTTON_PRESS:
1527 case GDK_3BUTTON_PRESS:
1528 /* Pick the current item as if the button were not pressed, and
1529 * then process the event.
1530 */
1531 canvas->state = event->state;
1532 pick_current_item (canvas, (GdkEvent *) event);
1533 canvas->state ^= mask;
1534 retval = emit_event (canvas, (GdkEvent *) event);
1535 break;
1537 case GDK_BUTTON_RELEASE:
1538 /* Process the event as if the button were pressed, then repick
1539 * after the button has been released
1540 */
1541 canvas->state = event->state;
1542 retval = emit_event (canvas, (GdkEvent *) event);
1543 event->state ^= mask;
1544 canvas->state = event->state;
1545 pick_current_item (canvas, (GdkEvent *) event);
1546 event->state ^= mask;
1548 break;
1550 default:
1551 g_assert_not_reached ();
1552 }
1554 return retval;
1555 }
1557 /**
1558 * Scroll event handler for the canvas.
1559 *
1560 * \todo FIXME: generate motion events to re-select items.
1561 */
1562 static gint
1563 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1564 {
1565 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1566 }
1568 static inline void request_motions(GdkWindow *w, GdkEventMotion *event) {
1569 gdk_window_get_pointer(w, NULL, NULL, NULL);
1570 #if HAS_GDK_EVENT_REQUEST_MOTIONS
1571 gdk_event_request_motions(event);
1572 #endif
1573 }
1575 /**
1576 * Motion event handler for the canvas.
1577 */
1578 static int
1579 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1580 {
1581 int status;
1582 SPCanvas *canvas = SP_CANVAS (widget);
1584 track_latency((GdkEvent *)event);
1586 if (event->window != SP_CANVAS_WINDOW (canvas))
1587 return FALSE;
1589 if (canvas->pixmap_gc == NULL) // canvas being deleted
1590 return FALSE;
1592 canvas->state = event->state;
1593 pick_current_item (canvas, (GdkEvent *) event);
1594 status = emit_event (canvas, (GdkEvent *) event);
1595 if (event->is_hint) {
1596 request_motions(widget->window, event);
1597 }
1599 return status;
1600 }
1602 static void
1603 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)
1604 {
1605 GtkWidget *widget = GTK_WIDGET (canvas);
1607 SPCanvasBuf buf;
1608 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1609 buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1610 } else {
1611 buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1612 }
1614 // Mark the region clean
1615 sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1617 buf.buf_rowstride = sw * 4;
1618 buf.rect.x0 = x0;
1619 buf.rect.y0 = y0;
1620 buf.rect.x1 = x1;
1621 buf.rect.y1 = y1;
1622 buf.visible_rect.x0 = draw_x1;
1623 buf.visible_rect.y0 = draw_y1;
1624 buf.visible_rect.x1 = draw_x2;
1625 buf.visible_rect.y1 = draw_y2;
1626 GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1627 buf.bg_color = (((color->red & 0xff00) << 8)
1628 | (color->green & 0xff00)
1629 | (color->blue >> 8));
1630 buf.is_empty = true;
1632 buf.ct = nr_create_cairo_context_canvasbuf (&(buf.visible_rect), &buf);
1634 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1635 SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1636 }
1638 #if ENABLE_LCMS
1639 cmsHTRANSFORM transf = 0;
1640 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1641 bool fromDisplay = prefs->getBool( "/options/displayprofile/from_display");
1642 if ( fromDisplay ) {
1643 transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" );
1644 } else {
1645 transf = Inkscape::colorprofile_get_display_transform();
1646 }
1647 #endif // ENABLE_LCMS
1649 if (buf.is_empty) {
1650 #if ENABLE_LCMS
1651 if ( transf && canvas->enable_cms_display_adj ) {
1652 cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1653 }
1654 #endif // ENABLE_LCMS
1655 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1656 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1657 canvas->pixmap_gc,
1658 TRUE,
1659 x0 - canvas->x0, y0 - canvas->y0,
1660 x1 - x0, y1 - y0);
1661 } else {
1663 #if ENABLE_LCMS
1664 if ( transf && canvas->enable_cms_display_adj ) {
1665 for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1666 guchar* p = buf.buf + (buf.buf_rowstride * yy);
1667 cmsDoTransform( transf, p, p, (x1 - x0) );
1668 }
1669 }
1670 #endif // ENABLE_LCMS
1672 // Now we only need to output the prepared pixmap to the actual screen, and this define chooses one
1673 // of the two ways to do it. The cairo way is direct and straightforward, but unfortunately
1674 // noticeably slower. I asked Carl Worth but he was unable so far to suggest any specific reason
1675 // for this slowness. So, for now we use the oldish method: squeeze out 32bpp buffer to 24bpp and
1676 // use gdk_draw_rgb_image_dithalign, for unfortunately gdk can only handle 24 bpp, which cairo
1677 // cannot handle at all. Still, this way is currently faster even despite the blit with squeeze.
1679 ///#define CANVAS_OUTPUT_VIA_CAIRO
1681 #ifdef CANVAS_OUTPUT_VIA_CAIRO
1683 buf.cst = cairo_image_surface_create_for_data (
1684 buf.buf,
1685 CAIRO_FORMAT_ARGB32, // unpacked, i.e. 32 bits! one byte is unused
1686 x1 - x0, y1 - y0,
1687 buf.buf_rowstride
1688 );
1689 cairo_t *window_ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1690 cairo_set_source_surface (window_ct, buf.cst, x0 - canvas->x0, y0 - canvas->y0);
1691 cairo_paint (window_ct);
1692 cairo_destroy (window_ct);
1693 cairo_surface_finish (buf.cst);
1694 cairo_surface_destroy (buf.cst);
1696 #else
1698 NRPixBlock b3;
1699 nr_pixblock_setup_fast (&b3, NR_PIXBLOCK_MODE_R8G8B8, x0, y0, x1, y1, TRUE);
1701 NRPixBlock b4;
1702 nr_pixblock_setup_extern (&b4, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1,
1703 buf.buf,
1704 buf.buf_rowstride,
1705 FALSE, FALSE);
1707 // this does the 32->24 squishing, using an assembler routine:
1708 nr_blit_pixblock_pixblock (&b3, &b4);
1710 gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1711 canvas->pixmap_gc,
1712 x0 - canvas->x0, y0 - canvas->y0,
1713 x1 - x0, y1 - y0,
1714 GDK_RGB_DITHER_MAX,
1715 NR_PIXBLOCK_PX(&b3),
1716 sw * 3,
1717 x0 - canvas->x0, y0 - canvas->y0);
1719 nr_pixblock_release (&b3);
1720 nr_pixblock_release (&b4);
1721 #endif
1722 }
1724 cairo_surface_t *cst = cairo_get_target(buf.ct);
1725 cairo_destroy (buf.ct);
1726 cairo_surface_finish (cst);
1727 cairo_surface_destroy (cst);
1729 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1730 nr_pixelstore_256K_free (buf.buf);
1731 } else {
1732 nr_pixelstore_1M_free (buf.buf);
1733 }
1734 }
1736 struct PaintRectSetup {
1737 SPCanvas* canvas;
1738 NRRectL big_rect;
1739 GTimeVal start_time;
1740 int max_pixels;
1741 Geom::Point mouse_loc;
1742 };
1744 /**
1745 * Paint the given rect, recursively subdividing the region until it is the size of a single
1746 * buffer.
1747 *
1748 * @return true if the drawing completes
1749 */
1750 static int
1751 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1752 {
1753 GTimeVal now;
1754 g_get_current_time (&now);
1756 glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1757 + (now.tv_usec - setup->start_time.tv_usec);
1759 // Allow only very fast buffers to be run together;
1760 // as soon as the total redraw time exceeds 1ms, cancel;
1761 // this returns control to the idle loop and allows Inkscape to process user input
1762 // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1763 // it will get back and finish painting what remains to paint.
1764 if (elapsed > 1000) {
1766 // Interrupting redraw isn't always good.
1767 // For example, when you drag one node of a big path, only the buffer containing
1768 // the mouse cursor will be redrawn again and again, and the rest of the path
1769 // will remain stale because Inkscape never has enough idle time to redraw all
1770 // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1771 // If this limit is set, and if we have aborted redraw more times than is allowed,
1772 // interrupting is blocked and we're forced to redraw full screen once
1773 // (after which we can again interrupt forced_redraw_limit times).
1774 if (setup->canvas->forced_redraw_limit < 0 ||
1775 setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1777 if (setup->canvas->forced_redraw_limit != -1) {
1778 setup->canvas->forced_redraw_count++;
1779 }
1781 return false;
1782 }
1783 }
1785 // Find the optimal buffer dimensions
1786 int bw = this_rect.x1 - this_rect.x0;
1787 int bh = this_rect.y1 - this_rect.y0;
1788 if ((bw < 1) || (bh < 1))
1789 return 0;
1791 if (bw * bh < setup->max_pixels) {
1792 // We are small enough
1793 sp_canvas_paint_single_buffer (setup->canvas,
1794 this_rect.x0, this_rect.y0,
1795 this_rect.x1, this_rect.y1,
1796 setup->big_rect.x0, setup->big_rect.y0,
1797 setup->big_rect.x1, setup->big_rect.y1, bw);
1798 return 1;
1799 }
1801 NRRectL lo = this_rect;
1802 NRRectL hi = this_rect;
1804 /*
1805 This test determines the redraw strategy:
1807 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1808 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1809 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1810 and seems to be faster for drawings with many smaller objects at zoom-out.
1812 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1813 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1814 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1815 faster.
1817 The default for now is the strips mode.
1818 */
1819 if (bw < bh || bh < 2 * TILE_SIZE) {
1820 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1821 int mid = ((long long) this_rect.x0 + (long long) this_rect.x1) / 2;
1822 // Make sure that mid lies on a tile boundary
1823 mid = (mid / TILE_SIZE) * TILE_SIZE;
1825 lo.x1 = mid;
1826 hi.x0 = mid;
1828 if (setup->mouse_loc[Geom::X] < mid) {
1829 // Always paint towards the mouse first
1830 return sp_canvas_paint_rect_internal(setup, lo)
1831 && sp_canvas_paint_rect_internal(setup, hi);
1832 } else {
1833 return sp_canvas_paint_rect_internal(setup, hi)
1834 && sp_canvas_paint_rect_internal(setup, lo);
1835 }
1836 } else {
1837 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1838 int mid = ((long long) this_rect.y0 + (long long) this_rect.y1) / 2;
1839 // Make sure that mid lies on a tile boundary
1840 mid = (mid / TILE_SIZE) * TILE_SIZE;
1842 lo.y1 = mid;
1843 hi.y0 = mid;
1845 if (setup->mouse_loc[Geom::Y] < mid) {
1846 // Always paint towards the mouse first
1847 return sp_canvas_paint_rect_internal(setup, lo)
1848 && sp_canvas_paint_rect_internal(setup, hi);
1849 } else {
1850 return sp_canvas_paint_rect_internal(setup, hi)
1851 && sp_canvas_paint_rect_internal(setup, lo);
1852 }
1853 }
1854 }
1857 /**
1858 * Helper that draws a specific rectangular part of the canvas.
1859 *
1860 * @return true if the rectangle painting succeeds.
1861 */
1862 static bool
1863 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1864 {
1865 g_return_val_if_fail (!canvas->need_update, false);
1867 NRRectL rect;
1868 rect.x0 = xx0;
1869 rect.x1 = xx1;
1870 rect.y0 = yy0;
1871 rect.y1 = yy1;
1873 // Clip rect-to-draw by the current visible area
1874 rect.x0 = MAX (rect.x0, canvas->x0);
1875 rect.y0 = MAX (rect.y0, canvas->y0);
1876 rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1877 rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1879 #ifdef DEBUG_REDRAW
1880 // paint the area to redraw yellow
1881 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1882 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1883 canvas->pixmap_gc,
1884 TRUE,
1885 rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1886 rect.x1 - rect.x0, rect.y1 - rect.y0);
1887 #endif
1889 PaintRectSetup setup;
1891 setup.canvas = canvas;
1892 setup.big_rect = rect;
1894 // Save the mouse location
1895 gint x, y;
1896 gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1897 setup.mouse_loc = sp_canvas_window_to_world (canvas, Geom::Point(x,y));
1899 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1900 // use 256K as a compromise to not slow down gradients
1901 // 256K is the cached buffer and we need 4 channels
1902 setup.max_pixels = 65536; // 256K/4
1903 } else {
1904 // paths only, so 1M works faster
1905 // 1M is the cached buffer and we need 4 channels
1906 setup.max_pixels = 262144;
1907 }
1909 // Start the clock
1910 g_get_current_time(&(setup.start_time));
1912 // Go
1913 return sp_canvas_paint_rect_internal(&setup, rect);
1914 }
1916 /**
1917 * Force a full redraw after a specified number of interrupted redraws
1918 */
1919 void
1920 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1921 g_return_if_fail(canvas != NULL);
1923 canvas->forced_redraw_limit = count;
1924 canvas->forced_redraw_count = 0;
1925 }
1927 /**
1928 * End forced full redraw requests
1929 */
1930 void
1931 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1932 g_return_if_fail(canvas != NULL);
1934 canvas->forced_redraw_limit = -1;
1935 }
1937 /**
1938 * The canvas widget's expose callback.
1939 */
1940 static gint
1941 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1942 {
1943 SPCanvas *canvas = SP_CANVAS (widget);
1945 if (!GTK_WIDGET_DRAWABLE (widget) ||
1946 (event->window != SP_CANVAS_WINDOW (canvas)))
1947 return FALSE;
1949 int n_rects;
1950 GdkRectangle *rects;
1951 gdk_region_get_rectangles (event->region, &rects, &n_rects);
1953 for (int i = 0; i < n_rects; i++) {
1954 NRRectL rect;
1956 rect.x0 = rects[i].x + canvas->x0;
1957 rect.y0 = rects[i].y + canvas->y0;
1958 rect.x1 = rect.x0 + rects[i].width;
1959 rect.y1 = rect.y0 + rects[i].height;
1961 sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1962 }
1964 if (n_rects > 0)
1965 g_free (rects);
1967 return FALSE;
1968 }
1970 /**
1971 * The canvas widget's keypress callback.
1972 */
1973 static gint
1974 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1975 {
1976 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1977 }
1979 /**
1980 * Crossing event handler for the canvas.
1981 */
1982 static gint
1983 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1984 {
1985 SPCanvas *canvas = SP_CANVAS (widget);
1987 if (event->window != SP_CANVAS_WINDOW (canvas))
1988 return FALSE;
1990 canvas->state = event->state;
1991 return pick_current_item (canvas, (GdkEvent *) event);
1992 }
1994 /**
1995 * Focus in handler for the canvas.
1996 */
1997 static gint
1998 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
1999 {
2000 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
2002 SPCanvas *canvas = SP_CANVAS (widget);
2004 if (canvas->focused_item) {
2005 return emit_event (canvas, (GdkEvent *) event);
2006 } else {
2007 return FALSE;
2008 }
2009 }
2011 /**
2012 * Focus out handler for the canvas.
2013 */
2014 static gint
2015 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
2016 {
2017 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2019 SPCanvas *canvas = SP_CANVAS (widget);
2021 if (canvas->focused_item)
2022 return emit_event (canvas, (GdkEvent *) event);
2023 else
2024 return FALSE;
2025 }
2027 /**
2028 * Helper that repaints the areas in the canvas that need it.
2029 *
2030 * @return true if all the dirty parts have been redrawn
2031 */
2032 static int
2033 paint (SPCanvas *canvas)
2034 {
2035 if (canvas->need_update) {
2036 sp_canvas_item_invoke_update (canvas->root, Geom::identity(), 0);
2037 canvas->need_update = FALSE;
2038 }
2040 if (!canvas->need_redraw)
2041 return TRUE;
2043 Gdk::Region to_paint;
2045 for (int j=canvas->tTop; j<canvas->tBottom; j++) {
2046 for (int i=canvas->tLeft; i<canvas->tRight; i++) {
2047 int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
2049 if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
2050 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
2051 TILE_SIZE, TILE_SIZE));
2052 }
2054 }
2055 }
2057 if (!to_paint.empty()) {
2058 Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
2059 typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
2060 for (Iter i=rect.begin(); i != rect.end(); ++i) {
2061 int x0 = (*i).get_x();
2062 int y0 = (*i).get_y();
2063 int x1 = x0 + (*i).get_width();
2064 int y1 = y0 + (*i).get_height();
2065 if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
2066 // Aborted
2067 return FALSE;
2068 };
2069 }
2070 }
2072 canvas->need_redraw = FALSE;
2074 // we've had a full unaborted redraw, reset the full redraw counter
2075 if (canvas->forced_redraw_limit != -1) {
2076 canvas->forced_redraw_count = 0;
2077 }
2079 return TRUE;
2080 }
2082 /**
2083 * Helper that invokes update, paint, and repick on canvas.
2084 */
2085 static int
2086 do_update (SPCanvas *canvas)
2087 {
2088 if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop durring interrupted display!
2089 return TRUE;
2091 if (canvas->drawing_disabled)
2092 return TRUE;
2094 /* Cause the update if necessary */
2095 if (canvas->need_update) {
2096 sp_canvas_item_invoke_update (canvas->root, Geom::identity(), 0);
2097 canvas->need_update = FALSE;
2098 }
2100 /* Paint if able to */
2101 if (GTK_WIDGET_DRAWABLE (canvas)) {
2102 return paint (canvas);
2103 }
2105 /* Pick new current item */
2106 while (canvas->need_repick) {
2107 canvas->need_repick = FALSE;
2108 pick_current_item (canvas, &canvas->pick_event);
2109 }
2111 return TRUE;
2112 }
2114 /**
2115 * Idle handler for the canvas that deals with pending updates and redraws.
2116 */
2117 static gint
2118 idle_handler (gpointer data)
2119 {
2120 GDK_THREADS_ENTER ();
2122 SPCanvas *canvas = SP_CANVAS (data);
2124 int const ret = do_update (canvas);
2126 if (ret) {
2127 /* Reset idle id */
2128 canvas->idle_id = 0;
2129 }
2131 GDK_THREADS_LEAVE ();
2133 return !ret;
2134 }
2136 /**
2137 * Convenience function to add an idle handler to a canvas.
2138 */
2139 static void
2140 add_idle (SPCanvas *canvas)
2141 {
2142 if (canvas->idle_id != 0)
2143 return;
2145 canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2146 }
2148 /**
2149 * Returns the root group of the specified canvas.
2150 */
2151 SPCanvasGroup *
2152 sp_canvas_root (SPCanvas *canvas)
2153 {
2154 g_return_val_if_fail (canvas != NULL, NULL);
2155 g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2157 return SP_CANVAS_GROUP (canvas->root);
2158 }
2160 /**
2161 * Scrolls canvas to specific position (cx and cy are measured in screen pixels)
2162 */
2163 void
2164 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2165 {
2166 g_return_if_fail (canvas != NULL);
2167 g_return_if_fail (SP_IS_CANVAS (canvas));
2169 int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels)
2170 int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do!
2171 int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the
2172 int dy = iy - canvas->y0; // canvas w.r.t its previous position
2174 canvas->dx0 = cx; // here the 'd' stands for double, not delta!
2175 canvas->dy0 = cy;
2176 canvas->x0 = ix;
2177 canvas->y0 = iy;
2179 sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2181 if (!clear) {
2182 // scrolling without zoom; redraw only the newly exposed areas
2183 if ((dx != 0) || (dy != 0)) {
2184 canvas->is_scrolling = is_scrolling;
2185 if (GTK_WIDGET_REALIZED (canvas)) {
2186 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2187 }
2188 }
2189 } else {
2190 // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2191 }
2192 }
2194 /**
2195 * Updates canvas if necessary.
2196 */
2197 void
2198 sp_canvas_update_now (SPCanvas *canvas)
2199 {
2200 g_return_if_fail (canvas != NULL);
2201 g_return_if_fail (SP_IS_CANVAS (canvas));
2203 if (!(canvas->need_update ||
2204 canvas->need_redraw))
2205 return;
2207 do_update (canvas);
2208 }
2210 /**
2211 * Update callback for canvas widget.
2212 */
2213 static void
2214 sp_canvas_request_update (SPCanvas *canvas)
2215 {
2216 canvas->need_update = TRUE;
2217 add_idle (canvas);
2218 }
2220 /**
2221 * Forces redraw of rectangular canvas area.
2222 */
2223 void
2224 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2225 {
2226 NRRectL bbox;
2227 NRRectL visible;
2228 NRRectL clip;
2230 g_return_if_fail (canvas != NULL);
2231 g_return_if_fail (SP_IS_CANVAS (canvas));
2233 if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2234 if ((x0 >= x1) || (y0 >= y1)) return;
2236 bbox.x0 = x0;
2237 bbox.y0 = y0;
2238 bbox.x1 = x1;
2239 bbox.y1 = y1;
2241 visible.x0 = canvas->x0;
2242 visible.y0 = canvas->y0;
2243 visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2244 visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2246 nr_rect_l_intersect (&clip, &bbox, &visible);
2248 sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2249 add_idle (canvas);
2250 }
2252 /**
2253 * Sets world coordinates from win and canvas.
2254 */
2255 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2256 {
2257 g_return_if_fail (canvas != NULL);
2258 g_return_if_fail (SP_IS_CANVAS (canvas));
2260 if (worldx) *worldx = canvas->x0 + winx;
2261 if (worldy) *worldy = canvas->y0 + winy;
2262 }
2264 /**
2265 * Sets win coordinates from world and canvas.
2266 */
2267 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2268 {
2269 g_return_if_fail (canvas != NULL);
2270 g_return_if_fail (SP_IS_CANVAS (canvas));
2272 if (winx) *winx = worldx - canvas->x0;
2273 if (winy) *winy = worldy - canvas->y0;
2274 }
2276 /**
2277 * Converts point from win to world coordinates.
2278 */
2279 Geom::Point sp_canvas_window_to_world(SPCanvas const *canvas, Geom::Point const win)
2280 {
2281 g_assert (canvas != NULL);
2282 g_assert (SP_IS_CANVAS (canvas));
2284 return Geom::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2285 }
2287 /**
2288 * Converts point from world to win coordinates.
2289 */
2290 Geom::Point sp_canvas_world_to_window(SPCanvas const *canvas, Geom::Point const world)
2291 {
2292 g_assert (canvas != NULL);
2293 g_assert (SP_IS_CANVAS (canvas));
2295 return Geom::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2296 }
2298 /**
2299 * Returns true if point given in world coordinates is inside window.
2300 */
2301 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, Geom::Point const &world)
2302 {
2303 g_assert( canvas != NULL );
2304 g_assert(SP_IS_CANVAS(canvas));
2306 GtkWidget const &w = *GTK_WIDGET(canvas);
2307 return ( ( canvas->x0 <= world[Geom::X] ) &&
2308 ( canvas->y0 <= world[Geom::Y] ) &&
2309 ( world[Geom::X] < canvas->x0 + w.allocation.width ) &&
2310 ( world[Geom::Y] < canvas->y0 + w.allocation.height ) );
2311 }
2313 /**
2314 * Return canvas window coordinates as Geom::Rect.
2315 */
2316 Geom::Rect SPCanvas::getViewbox() const
2317 {
2318 GtkWidget const *w = GTK_WIDGET(this);
2319 return Geom::Rect(Geom::Point(dx0, dy0),
2320 Geom::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2321 }
2323 /**
2324 * Return canvas window coordinates as IRect (a rectangle defined by integers).
2325 */
2326 NR::IRect SPCanvas::getViewboxIntegers() const
2327 {
2328 GtkWidget const *w = GTK_WIDGET(this);
2329 return NR::IRect(NR::IPoint(x0, y0),
2330 NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height));
2331 }
2333 inline int sp_canvas_tile_floor(int x)
2334 {
2335 return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2336 }
2338 inline int sp_canvas_tile_ceil(int x)
2339 {
2340 return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2341 }
2343 /**
2344 * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2345 */
2346 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2347 {
2348 if ( nl >= nr || nt >= nb ) {
2349 if ( canvas->tiles ) g_free(canvas->tiles);
2350 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2351 canvas->tileH=canvas->tileV=0;
2352 canvas->tiles=NULL;
2353 return;
2354 }
2355 int tl=sp_canvas_tile_floor(nl);
2356 int tt=sp_canvas_tile_floor(nt);
2357 int tr=sp_canvas_tile_ceil(nr);
2358 int tb=sp_canvas_tile_ceil(nb);
2360 int nh = tr-tl, nv = tb-tt;
2361 uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2362 for (int i=tl; i<tr; i++) {
2363 for (int j=tt; j<tb; j++) {
2364 int ind = (i-tl) + (j-tt)*nh;
2365 if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2366 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2367 } else {
2368 ntiles[ind]=0; // newly exposed areas get 0
2369 }
2370 }
2371 }
2372 if ( canvas->tiles ) g_free(canvas->tiles);
2373 canvas->tiles=ntiles;
2374 canvas->tLeft=tl;
2375 canvas->tTop=tt;
2376 canvas->tRight=tr;
2377 canvas->tBottom=tb;
2378 canvas->tileH=nh;
2379 canvas->tileV=nv;
2380 }
2382 /*
2383 * Helper that queues a canvas rectangle for redraw
2384 */
2385 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2386 canvas->need_redraw = TRUE;
2388 sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2389 }
2391 /**
2392 * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2393 */
2394 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2395 {
2396 if ( nl >= nr || nt >= nb ) {
2397 return;
2398 }
2399 int tl=sp_canvas_tile_floor(nl);
2400 int tt=sp_canvas_tile_floor(nt);
2401 int tr=sp_canvas_tile_ceil(nr);
2402 int tb=sp_canvas_tile_ceil(nb);
2403 if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2404 if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2405 if ( tr > canvas->tRight ) tr=canvas->tRight;
2406 if ( tt < canvas->tTop ) tt=canvas->tTop;
2407 if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2409 for (int i=tl; i<tr; i++) {
2410 for (int j=tt; j<tb; j++) {
2411 canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2412 }
2413 }
2414 }
2417 /*
2418 Local Variables:
2419 mode:c++
2420 c-file-style:"stroustrup"
2421 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2422 indent-tabs-mode:nil
2423 fill-column:99
2424 End:
2425 */
2426 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :