bool SVGCanvas::onEvent (GdkEvent * ev) const { g_assert (_dt); // Gdk::Event doesn't appear to be fully usable for this atm if (ev->type == GDK_BUTTON_PRESS) { // defocus any spinbuttons _widget->grab_focus(); } if ((ev->type == GDK_BUTTON_PRESS) && (ev->button.button == 3)) { if (ev->button.state & GDK_SHIFT_MASK) { sp_canvas_arena_set_sticky(SP_CANVAS_ARENA(_dt->drawing), true); } else { sp_canvas_arena_set_sticky(SP_CANVAS_ARENA(_dt->drawing), false); } } // The keypress events need to be passed to desktop handler explicitly, // because otherwise the event contexts only receive keypresses when the mouse cursor // is over the canvas. This redirection is only done for keypresses and only if there's no // current item on the canvas, because item events and all mouse events are caught // and passed on by the canvas acetate (I think). --bb if (ev->type == GDK_KEY_PRESS && !_spcanvas->current_item) { return sp_desktop_root_handler(0, ev, _dt); } return false; }
void SPSVGView::setDocument(SPDocument *document) { if (doc()) { doc()->getRoot()->invoke_hide(_dkey); } if (!_drawing) { _drawing = sp_canvas_item_new (_parent, SP_TYPE_CANVAS_ARENA, NULL); g_signal_connect (G_OBJECT (_drawing), "arena_event", G_CALLBACK (arena_handler), this); } View::setDocument (document); if (document) { Inkscape::DrawingItem *ai = document->getRoot()->invoke_show( SP_CANVAS_ARENA (_drawing)->drawing, _dkey, SP_ITEM_SHOW_DISPLAY); if (ai) { SP_CANVAS_ARENA (_drawing)->drawing.root()->prependChild(ai); } doRescale (!_rescale); } }
static void sp_canvas_arena_destroy (GtkObject *object) { SPCanvasArena *arena; arena = SP_CANVAS_ARENA (object); if (arena->active) { nr_object_unref ((NRObject *) arena->active); arena->active = NULL; } if (arena->root) { nr_arena_item_unref (arena->root); arena->root = NULL; } if (arena->arena) { #if 0 /* g_signal_disconnect_by_data (G_OBJECT (arena->arena), arena); */ g_signal_handlers_disconnect_matched (G_OBJECT(arena->arena), G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, arena); #endif nr_active_object_remove_listener_by_data ((NRActiveObject *) arena->arena, arena); nr_object_unref ((NRObject *) arena->arena); arena->arena = NULL; } if (GTK_OBJECT_CLASS (parent_class)->destroy) (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); }
static void sp_canvas_arena_update (SPCanvasItem *item, double *affine, unsigned int flags) { SPCanvasArena *arena; guint reset; arena = SP_CANVAS_ARENA (item); if (((SPCanvasItemClass *) parent_class)->update) (* ((SPCanvasItemClass *) parent_class)->update) (item, affine, flags); memcpy (NR_MATRIX_D_TO_DOUBLE (&arena->gc.transform), affine, 6 * sizeof (double)); if (flags & SP_CANVAS_UPDATE_AFFINE) { reset = NR_ARENA_ITEM_STATE_ALL; } else { reset = NR_ARENA_ITEM_STATE_NONE; } nr_arena_item_invoke_update (arena->root, NULL, &arena->gc, NR_ARENA_ITEM_STATE_ALL, reset); item->x1 = arena->root->bbox.x0 - 1; item->y1 = arena->root->bbox.y0 - 1; item->x2 = arena->root->bbox.x1 + 1; item->y2 = arena->root->bbox.y1 + 1; if (arena->cursor) { NRArenaItem *new_arena; /* Mess with enter/leave notifiers */ new_arena = nr_arena_item_invoke_pick (arena->root, arena->cx, arena->cy, nr_arena_global_delta, arena->sticky); if (new_arena != arena->active) { GdkEventCrossing ec; ec.window = GTK_WIDGET (item->canvas)->window; ec.send_event = TRUE; ec.subwindow = ec.window; ec.time = GDK_CURRENT_TIME; ec.x = arena->cx; ec.y = arena->cy; /* fixme: */ if (arena->active) { ec.type = GDK_LEAVE_NOTIFY; sp_canvas_arena_send_event (arena, (GdkEvent *) &ec); } /* fixme: This is not optimal - better track ::destroy (Lauris) */ if (arena->active) nr_object_unref ((NRObject *) arena->active); arena->active = new_arena; if (arena->active) nr_object_ref ((NRObject *) arena->active); if (arena->active) { ec.type = GDK_ENTER_NOTIFY; sp_canvas_arena_send_event (arena, (GdkEvent *) &ec); } } } }
static double sp_canvas_arena_point (SPCanvasItem *item, double x, double y, SPCanvasItem **actual_item) { SPCanvasArena *arena; NRArenaItem *picked; arena = SP_CANVAS_ARENA (item); nr_arena_item_invoke_update (arena->root, NULL, &arena->gc, NR_ARENA_ITEM_STATE_BBOX | NR_ARENA_ITEM_STATE_PICK, NR_ARENA_ITEM_STATE_NONE); picked = nr_arena_item_invoke_pick (arena->root, x, y, nr_arena_global_delta, arena->sticky); arena->picked = picked; if (picked) { *actual_item = item; return 0.0; } return 1e18; }
static gint sp_canvas_arena_event (SPCanvasItem *item, GdkEvent *event) { SPCanvasArena *arena; NRArenaItem *new_arena; gint ret; /* fixme: This sucks, we have to handle enter/leave notifiers */ arena = SP_CANVAS_ARENA (item); ret = FALSE; switch (event->type) { case GDK_ENTER_NOTIFY: if (!arena->cursor) { if (arena->active) { g_warning ("Cursor entered to arena with already active item"); nr_object_unref ((NRObject *) arena->active); } arena->cursor = TRUE; #if 0 gnome_canvas_w2c_d (item->canvas, event->crossing.x, event->crossing.y, &arena->cx, &arena->cy); #else arena->cx = event->crossing.x; arena->cy = event->crossing.y; #endif /* fixme: Not sure abut this, but seems the right thing (Lauris) */ nr_arena_item_invoke_update (arena->root, NULL, &arena->gc, NR_ARENA_ITEM_STATE_PICK, NR_ARENA_ITEM_STATE_NONE); arena->active = nr_arena_item_invoke_pick (arena->root, arena->cx, arena->cy, nr_arena_global_delta, arena->sticky); if (arena->active) nr_object_ref ((NRObject *) arena->active); ret = sp_canvas_arena_send_event (arena, event); } break; case GDK_LEAVE_NOTIFY: if (arena->cursor) { ret = sp_canvas_arena_send_event (arena, event); if (arena->active) nr_object_unref ((NRObject *) arena->active); arena->active = NULL; arena->cursor = FALSE; } break; case GDK_MOTION_NOTIFY: #if 0 gnome_canvas_w2c_d (item->canvas, event->motion.x, event->motion.y, &arena->cx, &arena->cy); #else arena->cx = event->motion.x; arena->cy = event->motion.y; #endif /* fixme: Not sure abut this, but seems the right thing (Lauris) */ nr_arena_item_invoke_update (arena->root, NULL, &arena->gc, NR_ARENA_ITEM_STATE_PICK, NR_ARENA_ITEM_STATE_NONE); new_arena = nr_arena_item_invoke_pick (arena->root, arena->cx, arena->cy, nr_arena_global_delta, arena->sticky); if (new_arena != arena->active) { GdkEventCrossing ec; ec.window = event->motion.window; ec.send_event = event->motion.send_event; ec.subwindow = event->motion.window; ec.time = event->motion.time; ec.x = event->motion.x; ec.y = event->motion.y; /* fixme: */ if (arena->active) { ec.type = GDK_LEAVE_NOTIFY; ret = sp_canvas_arena_send_event (arena, (GdkEvent *) &ec); } if (arena->active) nr_object_unref ((NRObject *) arena->active); arena->active = new_arena; if (arena->active) nr_object_ref ((NRObject *) arena->active); if (arena->active) { ec.type = GDK_ENTER_NOTIFY; ret = sp_canvas_arena_send_event (arena, (GdkEvent *) &ec); } } ret = sp_canvas_arena_send_event (arena, event); break; default: /* Just send event */ ret = sp_canvas_arena_send_event (arena, event); break; } return ret; }
static void sp_canvas_arena_render (SPCanvasItem *item, SPCanvasBuf *buf) { SPCanvasArena *arena; gint bw, bh, sw, sh; gint x, y; arena = SP_CANVAS_ARENA (item); nr_arena_item_invoke_update (arena->root, NULL, &arena->gc, NR_ARENA_ITEM_STATE_BBOX | NR_ARENA_ITEM_STATE_RENDER, NR_ARENA_ITEM_STATE_NONE); if (buf->is_bg) { sp_canvas_clear_buffer (buf); buf->is_bg = FALSE; buf->is_buf = TRUE; } bw = buf->rect.x1 - buf->rect.x0; bh = buf->rect.y1 - buf->rect.y0; if ((bw < 1) || (bh < 1)) return; /* 65536 is max cached buffer and we need 4 channels */ if (bw * bh < 16384) { /* We can go with single buffer */ sw = bw; sh = bh; } else if (bw <= 2048) { /* Go with row buffer */ sw = bw; sh = 16384 / bw; } else if (bh <= 2048) { /* Go with column buffer */ sw = 16384 / bh; sh = bh; } else { sw = 128; sh = 128; } /* fixme: RGB transformed bitmap blit is not implemented (Lauris) */ /* And even if it would be, unless it uses MMX there is little reason to go RGB */ #define STRICT_RGBA for (y = buf->rect.y0; y < buf->rect.y1; y += sh) { for (x = buf->rect.x0; x < buf->rect.x1; x += sw) { NRRectL area; #ifdef STRICT_RGBA NRPixBlock pb; #endif NRPixBlock cb; area.x0 = x; area.y0 = y; area.x1 = MIN (x + sw, buf->rect.x1); area.y1 = MIN (y + sh, buf->rect.y1); #ifdef STRICT_RGBA nr_pixblock_setup_fast (&pb, NR_PIXBLOCK_MODE_R8G8B8A8P, area.x0, area.y0, area.x1, area.y1, TRUE); /* fixme: */ pb.empty = FALSE; #endif nr_pixblock_setup_extern (&cb, NR_PIXBLOCK_MODE_R8G8B8, area.x0, area.y0, area.x1, area.y1, buf->buf + (y - buf->rect.y0) * buf->buf_rowstride + 3 * (x - buf->rect.x0), buf->buf_rowstride, FALSE, FALSE); #ifdef STRICT_RGBA nr_arena_item_invoke_render (arena->root, &area, &pb, 0); nr_blit_pixblock_pixblock (&cb, &pb); nr_pixblock_release (&pb); #else nr_arena_item_invoke_render (arena->root, &area, &cb, 0); #endif nr_pixblock_release (&cb); } } }
static gint sp_dropper_context_root_handler(SPEventContext *event_context, GdkEvent *event) { SPDropperContext *dc = (SPDropperContext *) event_context; int ret = FALSE; SPDesktop *desktop = event_context->desktop; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); int pick = prefs->getInt("/tools/dropper/pick", SP_DROPPER_PICK_VISIBLE); bool setalpha = prefs->getBool("/tools/dropper/setalpha", true); switch (event->type) { case GDK_BUTTON_PRESS: if (event->button.button == 1 && !event_context->space_panning) { dc->centre = Geom::Point(event->button.x, event->button.y); dc->dragging = TRUE; ret = TRUE; } break; case GDK_MOTION_NOTIFY: if (event->motion.state & GDK_BUTTON2_MASK) { // pass on middle-drag ret = FALSE; break; } else if (!event_context->space_panning) { // otherwise, constantly calculate color no matter is any button pressed or not double rw = 0.0; double W(0), R(0), G(0), B(0), A(0); if (dc->dragging) { // calculate average // radius rw = std::min(Geom::L2(Geom::Point(event->button.x, event->button.y) - dc->centre), 400.0); if (rw == 0) { // happens sometimes, little idea why... break; } Geom::Point const cd = desktop->w2d(dc->centre); Geom::Matrix const w2dt = desktop->w2d(); const double scale = rw * w2dt.descrim(); Geom::Matrix const sm( Geom::Scale(scale, scale) * Geom::Translate(cd) ); sp_canvas_item_affine_absolute(dc->area, sm); sp_canvas_item_show(dc->area); /* Get buffer */ const int x0 = (int) floor(dc->centre[Geom::X] - rw); const int y0 = (int) floor(dc->centre[Geom::Y] - rw); const int x1 = (int) ceil(dc->centre[Geom::X] + rw); const int y1 = (int) ceil(dc->centre[Geom::Y] + rw); if ((x1 > x0) && (y1 > y0)) { NRPixBlock pb; nr_pixblock_setup_fast(&pb, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1, TRUE); /* fixme: (Lauris) */ sp_canvas_arena_render_pixblock(SP_CANVAS_ARENA(sp_desktop_drawing(desktop)), &pb); for (int y = y0; y < y1; y++) { const unsigned char *s = NR_PIXBLOCK_PX(&pb) + (y - y0) * pb.rs; for (int x = x0; x < x1; x++) { const double dx = x - dc->centre[Geom::X]; const double dy = y - dc->centre[Geom::Y]; const double w = exp(-((dx * dx) + (dy * dy)) / (rw * rw)); W += w; R += w * s[0]; G += w * s[1]; B += w * s[2]; A += w * s[3]; s += 4; } } nr_pixblock_release(&pb); R = (R + 0.001) / (255.0 * W); G = (G + 0.001) / (255.0 * W); B = (B + 0.001) / (255.0 * W); A = (A + 0.001) / (255.0 * W); R = CLAMP(R, 0.0, 1.0); G = CLAMP(G, 0.0, 1.0); B = CLAMP(B, 0.0, 1.0); A = CLAMP(A, 0.0, 1.0); } } else { // pick single pixel NRPixBlock pb; int x = (int) floor(event->button.x); int y = (int) floor(event->button.y); nr_pixblock_setup_fast(&pb, NR_PIXBLOCK_MODE_R8G8B8A8P, x, y, x+1, y+1, TRUE); sp_canvas_arena_render_pixblock(SP_CANVAS_ARENA(sp_desktop_drawing(desktop)), &pb); const unsigned char *s = NR_PIXBLOCK_PX(&pb); R = s[0] / 255.0; G = s[1] / 255.0; B = s[2] / 255.0; A = s[3] / 255.0; } if (pick == SP_DROPPER_PICK_VISIBLE) { // compose with page color guint32 bg = sp_desktop_namedview(desktop)->pagecolor; R = R + (SP_RGBA32_R_F(bg)) * (1 - A); G = G + (SP_RGBA32_G_F(bg)) * (1 - A); B = B + (SP_RGBA32_B_F(bg)) * (1 - A); A = 1.0; } else { // un-premultiply color channels if (A > 0) { R /= A; G /= A; B /= A; } } if (fabs(A) < 1e-4) { A = 0; // suppress exponentials, CSS does not allow that } // remember color dc->R = R; dc->G = G; dc->B = B; dc->alpha = A; // status message double alpha_to_set = setalpha? dc->alpha : 1.0; guint32 c32 = SP_RGBA32_F_COMPOSE(R, G, B, alpha_to_set); gchar c[64]; sp_svg_write_color(c, sizeof(c), c32); // alpha of color under cursor, to show in the statusbar // locale-sensitive printf is OK, since this goes to the UI, not into SVG gchar *alpha = g_strdup_printf(_(" alpha %.3g"), alpha_to_set); // where the color is picked, to show in the statusbar gchar *where = dc->dragging ? g_strdup_printf(_(", averaged with radius %d"), (int) rw) : g_strdup_printf(_(" under cursor")); // message, to show in the statusbar const gchar *message = dc->dragging ? _("<b>Release mouse</b> to set color.") : _("<b>Click</b> to set fill, <b>Shift+click</b> to set stroke; <b>drag</b> to average color in area; with <b>Alt</b> to pick inverse color; <b>Ctrl+C</b> to copy the color under mouse to clipboard"); event_context->defaultMessageContext()->setF( Inkscape::NORMAL_MESSAGE, "<b>%s%s</b>%s. %s", c, (pick == SP_DROPPER_PICK_VISIBLE)? "" : alpha, where, message ); g_free(where); g_free(alpha); ret = TRUE; } break; case GDK_BUTTON_RELEASE: if (event->button.button == 1 && !event_context->space_panning) { sp_canvas_item_hide(dc->area); dc->dragging = FALSE; double alpha_to_set = setalpha? dc->alpha : 1.0; // do the actual color setting sp_desktop_set_color(desktop, (event->button.state & GDK_MOD1_MASK)? ColorRGBA(1 - dc->R, 1 - dc->G, 1 - dc->B, alpha_to_set) : ColorRGBA(dc->R, dc->G, dc->B, alpha_to_set), false, !(event->button.state & GDK_SHIFT_MASK)); // REJON: set aux. toolbar input to hex color! if (!(sp_desktop_selection(desktop)->isEmpty())) { sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_DROPPER, _("Set picked color")); } ret = TRUE; } break; case GDK_KEY_PRESS: switch (get_group0_keyval(&event->key)) { case GDK_Up: case GDK_Down: case GDK_KP_Up: case GDK_KP_Down: // prevent the zoom field from activation if (!MOD__CTRL_ONLY) { ret = TRUE; } break; case GDK_Escape: sp_desktop_selection(desktop)->clear(); default: break; } break; default: break; } if (!ret) { if (((SPEventContextClass *) parent_class)->root_handler) { ret = ((SPEventContextClass *) parent_class)->root_handler(event_context, event); } } return ret; }