1 #define __SP_CANVAS_C__
3 /** \file
4 * Port of GnomeCanvas for Inkscape needs
5 *
6 * Authors:
7 * Federico Mena <federico@nuclecu.unam.mx>
8 * Raph Levien <raph@gimp.org>
9 * Lauris Kaplinski <lauris@kaplinski.com>
10 * fred
11 * bbyak
12 *
13 * Copyright (C) 1998 The Free Software Foundation
14 * Copyright (C) 2002-2006 authors
15 *
16 * Released under GNU GPL, read the file 'COPYING' for more information
17 */
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
23 #include <libnr/nr-pixblock.h>
25 #include <gtk/gtkmain.h>
26 #include <gtk/gtksignal.h>
28 #include <gtkmm.h>
30 #include <helper/sp-marshal.h>
31 #include <display/sp-canvas.h>
32 #include "display-forward.h"
33 #include <libnr/nr-matrix-fns.h>
34 #include <libnr/nr-matrix-ops.h>
35 #include <libnr/nr-convex-hull.h>
36 #include "prefs-utils.h"
37 #include "inkscape.h"
38 #include "sodipodi-ctrlrect.h"
39 #if ENABLE_LCMS
40 #include "color-profile-fns.h"
41 #endif // ENABLE_LCMS
42 #include "display/rendermode.h"
43 #include "libnr/nr-blit.h"
44 #include "display/inkscape-cairo.h"
46 // Define this to visualize the regions to be redrawn
47 //#define DEBUG_REDRAW 1;
49 // Tiles are a way to minimize the number of redraws, eliminating too small redraws.
50 // The canvas stores a 2D array of ints, each representing a TILE_SIZExTILE_SIZE pixels tile.
51 // If any part of it is dirtied, the entire tile is dirtied (its int is nonzero) and repainted.
52 #define TILE_SIZE 16
54 static gint const sp_canvas_update_priority = G_PRIORITY_HIGH_IDLE;
56 #define SP_CANVAS_WINDOW(c) (((GtkWidget *) (c))->window)
58 enum {
59 SP_CANVAS_ITEM_VISIBLE = 1 << 7,
60 SP_CANVAS_ITEM_NEED_UPDATE = 1 << 8,
61 SP_CANVAS_ITEM_NEED_AFFINE = 1 << 9
62 };
64 /**
65 * A group of Items.
66 */
67 struct SPCanvasGroup {
68 SPCanvasItem item;
70 GList *items, *last;
71 };
73 /**
74 * The SPCanvasGroup vtable.
75 */
76 struct SPCanvasGroupClass {
77 SPCanvasItemClass parent_class;
78 };
80 /**
81 * The SPCanvas vtable.
82 */
83 struct SPCanvasClass {
84 GtkWidgetClass parent_class;
85 };
87 static void group_add (SPCanvasGroup *group, SPCanvasItem *item);
88 static void group_remove (SPCanvasGroup *group, SPCanvasItem *item);
90 /* SPCanvasItem */
92 enum {ITEM_EVENT, ITEM_LAST_SIGNAL};
95 static void sp_canvas_request_update (SPCanvas *canvas);
97 static void sp_canvas_item_class_init (SPCanvasItemClass *klass);
98 static void sp_canvas_item_init (SPCanvasItem *item);
99 static void sp_canvas_item_dispose (GObject *object);
100 static void sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args);
102 static int emit_event (SPCanvas *canvas, GdkEvent *event);
104 static guint item_signals[ITEM_LAST_SIGNAL] = { 0 };
106 static GtkObjectClass *item_parent_class;
108 /**
109 * Registers the SPCanvasItem class with Glib and returns its type number.
110 */
111 GType
112 sp_canvas_item_get_type (void)
113 {
114 static GType type = 0;
115 if (!type) {
116 static GTypeInfo const info = {
117 sizeof (SPCanvasItemClass),
118 NULL, NULL,
119 (GClassInitFunc) sp_canvas_item_class_init,
120 NULL, NULL,
121 sizeof (SPCanvasItem),
122 0,
123 (GInstanceInitFunc) sp_canvas_item_init,
124 NULL
125 };
126 type = g_type_register_static (GTK_TYPE_OBJECT, "SPCanvasItem", &info, (GTypeFlags)0);
127 }
129 return type;
130 }
132 /**
133 * Initializes the SPCanvasItem vtable and the "event" signal.
134 */
135 static void
136 sp_canvas_item_class_init (SPCanvasItemClass *klass)
137 {
138 GObjectClass *object_class = (GObjectClass *) klass;
140 /* fixme: Derive from GObject */
141 item_parent_class = (GtkObjectClass*)gtk_type_class (GTK_TYPE_OBJECT);
143 item_signals[ITEM_EVENT] = g_signal_new ("event",
144 G_TYPE_FROM_CLASS (klass),
145 G_SIGNAL_RUN_LAST,
146 ((glong)((guint8*)&(klass->event) - (guint8*)klass)),
147 NULL, NULL,
148 sp_marshal_BOOLEAN__POINTER,
149 G_TYPE_BOOLEAN, 1,
150 GDK_TYPE_EVENT);
152 object_class->dispose = sp_canvas_item_dispose;
153 }
155 /**
156 * Callback for initialization of SPCanvasItem.
157 */
158 static void
159 sp_canvas_item_init (SPCanvasItem *item)
160 {
161 item->flags |= SP_CANVAS_ITEM_VISIBLE;
162 item->xform = NR::Matrix(NR::identity());
163 }
165 /**
166 * Constructs new SPCanvasItem on SPCanvasGroup.
167 */
168 SPCanvasItem *
169 sp_canvas_item_new (SPCanvasGroup *parent, GtkType type, gchar const *first_arg_name, ...)
170 {
171 va_list args;
173 g_return_val_if_fail (parent != NULL, NULL);
174 g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL);
175 g_return_val_if_fail (gtk_type_is_a (type, sp_canvas_item_get_type ()), NULL);
177 SPCanvasItem *item = SP_CANVAS_ITEM (gtk_type_new (type));
179 va_start (args, first_arg_name);
180 sp_canvas_item_construct (item, parent, first_arg_name, args);
181 va_end (args);
183 return item;
184 }
186 /**
187 * Sets up the newly created SPCanvasItem.
188 *
189 * We make it static for encapsulation reasons since it was nowhere used.
190 */
191 static void
192 sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args)
193 {
194 g_return_if_fail (SP_IS_CANVAS_GROUP (parent));
195 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
197 item->parent = SP_CANVAS_ITEM (parent);
198 item->canvas = item->parent->canvas;
200 g_object_set_valist (G_OBJECT (item), first_arg_name, args);
202 group_add (SP_CANVAS_GROUP (item->parent), item);
204 sp_canvas_item_request_update (item);
205 }
207 /**
208 * Helper function that requests redraw only if item's visible flag is set.
209 */
210 static void
211 redraw_if_visible (SPCanvasItem *item)
212 {
213 if (item->flags & SP_CANVAS_ITEM_VISIBLE) {
214 int x0 = (int)(item->x1);
215 int x1 = (int)(item->x2);
216 int y0 = (int)(item->y1);
217 int y1 = (int)(item->y2);
219 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
220 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
221 }
222 }
223 }
225 /**
226 * Callback that removes item from all referers and destroys it.
227 */
228 static void
229 sp_canvas_item_dispose (GObject *object)
230 {
231 SPCanvasItem *item = SP_CANVAS_ITEM (object);
233 // Hack: if this is a ctrlrect, move it to 0,0;
234 // this redraws only the stroke of the rect to be deleted,
235 // avoiding redraw of the entire area
236 if (SP_IS_CTRLRECT(item)) {
237 SP_CTRLRECT(object)->setRectangle(NR::Rect(NR::Point(0,0),NR::Point(0,0)));
238 SP_CTRLRECT(object)->update(item->xform, 0);
239 } else {
240 redraw_if_visible (item);
241 }
242 item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
244 if (item == item->canvas->current_item) {
245 item->canvas->current_item = NULL;
246 item->canvas->need_repick = TRUE;
247 }
249 if (item == item->canvas->new_current_item) {
250 item->canvas->new_current_item = NULL;
251 item->canvas->need_repick = TRUE;
252 }
254 if (item == item->canvas->grabbed_item) {
255 item->canvas->grabbed_item = NULL;
256 gdk_pointer_ungrab (GDK_CURRENT_TIME);
257 }
259 if (item == item->canvas->focused_item)
260 item->canvas->focused_item = NULL;
262 if (item->parent) {
263 group_remove (SP_CANVAS_GROUP (item->parent), item);
264 }
266 G_OBJECT_CLASS (item_parent_class)->dispose (object);
267 }
269 /**
270 * Helper function to update item and its children.
271 *
272 * NB! affine is parent2canvas.
273 */
274 static void
275 sp_canvas_item_invoke_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
276 {
277 /* Apply the child item's transform */
278 NR::Matrix child_affine = item->xform * affine;
280 /* apply object flags to child flags */
281 int child_flags = flags & ~SP_CANVAS_UPDATE_REQUESTED;
283 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
284 child_flags |= SP_CANVAS_UPDATE_REQUESTED;
286 if (item->flags & SP_CANVAS_ITEM_NEED_AFFINE)
287 child_flags |= SP_CANVAS_UPDATE_AFFINE;
289 if (child_flags & (SP_CANVAS_UPDATE_REQUESTED | SP_CANVAS_UPDATE_AFFINE)) {
290 if (SP_CANVAS_ITEM_GET_CLASS (item)->update)
291 SP_CANVAS_ITEM_GET_CLASS (item)->update (item, child_affine, child_flags);
292 }
294 GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_UPDATE);
295 GTK_OBJECT_UNSET_FLAGS (item, SP_CANVAS_ITEM_NEED_AFFINE);
296 }
298 /**
299 * Helper function to invoke the point method of the item.
300 *
301 * The argument x, y should be in the parent's item-relative coordinate
302 * system. This routine applies the inverse of the item's transform,
303 * maintaining the affine invariant.
304 */
305 static double
306 sp_canvas_item_invoke_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
307 {
308 if (SP_CANVAS_ITEM_GET_CLASS (item)->point)
309 return SP_CANVAS_ITEM_GET_CLASS (item)->point (item, p, actual_item);
311 return NR_HUGE;
312 }
314 /**
315 * Makes the item's affine transformation matrix be equal to the specified
316 * matrix.
317 *
318 * @item: A canvas item.
319 * @affine: An affine transformation matrix.
320 */
321 void
322 sp_canvas_item_affine_absolute (SPCanvasItem *item, NR::Matrix const &affine)
323 {
324 item->xform = affine;
326 if (!(item->flags & SP_CANVAS_ITEM_NEED_AFFINE)) {
327 item->flags |= SP_CANVAS_ITEM_NEED_AFFINE;
328 if (item->parent != NULL) {
329 sp_canvas_item_request_update (item->parent);
330 } else {
331 sp_canvas_request_update (item->canvas);
332 }
333 }
335 item->canvas->need_repick = TRUE;
336 }
338 /**
339 * Convenience function to reorder items in a group's child list.
340 *
341 * This puts the specified link after the "before" link.
342 */
343 static void
344 put_item_after (GList *link, GList *before)
345 {
346 if (link == before)
347 return;
349 SPCanvasGroup *parent = SP_CANVAS_GROUP (SP_CANVAS_ITEM (link->data)->parent);
351 if (before == NULL) {
352 if (link == parent->items) return;
354 link->prev->next = link->next;
356 if (link->next) {
357 link->next->prev = link->prev;
358 } else {
359 parent->last = link->prev;
360 }
362 link->prev = before;
363 link->next = parent->items;
364 link->next->prev = link;
365 parent->items = link;
366 } else {
367 if ((link == parent->last) && (before == parent->last->prev))
368 return;
370 if (link->next)
371 link->next->prev = link->prev;
373 if (link->prev)
374 link->prev->next = link->next;
375 else {
376 parent->items = link->next;
377 parent->items->prev = NULL;
378 }
380 link->prev = before;
381 link->next = before->next;
383 link->prev->next = link;
385 if (link->next)
386 link->next->prev = link;
387 else
388 parent->last = link;
389 }
390 }
393 /**
394 * Raises the item in its parent's stack by the specified number of positions.
395 *
396 * \param item A canvas item.
397 * \param positions Number of steps to raise the item.
398 *
399 * If the number of positions is greater than the distance to the top of the
400 * stack, then the item is put at the top.
401 */
402 void
403 sp_canvas_item_raise (SPCanvasItem *item, int positions)
404 {
405 g_return_if_fail (item != NULL);
406 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
407 g_return_if_fail (positions >= 0);
409 if (!item->parent || positions == 0)
410 return;
412 SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
413 GList *link = g_list_find (parent->items, item);
414 g_assert (link != NULL);
416 GList *before;
417 for (before = link; positions && before; positions--)
418 before = before->next;
420 if (!before)
421 before = parent->last;
423 put_item_after (link, before);
425 redraw_if_visible (item);
426 item->canvas->need_repick = TRUE;
427 }
430 /**
431 * Lowers the item in its parent's stack by the specified number of positions.
432 *
433 * \param item A canvas item.
434 * \param positions Number of steps to lower the item.
435 *
436 * If the number of positions is greater than the distance to the bottom of the
437 * stack, then the item is put at the bottom.
438 **/
439 void
440 sp_canvas_item_lower (SPCanvasItem *item, int positions)
441 {
442 g_return_if_fail (item != NULL);
443 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
444 g_return_if_fail (positions >= 1);
446 if (!item->parent || positions == 0)
447 return;
449 SPCanvasGroup *parent = SP_CANVAS_GROUP (item->parent);
450 GList *link = g_list_find (parent->items, item);
451 g_assert (link != NULL);
453 GList *before;
454 if (link->prev)
455 for (before = link->prev; positions && before; positions--)
456 before = before->prev;
457 else
458 before = NULL;
460 put_item_after (link, before);
462 redraw_if_visible (item);
463 item->canvas->need_repick = TRUE;
464 }
466 /**
467 * Sets visible flag on item and requests a redraw.
468 */
469 void
470 sp_canvas_item_show (SPCanvasItem *item)
471 {
472 g_return_if_fail (item != NULL);
473 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
475 if (item->flags & SP_CANVAS_ITEM_VISIBLE)
476 return;
478 item->flags |= SP_CANVAS_ITEM_VISIBLE;
480 int x0 = (int)(item->x1);
481 int x1 = (int)(item->x2);
482 int y0 = (int)(item->y1);
483 int y1 = (int)(item->y2);
485 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
486 sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
487 item->canvas->need_repick = TRUE;
488 }
489 }
491 /**
492 * Clears visible flag on item and requests a redraw.
493 */
494 void
495 sp_canvas_item_hide (SPCanvasItem *item)
496 {
497 g_return_if_fail (item != NULL);
498 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
500 if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
501 return;
503 item->flags &= ~SP_CANVAS_ITEM_VISIBLE;
505 int x0 = (int)(item->x1);
506 int x1 = (int)(item->x2);
507 int y0 = (int)(item->y1);
508 int y1 = (int)(item->y2);
510 if (x0 !=0 || x1 !=0 || y0 !=0 || y1 !=0) {
511 sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
512 item->canvas->need_repick = TRUE;
513 }
514 }
516 /**
517 * Grab item under cursor.
518 *
519 * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE
520 */
521 int
522 sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
523 {
524 g_return_val_if_fail (item != NULL, -1);
525 g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1);
526 g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1);
528 if (item->canvas->grabbed_item)
529 return -1;
531 if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
532 return -1;
534 /* fixme: Top hack (Lauris) */
535 /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */
536 /* fixme: But Canvas actualle does get key events, so all we need is routing these here */
537 gdk_pointer_grab (SP_CANVAS_WINDOW (item->canvas), FALSE,
538 (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
539 NULL, cursor, etime);
541 item->canvas->grabbed_item = item;
542 item->canvas->grabbed_event_mask = event_mask;
543 item->canvas->current_item = item; /* So that events go to the grabbed item */
545 return 0;
546 }
548 /**
549 * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
550 * mouse.
551 *
552 * \param item A canvas item that holds a grab.
553 * \param etime The timestamp for ungrabbing the mouse.
554 */
555 void
556 sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime)
557 {
558 g_return_if_fail (item != NULL);
559 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
561 if (item->canvas->grabbed_item != item)
562 return;
564 item->canvas->grabbed_item = NULL;
566 gdk_pointer_ungrab (etime);
567 }
569 /**
570 * Returns the product of all transformation matrices from the root item down
571 * to the item.
572 */
573 NR::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item)
574 {
575 g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this?
577 NR::Matrix affine = NR::identity();
579 while (item) {
580 affine *= item->xform;
581 item = item->parent;
582 }
583 return affine;
584 }
586 /**
587 * Helper that returns true iff item is descendant of parent.
588 */
589 static bool is_descendant(SPCanvasItem const *item, SPCanvasItem const *parent)
590 {
591 while (item) {
592 if (item == parent)
593 return true;
594 item = item->parent;
595 }
597 return false;
598 }
600 /**
601 * Focus canvas, and item under cursor if it is not already focussed.
602 */
603 void
604 sp_canvas_item_grab_focus (SPCanvasItem *item)
605 {
606 g_return_if_fail (item != NULL);
607 g_return_if_fail (SP_IS_CANVAS_ITEM (item));
608 g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
610 SPCanvasItem *focused_item = item->canvas->focused_item;
612 if (focused_item) {
613 GdkEvent ev;
614 ev.focus_change.type = GDK_FOCUS_CHANGE;
615 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
616 ev.focus_change.send_event = FALSE;
617 ev.focus_change.in = FALSE;
619 emit_event (item->canvas, &ev);
620 }
622 item->canvas->focused_item = item;
623 gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
625 if (focused_item) {
626 GdkEvent ev;
627 ev.focus_change.type = GDK_FOCUS_CHANGE;
628 ev.focus_change.window = SP_CANVAS_WINDOW (item->canvas);
629 ev.focus_change.send_event = FALSE;
630 ev.focus_change.in = TRUE;
632 emit_event (item->canvas, &ev);
633 }
634 }
636 /**
637 * Requests that the canvas queue an update for the specified item.
638 *
639 * To be used only by item implementations.
640 */
641 void
642 sp_canvas_item_request_update (SPCanvasItem *item)
643 {
644 if (item->flags & SP_CANVAS_ITEM_NEED_UPDATE)
645 return;
647 item->flags |= SP_CANVAS_ITEM_NEED_UPDATE;
649 if (item->parent != NULL) {
650 /* Recurse up the tree */
651 sp_canvas_item_request_update (item->parent);
652 } else {
653 /* Have reached the top of the tree, make sure the update call gets scheduled. */
654 sp_canvas_request_update (item->canvas);
655 }
656 }
658 /**
659 * Returns position of item in group.
660 */
661 gint sp_canvas_item_order (SPCanvasItem * item)
662 {
663 return g_list_index (SP_CANVAS_GROUP (item->parent)->items, item);
664 }
666 /* SPCanvasGroup */
668 static void sp_canvas_group_class_init (SPCanvasGroupClass *klass);
669 static void sp_canvas_group_init (SPCanvasGroup *group);
670 static void sp_canvas_group_destroy (GtkObject *object);
672 static void sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
673 static double sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item);
674 static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf);
676 static SPCanvasItemClass *group_parent_class;
678 /**
679 * Registers SPCanvasGroup class with Gtk and returns its type number.
680 */
681 GType sp_canvas_group_get_type(void)
682 {
683 static GType type = 0;
684 if (!type) {
685 GTypeInfo info = {
686 sizeof(SPCanvasGroupClass),
687 0, // base_init
688 0, // base_finalize
689 (GClassInitFunc)sp_canvas_group_class_init,
690 0, // class_finalize
691 0, // class_data
692 sizeof(SPCanvasGroup),
693 0, // n_preallocs
694 (GInstanceInitFunc)sp_canvas_group_init,
695 0 // value_table
696 };
697 type = g_type_register_static(sp_canvas_item_get_type(), "SPCanvasGroup", &info, static_cast<GTypeFlags>(0));
698 }
699 return type;
700 }
702 /**
703 * Class initialization function for SPCanvasGroupClass
704 */
705 static void
706 sp_canvas_group_class_init (SPCanvasGroupClass *klass)
707 {
708 GtkObjectClass *object_class = (GtkObjectClass *) klass;
709 SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
711 group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
713 object_class->destroy = sp_canvas_group_destroy;
715 item_class->update = sp_canvas_group_update;
716 item_class->render = sp_canvas_group_render;
717 item_class->point = sp_canvas_group_point;
718 }
720 /**
721 * Callback. Empty.
722 */
723 static void
724 sp_canvas_group_init (SPCanvasGroup */*group*/)
725 {
726 /* Nothing here */
727 }
729 /**
730 * Callback that destroys all items in group and calls group's virtual
731 * destroy() function.
732 */
733 static void
734 sp_canvas_group_destroy (GtkObject *object)
735 {
736 g_return_if_fail (object != NULL);
737 g_return_if_fail (SP_IS_CANVAS_GROUP (object));
739 SPCanvasGroup const *group = SP_CANVAS_GROUP (object);
741 GList *list = group->items;
742 while (list) {
743 SPCanvasItem *child = (SPCanvasItem *)list->data;
744 list = list->next;
746 gtk_object_destroy (GTK_OBJECT (child));
747 }
749 if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
750 (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
751 }
753 /**
754 * Update handler for canvas groups
755 */
756 static void
757 sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
758 {
759 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
760 NR::ConvexHull corners(NR::Point(0, 0));
761 bool empty=true;
763 for (GList *list = group->items; list; list = list->next) {
764 SPCanvasItem *i = (SPCanvasItem *)list->data;
766 sp_canvas_item_invoke_update (i, affine, flags);
768 if ( i->x2 > i->x1 && i->y2 > i->y1 ) {
769 if (empty) {
770 corners = NR::ConvexHull(NR::Point(i->x1, i->y1));
771 empty = false;
772 } else {
773 corners.add(NR::Point(i->x1, i->y1));
774 }
775 corners.add(NR::Point(i->x2, i->y2));
776 }
777 }
779 NR::Maybe<NR::Rect> const bounds = corners.bounds();
780 if (bounds) {
781 item->x1 = bounds->min()[NR::X];
782 item->y1 = bounds->min()[NR::Y];
783 item->x2 = bounds->max()[NR::X];
784 item->y2 = bounds->max()[NR::Y];
785 } else {
786 // FIXME ?
787 item->x1 = item->x2 = item->y1 = item->y2 = 0;
788 }
789 }
791 /**
792 * Point handler for canvas groups.
793 */
794 static double
795 sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
796 {
797 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
798 double const x = p[NR::X];
799 double const y = p[NR::Y];
800 int x1 = (int)(x - item->canvas->close_enough);
801 int y1 = (int)(y - item->canvas->close_enough);
802 int x2 = (int)(x + item->canvas->close_enough);
803 int y2 = (int)(y + item->canvas->close_enough);
805 double best = 0.0;
806 *actual_item = NULL;
808 double dist = 0.0;
810 for (GList *list = group->items; list; list = list->next) {
811 SPCanvasItem *child = (SPCanvasItem *)list->data;
813 if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
814 SPCanvasItem *point_item = NULL; /* cater for incomplete item implementations */
816 int has_point;
817 if ((child->flags & SP_CANVAS_ITEM_VISIBLE) && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
818 dist = sp_canvas_item_invoke_point (child, p, &point_item);
819 has_point = TRUE;
820 } else
821 has_point = FALSE;
823 if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) {
824 best = dist;
825 *actual_item = point_item;
826 }
827 }
828 }
830 return best;
831 }
833 /**
834 * Renders all visible canvas group items in buf rectangle.
835 */
836 static void
837 sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf)
838 {
839 SPCanvasGroup const *group = SP_CANVAS_GROUP (item);
841 for (GList *list = group->items; list; list = list->next) {
842 SPCanvasItem *child = (SPCanvasItem *)list->data;
843 if (child->flags & SP_CANVAS_ITEM_VISIBLE) {
844 if ((child->x1 < buf->rect.x1) &&
845 (child->y1 < buf->rect.y1) &&
846 (child->x2 > buf->rect.x0) &&
847 (child->y2 > buf->rect.y0)) {
848 if (SP_CANVAS_ITEM_GET_CLASS (child)->render)
849 SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf);
850 }
851 }
852 }
853 }
855 /**
856 * Adds an item to a canvas group.
857 */
858 static void
859 group_add (SPCanvasGroup *group, SPCanvasItem *item)
860 {
861 gtk_object_ref (GTK_OBJECT (item));
862 gtk_object_sink (GTK_OBJECT (item));
864 if (!group->items) {
865 group->items = g_list_append (group->items, item);
866 group->last = group->items;
867 } else {
868 group->last = g_list_append (group->last, item)->next;
869 }
871 sp_canvas_item_request_update (item);
872 }
874 /**
875 * Removes an item from a canvas group
876 */
877 static void
878 group_remove (SPCanvasGroup *group, SPCanvasItem *item)
879 {
880 g_return_if_fail (group != NULL);
881 g_return_if_fail (SP_IS_CANVAS_GROUP (group));
882 g_return_if_fail (item != NULL);
884 for (GList *children = group->items; children; children = children->next) {
885 if (children->data == item) {
887 /* Unparent the child */
889 item->parent = NULL;
890 gtk_object_unref (GTK_OBJECT (item));
892 /* Remove it from the list */
894 if (children == group->last) group->last = children->prev;
896 group->items = g_list_remove_link (group->items, children);
897 g_list_free (children);
898 break;
899 }
900 }
901 }
903 /* SPCanvas */
905 static void sp_canvas_class_init (SPCanvasClass *klass);
906 static void sp_canvas_init (SPCanvas *canvas);
907 static void sp_canvas_destroy (GtkObject *object);
909 static void sp_canvas_realize (GtkWidget *widget);
910 static void sp_canvas_unrealize (GtkWidget *widget);
912 static void sp_canvas_size_request (GtkWidget *widget, GtkRequisition *req);
913 static void sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
915 static gint sp_canvas_button (GtkWidget *widget, GdkEventButton *event);
916 static gint sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event);
917 static gint sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event);
918 static gint sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event);
919 static gint sp_canvas_key (GtkWidget *widget, GdkEventKey *event);
920 static gint sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event);
921 static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event);
922 static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event);
924 static GtkWidgetClass *canvas_parent_class;
926 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb);
927 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb);
928 static void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val);
929 static int do_update (SPCanvas *canvas);
931 /**
932 * Registers the SPCanvas class if necessary, and returns the type ID
933 * associated to it.
934 *
935 * \return The type ID of the SPCanvas class.
936 **/
937 GType sp_canvas_get_type(void)
938 {
939 static GType type = 0;
940 if (!type) {
941 GTypeInfo info = {
942 sizeof(SPCanvasClass),
943 0, // base_init
944 0, // base_finalize
945 (GClassInitFunc)sp_canvas_class_init,
946 0, // class_finalize
947 0, // class_data
948 sizeof(SPCanvas),
949 0, // n_preallocs
950 (GInstanceInitFunc)sp_canvas_init,
951 0 // value_table
952 };
953 type = g_type_register_static(GTK_TYPE_WIDGET, "SPCanvas", &info, static_cast<GTypeFlags>(0));
954 }
955 return type;
956 }
958 /**
959 * Class initialization function for SPCanvasClass.
960 */
961 static void
962 sp_canvas_class_init (SPCanvasClass *klass)
963 {
964 GtkObjectClass *object_class = (GtkObjectClass *) klass;
965 GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
967 canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET);
969 object_class->destroy = sp_canvas_destroy;
971 widget_class->realize = sp_canvas_realize;
972 widget_class->unrealize = sp_canvas_unrealize;
973 widget_class->size_request = sp_canvas_size_request;
974 widget_class->size_allocate = sp_canvas_size_allocate;
975 widget_class->button_press_event = sp_canvas_button;
976 widget_class->button_release_event = sp_canvas_button;
977 widget_class->motion_notify_event = sp_canvas_motion;
978 widget_class->scroll_event = sp_canvas_scroll;
979 widget_class->expose_event = sp_canvas_expose;
980 widget_class->key_press_event = sp_canvas_key;
981 widget_class->key_release_event = sp_canvas_key;
982 widget_class->enter_notify_event = sp_canvas_crossing;
983 widget_class->leave_notify_event = sp_canvas_crossing;
984 widget_class->focus_in_event = sp_canvas_focus_in;
985 widget_class->focus_out_event = sp_canvas_focus_out;
986 }
988 /**
989 * Callback: object initialization for SPCanvas.
990 */
991 static void
992 sp_canvas_init (SPCanvas *canvas)
993 {
994 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW);
995 GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED);
996 GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
998 canvas->pick_event.type = GDK_LEAVE_NOTIFY;
999 canvas->pick_event.crossing.x = 0;
1000 canvas->pick_event.crossing.y = 0;
1002 /* Create the root item as a special case */
1003 canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ()));
1004 canvas->root->canvas = canvas;
1006 gtk_object_ref (GTK_OBJECT (canvas->root));
1007 gtk_object_sink (GTK_OBJECT (canvas->root));
1009 canvas->need_repick = TRUE;
1011 // See comment at in sp-canvas.h.
1012 canvas->gen_all_enter_events = false;
1014 canvas->tiles=NULL;
1015 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1016 canvas->tileH=canvas->tileV=0;
1018 canvas->forced_redraw_count = 0;
1019 canvas->forced_redraw_limit = -1;
1021 #if ENABLE_LCMS
1022 canvas->enable_cms_display_adj = false;
1023 canvas->cms_key = new Glib::ustring("");
1024 #endif // ENABLE_LCMS
1026 canvas->is_scrolling = false;
1028 }
1030 /**
1031 * Convenience function to remove the idle handler of a canvas.
1032 */
1033 static void
1034 remove_idle (SPCanvas *canvas)
1035 {
1036 if (canvas->idle_id) {
1037 gtk_idle_remove (canvas->idle_id);
1038 canvas->idle_id = 0;
1039 }
1040 }
1042 /*
1043 * Removes the transient state of the canvas (idle handler, grabs).
1044 */
1045 static void
1046 shutdown_transients (SPCanvas *canvas)
1047 {
1048 /* We turn off the need_redraw flag, since if the canvas is mapped again
1049 * it will request a redraw anyways. We do not turn off the need_update
1050 * flag, though, because updates are not queued when the canvas remaps
1051 * itself.
1052 */
1053 if (canvas->need_redraw) {
1054 canvas->need_redraw = FALSE;
1055 }
1056 if ( canvas->tiles ) g_free(canvas->tiles);
1057 canvas->tiles=NULL;
1058 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
1059 canvas->tileH=canvas->tileV=0;
1061 if (canvas->grabbed_item) {
1062 canvas->grabbed_item = NULL;
1063 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1064 }
1066 remove_idle (canvas);
1067 }
1069 /**
1070 * Destroy handler for SPCanvas.
1071 */
1072 static void
1073 sp_canvas_destroy (GtkObject *object)
1074 {
1075 SPCanvas *canvas = SP_CANVAS (object);
1077 if (canvas->root) {
1078 gtk_object_unref (GTK_OBJECT (canvas->root));
1079 canvas->root = NULL;
1080 }
1082 shutdown_transients (canvas);
1084 if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
1085 (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
1086 }
1088 /**
1089 * Returns new canvas as widget.
1090 */
1091 GtkWidget *
1092 sp_canvas_new_aa (void)
1093 {
1094 SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ());
1096 return (GtkWidget *) canvas;
1097 }
1099 /**
1100 * The canvas widget's realize callback.
1101 */
1102 static void
1103 sp_canvas_realize (GtkWidget *widget)
1104 {
1105 SPCanvas *canvas = SP_CANVAS (widget);
1107 GdkWindowAttr attributes;
1108 attributes.window_type = GDK_WINDOW_CHILD;
1109 attributes.x = widget->allocation.x;
1110 attributes.y = widget->allocation.y;
1111 attributes.width = widget->allocation.width;
1112 attributes.height = widget->allocation.height;
1113 attributes.wclass = GDK_INPUT_OUTPUT;
1114 attributes.visual = gdk_rgb_get_visual ();
1115 attributes.colormap = gdk_rgb_get_cmap ();
1116 attributes.event_mask = (gtk_widget_get_events (widget) |
1117 GDK_EXPOSURE_MASK |
1118 GDK_BUTTON_PRESS_MASK |
1119 GDK_BUTTON_RELEASE_MASK |
1120 GDK_POINTER_MOTION_MASK |
1121 GDK_PROXIMITY_IN_MASK |
1122 GDK_PROXIMITY_OUT_MASK |
1123 GDK_KEY_PRESS_MASK |
1124 GDK_KEY_RELEASE_MASK |
1125 GDK_ENTER_NOTIFY_MASK |
1126 GDK_LEAVE_NOTIFY_MASK |
1127 GDK_FOCUS_CHANGE_MASK);
1128 gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1130 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1131 gdk_window_set_user_data (widget->window, widget);
1133 if ( prefs_get_int_attribute ("options.useextinput", "value", 1) )
1134 gtk_widget_set_events(widget, attributes.event_mask);
1136 widget->style = gtk_style_attach (widget->style, widget->window);
1138 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1140 canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas));
1141 }
1143 /**
1144 * The canvas widget's unrealize callback.
1145 */
1146 static void
1147 sp_canvas_unrealize (GtkWidget *widget)
1148 {
1149 SPCanvas *canvas = SP_CANVAS (widget);
1151 canvas->current_item = NULL;
1152 canvas->grabbed_item = NULL;
1153 canvas->focused_item = NULL;
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->pixmap_gc == NULL) // canvas being deleted
1549 return FALSE;
1551 if (canvas->grabbed_event_mask & GDK_POINTER_MOTION_HINT_MASK) {
1552 gint x, y;
1553 gdk_window_get_pointer (widget->window, &x, &y, NULL);
1554 event->x = x;
1555 event->y = y;
1556 }
1558 canvas->state = event->state;
1559 pick_current_item (canvas, (GdkEvent *) event);
1561 return emit_event (canvas, (GdkEvent *) event);
1562 }
1564 static void
1565 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)
1566 {
1567 GtkWidget *widget = GTK_WIDGET (canvas);
1569 SPCanvasBuf buf;
1570 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1571 buf.buf = nr_pixelstore_256K_new (FALSE, 0);
1572 } else {
1573 buf.buf = nr_pixelstore_1M_new (FALSE, 0);
1574 }
1576 // Mark the region clean
1577 sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
1579 buf.buf_rowstride = sw * 4;
1580 buf.rect.x0 = x0;
1581 buf.rect.y0 = y0;
1582 buf.rect.x1 = x1;
1583 buf.rect.y1 = y1;
1584 buf.visible_rect.x0 = draw_x1;
1585 buf.visible_rect.y0 = draw_y1;
1586 buf.visible_rect.x1 = draw_x2;
1587 buf.visible_rect.y1 = draw_y2;
1588 GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
1589 buf.bg_color = (((color->red & 0xff00) << 8)
1590 | (color->green & 0xff00)
1591 | (color->blue >> 8));
1592 buf.is_empty = true;
1594 buf.ct = nr_create_cairo_context_canvasbuf (&(buf.visible_rect), &buf);
1596 if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
1597 SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
1598 }
1600 #if ENABLE_LCMS
1601 cmsHTRANSFORM transf = 0;
1602 long long int fromDisplay = prefs_get_int_attribute_limited( "options.displayprofile", "from_display", 0, 0, 1 );
1603 if ( fromDisplay ) {
1604 transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" );
1605 } else {
1606 transf = Inkscape::colorprofile_get_display_transform();
1607 }
1608 #endif // ENABLE_LCMS
1610 if (buf.is_empty) {
1611 #if ENABLE_LCMS
1612 if ( transf && canvas->enable_cms_display_adj ) {
1613 cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 );
1614 }
1615 #endif // ENABLE_LCMS
1616 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
1617 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1618 canvas->pixmap_gc,
1619 TRUE,
1620 x0 - canvas->x0, y0 - canvas->y0,
1621 x1 - x0, y1 - y0);
1622 } else {
1624 #if ENABLE_LCMS
1625 if ( transf && canvas->enable_cms_display_adj ) {
1626 for ( gint yy = 0; yy < (y1 - y0); yy++ ) {
1627 guchar* p = buf.buf + (sw * 3) * yy;
1628 cmsDoTransform( transf, p, p, (x1 - x0) );
1629 }
1630 }
1631 #endif // ENABLE_LCMS
1633 // Now we only need to output the prepared pixmap to the actual screen, and this define chooses one
1634 // of the two ways to do it. The cairo way is direct and straightforward, but unfortunately
1635 // noticeably slower. I asked Carl Worth but he was unable so far to suggest any specific reason
1636 // for this slowness. So, for now we use the oldish method: squeeze out 32bpp buffer to 24bpp and
1637 // use gdk_draw_rgb_image_dithalign, for unfortunately gdk can only handle 24 bpp, which cairo
1638 // cannot handle at all. Still, this way is currently faster even despite the blit with squeeze.
1640 ///#define CANVAS_OUTPUT_VIA_CAIRO
1642 #ifdef CANVAS_OUTPUT_VIA_CAIRO
1644 buf.cst = cairo_image_surface_create_for_data (
1645 buf.buf,
1646 CAIRO_FORMAT_ARGB32, // unpacked, i.e. 32 bits! one byte is unused
1647 x1 - x0, y1 - y0,
1648 buf.buf_rowstride
1649 );
1650 cairo_t *window_ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
1651 cairo_set_source_surface (window_ct, buf.cst, x0 - canvas->x0, y0 - canvas->y0);
1652 cairo_paint (window_ct);
1653 cairo_destroy (window_ct);
1654 cairo_surface_finish (buf.cst);
1655 cairo_surface_destroy (buf.cst);
1657 #else
1659 NRPixBlock b3;
1660 nr_pixblock_setup_fast (&b3, NR_PIXBLOCK_MODE_R8G8B8, x0, y0, x1, y1, TRUE);
1662 NRPixBlock b4;
1663 nr_pixblock_setup_extern (&b4, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1,
1664 buf.buf,
1665 buf.buf_rowstride,
1666 FALSE, FALSE);
1668 // this does the 32->24 squishing, using an assembler routine:
1669 nr_blit_pixblock_pixblock (&b3, &b4);
1671 gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas),
1672 canvas->pixmap_gc,
1673 x0 - canvas->x0, y0 - canvas->y0,
1674 x1 - x0, y1 - y0,
1675 GDK_RGB_DITHER_MAX,
1676 b3.data.px,
1677 sw * 3,
1678 x0 - canvas->x0, y0 - canvas->y0);
1680 nr_pixblock_release (&b3);
1681 nr_pixblock_release (&b4);
1683 #endif
1685 }
1687 cairo_surface_t *cst = cairo_get_target(buf.ct);
1688 cairo_destroy (buf.ct);
1689 cairo_surface_finish (cst);
1690 cairo_surface_destroy (cst);
1692 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1693 nr_pixelstore_256K_free (buf.buf);
1694 } else {
1695 nr_pixelstore_1M_free (buf.buf);
1696 }
1697 }
1699 struct PaintRectSetup {
1700 SPCanvas* canvas;
1701 NRRectL big_rect;
1702 GTimeVal start_time;
1703 int max_pixels;
1704 NR::Point mouse_loc;
1705 };
1707 /**
1708 * Paint the given rect, recursively subdividing the region until it is the size of a single
1709 * buffer.
1710 *
1711 * @return true if the drawing completes
1712 */
1713 static int
1714 sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
1715 {
1716 GTimeVal now;
1717 g_get_current_time (&now);
1719 glong elapsed = (now.tv_sec - setup->start_time.tv_sec) * 1000000
1720 + (now.tv_usec - setup->start_time.tv_usec);
1722 // Allow only very fast buffers to be run together;
1723 // as soon as the total redraw time exceeds 1ms, cancel;
1724 // this returns control to the idle loop and allows Inkscape to process user input
1725 // (potentially interrupting the redraw); as soon as Inkscape has some more idle time,
1726 // it will get back and finish painting what remains to paint.
1727 if (elapsed > 1000) {
1729 // Interrupting redraw isn't always good.
1730 // For example, when you drag one node of a big path, only the buffer containing
1731 // the mouse cursor will be redrawn again and again, and the rest of the path
1732 // will remain stale because Inkscape never has enough idle time to redraw all
1733 // of the screen. To work around this, such operations set a forced_redraw_limit > 0.
1734 // If this limit is set, and if we have aborted redraw more times than is allowed,
1735 // interrupting is blocked and we're forced to redraw full screen once
1736 // (after which we can again interrupt forced_redraw_limit times).
1737 if (setup->canvas->forced_redraw_limit < 0 ||
1738 setup->canvas->forced_redraw_count < setup->canvas->forced_redraw_limit) {
1740 if (setup->canvas->forced_redraw_limit != -1) {
1741 setup->canvas->forced_redraw_count++;
1742 }
1744 return false;
1745 }
1746 }
1748 // Find the optimal buffer dimensions
1749 int bw = this_rect.x1 - this_rect.x0;
1750 int bh = this_rect.y1 - this_rect.y0;
1751 if ((bw < 1) || (bh < 1))
1752 return 0;
1754 if (bw * bh < setup->max_pixels) {
1755 // We are small enough
1756 sp_canvas_paint_single_buffer (setup->canvas,
1757 this_rect.x0, this_rect.y0,
1758 this_rect.x1, this_rect.y1,
1759 setup->big_rect.x0, setup->big_rect.y0,
1760 setup->big_rect.x1, setup->big_rect.y1, bw);
1761 return 1;
1762 }
1764 NRRectL lo = this_rect;
1765 NRRectL hi = this_rect;
1767 /*
1768 This test determines the redraw strategy:
1770 bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on
1771 horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in
1772 both directions if the cursor is in the middle). This is traditional for Inkscape since old days,
1773 and seems to be faster for drawings with many smaller objects at zoom-out.
1775 bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in
1776 almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow
1777 (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically
1778 faster.
1780 The default for now is the strips mode.
1781 */
1782 if (bw < bh || bh < 2 * TILE_SIZE) {
1783 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1784 int mid = ((long long) this_rect.x0 + (long long) this_rect.x1) / 2;
1785 // Make sure that mid lies on a tile boundary
1786 mid = (mid / TILE_SIZE) * TILE_SIZE;
1788 lo.x1 = mid;
1789 hi.x0 = mid;
1791 if (setup->mouse_loc[NR::X] < mid) {
1792 // Always paint towards the mouse first
1793 return sp_canvas_paint_rect_internal(setup, lo)
1794 && sp_canvas_paint_rect_internal(setup, hi);
1795 } else {
1796 return sp_canvas_paint_rect_internal(setup, hi)
1797 && sp_canvas_paint_rect_internal(setup, lo);
1798 }
1799 } else {
1800 // to correctly calculate the mean of two ints, we need to sum them into a larger int type
1801 int mid = ((long long) this_rect.y0 + (long long) this_rect.y1) / 2;
1802 // Make sure that mid lies on a tile boundary
1803 mid = (mid / TILE_SIZE) * TILE_SIZE;
1805 lo.y1 = mid;
1806 hi.y0 = mid;
1808 if (setup->mouse_loc[NR::Y] < mid) {
1809 // Always paint towards the mouse first
1810 return sp_canvas_paint_rect_internal(setup, lo)
1811 && sp_canvas_paint_rect_internal(setup, hi);
1812 } else {
1813 return sp_canvas_paint_rect_internal(setup, hi)
1814 && sp_canvas_paint_rect_internal(setup, lo);
1815 }
1816 }
1817 }
1820 /**
1821 * Helper that draws a specific rectangular part of the canvas.
1822 *
1823 * @return true if the rectangle painting succeeds.
1824 */
1825 static bool
1826 sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
1827 {
1828 g_return_val_if_fail (!canvas->need_update, false);
1830 NRRectL rect;
1831 rect.x0 = xx0;
1832 rect.x1 = xx1;
1833 rect.y0 = yy0;
1834 rect.y1 = yy1;
1836 // Clip rect-to-draw by the current visible area
1837 rect.x0 = MAX (rect.x0, canvas->x0);
1838 rect.y0 = MAX (rect.y0, canvas->y0);
1839 rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width);
1840 rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height);
1842 #ifdef DEBUG_REDRAW
1843 // paint the area to redraw yellow
1844 gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00);
1845 gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas),
1846 canvas->pixmap_gc,
1847 TRUE,
1848 rect.x0 - canvas->x0, rect.y0 - canvas->y0,
1849 rect.x1 - rect.x0, rect.y1 - rect.y0);
1850 #endif
1852 PaintRectSetup setup;
1854 setup.canvas = canvas;
1855 setup.big_rect = rect;
1857 // Save the mouse location
1858 gint x, y;
1859 gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL);
1860 setup.mouse_loc = sp_canvas_window_to_world (canvas, NR::Point(x,y));
1862 if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
1863 // use 256K as a compromise to not slow down gradients
1864 // 256K is the cached buffer and we need 4 channels
1865 setup.max_pixels = 65536; // 256K/4
1866 } else {
1867 // paths only, so 1M works faster
1868 // 1M is the cached buffer and we need 4 channels
1869 setup.max_pixels = 262144;
1870 }
1872 // Start the clock
1873 g_get_current_time(&(setup.start_time));
1875 // Go
1876 return sp_canvas_paint_rect_internal(&setup, rect);
1877 }
1879 /**
1880 * Force a full redraw after a specified number of interrupted redraws
1881 */
1882 void
1883 sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) {
1884 g_return_if_fail(canvas != NULL);
1886 canvas->forced_redraw_limit = count;
1887 canvas->forced_redraw_count = 0;
1888 }
1890 /**
1891 * End forced full redraw requests
1892 */
1893 void
1894 sp_canvas_end_forced_full_redraws(SPCanvas *canvas) {
1895 g_return_if_fail(canvas != NULL);
1897 canvas->forced_redraw_limit = -1;
1898 }
1900 /**
1901 * The canvas widget's expose callback.
1902 */
1903 static gint
1904 sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
1905 {
1906 SPCanvas *canvas = SP_CANVAS (widget);
1908 if (!GTK_WIDGET_DRAWABLE (widget) ||
1909 (event->window != SP_CANVAS_WINDOW (canvas)))
1910 return FALSE;
1912 int n_rects;
1913 GdkRectangle *rects;
1914 gdk_region_get_rectangles (event->region, &rects, &n_rects);
1916 for (int i = 0; i < n_rects; i++) {
1917 NRRectL rect;
1919 rect.x0 = rects[i].x + canvas->x0;
1920 rect.y0 = rects[i].y + canvas->y0;
1921 rect.x1 = rect.x0 + rects[i].width;
1922 rect.y1 = rect.y0 + rects[i].height;
1924 sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
1925 }
1927 if (n_rects > 0)
1928 g_free (rects);
1930 return FALSE;
1931 }
1933 /**
1934 * The canvas widget's keypress callback.
1935 */
1936 static gint
1937 sp_canvas_key (GtkWidget *widget, GdkEventKey *event)
1938 {
1939 return emit_event (SP_CANVAS (widget), (GdkEvent *) event);
1940 }
1942 /**
1943 * Crossing event handler for the canvas.
1944 */
1945 static gint
1946 sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
1947 {
1948 SPCanvas *canvas = SP_CANVAS (widget);
1950 if (event->window != SP_CANVAS_WINDOW (canvas))
1951 return FALSE;
1953 canvas->state = event->state;
1954 return pick_current_item (canvas, (GdkEvent *) event);
1955 }
1957 /**
1958 * Focus in handler for the canvas.
1959 */
1960 static gint
1961 sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
1962 {
1963 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1965 SPCanvas *canvas = SP_CANVAS (widget);
1967 if (canvas->focused_item) {
1968 return emit_event (canvas, (GdkEvent *) event);
1969 } else {
1970 return FALSE;
1971 }
1972 }
1974 /**
1975 * Focus out handler for the canvas.
1976 */
1977 static gint
1978 sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
1979 {
1980 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1982 SPCanvas *canvas = SP_CANVAS (widget);
1984 if (canvas->focused_item)
1985 return emit_event (canvas, (GdkEvent *) event);
1986 else
1987 return FALSE;
1988 }
1990 /**
1991 * Helper that repaints the areas in the canvas that need it.
1992 *
1993 * @return true if all the dirty parts have been redrawn
1994 */
1995 static int
1996 paint (SPCanvas *canvas)
1997 {
1998 if (canvas->need_update) {
1999 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2000 canvas->need_update = FALSE;
2001 }
2003 if (!canvas->need_redraw)
2004 return TRUE;
2006 Gdk::Region to_paint;
2008 for (int j=canvas->tTop; j<canvas->tBottom; j++) {
2009 for (int i=canvas->tLeft; i<canvas->tRight; i++) {
2010 int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
2012 if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
2013 to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
2014 TILE_SIZE, TILE_SIZE));
2015 }
2017 }
2018 }
2020 if (!to_paint.empty()) {
2021 Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
2022 typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
2023 for (Iter i=rect.begin(); i != rect.end(); ++i) {
2024 int x0 = (*i).get_x();
2025 int y0 = (*i).get_y();
2026 int x1 = x0 + (*i).get_width();
2027 int y1 = y0 + (*i).get_height();
2028 if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
2029 // Aborted
2030 return FALSE;
2031 };
2032 }
2033 }
2035 canvas->need_redraw = FALSE;
2037 // we've had a full unaborted redraw, reset the full redraw counter
2038 if (canvas->forced_redraw_limit != -1) {
2039 canvas->forced_redraw_count = 0;
2040 }
2042 return TRUE;
2043 }
2045 /**
2046 * Helper that invokes update, paint, and repick on canvas.
2047 */
2048 static int
2049 do_update (SPCanvas *canvas)
2050 {
2051 if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop durring interrupted display!
2052 return TRUE;
2054 /* Cause the update if necessary */
2055 if (canvas->need_update) {
2056 sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0);
2057 canvas->need_update = FALSE;
2058 }
2060 /* Paint if able to */
2061 if (GTK_WIDGET_DRAWABLE (canvas)) {
2062 return paint (canvas);
2063 }
2065 /* Pick new current item */
2066 while (canvas->need_repick) {
2067 canvas->need_repick = FALSE;
2068 pick_current_item (canvas, &canvas->pick_event);
2069 }
2071 return TRUE;
2072 }
2074 /**
2075 * Idle handler for the canvas that deals with pending updates and redraws.
2076 */
2077 static gint
2078 idle_handler (gpointer data)
2079 {
2080 GDK_THREADS_ENTER ();
2082 SPCanvas *canvas = SP_CANVAS (data);
2084 int const ret = do_update (canvas);
2086 if (ret) {
2087 /* Reset idle id */
2088 canvas->idle_id = 0;
2089 }
2091 GDK_THREADS_LEAVE ();
2093 return !ret;
2094 }
2096 /**
2097 * Convenience function to add an idle handler to a canvas.
2098 */
2099 static void
2100 add_idle (SPCanvas *canvas)
2101 {
2102 if (canvas->idle_id != 0)
2103 return;
2105 canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas);
2106 }
2108 /**
2109 * Returns the root group of the specified canvas.
2110 */
2111 SPCanvasGroup *
2112 sp_canvas_root (SPCanvas *canvas)
2113 {
2114 g_return_val_if_fail (canvas != NULL, NULL);
2115 g_return_val_if_fail (SP_IS_CANVAS (canvas), NULL);
2117 return SP_CANVAS_GROUP (canvas->root);
2118 }
2120 /**
2121 * Scrolls canvas to specific position (cx and cy are measured in screen pixels)
2122 */
2123 void
2124 sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling)
2125 {
2126 g_return_if_fail (canvas != NULL);
2127 g_return_if_fail (SP_IS_CANVAS (canvas));
2129 int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels)
2130 int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do!
2131 int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the
2132 int dy = iy - canvas->y0; // canvas w.r.t its previous position
2134 canvas->dx0 = cx; // here the 'd' stands for double, not delta!
2135 canvas->dy0 = cy;
2136 canvas->x0 = ix;
2137 canvas->y0 = iy;
2139 sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
2141 if (!clear) {
2142 // scrolling without zoom; redraw only the newly exposed areas
2143 if ((dx != 0) || (dy != 0)) {
2144 canvas->is_scrolling = is_scrolling;
2145 if (GTK_WIDGET_REALIZED (canvas)) {
2146 gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy);
2147 }
2148 }
2149 } else {
2150 // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
2151 }
2152 }
2154 /**
2155 * Updates canvas if necessary.
2156 */
2157 void
2158 sp_canvas_update_now (SPCanvas *canvas)
2159 {
2160 g_return_if_fail (canvas != NULL);
2161 g_return_if_fail (SP_IS_CANVAS (canvas));
2163 if (!(canvas->need_update ||
2164 canvas->need_redraw))
2165 return;
2167 do_update (canvas);
2168 }
2170 /**
2171 * Update callback for canvas widget.
2172 */
2173 static void
2174 sp_canvas_request_update (SPCanvas *canvas)
2175 {
2176 canvas->need_update = TRUE;
2177 add_idle (canvas);
2178 }
2180 /**
2181 * Forces redraw of rectangular canvas area.
2182 */
2183 void
2184 sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1)
2185 {
2186 NRRectL bbox;
2187 NRRectL visible;
2188 NRRectL clip;
2190 g_return_if_fail (canvas != NULL);
2191 g_return_if_fail (SP_IS_CANVAS (canvas));
2193 if (!GTK_WIDGET_DRAWABLE (canvas)) return;
2194 if ((x0 >= x1) || (y0 >= y1)) return;
2196 bbox.x0 = x0;
2197 bbox.y0 = y0;
2198 bbox.x1 = x1;
2199 bbox.y1 = y1;
2201 visible.x0 = canvas->x0;
2202 visible.y0 = canvas->y0;
2203 visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width;
2204 visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height;
2206 nr_rect_l_intersect (&clip, &bbox, &visible);
2208 sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1);
2209 add_idle (canvas);
2210 }
2212 /**
2213 * Sets world coordinates from win and canvas.
2214 */
2215 void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
2216 {
2217 g_return_if_fail (canvas != NULL);
2218 g_return_if_fail (SP_IS_CANVAS (canvas));
2220 if (worldx) *worldx = canvas->x0 + winx;
2221 if (worldy) *worldy = canvas->y0 + winy;
2222 }
2224 /**
2225 * Sets win coordinates from world and canvas.
2226 */
2227 void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
2228 {
2229 g_return_if_fail (canvas != NULL);
2230 g_return_if_fail (SP_IS_CANVAS (canvas));
2232 if (winx) *winx = worldx - canvas->x0;
2233 if (winy) *winy = worldy - canvas->y0;
2234 }
2236 /**
2237 * Converts point from win to world coordinates.
2238 */
2239 NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win)
2240 {
2241 g_assert (canvas != NULL);
2242 g_assert (SP_IS_CANVAS (canvas));
2244 return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]);
2245 }
2247 /**
2248 * Converts point from world to win coordinates.
2249 */
2250 NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world)
2251 {
2252 g_assert (canvas != NULL);
2253 g_assert (SP_IS_CANVAS (canvas));
2255 return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0);
2256 }
2258 /**
2259 * Returns true if point given in world coordinates is inside window.
2260 */
2261 bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world)
2262 {
2263 g_assert( canvas != NULL );
2264 g_assert(SP_IS_CANVAS(canvas));
2266 using NR::X;
2267 using NR::Y;
2268 GtkWidget const &w = *GTK_WIDGET(canvas);
2269 return ( ( canvas->x0 <= world[X] ) &&
2270 ( canvas->y0 <= world[Y] ) &&
2271 ( world[X] < canvas->x0 + w.allocation.width ) &&
2272 ( world[Y] < canvas->y0 + w.allocation.height ) );
2273 }
2275 /**
2276 * Return canvas window coordinates as NR::Rect.
2277 */
2278 NR::Rect SPCanvas::getViewbox() const
2279 {
2280 GtkWidget const *w = GTK_WIDGET(this);
2281 return NR::Rect(NR::Point(dx0, dy0),
2282 NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height));
2283 }
2285 /**
2286 * Return canvas window coordinates as IRect (a rectangle defined by integers).
2287 */
2288 NR::IRect SPCanvas::getViewboxIntegers() const
2289 {
2290 GtkWidget const *w = GTK_WIDGET(this);
2291 return NR::IRect(NR::IPoint(x0, y0),
2292 NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height));
2293 }
2295 inline int sp_canvas_tile_floor(int x)
2296 {
2297 return (x & (~(TILE_SIZE - 1))) / TILE_SIZE;
2298 }
2300 inline int sp_canvas_tile_ceil(int x)
2301 {
2302 return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
2303 }
2305 /**
2306 * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
2307 */
2308 static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb)
2309 {
2310 if ( nl >= nr || nt >= nb ) {
2311 if ( canvas->tiles ) g_free(canvas->tiles);
2312 canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
2313 canvas->tileH=canvas->tileV=0;
2314 canvas->tiles=NULL;
2315 return;
2316 }
2317 int tl=sp_canvas_tile_floor(nl);
2318 int tt=sp_canvas_tile_floor(nt);
2319 int tr=sp_canvas_tile_ceil(nr);
2320 int tb=sp_canvas_tile_ceil(nb);
2322 int nh = tr-tl, nv = tb-tt;
2323 uint8_t* ntiles = (uint8_t*)g_malloc(nh*nv*sizeof(uint8_t));
2324 for (int i=tl; i<tr; i++) {
2325 for (int j=tt; j<tb; j++) {
2326 int ind = (i-tl) + (j-tt)*nh;
2327 if ( i >= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) {
2328 ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
2329 } else {
2330 ntiles[ind]=0; // newly exposed areas get 0
2331 }
2332 }
2333 }
2334 if ( canvas->tiles ) g_free(canvas->tiles);
2335 canvas->tiles=ntiles;
2336 canvas->tLeft=tl;
2337 canvas->tTop=tt;
2338 canvas->tRight=tr;
2339 canvas->tBottom=tb;
2340 canvas->tileH=nh;
2341 canvas->tileV=nv;
2342 }
2344 /*
2345 * Helper that queues a canvas rectangle for redraw
2346 */
2347 static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) {
2348 canvas->need_redraw = TRUE;
2350 sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1);
2351 }
2353 /**
2354 * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise)
2355 */
2356 void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val)
2357 {
2358 if ( nl >= nr || nt >= nb ) {
2359 return;
2360 }
2361 int tl=sp_canvas_tile_floor(nl);
2362 int tt=sp_canvas_tile_floor(nt);
2363 int tr=sp_canvas_tile_ceil(nr);
2364 int tb=sp_canvas_tile_ceil(nb);
2365 if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;
2366 if ( tl < canvas->tLeft ) tl=canvas->tLeft;
2367 if ( tr > canvas->tRight ) tr=canvas->tRight;
2368 if ( tt < canvas->tTop ) tt=canvas->tTop;
2369 if ( tb > canvas->tBottom ) tb=canvas->tBottom;
2371 for (int i=tl; i<tr; i++) {
2372 for (int j=tt; j<tb; j++) {
2373 canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = val;
2374 }
2375 }
2376 }
2379 /*
2380 Local Variables:
2381 mode:c++
2382 c-file-style:"stroustrup"
2383 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2384 indent-tabs-mode:nil
2385 fill-column:99
2386 End:
2387 */
2388 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :