static void spdc_attach_selection (SPDrawContext *dc, SPSelection *sel) { SPItem *item; /* We reset white and forget white/start/end anchors */ spdc_reset_white (dc); dc->sa = NULL; dc->ea = NULL; item = sp_selection_item (dc->selection); if (item && SP_IS_PATH (item)) { SPCurve *norm; NRMatrixD i2dt; GSList *l; /* Create new white data */ /* Item */ dc->white_item = item; /* Curve list */ /* We keep it in desktop coordinates to eliminate calculation errors */ norm = sp_shape_get_curve (SP_SHAPE (item)); sp_item_i2d_affine_d (dc->white_item, &i2dt); norm = sp_curve_transform (norm, NR_MATRIX_D_TO_DOUBLE (&i2dt)); g_return_if_fail (norm != NULL); dc->white_curves = sp_curve_split (norm); sp_curve_unref (norm); /* Anchor list */ for (l = dc->white_curves; l != NULL; l = l->next) { SPCurve *c; c = (SPCurve*)l->data; g_return_if_fail (c->end > 1); if (c->bpath->code == ART_MOVETO_OPEN) { ArtBpath *s, *e; SPDrawAnchor *a; s = sp_curve_first_bpath (c); e = sp_curve_last_bpath (c); a = sp_draw_anchor_new (dc, c, TRUE, s->x3, s->y3); dc->white_anchors = g_slist_prepend (dc->white_anchors, a); a = sp_draw_anchor_new (dc, c, FALSE, e->x3, e->y3); dc->white_anchors = g_slist_prepend (dc->white_anchors, a); } } /* fixme: recalculate active anchor? */ } }
static gint pencil_handle_motion_notify(SPPencilContext *const pc, GdkEventMotion const &mevent) { if ((mevent.state & GDK_CONTROL_MASK) && (mevent.state & GDK_BUTTON1_MASK)) { // mouse was accidentally moved during Ctrl+click; // ignore the motion and create a single point pc->is_drawing = false; return TRUE; } gint ret = FALSE; SPDesktop *const dt = pc->desktop; SPEventContext *event_context = SP_EVENT_CONTEXT(pc); if (event_context->space_panning || mevent.state & GDK_BUTTON2_MASK || mevent.state & GDK_BUTTON3_MASK) { // allow scrolling return FALSE; } if ( ( mevent.state & GDK_BUTTON1_MASK ) && !pc->grab && pc->is_drawing) { /* Grab mouse, so release will not pass unnoticed */ pc->grab = SP_CANVAS_ITEM(dt->acetate); sp_canvas_item_grab(pc->grab, ( GDK_KEY_PRESS_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK ), NULL, mevent.time); } /* Find desktop coordinates */ Geom::Point p = dt->w2d(Geom::Point(mevent.x, mevent.y)); /* Test whether we hit any anchor. */ SPDrawAnchor *anchor = spdc_test_inside(pc, Geom::Point(mevent.x, mevent.y)); if (pencil_within_tolerance) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); gint const tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100); if ( Geom::LInfty( Geom::Point(mevent.x,mevent.y) - pencil_drag_origin_w ) < tolerance ) { return FALSE; // Do not drag if we're within tolerance from origin. } } // Once the user has moved farther than tolerance from the original location // (indicating they intend to move the object, not click), then always process the // motion notify coordinates as given (no snapping back to origin) pencil_within_tolerance = false; switch (pc->state) { case SP_PENCIL_CONTEXT_ADDLINE: /* Set red endpoint */ if (anchor) { p = anchor->dp; } else { Geom::Point ptnr(p); spdc_endpoint_snap(pc, ptnr, mevent.state); p = ptnr; } spdc_set_endpoint(pc, p); ret = TRUE; break; default: /* We may be idle or already freehand */ if ( mevent.state & GDK_BUTTON1_MASK && pc->is_drawing ) { pc->state = SP_PENCIL_CONTEXT_FREEHAND; if ( !pc->sa && !pc->green_anchor ) { /* Create green anchor */ pc->green_anchor = sp_draw_anchor_new(pc, pc->green_curve, TRUE, pc->p[0]); } /** \todo * fixme: I am not sure whether we want to snap to anchors * in middle of freehand (Lauris) */ SnapManager &m = dt->namedview->snap_manager; if (anchor) { p = anchor->dp; } else if ((mevent.state & GDK_SHIFT_MASK) == 0) { m.setup(dt); Inkscape::SnappedPoint const s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p); if (s.getSnapped()) { s.getPoint(p); pc->prev_snap_was_succesful = true; } else { pc->prev_snap_was_succesful = false; } } if ( pc->npoints != 0) { // buttonpress may have happened before we entered draw context! if (!(pc->prev_snap_was_succesful && m.snapprefs.getSnapPostponedGlobally())) { // When snapping is enabled but temporarily on hold because the mouse is moving // fast, then we don't want to add nodes off-grid spdc_add_freehand_point(pc, p, mevent.state); ret = TRUE; } } if (anchor && !pc->anchor_statusbar) { pc->_message_context->set(Inkscape::NORMAL_MESSAGE, _("<b>Release</b> here to close and finish the path.")); pc->anchor_statusbar = true; } else if (!anchor && pc->anchor_statusbar) { pc->_message_context->clear(); pc->anchor_statusbar = false; } else if (!anchor) { pc->_message_context->set(Inkscape::NORMAL_MESSAGE, _("Drawing a freehand path")); } } else { if (anchor && !pc->anchor_statusbar) { pc->_message_context->set(Inkscape::NORMAL_MESSAGE, _("<b>Drag</b> to continue the path from this point.")); pc->anchor_statusbar = true; } else if (!anchor && pc->anchor_statusbar) { pc->_message_context->clear(); pc->anchor_statusbar = false; } } break; } return ret; }
gint sp_pencil_context_root_handler (SPEventContext *ec, GdkEvent *event) { SPDrawContext *dc; SPPencilContext *pc; SPDesktop *dt; NRPointF p; gint ret; SPDrawAnchor *anchor; NRPointF fp; dc = SP_DRAW_CONTEXT (ec); pc = SP_PENCIL_CONTEXT (ec); dt = ec->desktop; ret = FALSE; switch (event->type) { case GDK_BUTTON_PRESS: if (event->button.button == 1) { NRPointF fp; #if 0 /* Grab mouse, so release will not pass unnoticed */ dc->grab = SP_CANVAS_ITEM (dt->acetate); sp_canvas_item_grab (dc->grab, SPDC_EVENT_MASK, NULL, event->button.time); #endif /* Find desktop coordinates */ sp_desktop_w2d_xy_point (dt, &fp, event->button.x, event->button.y); p.x = fp.x; p.y = fp.y; /* Test, whether we hit any anchor */ anchor = test_inside (dc, event->button.x, event->button.y); switch (pc->state) { case SP_PENCIL_CONTEXT_ADDLINE: /* Current segment will be finished with release */ ret = TRUE; break; default: /* Set first point of sequence */ if (anchor) { p = anchor->dp; } dc->sa = anchor; spdc_set_startpoint (pc, &p, event->button.state); ret = TRUE; break; } } break; case GDK_MOTION_NOTIFY: #if 1 if ((event->motion.state & GDK_BUTTON1_MASK) && !dc->grab) { /* Grab mouse, so release will not pass unnoticed */ dc->grab = SP_CANVAS_ITEM (dt->acetate); sp_canvas_item_grab (dc->grab, SPDC_EVENT_MASK, NULL, event->button.time); } #endif /* Find desktop coordinates */ sp_desktop_w2d_xy_point (dt, &fp, event->motion.x, event->motion.y); p.x = fp.x; p.y = fp.y; /* Test, whether we hit any anchor */ anchor = test_inside (dc, event->button.x, event->button.y); switch (pc->state) { case SP_PENCIL_CONTEXT_ADDLINE: /* Set red endpoint */ if (anchor) { p = anchor->dp; } spdc_set_endpoint (pc, &p, event->motion.state); ret = TRUE; break; default: /* We may be idle or already freehand */ if (event->motion.state & GDK_BUTTON1_MASK) { pc->state = SP_PENCIL_CONTEXT_FREEHAND; if (!dc->sa && !dc->green_anchor) { /* Create green anchor */ dc->green_anchor = sp_draw_anchor_new (dc, dc->green_curve, TRUE, dc->p[0].x, dc->p[0].y); } /* fixme: I am not sure, whether we want to snap to anchors in middle of freehand (Lauris) */ if (anchor) { p = anchor->dp; } else { sp_desktop_free_snap (dt, &p); } spdc_add_freehand_point (pc, &p, event->motion.state); ret = TRUE; } break; } break; case GDK_BUTTON_RELEASE: if (event->button.button == 1) { NRPointF fp; /* Find desktop coordinates */ sp_desktop_w2d_xy_point (dt, &fp, event->motion.x, event->motion.y); p.x = fp.x; p.y = fp.y; /* Test, whether we hit any anchor */ anchor = test_inside (dc, event->button.x, event->button.y); switch (pc->state) { case SP_PENCIL_CONTEXT_IDLE: /* Releasing button in idle mode means single click */ /* We have already set up start point/anchor in button_press */ pc->state = SP_PENCIL_CONTEXT_ADDLINE; ret = TRUE; break; case SP_PENCIL_CONTEXT_ADDLINE: /* Finish segment now */ if (anchor) { p = anchor->dp; } dc->ea = anchor; spdc_finish_endpoint (pc, &p, !anchor, event->button.state); pc->state = SP_PENCIL_CONTEXT_IDLE; ret = TRUE; break; case SP_PENCIL_CONTEXT_FREEHAND: /* Finish segment now */ /* fixme: Clean up what follows (Lauris) */ if (anchor) { p = anchor->dp; } dc->ea = anchor; /* Write curves to object */ g_print ("Finishing freehand\n"); spdc_concat_colors_and_flush (dc, FALSE); dc->sa = NULL; dc->ea = NULL; if (dc->green_anchor) { dc->green_anchor = sp_draw_anchor_destroy (dc->green_anchor); } pc->state = SP_PENCIL_CONTEXT_IDLE; ret = TRUE; break; default: break; } #if 1 if (dc->grab) { /* Release grab now */ sp_canvas_item_ungrab (dc->grab, event->button.time); dc->grab = NULL; } #endif dc->grab = NULL; ret = TRUE; } break; default: break; } if (!ret) { if (((SPEventContextClass *) pencil_parent_class)->root_handler) ret = ((SPEventContextClass *) pencil_parent_class)->root_handler (ec, event); } return ret; }
gint sp_pen_context_root_handler (SPEventContext *ec, GdkEvent *event) { SPDrawContext *dc; SPPenContext *pc; SPDesktop *dt; NRPointF p; gint ret; SPDrawAnchor *anchor; NRPointF fp; dc = SP_DRAW_CONTEXT (ec); pc = SP_PEN_CONTEXT (ec); dt = ec->desktop; ret = FALSE; switch (event->type) { case GDK_BUTTON_PRESS: if (event->button.button == 1) { #if 0 /* Grab mouse, so release will not pass unnoticed */ dc->grab = SP_CANVAS_ITEM (dt->acetate); sp_canvas_item_grab (dc->grab, SPDC_EVENT_MASK, NULL, event->button.time); #endif /* Find desktop coordinates */ sp_desktop_w2d_xy_point (dt, &fp, event->button.x, event->button.y); p.x = fp.x; p.y = fp.y; /* Test, whether we hit any anchor */ anchor = test_inside (dc, event->button.x, event->button.y); switch (pc->mode) { case SP_PEN_CONTEXT_MODE_CLICK: /* In click mode we add point on release */ switch (pc->state) { case SP_PEN_CONTEXT_POINT: case SP_PEN_CONTEXT_CONTROL: case SP_PEN_CONTEXT_CLOSE: break; case SP_PEN_CONTEXT_STOP: /* This is allowed, if we just cancelled curve */ pc->state = SP_PEN_CONTEXT_POINT; break; default: break; } break; case SP_PEN_CONTEXT_MODE_DRAG: switch (pc->state) { case SP_PEN_CONTEXT_STOP: /* This is allowed, if we just cancelled curve */ case SP_PEN_CONTEXT_POINT: if (dc->npoints == 0) { /* Set start anchor */ dc->sa = anchor; if (anchor) { /* Adjust point to anchor if needed */ p = anchor->dp; } else { /* Create green anchor */ dc->green_anchor = sp_draw_anchor_new (dc, dc->green_curve, TRUE, p.x, p.y); } spdc_pen_set_point (pc, &p, event->motion.state); } else { /* Set end anchor */ dc->ea = anchor; spdc_pen_set_point (pc, &p, event->motion.state); if (dc->green_anchor && dc->green_anchor->active) { pc->state = SP_PEN_CONTEXT_CLOSE; ret = TRUE; break; } } pc->state = SP_PEN_CONTEXT_CONTROL; ret = TRUE; break; case SP_PEN_CONTEXT_CONTROL: g_warning ("Button down in CONTROL state"); break; case SP_PEN_CONTEXT_CLOSE: g_warning ("Button down in CLOSE state"); break; default: break; } break; default: break; } } break; case GDK_MOTION_NOTIFY: #if 1 if ((event->motion.state & GDK_BUTTON1_MASK) && !dc->grab) { /* Grab mouse, so release will not pass unnoticed */ dc->grab = SP_CANVAS_ITEM (dt->acetate); sp_canvas_item_grab (dc->grab, SPDC_EVENT_MASK, NULL, event->button.time); } #endif /* Find desktop coordinates */ sp_desktop_w2d_xy_point (dt, &fp, event->motion.x, event->motion.y); p.x = fp.x; p.y = fp.y; /* Test, whether we hit any anchor */ anchor = test_inside (dc, event->button.x, event->button.y); if (!anchor) { /* Snap only if not hitting anchor */ spdc_endpoint_snap (dc, &p, event->motion.state); } switch (pc->mode) { case SP_PEN_CONTEXT_MODE_CLICK: switch (pc->state) { case SP_PEN_CONTEXT_POINT: if (dc->npoints != 0) { /* Only set point, if we are already appending */ /* fixme: Snapping */ spdc_pen_set_point (pc, &p, event->motion.state); ret = TRUE; } break; case SP_PEN_CONTEXT_CONTROL: case SP_PEN_CONTEXT_CLOSE: /* Placing controls is last operation in CLOSE state */ /* fixme: Snapping */ spdc_pen_set_ctrl (pc, &p, event->motion.state); ret = TRUE; break; case SP_PEN_CONTEXT_STOP: /* This is perfectly valid */ break; default: break; } break; case SP_PEN_CONTEXT_MODE_DRAG: switch (pc->state) { case SP_PEN_CONTEXT_POINT: if (dc->npoints > 0) { /* Only set point, if we are already appending */ /* fixme: Snapping */ spdc_pen_set_point (pc, &p, event->motion.state); ret = TRUE; } break; case SP_PEN_CONTEXT_CONTROL: case SP_PEN_CONTEXT_CLOSE: /* Placing controls is last operation in CLOSE state */ /* fixme: Snapping */ spdc_pen_set_ctrl (pc, &p, event->motion.state); ret = TRUE; break; case SP_PEN_CONTEXT_STOP: /* This is perfectly valid */ break; default: break; } break; default: break; } break; case GDK_BUTTON_RELEASE: if (event->button.button == 1) { /* Find desktop coordinates */ sp_desktop_w2d_xy_point (dt, &fp, event->motion.x, event->motion.y); p.x = fp.x; p.y = fp.y; /* Test, whether we hit any anchor */ anchor = test_inside (dc, event->button.x, event->button.y); switch (pc->mode) { case SP_PEN_CONTEXT_MODE_CLICK: switch (pc->state) { case SP_PEN_CONTEXT_POINT: if (dc->npoints == 0) { /* Start new thread only with button release */ if (anchor) { p = anchor->dp; } dc->sa = anchor; spdc_pen_set_point (pc, &p, event->motion.state); } else { /* Set end anchor here */ dc->ea = anchor; } pc->state = SP_PEN_CONTEXT_CONTROL; ret = TRUE; break; case SP_PEN_CONTEXT_CONTROL: /* End current segment */ spdc_pen_finish_segment (pc, &p, event->button.state); pc->state = SP_PEN_CONTEXT_POINT; ret = TRUE; break; case SP_PEN_CONTEXT_CLOSE: /* End current segment */ spdc_pen_finish_segment (pc, &p, event->button.state); spdc_pen_finish (pc, TRUE); pc->state = SP_PEN_CONTEXT_POINT; ret = TRUE; break; case SP_PEN_CONTEXT_STOP: /* This is allowed, if we just cancelled curve */ pc->state = SP_PEN_CONTEXT_POINT; ret = TRUE; break; default: break; } break; case SP_PEN_CONTEXT_MODE_DRAG: switch (pc->state) { case SP_PEN_CONTEXT_POINT: case SP_PEN_CONTEXT_CONTROL: spdc_pen_finish_segment (pc, &p, event->button.state); break; case SP_PEN_CONTEXT_CLOSE: spdc_pen_finish_segment (pc, &p, event->button.state); spdc_pen_finish (pc, TRUE); break; case SP_PEN_CONTEXT_STOP: /* This is allowed, if we just cancelled curve */ break; default: break; } pc->state = SP_PEN_CONTEXT_POINT; ret = TRUE; break; default: break; } #if 1 if (dc->grab) { /* Release grab now */ sp_canvas_item_ungrab (dc->grab, event->button.time); dc->grab = NULL; } #endif ret = TRUE; } break; case GDK_2BUTTON_PRESS: spdc_pen_finish (pc, FALSE); ret = TRUE; break; case GDK_KEY_PRESS: /* fixme: */ switch (event->key.keyval) { case GDK_Return: spdc_pen_finish (pc, FALSE); ret = TRUE; break; case GDK_Escape: pc->state = SP_PEN_CONTEXT_STOP; spdc_reset_colors (dc); sp_canvas_item_hide (pc->c0); sp_canvas_item_hide (pc->c1); sp_canvas_item_hide (pc->cl0); sp_canvas_item_hide (pc->cl1); ret = TRUE; break; case GDK_BackSpace: if (sp_curve_is_empty (dc->green_curve)) { /* Same as cancel */ pc->state = SP_PEN_CONTEXT_STOP; spdc_reset_colors (dc); sp_canvas_item_hide (pc->c0); sp_canvas_item_hide (pc->c1); sp_canvas_item_hide (pc->cl0); sp_canvas_item_hide (pc->cl1); ret = TRUE; break; } else { NRPointF pt; ArtBpath *p; gint e; /* Reset red curve */ sp_curve_reset (dc->red_curve); /* Destroy topmost green bpath */ gtk_object_destroy (GTK_OBJECT (dc->green_bpaths->data)); dc->green_bpaths = g_slist_remove (dc->green_bpaths, dc->green_bpaths->data); /* Get last segment */ p = SP_CURVE_BPATH (dc->green_curve); e = SP_CURVE_LENGTH (dc->green_curve); if (e < 2) { g_warning ("Green curve length is %d", e); break; } dc->p[0].x = p[e - 2].x3; dc->p[0].y = p[e - 2].y3; dc->p[1].x = p[e - 1].x1; dc->p[1].y = p[e - 1].y1; if (dc->npoints < 4) { pt.x = p[e - 1].x3; pt.y = p[e - 1].y3; } else { pt.x = dc->p[3].x; pt.y = dc->p[3].y; } dc->npoints = 2; sp_curve_backspace (dc->green_curve); sp_canvas_item_hide (pc->c0); sp_canvas_item_hide (pc->c1); sp_canvas_item_hide (pc->cl0); sp_canvas_item_hide (pc->cl1); pc->state = SP_PEN_CONTEXT_POINT; spdc_pen_set_point (pc, &pt, event->motion.state); ret = TRUE; } break; default: break; } break; default: break; } if (!ret) { if (((SPEventContextClass *) pen_parent_class)->root_handler) return ((SPEventContextClass *) pen_parent_class)->root_handler (ec, event); } return ret; }