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 <libnr/nr-matrix-fns.h>
36 #include <libnr/nr-matrix-ops.h>
37 #include <libnr/nr-convex-hull.h>
38 #include "prefs-utils.h"
39 #include "inkscape.h"
40 #include "sodipodi-ctrlrect.h"
41 #if ENABLE_LCMS
42 #include "color-profile-fns.h"
43 #endif // ENABLE_LCMS
44 #include "display/rendermode.h"
45 #include "libnr/nr-blit.h"
46 #include "display/inkscape-cairo.h"
47 #include "debug/gdk-event-latency-tracker.h"
49 using Inkscape::Debug::GdkEventLatencyTracker;
51 // GTK_CHECK_VERSION returns false on failure
52 #define HAS_GDK_EVENT_REQUEST_MOTIONS GTK_CHECK_VERSION(2, 12, 0)
54 // gtk_check_version returns non-NULL on failure
55 static bool const HAS_BROKEN_MOTION_HINTS =
56 true || gtk_check_version(2, 12, 0) != NULL || !HAS_GDK_EVENT_REQUEST_MOTIONS;
58 // Define this to visualize the regions to be redrawn
59 //#define DEBUG_REDRAW 1;
61 // Tiles are a way to minimize the number of redraws, eliminating too small redraws.
62 // The canvas stores a 2D array of ints, each representing a TILE_SIZExTILE_SIZE pixels tile.
63 // If any part of it is dirtied, the entire tile is dirtied (its int is nonzero) and repainted.
64 #define TILE_SIZE 16
66 static gint const sp_canvas_update_priority = G_PRIORITY_HIGH_IDLE;
68 #define SP_CANVAS_WINDOW(c) (((GtkWidget *) (c))->window)
70 enum {
71 SP_CANVAS_ITEM_VISIBLE = 1 << 7,
72 SP_CANVAS_ITEM_NEED_UPDATE = 1 << 8,
73 SP_CANVAS_ITEM_NEED_AFFINE = 1 << 9
74 };
76 /**
77 * A group of Items.
78 */
79 struct SPCanvasGroup {
80 SPCanvasItem item;
82 GList *items, *last;
83 };
85 /**
86 * The SPCanvasGroup vtable.
87 */
88 struct SPCanvasGroupClass {
89 SPCanvasItemClass parent_class;
90 };
92 /**
93 * The SPCanvas vtable.
94 */
95 struct SPCanvasClass {
96 GtkWidgetClass parent_class;
97 };
99 static void group_add (SPCanvasGroup *group, SPCanvasItem *item);
100 static void group_remove (SPCanvasGroup *group, SPCanvasItem *item);
102 /* SPCanvasItem */
104 enum {ITEM_EVENT, ITEM_LAST_SIGNAL};
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);
117 static guint item_signals[ITEM_LAST_SIGNAL] = { 0 };
119 static GtkObjectClass *item_parent_class;
121 /**
122 * Registers the SPCanvasItem class with Glib and returns its type number.
123 */
124 GType
125 sp_canvas_item_get_type (void)
126 {
127 static GType type = 0;
128 if (!type) {
129 static GTypeInfo const info = {
130 sizeof (SPCanvasItemClass),
131 NULL, NULL,
132 (GClassInitFunc) sp_canvas_item_class_init,
133 NULL, NULL,
134 sizeof (SPCanvasItem),
135 0,
136 (GInstanceInitFunc) sp_canvas_item_init,
137 NULL
138 };
139 type = g_type_register_static (GTK_TYPE_OBJECT, "SPCanvasItem", &info, (GTypeFlags)0);
140 }
142 return type;
143 }
145 /**
146 * Initializes the SPCanvasItem vtable and the "event" signal.
147 */
148 static void
149 sp_canvas_item_class_init (SPCanvasItemClass *klass)
150 {
151 GObjectClass *object_class = (GObjectClass *) klass;
153 /* fixme: Derive from GObject */
154 item_parent_class = (GtkObjectClass*)gtk_type_class (GTK_TYPE_OBJECT);
156 item_signals[ITEM_EVENT] = g_signal_new ("event",
157 G_TYPE_FROM_CLASS (klass),
158 G_SIGNAL_RUN_LAST,
159 ((glong)((guint8*)&(klass->event) - (guint8*)klass)),
160 NULL, NULL,
161 sp_marshal_BOOLEAN__POINTER,
162 G_TYPE_BOOLEAN, 1,
163 GDK_TYPE_EVENT);
165 object_class->dispose = sp_canvas_item_dispose;
166 }
168 /**
169 * Callback for initialization of SPCanvasItem.
170 */
171 static void
172 sp_canvas_item_init (SPCanvasItem *item)
173 {
174 item->flags |= SP_CANVAS_ITEM_VISIBLE;
175 item->xform = Geom::Matrix(Geom::identity());
176 }
178 /**
179 * Constructs new SPCanvasItem on SPCanvasGroup.
180 */
181 SPCanvasItem *
182 sp_canvas_item_new (SPCanvasGroup *parent, GtkType type, gchar const *first_arg_name, ...)
183 {
184 va_list args;
186 g_return_val_if_fail (parent != NULL, NULL);
187 g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL);
188 g_return_val_if_fail (gtk_type_is_a (type, sp_canvas_item_get_type ()), NULL);
190 SPCanvasItem *item = SP_CANVAS_ITEM (gtk_type_new (type));
192 va_start (args, first_arg_name);
193 sp_canvas_item_construct (item, parent, first_arg_name, args);
194 va_end (args);
196 return item;
197 }
199 /**
200 * Sets up the newly created SPCanvasItem.
201 *
202 * We make it static for encapsulation reasons since it was nowhere used.
203 */
204 static void
205 sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args)
206 {
207 g_return_if_fail (SP_IS_CANVAS_GROUP (parent));
208 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
210 item->parent = SP_CANVAS_ITEM (parent);
211 item->canvas = item->parent->canvas;
213 g_object_set_valist (G_OBJECT (item), first_arg_name, args);
215 group_add (SP_CANVAS_GROUP (item->parent), item);
217 sp_canvas_item_request_update (item);
218 }
220 /**
221 * Helper function that requests redraw only if item's visible flag is set.
222 */
223 static void
224 redraw_if_visible (SPCanvasItem *item)
225 {
226 if (item->flags & SP_CANVAS_ITEM_VISIBLE) {
227 int x0 = (int)(item->x1);
228 int x1 = (int)(item->x2);
229 int y0 = (int)(item->y1);
230 int y1 = (int)(item->y2);
232 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
233 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
234 }
235 }
236 }
238 /**
239 * Callback that removes item from all referers and destroys it.
240 */
241 static void
242 sp_canvas_item_dispose (GObject *object)
243 {
244 SPCanvasItem *item = SP_CANVAS_ITEM (object);
246 // Hack: if this is a ctrlrect, move it to 0,0;
247 // this redraws only the stroke of the rect to be deleted,
248 // avoiding redraw of the entire area
249 if (SP_IS_CTRLRECT(item)) {
250 SP_CTRLRECT(object)->setRectangle(Geom::Rect(Geom::Point(0,0),Geom::Point(0,0)));
251 SP_CTRLRECT(object)->update(item->xform, 0);
252 } else {
253 redraw_if_visible (item);
254 }
255 item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
257 if (item == item->canvas->current_item) {
258 item->canvas->current_item = NULL;
259 item->canvas->need_repick = TRUE;
260 }
262 if (item == item->canvas->new_current_item) {
263 item->canvas->new_current_item = NULL;
264 item->canvas->need_repick = TRUE;
265 }
267 if (item == item->canvas->grabbed_item) {
268 item->canvas->grabbed_item = NULL;
269 gdk_pointer_ungrab (GDK_CURRENT_TIME);
270 }
272 if (item == item->canvas->focused_item)
273 item->canvas->focused_item = NULL;
275 if (item->parent) {
276 group_remove (SP_CANVAS_GROUP (item->parent), item);
277 }
279 G_OBJECT_CLASS (item_parent_class)->dispose (object);
280 }
282 /**
283 * Helper function to update item and its children.
284 *
285 * NB! affine is parent2canvas.
286 */
287 static void
288 sp_canvas_item_invoke_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags)
289 {
290 /* Apply the child item's transform */
291 Geom::Matrix child_affine = item->xform * affine;
293 /* apply object flags to child flags */
294 int child_flags = flags & ~SP_CANVAS_UPDATE_REQUESTED;
296 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
297 child_flags |= SP_CANVAS_UPDATE_REQUESTED;
299 if (item->flags & SP_CANVAS_ITEM_NEED_AFFINE)
300 child_flags |= SP_CANVAS_UPDATE_AFFINE;
302 if (child_flags & (SP_CANVAS_UPDATE_REQUESTED | SP_CANVAS_UPDATE_AFFINE)) {
303 if (SP_CANVAS_ITEM_GET_CLASS (item)->update)
304 SP_CANVAS_ITEM_GET_CLASS (item)->update (item, child_affine, child_flags);
305 }
307 GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_UPDATE);
308 GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_AFFINE);
309 }
311 /**
312 * Helper function to invoke the point method of the item.
313 *
314 * The argument x, y should be in the parent's item-relative coordinate
315 * system. This routine applies the inverse of the item's transform,
316 * maintaining the affine invariant.
317 */
318 static double
319 sp_canvas_item_invoke_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item)
320 {
321 if (SP_CANVAS_ITEM_GET_CLASS (item)->point)
322 return SP_CANVAS_ITEM_GET_CLASS (item)->point (item, p, actual_item);
324 return NR_HUGE;
325 }
327 /**
328 * Makes the item's affine transformation matrix be equal to the specified
329 * matrix.
330 *
331 * @item: A canvas item.
332 * @affine: An affine transformation matrix.
333 */
334 void
335 sp_canvas_item_affine_absolute (SPCanvasItem *item, Geom::Matrix const &affine)
336 {
337 item->xform = affine;
339 if (!(item->flags & SP_CANVAS_ITEM_NEED_AFFINE)) {
340 item->flags |= SP_CANVAS_ITEM_NEED_AFFINE;
341 if (item->parent != NULL) {
342 sp_canvas_item_request_update (item->parent);
343 } else {
344 sp_canvas_request_update (item->canvas);
345 }
346 }
348 item->canvas->need_repick = TRUE;
349 }
351 /**
352 * Convenience function to reorder items in a group's child list.
353 *
354 * This puts the specified link after the "before" link.
355 */
356 static void
357 put_item_after (GList *link, GList *before)
358 {
359 if (link == before)
360 return;
362 SPCanvasGroup *parent = SP_CANVAS_GROUP (SP_CANVAS_ITEM (link->data)->parent);
364 if (before == NULL) {
365 if (link == parent->items) return;
367 link->prev->next = link->next;
369 if (link->next) {
370 link->next->prev = link->prev;
371 } else {
372 parent->last = link->prev;
373 }
375 link->prev = before;
376 link->next = parent->items;
377 link->next->prev = link;
378 parent->items = link;
379 } else {
380 if ((link == parent->last) && (before == parent->last->prev))
381 return;
383 if (link->next)
384 link->next->prev = link->prev;
386 if (link->prev)
387 link->prev->next = link->next;
388 else {
389 parent->items = link->next;
390 parent->items->prev = NULL;
391 }
393 link->prev = before;
394 link->next = before->next;
396 link->prev->next = link;
398 if (link->next)
399 link->next->prev = link;
400 else
401 parent->last = link;
402 }
403 }
406 /**
407 * Raises the item in its parent's stack by the specified number of positions.
408 *
409 * \param item A canvas item.
410 * \param positions Number of steps to raise the item.
411 *
412 * If the number of positions is greater than the distance to the top of the
413 * stack, then the item is put at the top.
414 */
415 void
416 sp_canvas_item_raise (SPCanvasItem *item, int positions)
417 {
418 g_return_if_fail (item != NULL);
419 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
420 g_return_if_fail (positions >= 0);
422 if (!item->parent || positions == 0)
423 return;
425 SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
426 GList *link = g_list_find (parent->items, item);
427 g_assert (link != NULL);
429 GList *before;
430 for (before = link; positions && before; positions--)
431 before = before->next;
433 if (!before)
434 before = parent->last;
436 put_item_after (link, before);
438 redraw_if_visible (item);
439 item->canvas->need_repick = TRUE;
440 }
443 /**
444 * Lowers the item in its parent's stack by the specified number of positions.
445 *
446 * \param item A canvas item.
447 * \param positions Number of steps to lower the item.
448 *
449 * If the number of positions is greater than the distance to the bottom of the
450 * stack, then the item is put at the bottom.
451 **/
452 void
453 sp_canvas_item_lower (SPCanvasItem *item, int positions)
454 {
455 g_return_if_fail (item != NULL);
456 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
457 g_return_if_fail (positions >= 1);
459 if (!item->parent || positions == 0)
460 return;
462 SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
463 GList *link = g_list_find (parent->items, item);
464 g_assert (link != NULL);
466 GList *before;
467 if (link->prev)
468 for (before = link->prev; positions && before; positions--)
469 before = before->prev;
470 else
471 before = NULL;
473 put_item_after (link, before);
475 redraw_if_visible (item);
476 item->canvas->need_repick = TRUE;
477 }
479 /**
480 * Sets visible flag on item and requests a redraw.
481 */
482 void
483 sp_canvas_item_show (SPCanvasItem *item)
484 {
485 g_return_if_fail (item != NULL);
486 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
488 if (item->flags & SP_CANVAS_ITEM_VISIBLE)
489 return;
491 item->flags |= SP_CANVAS_ITEM_VISIBLE;
493 int x0 = (int)(item->x1);
494 int x1 = (int)(item->x2);
495 int y0 = (int)(item->y1);
496 int y1 = (int)(item->y2);
498 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
499 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
500 item->canvas->need_repick = TRUE;
501 }
502 }
504 /**
505 * Clears visible flag on item and requests a redraw.
506 */
507 void
508 sp_canvas_item_hide (SPCanvasItem *item)
509 {
510 g_return_if_fail (item != NULL);
511 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
513 if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
514 return;
516 item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
518 int x0 = (int)(item->x1);
519 int x1 = (int)(item->x2);
520 int y0 = (int)(item->y1);
521 int y1 = (int)(item->y2);
523 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
524 sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
525 item->canvas->need_repick = TRUE;
526 }
527 }
529 /**
530 * Grab item under cursor.
531 *
532 * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE
533 */
534 int
535 sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
536 {
537 g_return_val_if_fail (item != NULL, -1);
538 g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1);
539 g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1);
541 if (item->canvas->grabbed_item)
542 return -1;
544 if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
545 return -1;
547 if (HAS_BROKEN_MOTION_HINTS) {
548 event_mask &= ~GDK_POINTER_MOTION_HINT_MASK;
549 }
551 /* fixme: Top hack (Lauris) */
552 /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
553 /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
554 gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
555 (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
556 NULL, cursor, etime);
558 item->canvas->grabbed_item = item;
559 item->canvas->grabbed_event_mask = event_mask;
560 item->canvas->current_item = item; /* So that events go to the grabbed item */
562 return 0;
563 }
565 /**
566 * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
567 * mouse.
568 *
569 * \param item A canvas item that holds a grab.
570 * \param etime The timestamp for ungrabbing the mouse.
571 */
572 void
573 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
574 {
575 g_return_if_fail (item != NULL);
576 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
578 if (item->canvas->grabbed_item != item)
579 return;
581 item->canvas->grabbed_item = NULL;
583 gdk_pointer_ungrab (etime);
584 }
586 /**
587 * Returns the product of all transformation matrices from the root item down
588 * to the item.
589 */
590 Geom::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
591 {
592 g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
594 Geom::Matrix affine = Geom::identity();
596 while (item) {
597 affine *= item->xform;
598 item = item->parent;
599 }
600 return affine;
601 }
603 /**
604 * Helper that returns true iff item is descendant of parent.
605 */
606 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
607 {
608 while (item) {
609 if (item == parent)
610 return true;
611 item = item->parent;
612 }
614 return false;
615 }
617 /**
618 * Focus canvas, and item under cursor if it is not already focussed.
619 */
620 void
621 sp_canvas_item_grab_focus (SPCanvasItem *item)
622 {
623 g_return_if_fail (item != NULL);
624 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
625 g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
627 SPCanvasItem *focused_item = item->canvas->focused_item;
629 if (focused_item) {
630 GdkEvent ev;
631 ev.focus_change.type = GDK_FOCUS_CHANGE;
632 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
633 ev.focus_change.send_event = FALSE;
634 ev.focus_change.in = FALSE;
636 emit_event (item->canvas, &ev);
637 }
639 item->canvas->focused_item = item;
640 gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
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 = TRUE;
649 emit_event (item->canvas, &ev);
650 }
651 }
653 /**
654 * Requests that the canvas queue an update for the specified item.
655 *
656 * To be used only by item implementations.
657 */
658 void
659 sp_canvas_item_request_update (SPCanvasItem *item)
660 {
661 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
662 return;
664 item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
666 if (item->parent != NULL) {
667 /* Recurse up the tree */
668 sp_canvas_item_request_update (item->parent);
669 } else {
670 /* Have reached the top of the tree, make sure the update call gets scheduled. */
671 sp_canvas_request_update (item->canvas);
672 }
673 }
675 /**
676 * Returns position of item in group.
677 */
678 gint sp_canvas_item_order (SPCanvasItem * item)
679 {
680 return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
681 }
683 /* SPCanvasGroup */
685 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
686 static void sp_canvas_group_init (SPCanvasGroup *group);
687 static void sp_canvas_group_destroy (GtkObject *object);
689 static void sp_canvas_group_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags);
690 static double sp_canvas_group_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item);
691 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
693 static SPCanvasItemClass *group_parent_class;
695 /**
696 * Registers SPCanvasGroup class with Gtk and returns its type number.
697 */
698 GType sp_canvas_group_get_type(void)
699 {
700 static GType type = 0;
701 if (!type) {
702 GTypeInfo info = {
703 sizeof(SPCanvasGroupClass),
704 0, // base_init
705 0, // base_finalize
706 (GClassInitFunc)sp_canvas_group_class_init,
707 0, // class_finalize
708 0, // class_data
709 sizeof(SPCanvasGroup),
710 0, // n_preallocs
711 (GInstanceInitFunc)sp_canvas_group_init,
712 0 // value_table
713 };
714 type = g_type_register_static(sp_canvas_item_get_type(), "SPCanvasGroup", &info, static_cast<GTypeFlags>(0));
715 }
716 return type;
717 }
719 /**
720 * Class initialization function for SPCanvasGroupClass
721 */
722 static void
723 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
724 {
725 GtkObjectClass *object_class = (GtkObjectClass *) klass;
726 SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
728 group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
730 object_class->destroy = sp_canvas_group_destroy;
732 item_class->update = sp_canvas_group_update;
733 item_class->render = sp_canvas_group_render;
734 item_class->point = sp_canvas_group_point;
735 }
737 /**
738 * Callback. Empty.
739 */
740 static void
741 sp_canvas_group_init (SPCanvasGroup */*group*/)
742 {
743 /* Nothing here */
744 }
746 /**
747 * Callback that destroys all items in group and calls group's virtual
748 * destroy() function.
749 */
750 static void
751 sp_canvas_group_destroy (GtkObject *object)
752 {
753 g_return_if_fail (object != NULL);
754 g_return_if_fail (SP_IS_CANVAS_GROUP (object));
756 SPCanvasGroup const *group = SP_CANVAS_GROUP (object);
758 GList *list = group->items;
759 while (list) {
760 SPCanvasItem *child = (SPCanvasItem *)list->data;
761 list = list->next;
763 gtk_object_destroy (GTK_OBJECT (child));
764 }
766 if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
767 (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
768 }
770 /**
771 * Update handler for canvas groups
772 */
773 static void
774 sp_canvas_group_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags)
775 {
776 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
777 Geom::RectHull corners(Geom::Point(0, 0));
778 bool empty=true;
780 for (GList *list = group->items; list; list = list->next) {
781 SPCanvasItem *i = (SPCanvasItem *)list->data;
783 sp_canvas_item_invoke_update (i, affine, flags);
785 if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
786 if (empty) {
787 corners = Geom::RectHull(Geom::Point(i->x1, i->y1));
788 empty = false;
789 } else {
790 corners.add(Geom::Point(i->x1, i->y1));
791 }
792 corners.add(Geom::Point(i->x2, i->y2));
793 }
794 }
796 boost::optional<Geom::Rect> const bounds = corners.bounds();
797 if (bounds) {
798 item->x1 = bounds->min()[Geom::X];
799 item->y1 = bounds->min()[Geom::Y];
800 item->x2 = bounds->max()[Geom::X];
801 item->y2 = bounds->max()[Geom::Y];
802 } else {
803 // FIXME ?
804 item->x1 = item->x2 = item->y1 = item->y2 = 0;
805 }
806 }
808 /**
809 * Point handler for canvas groups.
810 */
811 static double
812 sp_canvas_group_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item)
813 {
814 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
815 double const x = p[Geom::X];
816 double const y = p[Geom::Y];
817 int x1 = (int)(x - item->canvas->close_enough);
818 int y1 = (int)(y - item->canvas->close_enough);
819 int x2 = (int)(x + item->canvas->close_enough);
820 int y2 = (int)(y + item->canvas->close_enough);
822 double best = 0.0;
823 *actual_item = NULL;
825 double dist = 0.0;
827 for (GList *list = group->items; list; list = list->next) {
828 SPCanvasItem *child = (SPCanvasItem *)list->data;
830 if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
831 SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
833 int has_point;
834 if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
835 dist = sp_canvas_item_invoke_point (child, p, &point_item);
836 has_point = TRUE;
837 } else
838 has_point = FALSE;
840 if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
841 best = dist;
842 *actual_item = point_item;
843 }
844 }
845 }
847 return best;
848 }
850 /**
851 * Renders all visible canvas group items in buf rectangle.
852 */
853 static void
854 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
855 {
856 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
858 for (GList *list = group->items; list; list = list->next) {
859 SPCanvasItem *child = (SPCanvasItem *)list->data;
860 if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
861 if ((child->x1 < buf->rect.x1) &&
862 (child->y1 < buf->rect.y1) &&
863 (child->x2 > buf->rect.x0) &&
864 (child->y2 > buf->rect.y0)) {
865 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
866 SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
867 }
868 }
869 }
870 }
872 /**
873 * Adds an item to a canvas group.
874 */
875 static void
876 group_add (SPCanvasGroup *group, SPCanvasItem *item)
877 {
878 gtk_object_ref (GTK_OBJECT (item));
879 gtk_object_sink (GTK_OBJECT (item));
881 if (!group->items) {
882 group->items = g_list_append (group->items, item);
883 group->last = group->items;
884 } else {
885 group->last = g_list_append (group->last, item)->next;
886 }
888 sp_canvas_item_request_update (item);
889 }
891 /**
892 * Removes an item from a canvas group
893 */
894 static void
895 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
896 {
897 g_return_if_fail (group != NULL);
898 g_return_if_fail (SP_IS_CANVAS_GROUP (group));
899 g_return_if_fail (item != NULL);
901 for (GList *children = group->items; children; children = children->next) {
902 if (children->data == item) {
904 /* Unparent the child */
906 item->parent = NULL;
907 gtk_object_unref (GTK_OBJECT (item));
909 /* Remove it from the list */
911 if (children == group->last) group->last = children->prev;
913 group->items = g_list_remove_link (group->items, children);
914 g_list_free (children);
915 break;
916 }
917 }
918 }
920 /* SPCanvas */
922 static void sp_canvas_class_init (SPCanvasClass *klass);
923 static void sp_canvas_init (SPCanvas *canvas);
924 static void sp_canvas_destroy (GtkObject *object);
926 static void sp_canvas_realize (GtkWidget *widget);
927 static void sp_canvas_unrealize (GtkWidget *widget);
929 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
930 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
932 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
933 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
934 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
935 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
936 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
937 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
938 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
939 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
941 static GtkWidgetClass *canvas_parent_class;
943 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb);
944 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb);
945 static void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val);
946 static int do_update (SPCanvas *canvas);
948 /**
949 * Registers the SPCanvas class if necessary, and returns the type ID
950 * associated to it.
951 *
952 * \return The type ID of the SPCanvas class.
953 **/
954 GType sp_canvas_get_type(void)
955 {
956 static GType type = 0;
957 if (!type) {
958 GTypeInfo info = {
959 sizeof(SPCanvasClass),
960 0, // base_init
961 0, // base_finalize
962 (GClassInitFunc)sp_canvas_class_init,
963 0, // class_finalize
964 0, // class_data
965 sizeof(SPCanvas),
966 0, // n_preallocs
967 (GInstanceInitFunc)sp_canvas_init,
968 0 // value_table
969 };
970 type = g_type_register_static(GTK_TYPE_WIDGET, "SPCanvas", &info, static_cast<GTypeFlags>(0));
971 }
972 return type;
973 }
975 /**
976 * Class initialization function for SPCanvasClass.
977 */
978 static void
979 sp_canvas_class_init (SPCanvasClass *klass)
980 {
981 GtkObjectClass *object_class = (GtkObjectClass *) klass;
982 GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
984 canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
986 object_class->destroy = sp_canvas_destroy;
988 widget_class->realize = sp_canvas_realize;
989 widget_class->unrealize = sp_canvas_unrealize;
990 widget_class->size_request = sp_canvas_size_request;
991 widget_class->size_allocate = sp_canvas_size_allocate;
992 widget_class->button_press_event = sp_canvas_button;
993 widget_class->button_release_event = sp_canvas_button;
994 widget_class->motion_notify_event = sp_canvas_motion;
995 widget_class->scroll_event = sp_canvas_scroll;
996 widget_class->expose_event = sp_canvas_expose;
997 widget_class->key_press_event = sp_canvas_key;
998 widget_class->key_release_event = sp_canvas_key;
999 widget_class->enter_notify_event = sp_canvas_crossing;
1000 widget_class->leave_notify_event = sp_canvas_crossing;
1001 widget_class->focus_in_event = sp_canvas_focus_in;
1002 widget_class->focus_out_event = sp_canvas_focus_out;
1003 }
1005 /**
1006 * Callback: object initialization for SPCanvas.
1007 */
1008 static void
1009 sp_canvas_init (SPCanvas *canvas)
1010 {
1011 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
1012 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
1013 GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
1015 canvas->pick_event.type = GDK_LEAVE_NOTIFY;
1016 canvas->pick_event.crossing.x = 0;
1017 canvas->pick_event.crossing.y = 0;
1019 /* Create the root item as a special case */
1020 canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
1021 canvas->root->canvas = canvas;
1023 gtk_object_ref (GTK_OBJECT (canvas->root));
1024 gtk_object_sink (GTK_OBJECT (canvas->root));
1026 canvas->need_repick = TRUE;
1028 // See comment at in sp-canvas.h.
1029 canvas->gen_all_enter_events = false;
1031 canvas->tiles=NULL;
1032 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1033 canvas->tileH=canvas->tileV=0;
1035 canvas->forced_redraw_count = 0;
1036 canvas->forced_redraw_limit = -1;
1038 #if ENABLE_LCMS
1039 canvas->enable_cms_display_adj = false;
1040 canvas->cms_key = new Glib::ustring("");
1041 #endif // ENABLE_LCMS
1043 canvas->is_scrolling = false;
1045 }
1047 /**
1048 * Convenience function to remove the idle handler of a canvas.
1049 */
1050 static void
1051 remove_idle (SPCanvas *canvas)
1052 {
1053 if (canvas->idle_id) {
1054 gtk_idle_remove (canvas->idle_id);
1055 canvas->idle_id = 0;
1056 }
1057 }
1059 /*
1060 * Removes the transient state of the canvas (idle handler, grabs).
1061 */
1062 static void
1063 shutdown_transients (SPCanvas *canvas)
1064 {
1065 /* We turn off the need_redraw flag, since if the canvas is mapped again
1066 * it will request a redraw anyways. We do not turn off the need_update
1067 * flag, though, because updates are not queued when the canvas remaps
1068 * itself.
1069 */
1070 if (canvas->need_redraw) {
1071 canvas->need_redraw = FALSE;
1072 }
1073 if ( canvas->tiles ) g_free(canvas->tiles);
1074 canvas->tiles=NULL;
1075 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1076 canvas->tileH=canvas->tileV=0;
1078 if (canvas->grabbed_item) {
1079 canvas->grabbed_item = NULL;
1080 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1081 }
1083 remove_idle (canvas);
1084 }
1086 /**
1087 * Destroy handler for SPCanvas.
1088 */
1089 static void
1090 sp_canvas_destroy (GtkObject *object)
1091 {
1092 SPCanvas *canvas = SP_CANVAS (object);
1094 if (canvas->root) {
1095 gtk_object_unref (GTK_OBJECT (canvas->root));
1096 canvas->root = NULL;
1097 }
1099 shutdown_transients (canvas);
1101 if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1102 (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1103 }
1105 static void track_latency(GdkEvent const *event) {
1106 GdkEventLatencyTracker &tracker = GdkEventLatencyTracker::default_tracker();
1107 boost::optional<double> latency = tracker.process(event);
1108 if (latency && *latency > 2.0) {
1109 g_warning("Event latency reached %f sec (%1.4f)", *latency, tracker.getSkew());
1110 }
1111 }
1113 /**
1114 * Returns new canvas as widget.
1115 */
1116 GtkWidget *
1117 sp_canvas_new_aa (void)
1118 {
1119 SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1121 return (GtkWidget *) canvas;
1122 }
1124 /**
1125 * The canvas widget's realize callback.
1126 */
1127 static void
1128 sp_canvas_realize (GtkWidget *widget)
1129 {
1130 SPCanvas *canvas = SP_CANVAS (widget);
1132 GdkWindowAttr attributes;
1133 attributes.window_type = GDK_WINDOW_CHILD;
1134 attributes.x = widget->allocation.x;
1135 attributes.y = widget->allocation.y;
1136 attributes.width = widget->allocation.width;
1137 attributes.height = widget->allocation.height;
1138 attributes.wclass = GDK_INPUT_OUTPUT;
1139 attributes.visual = gdk_rgb_get_visual ();
1140 attributes.colormap = gdk_rgb_get_cmap ();
1141 attributes.event_mask = (gtk_widget_get_events (widget) |
1142 GDK_EXPOSURE_MASK |
1143 GDK_BUTTON_PRESS_MASK |
1144 GDK_BUTTON_RELEASE_MASK |
1145 GDK_POINTER_MOTION_MASK |
1146 ( HAS_BROKEN_MOTION_HINTS ?
1147 0 : GDK_POINTER_MOTION_HINT_MASK ) |
1148 GDK_PROXIMITY_IN_MASK |
1149 GDK_PROXIMITY_OUT_MASK |
1150 GDK_KEY_PRESS_MASK |
1151 GDK_KEY_RELEASE_MASK |
1152 GDK_ENTER_NOTIFY_MASK |
1153 GDK_LEAVE_NOTIFY_MASK |
1154 GDK_FOCUS_CHANGE_MASK);
1155 gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1157 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1158 gdk_window_set_user_data (widget->window, widget);
1160 if ( prefs_get_int_attribute ("options.useextinput", "value", 1) )
1161 gtk_widget_set_events(widget, attributes.event_mask);
1163 widget->style = gtk_style_attach (widget->style, widget->window);
1165 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1167 canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1168 }
1170 /**
1171 * The canvas widget's unrealize callback.
1172 */
1173 static void
1174 sp_canvas_unrealize (GtkWidget *widget)
1175 {
1176 SPCanvas *canvas = SP_CANVAS (widget);
1178 canvas->current_item = NULL;
1179 canvas->grabbed_item = NULL;
1180 canvas->focused_item = NULL;
1182 shutdown_transients (canvas);
1184 gdk_gc_destroy (canvas->pixmap_gc);
1185 canvas->pixmap_gc = NULL;
1187 if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1188 (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1189 }
1191 /**
1192 * The canvas widget's size_request callback.
1193 */
1194 static void
1195 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1196 {
1197 static_cast<void>(SP_CANVAS (widget));
1199 req->width = 256;
1200 req->height = 256;
1201 }
1203 /**
1204 * The canvas widget's size_allocate callback.
1205 */
1206 static void
1207 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1208 {
1209 SPCanvas *canvas = SP_CANVAS (widget);
1211 /* Schedule redraw of new region */
1212 sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1213 if (allocation->width > widget->allocation.width) {
1214 sp_canvas_request_redraw (canvas,
1215 canvas->x0 + widget->allocation.width,
1216 0,
1217 canvas->x0 + allocation->width,
1218 canvas->y0 + allocation->height);
1219 }
1220 if (allocation->height > widget->allocation.height) {
1221 sp_canvas_request_redraw (canvas,
1222 0,
1223 canvas->y0 + widget->allocation.height,
1224 canvas->x0 + allocation->width,
1225 canvas->y0 + allocation->height);
1226 }
1228 widget->allocation = *allocation;
1230 if (GTK_WIDGET_REALIZED (widget)) {
1231 gdk_window_move_resize (widget->window,
1232 widget->allocation.x, widget->allocation.y,
1233 widget->allocation.width, widget->allocation.height);
1234 }
1235 }
1237 /**
1238 * Helper that emits an event for an item in the canvas, be it the current
1239 * item, grabbed item, or focused item, as appropriate.
1240 */
1241 static int
1242 emit_event (SPCanvas *canvas, GdkEvent *event)
1243 {
1244 guint mask;
1246 if (canvas->grabbed_item) {
1247 switch (event->type) {
1248 case GDK_ENTER_NOTIFY:
1249 mask = GDK_ENTER_NOTIFY_MASK;
1250 break;
1251 case GDK_LEAVE_NOTIFY:
1252 mask = GDK_LEAVE_NOTIFY_MASK;
1253 break;
1254 case GDK_MOTION_NOTIFY:
1255 mask = GDK_POINTER_MOTION_MASK;
1256 break;
1257 case GDK_BUTTON_PRESS:
1258 case GDK_2BUTTON_PRESS:
1259 case GDK_3BUTTON_PRESS:
1260 mask = GDK_BUTTON_PRESS_MASK;
1261 break;
1262 case GDK_BUTTON_RELEASE:
1263 mask = GDK_BUTTON_RELEASE_MASK;
1264 break;
1265 case GDK_KEY_PRESS:
1266 mask = GDK_KEY_PRESS_MASK;
1267 break;
1268 case GDK_KEY_RELEASE:
1269 mask = GDK_KEY_RELEASE_MASK;
1270 break;
1271 case GDK_SCROLL:
1272 mask = GDK_SCROLL;
1273 break;
1274 default:
1275 mask = 0;
1276 break;
1277 }
1279 if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1280 }
1282 /* Convert to world coordinates -- we have two cases because of diferent
1283 * offsets of the fields in the event structures.
1284 */
1286 GdkEvent ev = *event;
1288 switch (ev.type) {
1289 case GDK_ENTER_NOTIFY:
1290 case GDK_LEAVE_NOTIFY:
1291 ev.crossing.x += canvas->x0;
1292 ev.crossing.y += canvas->y0;
1293 break;
1294 case GDK_MOTION_NOTIFY:
1295 case GDK_BUTTON_PRESS:
1296 case GDK_2BUTTON_PRESS:
1297 case GDK_3BUTTON_PRESS:
1298 case GDK_BUTTON_RELEASE:
1299 ev.motion.x += canvas->x0;
1300 ev.motion.y += canvas->y0;
1301 break;
1302 default:
1303 break;
1304 }
1306 /* Choose where we send the event */
1308 /* canvas->current_item becomes NULL in some cases under Win32
1309 ** (e.g. if the pointer leaves the window). So this is a hack that
1310 ** Lauris applied to SP to get around the problem.
1311 */
1312 SPCanvasItem* item = NULL;
1313 if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1314 item = canvas->grabbed_item;
1315 } else {
1316 item = canvas->current_item;
1317 }
1319 if (canvas->focused_item &&
1320 ((event->type == GDK_KEY_PRESS) ||
1321 (event->type == GDK_KEY_RELEASE) ||
1322 (event->type == GDK_FOCUS_CHANGE))) {
1323 item = canvas->focused_item;
1324 }
1326 /* The event is propagated up the hierarchy (for if someone connected to
1327 * a group instead of a leaf event), and emission is stopped if a
1328 * handler returns TRUE, just like for GtkWidget events.
1329 */
1331 gint finished = FALSE;
1333 while (item && !finished) {
1334 gtk_object_ref (GTK_OBJECT (item));
1335 gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1336 SPCanvasItem *parent = item->parent;
1337 gtk_object_unref (GTK_OBJECT (item));
1338 item = parent;
1339 }
1341 return finished;
1342 }
1344 /**
1345 * Helper that re-picks the current item in the canvas, based on the event's
1346 * coordinates and emits enter/leave events for items as appropriate.
1347 */
1348 static int
1349 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1350 {
1351 int button_down = 0;
1352 double x, y;
1354 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1355 return FALSE;
1357 int retval = FALSE;
1359 if (canvas->gen_all_enter_events == false) {
1360 // If a button is down, we'll perform enter and leave events on the
1361 // current item, but not enter on any other item. This is more or
1362 // less like X pointer grabbing for canvas items.
1363 //
1364 button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1365 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1367 if (!button_down) canvas->left_grabbed_item = FALSE;
1368 }
1370 /* Save the event in the canvas. This is used to synthesize enter and
1371 * leave events in case the current item changes. It is also used to
1372 * re-pick the current item if the current one gets deleted. Also,
1373 * synthesize an enter event.
1374 */
1375 if (event != &canvas->pick_event) {
1376 if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1377 /* these fields have the same offsets in both types of events */
1379 canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY;
1380 canvas->pick_event.crossing.window = event->motion.window;
1381 canvas->pick_event.crossing.send_event = event->motion.send_event;
1382 canvas->pick_event.crossing.subwindow = NULL;
1383 canvas->pick_event.crossing.x = event->motion.x;
1384 canvas->pick_event.crossing.y = event->motion.y;
1385 canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL;
1386 canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR;
1387 canvas->pick_event.crossing.focus = FALSE;
1388 canvas->pick_event.crossing.state = event->motion.state;
1390 /* these fields don't have the same offsets in both types of events */
1392 if (event->type == GDK_MOTION_NOTIFY) {
1393 canvas->pick_event.crossing.x_root = event->motion.x_root;
1394 canvas->pick_event.crossing.y_root = event->motion.y_root;
1395 } else {
1396 canvas->pick_event.crossing.x_root = event->button.x_root;
1397 canvas->pick_event.crossing.y_root = event->button.y_root;
1398 }
1399 } else {
1400 canvas->pick_event = *event;
1401 }
1402 }
1404 /* Don't do anything else if this is a recursive call */
1405 if (canvas->in_repick) return retval;
1407 /* LeaveNotify means that there is no current item, so we don't look for one */
1408 if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1409 /* these fields don't have the same offsets in both types of events */
1411 if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1412 x = canvas->pick_event.crossing.x;
1413 y = canvas->pick_event.crossing.y;
1414 } else {
1415 x = canvas->pick_event.motion.x;
1416 y = canvas->pick_event.motion.y;
1417 }
1419 /* world coords */
1420 x += canvas->x0;
1421 y += canvas->y0;
1423 /* find the closest item */
1424 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1425 sp_canvas_item_invoke_point (canvas->root, Geom::Point(x, y), &canvas->new_current_item);
1426 } else {
1427 canvas->new_current_item = NULL;
1428 }
1429 } else {
1430 canvas->new_current_item = NULL;
1431 }
1433 if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1434 return retval; /* current item did not change */
1435 }
1437 /* Synthesize events for old and new current items */
1439 if ((canvas->new_current_item != canvas->current_item)
1440 && (canvas->current_item != NULL)
1441 && !canvas->left_grabbed_item) {
1442 GdkEvent new_event;
1443 SPCanvasItem *item;
1445 item = canvas->current_item;
1447 new_event = canvas->pick_event;
1448 new_event.type = GDK_LEAVE_NOTIFY;
1450 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1451 new_event.crossing.subwindow = NULL;
1452 canvas->in_repick = TRUE;
1453 retval = emit_event (canvas, &new_event);
1454 canvas->in_repick = FALSE;
1455 }
1457 if (canvas->gen_all_enter_events == false) {
1458 // new_current_item may have been set to NULL during the call to
1459 // emit_event() above
1460 if ((canvas->new_current_item != canvas->current_item) && button_down) {
1461 canvas->left_grabbed_item = TRUE;
1462 return retval;
1463 }
1464 }
1466 /* Handle the rest of cases */
1468 canvas->left_grabbed_item = FALSE;
1469 canvas->current_item = canvas->new_current_item;
1471 if (canvas->current_item != NULL) {
1472 GdkEvent new_event;
1474 new_event = canvas->pick_event;
1475 new_event.type = GDK_ENTER_NOTIFY;
1476 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1477 new_event.crossing.subwindow = NULL;
1478 retval = emit_event (canvas, &new_event);
1479 }
1481 return retval;
1482 }
1484 /**
1485 * Button event handler for the canvas.
1486 */
1487 static gint
1488 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1489 {
1490 SPCanvas *canvas = SP_CANVAS (widget);
1492 int retval = FALSE;
1494 /* dispatch normally regardless of the event's window if an item has
1495 has a pointer grab in effect */
1496 if (!canvas->grabbed_item &&
1497 event->window != SP_CANVAS_WINDOW (canvas))
1498 return retval;
1500 int mask;
1501 switch (event->button) {
1502 case 1:
1503 mask = GDK_BUTTON1_MASK;
1504 break;
1505 case 2:
1506 mask = GDK_BUTTON2_MASK;
1507 break;
1508 case 3:
1509 mask = GDK_BUTTON3_MASK;
1510 break;
1511 case 4:
1512 mask = GDK_BUTTON4_MASK;
1513 break;
1514 case 5:
1515 mask = GDK_BUTTON5_MASK;
1516 break;
1517 default:
1518 mask = 0;
1519 }
1521 switch (event->type) {
1522 case GDK_BUTTON_PRESS:
1523 case GDK_2BUTTON_PRESS:
1524 case GDK_3BUTTON_PRESS:
1525 /* Pick the current item as if the button were not pressed, and
1526 * then process the event.
1527 */
1528 canvas->state = event->state;
1529 pick_current_item (canvas, (GdkEvent *) event);
1530 canvas->state ^= mask;
1531 retval = emit_event (canvas, (GdkEvent *) event);
1532 break;
1534 case GDK_BUTTON_RELEASE:
1535 /* Process the event as if the button were pressed, then repick
1536 * after the button has been released
1537 */
1538 canvas->state = event->state;
1539 retval = emit_event (canvas, (GdkEvent *) event);
1540 event->state ^= mask;
1541 canvas->state = event->state;
1542 pick_current_item (canvas, (GdkEvent *) event);
1543 event->state ^= mask;
1544 break;
1546 default:
1547 g_assert_not_reached ();
1548 }
1550 return retval;
1551 }
1553 /**
1554 * Scroll event handler for the canvas.
1555 *
1556 * \todo FIXME: generate motion events to re-select items.
1557 */
1558 static gint
1559 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1560 {
1561 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1562 }
1564 static inline void request_motions(GdkWindow *w, GdkEventMotion *event) {
1565 gdk_window_get_pointer(w, NULL, NULL, NULL);
1566 #if HAS_GDK_EVENT_REQUEST_MOTIONS
1567 gdk_event_request_motions(event);
1568 #endif
1569 }
1571 /**
1572 * Motion event handler for the canvas.
1573 */
1574 static int
1575 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1576 {
1577 int status;
1578 SPCanvas *canvas = SP_CANVAS (widget);
1580 track_latency((GdkEvent *)event);
1582 if (event->window != SP_CANVAS_WINDOW (canvas))
1583 return FALSE;
1585 if (canvas->pixmap_gc == NULL) // canvas being deleted
1586 return FALSE;
1588 canvas->state = event->state;
1589 pick_current_item (canvas, (GdkEvent *) event);
1591 status = emit_event (canvas, (GdkEvent *) event);
1593 if (event->is_hint) {
1594 request_motions(widget->window, event);
1595 }
1597 return status;
1598 }
1600 static void
1601 sp_canvas_paint_single_buffer (SPCanvas *canvas, int x0, int y0, int x1, int y1, int draw_x1, int draw_y1, int draw_x2, int draw_y2, int sw)
1602 {
1603 GtkWidget *widget = GTK_WIDGET (canvas);
1605 SPCanvasBuf buf;
1606 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1607 buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1608 } else {
1609 buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1610 }
1612 // Mark the region clean
1613 sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1615 buf.buf_rowstride = sw * 4;
1616 buf.rect.x0 = x0;
1617 buf.rect.y0 = y0;
1618 buf.rect.x1 = x1;
1619 buf.rect.y1 = y1;
1620 buf.visible_rect.x0 = draw_x1;
1621 buf.visible_rect.y0 = draw_y1;
1622 buf.visible_rect.x1 = draw_x2;
1623 buf.visible_rect.y1 = draw_y2;
1624 GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1625 buf.bg_color = (((color->red & 0xff00) << 8)
1626 | (color->green & 0xff00)
1627 | (color->blue >> 8));
1628 buf.is_empty = true;
1630 buf.ct = nr_create_cairo_context_canvasbuf (&(buf.visible_rect), &buf);
1632 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1633 SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1634 }
1636 #if ENABLE_LCMS
1637 cmsHTRANSFORM transf = 0;
1638 long long int fromDisplay = prefs_get_int_attribute_limited( "options.displayprofile", "from_display", 0, 0, 1 );
1639 if ( fromDisplay ) {
1640 transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" );
1641 } else {
1642 transf = Inkscape::colorprofile_get_display_transform();
1643 }
1644 #endif // ENABLE_LCMS
1646 if (buf.is_empty) {
1647 #if ENABLE_LCMS
1648 if ( transf && canvas->enable_cms_display_adj ) {
1649 cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1650 }
1651 #endif // ENABLE_LCMS
1652 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1653 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1654 canvas->pixmap_gc,
1655 TRUE,
1656 x0 - canvas->x0, y0 - canvas->y0,
1657 x1 - x0, y1 - y0);
1658 } else {
1660 #if ENABLE_LCMS
1661 if ( transf && canvas->enable_cms_display_adj ) {
1662 for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1663 guchar* p = buf.buf + (sw * 3) * yy;
1664 cmsDoTransform( transf, p, p, (x1 - x0) );
1665 }
1666 }
1667 #endif // ENABLE_LCMS
1669 // Now we only need to output the prepared pixmap to the actual screen, and this define chooses one
1670 // of the two ways to do it. The cairo way is direct and straightforward, but unfortunately
1671 // noticeably slower. I asked Carl Worth but he was unable so far to suggest any specific reason
1672 // for this slowness. So, for now we use the oldish method: squeeze out 32bpp buffer to 24bpp and
1673 // use gdk_draw_rgb_image_dithalign, for unfortunately gdk can only handle 24 bpp, which cairo
1674 // cannot handle at all. Still, this way is currently faster even despite the blit with squeeze.
1676 ///#define CANVAS_OUTPUT_VIA_CAIRO
1678 #ifdef CANVAS_OUTPUT_VIA_CAIRO
1680 buf.cst = cairo_image_surface_create_for_data (
1681 buf.buf,
1682 CAIRO_FORMAT_ARGB32, // unpacked, i.e. 32 bits! one byte is unused
1683 x1 - x0, y1 - y0,
1684 buf.buf_rowstride
1685 );
1686 cairo_t *window_ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1687 cairo_set_source_surface (window_ct, buf.cst, x0 - canvas->x0, y0 - canvas->y0);
1688 cairo_paint (window_ct);
1689 cairo_destroy (window_ct);
1690 cairo_surface_finish (buf.cst);
1691 cairo_surface_destroy (buf.cst);
1693 #else
1695 NRPixBlock b3;
1696 nr_pixblock_setup_fast (&b3, NR_PIXBLOCK_MODE_R8G8B8, x0, y0, x1, y1, TRUE);
1698 NRPixBlock b4;
1699 nr_pixblock_setup_extern (&b4, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1,
1700 buf.buf,
1701 buf.buf_rowstride,
1702 FALSE, FALSE);
1704 // this does the 32->24 squishing, using an assembler routine:
1705 nr_blit_pixblock_pixblock (&b3, &b4);
1707 gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1708 canvas->pixmap_gc,
1709 x0 - canvas->x0, y0 - canvas->y0,
1710 x1 - x0, y1 - y0,
1711 GDK_RGB_DITHER_MAX,
1712 NR_PIXBLOCK_PX(&b3),
1713 sw * 3,
1714 x0 - canvas->x0, y0 - canvas->y0);
1716 nr_pixblock_release (&b3);
1717 nr_pixblock_release (&b4);
1718 #endif
1719 }
1721 cairo_surface_t *cst = cairo_get_target(buf.ct);
1722 cairo_destroy (buf.ct);
1723 cairo_surface_finish (cst);
1724 cairo_surface_destroy (cst);
1726 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1727 nr_pixelstore_256K_free (buf.buf);
1728 } else {
1729 nr_pixelstore_1M_free (buf.buf);
1730 }
1731 }
1733 struct PaintRectSetup {
1734 SPCanvas* canvas;
1735 NRRectL big_rect;
1736 GTimeVal start_time;
1737 int max_pixels;
1738 Geom::Point mouse_loc;
1739 };
1741 /**
1742 * Paint the given rect, recursively subdividing the region until it is the size of a single
1743 * buffer.
1744 *
1745 * @return true if the drawing completes
1746 */
1747 static int
1748 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1749 {
1750 GTimeVal now;
1751 g_get_current_time (&now);
1753 glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1754 + (now.tv_usec - setup->start_time.tv_usec);
1756 // Allow only very fast buffers to be run together;
1757 // as soon as the total redraw time exceeds 1ms, cancel;
1758 // this returns control to the idle loop and allows Inkscape to process user input
1759 // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1760 // it will get back and finish painting what remains to paint.
1761 if (elapsed > 1000) {
1763 // Interrupting redraw isn't always good.
1764 // For example, when you drag one node of a big path, only the buffer containing
1765 // the mouse cursor will be redrawn again and again, and the rest of the path
1766 // will remain stale because Inkscape never has enough idle time to redraw all
1767 // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1768 // If this limit is set, and if we have aborted redraw more times than is allowed,
1769 // interrupting is blocked and we're forced to redraw full screen once
1770 // (after which we can again interrupt forced_redraw_limit times).
1771 if (setup->canvas->forced_redraw_limit < 0 ||
1772 setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1774 if (setup->canvas->forced_redraw_limit != -1) {
1775 setup->canvas->forced_redraw_count++;
1776 }
1778 return false;
1779 }
1780 }
1782 // Find the optimal buffer dimensions
1783 int bw = this_rect.x1 - this_rect.x0;
1784 int bh = this_rect.y1 - this_rect.y0;
1785 if ((bw < 1) || (bh < 1))
1786 return 0;
1788 if (bw * bh < setup->max_pixels) {
1789 // We are small enough
1790 sp_canvas_paint_single_buffer (setup->canvas,
1791 this_rect.x0, this_rect.y0,
1792 this_rect.x1, this_rect.y1,
1793 setup->big_rect.x0, setup->big_rect.y0,
1794 setup->big_rect.x1, setup->big_rect.y1, bw);
1795 return 1;
1796 }
1798 NRRectL lo = this_rect;
1799 NRRectL hi = this_rect;
1801 /*
1802 This test determines the redraw strategy:
1804 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1805 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1806 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1807 and seems to be faster for drawings with many smaller objects at zoom-out.
1809 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1810 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1811 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1812 faster.
1814 The default for now is the strips mode.
1815 */
1816 if (bw < bh || bh < 2 * TILE_SIZE) {
1817 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1818 int mid = ((long long) this_rect.x0 + (long long) this_rect.x1) / 2;
1819 // Make sure that mid lies on a tile boundary
1820 mid = (mid / TILE_SIZE) * TILE_SIZE;
1822 lo.x1 = mid;
1823 hi.x0 = mid;
1825 if (setup->mouse_loc[Geom::X] < mid) {
1826 // Always paint towards the mouse first
1827 return sp_canvas_paint_rect_internal(setup, lo)
1828 && sp_canvas_paint_rect_internal(setup, hi);
1829 } else {
1830 return sp_canvas_paint_rect_internal(setup, hi)
1831 && sp_canvas_paint_rect_internal(setup, lo);
1832 }
1833 } else {
1834 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1835 int mid = ((long long) this_rect.y0 + (long long) this_rect.y1) / 2;
1836 // Make sure that mid lies on a tile boundary
1837 mid = (mid / TILE_SIZE) * TILE_SIZE;
1839 lo.y1 = mid;
1840 hi.y0 = mid;
1842 if (setup->mouse_loc[Geom::Y] < mid) {
1843 // Always paint towards the mouse first
1844 return sp_canvas_paint_rect_internal(setup, lo)
1845 && sp_canvas_paint_rect_internal(setup, hi);
1846 } else {
1847 return sp_canvas_paint_rect_internal(setup, hi)
1848 && sp_canvas_paint_rect_internal(setup, lo);
1849 }
1850 }
1851 }
1854 /**
1855 * Helper that draws a specific rectangular part of the canvas.
1856 *
1857 * @return true if the rectangle painting succeeds.
1858 */
1859 static bool
1860 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1861 {
1862 g_return_val_if_fail (!canvas->need_update, false);
1864 NRRectL rect;
1865 rect.x0 = xx0;
1866 rect.x1 = xx1;
1867 rect.y0 = yy0;
1868 rect.y1 = yy1;
1870 // Clip rect-to-draw by the current visible area
1871 rect.x0 = MAX (rect.x0, canvas->x0);
1872 rect.y0 = MAX (rect.y0, canvas->y0);
1873 rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1874 rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1876 #ifdef DEBUG_REDRAW
1877 // paint the area to redraw yellow
1878 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1879 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1880 canvas->pixmap_gc,
1881 TRUE,
1882 rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1883 rect.x1 - rect.x0, rect.y1 - rect.y0);
1884 #endif
1886 PaintRectSetup setup;
1888 setup.canvas = canvas;
1889 setup.big_rect = rect;
1891 // Save the mouse location
1892 gint x, y;
1893 gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1894 setup.mouse_loc = sp_canvas_window_to_world (canvas, Geom::Point(x,y));
1896 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1897 // use 256K as a compromise to not slow down gradients
1898 // 256K is the cached buffer and we need 4 channels
1899 setup.max_pixels = 65536; // 256K/4
1900 } else {
1901 // paths only, so 1M works faster
1902 // 1M is the cached buffer and we need 4 channels
1903 setup.max_pixels = 262144;
1904 }
1906 // Start the clock
1907 g_get_current_time(&(setup.start_time));
1909 // Go
1910 return sp_canvas_paint_rect_internal(&setup, rect);
1911 }
1913 /**
1914 * Force a full redraw after a specified number of interrupted redraws
1915 */
1916 void
1917 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1918 g_return_if_fail(canvas != NULL);
1920 canvas->forced_redraw_limit = count;
1921 canvas->forced_redraw_count = 0;
1922 }
1924 /**
1925 * End forced full redraw requests
1926 */
1927 void
1928 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1929 g_return_if_fail(canvas != NULL);
1931 canvas->forced_redraw_limit = -1;
1932 }
1934 /**
1935 * The canvas widget's expose callback.
1936 */
1937 static gint
1938 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1939 {
1940 SPCanvas *canvas = SP_CANVAS (widget);
1942 if (!GTK_WIDGET_DRAWABLE (widget) ||
1943 (event->window != SP_CANVAS_WINDOW (canvas)))
1944 return FALSE;
1946 int n_rects;
1947 GdkRectangle *rects;
1948 gdk_region_get_rectangles (event->region, &rects, &n_rects);
1950 for (int i = 0; i < n_rects; i++) {
1951 NRRectL rect;
1953 rect.x0 = rects[i].x + canvas->x0;
1954 rect.y0 = rects[i].y + canvas->y0;
1955 rect.x1 = rect.x0 + rects[i].width;
1956 rect.y1 = rect.y0 + rects[i].height;
1958 sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1959 }
1961 if (n_rects > 0)
1962 g_free (rects);
1964 return FALSE;
1965 }
1967 /**
1968 * The canvas widget's keypress callback.
1969 */
1970 static gint
1971 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1972 {
1973 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1974 }
1976 /**
1977 * Crossing event handler for the canvas.
1978 */
1979 static gint
1980 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1981 {
1982 SPCanvas *canvas = SP_CANVAS (widget);
1984 if (event->window != SP_CANVAS_WINDOW (canvas))
1985 return FALSE;
1987 canvas->state = event->state;
1988 return pick_current_item (canvas, (GdkEvent *) event);
1989 }
1991 /**
1992 * Focus in handler for the canvas.
1993 */
1994 static gint
1995 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
1996 {
1997 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1999 SPCanvas *canvas = SP_CANVAS (widget);
2001 if (canvas->focused_item) {
2002 return emit_event (canvas, (GdkEvent *) event);
2003 } else {
2004 return FALSE;
2005 }
2006 }
2008 /**
2009 * Focus out handler for the canvas.
2010 */
2011 static gint
2012 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
2013 {
2014 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2016 SPCanvas *canvas = SP_CANVAS (widget);
2018 if (canvas->focused_item)
2019 return emit_event (canvas, (GdkEvent *) event);
2020 else
2021 return FALSE;
2022 }
2024 /**
2025 * Helper that repaints the areas in the canvas that need it.
2026 *
2027 * @return true if all the dirty parts have been redrawn
2028 */
2029 static int
2030 paint (SPCanvas *canvas)
2031 {
2032 if (canvas->need_update) {
2033 sp_canvas_item_invoke_update (canvas->root, Geom::identity(), 0);
2034 canvas->need_update = FALSE;
2035 }
2037 if (!canvas->need_redraw)
2038 return TRUE;
2040 Gdk::Region to_paint;
2042 for (int j=canvas->tTop; j<canvas->tBottom; j++) {
2043 for (int i=canvas->tLeft; i<canvas->tRight; i++) {
2044 int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
2046 if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
2047 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
2048 TILE_SIZE, TILE_SIZE));
2049 }
2051 }
2052 }
2054 if (!to_paint.empty()) {
2055 Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
2056 typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
2057 for (Iter i=rect.begin(); i != rect.end(); ++i) {
2058 int x0 = (*i).get_x();
2059 int y0 = (*i).get_y();
2060 int x1 = x0 + (*i).get_width();
2061 int y1 = y0 + (*i).get_height();
2062 if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
2063 // Aborted
2064 return FALSE;
2065 };
2066 }
2067 }
2069 canvas->need_redraw = FALSE;
2071 // we've had a full unaborted redraw, reset the full redraw counter
2072 if (canvas->forced_redraw_limit != -1) {
2073 canvas->forced_redraw_count = 0;
2074 }
2076 return TRUE;
2077 }
2079 /**
2080 * Helper that invokes update, paint, and repick on canvas.
2081 */
2082 static int
2083 do_update (SPCanvas *canvas)
2084 {
2085 if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop durring interrupted display!
2086 return TRUE;
2088 /* Cause the update if necessary */
2089 if (canvas->need_update) {
2090 sp_canvas_item_invoke_update (canvas->root, Geom::identity(), 0);
2091 canvas->need_update = FALSE;
2092 }
2094 /* Paint if able to */
2095 if (GTK_WIDGET_DRAWABLE (canvas)) {
2096 return paint (canvas);
2097 }
2099 /* Pick new current item */
2100 while (canvas->need_repick) {
2101 canvas->need_repick = FALSE;
2102 pick_current_item (canvas, &canvas->pick_event);
2103 }
2105 return TRUE;
2106 }
2108 /**
2109 * Idle handler for the canvas that deals with pending updates and redraws.
2110 */
2111 static gint
2112 idle_handler (gpointer data)
2113 {
2114 GDK_THREADS_ENTER ();
2116 SPCanvas *canvas = SP_CANVAS (data);
2118 int const ret = do_update (canvas);
2120 if (ret) {
2121 /* Reset idle id */
2122 canvas->idle_id = 0;
2123 }
2125 GDK_THREADS_LEAVE ();
2127 return !ret;
2128 }
2130 /**
2131 * Convenience function to add an idle handler to a canvas.
2132 */
2133 static void
2134 add_idle (SPCanvas *canvas)
2135 {
2136 if (canvas->idle_id != 0)
2137 return;
2139 canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2140 }
2142 /**
2143 * Returns the root group of the specified canvas.
2144 */
2145 SPCanvasGroup *
2146 sp_canvas_root (SPCanvas *canvas)
2147 {
2148 g_return_val_if_fail (canvas != NULL, NULL);
2149 g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2151 return SP_CANVAS_GROUP (canvas->root);
2152 }
2154 /**
2155 * Scrolls canvas to specific position (cx and cy are measured in screen pixels)
2156 */
2157 void
2158 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2159 {
2160 g_return_if_fail (canvas != NULL);
2161 g_return_if_fail (SP_IS_CANVAS (canvas));
2163 int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels)
2164 int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do!
2165 int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the
2166 int dy = iy - canvas->y0; // canvas w.r.t its previous position
2168 canvas->dx0 = cx; // here the 'd' stands for double, not delta!
2169 canvas->dy0 = cy;
2170 canvas->x0 = ix;
2171 canvas->y0 = iy;
2173 sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2175 if (!clear) {
2176 // scrolling without zoom; redraw only the newly exposed areas
2177 if ((dx != 0) || (dy != 0)) {
2178 canvas->is_scrolling = is_scrolling;
2179 if (GTK_WIDGET_REALIZED (canvas)) {
2180 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2181 }
2182 }
2183 } else {
2184 // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2185 }
2186 }
2188 /**
2189 * Updates canvas if necessary.
2190 */
2191 void
2192 sp_canvas_update_now (SPCanvas *canvas)
2193 {
2194 g_return_if_fail (canvas != NULL);
2195 g_return_if_fail (SP_IS_CANVAS (canvas));
2197 if (!(canvas->need_update ||
2198 canvas->need_redraw))
2199 return;
2201 do_update (canvas);
2202 }
2204 /**
2205 * Update callback for canvas widget.
2206 */
2207 static void
2208 sp_canvas_request_update (SPCanvas *canvas)
2209 {
2210 canvas->need_update = TRUE;
2211 add_idle (canvas);
2212 }
2214 /**
2215 * Forces redraw of rectangular canvas area.
2216 */
2217 void
2218 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2219 {
2220 NRRectL bbox;
2221 NRRectL visible;
2222 NRRectL clip;
2224 g_return_if_fail (canvas != NULL);
2225 g_return_if_fail (SP_IS_CANVAS (canvas));
2227 if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2228 if ((x0 >= x1) || (y0 >= y1)) return;
2230 bbox.x0 = x0;
2231 bbox.y0 = y0;
2232 bbox.x1 = x1;
2233 bbox.y1 = y1;
2235 visible.x0 = canvas->x0;
2236 visible.y0 = canvas->y0;
2237 visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2238 visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2240 nr_rect_l_intersect (&clip, &bbox, &visible);
2242 sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2243 add_idle (canvas);
2244 }
2246 /**
2247 * Sets world coordinates from win and canvas.
2248 */
2249 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2250 {
2251 g_return_if_fail (canvas != NULL);
2252 g_return_if_fail (SP_IS_CANVAS (canvas));
2254 if (worldx) *worldx = canvas->x0 + winx;
2255 if (worldy) *worldy = canvas->y0 + winy;
2256 }
2258 /**
2259 * Sets win coordinates from world and canvas.
2260 */
2261 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2262 {
2263 g_return_if_fail (canvas != NULL);
2264 g_return_if_fail (SP_IS_CANVAS (canvas));
2266 if (winx) *winx = worldx - canvas->x0;
2267 if (winy) *winy = worldy - canvas->y0;
2268 }
2270 /**
2271 * Converts point from win to world coordinates.
2272 */
2273 Geom::Point sp_canvas_window_to_world(SPCanvas const *canvas, Geom::Point const win)
2274 {
2275 g_assert (canvas != NULL);
2276 g_assert (SP_IS_CANVAS (canvas));
2278 return Geom::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2279 }
2281 /**
2282 * Converts point from world to win coordinates.
2283 */
2284 Geom::Point sp_canvas_world_to_window(SPCanvas const *canvas, Geom::Point const world)
2285 {
2286 g_assert (canvas != NULL);
2287 g_assert (SP_IS_CANVAS (canvas));
2289 return Geom::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2290 }
2292 /**
2293 * Returns true if point given in world coordinates is inside window.
2294 */
2295 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, Geom::Point const &world)
2296 {
2297 g_assert( canvas != NULL );
2298 g_assert(SP_IS_CANVAS(canvas));
2300 GtkWidget const &w = *GTK_WIDGET(canvas);
2301 return ( ( canvas->x0 <= world[Geom::X] ) &&
2302 ( canvas->y0 <= world[Geom::Y] ) &&
2303 ( world[Geom::X] < canvas->x0 + w.allocation.width ) &&
2304 ( world[Geom::Y] < canvas->y0 + w.allocation.height ) );
2305 }
2307 /**
2308 * Return canvas window coordinates as Geom::Rect.
2309 */
2310 Geom::Rect SPCanvas::getViewbox() const
2311 {
2312 GtkWidget const *w = GTK_WIDGET(this);
2313 return Geom::Rect(Geom::Point(dx0, dy0),
2314 Geom::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2315 }
2317 /**
2318 * Return canvas window coordinates as IRect (a rectangle defined by integers).
2319 */
2320 NR::IRect SPCanvas::getViewboxIntegers() const
2321 {
2322 GtkWidget const *w = GTK_WIDGET(this);
2323 return NR::IRect(NR::IPoint(x0, y0),
2324 NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height));
2325 }
2327 inline int sp_canvas_tile_floor(int x)
2328 {
2329 return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2330 }
2332 inline int sp_canvas_tile_ceil(int x)
2333 {
2334 return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2335 }
2337 /**
2338 * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2339 */
2340 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2341 {
2342 if ( nl >= nr || nt >= nb ) {
2343 if ( canvas->tiles ) g_free(canvas->tiles);
2344 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2345 canvas->tileH=canvas->tileV=0;
2346 canvas->tiles=NULL;
2347 return;
2348 }
2349 int tl=sp_canvas_tile_floor(nl);
2350 int tt=sp_canvas_tile_floor(nt);
2351 int tr=sp_canvas_tile_ceil(nr);
2352 int tb=sp_canvas_tile_ceil(nb);
2354 int nh = tr-tl, nv = tb-tt;
2355 uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2356 for (int i=tl; i<tr; i++) {
2357 for (int j=tt; j<tb; j++) {
2358 int ind = (i-tl) + (j-tt)*nh;
2359 if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2360 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2361 } else {
2362 ntiles[ind]=0; // newly exposed areas get 0
2363 }
2364 }
2365 }
2366 if ( canvas->tiles ) g_free(canvas->tiles);
2367 canvas->tiles=ntiles;
2368 canvas->tLeft=tl;
2369 canvas->tTop=tt;
2370 canvas->tRight=tr;
2371 canvas->tBottom=tb;
2372 canvas->tileH=nh;
2373 canvas->tileV=nv;
2374 }
2376 /*
2377 * Helper that queues a canvas rectangle for redraw
2378 */
2379 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2380 canvas->need_redraw = TRUE;
2382 sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2383 }
2385 /**
2386 * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2387 */
2388 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2389 {
2390 if ( nl >= nr || nt >= nb ) {
2391 return;
2392 }
2393 int tl=sp_canvas_tile_floor(nl);
2394 int tt=sp_canvas_tile_floor(nt);
2395 int tr=sp_canvas_tile_ceil(nr);
2396 int tb=sp_canvas_tile_ceil(nb);
2397 if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2398 if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2399 if ( tr > canvas->tRight ) tr=canvas->tRight;
2400 if ( tt < canvas->tTop ) tt=canvas->tTop;
2401 if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2403 for (int i=tl; i<tr; i++) {
2404 for (int j=tt; j<tb; j++) {
2405 canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2406 }
2407 }
2408 }
2411 /*
2412 Local Variables:
2413 mode:c++
2414 c-file-style:"stroustrup"
2415 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2416 indent-tabs-mode:nil
2417 fill-column:99
2418 End:
2419 */
2420 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :