86b1e9eabbbb75580a0063238e705e55754a2aed
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
43 // Define this to visualize the regions to be redrawn
44 //#define DEBUG_REDRAW 1;
46 // Tiles are a way to minimize the number of redraws, eliminating too small redraws.
47 // The canvas stores a 2D array of ints, each representing a TILE_SIZExTILE_SIZE pixels tile.
48 // If any part of it is dirtied, the entire tile is dirtied (its int is nonzero) and repainted.
49 #define TILE_SIZE 16
51 enum {
52 RENDERMODE_NORMAL,
53 RENDERMODE_NOAA,
54 RENDERMODE_OUTLINE
55 };
57 static gint const sp_canvas_update_priority = G_PRIORITY_HIGH_IDLE;
59 #define SP_CANVAS_WINDOW(c) (((GtkWidget *) (c))->window)
61 enum {
62 SP_CANVAS_ITEM_VISIBLE = 1 << 7,
63 SP_CANVAS_ITEM_NEED_UPDATE = 1 << 8,
64 SP_CANVAS_ITEM_NEED_AFFINE = 1 << 9
65 };
67 /**
68 * A group of Items.
69 */
70 struct SPCanvasGroup {
71 SPCanvasItem item;
73 GList *items, *last;
74 };
76 /**
77 * The SPCanvasGroup vtable.
78 */
79 struct SPCanvasGroupClass {
80 SPCanvasItemClass parent_class;
81 };
83 /**
84 * The SPCanvas vtable.
85 */
86 struct SPCanvasClass {
87 GtkWidgetClass parent_class;
88 };
90 static void group_add (SPCanvasGroup *group, SPCanvasItem *item);
91 static void group_remove (SPCanvasGroup *group, SPCanvasItem *item);
93 /* SPCanvasItem */
95 enum {ITEM_EVENT, ITEM_LAST_SIGNAL};
98 static void sp_canvas_request_update (SPCanvas *canvas);
100 static void sp_canvas_item_class_init (SPCanvasItemClass *klass);
101 static void sp_canvas_item_init (SPCanvasItem *item);
102 static void sp_canvas_item_dispose (GObject *object);
103 static void sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args);
105 static int emit_event (SPCanvas *canvas, GdkEvent *event);
107 static guint item_signals[ITEM_LAST_SIGNAL] = { 0 };
109 static GtkObjectClass *item_parent_class;
111 /**
112 * Registers the SPCanvasItem class with Glib and returns its type number.
113 */
114 GType
115 sp_canvas_item_get_type (void)
116 {
117 static GType type = 0;
118 if (!type) {
119 static GTypeInfo const info = {
120 sizeof (SPCanvasItemClass),
121 NULL, NULL,
122 (GClassInitFunc) sp_canvas_item_class_init,
123 NULL, NULL,
124 sizeof (SPCanvasItem),
125 0,
126 (GInstanceInitFunc) sp_canvas_item_init,
127 NULL
128 };
129 type = g_type_register_static (GTK_TYPE_OBJECT, "SPCanvasItem", &info, (GTypeFlags)0);
130 }
132 return type;
133 }
135 /**
136 * Initializes the SPCanvasItem vtable and the "event" signal.
137 */
138 static void
139 sp_canvas_item_class_init (SPCanvasItemClass *klass)
140 {
141 GObjectClass *object_class = (GObjectClass *) klass;
143 /* fixme: Derive from GObject */
144 item_parent_class = (GtkObjectClass*)gtk_type_class (GTK_TYPE_OBJECT);
146 item_signals[ITEM_EVENT] = g_signal_new ("event",
147 G_TYPE_FROM_CLASS (klass),
148 G_SIGNAL_RUN_LAST,
149 ((glong)((guint8*)&(klass->event) - (guint8*)klass)),
150 NULL, NULL,
151 sp_marshal_BOOLEAN__POINTER,
152 G_TYPE_BOOLEAN, 1,
153 GDK_TYPE_EVENT);
155 object_class->dispose = sp_canvas_item_dispose;
156 }
158 /**
159 * Callback for initialization of SPCanvasItem.
160 */
161 static void
162 sp_canvas_item_init (SPCanvasItem *item)
163 {
164 item->flags |= SP_CANVAS_ITEM_VISIBLE;
165 item->xform = NR::Matrix(NR::identity());
166 }
168 /**
169 * Constructs new SPCanvasItem on SPCanvasGroup.
170 */
171 SPCanvasItem *
172 sp_canvas_item_new (SPCanvasGroup *parent, GtkType type, gchar const *first_arg_name, ...)
173 {
174 va_list args;
176 g_return_val_if_fail (parent != NULL, NULL);
177 g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL);
178 g_return_val_if_fail (gtk_type_is_a (type, sp_canvas_item_get_type ()), NULL);
180 SPCanvasItem *item = SP_CANVAS_ITEM (gtk_type_new (type));
182 va_start (args, first_arg_name);
183 sp_canvas_item_construct (item, parent, first_arg_name, args);
184 va_end (args);
186 return item;
187 }
189 /**
190 * Sets up the newly created SPCanvasItem.
191 *
192 * We make it static for encapsulation reasons since it was nowhere used.
193 */
194 static void
195 sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args)
196 {
197 g_return_if_fail (SP_IS_CANVAS_GROUP (parent));
198 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
200 item->parent = SP_CANVAS_ITEM (parent);
201 item->canvas = item->parent->canvas;
203 g_object_set_valist (G_OBJECT (item), first_arg_name, args);
205 group_add (SP_CANVAS_GROUP (item->parent), item);
207 sp_canvas_item_request_update (item);
208 }
210 /**
211 * Helper function that requests redraw only if item's visible flag is set.
212 */
213 static void
214 redraw_if_visible (SPCanvasItem *item)
215 {
216 if (item->flags & SP_CANVAS_ITEM_VISIBLE) {
217 int x0 = (int)(item->x1);
218 int x1 = (int)(item->x2);
219 int y0 = (int)(item->y1);
220 int y1 = (int)(item->y2);
222 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
223 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
224 }
225 }
226 }
228 /**
229 * Callback that removes item from all referers and destroys it.
230 */
231 static void
232 sp_canvas_item_dispose (GObject *object)
233 {
234 SPCanvasItem *item = SP_CANVAS_ITEM (object);
236 // Hack: if this is a ctrlrect, move it to 0,0;
237 // this redraws only the stroke of the rect to be deleted,
238 // avoiding redraw of the entire area
239 if (SP_IS_CTRLRECT(item)) {
240 SP_CTRLRECT(object)->setRectangle(NR::Rect(NR::Point(0,0),NR::Point(0,0)));
241 SP_CTRLRECT(object)->update(item->xform, 0);
242 } else {
243 redraw_if_visible (item);
244 }
245 item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
247 if (item == item->canvas->current_item) {
248 item->canvas->current_item = NULL;
249 item->canvas->need_repick = TRUE;
250 }
252 if (item == item->canvas->new_current_item) {
253 item->canvas->new_current_item = NULL;
254 item->canvas->need_repick = TRUE;
255 }
257 if (item == item->canvas->grabbed_item) {
258 item->canvas->grabbed_item = NULL;
259 gdk_pointer_ungrab (GDK_CURRENT_TIME);
260 }
262 if (item == item->canvas->focused_item)
263 item->canvas->focused_item = NULL;
265 if (item->parent) {
266 group_remove (SP_CANVAS_GROUP (item->parent), item);
267 }
269 G_OBJECT_CLASS (item_parent_class)->dispose (object);
270 }
272 /**
273 * Helper function to update item and its children.
274 *
275 * NB! affine is parent2canvas.
276 */
277 static void
278 sp_canvas_item_invoke_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
279 {
280 /* Apply the child item's transform */
281 NR::Matrix child_affine = item->xform * affine;
283 /* apply object flags to child flags */
284 int child_flags = flags & ~SP_CANVAS_UPDATE_REQUESTED;
286 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
287 child_flags |= SP_CANVAS_UPDATE_REQUESTED;
289 if (item->flags & SP_CANVAS_ITEM_NEED_AFFINE)
290 child_flags |= SP_CANVAS_UPDATE_AFFINE;
292 if (child_flags & (SP_CANVAS_UPDATE_REQUESTED | SP_CANVAS_UPDATE_AFFINE)) {
293 if (SP_CANVAS_ITEM_GET_CLASS (item)->update)
294 SP_CANVAS_ITEM_GET_CLASS (item)->update (item, child_affine, child_flags);
295 }
297 GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_UPDATE);
298 GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_AFFINE);
299 }
301 /**
302 * Helper function to invoke the point method of the item.
303 *
304 * The argument x, y should be in the parent's item-relative coordinate
305 * system. This routine applies the inverse of the item's transform,
306 * maintaining the affine invariant.
307 */
308 static double
309 sp_canvas_item_invoke_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
310 {
311 if (SP_CANVAS_ITEM_GET_CLASS (item)->point)
312 return SP_CANVAS_ITEM_GET_CLASS (item)->point (item, p, actual_item);
314 return NR_HUGE;
315 }
317 /**
318 * Makes the item's affine transformation matrix be equal to the specified
319 * matrix.
320 *
321 * @item: A canvas item.
322 * @affine: An affine transformation matrix.
323 */
324 void
325 sp_canvas_item_affine_absolute (SPCanvasItem *item, NR::Matrix const &affine)
326 {
327 item->xform = affine;
329 if (!(item->flags & SP_CANVAS_ITEM_NEED_AFFINE)) {
330 item->flags |= SP_CANVAS_ITEM_NEED_AFFINE;
331 if (item->parent != NULL) {
332 sp_canvas_item_request_update (item->parent);
333 } else {
334 sp_canvas_request_update (item->canvas);
335 }
336 }
338 item->canvas->need_repick = TRUE;
339 }
341 /**
342 * Convenience function to reorder items in a group's child list.
343 *
344 * This puts the specified link after the "before" link.
345 */
346 static void
347 put_item_after (GList *link, GList *before)
348 {
349 if (link == before)
350 return;
352 SPCanvasGroup *parent = SP_CANVAS_GROUP (SP_CANVAS_ITEM (link->data)->parent);
354 if (before == NULL) {
355 if (link == parent->items) return;
357 link->prev->next = link->next;
359 if (link->next) {
360 link->next->prev = link->prev;
361 } else {
362 parent->last = link->prev;
363 }
365 link->prev = before;
366 link->next = parent->items;
367 link->next->prev = link;
368 parent->items = link;
369 } else {
370 if ((link == parent->last) && (before == parent->last->prev))
371 return;
373 if (link->next)
374 link->next->prev = link->prev;
376 if (link->prev)
377 link->prev->next = link->next;
378 else {
379 parent->items = link->next;
380 parent->items->prev = NULL;
381 }
383 link->prev = before;
384 link->next = before->next;
386 link->prev->next = link;
388 if (link->next)
389 link->next->prev = link;
390 else
391 parent->last = link;
392 }
393 }
396 /**
397 * Raises the item in its parent's stack by the specified number of positions.
398 *
399 * \param item A canvas item.
400 * \param positions Number of steps to raise the item.
401 *
402 * If the number of positions is greater than the distance to the top of the
403 * stack, then the item is put at the top.
404 */
405 void
406 sp_canvas_item_raise (SPCanvasItem *item, int positions)
407 {
408 g_return_if_fail (item != NULL);
409 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
410 g_return_if_fail (positions >= 0);
412 if (!item->parent || positions == 0)
413 return;
415 SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
416 GList *link = g_list_find (parent->items, item);
417 g_assert (link != NULL);
419 GList *before;
420 for (before = link; positions && before; positions--)
421 before = before->next;
423 if (!before)
424 before = parent->last;
426 put_item_after (link, before);
428 redraw_if_visible (item);
429 item->canvas->need_repick = TRUE;
430 }
433 /**
434 * Lowers the item in its parent's stack by the specified number of positions.
435 *
436 * \param item A canvas item.
437 * \param positions Number of steps to lower the item.
438 *
439 * If the number of positions is greater than the distance to the bottom of the
440 * stack, then the item is put at the bottom.
441 **/
442 void
443 sp_canvas_item_lower (SPCanvasItem *item, int positions)
444 {
445 g_return_if_fail (item != NULL);
446 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
447 g_return_if_fail (positions >= 1);
449 if (!item->parent || positions == 0)
450 return;
452 SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
453 GList *link = g_list_find (parent->items, item);
454 g_assert (link != NULL);
456 GList *before;
457 if (link->prev)
458 for (before = link->prev; positions && before; positions--)
459 before = before->prev;
460 else
461 before = NULL;
463 put_item_after (link, before);
465 redraw_if_visible (item);
466 item->canvas->need_repick = TRUE;
467 }
469 /**
470 * Sets visible flag on item and requests a redraw.
471 */
472 void
473 sp_canvas_item_show (SPCanvasItem *item)
474 {
475 g_return_if_fail (item != NULL);
476 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
478 if (item->flags & SP_CANVAS_ITEM_VISIBLE)
479 return;
481 item->flags |= SP_CANVAS_ITEM_VISIBLE;
483 int x0 = (int)(item->x1);
484 int x1 = (int)(item->x2);
485 int y0 = (int)(item->y1);
486 int y1 = (int)(item->y2);
488 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
489 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
490 item->canvas->need_repick = TRUE;
491 }
492 }
494 /**
495 * Clears visible flag on item and requests a redraw.
496 */
497 void
498 sp_canvas_item_hide (SPCanvasItem *item)
499 {
500 g_return_if_fail (item != NULL);
501 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
503 if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
504 return;
506 item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
508 int x0 = (int)(item->x1);
509 int x1 = (int)(item->x2);
510 int y0 = (int)(item->y1);
511 int y1 = (int)(item->y2);
513 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
514 sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
515 item->canvas->need_repick = TRUE;
516 }
517 }
519 /**
520 * Grab item under cursor.
521 *
522 * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE
523 */
524 int
525 sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
526 {
527 g_return_val_if_fail (item != NULL, -1);
528 g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1);
529 g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1);
531 if (item->canvas->grabbed_item)
532 return -1;
534 if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
535 return -1;
537 /* fixme: Top hack (Lauris) */
538 /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
539 /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
540 gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
541 (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
542 NULL, cursor, etime);
544 item->canvas->grabbed_item = item;
545 item->canvas->grabbed_event_mask = event_mask;
546 item->canvas->current_item = item; /* So that events go to the grabbed item */
548 return 0;
549 }
551 /**
552 * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
553 * mouse.
554 *
555 * \param item A canvas item that holds a grab.
556 * \param etime The timestamp for ungrabbing the mouse.
557 */
558 void
559 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
560 {
561 g_return_if_fail (item != NULL);
562 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
564 if (item->canvas->grabbed_item != item)
565 return;
567 item->canvas->grabbed_item = NULL;
569 gdk_pointer_ungrab (etime);
570 }
572 /**
573 * Returns the product of all transformation matrices from the root item down
574 * to the item.
575 */
576 NR::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
577 {
578 g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
580 NR::Matrix affine = NR::identity();
582 while (item) {
583 affine *= item->xform;
584 item = item->parent;
585 }
586 return affine;
587 }
589 /**
590 * Helper that returns true iff item is descendant of parent.
591 */
592 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
593 {
594 while (item) {
595 if (item == parent)
596 return true;
597 item = item->parent;
598 }
600 return false;
601 }
603 /**
604 * Focus canvas, and item under cursor if it is not already focussed.
605 */
606 void
607 sp_canvas_item_grab_focus (SPCanvasItem *item)
608 {
609 g_return_if_fail (item != NULL);
610 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
611 g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
613 SPCanvasItem *focused_item = item->canvas->focused_item;
615 if (focused_item) {
616 GdkEvent ev;
617 ev.focus_change.type = GDK_FOCUS_CHANGE;
618 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
619 ev.focus_change.send_event = FALSE;
620 ev.focus_change.in = FALSE;
622 emit_event (item->canvas, &ev);
623 }
625 item->canvas->focused_item = item;
626 gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
628 if (focused_item) {
629 GdkEvent ev;
630 ev.focus_change.type = GDK_FOCUS_CHANGE;
631 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
632 ev.focus_change.send_event = FALSE;
633 ev.focus_change.in = TRUE;
635 emit_event (item->canvas, &ev);
636 }
637 }
639 /**
640 * Requests that the canvas queue an update for the specified item.
641 *
642 * To be used only by item implementations.
643 */
644 void
645 sp_canvas_item_request_update (SPCanvasItem *item)
646 {
647 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
648 return;
650 item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
652 if (item->parent != NULL) {
653 /* Recurse up the tree */
654 sp_canvas_item_request_update (item->parent);
655 } else {
656 /* Have reached the top of the tree, make sure the update call gets scheduled. */
657 sp_canvas_request_update (item->canvas);
658 }
659 }
661 /**
662 * Returns position of item in group.
663 */
664 gint sp_canvas_item_order (SPCanvasItem * item)
665 {
666 return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
667 }
669 /* SPCanvasGroup */
671 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
672 static void sp_canvas_group_init (SPCanvasGroup *group);
673 static void sp_canvas_group_destroy (GtkObject *object);
675 static void sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
676 static double sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item);
677 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
679 static SPCanvasItemClass *group_parent_class;
681 /**
682 * Registers SPCanvasGroup class with Gtk and returns its type number.
683 */
684 GtkType
685 sp_canvas_group_get_type (void)
686 {
687 static GtkType group_type = 0;
689 if (!group_type) {
690 static GtkTypeInfo const group_info = {
691 "SPCanvasGroup",
692 sizeof (SPCanvasGroup),
693 sizeof (SPCanvasGroupClass),
694 (GtkClassInitFunc) sp_canvas_group_class_init,
695 (GtkObjectInitFunc) sp_canvas_group_init,
696 NULL, NULL, NULL
697 };
699 group_type = gtk_type_unique (sp_canvas_item_get_type (), &group_info);
700 }
702 return group_type;
703 }
705 /**
706 * Class initialization function for SPCanvasGroupClass
707 */
708 static void
709 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
710 {
711 GtkObjectClass *object_class = (GtkObjectClass *) klass;
712 SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
714 group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
716 object_class->destroy = sp_canvas_group_destroy;
718 item_class->update = sp_canvas_group_update;
719 item_class->render = sp_canvas_group_render;
720 item_class->point = sp_canvas_group_point;
721 }
723 /**
724 * Callback. Empty.
725 */
726 static void
727 sp_canvas_group_init (SPCanvasGroup */*group*/)
728 {
729 /* Nothing here */
730 }
732 /**
733 * Callback that destroys all items in group and calls group's virtual
734 * destroy() function.
735 */
736 static void
737 sp_canvas_group_destroy (GtkObject *object)
738 {
739 g_return_if_fail (object != NULL);
740 g_return_if_fail (SP_IS_CANVAS_GROUP (object));
742 SPCanvasGroup const *group = SP_CANVAS_GROUP (object);
744 GList *list = group->items;
745 while (list) {
746 SPCanvasItem *child = (SPCanvasItem *)list->data;
747 list = list->next;
749 gtk_object_destroy (GTK_OBJECT (child));
750 }
752 if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
753 (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
754 }
756 /**
757 * Update handler for canvas groups
758 */
759 static void
760 sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
761 {
762 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
763 NR::ConvexHull corners(NR::Point(0, 0));
764 bool empty=true;
766 for (GList *list = group->items; list; list = list->next) {
767 SPCanvasItem *i = (SPCanvasItem *)list->data;
769 sp_canvas_item_invoke_update (i, affine, flags);
771 if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
772 if (empty) {
773 corners = NR::ConvexHull(NR::Point(i->x1, i->y1));
774 empty = false;
775 } else {
776 corners.add(NR::Point(i->x1, i->y1));
777 }
778 corners.add(NR::Point(i->x2, i->y2));
779 }
780 }
782 NR::Maybe<NR::Rect> const bounds = corners.bounds();
783 if (bounds) {
784 item->x1 = bounds->min()[NR::X];
785 item->y1 = bounds->min()[NR::Y];
786 item->x2 = bounds->max()[NR::X];
787 item->y2 = bounds->max()[NR::Y];
788 } else {
789 // FIXME ?
790 item->x1 = item->x2 = item->y1 = item->y2 = 0;
791 }
792 }
794 /**
795 * Point handler for canvas groups.
796 */
797 static double
798 sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
799 {
800 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
801 double const x = p[NR::X];
802 double const y = p[NR::Y];
803 int x1 = (int)(x - item->canvas->close_enough);
804 int y1 = (int)(y - item->canvas->close_enough);
805 int x2 = (int)(x + item->canvas->close_enough);
806 int y2 = (int)(y + item->canvas->close_enough);
808 double best = 0.0;
809 *actual_item = NULL;
811 double dist = 0.0;
813 for (GList *list = group->items; list; list = list->next) {
814 SPCanvasItem *child = (SPCanvasItem *)list->data;
816 if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
817 SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
819 int has_point;
820 if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
821 dist = sp_canvas_item_invoke_point (child, p, &point_item);
822 has_point = TRUE;
823 } else
824 has_point = FALSE;
826 if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
827 best = dist;
828 *actual_item = point_item;
829 }
830 }
831 }
833 return best;
834 }
836 /**
837 * Renders all visible canvas group items in buf rectangle.
838 */
839 static void
840 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
841 {
842 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
844 for (GList *list = group->items; list; list = list->next) {
845 SPCanvasItem *child = (SPCanvasItem *)list->data;
846 if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
847 if ((child->x1 < buf->rect.x1) &&
848 (child->y1 < buf->rect.y1) &&
849 (child->x2 > buf->rect.x0) &&
850 (child->y2 > buf->rect.y0)) {
851 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
852 SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
853 }
854 }
855 }
856 }
858 /**
859 * Adds an item to a canvas group.
860 */
861 static void
862 group_add (SPCanvasGroup *group, SPCanvasItem *item)
863 {
864 gtk_object_ref (GTK_OBJECT (item));
865 gtk_object_sink (GTK_OBJECT (item));
867 if (!group->items) {
868 group->items = g_list_append (group->items, item);
869 group->last = group->items;
870 } else {
871 group->last = g_list_append (group->last, item)->next;
872 }
874 sp_canvas_item_request_update (item);
875 }
877 /**
878 * Removes an item from a canvas group
879 */
880 static void
881 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
882 {
883 g_return_if_fail (group != NULL);
884 g_return_if_fail (SP_IS_CANVAS_GROUP (group));
885 g_return_if_fail (item != NULL);
887 for (GList *children = group->items; children; children = children->next) {
888 if (children->data == item) {
890 /* Unparent the child */
892 item->parent = NULL;
893 gtk_object_unref (GTK_OBJECT (item));
895 /* Remove it from the list */
897 if (children == group->last) group->last = children->prev;
899 group->items = g_list_remove_link (group->items, children);
900 g_list_free (children);
901 break;
902 }
903 }
904 }
906 /* SPCanvas */
908 static void sp_canvas_class_init (SPCanvasClass *klass);
909 static void sp_canvas_init (SPCanvas *canvas);
910 static void sp_canvas_destroy (GtkObject *object);
912 static void sp_canvas_realize (GtkWidget *widget);
913 static void sp_canvas_unrealize (GtkWidget *widget);
915 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
916 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
918 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
919 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
920 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
921 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
922 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
923 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
924 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
925 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
927 static GtkWidgetClass *canvas_parent_class;
929 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb);
930 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb);
931 static void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val);
932 static int do_update (SPCanvas *canvas);
934 /**
935 * Registers the SPCanvas class if necessary, and returns the type ID
936 * associated to it.
937 *
938 * \return The type ID of the SPCanvas class.
939 **/
940 GtkType
941 sp_canvas_get_type (void)
942 {
943 static GtkType canvas_type = 0;
945 if (!canvas_type) {
946 static GtkTypeInfo const canvas_info = {
947 "SPCanvas",
948 sizeof (SPCanvas),
949 sizeof (SPCanvasClass),
950 (GtkClassInitFunc) sp_canvas_class_init,
951 (GtkObjectInitFunc) sp_canvas_init,
952 NULL, NULL, NULL
953 };
955 canvas_type = gtk_type_unique (GTK_TYPE_WIDGET, &canvas_info);
956 }
958 return canvas_type;
959 }
961 /**
962 * Class initialization function for SPCanvasClass.
963 */
964 static void
965 sp_canvas_class_init (SPCanvasClass *klass)
966 {
967 GtkObjectClass *object_class = (GtkObjectClass *) klass;
968 GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
970 canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
972 object_class->destroy = sp_canvas_destroy;
974 widget_class->realize = sp_canvas_realize;
975 widget_class->unrealize = sp_canvas_unrealize;
976 widget_class->size_request = sp_canvas_size_request;
977 widget_class->size_allocate = sp_canvas_size_allocate;
978 widget_class->button_press_event = sp_canvas_button;
979 widget_class->button_release_event = sp_canvas_button;
980 widget_class->motion_notify_event = sp_canvas_motion;
981 widget_class->scroll_event = sp_canvas_scroll;
982 widget_class->expose_event = sp_canvas_expose;
983 widget_class->key_press_event = sp_canvas_key;
984 widget_class->key_release_event = sp_canvas_key;
985 widget_class->enter_notify_event = sp_canvas_crossing;
986 widget_class->leave_notify_event = sp_canvas_crossing;
987 widget_class->focus_in_event = sp_canvas_focus_in;
988 widget_class->focus_out_event = sp_canvas_focus_out;
989 }
991 /**
992 * Callback: object initialization for SPCanvas.
993 */
994 static void
995 sp_canvas_init (SPCanvas *canvas)
996 {
997 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
998 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
999 GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
1001 canvas->pick_event.type = GDK_LEAVE_NOTIFY;
1002 canvas->pick_event.crossing.x = 0;
1003 canvas->pick_event.crossing.y = 0;
1005 /* Create the root item as a special case */
1006 canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
1007 canvas->root->canvas = canvas;
1009 gtk_object_ref (GTK_OBJECT (canvas->root));
1010 gtk_object_sink (GTK_OBJECT (canvas->root));
1012 canvas->need_repick = TRUE;
1014 // See comment at in sp-canvas.h.
1015 canvas->gen_all_enter_events = false;
1017 canvas->tiles=NULL;
1018 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1019 canvas->tileH=canvas->tileV=0;
1021 canvas->forced_redraw_count = 0;
1022 canvas->forced_redraw_limit = -1;
1024 #if ENABLE_LCMS
1025 canvas->enable_cms_display_adj = false;
1026 canvas->cms_key = new Glib::ustring("");
1027 #endif // ENABLE_LCMS
1029 canvas->is_scrolling = false;
1031 }
1033 /**
1034 * Convenience function to remove the idle handler of a canvas.
1035 */
1036 static void
1037 remove_idle (SPCanvas *canvas)
1038 {
1039 if (canvas->idle_id) {
1040 gtk_idle_remove (canvas->idle_id);
1041 canvas->idle_id = 0;
1042 }
1043 }
1045 /*
1046 * Removes the transient state of the canvas (idle handler, grabs).
1047 */
1048 static void
1049 shutdown_transients (SPCanvas *canvas)
1050 {
1051 /* We turn off the need_redraw flag, since if the canvas is mapped again
1052 * it will request a redraw anyways. We do not turn off the need_update
1053 * flag, though, because updates are not queued when the canvas remaps
1054 * itself.
1055 */
1056 if (canvas->need_redraw) {
1057 canvas->need_redraw = FALSE;
1058 }
1059 if ( canvas->tiles ) g_free(canvas->tiles);
1060 canvas->tiles=NULL;
1061 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1062 canvas->tileH=canvas->tileV=0;
1064 if (canvas->grabbed_item) {
1065 canvas->grabbed_item = NULL;
1066 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1067 }
1069 remove_idle (canvas);
1070 }
1072 /**
1073 * Destroy handler for SPCanvas.
1074 */
1075 static void
1076 sp_canvas_destroy (GtkObject *object)
1077 {
1078 SPCanvas *canvas = SP_CANVAS (object);
1080 if (canvas->root) {
1081 gtk_object_unref (GTK_OBJECT (canvas->root));
1082 canvas->root = NULL;
1083 }
1085 shutdown_transients (canvas);
1087 if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1088 (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1089 }
1091 /**
1092 * Returns new canvas as widget.
1093 */
1094 GtkWidget *
1095 sp_canvas_new_aa (void)
1096 {
1097 SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1099 return (GtkWidget *) canvas;
1100 }
1102 /**
1103 * The canvas widget's realize callback.
1104 */
1105 static void
1106 sp_canvas_realize (GtkWidget *widget)
1107 {
1108 SPCanvas *canvas = SP_CANVAS (widget);
1110 GdkWindowAttr attributes;
1111 attributes.window_type = GDK_WINDOW_CHILD;
1112 attributes.x = widget->allocation.x;
1113 attributes.y = widget->allocation.y;
1114 attributes.width = widget->allocation.width;
1115 attributes.height = widget->allocation.height;
1116 attributes.wclass = GDK_INPUT_OUTPUT;
1117 attributes.visual = gdk_rgb_get_visual ();
1118 attributes.colormap = gdk_rgb_get_cmap ();
1119 attributes.event_mask = (gtk_widget_get_events (widget) |
1120 GDK_EXPOSURE_MASK |
1121 GDK_BUTTON_PRESS_MASK |
1122 GDK_BUTTON_RELEASE_MASK |
1123 GDK_POINTER_MOTION_MASK |
1124 GDK_PROXIMITY_IN_MASK |
1125 GDK_PROXIMITY_OUT_MASK |
1126 GDK_KEY_PRESS_MASK |
1127 GDK_KEY_RELEASE_MASK |
1128 GDK_ENTER_NOTIFY_MASK |
1129 GDK_LEAVE_NOTIFY_MASK |
1130 GDK_FOCUS_CHANGE_MASK);
1131 gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1133 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1134 gdk_window_set_user_data (widget->window, widget);
1136 if ( prefs_get_int_attribute ("options.useextinput", "value", 1) )
1137 gtk_widget_set_events(widget, attributes.event_mask);
1139 widget->style = gtk_style_attach (widget->style, widget->window);
1141 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1143 canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1144 }
1146 /**
1147 * The canvas widget's unrealize callback.
1148 */
1149 static void
1150 sp_canvas_unrealize (GtkWidget *widget)
1151 {
1152 SPCanvas *canvas = SP_CANVAS (widget);
1154 shutdown_transients (canvas);
1156 gdk_gc_destroy (canvas->pixmap_gc);
1157 canvas->pixmap_gc = NULL;
1159 if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1160 (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1161 }
1163 /**
1164 * The canvas widget's size_request callback.
1165 */
1166 static void
1167 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1168 {
1169 static_cast<void>(SP_CANVAS (widget));
1171 req->width = 256;
1172 req->height = 256;
1173 }
1175 /**
1176 * The canvas widget's size_allocate callback.
1177 */
1178 static void
1179 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1180 {
1181 SPCanvas *canvas = SP_CANVAS (widget);
1183 /* Schedule redraw of new region */
1184 sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1185 if (allocation->width > widget->allocation.width) {
1186 sp_canvas_request_redraw (canvas,
1187 canvas->x0 + widget->allocation.width,
1188 0,
1189 canvas->x0 + allocation->width,
1190 canvas->y0 + allocation->height);
1191 }
1192 if (allocation->height > widget->allocation.height) {
1193 sp_canvas_request_redraw (canvas,
1194 0,
1195 canvas->y0 + widget->allocation.height,
1196 canvas->x0 + allocation->width,
1197 canvas->y0 + allocation->height);
1198 }
1200 widget->allocation = *allocation;
1202 if (GTK_WIDGET_REALIZED (widget)) {
1203 gdk_window_move_resize (widget->window,
1204 widget->allocation.x, widget->allocation.y,
1205 widget->allocation.width, widget->allocation.height);
1206 }
1207 }
1209 /**
1210 * Helper that emits an event for an item in the canvas, be it the current
1211 * item, grabbed item, or focused item, as appropriate.
1212 */
1213 static int
1214 emit_event (SPCanvas *canvas, GdkEvent *event)
1215 {
1216 guint mask;
1218 if (canvas->grabbed_item) {
1219 switch (event->type) {
1220 case GDK_ENTER_NOTIFY:
1221 mask = GDK_ENTER_NOTIFY_MASK;
1222 break;
1223 case GDK_LEAVE_NOTIFY:
1224 mask = GDK_LEAVE_NOTIFY_MASK;
1225 break;
1226 case GDK_MOTION_NOTIFY:
1227 mask = GDK_POINTER_MOTION_MASK;
1228 break;
1229 case GDK_BUTTON_PRESS:
1230 case GDK_2BUTTON_PRESS:
1231 case GDK_3BUTTON_PRESS:
1232 mask = GDK_BUTTON_PRESS_MASK;
1233 break;
1234 case GDK_BUTTON_RELEASE:
1235 mask = GDK_BUTTON_RELEASE_MASK;
1236 break;
1237 case GDK_KEY_PRESS:
1238 mask = GDK_KEY_PRESS_MASK;
1239 break;
1240 case GDK_KEY_RELEASE:
1241 mask = GDK_KEY_RELEASE_MASK;
1242 break;
1243 case GDK_SCROLL:
1244 mask = GDK_SCROLL;
1245 break;
1246 default:
1247 mask = 0;
1248 break;
1249 }
1251 if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1252 }
1254 /* Convert to world coordinates -- we have two cases because of diferent
1255 * offsets of the fields in the event structures.
1256 */
1258 GdkEvent ev = *event;
1260 switch (ev.type) {
1261 case GDK_ENTER_NOTIFY:
1262 case GDK_LEAVE_NOTIFY:
1263 ev.crossing.x += canvas->x0;
1264 ev.crossing.y += canvas->y0;
1265 break;
1266 case GDK_MOTION_NOTIFY:
1267 case GDK_BUTTON_PRESS:
1268 case GDK_2BUTTON_PRESS:
1269 case GDK_3BUTTON_PRESS:
1270 case GDK_BUTTON_RELEASE:
1271 ev.motion.x += canvas->x0;
1272 ev.motion.y += canvas->y0;
1273 break;
1274 default:
1275 break;
1276 }
1278 /* Choose where we send the event */
1280 /* canvas->current_item becomes NULL in some cases under Win32
1281 ** (e.g. if the pointer leaves the window). So this is a hack that
1282 ** Lauris applied to SP to get around the problem.
1283 */
1284 SPCanvasItem* item = NULL;
1285 if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1286 item = canvas->grabbed_item;
1287 } else {
1288 item = canvas->current_item;
1289 }
1291 if (canvas->focused_item &&
1292 ((event->type == GDK_KEY_PRESS) ||
1293 (event->type == GDK_KEY_RELEASE) ||
1294 (event->type == GDK_FOCUS_CHANGE))) {
1295 item = canvas->focused_item;
1296 }
1298 /* The event is propagated up the hierarchy (for if someone connected to
1299 * a group instead of a leaf event), and emission is stopped if a
1300 * handler returns TRUE, just like for GtkWidget events.
1301 */
1303 gint finished = FALSE;
1305 while (item && !finished) {
1306 gtk_object_ref (GTK_OBJECT (item));
1307 gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1308 SPCanvasItem *parent = item->parent;
1309 gtk_object_unref (GTK_OBJECT (item));
1310 item = parent;
1311 }
1313 return finished;
1314 }
1316 /**
1317 * Helper that re-picks the current item in the canvas, based on the event's
1318 * coordinates and emits enter/leave events for items as appropriate.
1319 */
1320 static int
1321 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1322 {
1323 int button_down = 0;
1324 double x, y;
1326 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1327 return FALSE;
1329 int retval = FALSE;
1331 if (canvas->gen_all_enter_events == false) {
1332 // If a button is down, we'll perform enter and leave events on the
1333 // current item, but not enter on any other item. This is more or
1334 // less like X pointer grabbing for canvas items.
1335 //
1336 button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1337 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1339 if (!button_down) canvas->left_grabbed_item = FALSE;
1340 }
1342 /* Save the event in the canvas. This is used to synthesize enter and
1343 * leave events in case the current item changes. It is also used to
1344 * re-pick the current item if the current one gets deleted. Also,
1345 * synthesize an enter event.
1346 */
1347 if (event != &canvas->pick_event) {
1348 if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1349 /* these fields have the same offsets in both types of events */
1351 canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY;
1352 canvas->pick_event.crossing.window = event->motion.window;
1353 canvas->pick_event.crossing.send_event = event->motion.send_event;
1354 canvas->pick_event.crossing.subwindow = NULL;
1355 canvas->pick_event.crossing.x = event->motion.x;
1356 canvas->pick_event.crossing.y = event->motion.y;
1357 canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL;
1358 canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR;
1359 canvas->pick_event.crossing.focus = FALSE;
1360 canvas->pick_event.crossing.state = event->motion.state;
1362 /* these fields don't have the same offsets in both types of events */
1364 if (event->type == GDK_MOTION_NOTIFY) {
1365 canvas->pick_event.crossing.x_root = event->motion.x_root;
1366 canvas->pick_event.crossing.y_root = event->motion.y_root;
1367 } else {
1368 canvas->pick_event.crossing.x_root = event->button.x_root;
1369 canvas->pick_event.crossing.y_root = event->button.y_root;
1370 }
1371 } else {
1372 canvas->pick_event = *event;
1373 }
1374 }
1376 /* Don't do anything else if this is a recursive call */
1377 if (canvas->in_repick) return retval;
1379 /* LeaveNotify means that there is no current item, so we don't look for one */
1380 if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1381 /* these fields don't have the same offsets in both types of events */
1383 if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1384 x = canvas->pick_event.crossing.x;
1385 y = canvas->pick_event.crossing.y;
1386 } else {
1387 x = canvas->pick_event.motion.x;
1388 y = canvas->pick_event.motion.y;
1389 }
1391 /* world coords */
1392 x += canvas->x0;
1393 y += canvas->y0;
1395 /* find the closest item */
1396 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1397 sp_canvas_item_invoke_point (canvas->root, NR::Point(x, y), &canvas->new_current_item);
1398 } else {
1399 canvas->new_current_item = NULL;
1400 }
1401 } else {
1402 canvas->new_current_item = NULL;
1403 }
1405 if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1406 return retval; /* current item did not change */
1407 }
1409 /* Synthesize events for old and new current items */
1411 if ((canvas->new_current_item != canvas->current_item)
1412 && (canvas->current_item != NULL)
1413 && !canvas->left_grabbed_item) {
1414 GdkEvent new_event;
1415 SPCanvasItem *item;
1417 item = canvas->current_item;
1419 new_event = canvas->pick_event;
1420 new_event.type = GDK_LEAVE_NOTIFY;
1422 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1423 new_event.crossing.subwindow = NULL;
1424 canvas->in_repick = TRUE;
1425 retval = emit_event (canvas, &new_event);
1426 canvas->in_repick = FALSE;
1427 }
1429 if (canvas->gen_all_enter_events == false) {
1430 // new_current_item may have been set to NULL during the call to
1431 // emit_event() above
1432 if ((canvas->new_current_item != canvas->current_item) && button_down) {
1433 canvas->left_grabbed_item = TRUE;
1434 return retval;
1435 }
1436 }
1438 /* Handle the rest of cases */
1440 canvas->left_grabbed_item = FALSE;
1441 canvas->current_item = canvas->new_current_item;
1443 if (canvas->current_item != NULL) {
1444 GdkEvent new_event;
1446 new_event = canvas->pick_event;
1447 new_event.type = GDK_ENTER_NOTIFY;
1448 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1449 new_event.crossing.subwindow = NULL;
1450 retval = emit_event (canvas, &new_event);
1451 }
1453 return retval;
1454 }
1456 /**
1457 * Button event handler for the canvas.
1458 */
1459 static gint
1460 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1461 {
1462 SPCanvas *canvas = SP_CANVAS (widget);
1464 int retval = FALSE;
1466 /* dispatch normally regardless of the event's window if an item has
1467 has a pointer grab in effect */
1468 if (!canvas->grabbed_item &&
1469 event->window != SP_CANVAS_WINDOW (canvas))
1470 return retval;
1472 int mask;
1473 switch (event->button) {
1474 case 1:
1475 mask = GDK_BUTTON1_MASK;
1476 break;
1477 case 2:
1478 mask = GDK_BUTTON2_MASK;
1479 break;
1480 case 3:
1481 mask = GDK_BUTTON3_MASK;
1482 break;
1483 case 4:
1484 mask = GDK_BUTTON4_MASK;
1485 break;
1486 case 5:
1487 mask = GDK_BUTTON5_MASK;
1488 break;
1489 default:
1490 mask = 0;
1491 }
1493 switch (event->type) {
1494 case GDK_BUTTON_PRESS:
1495 case GDK_2BUTTON_PRESS:
1496 case GDK_3BUTTON_PRESS:
1497 /* Pick the current item as if the button were not pressed, and
1498 * then process the event.
1499 */
1500 canvas->state = event->state;
1501 pick_current_item (canvas, (GdkEvent *) event);
1502 canvas->state ^= mask;
1503 retval = emit_event (canvas, (GdkEvent *) event);
1504 break;
1506 case GDK_BUTTON_RELEASE:
1507 /* Process the event as if the button were pressed, then repick
1508 * after the button has been released
1509 */
1510 canvas->state = event->state;
1511 retval = emit_event (canvas, (GdkEvent *) event);
1512 event->state ^= mask;
1513 canvas->state = event->state;
1514 pick_current_item (canvas, (GdkEvent *) event);
1515 event->state ^= mask;
1516 break;
1518 default:
1519 g_assert_not_reached ();
1520 }
1522 return retval;
1523 }
1525 /**
1526 * Scroll event handler for the canvas.
1527 *
1528 * \todo FIXME: generate motion events to re-select items.
1529 */
1530 static gint
1531 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1532 {
1533 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1534 }
1536 /**
1537 * Motion event handler for the canvas.
1538 */
1539 static int
1540 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1541 {
1542 SPCanvas *canvas = SP_CANVAS (widget);
1544 if (event->window != SP_CANVAS_WINDOW (canvas))
1545 return FALSE;
1547 if (canvas->grabbed_event_mask & GDK_POINTER_MOTION_HINT_MASK) {
1548 gint x, y;
1549 gdk_window_get_pointer (widget->window, &x, &y, NULL);
1550 event->x = x;
1551 event->y = y;
1552 }
1554 canvas->state = event->state;
1555 pick_current_item (canvas, (GdkEvent *) event);
1557 return emit_event (canvas, (GdkEvent *) event);
1558 }
1560 static void
1561 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)
1562 {
1563 GtkWidget *widget = GTK_WIDGET (canvas);
1565 SPCanvasBuf buf;
1566 if (canvas->rendermode != RENDERMODE_OUTLINE) {
1567 buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1568 } else {
1569 buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1570 }
1572 // Mark the region clean
1573 sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1575 buf.buf_rowstride = sw * 3; // CAIRO FIXME: for cairo output, the buffer must be RGB unpacked, i.e. sw * 4
1576 buf.rect.x0 = x0;
1577 buf.rect.y0 = y0;
1578 buf.rect.x1 = x1;
1579 buf.rect.y1 = y1;
1580 buf.visible_rect.x0 = draw_x1;
1581 buf.visible_rect.y0 = draw_y1;
1582 buf.visible_rect.x1 = draw_x2;
1583 buf.visible_rect.y1 = draw_y2;
1584 GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1585 buf.bg_color = (((color->red & 0xff00) << 8)
1586 | (color->green & 0xff00)
1587 | (color->blue >> 8));
1588 buf.is_empty = true;
1590 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1591 SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1592 }
1594 #if ENABLE_LCMS
1595 cmsHTRANSFORM transf = 0;
1596 long long int fromDisplay = prefs_get_int_attribute_limited( "options.displayprofile", "from_display", 0, 0, 1 );
1597 if ( fromDisplay ) {
1598 transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" );
1599 } else {
1600 transf = Inkscape::colorprofile_get_display_transform();
1601 }
1602 #endif // ENABLE_LCMS
1604 if (buf.is_empty) {
1605 #if ENABLE_LCMS
1606 if ( transf && canvas->enable_cms_display_adj ) {
1607 cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1608 }
1609 #endif // ENABLE_LCMS
1610 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1611 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1612 canvas->pixmap_gc,
1613 TRUE,
1614 x0 - canvas->x0, y0 - canvas->y0,
1615 x1 - x0, y1 - y0);
1616 } else {
1617 /*
1618 // CAIRO FIXME: after SPCanvasBuf is made 32bpp throughout, this rgb_draw below can be replaced with the below.
1619 // Why this must not be done currently:
1620 // - all canvas items (handles, nodes etc) paint themselves assuming 24bpp
1621 // - cairo assumes bgra, but we have rgba, so r and b get swapped (until we paint all with cairo too)
1622 // - it does not seem to be any faster; in fact since with 32bpp, buf contains less pixels,
1623 // we need more bufs to paint a given area and as a result it's even a bit slower
1625 cairo_surface_t* cst = cairo_image_surface_create_for_data (
1626 buf.buf,
1627 CAIRO_FORMAT_RGB24, // unpacked, i.e. 32 bits! one byte is unused
1628 x1 - x0, y1 - y0,
1629 buf.buf_rowstride
1630 );
1631 cairo_t *ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1632 cairo_set_source_surface (ct, cst, x0 - canvas->x0, y0 - canvas->y0);
1633 cairo_paint (ct);
1634 cairo_destroy (ct);
1635 cairo_surface_finish (cst);
1636 cairo_surface_destroy (cst);
1637 */
1639 #if ENABLE_LCMS
1640 if ( transf && canvas->enable_cms_display_adj ) {
1641 for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1642 guchar* p = buf.buf + (sw * 3) * yy;
1643 cmsDoTransform( transf, p, p, (x1 - x0) );
1644 }
1645 }
1646 #endif // ENABLE_LCMS
1648 gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1649 canvas->pixmap_gc,
1650 x0 - canvas->x0, y0 - canvas->y0,
1651 x1 - x0, y1 - y0,
1652 GDK_RGB_DITHER_MAX,
1653 buf.buf,
1654 sw * 3,
1655 x0 - canvas->x0, y0 - canvas->y0);
1656 }
1658 if (canvas->rendermode != RENDERMODE_OUTLINE) {
1659 nr_pixelstore_256K_free (buf.buf);
1660 } else {
1661 nr_pixelstore_1M_free (buf.buf);
1662 }
1663 }
1665 struct PaintRectSetup {
1666 SPCanvas* canvas;
1667 NRRectL big_rect;
1668 GTimeVal start_time;
1669 int max_pixels;
1670 NR::Point mouse_loc;
1671 };
1673 /**
1674 * Paint the given rect, recursively subdividing the region until it is the size of a single
1675 * buffer.
1676 *
1677 * @return true if the drawing completes
1678 */
1679 static int
1680 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1681 {
1682 GTimeVal now;
1683 g_get_current_time (&now);
1685 glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1686 + (now.tv_usec - setup->start_time.tv_usec);
1688 // Allow only very fast buffers to be run together;
1689 // as soon as the total redraw time exceeds 1ms, cancel;
1690 // this returns control to the idle loop and allows Inkscape to process user input
1691 // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1692 // it will get back and finish painting what remains to paint.
1693 if (elapsed > 1000) {
1695 // Interrupting redraw isn't always good.
1696 // For example, when you drag one node of a big path, only the buffer containing
1697 // the mouse cursor will be redrawn again and again, and the rest of the path
1698 // will remain stale because Inkscape never has enough idle time to redraw all
1699 // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1700 // If this limit is set, and if we have aborted redraw more times than is allowed,
1701 // interrupting is blocked and we're forced to redraw full screen once
1702 // (after which we can again interrupt forced_redraw_limit times).
1703 if (setup->canvas->forced_redraw_limit < 0 ||
1704 setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1706 if (setup->canvas->forced_redraw_limit != -1) {
1707 setup->canvas->forced_redraw_count++;
1708 }
1710 return false;
1711 }
1712 }
1714 // Find the optimal buffer dimensions
1715 int bw = this_rect.x1 - this_rect.x0;
1716 int bh = this_rect.y1 - this_rect.y0;
1717 if ((bw < 1) || (bh < 1))
1718 return 0;
1720 if (bw * bh < setup->max_pixels) {
1721 // We are small enough
1722 sp_canvas_paint_single_buffer (setup->canvas,
1723 this_rect.x0, this_rect.y0,
1724 this_rect.x1, this_rect.y1,
1725 setup->big_rect.x0, setup->big_rect.y0,
1726 setup->big_rect.x1, setup->big_rect.y1, bw);
1727 return 1;
1728 }
1730 NRRectL lo = this_rect;
1731 NRRectL hi = this_rect;
1733 /*
1734 This test determines the redraw strategy:
1736 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1737 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1738 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1739 and seems to be faster for drawings with many smaller objects at zoom-out.
1741 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1742 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1743 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1744 faster.
1746 The default for now is the strips mode.
1747 */
1748 if (bw < bh || bh < 2 * TILE_SIZE) {
1749 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1750 int mid = ((long long) this_rect.x0 + (long long) this_rect.x1) / 2;
1751 // Make sure that mid lies on a tile boundary
1752 mid = (mid / TILE_SIZE) * TILE_SIZE;
1754 lo.x1 = mid;
1755 hi.x0 = mid;
1757 if (setup->mouse_loc[NR::X] < mid) {
1758 // Always paint towards the mouse first
1759 return sp_canvas_paint_rect_internal(setup, lo)
1760 && sp_canvas_paint_rect_internal(setup, hi);
1761 } else {
1762 return sp_canvas_paint_rect_internal(setup, hi)
1763 && sp_canvas_paint_rect_internal(setup, lo);
1764 }
1765 } else {
1766 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1767 int mid = ((long long) this_rect.y0 + (long long) this_rect.y1) / 2;
1768 // Make sure that mid lies on a tile boundary
1769 mid = (mid / TILE_SIZE) * TILE_SIZE;
1771 lo.y1 = mid;
1772 hi.y0 = mid;
1774 if (setup->mouse_loc[NR::Y] < mid) {
1775 // Always paint towards the mouse first
1776 return sp_canvas_paint_rect_internal(setup, lo)
1777 && sp_canvas_paint_rect_internal(setup, hi);
1778 } else {
1779 return sp_canvas_paint_rect_internal(setup, hi)
1780 && sp_canvas_paint_rect_internal(setup, lo);
1781 }
1782 }
1783 }
1786 /**
1787 * Helper that draws a specific rectangular part of the canvas.
1788 *
1789 * @return true if the rectangle painting succeeds.
1790 */
1791 static bool
1792 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1793 {
1794 g_return_val_if_fail (!canvas->need_update, false);
1796 NRRectL rect;
1797 rect.x0 = xx0;
1798 rect.x1 = xx1;
1799 rect.y0 = yy0;
1800 rect.y1 = yy1;
1802 // Clip rect-to-draw by the current visible area
1803 rect.x0 = MAX (rect.x0, canvas->x0);
1804 rect.y0 = MAX (rect.y0, canvas->y0);
1805 rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1806 rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1808 #ifdef DEBUG_REDRAW
1809 // paint the area to redraw yellow
1810 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1811 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1812 canvas->pixmap_gc,
1813 TRUE,
1814 rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1815 rect.x1 - rect.x0, rect.y1 - rect.y0);
1816 #endif
1818 PaintRectSetup setup;
1820 setup.canvas = canvas;
1821 setup.big_rect = rect;
1823 // Save the mouse location
1824 gint x, y;
1825 gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1826 setup.mouse_loc = sp_canvas_window_to_world (canvas, NR::Point(x,y));
1828 // CAIRO FIXME: the sw/sh calculations below all assume 24bpp, need fixing for 32bpp
1829 if (canvas->rendermode != RENDERMODE_OUTLINE) {
1830 // use 256K as a compromise to not slow down gradients
1831 // 256K is the cached buffer and we need 3 channels
1832 setup.max_pixels = 87381; // 256K/3
1833 } else {
1834 // paths only, so 1M works faster
1835 // 1M is the cached buffer and we need 3 channels
1836 setup.max_pixels = 349525;
1837 }
1839 // Start the clock
1840 g_get_current_time(&(setup.start_time));
1842 // Go
1843 return sp_canvas_paint_rect_internal(&setup, rect);
1844 }
1846 /**
1847 * Force a full redraw after a specified number of interrupted redraws
1848 */
1849 void
1850 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1851 g_return_if_fail(canvas != NULL);
1853 canvas->forced_redraw_limit = count;
1854 canvas->forced_redraw_count = 0;
1855 }
1857 /**
1858 * End forced full redraw requests
1859 */
1860 void
1861 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1862 g_return_if_fail(canvas != NULL);
1864 canvas->forced_redraw_limit = -1;
1865 }
1867 /**
1868 * The canvas widget's expose callback.
1869 */
1870 static gint
1871 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1872 {
1873 SPCanvas *canvas = SP_CANVAS (widget);
1875 if (!GTK_WIDGET_DRAWABLE (widget) ||
1876 (event->window != SP_CANVAS_WINDOW (canvas)))
1877 return FALSE;
1879 int n_rects;
1880 GdkRectangle *rects;
1881 gdk_region_get_rectangles (event->region, &rects, &n_rects);
1883 for (int i = 0; i < n_rects; i++) {
1884 NRRectL rect;
1886 rect.x0 = rects[i].x + canvas->x0;
1887 rect.y0 = rects[i].y + canvas->y0;
1888 rect.x1 = rect.x0 + rects[i].width;
1889 rect.y1 = rect.y0 + rects[i].height;
1891 sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1892 }
1894 if (n_rects > 0)
1895 g_free (rects);
1897 return FALSE;
1898 }
1900 /**
1901 * The canvas widget's keypress callback.
1902 */
1903 static gint
1904 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1905 {
1906 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1907 }
1909 /**
1910 * Crossing event handler for the canvas.
1911 */
1912 static gint
1913 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1914 {
1915 SPCanvas *canvas = SP_CANVAS (widget);
1917 if (event->window != SP_CANVAS_WINDOW (canvas))
1918 return FALSE;
1920 canvas->state = event->state;
1921 return pick_current_item (canvas, (GdkEvent *) event);
1922 }
1924 /**
1925 * Focus in handler for the canvas.
1926 */
1927 static gint
1928 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
1929 {
1930 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1932 SPCanvas *canvas = SP_CANVAS (widget);
1934 if (canvas->focused_item) {
1935 return emit_event (canvas, (GdkEvent *) event);
1936 } else {
1937 return FALSE;
1938 }
1939 }
1941 /**
1942 * Focus out handler for the canvas.
1943 */
1944 static gint
1945 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
1946 {
1947 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1949 SPCanvas *canvas = SP_CANVAS (widget);
1951 if (canvas->focused_item)
1952 return emit_event (canvas, (GdkEvent *) event);
1953 else
1954 return FALSE;
1955 }
1957 /**
1958 * Helper that repaints the areas in the canvas that need it.
1959 *
1960 * @return true if all the dirty parts have been redrawn
1961 */
1962 static int
1963 paint (SPCanvas *canvas)
1964 {
1965 if (canvas->need_update) {
1966 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
1967 canvas->need_update = FALSE;
1968 }
1970 if (!canvas->need_redraw)
1971 return TRUE;
1973 Gdk::Region to_paint;
1975 for (int j=canvas->tTop; j<canvas->tBottom; j++) {
1976 for (int i=canvas->tLeft; i<canvas->tRight; i++) {
1977 int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
1979 if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
1980 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
1981 TILE_SIZE, TILE_SIZE));
1982 }
1984 }
1985 }
1987 if (!to_paint.empty()) {
1988 Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
1989 typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
1990 for (Iter i=rect.begin(); i != rect.end(); ++i) {
1991 int x0 = (*i).get_x();
1992 int y0 = (*i).get_y();
1993 int x1 = x0 + (*i).get_width();
1994 int y1 = y0 + (*i).get_height();
1995 if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
1996 // Aborted
1997 return FALSE;
1998 };
1999 }
2000 }
2002 canvas->need_redraw = FALSE;
2004 // we've had a full unaborted redraw, reset the full redraw counter
2005 if (canvas->forced_redraw_limit != -1) {
2006 canvas->forced_redraw_count = 0;
2007 }
2009 return TRUE;
2010 }
2012 /**
2013 * Helper that invokes update, paint, and repick on canvas.
2014 */
2015 static int
2016 do_update (SPCanvas *canvas)
2017 {
2018 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
2019 return TRUE;
2021 /* Cause the update if necessary */
2022 if (canvas->need_update) {
2023 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2024 canvas->need_update = FALSE;
2025 }
2027 /* Paint if able to */
2028 if (GTK_WIDGET_DRAWABLE (canvas)) {
2029 return paint (canvas);
2030 }
2032 /* Pick new current item */
2033 while (canvas->need_repick) {
2034 canvas->need_repick = FALSE;
2035 pick_current_item (canvas, &canvas->pick_event);
2036 }
2038 return TRUE;
2039 }
2041 /**
2042 * Idle handler for the canvas that deals with pending updates and redraws.
2043 */
2044 static gint
2045 idle_handler (gpointer data)
2046 {
2047 GDK_THREADS_ENTER ();
2049 SPCanvas *canvas = SP_CANVAS (data);
2051 int const ret = do_update (canvas);
2053 if (ret) {
2054 /* Reset idle id */
2055 canvas->idle_id = 0;
2056 }
2058 GDK_THREADS_LEAVE ();
2060 return !ret;
2061 }
2063 /**
2064 * Convenience function to add an idle handler to a canvas.
2065 */
2066 static void
2067 add_idle (SPCanvas *canvas)
2068 {
2069 if (canvas->idle_id != 0)
2070 return;
2072 canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2073 }
2075 /**
2076 * Returns the root group of the specified canvas.
2077 */
2078 SPCanvasGroup *
2079 sp_canvas_root (SPCanvas *canvas)
2080 {
2081 g_return_val_if_fail (canvas != NULL, NULL);
2082 g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2084 return SP_CANVAS_GROUP (canvas->root);
2085 }
2087 /**
2088 * Scrolls canvas to specific position (cx and cy are measured in screen pixels)
2089 */
2090 void
2091 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2092 {
2093 g_return_if_fail (canvas != NULL);
2094 g_return_if_fail (SP_IS_CANVAS (canvas));
2096 int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels)
2097 int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do!
2098 int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the
2099 int dy = iy - canvas->y0; // canvas w.r.t its previous position
2101 canvas->dx0 = cx; // here the 'd' stands for double, not delta!
2102 canvas->dy0 = cy;
2103 canvas->x0 = ix;
2104 canvas->y0 = iy;
2106 sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2108 if (!clear) {
2109 // scrolling without zoom; redraw only the newly exposed areas
2110 if ((dx != 0) || (dy != 0)) {
2111 canvas->is_scrolling = is_scrolling;
2112 if (GTK_WIDGET_REALIZED (canvas)) {
2113 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2114 }
2115 }
2116 } else {
2117 // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2118 }
2119 }
2121 /**
2122 * Updates canvas if necessary.
2123 */
2124 void
2125 sp_canvas_update_now (SPCanvas *canvas)
2126 {
2127 g_return_if_fail (canvas != NULL);
2128 g_return_if_fail (SP_IS_CANVAS (canvas));
2130 if (!(canvas->need_update ||
2131 canvas->need_redraw))
2132 return;
2134 do_update (canvas);
2135 }
2137 /**
2138 * Update callback for canvas widget.
2139 */
2140 static void
2141 sp_canvas_request_update (SPCanvas *canvas)
2142 {
2143 canvas->need_update = TRUE;
2144 add_idle (canvas);
2145 }
2147 /**
2148 * Forces redraw of rectangular canvas area.
2149 */
2150 void
2151 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2152 {
2153 NRRectL bbox;
2154 NRRectL visible;
2155 NRRectL clip;
2157 g_return_if_fail (canvas != NULL);
2158 g_return_if_fail (SP_IS_CANVAS (canvas));
2160 if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2161 if ((x0 >= x1) || (y0 >= y1)) return;
2163 bbox.x0 = x0;
2164 bbox.y0 = y0;
2165 bbox.x1 = x1;
2166 bbox.y1 = y1;
2168 visible.x0 = canvas->x0;
2169 visible.y0 = canvas->y0;
2170 visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2171 visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2173 nr_rect_l_intersect (&clip, &bbox, &visible);
2175 sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2176 add_idle (canvas);
2177 }
2179 /**
2180 * Sets world coordinates from win and canvas.
2181 */
2182 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2183 {
2184 g_return_if_fail (canvas != NULL);
2185 g_return_if_fail (SP_IS_CANVAS (canvas));
2187 if (worldx) *worldx = canvas->x0 + winx;
2188 if (worldy) *worldy = canvas->y0 + winy;
2189 }
2191 /**
2192 * Sets win coordinates from world and canvas.
2193 */
2194 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2195 {
2196 g_return_if_fail (canvas != NULL);
2197 g_return_if_fail (SP_IS_CANVAS (canvas));
2199 if (winx) *winx = worldx - canvas->x0;
2200 if (winy) *winy = worldy - canvas->y0;
2201 }
2203 /**
2204 * Converts point from win to world coordinates.
2205 */
2206 NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win)
2207 {
2208 g_assert (canvas != NULL);
2209 g_assert (SP_IS_CANVAS (canvas));
2211 return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2212 }
2214 /**
2215 * Converts point from world to win coordinates.
2216 */
2217 NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world)
2218 {
2219 g_assert (canvas != NULL);
2220 g_assert (SP_IS_CANVAS (canvas));
2222 return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2223 }
2225 /**
2226 * Returns true if point given in world coordinates is inside window.
2227 */
2228 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world)
2229 {
2230 g_assert( canvas != NULL );
2231 g_assert(SP_IS_CANVAS(canvas));
2233 using NR::X;
2234 using NR::Y;
2235 GtkWidget const &w = *GTK_WIDGET(canvas);
2236 return ( ( canvas->x0 <= world[X] ) &&
2237 ( canvas->y0 <= world[Y] ) &&
2238 ( world[X] < canvas->x0 + w.allocation.width ) &&
2239 ( world[Y] < canvas->y0 + w.allocation.height ) );
2240 }
2242 /**
2243 * Return canvas window coordinates as NR::Rect.
2244 */
2245 NR::Rect SPCanvas::getViewbox() const
2246 {
2247 GtkWidget const *w = GTK_WIDGET(this);
2248 return NR::Rect(NR::Point(dx0, dy0),
2249 NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2250 }
2252 /**
2253 * Return canvas window coordinates as IRect (a rectangle defined by integers).
2254 */
2255 NR::IRect SPCanvas::getViewboxIntegers() const
2256 {
2257 GtkWidget const *w = GTK_WIDGET(this);
2258 return NR::IRect(NR::IPoint(x0, y0),
2259 NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height));
2260 }
2262 inline int sp_canvas_tile_floor(int x)
2263 {
2264 return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2265 }
2267 inline int sp_canvas_tile_ceil(int x)
2268 {
2269 return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2270 }
2272 /**
2273 * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2274 */
2275 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2276 {
2277 if ( nl >= nr || nt >= nb ) {
2278 if ( canvas->tiles ) g_free(canvas->tiles);
2279 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2280 canvas->tileH=canvas->tileV=0;
2281 canvas->tiles=NULL;
2282 return;
2283 }
2284 int tl=sp_canvas_tile_floor(nl);
2285 int tt=sp_canvas_tile_floor(nt);
2286 int tr=sp_canvas_tile_ceil(nr);
2287 int tb=sp_canvas_tile_ceil(nb);
2289 int nh = tr-tl, nv = tb-tt;
2290 uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2291 for (int i=tl; i<tr; i++) {
2292 for (int j=tt; j<tb; j++) {
2293 int ind = (i-tl) + (j-tt)*nh;
2294 if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2295 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2296 } else {
2297 ntiles[ind]=0; // newly exposed areas get 0
2298 }
2299 }
2300 }
2301 if ( canvas->tiles ) g_free(canvas->tiles);
2302 canvas->tiles=ntiles;
2303 canvas->tLeft=tl;
2304 canvas->tTop=tt;
2305 canvas->tRight=tr;
2306 canvas->tBottom=tb;
2307 canvas->tileH=nh;
2308 canvas->tileV=nv;
2309 }
2311 /*
2312 * Helper that queues a canvas rectangle for redraw
2313 */
2314 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2315 canvas->need_redraw = TRUE;
2317 sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2318 }
2320 /**
2321 * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2322 */
2323 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2324 {
2325 if ( nl >= nr || nt >= nb ) {
2326 return;
2327 }
2328 int tl=sp_canvas_tile_floor(nl);
2329 int tt=sp_canvas_tile_floor(nt);
2330 int tr=sp_canvas_tile_ceil(nr);
2331 int tb=sp_canvas_tile_ceil(nb);
2332 if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2333 if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2334 if ( tr > canvas->tRight ) tr=canvas->tRight;
2335 if ( tt < canvas->tTop ) tt=canvas->tTop;
2336 if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2338 for (int i=tl; i<tr; i++) {
2339 for (int j=tt; j<tb; j++) {
2340 canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2341 }
2342 }
2343 }
2346 /*
2347 Local Variables:
2348 mode:c++
2349 c-file-style:"stroustrup"
2350 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2351 indent-tabs-mode:nil
2352 fill-column:99
2353 End:
2354 */
2355 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :