sp-canvas.cpp revision a46d5d044ce7d9ad174a4adc302ac1b5f428c7dc
4a7ebbf91a516d588a7dcc45921c17a896b65f62JazzyNico * Port of GnomeCanvas for Inkscape needs
4a7ebbf91a516d588a7dcc45921c17a896b65f62JazzyNico * Federico Mena <federico@nuclecu.unam.mx>
4a7ebbf91a516d588a7dcc45921c17a896b65f62JazzyNico * Raph Levien <raph@gimp.org>
4a7ebbf91a516d588a7dcc45921c17a896b65f62JazzyNico * Lauris Kaplinski <lauris@kaplinski.com>
4a7ebbf91a516d588a7dcc45921c17a896b65f62JazzyNico * Copyright (C) 1998 The Free Software Foundation
4a7ebbf91a516d588a7dcc45921c17a896b65f62JazzyNico * Copyright (C) 2002-2006 authors
4a7ebbf91a516d588a7dcc45921c17a896b65f62JazzyNico * Released under GNU GPL, read the file 'COPYING' for more information
4a7ebbf91a516d588a7dcc45921c17a896b65f62JazzyNico#endif // ENABLE_LCMS
4a7ebbf91a516d588a7dcc45921c17a896b65f62JazzyNico// gtk_check_version returns non-NULL on failure
4a7ebbf91a516d588a7dcc45921c17a896b65f62JazzyNicostatic bool const HAS_BROKEN_MOTION_HINTS =
4a7ebbf91a516d588a7dcc45921c17a896b65f62JazzyNico// Define this to visualize the regions to be redrawn
4a7ebbf91a516d588a7dcc45921c17a896b65f62JazzyNico//#define DEBUG_REDRAW 1;
4a7ebbf91a516d588a7dcc45921c17a896b65f62JazzyNico// Tiles are a way to minimize the number of redraws, eliminating too small redraws.
4a7ebbf91a516d588a7dcc45921c17a896b65f62JazzyNico// The canvas stores a 2D array of ints, each representing a TILE_SIZExTILE_SIZE pixels tile.
4a7ebbf91a516d588a7dcc45921c17a896b65f62JazzyNico// If any part of it is dirtied, the entire tile is dirtied (its int is nonzero) and repainted.
4a7ebbf91a516d588a7dcc45921c17a896b65f62JazzyNicostatic gint const sp_canvas_update_priority = G_PRIORITY_HIGH_IDLE;
4a7ebbf91a516d588a7dcc45921c17a896b65f62JazzyNico#define SP_CANVAS_WINDOW(c) (((GtkWidget *) (c))->window)
4a7ebbf91a516d588a7dcc45921c17a896b65f62JazzyNico * A group of Items.
4a7ebbf91a516d588a7dcc45921c17a896b65f62JazzyNico * The SPCanvasGroup vtable.
4a7ebbf91a516d588a7dcc45921c17a896b65f62JazzyNico * The SPCanvas vtable.
4a7ebbf91a516d588a7dcc45921c17a896b65f62JazzyNicostatic void group_add (SPCanvasGroup *group, SPCanvasItem *item);
4a7ebbf91a516d588a7dcc45921c17a896b65f62JazzyNicostatic void group_remove (SPCanvasGroup *group, SPCanvasItem *item);
4a7ebbf91a516d588a7dcc45921c17a896b65f62JazzyNico/* SPCanvasItem */
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapennerstatic void sp_canvas_request_update (SPCanvas *canvas);
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapennerstatic void sp_canvas_item_class_init (SPCanvasItemClass *klass);
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapennerstatic void sp_canvas_item_init (SPCanvasItem *item);
0033c073cef3bdf51409b8f8b37914941f340257apennerstatic void sp_canvas_item_dispose (GObject *object);
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapennerstatic void sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args);
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapennerstatic int emit_event (SPCanvas *canvas, GdkEvent *event);
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapennerstatic int pick_current_item (SPCanvas *canvas, GdkEvent *event);
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapenner * Registers the SPCanvasItem class with Glib and returns its type number.
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapenner type = g_type_register_static (GTK_TYPE_OBJECT, "SPCanvasItem", &info, (GTypeFlags)0);
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapenner * Initializes the SPCanvasItem vtable and the "event" signal.
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapenner GObjectClass *object_class = (GObjectClass *) klass;
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapenner ((glong)((guint8*)&(klass->event) - (guint8*)klass)),
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapenner * Callback for initialization of SPCanvasItem.
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapenner // TODO items should not be visible on creation - this causes kludges with items
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapenner // that should be initially invisible; examples of such items: node handles, the CtrlRect
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapenner // used for rubberbanding, path outline, etc.
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapenner * Constructs new SPCanvasItem on SPCanvasGroup.
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapennersp_canvas_item_new (SPCanvasGroup *parent, GtkType type, gchar const *first_arg_name, ...)
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapenner g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL);
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapenner g_return_val_if_fail (gtk_type_is_a (type, sp_canvas_item_get_type ()), NULL);
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapenner SPCanvasItem *item = SP_CANVAS_ITEM (gtk_type_new (type));
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapenner sp_canvas_item_construct (item, parent, first_arg_name, args);
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapenner * Sets up the newly created SPCanvasItem.
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapenner * We make it static for encapsulation reasons since it was nowhere used.
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapennersp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args)
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapenner g_object_set_valist (G_OBJECT (item), first_arg_name, args);
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapenner * Helper function that requests redraw only if item's visible flag is set.
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapenner sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapenner * Callback that removes item from all referers and destroys it.
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapenner // Hack: if this is a ctrlrect, move it to 0,0;
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapenner // this redraws only the stroke of the rect to be deleted,
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapenner // avoiding redraw of the entire area
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapenner SP_CTRLRECT(object)->setRectangle(Geom::Rect(Geom::Point(0,0),Geom::Point(0,0)));
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapenner group_remove (SP_CANVAS_GROUP (item->parent), item);
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapenner G_OBJECT_CLASS (g_type_class_peek(g_type_parent(sp_canvas_item_get_type())))->dispose (object);
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapenner * Helper function to update item and its children.
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapenner * NB! affine is parent2canvas.
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapennersp_canvas_item_invoke_update (SPCanvasItem *item, Geom::Affine const &affine, unsigned int flags)
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapenner /* Apply the child item's transform */
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapenner /* apply object flags to child flags */
ffbd2f8fa3a83d1d4ad8cd672cf738ffaa5cdfeeapenner int child_flags = flags & ~SP_CANVAS_UPDATE_REQUESTED;
4a7ebbf91a516d588a7dcc45921c17a896b65f62JazzyNico if (child_flags & (SP_CANVAS_UPDATE_REQUESTED | SP_CANVAS_UPDATE_AFFINE)) {
return NR_HUGE;
if (!before)
sp_canvas_request_redraw (item->canvas, (int)(item->x1), (int)(item->y1), (int)(item->x2 + 1), (int)(item->y2 + 1));
sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)(item->x2 + 1), (int)(item->y2 + 1));
if (HAS_BROKEN_MOTION_HINTS) {
while (item) {
return affine;
while (item) {
if (focused_item) {
if (focused_item) {
static void sp_canvas_group_update (SPCanvasItem *item, Geom::Affine const &affine, unsigned int flags);
static double sp_canvas_group_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item);
if (!type) {
sizeof(SPCanvasGroupClass),
sizeof(SPCanvasGroup),
type = g_type_register_static(sp_canvas_item_get_type(), "SPCanvasGroup", &info, static_cast<GTypeFlags>(0));
return type;
while (list) {
bool empty=true;
if (empty) {
empty = false;
if (bounds) {
double const x = p[Geom::X];
double const y = p[Geom::Y];
int has_point;
return best;
if (!type) {
sizeof(SPCanvasClass),
sizeof(SPCanvas),
return type;
// See comment at in sp-canvas.h.
#if ENABLE_LCMS
sp_canvas_new_aa (void)
0 : GDK_POINTER_MOTION_HINT_MASK ) |
widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height);
case GDK_ENTER_NOTIFY:
case GDK_LEAVE_NOTIFY:
case GDK_MOTION_NOTIFY:
case GDK_BUTTON_PRESS:
case GDK_2BUTTON_PRESS:
case GDK_3BUTTON_PRESS:
case GDK_BUTTON_RELEASE:
case GDK_KEY_PRESS:
case GDK_KEY_RELEASE:
case GDK_SCROLL:
mask = 0;
case GDK_ENTER_NOTIFY:
case GDK_LEAVE_NOTIFY:
case GDK_MOTION_NOTIFY:
case GDK_BUTTON_PRESS:
case GDK_2BUTTON_PRESS:
case GDK_3BUTTON_PRESS:
case GDK_BUTTON_RELEASE:
// See this bug report: https://bugs.launchpad.net/inkscape/+bug/522335/comments/8
return finished;
int button_down = 0;
if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
return FALSE;
return retval;
return retval;
static gint
return retval;
int mask;
mask = 0;
case GDK_BUTTON_PRESS:
case GDK_2BUTTON_PRESS:
case GDK_3BUTTON_PRESS:
case GDK_BUTTON_RELEASE:
return retval;
static gint
int status;
return FALSE;
return FALSE;
return status;
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)
#if ENABLE_LCMS
if ( fromDisplay ) {
#if ENABLE_LCMS
TRUE,
#if ENABLE_LCMS
#ifdef CANVAS_OUTPUT_VIA_CAIRO
struct PaintRectSetup {
int max_pixels;
#ifdef DEBUG_REDRAW
TRUE,
gint x, y;
static gint
return FALSE;
int n_rects;
for (int i = 0; i < n_rects; i++) {
if (n_rects > 0)
return FALSE;
static gint
static gint
return FALSE;
static gint
return FALSE;
static gint
return FALSE;
return TRUE;
return FALSE;
return TRUE;
if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop during interrupted display!
return TRUE;
return TRUE;
return TRUE;
static gint
if (ret) {
return !ret;
sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height);
if (!clear) {
void sp_canvas_window_to_world(SPCanvas const *canvas, double winx, double winy, double *worldx, double *worldy)
void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double worldy, double *winx, double *winy)
inline int sp_canvas_tile_floor(int x)
inline int sp_canvas_tile_ceil(int x)
* Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array
ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile
if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return;