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"
41 // Define this to visualize the regions to be redrawn
42 //#define DEBUG_REDRAW 1;
44 // Tiles are a way to minimize the number of redraws, eliminating too small redraws.
45 // The canvas stores a 2D array of ints, each representing a TILE_SIZExTILE_SIZE pixels tile.
46 // If any part of it is dirtied, the entire tile is dirtied (its int is nonzero) and repainted.
47 #define TILE_SIZE 32
49 enum {
50 RENDERMODE_NORMAL,
51 RENDERMODE_NOAA,
52 RENDERMODE_OUTLINE
53 };
55 const gint sp_canvas_update_priority = G_PRIORITY_HIGH_IDLE;
57 #define SP_CANVAS_WINDOW(c) (((GtkWidget *) (c))->window)
59 enum {
60 SP_CANVAS_ITEM_VISIBLE = 1 << 7,
61 SP_CANVAS_ITEM_NEED_UPDATE = 1 << 8,
62 SP_CANVAS_ITEM_NEED_AFFINE = 1 << 9
63 };
65 /**
66 * A group of Items.
67 */
68 struct SPCanvasGroup {
69 SPCanvasItem item;
71 GList *items, *last;
72 };
74 /**
75 * The SPCanvasGroup vtable.
76 */
77 struct SPCanvasGroupClass {
78 SPCanvasItemClass parent_class;
79 };
81 /**
82 * The SPCanvas vtable.
83 */
84 struct SPCanvasClass {
85 GtkWidgetClass parent_class;
86 };
88 static void group_add (SPCanvasGroup *group, SPCanvasItem *item);
89 static void group_remove (SPCanvasGroup *group, SPCanvasItem *item);
91 /* SPCanvasItem */
93 enum {ITEM_EVENT, ITEM_LAST_SIGNAL};
96 static void sp_canvas_request_update (SPCanvas *canvas);
98 static void sp_canvas_item_class_init (SPCanvasItemClass *klass);
99 static void sp_canvas_item_init (SPCanvasItem *item);
100 static void sp_canvas_item_dispose (GObject *object);
101 static void sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, const gchar *first_arg_name, va_list args);
103 static int emit_event (SPCanvas *canvas, GdkEvent *event);
105 static guint item_signals[ITEM_LAST_SIGNAL] = { 0 };
107 static GtkObjectClass *item_parent_class;
109 /**
110 * Registers the SPCanvasItem class with Glib and returns its type number.
111 */
112 GType
113 sp_canvas_item_get_type (void)
114 {
115 static GType type = 0;
116 if (!type) {
117 static const GTypeInfo info = {
118 sizeof (SPCanvasItemClass),
119 NULL, NULL,
120 (GClassInitFunc) sp_canvas_item_class_init,
121 NULL, NULL,
122 sizeof (SPCanvasItem),
123 0,
124 (GInstanceInitFunc) sp_canvas_item_init,
125 NULL
126 };
127 type = g_type_register_static (GTK_TYPE_OBJECT, "SPCanvasItem", &info, (GTypeFlags)0);
128 }
130 return type;
131 }
133 /**
134 * Initializes the SPCanvasItem vtable and the "event" signal.
135 */
136 static void
137 sp_canvas_item_class_init (SPCanvasItemClass *klass)
138 {
139 GObjectClass *object_class = (GObjectClass *) klass;
141 /* fixme: Derive from GObject */
142 item_parent_class = (GtkObjectClass*)gtk_type_class (GTK_TYPE_OBJECT);
144 item_signals[ITEM_EVENT] = g_signal_new ("event",
145 G_TYPE_FROM_CLASS (klass),
146 G_SIGNAL_RUN_LAST,
147 G_STRUCT_OFFSET (SPCanvasItemClass, event),
148 NULL, NULL,
149 sp_marshal_BOOLEAN__POINTER,
150 G_TYPE_BOOLEAN, 1,
151 GDK_TYPE_EVENT);
153 object_class->dispose = sp_canvas_item_dispose;
154 }
156 /**
157 * Callback for initialization of SPCanvasItem.
158 */
159 static void
160 sp_canvas_item_init (SPCanvasItem *item)
161 {
162 item->flags |= SP_CANVAS_ITEM_VISIBLE;
163 item->xform = NR::Matrix(NR::identity());
164 }
166 /**
167 * Constructs new SPCanvasItem on SPCanvasGroup.
168 */
169 SPCanvasItem *
170 sp_canvas_item_new (SPCanvasGroup *parent, GtkType type, const gchar *first_arg_name, ...)
171 {
172 va_list args;
174 g_return_val_if_fail (parent != NULL, NULL);
175 g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL);
176 g_return_val_if_fail (gtk_type_is_a (type, sp_canvas_item_get_type ()), NULL);
178 SPCanvasItem *item = SP_CANVAS_ITEM (gtk_type_new (type));
180 va_start (args, first_arg_name);
181 sp_canvas_item_construct (item, parent, first_arg_name, args);
182 va_end (args);
184 return item;
185 }
187 /**
188 * Sets up the newly created SPCanvasItem.
189 *
190 * We make it static for encapsulation reasons since it was nowhere used.
191 */
192 static void
193 sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, const gchar *first_arg_name, va_list args)
194 {
195 g_return_if_fail (SP_IS_CANVAS_GROUP (parent));
196 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
198 item->parent = SP_CANVAS_ITEM (parent);
199 item->canvas = item->parent->canvas;
201 g_object_set_valist (G_OBJECT (item), first_arg_name, args);
203 group_add (SP_CANVAS_GROUP (item->parent), item);
205 sp_canvas_item_request_update (item);
206 }
208 /**
209 * Helper function that requests redraw only if item's visible flag is set.
210 */
211 static void
212 redraw_if_visible (SPCanvasItem *item)
213 {
214 if (item->flags & SP_CANVAS_ITEM_VISIBLE) {
215 int x0 = (int)(item->x1);
216 int x1 = (int)(item->x2);
217 int y0 = (int)(item->y1);
218 int y1 = (int)(item->y2);
220 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
221 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
222 }
223 }
224 }
226 /**
227 * Callback that removes item from all referers and destroys it.
228 */
229 static void
230 sp_canvas_item_dispose (GObject *object)
231 {
232 SPCanvasItem *item = SP_CANVAS_ITEM (object);
234 // Hack: if this is a ctrlrect, move it to 0,0;
235 // this redraws only the stroke of the rect to be deleted,
236 // avoiding redraw of the entire area
237 if (SP_IS_CTRLRECT(item)) {
238 SP_CTRLRECT(object)->setRectangle(NR::Rect(NR::Point(0,0),NR::Point(0,0)));
239 SP_CTRLRECT(object)->update(item->xform, 0);
240 } else {
241 redraw_if_visible (item);
242 }
243 item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
245 if (item == item->canvas->current_item) {
246 item->canvas->current_item = NULL;
247 item->canvas->need_repick = TRUE;
248 }
250 if (item == item->canvas->new_current_item) {
251 item->canvas->new_current_item = NULL;
252 item->canvas->need_repick = TRUE;
253 }
255 if (item == item->canvas->grabbed_item) {
256 item->canvas->grabbed_item = NULL;
257 gdk_pointer_ungrab (GDK_CURRENT_TIME);
258 }
260 if (item == item->canvas->focused_item)
261 item->canvas->focused_item = NULL;
263 if (item->parent) {
264 group_remove (SP_CANVAS_GROUP (item->parent), item);
265 }
267 G_OBJECT_CLASS (item_parent_class)->dispose (object);
268 }
270 /**
271 * Helper function to update item and its children.
272 *
273 * NB! affine is parent2canvas.
274 */
275 static void
276 sp_canvas_item_invoke_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
277 {
278 /* Apply the child item's transform */
279 NR::Matrix child_affine = item->xform * affine;
281 /* apply object flags to child flags */
282 int child_flags = flags & ~SP_CANVAS_UPDATE_REQUESTED;
284 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
285 child_flags |= SP_CANVAS_UPDATE_REQUESTED;
287 if (item->flags & SP_CANVAS_ITEM_NEED_AFFINE)
288 child_flags |= SP_CANVAS_UPDATE_AFFINE;
290 if (child_flags & (SP_CANVAS_UPDATE_REQUESTED | SP_CANVAS_UPDATE_AFFINE)) {
291 if (SP_CANVAS_ITEM_GET_CLASS (item)->update)
292 SP_CANVAS_ITEM_GET_CLASS (item)->update (item, child_affine, child_flags);
293 }
295 GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_UPDATE);
296 GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_AFFINE);
297 }
299 /**
300 * Helper function to invoke the point method of the item.
301 *
302 * The argument x, y should be in the parent's item-relative coordinate
303 * system. This routine applies the inverse of the item's transform,
304 * maintaining the affine invariant.
305 */
306 static double
307 sp_canvas_item_invoke_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
308 {
309 if (SP_CANVAS_ITEM_GET_CLASS (item)->point)
310 return SP_CANVAS_ITEM_GET_CLASS (item)->point (item, p, actual_item);
312 return NR_HUGE;
313 }
315 /**
316 * Makes the item's affine transformation matrix be equal to the specified
317 * matrix.
318 *
319 * @item: A canvas item.
320 * @affine: An affine transformation matrix.
321 */
322 void
323 sp_canvas_item_affine_absolute (SPCanvasItem *item, NR::Matrix const& affine)
324 {
325 item->xform = affine;
327 if (!(item->flags & SP_CANVAS_ITEM_NEED_AFFINE)) {
328 item->flags |= SP_CANVAS_ITEM_NEED_AFFINE;
329 if (item->parent != NULL) {
330 sp_canvas_item_request_update (item->parent);
331 } else {
332 sp_canvas_request_update (item->canvas);
333 }
334 }
336 item->canvas->need_repick = TRUE;
337 }
339 /**
340 * Convenience function to reorder items in a group's child list.
341 *
342 * This puts the specified link after the "before" link.
343 */
344 static void
345 put_item_after (GList *link, GList *before)
346 {
347 if (link == before)
348 return;
350 SPCanvasGroup *parent = SP_CANVAS_GROUP (SP_CANVAS_ITEM (link->data)->parent);
352 if (before == NULL) {
353 if (link == parent->items) return;
355 link->prev->next = link->next;
357 if (link->next) {
358 link->next->prev = link->prev;
359 } else {
360 parent->last = link->prev;
361 }
363 link->prev = before;
364 link->next = parent->items;
365 link->next->prev = link;
366 parent->items = link;
367 } else {
368 if ((link == parent->last) && (before == parent->last->prev))
369 return;
371 if (link->next)
372 link->next->prev = link->prev;
374 if (link->prev)
375 link->prev->next = link->next;
376 else {
377 parent->items = link->next;
378 parent->items->prev = NULL;
379 }
381 link->prev = before;
382 link->next = before->next;
384 link->prev->next = link;
386 if (link->next)
387 link->next->prev = link;
388 else
389 parent->last = link;
390 }
391 }
394 /**
395 * Raises the item in its parent's stack by the specified number of positions.
396 *
397 * \param item A canvas item.
398 * \param positions Number of steps to raise the item.
399 *
400 * If the number of positions is greater than the distance to the top of the
401 * stack, then the item is put at the top.
402 */
403 void
404 sp_canvas_item_raise (SPCanvasItem *item, int positions)
405 {
406 g_return_if_fail (item != NULL);
407 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
408 g_return_if_fail (positions >= 0);
410 if (!item->parent || positions == 0)
411 return;
413 SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
414 GList *link = g_list_find (parent->items, item);
415 g_assert (link != NULL);
417 GList *before;
418 for (before = link; positions && before; positions--)
419 before = before->next;
421 if (!before)
422 before = parent->last;
424 put_item_after (link, before);
426 redraw_if_visible (item);
427 item->canvas->need_repick = TRUE;
428 }
431 /**
432 * Lowers the item in its parent's stack by the specified number of positions.
433 *
434 * \param item A canvas item.
435 * \param positions Number of steps to lower the item.
436 *
437 * If the number of positions is greater than the distance to the bottom of the
438 * stack, then the item is put at the bottom.
439 **/
440 void
441 sp_canvas_item_lower (SPCanvasItem *item, int positions)
442 {
443 g_return_if_fail (item != NULL);
444 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
445 g_return_if_fail (positions >= 1);
447 if (!item->parent || positions == 0)
448 return;
450 SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
451 GList *link = g_list_find (parent->items, item);
452 g_assert (link != NULL);
454 GList *before;
455 if (link->prev)
456 for (before = link->prev; positions && before; positions--)
457 before = before->prev;
458 else
459 before = NULL;
461 put_item_after (link, before);
463 redraw_if_visible (item);
464 item->canvas->need_repick = TRUE;
465 }
467 /**
468 * Sets visible flag on item and requests a redraw.
469 */
470 void
471 sp_canvas_item_show (SPCanvasItem *item)
472 {
473 g_return_if_fail (item != NULL);
474 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
476 if (item->flags & SP_CANVAS_ITEM_VISIBLE)
477 return;
479 item->flags |= SP_CANVAS_ITEM_VISIBLE;
481 int x0 = (int)(item->x1);
482 int x1 = (int)(item->x2);
483 int y0 = (int)(item->y1);
484 int y1 = (int)(item->y2);
486 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
487 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
488 item->canvas->need_repick = TRUE;
489 }
490 }
492 /**
493 * Clears visible flag on item and requests a redraw.
494 */
495 void
496 sp_canvas_item_hide (SPCanvasItem *item)
497 {
498 g_return_if_fail (item != NULL);
499 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
501 if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
502 return;
504 item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
506 int x0 = (int)(item->x1);
507 int x1 = (int)(item->x2);
508 int y0 = (int)(item->y1);
509 int y1 = (int)(item->y2);
511 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
512 sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
513 item->canvas->need_repick = TRUE;
514 }
515 }
517 /**
518 * Grab item under cursor.
519 *
520 * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE
521 */
522 int
523 sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
524 {
525 g_return_val_if_fail (item != NULL, -1);
526 g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1);
527 g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1);
529 if (item->canvas->grabbed_item)
530 return -1;
532 if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
533 return -1;
535 /* fixme: Top hack (Lauris) */
536 /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
537 /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
538 gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
539 (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
540 NULL, cursor, etime);
542 item->canvas->grabbed_item = item;
543 item->canvas->grabbed_event_mask = event_mask;
544 item->canvas->current_item = item; /* So that events go to the grabbed item */
546 return 0;
547 }
549 /**
550 * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
551 * mouse.
552 *
553 * \param item A canvas item that holds a grab.
554 * \param etime The timestamp for ungrabbing the mouse.
555 */
556 void
557 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
558 {
559 g_return_if_fail (item != NULL);
560 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
562 if (item->canvas->grabbed_item != item)
563 return;
565 item->canvas->grabbed_item = NULL;
567 gdk_pointer_ungrab (etime);
568 }
570 /**
571 * Returns the product of all transformation matrices from the root item down
572 * to the item.
573 */
574 NR::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
575 {
576 g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
578 NR::Matrix affine = NR::identity();
580 while (item) {
581 affine *= item->xform;
582 item = item->parent;
583 }
584 return affine;
585 }
587 /**
588 * Helper that returns true iff item is descendant of parent.
589 */
590 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
591 {
592 while (item) {
593 if (item == parent)
594 return true;
595 item = item->parent;
596 }
598 return false;
599 }
601 /**
602 * Focus canvas, and item under cursor if it is not already focussed.
603 */
604 void
605 sp_canvas_item_grab_focus (SPCanvasItem *item)
606 {
607 g_return_if_fail (item != NULL);
608 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
609 g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
611 SPCanvasItem *focused_item = item->canvas->focused_item;
613 if (focused_item) {
614 GdkEvent ev;
615 ev.focus_change.type = GDK_FOCUS_CHANGE;
616 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
617 ev.focus_change.send_event = FALSE;
618 ev.focus_change.in = FALSE;
620 emit_event (item->canvas, &ev);
621 }
623 item->canvas->focused_item = item;
624 gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
626 if (focused_item) {
627 GdkEvent ev;
628 ev.focus_change.type = GDK_FOCUS_CHANGE;
629 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
630 ev.focus_change.send_event = FALSE;
631 ev.focus_change.in = TRUE;
633 emit_event (item->canvas, &ev);
634 }
635 }
637 /**
638 * Requests that the canvas queue an update for the specified item.
639 *
640 * To be used only by item implementations.
641 */
642 void
643 sp_canvas_item_request_update (SPCanvasItem *item)
644 {
645 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
646 return;
648 item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
650 if (item->parent != NULL) {
651 /* Recurse up the tree */
652 sp_canvas_item_request_update (item->parent);
653 } else {
654 /* Have reached the top of the tree, make sure the update call gets scheduled. */
655 sp_canvas_request_update (item->canvas);
656 }
657 }
659 /**
660 * Returns position of item in group.
661 */
662 gint sp_canvas_item_order (SPCanvasItem * item)
663 {
664 return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
665 }
667 /* SPCanvasGroup */
669 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
670 static void sp_canvas_group_init (SPCanvasGroup *group);
671 static void sp_canvas_group_destroy (GtkObject *object);
673 static void sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
674 static double sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item);
675 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
677 static SPCanvasItemClass *group_parent_class;
679 /**
680 * Registers SPCanvasGroup class with Gtk and returns its type number.
681 */
682 GtkType
683 sp_canvas_group_get_type (void)
684 {
685 static GtkType group_type = 0;
687 if (!group_type) {
688 static const GtkTypeInfo group_info = {
689 "SPCanvasGroup",
690 sizeof (SPCanvasGroup),
691 sizeof (SPCanvasGroupClass),
692 (GtkClassInitFunc) sp_canvas_group_class_init,
693 (GtkObjectInitFunc) sp_canvas_group_init,
694 NULL, NULL, NULL
695 };
697 group_type = gtk_type_unique (sp_canvas_item_get_type (), &group_info);
698 }
700 return group_type;
701 }
703 /**
704 * Class initialization function for SPCanvasGroupClass
705 */
706 static void
707 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
708 {
709 GtkObjectClass *object_class = (GtkObjectClass *) klass;
710 SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
712 group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
714 object_class->destroy = sp_canvas_group_destroy;
716 item_class->update = sp_canvas_group_update;
717 item_class->render = sp_canvas_group_render;
718 item_class->point = sp_canvas_group_point;
719 }
721 /**
722 * Callback. Empty.
723 */
724 static void
725 sp_canvas_group_init (SPCanvasGroup */*group*/)
726 {
727 /* Nothing here */
728 }
730 /**
731 * Callback that destroys all items in group and calls group's virtual
732 * destroy() function.
733 */
734 static void
735 sp_canvas_group_destroy (GtkObject *object)
736 {
737 g_return_if_fail (object != NULL);
738 g_return_if_fail (SP_IS_CANVAS_GROUP (object));
740 const SPCanvasGroup *group = SP_CANVAS_GROUP (object);
742 GList *list = group->items;
743 while (list) {
744 SPCanvasItem *child = (SPCanvasItem *)list->data;
745 list = list->next;
747 gtk_object_destroy (GTK_OBJECT (child));
748 }
750 if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
751 (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
752 }
754 /**
755 * Update handler for canvas groups
756 */
757 static void
758 sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
759 {
760 const SPCanvasGroup *group = SP_CANVAS_GROUP (item);
761 NR::ConvexHull corners(NR::Point(0, 0));
762 bool empty=true;
764 for (GList *list = group->items; list; list = list->next) {
765 SPCanvasItem *i = (SPCanvasItem *)list->data;
767 sp_canvas_item_invoke_update (i, affine, flags);
769 if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
770 if (empty) {
771 corners = NR::ConvexHull(NR::Point(i->x1, i->y1));
772 empty = false;
773 } else {
774 corners.add(NR::Point(i->x1, i->y1));
775 }
776 corners.add(NR::Point(i->x2, i->y2));
777 }
778 }
780 NR::Maybe<NR::Rect> const bounds = corners.bounds();
781 if (bounds) {
782 item->x1 = bounds->min()[NR::X];
783 item->y1 = bounds->min()[NR::Y];
784 item->x2 = bounds->max()[NR::X];
785 item->y2 = bounds->max()[NR::Y];
786 } else {
787 // FIXME ?
788 item->x1 = item->x2 = item->y1 = item->y2 = 0;
789 }
790 }
792 /**
793 * Point handler for canvas groups.
794 */
795 static double
796 sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
797 {
798 const SPCanvasGroup *group = SP_CANVAS_GROUP (item);
799 const double x = p[NR::X];
800 const double y = p[NR::Y];
801 int x1 = (int)(x - item->canvas->close_enough);
802 int y1 = (int)(y - item->canvas->close_enough);
803 int x2 = (int)(x + item->canvas->close_enough);
804 int y2 = (int)(y + item->canvas->close_enough);
806 double best = 0.0;
807 *actual_item = NULL;
809 double dist = 0.0;
811 for (GList *list = group->items; list; list = list->next) {
812 SPCanvasItem *child = (SPCanvasItem *)list->data;
814 if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
815 SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
817 int has_point;
818 if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
819 dist = sp_canvas_item_invoke_point (child, p, &point_item);
820 has_point = TRUE;
821 } else
822 has_point = FALSE;
824 if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
825 best = dist;
826 *actual_item = point_item;
827 }
828 }
829 }
831 return best;
832 }
834 /**
835 * Renders all visible canvas group items in buf rectangle.
836 */
837 static void
838 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
839 {
840 const SPCanvasGroup *group = SP_CANVAS_GROUP (item);
842 for (GList *list = group->items; list; list = list->next) {
843 SPCanvasItem *child = (SPCanvasItem *)list->data;
844 if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
845 if ((child->x1 < buf->rect.x1) &&
846 (child->y1 < buf->rect.y1) &&
847 (child->x2 > buf->rect.x0) &&
848 (child->y2 > buf->rect.y0)) {
849 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
850 SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
851 }
852 }
853 }
854 }
856 /**
857 * Adds an item to a canvas group.
858 */
859 static void
860 group_add (SPCanvasGroup *group, SPCanvasItem *item)
861 {
862 gtk_object_ref (GTK_OBJECT (item));
863 gtk_object_sink (GTK_OBJECT (item));
865 if (!group->items) {
866 group->items = g_list_append (group->items, item);
867 group->last = group->items;
868 } else {
869 group->last = g_list_append (group->last, item)->next;
870 }
872 sp_canvas_item_request_update (item);
873 }
875 /**
876 * Removes an item from a canvas group
877 */
878 static void
879 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
880 {
881 g_return_if_fail (group != NULL);
882 g_return_if_fail (SP_IS_CANVAS_GROUP (group));
883 g_return_if_fail (item != NULL);
885 for (GList *children = group->items; children; children = children->next) {
886 if (children->data == item) {
888 /* Unparent the child */
890 item->parent = NULL;
891 gtk_object_unref (GTK_OBJECT (item));
893 /* Remove it from the list */
895 if (children == group->last) group->last = children->prev;
897 group->items = g_list_remove_link (group->items, children);
898 g_list_free (children);
899 break;
900 }
901 }
902 }
904 /* SPCanvas */
906 static void sp_canvas_class_init (SPCanvasClass *klass);
907 static void sp_canvas_init (SPCanvas *canvas);
908 static void sp_canvas_destroy (GtkObject *object);
910 static void sp_canvas_realize (GtkWidget *widget);
911 static void sp_canvas_unrealize (GtkWidget *widget);
913 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
914 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
916 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
917 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
918 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
919 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
920 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
921 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
922 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
923 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
925 static GtkWidgetClass *canvas_parent_class;
927 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb);
928 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb);
929 static void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val);
930 static int do_update (SPCanvas *canvas);
932 /**
933 * Registers the SPCanvas class if necessary, and returns the type ID
934 * associated to it.
935 *
936 * \return The type ID of the SPCanvas class.
937 **/
938 GtkType
939 sp_canvas_get_type (void)
940 {
941 static GtkType canvas_type = 0;
943 if (!canvas_type) {
944 static const GtkTypeInfo canvas_info = {
945 "SPCanvas",
946 sizeof (SPCanvas),
947 sizeof (SPCanvasClass),
948 (GtkClassInitFunc) sp_canvas_class_init,
949 (GtkObjectInitFunc) sp_canvas_init,
950 NULL, NULL, NULL
951 };
953 canvas_type = gtk_type_unique (GTK_TYPE_WIDGET, &canvas_info);
954 }
956 return canvas_type;
957 }
959 /**
960 * Class initialization function for SPCanvasClass.
961 */
962 static void
963 sp_canvas_class_init (SPCanvasClass *klass)
964 {
965 GtkObjectClass *object_class = (GtkObjectClass *) klass;
966 GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
968 canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
970 object_class->destroy = sp_canvas_destroy;
972 widget_class->realize = sp_canvas_realize;
973 widget_class->unrealize = sp_canvas_unrealize;
974 widget_class->size_request = sp_canvas_size_request;
975 widget_class->size_allocate = sp_canvas_size_allocate;
976 widget_class->button_press_event = sp_canvas_button;
977 widget_class->button_release_event = sp_canvas_button;
978 widget_class->motion_notify_event = sp_canvas_motion;
979 widget_class->scroll_event = sp_canvas_scroll;
980 widget_class->expose_event = sp_canvas_expose;
981 widget_class->key_press_event = sp_canvas_key;
982 widget_class->key_release_event = sp_canvas_key;
983 widget_class->enter_notify_event = sp_canvas_crossing;
984 widget_class->leave_notify_event = sp_canvas_crossing;
985 widget_class->focus_in_event = sp_canvas_focus_in;
986 widget_class->focus_out_event = sp_canvas_focus_out;
987 }
989 /**
990 * Callback: object initialization for SPCanvas.
991 */
992 static void
993 sp_canvas_init (SPCanvas *canvas)
994 {
995 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
996 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
997 GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
999 canvas->pick_event.type = GDK_LEAVE_NOTIFY;
1000 canvas->pick_event.crossing.x = 0;
1001 canvas->pick_event.crossing.y = 0;
1003 /* Create the root item as a special case */
1004 canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
1005 canvas->root->canvas = canvas;
1007 gtk_object_ref (GTK_OBJECT (canvas->root));
1008 gtk_object_sink (GTK_OBJECT (canvas->root));
1010 canvas->need_repick = TRUE;
1012 // See comment at in sp-canvas.h.
1013 canvas->gen_all_enter_events = false;
1015 canvas->tiles=NULL;
1016 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1017 canvas->tileH=canvas->tileV=0;
1019 canvas->forced_redraw_count = 0;
1020 canvas->forced_redraw_limit = -1;
1022 canvas->is_scrolling = false;
1024 }
1026 /**
1027 * Convenience function to remove the idle handler of a canvas.
1028 */
1029 static void
1030 remove_idle (SPCanvas *canvas)
1031 {
1032 if (canvas->idle_id) {
1033 gtk_idle_remove (canvas->idle_id);
1034 canvas->idle_id = 0;
1035 }
1036 }
1038 /*
1039 * Removes the transient state of the canvas (idle handler, grabs).
1040 */
1041 static void
1042 shutdown_transients (SPCanvas *canvas)
1043 {
1044 /* We turn off the need_redraw flag, since if the canvas is mapped again
1045 * it will request a redraw anyways. We do not turn off the need_update
1046 * flag, though, because updates are not queued when the canvas remaps
1047 * itself.
1048 */
1049 if (canvas->need_redraw) {
1050 canvas->need_redraw = FALSE;
1051 }
1052 if ( canvas->tiles ) g_free(canvas->tiles);
1053 canvas->tiles=NULL;
1054 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1055 canvas->tileH=canvas->tileV=0;
1057 if (canvas->grabbed_item) {
1058 canvas->grabbed_item = NULL;
1059 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1060 }
1062 remove_idle (canvas);
1063 }
1065 /**
1066 * Destroy handler for SPCanvas.
1067 */
1068 static void
1069 sp_canvas_destroy (GtkObject *object)
1070 {
1071 SPCanvas *canvas = SP_CANVAS (object);
1073 if (canvas->root) {
1074 gtk_object_unref (GTK_OBJECT (canvas->root));
1075 canvas->root = NULL;
1076 }
1078 shutdown_transients (canvas);
1080 if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1081 (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1082 }
1084 /**
1085 * Returns new canvas as widget.
1086 */
1087 GtkWidget *
1088 sp_canvas_new_aa (void)
1089 {
1090 SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1092 return (GtkWidget *) canvas;
1093 }
1095 /**
1096 * The canvas widget's realize callback.
1097 */
1098 static void
1099 sp_canvas_realize (GtkWidget *widget)
1100 {
1101 SPCanvas *canvas = SP_CANVAS (widget);
1103 GdkWindowAttr attributes;
1104 attributes.window_type = GDK_WINDOW_CHILD;
1105 attributes.x = widget->allocation.x;
1106 attributes.y = widget->allocation.y;
1107 attributes.width = widget->allocation.width;
1108 attributes.height = widget->allocation.height;
1109 attributes.wclass = GDK_INPUT_OUTPUT;
1110 attributes.visual = gdk_rgb_get_visual ();
1111 attributes.colormap = gdk_rgb_get_cmap ();
1112 attributes.event_mask = (gtk_widget_get_events (widget) |
1113 GDK_EXPOSURE_MASK |
1114 GDK_BUTTON_PRESS_MASK |
1115 GDK_BUTTON_RELEASE_MASK |
1116 GDK_POINTER_MOTION_MASK |
1117 GDK_PROXIMITY_IN_MASK |
1118 GDK_PROXIMITY_OUT_MASK |
1119 GDK_KEY_PRESS_MASK |
1120 GDK_KEY_RELEASE_MASK |
1121 GDK_ENTER_NOTIFY_MASK |
1122 GDK_LEAVE_NOTIFY_MASK |
1123 GDK_FOCUS_CHANGE_MASK);
1124 gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1126 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1127 gdk_window_set_user_data (widget->window, widget);
1129 if ( prefs_get_int_attribute ("options.useextinput", "value", 1) )
1130 gtk_widget_set_events(widget, attributes.event_mask);
1132 widget->style = gtk_style_attach (widget->style, widget->window);
1134 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1136 canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1137 }
1139 /**
1140 * The canvas widget's unrealize callback.
1141 */
1142 static void
1143 sp_canvas_unrealize (GtkWidget *widget)
1144 {
1145 SPCanvas *canvas = SP_CANVAS (widget);
1147 shutdown_transients (canvas);
1149 gdk_gc_destroy (canvas->pixmap_gc);
1150 canvas->pixmap_gc = NULL;
1152 if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
1153 (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
1154 }
1156 /**
1157 * The canvas widget's size_request callback.
1158 */
1159 static void
1160 sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req)
1161 {
1162 static_cast<void>(SP_CANVAS (widget));
1164 req->width = 256;
1165 req->height = 256;
1166 }
1168 /**
1169 * The canvas widget's size_allocate callback.
1170 */
1171 static void
1172 sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1173 {
1174 SPCanvas *canvas = SP_CANVAS (widget);
1176 /* Schedule redraw of new region */
1177 sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
1178 if (allocation->width > widget->allocation.width) {
1179 sp_canvas_request_redraw (canvas,
1180 canvas->x0 + widget->allocation.width,
1181 0,
1182 canvas->x0 + allocation->width,
1183 canvas->y0 + allocation->height);
1184 }
1185 if (allocation->height > widget->allocation.height) {
1186 sp_canvas_request_redraw (canvas,
1187 0,
1188 canvas->y0 + widget->allocation.height,
1189 canvas->x0 + allocation->width,
1190 canvas->y0 + allocation->height);
1191 }
1193 widget->allocation = *allocation;
1195 if (GTK_WIDGET_REALIZED (widget)) {
1196 gdk_window_move_resize (widget->window,
1197 widget->allocation.x, widget->allocation.y,
1198 widget->allocation.width, widget->allocation.height);
1199 }
1200 }
1202 /**
1203 * Helper that emits an event for an item in the canvas, be it the current
1204 * item, grabbed item, or focused item, as appropriate.
1205 */
1206 static int
1207 emit_event (SPCanvas *canvas, GdkEvent *event)
1208 {
1209 guint mask;
1211 if (canvas->grabbed_item) {
1212 switch (event->type) {
1213 case GDK_ENTER_NOTIFY:
1214 mask = GDK_ENTER_NOTIFY_MASK;
1215 break;
1216 case GDK_LEAVE_NOTIFY:
1217 mask = GDK_LEAVE_NOTIFY_MASK;
1218 break;
1219 case GDK_MOTION_NOTIFY:
1220 mask = GDK_POINTER_MOTION_MASK;
1221 break;
1222 case GDK_BUTTON_PRESS:
1223 case GDK_2BUTTON_PRESS:
1224 case GDK_3BUTTON_PRESS:
1225 mask = GDK_BUTTON_PRESS_MASK;
1226 break;
1227 case GDK_BUTTON_RELEASE:
1228 mask = GDK_BUTTON_RELEASE_MASK;
1229 break;
1230 case GDK_KEY_PRESS:
1231 mask = GDK_KEY_PRESS_MASK;
1232 break;
1233 case GDK_KEY_RELEASE:
1234 mask = GDK_KEY_RELEASE_MASK;
1235 break;
1236 case GDK_SCROLL:
1237 mask = GDK_SCROLL;
1238 break;
1239 default:
1240 mask = 0;
1241 break;
1242 }
1244 if (!(mask & canvas->grabbed_event_mask)) return FALSE;
1245 }
1247 /* Convert to world coordinates -- we have two cases because of diferent
1248 * offsets of the fields in the event structures.
1249 */
1251 GdkEvent ev = *event;
1253 switch (ev.type) {
1254 case GDK_ENTER_NOTIFY:
1255 case GDK_LEAVE_NOTIFY:
1256 ev.crossing.x += canvas->x0;
1257 ev.crossing.y += canvas->y0;
1258 break;
1259 case GDK_MOTION_NOTIFY:
1260 case GDK_BUTTON_PRESS:
1261 case GDK_2BUTTON_PRESS:
1262 case GDK_3BUTTON_PRESS:
1263 case GDK_BUTTON_RELEASE:
1264 ev.motion.x += canvas->x0;
1265 ev.motion.y += canvas->y0;
1266 break;
1267 default:
1268 break;
1269 }
1271 /* Choose where we send the event */
1273 /* canvas->current_item becomes NULL in some cases under Win32
1274 ** (e.g. if the pointer leaves the window). So this is a hack that
1275 ** Lauris applied to SP to get around the problem.
1276 */
1277 SPCanvasItem* item = NULL;
1278 if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
1279 item = canvas->grabbed_item;
1280 } else {
1281 item = canvas->current_item;
1282 }
1284 if (canvas->focused_item &&
1285 ((event->type == GDK_KEY_PRESS) ||
1286 (event->type == GDK_KEY_RELEASE) ||
1287 (event->type == GDK_FOCUS_CHANGE))) {
1288 item = canvas->focused_item;
1289 }
1291 /* The event is propagated up the hierarchy (for if someone connected to
1292 * a group instead of a leaf event), and emission is stopped if a
1293 * handler returns TRUE, just like for GtkWidget events.
1294 */
1296 gint finished = FALSE;
1298 while (item && !finished) {
1299 gtk_object_ref (GTK_OBJECT (item));
1300 gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished);
1301 SPCanvasItem *parent = item->parent;
1302 gtk_object_unref (GTK_OBJECT (item));
1303 item = parent;
1304 }
1306 return finished;
1307 }
1309 /**
1310 * Helper that re-picks the current item in the canvas, based on the event's
1311 * coordinates and emits enter/leave events for items as appropriate.
1312 */
1313 static int
1314 pick_current_item (SPCanvas *canvas, GdkEvent *event)
1315 {
1316 int button_down = 0;
1317 double x, y;
1319 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1320 return FALSE;
1322 int retval = FALSE;
1324 if (canvas->gen_all_enter_events == false) {
1325 // If a button is down, we'll perform enter and leave events on the
1326 // current item, but not enter on any other item. This is more or
1327 // less like X pointer grabbing for canvas items.
1328 //
1329 button_down = canvas->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1330 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK);
1332 if (!button_down) canvas->left_grabbed_item = FALSE;
1333 }
1335 /* Save the event in the canvas. This is used to synthesize enter and
1336 * leave events in case the current item changes. It is also used to
1337 * re-pick the current item if the current one gets deleted. Also,
1338 * synthesize an enter event.
1339 */
1340 if (event != &canvas->pick_event) {
1341 if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
1342 /* these fields have the same offsets in both types of events */
1344 canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY;
1345 canvas->pick_event.crossing.window = event->motion.window;
1346 canvas->pick_event.crossing.send_event = event->motion.send_event;
1347 canvas->pick_event.crossing.subwindow = NULL;
1348 canvas->pick_event.crossing.x = event->motion.x;
1349 canvas->pick_event.crossing.y = event->motion.y;
1350 canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL;
1351 canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR;
1352 canvas->pick_event.crossing.focus = FALSE;
1353 canvas->pick_event.crossing.state = event->motion.state;
1355 /* these fields don't have the same offsets in both types of events */
1357 if (event->type == GDK_MOTION_NOTIFY) {
1358 canvas->pick_event.crossing.x_root = event->motion.x_root;
1359 canvas->pick_event.crossing.y_root = event->motion.y_root;
1360 } else {
1361 canvas->pick_event.crossing.x_root = event->button.x_root;
1362 canvas->pick_event.crossing.y_root = event->button.y_root;
1363 }
1364 } else {
1365 canvas->pick_event = *event;
1366 }
1367 }
1369 /* Don't do anything else if this is a recursive call */
1370 if (canvas->in_repick) return retval;
1372 /* LeaveNotify means that there is no current item, so we don't look for one */
1373 if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
1374 /* these fields don't have the same offsets in both types of events */
1376 if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
1377 x = canvas->pick_event.crossing.x;
1378 y = canvas->pick_event.crossing.y;
1379 } else {
1380 x = canvas->pick_event.motion.x;
1381 y = canvas->pick_event.motion.y;
1382 }
1384 /* world coords */
1385 x += canvas->x0;
1386 y += canvas->y0;
1388 /* find the closest item */
1389 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1390 sp_canvas_item_invoke_point (canvas->root, NR::Point(x, y), &canvas->new_current_item);
1391 } else {
1392 canvas->new_current_item = NULL;
1393 }
1394 } else {
1395 canvas->new_current_item = NULL;
1396 }
1398 if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) {
1399 return retval; /* current item did not change */
1400 }
1402 /* Synthesize events for old and new current items */
1404 if ((canvas->new_current_item != canvas->current_item)
1405 && (canvas->current_item != NULL)
1406 && !canvas->left_grabbed_item) {
1407 GdkEvent new_event;
1408 SPCanvasItem *item;
1410 item = canvas->current_item;
1412 new_event = canvas->pick_event;
1413 new_event.type = GDK_LEAVE_NOTIFY;
1415 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1416 new_event.crossing.subwindow = NULL;
1417 canvas->in_repick = TRUE;
1418 retval = emit_event (canvas, &new_event);
1419 canvas->in_repick = FALSE;
1420 }
1422 if (canvas->gen_all_enter_events == false) {
1423 // new_current_item may have been set to NULL during the call to
1424 // emit_event() above
1425 if ((canvas->new_current_item != canvas->current_item) && button_down) {
1426 canvas->left_grabbed_item = TRUE;
1427 return retval;
1428 }
1429 }
1431 /* Handle the rest of cases */
1433 canvas->left_grabbed_item = FALSE;
1434 canvas->current_item = canvas->new_current_item;
1436 if (canvas->current_item != NULL) {
1437 GdkEvent new_event;
1439 new_event = canvas->pick_event;
1440 new_event.type = GDK_ENTER_NOTIFY;
1441 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
1442 new_event.crossing.subwindow = NULL;
1443 retval = emit_event (canvas, &new_event);
1444 }
1446 return retval;
1447 }
1449 /**
1450 * Button event handler for the canvas.
1451 */
1452 static gint
1453 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
1454 {
1455 SPCanvas *canvas = SP_CANVAS (widget);
1457 int retval = FALSE;
1459 /* dispatch normally regardless of the event's window if an item has
1460 has a pointer grab in effect */
1461 if (!canvas->grabbed_item &&
1462 event->window != SP_CANVAS_WINDOW (canvas))
1463 return retval;
1465 int mask;
1466 switch (event->button) {
1467 case 1:
1468 mask = GDK_BUTTON1_MASK;
1469 break;
1470 case 2:
1471 mask = GDK_BUTTON2_MASK;
1472 break;
1473 case 3:
1474 mask = GDK_BUTTON3_MASK;
1475 break;
1476 case 4:
1477 mask = GDK_BUTTON4_MASK;
1478 break;
1479 case 5:
1480 mask = GDK_BUTTON5_MASK;
1481 break;
1482 default:
1483 mask = 0;
1484 }
1486 switch (event->type) {
1487 case GDK_BUTTON_PRESS:
1488 case GDK_2BUTTON_PRESS:
1489 case GDK_3BUTTON_PRESS:
1490 /* Pick the current item as if the button were not pressed, and
1491 * then process the event.
1492 */
1493 canvas->state = event->state;
1494 pick_current_item (canvas, (GdkEvent *) event);
1495 canvas->state ^= mask;
1496 retval = emit_event (canvas, (GdkEvent *) event);
1497 break;
1499 case GDK_BUTTON_RELEASE:
1500 /* Process the event as if the button were pressed, then repick
1501 * after the button has been released
1502 */
1503 canvas->state = event->state;
1504 retval = emit_event (canvas, (GdkEvent *) event);
1505 event->state ^= mask;
1506 canvas->state = event->state;
1507 pick_current_item (canvas, (GdkEvent *) event);
1508 event->state ^= mask;
1509 break;
1511 default:
1512 g_assert_not_reached ();
1513 }
1515 return retval;
1516 }
1518 /**
1519 * Scroll event handler for the canvas.
1520 *
1521 * \todo FIXME: generate motion events to re-select items.
1522 */
1523 static gint
1524 sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
1525 {
1526 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1527 }
1529 /**
1530 * Motion event handler for the canvas.
1531 */
1532 static int
1533 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
1534 {
1535 SPCanvas *canvas = SP_CANVAS (widget);
1537 if (event->window != SP_CANVAS_WINDOW (canvas))
1538 return FALSE;
1540 if (canvas->grabbed_event_mask & GDK_POINTER_MOTION_HINT_MASK) {
1541 gint x, y;
1542 gdk_window_get_pointer (widget->window, &x, &y, NULL);
1543 event->x = x;
1544 event->y = y;
1545 }
1547 canvas->state = event->state;
1548 pick_current_item (canvas, (GdkEvent *) event);
1550 return emit_event (canvas, (GdkEvent *) event);
1551 }
1553 static void
1554 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)
1555 {
1556 GtkWidget *widget = GTK_WIDGET (canvas);
1558 SPCanvasBuf buf;
1559 if (canvas->rendermode != RENDERMODE_OUTLINE) {
1560 buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1561 } else {
1562 buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1563 }
1565 // Mark the region clean
1566 sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1568 buf.buf_rowstride = sw * 3; // CAIRO FIXME: for cairo output, the buffer must be RGB unpacked, i.e. sw * 4
1569 buf.rect.x0 = x0;
1570 buf.rect.y0 = y0;
1571 buf.rect.x1 = x1;
1572 buf.rect.y1 = y1;
1573 buf.visible_rect.x0 = draw_x1;
1574 buf.visible_rect.y0 = draw_y1;
1575 buf.visible_rect.x1 = draw_x2;
1576 buf.visible_rect.y1 = draw_y2;
1577 GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1578 buf.bg_color = (((color->red & 0xff00) << 8)
1579 | (color->green & 0xff00)
1580 | (color->blue >> 8));
1581 buf.is_empty = true;
1583 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1584 SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1585 }
1587 if (buf.is_empty) {
1588 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1589 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1590 canvas->pixmap_gc,
1591 TRUE,
1592 x0 - canvas->x0, y0 - canvas->y0,
1593 x1 - x0, y1 - y0);
1594 } else {
1595 /*
1596 // CAIRO FIXME: after SPCanvasBuf is made 32bpp throughout, this rgb_draw below can be replaced with the below.
1597 // Why this must not be done currently:
1598 // - all canvas items (handles, nodes etc) paint themselves assuming 24bpp
1599 // - cairo assumes bgra, but we have rgba, so r and b get swapped (until we paint all with cairo too)
1600 // - it does not seem to be any faster; in fact since with 32bpp, buf contains less pixels,
1601 // we need more bufs to paint a given area and as a result it's even a bit slower
1603 cairo_surface_t* cst = cairo_image_surface_create_for_data (
1604 buf.buf,
1605 CAIRO_FORMAT_RGB24, // unpacked, i.e. 32 bits! one byte is unused
1606 x1 - x0, y1 - y0,
1607 buf.buf_rowstride
1608 );
1609 cairo_t *ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1610 cairo_set_source_surface (ct, cst, x0 - canvas->x0, y0 - canvas->y0);
1611 cairo_paint (ct);
1612 cairo_destroy (ct);
1613 cairo_surface_finish (cst);
1614 cairo_surface_destroy (cst);
1615 */
1617 gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1618 canvas->pixmap_gc,
1619 x0 - canvas->x0, y0 - canvas->y0,
1620 x1 - x0, y1 - y0,
1621 GDK_RGB_DITHER_MAX,
1622 buf.buf,
1623 sw * 3,
1624 x0 - canvas->x0, y0 - canvas->y0);
1625 }
1627 if (canvas->rendermode != RENDERMODE_OUTLINE) {
1628 nr_pixelstore_256K_free (buf.buf);
1629 } else {
1630 nr_pixelstore_1M_free (buf.buf);
1631 }
1632 }
1634 struct PaintRectSetup {
1635 SPCanvas* canvas;
1636 NRRectL big_rect;
1637 GTimeVal start_time;
1638 int max_pixels;
1639 NR::Point mouse_loc;
1640 };
1642 /**
1643 * Paint the given rect, recursively subdividing the region until it is the size of a single
1644 * buffer.
1645 *
1646 * @return true if the drawing completes
1647 */
1648 static int
1649 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1650 {
1651 GTimeVal now;
1652 g_get_current_time (&now);
1654 glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1655 + (now.tv_usec - setup->start_time.tv_usec);
1657 // Allow only very fast buffers to be run together;
1658 // as soon as the total redraw time exceeds 1ms, cancel;
1659 // this returns control to the idle loop and allows Inkscape to process user input
1660 // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1661 // it will get back and finish painting what remains to paint.
1662 if (elapsed > 1000) {
1664 // Interrupting redraw isn't always good.
1665 // For example, when you drag one node of a big path, only the buffer containing
1666 // the mouse cursor will be redrawn again and again, and the rest of the path
1667 // will remain stale because Inkscape never has enough idle time to redraw all
1668 // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1669 // If this limit is set, and if we have aborted redraw more times than is allowed,
1670 // interrupting is blocked and we're forced to redraw full screen once
1671 // (after which we can again interrupt forced_redraw_limit times).
1672 if (setup->canvas->forced_redraw_limit < 0 ||
1673 setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1675 if (setup->canvas->forced_redraw_limit != -1) {
1676 setup->canvas->forced_redraw_count++;
1677 }
1679 return false;
1680 }
1681 }
1683 // Find the optimal buffer dimensions
1684 int bw = this_rect.x1 - this_rect.x0;
1685 int bh = this_rect.y1 - this_rect.y0;
1686 if ((bw < 1) || (bh < 1))
1687 return 0;
1689 if (bw * bh < setup->max_pixels) {
1690 // We are small enough
1691 sp_canvas_paint_single_buffer (setup->canvas,
1692 this_rect.x0, this_rect.y0,
1693 this_rect.x1, this_rect.y1,
1694 setup->big_rect.x0, setup->big_rect.y0,
1695 setup->big_rect.x1, setup->big_rect.y1, bw);
1696 return 1;
1697 }
1699 NRRectL lo = this_rect;
1700 NRRectL hi = this_rect;
1702 /*
1703 This test determines the redraw strategy:
1705 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1706 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1707 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1708 and seems to be faster for drawings with many smaller objects at zoom-out.
1710 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1711 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1712 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1713 faster.
1715 The default for now is the strips mode.
1716 */
1717 if (bw < bh) {
1718 int mid = (this_rect.x0 + this_rect.x1) / 2;
1719 // Make sure that mid lies on a tile boundary
1720 mid = (mid / TILE_SIZE) * TILE_SIZE;
1722 lo.x1 = mid;
1723 hi.x0 = mid;
1725 if (setup->mouse_loc[NR::X] < mid) {
1726 // Always paint towards the mouse first
1727 return sp_canvas_paint_rect_internal(setup, lo)
1728 && sp_canvas_paint_rect_internal(setup, hi);
1729 } else {
1730 return sp_canvas_paint_rect_internal(setup, hi)
1731 && sp_canvas_paint_rect_internal(setup, lo);
1732 }
1733 } else {
1734 int mid = (this_rect.y0 + this_rect.y1) / 2;
1735 // Make sure that mid lies on a tile boundary
1736 mid = (mid / TILE_SIZE) * TILE_SIZE;
1738 lo.y1 = mid;
1739 hi.y0 = mid;
1741 if (setup->mouse_loc[NR::Y] < mid) {
1742 // Always paint towards the mouse first
1743 return sp_canvas_paint_rect_internal(setup, lo)
1744 && sp_canvas_paint_rect_internal(setup, hi);
1745 } else {
1746 return sp_canvas_paint_rect_internal(setup, hi)
1747 && sp_canvas_paint_rect_internal(setup, lo);
1748 }
1749 }
1750 }
1753 /**
1754 * Helper that draws a specific rectangular part of the canvas.
1755 *
1756 * @return true if the rectangle painting succeeds.
1757 */
1758 static bool
1759 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1760 {
1761 g_return_val_if_fail (!canvas->need_update, false);
1763 NRRectL rect;
1764 rect.x0 = xx0;
1765 rect.x1 = xx1;
1766 rect.y0 = yy0;
1767 rect.y1 = yy1;
1769 // Clip rect-to-draw by the current visible area
1770 rect.x0 = MAX (rect.x0, canvas->x0);
1771 rect.y0 = MAX (rect.y0, canvas->y0);
1772 rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1773 rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1775 #ifdef DEBUG_REDRAW
1776 // paint the area to redraw yellow
1777 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1778 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1779 canvas->pixmap_gc,
1780 TRUE,
1781 rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1782 rect.x1 - rect.x0, rect.y1 - rect.y0);
1783 #endif
1785 PaintRectSetup setup;
1787 setup.canvas = canvas;
1788 setup.big_rect = rect;
1790 // Save the mouse location
1791 gint x, y;
1792 gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1793 setup.mouse_loc = sp_canvas_window_to_world (canvas, NR::Point(x,y));
1795 // CAIRO FIXME: the sw/sh calculations below all assume 24bpp, need fixing for 32bpp
1796 if (canvas->rendermode != RENDERMODE_OUTLINE) {
1797 // use 256K as a compromise to not slow down gradients
1798 // 256K is the cached buffer and we need 3 channels
1799 setup.max_pixels = 87381; // 256K/3
1800 } else {
1801 // paths only, so 1M works faster
1802 // 1M is the cached buffer and we need 3 channels
1803 setup.max_pixels = 349525;
1804 }
1806 // Start the clock
1807 g_get_current_time(&(setup.start_time));
1809 // Go
1810 return sp_canvas_paint_rect_internal(&setup, rect);
1811 }
1813 /**
1814 * Force a full redraw after a specified number of interrupted redraws
1815 */
1816 void
1817 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1818 g_return_if_fail(canvas != NULL);
1820 canvas->forced_redraw_limit = count;
1821 canvas->forced_redraw_count = 0;
1822 }
1824 /**
1825 * End forced full redraw requests
1826 */
1827 void
1828 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1829 g_return_if_fail(canvas != NULL);
1831 canvas->forced_redraw_limit = -1;
1832 }
1834 /**
1835 * The canvas widget's expose callback.
1836 */
1837 static gint
1838 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1839 {
1840 SPCanvas *canvas = SP_CANVAS (widget);
1842 if (!GTK_WIDGET_DRAWABLE (widget) ||
1843 (event->window != SP_CANVAS_WINDOW (canvas)))
1844 return FALSE;
1846 int n_rects;
1847 GdkRectangle *rects;
1848 gdk_region_get_rectangles (event->region, &rects, &n_rects);
1850 for (int i = 0; i < n_rects; i++) {
1851 NRRectL rect;
1853 rect.x0 = rects[i].x + canvas->x0;
1854 rect.y0 = rects[i].y + canvas->y0;
1855 rect.x1 = rect.x0 + rects[i].width;
1856 rect.y1 = rect.y0 + rects[i].height;
1858 sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1859 }
1861 if (n_rects > 0)
1862 g_free (rects);
1864 return FALSE;
1865 }
1867 /**
1868 * The canvas widget's keypress callback.
1869 */
1870 static gint
1871 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1872 {
1873 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1874 }
1876 /**
1877 * Crossing event handler for the canvas.
1878 */
1879 static gint
1880 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1881 {
1882 SPCanvas *canvas = SP_CANVAS (widget);
1884 if (event->window != SP_CANVAS_WINDOW (canvas))
1885 return FALSE;
1887 canvas->state = event->state;
1888 return pick_current_item (canvas, (GdkEvent *) event);
1889 }
1891 /**
1892 * Focus in handler for the canvas.
1893 */
1894 static gint
1895 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
1896 {
1897 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1899 SPCanvas *canvas = SP_CANVAS (widget);
1901 if (canvas->focused_item) {
1902 return emit_event (canvas, (GdkEvent *) event);
1903 } else {
1904 return FALSE;
1905 }
1906 }
1908 /**
1909 * Focus out handler for the canvas.
1910 */
1911 static gint
1912 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
1913 {
1914 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1916 SPCanvas *canvas = SP_CANVAS (widget);
1918 if (canvas->focused_item)
1919 return emit_event (canvas, (GdkEvent *) event);
1920 else
1921 return FALSE;
1922 }
1924 /**
1925 * Helper that repaints the areas in the canvas that need it.
1926 *
1927 * @return true if all the dirty parts have been redrawn
1928 */
1929 static int
1930 paint (SPCanvas *canvas)
1931 {
1932 if (canvas->need_update) {
1933 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
1934 canvas->need_update = FALSE;
1935 }
1937 if (!canvas->need_redraw)
1938 return TRUE;
1940 Gdk::Region to_paint;
1942 for (int j=canvas->tTop; j<canvas->tBottom; j++) {
1943 for (int i=canvas->tLeft; i<canvas->tRight; i++) {
1944 int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
1946 if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
1947 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
1948 TILE_SIZE, TILE_SIZE));
1949 }
1951 }
1952 }
1954 if (!to_paint.empty()) {
1955 Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
1956 typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
1957 for (Iter i=rect.begin(); i != rect.end(); ++i) {
1958 int x0 = (*i).get_x();
1959 int y0 = (*i).get_y();
1960 int x1 = x0 + (*i).get_width();
1961 int y1 = y0 + (*i).get_height();
1962 if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
1963 // Aborted
1964 return FALSE;
1965 };
1966 }
1967 }
1969 canvas->need_redraw = FALSE;
1971 // we've had a full unaborted redraw, reset the full redraw counter
1972 if (canvas->forced_redraw_limit != -1) {
1973 canvas->forced_redraw_count = 0;
1974 }
1976 return TRUE;
1977 }
1979 /**
1980 * Helper that invokes update, paint, and repick on canvas.
1981 */
1982 static int
1983 do_update (SPCanvas *canvas)
1984 {
1985 if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
1986 return TRUE;
1988 /* Cause the update if necessary */
1989 if (canvas->need_update) {
1990 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
1991 canvas->need_update = FALSE;
1992 }
1994 /* Paint if able to */
1995 if (GTK_WIDGET_DRAWABLE (canvas)) {
1996 return paint (canvas);
1997 }
1999 /* Pick new current item */
2000 while (canvas->need_repick) {
2001 canvas->need_repick = FALSE;
2002 pick_current_item (canvas, &canvas->pick_event);
2003 }
2005 return TRUE;
2006 }
2008 /**
2009 * Idle handler for the canvas that deals with pending updates and redraws.
2010 */
2011 static gint
2012 idle_handler (gpointer data)
2013 {
2014 GDK_THREADS_ENTER ();
2016 SPCanvas *canvas = SP_CANVAS (data);
2018 const int ret = do_update (canvas);
2020 if (ret) {
2021 /* Reset idle id */
2022 canvas->idle_id = 0;
2023 }
2025 GDK_THREADS_LEAVE ();
2027 return !ret;
2028 }
2030 /**
2031 * Convenience function to add an idle handler to a canvas.
2032 */
2033 static void
2034 add_idle (SPCanvas *canvas)
2035 {
2036 if (canvas->idle_id != 0)
2037 return;
2039 canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2040 }
2042 /**
2043 * Returns the root group of the specified canvas.
2044 */
2045 SPCanvasGroup *
2046 sp_canvas_root (SPCanvas *canvas)
2047 {
2048 g_return_val_if_fail (canvas != NULL, NULL);
2049 g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2051 return SP_CANVAS_GROUP (canvas->root);
2052 }
2054 /**
2055 * Scrolls canvas to specific position.
2056 */
2057 void
2058 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2059 {
2060 g_return_if_fail (canvas != NULL);
2061 g_return_if_fail (SP_IS_CANVAS (canvas));
2063 int ix = (int) (cx + 0.5);
2064 int iy = (int) (cy + 0.5);
2065 int dx = ix - canvas->x0;
2066 int dy = iy - canvas->y0;
2068 canvas->dx0 = cx;
2069 canvas->dy0 = cy;
2070 canvas->x0 = ix;
2071 canvas->y0 = iy;
2073 sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2075 if (!clear) {
2076 // scrolling without zoom; redraw only the newly exposed areas
2077 if ((dx != 0) || (dy != 0)) {
2078 canvas->is_scrolling = is_scrolling;
2079 if (GTK_WIDGET_REALIZED (canvas)) {
2080 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2081 }
2082 }
2083 } else {
2084 // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2085 }
2087 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
2088 SPEventContext *ec = inkscape_active_event_context();
2089 if (SP_IS_3DBOX_CONTEXT (ec)) {
2090 // We could avoid redraw during panning by checking the status of is_scrolling, but this is
2091 // neither faster nor does it get rid of artefacts, so we update PLs unconditionally
2092 SP3DBoxContext *bc = SP_3DBOX_CONTEXT (ec);
2093 bc->_vpdrag->updateLines();
2094 }
2095 }
2097 /**
2098 * Updates canvas if necessary.
2099 */
2100 void
2101 sp_canvas_update_now (SPCanvas *canvas)
2102 {
2103 g_return_if_fail (canvas != NULL);
2104 g_return_if_fail (SP_IS_CANVAS (canvas));
2106 if (!(canvas->need_update ||
2107 canvas->need_redraw))
2108 return;
2110 do_update (canvas);
2111 }
2113 /**
2114 * Update callback for canvas widget.
2115 */
2116 static void
2117 sp_canvas_request_update (SPCanvas *canvas)
2118 {
2119 canvas->need_update = TRUE;
2120 add_idle (canvas);
2121 }
2123 /**
2124 * Forces redraw of rectangular canvas area.
2125 */
2126 void
2127 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2128 {
2129 NRRectL bbox;
2130 NRRectL visible;
2131 NRRectL clip;
2133 g_return_if_fail (canvas != NULL);
2134 g_return_if_fail (SP_IS_CANVAS (canvas));
2136 if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2137 if ((x0 >= x1) || (y0 >= y1)) return;
2139 bbox.x0 = x0;
2140 bbox.y0 = y0;
2141 bbox.x1 = x1;
2142 bbox.y1 = y1;
2144 visible.x0 = canvas->x0;
2145 visible.y0 = canvas->y0;
2146 visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2147 visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2149 nr_rect_l_intersect (&clip, &bbox, &visible);
2151 sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2152 add_idle (canvas);
2153 }
2155 /**
2156 * Sets world coordinates from win and canvas.
2157 */
2158 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2159 {
2160 g_return_if_fail (canvas != NULL);
2161 g_return_if_fail (SP_IS_CANVAS (canvas));
2163 if (worldx) *worldx = canvas->x0 + winx;
2164 if (worldy) *worldy = canvas->y0 + winy;
2165 }
2167 /**
2168 * Sets win coordinates from world and canvas.
2169 */
2170 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2171 {
2172 g_return_if_fail (canvas != NULL);
2173 g_return_if_fail (SP_IS_CANVAS (canvas));
2175 if (winx) *winx = worldx - canvas->x0;
2176 if (winy) *winy = worldy - canvas->y0;
2177 }
2179 /**
2180 * Converts point from win to world coordinates.
2181 */
2182 NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win)
2183 {
2184 g_assert (canvas != NULL);
2185 g_assert (SP_IS_CANVAS (canvas));
2187 return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2188 }
2190 /**
2191 * Converts point from world to win coordinates.
2192 */
2193 NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world)
2194 {
2195 g_assert (canvas != NULL);
2196 g_assert (SP_IS_CANVAS (canvas));
2198 return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2199 }
2201 /**
2202 * Returns true if point given in world coordinates is inside window.
2203 */
2204 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world)
2205 {
2206 g_assert( canvas != NULL );
2207 g_assert(SP_IS_CANVAS(canvas));
2209 using NR::X;
2210 using NR::Y;
2211 GtkWidget const &w = *GTK_WIDGET(canvas);
2212 return ( ( canvas->x0 <= world[X] ) &&
2213 ( canvas->y0 <= world[Y] ) &&
2214 ( world[X] < canvas->x0 + w.allocation.width ) &&
2215 ( world[Y] < canvas->y0 + w.allocation.height ) );
2216 }
2218 /**
2219 * Return canvas window coordinates as NR::Rect.
2220 */
2221 NR::Rect SPCanvas::getViewbox() const
2222 {
2223 GtkWidget const *w = GTK_WIDGET(this);
2225 return NR::Rect(NR::Point(dx0, dy0),
2226 NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2227 }
2229 inline int sp_canvas_tile_floor(int x)
2230 {
2231 return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2232 }
2234 inline int sp_canvas_tile_ceil(int x)
2235 {
2236 return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2237 }
2239 /**
2240 * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2241 */
2242 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2243 {
2244 if ( nl >= nr || nt >= nb ) {
2245 if ( canvas->tiles ) g_free(canvas->tiles);
2246 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2247 canvas->tileH=canvas->tileV=0;
2248 canvas->tiles=NULL;
2249 return;
2250 }
2251 int tl=sp_canvas_tile_floor(nl);
2252 int tt=sp_canvas_tile_floor(nt);
2253 int tr=sp_canvas_tile_ceil(nr);
2254 int tb=sp_canvas_tile_ceil(nb);
2256 int nh = tr-tl, nv = tb-tt;
2257 uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2258 for (int i=tl; i<tr; i++) {
2259 for (int j=tt; j<tb; j++) {
2260 int ind = (i-tl) + (j-tt)*nh;
2261 if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2262 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2263 } else {
2264 ntiles[ind]=0; // newly exposed areas get 0
2265 }
2266 }
2267 }
2268 if ( canvas->tiles ) g_free(canvas->tiles);
2269 canvas->tiles=ntiles;
2270 canvas->tLeft=tl;
2271 canvas->tTop=tt;
2272 canvas->tRight=tr;
2273 canvas->tBottom=tb;
2274 canvas->tileH=nh;
2275 canvas->tileV=nv;
2276 }
2278 /*
2279 * Helper that queues a canvas rectangle for redraw
2280 */
2281 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2282 canvas->need_redraw = TRUE;
2284 sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2285 }
2287 /**
2288 * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2289 */
2290 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2291 {
2292 if ( nl >= nr || nt >= nb ) {
2293 return;
2294 }
2295 int tl=sp_canvas_tile_floor(nl);
2296 int tt=sp_canvas_tile_floor(nt);
2297 int tr=sp_canvas_tile_ceil(nr);
2298 int tb=sp_canvas_tile_ceil(nb);
2299 if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2300 if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2301 if ( tr > canvas->tRight ) tr=canvas->tRight;
2302 if ( tt < canvas->tTop ) tt=canvas->tTop;
2303 if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2305 for (int i=tl; i<tr; i++) {
2306 for (int j=tt; j<tb; j++) {
2307 canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2308 }
2309 }
2310 }
2313 /*
2314 Local Variables:
2315 mode:c++
2316 c-file-style:"stroustrup"
2317 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2318 indent-tabs-mode:nil
2319 fill-column:99
2320 End:
2321 */
2322 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :