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 shutdown_transients (canvas);
1151 gdk_gc_destroy (canvas->pixmap_gc);
1152 canvas->pixmap_gc = NULL;
1154 if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1155 (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1156 }
1158 /**
1159 * The canvas widget's size_request callback.
1160 */
1161 static void
1162 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1163 {
1164 static_cast<void>(SP_CANVAS (widget));
1166 req->width = 256;
1167 req->height = 256;
1168 }
1170 /**
1171 * The canvas widget's size_allocate callback.
1172 */
1173 static void
1174 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1175 {
1176 SPCanvas *canvas = SP_CANVAS (widget);
1178 /* Schedule redraw of new region */
1179 sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1180 if (allocation->width > widget->allocation.width) {
1181 sp_canvas_request_redraw (canvas,
1182 canvas->x0 + widget->allocation.width,
1183 0,
1184 canvas->x0 + allocation->width,
1185 canvas->y0 + allocation->height);
1186 }
1187 if (allocation->height > widget->allocation.height) {
1188 sp_canvas_request_redraw (canvas,
1189 0,
1190 canvas->y0 + widget->allocation.height,
1191 canvas->x0 + allocation->width,
1192 canvas->y0 + allocation->height);
1193 }
1195 widget->allocation = *allocation;
1197 if (GTK_WIDGET_REALIZED (widget)) {
1198 gdk_window_move_resize (widget->window,
1199 widget->allocation.x, widget->allocation.y,
1200 widget->allocation.width, widget->allocation.height);
1201 }
1202 }
1204 /**
1205 * Helper that emits an event for an item in the canvas, be it the current
1206 * item, grabbed item, or focused item, as appropriate.
1207 */
1208 static int
1209 emit_event (SPCanvas *canvas, GdkEvent *event)
1210 {
1211 guint mask;
1213 if (canvas->grabbed_item) {
1214 switch (event->type) {
1215 case GDK_ENTER_NOTIFY:
1216 mask = GDK_ENTER_NOTIFY_MASK;
1217 break;
1218 case GDK_LEAVE_NOTIFY:
1219 mask = GDK_LEAVE_NOTIFY_MASK;
1220 break;
1221 case GDK_MOTION_NOTIFY:
1222 mask = GDK_POINTER_MOTION_MASK;
1223 break;
1224 case GDK_BUTTON_PRESS:
1225 case GDK_2BUTTON_PRESS:
1226 case GDK_3BUTTON_PRESS:
1227 mask = GDK_BUTTON_PRESS_MASK;
1228 break;
1229 case GDK_BUTTON_RELEASE:
1230 mask = GDK_BUTTON_RELEASE_MASK;
1231 break;
1232 case GDK_KEY_PRESS:
1233 mask = GDK_KEY_PRESS_MASK;
1234 break;
1235 case GDK_KEY_RELEASE:
1236 mask = GDK_KEY_RELEASE_MASK;
1237 break;
1238 case GDK_SCROLL:
1239 mask = GDK_SCROLL;
1240 break;
1241 default:
1242 mask = 0;
1243 break;
1244 }
1246 if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1247 }
1249 /* Convert to world coordinates -- we have two cases because of diferent
1250 * offsets of the fields in the event structures.
1251 */
1253 GdkEvent ev = *event;
1255 switch (ev.type) {
1256 case GDK_ENTER_NOTIFY:
1257 case GDK_LEAVE_NOTIFY:
1258 ev.crossing.x += canvas->x0;
1259 ev.crossing.y += canvas->y0;
1260 break;
1261 case GDK_MOTION_NOTIFY:
1262 case GDK_BUTTON_PRESS:
1263 case GDK_2BUTTON_PRESS:
1264 case GDK_3BUTTON_PRESS:
1265 case GDK_BUTTON_RELEASE:
1266 ev.motion.x += canvas->x0;
1267 ev.motion.y += canvas->y0;
1268 break;
1269 default:
1270 break;
1271 }
1273 /* Choose where we send the event */
1275 /* canvas->current_item becomes NULL in some cases under Win32
1276 ** (e.g. if the pointer leaves the window). So this is a hack that
1277 ** Lauris applied to SP to get around the problem.
1278 */
1279 SPCanvasItem* item = NULL;
1280 if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1281 item = canvas->grabbed_item;
1282 } else {
1283 item = canvas->current_item;
1284 }
1286 if (canvas->focused_item &&
1287 ((event->type == GDK_KEY_PRESS) ||
1288 (event->type == GDK_KEY_RELEASE) ||
1289 (event->type == GDK_FOCUS_CHANGE))) {
1290 item = canvas->focused_item;
1291 }
1293 /* The event is propagated up the hierarchy (for if someone connected to
1294 * a group instead of a leaf event), and emission is stopped if a
1295 * handler returns TRUE, just like for GtkWidget events.
1296 */
1298 gint finished = FALSE;
1300 while (item && !finished) {
1301 gtk_object_ref (GTK_OBJECT (item));
1302 gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1303 SPCanvasItem *parent = item->parent;
1304 gtk_object_unref (GTK_OBJECT (item));
1305 item = parent;
1306 }
1308 return finished;
1309 }
1311 /**
1312 * Helper that re-picks the current item in the canvas, based on the event's
1313 * coordinates and emits enter/leave events for items as appropriate.
1314 */
1315 static int
1316 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1317 {
1318 int button_down = 0;
1319 double x, y;
1321 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1322 return FALSE;
1324 int retval = FALSE;
1326 if (canvas->gen_all_enter_events == false) {
1327 // If a button is down, we'll perform enter and leave events on the
1328 // current item, but not enter on any other item. This is more or
1329 // less like X pointer grabbing for canvas items.
1330 //
1331 button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1332 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1334 if (!button_down) canvas->left_grabbed_item = FALSE;
1335 }
1337 /* Save the event in the canvas. This is used to synthesize enter and
1338 * leave events in case the current item changes. It is also used to
1339 * re-pick the current item if the current one gets deleted. Also,
1340 * synthesize an enter event.
1341 */
1342 if (event != &canvas->pick_event) {
1343 if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1344 /* these fields have the same offsets in both types of events */
1346 canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY;
1347 canvas->pick_event.crossing.window = event->motion.window;
1348 canvas->pick_event.crossing.send_event = event->motion.send_event;
1349 canvas->pick_event.crossing.subwindow = NULL;
1350 canvas->pick_event.crossing.x = event->motion.x;
1351 canvas->pick_event.crossing.y = event->motion.y;
1352 canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL;
1353 canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR;
1354 canvas->pick_event.crossing.focus = FALSE;
1355 canvas->pick_event.crossing.state = event->motion.state;
1357 /* these fields don't have the same offsets in both types of events */
1359 if (event->type == GDK_MOTION_NOTIFY) {
1360 canvas->pick_event.crossing.x_root = event->motion.x_root;
1361 canvas->pick_event.crossing.y_root = event->motion.y_root;
1362 } else {
1363 canvas->pick_event.crossing.x_root = event->button.x_root;
1364 canvas->pick_event.crossing.y_root = event->button.y_root;
1365 }
1366 } else {
1367 canvas->pick_event = *event;
1368 }
1369 }
1371 /* Don't do anything else if this is a recursive call */
1372 if (canvas->in_repick) return retval;
1374 /* LeaveNotify means that there is no current item, so we don't look for one */
1375 if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1376 /* these fields don't have the same offsets in both types of events */
1378 if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1379 x = canvas->pick_event.crossing.x;
1380 y = canvas->pick_event.crossing.y;
1381 } else {
1382 x = canvas->pick_event.motion.x;
1383 y = canvas->pick_event.motion.y;
1384 }
1386 /* world coords */
1387 x += canvas->x0;
1388 y += canvas->y0;
1390 /* find the closest item */
1391 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1392 sp_canvas_item_invoke_point (canvas->root, NR::Point(x, y), &canvas->new_current_item);
1393 } else {
1394 canvas->new_current_item = NULL;
1395 }
1396 } else {
1397 canvas->new_current_item = NULL;
1398 }
1400 if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1401 return retval; /* current item did not change */
1402 }
1404 /* Synthesize events for old and new current items */
1406 if ((canvas->new_current_item != canvas->current_item)
1407 && (canvas->current_item != NULL)
1408 && !canvas->left_grabbed_item) {
1409 GdkEvent new_event;
1410 SPCanvasItem *item;
1412 item = canvas->current_item;
1414 new_event = canvas->pick_event;
1415 new_event.type = GDK_LEAVE_NOTIFY;
1417 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1418 new_event.crossing.subwindow = NULL;
1419 canvas->in_repick = TRUE;
1420 retval = emit_event (canvas, &new_event);
1421 canvas->in_repick = FALSE;
1422 }
1424 if (canvas->gen_all_enter_events == false) {
1425 // new_current_item may have been set to NULL during the call to
1426 // emit_event() above
1427 if ((canvas->new_current_item != canvas->current_item) && button_down) {
1428 canvas->left_grabbed_item = TRUE;
1429 return retval;
1430 }
1431 }
1433 /* Handle the rest of cases */
1435 canvas->left_grabbed_item = FALSE;
1436 canvas->current_item = canvas->new_current_item;
1438 if (canvas->current_item != NULL) {
1439 GdkEvent new_event;
1441 new_event = canvas->pick_event;
1442 new_event.type = GDK_ENTER_NOTIFY;
1443 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1444 new_event.crossing.subwindow = NULL;
1445 retval = emit_event (canvas, &new_event);
1446 }
1448 return retval;
1449 }
1451 /**
1452 * Button event handler for the canvas.
1453 */
1454 static gint
1455 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1456 {
1457 SPCanvas *canvas = SP_CANVAS (widget);
1459 int retval = FALSE;
1461 /* dispatch normally regardless of the event's window if an item has
1462 has a pointer grab in effect */
1463 if (!canvas->grabbed_item &&
1464 event->window != SP_CANVAS_WINDOW (canvas))
1465 return retval;
1467 int mask;
1468 switch (event->button) {
1469 case 1:
1470 mask = GDK_BUTTON1_MASK;
1471 break;
1472 case 2:
1473 mask = GDK_BUTTON2_MASK;
1474 break;
1475 case 3:
1476 mask = GDK_BUTTON3_MASK;
1477 break;
1478 case 4:
1479 mask = GDK_BUTTON4_MASK;
1480 break;
1481 case 5:
1482 mask = GDK_BUTTON5_MASK;
1483 break;
1484 default:
1485 mask = 0;
1486 }
1488 switch (event->type) {
1489 case GDK_BUTTON_PRESS:
1490 case GDK_2BUTTON_PRESS:
1491 case GDK_3BUTTON_PRESS:
1492 /* Pick the current item as if the button were not pressed, and
1493 * then process the event.
1494 */
1495 canvas->state = event->state;
1496 pick_current_item (canvas, (GdkEvent *) event);
1497 canvas->state ^= mask;
1498 retval = emit_event (canvas, (GdkEvent *) event);
1499 break;
1501 case GDK_BUTTON_RELEASE:
1502 /* Process the event as if the button were pressed, then repick
1503 * after the button has been released
1504 */
1505 canvas->state = event->state;
1506 retval = emit_event (canvas, (GdkEvent *) event);
1507 event->state ^= mask;
1508 canvas->state = event->state;
1509 pick_current_item (canvas, (GdkEvent *) event);
1510 event->state ^= mask;
1511 break;
1513 default:
1514 g_assert_not_reached ();
1515 }
1517 return retval;
1518 }
1520 /**
1521 * Scroll event handler for the canvas.
1522 *
1523 * \todo FIXME: generate motion events to re-select items.
1524 */
1525 static gint
1526 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1527 {
1528 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1529 }
1531 /**
1532 * Motion event handler for the canvas.
1533 */
1534 static int
1535 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1536 {
1537 SPCanvas *canvas = SP_CANVAS (widget);
1539 if (event->window != SP_CANVAS_WINDOW (canvas))
1540 return FALSE;
1542 if (canvas->grabbed_event_mask & GDK_POINTER_MOTION_HINT_MASK) {
1543 gint x, y;
1544 gdk_window_get_pointer (widget->window, &x, &y, NULL);
1545 event->x = x;
1546 event->y = y;
1547 }
1549 canvas->state = event->state;
1550 pick_current_item (canvas, (GdkEvent *) event);
1552 return emit_event (canvas, (GdkEvent *) event);
1553 }
1555 static void
1556 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)
1557 {
1558 GtkWidget *widget = GTK_WIDGET (canvas);
1560 SPCanvasBuf buf;
1561 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1562 buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1563 } else {
1564 buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1565 }
1567 // Mark the region clean
1568 sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1570 buf.buf_rowstride = sw * 3; // CAIRO FIXME: for cairo output, the buffer must be RGB unpacked, i.e. sw * 4
1571 buf.rect.x0 = x0;
1572 buf.rect.y0 = y0;
1573 buf.rect.x1 = x1;
1574 buf.rect.y1 = y1;
1575 buf.visible_rect.x0 = draw_x1;
1576 buf.visible_rect.y0 = draw_y1;
1577 buf.visible_rect.x1 = draw_x2;
1578 buf.visible_rect.y1 = draw_y2;
1579 GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1580 buf.bg_color = (((color->red & 0xff00) << 8)
1581 | (color->green & 0xff00)
1582 | (color->blue >> 8));
1583 buf.is_empty = true;
1585 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1586 SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1587 }
1589 #if ENABLE_LCMS
1590 cmsHTRANSFORM transf = 0;
1591 long long int fromDisplay = prefs_get_int_attribute_limited( "options.displayprofile", "from_display", 0, 0, 1 );
1592 if ( fromDisplay ) {
1593 transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" );
1594 } else {
1595 transf = Inkscape::colorprofile_get_display_transform();
1596 }
1597 #endif // ENABLE_LCMS
1599 if (buf.is_empty) {
1600 #if ENABLE_LCMS
1601 if ( transf && canvas->enable_cms_display_adj ) {
1602 cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1603 }
1604 #endif // ENABLE_LCMS
1605 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1606 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1607 canvas->pixmap_gc,
1608 TRUE,
1609 x0 - canvas->x0, y0 - canvas->y0,
1610 x1 - x0, y1 - y0);
1611 } else {
1612 /*
1613 // CAIRO FIXME: after SPCanvasBuf is made 32bpp throughout, this rgb_draw below can be replaced with the below.
1614 // Why this must not be done currently:
1615 // - all canvas items (handles, nodes etc) paint themselves assuming 24bpp
1616 // - cairo assumes bgra, but we have rgba, so r and b get swapped (until we paint all with cairo too)
1617 // - it does not seem to be any faster; in fact since with 32bpp, buf contains less pixels,
1618 // we need more bufs to paint a given area and as a result it's even a bit slower
1620 cairo_surface_t* cst = cairo_image_surface_create_for_data (
1621 buf.buf,
1622 CAIRO_FORMAT_RGB24, // unpacked, i.e. 32 bits! one byte is unused
1623 x1 - x0, y1 - y0,
1624 buf.buf_rowstride
1625 );
1626 cairo_t *ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1627 cairo_set_source_surface (ct, cst, x0 - canvas->x0, y0 - canvas->y0);
1628 cairo_paint (ct);
1629 cairo_destroy (ct);
1630 cairo_surface_finish (cst);
1631 cairo_surface_destroy (cst);
1632 */
1634 #if ENABLE_LCMS
1635 if ( transf && canvas->enable_cms_display_adj ) {
1636 for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1637 guchar* p = buf.buf + (sw * 3) * yy;
1638 cmsDoTransform( transf, p, p, (x1 - x0) );
1639 }
1640 }
1641 #endif // ENABLE_LCMS
1643 gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1644 canvas->pixmap_gc,
1645 x0 - canvas->x0, y0 - canvas->y0,
1646 x1 - x0, y1 - y0,
1647 GDK_RGB_DITHER_MAX,
1648 buf.buf,
1649 sw * 3,
1650 x0 - canvas->x0, y0 - canvas->y0);
1651 }
1653 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1654 nr_pixelstore_256K_free (buf.buf);
1655 } else {
1656 nr_pixelstore_1M_free (buf.buf);
1657 }
1658 }
1660 struct PaintRectSetup {
1661 SPCanvas* canvas;
1662 NRRectL big_rect;
1663 GTimeVal start_time;
1664 int max_pixels;
1665 NR::Point mouse_loc;
1666 };
1668 /**
1669 * Paint the given rect, recursively subdividing the region until it is the size of a single
1670 * buffer.
1671 *
1672 * @return true if the drawing completes
1673 */
1674 static int
1675 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1676 {
1677 GTimeVal now;
1678 g_get_current_time (&now);
1680 glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1681 + (now.tv_usec - setup->start_time.tv_usec);
1683 // Allow only very fast buffers to be run together;
1684 // as soon as the total redraw time exceeds 1ms, cancel;
1685 // this returns control to the idle loop and allows Inkscape to process user input
1686 // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1687 // it will get back and finish painting what remains to paint.
1688 if (elapsed > 1000) {
1690 // Interrupting redraw isn't always good.
1691 // For example, when you drag one node of a big path, only the buffer containing
1692 // the mouse cursor will be redrawn again and again, and the rest of the path
1693 // will remain stale because Inkscape never has enough idle time to redraw all
1694 // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1695 // If this limit is set, and if we have aborted redraw more times than is allowed,
1696 // interrupting is blocked and we're forced to redraw full screen once
1697 // (after which we can again interrupt forced_redraw_limit times).
1698 if (setup->canvas->forced_redraw_limit < 0 ||
1699 setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1701 if (setup->canvas->forced_redraw_limit != -1) {
1702 setup->canvas->forced_redraw_count++;
1703 }
1705 return false;
1706 }
1707 }
1709 // Find the optimal buffer dimensions
1710 int bw = this_rect.x1 - this_rect.x0;
1711 int bh = this_rect.y1 - this_rect.y0;
1712 if ((bw < 1) || (bh < 1))
1713 return 0;
1715 if (bw * bh < setup->max_pixels) {
1716 // We are small enough
1717 sp_canvas_paint_single_buffer (setup->canvas,
1718 this_rect.x0, this_rect.y0,
1719 this_rect.x1, this_rect.y1,
1720 setup->big_rect.x0, setup->big_rect.y0,
1721 setup->big_rect.x1, setup->big_rect.y1, bw);
1722 return 1;
1723 }
1725 NRRectL lo = this_rect;
1726 NRRectL hi = this_rect;
1728 /*
1729 This test determines the redraw strategy:
1731 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1732 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1733 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1734 and seems to be faster for drawings with many smaller objects at zoom-out.
1736 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1737 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1738 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1739 faster.
1741 The default for now is the strips mode.
1742 */
1743 if (bw < bh || bh < 2 * TILE_SIZE) {
1744 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1745 int mid = ((long long) this_rect.x0 + (long long) this_rect.x1) / 2;
1746 // Make sure that mid lies on a tile boundary
1747 mid = (mid / TILE_SIZE) * TILE_SIZE;
1749 lo.x1 = mid;
1750 hi.x0 = mid;
1752 if (setup->mouse_loc[NR::X] < mid) {
1753 // Always paint towards the mouse first
1754 return sp_canvas_paint_rect_internal(setup, lo)
1755 && sp_canvas_paint_rect_internal(setup, hi);
1756 } else {
1757 return sp_canvas_paint_rect_internal(setup, hi)
1758 && sp_canvas_paint_rect_internal(setup, lo);
1759 }
1760 } else {
1761 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1762 int mid = ((long long) this_rect.y0 + (long long) this_rect.y1) / 2;
1763 // Make sure that mid lies on a tile boundary
1764 mid = (mid / TILE_SIZE) * TILE_SIZE;
1766 lo.y1 = mid;
1767 hi.y0 = mid;
1769 if (setup->mouse_loc[NR::Y] < mid) {
1770 // Always paint towards the mouse first
1771 return sp_canvas_paint_rect_internal(setup, lo)
1772 && sp_canvas_paint_rect_internal(setup, hi);
1773 } else {
1774 return sp_canvas_paint_rect_internal(setup, hi)
1775 && sp_canvas_paint_rect_internal(setup, lo);
1776 }
1777 }
1778 }
1781 /**
1782 * Helper that draws a specific rectangular part of the canvas.
1783 *
1784 * @return true if the rectangle painting succeeds.
1785 */
1786 static bool
1787 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1788 {
1789 g_return_val_if_fail (!canvas->need_update, false);
1791 NRRectL rect;
1792 rect.x0 = xx0;
1793 rect.x1 = xx1;
1794 rect.y0 = yy0;
1795 rect.y1 = yy1;
1797 // Clip rect-to-draw by the current visible area
1798 rect.x0 = MAX (rect.x0, canvas->x0);
1799 rect.y0 = MAX (rect.y0, canvas->y0);
1800 rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1801 rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1803 #ifdef DEBUG_REDRAW
1804 // paint the area to redraw yellow
1805 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1806 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1807 canvas->pixmap_gc,
1808 TRUE,
1809 rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1810 rect.x1 - rect.x0, rect.y1 - rect.y0);
1811 #endif
1813 PaintRectSetup setup;
1815 setup.canvas = canvas;
1816 setup.big_rect = rect;
1818 // Save the mouse location
1819 gint x, y;
1820 gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1821 setup.mouse_loc = sp_canvas_window_to_world (canvas, NR::Point(x,y));
1823 // CAIRO FIXME: the sw/sh calculations below all assume 24bpp, need fixing for 32bpp
1824 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1825 // use 256K as a compromise to not slow down gradients
1826 // 256K is the cached buffer and we need 3 channels
1827 setup.max_pixels = 87381; // 256K/3
1828 } else {
1829 // paths only, so 1M works faster
1830 // 1M is the cached buffer and we need 3 channels
1831 setup.max_pixels = 349525;
1832 }
1834 // Start the clock
1835 g_get_current_time(&(setup.start_time));
1837 // Go
1838 return sp_canvas_paint_rect_internal(&setup, rect);
1839 }
1841 /**
1842 * Force a full redraw after a specified number of interrupted redraws
1843 */
1844 void
1845 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1846 g_return_if_fail(canvas != NULL);
1848 canvas->forced_redraw_limit = count;
1849 canvas->forced_redraw_count = 0;
1850 }
1852 /**
1853 * End forced full redraw requests
1854 */
1855 void
1856 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1857 g_return_if_fail(canvas != NULL);
1859 canvas->forced_redraw_limit = -1;
1860 }
1862 /**
1863 * The canvas widget's expose callback.
1864 */
1865 static gint
1866 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1867 {
1868 SPCanvas *canvas = SP_CANVAS (widget);
1870 if (!GTK_WIDGET_DRAWABLE (widget) ||
1871 (event->window != SP_CANVAS_WINDOW (canvas)))
1872 return FALSE;
1874 int n_rects;
1875 GdkRectangle *rects;
1876 gdk_region_get_rectangles (event->region, &rects, &n_rects);
1878 for (int i = 0; i < n_rects; i++) {
1879 NRRectL rect;
1881 rect.x0 = rects[i].x + canvas->x0;
1882 rect.y0 = rects[i].y + canvas->y0;
1883 rect.x1 = rect.x0 + rects[i].width;
1884 rect.y1 = rect.y0 + rects[i].height;
1886 sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1887 }
1889 if (n_rects > 0)
1890 g_free (rects);
1892 return FALSE;
1893 }
1895 /**
1896 * The canvas widget's keypress callback.
1897 */
1898 static gint
1899 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1900 {
1901 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1902 }
1904 /**
1905 * Crossing event handler for the canvas.
1906 */
1907 static gint
1908 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1909 {
1910 SPCanvas *canvas = SP_CANVAS (widget);
1912 if (event->window != SP_CANVAS_WINDOW (canvas))
1913 return FALSE;
1915 canvas->state = event->state;
1916 return pick_current_item (canvas, (GdkEvent *) event);
1917 }
1919 /**
1920 * Focus in handler for the canvas.
1921 */
1922 static gint
1923 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
1924 {
1925 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1927 SPCanvas *canvas = SP_CANVAS (widget);
1929 if (canvas->focused_item) {
1930 return emit_event (canvas, (GdkEvent *) event);
1931 } else {
1932 return FALSE;
1933 }
1934 }
1936 /**
1937 * Focus out handler for the canvas.
1938 */
1939 static gint
1940 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
1941 {
1942 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1944 SPCanvas *canvas = SP_CANVAS (widget);
1946 if (canvas->focused_item)
1947 return emit_event (canvas, (GdkEvent *) event);
1948 else
1949 return FALSE;
1950 }
1952 /**
1953 * Helper that repaints the areas in the canvas that need it.
1954 *
1955 * @return true if all the dirty parts have been redrawn
1956 */
1957 static int
1958 paint (SPCanvas *canvas)
1959 {
1960 if (canvas->need_update) {
1961 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
1962 canvas->need_update = FALSE;
1963 }
1965 if (!canvas->need_redraw)
1966 return TRUE;
1968 Gdk::Region to_paint;
1970 for (int j=canvas->tTop; j<canvas->tBottom; j++) {
1971 for (int i=canvas->tLeft; i<canvas->tRight; i++) {
1972 int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
1974 if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
1975 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
1976 TILE_SIZE, TILE_SIZE));
1977 }
1979 }
1980 }
1982 if (!to_paint.empty()) {
1983 Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
1984 typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
1985 for (Iter i=rect.begin(); i != rect.end(); ++i) {
1986 int x0 = (*i).get_x();
1987 int y0 = (*i).get_y();
1988 int x1 = x0 + (*i).get_width();
1989 int y1 = y0 + (*i).get_height();
1990 if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
1991 // Aborted
1992 return FALSE;
1993 };
1994 }
1995 }
1997 canvas->need_redraw = FALSE;
1999 // we've had a full unaborted redraw, reset the full redraw counter
2000 if (canvas->forced_redraw_limit != -1) {
2001 canvas->forced_redraw_count = 0;
2002 }
2004 return TRUE;
2005 }
2007 /**
2008 * Helper that invokes update, paint, and repick on canvas.
2009 */
2010 static int
2011 do_update (SPCanvas *canvas)
2012 {
2013 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
2014 return TRUE;
2016 /* Cause the update if necessary */
2017 if (canvas->need_update) {
2018 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2019 canvas->need_update = FALSE;
2020 }
2022 /* Paint if able to */
2023 if (GTK_WIDGET_DRAWABLE (canvas)) {
2024 return paint (canvas);
2025 }
2027 /* Pick new current item */
2028 while (canvas->need_repick) {
2029 canvas->need_repick = FALSE;
2030 pick_current_item (canvas, &canvas->pick_event);
2031 }
2033 return TRUE;
2034 }
2036 /**
2037 * Idle handler for the canvas that deals with pending updates and redraws.
2038 */
2039 static gint
2040 idle_handler (gpointer data)
2041 {
2042 GDK_THREADS_ENTER ();
2044 SPCanvas *canvas = SP_CANVAS (data);
2046 int const ret = do_update (canvas);
2048 if (ret) {
2049 /* Reset idle id */
2050 canvas->idle_id = 0;
2051 }
2053 GDK_THREADS_LEAVE ();
2055 return !ret;
2056 }
2058 /**
2059 * Convenience function to add an idle handler to a canvas.
2060 */
2061 static void
2062 add_idle (SPCanvas *canvas)
2063 {
2064 if (canvas->idle_id != 0)
2065 return;
2067 canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2068 }
2070 /**
2071 * Returns the root group of the specified canvas.
2072 */
2073 SPCanvasGroup *
2074 sp_canvas_root (SPCanvas *canvas)
2075 {
2076 g_return_val_if_fail (canvas != NULL, NULL);
2077 g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2079 return SP_CANVAS_GROUP (canvas->root);
2080 }
2082 /**
2083 * Scrolls canvas to specific position (cx and cy are measured in screen pixels)
2084 */
2085 void
2086 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2087 {
2088 g_return_if_fail (canvas != NULL);
2089 g_return_if_fail (SP_IS_CANVAS (canvas));
2091 int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels)
2092 int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do!
2093 int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the
2094 int dy = iy - canvas->y0; // canvas w.r.t its previous position
2096 canvas->dx0 = cx; // here the 'd' stands for double, not delta!
2097 canvas->dy0 = cy;
2098 canvas->x0 = ix;
2099 canvas->y0 = iy;
2101 sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2103 if (!clear) {
2104 // scrolling without zoom; redraw only the newly exposed areas
2105 if ((dx != 0) || (dy != 0)) {
2106 canvas->is_scrolling = is_scrolling;
2107 if (GTK_WIDGET_REALIZED (canvas)) {
2108 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2109 }
2110 }
2111 } else {
2112 // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2113 }
2114 }
2116 /**
2117 * Updates canvas if necessary.
2118 */
2119 void
2120 sp_canvas_update_now (SPCanvas *canvas)
2121 {
2122 g_return_if_fail (canvas != NULL);
2123 g_return_if_fail (SP_IS_CANVAS (canvas));
2125 if (!(canvas->need_update ||
2126 canvas->need_redraw))
2127 return;
2129 do_update (canvas);
2130 }
2132 /**
2133 * Update callback for canvas widget.
2134 */
2135 static void
2136 sp_canvas_request_update (SPCanvas *canvas)
2137 {
2138 canvas->need_update = TRUE;
2139 add_idle (canvas);
2140 }
2142 /**
2143 * Forces redraw of rectangular canvas area.
2144 */
2145 void
2146 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2147 {
2148 NRRectL bbox;
2149 NRRectL visible;
2150 NRRectL clip;
2152 g_return_if_fail (canvas != NULL);
2153 g_return_if_fail (SP_IS_CANVAS (canvas));
2155 if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2156 if ((x0 >= x1) || (y0 >= y1)) return;
2158 bbox.x0 = x0;
2159 bbox.y0 = y0;
2160 bbox.x1 = x1;
2161 bbox.y1 = y1;
2163 visible.x0 = canvas->x0;
2164 visible.y0 = canvas->y0;
2165 visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2166 visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2168 nr_rect_l_intersect (&clip, &bbox, &visible);
2170 sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2171 add_idle (canvas);
2172 }
2174 /**
2175 * Sets world coordinates from win and canvas.
2176 */
2177 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2178 {
2179 g_return_if_fail (canvas != NULL);
2180 g_return_if_fail (SP_IS_CANVAS (canvas));
2182 if (worldx) *worldx = canvas->x0 + winx;
2183 if (worldy) *worldy = canvas->y0 + winy;
2184 }
2186 /**
2187 * Sets win coordinates from world and canvas.
2188 */
2189 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2190 {
2191 g_return_if_fail (canvas != NULL);
2192 g_return_if_fail (SP_IS_CANVAS (canvas));
2194 if (winx) *winx = worldx - canvas->x0;
2195 if (winy) *winy = worldy - canvas->y0;
2196 }
2198 /**
2199 * Converts point from win to world coordinates.
2200 */
2201 NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win)
2202 {
2203 g_assert (canvas != NULL);
2204 g_assert (SP_IS_CANVAS (canvas));
2206 return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2207 }
2209 /**
2210 * Converts point from world to win coordinates.
2211 */
2212 NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world)
2213 {
2214 g_assert (canvas != NULL);
2215 g_assert (SP_IS_CANVAS (canvas));
2217 return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2218 }
2220 /**
2221 * Returns true if point given in world coordinates is inside window.
2222 */
2223 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world)
2224 {
2225 g_assert( canvas != NULL );
2226 g_assert(SP_IS_CANVAS(canvas));
2228 using NR::X;
2229 using NR::Y;
2230 GtkWidget const &w = *GTK_WIDGET(canvas);
2231 return ( ( canvas->x0 <= world[X] ) &&
2232 ( canvas->y0 <= world[Y] ) &&
2233 ( world[X] < canvas->x0 + w.allocation.width ) &&
2234 ( world[Y] < canvas->y0 + w.allocation.height ) );
2235 }
2237 /**
2238 * Return canvas window coordinates as NR::Rect.
2239 */
2240 NR::Rect SPCanvas::getViewbox() const
2241 {
2242 GtkWidget const *w = GTK_WIDGET(this);
2243 return NR::Rect(NR::Point(dx0, dy0),
2244 NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2245 }
2247 /**
2248 * Return canvas window coordinates as IRect (a rectangle defined by integers).
2249 */
2250 NR::IRect SPCanvas::getViewboxIntegers() const
2251 {
2252 GtkWidget const *w = GTK_WIDGET(this);
2253 return NR::IRect(NR::IPoint(x0, y0),
2254 NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height));
2255 }
2257 inline int sp_canvas_tile_floor(int x)
2258 {
2259 return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2260 }
2262 inline int sp_canvas_tile_ceil(int x)
2263 {
2264 return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2265 }
2267 /**
2268 * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2269 */
2270 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2271 {
2272 if ( nl >= nr || nt >= nb ) {
2273 if ( canvas->tiles ) g_free(canvas->tiles);
2274 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2275 canvas->tileH=canvas->tileV=0;
2276 canvas->tiles=NULL;
2277 return;
2278 }
2279 int tl=sp_canvas_tile_floor(nl);
2280 int tt=sp_canvas_tile_floor(nt);
2281 int tr=sp_canvas_tile_ceil(nr);
2282 int tb=sp_canvas_tile_ceil(nb);
2284 int nh = tr-tl, nv = tb-tt;
2285 uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2286 for (int i=tl; i<tr; i++) {
2287 for (int j=tt; j<tb; j++) {
2288 int ind = (i-tl) + (j-tt)*nh;
2289 if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2290 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2291 } else {
2292 ntiles[ind]=0; // newly exposed areas get 0
2293 }
2294 }
2295 }
2296 if ( canvas->tiles ) g_free(canvas->tiles);
2297 canvas->tiles=ntiles;
2298 canvas->tLeft=tl;
2299 canvas->tTop=tt;
2300 canvas->tRight=tr;
2301 canvas->tBottom=tb;
2302 canvas->tileH=nh;
2303 canvas->tileV=nv;
2304 }
2306 /*
2307 * Helper that queues a canvas rectangle for redraw
2308 */
2309 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2310 canvas->need_redraw = TRUE;
2312 sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2313 }
2315 /**
2316 * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2317 */
2318 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2319 {
2320 if ( nl >= nr || nt >= nb ) {
2321 return;
2322 }
2323 int tl=sp_canvas_tile_floor(nl);
2324 int tt=sp_canvas_tile_floor(nt);
2325 int tr=sp_canvas_tile_ceil(nr);
2326 int tb=sp_canvas_tile_ceil(nb);
2327 if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2328 if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2329 if ( tr > canvas->tRight ) tr=canvas->tRight;
2330 if ( tt < canvas->tTop ) tt=canvas->tTop;
2331 if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2333 for (int i=tl; i<tr; i++) {
2334 for (int j=tt; j<tb; j++) {
2335 canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2336 }
2337 }
2338 }
2341 /*
2342 Local Variables:
2343 mode:c++
2344 c-file-style:"stroustrup"
2345 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2346 indent-tabs-mode:nil
2347 fill-column:99
2348 End:
2349 */
2350 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :