2af12b6c373525cc91c233c742630b613b8b269a
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 "box3d-context.h"
38 #include "inkscape.h"
39 #include "sodipodi-ctrlrect.h"
40 #if ENABLE_LCMS
41 #include "color-profile-fns.h"
42 #endif // ENABLE_LCMS
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 enum {
53 RENDERMODE_NORMAL,
54 RENDERMODE_NOAA,
55 RENDERMODE_OUTLINE
56 };
58 static gint const sp_canvas_update_priority = G_PRIORITY_HIGH_IDLE;
60 #define SP_CANVAS_WINDOW(c) (((GtkWidget *) (c))->window)
62 enum {
63 SP_CANVAS_ITEM_VISIBLE = 1 << 7,
64 SP_CANVAS_ITEM_NEED_UPDATE = 1 << 8,
65 SP_CANVAS_ITEM_NEED_AFFINE = 1 << 9
66 };
68 /**
69 * A group of Items.
70 */
71 struct SPCanvasGroup {
72 SPCanvasItem item;
74 GList *items, *last;
75 };
77 /**
78 * The SPCanvasGroup vtable.
79 */
80 struct SPCanvasGroupClass {
81 SPCanvasItemClass parent_class;
82 };
84 /**
85 * The SPCanvas vtable.
86 */
87 struct SPCanvasClass {
88 GtkWidgetClass parent_class;
89 };
91 static void group_add (SPCanvasGroup *group, SPCanvasItem *item);
92 static void group_remove (SPCanvasGroup *group, SPCanvasItem *item);
94 /* SPCanvasItem */
96 enum {ITEM_EVENT, ITEM_LAST_SIGNAL};
99 static void sp_canvas_request_update (SPCanvas *canvas);
101 static void sp_canvas_item_class_init (SPCanvasItemClass *klass);
102 static void sp_canvas_item_init (SPCanvasItem *item);
103 static void sp_canvas_item_dispose (GObject *object);
104 static void sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args);
106 static int emit_event (SPCanvas *canvas, GdkEvent *event);
108 static guint item_signals[ITEM_LAST_SIGNAL] = { 0 };
110 static GtkObjectClass *item_parent_class;
112 /**
113 * Registers the SPCanvasItem class with Glib and returns its type number.
114 */
115 GType
116 sp_canvas_item_get_type (void)
117 {
118 static GType type = 0;
119 if (!type) {
120 static GTypeInfo const info = {
121 sizeof (SPCanvasItemClass),
122 NULL, NULL,
123 (GClassInitFunc) sp_canvas_item_class_init,
124 NULL, NULL,
125 sizeof (SPCanvasItem),
126 0,
127 (GInstanceInitFunc) sp_canvas_item_init,
128 NULL
129 };
130 type = g_type_register_static (GTK_TYPE_OBJECT, "SPCanvasItem", &info, (GTypeFlags)0);
131 }
133 return type;
134 }
136 /**
137 * Initializes the SPCanvasItem vtable and the "event" signal.
138 */
139 static void
140 sp_canvas_item_class_init (SPCanvasItemClass *klass)
141 {
142 GObjectClass *object_class = (GObjectClass *) klass;
144 /* fixme: Derive from GObject */
145 item_parent_class = (GtkObjectClass*)gtk_type_class (GTK_TYPE_OBJECT);
147 item_signals[ITEM_EVENT] = g_signal_new ("event",
148 G_TYPE_FROM_CLASS (klass),
149 G_SIGNAL_RUN_LAST,
150 G_STRUCT_OFFSET (SPCanvasItemClass, event),
151 NULL, NULL,
152 sp_marshal_BOOLEAN__POINTER,
153 G_TYPE_BOOLEAN, 1,
154 GDK_TYPE_EVENT);
156 object_class->dispose = sp_canvas_item_dispose;
157 }
159 /**
160 * Callback for initialization of SPCanvasItem.
161 */
162 static void
163 sp_canvas_item_init (SPCanvasItem *item)
164 {
165 item->flags |= SP_CANVAS_ITEM_VISIBLE;
166 item->xform = NR::Matrix(NR::identity());
167 }
169 /**
170 * Constructs new SPCanvasItem on SPCanvasGroup.
171 */
172 SPCanvasItem *
173 sp_canvas_item_new (SPCanvasGroup *parent, GtkType type, gchar const *first_arg_name, ...)
174 {
175 va_list args;
177 g_return_val_if_fail (parent != NULL, NULL);
178 g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL);
179 g_return_val_if_fail (gtk_type_is_a (type, sp_canvas_item_get_type ()), NULL);
181 SPCanvasItem *item = SP_CANVAS_ITEM (gtk_type_new (type));
183 va_start (args, first_arg_name);
184 sp_canvas_item_construct (item, parent, first_arg_name, args);
185 va_end (args);
187 return item;
188 }
190 /**
191 * Sets up the newly created SPCanvasItem.
192 *
193 * We make it static for encapsulation reasons since it was nowhere used.
194 */
195 static void
196 sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args)
197 {
198 g_return_if_fail (SP_IS_CANVAS_GROUP (parent));
199 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
201 item->parent = SP_CANVAS_ITEM (parent);
202 item->canvas = item->parent->canvas;
204 g_object_set_valist (G_OBJECT (item), first_arg_name, args);
206 group_add (SP_CANVAS_GROUP (item->parent), item);
208 sp_canvas_item_request_update (item);
209 }
211 /**
212 * Helper function that requests redraw only if item's visible flag is set.
213 */
214 static void
215 redraw_if_visible (SPCanvasItem *item)
216 {
217 if (item->flags & SP_CANVAS_ITEM_VISIBLE) {
218 int x0 = (int)(item->x1);
219 int x1 = (int)(item->x2);
220 int y0 = (int)(item->y1);
221 int y1 = (int)(item->y2);
223 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
224 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
225 }
226 }
227 }
229 /**
230 * Callback that removes item from all referers and destroys it.
231 */
232 static void
233 sp_canvas_item_dispose (GObject *object)
234 {
235 SPCanvasItem *item = SP_CANVAS_ITEM (object);
237 // Hack: if this is a ctrlrect, move it to 0,0;
238 // this redraws only the stroke of the rect to be deleted,
239 // avoiding redraw of the entire area
240 if (SP_IS_CTRLRECT(item)) {
241 SP_CTRLRECT(object)->setRectangle(NR::Rect(NR::Point(0,0),NR::Point(0,0)));
242 SP_CTRLRECT(object)->update(item->xform, 0);
243 } else {
244 redraw_if_visible (item);
245 }
246 item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
248 if (item == item->canvas->current_item) {
249 item->canvas->current_item = NULL;
250 item->canvas->need_repick = TRUE;
251 }
253 if (item == item->canvas->new_current_item) {
254 item->canvas->new_current_item = NULL;
255 item->canvas->need_repick = TRUE;
256 }
258 if (item == item->canvas->grabbed_item) {
259 item->canvas->grabbed_item = NULL;
260 gdk_pointer_ungrab (GDK_CURRENT_TIME);
261 }
263 if (item == item->canvas->focused_item)
264 item->canvas->focused_item = NULL;
266 if (item->parent) {
267 group_remove (SP_CANVAS_GROUP (item->parent), item);
268 }
270 G_OBJECT_CLASS (item_parent_class)->dispose (object);
271 }
273 /**
274 * Helper function to update item and its children.
275 *
276 * NB! affine is parent2canvas.
277 */
278 static void
279 sp_canvas_item_invoke_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
280 {
281 /* Apply the child item's transform */
282 NR::Matrix child_affine = item->xform * affine;
284 /* apply object flags to child flags */
285 int child_flags = flags & ~SP_CANVAS_UPDATE_REQUESTED;
287 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
288 child_flags |= SP_CANVAS_UPDATE_REQUESTED;
290 if (item->flags & SP_CANVAS_ITEM_NEED_AFFINE)
291 child_flags |= SP_CANVAS_UPDATE_AFFINE;
293 if (child_flags & (SP_CANVAS_UPDATE_REQUESTED | SP_CANVAS_UPDATE_AFFINE)) {
294 if (SP_CANVAS_ITEM_GET_CLASS (item)->update)
295 SP_CANVAS_ITEM_GET_CLASS (item)->update (item, child_affine, child_flags);
296 }
298 GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_UPDATE);
299 GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_AFFINE);
300 }
302 /**
303 * Helper function to invoke the point method of the item.
304 *
305 * The argument x, y should be in the parent's item-relative coordinate
306 * system. This routine applies the inverse of the item's transform,
307 * maintaining the affine invariant.
308 */
309 static double
310 sp_canvas_item_invoke_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
311 {
312 if (SP_CANVAS_ITEM_GET_CLASS (item)->point)
313 return SP_CANVAS_ITEM_GET_CLASS (item)->point (item, p, actual_item);
315 return NR_HUGE;
316 }
318 /**
319 * Makes the item's affine transformation matrix be equal to the specified
320 * matrix.
321 *
322 * @item: A canvas item.
323 * @affine: An affine transformation matrix.
324 */
325 void
326 sp_canvas_item_affine_absolute (SPCanvasItem *item, NR::Matrix const &affine)
327 {
328 item->xform = affine;
330 if (!(item->flags & SP_CANVAS_ITEM_NEED_AFFINE)) {
331 item->flags |= SP_CANVAS_ITEM_NEED_AFFINE;
332 if (item->parent != NULL) {
333 sp_canvas_item_request_update (item->parent);
334 } else {
335 sp_canvas_request_update (item->canvas);
336 }
337 }
339 item->canvas->need_repick = TRUE;
340 }
342 /**
343 * Convenience function to reorder items in a group's child list.
344 *
345 * This puts the specified link after the "before" link.
346 */
347 static void
348 put_item_after (GList *link, GList *before)
349 {
350 if (link == before)
351 return;
353 SPCanvasGroup *parent = SP_CANVAS_GROUP (SP_CANVAS_ITEM (link->data)->parent);
355 if (before == NULL) {
356 if (link == parent->items) return;
358 link->prev->next = link->next;
360 if (link->next) {
361 link->next->prev = link->prev;
362 } else {
363 parent->last = link->prev;
364 }
366 link->prev = before;
367 link->next = parent->items;
368 link->next->prev = link;
369 parent->items = link;
370 } else {
371 if ((link == parent->last) && (before == parent->last->prev))
372 return;
374 if (link->next)
375 link->next->prev = link->prev;
377 if (link->prev)
378 link->prev->next = link->next;
379 else {
380 parent->items = link->next;
381 parent->items->prev = NULL;
382 }
384 link->prev = before;
385 link->next = before->next;
387 link->prev->next = link;
389 if (link->next)
390 link->next->prev = link;
391 else
392 parent->last = link;
393 }
394 }
397 /**
398 * Raises the item in its parent's stack by the specified number of positions.
399 *
400 * \param item A canvas item.
401 * \param positions Number of steps to raise the item.
402 *
403 * If the number of positions is greater than the distance to the top of the
404 * stack, then the item is put at the top.
405 */
406 void
407 sp_canvas_item_raise (SPCanvasItem *item, int positions)
408 {
409 g_return_if_fail (item != NULL);
410 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
411 g_return_if_fail (positions >= 0);
413 if (!item->parent || positions == 0)
414 return;
416 SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
417 GList *link = g_list_find (parent->items, item);
418 g_assert (link != NULL);
420 GList *before;
421 for (before = link; positions && before; positions--)
422 before = before->next;
424 if (!before)
425 before = parent->last;
427 put_item_after (link, before);
429 redraw_if_visible (item);
430 item->canvas->need_repick = TRUE;
431 }
434 /**
435 * Lowers the item in its parent's stack by the specified number of positions.
436 *
437 * \param item A canvas item.
438 * \param positions Number of steps to lower the item.
439 *
440 * If the number of positions is greater than the distance to the bottom of the
441 * stack, then the item is put at the bottom.
442 **/
443 void
444 sp_canvas_item_lower (SPCanvasItem *item, int positions)
445 {
446 g_return_if_fail (item != NULL);
447 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
448 g_return_if_fail (positions >= 1);
450 if (!item->parent || positions == 0)
451 return;
453 SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
454 GList *link = g_list_find (parent->items, item);
455 g_assert (link != NULL);
457 GList *before;
458 if (link->prev)
459 for (before = link->prev; positions && before; positions--)
460 before = before->prev;
461 else
462 before = NULL;
464 put_item_after (link, before);
466 redraw_if_visible (item);
467 item->canvas->need_repick = TRUE;
468 }
470 /**
471 * Sets visible flag on item and requests a redraw.
472 */
473 void
474 sp_canvas_item_show (SPCanvasItem *item)
475 {
476 g_return_if_fail (item != NULL);
477 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
479 if (item->flags & SP_CANVAS_ITEM_VISIBLE)
480 return;
482 item->flags |= SP_CANVAS_ITEM_VISIBLE;
484 int x0 = (int)(item->x1);
485 int x1 = (int)(item->x2);
486 int y0 = (int)(item->y1);
487 int y1 = (int)(item->y2);
489 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
490 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
491 item->canvas->need_repick = TRUE;
492 }
493 }
495 /**
496 * Clears visible flag on item and requests a redraw.
497 */
498 void
499 sp_canvas_item_hide (SPCanvasItem *item)
500 {
501 g_return_if_fail (item != NULL);
502 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
504 if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
505 return;
507 item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
509 int x0 = (int)(item->x1);
510 int x1 = (int)(item->x2);
511 int y0 = (int)(item->y1);
512 int y1 = (int)(item->y2);
514 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
515 sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
516 item->canvas->need_repick = TRUE;
517 }
518 }
520 /**
521 * Grab item under cursor.
522 *
523 * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE
524 */
525 int
526 sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
527 {
528 g_return_val_if_fail (item != NULL, -1);
529 g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1);
530 g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1);
532 if (item->canvas->grabbed_item)
533 return -1;
535 if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
536 return -1;
538 /* fixme: Top hack (Lauris) */
539 /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
540 /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
541 gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
542 (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
543 NULL, cursor, etime);
545 item->canvas->grabbed_item = item;
546 item->canvas->grabbed_event_mask = event_mask;
547 item->canvas->current_item = item; /* So that events go to the grabbed item */
549 return 0;
550 }
552 /**
553 * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
554 * mouse.
555 *
556 * \param item A canvas item that holds a grab.
557 * \param etime The timestamp for ungrabbing the mouse.
558 */
559 void
560 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
561 {
562 g_return_if_fail (item != NULL);
563 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
565 if (item->canvas->grabbed_item != item)
566 return;
568 item->canvas->grabbed_item = NULL;
570 gdk_pointer_ungrab (etime);
571 }
573 /**
574 * Returns the product of all transformation matrices from the root item down
575 * to the item.
576 */
577 NR::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
578 {
579 g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
581 NR::Matrix affine = NR::identity();
583 while (item) {
584 affine *= item->xform;
585 item = item->parent;
586 }
587 return affine;
588 }
590 /**
591 * Helper that returns true iff item is descendant of parent.
592 */
593 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
594 {
595 while (item) {
596 if (item == parent)
597 return true;
598 item = item->parent;
599 }
601 return false;
602 }
604 /**
605 * Focus canvas, and item under cursor if it is not already focussed.
606 */
607 void
608 sp_canvas_item_grab_focus (SPCanvasItem *item)
609 {
610 g_return_if_fail (item != NULL);
611 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
612 g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
614 SPCanvasItem *focused_item = item->canvas->focused_item;
616 if (focused_item) {
617 GdkEvent ev;
618 ev.focus_change.type = GDK_FOCUS_CHANGE;
619 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
620 ev.focus_change.send_event = FALSE;
621 ev.focus_change.in = FALSE;
623 emit_event (item->canvas, &ev);
624 }
626 item->canvas->focused_item = item;
627 gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
629 if (focused_item) {
630 GdkEvent ev;
631 ev.focus_change.type = GDK_FOCUS_CHANGE;
632 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
633 ev.focus_change.send_event = FALSE;
634 ev.focus_change.in = TRUE;
636 emit_event (item->canvas, &ev);
637 }
638 }
640 /**
641 * Requests that the canvas queue an update for the specified item.
642 *
643 * To be used only by item implementations.
644 */
645 void
646 sp_canvas_item_request_update (SPCanvasItem *item)
647 {
648 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
649 return;
651 item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
653 if (item->parent != NULL) {
654 /* Recurse up the tree */
655 sp_canvas_item_request_update (item->parent);
656 } else {
657 /* Have reached the top of the tree, make sure the update call gets scheduled. */
658 sp_canvas_request_update (item->canvas);
659 }
660 }
662 /**
663 * Returns position of item in group.
664 */
665 gint sp_canvas_item_order (SPCanvasItem * item)
666 {
667 return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
668 }
670 /* SPCanvasGroup */
672 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
673 static void sp_canvas_group_init (SPCanvasGroup *group);
674 static void sp_canvas_group_destroy (GtkObject *object);
676 static void sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
677 static double sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item);
678 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
680 static SPCanvasItemClass *group_parent_class;
682 /**
683 * Registers SPCanvasGroup class with Gtk and returns its type number.
684 */
685 GtkType
686 sp_canvas_group_get_type (void)
687 {
688 static GtkType group_type = 0;
690 if (!group_type) {
691 static GtkTypeInfo const group_info = {
692 "SPCanvasGroup",
693 sizeof (SPCanvasGroup),
694 sizeof (SPCanvasGroupClass),
695 (GtkClassInitFunc) sp_canvas_group_class_init,
696 (GtkObjectInitFunc) sp_canvas_group_init,
697 NULL, NULL, NULL
698 };
700 group_type = gtk_type_unique (sp_canvas_item_get_type (), &group_info);
701 }
703 return group_type;
704 }
706 /**
707 * Class initialization function for SPCanvasGroupClass
708 */
709 static void
710 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
711 {
712 GtkObjectClass *object_class = (GtkObjectClass *) klass;
713 SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
715 group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
717 object_class->destroy = sp_canvas_group_destroy;
719 item_class->update = sp_canvas_group_update;
720 item_class->render = sp_canvas_group_render;
721 item_class->point = sp_canvas_group_point;
722 }
724 /**
725 * Callback. Empty.
726 */
727 static void
728 sp_canvas_group_init (SPCanvasGroup */*group*/)
729 {
730 /* Nothing here */
731 }
733 /**
734 * Callback that destroys all items in group and calls group's virtual
735 * destroy() function.
736 */
737 static void
738 sp_canvas_group_destroy (GtkObject *object)
739 {
740 g_return_if_fail (object != NULL);
741 g_return_if_fail (SP_IS_CANVAS_GROUP (object));
743 SPCanvasGroup const *group = SP_CANVAS_GROUP (object);
745 GList *list = group->items;
746 while (list) {
747 SPCanvasItem *child = (SPCanvasItem *)list->data;
748 list = list->next;
750 gtk_object_destroy (GTK_OBJECT (child));
751 }
753 if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
754 (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
755 }
757 /**
758 * Update handler for canvas groups
759 */
760 static void
761 sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
762 {
763 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
764 NR::ConvexHull corners(NR::Point(0, 0));
765 bool empty=true;
767 for (GList *list = group->items; list; list = list->next) {
768 SPCanvasItem *i = (SPCanvasItem *)list->data;
770 sp_canvas_item_invoke_update (i, affine, flags);
772 if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
773 if (empty) {
774 corners = NR::ConvexHull(NR::Point(i->x1, i->y1));
775 empty = false;
776 } else {
777 corners.add(NR::Point(i->x1, i->y1));
778 }
779 corners.add(NR::Point(i->x2, i->y2));
780 }
781 }
783 NR::Maybe<NR::Rect> const bounds = corners.bounds();
784 if (bounds) {
785 item->x1 = bounds->min()[NR::X];
786 item->y1 = bounds->min()[NR::Y];
787 item->x2 = bounds->max()[NR::X];
788 item->y2 = bounds->max()[NR::Y];
789 } else {
790 // FIXME ?
791 item->x1 = item->x2 = item->y1 = item->y2 = 0;
792 }
793 }
795 /**
796 * Point handler for canvas groups.
797 */
798 static double
799 sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
800 {
801 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
802 double const x = p[NR::X];
803 double const y = p[NR::Y];
804 int x1 = (int)(x - item->canvas->close_enough);
805 int y1 = (int)(y - item->canvas->close_enough);
806 int x2 = (int)(x + item->canvas->close_enough);
807 int y2 = (int)(y + item->canvas->close_enough);
809 double best = 0.0;
810 *actual_item = NULL;
812 double dist = 0.0;
814 for (GList *list = group->items; list; list = list->next) {
815 SPCanvasItem *child = (SPCanvasItem *)list->data;
817 if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
818 SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
820 int has_point;
821 if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
822 dist = sp_canvas_item_invoke_point (child, p, &point_item);
823 has_point = TRUE;
824 } else
825 has_point = FALSE;
827 if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
828 best = dist;
829 *actual_item = point_item;
830 }
831 }
832 }
834 return best;
835 }
837 /**
838 * Renders all visible canvas group items in buf rectangle.
839 */
840 static void
841 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
842 {
843 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
845 for (GList *list = group->items; list; list = list->next) {
846 SPCanvasItem *child = (SPCanvasItem *)list->data;
847 if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
848 if ((child->x1 < buf->rect.x1) &&
849 (child->y1 < buf->rect.y1) &&
850 (child->x2 > buf->rect.x0) &&
851 (child->y2 > buf->rect.y0)) {
852 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
853 SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
854 }
855 }
856 }
857 }
859 /**
860 * Adds an item to a canvas group.
861 */
862 static void
863 group_add (SPCanvasGroup *group, SPCanvasItem *item)
864 {
865 gtk_object_ref (GTK_OBJECT (item));
866 gtk_object_sink (GTK_OBJECT (item));
868 if (!group->items) {
869 group->items = g_list_append (group->items, item);
870 group->last = group->items;
871 } else {
872 group->last = g_list_append (group->last, item)->next;
873 }
875 sp_canvas_item_request_update (item);
876 }
878 /**
879 * Removes an item from a canvas group
880 */
881 static void
882 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
883 {
884 g_return_if_fail (group != NULL);
885 g_return_if_fail (SP_IS_CANVAS_GROUP (group));
886 g_return_if_fail (item != NULL);
888 for (GList *children = group->items; children; children = children->next) {
889 if (children->data == item) {
891 /* Unparent the child */
893 item->parent = NULL;
894 gtk_object_unref (GTK_OBJECT (item));
896 /* Remove it from the list */
898 if (children == group->last) group->last = children->prev;
900 group->items = g_list_remove_link (group->items, children);
901 g_list_free (children);
902 break;
903 }
904 }
905 }
907 /* SPCanvas */
909 static void sp_canvas_class_init (SPCanvasClass *klass);
910 static void sp_canvas_init (SPCanvas *canvas);
911 static void sp_canvas_destroy (GtkObject *object);
913 static void sp_canvas_realize (GtkWidget *widget);
914 static void sp_canvas_unrealize (GtkWidget *widget);
916 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
917 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
919 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
920 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
921 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
922 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
923 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
924 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
925 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
926 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
928 static GtkWidgetClass *canvas_parent_class;
930 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb);
931 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb);
932 static void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val);
933 static int do_update (SPCanvas *canvas);
935 /**
936 * Registers the SPCanvas class if necessary, and returns the type ID
937 * associated to it.
938 *
939 * \return The type ID of the SPCanvas class.
940 **/
941 GtkType
942 sp_canvas_get_type (void)
943 {
944 static GtkType canvas_type = 0;
946 if (!canvas_type) {
947 static GtkTypeInfo const canvas_info = {
948 "SPCanvas",
949 sizeof (SPCanvas),
950 sizeof (SPCanvasClass),
951 (GtkClassInitFunc) sp_canvas_class_init,
952 (GtkObjectInitFunc) sp_canvas_init,
953 NULL, NULL, NULL
954 };
956 canvas_type = gtk_type_unique (GTK_TYPE_WIDGET, &canvas_info);
957 }
959 return canvas_type;
960 }
962 /**
963 * Class initialization function for SPCanvasClass.
964 */
965 static void
966 sp_canvas_class_init (SPCanvasClass *klass)
967 {
968 GtkObjectClass *object_class = (GtkObjectClass *) klass;
969 GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
971 canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
973 object_class->destroy = sp_canvas_destroy;
975 widget_class->realize = sp_canvas_realize;
976 widget_class->unrealize = sp_canvas_unrealize;
977 widget_class->size_request = sp_canvas_size_request;
978 widget_class->size_allocate = sp_canvas_size_allocate;
979 widget_class->button_press_event = sp_canvas_button;
980 widget_class->button_release_event = sp_canvas_button;
981 widget_class->motion_notify_event = sp_canvas_motion;
982 widget_class->scroll_event = sp_canvas_scroll;
983 widget_class->expose_event = sp_canvas_expose;
984 widget_class->key_press_event = sp_canvas_key;
985 widget_class->key_release_event = sp_canvas_key;
986 widget_class->enter_notify_event = sp_canvas_crossing;
987 widget_class->leave_notify_event = sp_canvas_crossing;
988 widget_class->focus_in_event = sp_canvas_focus_in;
989 widget_class->focus_out_event = sp_canvas_focus_out;
990 }
992 /**
993 * Callback: object initialization for SPCanvas.
994 */
995 static void
996 sp_canvas_init (SPCanvas *canvas)
997 {
998 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
999 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
1000 GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
1002 canvas->pick_event.type = GDK_LEAVE_NOTIFY;
1003 canvas->pick_event.crossing.x = 0;
1004 canvas->pick_event.crossing.y = 0;
1006 /* Create the root item as a special case */
1007 canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
1008 canvas->root->canvas = canvas;
1010 gtk_object_ref (GTK_OBJECT (canvas->root));
1011 gtk_object_sink (GTK_OBJECT (canvas->root));
1013 canvas->need_repick = TRUE;
1015 // See comment at in sp-canvas.h.
1016 canvas->gen_all_enter_events = false;
1018 canvas->tiles=NULL;
1019 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1020 canvas->tileH=canvas->tileV=0;
1022 canvas->forced_redraw_count = 0;
1023 canvas->forced_redraw_limit = -1;
1025 #if ENABLE_LCMS
1026 canvas->enable_cms_display_adj = false;
1027 canvas->cms_key = new Glib::ustring("");
1028 #endif // ENABLE_LCMS
1030 canvas->is_scrolling = false;
1032 }
1034 /**
1035 * Convenience function to remove the idle handler of a canvas.
1036 */
1037 static void
1038 remove_idle (SPCanvas *canvas)
1039 {
1040 if (canvas->idle_id) {
1041 gtk_idle_remove (canvas->idle_id);
1042 canvas->idle_id = 0;
1043 }
1044 }
1046 /*
1047 * Removes the transient state of the canvas (idle handler, grabs).
1048 */
1049 static void
1050 shutdown_transients (SPCanvas *canvas)
1051 {
1052 /* We turn off the need_redraw flag, since if the canvas is mapped again
1053 * it will request a redraw anyways. We do not turn off the need_update
1054 * flag, though, because updates are not queued when the canvas remaps
1055 * itself.
1056 */
1057 if (canvas->need_redraw) {
1058 canvas->need_redraw = FALSE;
1059 }
1060 if ( canvas->tiles ) g_free(canvas->tiles);
1061 canvas->tiles=NULL;
1062 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1063 canvas->tileH=canvas->tileV=0;
1065 if (canvas->grabbed_item) {
1066 canvas->grabbed_item = NULL;
1067 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1068 }
1070 remove_idle (canvas);
1071 }
1073 /**
1074 * Destroy handler for SPCanvas.
1075 */
1076 static void
1077 sp_canvas_destroy (GtkObject *object)
1078 {
1079 SPCanvas *canvas = SP_CANVAS (object);
1081 if (canvas->root) {
1082 gtk_object_unref (GTK_OBJECT (canvas->root));
1083 canvas->root = NULL;
1084 }
1086 shutdown_transients (canvas);
1088 if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1089 (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1090 }
1092 /**
1093 * Returns new canvas as widget.
1094 */
1095 GtkWidget *
1096 sp_canvas_new_aa (void)
1097 {
1098 SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1100 return (GtkWidget *) canvas;
1101 }
1103 /**
1104 * The canvas widget's realize callback.
1105 */
1106 static void
1107 sp_canvas_realize (GtkWidget *widget)
1108 {
1109 SPCanvas *canvas = SP_CANVAS (widget);
1111 GdkWindowAttr attributes;
1112 attributes.window_type = GDK_WINDOW_CHILD;
1113 attributes.x = widget->allocation.x;
1114 attributes.y = widget->allocation.y;
1115 attributes.width = widget->allocation.width;
1116 attributes.height = widget->allocation.height;
1117 attributes.wclass = GDK_INPUT_OUTPUT;
1118 attributes.visual = gdk_rgb_get_visual ();
1119 attributes.colormap = gdk_rgb_get_cmap ();
1120 attributes.event_mask = (gtk_widget_get_events (widget) |
1121 GDK_EXPOSURE_MASK |
1122 GDK_BUTTON_PRESS_MASK |
1123 GDK_BUTTON_RELEASE_MASK |
1124 GDK_POINTER_MOTION_MASK |
1125 GDK_PROXIMITY_IN_MASK |
1126 GDK_PROXIMITY_OUT_MASK |
1127 GDK_KEY_PRESS_MASK |
1128 GDK_KEY_RELEASE_MASK |
1129 GDK_ENTER_NOTIFY_MASK |
1130 GDK_LEAVE_NOTIFY_MASK |
1131 GDK_FOCUS_CHANGE_MASK);
1132 gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1134 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1135 gdk_window_set_user_data (widget->window, widget);
1137 if ( prefs_get_int_attribute ("options.useextinput", "value", 1) )
1138 gtk_widget_set_events(widget, attributes.event_mask);
1140 widget->style = gtk_style_attach (widget->style, widget->window);
1142 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1144 canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1145 }
1147 /**
1148 * The canvas widget's unrealize callback.
1149 */
1150 static void
1151 sp_canvas_unrealize (GtkWidget *widget)
1152 {
1153 SPCanvas *canvas = SP_CANVAS (widget);
1155 shutdown_transients (canvas);
1157 gdk_gc_destroy (canvas->pixmap_gc);
1158 canvas->pixmap_gc = NULL;
1160 if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1161 (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1162 }
1164 /**
1165 * The canvas widget's size_request callback.
1166 */
1167 static void
1168 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1169 {
1170 static_cast<void>(SP_CANVAS (widget));
1172 req->width = 256;
1173 req->height = 256;
1174 }
1176 /**
1177 * The canvas widget's size_allocate callback.
1178 */
1179 static void
1180 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1181 {
1182 SPCanvas *canvas = SP_CANVAS (widget);
1184 /* Schedule redraw of new region */
1185 sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1186 if (allocation->width > widget->allocation.width) {
1187 sp_canvas_request_redraw (canvas,
1188 canvas->x0 + widget->allocation.width,
1189 0,
1190 canvas->x0 + allocation->width,
1191 canvas->y0 + allocation->height);
1192 }
1193 if (allocation->height > widget->allocation.height) {
1194 sp_canvas_request_redraw (canvas,
1195 0,
1196 canvas->y0 + widget->allocation.height,
1197 canvas->x0 + allocation->width,
1198 canvas->y0 + allocation->height);
1199 }
1201 widget->allocation = *allocation;
1203 if (GTK_WIDGET_REALIZED (widget)) {
1204 gdk_window_move_resize (widget->window,
1205 widget->allocation.x, widget->allocation.y,
1206 widget->allocation.width, widget->allocation.height);
1207 }
1208 }
1210 /**
1211 * Helper that emits an event for an item in the canvas, be it the current
1212 * item, grabbed item, or focused item, as appropriate.
1213 */
1214 static int
1215 emit_event (SPCanvas *canvas, GdkEvent *event)
1216 {
1217 guint mask;
1219 if (canvas->grabbed_item) {
1220 switch (event->type) {
1221 case GDK_ENTER_NOTIFY:
1222 mask = GDK_ENTER_NOTIFY_MASK;
1223 break;
1224 case GDK_LEAVE_NOTIFY:
1225 mask = GDK_LEAVE_NOTIFY_MASK;
1226 break;
1227 case GDK_MOTION_NOTIFY:
1228 mask = GDK_POINTER_MOTION_MASK;
1229 break;
1230 case GDK_BUTTON_PRESS:
1231 case GDK_2BUTTON_PRESS:
1232 case GDK_3BUTTON_PRESS:
1233 mask = GDK_BUTTON_PRESS_MASK;
1234 break;
1235 case GDK_BUTTON_RELEASE:
1236 mask = GDK_BUTTON_RELEASE_MASK;
1237 break;
1238 case GDK_KEY_PRESS:
1239 mask = GDK_KEY_PRESS_MASK;
1240 break;
1241 case GDK_KEY_RELEASE:
1242 mask = GDK_KEY_RELEASE_MASK;
1243 break;
1244 case GDK_SCROLL:
1245 mask = GDK_SCROLL;
1246 break;
1247 default:
1248 mask = 0;
1249 break;
1250 }
1252 if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1253 }
1255 /* Convert to world coordinates -- we have two cases because of diferent
1256 * offsets of the fields in the event structures.
1257 */
1259 GdkEvent ev = *event;
1261 switch (ev.type) {
1262 case GDK_ENTER_NOTIFY:
1263 case GDK_LEAVE_NOTIFY:
1264 ev.crossing.x += canvas->x0;
1265 ev.crossing.y += canvas->y0;
1266 break;
1267 case GDK_MOTION_NOTIFY:
1268 case GDK_BUTTON_PRESS:
1269 case GDK_2BUTTON_PRESS:
1270 case GDK_3BUTTON_PRESS:
1271 case GDK_BUTTON_RELEASE:
1272 ev.motion.x += canvas->x0;
1273 ev.motion.y += canvas->y0;
1274 break;
1275 default:
1276 break;
1277 }
1279 /* Choose where we send the event */
1281 /* canvas->current_item becomes NULL in some cases under Win32
1282 ** (e.g. if the pointer leaves the window). So this is a hack that
1283 ** Lauris applied to SP to get around the problem.
1284 */
1285 SPCanvasItem* item = NULL;
1286 if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1287 item = canvas->grabbed_item;
1288 } else {
1289 item = canvas->current_item;
1290 }
1292 if (canvas->focused_item &&
1293 ((event->type == GDK_KEY_PRESS) ||
1294 (event->type == GDK_KEY_RELEASE) ||
1295 (event->type == GDK_FOCUS_CHANGE))) {
1296 item = canvas->focused_item;
1297 }
1299 /* The event is propagated up the hierarchy (for if someone connected to
1300 * a group instead of a leaf event), and emission is stopped if a
1301 * handler returns TRUE, just like for GtkWidget events.
1302 */
1304 gint finished = FALSE;
1306 while (item && !finished) {
1307 gtk_object_ref (GTK_OBJECT (item));
1308 gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1309 SPCanvasItem *parent = item->parent;
1310 gtk_object_unref (GTK_OBJECT (item));
1311 item = parent;
1312 }
1314 return finished;
1315 }
1317 /**
1318 * Helper that re-picks the current item in the canvas, based on the event's
1319 * coordinates and emits enter/leave events for items as appropriate.
1320 */
1321 static int
1322 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1323 {
1324 int button_down = 0;
1325 double x, y;
1327 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1328 return FALSE;
1330 int retval = FALSE;
1332 if (canvas->gen_all_enter_events == false) {
1333 // If a button is down, we'll perform enter and leave events on the
1334 // current item, but not enter on any other item. This is more or
1335 // less like X pointer grabbing for canvas items.
1336 //
1337 button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1338 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1340 if (!button_down) canvas->left_grabbed_item = FALSE;
1341 }
1343 /* Save the event in the canvas. This is used to synthesize enter and
1344 * leave events in case the current item changes. It is also used to
1345 * re-pick the current item if the current one gets deleted. Also,
1346 * synthesize an enter event.
1347 */
1348 if (event != &canvas->pick_event) {
1349 if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1350 /* these fields have the same offsets in both types of events */
1352 canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY;
1353 canvas->pick_event.crossing.window = event->motion.window;
1354 canvas->pick_event.crossing.send_event = event->motion.send_event;
1355 canvas->pick_event.crossing.subwindow = NULL;
1356 canvas->pick_event.crossing.x = event->motion.x;
1357 canvas->pick_event.crossing.y = event->motion.y;
1358 canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL;
1359 canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR;
1360 canvas->pick_event.crossing.focus = FALSE;
1361 canvas->pick_event.crossing.state = event->motion.state;
1363 /* these fields don't have the same offsets in both types of events */
1365 if (event->type == GDK_MOTION_NOTIFY) {
1366 canvas->pick_event.crossing.x_root = event->motion.x_root;
1367 canvas->pick_event.crossing.y_root = event->motion.y_root;
1368 } else {
1369 canvas->pick_event.crossing.x_root = event->button.x_root;
1370 canvas->pick_event.crossing.y_root = event->button.y_root;
1371 }
1372 } else {
1373 canvas->pick_event = *event;
1374 }
1375 }
1377 /* Don't do anything else if this is a recursive call */
1378 if (canvas->in_repick) return retval;
1380 /* LeaveNotify means that there is no current item, so we don't look for one */
1381 if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1382 /* these fields don't have the same offsets in both types of events */
1384 if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1385 x = canvas->pick_event.crossing.x;
1386 y = canvas->pick_event.crossing.y;
1387 } else {
1388 x = canvas->pick_event.motion.x;
1389 y = canvas->pick_event.motion.y;
1390 }
1392 /* world coords */
1393 x += canvas->x0;
1394 y += canvas->y0;
1396 /* find the closest item */
1397 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1398 sp_canvas_item_invoke_point (canvas->root, NR::Point(x, y), &canvas->new_current_item);
1399 } else {
1400 canvas->new_current_item = NULL;
1401 }
1402 } else {
1403 canvas->new_current_item = NULL;
1404 }
1406 if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1407 return retval; /* current item did not change */
1408 }
1410 /* Synthesize events for old and new current items */
1412 if ((canvas->new_current_item != canvas->current_item)
1413 && (canvas->current_item != NULL)
1414 && !canvas->left_grabbed_item) {
1415 GdkEvent new_event;
1416 SPCanvasItem *item;
1418 item = canvas->current_item;
1420 new_event = canvas->pick_event;
1421 new_event.type = GDK_LEAVE_NOTIFY;
1423 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1424 new_event.crossing.subwindow = NULL;
1425 canvas->in_repick = TRUE;
1426 retval = emit_event (canvas, &new_event);
1427 canvas->in_repick = FALSE;
1428 }
1430 if (canvas->gen_all_enter_events == false) {
1431 // new_current_item may have been set to NULL during the call to
1432 // emit_event() above
1433 if ((canvas->new_current_item != canvas->current_item) && button_down) {
1434 canvas->left_grabbed_item = TRUE;
1435 return retval;
1436 }
1437 }
1439 /* Handle the rest of cases */
1441 canvas->left_grabbed_item = FALSE;
1442 canvas->current_item = canvas->new_current_item;
1444 if (canvas->current_item != NULL) {
1445 GdkEvent new_event;
1447 new_event = canvas->pick_event;
1448 new_event.type = GDK_ENTER_NOTIFY;
1449 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1450 new_event.crossing.subwindow = NULL;
1451 retval = emit_event (canvas, &new_event);
1452 }
1454 return retval;
1455 }
1457 /**
1458 * Button event handler for the canvas.
1459 */
1460 static gint
1461 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1462 {
1463 SPCanvas *canvas = SP_CANVAS (widget);
1465 int retval = FALSE;
1467 /* dispatch normally regardless of the event's window if an item has
1468 has a pointer grab in effect */
1469 if (!canvas->grabbed_item &&
1470 event->window != SP_CANVAS_WINDOW (canvas))
1471 return retval;
1473 int mask;
1474 switch (event->button) {
1475 case 1:
1476 mask = GDK_BUTTON1_MASK;
1477 break;
1478 case 2:
1479 mask = GDK_BUTTON2_MASK;
1480 break;
1481 case 3:
1482 mask = GDK_BUTTON3_MASK;
1483 break;
1484 case 4:
1485 mask = GDK_BUTTON4_MASK;
1486 break;
1487 case 5:
1488 mask = GDK_BUTTON5_MASK;
1489 break;
1490 default:
1491 mask = 0;
1492 }
1494 switch (event->type) {
1495 case GDK_BUTTON_PRESS:
1496 case GDK_2BUTTON_PRESS:
1497 case GDK_3BUTTON_PRESS:
1498 /* Pick the current item as if the button were not pressed, and
1499 * then process the event.
1500 */
1501 canvas->state = event->state;
1502 pick_current_item (canvas, (GdkEvent *) event);
1503 canvas->state ^= mask;
1504 retval = emit_event (canvas, (GdkEvent *) event);
1505 break;
1507 case GDK_BUTTON_RELEASE:
1508 /* Process the event as if the button were pressed, then repick
1509 * after the button has been released
1510 */
1511 canvas->state = event->state;
1512 retval = emit_event (canvas, (GdkEvent *) event);
1513 event->state ^= mask;
1514 canvas->state = event->state;
1515 pick_current_item (canvas, (GdkEvent *) event);
1516 event->state ^= mask;
1517 break;
1519 default:
1520 g_assert_not_reached ();
1521 }
1523 return retval;
1524 }
1526 /**
1527 * Scroll event handler for the canvas.
1528 *
1529 * \todo FIXME: generate motion events to re-select items.
1530 */
1531 static gint
1532 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1533 {
1534 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1535 }
1537 /**
1538 * Motion event handler for the canvas.
1539 */
1540 static int
1541 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1542 {
1543 SPCanvas *canvas = SP_CANVAS (widget);
1545 if (event->window != SP_CANVAS_WINDOW (canvas))
1546 return FALSE;
1548 if (canvas->grabbed_event_mask & GDK_POINTER_MOTION_HINT_MASK) {
1549 gint x, y;
1550 gdk_window_get_pointer (widget->window, &x, &y, NULL);
1551 event->x = x;
1552 event->y = y;
1553 }
1555 canvas->state = event->state;
1556 pick_current_item (canvas, (GdkEvent *) event);
1558 return emit_event (canvas, (GdkEvent *) event);
1559 }
1561 static void
1562 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)
1563 {
1564 GtkWidget *widget = GTK_WIDGET (canvas);
1566 SPCanvasBuf buf;
1567 if (canvas->rendermode != RENDERMODE_OUTLINE) {
1568 buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1569 } else {
1570 buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1571 }
1573 // Mark the region clean
1574 sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1576 buf.buf_rowstride = sw * 3; // CAIRO FIXME: for cairo output, the buffer must be RGB unpacked, i.e. sw * 4
1577 buf.rect.x0 = x0;
1578 buf.rect.y0 = y0;
1579 buf.rect.x1 = x1;
1580 buf.rect.y1 = y1;
1581 buf.visible_rect.x0 = draw_x1;
1582 buf.visible_rect.y0 = draw_y1;
1583 buf.visible_rect.x1 = draw_x2;
1584 buf.visible_rect.y1 = draw_y2;
1585 GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1586 buf.bg_color = (((color->red & 0xff00) << 8)
1587 | (color->green & 0xff00)
1588 | (color->blue >> 8));
1589 buf.is_empty = true;
1591 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1592 SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1593 }
1595 #if ENABLE_LCMS
1596 cmsHTRANSFORM transf = 0;
1597 long long int fromDisplay = prefs_get_int_attribute_limited( "options.displayprofile", "from_display", 0, 0, 1 );
1598 if ( fromDisplay ) {
1599 transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" );
1600 } else {
1601 transf = Inkscape::colorprofile_get_display_transform();
1602 }
1603 #endif // ENABLE_LCMS
1605 if (buf.is_empty) {
1606 #if ENABLE_LCMS
1607 if ( transf && canvas->enable_cms_display_adj ) {
1608 cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1609 }
1610 #endif // ENABLE_LCMS
1611 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1612 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1613 canvas->pixmap_gc,
1614 TRUE,
1615 x0 - canvas->x0, y0 - canvas->y0,
1616 x1 - x0, y1 - y0);
1617 } else {
1618 /*
1619 // CAIRO FIXME: after SPCanvasBuf is made 32bpp throughout, this rgb_draw below can be replaced with the below.
1620 // Why this must not be done currently:
1621 // - all canvas items (handles, nodes etc) paint themselves assuming 24bpp
1622 // - cairo assumes bgra, but we have rgba, so r and b get swapped (until we paint all with cairo too)
1623 // - it does not seem to be any faster; in fact since with 32bpp, buf contains less pixels,
1624 // we need more bufs to paint a given area and as a result it's even a bit slower
1626 cairo_surface_t* cst = cairo_image_surface_create_for_data (
1627 buf.buf,
1628 CAIRO_FORMAT_RGB24, // unpacked, i.e. 32 bits! one byte is unused
1629 x1 - x0, y1 - y0,
1630 buf.buf_rowstride
1631 );
1632 cairo_t *ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1633 cairo_set_source_surface (ct, cst, x0 - canvas->x0, y0 - canvas->y0);
1634 cairo_paint (ct);
1635 cairo_destroy (ct);
1636 cairo_surface_finish (cst);
1637 cairo_surface_destroy (cst);
1638 */
1640 #if ENABLE_LCMS
1641 if ( transf && canvas->enable_cms_display_adj ) {
1642 for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1643 guchar* p = buf.buf + (sw * 3) * yy;
1644 cmsDoTransform( transf, p, p, (x1 - x0) );
1645 }
1646 }
1647 #endif // ENABLE_LCMS
1649 gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1650 canvas->pixmap_gc,
1651 x0 - canvas->x0, y0 - canvas->y0,
1652 x1 - x0, y1 - y0,
1653 GDK_RGB_DITHER_MAX,
1654 buf.buf,
1655 sw * 3,
1656 x0 - canvas->x0, y0 - canvas->y0);
1657 }
1659 if (canvas->rendermode != RENDERMODE_OUTLINE) {
1660 nr_pixelstore_256K_free (buf.buf);
1661 } else {
1662 nr_pixelstore_1M_free (buf.buf);
1663 }
1664 }
1666 struct PaintRectSetup {
1667 SPCanvas* canvas;
1668 NRRectL big_rect;
1669 GTimeVal start_time;
1670 int max_pixels;
1671 NR::Point mouse_loc;
1672 };
1674 /**
1675 * Paint the given rect, recursively subdividing the region until it is the size of a single
1676 * buffer.
1677 *
1678 * @return true if the drawing completes
1679 */
1680 static int
1681 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1682 {
1683 GTimeVal now;
1684 g_get_current_time (&now);
1686 glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1687 + (now.tv_usec - setup->start_time.tv_usec);
1689 // Allow only very fast buffers to be run together;
1690 // as soon as the total redraw time exceeds 1ms, cancel;
1691 // this returns control to the idle loop and allows Inkscape to process user input
1692 // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1693 // it will get back and finish painting what remains to paint.
1694 if (elapsed > 1000) {
1696 // Interrupting redraw isn't always good.
1697 // For example, when you drag one node of a big path, only the buffer containing
1698 // the mouse cursor will be redrawn again and again, and the rest of the path
1699 // will remain stale because Inkscape never has enough idle time to redraw all
1700 // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1701 // If this limit is set, and if we have aborted redraw more times than is allowed,
1702 // interrupting is blocked and we're forced to redraw full screen once
1703 // (after which we can again interrupt forced_redraw_limit times).
1704 if (setup->canvas->forced_redraw_limit < 0 ||
1705 setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1707 if (setup->canvas->forced_redraw_limit != -1) {
1708 setup->canvas->forced_redraw_count++;
1709 }
1711 return false;
1712 }
1713 }
1715 // Find the optimal buffer dimensions
1716 int bw = this_rect.x1 - this_rect.x0;
1717 int bh = this_rect.y1 - this_rect.y0;
1718 if ((bw < 1) || (bh < 1))
1719 return 0;
1721 if (bw * bh < setup->max_pixels) {
1722 // We are small enough
1723 sp_canvas_paint_single_buffer (setup->canvas,
1724 this_rect.x0, this_rect.y0,
1725 this_rect.x1, this_rect.y1,
1726 setup->big_rect.x0, setup->big_rect.y0,
1727 setup->big_rect.x1, setup->big_rect.y1, bw);
1728 return 1;
1729 }
1731 NRRectL lo = this_rect;
1732 NRRectL hi = this_rect;
1734 /*
1735 This test determines the redraw strategy:
1737 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1738 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1739 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1740 and seems to be faster for drawings with many smaller objects at zoom-out.
1742 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1743 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1744 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1745 faster.
1747 The default for now is the strips mode.
1748 */
1749 if (bw < bh || bh < 2 * TILE_SIZE) {
1750 int mid = (this_rect.x0 + 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 int mid = (this_rect.y0 + this_rect.y1) / 2;
1767 // Make sure that mid lies on a tile boundary
1768 mid = (mid / TILE_SIZE) * TILE_SIZE;
1770 lo.y1 = mid;
1771 hi.y0 = mid;
1773 if (setup->mouse_loc[NR::Y] < mid) {
1774 // Always paint towards the mouse first
1775 return sp_canvas_paint_rect_internal(setup, lo)
1776 && sp_canvas_paint_rect_internal(setup, hi);
1777 } else {
1778 return sp_canvas_paint_rect_internal(setup, hi)
1779 && sp_canvas_paint_rect_internal(setup, lo);
1780 }
1781 }
1782 }
1785 /**
1786 * Helper that draws a specific rectangular part of the canvas.
1787 *
1788 * @return true if the rectangle painting succeeds.
1789 */
1790 static bool
1791 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1792 {
1793 g_return_val_if_fail (!canvas->need_update, false);
1795 NRRectL rect;
1796 rect.x0 = xx0;
1797 rect.x1 = xx1;
1798 rect.y0 = yy0;
1799 rect.y1 = yy1;
1801 // Clip rect-to-draw by the current visible area
1802 rect.x0 = MAX (rect.x0, canvas->x0);
1803 rect.y0 = MAX (rect.y0, canvas->y0);
1804 rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1805 rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1807 #ifdef DEBUG_REDRAW
1808 // paint the area to redraw yellow
1809 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1810 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1811 canvas->pixmap_gc,
1812 TRUE,
1813 rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1814 rect.x1 - rect.x0, rect.y1 - rect.y0);
1815 #endif
1817 PaintRectSetup setup;
1819 setup.canvas = canvas;
1820 setup.big_rect = rect;
1822 // Save the mouse location
1823 gint x, y;
1824 gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1825 setup.mouse_loc = sp_canvas_window_to_world (canvas, NR::Point(x,y));
1827 // CAIRO FIXME: the sw/sh calculations below all assume 24bpp, need fixing for 32bpp
1828 if (canvas->rendermode != RENDERMODE_OUTLINE) {
1829 // use 256K as a compromise to not slow down gradients
1830 // 256K is the cached buffer and we need 3 channels
1831 setup.max_pixels = 87381; // 256K/3
1832 } else {
1833 // paths only, so 1M works faster
1834 // 1M is the cached buffer and we need 3 channels
1835 setup.max_pixels = 349525;
1836 }
1838 // Start the clock
1839 g_get_current_time(&(setup.start_time));
1841 // Go
1842 return sp_canvas_paint_rect_internal(&setup, rect);
1843 }
1845 /**
1846 * Force a full redraw after a specified number of interrupted redraws
1847 */
1848 void
1849 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1850 g_return_if_fail(canvas != NULL);
1852 canvas->forced_redraw_limit = count;
1853 canvas->forced_redraw_count = 0;
1854 }
1856 /**
1857 * End forced full redraw requests
1858 */
1859 void
1860 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1861 g_return_if_fail(canvas != NULL);
1863 canvas->forced_redraw_limit = -1;
1864 }
1866 /**
1867 * The canvas widget's expose callback.
1868 */
1869 static gint
1870 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1871 {
1872 SPCanvas *canvas = SP_CANVAS (widget);
1874 if (!GTK_WIDGET_DRAWABLE (widget) ||
1875 (event->window != SP_CANVAS_WINDOW (canvas)))
1876 return FALSE;
1878 int n_rects;
1879 GdkRectangle *rects;
1880 gdk_region_get_rectangles (event->region, &rects, &n_rects);
1882 for (int i = 0; i < n_rects; i++) {
1883 NRRectL rect;
1885 rect.x0 = rects[i].x + canvas->x0;
1886 rect.y0 = rects[i].y + canvas->y0;
1887 rect.x1 = rect.x0 + rects[i].width;
1888 rect.y1 = rect.y0 + rects[i].height;
1890 sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1891 }
1893 if (n_rects > 0)
1894 g_free (rects);
1896 return FALSE;
1897 }
1899 /**
1900 * The canvas widget's keypress callback.
1901 */
1902 static gint
1903 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1904 {
1905 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1906 }
1908 /**
1909 * Crossing event handler for the canvas.
1910 */
1911 static gint
1912 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1913 {
1914 SPCanvas *canvas = SP_CANVAS (widget);
1916 if (event->window != SP_CANVAS_WINDOW (canvas))
1917 return FALSE;
1919 canvas->state = event->state;
1920 return pick_current_item (canvas, (GdkEvent *) event);
1921 }
1923 /**
1924 * Focus in handler for the canvas.
1925 */
1926 static gint
1927 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
1928 {
1929 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1931 SPCanvas *canvas = SP_CANVAS (widget);
1933 if (canvas->focused_item) {
1934 return emit_event (canvas, (GdkEvent *) event);
1935 } else {
1936 return FALSE;
1937 }
1938 }
1940 /**
1941 * Focus out handler for the canvas.
1942 */
1943 static gint
1944 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
1945 {
1946 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1948 SPCanvas *canvas = SP_CANVAS (widget);
1950 if (canvas->focused_item)
1951 return emit_event (canvas, (GdkEvent *) event);
1952 else
1953 return FALSE;
1954 }
1956 /**
1957 * Helper that repaints the areas in the canvas that need it.
1958 *
1959 * @return true if all the dirty parts have been redrawn
1960 */
1961 static int
1962 paint (SPCanvas *canvas)
1963 {
1964 if (canvas->need_update) {
1965 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
1966 canvas->need_update = FALSE;
1967 }
1969 if (!canvas->need_redraw)
1970 return TRUE;
1972 Gdk::Region to_paint;
1974 for (int j=canvas->tTop; j<canvas->tBottom; j++) {
1975 for (int i=canvas->tLeft; i<canvas->tRight; i++) {
1976 int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
1978 if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
1979 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
1980 TILE_SIZE, TILE_SIZE));
1981 }
1983 }
1984 }
1986 if (!to_paint.empty()) {
1987 Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
1988 typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
1989 for (Iter i=rect.begin(); i != rect.end(); ++i) {
1990 int x0 = (*i).get_x();
1991 int y0 = (*i).get_y();
1992 int x1 = x0 + (*i).get_width();
1993 int y1 = y0 + (*i).get_height();
1994 if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
1995 // Aborted
1996 return FALSE;
1997 };
1998 }
1999 }
2001 canvas->need_redraw = FALSE;
2003 // we've had a full unaborted redraw, reset the full redraw counter
2004 if (canvas->forced_redraw_limit != -1) {
2005 canvas->forced_redraw_count = 0;
2006 }
2008 return TRUE;
2009 }
2011 /**
2012 * Helper that invokes update, paint, and repick on canvas.
2013 */
2014 static int
2015 do_update (SPCanvas *canvas)
2016 {
2017 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
2018 return TRUE;
2020 /* Cause the update if necessary */
2021 if (canvas->need_update) {
2022 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2023 canvas->need_update = FALSE;
2024 }
2026 /* Paint if able to */
2027 if (GTK_WIDGET_DRAWABLE (canvas)) {
2028 return paint (canvas);
2029 }
2031 /* Pick new current item */
2032 while (canvas->need_repick) {
2033 canvas->need_repick = FALSE;
2034 pick_current_item (canvas, &canvas->pick_event);
2035 }
2037 return TRUE;
2038 }
2040 /**
2041 * Idle handler for the canvas that deals with pending updates and redraws.
2042 */
2043 static gint
2044 idle_handler (gpointer data)
2045 {
2046 GDK_THREADS_ENTER ();
2048 SPCanvas *canvas = SP_CANVAS (data);
2050 int const ret = do_update (canvas);
2052 if (ret) {
2053 /* Reset idle id */
2054 canvas->idle_id = 0;
2055 }
2057 GDK_THREADS_LEAVE ();
2059 return !ret;
2060 }
2062 /**
2063 * Convenience function to add an idle handler to a canvas.
2064 */
2065 static void
2066 add_idle (SPCanvas *canvas)
2067 {
2068 if (canvas->idle_id != 0)
2069 return;
2071 canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2072 }
2074 /**
2075 * Returns the root group of the specified canvas.
2076 */
2077 SPCanvasGroup *
2078 sp_canvas_root (SPCanvas *canvas)
2079 {
2080 g_return_val_if_fail (canvas != NULL, NULL);
2081 g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2083 return SP_CANVAS_GROUP (canvas->root);
2084 }
2086 /**
2087 * Scrolls canvas to specific position.
2088 */
2089 void
2090 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2091 {
2092 g_return_if_fail (canvas != NULL);
2093 g_return_if_fail (SP_IS_CANVAS (canvas));
2095 int ix = (int) (cx + 0.5);
2096 int iy = (int) (cy + 0.5);
2097 int dx = ix - canvas->x0;
2098 int dy = iy - canvas->y0;
2100 canvas->dx0 = cx;
2101 canvas->dy0 = cy;
2102 canvas->x0 = ix;
2103 canvas->y0 = iy;
2105 sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2107 if (!clear) {
2108 // scrolling without zoom; redraw only the newly exposed areas
2109 if ((dx != 0) || (dy != 0)) {
2110 canvas->is_scrolling = is_scrolling;
2111 if (GTK_WIDGET_REALIZED (canvas)) {
2112 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2113 }
2114 }
2115 } else {
2116 // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2117 }
2119 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
2120 SPEventContext *ec = inkscape_active_event_context();
2121 if (SP_IS_3DBOX_CONTEXT (ec)) {
2122 // We could avoid redraw during panning by checking the status of is_scrolling, but this is
2123 // neither faster nor does it get rid of artefacts, so we update PLs unconditionally
2124 SP3DBoxContext *bc = SP_3DBOX_CONTEXT (ec);
2125 bc->_vpdrag->updateLines();
2126 }
2127 }
2129 /**
2130 * Updates canvas if necessary.
2131 */
2132 void
2133 sp_canvas_update_now (SPCanvas *canvas)
2134 {
2135 g_return_if_fail (canvas != NULL);
2136 g_return_if_fail (SP_IS_CANVAS (canvas));
2138 if (!(canvas->need_update ||
2139 canvas->need_redraw))
2140 return;
2142 do_update (canvas);
2143 }
2145 /**
2146 * Update callback for canvas widget.
2147 */
2148 static void
2149 sp_canvas_request_update (SPCanvas *canvas)
2150 {
2151 canvas->need_update = TRUE;
2152 add_idle (canvas);
2153 }
2155 /**
2156 * Forces redraw of rectangular canvas area.
2157 */
2158 void
2159 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2160 {
2161 NRRectL bbox;
2162 NRRectL visible;
2163 NRRectL clip;
2165 g_return_if_fail (canvas != NULL);
2166 g_return_if_fail (SP_IS_CANVAS (canvas));
2168 if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2169 if ((x0 >= x1) || (y0 >= y1)) return;
2171 bbox.x0 = x0;
2172 bbox.y0 = y0;
2173 bbox.x1 = x1;
2174 bbox.y1 = y1;
2176 visible.x0 = canvas->x0;
2177 visible.y0 = canvas->y0;
2178 visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2179 visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2181 nr_rect_l_intersect (&clip, &bbox, &visible);
2183 sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2184 add_idle (canvas);
2185 }
2187 /**
2188 * Sets world coordinates from win and canvas.
2189 */
2190 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2191 {
2192 g_return_if_fail (canvas != NULL);
2193 g_return_if_fail (SP_IS_CANVAS (canvas));
2195 if (worldx) *worldx = canvas->x0 + winx;
2196 if (worldy) *worldy = canvas->y0 + winy;
2197 }
2199 /**
2200 * Sets win coordinates from world and canvas.
2201 */
2202 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2203 {
2204 g_return_if_fail (canvas != NULL);
2205 g_return_if_fail (SP_IS_CANVAS (canvas));
2207 if (winx) *winx = worldx - canvas->x0;
2208 if (winy) *winy = worldy - canvas->y0;
2209 }
2211 /**
2212 * Converts point from win to world coordinates.
2213 */
2214 NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win)
2215 {
2216 g_assert (canvas != NULL);
2217 g_assert (SP_IS_CANVAS (canvas));
2219 return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2220 }
2222 /**
2223 * Converts point from world to win coordinates.
2224 */
2225 NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world)
2226 {
2227 g_assert (canvas != NULL);
2228 g_assert (SP_IS_CANVAS (canvas));
2230 return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2231 }
2233 /**
2234 * Returns true if point given in world coordinates is inside window.
2235 */
2236 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world)
2237 {
2238 g_assert( canvas != NULL );
2239 g_assert(SP_IS_CANVAS(canvas));
2241 using NR::X;
2242 using NR::Y;
2243 GtkWidget const &w = *GTK_WIDGET(canvas);
2244 return ( ( canvas->x0 <= world[X] ) &&
2245 ( canvas->y0 <= world[Y] ) &&
2246 ( world[X] < canvas->x0 + w.allocation.width ) &&
2247 ( world[Y] < canvas->y0 + w.allocation.height ) );
2248 }
2250 /**
2251 * Return canvas window coordinates as NR::Rect.
2252 */
2253 NR::Rect SPCanvas::getViewbox() const
2254 {
2255 GtkWidget const *w = GTK_WIDGET(this);
2257 return NR::Rect(NR::Point(dx0, dy0),
2258 NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2259 }
2261 inline int sp_canvas_tile_floor(int x)
2262 {
2263 return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2264 }
2266 inline int sp_canvas_tile_ceil(int x)
2267 {
2268 return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2269 }
2271 /**
2272 * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2273 */
2274 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2275 {
2276 if ( nl >= nr || nt >= nb ) {
2277 if ( canvas->tiles ) g_free(canvas->tiles);
2278 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2279 canvas->tileH=canvas->tileV=0;
2280 canvas->tiles=NULL;
2281 return;
2282 }
2283 int tl=sp_canvas_tile_floor(nl);
2284 int tt=sp_canvas_tile_floor(nt);
2285 int tr=sp_canvas_tile_ceil(nr);
2286 int tb=sp_canvas_tile_ceil(nb);
2288 int nh = tr-tl, nv = tb-tt;
2289 uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2290 for (int i=tl; i<tr; i++) {
2291 for (int j=tt; j<tb; j++) {
2292 int ind = (i-tl) + (j-tt)*nh;
2293 if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2294 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2295 } else {
2296 ntiles[ind]=0; // newly exposed areas get 0
2297 }
2298 }
2299 }
2300 if ( canvas->tiles ) g_free(canvas->tiles);
2301 canvas->tiles=ntiles;
2302 canvas->tLeft=tl;
2303 canvas->tTop=tt;
2304 canvas->tRight=tr;
2305 canvas->tBottom=tb;
2306 canvas->tileH=nh;
2307 canvas->tileV=nv;
2308 }
2310 /*
2311 * Helper that queues a canvas rectangle for redraw
2312 */
2313 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2314 canvas->need_redraw = TRUE;
2316 sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2317 }
2319 /**
2320 * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2321 */
2322 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2323 {
2324 if ( nl >= nr || nt >= nb ) {
2325 return;
2326 }
2327 int tl=sp_canvas_tile_floor(nl);
2328 int tt=sp_canvas_tile_floor(nt);
2329 int tr=sp_canvas_tile_ceil(nr);
2330 int tb=sp_canvas_tile_ceil(nb);
2331 if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2332 if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2333 if ( tr > canvas->tRight ) tr=canvas->tRight;
2334 if ( tt < canvas->tTop ) tt=canvas->tTop;
2335 if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2337 for (int i=tl; i<tr; i++) {
2338 for (int j=tt; j<tb; j++) {
2339 canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2340 }
2341 }
2342 }
2345 /*
2346 Local Variables:
2347 mode:c++
2348 c-file-style:"stroustrup"
2349 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2350 indent-tabs-mode:nil
2351 fill-column:99
2352 End:
2353 */
2354 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :