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>
28 #include <gtkmm.h>
30 #include <helper/sp-marshal.h>
31 #include <display/sp-canvas.h>
32 #include "display-forward.h"
33 #include <libnr/nr-matrix-fns.h>
34 #include <libnr/nr-matrix-ops.h>
35 #include <libnr/nr-convex-hull.h>
36 #include "prefs-utils.h"
37 #include "inkscape.h"
38 #include "sodipodi-ctrlrect.h"
39 #if ENABLE_LCMS
40 #include "color-profile-fns.h"
41 #endif // ENABLE_LCMS
42 #include "display/rendermode.h"
44 // Define this to visualize the regions to be redrawn
45 //#define DEBUG_REDRAW 1;
47 // Tiles are a way to minimize the number of redraws, eliminating too small redraws.
48 // The canvas stores a 2D array of ints, each representing a TILE_SIZExTILE_SIZE pixels tile.
49 // If any part of it is dirtied, the entire tile is dirtied (its int is nonzero) and repainted.
50 #define TILE_SIZE 16
52 static gint const sp_canvas_update_priority = G_PRIORITY_HIGH_IDLE;
54 #define SP_CANVAS_WINDOW(c) (((GtkWidget *) (c))->window)
56 enum {
57 SP_CANVAS_ITEM_VISIBLE = 1 << 7,
58 SP_CANVAS_ITEM_NEED_UPDATE = 1 << 8,
59 SP_CANVAS_ITEM_NEED_AFFINE = 1 << 9
60 };
62 /**
63 * A group of Items.
64 */
65 struct SPCanvasGroup {
66 SPCanvasItem item;
68 GList *items, *last;
69 };
71 /**
72 * The SPCanvasGroup vtable.
73 */
74 struct SPCanvasGroupClass {
75 SPCanvasItemClass parent_class;
76 };
78 /**
79 * The SPCanvas vtable.
80 */
81 struct SPCanvasClass {
82 GtkWidgetClass parent_class;
83 };
85 static void group_add (SPCanvasGroup *group, SPCanvasItem *item);
86 static void group_remove (SPCanvasGroup *group, SPCanvasItem *item);
88 /* SPCanvasItem */
90 enum {ITEM_EVENT, ITEM_LAST_SIGNAL};
93 static void sp_canvas_request_update (SPCanvas *canvas);
95 static void sp_canvas_item_class_init (SPCanvasItemClass *klass);
96 static void sp_canvas_item_init (SPCanvasItem *item);
97 static void sp_canvas_item_dispose (GObject *object);
98 static void sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args);
100 static int emit_event (SPCanvas *canvas, GdkEvent *event);
102 static guint item_signals[ITEM_LAST_SIGNAL] = { 0 };
104 static GtkObjectClass *item_parent_class;
106 /**
107 * Registers the SPCanvasItem class with Glib and returns its type number.
108 */
109 GType
110 sp_canvas_item_get_type (void)
111 {
112 static GType type = 0;
113 if (!type) {
114 static GTypeInfo const info = {
115 sizeof (SPCanvasItemClass),
116 NULL, NULL,
117 (GClassInitFunc) sp_canvas_item_class_init,
118 NULL, NULL,
119 sizeof (SPCanvasItem),
120 0,
121 (GInstanceInitFunc) sp_canvas_item_init,
122 NULL
123 };
124 type = g_type_register_static (GTK_TYPE_OBJECT, "SPCanvasItem", &info, (GTypeFlags)0);
125 }
127 return type;
128 }
130 /**
131 * Initializes the SPCanvasItem vtable and the "event" signal.
132 */
133 static void
134 sp_canvas_item_class_init (SPCanvasItemClass *klass)
135 {
136 GObjectClass *object_class = (GObjectClass *) klass;
138 /* fixme: Derive from GObject */
139 item_parent_class = (GtkObjectClass*)gtk_type_class (GTK_TYPE_OBJECT);
141 item_signals[ITEM_EVENT] = g_signal_new ("event",
142 G_TYPE_FROM_CLASS (klass),
143 G_SIGNAL_RUN_LAST,
144 ((glong)((guint8*)&(klass->event) - (guint8*)klass)),
145 NULL, NULL,
146 sp_marshal_BOOLEAN__POINTER,
147 G_TYPE_BOOLEAN, 1,
148 GDK_TYPE_EVENT);
150 object_class->dispose = sp_canvas_item_dispose;
151 }
153 /**
154 * Callback for initialization of SPCanvasItem.
155 */
156 static void
157 sp_canvas_item_init (SPCanvasItem *item)
158 {
159 item->flags |= SP_CANVAS_ITEM_VISIBLE;
160 item->xform = NR::Matrix(NR::identity());
161 }
163 /**
164 * Constructs new SPCanvasItem on SPCanvasGroup.
165 */
166 SPCanvasItem *
167 sp_canvas_item_new (SPCanvasGroup *parent, GtkType type, gchar const *first_arg_name, ...)
168 {
169 va_list args;
171 g_return_val_if_fail (parent != NULL, NULL);
172 g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL);
173 g_return_val_if_fail (gtk_type_is_a (type, sp_canvas_item_get_type ()), NULL);
175 SPCanvasItem *item = SP_CANVAS_ITEM (gtk_type_new (type));
177 va_start (args, first_arg_name);
178 sp_canvas_item_construct (item, parent, first_arg_name, args);
179 va_end (args);
181 return item;
182 }
184 /**
185 * Sets up the newly created SPCanvasItem.
186 *
187 * We make it static for encapsulation reasons since it was nowhere used.
188 */
189 static void
190 sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args)
191 {
192 g_return_if_fail (SP_IS_CANVAS_GROUP (parent));
193 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
195 item->parent = SP_CANVAS_ITEM (parent);
196 item->canvas = item->parent->canvas;
198 g_object_set_valist (G_OBJECT (item), first_arg_name, args);
200 group_add (SP_CANVAS_GROUP (item->parent), item);
202 sp_canvas_item_request_update (item);
203 }
205 /**
206 * Helper function that requests redraw only if item's visible flag is set.
207 */
208 static void
209 redraw_if_visible (SPCanvasItem *item)
210 {
211 if (item->flags & SP_CANVAS_ITEM_VISIBLE) {
212 int x0 = (int)(item->x1);
213 int x1 = (int)(item->x2);
214 int y0 = (int)(item->y1);
215 int y1 = (int)(item->y2);
217 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
218 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
219 }
220 }
221 }
223 /**
224 * Callback that removes item from all referers and destroys it.
225 */
226 static void
227 sp_canvas_item_dispose (GObject *object)
228 {
229 SPCanvasItem *item = SP_CANVAS_ITEM (object);
231 // Hack: if this is a ctrlrect, move it to 0,0;
232 // this redraws only the stroke of the rect to be deleted,
233 // avoiding redraw of the entire area
234 if (SP_IS_CTRLRECT(item)) {
235 SP_CTRLRECT(object)->setRectangle(NR::Rect(NR::Point(0,0),NR::Point(0,0)));
236 SP_CTRLRECT(object)->update(item->xform, 0);
237 } else {
238 redraw_if_visible (item);
239 }
240 item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
242 if (item == item->canvas->current_item) {
243 item->canvas->current_item = NULL;
244 item->canvas->need_repick = TRUE;
245 }
247 if (item == item->canvas->new_current_item) {
248 item->canvas->new_current_item = NULL;
249 item->canvas->need_repick = TRUE;
250 }
252 if (item == item->canvas->grabbed_item) {
253 item->canvas->grabbed_item = NULL;
254 gdk_pointer_ungrab (GDK_CURRENT_TIME);
255 }
257 if (item == item->canvas->focused_item)
258 item->canvas->focused_item = NULL;
260 if (item->parent) {
261 group_remove (SP_CANVAS_GROUP (item->parent), item);
262 }
264 G_OBJECT_CLASS (item_parent_class)->dispose (object);
265 }
267 /**
268 * Helper function to update item and its children.
269 *
270 * NB! affine is parent2canvas.
271 */
272 static void
273 sp_canvas_item_invoke_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
274 {
275 /* Apply the child item's transform */
276 NR::Matrix child_affine = item->xform * affine;
278 /* apply object flags to child flags */
279 int child_flags = flags & ~SP_CANVAS_UPDATE_REQUESTED;
281 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
282 child_flags |= SP_CANVAS_UPDATE_REQUESTED;
284 if (item->flags & SP_CANVAS_ITEM_NEED_AFFINE)
285 child_flags |= SP_CANVAS_UPDATE_AFFINE;
287 if (child_flags & (SP_CANVAS_UPDATE_REQUESTED | SP_CANVAS_UPDATE_AFFINE)) {
288 if (SP_CANVAS_ITEM_GET_CLASS (item)->update)
289 SP_CANVAS_ITEM_GET_CLASS (item)->update (item, child_affine, child_flags);
290 }
292 GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_UPDATE);
293 GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_AFFINE);
294 }
296 /**
297 * Helper function to invoke the point method of the item.
298 *
299 * The argument x, y should be in the parent's item-relative coordinate
300 * system. This routine applies the inverse of the item's transform,
301 * maintaining the affine invariant.
302 */
303 static double
304 sp_canvas_item_invoke_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
305 {
306 if (SP_CANVAS_ITEM_GET_CLASS (item)->point)
307 return SP_CANVAS_ITEM_GET_CLASS (item)->point (item, p, actual_item);
309 return NR_HUGE;
310 }
312 /**
313 * Makes the item's affine transformation matrix be equal to the specified
314 * matrix.
315 *
316 * @item: A canvas item.
317 * @affine: An affine transformation matrix.
318 */
319 void
320 sp_canvas_item_affine_absolute (SPCanvasItem *item, NR::Matrix const &affine)
321 {
322 item->xform = affine;
324 if (!(item->flags & SP_CANVAS_ITEM_NEED_AFFINE)) {
325 item->flags |= SP_CANVAS_ITEM_NEED_AFFINE;
326 if (item->parent != NULL) {
327 sp_canvas_item_request_update (item->parent);
328 } else {
329 sp_canvas_request_update (item->canvas);
330 }
331 }
333 item->canvas->need_repick = TRUE;
334 }
336 /**
337 * Convenience function to reorder items in a group's child list.
338 *
339 * This puts the specified link after the "before" link.
340 */
341 static void
342 put_item_after (GList *link, GList *before)
343 {
344 if (link == before)
345 return;
347 SPCanvasGroup *parent = SP_CANVAS_GROUP (SP_CANVAS_ITEM (link->data)->parent);
349 if (before == NULL) {
350 if (link == parent->items) return;
352 link->prev->next = link->next;
354 if (link->next) {
355 link->next->prev = link->prev;
356 } else {
357 parent->last = link->prev;
358 }
360 link->prev = before;
361 link->next = parent->items;
362 link->next->prev = link;
363 parent->items = link;
364 } else {
365 if ((link == parent->last) && (before == parent->last->prev))
366 return;
368 if (link->next)
369 link->next->prev = link->prev;
371 if (link->prev)
372 link->prev->next = link->next;
373 else {
374 parent->items = link->next;
375 parent->items->prev = NULL;
376 }
378 link->prev = before;
379 link->next = before->next;
381 link->prev->next = link;
383 if (link->next)
384 link->next->prev = link;
385 else
386 parent->last = link;
387 }
388 }
391 /**
392 * Raises the item in its parent's stack by the specified number of positions.
393 *
394 * \param item A canvas item.
395 * \param positions Number of steps to raise the item.
396 *
397 * If the number of positions is greater than the distance to the top of the
398 * stack, then the item is put at the top.
399 */
400 void
401 sp_canvas_item_raise (SPCanvasItem *item, int positions)
402 {
403 g_return_if_fail (item != NULL);
404 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
405 g_return_if_fail (positions >= 0);
407 if (!item->parent || positions == 0)
408 return;
410 SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
411 GList *link = g_list_find (parent->items, item);
412 g_assert (link != NULL);
414 GList *before;
415 for (before = link; positions && before; positions--)
416 before = before->next;
418 if (!before)
419 before = parent->last;
421 put_item_after (link, before);
423 redraw_if_visible (item);
424 item->canvas->need_repick = TRUE;
425 }
428 /**
429 * Lowers the item in its parent's stack by the specified number of positions.
430 *
431 * \param item A canvas item.
432 * \param positions Number of steps to lower the item.
433 *
434 * If the number of positions is greater than the distance to the bottom of the
435 * stack, then the item is put at the bottom.
436 **/
437 void
438 sp_canvas_item_lower (SPCanvasItem *item, int positions)
439 {
440 g_return_if_fail (item != NULL);
441 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
442 g_return_if_fail (positions >= 1);
444 if (!item->parent || positions == 0)
445 return;
447 SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
448 GList *link = g_list_find (parent->items, item);
449 g_assert (link != NULL);
451 GList *before;
452 if (link->prev)
453 for (before = link->prev; positions && before; positions--)
454 before = before->prev;
455 else
456 before = NULL;
458 put_item_after (link, before);
460 redraw_if_visible (item);
461 item->canvas->need_repick = TRUE;
462 }
464 /**
465 * Sets visible flag on item and requests a redraw.
466 */
467 void
468 sp_canvas_item_show (SPCanvasItem *item)
469 {
470 g_return_if_fail (item != NULL);
471 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
473 if (item->flags & SP_CANVAS_ITEM_VISIBLE)
474 return;
476 item->flags |= SP_CANVAS_ITEM_VISIBLE;
478 int x0 = (int)(item->x1);
479 int x1 = (int)(item->x2);
480 int y0 = (int)(item->y1);
481 int y1 = (int)(item->y2);
483 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
484 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
485 item->canvas->need_repick = TRUE;
486 }
487 }
489 /**
490 * Clears visible flag on item and requests a redraw.
491 */
492 void
493 sp_canvas_item_hide (SPCanvasItem *item)
494 {
495 g_return_if_fail (item != NULL);
496 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
498 if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
499 return;
501 item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
503 int x0 = (int)(item->x1);
504 int x1 = (int)(item->x2);
505 int y0 = (int)(item->y1);
506 int y1 = (int)(item->y2);
508 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
509 sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
510 item->canvas->need_repick = TRUE;
511 }
512 }
514 /**
515 * Grab item under cursor.
516 *
517 * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE
518 */
519 int
520 sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
521 {
522 g_return_val_if_fail (item != NULL, -1);
523 g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1);
524 g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1);
526 if (item->canvas->grabbed_item)
527 return -1;
529 if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
530 return -1;
532 /* fixme: Top hack (Lauris) */
533 /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
534 /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
535 gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
536 (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
537 NULL, cursor, etime);
539 item->canvas->grabbed_item = item;
540 item->canvas->grabbed_event_mask = event_mask;
541 item->canvas->current_item = item; /* So that events go to the grabbed item */
543 return 0;
544 }
546 /**
547 * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
548 * mouse.
549 *
550 * \param item A canvas item that holds a grab.
551 * \param etime The timestamp for ungrabbing the mouse.
552 */
553 void
554 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
555 {
556 g_return_if_fail (item != NULL);
557 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
559 if (item->canvas->grabbed_item != item)
560 return;
562 item->canvas->grabbed_item = NULL;
564 gdk_pointer_ungrab (etime);
565 }
567 /**
568 * Returns the product of all transformation matrices from the root item down
569 * to the item.
570 */
571 NR::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
572 {
573 g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
575 NR::Matrix affine = NR::identity();
577 while (item) {
578 affine *= item->xform;
579 item = item->parent;
580 }
581 return affine;
582 }
584 /**
585 * Helper that returns true iff item is descendant of parent.
586 */
587 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
588 {
589 while (item) {
590 if (item == parent)
591 return true;
592 item = item->parent;
593 }
595 return false;
596 }
598 /**
599 * Focus canvas, and item under cursor if it is not already focussed.
600 */
601 void
602 sp_canvas_item_grab_focus (SPCanvasItem *item)
603 {
604 g_return_if_fail (item != NULL);
605 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
606 g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
608 SPCanvasItem *focused_item = item->canvas->focused_item;
610 if (focused_item) {
611 GdkEvent ev;
612 ev.focus_change.type = GDK_FOCUS_CHANGE;
613 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
614 ev.focus_change.send_event = FALSE;
615 ev.focus_change.in = FALSE;
617 emit_event (item->canvas, &ev);
618 }
620 item->canvas->focused_item = item;
621 gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
623 if (focused_item) {
624 GdkEvent ev;
625 ev.focus_change.type = GDK_FOCUS_CHANGE;
626 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
627 ev.focus_change.send_event = FALSE;
628 ev.focus_change.in = TRUE;
630 emit_event (item->canvas, &ev);
631 }
632 }
634 /**
635 * Requests that the canvas queue an update for the specified item.
636 *
637 * To be used only by item implementations.
638 */
639 void
640 sp_canvas_item_request_update (SPCanvasItem *item)
641 {
642 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
643 return;
645 item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
647 if (item->parent != NULL) {
648 /* Recurse up the tree */
649 sp_canvas_item_request_update (item->parent);
650 } else {
651 /* Have reached the top of the tree, make sure the update call gets scheduled. */
652 sp_canvas_request_update (item->canvas);
653 }
654 }
656 /**
657 * Returns position of item in group.
658 */
659 gint sp_canvas_item_order (SPCanvasItem * item)
660 {
661 return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
662 }
664 /* SPCanvasGroup */
666 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
667 static void sp_canvas_group_init (SPCanvasGroup *group);
668 static void sp_canvas_group_destroy (GtkObject *object);
670 static void sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
671 static double sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item);
672 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
674 static SPCanvasItemClass *group_parent_class;
676 /**
677 * Registers SPCanvasGroup class with Gtk and returns its type number.
678 */
679 GtkType
680 sp_canvas_group_get_type (void)
681 {
682 static GtkType group_type = 0;
684 if (!group_type) {
685 static GtkTypeInfo const group_info = {
686 "SPCanvasGroup",
687 sizeof (SPCanvasGroup),
688 sizeof (SPCanvasGroupClass),
689 (GtkClassInitFunc) sp_canvas_group_class_init,
690 (GtkObjectInitFunc) sp_canvas_group_init,
691 NULL, NULL, NULL
692 };
694 group_type = gtk_type_unique (sp_canvas_item_get_type (), &group_info);
695 }
697 return group_type;
698 }
700 /**
701 * Class initialization function for SPCanvasGroupClass
702 */
703 static void
704 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
705 {
706 GtkObjectClass *object_class = (GtkObjectClass *) klass;
707 SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
709 group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
711 object_class->destroy = sp_canvas_group_destroy;
713 item_class->update = sp_canvas_group_update;
714 item_class->render = sp_canvas_group_render;
715 item_class->point = sp_canvas_group_point;
716 }
718 /**
719 * Callback. Empty.
720 */
721 static void
722 sp_canvas_group_init (SPCanvasGroup */*group*/)
723 {
724 /* Nothing here */
725 }
727 /**
728 * Callback that destroys all items in group and calls group's virtual
729 * destroy() function.
730 */
731 static void
732 sp_canvas_group_destroy (GtkObject *object)
733 {
734 g_return_if_fail (object != NULL);
735 g_return_if_fail (SP_IS_CANVAS_GROUP (object));
737 SPCanvasGroup const *group = SP_CANVAS_GROUP (object);
739 GList *list = group->items;
740 while (list) {
741 SPCanvasItem *child = (SPCanvasItem *)list->data;
742 list = list->next;
744 gtk_object_destroy (GTK_OBJECT (child));
745 }
747 if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
748 (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
749 }
751 /**
752 * Update handler for canvas groups
753 */
754 static void
755 sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
756 {
757 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
758 NR::ConvexHull corners(NR::Point(0, 0));
759 bool empty=true;
761 for (GList *list = group->items; list; list = list->next) {
762 SPCanvasItem *i = (SPCanvasItem *)list->data;
764 sp_canvas_item_invoke_update (i, affine, flags);
766 if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
767 if (empty) {
768 corners = NR::ConvexHull(NR::Point(i->x1, i->y1));
769 empty = false;
770 } else {
771 corners.add(NR::Point(i->x1, i->y1));
772 }
773 corners.add(NR::Point(i->x2, i->y2));
774 }
775 }
777 NR::Maybe<NR::Rect> const bounds = corners.bounds();
778 if (bounds) {
779 item->x1 = bounds->min()[NR::X];
780 item->y1 = bounds->min()[NR::Y];
781 item->x2 = bounds->max()[NR::X];
782 item->y2 = bounds->max()[NR::Y];
783 } else {
784 // FIXME ?
785 item->x1 = item->x2 = item->y1 = item->y2 = 0;
786 }
787 }
789 /**
790 * Point handler for canvas groups.
791 */
792 static double
793 sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
794 {
795 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
796 double const x = p[NR::X];
797 double const y = p[NR::Y];
798 int x1 = (int)(x - item->canvas->close_enough);
799 int y1 = (int)(y - item->canvas->close_enough);
800 int x2 = (int)(x + item->canvas->close_enough);
801 int y2 = (int)(y + item->canvas->close_enough);
803 double best = 0.0;
804 *actual_item = NULL;
806 double dist = 0.0;
808 for (GList *list = group->items; list; list = list->next) {
809 SPCanvasItem *child = (SPCanvasItem *)list->data;
811 if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
812 SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
814 int has_point;
815 if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
816 dist = sp_canvas_item_invoke_point (child, p, &point_item);
817 has_point = TRUE;
818 } else
819 has_point = FALSE;
821 if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
822 best = dist;
823 *actual_item = point_item;
824 }
825 }
826 }
828 return best;
829 }
831 /**
832 * Renders all visible canvas group items in buf rectangle.
833 */
834 static void
835 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
836 {
837 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
839 for (GList *list = group->items; list; list = list->next) {
840 SPCanvasItem *child = (SPCanvasItem *)list->data;
841 if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
842 if ((child->x1 < buf->rect.x1) &&
843 (child->y1 < buf->rect.y1) &&
844 (child->x2 > buf->rect.x0) &&
845 (child->y2 > buf->rect.y0)) {
846 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
847 SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
848 }
849 }
850 }
851 }
853 /**
854 * Adds an item to a canvas group.
855 */
856 static void
857 group_add (SPCanvasGroup *group, SPCanvasItem *item)
858 {
859 gtk_object_ref (GTK_OBJECT (item));
860 gtk_object_sink (GTK_OBJECT (item));
862 if (!group->items) {
863 group->items = g_list_append (group->items, item);
864 group->last = group->items;
865 } else {
866 group->last = g_list_append (group->last, item)->next;
867 }
869 sp_canvas_item_request_update (item);
870 }
872 /**
873 * Removes an item from a canvas group
874 */
875 static void
876 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
877 {
878 g_return_if_fail (group != NULL);
879 g_return_if_fail (SP_IS_CANVAS_GROUP (group));
880 g_return_if_fail (item != NULL);
882 for (GList *children = group->items; children; children = children->next) {
883 if (children->data == item) {
885 /* Unparent the child */
887 item->parent = NULL;
888 gtk_object_unref (GTK_OBJECT (item));
890 /* Remove it from the list */
892 if (children == group->last) group->last = children->prev;
894 group->items = g_list_remove_link (group->items, children);
895 g_list_free (children);
896 break;
897 }
898 }
899 }
901 /* SPCanvas */
903 static void sp_canvas_class_init (SPCanvasClass *klass);
904 static void sp_canvas_init (SPCanvas *canvas);
905 static void sp_canvas_destroy (GtkObject *object);
907 static void sp_canvas_realize (GtkWidget *widget);
908 static void sp_canvas_unrealize (GtkWidget *widget);
910 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
911 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
913 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
914 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
915 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
916 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
917 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
918 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
919 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
920 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
922 static GtkWidgetClass *canvas_parent_class;
924 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb);
925 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb);
926 static void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val);
927 static int do_update (SPCanvas *canvas);
929 /**
930 * Registers the SPCanvas class if necessary, and returns the type ID
931 * associated to it.
932 *
933 * \return The type ID of the SPCanvas class.
934 **/
935 GtkType
936 sp_canvas_get_type (void)
937 {
938 static GtkType canvas_type = 0;
940 if (!canvas_type) {
941 static GtkTypeInfo const canvas_info = {
942 "SPCanvas",
943 sizeof (SPCanvas),
944 sizeof (SPCanvasClass),
945 (GtkClassInitFunc) sp_canvas_class_init,
946 (GtkObjectInitFunc) sp_canvas_init,
947 NULL, NULL, NULL
948 };
950 canvas_type = gtk_type_unique (GTK_TYPE_WIDGET, &canvas_info);
951 }
953 return canvas_type;
954 }
956 /**
957 * Class initialization function for SPCanvasClass.
958 */
959 static void
960 sp_canvas_class_init (SPCanvasClass *klass)
961 {
962 GtkObjectClass *object_class = (GtkObjectClass *) klass;
963 GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
965 canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
967 object_class->destroy = sp_canvas_destroy;
969 widget_class->realize = sp_canvas_realize;
970 widget_class->unrealize = sp_canvas_unrealize;
971 widget_class->size_request = sp_canvas_size_request;
972 widget_class->size_allocate = sp_canvas_size_allocate;
973 widget_class->button_press_event = sp_canvas_button;
974 widget_class->button_release_event = sp_canvas_button;
975 widget_class->motion_notify_event = sp_canvas_motion;
976 widget_class->scroll_event = sp_canvas_scroll;
977 widget_class->expose_event = sp_canvas_expose;
978 widget_class->key_press_event = sp_canvas_key;
979 widget_class->key_release_event = sp_canvas_key;
980 widget_class->enter_notify_event = sp_canvas_crossing;
981 widget_class->leave_notify_event = sp_canvas_crossing;
982 widget_class->focus_in_event = sp_canvas_focus_in;
983 widget_class->focus_out_event = sp_canvas_focus_out;
984 }
986 /**
987 * Callback: object initialization for SPCanvas.
988 */
989 static void
990 sp_canvas_init (SPCanvas *canvas)
991 {
992 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
993 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
994 GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
996 canvas->pick_event.type = GDK_LEAVE_NOTIFY;
997 canvas->pick_event.crossing.x = 0;
998 canvas->pick_event.crossing.y = 0;
1000 /* Create the root item as a special case */
1001 canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
1002 canvas->root->canvas = canvas;
1004 gtk_object_ref (GTK_OBJECT (canvas->root));
1005 gtk_object_sink (GTK_OBJECT (canvas->root));
1007 canvas->need_repick = TRUE;
1009 // See comment at in sp-canvas.h.
1010 canvas->gen_all_enter_events = false;
1012 canvas->tiles=NULL;
1013 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1014 canvas->tileH=canvas->tileV=0;
1016 canvas->forced_redraw_count = 0;
1017 canvas->forced_redraw_limit = -1;
1019 #if ENABLE_LCMS
1020 canvas->enable_cms_display_adj = false;
1021 canvas->cms_key = new Glib::ustring("");
1022 #endif // ENABLE_LCMS
1024 canvas->is_scrolling = false;
1026 }
1028 /**
1029 * Convenience function to remove the idle handler of a canvas.
1030 */
1031 static void
1032 remove_idle (SPCanvas *canvas)
1033 {
1034 if (canvas->idle_id) {
1035 gtk_idle_remove (canvas->idle_id);
1036 canvas->idle_id = 0;
1037 }
1038 }
1040 /*
1041 * Removes the transient state of the canvas (idle handler, grabs).
1042 */
1043 static void
1044 shutdown_transients (SPCanvas *canvas)
1045 {
1046 /* We turn off the need_redraw flag, since if the canvas is mapped again
1047 * it will request a redraw anyways. We do not turn off the need_update
1048 * flag, though, because updates are not queued when the canvas remaps
1049 * itself.
1050 */
1051 if (canvas->need_redraw) {
1052 canvas->need_redraw = FALSE;
1053 }
1054 if ( canvas->tiles ) g_free(canvas->tiles);
1055 canvas->tiles=NULL;
1056 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1057 canvas->tileH=canvas->tileV=0;
1059 if (canvas->grabbed_item) {
1060 canvas->grabbed_item = NULL;
1061 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1062 }
1064 remove_idle (canvas);
1065 }
1067 /**
1068 * Destroy handler for SPCanvas.
1069 */
1070 static void
1071 sp_canvas_destroy (GtkObject *object)
1072 {
1073 SPCanvas *canvas = SP_CANVAS (object);
1075 if (canvas->root) {
1076 gtk_object_unref (GTK_OBJECT (canvas->root));
1077 canvas->root = NULL;
1078 }
1080 shutdown_transients (canvas);
1082 if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1083 (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1084 }
1086 /**
1087 * Returns new canvas as widget.
1088 */
1089 GtkWidget *
1090 sp_canvas_new_aa (void)
1091 {
1092 SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1094 return (GtkWidget *) canvas;
1095 }
1097 /**
1098 * The canvas widget's realize callback.
1099 */
1100 static void
1101 sp_canvas_realize (GtkWidget *widget)
1102 {
1103 SPCanvas *canvas = SP_CANVAS (widget);
1105 GdkWindowAttr attributes;
1106 attributes.window_type = GDK_WINDOW_CHILD;
1107 attributes.x = widget->allocation.x;
1108 attributes.y = widget->allocation.y;
1109 attributes.width = widget->allocation.width;
1110 attributes.height = widget->allocation.height;
1111 attributes.wclass = GDK_INPUT_OUTPUT;
1112 attributes.visual = gdk_rgb_get_visual ();
1113 attributes.colormap = gdk_rgb_get_cmap ();
1114 attributes.event_mask = (gtk_widget_get_events (widget) |
1115 GDK_EXPOSURE_MASK |
1116 GDK_BUTTON_PRESS_MASK |
1117 GDK_BUTTON_RELEASE_MASK |
1118 GDK_POINTER_MOTION_MASK |
1119 GDK_PROXIMITY_IN_MASK |
1120 GDK_PROXIMITY_OUT_MASK |
1121 GDK_KEY_PRESS_MASK |
1122 GDK_KEY_RELEASE_MASK |
1123 GDK_ENTER_NOTIFY_MASK |
1124 GDK_LEAVE_NOTIFY_MASK |
1125 GDK_FOCUS_CHANGE_MASK);
1126 gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1128 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1129 gdk_window_set_user_data (widget->window, widget);
1131 if ( prefs_get_int_attribute ("options.useextinput", "value", 1) )
1132 gtk_widget_set_events(widget, attributes.event_mask);
1134 widget->style = gtk_style_attach (widget->style, widget->window);
1136 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1138 canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1139 }
1141 /**
1142 * The canvas widget's unrealize callback.
1143 */
1144 static void
1145 sp_canvas_unrealize (GtkWidget *widget)
1146 {
1147 SPCanvas *canvas = SP_CANVAS (widget);
1149 canvas->current_item = NULL;
1150 canvas->grabbed_item = NULL;
1151 canvas->focused_item = NULL;
1153 shutdown_transients (canvas);
1155 gdk_gc_destroy (canvas->pixmap_gc);
1156 canvas->pixmap_gc = NULL;
1158 if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1159 (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1160 }
1162 /**
1163 * The canvas widget's size_request callback.
1164 */
1165 static void
1166 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1167 {
1168 static_cast<void>(SP_CANVAS (widget));
1170 req->width = 256;
1171 req->height = 256;
1172 }
1174 /**
1175 * The canvas widget's size_allocate callback.
1176 */
1177 static void
1178 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1179 {
1180 SPCanvas *canvas = SP_CANVAS (widget);
1182 /* Schedule redraw of new region */
1183 sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1184 if (allocation->width > widget->allocation.width) {
1185 sp_canvas_request_redraw (canvas,
1186 canvas->x0 + widget->allocation.width,
1187 0,
1188 canvas->x0 + allocation->width,
1189 canvas->y0 + allocation->height);
1190 }
1191 if (allocation->height > widget->allocation.height) {
1192 sp_canvas_request_redraw (canvas,
1193 0,
1194 canvas->y0 + widget->allocation.height,
1195 canvas->x0 + allocation->width,
1196 canvas->y0 + allocation->height);
1197 }
1199 widget->allocation = *allocation;
1201 if (GTK_WIDGET_REALIZED (widget)) {
1202 gdk_window_move_resize (widget->window,
1203 widget->allocation.x, widget->allocation.y,
1204 widget->allocation.width, widget->allocation.height);
1205 }
1206 }
1208 /**
1209 * Helper that emits an event for an item in the canvas, be it the current
1210 * item, grabbed item, or focused item, as appropriate.
1211 */
1212 static int
1213 emit_event (SPCanvas *canvas, GdkEvent *event)
1214 {
1215 guint mask;
1217 if (canvas->grabbed_item) {
1218 switch (event->type) {
1219 case GDK_ENTER_NOTIFY:
1220 mask = GDK_ENTER_NOTIFY_MASK;
1221 break;
1222 case GDK_LEAVE_NOTIFY:
1223 mask = GDK_LEAVE_NOTIFY_MASK;
1224 break;
1225 case GDK_MOTION_NOTIFY:
1226 mask = GDK_POINTER_MOTION_MASK;
1227 break;
1228 case GDK_BUTTON_PRESS:
1229 case GDK_2BUTTON_PRESS:
1230 case GDK_3BUTTON_PRESS:
1231 mask = GDK_BUTTON_PRESS_MASK;
1232 break;
1233 case GDK_BUTTON_RELEASE:
1234 mask = GDK_BUTTON_RELEASE_MASK;
1235 break;
1236 case GDK_KEY_PRESS:
1237 mask = GDK_KEY_PRESS_MASK;
1238 break;
1239 case GDK_KEY_RELEASE:
1240 mask = GDK_KEY_RELEASE_MASK;
1241 break;
1242 case GDK_SCROLL:
1243 mask = GDK_SCROLL;
1244 break;
1245 default:
1246 mask = 0;
1247 break;
1248 }
1250 if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1251 }
1253 /* Convert to world coordinates -- we have two cases because of diferent
1254 * offsets of the fields in the event structures.
1255 */
1257 GdkEvent ev = *event;
1259 switch (ev.type) {
1260 case GDK_ENTER_NOTIFY:
1261 case GDK_LEAVE_NOTIFY:
1262 ev.crossing.x += canvas->x0;
1263 ev.crossing.y += canvas->y0;
1264 break;
1265 case GDK_MOTION_NOTIFY:
1266 case GDK_BUTTON_PRESS:
1267 case GDK_2BUTTON_PRESS:
1268 case GDK_3BUTTON_PRESS:
1269 case GDK_BUTTON_RELEASE:
1270 ev.motion.x += canvas->x0;
1271 ev.motion.y += canvas->y0;
1272 break;
1273 default:
1274 break;
1275 }
1277 /* Choose where we send the event */
1279 /* canvas->current_item becomes NULL in some cases under Win32
1280 ** (e.g. if the pointer leaves the window). So this is a hack that
1281 ** Lauris applied to SP to get around the problem.
1282 */
1283 SPCanvasItem* item = NULL;
1284 if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1285 item = canvas->grabbed_item;
1286 } else {
1287 item = canvas->current_item;
1288 }
1290 if (canvas->focused_item &&
1291 ((event->type == GDK_KEY_PRESS) ||
1292 (event->type == GDK_KEY_RELEASE) ||
1293 (event->type == GDK_FOCUS_CHANGE))) {
1294 item = canvas->focused_item;
1295 }
1297 /* The event is propagated up the hierarchy (for if someone connected to
1298 * a group instead of a leaf event), and emission is stopped if a
1299 * handler returns TRUE, just like for GtkWidget events.
1300 */
1302 gint finished = FALSE;
1304 while (item && !finished) {
1305 gtk_object_ref (GTK_OBJECT (item));
1306 gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1307 SPCanvasItem *parent = item->parent;
1308 gtk_object_unref (GTK_OBJECT (item));
1309 item = parent;
1310 }
1312 return finished;
1313 }
1315 /**
1316 * Helper that re-picks the current item in the canvas, based on the event's
1317 * coordinates and emits enter/leave events for items as appropriate.
1318 */
1319 static int
1320 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1321 {
1322 int button_down = 0;
1323 double x, y;
1325 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1326 return FALSE;
1328 int retval = FALSE;
1330 if (canvas->gen_all_enter_events == false) {
1331 // If a button is down, we'll perform enter and leave events on the
1332 // current item, but not enter on any other item. This is more or
1333 // less like X pointer grabbing for canvas items.
1334 //
1335 button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1336 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1338 if (!button_down) canvas->left_grabbed_item = FALSE;
1339 }
1341 /* Save the event in the canvas. This is used to synthesize enter and
1342 * leave events in case the current item changes. It is also used to
1343 * re-pick the current item if the current one gets deleted. Also,
1344 * synthesize an enter event.
1345 */
1346 if (event != &canvas->pick_event) {
1347 if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1348 /* these fields have the same offsets in both types of events */
1350 canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY;
1351 canvas->pick_event.crossing.window = event->motion.window;
1352 canvas->pick_event.crossing.send_event = event->motion.send_event;
1353 canvas->pick_event.crossing.subwindow = NULL;
1354 canvas->pick_event.crossing.x = event->motion.x;
1355 canvas->pick_event.crossing.y = event->motion.y;
1356 canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL;
1357 canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR;
1358 canvas->pick_event.crossing.focus = FALSE;
1359 canvas->pick_event.crossing.state = event->motion.state;
1361 /* these fields don't have the same offsets in both types of events */
1363 if (event->type == GDK_MOTION_NOTIFY) {
1364 canvas->pick_event.crossing.x_root = event->motion.x_root;
1365 canvas->pick_event.crossing.y_root = event->motion.y_root;
1366 } else {
1367 canvas->pick_event.crossing.x_root = event->button.x_root;
1368 canvas->pick_event.crossing.y_root = event->button.y_root;
1369 }
1370 } else {
1371 canvas->pick_event = *event;
1372 }
1373 }
1375 /* Don't do anything else if this is a recursive call */
1376 if (canvas->in_repick) return retval;
1378 /* LeaveNotify means that there is no current item, so we don't look for one */
1379 if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1380 /* these fields don't have the same offsets in both types of events */
1382 if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1383 x = canvas->pick_event.crossing.x;
1384 y = canvas->pick_event.crossing.y;
1385 } else {
1386 x = canvas->pick_event.motion.x;
1387 y = canvas->pick_event.motion.y;
1388 }
1390 /* world coords */
1391 x += canvas->x0;
1392 y += canvas->y0;
1394 /* find the closest item */
1395 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1396 sp_canvas_item_invoke_point (canvas->root, NR::Point(x, y), &canvas->new_current_item);
1397 } else {
1398 canvas->new_current_item = NULL;
1399 }
1400 } else {
1401 canvas->new_current_item = NULL;
1402 }
1404 if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1405 return retval; /* current item did not change */
1406 }
1408 /* Synthesize events for old and new current items */
1410 if ((canvas->new_current_item != canvas->current_item)
1411 && (canvas->current_item != NULL)
1412 && !canvas->left_grabbed_item) {
1413 GdkEvent new_event;
1414 SPCanvasItem *item;
1416 item = canvas->current_item;
1418 new_event = canvas->pick_event;
1419 new_event.type = GDK_LEAVE_NOTIFY;
1421 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1422 new_event.crossing.subwindow = NULL;
1423 canvas->in_repick = TRUE;
1424 retval = emit_event (canvas, &new_event);
1425 canvas->in_repick = FALSE;
1426 }
1428 if (canvas->gen_all_enter_events == false) {
1429 // new_current_item may have been set to NULL during the call to
1430 // emit_event() above
1431 if ((canvas->new_current_item != canvas->current_item) && button_down) {
1432 canvas->left_grabbed_item = TRUE;
1433 return retval;
1434 }
1435 }
1437 /* Handle the rest of cases */
1439 canvas->left_grabbed_item = FALSE;
1440 canvas->current_item = canvas->new_current_item;
1442 if (canvas->current_item != NULL) {
1443 GdkEvent new_event;
1445 new_event = canvas->pick_event;
1446 new_event.type = GDK_ENTER_NOTIFY;
1447 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1448 new_event.crossing.subwindow = NULL;
1449 retval = emit_event (canvas, &new_event);
1450 }
1452 return retval;
1453 }
1455 /**
1456 * Button event handler for the canvas.
1457 */
1458 static gint
1459 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1460 {
1461 SPCanvas *canvas = SP_CANVAS (widget);
1463 int retval = FALSE;
1465 /* dispatch normally regardless of the event's window if an item has
1466 has a pointer grab in effect */
1467 if (!canvas->grabbed_item &&
1468 event->window != SP_CANVAS_WINDOW (canvas))
1469 return retval;
1471 int mask;
1472 switch (event->button) {
1473 case 1:
1474 mask = GDK_BUTTON1_MASK;
1475 break;
1476 case 2:
1477 mask = GDK_BUTTON2_MASK;
1478 break;
1479 case 3:
1480 mask = GDK_BUTTON3_MASK;
1481 break;
1482 case 4:
1483 mask = GDK_BUTTON4_MASK;
1484 break;
1485 case 5:
1486 mask = GDK_BUTTON5_MASK;
1487 break;
1488 default:
1489 mask = 0;
1490 }
1492 switch (event->type) {
1493 case GDK_BUTTON_PRESS:
1494 case GDK_2BUTTON_PRESS:
1495 case GDK_3BUTTON_PRESS:
1496 /* Pick the current item as if the button were not pressed, and
1497 * then process the event.
1498 */
1499 canvas->state = event->state;
1500 pick_current_item (canvas, (GdkEvent *) event);
1501 canvas->state ^= mask;
1502 retval = emit_event (canvas, (GdkEvent *) event);
1503 break;
1505 case GDK_BUTTON_RELEASE:
1506 /* Process the event as if the button were pressed, then repick
1507 * after the button has been released
1508 */
1509 canvas->state = event->state;
1510 retval = emit_event (canvas, (GdkEvent *) event);
1511 event->state ^= mask;
1512 canvas->state = event->state;
1513 pick_current_item (canvas, (GdkEvent *) event);
1514 event->state ^= mask;
1515 break;
1517 default:
1518 g_assert_not_reached ();
1519 }
1521 return retval;
1522 }
1524 /**
1525 * Scroll event handler for the canvas.
1526 *
1527 * \todo FIXME: generate motion events to re-select items.
1528 */
1529 static gint
1530 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1531 {
1532 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1533 }
1535 /**
1536 * Motion event handler for the canvas.
1537 */
1538 static int
1539 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1540 {
1541 SPCanvas *canvas = SP_CANVAS (widget);
1543 if (event->window != SP_CANVAS_WINDOW (canvas))
1544 return FALSE;
1546 if (canvas->pixmap_gc == NULL) // canvas being deleted
1547 return FALSE;
1549 if (canvas->grabbed_event_mask & GDK_POINTER_MOTION_HINT_MASK) {
1550 gint x, y;
1551 gdk_window_get_pointer (widget->window, &x, &y, NULL);
1552 event->x = x;
1553 event->y = y;
1554 }
1556 canvas->state = event->state;
1557 pick_current_item (canvas, (GdkEvent *) event);
1559 return emit_event (canvas, (GdkEvent *) event);
1560 }
1562 static void
1563 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)
1564 {
1565 GtkWidget *widget = GTK_WIDGET (canvas);
1567 SPCanvasBuf buf;
1568 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1569 buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1570 } else {
1571 buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1572 }
1574 // Mark the region clean
1575 sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1577 buf.buf_rowstride = sw * 3; // CAIRO FIXME: for cairo output, the buffer must be RGB unpacked, i.e. sw * 4
1578 buf.rect.x0 = x0;
1579 buf.rect.y0 = y0;
1580 buf.rect.x1 = x1;
1581 buf.rect.y1 = y1;
1582 buf.visible_rect.x0 = draw_x1;
1583 buf.visible_rect.y0 = draw_y1;
1584 buf.visible_rect.x1 = draw_x2;
1585 buf.visible_rect.y1 = draw_y2;
1586 GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1587 buf.bg_color = (((color->red & 0xff00) << 8)
1588 | (color->green & 0xff00)
1589 | (color->blue >> 8));
1590 buf.is_empty = true;
1592 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1593 SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1594 }
1596 #if ENABLE_LCMS
1597 cmsHTRANSFORM transf = 0;
1598 long long int fromDisplay = prefs_get_int_attribute_limited( "options.displayprofile", "from_display", 0, 0, 1 );
1599 if ( fromDisplay ) {
1600 transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" );
1601 } else {
1602 transf = Inkscape::colorprofile_get_display_transform();
1603 }
1604 #endif // ENABLE_LCMS
1606 if (buf.is_empty) {
1607 #if ENABLE_LCMS
1608 if ( transf && canvas->enable_cms_display_adj ) {
1609 cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1610 }
1611 #endif // ENABLE_LCMS
1612 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1613 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1614 canvas->pixmap_gc,
1615 TRUE,
1616 x0 - canvas->x0, y0 - canvas->y0,
1617 x1 - x0, y1 - y0);
1618 } else {
1619 /*
1620 // CAIRO FIXME: after SPCanvasBuf is made 32bpp throughout, this rgb_draw below can be replaced with the below.
1621 // Why this must not be done currently:
1622 // - all canvas items (handles, nodes etc) paint themselves assuming 24bpp
1623 // - cairo assumes bgra, but we have rgba, so r and b get swapped (until we paint all with cairo too)
1624 // - it does not seem to be any faster; in fact since with 32bpp, buf contains less pixels,
1625 // we need more bufs to paint a given area and as a result it's even a bit slower
1627 cairo_surface_t* cst = cairo_image_surface_create_for_data (
1628 buf.buf,
1629 CAIRO_FORMAT_RGB24, // unpacked, i.e. 32 bits! one byte is unused
1630 x1 - x0, y1 - y0,
1631 buf.buf_rowstride
1632 );
1633 cairo_t *ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1634 cairo_set_source_surface (ct, cst, x0 - canvas->x0, y0 - canvas->y0);
1635 cairo_paint (ct);
1636 cairo_destroy (ct);
1637 cairo_surface_finish (cst);
1638 cairo_surface_destroy (cst);
1639 */
1641 #if ENABLE_LCMS
1642 if ( transf && canvas->enable_cms_display_adj ) {
1643 for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1644 guchar* p = buf.buf + (sw * 3) * yy;
1645 cmsDoTransform( transf, p, p, (x1 - x0) );
1646 }
1647 }
1648 #endif // ENABLE_LCMS
1650 gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1651 canvas->pixmap_gc,
1652 x0 - canvas->x0, y0 - canvas->y0,
1653 x1 - x0, y1 - y0,
1654 GDK_RGB_DITHER_MAX,
1655 buf.buf,
1656 sw * 3,
1657 x0 - canvas->x0, y0 - canvas->y0);
1658 }
1660 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1661 nr_pixelstore_256K_free (buf.buf);
1662 } else {
1663 nr_pixelstore_1M_free (buf.buf);
1664 }
1665 }
1667 struct PaintRectSetup {
1668 SPCanvas* canvas;
1669 NRRectL big_rect;
1670 GTimeVal start_time;
1671 int max_pixels;
1672 NR::Point mouse_loc;
1673 };
1675 /**
1676 * Paint the given rect, recursively subdividing the region until it is the size of a single
1677 * buffer.
1678 *
1679 * @return true if the drawing completes
1680 */
1681 static int
1682 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1683 {
1684 GTimeVal now;
1685 g_get_current_time (&now);
1687 glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1688 + (now.tv_usec - setup->start_time.tv_usec);
1690 // Allow only very fast buffers to be run together;
1691 // as soon as the total redraw time exceeds 1ms, cancel;
1692 // this returns control to the idle loop and allows Inkscape to process user input
1693 // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1694 // it will get back and finish painting what remains to paint.
1695 if (elapsed > 1000) {
1697 // Interrupting redraw isn't always good.
1698 // For example, when you drag one node of a big path, only the buffer containing
1699 // the mouse cursor will be redrawn again and again, and the rest of the path
1700 // will remain stale because Inkscape never has enough idle time to redraw all
1701 // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1702 // If this limit is set, and if we have aborted redraw more times than is allowed,
1703 // interrupting is blocked and we're forced to redraw full screen once
1704 // (after which we can again interrupt forced_redraw_limit times).
1705 if (setup->canvas->forced_redraw_limit < 0 ||
1706 setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1708 if (setup->canvas->forced_redraw_limit != -1) {
1709 setup->canvas->forced_redraw_count++;
1710 }
1712 return false;
1713 }
1714 }
1716 // Find the optimal buffer dimensions
1717 int bw = this_rect.x1 - this_rect.x0;
1718 int bh = this_rect.y1 - this_rect.y0;
1719 if ((bw < 1) || (bh < 1))
1720 return 0;
1722 if (bw * bh < setup->max_pixels) {
1723 // We are small enough
1724 sp_canvas_paint_single_buffer (setup->canvas,
1725 this_rect.x0, this_rect.y0,
1726 this_rect.x1, this_rect.y1,
1727 setup->big_rect.x0, setup->big_rect.y0,
1728 setup->big_rect.x1, setup->big_rect.y1, bw);
1729 return 1;
1730 }
1732 NRRectL lo = this_rect;
1733 NRRectL hi = this_rect;
1735 /*
1736 This test determines the redraw strategy:
1738 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1739 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1740 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1741 and seems to be faster for drawings with many smaller objects at zoom-out.
1743 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1744 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1745 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1746 faster.
1748 The default for now is the strips mode.
1749 */
1750 if (bw < bh || bh < 2 * TILE_SIZE) {
1751 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1752 int mid = ((long long) this_rect.x0 + (long long) this_rect.x1) / 2;
1753 // Make sure that mid lies on a tile boundary
1754 mid = (mid / TILE_SIZE) * TILE_SIZE;
1756 lo.x1 = mid;
1757 hi.x0 = mid;
1759 if (setup->mouse_loc[NR::X] < mid) {
1760 // Always paint towards the mouse first
1761 return sp_canvas_paint_rect_internal(setup, lo)
1762 && sp_canvas_paint_rect_internal(setup, hi);
1763 } else {
1764 return sp_canvas_paint_rect_internal(setup, hi)
1765 && sp_canvas_paint_rect_internal(setup, lo);
1766 }
1767 } else {
1768 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1769 int mid = ((long long) this_rect.y0 + (long long) this_rect.y1) / 2;
1770 // Make sure that mid lies on a tile boundary
1771 mid = (mid / TILE_SIZE) * TILE_SIZE;
1773 lo.y1 = mid;
1774 hi.y0 = mid;
1776 if (setup->mouse_loc[NR::Y] < mid) {
1777 // Always paint towards the mouse first
1778 return sp_canvas_paint_rect_internal(setup, lo)
1779 && sp_canvas_paint_rect_internal(setup, hi);
1780 } else {
1781 return sp_canvas_paint_rect_internal(setup, hi)
1782 && sp_canvas_paint_rect_internal(setup, lo);
1783 }
1784 }
1785 }
1788 /**
1789 * Helper that draws a specific rectangular part of the canvas.
1790 *
1791 * @return true if the rectangle painting succeeds.
1792 */
1793 static bool
1794 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1795 {
1796 g_return_val_if_fail (!canvas->need_update, false);
1798 NRRectL rect;
1799 rect.x0 = xx0;
1800 rect.x1 = xx1;
1801 rect.y0 = yy0;
1802 rect.y1 = yy1;
1804 // Clip rect-to-draw by the current visible area
1805 rect.x0 = MAX (rect.x0, canvas->x0);
1806 rect.y0 = MAX (rect.y0, canvas->y0);
1807 rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1808 rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1810 #ifdef DEBUG_REDRAW
1811 // paint the area to redraw yellow
1812 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1813 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1814 canvas->pixmap_gc,
1815 TRUE,
1816 rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1817 rect.x1 - rect.x0, rect.y1 - rect.y0);
1818 #endif
1820 PaintRectSetup setup;
1822 setup.canvas = canvas;
1823 setup.big_rect = rect;
1825 // Save the mouse location
1826 gint x, y;
1827 gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1828 setup.mouse_loc = sp_canvas_window_to_world (canvas, NR::Point(x,y));
1830 // CAIRO FIXME: the sw/sh calculations below all assume 24bpp, need fixing for 32bpp
1831 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1832 // use 256K as a compromise to not slow down gradients
1833 // 256K is the cached buffer and we need 3 channels
1834 setup.max_pixels = 87381; // 256K/3
1835 } else {
1836 // paths only, so 1M works faster
1837 // 1M is the cached buffer and we need 3 channels
1838 setup.max_pixels = 349525;
1839 }
1841 // Start the clock
1842 g_get_current_time(&(setup.start_time));
1844 // Go
1845 return sp_canvas_paint_rect_internal(&setup, rect);
1846 }
1848 /**
1849 * Force a full redraw after a specified number of interrupted redraws
1850 */
1851 void
1852 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1853 g_return_if_fail(canvas != NULL);
1855 canvas->forced_redraw_limit = count;
1856 canvas->forced_redraw_count = 0;
1857 }
1859 /**
1860 * End forced full redraw requests
1861 */
1862 void
1863 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1864 g_return_if_fail(canvas != NULL);
1866 canvas->forced_redraw_limit = -1;
1867 }
1869 /**
1870 * The canvas widget's expose callback.
1871 */
1872 static gint
1873 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1874 {
1875 SPCanvas *canvas = SP_CANVAS (widget);
1877 if (!GTK_WIDGET_DRAWABLE (widget) ||
1878 (event->window != SP_CANVAS_WINDOW (canvas)))
1879 return FALSE;
1881 int n_rects;
1882 GdkRectangle *rects;
1883 gdk_region_get_rectangles (event->region, &rects, &n_rects);
1885 for (int i = 0; i < n_rects; i++) {
1886 NRRectL rect;
1888 rect.x0 = rects[i].x + canvas->x0;
1889 rect.y0 = rects[i].y + canvas->y0;
1890 rect.x1 = rect.x0 + rects[i].width;
1891 rect.y1 = rect.y0 + rects[i].height;
1893 sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1894 }
1896 if (n_rects > 0)
1897 g_free (rects);
1899 return FALSE;
1900 }
1902 /**
1903 * The canvas widget's keypress callback.
1904 */
1905 static gint
1906 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1907 {
1908 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1909 }
1911 /**
1912 * Crossing event handler for the canvas.
1913 */
1914 static gint
1915 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1916 {
1917 SPCanvas *canvas = SP_CANVAS (widget);
1919 if (event->window != SP_CANVAS_WINDOW (canvas))
1920 return FALSE;
1922 canvas->state = event->state;
1923 return pick_current_item (canvas, (GdkEvent *) event);
1924 }
1926 /**
1927 * Focus in handler for the canvas.
1928 */
1929 static gint
1930 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
1931 {
1932 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1934 SPCanvas *canvas = SP_CANVAS (widget);
1936 if (canvas->focused_item) {
1937 return emit_event (canvas, (GdkEvent *) event);
1938 } else {
1939 return FALSE;
1940 }
1941 }
1943 /**
1944 * Focus out handler for the canvas.
1945 */
1946 static gint
1947 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
1948 {
1949 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1951 SPCanvas *canvas = SP_CANVAS (widget);
1953 if (canvas->focused_item)
1954 return emit_event (canvas, (GdkEvent *) event);
1955 else
1956 return FALSE;
1957 }
1959 /**
1960 * Helper that repaints the areas in the canvas that need it.
1961 *
1962 * @return true if all the dirty parts have been redrawn
1963 */
1964 static int
1965 paint (SPCanvas *canvas)
1966 {
1967 if (canvas->need_update) {
1968 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
1969 canvas->need_update = FALSE;
1970 }
1972 if (!canvas->need_redraw)
1973 return TRUE;
1975 Gdk::Region to_paint;
1977 for (int j=canvas->tTop; j<canvas->tBottom; j++) {
1978 for (int i=canvas->tLeft; i<canvas->tRight; i++) {
1979 int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
1981 if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
1982 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
1983 TILE_SIZE, TILE_SIZE));
1984 }
1986 }
1987 }
1989 if (!to_paint.empty()) {
1990 Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
1991 typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
1992 for (Iter i=rect.begin(); i != rect.end(); ++i) {
1993 int x0 = (*i).get_x();
1994 int y0 = (*i).get_y();
1995 int x1 = x0 + (*i).get_width();
1996 int y1 = y0 + (*i).get_height();
1997 if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
1998 // Aborted
1999 return FALSE;
2000 };
2001 }
2002 }
2004 canvas->need_redraw = FALSE;
2006 // we've had a full unaborted redraw, reset the full redraw counter
2007 if (canvas->forced_redraw_limit != -1) {
2008 canvas->forced_redraw_count = 0;
2009 }
2011 return TRUE;
2012 }
2014 /**
2015 * Helper that invokes update, paint, and repick on canvas.
2016 */
2017 static int
2018 do_update (SPCanvas *canvas)
2019 {
2020 if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop durring interrupted display!
2021 return TRUE;
2023 /* Cause the update if necessary */
2024 if (canvas->need_update) {
2025 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2026 canvas->need_update = FALSE;
2027 }
2029 /* Paint if able to */
2030 if (GTK_WIDGET_DRAWABLE (canvas)) {
2031 return paint (canvas);
2032 }
2034 /* Pick new current item */
2035 while (canvas->need_repick) {
2036 canvas->need_repick = FALSE;
2037 pick_current_item (canvas, &canvas->pick_event);
2038 }
2040 return TRUE;
2041 }
2043 /**
2044 * Idle handler for the canvas that deals with pending updates and redraws.
2045 */
2046 static gint
2047 idle_handler (gpointer data)
2048 {
2049 GDK_THREADS_ENTER ();
2051 SPCanvas *canvas = SP_CANVAS (data);
2053 int const ret = do_update (canvas);
2055 if (ret) {
2056 /* Reset idle id */
2057 canvas->idle_id = 0;
2058 }
2060 GDK_THREADS_LEAVE ();
2062 return !ret;
2063 }
2065 /**
2066 * Convenience function to add an idle handler to a canvas.
2067 */
2068 static void
2069 add_idle (SPCanvas *canvas)
2070 {
2071 if (canvas->idle_id != 0)
2072 return;
2074 canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2075 }
2077 /**
2078 * Returns the root group of the specified canvas.
2079 */
2080 SPCanvasGroup *
2081 sp_canvas_root (SPCanvas *canvas)
2082 {
2083 g_return_val_if_fail (canvas != NULL, NULL);
2084 g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2086 return SP_CANVAS_GROUP (canvas->root);
2087 }
2089 /**
2090 * Scrolls canvas to specific position (cx and cy are measured in screen pixels)
2091 */
2092 void
2093 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2094 {
2095 g_return_if_fail (canvas != NULL);
2096 g_return_if_fail (SP_IS_CANVAS (canvas));
2098 int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels)
2099 int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do!
2100 int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the
2101 int dy = iy - canvas->y0; // canvas w.r.t its previous position
2103 canvas->dx0 = cx; // here the 'd' stands for double, not delta!
2104 canvas->dy0 = cy;
2105 canvas->x0 = ix;
2106 canvas->y0 = iy;
2108 sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2110 if (!clear) {
2111 // scrolling without zoom; redraw only the newly exposed areas
2112 if ((dx != 0) || (dy != 0)) {
2113 canvas->is_scrolling = is_scrolling;
2114 if (GTK_WIDGET_REALIZED (canvas)) {
2115 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2116 }
2117 }
2118 } else {
2119 // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2120 }
2121 }
2123 /**
2124 * Updates canvas if necessary.
2125 */
2126 void
2127 sp_canvas_update_now (SPCanvas *canvas)
2128 {
2129 g_return_if_fail (canvas != NULL);
2130 g_return_if_fail (SP_IS_CANVAS (canvas));
2132 if (!(canvas->need_update ||
2133 canvas->need_redraw))
2134 return;
2136 do_update (canvas);
2137 }
2139 /**
2140 * Update callback for canvas widget.
2141 */
2142 static void
2143 sp_canvas_request_update (SPCanvas *canvas)
2144 {
2145 canvas->need_update = TRUE;
2146 add_idle (canvas);
2147 }
2149 /**
2150 * Forces redraw of rectangular canvas area.
2151 */
2152 void
2153 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2154 {
2155 NRRectL bbox;
2156 NRRectL visible;
2157 NRRectL clip;
2159 g_return_if_fail (canvas != NULL);
2160 g_return_if_fail (SP_IS_CANVAS (canvas));
2162 if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2163 if ((x0 >= x1) || (y0 >= y1)) return;
2165 bbox.x0 = x0;
2166 bbox.y0 = y0;
2167 bbox.x1 = x1;
2168 bbox.y1 = y1;
2170 visible.x0 = canvas->x0;
2171 visible.y0 = canvas->y0;
2172 visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2173 visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2175 nr_rect_l_intersect (&clip, &bbox, &visible);
2177 sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2178 add_idle (canvas);
2179 }
2181 /**
2182 * Sets world coordinates from win and canvas.
2183 */
2184 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2185 {
2186 g_return_if_fail (canvas != NULL);
2187 g_return_if_fail (SP_IS_CANVAS (canvas));
2189 if (worldx) *worldx = canvas->x0 + winx;
2190 if (worldy) *worldy = canvas->y0 + winy;
2191 }
2193 /**
2194 * Sets win coordinates from world and canvas.
2195 */
2196 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2197 {
2198 g_return_if_fail (canvas != NULL);
2199 g_return_if_fail (SP_IS_CANVAS (canvas));
2201 if (winx) *winx = worldx - canvas->x0;
2202 if (winy) *winy = worldy - canvas->y0;
2203 }
2205 /**
2206 * Converts point from win to world coordinates.
2207 */
2208 NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win)
2209 {
2210 g_assert (canvas != NULL);
2211 g_assert (SP_IS_CANVAS (canvas));
2213 return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2214 }
2216 /**
2217 * Converts point from world to win coordinates.
2218 */
2219 NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world)
2220 {
2221 g_assert (canvas != NULL);
2222 g_assert (SP_IS_CANVAS (canvas));
2224 return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2225 }
2227 /**
2228 * Returns true if point given in world coordinates is inside window.
2229 */
2230 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world)
2231 {
2232 g_assert( canvas != NULL );
2233 g_assert(SP_IS_CANVAS(canvas));
2235 using NR::X;
2236 using NR::Y;
2237 GtkWidget const &w = *GTK_WIDGET(canvas);
2238 return ( ( canvas->x0 <= world[X] ) &&
2239 ( canvas->y0 <= world[Y] ) &&
2240 ( world[X] < canvas->x0 + w.allocation.width ) &&
2241 ( world[Y] < canvas->y0 + w.allocation.height ) );
2242 }
2244 /**
2245 * Return canvas window coordinates as NR::Rect.
2246 */
2247 NR::Rect SPCanvas::getViewbox() const
2248 {
2249 GtkWidget const *w = GTK_WIDGET(this);
2250 return NR::Rect(NR::Point(dx0, dy0),
2251 NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2252 }
2254 /**
2255 * Return canvas window coordinates as IRect (a rectangle defined by integers).
2256 */
2257 NR::IRect SPCanvas::getViewboxIntegers() const
2258 {
2259 GtkWidget const *w = GTK_WIDGET(this);
2260 return NR::IRect(NR::IPoint(x0, y0),
2261 NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height));
2262 }
2264 inline int sp_canvas_tile_floor(int x)
2265 {
2266 return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2267 }
2269 inline int sp_canvas_tile_ceil(int x)
2270 {
2271 return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2272 }
2274 /**
2275 * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2276 */
2277 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2278 {
2279 if ( nl >= nr || nt >= nb ) {
2280 if ( canvas->tiles ) g_free(canvas->tiles);
2281 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2282 canvas->tileH=canvas->tileV=0;
2283 canvas->tiles=NULL;
2284 return;
2285 }
2286 int tl=sp_canvas_tile_floor(nl);
2287 int tt=sp_canvas_tile_floor(nt);
2288 int tr=sp_canvas_tile_ceil(nr);
2289 int tb=sp_canvas_tile_ceil(nb);
2291 int nh = tr-tl, nv = tb-tt;
2292 uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2293 for (int i=tl; i<tr; i++) {
2294 for (int j=tt; j<tb; j++) {
2295 int ind = (i-tl) + (j-tt)*nh;
2296 if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2297 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2298 } else {
2299 ntiles[ind]=0; // newly exposed areas get 0
2300 }
2301 }
2302 }
2303 if ( canvas->tiles ) g_free(canvas->tiles);
2304 canvas->tiles=ntiles;
2305 canvas->tLeft=tl;
2306 canvas->tTop=tt;
2307 canvas->tRight=tr;
2308 canvas->tBottom=tb;
2309 canvas->tileH=nh;
2310 canvas->tileV=nv;
2311 }
2313 /*
2314 * Helper that queues a canvas rectangle for redraw
2315 */
2316 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2317 canvas->need_redraw = TRUE;
2319 sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2320 }
2322 /**
2323 * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2324 */
2325 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2326 {
2327 if ( nl >= nr || nt >= nb ) {
2328 return;
2329 }
2330 int tl=sp_canvas_tile_floor(nl);
2331 int tt=sp_canvas_tile_floor(nt);
2332 int tr=sp_canvas_tile_ceil(nr);
2333 int tb=sp_canvas_tile_ceil(nb);
2334 if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2335 if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2336 if ( tr > canvas->tRight ) tr=canvas->tRight;
2337 if ( tt < canvas->tTop ) tt=canvas->tTop;
2338 if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2340 for (int i=tl; i<tr; i++) {
2341 for (int j=tt; j<tb; j++) {
2342 canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2343 }
2344 }
2345 }
2348 /*
2349 Local Variables:
2350 mode:c++
2351 c-file-style:"stroustrup"
2352 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2353 indent-tabs-mode:nil
2354 fill-column:99
2355 End:
2356 */
2357 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :