1 /** \file
2 * Port of GnomeCanvas for Inkscape needs
3 *
4 * Authors:
5 * Federico Mena <federico@nuclecu.unam.mx>
6 * Raph Levien <raph@gimp.org>
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * fred
9 * bbyak
10 *
11 * Copyright (C) 1998 The Free Software Foundation
12 * Copyright (C) 2002-2006 authors
13 *
14 * Released under GNU GPL, read the file 'COPYING' for more information
15 */
17 #ifdef HAVE_CONFIG_H
18 # include "config.h"
19 #endif
21 #include <libnr/nr-pixblock.h>
23 #include <gtk/gtkmain.h>
24 #include <gtk/gtksignal.h>
25 #include <gtk/gtkversion.h>
27 #include <gtkmm.h>
29 #include "helper/sp-marshal.h"
30 #include <helper/recthull.h>
31 #include "display/sp-canvas.h"
32 #include "display/sp-canvas-group.h"
33 #include <2geom/matrix.h>
34 #include "libnr/nr-convex-hull.h"
35 #include "preferences.h"
36 #include "inkscape.h"
37 #include "sodipodi-ctrlrect.h"
38 #if ENABLE_LCMS
39 #include "color-profile-fns.h"
40 #endif // ENABLE_LCMS
41 #include "display/rendermode.h"
42 #include "libnr/nr-blit.h"
43 #include "display/inkscape-cairo.h"
44 #include "debug/gdk-event-latency-tracker.h"
45 #include "desktop.h"
46 #include "sp-namedview.h"
48 using Inkscape::Debug::GdkEventLatencyTracker;
50 // GTK_CHECK_VERSION returns false on failure
51 #define HAS_GDK_EVENT_REQUEST_MOTIONS GTK_CHECK_VERSION(2, 12, 0)
53 // gtk_check_version returns non-NULL on failure
54 static bool const HAS_BROKEN_MOTION_HINTS =
55 true || gtk_check_version(2, 12, 0) != NULL || !HAS_GDK_EVENT_REQUEST_MOTIONS;
57 // Define this to visualize the regions to be redrawn
58 //#define DEBUG_REDRAW 1;
60 // Tiles are a way to minimize the number of redraws, eliminating too small redraws.
61 // The canvas stores a 2D array of ints, each representing a TILE_SIZExTILE_SIZE pixels tile.
62 // If any part of it is dirtied, the entire tile is dirtied (its int is nonzero) and repainted.
63 #define TILE_SIZE 16
65 static gint const sp_canvas_update_priority = G_PRIORITY_HIGH_IDLE;
67 #define SP_CANVAS_WINDOW(c) (((GtkWidget *) (c))->window)
69 enum {
70 SP_CANVAS_ITEM_VISIBLE = 1 << 7,
71 SP_CANVAS_ITEM_NEED_UPDATE = 1 << 8,
72 SP_CANVAS_ITEM_NEED_AFFINE = 1 << 9
73 };
75 /**
76 * A group of Items.
77 */
78 struct SPCanvasGroup {
79 SPCanvasItem item;
81 GList *items, *last;
82 };
84 /**
85 * The SPCanvasGroup vtable.
86 */
87 struct SPCanvasGroupClass {
88 SPCanvasItemClass parent_class;
89 };
91 /**
92 * The SPCanvas vtable.
93 */
94 struct SPCanvasClass {
95 GtkWidgetClass parent_class;
96 };
98 static void group_add (SPCanvasGroup *group, SPCanvasItem *item);
99 static void group_remove (SPCanvasGroup *group, SPCanvasItem *item);
101 /* SPCanvasItem */
103 enum {ITEM_EVENT, ITEM_LAST_SIGNAL};
104 enum {PROP_0, PROP_VISIBLE};
107 static void sp_canvas_request_update (SPCanvas *canvas);
109 static void track_latency(GdkEvent const *event);
110 static void sp_canvas_item_class_init (SPCanvasItemClass *klass);
111 static void sp_canvas_item_init (SPCanvasItem *item);
112 static void sp_canvas_item_dispose (GObject *object);
113 static void sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args);
115 static int emit_event (SPCanvas *canvas, GdkEvent *event);
116 static int pick_current_item (SPCanvas *canvas, GdkEvent *event);
118 static guint item_signals[ITEM_LAST_SIGNAL] = { 0 };
120 /**
121 * Registers the SPCanvasItem class with Glib and returns its type number.
122 */
123 GType
124 sp_canvas_item_get_type (void)
125 {
126 static GType type = 0;
127 if (!type) {
128 static GTypeInfo const info = {
129 sizeof (SPCanvasItemClass),
130 NULL, NULL,
131 (GClassInitFunc) sp_canvas_item_class_init,
132 NULL, NULL,
133 sizeof (SPCanvasItem),
134 0,
135 (GInstanceInitFunc) sp_canvas_item_init,
136 NULL
137 };
138 type = g_type_register_static (GTK_TYPE_OBJECT, "SPCanvasItem", &info, (GTypeFlags)0);
139 }
141 return type;
142 }
144 /**
145 * Initializes the SPCanvasItem vtable and the "event" signal.
146 */
147 static void
148 sp_canvas_item_class_init (SPCanvasItemClass *klass)
149 {
150 GObjectClass *object_class = (GObjectClass *) klass;
152 item_signals[ITEM_EVENT] = g_signal_new ("event",
153 G_TYPE_FROM_CLASS (klass),
154 G_SIGNAL_RUN_LAST,
155 ((glong)((guint8*)&(klass->event) - (guint8*)klass)),
156 NULL, NULL,
157 sp_marshal_BOOLEAN__POINTER,
158 G_TYPE_BOOLEAN, 1,
159 GDK_TYPE_EVENT);
161 object_class->dispose = sp_canvas_item_dispose;
162 }
164 /**
165 * Callback for initialization of SPCanvasItem.
166 */
167 static void
168 sp_canvas_item_init (SPCanvasItem *item)
169 {
170 // TODO items should not be visible on creation - this causes kludges with items
171 // that should be initially invisible; examples of such items: node handles, the CtrlRect
172 // used for rubberbanding, path outline, etc.
173 item->flags |= SP_CANVAS_ITEM_VISIBLE;
174 item->xform = Geom::Matrix(Geom::identity());
175 }
177 /**
178 * Constructs new SPCanvasItem on SPCanvasGroup.
179 */
180 SPCanvasItem *
181 sp_canvas_item_new (SPCanvasGroup *parent, GtkType type, gchar const *first_arg_name, ...)
182 {
183 va_list args;
185 g_return_val_if_fail (parent != NULL, NULL);
186 g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL);
187 g_return_val_if_fail (gtk_type_is_a (type, sp_canvas_item_get_type ()), NULL);
189 SPCanvasItem *item = SP_CANVAS_ITEM (gtk_type_new (type));
191 va_start (args, first_arg_name);
192 sp_canvas_item_construct (item, parent, first_arg_name, args);
193 va_end (args);
195 return item;
196 }
198 /**
199 * Sets up the newly created SPCanvasItem.
200 *
201 * We make it static for encapsulation reasons since it was nowhere used.
202 */
203 static void
204 sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args)
205 {
206 g_return_if_fail (SP_IS_CANVAS_GROUP (parent));
207 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
209 item->parent = SP_CANVAS_ITEM (parent);
210 item->canvas = item->parent->canvas;
212 g_object_set_valist (G_OBJECT (item), first_arg_name, args);
214 group_add (SP_CANVAS_GROUP (item->parent), item);
216 sp_canvas_item_request_update (item);
217 }
219 /**
220 * Helper function that requests redraw only if item's visible flag is set.
221 */
222 static void
223 redraw_if_visible (SPCanvasItem *item)
224 {
225 if (item->flags & SP_CANVAS_ITEM_VISIBLE) {
226 int x0 = (int)(item->x1);
227 int x1 = (int)(item->x2);
228 int y0 = (int)(item->y1);
229 int y1 = (int)(item->y2);
231 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
232 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
233 }
234 }
235 }
237 /**
238 * Callback that removes item from all referers and destroys it.
239 */
240 static void
241 sp_canvas_item_dispose (GObject *object)
242 {
243 SPCanvasItem *item = SP_CANVAS_ITEM (object);
245 // Hack: if this is a ctrlrect, move it to 0,0;
246 // this redraws only the stroke of the rect to be deleted,
247 // avoiding redraw of the entire area
248 if (SP_IS_CTRLRECT(item)) {
249 SP_CTRLRECT(object)->setRectangle(Geom::Rect(Geom::Point(0,0),Geom::Point(0,0)));
250 SP_CTRLRECT(object)->update(item->xform, 0);
251 } else {
252 redraw_if_visible (item);
253 }
254 item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
256 if (item == item->canvas->current_item) {
257 item->canvas->current_item = NULL;
258 item->canvas->need_repick = TRUE;
259 }
261 if (item == item->canvas->new_current_item) {
262 item->canvas->new_current_item = NULL;
263 item->canvas->need_repick = TRUE;
264 }
266 if (item == item->canvas->grabbed_item) {
267 item->canvas->grabbed_item = NULL;
268 gdk_pointer_ungrab (GDK_CURRENT_TIME);
269 }
271 if (item == item->canvas->focused_item)
272 item->canvas->focused_item = NULL;
274 if (item->parent) {
275 group_remove (SP_CANVAS_GROUP (item->parent), item);
276 }
278 G_OBJECT_CLASS (g_type_class_peek(g_type_parent(sp_canvas_item_get_type())))->dispose (object);
279 }
281 /**
282 * Helper function to update item and its children.
283 *
284 * NB! affine is parent2canvas.
285 */
286 static void
287 sp_canvas_item_invoke_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags)
288 {
289 /* Apply the child item's transform */
290 Geom::Matrix child_affine = item->xform * affine;
292 /* apply object flags to child flags */
293 int child_flags = flags & ~SP_CANVAS_UPDATE_REQUESTED;
295 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
296 child_flags |= SP_CANVAS_UPDATE_REQUESTED;
298 if (item->flags & SP_CANVAS_ITEM_NEED_AFFINE)
299 child_flags |= SP_CANVAS_UPDATE_AFFINE;
301 if (child_flags & (SP_CANVAS_UPDATE_REQUESTED | SP_CANVAS_UPDATE_AFFINE)) {
302 if (SP_CANVAS_ITEM_GET_CLASS (item)->update)
303 SP_CANVAS_ITEM_GET_CLASS (item)->update (item, child_affine, child_flags);
304 }
306 GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_UPDATE);
307 GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_AFFINE);
308 }
310 /**
311 * Helper function to invoke the point method of the item.
312 *
313 * The argument x, y should be in the parent's item-relative coordinate
314 * system. This routine applies the inverse of the item's transform,
315 * maintaining the affine invariant.
316 */
317 static double
318 sp_canvas_item_invoke_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item)
319 {
320 if (SP_CANVAS_ITEM_GET_CLASS (item)->point)
321 return SP_CANVAS_ITEM_GET_CLASS (item)->point (item, p, actual_item);
323 return NR_HUGE;
324 }
326 /**
327 * Makes the item's affine transformation matrix be equal to the specified
328 * matrix.
329 *
330 * @item: A canvas item.
331 * @affine: An affine transformation matrix.
332 */
333 void
334 sp_canvas_item_affine_absolute (SPCanvasItem *item, Geom::Matrix const &affine)
335 {
336 item->xform = affine;
338 if (!(item->flags & SP_CANVAS_ITEM_NEED_AFFINE)) {
339 item->flags |= SP_CANVAS_ITEM_NEED_AFFINE;
340 if (item->parent != NULL) {
341 sp_canvas_item_request_update (item->parent);
342 } else {
343 sp_canvas_request_update (item->canvas);
344 }
345 }
347 item->canvas->need_repick = TRUE;
348 }
350 /**
351 * Convenience function to reorder items in a group's child list.
352 *
353 * This puts the specified link after the "before" link.
354 */
355 static void
356 put_item_after (GList *link, GList *before)
357 {
358 if (link == before)
359 return;
361 SPCanvasGroup *parent = SP_CANVAS_GROUP (SP_CANVAS_ITEM (link->data)->parent);
363 if (before == NULL) {
364 if (link == parent->items) return;
366 link->prev->next = link->next;
368 if (link->next) {
369 link->next->prev = link->prev;
370 } else {
371 parent->last = link->prev;
372 }
374 link->prev = before;
375 link->next = parent->items;
376 link->next->prev = link;
377 parent->items = link;
378 } else {
379 if ((link == parent->last) && (before == parent->last->prev))
380 return;
382 if (link->next)
383 link->next->prev = link->prev;
385 if (link->prev)
386 link->prev->next = link->next;
387 else {
388 parent->items = link->next;
389 parent->items->prev = NULL;
390 }
392 link->prev = before;
393 link->next = before->next;
395 link->prev->next = link;
397 if (link->next)
398 link->next->prev = link;
399 else
400 parent->last = link;
401 }
402 }
405 /**
406 * Raises the item in its parent's stack by the specified number of positions.
407 *
408 * \param item A canvas item.
409 * \param positions Number of steps to raise the item.
410 *
411 * If the number of positions is greater than the distance to the top of the
412 * stack, then the item is put at the top.
413 */
414 void
415 sp_canvas_item_raise (SPCanvasItem *item, int positions)
416 {
417 g_return_if_fail (item != NULL);
418 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
419 g_return_if_fail (positions >= 0);
421 if (!item->parent || positions == 0)
422 return;
424 SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
425 GList *link = g_list_find (parent->items, item);
426 g_assert (link != NULL);
428 GList *before;
429 for (before = link; positions && before; positions--)
430 before = before->next;
432 if (!before)
433 before = parent->last;
435 put_item_after (link, before);
437 redraw_if_visible (item);
438 item->canvas->need_repick = TRUE;
439 }
442 /**
443 * Lowers the item in its parent's stack by the specified number of positions.
444 *
445 * \param item A canvas item.
446 * \param positions Number of steps to lower the item.
447 *
448 * If the number of positions is greater than the distance to the bottom of the
449 * stack, then the item is put at the bottom.
450 **/
451 void
452 sp_canvas_item_lower (SPCanvasItem *item, int positions)
453 {
454 g_return_if_fail (item != NULL);
455 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
456 g_return_if_fail (positions >= 1);
458 if (!item->parent || positions == 0)
459 return;
461 SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
462 GList *link = g_list_find (parent->items, item);
463 g_assert (link != NULL);
465 GList *before;
466 if (link->prev)
467 for (before = link->prev; positions && before; positions--)
468 before = before->prev;
469 else
470 before = NULL;
472 put_item_after (link, before);
474 redraw_if_visible (item);
475 item->canvas->need_repick = TRUE;
476 }
478 bool
479 sp_canvas_item_is_visible (SPCanvasItem *item)
480 {
481 return item->flags & SP_CANVAS_ITEM_VISIBLE;
482 }
485 /**
486 * Sets visible flag on item and requests a redraw.
487 */
488 void
489 sp_canvas_item_show (SPCanvasItem *item)
490 {
491 g_return_if_fail (item != NULL);
492 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
494 if (item->flags & SP_CANVAS_ITEM_VISIBLE)
495 return;
497 item->flags |= SP_CANVAS_ITEM_VISIBLE;
499 int x0 = (int)(item->x1);
500 int x1 = (int)(item->x2);
501 int y0 = (int)(item->y1);
502 int y1 = (int)(item->y2);
504 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
505 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
506 item->canvas->need_repick = TRUE;
507 }
508 }
510 /**
511 * Clears visible flag on item and requests a redraw.
512 */
513 void
514 sp_canvas_item_hide (SPCanvasItem *item)
515 {
516 g_return_if_fail (item != NULL);
517 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
519 if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
520 return;
522 item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
524 int x0 = (int)(item->x1);
525 int x1 = (int)(item->x2);
526 int y0 = (int)(item->y1);
527 int y1 = (int)(item->y2);
529 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
530 sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
531 item->canvas->need_repick = TRUE;
532 }
533 }
535 /**
536 * Grab item under cursor.
537 *
538 * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE
539 */
540 int
541 sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
542 {
543 g_return_val_if_fail (item != NULL, -1);
544 g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1);
545 g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1);
547 if (item->canvas->grabbed_item)
548 return -1;
550 // This test disallows grabbing events by an invisible item, which may be useful
551 // sometimes. An example is the hidden control point used for the selector component,
552 // where it is used for object selection and rubberbanding. There seems to be nothing
553 // preventing this except this test, so I removed it.
554 // -- Krzysztof Kosiński, 2009.08.12
555 //if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
556 // return -1;
558 if (HAS_BROKEN_MOTION_HINTS) {
559 event_mask &= ~GDK_POINTER_MOTION_HINT_MASK;
560 }
562 /* fixme: Top hack (Lauris) */
563 /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
564 /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
565 gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
566 (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
567 NULL, cursor, etime);
569 item->canvas->grabbed_item = item;
570 item->canvas->grabbed_event_mask = event_mask;
571 item->canvas->current_item = item; /* So that events go to the grabbed item */
573 return 0;
574 }
576 /**
577 * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
578 * mouse.
579 *
580 * \param item A canvas item that holds a grab.
581 * \param etime The timestamp for ungrabbing the mouse.
582 */
583 void
584 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
585 {
586 g_return_if_fail (item != NULL);
587 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
589 if (item->canvas->grabbed_item != item)
590 return;
592 item->canvas->grabbed_item = NULL;
594 gdk_pointer_ungrab (etime);
595 }
597 /**
598 * Returns the product of all transformation matrices from the root item down
599 * to the item.
600 */
601 Geom::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
602 {
603 g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
605 Geom::Matrix affine = Geom::identity();
607 while (item) {
608 affine *= item->xform;
609 item = item->parent;
610 }
611 return affine;
612 }
614 /**
615 * Helper that returns true iff item is descendant of parent.
616 */
617 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
618 {
619 while (item) {
620 if (item == parent)
621 return true;
622 item = item->parent;
623 }
625 return false;
626 }
628 /**
629 * Focus canvas, and item under cursor if it is not already focussed.
630 */
631 void
632 sp_canvas_item_grab_focus (SPCanvasItem *item)
633 {
634 g_return_if_fail (item != NULL);
635 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
636 g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
638 SPCanvasItem *focused_item = item->canvas->focused_item;
640 if (focused_item) {
641 GdkEvent ev;
642 ev.focus_change.type = GDK_FOCUS_CHANGE;
643 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
644 ev.focus_change.send_event = FALSE;
645 ev.focus_change.in = FALSE;
647 emit_event (item->canvas, &ev);
648 }
650 item->canvas->focused_item = item;
651 gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
653 if (focused_item) {
654 GdkEvent ev;
655 ev.focus_change.type = GDK_FOCUS_CHANGE;
656 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
657 ev.focus_change.send_event = FALSE;
658 ev.focus_change.in = TRUE;
660 emit_event (item->canvas, &ev);
661 }
662 }
664 /**
665 * Requests that the canvas queue an update for the specified item.
666 *
667 * To be used only by item implementations.
668 */
669 void
670 sp_canvas_item_request_update (SPCanvasItem *item)
671 {
672 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
673 return;
675 item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
677 if (item->parent != NULL) {
678 /* Recurse up the tree */
679 sp_canvas_item_request_update (item->parent);
680 } else {
681 /* Have reached the top of the tree, make sure the update call gets scheduled. */
682 sp_canvas_request_update (item->canvas);
683 }
684 }
686 /**
687 * Returns position of item in group.
688 */
689 gint sp_canvas_item_order (SPCanvasItem * item)
690 {
691 return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
692 }
694 /* SPCanvasGroup */
696 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
697 static void sp_canvas_group_init (SPCanvasGroup *group);
698 static void sp_canvas_group_destroy (GtkObject *object);
700 static void sp_canvas_group_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags);
701 static double sp_canvas_group_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item);
702 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
704 static SPCanvasItemClass *group_parent_class;
706 /**
707 * Registers SPCanvasGroup class with Gtk and returns its type number.
708 */
709 GType sp_canvas_group_get_type(void)
710 {
711 static GType type = 0;
712 if (!type) {
713 GTypeInfo info = {
714 sizeof(SPCanvasGroupClass),
715 0, // base_init
716 0, // base_finalize
717 (GClassInitFunc)sp_canvas_group_class_init,
718 0, // class_finalize
719 0, // class_data
720 sizeof(SPCanvasGroup),
721 0, // n_preallocs
722 (GInstanceInitFunc)sp_canvas_group_init,
723 0 // value_table
724 };
725 type = g_type_register_static(sp_canvas_item_get_type(), "SPCanvasGroup", &info, static_cast<GTypeFlags>(0));
726 }
727 return type;
728 }
730 /**
731 * Class initialization function for SPCanvasGroupClass
732 */
733 static void
734 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
735 {
736 GtkObjectClass *object_class = (GtkObjectClass *) klass;
737 SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
739 group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
741 object_class->destroy = sp_canvas_group_destroy;
743 item_class->update = sp_canvas_group_update;
744 item_class->render = sp_canvas_group_render;
745 item_class->point = sp_canvas_group_point;
746 }
748 /**
749 * Callback. Empty.
750 */
751 static void
752 sp_canvas_group_init (SPCanvasGroup */*group*/)
753 {
754 /* Nothing here */
755 }
757 /**
758 * Callback that destroys all items in group and calls group's virtual
759 * destroy() function.
760 */
761 static void
762 sp_canvas_group_destroy (GtkObject *object)
763 {
764 g_return_if_fail (object != NULL);
765 g_return_if_fail (SP_IS_CANVAS_GROUP (object));
767 SPCanvasGroup const *group = SP_CANVAS_GROUP (object);
769 GList *list = group->items;
770 while (list) {
771 SPCanvasItem *child = (SPCanvasItem *)list->data;
772 list = list->next;
774 gtk_object_destroy (GTK_OBJECT (child));
775 }
777 if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
778 (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
779 }
781 /**
782 * Update handler for canvas groups
783 */
784 static void
785 sp_canvas_group_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags)
786 {
787 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
788 Geom::RectHull corners(Geom::Point(0, 0));
789 bool empty=true;
791 for (GList *list = group->items; list; list = list->next) {
792 SPCanvasItem *i = (SPCanvasItem *)list->data;
794 sp_canvas_item_invoke_update (i, affine, flags);
796 if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
797 if (empty) {
798 corners = Geom::RectHull(Geom::Point(i->x1, i->y1));
799 empty = false;
800 } else {
801 corners.add(Geom::Point(i->x1, i->y1));
802 }
803 corners.add(Geom::Point(i->x2, i->y2));
804 }
805 }
807 Geom::OptRect const bounds = corners.bounds();
808 if (bounds) {
809 item->x1 = bounds->min()[Geom::X];
810 item->y1 = bounds->min()[Geom::Y];
811 item->x2 = bounds->max()[Geom::X];
812 item->y2 = bounds->max()[Geom::Y];
813 } else {
814 // FIXME ?
815 item->x1 = item->x2 = item->y1 = item->y2 = 0;
816 }
817 }
819 /**
820 * Point handler for canvas groups.
821 */
822 static double
823 sp_canvas_group_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item)
824 {
825 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
826 double const x = p[Geom::X];
827 double const y = p[Geom::Y];
828 int x1 = (int)(x - item->canvas->close_enough);
829 int y1 = (int)(y - item->canvas->close_enough);
830 int x2 = (int)(x + item->canvas->close_enough);
831 int y2 = (int)(y + item->canvas->close_enough);
833 double best = 0.0;
834 *actual_item = NULL;
836 double dist = 0.0;
838 for (GList *list = group->items; list; list = list->next) {
839 SPCanvasItem *child = (SPCanvasItem *)list->data;
841 if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
842 SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
844 int has_point;
845 if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
846 dist = sp_canvas_item_invoke_point (child, p, &point_item);
847 has_point = TRUE;
848 } else
849 has_point = FALSE;
851 if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
852 best = dist;
853 *actual_item = point_item;
854 }
855 }
856 }
858 return best;
859 }
861 /**
862 * Renders all visible canvas group items in buf rectangle.
863 */
864 static void
865 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
866 {
867 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
869 for (GList *list = group->items; list; list = list->next) {
870 SPCanvasItem *child = (SPCanvasItem *)list->data;
871 if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
872 if ((child->x1 < buf->rect.x1) &&
873 (child->y1 < buf->rect.y1) &&
874 (child->x2 > buf->rect.x0) &&
875 (child->y2 > buf->rect.y0)) {
876 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
877 SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
878 }
879 }
880 }
881 }
883 /**
884 * Adds an item to a canvas group.
885 */
886 static void
887 group_add (SPCanvasGroup *group, SPCanvasItem *item)
888 {
889 gtk_object_ref (GTK_OBJECT (item));
890 gtk_object_sink (GTK_OBJECT (item));
892 if (!group->items) {
893 group->items = g_list_append (group->items, item);
894 group->last = group->items;
895 } else {
896 group->last = g_list_append (group->last, item)->next;
897 }
899 sp_canvas_item_request_update (item);
900 }
902 /**
903 * Removes an item from a canvas group
904 */
905 static void
906 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
907 {
908 g_return_if_fail (group != NULL);
909 g_return_if_fail (SP_IS_CANVAS_GROUP (group));
910 g_return_if_fail (item != NULL);
912 for (GList *children = group->items; children; children = children->next) {
913 if (children->data == item) {
915 /* Unparent the child */
917 item->parent = NULL;
918 gtk_object_unref (GTK_OBJECT (item));
920 /* Remove it from the list */
922 if (children == group->last) group->last = children->prev;
924 group->items = g_list_remove_link (group->items, children);
925 g_list_free (children);
926 break;
927 }
928 }
929 }
931 /* SPCanvas */
933 static void sp_canvas_class_init (SPCanvasClass *klass);
934 static void sp_canvas_init (SPCanvas *canvas);
935 static void sp_canvas_destroy (GtkObject *object);
937 static void sp_canvas_realize (GtkWidget *widget);
938 static void sp_canvas_unrealize (GtkWidget *widget);
940 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
941 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
943 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
944 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
945 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
946 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
947 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
948 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
949 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
950 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
952 static GtkWidgetClass *canvas_parent_class;
954 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb);
955 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb);
956 static void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val);
957 static int do_update (SPCanvas *canvas);
959 /**
960 * Registers the SPCanvas class if necessary, and returns the type ID
961 * associated to it.
962 *
963 * \return The type ID of the SPCanvas class.
964 **/
965 GType sp_canvas_get_type(void)
966 {
967 static GType type = 0;
968 if (!type) {
969 GTypeInfo info = {
970 sizeof(SPCanvasClass),
971 0, // base_init
972 0, // base_finalize
973 (GClassInitFunc)sp_canvas_class_init,
974 0, // class_finalize
975 0, // class_data
976 sizeof(SPCanvas),
977 0, // n_preallocs
978 (GInstanceInitFunc)sp_canvas_init,
979 0 // value_table
980 };
981 type = g_type_register_static(GTK_TYPE_WIDGET, "SPCanvas", &info, static_cast<GTypeFlags>(0));
982 }
983 return type;
984 }
986 /**
987 * Class initialization function for SPCanvasClass.
988 */
989 static void
990 sp_canvas_class_init (SPCanvasClass *klass)
991 {
992 GtkObjectClass *object_class = (GtkObjectClass *) klass;
993 GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
995 canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
997 object_class->destroy = sp_canvas_destroy;
999 widget_class->realize = sp_canvas_realize;
1000 widget_class->unrealize = sp_canvas_unrealize;
1001 widget_class->size_request = sp_canvas_size_request;
1002 widget_class->size_allocate = sp_canvas_size_allocate;
1003 widget_class->button_press_event = sp_canvas_button;
1004 widget_class->button_release_event = sp_canvas_button;
1005 widget_class->motion_notify_event = sp_canvas_motion;
1006 widget_class->scroll_event = sp_canvas_scroll;
1007 widget_class->expose_event = sp_canvas_expose;
1008 widget_class->key_press_event = sp_canvas_key;
1009 widget_class->key_release_event = sp_canvas_key;
1010 widget_class->enter_notify_event = sp_canvas_crossing;
1011 widget_class->leave_notify_event = sp_canvas_crossing;
1012 widget_class->focus_in_event = sp_canvas_focus_in;
1013 widget_class->focus_out_event = sp_canvas_focus_out;
1014 }
1016 /**
1017 * Callback: object initialization for SPCanvas.
1018 */
1019 static void
1020 sp_canvas_init (SPCanvas *canvas)
1021 {
1022 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
1023 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
1024 GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
1026 canvas->pick_event.type = GDK_LEAVE_NOTIFY;
1027 canvas->pick_event.crossing.x = 0;
1028 canvas->pick_event.crossing.y = 0;
1030 /* Create the root item as a special case */
1031 canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
1032 canvas->root->canvas = canvas;
1034 gtk_object_ref (GTK_OBJECT (canvas->root));
1035 gtk_object_sink (GTK_OBJECT (canvas->root));
1037 canvas->need_repick = TRUE;
1039 // See comment at in sp-canvas.h.
1040 canvas->gen_all_enter_events = false;
1042 canvas->drawing_disabled = false;
1044 canvas->tiles=NULL;
1045 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1046 canvas->tileH=canvas->tileV=0;
1048 canvas->forced_redraw_count = 0;
1049 canvas->forced_redraw_limit = -1;
1051 #if ENABLE_LCMS
1052 canvas->enable_cms_display_adj = false;
1053 canvas->cms_key = new Glib::ustring("");
1054 #endif // ENABLE_LCMS
1056 canvas->is_scrolling = false;
1057 }
1059 /**
1060 * Convenience function to remove the idle handler of a canvas.
1061 */
1062 static void
1063 remove_idle (SPCanvas *canvas)
1064 {
1065 if (canvas->idle_id) {
1066 gtk_idle_remove (canvas->idle_id);
1067 canvas->idle_id = 0;
1068 }
1069 }
1071 /*
1072 * Removes the transient state of the canvas (idle handler, grabs).
1073 */
1074 static void
1075 shutdown_transients (SPCanvas *canvas)
1076 {
1077 /* We turn off the need_redraw flag, since if the canvas is mapped again
1078 * it will request a redraw anyways. We do not turn off the need_update
1079 * flag, though, because updates are not queued when the canvas remaps
1080 * itself.
1081 */
1082 if (canvas->need_redraw) {
1083 canvas->need_redraw = FALSE;
1084 }
1085 if ( canvas->tiles ) g_free(canvas->tiles);
1086 canvas->tiles=NULL;
1087 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1088 canvas->tileH=canvas->tileV=0;
1090 if (canvas->grabbed_item) {
1091 canvas->grabbed_item = NULL;
1092 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1093 }
1095 remove_idle (canvas);
1096 }
1098 /**
1099 * Destroy handler for SPCanvas.
1100 */
1101 static void
1102 sp_canvas_destroy (GtkObject *object)
1103 {
1104 SPCanvas *canvas = SP_CANVAS (object);
1106 if (canvas->root) {
1107 gtk_object_unref (GTK_OBJECT (canvas->root));
1108 canvas->root = NULL;
1109 }
1111 shutdown_transients (canvas);
1113 if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1114 (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1115 }
1117 static void track_latency(GdkEvent const *event) {
1118 GdkEventLatencyTracker &tracker = GdkEventLatencyTracker::default_tracker();
1119 boost::optional<double> latency = tracker.process(event);
1120 if (latency && *latency > 2.0) {
1121 //g_warning("Event latency reached %f sec (%1.4f)", *latency, tracker.getSkew());
1122 }
1123 }
1125 /**
1126 * Returns new canvas as widget.
1127 */
1128 GtkWidget *
1129 sp_canvas_new_aa (void)
1130 {
1131 SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1133 return (GtkWidget *) canvas;
1134 }
1136 /**
1137 * The canvas widget's realize callback.
1138 */
1139 static void
1140 sp_canvas_realize (GtkWidget *widget)
1141 {
1142 SPCanvas *canvas = SP_CANVAS (widget);
1144 GdkWindowAttr attributes;
1145 attributes.window_type = GDK_WINDOW_CHILD;
1146 attributes.x = widget->allocation.x;
1147 attributes.y = widget->allocation.y;
1148 attributes.width = widget->allocation.width;
1149 attributes.height = widget->allocation.height;
1150 attributes.wclass = GDK_INPUT_OUTPUT;
1151 attributes.visual = gdk_rgb_get_visual ();
1152 attributes.colormap = gdk_rgb_get_cmap ();
1153 attributes.event_mask = (gtk_widget_get_events (widget) |
1154 GDK_EXPOSURE_MASK |
1155 GDK_BUTTON_PRESS_MASK |
1156 GDK_BUTTON_RELEASE_MASK |
1157 GDK_POINTER_MOTION_MASK |
1158 ( HAS_BROKEN_MOTION_HINTS ?
1159 0 : GDK_POINTER_MOTION_HINT_MASK ) |
1160 GDK_PROXIMITY_IN_MASK |
1161 GDK_PROXIMITY_OUT_MASK |
1162 GDK_KEY_PRESS_MASK |
1163 GDK_KEY_RELEASE_MASK |
1164 GDK_ENTER_NOTIFY_MASK |
1165 GDK_LEAVE_NOTIFY_MASK |
1166 GDK_FOCUS_CHANGE_MASK);
1167 gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1169 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1170 gdk_window_set_user_data (widget->window, widget);
1172 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1173 if ( prefs->getBool("/options/useextinput/value", true) )
1174 gtk_widget_set_events(widget, attributes.event_mask);
1176 widget->style = gtk_style_attach (widget->style, widget->window);
1178 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1180 canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1181 }
1183 /**
1184 * The canvas widget's unrealize callback.
1185 */
1186 static void
1187 sp_canvas_unrealize (GtkWidget *widget)
1188 {
1189 SPCanvas *canvas = SP_CANVAS (widget);
1191 canvas->current_item = NULL;
1192 canvas->grabbed_item = NULL;
1193 canvas->focused_item = NULL;
1195 shutdown_transients (canvas);
1197 gdk_gc_destroy (canvas->pixmap_gc);
1198 canvas->pixmap_gc = NULL;
1200 if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1201 (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1202 }
1204 /**
1205 * The canvas widget's size_request callback.
1206 */
1207 static void
1208 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1209 {
1210 static_cast<void>(SP_CANVAS (widget));
1212 req->width = 256;
1213 req->height = 256;
1214 }
1216 /**
1217 * The canvas widget's size_allocate callback.
1218 */
1219 static void
1220 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1221 {
1222 SPCanvas *canvas = SP_CANVAS (widget);
1224 /* Schedule redraw of new region */
1225 sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1226 if (allocation->width > widget->allocation.width) {
1227 sp_canvas_request_redraw (canvas,
1228 canvas->x0 + widget->allocation.width,
1229 0,
1230 canvas->x0 + allocation->width,
1231 canvas->y0 + allocation->height);
1232 }
1233 if (allocation->height > widget->allocation.height) {
1234 sp_canvas_request_redraw (canvas,
1235 0,
1236 canvas->y0 + widget->allocation.height,
1237 canvas->x0 + allocation->width,
1238 canvas->y0 + allocation->height);
1239 }
1241 widget->allocation = *allocation;
1243 if (GTK_WIDGET_REALIZED (widget)) {
1244 gdk_window_move_resize (widget->window,
1245 widget->allocation.x, widget->allocation.y,
1246 widget->allocation.width, widget->allocation.height);
1247 }
1248 }
1250 /**
1251 * Helper that emits an event for an item in the canvas, be it the current
1252 * item, grabbed item, or focused item, as appropriate.
1253 */
1254 static int
1255 emit_event (SPCanvas *canvas, GdkEvent *event)
1256 {
1257 guint mask;
1259 if (canvas->grabbed_item) {
1260 switch (event->type) {
1261 case GDK_ENTER_NOTIFY:
1262 mask = GDK_ENTER_NOTIFY_MASK;
1263 break;
1264 case GDK_LEAVE_NOTIFY:
1265 mask = GDK_LEAVE_NOTIFY_MASK;
1266 break;
1267 case GDK_MOTION_NOTIFY:
1268 mask = GDK_POINTER_MOTION_MASK;
1269 break;
1270 case GDK_BUTTON_PRESS:
1271 case GDK_2BUTTON_PRESS:
1272 case GDK_3BUTTON_PRESS:
1273 mask = GDK_BUTTON_PRESS_MASK;
1274 break;
1275 case GDK_BUTTON_RELEASE:
1276 mask = GDK_BUTTON_RELEASE_MASK;
1277 break;
1278 case GDK_KEY_PRESS:
1279 mask = GDK_KEY_PRESS_MASK;
1280 break;
1281 case GDK_KEY_RELEASE:
1282 mask = GDK_KEY_RELEASE_MASK;
1283 break;
1284 case GDK_SCROLL:
1285 mask = GDK_SCROLL;
1286 break;
1287 default:
1288 mask = 0;
1289 break;
1290 }
1292 if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1293 }
1295 /* Convert to world coordinates -- we have two cases because of different
1296 * offsets of the fields in the event structures.
1297 */
1299 GdkEvent ev = *event;
1301 switch (ev.type) {
1302 case GDK_ENTER_NOTIFY:
1303 case GDK_LEAVE_NOTIFY:
1304 ev.crossing.x += canvas->x0;
1305 ev.crossing.y += canvas->y0;
1306 break;
1307 case GDK_MOTION_NOTIFY:
1308 case GDK_BUTTON_PRESS:
1309 case GDK_2BUTTON_PRESS:
1310 case GDK_3BUTTON_PRESS:
1311 case GDK_BUTTON_RELEASE:
1312 ev.motion.x += canvas->x0;
1313 ev.motion.y += canvas->y0;
1314 break;
1315 default:
1316 break;
1317 }
1319 /* Choose where we send the event */
1321 /* canvas->current_item becomes NULL in some cases under Win32
1322 ** (e.g. if the pointer leaves the window). So this is a hack that
1323 ** Lauris applied to SP to get around the problem.
1324 */
1325 SPCanvasItem* item = NULL;
1326 if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1327 item = canvas->grabbed_item;
1328 } else {
1329 // Make sure that current_item is up-to-date. If a snap indicator was just deleted, then
1330 // sp_canvas_item_dispose has been called and there is no current_item specified. We need
1331 // that though because otherwise we don't know where to send this event to, leading to a
1332 // lost event. We can't wait for idle events to have current_item updated, we need it now!
1333 // Otherwise, scrolling when hovering above a pre-snap indicator won't work (for example)
1334 // See this bug report: https://bugs.launchpad.net/inkscape/+bug/522335/comments/8
1335 if (canvas->need_repick && !canvas->in_repick && event->type == GDK_SCROLL) {
1336 // To avoid side effects, we'll only do this for scroll events, because this is the
1337 // only thing we want to fix here. An example of a reported side effect is that
1338 // otherwise selection of nodes in the node editor by dragging a rectangle using a
1339 // tablet will break
1340 canvas->need_repick = FALSE;
1341 pick_current_item (canvas, (GdkEvent *) event);
1342 }
1343 item = canvas->current_item;
1344 }
1346 if (canvas->focused_item &&
1347 ((event->type == GDK_KEY_PRESS) ||
1348 (event->type == GDK_KEY_RELEASE) ||
1349 (event->type == GDK_FOCUS_CHANGE))) {
1350 item = canvas->focused_item;
1351 }
1353 /* The event is propagated up the hierarchy (for if someone connected to
1354 * a group instead of a leaf event), and emission is stopped if a
1355 * handler returns TRUE, just like for GtkWidget events.
1356 */
1358 gint finished = FALSE;
1360 while (item && !finished) {
1361 gtk_object_ref (GTK_OBJECT (item));
1362 gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1363 SPCanvasItem *parent = item->parent;
1364 gtk_object_unref (GTK_OBJECT (item));
1365 item = parent;
1366 }
1368 return finished;
1369 }
1371 /**
1372 * Helper that re-picks the current item in the canvas, based on the event's
1373 * coordinates and emits enter/leave events for items as appropriate.
1374 */
1375 static int
1376 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1377 {
1378 int button_down = 0;
1379 double x, y;
1381 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1382 return FALSE;
1384 int retval = FALSE;
1386 if (canvas->gen_all_enter_events == false) {
1387 // If a button is down, we'll perform enter and leave events on the
1388 // current item, but not enter on any other item. This is more or
1389 // less like X pointer grabbing for canvas items.
1390 //
1391 button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1392 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1394 if (!button_down) canvas->left_grabbed_item = FALSE;
1395 }
1397 /* Save the event in the canvas. This is used to synthesize enter and
1398 * leave events in case the current item changes. It is also used to
1399 * re-pick the current item if the current one gets deleted. Also,
1400 * synthesize an enter event.
1401 */
1402 if (event != &canvas->pick_event) {
1403 if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1404 /* these fields have the same offsets in both types of events */
1406 canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY;
1407 canvas->pick_event.crossing.window = event->motion.window;
1408 canvas->pick_event.crossing.send_event = event->motion.send_event;
1409 canvas->pick_event.crossing.subwindow = NULL;
1410 canvas->pick_event.crossing.x = event->motion.x;
1411 canvas->pick_event.crossing.y = event->motion.y;
1412 canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL;
1413 canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR;
1414 canvas->pick_event.crossing.focus = FALSE;
1415 canvas->pick_event.crossing.state = event->motion.state;
1417 /* these fields don't have the same offsets in both types of events */
1419 if (event->type == GDK_MOTION_NOTIFY) {
1420 canvas->pick_event.crossing.x_root = event->motion.x_root;
1421 canvas->pick_event.crossing.y_root = event->motion.y_root;
1422 } else {
1423 canvas->pick_event.crossing.x_root = event->button.x_root;
1424 canvas->pick_event.crossing.y_root = event->button.y_root;
1425 }
1426 } else {
1427 canvas->pick_event = *event;
1428 }
1429 }
1431 /* Don't do anything else if this is a recursive call */
1432 if (canvas->in_repick) return retval;
1434 /* LeaveNotify means that there is no current item, so we don't look for one */
1435 if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1436 /* these fields don't have the same offsets in both types of events */
1438 if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1439 x = canvas->pick_event.crossing.x;
1440 y = canvas->pick_event.crossing.y;
1441 } else {
1442 x = canvas->pick_event.motion.x;
1443 y = canvas->pick_event.motion.y;
1444 }
1446 /* world coords */
1447 x += canvas->x0;
1448 y += canvas->y0;
1450 /* find the closest item */
1451 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1452 sp_canvas_item_invoke_point (canvas->root, Geom::Point(x, y), &canvas->new_current_item);
1453 } else {
1454 canvas->new_current_item = NULL;
1455 }
1456 } else {
1457 canvas->new_current_item = NULL;
1458 }
1460 if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1461 return retval; /* current item did not change */
1462 }
1464 /* Synthesize events for old and new current items */
1466 if ((canvas->new_current_item != canvas->current_item)
1467 && (canvas->current_item != NULL)
1468 && !canvas->left_grabbed_item) {
1469 GdkEvent new_event;
1470 SPCanvasItem *item;
1472 item = canvas->current_item;
1474 new_event = canvas->pick_event;
1475 new_event.type = GDK_LEAVE_NOTIFY;
1477 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1478 new_event.crossing.subwindow = NULL;
1479 canvas->in_repick = TRUE;
1480 retval = emit_event (canvas, &new_event);
1481 canvas->in_repick = FALSE;
1482 }
1484 if (canvas->gen_all_enter_events == false) {
1485 // new_current_item may have been set to NULL during the call to
1486 // emit_event() above
1487 if ((canvas->new_current_item != canvas->current_item) && button_down) {
1488 canvas->left_grabbed_item = TRUE;
1489 return retval;
1490 }
1491 }
1493 /* Handle the rest of cases */
1495 canvas->left_grabbed_item = FALSE;
1496 canvas->current_item = canvas->new_current_item;
1498 if (canvas->current_item != NULL) {
1499 GdkEvent new_event;
1501 new_event = canvas->pick_event;
1502 new_event.type = GDK_ENTER_NOTIFY;
1503 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1504 new_event.crossing.subwindow = NULL;
1505 retval = emit_event (canvas, &new_event);
1506 }
1510 return retval;
1511 }
1513 /**
1514 * Button event handler for the canvas.
1515 */
1516 static gint
1517 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1518 {
1519 SPCanvas *canvas = SP_CANVAS (widget);
1521 int retval = FALSE;
1523 /* dispatch normally regardless of the event's window if an item
1524 has a pointer grab in effect */
1525 if (!canvas->grabbed_item &&
1526 event->window != SP_CANVAS_WINDOW (canvas))
1527 return retval;
1529 int mask;
1530 switch (event->button) {
1531 case 1:
1532 mask = GDK_BUTTON1_MASK;
1533 break;
1534 case 2:
1535 mask = GDK_BUTTON2_MASK;
1536 break;
1537 case 3:
1538 mask = GDK_BUTTON3_MASK;
1539 break;
1540 case 4:
1541 mask = GDK_BUTTON4_MASK;
1542 break;
1543 case 5:
1544 mask = GDK_BUTTON5_MASK;
1545 break;
1546 default:
1547 mask = 0;
1548 }
1550 switch (event->type) {
1551 case GDK_BUTTON_PRESS:
1552 case GDK_2BUTTON_PRESS:
1553 case GDK_3BUTTON_PRESS:
1554 /* Pick the current item as if the button were not pressed, and
1555 * then process the event.
1556 */
1557 canvas->state = event->state;
1558 pick_current_item (canvas, (GdkEvent *) event);
1559 canvas->state ^= mask;
1560 retval = emit_event (canvas, (GdkEvent *) event);
1561 break;
1563 case GDK_BUTTON_RELEASE:
1564 /* Process the event as if the button were pressed, then repick
1565 * after the button has been released
1566 */
1567 canvas->state = event->state;
1568 retval = emit_event (canvas, (GdkEvent *) event);
1569 event->state ^= mask;
1570 canvas->state = event->state;
1571 pick_current_item (canvas, (GdkEvent *) event);
1572 event->state ^= mask;
1574 break;
1576 default:
1577 g_assert_not_reached ();
1578 }
1580 return retval;
1581 }
1583 /**
1584 * Scroll event handler for the canvas.
1585 *
1586 * \todo FIXME: generate motion events to re-select items.
1587 */
1588 static gint
1589 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1590 {
1591 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1592 }
1594 static inline void request_motions(GdkWindow *w, GdkEventMotion *event) {
1595 gdk_window_get_pointer(w, NULL, NULL, NULL);
1596 #if HAS_GDK_EVENT_REQUEST_MOTIONS
1597 gdk_event_request_motions(event);
1598 #endif
1599 }
1601 /**
1602 * Motion event handler for the canvas.
1603 */
1604 static int
1605 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1606 {
1607 int status;
1608 SPCanvas *canvas = SP_CANVAS (widget);
1610 track_latency((GdkEvent *)event);
1612 if (event->window != SP_CANVAS_WINDOW (canvas))
1613 return FALSE;
1615 if (canvas->pixmap_gc == NULL) // canvas being deleted
1616 return FALSE;
1618 canvas->state = event->state;
1619 pick_current_item (canvas, (GdkEvent *) event);
1620 status = emit_event (canvas, (GdkEvent *) event);
1621 if (event->is_hint) {
1622 request_motions(widget->window, event);
1623 }
1625 return status;
1626 }
1628 static void
1629 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)
1630 {
1631 GtkWidget *widget = GTK_WIDGET (canvas);
1633 SPCanvasBuf buf;
1634 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1635 buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1636 } else {
1637 buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1638 }
1640 // Mark the region clean
1641 sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1643 buf.buf_rowstride = sw * 4;
1644 buf.rect.x0 = x0;
1645 buf.rect.y0 = y0;
1646 buf.rect.x1 = x1;
1647 buf.rect.y1 = y1;
1648 buf.visible_rect.x0 = draw_x1;
1649 buf.visible_rect.y0 = draw_y1;
1650 buf.visible_rect.x1 = draw_x2;
1651 buf.visible_rect.y1 = draw_y2;
1652 GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1653 buf.bg_color = (((color->red & 0xff00) << 8)
1654 | (color->green & 0xff00)
1655 | (color->blue >> 8));
1656 buf.is_empty = true;
1658 buf.ct = nr_create_cairo_context_canvasbuf (&(buf.visible_rect), &buf);
1660 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1661 SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1662 }
1664 #if ENABLE_LCMS
1665 cmsHTRANSFORM transf = 0;
1666 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1667 bool fromDisplay = prefs->getBool( "/options/displayprofile/from_display");
1668 if ( fromDisplay ) {
1669 transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" );
1670 } else {
1671 transf = Inkscape::colorprofile_get_display_transform();
1672 }
1673 #endif // ENABLE_LCMS
1675 if (buf.is_empty) {
1676 #if ENABLE_LCMS
1677 if ( transf && canvas->enable_cms_display_adj ) {
1678 cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1679 }
1680 #endif // ENABLE_LCMS
1681 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1682 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1683 canvas->pixmap_gc,
1684 TRUE,
1685 x0 - canvas->x0, y0 - canvas->y0,
1686 x1 - x0, y1 - y0);
1687 } else {
1689 #if ENABLE_LCMS
1690 if ( transf && canvas->enable_cms_display_adj ) {
1691 for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1692 guchar* p = buf.buf + (buf.buf_rowstride * yy);
1693 cmsDoTransform( transf, p, p, (x1 - x0) );
1694 }
1695 }
1696 #endif // ENABLE_LCMS
1698 // Now we only need to output the prepared pixmap to the actual screen, and this define chooses one
1699 // of the two ways to do it. The cairo way is direct and straightforward, but unfortunately
1700 // noticeably slower. I asked Carl Worth but he was unable so far to suggest any specific reason
1701 // for this slowness. So, for now we use the oldish method: squeeze out 32bpp buffer to 24bpp and
1702 // use gdk_draw_rgb_image_dithalign, for unfortunately gdk can only handle 24 bpp, which cairo
1703 // cannot handle at all. Still, this way is currently faster even despite the blit with squeeze.
1705 ///#define CANVAS_OUTPUT_VIA_CAIRO
1707 #ifdef CANVAS_OUTPUT_VIA_CAIRO
1709 buf.cst = cairo_image_surface_create_for_data (
1710 buf.buf,
1711 CAIRO_FORMAT_ARGB32, // unpacked, i.e. 32 bits! one byte is unused
1712 x1 - x0, y1 - y0,
1713 buf.buf_rowstride
1714 );
1715 cairo_t *window_ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1716 cairo_set_source_surface (window_ct, buf.cst, x0 - canvas->x0, y0 - canvas->y0);
1717 cairo_paint (window_ct);
1718 cairo_destroy (window_ct);
1719 cairo_surface_finish (buf.cst);
1720 cairo_surface_destroy (buf.cst);
1722 #else
1724 NRPixBlock b3;
1725 nr_pixblock_setup_fast (&b3, NR_PIXBLOCK_MODE_R8G8B8, x0, y0, x1, y1, TRUE);
1727 NRPixBlock b4;
1728 nr_pixblock_setup_extern (&b4, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1,
1729 buf.buf,
1730 buf.buf_rowstride,
1731 FALSE, FALSE);
1733 // this does the 32->24 squishing, using an assembler routine:
1734 nr_blit_pixblock_pixblock (&b3, &b4);
1736 gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1737 canvas->pixmap_gc,
1738 x0 - canvas->x0, y0 - canvas->y0,
1739 x1 - x0, y1 - y0,
1740 GDK_RGB_DITHER_MAX,
1741 NR_PIXBLOCK_PX(&b3),
1742 sw * 3,
1743 x0 - canvas->x0, y0 - canvas->y0);
1745 nr_pixblock_release (&b3);
1746 nr_pixblock_release (&b4);
1747 #endif
1748 }
1750 cairo_surface_t *cst = cairo_get_target(buf.ct);
1751 cairo_destroy (buf.ct);
1752 cairo_surface_finish (cst);
1753 cairo_surface_destroy (cst);
1755 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1756 nr_pixelstore_256K_free (buf.buf);
1757 } else {
1758 nr_pixelstore_1M_free (buf.buf);
1759 }
1760 }
1762 struct PaintRectSetup {
1763 SPCanvas* canvas;
1764 NRRectL big_rect;
1765 GTimeVal start_time;
1766 int max_pixels;
1767 Geom::Point mouse_loc;
1768 };
1770 /**
1771 * Paint the given rect, recursively subdividing the region until it is the size of a single
1772 * buffer.
1773 *
1774 * @return true if the drawing completes
1775 */
1776 static int
1777 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1778 {
1779 GTimeVal now;
1780 g_get_current_time (&now);
1782 glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1783 + (now.tv_usec - setup->start_time.tv_usec);
1785 // Allow only very fast buffers to be run together;
1786 // as soon as the total redraw time exceeds 1ms, cancel;
1787 // this returns control to the idle loop and allows Inkscape to process user input
1788 // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1789 // it will get back and finish painting what remains to paint.
1790 if (elapsed > 1000) {
1792 // Interrupting redraw isn't always good.
1793 // For example, when you drag one node of a big path, only the buffer containing
1794 // the mouse cursor will be redrawn again and again, and the rest of the path
1795 // will remain stale because Inkscape never has enough idle time to redraw all
1796 // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1797 // If this limit is set, and if we have aborted redraw more times than is allowed,
1798 // interrupting is blocked and we're forced to redraw full screen once
1799 // (after which we can again interrupt forced_redraw_limit times).
1800 if (setup->canvas->forced_redraw_limit < 0 ||
1801 setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1803 if (setup->canvas->forced_redraw_limit != -1) {
1804 setup->canvas->forced_redraw_count++;
1805 }
1807 return false;
1808 }
1809 }
1811 // Find the optimal buffer dimensions
1812 int bw = this_rect.x1 - this_rect.x0;
1813 int bh = this_rect.y1 - this_rect.y0;
1814 if ((bw < 1) || (bh < 1))
1815 return 0;
1817 if (bw * bh < setup->max_pixels) {
1818 // We are small enough
1819 sp_canvas_paint_single_buffer (setup->canvas,
1820 this_rect.x0, this_rect.y0,
1821 this_rect.x1, this_rect.y1,
1822 setup->big_rect.x0, setup->big_rect.y0,
1823 setup->big_rect.x1, setup->big_rect.y1, bw);
1824 return 1;
1825 }
1827 NRRectL lo = this_rect;
1828 NRRectL hi = this_rect;
1830 /*
1831 This test determines the redraw strategy:
1833 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1834 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1835 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1836 and seems to be faster for drawings with many smaller objects at zoom-out.
1838 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1839 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1840 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1841 faster.
1843 The default for now is the strips mode.
1844 */
1845 if (bw < bh || bh < 2 * TILE_SIZE) {
1846 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1847 int mid = ((long long) this_rect.x0 + (long long) this_rect.x1) / 2;
1848 // Make sure that mid lies on a tile boundary
1849 mid = (mid / TILE_SIZE) * TILE_SIZE;
1851 lo.x1 = mid;
1852 hi.x0 = mid;
1854 if (setup->mouse_loc[Geom::X] < mid) {
1855 // Always paint towards the mouse first
1856 return sp_canvas_paint_rect_internal(setup, lo)
1857 && sp_canvas_paint_rect_internal(setup, hi);
1858 } else {
1859 return sp_canvas_paint_rect_internal(setup, hi)
1860 && sp_canvas_paint_rect_internal(setup, lo);
1861 }
1862 } else {
1863 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1864 int mid = ((long long) this_rect.y0 + (long long) this_rect.y1) / 2;
1865 // Make sure that mid lies on a tile boundary
1866 mid = (mid / TILE_SIZE) * TILE_SIZE;
1868 lo.y1 = mid;
1869 hi.y0 = mid;
1871 if (setup->mouse_loc[Geom::Y] < mid) {
1872 // Always paint towards the mouse first
1873 return sp_canvas_paint_rect_internal(setup, lo)
1874 && sp_canvas_paint_rect_internal(setup, hi);
1875 } else {
1876 return sp_canvas_paint_rect_internal(setup, hi)
1877 && sp_canvas_paint_rect_internal(setup, lo);
1878 }
1879 }
1880 }
1883 /**
1884 * Helper that draws a specific rectangular part of the canvas.
1885 *
1886 * @return true if the rectangle painting succeeds.
1887 */
1888 static bool
1889 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1890 {
1891 g_return_val_if_fail (!canvas->need_update, false);
1893 NRRectL rect;
1894 rect.x0 = xx0;
1895 rect.x1 = xx1;
1896 rect.y0 = yy0;
1897 rect.y1 = yy1;
1899 // Clip rect-to-draw by the current visible area
1900 rect.x0 = MAX (rect.x0, canvas->x0);
1901 rect.y0 = MAX (rect.y0, canvas->y0);
1902 rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1903 rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1905 #ifdef DEBUG_REDRAW
1906 // paint the area to redraw yellow
1907 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1908 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1909 canvas->pixmap_gc,
1910 TRUE,
1911 rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1912 rect.x1 - rect.x0, rect.y1 - rect.y0);
1913 #endif
1915 PaintRectSetup setup;
1917 setup.canvas = canvas;
1918 setup.big_rect = rect;
1920 // Save the mouse location
1921 gint x, y;
1922 gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1923 setup.mouse_loc = sp_canvas_window_to_world (canvas, Geom::Point(x,y));
1925 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1926 // use 256K as a compromise to not slow down gradients
1927 // 256K is the cached buffer and we need 4 channels
1928 setup.max_pixels = 65536; // 256K/4
1929 } else {
1930 // paths only, so 1M works faster
1931 // 1M is the cached buffer and we need 4 channels
1932 setup.max_pixels = 262144;
1933 }
1935 // Start the clock
1936 g_get_current_time(&(setup.start_time));
1938 // Go
1939 return sp_canvas_paint_rect_internal(&setup, rect);
1940 }
1942 /**
1943 * Force a full redraw after a specified number of interrupted redraws
1944 */
1945 void
1946 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1947 g_return_if_fail(canvas != NULL);
1949 canvas->forced_redraw_limit = count;
1950 canvas->forced_redraw_count = 0;
1951 }
1953 /**
1954 * End forced full redraw requests
1955 */
1956 void
1957 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1958 g_return_if_fail(canvas != NULL);
1960 canvas->forced_redraw_limit = -1;
1961 }
1963 /**
1964 * The canvas widget's expose callback.
1965 */
1966 static gint
1967 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1968 {
1969 SPCanvas *canvas = SP_CANVAS (widget);
1971 if (!GTK_WIDGET_DRAWABLE (widget) ||
1972 (event->window != SP_CANVAS_WINDOW (canvas)))
1973 return FALSE;
1975 int n_rects;
1976 GdkRectangle *rects;
1977 gdk_region_get_rectangles (event->region, &rects, &n_rects);
1979 for (int i = 0; i < n_rects; i++) {
1980 NRRectL rect;
1982 rect.x0 = rects[i].x + canvas->x0;
1983 rect.y0 = rects[i].y + canvas->y0;
1984 rect.x1 = rect.x0 + rects[i].width;
1985 rect.y1 = rect.y0 + rects[i].height;
1987 sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1988 }
1990 if (n_rects > 0)
1991 g_free (rects);
1993 return FALSE;
1994 }
1996 /**
1997 * The canvas widget's keypress callback.
1998 */
1999 static gint
2000 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
2001 {
2002 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
2003 }
2005 /**
2006 * Crossing event handler for the canvas.
2007 */
2008 static gint
2009 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
2010 {
2011 SPCanvas *canvas = SP_CANVAS (widget);
2013 if (event->window != SP_CANVAS_WINDOW (canvas))
2014 return FALSE;
2016 canvas->state = event->state;
2017 return pick_current_item (canvas, (GdkEvent *) event);
2018 }
2020 /**
2021 * Focus in handler for the canvas.
2022 */
2023 static gint
2024 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
2025 {
2026 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
2028 SPCanvas *canvas = SP_CANVAS (widget);
2030 if (canvas->focused_item) {
2031 return emit_event (canvas, (GdkEvent *) event);
2032 } else {
2033 return FALSE;
2034 }
2035 }
2037 /**
2038 * Focus out handler for the canvas.
2039 */
2040 static gint
2041 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
2042 {
2043 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2045 SPCanvas *canvas = SP_CANVAS (widget);
2047 if (canvas->focused_item)
2048 return emit_event (canvas, (GdkEvent *) event);
2049 else
2050 return FALSE;
2051 }
2053 /**
2054 * Helper that repaints the areas in the canvas that need it.
2055 *
2056 * @return true if all the dirty parts have been redrawn
2057 */
2058 static int
2059 paint (SPCanvas *canvas)
2060 {
2061 if (canvas->need_update) {
2062 sp_canvas_item_invoke_update (canvas->root, Geom::identity(), 0);
2063 canvas->need_update = FALSE;
2064 }
2066 if (!canvas->need_redraw)
2067 return TRUE;
2069 Gdk::Region to_paint;
2071 for (int j=canvas->tTop; j<canvas->tBottom; j++) {
2072 for (int i=canvas->tLeft; i<canvas->tRight; i++) {
2073 int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
2075 if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
2076 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
2077 TILE_SIZE, TILE_SIZE));
2078 }
2080 }
2081 }
2083 if (!to_paint.empty()) {
2084 Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
2085 typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
2086 for (Iter i=rect.begin(); i != rect.end(); ++i) {
2087 int x0 = (*i).get_x();
2088 int y0 = (*i).get_y();
2089 int x1 = x0 + (*i).get_width();
2090 int y1 = y0 + (*i).get_height();
2091 if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
2092 // Aborted
2093 return FALSE;
2094 };
2095 }
2096 }
2098 canvas->need_redraw = FALSE;
2100 // we've had a full unaborted redraw, reset the full redraw counter
2101 if (canvas->forced_redraw_limit != -1) {
2102 canvas->forced_redraw_count = 0;
2103 }
2105 return TRUE;
2106 }
2108 /**
2109 * Helper that invokes update, paint, and repick on canvas.
2110 */
2111 static int
2112 do_update (SPCanvas *canvas)
2113 {
2114 if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop during interrupted display!
2115 return TRUE;
2117 if (canvas->drawing_disabled)
2118 return TRUE;
2120 /* Cause the update if necessary */
2121 if (canvas->need_update) {
2122 sp_canvas_item_invoke_update (canvas->root, Geom::identity(), 0);
2123 canvas->need_update = FALSE;
2124 }
2126 /* Paint if able to */
2127 if (GTK_WIDGET_DRAWABLE (canvas)) {
2128 return paint (canvas);
2129 }
2131 /* Pick new current item */
2132 while (canvas->need_repick) {
2133 canvas->need_repick = FALSE;
2134 pick_current_item (canvas, &canvas->pick_event);
2135 }
2137 return TRUE;
2138 }
2140 /**
2141 * Idle handler for the canvas that deals with pending updates and redraws.
2142 */
2143 static gint
2144 idle_handler (gpointer data)
2145 {
2146 GDK_THREADS_ENTER ();
2148 SPCanvas *canvas = SP_CANVAS (data);
2150 int const ret = do_update (canvas);
2152 if (ret) {
2153 /* Reset idle id */
2154 canvas->idle_id = 0;
2155 }
2157 GDK_THREADS_LEAVE ();
2159 return !ret;
2160 }
2162 /**
2163 * Convenience function to add an idle handler to a canvas.
2164 */
2165 static void
2166 add_idle (SPCanvas *canvas)
2167 {
2168 if (canvas->idle_id != 0)
2169 return;
2171 canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2172 }
2174 /**
2175 * Returns the root group of the specified canvas.
2176 */
2177 SPCanvasGroup *
2178 sp_canvas_root (SPCanvas *canvas)
2179 {
2180 g_return_val_if_fail (canvas != NULL, NULL);
2181 g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2183 return SP_CANVAS_GROUP (canvas->root);
2184 }
2186 /**
2187 * Scrolls canvas to specific position (cx and cy are measured in screen pixels)
2188 */
2189 void
2190 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2191 {
2192 g_return_if_fail (canvas != NULL);
2193 g_return_if_fail (SP_IS_CANVAS (canvas));
2195 int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels)
2196 int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do!
2197 int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the
2198 int dy = iy - canvas->y0; // canvas w.r.t its previous position
2200 canvas->dx0 = cx; // here the 'd' stands for double, not delta!
2201 canvas->dy0 = cy;
2202 canvas->x0 = ix;
2203 canvas->y0 = iy;
2205 sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2207 if (!clear) {
2208 // scrolling without zoom; redraw only the newly exposed areas
2209 if ((dx != 0) || (dy != 0)) {
2210 canvas->is_scrolling = is_scrolling;
2211 if (GTK_WIDGET_REALIZED (canvas)) {
2212 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2213 }
2214 }
2215 } else {
2216 // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2217 }
2219 }
2221 /**
2222 * Updates canvas if necessary.
2223 */
2224 void
2225 sp_canvas_update_now (SPCanvas *canvas)
2226 {
2227 g_return_if_fail (canvas != NULL);
2228 g_return_if_fail (SP_IS_CANVAS (canvas));
2230 if (!(canvas->need_update ||
2231 canvas->need_redraw))
2232 return;
2234 do_update (canvas);
2235 }
2237 /**
2238 * Update callback for canvas widget.
2239 */
2240 static void
2241 sp_canvas_request_update (SPCanvas *canvas)
2242 {
2243 canvas->need_update = TRUE;
2244 add_idle (canvas);
2245 }
2247 /**
2248 * Forces redraw of rectangular canvas area.
2249 */
2250 void
2251 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2252 {
2253 NRRectL bbox;
2254 NRRectL visible;
2255 NRRectL clip;
2257 g_return_if_fail (canvas != NULL);
2258 g_return_if_fail (SP_IS_CANVAS (canvas));
2260 if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2261 if ((x0 >= x1) || (y0 >= y1)) return;
2263 bbox.x0 = x0;
2264 bbox.y0 = y0;
2265 bbox.x1 = x1;
2266 bbox.y1 = y1;
2268 visible.x0 = canvas->x0;
2269 visible.y0 = canvas->y0;
2270 visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2271 visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2273 nr_rect_l_intersect (&clip, &bbox, &visible);
2275 sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2276 add_idle (canvas);
2277 }
2279 /**
2280 * Sets world coordinates from win and canvas.
2281 */
2282 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2283 {
2284 g_return_if_fail (canvas != NULL);
2285 g_return_if_fail (SP_IS_CANVAS (canvas));
2287 if (worldx) *worldx = canvas->x0 + winx;
2288 if (worldy) *worldy = canvas->y0 + winy;
2289 }
2291 /**
2292 * Sets win coordinates from world and canvas.
2293 */
2294 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2295 {
2296 g_return_if_fail (canvas != NULL);
2297 g_return_if_fail (SP_IS_CANVAS (canvas));
2299 if (winx) *winx = worldx - canvas->x0;
2300 if (winy) *winy = worldy - canvas->y0;
2301 }
2303 /**
2304 * Converts point from win to world coordinates.
2305 */
2306 Geom::Point sp_canvas_window_to_world(SPCanvas const *canvas, Geom::Point const win)
2307 {
2308 g_assert (canvas != NULL);
2309 g_assert (SP_IS_CANVAS (canvas));
2311 return Geom::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2312 }
2314 /**
2315 * Converts point from world to win coordinates.
2316 */
2317 Geom::Point sp_canvas_world_to_window(SPCanvas const *canvas, Geom::Point const world)
2318 {
2319 g_assert (canvas != NULL);
2320 g_assert (SP_IS_CANVAS (canvas));
2322 return Geom::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2323 }
2325 /**
2326 * Returns true if point given in world coordinates is inside window.
2327 */
2328 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, Geom::Point const &world)
2329 {
2330 g_assert( canvas != NULL );
2331 g_assert(SP_IS_CANVAS(canvas));
2333 GtkWidget const &w = *GTK_WIDGET(canvas);
2334 return ( ( canvas->x0 <= world[Geom::X] ) &&
2335 ( canvas->y0 <= world[Geom::Y] ) &&
2336 ( world[Geom::X] < canvas->x0 + w.allocation.width ) &&
2337 ( world[Geom::Y] < canvas->y0 + w.allocation.height ) );
2338 }
2340 /**
2341 * Return canvas window coordinates as Geom::Rect.
2342 */
2343 Geom::Rect SPCanvas::getViewbox() const
2344 {
2345 GtkWidget const *w = GTK_WIDGET(this);
2346 return Geom::Rect(Geom::Point(dx0, dy0),
2347 Geom::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2348 }
2350 /**
2351 * Return canvas window coordinates as IRect (a rectangle defined by integers).
2352 */
2353 NR::IRect SPCanvas::getViewboxIntegers() const
2354 {
2355 GtkWidget const *w = GTK_WIDGET(this);
2356 return NR::IRect(NR::IPoint(x0, y0),
2357 NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height));
2358 }
2360 inline int sp_canvas_tile_floor(int x)
2361 {
2362 return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2363 }
2365 inline int sp_canvas_tile_ceil(int x)
2366 {
2367 return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2368 }
2370 /**
2371 * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2372 */
2373 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2374 {
2375 if ( nl >= nr || nt >= nb ) {
2376 if ( canvas->tiles ) g_free(canvas->tiles);
2377 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2378 canvas->tileH=canvas->tileV=0;
2379 canvas->tiles=NULL;
2380 return;
2381 }
2382 int tl=sp_canvas_tile_floor(nl);
2383 int tt=sp_canvas_tile_floor(nt);
2384 int tr=sp_canvas_tile_ceil(nr);
2385 int tb=sp_canvas_tile_ceil(nb);
2387 int nh = tr-tl, nv = tb-tt;
2388 uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2389 for (int i=tl; i<tr; i++) {
2390 for (int j=tt; j<tb; j++) {
2391 int ind = (i-tl) + (j-tt)*nh;
2392 if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2393 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2394 } else {
2395 ntiles[ind]=0; // newly exposed areas get 0
2396 }
2397 }
2398 }
2399 if ( canvas->tiles ) g_free(canvas->tiles);
2400 canvas->tiles=ntiles;
2401 canvas->tLeft=tl;
2402 canvas->tTop=tt;
2403 canvas->tRight=tr;
2404 canvas->tBottom=tb;
2405 canvas->tileH=nh;
2406 canvas->tileV=nv;
2407 }
2409 /*
2410 * Helper that queues a canvas rectangle for redraw
2411 */
2412 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2413 canvas->need_redraw = TRUE;
2415 sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2416 }
2418 /**
2419 * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2420 */
2421 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2422 {
2423 if ( nl >= nr || nt >= nb ) {
2424 return;
2425 }
2426 int tl=sp_canvas_tile_floor(nl);
2427 int tt=sp_canvas_tile_floor(nt);
2428 int tr=sp_canvas_tile_ceil(nr);
2429 int tb=sp_canvas_tile_ceil(nb);
2430 if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2431 if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2432 if ( tr > canvas->tRight ) tr=canvas->tRight;
2433 if ( tt < canvas->tTop ) tt=canvas->tTop;
2434 if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2436 for (int i=tl; i<tr; i++) {
2437 for (int j=tt; j<tb; j++) {
2438 canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2439 }
2440 }
2441 }
2444 /*
2445 Local Variables:
2446 mode:c++
2447 c-file-style:"stroustrup"
2448 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2449 indent-tabs-mode:nil
2450 fill-column:99
2451 End:
2452 */
2453 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :