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};
106 enum {PROP_0, PROP_VISIBLE};
109 static void sp_canvas_request_update (SPCanvas *canvas);
111 static void track_latency(GdkEvent const *event);
112 static void sp_canvas_item_class_init (SPCanvasItemClass *klass);
113 static void sp_canvas_item_init (SPCanvasItem *item);
114 static void sp_canvas_item_dispose (GObject *object);
115 static void sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args);
117 static int emit_event (SPCanvas *canvas, GdkEvent *event);
118 static int pick_current_item (SPCanvas *canvas, GdkEvent *event);
120 static guint item_signals[ITEM_LAST_SIGNAL] = { 0 };
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 item_signals[ITEM_EVENT] = g_signal_new ("event",
155 G_TYPE_FROM_CLASS (klass),
156 G_SIGNAL_RUN_LAST,
157 ((glong)((guint8*)&(klass->event) - (guint8*)klass)),
158 NULL, NULL,
159 sp_marshal_BOOLEAN__POINTER,
160 G_TYPE_BOOLEAN, 1,
161 GDK_TYPE_EVENT);
163 object_class->dispose = sp_canvas_item_dispose;
164 }
166 /**
167 * Callback for initialization of SPCanvasItem.
168 */
169 static void
170 sp_canvas_item_init (SPCanvasItem *item)
171 {
172 // TODO items should not be visible on creation - this causes kludges with items
173 // that should be initially invisible; examples of such items: node handles, the CtrlRect
174 // used for rubberbanding, path outline, etc.
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 (g_type_class_peek(g_type_parent(sp_canvas_item_get_type())))->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 bool
481 sp_canvas_item_is_visible (SPCanvasItem *item)
482 {
483 return item->flags & SP_CANVAS_ITEM_VISIBLE;
484 }
487 /**
488 * Sets visible flag on item and requests a redraw.
489 */
490 void
491 sp_canvas_item_show (SPCanvasItem *item)
492 {
493 g_return_if_fail (item != NULL);
494 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
496 if (item->flags & SP_CANVAS_ITEM_VISIBLE)
497 return;
499 item->flags |= SP_CANVAS_ITEM_VISIBLE;
501 int x0 = (int)(item->x1);
502 int x1 = (int)(item->x2);
503 int y0 = (int)(item->y1);
504 int y1 = (int)(item->y2);
506 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
507 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
508 item->canvas->need_repick = TRUE;
509 }
510 }
512 /**
513 * Clears visible flag on item and requests a redraw.
514 */
515 void
516 sp_canvas_item_hide (SPCanvasItem *item)
517 {
518 g_return_if_fail (item != NULL);
519 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
521 if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
522 return;
524 item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
526 int x0 = (int)(item->x1);
527 int x1 = (int)(item->x2);
528 int y0 = (int)(item->y1);
529 int y1 = (int)(item->y2);
531 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
532 sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
533 item->canvas->need_repick = TRUE;
534 }
535 }
537 /**
538 * Grab item under cursor.
539 *
540 * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE
541 */
542 int
543 sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
544 {
545 g_return_val_if_fail (item != NULL, -1);
546 g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1);
547 g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1);
549 if (item->canvas->grabbed_item)
550 return -1;
552 // This test disallows grabbing events by an invisible item, which may be useful
553 // sometimes. An example is the hidden control point used for the selector component,
554 // where it is used for object selection and rubberbanding. There seems to be nothing
555 // preventing this except this test, so I removed it.
556 // -- Krzysztof Kosiński, 2009.08.12
557 //if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
558 // return -1;
560 if (HAS_BROKEN_MOTION_HINTS) {
561 event_mask &= ~GDK_POINTER_MOTION_HINT_MASK;
562 }
564 /* fixme: Top hack (Lauris) */
565 /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
566 /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
567 gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
568 (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
569 NULL, cursor, etime);
571 item->canvas->grabbed_item = item;
572 item->canvas->grabbed_event_mask = event_mask;
573 item->canvas->current_item = item; /* So that events go to the grabbed item */
575 return 0;
576 }
578 /**
579 * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
580 * mouse.
581 *
582 * \param item A canvas item that holds a grab.
583 * \param etime The timestamp for ungrabbing the mouse.
584 */
585 void
586 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
587 {
588 g_return_if_fail (item != NULL);
589 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
591 if (item->canvas->grabbed_item != item)
592 return;
594 item->canvas->grabbed_item = NULL;
596 gdk_pointer_ungrab (etime);
597 }
599 /**
600 * Returns the product of all transformation matrices from the root item down
601 * to the item.
602 */
603 Geom::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
604 {
605 g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
607 Geom::Matrix affine = Geom::identity();
609 while (item) {
610 affine *= item->xform;
611 item = item->parent;
612 }
613 return affine;
614 }
616 /**
617 * Helper that returns true iff item is descendant of parent.
618 */
619 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
620 {
621 while (item) {
622 if (item == parent)
623 return true;
624 item = item->parent;
625 }
627 return false;
628 }
630 /**
631 * Focus canvas, and item under cursor if it is not already focussed.
632 */
633 void
634 sp_canvas_item_grab_focus (SPCanvasItem *item)
635 {
636 g_return_if_fail (item != NULL);
637 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
638 g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
640 SPCanvasItem *focused_item = item->canvas->focused_item;
642 if (focused_item) {
643 GdkEvent ev;
644 ev.focus_change.type = GDK_FOCUS_CHANGE;
645 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
646 ev.focus_change.send_event = FALSE;
647 ev.focus_change.in = FALSE;
649 emit_event (item->canvas, &ev);
650 }
652 item->canvas->focused_item = item;
653 gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
655 if (focused_item) {
656 GdkEvent ev;
657 ev.focus_change.type = GDK_FOCUS_CHANGE;
658 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
659 ev.focus_change.send_event = FALSE;
660 ev.focus_change.in = TRUE;
662 emit_event (item->canvas, &ev);
663 }
664 }
666 /**
667 * Requests that the canvas queue an update for the specified item.
668 *
669 * To be used only by item implementations.
670 */
671 void
672 sp_canvas_item_request_update (SPCanvasItem *item)
673 {
674 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
675 return;
677 item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
679 if (item->parent != NULL) {
680 /* Recurse up the tree */
681 sp_canvas_item_request_update (item->parent);
682 } else {
683 /* Have reached the top of the tree, make sure the update call gets scheduled. */
684 sp_canvas_request_update (item->canvas);
685 }
686 }
688 /**
689 * Returns position of item in group.
690 */
691 gint sp_canvas_item_order (SPCanvasItem * item)
692 {
693 return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
694 }
696 /* SPCanvasGroup */
698 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
699 static void sp_canvas_group_init (SPCanvasGroup *group);
700 static void sp_canvas_group_destroy (GtkObject *object);
702 static void sp_canvas_group_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags);
703 static double sp_canvas_group_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item);
704 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
706 static SPCanvasItemClass *group_parent_class;
708 /**
709 * Registers SPCanvasGroup class with Gtk and returns its type number.
710 */
711 GType sp_canvas_group_get_type(void)
712 {
713 static GType type = 0;
714 if (!type) {
715 GTypeInfo info = {
716 sizeof(SPCanvasGroupClass),
717 0, // base_init
718 0, // base_finalize
719 (GClassInitFunc)sp_canvas_group_class_init,
720 0, // class_finalize
721 0, // class_data
722 sizeof(SPCanvasGroup),
723 0, // n_preallocs
724 (GInstanceInitFunc)sp_canvas_group_init,
725 0 // value_table
726 };
727 type = g_type_register_static(sp_canvas_item_get_type(), "SPCanvasGroup", &info, static_cast<GTypeFlags>(0));
728 }
729 return type;
730 }
732 /**
733 * Class initialization function for SPCanvasGroupClass
734 */
735 static void
736 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
737 {
738 GtkObjectClass *object_class = (GtkObjectClass *) klass;
739 SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
741 group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
743 object_class->destroy = sp_canvas_group_destroy;
745 item_class->update = sp_canvas_group_update;
746 item_class->render = sp_canvas_group_render;
747 item_class->point = sp_canvas_group_point;
748 }
750 /**
751 * Callback. Empty.
752 */
753 static void
754 sp_canvas_group_init (SPCanvasGroup */*group*/)
755 {
756 /* Nothing here */
757 }
759 /**
760 * Callback that destroys all items in group and calls group's virtual
761 * destroy() function.
762 */
763 static void
764 sp_canvas_group_destroy (GtkObject *object)
765 {
766 g_return_if_fail (object != NULL);
767 g_return_if_fail (SP_IS_CANVAS_GROUP (object));
769 SPCanvasGroup const *group = SP_CANVAS_GROUP (object);
771 GList *list = group->items;
772 while (list) {
773 SPCanvasItem *child = (SPCanvasItem *)list->data;
774 list = list->next;
776 gtk_object_destroy (GTK_OBJECT (child));
777 }
779 if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
780 (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
781 }
783 /**
784 * Update handler for canvas groups
785 */
786 static void
787 sp_canvas_group_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags)
788 {
789 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
790 Geom::RectHull corners(Geom::Point(0, 0));
791 bool empty=true;
793 for (GList *list = group->items; list; list = list->next) {
794 SPCanvasItem *i = (SPCanvasItem *)list->data;
796 sp_canvas_item_invoke_update (i, affine, flags);
798 if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
799 if (empty) {
800 corners = Geom::RectHull(Geom::Point(i->x1, i->y1));
801 empty = false;
802 } else {
803 corners.add(Geom::Point(i->x1, i->y1));
804 }
805 corners.add(Geom::Point(i->x2, i->y2));
806 }
807 }
809 Geom::OptRect const bounds = corners.bounds();
810 if (bounds) {
811 item->x1 = bounds->min()[Geom::X];
812 item->y1 = bounds->min()[Geom::Y];
813 item->x2 = bounds->max()[Geom::X];
814 item->y2 = bounds->max()[Geom::Y];
815 } else {
816 // FIXME ?
817 item->x1 = item->x2 = item->y1 = item->y2 = 0;
818 }
819 }
821 /**
822 * Point handler for canvas groups.
823 */
824 static double
825 sp_canvas_group_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item)
826 {
827 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
828 double const x = p[Geom::X];
829 double const y = p[Geom::Y];
830 int x1 = (int)(x - item->canvas->close_enough);
831 int y1 = (int)(y - item->canvas->close_enough);
832 int x2 = (int)(x + item->canvas->close_enough);
833 int y2 = (int)(y + item->canvas->close_enough);
835 double best = 0.0;
836 *actual_item = NULL;
838 double dist = 0.0;
840 for (GList *list = group->items; list; list = list->next) {
841 SPCanvasItem *child = (SPCanvasItem *)list->data;
843 if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
844 SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
846 int has_point;
847 if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
848 dist = sp_canvas_item_invoke_point (child, p, &point_item);
849 has_point = TRUE;
850 } else
851 has_point = FALSE;
853 if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
854 best = dist;
855 *actual_item = point_item;
856 }
857 }
858 }
860 return best;
861 }
863 /**
864 * Renders all visible canvas group items in buf rectangle.
865 */
866 static void
867 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
868 {
869 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
871 for (GList *list = group->items; list; list = list->next) {
872 SPCanvasItem *child = (SPCanvasItem *)list->data;
873 if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
874 if ((child->x1 < buf->rect.x1) &&
875 (child->y1 < buf->rect.y1) &&
876 (child->x2 > buf->rect.x0) &&
877 (child->y2 > buf->rect.y0)) {
878 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
879 SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
880 }
881 }
882 }
883 }
885 /**
886 * Adds an item to a canvas group.
887 */
888 static void
889 group_add (SPCanvasGroup *group, SPCanvasItem *item)
890 {
891 gtk_object_ref (GTK_OBJECT (item));
892 gtk_object_sink (GTK_OBJECT (item));
894 if (!group->items) {
895 group->items = g_list_append (group->items, item);
896 group->last = group->items;
897 } else {
898 group->last = g_list_append (group->last, item)->next;
899 }
901 sp_canvas_item_request_update (item);
902 }
904 /**
905 * Removes an item from a canvas group
906 */
907 static void
908 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
909 {
910 g_return_if_fail (group != NULL);
911 g_return_if_fail (SP_IS_CANVAS_GROUP (group));
912 g_return_if_fail (item != NULL);
914 for (GList *children = group->items; children; children = children->next) {
915 if (children->data == item) {
917 /* Unparent the child */
919 item->parent = NULL;
920 gtk_object_unref (GTK_OBJECT (item));
922 /* Remove it from the list */
924 if (children == group->last) group->last = children->prev;
926 group->items = g_list_remove_link (group->items, children);
927 g_list_free (children);
928 break;
929 }
930 }
931 }
933 /* SPCanvas */
935 static void sp_canvas_class_init (SPCanvasClass *klass);
936 static void sp_canvas_init (SPCanvas *canvas);
937 static void sp_canvas_destroy (GtkObject *object);
939 static void sp_canvas_realize (GtkWidget *widget);
940 static void sp_canvas_unrealize (GtkWidget *widget);
942 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
943 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
945 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
946 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
947 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
948 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
949 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
950 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
951 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
952 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
954 static GtkWidgetClass *canvas_parent_class;
956 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb);
957 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb);
958 static void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val);
959 static int do_update (SPCanvas *canvas);
961 /**
962 * Registers the SPCanvas class if necessary, and returns the type ID
963 * associated to it.
964 *
965 * \return The type ID of the SPCanvas class.
966 **/
967 GType sp_canvas_get_type(void)
968 {
969 static GType type = 0;
970 if (!type) {
971 GTypeInfo info = {
972 sizeof(SPCanvasClass),
973 0, // base_init
974 0, // base_finalize
975 (GClassInitFunc)sp_canvas_class_init,
976 0, // class_finalize
977 0, // class_data
978 sizeof(SPCanvas),
979 0, // n_preallocs
980 (GInstanceInitFunc)sp_canvas_init,
981 0 // value_table
982 };
983 type = g_type_register_static(GTK_TYPE_WIDGET, "SPCanvas", &info, static_cast<GTypeFlags>(0));
984 }
985 return type;
986 }
988 /**
989 * Class initialization function for SPCanvasClass.
990 */
991 static void
992 sp_canvas_class_init (SPCanvasClass *klass)
993 {
994 GtkObjectClass *object_class = (GtkObjectClass *) klass;
995 GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
997 canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
999 object_class->destroy = sp_canvas_destroy;
1001 widget_class->realize = sp_canvas_realize;
1002 widget_class->unrealize = sp_canvas_unrealize;
1003 widget_class->size_request = sp_canvas_size_request;
1004 widget_class->size_allocate = sp_canvas_size_allocate;
1005 widget_class->button_press_event = sp_canvas_button;
1006 widget_class->button_release_event = sp_canvas_button;
1007 widget_class->motion_notify_event = sp_canvas_motion;
1008 widget_class->scroll_event = sp_canvas_scroll;
1009 widget_class->expose_event = sp_canvas_expose;
1010 widget_class->key_press_event = sp_canvas_key;
1011 widget_class->key_release_event = sp_canvas_key;
1012 widget_class->enter_notify_event = sp_canvas_crossing;
1013 widget_class->leave_notify_event = sp_canvas_crossing;
1014 widget_class->focus_in_event = sp_canvas_focus_in;
1015 widget_class->focus_out_event = sp_canvas_focus_out;
1016 }
1018 /**
1019 * Callback: object initialization for SPCanvas.
1020 */
1021 static void
1022 sp_canvas_init (SPCanvas *canvas)
1023 {
1024 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
1025 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
1026 GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
1028 canvas->pick_event.type = GDK_LEAVE_NOTIFY;
1029 canvas->pick_event.crossing.x = 0;
1030 canvas->pick_event.crossing.y = 0;
1032 /* Create the root item as a special case */
1033 canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
1034 canvas->root->canvas = canvas;
1036 gtk_object_ref (GTK_OBJECT (canvas->root));
1037 gtk_object_sink (GTK_OBJECT (canvas->root));
1039 canvas->need_repick = TRUE;
1041 // See comment at in sp-canvas.h.
1042 canvas->gen_all_enter_events = false;
1044 canvas->drawing_disabled = false;
1046 canvas->tiles=NULL;
1047 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1048 canvas->tileH=canvas->tileV=0;
1050 canvas->forced_redraw_count = 0;
1051 canvas->forced_redraw_limit = -1;
1053 #if ENABLE_LCMS
1054 canvas->enable_cms_display_adj = false;
1055 canvas->cms_key = new Glib::ustring("");
1056 #endif // ENABLE_LCMS
1058 canvas->is_scrolling = false;
1059 }
1061 /**
1062 * Convenience function to remove the idle handler of a canvas.
1063 */
1064 static void
1065 remove_idle (SPCanvas *canvas)
1066 {
1067 if (canvas->idle_id) {
1068 gtk_idle_remove (canvas->idle_id);
1069 canvas->idle_id = 0;
1070 }
1071 }
1073 /*
1074 * Removes the transient state of the canvas (idle handler, grabs).
1075 */
1076 static void
1077 shutdown_transients (SPCanvas *canvas)
1078 {
1079 /* We turn off the need_redraw flag, since if the canvas is mapped again
1080 * it will request a redraw anyways. We do not turn off the need_update
1081 * flag, though, because updates are not queued when the canvas remaps
1082 * itself.
1083 */
1084 if (canvas->need_redraw) {
1085 canvas->need_redraw = FALSE;
1086 }
1087 if ( canvas->tiles ) g_free(canvas->tiles);
1088 canvas->tiles=NULL;
1089 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1090 canvas->tileH=canvas->tileV=0;
1092 if (canvas->grabbed_item) {
1093 canvas->grabbed_item = NULL;
1094 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1095 }
1097 remove_idle (canvas);
1098 }
1100 /**
1101 * Destroy handler for SPCanvas.
1102 */
1103 static void
1104 sp_canvas_destroy (GtkObject *object)
1105 {
1106 SPCanvas *canvas = SP_CANVAS (object);
1108 if (canvas->root) {
1109 gtk_object_unref (GTK_OBJECT (canvas->root));
1110 canvas->root = NULL;
1111 }
1113 shutdown_transients (canvas);
1115 if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1116 (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1117 }
1119 static void track_latency(GdkEvent const *event) {
1120 GdkEventLatencyTracker &tracker = GdkEventLatencyTracker::default_tracker();
1121 boost::optional<double> latency = tracker.process(event);
1122 if (latency && *latency > 2.0) {
1123 //g_warning("Event latency reached %f sec (%1.4f)", *latency, tracker.getSkew());
1124 }
1125 }
1127 /**
1128 * Returns new canvas as widget.
1129 */
1130 GtkWidget *
1131 sp_canvas_new_aa (void)
1132 {
1133 SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1135 return (GtkWidget *) canvas;
1136 }
1138 /**
1139 * The canvas widget's realize callback.
1140 */
1141 static void
1142 sp_canvas_realize (GtkWidget *widget)
1143 {
1144 SPCanvas *canvas = SP_CANVAS (widget);
1146 GdkWindowAttr attributes;
1147 attributes.window_type = GDK_WINDOW_CHILD;
1148 attributes.x = widget->allocation.x;
1149 attributes.y = widget->allocation.y;
1150 attributes.width = widget->allocation.width;
1151 attributes.height = widget->allocation.height;
1152 attributes.wclass = GDK_INPUT_OUTPUT;
1153 attributes.visual = gdk_rgb_get_visual ();
1154 attributes.colormap = gdk_rgb_get_cmap ();
1155 attributes.event_mask = (gtk_widget_get_events (widget) |
1156 GDK_EXPOSURE_MASK |
1157 GDK_BUTTON_PRESS_MASK |
1158 GDK_BUTTON_RELEASE_MASK |
1159 GDK_POINTER_MOTION_MASK |
1160 ( HAS_BROKEN_MOTION_HINTS ?
1161 0 : GDK_POINTER_MOTION_HINT_MASK ) |
1162 GDK_PROXIMITY_IN_MASK |
1163 GDK_PROXIMITY_OUT_MASK |
1164 GDK_KEY_PRESS_MASK |
1165 GDK_KEY_RELEASE_MASK |
1166 GDK_ENTER_NOTIFY_MASK |
1167 GDK_LEAVE_NOTIFY_MASK |
1168 GDK_FOCUS_CHANGE_MASK);
1169 gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1171 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1172 gdk_window_set_user_data (widget->window, widget);
1174 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1175 if ( prefs->getBool("/options/useextinput/value", true) )
1176 gtk_widget_set_events(widget, attributes.event_mask);
1178 widget->style = gtk_style_attach (widget->style, widget->window);
1180 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1182 canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1183 }
1185 /**
1186 * The canvas widget's unrealize callback.
1187 */
1188 static void
1189 sp_canvas_unrealize (GtkWidget *widget)
1190 {
1191 SPCanvas *canvas = SP_CANVAS (widget);
1193 canvas->current_item = NULL;
1194 canvas->grabbed_item = NULL;
1195 canvas->focused_item = NULL;
1197 shutdown_transients (canvas);
1199 gdk_gc_destroy (canvas->pixmap_gc);
1200 canvas->pixmap_gc = NULL;
1202 if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1203 (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1204 }
1206 /**
1207 * The canvas widget's size_request callback.
1208 */
1209 static void
1210 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1211 {
1212 static_cast<void>(SP_CANVAS (widget));
1214 req->width = 256;
1215 req->height = 256;
1216 }
1218 /**
1219 * The canvas widget's size_allocate callback.
1220 */
1221 static void
1222 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1223 {
1224 SPCanvas *canvas = SP_CANVAS (widget);
1226 /* Schedule redraw of new region */
1227 sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1228 if (allocation->width > widget->allocation.width) {
1229 sp_canvas_request_redraw (canvas,
1230 canvas->x0 + widget->allocation.width,
1231 0,
1232 canvas->x0 + allocation->width,
1233 canvas->y0 + allocation->height);
1234 }
1235 if (allocation->height > widget->allocation.height) {
1236 sp_canvas_request_redraw (canvas,
1237 0,
1238 canvas->y0 + widget->allocation.height,
1239 canvas->x0 + allocation->width,
1240 canvas->y0 + allocation->height);
1241 }
1243 widget->allocation = *allocation;
1245 if (GTK_WIDGET_REALIZED (widget)) {
1246 gdk_window_move_resize (widget->window,
1247 widget->allocation.x, widget->allocation.y,
1248 widget->allocation.width, widget->allocation.height);
1249 }
1250 }
1252 /**
1253 * Helper that emits an event for an item in the canvas, be it the current
1254 * item, grabbed item, or focused item, as appropriate.
1255 */
1256 static int
1257 emit_event (SPCanvas *canvas, GdkEvent *event)
1258 {
1259 guint mask;
1261 if (canvas->grabbed_item) {
1262 switch (event->type) {
1263 case GDK_ENTER_NOTIFY:
1264 mask = GDK_ENTER_NOTIFY_MASK;
1265 break;
1266 case GDK_LEAVE_NOTIFY:
1267 mask = GDK_LEAVE_NOTIFY_MASK;
1268 break;
1269 case GDK_MOTION_NOTIFY:
1270 mask = GDK_POINTER_MOTION_MASK;
1271 break;
1272 case GDK_BUTTON_PRESS:
1273 case GDK_2BUTTON_PRESS:
1274 case GDK_3BUTTON_PRESS:
1275 mask = GDK_BUTTON_PRESS_MASK;
1276 break;
1277 case GDK_BUTTON_RELEASE:
1278 mask = GDK_BUTTON_RELEASE_MASK;
1279 break;
1280 case GDK_KEY_PRESS:
1281 mask = GDK_KEY_PRESS_MASK;
1282 break;
1283 case GDK_KEY_RELEASE:
1284 mask = GDK_KEY_RELEASE_MASK;
1285 break;
1286 case GDK_SCROLL:
1287 mask = GDK_SCROLL;
1288 break;
1289 default:
1290 mask = 0;
1291 break;
1292 }
1294 if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1295 }
1297 /* Convert to world coordinates -- we have two cases because of different
1298 * offsets of the fields in the event structures.
1299 */
1301 GdkEvent ev = *event;
1303 switch (ev.type) {
1304 case GDK_ENTER_NOTIFY:
1305 case GDK_LEAVE_NOTIFY:
1306 ev.crossing.x += canvas->x0;
1307 ev.crossing.y += canvas->y0;
1308 break;
1309 case GDK_MOTION_NOTIFY:
1310 case GDK_BUTTON_PRESS:
1311 case GDK_2BUTTON_PRESS:
1312 case GDK_3BUTTON_PRESS:
1313 case GDK_BUTTON_RELEASE:
1314 ev.motion.x += canvas->x0;
1315 ev.motion.y += canvas->y0;
1316 break;
1317 default:
1318 break;
1319 }
1321 /* Choose where we send the event */
1323 /* canvas->current_item becomes NULL in some cases under Win32
1324 ** (e.g. if the pointer leaves the window). So this is a hack that
1325 ** Lauris applied to SP to get around the problem.
1326 */
1327 SPCanvasItem* item = NULL;
1328 if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1329 item = canvas->grabbed_item;
1330 } else {
1331 // Make sure that current_item is up-to-date. If a snap indicator was just deleted, then
1332 // sp_canvas_item_dispose has been called and there is no current_item specified. We need
1333 // that though because otherwise we don't know where to send this event to, leading to a
1334 // lost event. We can't wait for idle events to have current_item updated, we need it now!
1335 // Otherwise, scrolling when hovering above a pre-snap indicator won't work (for example)
1336 // See this bug report: https://bugs.launchpad.net/inkscape/+bug/522335/comments/8
1337 if (canvas->need_repick && !canvas->in_repick && event->type == GDK_SCROLL) {
1338 // To avoid side effects, we'll only do this for scroll events, because this is the
1339 // only thing we want to fix here. An example of a reported side effect is that
1340 // otherwise selection of nodes in the node editor by dragging a rectangle using a
1341 // tablet will break
1342 canvas->need_repick = FALSE;
1343 pick_current_item (canvas, (GdkEvent *) event);
1344 }
1345 item = canvas->current_item;
1346 }
1348 if (canvas->focused_item &&
1349 ((event->type == GDK_KEY_PRESS) ||
1350 (event->type == GDK_KEY_RELEASE) ||
1351 (event->type == GDK_FOCUS_CHANGE))) {
1352 item = canvas->focused_item;
1353 }
1355 /* The event is propagated up the hierarchy (for if someone connected to
1356 * a group instead of a leaf event), and emission is stopped if a
1357 * handler returns TRUE, just like for GtkWidget events.
1358 */
1360 gint finished = FALSE;
1362 while (item && !finished) {
1363 gtk_object_ref (GTK_OBJECT (item));
1364 gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1365 SPCanvasItem *parent = item->parent;
1366 gtk_object_unref (GTK_OBJECT (item));
1367 item = parent;
1368 }
1370 return finished;
1371 }
1373 /**
1374 * Helper that re-picks the current item in the canvas, based on the event's
1375 * coordinates and emits enter/leave events for items as appropriate.
1376 */
1377 static int
1378 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1379 {
1380 int button_down = 0;
1381 double x, y;
1383 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1384 return FALSE;
1386 int retval = FALSE;
1388 if (canvas->gen_all_enter_events == false) {
1389 // If a button is down, we'll perform enter and leave events on the
1390 // current item, but not enter on any other item. This is more or
1391 // less like X pointer grabbing for canvas items.
1392 //
1393 button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1394 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1396 if (!button_down) canvas->left_grabbed_item = FALSE;
1397 }
1399 /* Save the event in the canvas. This is used to synthesize enter and
1400 * leave events in case the current item changes. It is also used to
1401 * re-pick the current item if the current one gets deleted. Also,
1402 * synthesize an enter event.
1403 */
1404 if (event != &canvas->pick_event) {
1405 if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1406 /* these fields have the same offsets in both types of events */
1408 canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY;
1409 canvas->pick_event.crossing.window = event->motion.window;
1410 canvas->pick_event.crossing.send_event = event->motion.send_event;
1411 canvas->pick_event.crossing.subwindow = NULL;
1412 canvas->pick_event.crossing.x = event->motion.x;
1413 canvas->pick_event.crossing.y = event->motion.y;
1414 canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL;
1415 canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR;
1416 canvas->pick_event.crossing.focus = FALSE;
1417 canvas->pick_event.crossing.state = event->motion.state;
1419 /* these fields don't have the same offsets in both types of events */
1421 if (event->type == GDK_MOTION_NOTIFY) {
1422 canvas->pick_event.crossing.x_root = event->motion.x_root;
1423 canvas->pick_event.crossing.y_root = event->motion.y_root;
1424 } else {
1425 canvas->pick_event.crossing.x_root = event->button.x_root;
1426 canvas->pick_event.crossing.y_root = event->button.y_root;
1427 }
1428 } else {
1429 canvas->pick_event = *event;
1430 }
1431 }
1433 /* Don't do anything else if this is a recursive call */
1434 if (canvas->in_repick) return retval;
1436 /* LeaveNotify means that there is no current item, so we don't look for one */
1437 if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1438 /* these fields don't have the same offsets in both types of events */
1440 if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1441 x = canvas->pick_event.crossing.x;
1442 y = canvas->pick_event.crossing.y;
1443 } else {
1444 x = canvas->pick_event.motion.x;
1445 y = canvas->pick_event.motion.y;
1446 }
1448 /* world coords */
1449 x += canvas->x0;
1450 y += canvas->y0;
1452 /* find the closest item */
1453 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1454 sp_canvas_item_invoke_point (canvas->root, Geom::Point(x, y), &canvas->new_current_item);
1455 } else {
1456 canvas->new_current_item = NULL;
1457 }
1458 } else {
1459 canvas->new_current_item = NULL;
1460 }
1462 if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1463 return retval; /* current item did not change */
1464 }
1466 /* Synthesize events for old and new current items */
1468 if ((canvas->new_current_item != canvas->current_item)
1469 && (canvas->current_item != NULL)
1470 && !canvas->left_grabbed_item) {
1471 GdkEvent new_event;
1472 SPCanvasItem *item;
1474 item = canvas->current_item;
1476 new_event = canvas->pick_event;
1477 new_event.type = GDK_LEAVE_NOTIFY;
1479 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1480 new_event.crossing.subwindow = NULL;
1481 canvas->in_repick = TRUE;
1482 retval = emit_event (canvas, &new_event);
1483 canvas->in_repick = FALSE;
1484 }
1486 if (canvas->gen_all_enter_events == false) {
1487 // new_current_item may have been set to NULL during the call to
1488 // emit_event() above
1489 if ((canvas->new_current_item != canvas->current_item) && button_down) {
1490 canvas->left_grabbed_item = TRUE;
1491 return retval;
1492 }
1493 }
1495 /* Handle the rest of cases */
1497 canvas->left_grabbed_item = FALSE;
1498 canvas->current_item = canvas->new_current_item;
1500 if (canvas->current_item != NULL) {
1501 GdkEvent new_event;
1503 new_event = canvas->pick_event;
1504 new_event.type = GDK_ENTER_NOTIFY;
1505 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1506 new_event.crossing.subwindow = NULL;
1507 retval = emit_event (canvas, &new_event);
1508 }
1512 return retval;
1513 }
1515 /**
1516 * Button event handler for the canvas.
1517 */
1518 static gint
1519 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1520 {
1521 SPCanvas *canvas = SP_CANVAS (widget);
1523 int retval = FALSE;
1525 /* dispatch normally regardless of the event's window if an item
1526 has a pointer grab in effect */
1527 if (!canvas->grabbed_item &&
1528 event->window != SP_CANVAS_WINDOW (canvas))
1529 return retval;
1531 int mask;
1532 switch (event->button) {
1533 case 1:
1534 mask = GDK_BUTTON1_MASK;
1535 break;
1536 case 2:
1537 mask = GDK_BUTTON2_MASK;
1538 break;
1539 case 3:
1540 mask = GDK_BUTTON3_MASK;
1541 break;
1542 case 4:
1543 mask = GDK_BUTTON4_MASK;
1544 break;
1545 case 5:
1546 mask = GDK_BUTTON5_MASK;
1547 break;
1548 default:
1549 mask = 0;
1550 }
1552 switch (event->type) {
1553 case GDK_BUTTON_PRESS:
1554 case GDK_2BUTTON_PRESS:
1555 case GDK_3BUTTON_PRESS:
1556 /* Pick the current item as if the button were not pressed, and
1557 * then process the event.
1558 */
1559 canvas->state = event->state;
1560 pick_current_item (canvas, (GdkEvent *) event);
1561 canvas->state ^= mask;
1562 retval = emit_event (canvas, (GdkEvent *) event);
1563 break;
1565 case GDK_BUTTON_RELEASE:
1566 /* Process the event as if the button were pressed, then repick
1567 * after the button has been released
1568 */
1569 canvas->state = event->state;
1570 retval = emit_event (canvas, (GdkEvent *) event);
1571 event->state ^= mask;
1572 canvas->state = event->state;
1573 pick_current_item (canvas, (GdkEvent *) event);
1574 event->state ^= mask;
1576 break;
1578 default:
1579 g_assert_not_reached ();
1580 }
1582 return retval;
1583 }
1585 /**
1586 * Scroll event handler for the canvas.
1587 *
1588 * \todo FIXME: generate motion events to re-select items.
1589 */
1590 static gint
1591 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1592 {
1593 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1594 }
1596 static inline void request_motions(GdkWindow *w, GdkEventMotion *event) {
1597 gdk_window_get_pointer(w, NULL, NULL, NULL);
1598 #if HAS_GDK_EVENT_REQUEST_MOTIONS
1599 gdk_event_request_motions(event);
1600 #endif
1601 }
1603 /**
1604 * Motion event handler for the canvas.
1605 */
1606 static int
1607 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1608 {
1609 int status;
1610 SPCanvas *canvas = SP_CANVAS (widget);
1612 track_latency((GdkEvent *)event);
1614 if (event->window != SP_CANVAS_WINDOW (canvas))
1615 return FALSE;
1617 if (canvas->pixmap_gc == NULL) // canvas being deleted
1618 return FALSE;
1620 canvas->state = event->state;
1621 pick_current_item (canvas, (GdkEvent *) event);
1622 status = emit_event (canvas, (GdkEvent *) event);
1623 if (event->is_hint) {
1624 request_motions(widget->window, event);
1625 }
1627 return status;
1628 }
1630 static void
1631 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)
1632 {
1633 GtkWidget *widget = GTK_WIDGET (canvas);
1635 SPCanvasBuf buf;
1636 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1637 buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1638 } else {
1639 buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1640 }
1642 // Mark the region clean
1643 sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1645 buf.buf_rowstride = sw * 4;
1646 buf.rect.x0 = x0;
1647 buf.rect.y0 = y0;
1648 buf.rect.x1 = x1;
1649 buf.rect.y1 = y1;
1650 buf.visible_rect.x0 = draw_x1;
1651 buf.visible_rect.y0 = draw_y1;
1652 buf.visible_rect.x1 = draw_x2;
1653 buf.visible_rect.y1 = draw_y2;
1654 GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1655 buf.bg_color = (((color->red & 0xff00) << 8)
1656 | (color->green & 0xff00)
1657 | (color->blue >> 8));
1658 buf.is_empty = true;
1660 buf.ct = nr_create_cairo_context_canvasbuf (&(buf.visible_rect), &buf);
1662 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1663 SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1664 }
1666 #if ENABLE_LCMS
1667 cmsHTRANSFORM transf = 0;
1668 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1669 bool fromDisplay = prefs->getBool( "/options/displayprofile/from_display");
1670 if ( fromDisplay ) {
1671 transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" );
1672 } else {
1673 transf = Inkscape::colorprofile_get_display_transform();
1674 }
1675 #endif // ENABLE_LCMS
1677 if (buf.is_empty) {
1678 #if ENABLE_LCMS
1679 if ( transf && canvas->enable_cms_display_adj ) {
1680 cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1681 }
1682 #endif // ENABLE_LCMS
1683 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1684 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1685 canvas->pixmap_gc,
1686 TRUE,
1687 x0 - canvas->x0, y0 - canvas->y0,
1688 x1 - x0, y1 - y0);
1689 } else {
1691 #if ENABLE_LCMS
1692 if ( transf && canvas->enable_cms_display_adj ) {
1693 for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1694 guchar* p = buf.buf + (buf.buf_rowstride * yy);
1695 cmsDoTransform( transf, p, p, (x1 - x0) );
1696 }
1697 }
1698 #endif // ENABLE_LCMS
1700 // Now we only need to output the prepared pixmap to the actual screen, and this define chooses one
1701 // of the two ways to do it. The cairo way is direct and straightforward, but unfortunately
1702 // noticeably slower. I asked Carl Worth but he was unable so far to suggest any specific reason
1703 // for this slowness. So, for now we use the oldish method: squeeze out 32bpp buffer to 24bpp and
1704 // use gdk_draw_rgb_image_dithalign, for unfortunately gdk can only handle 24 bpp, which cairo
1705 // cannot handle at all. Still, this way is currently faster even despite the blit with squeeze.
1707 ///#define CANVAS_OUTPUT_VIA_CAIRO
1709 #ifdef CANVAS_OUTPUT_VIA_CAIRO
1711 buf.cst = cairo_image_surface_create_for_data (
1712 buf.buf,
1713 CAIRO_FORMAT_ARGB32, // unpacked, i.e. 32 bits! one byte is unused
1714 x1 - x0, y1 - y0,
1715 buf.buf_rowstride
1716 );
1717 cairo_t *window_ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1718 cairo_set_source_surface (window_ct, buf.cst, x0 - canvas->x0, y0 - canvas->y0);
1719 cairo_paint (window_ct);
1720 cairo_destroy (window_ct);
1721 cairo_surface_finish (buf.cst);
1722 cairo_surface_destroy (buf.cst);
1724 #else
1726 NRPixBlock b3;
1727 nr_pixblock_setup_fast (&b3, NR_PIXBLOCK_MODE_R8G8B8, x0, y0, x1, y1, TRUE);
1729 NRPixBlock b4;
1730 nr_pixblock_setup_extern (&b4, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1,
1731 buf.buf,
1732 buf.buf_rowstride,
1733 FALSE, FALSE);
1735 // this does the 32->24 squishing, using an assembler routine:
1736 nr_blit_pixblock_pixblock (&b3, &b4);
1738 gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1739 canvas->pixmap_gc,
1740 x0 - canvas->x0, y0 - canvas->y0,
1741 x1 - x0, y1 - y0,
1742 GDK_RGB_DITHER_MAX,
1743 NR_PIXBLOCK_PX(&b3),
1744 sw * 3,
1745 x0 - canvas->x0, y0 - canvas->y0);
1747 nr_pixblock_release (&b3);
1748 nr_pixblock_release (&b4);
1749 #endif
1750 }
1752 cairo_surface_t *cst = cairo_get_target(buf.ct);
1753 cairo_destroy (buf.ct);
1754 cairo_surface_finish (cst);
1755 cairo_surface_destroy (cst);
1757 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1758 nr_pixelstore_256K_free (buf.buf);
1759 } else {
1760 nr_pixelstore_1M_free (buf.buf);
1761 }
1762 }
1764 struct PaintRectSetup {
1765 SPCanvas* canvas;
1766 NRRectL big_rect;
1767 GTimeVal start_time;
1768 int max_pixels;
1769 Geom::Point mouse_loc;
1770 };
1772 /**
1773 * Paint the given rect, recursively subdividing the region until it is the size of a single
1774 * buffer.
1775 *
1776 * @return true if the drawing completes
1777 */
1778 static int
1779 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1780 {
1781 GTimeVal now;
1782 g_get_current_time (&now);
1784 glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1785 + (now.tv_usec - setup->start_time.tv_usec);
1787 // Allow only very fast buffers to be run together;
1788 // as soon as the total redraw time exceeds 1ms, cancel;
1789 // this returns control to the idle loop and allows Inkscape to process user input
1790 // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1791 // it will get back and finish painting what remains to paint.
1792 if (elapsed > 1000) {
1794 // Interrupting redraw isn't always good.
1795 // For example, when you drag one node of a big path, only the buffer containing
1796 // the mouse cursor will be redrawn again and again, and the rest of the path
1797 // will remain stale because Inkscape never has enough idle time to redraw all
1798 // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1799 // If this limit is set, and if we have aborted redraw more times than is allowed,
1800 // interrupting is blocked and we're forced to redraw full screen once
1801 // (after which we can again interrupt forced_redraw_limit times).
1802 if (setup->canvas->forced_redraw_limit < 0 ||
1803 setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1805 if (setup->canvas->forced_redraw_limit != -1) {
1806 setup->canvas->forced_redraw_count++;
1807 }
1809 return false;
1810 }
1811 }
1813 // Find the optimal buffer dimensions
1814 int bw = this_rect.x1 - this_rect.x0;
1815 int bh = this_rect.y1 - this_rect.y0;
1816 if ((bw < 1) || (bh < 1))
1817 return 0;
1819 if (bw * bh < setup->max_pixels) {
1820 // We are small enough
1821 sp_canvas_paint_single_buffer (setup->canvas,
1822 this_rect.x0, this_rect.y0,
1823 this_rect.x1, this_rect.y1,
1824 setup->big_rect.x0, setup->big_rect.y0,
1825 setup->big_rect.x1, setup->big_rect.y1, bw);
1826 return 1;
1827 }
1829 NRRectL lo = this_rect;
1830 NRRectL hi = this_rect;
1832 /*
1833 This test determines the redraw strategy:
1835 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1836 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1837 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1838 and seems to be faster for drawings with many smaller objects at zoom-out.
1840 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1841 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1842 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1843 faster.
1845 The default for now is the strips mode.
1846 */
1847 if (bw < bh || bh < 2 * TILE_SIZE) {
1848 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1849 int mid = ((long long) this_rect.x0 + (long long) this_rect.x1) / 2;
1850 // Make sure that mid lies on a tile boundary
1851 mid = (mid / TILE_SIZE) * TILE_SIZE;
1853 lo.x1 = mid;
1854 hi.x0 = mid;
1856 if (setup->mouse_loc[Geom::X] < mid) {
1857 // Always paint towards the mouse first
1858 return sp_canvas_paint_rect_internal(setup, lo)
1859 && sp_canvas_paint_rect_internal(setup, hi);
1860 } else {
1861 return sp_canvas_paint_rect_internal(setup, hi)
1862 && sp_canvas_paint_rect_internal(setup, lo);
1863 }
1864 } else {
1865 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1866 int mid = ((long long) this_rect.y0 + (long long) this_rect.y1) / 2;
1867 // Make sure that mid lies on a tile boundary
1868 mid = (mid / TILE_SIZE) * TILE_SIZE;
1870 lo.y1 = mid;
1871 hi.y0 = mid;
1873 if (setup->mouse_loc[Geom::Y] < mid) {
1874 // Always paint towards the mouse first
1875 return sp_canvas_paint_rect_internal(setup, lo)
1876 && sp_canvas_paint_rect_internal(setup, hi);
1877 } else {
1878 return sp_canvas_paint_rect_internal(setup, hi)
1879 && sp_canvas_paint_rect_internal(setup, lo);
1880 }
1881 }
1882 }
1885 /**
1886 * Helper that draws a specific rectangular part of the canvas.
1887 *
1888 * @return true if the rectangle painting succeeds.
1889 */
1890 static bool
1891 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1892 {
1893 g_return_val_if_fail (!canvas->need_update, false);
1895 NRRectL rect;
1896 rect.x0 = xx0;
1897 rect.x1 = xx1;
1898 rect.y0 = yy0;
1899 rect.y1 = yy1;
1901 // Clip rect-to-draw by the current visible area
1902 rect.x0 = MAX (rect.x0, canvas->x0);
1903 rect.y0 = MAX (rect.y0, canvas->y0);
1904 rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1905 rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1907 #ifdef DEBUG_REDRAW
1908 // paint the area to redraw yellow
1909 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1910 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1911 canvas->pixmap_gc,
1912 TRUE,
1913 rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1914 rect.x1 - rect.x0, rect.y1 - rect.y0);
1915 #endif
1917 PaintRectSetup setup;
1919 setup.canvas = canvas;
1920 setup.big_rect = rect;
1922 // Save the mouse location
1923 gint x, y;
1924 gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1925 setup.mouse_loc = sp_canvas_window_to_world (canvas, Geom::Point(x,y));
1927 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1928 // use 256K as a compromise to not slow down gradients
1929 // 256K is the cached buffer and we need 4 channels
1930 setup.max_pixels = 65536; // 256K/4
1931 } else {
1932 // paths only, so 1M works faster
1933 // 1M is the cached buffer and we need 4 channels
1934 setup.max_pixels = 262144;
1935 }
1937 // Start the clock
1938 g_get_current_time(&(setup.start_time));
1940 // Go
1941 return sp_canvas_paint_rect_internal(&setup, rect);
1942 }
1944 /**
1945 * Force a full redraw after a specified number of interrupted redraws
1946 */
1947 void
1948 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1949 g_return_if_fail(canvas != NULL);
1951 canvas->forced_redraw_limit = count;
1952 canvas->forced_redraw_count = 0;
1953 }
1955 /**
1956 * End forced full redraw requests
1957 */
1958 void
1959 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1960 g_return_if_fail(canvas != NULL);
1962 canvas->forced_redraw_limit = -1;
1963 }
1965 /**
1966 * The canvas widget's expose callback.
1967 */
1968 static gint
1969 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1970 {
1971 SPCanvas *canvas = SP_CANVAS (widget);
1973 if (!GTK_WIDGET_DRAWABLE (widget) ||
1974 (event->window != SP_CANVAS_WINDOW (canvas)))
1975 return FALSE;
1977 int n_rects;
1978 GdkRectangle *rects;
1979 gdk_region_get_rectangles (event->region, &rects, &n_rects);
1981 for (int i = 0; i < n_rects; i++) {
1982 NRRectL rect;
1984 rect.x0 = rects[i].x + canvas->x0;
1985 rect.y0 = rects[i].y + canvas->y0;
1986 rect.x1 = rect.x0 + rects[i].width;
1987 rect.y1 = rect.y0 + rects[i].height;
1989 sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1990 }
1992 if (n_rects > 0)
1993 g_free (rects);
1995 return FALSE;
1996 }
1998 /**
1999 * The canvas widget's keypress callback.
2000 */
2001 static gint
2002 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
2003 {
2004 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
2005 }
2007 /**
2008 * Crossing event handler for the canvas.
2009 */
2010 static gint
2011 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
2012 {
2013 SPCanvas *canvas = SP_CANVAS (widget);
2015 if (event->window != SP_CANVAS_WINDOW (canvas))
2016 return FALSE;
2018 canvas->state = event->state;
2019 return pick_current_item (canvas, (GdkEvent *) event);
2020 }
2022 /**
2023 * Focus in handler for the canvas.
2024 */
2025 static gint
2026 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
2027 {
2028 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
2030 SPCanvas *canvas = SP_CANVAS (widget);
2032 if (canvas->focused_item) {
2033 return emit_event (canvas, (GdkEvent *) event);
2034 } else {
2035 return FALSE;
2036 }
2037 }
2039 /**
2040 * Focus out handler for the canvas.
2041 */
2042 static gint
2043 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
2044 {
2045 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2047 SPCanvas *canvas = SP_CANVAS (widget);
2049 if (canvas->focused_item)
2050 return emit_event (canvas, (GdkEvent *) event);
2051 else
2052 return FALSE;
2053 }
2055 /**
2056 * Helper that repaints the areas in the canvas that need it.
2057 *
2058 * @return true if all the dirty parts have been redrawn
2059 */
2060 static int
2061 paint (SPCanvas *canvas)
2062 {
2063 if (canvas->need_update) {
2064 sp_canvas_item_invoke_update (canvas->root, Geom::identity(), 0);
2065 canvas->need_update = FALSE;
2066 }
2068 if (!canvas->need_redraw)
2069 return TRUE;
2071 Gdk::Region to_paint;
2073 for (int j=canvas->tTop; j<canvas->tBottom; j++) {
2074 for (int i=canvas->tLeft; i<canvas->tRight; i++) {
2075 int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
2077 if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
2078 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
2079 TILE_SIZE, TILE_SIZE));
2080 }
2082 }
2083 }
2085 if (!to_paint.empty()) {
2086 Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
2087 typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
2088 for (Iter i=rect.begin(); i != rect.end(); ++i) {
2089 int x0 = (*i).get_x();
2090 int y0 = (*i).get_y();
2091 int x1 = x0 + (*i).get_width();
2092 int y1 = y0 + (*i).get_height();
2093 if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
2094 // Aborted
2095 return FALSE;
2096 };
2097 }
2098 }
2100 canvas->need_redraw = FALSE;
2102 // we've had a full unaborted redraw, reset the full redraw counter
2103 if (canvas->forced_redraw_limit != -1) {
2104 canvas->forced_redraw_count = 0;
2105 }
2107 return TRUE;
2108 }
2110 /**
2111 * Helper that invokes update, paint, and repick on canvas.
2112 */
2113 static int
2114 do_update (SPCanvas *canvas)
2115 {
2116 if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop during interrupted display!
2117 return TRUE;
2119 if (canvas->drawing_disabled)
2120 return TRUE;
2122 /* Cause the update if necessary */
2123 if (canvas->need_update) {
2124 sp_canvas_item_invoke_update (canvas->root, Geom::identity(), 0);
2125 canvas->need_update = FALSE;
2126 }
2128 /* Paint if able to */
2129 if (GTK_WIDGET_DRAWABLE (canvas)) {
2130 return paint (canvas);
2131 }
2133 /* Pick new current item */
2134 while (canvas->need_repick) {
2135 canvas->need_repick = FALSE;
2136 pick_current_item (canvas, &canvas->pick_event);
2137 }
2139 return TRUE;
2140 }
2142 /**
2143 * Idle handler for the canvas that deals with pending updates and redraws.
2144 */
2145 static gint
2146 idle_handler (gpointer data)
2147 {
2148 GDK_THREADS_ENTER ();
2150 SPCanvas *canvas = SP_CANVAS (data);
2152 int const ret = do_update (canvas);
2154 if (ret) {
2155 /* Reset idle id */
2156 canvas->idle_id = 0;
2157 }
2159 GDK_THREADS_LEAVE ();
2161 return !ret;
2162 }
2164 /**
2165 * Convenience function to add an idle handler to a canvas.
2166 */
2167 static void
2168 add_idle (SPCanvas *canvas)
2169 {
2170 if (canvas->idle_id != 0)
2171 return;
2173 canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2174 }
2176 /**
2177 * Returns the root group of the specified canvas.
2178 */
2179 SPCanvasGroup *
2180 sp_canvas_root (SPCanvas *canvas)
2181 {
2182 g_return_val_if_fail (canvas != NULL, NULL);
2183 g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2185 return SP_CANVAS_GROUP (canvas->root);
2186 }
2188 /**
2189 * Scrolls canvas to specific position (cx and cy are measured in screen pixels)
2190 */
2191 void
2192 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2193 {
2194 g_return_if_fail (canvas != NULL);
2195 g_return_if_fail (SP_IS_CANVAS (canvas));
2197 int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels)
2198 int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do!
2199 int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the
2200 int dy = iy - canvas->y0; // canvas w.r.t its previous position
2202 canvas->dx0 = cx; // here the 'd' stands for double, not delta!
2203 canvas->dy0 = cy;
2204 canvas->x0 = ix;
2205 canvas->y0 = iy;
2207 sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2209 if (!clear) {
2210 // scrolling without zoom; redraw only the newly exposed areas
2211 if ((dx != 0) || (dy != 0)) {
2212 canvas->is_scrolling = is_scrolling;
2213 if (GTK_WIDGET_REALIZED (canvas)) {
2214 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2215 }
2216 }
2217 } else {
2218 // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2219 }
2221 }
2223 /**
2224 * Updates canvas if necessary.
2225 */
2226 void
2227 sp_canvas_update_now (SPCanvas *canvas)
2228 {
2229 g_return_if_fail (canvas != NULL);
2230 g_return_if_fail (SP_IS_CANVAS (canvas));
2232 if (!(canvas->need_update ||
2233 canvas->need_redraw))
2234 return;
2236 do_update (canvas);
2237 }
2239 /**
2240 * Update callback for canvas widget.
2241 */
2242 static void
2243 sp_canvas_request_update (SPCanvas *canvas)
2244 {
2245 canvas->need_update = TRUE;
2246 add_idle (canvas);
2247 }
2249 /**
2250 * Forces redraw of rectangular canvas area.
2251 */
2252 void
2253 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2254 {
2255 NRRectL bbox;
2256 NRRectL visible;
2257 NRRectL clip;
2259 g_return_if_fail (canvas != NULL);
2260 g_return_if_fail (SP_IS_CANVAS (canvas));
2262 if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2263 if ((x0 >= x1) || (y0 >= y1)) return;
2265 bbox.x0 = x0;
2266 bbox.y0 = y0;
2267 bbox.x1 = x1;
2268 bbox.y1 = y1;
2270 visible.x0 = canvas->x0;
2271 visible.y0 = canvas->y0;
2272 visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2273 visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2275 nr_rect_l_intersect (&clip, &bbox, &visible);
2277 sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2278 add_idle (canvas);
2279 }
2281 /**
2282 * Sets world coordinates from win and canvas.
2283 */
2284 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2285 {
2286 g_return_if_fail (canvas != NULL);
2287 g_return_if_fail (SP_IS_CANVAS (canvas));
2289 if (worldx) *worldx = canvas->x0 + winx;
2290 if (worldy) *worldy = canvas->y0 + winy;
2291 }
2293 /**
2294 * Sets win coordinates from world and canvas.
2295 */
2296 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2297 {
2298 g_return_if_fail (canvas != NULL);
2299 g_return_if_fail (SP_IS_CANVAS (canvas));
2301 if (winx) *winx = worldx - canvas->x0;
2302 if (winy) *winy = worldy - canvas->y0;
2303 }
2305 /**
2306 * Converts point from win to world coordinates.
2307 */
2308 Geom::Point sp_canvas_window_to_world(SPCanvas const *canvas, Geom::Point const win)
2309 {
2310 g_assert (canvas != NULL);
2311 g_assert (SP_IS_CANVAS (canvas));
2313 return Geom::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2314 }
2316 /**
2317 * Converts point from world to win coordinates.
2318 */
2319 Geom::Point sp_canvas_world_to_window(SPCanvas const *canvas, Geom::Point const world)
2320 {
2321 g_assert (canvas != NULL);
2322 g_assert (SP_IS_CANVAS (canvas));
2324 return Geom::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2325 }
2327 /**
2328 * Returns true if point given in world coordinates is inside window.
2329 */
2330 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, Geom::Point const &world)
2331 {
2332 g_assert( canvas != NULL );
2333 g_assert(SP_IS_CANVAS(canvas));
2335 GtkWidget const &w = *GTK_WIDGET(canvas);
2336 return ( ( canvas->x0 <= world[Geom::X] ) &&
2337 ( canvas->y0 <= world[Geom::Y] ) &&
2338 ( world[Geom::X] < canvas->x0 + w.allocation.width ) &&
2339 ( world[Geom::Y] < canvas->y0 + w.allocation.height ) );
2340 }
2342 /**
2343 * Return canvas window coordinates as Geom::Rect.
2344 */
2345 Geom::Rect SPCanvas::getViewbox() const
2346 {
2347 GtkWidget const *w = GTK_WIDGET(this);
2348 return Geom::Rect(Geom::Point(dx0, dy0),
2349 Geom::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2350 }
2352 /**
2353 * Return canvas window coordinates as IRect (a rectangle defined by integers).
2354 */
2355 NR::IRect SPCanvas::getViewboxIntegers() const
2356 {
2357 GtkWidget const *w = GTK_WIDGET(this);
2358 return NR::IRect(NR::IPoint(x0, y0),
2359 NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height));
2360 }
2362 inline int sp_canvas_tile_floor(int x)
2363 {
2364 return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2365 }
2367 inline int sp_canvas_tile_ceil(int x)
2368 {
2369 return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2370 }
2372 /**
2373 * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2374 */
2375 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2376 {
2377 if ( nl >= nr || nt >= nb ) {
2378 if ( canvas->tiles ) g_free(canvas->tiles);
2379 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2380 canvas->tileH=canvas->tileV=0;
2381 canvas->tiles=NULL;
2382 return;
2383 }
2384 int tl=sp_canvas_tile_floor(nl);
2385 int tt=sp_canvas_tile_floor(nt);
2386 int tr=sp_canvas_tile_ceil(nr);
2387 int tb=sp_canvas_tile_ceil(nb);
2389 int nh = tr-tl, nv = tb-tt;
2390 uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2391 for (int i=tl; i<tr; i++) {
2392 for (int j=tt; j<tb; j++) {
2393 int ind = (i-tl) + (j-tt)*nh;
2394 if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2395 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2396 } else {
2397 ntiles[ind]=0; // newly exposed areas get 0
2398 }
2399 }
2400 }
2401 if ( canvas->tiles ) g_free(canvas->tiles);
2402 canvas->tiles=ntiles;
2403 canvas->tLeft=tl;
2404 canvas->tTop=tt;
2405 canvas->tRight=tr;
2406 canvas->tBottom=tb;
2407 canvas->tileH=nh;
2408 canvas->tileV=nv;
2409 }
2411 /*
2412 * Helper that queues a canvas rectangle for redraw
2413 */
2414 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2415 canvas->need_redraw = TRUE;
2417 sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2418 }
2420 /**
2421 * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2422 */
2423 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2424 {
2425 if ( nl >= nr || nt >= nb ) {
2426 return;
2427 }
2428 int tl=sp_canvas_tile_floor(nl);
2429 int tt=sp_canvas_tile_floor(nt);
2430 int tr=sp_canvas_tile_ceil(nr);
2431 int tb=sp_canvas_tile_ceil(nb);
2432 if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2433 if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2434 if ( tr > canvas->tRight ) tr=canvas->tRight;
2435 if ( tt < canvas->tTop ) tt=canvas->tTop;
2436 if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2438 for (int i=tl; i<tr; i++) {
2439 for (int j=tt; j<tb; j++) {
2440 canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2441 }
2442 }
2443 }
2446 /*
2447 Local Variables:
2448 mode:c++
2449 c-file-style:"stroustrup"
2450 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2451 indent-tabs-mode:nil
2452 fill-column:99
2453 End:
2454 */
2455 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :