aee53838f45275e72daf006ea3f036d6fabe7a3c
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->tiles=NULL;
1033 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1034 canvas->tileH=canvas->tileV=0;
1036 canvas->forced_redraw_count = 0;
1037 canvas->forced_redraw_limit = -1;
1039 #if ENABLE_LCMS
1040 canvas->enable_cms_display_adj = false;
1041 canvas->cms_key = new Glib::ustring("");
1042 #endif // ENABLE_LCMS
1044 canvas->is_scrolling = false;
1045 }
1047 /**
1048 * Convenience function to remove the idle handler of a canvas.
1049 */
1050 static void
1051 remove_idle (SPCanvas *canvas)
1052 {
1053 if (canvas->idle_id) {
1054 gtk_idle_remove (canvas->idle_id);
1055 canvas->idle_id = 0;
1056 }
1057 }
1059 /*
1060 * Removes the transient state of the canvas (idle handler, grabs).
1061 */
1062 static void
1063 shutdown_transients (SPCanvas *canvas)
1064 {
1065 /* We turn off the need_redraw flag, since if the canvas is mapped again
1066 * it will request a redraw anyways. We do not turn off the need_update
1067 * flag, though, because updates are not queued when the canvas remaps
1068 * itself.
1069 */
1070 if (canvas->need_redraw) {
1071 canvas->need_redraw = FALSE;
1072 }
1073 if ( canvas->tiles ) g_free(canvas->tiles);
1074 canvas->tiles=NULL;
1075 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1076 canvas->tileH=canvas->tileV=0;
1078 if (canvas->grabbed_item) {
1079 canvas->grabbed_item = NULL;
1080 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1081 }
1083 remove_idle (canvas);
1084 }
1086 /**
1087 * Destroy handler for SPCanvas.
1088 */
1089 static void
1090 sp_canvas_destroy (GtkObject *object)
1091 {
1092 SPCanvas *canvas = SP_CANVAS (object);
1094 if (canvas->root) {
1095 gtk_object_unref (GTK_OBJECT (canvas->root));
1096 canvas->root = NULL;
1097 }
1099 shutdown_transients (canvas);
1101 if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1102 (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1103 }
1105 static void track_latency(GdkEvent const *event) {
1106 GdkEventLatencyTracker &tracker = GdkEventLatencyTracker::default_tracker();
1107 boost::optional<double> latency = tracker.process(event);
1108 if (latency && *latency > 2.0) {
1109 //g_warning("Event latency reached %f sec (%1.4f)", *latency, tracker.getSkew());
1110 }
1111 }
1113 /**
1114 * Returns new canvas as widget.
1115 */
1116 GtkWidget *
1117 sp_canvas_new_aa (void)
1118 {
1119 SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1121 return (GtkWidget *) canvas;
1122 }
1124 /**
1125 * The canvas widget's realize callback.
1126 */
1127 static void
1128 sp_canvas_realize (GtkWidget *widget)
1129 {
1130 SPCanvas *canvas = SP_CANVAS (widget);
1132 GdkWindowAttr attributes;
1133 attributes.window_type = GDK_WINDOW_CHILD;
1134 attributes.x = widget->allocation.x;
1135 attributes.y = widget->allocation.y;
1136 attributes.width = widget->allocation.width;
1137 attributes.height = widget->allocation.height;
1138 attributes.wclass = GDK_INPUT_OUTPUT;
1139 attributes.visual = gdk_rgb_get_visual ();
1140 attributes.colormap = gdk_rgb_get_cmap ();
1141 attributes.event_mask = (gtk_widget_get_events (widget) |
1142 GDK_EXPOSURE_MASK |
1143 GDK_BUTTON_PRESS_MASK |
1144 GDK_BUTTON_RELEASE_MASK |
1145 GDK_POINTER_MOTION_MASK |
1146 ( HAS_BROKEN_MOTION_HINTS ?
1147 0 : GDK_POINTER_MOTION_HINT_MASK ) |
1148 GDK_PROXIMITY_IN_MASK |
1149 GDK_PROXIMITY_OUT_MASK |
1150 GDK_KEY_PRESS_MASK |
1151 GDK_KEY_RELEASE_MASK |
1152 GDK_ENTER_NOTIFY_MASK |
1153 GDK_LEAVE_NOTIFY_MASK |
1154 GDK_FOCUS_CHANGE_MASK);
1155 gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1157 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1158 gdk_window_set_user_data (widget->window, widget);
1160 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1161 if ( prefs->getBool("/options/useextinput/value", true) )
1162 gtk_widget_set_events(widget, attributes.event_mask);
1164 widget->style = gtk_style_attach (widget->style, widget->window);
1166 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1168 canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1169 }
1171 /**
1172 * The canvas widget's unrealize callback.
1173 */
1174 static void
1175 sp_canvas_unrealize (GtkWidget *widget)
1176 {
1177 SPCanvas *canvas = SP_CANVAS (widget);
1179 canvas->current_item = NULL;
1180 canvas->grabbed_item = NULL;
1181 canvas->focused_item = NULL;
1183 shutdown_transients (canvas);
1185 gdk_gc_destroy (canvas->pixmap_gc);
1186 canvas->pixmap_gc = NULL;
1188 if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1189 (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1190 }
1192 /**
1193 * The canvas widget's size_request callback.
1194 */
1195 static void
1196 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1197 {
1198 static_cast<void>(SP_CANVAS (widget));
1200 req->width = 256;
1201 req->height = 256;
1202 }
1204 /**
1205 * The canvas widget's size_allocate callback.
1206 */
1207 static void
1208 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1209 {
1210 SPCanvas *canvas = SP_CANVAS (widget);
1212 /* Schedule redraw of new region */
1213 sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1214 if (allocation->width > widget->allocation.width) {
1215 sp_canvas_request_redraw (canvas,
1216 canvas->x0 + widget->allocation.width,
1217 0,
1218 canvas->x0 + allocation->width,
1219 canvas->y0 + allocation->height);
1220 }
1221 if (allocation->height > widget->allocation.height) {
1222 sp_canvas_request_redraw (canvas,
1223 0,
1224 canvas->y0 + widget->allocation.height,
1225 canvas->x0 + allocation->width,
1226 canvas->y0 + allocation->height);
1227 }
1229 widget->allocation = *allocation;
1231 if (GTK_WIDGET_REALIZED (widget)) {
1232 gdk_window_move_resize (widget->window,
1233 widget->allocation.x, widget->allocation.y,
1234 widget->allocation.width, widget->allocation.height);
1235 }
1236 }
1238 /**
1239 * Helper that emits an event for an item in the canvas, be it the current
1240 * item, grabbed item, or focused item, as appropriate.
1241 */
1242 static int
1243 emit_event (SPCanvas *canvas, GdkEvent *event)
1244 {
1245 guint mask;
1247 if (canvas->grabbed_item) {
1248 switch (event->type) {
1249 case GDK_ENTER_NOTIFY:
1250 mask = GDK_ENTER_NOTIFY_MASK;
1251 break;
1252 case GDK_LEAVE_NOTIFY:
1253 mask = GDK_LEAVE_NOTIFY_MASK;
1254 break;
1255 case GDK_MOTION_NOTIFY:
1256 mask = GDK_POINTER_MOTION_MASK;
1257 break;
1258 case GDK_BUTTON_PRESS:
1259 case GDK_2BUTTON_PRESS:
1260 case GDK_3BUTTON_PRESS:
1261 mask = GDK_BUTTON_PRESS_MASK;
1262 break;
1263 case GDK_BUTTON_RELEASE:
1264 mask = GDK_BUTTON_RELEASE_MASK;
1265 break;
1266 case GDK_KEY_PRESS:
1267 mask = GDK_KEY_PRESS_MASK;
1268 break;
1269 case GDK_KEY_RELEASE:
1270 mask = GDK_KEY_RELEASE_MASK;
1271 break;
1272 case GDK_SCROLL:
1273 mask = GDK_SCROLL;
1274 break;
1275 default:
1276 mask = 0;
1277 break;
1278 }
1280 if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1281 }
1283 /* Convert to world coordinates -- we have two cases because of different
1284 * offsets of the fields in the event structures.
1285 */
1287 GdkEvent ev = *event;
1289 switch (ev.type) {
1290 case GDK_ENTER_NOTIFY:
1291 case GDK_LEAVE_NOTIFY:
1292 ev.crossing.x += canvas->x0;
1293 ev.crossing.y += canvas->y0;
1294 break;
1295 case GDK_MOTION_NOTIFY:
1296 case GDK_BUTTON_PRESS:
1297 case GDK_2BUTTON_PRESS:
1298 case GDK_3BUTTON_PRESS:
1299 case GDK_BUTTON_RELEASE:
1300 ev.motion.x += canvas->x0;
1301 ev.motion.y += canvas->y0;
1302 break;
1303 default:
1304 break;
1305 }
1307 /* Choose where we send the event */
1309 /* canvas->current_item becomes NULL in some cases under Win32
1310 ** (e.g. if the pointer leaves the window). So this is a hack that
1311 ** Lauris applied to SP to get around the problem.
1312 */
1313 SPCanvasItem* item = NULL;
1314 if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1315 item = canvas->grabbed_item;
1316 } else {
1317 item = canvas->current_item;
1318 }
1320 if (canvas->focused_item &&
1321 ((event->type == GDK_KEY_PRESS) ||
1322 (event->type == GDK_KEY_RELEASE) ||
1323 (event->type == GDK_FOCUS_CHANGE))) {
1324 item = canvas->focused_item;
1325 }
1327 /* The event is propagated up the hierarchy (for if someone connected to
1328 * a group instead of a leaf event), and emission is stopped if a
1329 * handler returns TRUE, just like for GtkWidget events.
1330 */
1332 gint finished = FALSE;
1334 while (item && !finished) {
1335 gtk_object_ref (GTK_OBJECT (item));
1336 gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1337 SPCanvasItem *parent = item->parent;
1338 gtk_object_unref (GTK_OBJECT (item));
1339 item = parent;
1340 }
1342 return finished;
1343 }
1345 /**
1346 * Helper that re-picks the current item in the canvas, based on the event's
1347 * coordinates and emits enter/leave events for items as appropriate.
1348 */
1349 static int
1350 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1351 {
1352 int button_down = 0;
1353 double x, y;
1355 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1356 return FALSE;
1358 int retval = FALSE;
1360 if (canvas->gen_all_enter_events == false) {
1361 // If a button is down, we'll perform enter and leave events on the
1362 // current item, but not enter on any other item. This is more or
1363 // less like X pointer grabbing for canvas items.
1364 //
1365 button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1366 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1368 if (!button_down) canvas->left_grabbed_item = FALSE;
1369 }
1371 /* Save the event in the canvas. This is used to synthesize enter and
1372 * leave events in case the current item changes. It is also used to
1373 * re-pick the current item if the current one gets deleted. Also,
1374 * synthesize an enter event.
1375 */
1376 if (event != &canvas->pick_event) {
1377 if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1378 /* these fields have the same offsets in both types of events */
1380 canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY;
1381 canvas->pick_event.crossing.window = event->motion.window;
1382 canvas->pick_event.crossing.send_event = event->motion.send_event;
1383 canvas->pick_event.crossing.subwindow = NULL;
1384 canvas->pick_event.crossing.x = event->motion.x;
1385 canvas->pick_event.crossing.y = event->motion.y;
1386 canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL;
1387 canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR;
1388 canvas->pick_event.crossing.focus = FALSE;
1389 canvas->pick_event.crossing.state = event->motion.state;
1391 /* these fields don't have the same offsets in both types of events */
1393 if (event->type == GDK_MOTION_NOTIFY) {
1394 canvas->pick_event.crossing.x_root = event->motion.x_root;
1395 canvas->pick_event.crossing.y_root = event->motion.y_root;
1396 } else {
1397 canvas->pick_event.crossing.x_root = event->button.x_root;
1398 canvas->pick_event.crossing.y_root = event->button.y_root;
1399 }
1400 } else {
1401 canvas->pick_event = *event;
1402 }
1403 }
1405 /* Don't do anything else if this is a recursive call */
1406 if (canvas->in_repick) return retval;
1408 /* LeaveNotify means that there is no current item, so we don't look for one */
1409 if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1410 /* these fields don't have the same offsets in both types of events */
1412 if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1413 x = canvas->pick_event.crossing.x;
1414 y = canvas->pick_event.crossing.y;
1415 } else {
1416 x = canvas->pick_event.motion.x;
1417 y = canvas->pick_event.motion.y;
1418 }
1420 /* world coords */
1421 x += canvas->x0;
1422 y += canvas->y0;
1424 /* find the closest item */
1425 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1426 sp_canvas_item_invoke_point (canvas->root, Geom::Point(x, y), &canvas->new_current_item);
1427 } else {
1428 canvas->new_current_item = NULL;
1429 }
1430 } else {
1431 canvas->new_current_item = NULL;
1432 }
1434 if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1435 return retval; /* current item did not change */
1436 }
1438 /* Synthesize events for old and new current items */
1440 if ((canvas->new_current_item != canvas->current_item)
1441 && (canvas->current_item != NULL)
1442 && !canvas->left_grabbed_item) {
1443 GdkEvent new_event;
1444 SPCanvasItem *item;
1446 item = canvas->current_item;
1448 new_event = canvas->pick_event;
1449 new_event.type = GDK_LEAVE_NOTIFY;
1451 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1452 new_event.crossing.subwindow = NULL;
1453 canvas->in_repick = TRUE;
1454 retval = emit_event (canvas, &new_event);
1455 canvas->in_repick = FALSE;
1456 }
1458 if (canvas->gen_all_enter_events == false) {
1459 // new_current_item may have been set to NULL during the call to
1460 // emit_event() above
1461 if ((canvas->new_current_item != canvas->current_item) && button_down) {
1462 canvas->left_grabbed_item = TRUE;
1463 return retval;
1464 }
1465 }
1467 /* Handle the rest of cases */
1469 canvas->left_grabbed_item = FALSE;
1470 canvas->current_item = canvas->new_current_item;
1472 if (canvas->current_item != NULL) {
1473 GdkEvent new_event;
1475 new_event = canvas->pick_event;
1476 new_event.type = GDK_ENTER_NOTIFY;
1477 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1478 new_event.crossing.subwindow = NULL;
1479 retval = emit_event (canvas, &new_event);
1480 }
1482 return retval;
1483 }
1485 /**
1486 * Button event handler for the canvas.
1487 */
1488 static gint
1489 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1490 {
1491 SPCanvas *canvas = SP_CANVAS (widget);
1493 int retval = FALSE;
1495 /* dispatch normally regardless of the event's window if an item
1496 has a pointer grab in effect */
1497 if (!canvas->grabbed_item &&
1498 event->window != SP_CANVAS_WINDOW (canvas))
1499 return retval;
1501 int mask;
1502 switch (event->button) {
1503 case 1:
1504 mask = GDK_BUTTON1_MASK;
1505 break;
1506 case 2:
1507 mask = GDK_BUTTON2_MASK;
1508 break;
1509 case 3:
1510 mask = GDK_BUTTON3_MASK;
1511 break;
1512 case 4:
1513 mask = GDK_BUTTON4_MASK;
1514 break;
1515 case 5:
1516 mask = GDK_BUTTON5_MASK;
1517 break;
1518 default:
1519 mask = 0;
1520 }
1522 switch (event->type) {
1523 case GDK_BUTTON_PRESS:
1524 case GDK_2BUTTON_PRESS:
1525 case GDK_3BUTTON_PRESS:
1526 /* Pick the current item as if the button were not pressed, and
1527 * then process the event.
1528 */
1529 canvas->state = event->state;
1530 pick_current_item (canvas, (GdkEvent *) event);
1531 canvas->state ^= mask;
1532 retval = emit_event (canvas, (GdkEvent *) event);
1533 break;
1535 case GDK_BUTTON_RELEASE:
1536 /* Process the event as if the button were pressed, then repick
1537 * after the button has been released
1538 */
1539 canvas->state = event->state;
1540 retval = emit_event (canvas, (GdkEvent *) event);
1541 event->state ^= mask;
1542 canvas->state = event->state;
1543 pick_current_item (canvas, (GdkEvent *) event);
1544 event->state ^= mask;
1546 break;
1548 default:
1549 g_assert_not_reached ();
1550 }
1552 return retval;
1553 }
1555 /**
1556 * Scroll event handler for the canvas.
1557 *
1558 * \todo FIXME: generate motion events to re-select items.
1559 */
1560 static gint
1561 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1562 {
1563 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1564 }
1566 static inline void request_motions(GdkWindow *w, GdkEventMotion *event) {
1567 gdk_window_get_pointer(w, NULL, NULL, NULL);
1568 #if HAS_GDK_EVENT_REQUEST_MOTIONS
1569 gdk_event_request_motions(event);
1570 #endif
1571 }
1573 /**
1574 * Motion event handler for the canvas.
1575 */
1576 static int
1577 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1578 {
1579 int status;
1580 SPCanvas *canvas = SP_CANVAS (widget);
1582 track_latency((GdkEvent *)event);
1584 if (event->window != SP_CANVAS_WINDOW (canvas))
1585 return FALSE;
1587 if (canvas->pixmap_gc == NULL) // canvas being deleted
1588 return FALSE;
1590 canvas->state = event->state;
1591 pick_current_item (canvas, (GdkEvent *) event);
1592 status = emit_event (canvas, (GdkEvent *) event);
1593 if (event->is_hint) {
1594 request_motions(widget->window, event);
1595 }
1597 return status;
1598 }
1600 static void
1601 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)
1602 {
1603 GtkWidget *widget = GTK_WIDGET (canvas);
1605 SPCanvasBuf buf;
1606 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1607 buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1608 } else {
1609 buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1610 }
1612 // Mark the region clean
1613 sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1615 buf.buf_rowstride = sw * 4;
1616 buf.rect.x0 = x0;
1617 buf.rect.y0 = y0;
1618 buf.rect.x1 = x1;
1619 buf.rect.y1 = y1;
1620 buf.visible_rect.x0 = draw_x1;
1621 buf.visible_rect.y0 = draw_y1;
1622 buf.visible_rect.x1 = draw_x2;
1623 buf.visible_rect.y1 = draw_y2;
1624 GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1625 buf.bg_color = (((color->red & 0xff00) << 8)
1626 | (color->green & 0xff00)
1627 | (color->blue >> 8));
1628 buf.is_empty = true;
1630 buf.ct = nr_create_cairo_context_canvasbuf (&(buf.visible_rect), &buf);
1632 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1633 SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1634 }
1636 #if ENABLE_LCMS
1637 cmsHTRANSFORM transf = 0;
1638 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1639 bool fromDisplay = prefs->getBool( "/options/displayprofile/from_display");
1640 if ( fromDisplay ) {
1641 transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" );
1642 } else {
1643 transf = Inkscape::colorprofile_get_display_transform();
1644 }
1645 #endif // ENABLE_LCMS
1647 if (buf.is_empty) {
1648 #if ENABLE_LCMS
1649 if ( transf && canvas->enable_cms_display_adj ) {
1650 cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1651 }
1652 #endif // ENABLE_LCMS
1653 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1654 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1655 canvas->pixmap_gc,
1656 TRUE,
1657 x0 - canvas->x0, y0 - canvas->y0,
1658 x1 - x0, y1 - y0);
1659 } else {
1661 #if ENABLE_LCMS
1662 if ( transf && canvas->enable_cms_display_adj ) {
1663 for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1664 guchar* p = buf.buf + (buf.buf_rowstride * yy);
1665 cmsDoTransform( transf, p, p, (x1 - x0) );
1666 }
1667 }
1668 #endif // ENABLE_LCMS
1670 // Now we only need to output the prepared pixmap to the actual screen, and this define chooses one
1671 // of the two ways to do it. The cairo way is direct and straightforward, but unfortunately
1672 // noticeably slower. I asked Carl Worth but he was unable so far to suggest any specific reason
1673 // for this slowness. So, for now we use the oldish method: squeeze out 32bpp buffer to 24bpp and
1674 // use gdk_draw_rgb_image_dithalign, for unfortunately gdk can only handle 24 bpp, which cairo
1675 // cannot handle at all. Still, this way is currently faster even despite the blit with squeeze.
1677 ///#define CANVAS_OUTPUT_VIA_CAIRO
1679 #ifdef CANVAS_OUTPUT_VIA_CAIRO
1681 buf.cst = cairo_image_surface_create_for_data (
1682 buf.buf,
1683 CAIRO_FORMAT_ARGB32, // unpacked, i.e. 32 bits! one byte is unused
1684 x1 - x0, y1 - y0,
1685 buf.buf_rowstride
1686 );
1687 cairo_t *window_ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1688 cairo_set_source_surface (window_ct, buf.cst, x0 - canvas->x0, y0 - canvas->y0);
1689 cairo_paint (window_ct);
1690 cairo_destroy (window_ct);
1691 cairo_surface_finish (buf.cst);
1692 cairo_surface_destroy (buf.cst);
1694 #else
1696 NRPixBlock b3;
1697 nr_pixblock_setup_fast (&b3, NR_PIXBLOCK_MODE_R8G8B8, x0, y0, x1, y1, TRUE);
1699 NRPixBlock b4;
1700 nr_pixblock_setup_extern (&b4, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1,
1701 buf.buf,
1702 buf.buf_rowstride,
1703 FALSE, FALSE);
1705 // this does the 32->24 squishing, using an assembler routine:
1706 nr_blit_pixblock_pixblock (&b3, &b4);
1708 gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1709 canvas->pixmap_gc,
1710 x0 - canvas->x0, y0 - canvas->y0,
1711 x1 - x0, y1 - y0,
1712 GDK_RGB_DITHER_MAX,
1713 NR_PIXBLOCK_PX(&b3),
1714 sw * 3,
1715 x0 - canvas->x0, y0 - canvas->y0);
1717 nr_pixblock_release (&b3);
1718 nr_pixblock_release (&b4);
1719 #endif
1720 }
1722 cairo_surface_t *cst = cairo_get_target(buf.ct);
1723 cairo_destroy (buf.ct);
1724 cairo_surface_finish (cst);
1725 cairo_surface_destroy (cst);
1727 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1728 nr_pixelstore_256K_free (buf.buf);
1729 } else {
1730 nr_pixelstore_1M_free (buf.buf);
1731 }
1732 }
1734 struct PaintRectSetup {
1735 SPCanvas* canvas;
1736 NRRectL big_rect;
1737 GTimeVal start_time;
1738 int max_pixels;
1739 Geom::Point mouse_loc;
1740 };
1742 /**
1743 * Paint the given rect, recursively subdividing the region until it is the size of a single
1744 * buffer.
1745 *
1746 * @return true if the drawing completes
1747 */
1748 static int
1749 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1750 {
1751 GTimeVal now;
1752 g_get_current_time (&now);
1754 glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1755 + (now.tv_usec - setup->start_time.tv_usec);
1757 // Allow only very fast buffers to be run together;
1758 // as soon as the total redraw time exceeds 1ms, cancel;
1759 // this returns control to the idle loop and allows Inkscape to process user input
1760 // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1761 // it will get back and finish painting what remains to paint.
1762 if (elapsed > 1000) {
1764 // Interrupting redraw isn't always good.
1765 // For example, when you drag one node of a big path, only the buffer containing
1766 // the mouse cursor will be redrawn again and again, and the rest of the path
1767 // will remain stale because Inkscape never has enough idle time to redraw all
1768 // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1769 // If this limit is set, and if we have aborted redraw more times than is allowed,
1770 // interrupting is blocked and we're forced to redraw full screen once
1771 // (after which we can again interrupt forced_redraw_limit times).
1772 if (setup->canvas->forced_redraw_limit < 0 ||
1773 setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1775 if (setup->canvas->forced_redraw_limit != -1) {
1776 setup->canvas->forced_redraw_count++;
1777 }
1779 return false;
1780 }
1781 }
1783 // Find the optimal buffer dimensions
1784 int bw = this_rect.x1 - this_rect.x0;
1785 int bh = this_rect.y1 - this_rect.y0;
1786 if ((bw < 1) || (bh < 1))
1787 return 0;
1789 if (bw * bh < setup->max_pixels) {
1790 // We are small enough
1791 sp_canvas_paint_single_buffer (setup->canvas,
1792 this_rect.x0, this_rect.y0,
1793 this_rect.x1, this_rect.y1,
1794 setup->big_rect.x0, setup->big_rect.y0,
1795 setup->big_rect.x1, setup->big_rect.y1, bw);
1796 return 1;
1797 }
1799 NRRectL lo = this_rect;
1800 NRRectL hi = this_rect;
1802 /*
1803 This test determines the redraw strategy:
1805 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1806 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1807 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1808 and seems to be faster for drawings with many smaller objects at zoom-out.
1810 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1811 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1812 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1813 faster.
1815 The default for now is the strips mode.
1816 */
1817 if (bw < bh || bh < 2 * TILE_SIZE) {
1818 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1819 int mid = ((long long) this_rect.x0 + (long long) this_rect.x1) / 2;
1820 // Make sure that mid lies on a tile boundary
1821 mid = (mid / TILE_SIZE) * TILE_SIZE;
1823 lo.x1 = mid;
1824 hi.x0 = mid;
1826 if (setup->mouse_loc[Geom::X] < mid) {
1827 // Always paint towards the mouse first
1828 return sp_canvas_paint_rect_internal(setup, lo)
1829 && sp_canvas_paint_rect_internal(setup, hi);
1830 } else {
1831 return sp_canvas_paint_rect_internal(setup, hi)
1832 && sp_canvas_paint_rect_internal(setup, lo);
1833 }
1834 } else {
1835 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1836 int mid = ((long long) this_rect.y0 + (long long) this_rect.y1) / 2;
1837 // Make sure that mid lies on a tile boundary
1838 mid = (mid / TILE_SIZE) * TILE_SIZE;
1840 lo.y1 = mid;
1841 hi.y0 = mid;
1843 if (setup->mouse_loc[Geom::Y] < mid) {
1844 // Always paint towards the mouse first
1845 return sp_canvas_paint_rect_internal(setup, lo)
1846 && sp_canvas_paint_rect_internal(setup, hi);
1847 } else {
1848 return sp_canvas_paint_rect_internal(setup, hi)
1849 && sp_canvas_paint_rect_internal(setup, lo);
1850 }
1851 }
1852 }
1855 /**
1856 * Helper that draws a specific rectangular part of the canvas.
1857 *
1858 * @return true if the rectangle painting succeeds.
1859 */
1860 static bool
1861 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1862 {
1863 g_return_val_if_fail (!canvas->need_update, false);
1865 NRRectL rect;
1866 rect.x0 = xx0;
1867 rect.x1 = xx1;
1868 rect.y0 = yy0;
1869 rect.y1 = yy1;
1871 // Clip rect-to-draw by the current visible area
1872 rect.x0 = MAX (rect.x0, canvas->x0);
1873 rect.y0 = MAX (rect.y0, canvas->y0);
1874 rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1875 rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1877 #ifdef DEBUG_REDRAW
1878 // paint the area to redraw yellow
1879 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1880 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1881 canvas->pixmap_gc,
1882 TRUE,
1883 rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1884 rect.x1 - rect.x0, rect.y1 - rect.y0);
1885 #endif
1887 PaintRectSetup setup;
1889 setup.canvas = canvas;
1890 setup.big_rect = rect;
1892 // Save the mouse location
1893 gint x, y;
1894 gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1895 setup.mouse_loc = sp_canvas_window_to_world (canvas, Geom::Point(x,y));
1897 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1898 // use 256K as a compromise to not slow down gradients
1899 // 256K is the cached buffer and we need 4 channels
1900 setup.max_pixels = 65536; // 256K/4
1901 } else {
1902 // paths only, so 1M works faster
1903 // 1M is the cached buffer and we need 4 channels
1904 setup.max_pixels = 262144;
1905 }
1907 // Start the clock
1908 g_get_current_time(&(setup.start_time));
1910 // Go
1911 return sp_canvas_paint_rect_internal(&setup, rect);
1912 }
1914 /**
1915 * Force a full redraw after a specified number of interrupted redraws
1916 */
1917 void
1918 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1919 g_return_if_fail(canvas != NULL);
1921 canvas->forced_redraw_limit = count;
1922 canvas->forced_redraw_count = 0;
1923 }
1925 /**
1926 * End forced full redraw requests
1927 */
1928 void
1929 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1930 g_return_if_fail(canvas != NULL);
1932 canvas->forced_redraw_limit = -1;
1933 }
1935 /**
1936 * The canvas widget's expose callback.
1937 */
1938 static gint
1939 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1940 {
1941 SPCanvas *canvas = SP_CANVAS (widget);
1943 if (!GTK_WIDGET_DRAWABLE (widget) ||
1944 (event->window != SP_CANVAS_WINDOW (canvas)))
1945 return FALSE;
1947 int n_rects;
1948 GdkRectangle *rects;
1949 gdk_region_get_rectangles (event->region, &rects, &n_rects);
1951 for (int i = 0; i < n_rects; i++) {
1952 NRRectL rect;
1954 rect.x0 = rects[i].x + canvas->x0;
1955 rect.y0 = rects[i].y + canvas->y0;
1956 rect.x1 = rect.x0 + rects[i].width;
1957 rect.y1 = rect.y0 + rects[i].height;
1959 sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1960 }
1962 if (n_rects > 0)
1963 g_free (rects);
1965 return FALSE;
1966 }
1968 /**
1969 * The canvas widget's keypress callback.
1970 */
1971 static gint
1972 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1973 {
1974 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1975 }
1977 /**
1978 * Crossing event handler for the canvas.
1979 */
1980 static gint
1981 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1982 {
1983 SPCanvas *canvas = SP_CANVAS (widget);
1985 if (event->window != SP_CANVAS_WINDOW (canvas))
1986 return FALSE;
1988 canvas->state = event->state;
1989 return pick_current_item (canvas, (GdkEvent *) event);
1990 }
1992 /**
1993 * Focus in handler for the canvas.
1994 */
1995 static gint
1996 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
1997 {
1998 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
2000 SPCanvas *canvas = SP_CANVAS (widget);
2002 if (canvas->focused_item) {
2003 return emit_event (canvas, (GdkEvent *) event);
2004 } else {
2005 return FALSE;
2006 }
2007 }
2009 /**
2010 * Focus out handler for the canvas.
2011 */
2012 static gint
2013 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
2014 {
2015 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2017 SPCanvas *canvas = SP_CANVAS (widget);
2019 if (canvas->focused_item)
2020 return emit_event (canvas, (GdkEvent *) event);
2021 else
2022 return FALSE;
2023 }
2025 /**
2026 * Helper that repaints the areas in the canvas that need it.
2027 *
2028 * @return true if all the dirty parts have been redrawn
2029 */
2030 static int
2031 paint (SPCanvas *canvas)
2032 {
2033 if (canvas->need_update) {
2034 sp_canvas_item_invoke_update (canvas->root, Geom::identity(), 0);
2035 canvas->need_update = FALSE;
2036 }
2038 if (!canvas->need_redraw)
2039 return TRUE;
2041 Gdk::Region to_paint;
2043 for (int j=canvas->tTop; j<canvas->tBottom; j++) {
2044 for (int i=canvas->tLeft; i<canvas->tRight; i++) {
2045 int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
2047 if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
2048 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
2049 TILE_SIZE, TILE_SIZE));
2050 }
2052 }
2053 }
2055 if (!to_paint.empty()) {
2056 Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
2057 typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
2058 for (Iter i=rect.begin(); i != rect.end(); ++i) {
2059 int x0 = (*i).get_x();
2060 int y0 = (*i).get_y();
2061 int x1 = x0 + (*i).get_width();
2062 int y1 = y0 + (*i).get_height();
2063 if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
2064 // Aborted
2065 return FALSE;
2066 };
2067 }
2068 }
2070 canvas->need_redraw = FALSE;
2072 // we've had a full unaborted redraw, reset the full redraw counter
2073 if (canvas->forced_redraw_limit != -1) {
2074 canvas->forced_redraw_count = 0;
2075 }
2077 return TRUE;
2078 }
2080 /**
2081 * Helper that invokes update, paint, and repick on canvas.
2082 */
2083 static int
2084 do_update (SPCanvas *canvas)
2085 {
2086 if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop durring interrupted display!
2087 return TRUE;
2089 /* Cause the update if necessary */
2090 if (canvas->need_update) {
2091 sp_canvas_item_invoke_update (canvas->root, Geom::identity(), 0);
2092 canvas->need_update = FALSE;
2093 }
2095 /* Paint if able to */
2096 if (GTK_WIDGET_DRAWABLE (canvas)) {
2097 return paint (canvas);
2098 }
2100 /* Pick new current item */
2101 while (canvas->need_repick) {
2102 canvas->need_repick = FALSE;
2103 pick_current_item (canvas, &canvas->pick_event);
2104 }
2106 return TRUE;
2107 }
2109 /**
2110 * Idle handler for the canvas that deals with pending updates and redraws.
2111 */
2112 static gint
2113 idle_handler (gpointer data)
2114 {
2115 GDK_THREADS_ENTER ();
2117 SPCanvas *canvas = SP_CANVAS (data);
2119 int const ret = do_update (canvas);
2121 if (ret) {
2122 /* Reset idle id */
2123 canvas->idle_id = 0;
2124 }
2126 GDK_THREADS_LEAVE ();
2128 return !ret;
2129 }
2131 /**
2132 * Convenience function to add an idle handler to a canvas.
2133 */
2134 static void
2135 add_idle (SPCanvas *canvas)
2136 {
2137 if (canvas->idle_id != 0)
2138 return;
2140 canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2141 }
2143 /**
2144 * Returns the root group of the specified canvas.
2145 */
2146 SPCanvasGroup *
2147 sp_canvas_root (SPCanvas *canvas)
2148 {
2149 g_return_val_if_fail (canvas != NULL, NULL);
2150 g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2152 return SP_CANVAS_GROUP (canvas->root);
2153 }
2155 /**
2156 * Scrolls canvas to specific position (cx and cy are measured in screen pixels)
2157 */
2158 void
2159 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2160 {
2161 g_return_if_fail (canvas != NULL);
2162 g_return_if_fail (SP_IS_CANVAS (canvas));
2164 int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels)
2165 int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do!
2166 int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the
2167 int dy = iy - canvas->y0; // canvas w.r.t its previous position
2169 canvas->dx0 = cx; // here the 'd' stands for double, not delta!
2170 canvas->dy0 = cy;
2171 canvas->x0 = ix;
2172 canvas->y0 = iy;
2174 sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2176 if (!clear) {
2177 // scrolling without zoom; redraw only the newly exposed areas
2178 if ((dx != 0) || (dy != 0)) {
2179 canvas->is_scrolling = is_scrolling;
2180 if (GTK_WIDGET_REALIZED (canvas)) {
2181 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2182 }
2183 }
2184 } else {
2185 // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2186 }
2187 }
2189 /**
2190 * Updates canvas if necessary.
2191 */
2192 void
2193 sp_canvas_update_now (SPCanvas *canvas)
2194 {
2195 g_return_if_fail (canvas != NULL);
2196 g_return_if_fail (SP_IS_CANVAS (canvas));
2198 if (!(canvas->need_update ||
2199 canvas->need_redraw))
2200 return;
2202 do_update (canvas);
2203 }
2205 /**
2206 * Update callback for canvas widget.
2207 */
2208 static void
2209 sp_canvas_request_update (SPCanvas *canvas)
2210 {
2211 canvas->need_update = TRUE;
2212 add_idle (canvas);
2213 }
2215 /**
2216 * Forces redraw of rectangular canvas area.
2217 */
2218 void
2219 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2220 {
2221 NRRectL bbox;
2222 NRRectL visible;
2223 NRRectL clip;
2225 g_return_if_fail (canvas != NULL);
2226 g_return_if_fail (SP_IS_CANVAS (canvas));
2228 if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2229 if ((x0 >= x1) || (y0 >= y1)) return;
2231 bbox.x0 = x0;
2232 bbox.y0 = y0;
2233 bbox.x1 = x1;
2234 bbox.y1 = y1;
2236 visible.x0 = canvas->x0;
2237 visible.y0 = canvas->y0;
2238 visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2239 visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2241 nr_rect_l_intersect (&clip, &bbox, &visible);
2243 sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2244 add_idle (canvas);
2245 }
2247 /**
2248 * Sets world coordinates from win and canvas.
2249 */
2250 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2251 {
2252 g_return_if_fail (canvas != NULL);
2253 g_return_if_fail (SP_IS_CANVAS (canvas));
2255 if (worldx) *worldx = canvas->x0 + winx;
2256 if (worldy) *worldy = canvas->y0 + winy;
2257 }
2259 /**
2260 * Sets win coordinates from world and canvas.
2261 */
2262 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2263 {
2264 g_return_if_fail (canvas != NULL);
2265 g_return_if_fail (SP_IS_CANVAS (canvas));
2267 if (winx) *winx = worldx - canvas->x0;
2268 if (winy) *winy = worldy - canvas->y0;
2269 }
2271 /**
2272 * Converts point from win to world coordinates.
2273 */
2274 Geom::Point sp_canvas_window_to_world(SPCanvas const *canvas, Geom::Point const win)
2275 {
2276 g_assert (canvas != NULL);
2277 g_assert (SP_IS_CANVAS (canvas));
2279 return Geom::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2280 }
2282 /**
2283 * Converts point from world to win coordinates.
2284 */
2285 Geom::Point sp_canvas_world_to_window(SPCanvas const *canvas, Geom::Point const world)
2286 {
2287 g_assert (canvas != NULL);
2288 g_assert (SP_IS_CANVAS (canvas));
2290 return Geom::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2291 }
2293 /**
2294 * Returns true if point given in world coordinates is inside window.
2295 */
2296 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, Geom::Point const &world)
2297 {
2298 g_assert( canvas != NULL );
2299 g_assert(SP_IS_CANVAS(canvas));
2301 GtkWidget const &w = *GTK_WIDGET(canvas);
2302 return ( ( canvas->x0 <= world[Geom::X] ) &&
2303 ( canvas->y0 <= world[Geom::Y] ) &&
2304 ( world[Geom::X] < canvas->x0 + w.allocation.width ) &&
2305 ( world[Geom::Y] < canvas->y0 + w.allocation.height ) );
2306 }
2308 /**
2309 * Return canvas window coordinates as Geom::Rect.
2310 */
2311 Geom::Rect SPCanvas::getViewbox() const
2312 {
2313 GtkWidget const *w = GTK_WIDGET(this);
2314 return Geom::Rect(Geom::Point(dx0, dy0),
2315 Geom::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2316 }
2318 /**
2319 * Return canvas window coordinates as IRect (a rectangle defined by integers).
2320 */
2321 NR::IRect SPCanvas::getViewboxIntegers() const
2322 {
2323 GtkWidget const *w = GTK_WIDGET(this);
2324 return NR::IRect(NR::IPoint(x0, y0),
2325 NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height));
2326 }
2328 inline int sp_canvas_tile_floor(int x)
2329 {
2330 return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2331 }
2333 inline int sp_canvas_tile_ceil(int x)
2334 {
2335 return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2336 }
2338 /**
2339 * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2340 */
2341 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2342 {
2343 if ( nl >= nr || nt >= nb ) {
2344 if ( canvas->tiles ) g_free(canvas->tiles);
2345 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2346 canvas->tileH=canvas->tileV=0;
2347 canvas->tiles=NULL;
2348 return;
2349 }
2350 int tl=sp_canvas_tile_floor(nl);
2351 int tt=sp_canvas_tile_floor(nt);
2352 int tr=sp_canvas_tile_ceil(nr);
2353 int tb=sp_canvas_tile_ceil(nb);
2355 int nh = tr-tl, nv = tb-tt;
2356 uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2357 for (int i=tl; i<tr; i++) {
2358 for (int j=tt; j<tb; j++) {
2359 int ind = (i-tl) + (j-tt)*nh;
2360 if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2361 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2362 } else {
2363 ntiles[ind]=0; // newly exposed areas get 0
2364 }
2365 }
2366 }
2367 if ( canvas->tiles ) g_free(canvas->tiles);
2368 canvas->tiles=ntiles;
2369 canvas->tLeft=tl;
2370 canvas->tTop=tt;
2371 canvas->tRight=tr;
2372 canvas->tBottom=tb;
2373 canvas->tileH=nh;
2374 canvas->tileV=nv;
2375 }
2377 /*
2378 * Helper that queues a canvas rectangle for redraw
2379 */
2380 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2381 canvas->need_redraw = TRUE;
2383 sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2384 }
2386 /**
2387 * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2388 */
2389 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2390 {
2391 if ( nl >= nr || nt >= nb ) {
2392 return;
2393 }
2394 int tl=sp_canvas_tile_floor(nl);
2395 int tt=sp_canvas_tile_floor(nt);
2396 int tr=sp_canvas_tile_ceil(nr);
2397 int tb=sp_canvas_tile_ceil(nb);
2398 if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2399 if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2400 if ( tr > canvas->tRight ) tr=canvas->tRight;
2401 if ( tt < canvas->tTop ) tt=canvas->tTop;
2402 if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2404 for (int i=tl; i<tr; i++) {
2405 for (int j=tt; j<tb; j++) {
2406 canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2407 }
2408 }
2409 }
2412 /*
2413 Local Variables:
2414 mode:c++
2415 c-file-style:"stroustrup"
2416 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2417 indent-tabs-mode:nil
2418 fill-column:99
2419 End:
2420 */
2421 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :