diff options
| author | Ted Gould <ted@gould.cx> | 2010-01-01 04:39:28 +0000 |
|---|---|---|
| committer | Ted Gould <ted@gould.cx> | 2010-01-01 04:39:28 +0000 |
| commit | c22deac174d6b12cf55640f0f5336218cb36defb (patch) | |
| tree | bcb6da5aea8703ee9ee515d6112f3f6d0f6e49f5 /src | |
| parent | Fixing some warnings/errors (diff) | |
| parent | adding 'inductiveload' to AUTHORS (diff) | |
| download | inkscape-c22deac174d6b12cf55640f0f5336218cb36defb.tar.gz inkscape-c22deac174d6b12cf55640f0f5336218cb36defb.zip | |
Keeping up with trunk
(bzr r8254.1.49)
Diffstat (limited to 'src')
58 files changed, 2039 insertions, 2084 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index a064496ce..9d5177519 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -234,20 +234,18 @@ inkview_LDADD = $(all_libs) libinkversion_a_SOURCES = inkscape-version.cpp inkscape-version.h if USE_SVN_VERSION -inkscape_version_deps = $(top_srcdir)/.svn/entries +inkscape_version_deps = $(top_srcdir)/.bzr/branch/last-revision endif -# If this is an SVN snapshot build, regenerate this file every time -# someone updates the SVN working directory. +# If this is an BZR snapshot build, regenerate this file every time +# someone updates the BZR working directory. inkscape-version.cpp: $(inkscape_version_deps) - VER_PREFIX="$(VERSION)"; \ - if test -x "$(srcdir)/.svn" -a ! -z `which svn`; then \ - VER_SVNREV=" r`LANG=en svn info $(srcdir) | sed -n -e '/^Revision:/s/Revision: \(.*\)/\1/p'`"; \ - if test ! -z "`svn status -q $(srcdir)`"; then \ - VER_CUSTOM=" custom"; \ - fi; \ + VER_PREFIX="$(VERSION)";\ + VER_BZRREV=" r`bzr log -r '-1..' | sed -n -e '/^revno:/s/revno: \(.*\)/\1/p'`"; \ + if test ! -z "`bzr status -S -V $(srcdir)`"; then \ + VER_CUSTOM=" custom"; \ fi; \ - VERSION="$$VER_PREFIX$$VER_SVNREV$$VER_CUSTOM"; \ + VERSION="$$VER_PREFIX$$VER_BZRREV$$VER_CUSTOM"; \ echo "namespace Inkscape { " \ "char const *version_string = \"$$VERSION\"; " \ "}" > inkscape-version.new.cpp; \ diff --git a/src/box3d-context.cpp b/src/box3d-context.cpp index e3476deb3..c8fbfa877 100644 --- a/src/box3d-context.cpp +++ b/src/box3d-context.cpp @@ -123,16 +123,17 @@ static void sp_box3d_context_init(Box3DContext *box3d_context) static void sp_box3d_context_finish(SPEventContext *ec) { - Box3DContext *bc = SP_BOX3D_CONTEXT(ec); - SPDesktop *desktop = ec->desktop; + Box3DContext *bc = SP_BOX3D_CONTEXT(ec); + SPDesktop *desktop = ec->desktop; - sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), GDK_CURRENT_TIME); - sp_box3d_finish(bc); + sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), GDK_CURRENT_TIME); + sp_box3d_finish(bc); bc->sel_changed_connection.disconnect(); +// sp_repr_remove_listener_by_data(cc->active_shape_repr, cc); if (((SPEventContextClass *) parent_class)->finish) { - ((SPEventContextClass *) parent_class)->finish(ec); - } + ((SPEventContextClass *) parent_class)->finish(ec); + } } @@ -179,13 +180,14 @@ static void sp_box3d_context_selection_changed(Inkscape::Selection *selection, g if (selection->perspList().size() == 1) { // selecting a single box changes the current perspective - ec->desktop->doc()->current_persp3d = selection->perspList().front(); + ec->desktop->doc()->setCurrentPersp3D(selection->perspList().front()); } } -/* create a default perspective in document defs if none is present - (can happen after 'vacuum defs' or when a pre-0.46 file is opened) */ -static void sp_box3d_context_check_for_persp_in_defs(SPDocument *document) { +/* Create a default perspective in document defs if none is present (which can happen, among other + * circumstances, after 'vacuum defs' or when a pre-0.46 file is opened). + */ +static void sp_box3d_context_ensure_persp_in_defs(SPDocument *document) { SPDefs *defs = (SPDefs *) SP_DOCUMENT_DEFS(document); bool has_persp = false; @@ -197,7 +199,7 @@ static void sp_box3d_context_check_for_persp_in_defs(SPDocument *document) { } if (!has_persp) { - document->current_persp3d = persp3d_create_xml_element (document); + document->setCurrentPersp3D(persp3d_create_xml_element (document)); } } @@ -209,8 +211,6 @@ static void sp_box3d_context_setup(SPEventContext *ec) ((SPEventContextClass *) parent_class)->setup(ec); } - sp_box3d_context_check_for_persp_in_defs(sp_desktop_document (ec->desktop)); - ec->shape_editor = new ShapeEditor(ec->desktop); SPItem *item = sp_desktop_selection(ec->desktop)->singleItem(); @@ -267,13 +267,13 @@ static gint sp_box3d_context_root_handler(SPEventContext *event_context, GdkEven static bool dragging; SPDesktop *desktop = event_context->desktop; + SPDocument *document = sp_desktop_document (desktop); Inkscape::Selection *selection = sp_desktop_selection (desktop); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12); Box3DContext *bc = SP_BOX3D_CONTEXT(event_context); - g_assert (SP_ACTIVE_DOCUMENT->current_persp3d); - Persp3D *cur_persp = SP_ACTIVE_DOCUMENT->current_persp3d; + Persp3D *cur_persp = document->getCurrentPersp3D(); event_context->tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100); @@ -300,8 +300,14 @@ static gint sp_box3d_context_root_handler(SPEventContext *event_context, GdkEven bc->drag_ptB = from_2geom(button_dt); bc->drag_ptC = from_2geom(button_dt); + // This can happen after saving when the last remaining perspective was purged and must be recreated. + if (!cur_persp) { + sp_box3d_context_ensure_persp_in_defs(document); + cur_persp = document->getCurrentPersp3D(); + } + /* Projective preimages of clicked point under current perspective */ - bc->drag_origin_proj = cur_persp->tmat.preimage (from_2geom(button_dt), 0, Proj::Z); + bc->drag_origin_proj = cur_persp->perspective_impl->tmat.preimage (from_2geom(button_dt), 0, Proj::Z); bc->drag_ptB_proj = bc->drag_origin_proj; bc->drag_ptC_proj = bc->drag_origin_proj; bc->drag_ptC_proj.normalize(); @@ -355,7 +361,7 @@ static gint sp_box3d_context_root_handler(SPEventContext *event_context, GdkEven bc->drag_ptB = from_2geom(motion_dt); bc->drag_ptC = from_2geom(motion_dt); - bc->drag_ptB_proj = cur_persp->tmat.preimage (from_2geom(motion_dt), 0, Proj::Z); + bc->drag_ptB_proj = cur_persp->perspective_impl->tmat.preimage (from_2geom(motion_dt), 0, Proj::Z); bc->drag_ptC_proj = bc->drag_ptB_proj; bc->drag_ptC_proj.normalize(); bc->drag_ptC_proj[Proj::Z] = 0.25; @@ -364,16 +370,16 @@ static gint sp_box3d_context_root_handler(SPEventContext *event_context, GdkEven // perspective line from drag_ptB to vanishing point Y. if (!bc->ctrl_dragged) { /* snapping */ - Box3D::PerspectiveLine pline (bc->drag_ptB, Proj::Z, SP_ACTIVE_DOCUMENT->current_persp3d); + Box3D::PerspectiveLine pline (bc->drag_ptB, Proj::Z, document->getCurrentPersp3D()); bc->drag_ptC = pline.closest_to (from_2geom(motion_dt)); bc->drag_ptB_proj.normalize(); - bc->drag_ptC_proj = cur_persp->tmat.preimage (bc->drag_ptC, bc->drag_ptB_proj[Proj::X], Proj::X); + bc->drag_ptC_proj = cur_persp->perspective_impl->tmat.preimage (bc->drag_ptC, bc->drag_ptB_proj[Proj::X], Proj::X); } else { bc->drag_ptC = from_2geom(motion_dt); bc->drag_ptB_proj.normalize(); - bc->drag_ptC_proj = cur_persp->tmat.preimage (from_2geom(motion_dt), bc->drag_ptB_proj[Proj::X], Proj::X); + bc->drag_ptC_proj = cur_persp->perspective_impl->tmat.preimage (from_2geom(motion_dt), bc->drag_ptB_proj[Proj::X], Proj::X); } Geom::Point pt2g = to_2geom(bc->drag_ptC); m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, pt2g, Inkscape::SNAPSOURCE_HANDLE); @@ -424,43 +430,43 @@ static gint sp_box3d_context_root_handler(SPEventContext *event_context, GdkEven break; case GDK_bracketright: - persp3d_rotate_VP (inkscape_active_document()->current_persp3d, Proj::X, -180/snaps, MOD__ALT); - sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_3DBOX, + persp3d_rotate_VP (document->getCurrentPersp3D(), Proj::X, -180/snaps, MOD__ALT); + sp_document_done(document, SP_VERB_CONTEXT_3DBOX, _("Change perspective (angle of PLs)")); ret = true; break; case GDK_bracketleft: - persp3d_rotate_VP (inkscape_active_document()->current_persp3d, Proj::X, 180/snaps, MOD__ALT); - sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_3DBOX, + persp3d_rotate_VP (document->getCurrentPersp3D(), Proj::X, 180/snaps, MOD__ALT); + sp_document_done(document, SP_VERB_CONTEXT_3DBOX, _("Change perspective (angle of PLs)")); ret = true; break; case GDK_parenright: - persp3d_rotate_VP (inkscape_active_document()->current_persp3d, Proj::Y, -180/snaps, MOD__ALT); - sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_3DBOX, + persp3d_rotate_VP (document->getCurrentPersp3D(), Proj::Y, -180/snaps, MOD__ALT); + sp_document_done(document, SP_VERB_CONTEXT_3DBOX, _("Change perspective (angle of PLs)")); ret = true; break; case GDK_parenleft: - persp3d_rotate_VP (inkscape_active_document()->current_persp3d, Proj::Y, 180/snaps, MOD__ALT); - sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_3DBOX, + persp3d_rotate_VP (document->getCurrentPersp3D(), Proj::Y, 180/snaps, MOD__ALT); + sp_document_done(document, SP_VERB_CONTEXT_3DBOX, _("Change perspective (angle of PLs)")); ret = true; break; case GDK_braceright: - persp3d_rotate_VP (inkscape_active_document()->current_persp3d, Proj::Z, -180/snaps, MOD__ALT); - sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_3DBOX, + persp3d_rotate_VP (document->getCurrentPersp3D(), Proj::Z, -180/snaps, MOD__ALT); + sp_document_done(document, SP_VERB_CONTEXT_3DBOX, _("Change perspective (angle of PLs)")); ret = true; break; case GDK_braceleft: - persp3d_rotate_VP (inkscape_active_document()->current_persp3d, Proj::Z, 180/snaps, MOD__ALT); - sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_3DBOX, + persp3d_rotate_VP (document->getCurrentPersp3D(), Proj::Z, 180/snaps, MOD__ALT); + sp_document_done(document, SP_VERB_CONTEXT_3DBOX, _("Change perspective (angle of PLs)")); ret = true; break; @@ -468,7 +474,7 @@ static gint sp_box3d_context_root_handler(SPEventContext *event_context, GdkEven /* TODO: what is this??? case GDK_O: if (MOD__CTRL && MOD__SHIFT) { - Box3D::create_canvas_point(persp3d_get_VP(inkscape_active_document()->current_persp3d, Proj::W).affine(), + Box3D::create_canvas_point(persp3d_get_VP(document()->getCurrentPersp3D(), Proj::W).affine(), 6, 0xff00ff00); } ret = true; @@ -483,6 +489,16 @@ static gint sp_box3d_context_root_handler(SPEventContext *event_context, GdkEven } break; + case GDK_p: + case GDK_P: + if (MOD__SHIFT_ONLY) { + if (document->getCurrentPersp3D()) { + persp3d_print_debugging_info (document->getCurrentPersp3D()); + } + ret = true; + } + break; + case GDK_x: case GDK_X: if (MOD__ALT_ONLY) { @@ -598,7 +614,7 @@ static void sp_box3d_drag(Box3DContext &bc, guint /*state*/) // TODO: It would be nice to show the VPs during dragging, but since there is no selection // at this point (only after finishing the box), we must do this "manually" - /**** bc._vpdrag->updateDraggers(); ****/ + /* bc._vpdrag->updateDraggers(); */ sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 5); } @@ -631,7 +647,7 @@ static void sp_box3d_finish(Box3DContext *bc) if ( bc->item != NULL ) { SPDesktop * desktop = SP_EVENT_CONTEXT_DESKTOP(bc); SPDocument *doc = sp_desktop_document(desktop); - if (!doc || !doc->current_persp3d) + if (!doc || !doc->getCurrentPersp3D()) return; SPBox3D *box = SP_BOX3D(bc->item); diff --git a/src/box3d.cpp b/src/box3d.cpp index 93efa5c35..aa2dc55e3 100644 --- a/src/box3d.cpp +++ b/src/box3d.cpp @@ -127,10 +127,8 @@ box3d_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr) // TODO: Create/link to the correct perspective SPDocument *doc = SP_OBJECT_DOCUMENT(box); - if (!doc) { - g_print ("No document for the box!!!!\n"); + if (!doc) return; - } box->persp_ref->changedSignal().connect(sigc::bind(sigc::ptr_fun(box3d_ref_changed), box)); @@ -150,13 +148,33 @@ box3d_release(SPObject *object) if (box->persp_href) { g_free(box->persp_href); } + + // We have to store this here because the Persp3DReference gets destroyed below, but we need to + // access it to call persp3d_remove_box(), which cannot be called earlier because the reference + // needs to be destroyed first. + Persp3D *persp = box3d_get_perspective(box); + if (box->persp_ref) { box->persp_ref->detach(); delete box->persp_ref; box->persp_ref = NULL; } - //persp3d_remove_box (box3d_get_perspective(box), box); + if (persp) { + persp3d_remove_box (persp, box); + /* + // TODO: This deletes a perspective when the last box referring to it is gone. Eventually, + // it would be nice to have this but currently it crashes when undoing/redoing box deletion + // Reason: When redoing a box deletion, the associated perspective is deleted twice, first + // by the following code and then again by the redo mechanism! Perhaps we should perform + // deletion of the perspective from another location "outside" the undo/redo mechanism? + if (persp->perspective_impl->boxes.empty()) { + SPDocument *doc = SP_OBJECT_DOCUMENT(box); + persp->deleteObject(); + doc->setCurrentPersp3D(persp3d_document_first_persp(doc)); + } + */ + } if (((SPObjectClass *) parent_class)->release) ((SPObjectClass *) parent_class)->release(object); @@ -226,16 +244,10 @@ box3d_ref_changed(SPObject *old_ref, SPObject *ref, SPBox3D *box) if (old_ref) { sp_signal_disconnect_by_data(old_ref, box); persp3d_remove_box (SP_PERSP3D(old_ref), box); - /* Note: This sometimes leads to attempts to remove boxes twice from the list of selected/transformed - boxes in a perspectives, but this should be uncritical. */ - persp3d_remove_box_transform (SP_PERSP3D(old_ref), box); } if ( SP_IS_PERSP3D(ref) && ref != box ) // FIXME: Comparisons sane? { persp3d_add_box (SP_PERSP3D(ref), box); - /* Note: This sometimes leads to attempts to add boxes twice to the list of selected/transformed - boxes in a perspectives, but this should be uncritical. */ - persp3d_add_box_transform (SP_PERSP3D(ref), box); } } @@ -278,7 +290,7 @@ static Inkscape::XML::Node *box3d_write(SPObject *object, Inkscape::XML::Documen repr->setAttribute("inkscape:perspectiveID", uri_string); g_free(uri_string); } else { - Inkscape::XML::Node *persp_repr = SP_OBJECT_REPR(doc->current_persp3d); + Inkscape::XML::Node *persp_repr = SP_OBJECT_REPR(doc->getCurrentPersp3D()); const gchar *persp_id = persp_repr->attribute("id"); gchar *href = g_strdup_printf("#%s", persp_id); repr->setAttribute("inkscape:perspectiveID", href); @@ -331,37 +343,8 @@ box3d_set_transform(SPItem *item, Geom::Matrix const &xform) { SPBox3D *box = SP_BOX3D(item); - /* check whether we need to unlink any boxes from their perspectives */ - Persp3D *persp = box3d_get_perspective(box); - Persp3D *transf_persp; - - if (sp_desktop_document(inkscape_active_desktop()) == SP_OBJECT_DOCUMENT(item) && - !persp3d_has_all_boxes_in_selection (persp)) { - std::list<SPBox3D *> selboxes = sp_desktop_selection(inkscape_active_desktop())->box3DList(); - - /* create a new perspective as a copy of the current one and link the selected boxes to it */ - transf_persp = persp3d_create_xml_element (SP_OBJECT_DOCUMENT(persp), persp); - - for (std::list<SPBox3D *>::iterator b = selboxes.begin(); b != selboxes.end(); ++b) { - box3d_switch_perspectives(*b, persp, transf_persp); - } - } else { - transf_persp = persp; - } - - /* only transform the perspective once, even if it has several selected boxes */ - if(!persp3d_was_transformed (transf_persp)) { - /* concatenate the affine transformation with the perspective mapping; this - function also triggers repr updates of boxes and the perspective itself */ - persp3d_apply_affine_transformation(transf_persp, xform); - } - - box3d_mark_transformed(box); - - if (persp3d_all_transformed(transf_persp)) { - /* all boxes were transformed; make perspective sensitive for further transformations */ - persp3d_unset_transforms(transf_persp); - } + // We don't apply the transform to the box directly but instead to its perspective (which is + // done in sp_selection_apply_affine). Here we only adjust strokes, patterns, etc. Geom::Matrix ret(Geom::Matrix(xform).without_translation()); gdouble const sw = hypot(ret[0], ret[1]); @@ -412,9 +395,9 @@ box3d_get_corner_screen (SPBox3D const *box, guint id, bool item_coords) { } Geom::Matrix const i2d (sp_item_i2d_affine (SP_ITEM(box))); if (item_coords) { - return box3d_get_perspective(box)->tmat.image(proj_corner).affine() * i2d.inverse(); + return box3d_get_perspective(box)->perspective_impl->tmat.image(proj_corner).affine() * i2d.inverse(); } else { - return box3d_get_perspective(box)->tmat.image(proj_corner).affine(); + return box3d_get_perspective(box)->perspective_impl->tmat.image(proj_corner).affine(); } } @@ -435,7 +418,7 @@ box3d_get_center_screen (SPBox3D *box) { return Geom::Point (NR_HUGE, NR_HUGE); } Geom::Matrix const i2d (sp_item_i2d_affine (SP_ITEM(box))); - return box3d_get_perspective(box)->tmat.image(proj_center).affine() * i2d.inverse(); + return box3d_get_perspective(box)->perspective_impl->tmat.image(proj_center).affine() * i2d.inverse(); } /* @@ -461,13 +444,13 @@ box3d_snap (SPBox3D *box, int id, Proj::Pt3 const &pt_proj, Proj::Pt3 const &sta Proj::Pt3 D_proj (x_coord, y_coord + diff_y, z_coord, 1.0); Proj::Pt3 E_proj (x_coord - diff_x, y_coord + diff_y, z_coord, 1.0); - Persp3D *persp = box3d_get_perspective(box); - Geom::Point A = persp->tmat.image(A_proj).affine(); - Geom::Point B = persp->tmat.image(B_proj).affine(); - Geom::Point C = persp->tmat.image(C_proj).affine(); - Geom::Point D = persp->tmat.image(D_proj).affine(); - Geom::Point E = persp->tmat.image(E_proj).affine(); - Geom::Point pt = persp->tmat.image(pt_proj).affine(); + Persp3DImpl *persp_impl = box3d_get_perspective(box)->perspective_impl; + Geom::Point A = persp_impl->tmat.image(A_proj).affine(); + Geom::Point B = persp_impl->tmat.image(B_proj).affine(); + Geom::Point C = persp_impl->tmat.image(C_proj).affine(); + Geom::Point D = persp_impl->tmat.image(D_proj).affine(); + Geom::Point E = persp_impl->tmat.image(E_proj).affine(); + Geom::Point pt = persp_impl->tmat.image(pt_proj).affine(); // TODO: Replace these lines between corners with lines from a corner to a vanishing point // (this might help to prevent rounding errors if the box is small) @@ -523,7 +506,7 @@ box3d_snap (SPBox3D *box, int id, Proj::Pt3 const &pt_proj, Proj::Pt3 const &sta remember_snap_index = snap_index; result = snap_pts[snap_index]; } - return box3d_get_perspective(box)->tmat.preimage (result, z_coord, Proj::Z); + return box3d_get_perspective(box)->perspective_impl->tmat.preimage (result, z_coord, Proj::Z); } void @@ -535,8 +518,9 @@ box3d_set_corner (SPBox3D *box, const guint id, Geom::Point const &new_pos, cons /* update corners 0 and 7 according to which handle was moved and to the axes of movement */ if (!(movement & Box3D::Z)) { - Proj::Pt3 pt_proj (box3d_get_perspective(box)->tmat.preimage (new_pos, (id < 4) ? box->orig_corner0[Proj::Z] : - box->orig_corner7[Proj::Z], Proj::Z)); + Persp3DImpl *persp_impl = box3d_get_perspective(box)->perspective_impl; + Proj::Pt3 pt_proj (persp_impl->tmat.preimage (new_pos, (id < 4) ? box->orig_corner0[Proj::Z] : + box->orig_corner7[Proj::Z], Proj::Z)); if (constrained) { pt_proj = box3d_snap (box, id, pt_proj, box3d_get_proj_corner (id, box->save_corner0, box->save_corner7)); } @@ -553,13 +537,14 @@ box3d_set_corner (SPBox3D *box, const guint id, Geom::Point const &new_pos, cons 1.0); } else { Persp3D *persp = box3d_get_perspective(box); - Box3D::PerspectiveLine pl(persp->tmat.image( + Persp3DImpl *persp_impl = box3d_get_perspective(box)->perspective_impl; + Box3D::PerspectiveLine pl(persp_impl->tmat.image( box3d_get_proj_corner (id, box->save_corner0, box->save_corner7)).affine(), Proj::Z, persp); Geom::Point new_pos_snapped(pl.closest_to(new_pos)); - Proj::Pt3 pt_proj (persp->tmat.preimage (new_pos_snapped, - box3d_get_proj_corner (box, id)[(movement & Box3D::Y) ? Proj::X : Proj::Y], - (movement & Box3D::Y) ? Proj::X : Proj::Y)); + Proj::Pt3 pt_proj (persp_impl->tmat.preimage (new_pos_snapped, + box3d_get_proj_corner (box, id)[(movement & Box3D::Y) ? Proj::X : Proj::Y], + (movement & Box3D::Y) ? Proj::X : Proj::Y)); bool corner0_move_x = !(id & Box3D::X) && (movement & Box3D::X); bool corner0_move_y = !(id & Box3D::Y) && (movement & Box3D::Y); bool corner7_move_x = (id & Box3D::X) && (movement & Box3D::X); @@ -590,9 +575,9 @@ void box3d_set_center (SPBox3D *box, Geom::Point const &new_pos, Geom::Point con double radx = (box->orig_corner7[Proj::X] - box->orig_corner0[Proj::X]) / 2; double rady = (box->orig_corner7[Proj::Y] - box->orig_corner0[Proj::Y]) / 2; - Proj::Pt3 pt_proj (persp->tmat.preimage (new_pos, coord, Proj::Z)); + Proj::Pt3 pt_proj (persp->perspective_impl->tmat.preimage (new_pos, coord, Proj::Z)); if (constrained) { - Proj::Pt3 old_pos_proj (persp->tmat.preimage (old_pos, coord, Proj::Z)); + Proj::Pt3 old_pos_proj (persp->perspective_impl->tmat.preimage (old_pos, coord, Proj::Z)); old_pos_proj.normalize(); pt_proj = box3d_snap (box, -1, pt_proj, old_pos_proj); } @@ -612,7 +597,7 @@ void box3d_set_center (SPBox3D *box, Geom::Point const &new_pos, Geom::Point con Box3D::PerspectiveLine pl(old_pos, Proj::Z, persp); Geom::Point new_pos_snapped(pl.closest_to(new_pos)); - Proj::Pt3 pt_proj (persp->tmat.preimage (new_pos_snapped, coord, Proj::X)); + Proj::Pt3 pt_proj (persp->perspective_impl->tmat.preimage (new_pos_snapped, coord, Proj::X)); /* normalizing pt_proj is essential because we want to mingle affine coordinates */ pt_proj.normalize(); @@ -636,6 +621,7 @@ void box3d_corners_for_PLs (const SPBox3D * box, Proj::Axis axis, { Persp3D *persp = box3d_get_perspective(box); g_return_if_fail (persp); + Persp3DImpl *persp_impl = persp->perspective_impl; //box->orig_corner0.normalize(); //box->orig_corner7.normalize(); double coord = (box->orig_corner0[axis] > box->orig_corner7[axis]) ? @@ -666,10 +652,10 @@ void box3d_corners_for_PLs (const SPBox3D * box, Proj::Axis axis, default: return; } - corner1 = persp->tmat.image(c1).affine(); - corner2 = persp->tmat.image(c2).affine(); - corner3 = persp->tmat.image(c3).affine(); - corner4 = persp->tmat.image(c4).affine(); + corner1 = persp_impl->tmat.image(c1).affine(); + corner2 = persp_impl->tmat.image(c2).affine(); + corner3 = persp_impl->tmat.image(c3).affine(); + corner4 = persp_impl->tmat.image(c4).affine(); } /* Auxiliary function: Checks whether the half-line from A to B crosses the line segment joining C and D */ @@ -1027,7 +1013,7 @@ box3d_recompute_z_orders (SPBox3D *box) { Geom::Point dirs[3]; for (int i = 0; i < 3; ++i) { dirs[i] = persp3d_get_PL_dir_from_pt(persp, c3, Box3D::toProj(Box3D::axes[i])); - if (persp3d_VP_is_finite(persp, Proj::axes[i])) { + if (persp3d_VP_is_finite(persp->perspective_impl, Proj::axes[i])) { num_finite++; axis_finite = Box3D::axes[i]; } else { @@ -1212,7 +1198,7 @@ box3d_pt_lies_in_PL_sector (SPBox3D const *box, Geom::Point const &pt, int id1, Geom::Point c2(box3d_get_corner_screen(box, id2, false)); int ret = 0; - if (persp3d_VP_is_finite(persp, Box3D::toProj(axis))) { + if (persp3d_VP_is_finite(persp->perspective_impl, Box3D::toProj(axis))) { Geom::Point vp(persp3d_get_VP(persp, Box3D::toProj(axis)).affine()); Geom::Point v1(c1 - vp); Geom::Point v2(c2 - vp); @@ -1239,7 +1225,7 @@ int box3d_VP_lies_in_PL_sector (SPBox3D const *box, Proj::Axis vpdir, int id1, int id2, Box3D::Axis axis) { Persp3D *persp = box3d_get_perspective(box); - if (!persp3d_VP_is_finite(persp, vpdir)) { + if (!persp3d_VP_is_finite(persp->perspective_impl, vpdir)) { return 0; } else { return box3d_pt_lies_in_PL_sector(box, persp3d_get_VP(persp, vpdir).affine(), id1, id2, axis); @@ -1302,31 +1288,6 @@ box3d_check_for_swapped_coords(SPBox3D *box) { box3d_exchange_coords(box); } -void -box3d_add_to_selection(SPBox3D *box) { - Persp3D *persp = box3d_get_perspective(box); - g_return_if_fail(persp); - persp3d_add_box_transform(persp, box); -} - -void -box3d_remove_from_selection(SPBox3D *box) { - Persp3D *persp = box3d_get_perspective(box); - if (!persp) { - /* this can happen if a box is deleted through undo and the persp_ref is already detached; - should we rebuild the boxes of each perspective in this case or is it safe to leave it alone? */ - return; - } - persp3d_remove_box_transform(persp, box); -} - -void -box3d_mark_transformed(SPBox3D *box) { - Persp3D *persp = box3d_get_perspective(box); - g_return_if_fail(persp); - persp3d_set_box_transformed(persp, box, true); -} - static void box3d_extract_boxes_rec(SPObject *obj, std::list<SPBox3D *> &boxes) { if (SP_IS_BOX3D(obj)) { @@ -1360,16 +1321,13 @@ box3d_switch_perspectives(SPBox3D *box, Persp3D *old_persp, Persp3D *new_persp, Geom::Point corner0_screen = box3d_get_corner_screen(box, 0, false); Geom::Point corner7_screen = box3d_get_corner_screen(box, 7, false); - box->orig_corner0 = new_persp->tmat.preimage(corner0_screen, z0, Proj::Z); - box->orig_corner7 = new_persp->tmat.preimage(corner7_screen, z7, Proj::Z); + box->orig_corner0 = new_persp->perspective_impl->tmat.preimage(corner0_screen, z0, Proj::Z); + box->orig_corner7 = new_persp->perspective_impl->tmat.preimage(corner7_screen, z7, Proj::Z); } persp3d_remove_box (old_persp, box); persp3d_add_box (new_persp, box); - persp3d_remove_box_transform (old_persp, box); - persp3d_add_box_transform (new_persp, box); - gchar *href = g_strdup_printf("#%s", SP_OBJECT_REPR(new_persp)->attribute("id")); SP_OBJECT_REPR(box)->setAttribute("inkscape:perspectiveID", href); g_free(href); diff --git a/src/box3d.h b/src/box3d.h index b6d962a3c..9f2e1d78e 100644 --- a/src/box3d.h +++ b/src/box3d.h @@ -70,10 +70,6 @@ int box3d_VP_lies_in_PL_sector (SPBox3D const *box, Proj::Axis vpdir, int id1, i void box3d_relabel_corners(SPBox3D *box); void box3d_check_for_swapped_coords(SPBox3D *box); -void box3d_add_to_selection(SPBox3D *box); -void box3d_remove_from_selection(SPBox3D *box); -void box3d_mark_transformed(SPBox3D *box); - std::list<SPBox3D *> box3d_extract_boxes(SPObject *obj); Persp3D *box3d_get_perspective(SPBox3D const *box); diff --git a/src/conn-avoid-ref.cpp b/src/conn-avoid-ref.cpp index b9dc218b4..b2aa0ce6b 100644 --- a/src/conn-avoid-ref.cpp +++ b/src/conn-avoid-ref.cpp @@ -19,6 +19,7 @@ #include "2geom/line.h" #include "2geom/crossing.h" #include "2geom/convex-cover.h" +#include "helper/geom-curves.h" #include "svg/stringstream.h" #include "conn-avoid-ref.h" #include "connection-points.h" @@ -32,6 +33,7 @@ #include "desktop.h" #include "desktop-handles.h" #include "sp-namedview.h" +#include "sp-item-group.h" #include "inkscape.h" #include <glibmm/i18n.h> @@ -232,7 +234,7 @@ void SPAvoidRef::addConnectionPoint(ConnectionPoint &cp) else ostr<<'|'<<cp; - this->setConnectionPointsAttrUndoable( ostr.str().c_str(), _("Added a new connection point") ); + this->setConnectionPointsAttrUndoable( ostr.str().c_str(), _("Add a new connection point") ); } void SPAvoidRef::updateConnectionPoint(ConnectionPoint &cp) @@ -257,7 +259,7 @@ void SPAvoidRef::updateConnectionPoint(ConnectionPoint &cp) else ostr<<'|'<<*to_write; } - this->setConnectionPointsAttrUndoable( ostr.str().c_str(), _("Moved a connection point") ); + this->setConnectionPointsAttrUndoable( ostr.str().c_str(), _("Move a connection point") ); } } @@ -277,7 +279,7 @@ void SPAvoidRef::deleteConnectionPoint(ConnectionPoint &cp) } } } - this->setConnectionPointsAttrUndoable( ostr.str().c_str(), _("Removed a connection point") ); + this->setConnectionPointsAttrUndoable( ostr.str().c_str(), _("Remove a connection point") ); } } @@ -424,134 +426,123 @@ bool SPAvoidRef::isValidConnPointId( const int type, const int id ) return true; } -static Avoid::Polygon avoid_item_poly(SPItem const *item) +static std::vector<Geom::Point> approxCurveWithPoints(SPCurve *curve) { - SPDesktop *desktop = inkscape_active_desktop(); - g_assert(desktop != NULL); - - // TODO: The right way to do this is to return the convex hull of - // the object, or an approximation in the case of a rounded - // object. Specific SPItems will need to have a new - // function that returns points for the convex hull. - // For some objects it is enough to feed the snappoints to - // some convex hull code, though not NR::ConvexHull as this - // only keeps the bounding box of the convex hull currently. - - double spacing = desktop->namedview->connector_spacing; - - // [sommer] If item is a shape, use an approximation of its convex hull + // The number of segments to use for not straight curves approximation + const unsigned NUM_SEGS = 4; + + const Geom::PathVector& curve_pv = curve->get_pathvector(); + + // The structure to hold the output + std::vector<Geom::Point> poly_points; + + // Iterate over all curves, adding the endpoints for linear curves and + // sampling the other curves + double seg_size = 1.0 / NUM_SEGS; + double at; + at = 0; + Geom::PathVector::const_iterator pit = curve_pv.begin(); + while (pit != curve_pv.end()) { - // MJW: Disable this for the moment. It still has some issues. - const bool convex_hull_approximation_enabled = false; - - if ( convex_hull_approximation_enabled && SP_IS_SHAPE (item) ) { - // The number of points to use for approximation - const unsigned NUM_POINTS = 64; - -// printf("[sommer] is a shape\n"); - SPCurve* curve = sp_shape_get_curve (SP_SHAPE (item)); - if (curve) { -// printf("[sommer] is a curve\n"); - - // apply all transformations - Geom::Matrix itd_mat = sp_item_i2doc_affine(item); - curve->transform(itd_mat); - - // iterate over all paths - const Geom::PathVector& curve_pv = curve->get_pathvector(); - std::vector<Geom::Point> hull_points; - for (Geom::PathVector::const_iterator i = curve_pv.begin(); i != curve_pv.end(); i++) { - const Geom::Path& curve_pv_path = *i; -// printf("[sommer] tracing sub-path\n"); - - // FIXME: enlarge path by "desktop->namedview->connector_spacing" (using sp_selected_path_do_offset)? - - // use appropriate fraction of points for this path (first one gets any remainder) - unsigned num_points = NUM_POINTS / curve_pv.size(); - if (i == curve_pv.begin()) num_points += NUM_POINTS - (num_points * curve_pv.size()); - printf("[sommer] using %d points for this path\n", num_points); - - // sample points along the path for approximation of convex hull - for (unsigned n = 0; n < num_points; n++) { - double at = curve_pv_path.size() / static_cast<double>(num_points) * n; - Geom::Point pt = curve_pv_path.pointAt(at); - hull_points.push_back(pt); - } - } - - curve->unref(); - - // create convex hull from all sampled points - Geom::ConvexHull hull(hull_points); - - // store expanded convex hull in Avoid::Polygn - unsigned n = 0; - Avoid::Polygon poly; -/* - const Geom::Point& old_pt = *hull.boundary.begin(); -*/ - - Geom::Line hull_edge(*hull.boundary.begin(), *(hull.boundary.begin()+1)); - Geom::Line parallel_hull_edge; - parallel_hull_edge.origin(hull_edge.origin()+hull_edge.versor().ccw()*spacing); - parallel_hull_edge.versor(hull_edge.versor()); - Geom::Line bisector = Geom::make_angle_bisector_line( *(hull.boundary.end()), *hull.boundary.begin(), - *(hull.boundary.begin()+1)); - Geom::OptCrossing int_pt = Geom::intersection(parallel_hull_edge, bisector); - - if (int_pt) + Geom::Path::const_iterator cit = pit->begin(); + while (cit != pit->end()) + if (dynamic_cast<Geom::CubicBezier const*>(&*cit)) + { + at += seg_size; + if (at <= 1.0 ) + poly_points.push_back(cit->pointAt(at)); + else { - Avoid::Point avoid_pt((parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::X], - (parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::Y]); -// printf("[sommer] %f, %f\n", old_pt[Geom::X], old_pt[Geom::Y]); -/* printf("[sommer] %f, %f\n", (parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::X], - (parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::Y]);*/ - poly.ps.push_back(avoid_pt); - } - for (std::vector<Geom::Point>::const_iterator i = hull.boundary.begin() + 1; i != hull.boundary.end(); i++, n++) { -/* - const Geom::Point& old_pt = *i; -*/ - Geom::Line hull_edge(*i, *(i+1)); - Geom::Line parallel_hull_edge; - parallel_hull_edge.origin(hull_edge.origin()+hull_edge.versor().ccw()*spacing); - parallel_hull_edge.versor(hull_edge.versor()); - Geom::Line bisector = Geom::make_angle_bisector_line( *(i-1), *i, *(i+1)); - Geom::OptCrossing intersect_pt = Geom::intersection(parallel_hull_edge, bisector); - - if (int_pt) - { - Avoid::Point avoid_pt((parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::X], - (parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::Y]); -/* printf("[sommer] %f, %f\n", old_pt[Geom::X], old_pt[Geom::Y]); - printf("[sommer] %f, %f\n", (parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::X], - (parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::Y]);*/ - poly.ps.push_back(avoid_pt); - } + at = 0.0; + ++cit; } + } + else + { + poly_points.push_back(cit->finalPoint()); + ++cit; + } + ++pit; + } + return poly_points; +} +static std::vector<Geom::Point> approxItemWithPoints(SPItem const *item, const Geom::Matrix& item_transform) +{ + // The structure to hold the output + std::vector<Geom::Point> poly_points; - return poly; - }// else printf("[sommer] is no curve\n"); - }// else printf("[sommer] is no shape\n"); + if (SP_IS_GROUP(item)) + { + SPGroup* group = SP_GROUP(item); + // consider all first-order children + for (GSList const* i = sp_item_group_item_list(group); i != NULL; i = i->next) { + SPItem* child_item = SP_ITEM(i->data); + std::vector<Geom::Point> child_points = approxItemWithPoints(child_item, item_transform * child_item->transform); + poly_points.insert(poly_points.end(), child_points.begin(), child_points.end()); + } } - - Geom::OptRect rHull = item->getBounds(sp_item_i2doc_affine(item)); - if (!rHull) { - return Avoid::Polygon(); + else if (SP_IS_SHAPE(item)) + { + SPCurve* item_curve = sp_shape_get_curve(SP_SHAPE(item)); + // make sure it has an associated curve + if (item_curve) + { + // apply transformations (up to common ancestor) + item_curve->transform(item_transform); + std::vector<Geom::Point> curve_points = approxCurveWithPoints(item_curve); + poly_points.insert(poly_points.end(), curve_points.begin(), curve_points.end()); + item_curve->unref(); + } } - // Add a little buffer around the edge of each object. - Geom::Rect rExpandedHull = *rHull; - rExpandedHull.expandBy(spacing); - Avoid::Polygon poly(4); + return poly_points; +} +static Avoid::Polygon avoid_item_poly(SPItem const *item) +{ + SPDesktop *desktop = inkscape_active_desktop(); + g_assert(desktop != NULL); + double spacing = desktop->namedview->connector_spacing; + + Geom::Matrix itd_mat = sp_item_i2doc_affine(item); + std::vector<Geom::Point> hull_points; + hull_points = approxItemWithPoints(item, itd_mat); - for (size_t n = 0; n < 4; ++n) { - Geom::Point hullPoint = rExpandedHull.corner(n); - poly.ps[n].x = hullPoint[Geom::X]; - poly.ps[n].y = hullPoint[Geom::Y]; - } + // create convex hull from all sampled points + Geom::ConvexHull hull(hull_points); + + // enlarge path by "desktop->namedview->connector_spacing" + // store expanded convex hull in Avoid::Polygn + Avoid::Polygon poly; + Geom::Line hull_edge(hull[-1], hull[0]); + Geom::Line prev_parallel_hull_edge; + prev_parallel_hull_edge.origin(hull_edge.origin()+hull_edge.versor().ccw()*spacing); + prev_parallel_hull_edge.versor(hull_edge.versor()); + int hull_size = hull.boundary.size(); + for (int i = 0; i <= hull_size; ++i) + { + hull_edge.setBy2Points(hull[i], hull[i+1]); + Geom::Line parallel_hull_edge; + parallel_hull_edge.origin(hull_edge.origin()+hull_edge.versor().ccw()*spacing); + parallel_hull_edge.versor(hull_edge.versor()); + + // determine the intersection point + + Geom::OptCrossing int_pt = Geom::intersection(parallel_hull_edge, prev_parallel_hull_edge); + if (int_pt) + { + Avoid::Point avoid_pt((parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::X], + (parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::Y]); + poly.ps.push_back(avoid_pt); + } + else + { + // something went wrong... + std::cout<<"conn-avoid-ref.cpp: avoid_item_poly: Geom:intersection failed."<<std::endl; + } + prev_parallel_hull_edge = parallel_hull_edge; + } return poly; } diff --git a/src/connector-context.cpp b/src/connector-context.cpp index 307d59d1f..0fc9de9d0 100644 --- a/src/connector-context.cpp +++ b/src/connector-context.cpp @@ -156,8 +156,8 @@ #include "connector-context.h" #include "pixmaps/cursor-connector.xpm" #include "pixmaps/cursor-node.xpm" -#include "pixmaps/cursor-node-m.xpm" -#include "pixmaps/cursor-node-d.xpm" +//#include "pixmaps/cursor-node-m.xpm" +//#include "pixmaps/cursor-node-d.xpm" #include "xml/node-event-vector.h" #include "xml/repr.h" #include "svg/svg.h" @@ -625,7 +625,6 @@ sp_connector_context_item_handler(SPEventContext *event_context, SPItem *item, G { spcc_reset_colors(cc); cc->state = SP_CONNECTOR_CONTEXT_IDLE; -// sp_event_context_snap_window_closed(event_context); } if (cc->state != SP_CONNECTOR_CONTEXT_IDLE) { // Doing something else like rerouting. @@ -757,7 +756,7 @@ connector_handle_button_press(SPConnectorContext *const cc, GdkEventButton const switch (cc->state) { case SP_CONNECTOR_CONTEXT_STOP: - /* This is allowed, if we just cancelled curve */ + /* This is allowed, if we just canceled curve */ case SP_CONNECTOR_CONTEXT_IDLE: { if ( cc->npoints == 0 ) { @@ -772,8 +771,6 @@ connector_handle_button_press(SPConnectorContext *const cc, GdkEventButton const // Test whether we clicked on a connection point cc->sid = conn_pt_handle_test(cc, p); -// sp_event_context_snap_window_open(event_context); - if (!cc->sid) { // This is the first point, so just snap it to the grid // as there's no other points to go off. @@ -800,7 +797,6 @@ connector_handle_button_press(SPConnectorContext *const cc, GdkEventButton const } cc_set_active_conn(cc, cc->newconn); cc->state = SP_CONNECTOR_CONTEXT_IDLE; -// sp_event_context_snap_window_closed(event_context); ret = TRUE; break; } @@ -819,7 +815,6 @@ connector_handle_button_press(SPConnectorContext *const cc, GdkEventButton const cc_connector_rerouting_finish(cc, &p); cc->state = SP_CONNECTOR_CONTEXT_IDLE; -// sp_event_context_snap_window_closed(event_context); // Don't set ret to TRUE, so we drop through to the // parent handler which will open the context menu. @@ -827,7 +822,6 @@ connector_handle_button_press(SPConnectorContext *const cc, GdkEventButton const else if (cc->npoints != 0) { spcc_connector_finish(cc); cc->state = SP_CONNECTOR_CONTEXT_IDLE; -// sp_event_context_snap_window_closed(event_context); ret = TRUE; } } @@ -879,7 +873,6 @@ connector_handle_button_press(SPConnectorContext *const cc, GdkEventButton const if ( cc->selected_handle ) { -// sp_event_context_snap_window_open(event_context); cc->state = SP_CONNECTOR_CONTEXT_DRAGGING; cc->selection->set( SP_OBJECT( cc->active_shape ) ); } @@ -1047,7 +1040,6 @@ connector_handle_button_release(SPConnectorContext *const cc, GdkEventButton con } cc_set_active_conn(cc, cc->newconn); cc->state = SP_CONNECTOR_CONTEXT_IDLE; -// sp_event_context_snap_window_closed(event_context); break; } case SP_CONNECTOR_CONTEXT_REROUTING: @@ -1057,7 +1049,6 @@ connector_handle_button_release(SPConnectorContext *const cc, GdkEventButton con sp_document_ensure_up_to_date(doc); cc->state = SP_CONNECTOR_CONTEXT_IDLE; -// sp_event_context_snap_window_closed(event_context); return TRUE; break; } @@ -1077,10 +1068,8 @@ connector_handle_button_release(SPConnectorContext *const cc, GdkEventButton con if (!cc->within_tolerance) { -// sp_event_context_snap_window_open(event_context); m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE); sp_knot_set_position(cc->selected_handle, p, 0); -// sp_event_context_snap_window_closed(event_context); ConnectionPoint& cp = cc->connpthandles[cc->selected_handle]; cp.pos = p * sp_item_dt2i_affine(cc->active_shape); cc->active_shape->avoidRef->updateConnectionPoint(cp); @@ -1092,11 +1081,9 @@ connector_handle_button_release(SPConnectorContext *const cc, GdkEventButton con case SP_CONNECTOR_CONTEXT_NEWCONNPOINT: -// sp_event_context_snap_window_open( event_context ); m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE); sp_knot_set_position(cc->selected_handle, p, 0); -// sp_event_context_snap_window_closed(event_context); ConnectionPoint cp; cp.type = ConnPointUserDefined; @@ -1137,7 +1124,6 @@ connector_handle_key_press(SPConnectorContext *const cc, guint const keyval) if (cc->npoints != 0) { spcc_connector_finish(cc); cc->state = SP_CONNECTOR_CONTEXT_IDLE; -// sp_event_context_snap_window_closed(SP_EVENT_CONTEXT(cc)); ret = TRUE; } break; @@ -1152,7 +1138,6 @@ connector_handle_key_press(SPConnectorContext *const cc, guint const keyval) sp_document_undo(doc); cc->state = SP_CONNECTOR_CONTEXT_IDLE; -// sp_event_context_snap_window_closed(SP_EVENT_CONTEXT(cc)); desktop->messageStack()->flash( Inkscape::NORMAL_MESSAGE, _("Connector endpoint drag cancelled.")); ret = TRUE; @@ -1160,7 +1145,6 @@ connector_handle_key_press(SPConnectorContext *const cc, guint const keyval) else if (cc->npoints != 0) { // if drawing, cancel, otherwise pass it up for deselecting cc->state = SP_CONNECTOR_CONTEXT_STOP; -// sp_event_context_snap_window_closed(SP_EVENT_CONTEXT(cc)); spcc_reset_colors(cc); ret = TRUE; } @@ -1200,10 +1184,8 @@ connector_handle_key_press(SPConnectorContext *const cc, guint const keyval) if (!cc->within_tolerance) { -// sp_event_context_snap_window_open(event_context); m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE); sp_knot_set_position(cc->selected_handle, p, 0); -// sp_event_context_snap_window_closed(event_context); ConnectionPoint& cp = cc->connpthandles[cc->selected_handle]; cp.pos = p * sp_item_dt2i_affine(cc->active_shape); cc->active_shape->avoidRef->updateConnectionPoint(cp); @@ -1229,13 +1211,10 @@ connector_handle_key_press(SPConnectorContext *const cc, guint const keyval) SnapManager &m = desktop->namedview->snap_manager; m.setup(desktop); Geom::Point p = cc->selected_handle->pos; -// SPEventContext* event_context = SP_EVENT_CONTEXT( cc ); -// sp_event_context_snap_window_open( event_context ); m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE); sp_knot_set_position(cc->selected_handle, p, 0); -// sp_event_context_snap_window_closed(event_context); ConnectionPoint cp; cp.type = ConnPointUserDefined; @@ -1571,7 +1550,6 @@ endpt_handler(SPKnot */*knot*/, GdkEvent *event, SPConnectorContext *cc) cc->clickedhandle = cc->active_handle; cc_clear_active_conn(cc); cc->state = SP_CONNECTOR_CONTEXT_REROUTING; -// sp_event_context_snap_window_open(SP_EVENT_CONTEXT(cc)); // Disconnect from attached shape unsigned ind = (cc->active_handle == cc->endpt_handle[0]) ? 0 : 1; diff --git a/src/display/canvas-axonomgrid.cpp b/src/display/canvas-axonomgrid.cpp index a92e7cf5b..ee05cd01c 100644 --- a/src/display/canvas-axonomgrid.cpp +++ b/src/display/canvas-axonomgrid.cpp @@ -359,9 +359,9 @@ CanvasAxonomGrid::readRepr() } if ( (value = repr->attribute("snapvisiblegridlinesonly")) ) { - g_assert(snapper != NULL); - snapper->setSnapVisibleOnly(strcmp(value,"false") != 0 && strcmp(value, "0") != 0); - } + g_assert(snapper != NULL); + snapper->setSnapVisibleOnly(strcmp(value,"false") != 0 && strcmp(value, "0") != 0); + } for (GSList *l = canvasitems; l != NULL; l = l->next) { sp_canvas_item_request_update ( SP_CANVAS_ITEM(l->data) ); @@ -671,9 +671,9 @@ CanvasAxonomGridSnapper::CanvasAxonomGridSnapper(CanvasAxonomGrid *grid, SnapMan */ Geom::Coord CanvasAxonomGridSnapper::getSnapperTolerance() const { - SPDesktop const *dt = _snapmanager->getDesktop(); - double const zoom = dt ? dt->current_zoom() : 1; - return _snapmanager->snapprefs.getGridTolerance() / zoom; + SPDesktop const *dt = _snapmanager->getDesktop(); + double const zoom = dt ? dt->current_zoom() : 1; + return _snapmanager->snapprefs.getGridTolerance() / zoom; } bool CanvasAxonomGridSnapper::getSnapperAlwaysSnap() const @@ -694,22 +694,22 @@ CanvasAxonomGridSnapper::_getSnapLines(Geom::Point const &p) const double spacing_v; if (getSnapVisibleOnly()) { - // Only snapping to visible grid lines - spacing_h = grid->spacing_ylines; // this is the spacing of the visible grid lines measured in screen pixels - spacing_v = grid->lyw; // vertical - // convert screen pixels to px - // FIXME: after we switch to snapping dist in screen pixels, this will be unnecessary - SPDesktop const *dt = _snapmanager->getDesktop(); - if (dt) { - spacing_h /= dt->current_zoom(); - spacing_v /= dt->current_zoom(); - } - } else { - // Snapping to any grid line, whether it's visible or not - spacing_h = grid->lengthy /(grid->tan_angle[X] + grid->tan_angle[Z]); - spacing_v = grid->lengthy; - - } + // Only snapping to visible grid lines + spacing_h = grid->spacing_ylines; // this is the spacing of the visible grid lines measured in screen pixels + spacing_v = grid->lyw; // vertical + // convert screen pixels to px + // FIXME: after we switch to snapping dist in screen pixels, this will be unnecessary + SPDesktop const *dt = _snapmanager->getDesktop(); + if (dt) { + spacing_h /= dt->current_zoom(); + spacing_v /= dt->current_zoom(); + } + } else { + // Snapping to any grid line, whether it's visible or not + spacing_h = grid->lengthy /(grid->tan_angle[X] + grid->tan_angle[Z]); + spacing_v = grid->lengthy; + + } // In an axonometric grid, any point will be surrounded by 6 grid lines: // - 2 vertical grid lines, one left and one right from the point @@ -746,18 +746,18 @@ CanvasAxonomGridSnapper::_getSnapLines(Geom::Point const &p) const Geom::Point p_x(0, y_proj_along_x_max); Geom::Line line_x(p_x, p_x + vers_x); Geom::Point p_z(0, y_proj_along_z_max); - Geom::Line line_z(p_z, p_z + vers_z); + Geom::Line line_z(p_z, p_z + vers_z); Geom::OptCrossing inters = Geom::OptCrossing(); // empty by default - try - { - inters = Geom::intersection(line_x, line_z); - } - catch (Geom::InfiniteSolutions e) - { - // We're probably dealing with parallel lines; this is useless! - return s; - } + try + { + inters = Geom::intersection(line_x, line_z); + } + catch (Geom::InfiniteSolutions e) + { + // We're probably dealing with parallel lines; this is useless! + return s; + } // Determine which half of the parallelogram to use bool use_left_half = true; @@ -765,7 +765,7 @@ CanvasAxonomGridSnapper::_getSnapLines(Geom::Point const &p) const if (inters) { Geom::Point inters_pt = line_x.pointAt((*inters).ta); - use_left_half = (p[Geom::X] - grid->origin[Geom::X]) < inters_pt[Geom::X]; + use_left_half = (p[Geom::X] - grid->origin[Geom::X]) < inters_pt[Geom::X]; use_right_half = !use_left_half; } @@ -786,16 +786,16 @@ CanvasAxonomGridSnapper::_getSnapLines(Geom::Point const &p) const return s; } -void CanvasAxonomGridSnapper::_addSnappedLine(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, Geom::Point const normal_to_line, Geom::Point const point_on_line) const +void CanvasAxonomGridSnapper::_addSnappedLine(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, Geom::Point const normal_to_line, Geom::Point const point_on_line) const { - SnappedLine dummy = SnappedLine(snapped_point, snapped_distance, source, Inkscape::SNAPTARGET_GRID, getSnapperTolerance(), getSnapperAlwaysSnap(), normal_to_line, point_on_line); + SnappedLine dummy = SnappedLine(snapped_point, snapped_distance, source, source_num, Inkscape::SNAPTARGET_GRID, getSnapperTolerance(), getSnapperAlwaysSnap(), normal_to_line, point_on_line); sc.grid_lines.push_back(dummy); } -void CanvasAxonomGridSnapper::_addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source) const +void CanvasAxonomGridSnapper::_addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num) const { - SnappedPoint dummy = SnappedPoint(snapped_point, source, Inkscape::SNAPTARGET_GRID, snapped_distance, getSnapperTolerance(), getSnapperAlwaysSnap(), true); - sc.points.push_back(dummy); + SnappedPoint dummy = SnappedPoint(snapped_point, source, source_num, Inkscape::SNAPTARGET_GRID, snapped_distance, getSnapperTolerance(), getSnapperAlwaysSnap(), true); + sc.points.push_back(dummy); } bool CanvasAxonomGridSnapper::ThisSnapperMightSnap() const diff --git a/src/display/canvas-axonomgrid.h b/src/display/canvas-axonomgrid.h index e36804d7c..4b1cd4834 100644 --- a/src/display/canvas-axonomgrid.h +++ b/src/display/canvas-axonomgrid.h @@ -78,8 +78,8 @@ public: private: LineList _getSnapLines(Geom::Point const &p) const; - void _addSnappedLine(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, Geom::Point const normal_to_line, const Geom::Point point_on_line) const; - void _addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source) const; + void _addSnappedLine(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, Geom::Point const normal_to_line, const Geom::Point point_on_line) const; + void _addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num) const; CanvasAxonomGrid *grid; }; diff --git a/src/display/canvas-grid.cpp b/src/display/canvas-grid.cpp index 5037c0375..3532c504a 100644 --- a/src/display/canvas-grid.cpp +++ b/src/display/canvas-grid.cpp @@ -635,9 +635,9 @@ CanvasXYGrid::readRepr() } if ( (value = repr->attribute("snapvisiblegridlinesonly")) ) { - g_assert(snapper != NULL); - snapper->setSnapVisibleOnly(strcmp(value,"false") != 0 && strcmp(value, "0") != 0); - } + g_assert(snapper != NULL); + snapper->setSnapVisibleOnly(strcmp(value,"false") != 0 && strcmp(value, "0") != 0); + } for (GSList *l = canvasitems; l != NULL; l = l->next) { sp_canvas_item_request_update ( SP_CANVAS_ITEM(l->data) ); @@ -972,9 +972,9 @@ CanvasXYGridSnapper::CanvasXYGridSnapper(CanvasXYGrid *grid, SnapManager *sm, Ge */ Geom::Coord CanvasXYGridSnapper::getSnapperTolerance() const { - SPDesktop const *dt = _snapmanager->getDesktop(); - double const zoom = dt ? dt->current_zoom() : 1; - return _snapmanager->snapprefs.getGridTolerance() / zoom; + SPDesktop const *dt = _snapmanager->getDesktop(); + double const zoom = dt ? dt->current_zoom() : 1; + return _snapmanager->snapprefs.getGridTolerance() / zoom; } bool CanvasXYGridSnapper::getSnapperAlwaysSnap() const @@ -993,20 +993,20 @@ CanvasXYGridSnapper::_getSnapLines(Geom::Point const &p) const for (unsigned int i = 0; i < 2; ++i) { - double spacing; - - if (getSnapVisibleOnly()) { - // Only snapping to visible grid lines - spacing = grid->sw[i]; // this is the spacing of the visible grid lines measured in screen pixels - // convert screen pixels to px - // FIXME: after we switch to snapping dist in screen pixels, this will be unnecessary - SPDesktop const *dt = _snapmanager->getDesktop(); - if (dt) { - spacing /= dt->current_zoom(); - } + double spacing; + + if (getSnapVisibleOnly()) { + // Only snapping to visible grid lines + spacing = grid->sw[i]; // this is the spacing of the visible grid lines measured in screen pixels + // convert screen pixels to px + // FIXME: after we switch to snapping dist in screen pixels, this will be unnecessary + SPDesktop const *dt = _snapmanager->getDesktop(); + if (dt) { + spacing /= dt->current_zoom(); + } } else { - // Snapping to any grid line, whether it's visible or not - spacing = grid->spacing[i]; + // Snapping to any grid line, whether it's visible or not + spacing = grid->spacing[i]; } Geom::Coord rounded; @@ -1024,16 +1024,16 @@ CanvasXYGridSnapper::_getSnapLines(Geom::Point const &p) const return s; } -void CanvasXYGridSnapper::_addSnappedLine(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, Geom::Point const normal_to_line, Geom::Point const point_on_line) const +void CanvasXYGridSnapper::_addSnappedLine(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, Geom::Point const normal_to_line, Geom::Point const point_on_line) const { - SnappedLine dummy = SnappedLine(snapped_point, snapped_distance, source, Inkscape::SNAPTARGET_GRID, getSnapperTolerance(), getSnapperAlwaysSnap(), normal_to_line, point_on_line); + SnappedLine dummy = SnappedLine(snapped_point, snapped_distance, source, source_num, Inkscape::SNAPTARGET_GRID, getSnapperTolerance(), getSnapperAlwaysSnap(), normal_to_line, point_on_line); sc.grid_lines.push_back(dummy); } -void CanvasXYGridSnapper::_addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source) const +void CanvasXYGridSnapper::_addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num) const { - SnappedPoint dummy = SnappedPoint(snapped_point, source, Inkscape::SNAPTARGET_GRID, snapped_distance, getSnapperTolerance(), getSnapperAlwaysSnap(), true); - sc.points.push_back(dummy); + SnappedPoint dummy = SnappedPoint(snapped_point, source, source_num, Inkscape::SNAPTARGET_GRID, snapped_distance, getSnapperTolerance(), getSnapperAlwaysSnap(), true); + sc.points.push_back(dummy); } /** diff --git a/src/display/canvas-grid.h b/src/display/canvas-grid.h index 58cfbf735..daf28c15c 100644 --- a/src/display/canvas-grid.h +++ b/src/display/canvas-grid.h @@ -166,8 +166,8 @@ public: private: LineList _getSnapLines(Geom::Point const &p) const; - void _addSnappedLine(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, Geom::Point const normal_to_line, const Geom::Point point_on_line) const; - void _addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source) const; + void _addSnappedLine(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, Geom::Point const normal_to_line, const Geom::Point point_on_line) const; + void _addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num) const; CanvasXYGrid *grid; }; diff --git a/src/display/snap-indicator.cpp b/src/display/snap-indicator.cpp index 20ea7d58c..fdea9cbbf 100644 --- a/src/display/snap-indicator.cpp +++ b/src/display/snap-indicator.cpp @@ -46,11 +46,10 @@ SnapIndicator::set_new_snaptarget(Inkscape::SnappedPoint const p) g_assert(_desktop != NULL); - /* Commented out for now, because this might hide any snapping bug! if (!p.getSnapped()) { - return; // If we haven't snapped, then it is of no use to draw a snapindicator + g_warning("No snapping took place, so no snap target will be displayed"); + return; // If we haven't snapped, then it is of no use to draw a snapindicator } - */ Inkscape::Preferences *prefs = Inkscape::Preferences::get(); bool value = prefs->getBool("/options/snapindicator/value", true); diff --git a/src/document.cpp b/src/document.cpp index a3ad6f7be..3104ade28 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -97,10 +97,9 @@ SPDocument::SPDocument() : rerouting_handler_id(0), profileManager(0), // deferred until after other initialization router(new Avoid::Router(Avoid::PolyLineRouting|Avoid::OrthogonalRouting)), - perspectives(0), - current_persp3d(0), _collection_queue(0), - oldSignalsConnected(false) + oldSignalsConnected(false), + current_persp3d(0) { // Penalise libavoid for choosing paths with needless extra segments. // This results in much better looking orthogonal connector paths. @@ -216,26 +215,43 @@ SPDocument::~SPDocument() { //delete this->_whiteboard_session_manager; } -void SPDocument::add_persp3d (Persp3D * const /*persp*/) -{ - SPDefs *defs = SP_ROOT(this->root)->defs; - for (SPObject *i = sp_object_first_child(SP_OBJECT(defs)); i != NULL; i = SP_OBJECT_NEXT(i) ) { - if (SP_IS_PERSP3D(i)) { - g_print ("Encountered a Persp3D in defs\n"); - } +Persp3D * +SPDocument::getCurrentPersp3D() { + // Check if current_persp3d is still valid + std::vector<Persp3D*> plist; + getPerspectivesInDefs(plist); + for (unsigned int i = 0; i < plist.size(); ++i) { + if (current_persp3d == plist[i]) + return current_persp3d; } - g_print ("Adding Persp3D to defs\n"); - persp3d_create_xml_element (this); + // If not, return the first perspective in defs (which may be NULL of none exists) + current_persp3d = persp3d_document_first_persp (this); + + return current_persp3d; } -void SPDocument::remove_persp3d (Persp3D * const /*persp*/) -{ - // TODO: Delete the repr, maybe perform a check if any boxes are still linked to the perspective. - // Anything else? - g_print ("Please implement deletion of perspectives here.\n"); +Persp3DImpl * +SPDocument::getCurrentPersp3DImpl() { + return current_persp3d_impl; } +void +SPDocument::setCurrentPersp3D(Persp3D * const persp) { + current_persp3d = persp; + //current_persp3d_impl = persp->perspective_impl; +} + +void +SPDocument::getPerspectivesInDefs(std::vector<Persp3D*> &list) { + SPDefs *defs = SP_ROOT(this->root)->defs; + for (SPObject *i = sp_object_first_child(SP_OBJECT(defs)); i != NULL; i = SP_OBJECT_NEXT(i) ) { + if (SP_IS_PERSP3D(i)) + list.push_back(SP_PERSP3D(i)); + } +} + +/** void SPDocument::initialize_current_persp3d() { this->current_persp3d = persp3d_document_first_persp(this); @@ -243,6 +259,7 @@ void SPDocument::initialize_current_persp3d() this->current_persp3d = persp3d_create_xml_element(this); } } +**/ unsigned long SPDocument::serial() const { return priv->serial; @@ -390,10 +407,14 @@ sp_document_create(Inkscape::XML::Document *rdoc, inkscape_ref(); } - // Remark: Here, we used to create a "currentpersp3d" element in the document defs. - // But this is probably a bad idea since we need to adapt it for every change of selection, which will - // completely clutter the undo history. Maybe rather save it to prefs on exit and re-read it on startup? - document->initialize_current_persp3d(); + // Check if the document already has a perspective (e.g., when opening an existing + // document). If not, create a new one and set it as the current perspective. + document->setCurrentPersp3D(persp3d_document_first_persp(document)); + if (!document->getCurrentPersp3D()) { + //document->setCurrentPersp3D(persp3d_create_xml_element (document)); + Persp3DImpl *persp_impl = new Persp3DImpl(); + document->setCurrentPersp3DImpl(persp_impl); + } sp_document_set_undo_sensitive(document, true); @@ -746,11 +767,13 @@ SPDocument::emitReconstructionFinish(void) { // printf("Finishing Reconstruction\n"); priv->_reconstruction_finish_signal.emit(); - + +/** // Reference to the old persp3d object is invalid after reconstruction. initialize_current_persp3d(); return; +**/ } sigc::connection SPDocument::connectCommit(SPDocument::CommitSignal::slot_type slot) diff --git a/src/document.h b/src/document.h index 06174c265..e83c37a96 100644 --- a/src/document.h +++ b/src/document.h @@ -55,6 +55,7 @@ namespace Inkscape { class SP3DBox; class Persp3D; +class Persp3DImpl; namespace Proj { class TransfMat3x4; @@ -107,17 +108,26 @@ struct SPDocument : public Inkscape::GC::Managed<>, // Instance of the connector router Avoid::Router *router; - GSList *perspectives; - - Persp3D *current_persp3d; // "currently active" perspective (e.g., newly created boxes are attached to this one) - GSList *_collection_queue; bool oldSignalsConnected; - void add_persp3d(Persp3D * const persp); - void remove_persp3d(Persp3D * const persp); - void initialize_current_persp3d(); + void setCurrentPersp3D(Persp3D * const persp); + inline void setCurrentPersp3DImpl(Persp3DImpl * const persp_impl) { current_persp3d_impl = persp_impl; } + /* + * getCurrentPersp3D returns current_persp3d (if non-NULL) or the first + * perspective in the defs. If no perspective exists, returns NULL. + */ + Persp3D * getCurrentPersp3D(); + Persp3DImpl * getCurrentPersp3DImpl(); + void getPerspectivesInDefs(std::vector<Persp3D*> &list); + unsigned int numPerspectivesInDefs() { + std::vector<Persp3D*> list; + getPerspectivesInDefs(list); + return list.size(); + } + + //void initialize_current_persp3d(); sigc::connection connectModified(ModifiedSignal::slot_type slot); sigc::connection connectURISet(URISetSignal::slot_type slot); @@ -155,6 +165,9 @@ private: SPDocument(SPDocument const &); // no copy void operator=(SPDocument const &); // no assign + Persp3D *current_persp3d; /**< Currently 'active' perspective (to which, e.g., newly created boxes are attached) */ + Persp3DImpl *current_persp3d_impl; + public: sigc::connection connectReconstructionStart(ReconstructionStart::slot_type slot); sigc::connection connectReconstructionFinish(ReconstructionFinish::slot_type slot); diff --git a/src/event-context.cpp b/src/event-context.cpp index 9b846f2b7..918e3d419 100644 --- a/src/event-context.cpp +++ b/src/event-context.cpp @@ -174,7 +174,7 @@ sp_event_context_dispose(GObject *object) } if (ec->_delayed_snap_event) { - delete ec->_delayed_snap_event; + delete ec->_delayed_snap_event; } G_OBJECT_CLASS(parent_class)->dispose(object); @@ -374,9 +374,9 @@ static gint sp_event_context_private_root_handler(SPEventContext *event_context, switch (event->button.button) { case 1: if (event_context->space_panning) { - // When starting panning, make sure there are no snap events pending because these might disable the panning again - sp_event_context_discard_delayed_snap_event(event_context); - panning = 1; + // When starting panning, make sure there are no snap events pending because these might disable the panning again + sp_event_context_discard_delayed_snap_event(event_context); + panning = 1; sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate), GDK_KEY_RELEASE_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK, NULL, event->button.time-1); @@ -387,9 +387,9 @@ static gint sp_event_context_private_root_handler(SPEventContext *event_context, if (event->button.state & GDK_SHIFT_MASK) { zoom_rb = 2; } else { - // When starting panning, make sure there are no snap events pending because these might disable the panning again - sp_event_context_discard_delayed_snap_event(event_context); - panning = 2; + // When starting panning, make sure there are no snap events pending because these might disable the panning again + sp_event_context_discard_delayed_snap_event(event_context); + panning = 2; sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate), GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK, NULL, event->button.time-1); @@ -399,9 +399,9 @@ static gint sp_event_context_private_root_handler(SPEventContext *event_context, case 3: if (event->button.state & GDK_SHIFT_MASK || event->button.state & GDK_CONTROL_MASK) { - // When starting panning, make sure there are no snap events pending because these might disable the panning again - sp_event_context_discard_delayed_snap_event(event_context); - panning = 3; + // When starting panning, make sure there are no snap events pending because these might disable the panning again + sp_event_context_discard_delayed_snap_event(event_context); + panning = 3; sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate), GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK, NULL, event->button.time); @@ -421,7 +421,7 @@ static gint sp_event_context_private_root_handler(SPEventContext *event_context, || (panning == 3 && !(event->motion.state & GDK_BUTTON3_MASK)) ) { /* Gdk seems to lose button release for us sometimes :-( */ - panning = 0; + panning = 0; sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), event->button.time); ret = TRUE; @@ -514,11 +514,11 @@ static gint sp_event_context_private_root_handler(SPEventContext *event_context, } break; case GDK_KEY_PRESS: - { - double const acceleration = prefs->getDoubleLimited("/options/scrollingacceleration/value", 0, 0, 6); - int const key_scroll = prefs->getIntLimited("/options/keyscroll/value", 10, 0, 1000); + { + double const acceleration = prefs->getDoubleLimited("/options/scrollingacceleration/value", 0, 0, 6); + int const key_scroll = prefs->getIntLimited("/options/keyscroll/value", 10, 0, 1000); - switch (get_group0_keyval(&event->key)) { + switch (get_group0_keyval(&event->key)) { // GDK insists on stealing these keys (F1 for no idea what, tab for cycling widgets // in the editing window). So we resteal them back and run our regular shortcut // invoker on them. @@ -545,11 +545,11 @@ static gint sp_event_context_private_root_handler(SPEventContext *event_context, break; case GDK_Q: case GDK_q: - if (desktop->quick_zoomed()) { - ret = TRUE; - } + if (desktop->quick_zoomed()) { + ret = TRUE; + } if (!MOD__SHIFT && !MOD__CTRL && !MOD__ALT) { - desktop->zoom_quick(true); + desktop->zoom_quick(true); ret = TRUE; } break; @@ -632,7 +632,7 @@ static gint sp_event_context_private_root_handler(SPEventContext *event_context, default: break; } - } + } break; case GDK_KEY_RELEASE: switch (get_group0_keyval(&event->key)) { @@ -651,8 +651,8 @@ static gint sp_event_context_private_root_handler(SPEventContext *event_context, break; case GDK_Q: case GDK_q: - if (desktop->quick_zoomed()) { - desktop->zoom_quick(false); + if (desktop->quick_zoomed()) { + desktop->zoom_quick(false); ret = TRUE; } break; @@ -907,24 +907,24 @@ gint sp_event_context_root_handler(SPEventContext * event_context, GdkEvent * event) { switch (event->type) { - case GDK_MOTION_NOTIFY: - sp_event_context_snap_delay_handler(event_context, NULL, NULL, (GdkEventMotion *)event, DelayedSnapEvent::EVENTCONTEXT_ROOT_HANDLER); - break; - case GDK_BUTTON_RELEASE: - if (event_context->_delayed_snap_event) { - // If we have any pending snapping action, then invoke it now - sp_event_context_snap_watchdog_callback(event_context->_delayed_snap_event); - } - break; - case GDK_BUTTON_PRESS: + case GDK_MOTION_NOTIFY: + sp_event_context_snap_delay_handler(event_context, NULL, NULL, (GdkEventMotion *)event, DelayedSnapEvent::EVENTCONTEXT_ROOT_HANDLER); + break; + case GDK_BUTTON_RELEASE: + if (event_context->_delayed_snap_event) { + // If we have any pending snapping action, then invoke it now + sp_event_context_snap_watchdog_callback(event_context->_delayed_snap_event); + } + break; + case GDK_BUTTON_PRESS: case GDK_2BUTTON_PRESS: case GDK_3BUTTON_PRESS: - // Snapping will be on hold if we're moving the mouse at high speeds. When starting - // drawing a new shape we really should snap though. - event_context->desktop->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false); - break; + // Snapping will be on hold if we're moving the mouse at high speeds. When starting + // drawing a new shape we really should snap though. + event_context->desktop->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false); + break; default: - break; + break; } return sp_event_context_virtual_root_handler(event_context, event); @@ -933,9 +933,9 @@ sp_event_context_root_handler(SPEventContext * event_context, GdkEvent * event) gint sp_event_context_virtual_root_handler(SPEventContext * event_context, GdkEvent * event) { - gint ret = ((SPEventContextClass *) G_OBJECT_GET_CLASS(event_context))->root_handler(event_context, event); - set_event_location(event_context->desktop, event); - return ret; + gint ret = ((SPEventContextClass *) G_OBJECT_GET_CLASS(event_context))->root_handler(event_context, event); + set_event_location(event_context->desktop, event); + return ret; } /** @@ -944,27 +944,27 @@ sp_event_context_virtual_root_handler(SPEventContext * event_context, GdkEvent * gint sp_event_context_item_handler(SPEventContext * event_context, SPItem * item, GdkEvent * event) { - switch (event->type) { - case GDK_MOTION_NOTIFY: - sp_event_context_snap_delay_handler(event_context, item, NULL, (GdkEventMotion *)event, DelayedSnapEvent::EVENTCONTEXT_ITEM_HANDLER); - break; - case GDK_BUTTON_RELEASE: - if (event_context->_delayed_snap_event) { - // If we have any pending snapping action, then invoke it now - sp_event_context_snap_watchdog_callback(event_context->_delayed_snap_event); - } - break; - /*case GDK_BUTTON_PRESS: - case GDK_2BUTTON_PRESS: - case GDK_3BUTTON_PRESS: - // Snapping will be on hold if we're moving the mouse at high speeds. When starting - // drawing a new shape we really should snap though. - event_context->desktop->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false); - break; - */ - default: - break; - } + switch (event->type) { + case GDK_MOTION_NOTIFY: + sp_event_context_snap_delay_handler(event_context, item, NULL, (GdkEventMotion *)event, DelayedSnapEvent::EVENTCONTEXT_ITEM_HANDLER); + break; + case GDK_BUTTON_RELEASE: + if (event_context->_delayed_snap_event) { + // If we have any pending snapping action, then invoke it now + sp_event_context_snap_watchdog_callback(event_context->_delayed_snap_event); + } + break; + /*case GDK_BUTTON_PRESS: + case GDK_2BUTTON_PRESS: + case GDK_3BUTTON_PRESS: + // Snapping will be on hold if we're moving the mouse at high speeds. When starting + // drawing a new shape we really should snap though. + event_context->desktop->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false); + break; + */ + default: + break; + } return sp_event_context_virtual_item_handler(event_context, item, event); } @@ -972,15 +972,15 @@ sp_event_context_item_handler(SPEventContext * event_context, SPItem * item, Gdk gint sp_event_context_virtual_item_handler(SPEventContext * event_context, SPItem * item, GdkEvent * event) { - gint ret = ((SPEventContextClass *) G_OBJECT_GET_CLASS(event_context))->item_handler(event_context, item, event); + gint ret = ((SPEventContextClass *) G_OBJECT_GET_CLASS(event_context))->item_handler(event_context, item, event); - if (! ret) { - ret = sp_event_context_virtual_root_handler(event_context, event); - } else { - set_event_location(event_context->desktop, event); - } + if (! ret) { + ret = sp_event_context_virtual_root_handler(event_context, event); + } else { + set_event_location(event_context->desktop, event); + } - return ret; + return ret; } /** @@ -1174,124 +1174,124 @@ event_context_print_event_info(GdkEvent *event, bool print_return) { void sp_event_context_snap_delay_handler(SPEventContext *ec, SPItem* const item, SPKnot* const knot, GdkEventMotion *event, DelayedSnapEvent::DelayedSnapEventOrigin origin) { - static guint32 prev_time; - static boost::optional<Geom::Point> prev_pos; + static guint32 prev_time; + static boost::optional<Geom::Point> prev_pos; - // Snapping occurs when dragging with the left mouse button down, or when hovering e.g. in the pen tool with left mouse button up + // Snapping occurs when dragging with the left mouse button down, or when hovering e.g. in the pen tool with left mouse button up bool const c1 = event->state & GDK_BUTTON2_MASK; // We shouldn't hold back any events when other mouse buttons have been bool const c2 = event->state & GDK_BUTTON3_MASK; // pressed, e.g. when scrolling with the middle mouse button; if we do then - // Inkscape will get stuck in an unresponsive state + // Inkscape will get stuck in an unresponsive state bool const c3 = tools_isactive(ec->desktop, TOOLS_CALLIGRAPHIC); // The snap delay will repeat the last motion event, which will lead to // erroneous points in the calligraphy context. And because we don't snap // in this context, we might just as well disable the snap delay all together if (c1 || c2 || c3) { - // Make sure that we don't send any pending snap events to a context if we know in advance - // that we're not going to snap any way (e.g. while scrolling with middle mouse button) - // Any motion event might affect the state of the context, leading to unexpected behavior - sp_event_context_discard_delayed_snap_event(ec); + // Make sure that we don't send any pending snap events to a context if we know in advance + // that we're not going to snap any way (e.g. while scrolling with middle mouse button) + // Any motion event might affect the state of the context, leading to unexpected behavior + sp_event_context_discard_delayed_snap_event(ec); } else if (ec->desktop && ec->desktop->namedview->snap_manager.snapprefs.getSnapEnabledGlobally()) { - // Snap when speed drops below e.g. 0.02 px/msec, or when no motion events have occurred for some period. - // i.e. snap when we're at stand still. A speed threshold enforces snapping for tablets, which might never - // be fully at stand still and might keep spitting out motion events. - ec->desktop->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(true); // put snapping on hold - - Geom::Point event_pos(event->x, event->y); - guint32 event_t = gdk_event_get_time ( (GdkEvent *) event ); - - if (prev_pos) { - Geom::Coord dist = Geom::L2(event_pos - *prev_pos); - guint32 delta_t = event_t - prev_time; - gdouble speed = delta_t > 0 ? dist/delta_t : 1000; - //std::cout << "Mouse speed = " << speed << " px/msec " << std::endl; - if (speed > 0.02) { // Jitter threshold, might be needed for tablets - // We're moving fast, so postpone any snapping until the next GDK_MOTION_NOTIFY event. We - // will keep on postponing the snapping as long as the speed is high. - // We must snap at some point in time though, so set a watchdog timer at some time from - // now, just in case there's no future motion event that drops under the speed limit (when - // stopping abruptly) - delete ec->_delayed_snap_event; - ec->_delayed_snap_event = new DelayedSnapEvent(ec, item, knot, event, origin); // watchdog is reset, i.e. pushed forward in time - // If the watchdog expires before a new motion event is received, we will snap (as explained - // above). This means however that when the timer is too short, we will always snap and that the - // speed threshold is ineffective. In the extreme case the delay is set to zero, and snapping will - // be immediate, as it used to be in the old days ;-). - } else { // Speed is very low, so we're virtually at stand still - // But if we're really standing still, then we should snap now. We could use some low-pass filtering, - // otherwise snapping occurs for each jitter movement. For this filtering we'll leave the watchdog to expire, - // snap, and set a new watchdog again. - if (ec->_delayed_snap_event == NULL) { // no watchdog has been set - // it might have already expired, so we'll set a new one; the snapping frequency will be limited by this - ec->_delayed_snap_event = new DelayedSnapEvent(ec, item, knot, event, origin); - } // else: watchdog has been set before and we'll wait for it to expire - } - } else { - // This is the first GDK_MOTION_NOTIFY event, so postpone snapping and set the watchdog - g_assert(ec->_delayed_snap_event == NULL); - ec->_delayed_snap_event = new DelayedSnapEvent(ec, item, knot, event, origin); - } - - prev_pos = event_pos; - prev_time = event_t; + // Snap when speed drops below e.g. 0.02 px/msec, or when no motion events have occurred for some period. + // i.e. snap when we're at stand still. A speed threshold enforces snapping for tablets, which might never + // be fully at stand still and might keep spitting out motion events. + ec->desktop->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(true); // put snapping on hold + + Geom::Point event_pos(event->x, event->y); + guint32 event_t = gdk_event_get_time ( (GdkEvent *) event ); + + if (prev_pos) { + Geom::Coord dist = Geom::L2(event_pos - *prev_pos); + guint32 delta_t = event_t - prev_time; + gdouble speed = delta_t > 0 ? dist/delta_t : 1000; + //std::cout << "Mouse speed = " << speed << " px/msec " << std::endl; + if (speed > 0.02) { // Jitter threshold, might be needed for tablets + // We're moving fast, so postpone any snapping until the next GDK_MOTION_NOTIFY event. We + // will keep on postponing the snapping as long as the speed is high. + // We must snap at some point in time though, so set a watchdog timer at some time from + // now, just in case there's no future motion event that drops under the speed limit (when + // stopping abruptly) + delete ec->_delayed_snap_event; + ec->_delayed_snap_event = new DelayedSnapEvent(ec, item, knot, event, origin); // watchdog is reset, i.e. pushed forward in time + // If the watchdog expires before a new motion event is received, we will snap (as explained + // above). This means however that when the timer is too short, we will always snap and that the + // speed threshold is ineffective. In the extreme case the delay is set to zero, and snapping will + // be immediate, as it used to be in the old days ;-). + } else { // Speed is very low, so we're virtually at stand still + // But if we're really standing still, then we should snap now. We could use some low-pass filtering, + // otherwise snapping occurs for each jitter movement. For this filtering we'll leave the watchdog to expire, + // snap, and set a new watchdog again. + if (ec->_delayed_snap_event == NULL) { // no watchdog has been set + // it might have already expired, so we'll set a new one; the snapping frequency will be limited this way + ec->_delayed_snap_event = new DelayedSnapEvent(ec, item, knot, event, origin); + } // else: watchdog has been set before and we'll wait for it to expire + } + } else { + // This is the first GDK_MOTION_NOTIFY event, so postpone snapping and set the watchdog + g_assert(ec->_delayed_snap_event == NULL); + ec->_delayed_snap_event = new DelayedSnapEvent(ec, item, knot, event, origin); + } + + prev_pos = event_pos; + prev_time = event_t; } } gboolean sp_event_context_snap_watchdog_callback(gpointer data) { - // Snap NOW! For this the "postponed" flag will be reset and the last motion event will be repeated - DelayedSnapEvent *dse = reinterpret_cast<DelayedSnapEvent*>(data); - - if (dse == NULL) { - // This might occur when this method is called directly, i.e. not through the timer - // E.g. on GDK_BUTTON_RELEASE in sp_event_context_root_handler() - return FALSE; - } - - SPEventContext *ec = dse->getEventContext(); - if (ec == NULL || ec->desktop == NULL) { - return false; - } - - SPDesktop *dt = ec->desktop; - dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false); - - switch (dse->getOrigin()) { - case DelayedSnapEvent::EVENTCONTEXT_ROOT_HANDLER: - sp_event_context_virtual_root_handler(ec, dse->getEvent()); - break; - case DelayedSnapEvent::EVENTCONTEXT_ITEM_HANDLER: - { - SPItem* item = NULL; - item = dse->getItem(); - if (item && SP_IS_ITEM(item)) { - sp_event_context_virtual_item_handler(ec, item, dse->getEvent()); - } - } - break; - case DelayedSnapEvent::KNOT_HANDLER: - { - SPKnot* knot = dse->getKnot(); - if (knot && SP_IS_KNOT(knot)) { - sp_knot_handler_request_position(dse->getEvent(), knot); - } - } - break; - default: - g_warning("Origin of snap-delay event has not been defined!;"); - break; - } - - ec->_delayed_snap_event = NULL; - delete dse; - - return FALSE; //Kills the timer and stops it from executing this callback over and over again. + // Snap NOW! For this the "postponed" flag will be reset and the last motion event will be repeated + DelayedSnapEvent *dse = reinterpret_cast<DelayedSnapEvent*>(data); + + if (dse == NULL) { + // This might occur when this method is called directly, i.e. not through the timer + // E.g. on GDK_BUTTON_RELEASE in sp_event_context_root_handler() + return FALSE; + } + + SPEventContext *ec = dse->getEventContext(); + if (ec == NULL || ec->desktop == NULL) { + return false; + } + + SPDesktop *dt = ec->desktop; + dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false); + + switch (dse->getOrigin()) { + case DelayedSnapEvent::EVENTCONTEXT_ROOT_HANDLER: + sp_event_context_virtual_root_handler(ec, dse->getEvent()); + break; + case DelayedSnapEvent::EVENTCONTEXT_ITEM_HANDLER: + { + SPItem* item = NULL; + item = dse->getItem(); + if (item && SP_IS_ITEM(item)) { + sp_event_context_virtual_item_handler(ec, item, dse->getEvent()); + } + } + break; + case DelayedSnapEvent::KNOT_HANDLER: + { + SPKnot* knot = dse->getKnot(); + if (knot && SP_IS_KNOT(knot)) { + sp_knot_handler_request_position(dse->getEvent(), knot); + } + } + break; + default: + g_warning("Origin of snap-delay event has not been defined!;"); + break; + } + + ec->_delayed_snap_event = NULL; + delete dse; + + return FALSE; //Kills the timer and stops it from executing this callback over and over again. } void sp_event_context_discard_delayed_snap_event(SPEventContext *ec) { - delete ec->_delayed_snap_event; - ec->_delayed_snap_event = NULL; + delete ec->_delayed_snap_event; + ec->_delayed_snap_event = NULL; } diff --git a/src/gradient-drag.cpp b/src/gradient-drag.cpp index c16ed2456..e61bd9552 100644 --- a/src/gradient-drag.cpp +++ b/src/gradient-drag.cpp @@ -609,7 +609,7 @@ gr_knot_moved_handler(SPKnot *knot, Geom::Point const &ppointer, guint state, gp dist = fabs(p[Geom::Y] - dragger->parent->hor_levels[i]); if (dist < snap_dist) { p[Geom::Y] = dragger->parent->hor_levels[i]; - s = Inkscape::SnappedPoint(p, Inkscape::SNAPSOURCE_HANDLE, Inkscape::SNAPTARGET_GRADIENTS_PARENT_BBOX, dist, snap_dist, false, false); + s = Inkscape::SnappedPoint(p, Inkscape::SNAPSOURCE_HANDLE, 0, Inkscape::SNAPTARGET_GRADIENTS_PARENT_BBOX, dist, snap_dist, false, false); was_snapped = true; sp_knot_moveto (knot, p); } @@ -618,7 +618,7 @@ gr_knot_moved_handler(SPKnot *knot, Geom::Point const &ppointer, guint state, gp dist = fabs(p[Geom::X] - dragger->parent->vert_levels[i]); if (dist < snap_dist) { p[Geom::X] = dragger->parent->vert_levels[i]; - s = Inkscape::SnappedPoint(p, Inkscape::SNAPSOURCE_HANDLE, Inkscape::SNAPTARGET_GRADIENTS_PARENT_BBOX, dist, snap_dist, false, false); + s = Inkscape::SnappedPoint(p, Inkscape::SNAPSOURCE_HANDLE, 0, Inkscape::SNAPTARGET_GRADIENTS_PARENT_BBOX, dist, snap_dist, false, false); was_snapped = true; sp_knot_moveto (knot, p); } diff --git a/src/guide-snapper.cpp b/src/guide-snapper.cpp index 5cf97958a..9121e3ee2 100644 --- a/src/guide-snapper.cpp +++ b/src/guide-snapper.cpp @@ -68,22 +68,22 @@ bool Inkscape::GuideSnapper::ThisSnapperMightSnap() const return (_snap_enabled && _snapmanager->snapprefs.getSnapToGuides() && _snapmanager->getNamedView()->showguides); } -void Inkscape::GuideSnapper::_addSnappedLine(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, Geom::Point const normal_to_line, Geom::Point const point_on_line) const +void Inkscape::GuideSnapper::_addSnappedLine(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, Geom::Point const normal_to_line, Geom::Point const point_on_line) const { - SnappedLine dummy = SnappedLine(snapped_point, snapped_distance, source, Inkscape::SNAPTARGET_GUIDE, getSnapperTolerance(), getSnapperAlwaysSnap(), normal_to_line, point_on_line); + SnappedLine dummy = SnappedLine(snapped_point, snapped_distance, source, source_num, Inkscape::SNAPTARGET_GUIDE, getSnapperTolerance(), getSnapperAlwaysSnap(), normal_to_line, point_on_line); sc.guide_lines.push_back(dummy); } -void Inkscape::GuideSnapper::_addSnappedLinesOrigin(SnappedConstraints &sc, Geom::Point const origin, Geom::Coord const snapped_distance, SnapSourceType const &source) const +void Inkscape::GuideSnapper::_addSnappedLinesOrigin(SnappedConstraints &sc, Geom::Point const origin, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num) const { - SnappedPoint dummy = SnappedPoint(origin, source, Inkscape::SNAPTARGET_GUIDE_ORIGIN, snapped_distance, getSnapperTolerance(), getSnapperAlwaysSnap(), true); + SnappedPoint dummy = SnappedPoint(origin, source, source_num, Inkscape::SNAPTARGET_GUIDE_ORIGIN, snapped_distance, getSnapperTolerance(), getSnapperAlwaysSnap(), true); sc.points.push_back(dummy); } -void Inkscape::GuideSnapper::_addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source) const +void Inkscape::GuideSnapper::_addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num) const { - SnappedPoint dummy = SnappedPoint(snapped_point, source, Inkscape::SNAPTARGET_GUIDE, snapped_distance, getSnapperTolerance(), getSnapperAlwaysSnap(), true); + SnappedPoint dummy = SnappedPoint(snapped_point, source, source_num, Inkscape::SNAPTARGET_GUIDE, snapped_distance, getSnapperTolerance(), getSnapperAlwaysSnap(), true); sc.points.push_back(dummy); } diff --git a/src/guide-snapper.h b/src/guide-snapper.h index 1dc602f72..5adac6e22 100644 --- a/src/guide-snapper.h +++ b/src/guide-snapper.h @@ -30,13 +30,13 @@ public: bool ThisSnapperMightSnap() const; Geom::Coord getSnapperTolerance() const; //returns the tolerance of the snapper in screen pixels (i.e. independent of zoom) - bool getSnapperAlwaysSnap() const; //if true, then the snapper will always snap, regardless of its tolerance + bool getSnapperAlwaysSnap() const; //if true, then the snapper will always snap, regardless of its tolerance private: LineList _getSnapLines(Geom::Point const &p) const; - void _addSnappedLine(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, Geom::Point const normal_to_line, Geom::Point const point_on_line) const; - void _addSnappedLinesOrigin(SnappedConstraints &sc, Geom::Point const origin, Geom::Coord const snapped_distance, SnapSourceType const &source) const; - void _addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source) const; + void _addSnappedLine(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, Geom::Point const normal_to_line, Geom::Point const point_on_line) const; + void _addSnappedLinesOrigin(SnappedConstraints &sc, Geom::Point const origin, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num) const; + void _addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num) const; }; } diff --git a/src/libnr/nr-compose.cpp b/src/libnr/nr-compose.cpp index 3b99678e2..74f9d036b 100644 --- a/src/libnr/nr-compose.cpp +++ b/src/libnr/nr-compose.cpp @@ -773,6 +773,7 @@ nr_R8G8B8A8_P_EMPTY_A8_RGBA32 (unsigned char *px, int w, int h, int rs, const un c[3] = a; /* WARNING: MMX composer REQUIRES w > 0 and h > 0 */ nr_mmx_R8G8B8A8_P_EMPTY_A8_RGBAP (px, w, h, rs, mpx, mrs, c); + // This mmx optimized code is approx. 2x faster than the non-optimized code below (Measured by Diederik van Lierop, 2009-12-17) return; } #endif diff --git a/src/libnrtype/FontFactory.cpp b/src/libnrtype/FontFactory.cpp index fec9316b9..1f85ee5ca 100644 --- a/src/libnrtype/FontFactory.cpp +++ b/src/libnrtype/FontFactory.cpp @@ -26,6 +26,10 @@ /* Freetype2 */ # include <pango/pangoft2.h> +#include <ext/hash_map> + + +typedef __gnu_cxx::hash_map<PangoFontDescription*, font_instance*, font_descr_hash, font_descr_equal> FaceMapType; // need to avoid using the size field size_t font_descr_hash::operator()( PangoFontDescription *const &x) const { @@ -299,20 +303,25 @@ font_factory *font_factory::Default(void) return lUsine; } -font_factory::font_factory(void) -{ - fontSize = 512; - nbEnt = 0; - maxEnt = 32; - ents = (font_entry*)g_malloc(maxEnt*sizeof(font_entry)); +font_factory::font_factory(void) : + nbEnt(0), + maxEnt(32), + ents(static_cast<font_entry*>(g_malloc(maxEnt*sizeof(font_entry)))), #ifdef USE_PANGO_WIN32 - hScreenDC = pango_win32_get_dc(); - fontServer = pango_win32_font_map_for_display(); - fontContext = pango_win32_get_context(); - pangoFontCache = pango_win32_font_map_get_font_cache(fontServer); + fontServer(pango_win32_font_map_for_display()), + fontContext(pango_win32_get_context()), + pangoFontCache(pango_win32_font_map_get_font_cache(fontServer)), + hScreenDC(pango_win32_get_dc()), +#else + fontServer(pango_ft2_font_map_new()), + fontContext(0), +#endif + fontSize(512), + loadedPtr(new FaceMapType()) +{ +#ifdef USE_PANGO_WIN32 #else - fontServer = pango_ft2_font_map_new(); pango_ft2_font_map_set_resolution((PangoFT2FontMap*)fontServer, 72, 72); fontContext = pango_ft2_font_map_create_context((PangoFT2FontMap*)fontServer); pango_ft2_font_map_set_default_substitute((PangoFT2FontMap*)fontServer,FactorySubstituteFunc,this,NULL); @@ -321,6 +330,11 @@ font_factory::font_factory(void) font_factory::~font_factory(void) { + if (loadedPtr) { + FaceMapType* tmp = static_cast<FaceMapType*>(loadedPtr); + loadedPtr = 0; + } + for (int i = 0;i < nbEnt;i++) ents[i].f->Unref(); if ( ents ) g_free(ents); @@ -793,6 +807,7 @@ font_instance *font_factory::Face(PangoFontDescription *descr, bool canFail) font_instance *res = NULL; + FaceMapType& loadedFaces = *static_cast<FaceMapType*>(loadedPtr); if ( loadedFaces.find(descr) == loadedFaces.end() ) { // not yet loaded PangoFont *nFace = NULL; @@ -849,8 +864,9 @@ font_instance *font_factory::Face(PangoFontDescription *descr, bool canFail) res->Ref(); AddInCache(res); } - if(res) - res->InitTheFace(); + if (res) { + res->InitTheFace(); + } return res; } @@ -924,15 +940,18 @@ font_instance *font_factory::Face(char const *family, NRTypePosDef apos) void font_factory::UnrefFace(font_instance *who) { - if ( who == NULL ) return; - if ( loadedFaces.find(who->descr) == loadedFaces.end() ) { - // not found - char *tc = pango_font_description_to_string(who->descr); - g_warning("unrefFace %p=%s: failed\n",who,tc); - g_free(tc); - } else { - loadedFaces.erase(loadedFaces.find(who->descr)); - // printf("unrefFace %p: success\n",who); + if ( who ) { + FaceMapType& loadedFaces = *static_cast<FaceMapType*>(loadedPtr); + + if ( loadedFaces.find(who->descr) == loadedFaces.end() ) { + // not found + char *tc = pango_font_description_to_string(who->descr); + g_warning("unrefFace %p=%s: failed\n",who,tc); + g_free(tc); + } else { + loadedFaces.erase(loadedFaces.find(who->descr)); + // printf("unrefFace %p: success\n",who); + } } } diff --git a/src/libnrtype/FontFactory.h b/src/libnrtype/FontFactory.h index 9f4b31a2e..8d85bcf3e 100644 --- a/src/libnrtype/FontFactory.h +++ b/src/libnrtype/FontFactory.h @@ -11,7 +11,6 @@ #include <functional> #include <algorithm> -#include <ext/hash_map> #ifdef HAVE_CONFIG_H # include <config.h> @@ -84,31 +83,29 @@ public: double fontSize; /**< The huge fontsize used as workaround for hinting. * Different between freetype and win32. */ - __gnu_cxx::hash_map<PangoFontDescription*, font_instance*, font_descr_hash, font_descr_equal> loadedFaces; - font_factory(); virtual ~font_factory(); /// Returns the default font_factory. static font_factory* Default(); - + /// Constructs a pango string for use with the fontStringMap (see below) Glib::ustring ConstructFontSpecification(PangoFontDescription *font); Glib::ustring ConstructFontSpecification(font_instance *font); - + /// Returns strings to be used in the UI for family and face (or "style" as the column is labeled) Glib::ustring GetUIFamilyString(PangoFontDescription const *fontDescr); Glib::ustring GetUIStyleString(PangoFontDescription const *fontDescr); - + /// Modifiers for the font specification (returns new font specification) Glib::ustring ReplaceFontSpecificationFamily(const Glib::ustring & fontSpec, const Glib::ustring & newFamily); Glib::ustring FontSpecificationSetItalic(const Glib::ustring & fontSpec, bool turnOn); Glib::ustring FontSpecificationSetBold(const Glib::ustring & fontSpec, bool turnOn); - + // Gathers all strings needed for UI while storing pango information in // fontInstanceMap and fontStringMap void GetUIFamiliesAndStyles(FamilyToStylesMap *map); - + /// Retrieve a font_instance from a style object, first trying to use the font-specification, the CSS information font_instance* FaceFromStyle(SPStyle const *style); @@ -129,17 +126,19 @@ public: // internal void AddInCache(font_instance *who); - + private: + void* loadedPtr; + // These two maps are used for translating between what's in the UI and a pango // font description. This is necessary because Pango cannot always // reproduce these structures from the names it gave us in the first place. - + // Key: A string produced by font_factory::ConstructFontSpecification // Value: The associated PangoFontDescription typedef std::map<Glib::ustring, PangoFontDescription *> PangoStringToDescrMap; PangoStringToDescrMap fontInstanceMap; - + // Key: Family name in UI + Style name in UI // Value: The associated string that should be produced with font_factory::ConstructFontSpecification typedef std::map<Glib::ustring, Glib::ustring> UIStringToPangoStringMap; diff --git a/src/libnrtype/FontInstance.cpp b/src/libnrtype/FontInstance.cpp index e1413b46e..f34a230c1 100644 --- a/src/libnrtype/FontInstance.cpp +++ b/src/libnrtype/FontInstance.cpp @@ -29,6 +29,22 @@ # include FT_TRUETYPE_TABLES_H # include <pango/pangoft2.h> +#include <ext/hash_map> + + +// the various raster_font in use at a given time are held in a hash_map whose indices are the +// styles, hence the 2 following 'classes' +struct font_style_hash : public std::unary_function<font_style, size_t> { + size_t operator()(font_style const &x) const; +}; + +struct font_style_equal : public std::binary_function<font_style, font_style, bool> { + bool operator()(font_style const &a, font_style const &b); +}; + + +typedef __gnu_cxx::hash_map<font_style, raster_font*, font_style_hash, font_style_equal> StyleMap; + size_t font_style_hash::operator()(const font_style &x) const { @@ -155,36 +171,61 @@ static int ft2_cubic_to(FREETYPE_VECTOR *control1, FREETYPE_VECTOR *control2, FR * */ -font_instance::font_instance(void) +font_instance::font_instance(void) : + pFont(0), + descr(0), + refCount(0), + daddy(0), + nbGlyph(0), + maxGlyph(0), + glyphs(0), + loadedPtr(new StyleMap()), + theFace(0) { - //printf("font instance born\n"); - descr=NULL; - pFont=NULL; - refCount=0; - daddy=NULL; - nbGlyph=maxGlyph=0; - glyphs=NULL; - theFace=NULL; + //printf("font instance born\n"); } font_instance::~font_instance(void) { - if ( daddy ) daddy->UnrefFace(this); - //printf("font instance death\n"); - if ( pFont ) g_object_unref(pFont); - pFont=NULL; - if ( descr ) pango_font_description_free(descr); - descr=NULL; - // if ( theFace ) FT_Done_Face(theFace); // owned by pFont. don't touch - theFace=NULL; - - for (int i=0;i<nbGlyph;i++) { - if ( glyphs[i].outline ) delete glyphs[i].outline; - if ( glyphs[i].pathvector ) delete glyphs[i].pathvector; - } - if ( glyphs ) free(glyphs); - nbGlyph=maxGlyph=0; - glyphs=NULL; + if ( loadedPtr ) { + StyleMap* tmp = static_cast<StyleMap*>(loadedPtr); + delete tmp; + loadedPtr = 0; + } + + if ( daddy ) { + daddy->UnrefFace(this); + daddy = 0; + } + + //printf("font instance death\n"); + if ( pFont ) { + g_object_unref(pFont); + pFont = 0; + } + + if ( descr ) { + pango_font_description_free(descr); + descr = 0; + } + + // if ( theFace ) FT_Done_Face(theFace); // owned by pFont. don't touch + theFace = 0; + + for (int i=0;i<nbGlyph;i++) { + if ( glyphs[i].outline ) { + delete glyphs[i].outline; + } + if ( glyphs[i].pathvector ) { + delete glyphs[i].pathvector; + } + } + if ( glyphs ) { + free(glyphs); + glyphs = 0; + } + nbGlyph = 0; + maxGlyph = 0; } void font_instance::Ref(void) @@ -728,7 +769,8 @@ raster_font* font_instance::RasterFont(const font_style &inStyle) nStyle.dashes=(double*)malloc(nStyle.nbDash*sizeof(double)); memcpy(nStyle.dashes,savDashes,nStyle.nbDash*sizeof(double)); } - if ( loadedStyles.find(nStyle) == loadedStyles.end() ) { + StyleMap& loadedStyles = *static_cast<StyleMap*>(loadedPtr); + if ( loadedStyles.find(nStyle) == loadedStyles.end() ) { raster_font *nR = new raster_font(nStyle); nR->Ref(); nR->daddy=this; @@ -746,15 +788,17 @@ raster_font* font_instance::RasterFont(const font_style &inStyle) void font_instance::RemoveRasterFont(raster_font* who) { - if ( who == NULL ) return; - if ( loadedStyles.find(who->style) == loadedStyles.end() ) { - //g_print("RemoveRasterFont failed \n"); - // not found - } else { - loadedStyles.erase(loadedStyles.find(who->style)); - //g_print("RemoveRasterFont\n"); - Unref(); - } + if ( who ) { + StyleMap& loadedStyles = *static_cast<StyleMap*>(loadedPtr); + if ( loadedStyles.find(who->style) == loadedStyles.end() ) { + //g_print("RemoveRasterFont failed \n"); + // not found + } else { + loadedStyles.erase(loadedStyles.find(who->style)); + //g_print("RemoveRasterFont\n"); + Unref(); + } + } } diff --git a/src/libnrtype/Layout-TNG-OutIter.cpp b/src/libnrtype/Layout-TNG-OutIter.cpp index f4e8e4031..0682e3570 100644 --- a/src/libnrtype/Layout-TNG-OutIter.cpp +++ b/src/libnrtype/Layout-TNG-OutIter.cpp @@ -227,7 +227,11 @@ boost::optional<Geom::Point> Layout::baselineAnchorPoint() const Geom::Point left_pt = this->characterAnchorPoint(pos); pos.thisEndOfLine(); Geom::Point right_pt = this->characterAnchorPoint(pos); - Geom::Point mid_pt = (left_pt + right_pt)/2; + + if (this->_blockProgression() == LEFT_TO_RIGHT || this->_blockProgression() == RIGHT_TO_LEFT) { + left_pt = Geom::Point(left_pt[Geom::Y], left_pt[Geom::X]); + right_pt = Geom::Point(right_pt[Geom::Y], right_pt[Geom::X]); + } switch (this->paragraphAlignment(pos)) { case LEFT: @@ -235,7 +239,7 @@ boost::optional<Geom::Point> Layout::baselineAnchorPoint() const return left_pt; break; case CENTER: - return mid_pt; + return (left_pt + right_pt)/2; // middle point break; case RIGHT: return right_pt; diff --git a/src/libnrtype/font-instance.h b/src/libnrtype/font-instance.h index 4209a20af..521c9a424 100644 --- a/src/libnrtype/font-instance.h +++ b/src/libnrtype/font-instance.h @@ -1,7 +1,6 @@ #ifndef SEEN_LIBNRTYPE_FONT_INSTANCE_H #define SEEN_LIBNRTYPE_FONT_INSTANCE_H -#include <ext/hash_map> #include <map> #include <pango/pango-types.h> #include <pango/pango-font.h> @@ -16,33 +15,21 @@ #include <2geom/d2.h> // the font_instance are the template of several raster_font; they provide metrics and outlines -// that are drawn by the raster_font, so the raster_font needs info relative to the way the +// that are drawn by the raster_font, so the raster_font needs info relative to the way the // font need to be drawn. note that fontsize is a scale factor in the transform matrix // of the style -// the various raster_font in use at a given time are held in a hash_map whose indices are the -// styles, hence the 2 following 'classes' -struct font_style_hash : public std::unary_function<font_style, size_t> { - size_t operator()(font_style const &x) const; -}; - -struct font_style_equal : public std::binary_function<font_style, font_style, bool> { - bool operator()(font_style const &a, font_style const &b); -}; - class font_instance { public: - // hashmap to get the raster_font for a given style - __gnu_cxx::hash_map<font_style, raster_font*, font_style_hash, font_style_equal> loadedStyles; - // the real source of the font + // the real source of the font PangoFont* pFont; - // depending on the rendering backend, different temporary data + // depending on the rendering backend, different temporary data - // that's the font's fingerprint; this particular PangoFontDescription gives the entry at which this font_instance - // resides in the font_factory loadedFaces hash_map + // that's the font's fingerprint; this particular PangoFontDescription gives the entry at which this font_instance + // resides in the font_factory loadedFaces hash_map PangoFontDescription* descr; - // refcount + // refcount int refCount; - // font_factory owning this font_instance + // font_factory owning this font_instance font_factory* daddy; // common glyph definitions for all the rasterfonts @@ -58,38 +45,38 @@ public: bool IsOutlineFont(void); // utility void InstallFace(PangoFont* iFace); // utility; should reset the pFont field if loading failed - // in case the PangoFont is a bitmap font, for example. that way, the calling function - // will be able to check the validity of the font before installing it in loadedFaces + // in case the PangoFont is a bitmap font, for example. that way, the calling function + // will be able to check the validity of the font before installing it in loadedFaces void InitTheFace(); int MapUnicodeChar(gunichar c); // calls the relevant unicode->glyph index function void LoadGlyph(int glyph_id); // the main backend-dependent function - // loads the given glyph's info - - // nota: all coordinates returned by these functions are on a [0..1] scale; you need to multiply - // by the fontsize to get the real sizes + // loads the given glyph's info + + // nota: all coordinates returned by these functions are on a [0..1] scale; you need to multiply + // by the fontsize to get the real sizes Path* Outline(int glyph_id, Path *copyInto=NULL); - // queries the outline of the glyph (in livarot Path form), and copies it into copyInto instead - // of allocating a new Path if copyInto != NULL + // queries the outline of the glyph (in livarot Path form), and copies it into copyInto instead + // of allocating a new Path if copyInto != NULL Geom::PathVector* PathVector(int glyph_id); // returns the 2geom-type pathvector for this glyph. no refcounting needed, it's deallocated when the font_instance dies double Advance(int glyph_id, bool vertical); - // nominal advance of the font. + // nominal advance of the font. bool FontMetrics(double &ascent, double &descent, double &leading); bool FontSlope(double &run, double &rise); // for generating slanted cursors for oblique fonts Geom::OptRect BBox(int glyph_id); - // creates a rasterfont for the given style + // creates a rasterfont for the given style raster_font* RasterFont(Geom::Matrix const &trs, double stroke_width, bool vertical = false, JoinType stroke_join = join_straight, ButtType stroke_cap = butt_straight, float miter_limit = 4.0); - // the dashes array in iStyle is copied + // the dashes array in iStyle is copied raster_font* RasterFont(font_style const &iStyle); - // private use: tells the font_instance that the raster_font 'who' has died + // private use: tells the font_instance that the raster_font 'who' has died void RemoveRasterFont(raster_font *who); - // attribute queries + // attribute queries unsigned Name(gchar *str, unsigned size); unsigned PSName(gchar *str, unsigned size); unsigned Family(gchar *str, unsigned size); @@ -98,10 +85,13 @@ public: private: void FreeTheFace(); + // hashmap to get the raster_font for a given style + void* loadedPtr; // Pointer to a hash_map. Moved into .cpp to not expose use of __gnu_cxx extension. + #ifdef USE_PANGO_WIN32 HFONT theFace; #else - FT_Face theFace; + FT_Face theFace; // it's a pointer in fact; no worries to ref/unref it, pango does its magic // as long as pFont is valid, theFace is too #endif diff --git a/src/line-snapper.cpp b/src/line-snapper.cpp index 5d5a77280..31fa07515 100644 --- a/src/line-snapper.cpp +++ b/src/line-snapper.cpp @@ -25,7 +25,7 @@ void Inkscape::LineSnapper::freeSnap(SnappedConstraints &sc, Inkscape::SnapPreferences::PointType const &t, Geom::Point const &p, SnapSourceType const &source_type, - bool const &/*f*/, + long source_num, Geom::OptRect const &/*bbox_to_snap*/, std::vector<SPItem const *> const */*it*/, std::vector<std::pair<Geom::Point, int> > */*unselected_nodes*/) const @@ -49,13 +49,13 @@ void Inkscape::LineSnapper::freeSnap(SnappedConstraints &sc, Geom::Coord const dist = Geom::L2(p_proj - p); //Store any line that's within snapping range if (dist < getSnapperTolerance()) { - _addSnappedLine(sc, p_proj, dist, source_type, i->first, i->second); + _addSnappedLine(sc, p_proj, dist, source_type, source_num, i->first, i->second); // For any line that's within range, we will also look at it's "point on line" p1. For guides // this point coincides with its origin; for grids this is of no use, but we cannot // discern between grids and guides here Geom::Coord const dist_p1 = Geom::L2(p1 - p); if (dist_p1 < getSnapperTolerance()) { - _addSnappedLinesOrigin(sc, p1, dist_p1, source_type); + _addSnappedLinesOrigin(sc, p1, dist_p1, source_type, source_num); // Only relevant for guides; grids don't have an origin per line // Therefore _addSnappedLinesOrigin() will only be implemented for guides } @@ -69,7 +69,7 @@ void Inkscape::LineSnapper::constrainedSnap(SnappedConstraints &sc, Inkscape::SnapPreferences::PointType const &t, Geom::Point const &p, SnapSourceType const &source_type, - bool const &/*f*/, + long source_num, Geom::OptRect const &/*bbox_to_snap*/, ConstraintLine const &c, std::vector<SPItem const *> const */*it*/) const @@ -112,13 +112,13 @@ void Inkscape::LineSnapper::constrainedSnap(SnappedConstraints &sc, // This snappoint is therefore fully constrained, so there's no need // to look for additional intersections; just return the snapped point // and forget about the line - _addSnappedPoint(sc, t, dist, source_type); + _addSnappedPoint(sc, t, dist, source_type, source_num); // For any line that's within range, we will also look at it's "point on line" p1. For guides // this point coincides with its origin; for grids this is of no use, but we cannot // discern between grids and guides here Geom::Coord const dist_p1 = Geom::L2(p1 - p); if (dist_p1 < getSnapperTolerance()) { - _addSnappedLinesOrigin(sc, p1, dist_p1, source_type); + _addSnappedLinesOrigin(sc, p1, dist_p1, source_type, source_num); // Only relevant for guides; grids don't have an origin per line // Therefore _addSnappedLinesOrigin() will only be implemented for guides } @@ -130,7 +130,7 @@ void Inkscape::LineSnapper::constrainedSnap(SnappedConstraints &sc, // Will only be overridden in the guide-snapper class, because grid lines don't have an origin; the // grid-snapper classes will use this default empty method -void Inkscape::LineSnapper::_addSnappedLinesOrigin(SnappedConstraints &/*sc*/, Geom::Point const /*origin*/, Geom::Coord const /*snapped_distance*/, SnapSourceType const &/*source_type*/) const +void Inkscape::LineSnapper::_addSnappedLinesOrigin(SnappedConstraints &/*sc*/, Geom::Point const /*origin*/, Geom::Coord const /*snapped_distance*/, SnapSourceType const &/*source_type*/, long /*source_num*/) const { } diff --git a/src/line-snapper.h b/src/line-snapper.h index 4ad08a99f..af36b8330 100644 --- a/src/line-snapper.h +++ b/src/line-snapper.h @@ -28,7 +28,7 @@ public: Inkscape::SnapPreferences::PointType const &t, Geom::Point const &p, SnapSourceType const &source_type, - bool const &first_point, + long source_num, Geom::OptRect const &bbox_to_snap, std::vector<SPItem const *> const *it, std::vector<std::pair<Geom::Point, int> > *unselected_nodes) const; @@ -37,7 +37,7 @@ public: Inkscape::SnapPreferences::PointType const &t, Geom::Point const &p, SnapSourceType const &source_type, - bool const &first_point, + long source_num, Geom::OptRect const &bbox_to_snap, ConstraintLine const &c, std::vector<SPItem const *> const *it) const; @@ -54,12 +54,12 @@ private: */ virtual LineList _getSnapLines(Geom::Point const &p) const = 0; - virtual void _addSnappedLine(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, Geom::Point const normal_to_line, Geom::Point const point_on_line) const = 0; + virtual void _addSnappedLine(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, Geom::Point const normal_to_line, Geom::Point const point_on_line) const = 0; // Will only be implemented for guide lines, because grid lines don't have an origin - virtual void _addSnappedLinesOrigin(SnappedConstraints &sc, Geom::Point const origin, Geom::Coord const snapped_distance, SnapSourceType const &source) const; + virtual void _addSnappedLinesOrigin(SnappedConstraints &sc, Geom::Point const origin, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num) const; - virtual void _addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source) const = 0; + virtual void _addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num) const = 0; }; } diff --git a/src/live_effects/lpe-perspective_path.cpp b/src/live_effects/lpe-perspective_path.cpp index 091a4d9ae..3d18318c5 100644 --- a/src/live_effects/lpe-perspective_path.cpp +++ b/src/live_effects/lpe-perspective_path.cpp @@ -58,7 +58,7 @@ LPEPerspectivePath::LPEPerspectivePath(LivePathEffectObject *lpeobject) : Persp3D *persp = persp3d_document_first_persp(inkscape_active_document()); - Proj::TransfMat3x4 pmat = persp->tmat; + Proj::TransfMat3x4 pmat = persp->perspective_impl->tmat; pmat.copy_tmat(tmat); } diff --git a/src/nodepath.cpp b/src/nodepath.cpp index 8f17ae013..1881dd72b 100644 --- a/src/nodepath.cpp +++ b/src/nodepath.cpp @@ -1367,7 +1367,7 @@ static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, * must provide that information. */ // Build a list of the unselected nodes to which the snapper should snap - std::vector<std::pair<Geom::Point, int> > unselected_nodes; + std::vector<std::pair<Geom::Point, int> > unselected_nodes; for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) { Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data; for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) { @@ -1388,39 +1388,39 @@ static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *closest_node = NULL; Geom::Coord closest_dist = NR_HUGE; - if (closest_only) { - for (GList *l = nodepath->selected; l != NULL; l = l->next) { - Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data; - Geom::Coord dist = Geom::L2(nodepath->drag_origin_mouse - n->origin); - if (dist < closest_dist) { - closest_node = n; - closest_dist = dist; - } - } + if (closest_only) { + for (GList *l = nodepath->selected; l != NULL; l = l->next) { + Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data; + Geom::Coord dist = Geom::L2(nodepath->drag_origin_mouse - n->origin); + if (dist < closest_dist) { + closest_node = n; + closest_dist = dist; + } + } } - // Iterate through all selected nodes - m.setup(nodepath->desktop, false, nodepath->item, &unselected_nodes); - for (GList *l = nodepath->selected; l != NULL; l = l->next) { + // Iterate through all selected nodes + m.setup(nodepath->desktop, false, nodepath->item, &unselected_nodes); + for (GList *l = nodepath->selected; l != NULL; l = l->next) { Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data; if (!closest_only || n == closest_node) { //try to snap either all selected nodes or only the closest one - Inkscape::SnappedPoint s; - Inkscape::SnapSourceType source_type = (n->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPSOURCE_NODE_SMOOTH : Inkscape::SNAPSOURCE_NODE_CUSP); - if (constrained) { - Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint; - dedicated_constraint.setPoint(n->pos); - s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), source_type, dedicated_constraint, false); - } else { - s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), source_type); - } - - if (s.getSnapped()) { - s.setPointerDistance(Geom::L2(nodepath->drag_origin_mouse - n->origin)); - if (!s.isOtherSnapBetter(best, true)) { - best = s; - best_pt = from_2geom(s.getPoint()) - n->pos; - } - } + Inkscape::SnappedPoint s; + Inkscape::SnapSourceType source_type = (n->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPSOURCE_NODE_SMOOTH : Inkscape::SNAPSOURCE_NODE_CUSP); + if (constrained) { + Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint; + dedicated_constraint.setPoint(n->pos); + s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), source_type, dedicated_constraint, false); + } else { + s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), source_type); + } + + if (s.getSnapped()) { + s.setPointerDistance(Geom::L2(nodepath->drag_origin_mouse - n->origin)); + if (!s.isOtherSnapBetter(best, true)) { + best = s; + best_pt = from_2geom(s.getPoint()) - n->pos; + } + } } } @@ -3955,9 +3955,9 @@ static gboolean node_handle_request(SPKnot *knot, Geom::Point &p, guint state, g Inkscape::SnappedPoint s; if ((state & GDK_SHIFT_MASK) != 0) { - // We will not try to snap when the shift-key is pressed - // so remove the old snap indicator and don't wait for it to time-out - desktop->snapindicator->remove_snaptarget(); + // We will not try to snap when the shift-key is pressed + // so remove the old snap indicator and don't wait for it to time-out + desktop->snapindicator->remove_snaptarget(); } Inkscape::NodePath::Node *othernode = opposite->other; diff --git a/src/object-snapper.cpp b/src/object-snapper.cpp index 22d438c1e..88c260782 100644 --- a/src/object-snapper.cpp +++ b/src/object-snapper.cpp @@ -67,9 +67,9 @@ Inkscape::ObjectSnapper::~ObjectSnapper() */ Geom::Coord Inkscape::ObjectSnapper::getSnapperTolerance() const { - SPDesktop const *dt = _snapmanager->getDesktop(); - double const zoom = dt ? dt->current_zoom() : 1; - return _snapmanager->snapprefs.getObjectTolerance() / zoom; + SPDesktop const *dt = _snapmanager->getDesktop(); + double const zoom = dt ? dt->current_zoom() : 1; + return _snapmanager->snapprefs.getObjectTolerance() / zoom; } bool Inkscape::ObjectSnapper::getSnapperAlwaysSnap() const @@ -81,7 +81,6 @@ bool Inkscape::ObjectSnapper::getSnapperAlwaysSnap() const * Find all items within snapping range. * \param parent Pointer to the document's root, or to a clipped path or mask object * \param it List of items to ignore - * \param first_point If true then this point is the first one from a whole bunch of points * \param bbox_to_snap Bounding box hulling the whole bunch of points, all from the same selection and having the same transformation * \param DimensionToSnap Snap in X, Y, or both directions. */ @@ -173,7 +172,7 @@ void Inkscape::ObjectSnapper::_findCandidates(SPObject* parent, void Inkscape::ObjectSnapper::_collectNodes(Inkscape::SnapPreferences::PointType const &t, - bool const &first_point) const + bool const &first_point) const { // Now, let's first collect all points to snap to. If we have a whole bunch of points to snap, // e.g. when translating an item using the selector tool, then we will only do this for the @@ -212,47 +211,47 @@ void Inkscape::ObjectSnapper::_collectNodes(Inkscape::SnapPreferences::PointType g_return_if_fail(root_item); //Collect all nodes so we can snap to them - if (p_is_a_node || !(_snapmanager->snapprefs.getStrictSnapping() && !p_is_a_node) || p_is_a_guide) { - // Note: there are two ways in which intersections are considered: - // Method 1: Intersections are calculated for each shape individually, for both the - // snap source and snap target (see sp_shape_snappoints) - // Method 2: Intersections are calculated for each curve or line that we've snapped to, i.e. only for - // the target (see the intersect() method in the SnappedCurve and SnappedLine classes) - // Some differences: - // - Method 1 doesn't find intersections within a set of multiple objects - // - Method 2 only works for targets - // When considering intersections as snap targets: - // - Method 1 only works when snapping to nodes, whereas - // - Method 2 only works when snapping to paths - // - There will be performance differences too! - // If both methods are being used simultaneously, then this might lead to duplicate targets! - - // Well, here we will be looking for snap TARGETS. Both methods can therefore be used. - // When snapping to paths, we will get a collection of snapped lines and snapped curves. findBestSnap() will - // go hunting for intersections (but only when asked to in the prefs of course). In that case we can just - // temporarily block the intersections in sp_item_snappoints, we don't need duplicates. If we're not snapping to - // paths though but only to item nodes then we should still look for the intersections in sp_item_snappoints() - bool old_pref = _snapmanager->snapprefs.getSnapIntersectionCS(); - if (_snapmanager->snapprefs.getSnapToItemPath()) { - _snapmanager->snapprefs.setSnapIntersectionCS(false); - } - - sp_item_snappoints(root_item, true, *_points_to_snap_to, &_snapmanager->snapprefs); - - if (_snapmanager->snapprefs.getSnapToItemPath()) { - _snapmanager->snapprefs.setSnapIntersectionCS(old_pref); - } - } + if (p_is_a_node || !(_snapmanager->snapprefs.getStrictSnapping() && !p_is_a_node) || p_is_a_guide) { + // Note: there are two ways in which intersections are considered: + // Method 1: Intersections are calculated for each shape individually, for both the + // snap source and snap target (see sp_shape_snappoints) + // Method 2: Intersections are calculated for each curve or line that we've snapped to, i.e. only for + // the target (see the intersect() method in the SnappedCurve and SnappedLine classes) + // Some differences: + // - Method 1 doesn't find intersections within a set of multiple objects + // - Method 2 only works for targets + // When considering intersections as snap targets: + // - Method 1 only works when snapping to nodes, whereas + // - Method 2 only works when snapping to paths + // - There will be performance differences too! + // If both methods are being used simultaneously, then this might lead to duplicate targets! + + // Well, here we will be looking for snap TARGETS. Both methods can therefore be used. + // When snapping to paths, we will get a collection of snapped lines and snapped curves. findBestSnap() will + // go hunting for intersections (but only when asked to in the prefs of course). In that case we can just + // temporarily block the intersections in sp_item_snappoints, we don't need duplicates. If we're not snapping to + // paths though but only to item nodes then we should still look for the intersections in sp_item_snappoints() + bool old_pref = _snapmanager->snapprefs.getSnapIntersectionCS(); + if (_snapmanager->snapprefs.getSnapToItemPath()) { + _snapmanager->snapprefs.setSnapIntersectionCS(false); + } + + sp_item_snappoints(root_item, true, *_points_to_snap_to, &_snapmanager->snapprefs); + + if (_snapmanager->snapprefs.getSnapToItemPath()) { + _snapmanager->snapprefs.setSnapIntersectionCS(old_pref); + } + } //Collect the bounding box's corners so we can snap to them - if (p_is_a_bbox || !(_snapmanager->snapprefs.getStrictSnapping() && !p_is_a_bbox) || p_is_a_guide) { - // Discard the bbox of a clipped path / mask, because we don't want to snap to both the bbox - // of the item AND the bbox of the clipping path at the same time - if (!(*i).clip_or_mask) { - Geom::OptRect b = sp_item_bbox_desktop(root_item, bbox_type); - getBBoxPoints(b, _points_to_snap_to, true, _snapmanager->snapprefs.getSnapToBBoxNode(), _snapmanager->snapprefs.getSnapBBoxEdgeMidpoints(), _snapmanager->snapprefs.getSnapBBoxMidpoints()); - } - } + if (p_is_a_bbox || !(_snapmanager->snapprefs.getStrictSnapping() && !p_is_a_bbox) || p_is_a_guide) { + // Discard the bbox of a clipped path / mask, because we don't want to snap to both the bbox + // of the item AND the bbox of the clipping path at the same time + if (!(*i).clip_or_mask) { + Geom::OptRect b = sp_item_bbox_desktop(root_item, bbox_type); + getBBoxPoints(b, _points_to_snap_to, true, _snapmanager->snapprefs.getSnapToBBoxNode(), _snapmanager->snapprefs.getSnapBBoxEdgeMidpoints(), _snapmanager->snapprefs.getSnapBBoxMidpoints()); + } + } } } } @@ -261,12 +260,12 @@ void Inkscape::ObjectSnapper::_snapNodes(SnappedConstraints &sc, Inkscape::SnapPreferences::PointType const &t, Geom::Point const &p, SnapSourceType const &source_type, - bool const &first_point, + long source_num, std::vector<std::pair<Geom::Point, int> > *unselected_nodes) const { // Iterate through all nodes, find out which one is the closest to p, and snap to it! - _collectNodes(t, first_point); + _collectNodes(t, source_num == 0); if (unselected_nodes != NULL) { _points_to_snap_to->insert(_points_to_snap_to->end(), unselected_nodes->begin(), unselected_nodes->end()); @@ -278,7 +277,7 @@ void Inkscape::ObjectSnapper::_snapNodes(SnappedConstraints &sc, for (std::vector<std::pair<Geom::Point, int> >::const_iterator k = _points_to_snap_to->begin(); k != _points_to_snap_to->end(); k++) { Geom::Coord dist = Geom::L2((*k).first - p); if (dist < getSnapperTolerance() && dist < s.getSnapDistance()) { - s = SnappedPoint((*k).first, source_type, static_cast<Inkscape::SnapTargetType>((*k).second), dist, getSnapperTolerance(), getSnapperAlwaysSnap(), true); + s = SnappedPoint((*k).first, source_type, source_num, static_cast<Inkscape::SnapTargetType>((*k).second), dist, getSnapperTolerance(), getSnapperAlwaysSnap(), true); success = true; } } @@ -298,10 +297,10 @@ void Inkscape::ObjectSnapper::_snapTranslatingGuideToNodes(SnappedConstraints &s // Although we won't snap to paths here (which would give us under constrained snaps) we can still snap to intersections of paths. if (_snapmanager->snapprefs.getSnapToItemPath() || _snapmanager->snapprefs.getSnapToBBoxPath() || _snapmanager->snapprefs.getSnapToPageBorder()) { - _collectPaths(t, true); - _snapPaths(sc, t, p, SNAPSOURCE_GUIDE, true, NULL, NULL); - // The paths themselves should be discarded in findBestSnap(), as we should only snap to their intersections - } + _collectPaths(t, true); + _snapPaths(sc, t, p, SNAPSOURCE_GUIDE, 0, NULL, NULL); + // The paths themselves should be discarded in findBestSnap(), as we should only snap to their intersections + } SnappedPoint s; @@ -313,7 +312,7 @@ void Inkscape::ObjectSnapper::_snapTranslatingGuideToNodes(SnappedConstraints &s Geom::Coord dist = Geom::L2((*k).first - p_proj); // distance from node to the guide Geom::Coord dist2 = Geom::L2(p - p_proj); // distance from projection of node on the guide, to the mouse location if ((dist < tol && dist2 < tol) || getSnapperAlwaysSnap()) { - s = SnappedPoint((*k).first, SNAPSOURCE_GUIDE, static_cast<Inkscape::SnapTargetType>((*k).second), dist, tol, getSnapperAlwaysSnap(), true); + s = SnappedPoint((*k).first, SNAPSOURCE_GUIDE, 0, static_cast<Inkscape::SnapTargetType>((*k).second), dist, tol, getSnapperAlwaysSnap(), true); sc.points.push_back(s); } } @@ -428,11 +427,11 @@ void Inkscape::ObjectSnapper::_snapPaths(SnappedConstraints &sc, Inkscape::SnapPreferences::PointType const &t, Geom::Point const &p, SnapSourceType const &source_type, - bool const &first_point, + long source_num, std::vector<std::pair<Geom::Point, int> > *unselected_nodes, SPPath const *selected_path) const { - _collectPaths(t, first_point); + _collectPaths(t, source_num == 0); // Now we can finally do the real snapping, using the paths collected above g_assert(_snapmanager->getDesktop() != NULL); @@ -440,7 +439,7 @@ void Inkscape::ObjectSnapper::_snapPaths(SnappedConstraints &sc, bool const node_tool_active = _snapmanager->snapprefs.getSnapToItemPath() && selected_path != NULL; - if (first_point) { + if (source_num == 0) { /* findCandidates() is used for snapping to both paths and nodes. It ignores the path that is * currently being edited, because that path requires special care: when snapping to nodes * only the unselected nodes of that path should be considered, and these will be passed on separately. @@ -503,7 +502,7 @@ void Inkscape::ObjectSnapper::_snapPaths(SnappedConstraints &sc, if (!being_edited || (c1 && c2)) { Geom::Coord const dist = Geom::distance(sp_doc, p_doc); if (dist < getSnapperTolerance()) { - sc.curves.push_back(Inkscape::SnappedCurve(sp_dt, dist, getSnapperTolerance(), getSnapperAlwaysSnap(), false, curve, source_type, it_p->second)); + sc.curves.push_back(Inkscape::SnappedCurve(sp_dt, dist, getSnapperTolerance(), getSnapperAlwaysSnap(), false, curve, source_type, source_num, it_p->second)); } } } @@ -535,11 +534,11 @@ void Inkscape::ObjectSnapper::_snapPathsConstrained(SnappedConstraints &sc, Inkscape::SnapPreferences::PointType const &t, Geom::Point const &p, SnapSourceType const source_type, - bool const &first_point, + long source_num, ConstraintLine const &c) const { - _collectPaths(t, first_point); + _collectPaths(t, source_num == 0); // Now we can finally do the real snapping, using the paths collected above @@ -577,7 +576,7 @@ void Inkscape::ObjectSnapper::_snapPathsConstrained(SnappedConstraints &sc, // When it's within snapping range, then return it // (within snapping range == between p_min_on_cl and p_max_on_cl == 0 < ta < 1) Geom::Coord dist = Geom::L2(_snapmanager->getDesktop()->dt2doc(p_proj_on_cl) - p_inters); - SnappedPoint s(_snapmanager->getDesktop()->doc2dt(p_inters), source_type, k->second, dist, getSnapperTolerance(), getSnapperAlwaysSnap(), true); + SnappedPoint s(_snapmanager->getDesktop()->doc2dt(p_inters), source_type, source_num, k->second, dist, getSnapperTolerance(), getSnapperAlwaysSnap(), true); sc.points.push_back(s); } } @@ -591,7 +590,7 @@ void Inkscape::ObjectSnapper::freeSnap(SnappedConstraints &sc, Inkscape::SnapPreferences::PointType const &t, Geom::Point const &p, SnapSourceType const &source_type, - bool const &first_point, + long source_num, Geom::OptRect const &bbox_to_snap, std::vector<SPItem const *> const *it, std::vector<std::pair<Geom::Point, int> > *unselected_nodes) const @@ -601,17 +600,17 @@ void Inkscape::ObjectSnapper::freeSnap(SnappedConstraints &sc, } /* Get a list of all the SPItems that we will try to snap to */ - if (first_point) { + if (source_num == 0) { Geom::Rect const local_bbox_to_snap = bbox_to_snap ? *bbox_to_snap : Geom::Rect(p, p); - _findCandidates(sp_document_root(_snapmanager->getDocument()), it, first_point, local_bbox_to_snap, TRANSL_SNAP_XY, false, Geom::identity()); + _findCandidates(sp_document_root(_snapmanager->getDocument()), it, source_num == 0, local_bbox_to_snap, TRANSL_SNAP_XY, false, Geom::identity()); } if (_snapmanager->snapprefs.getSnapToItemNode() || _snapmanager->snapprefs.getSnapSmoothNodes() - || _snapmanager->snapprefs.getSnapToBBoxNode() || _snapmanager->snapprefs.getSnapToPageBorder() - || _snapmanager->snapprefs.getSnapLineMidpoints() || _snapmanager->snapprefs.getSnapObjectMidpoints() - || _snapmanager->snapprefs.getSnapBBoxEdgeMidpoints() || _snapmanager->snapprefs.getSnapBBoxMidpoints() - || _snapmanager->snapprefs.getIncludeItemCenter()) { - _snapNodes(sc, t, p, source_type, first_point, unselected_nodes); + || _snapmanager->snapprefs.getSnapToBBoxNode() || _snapmanager->snapprefs.getSnapToPageBorder() + || _snapmanager->snapprefs.getSnapLineMidpoints() || _snapmanager->snapprefs.getSnapObjectMidpoints() + || _snapmanager->snapprefs.getSnapBBoxEdgeMidpoints() || _snapmanager->snapprefs.getSnapBBoxMidpoints() + || _snapmanager->snapprefs.getIncludeItemCenter()) { + _snapNodes(sc, t, p, source_type, source_num, unselected_nodes); } if (_snapmanager->snapprefs.getSnapToItemPath() || _snapmanager->snapprefs.getSnapToBBoxPath() || _snapmanager->snapprefs.getSnapToPageBorder()) { @@ -625,13 +624,13 @@ void Inkscape::ObjectSnapper::freeSnap(SnappedConstraints &sc, SPPath *path = NULL; if (it != NULL) { if (it->size() == 1 && SP_IS_PATH(*it->begin())) { - path = SP_PATH(*it->begin()); + path = SP_PATH(*it->begin()); } // else: *it->begin() might be a SPGroup, e.g. when editing a LPE of text that has been converted to a group of paths // as reported in bug #356743. In that case we can just ignore it, i.e. not snap to this item } - _snapPaths(sc, t, p, source_type, first_point, unselected_nodes, path); + _snapPaths(sc, t, p, source_type, source_num, unselected_nodes, path); } else { - _snapPaths(sc, t, p, source_type, first_point, NULL, NULL); + _snapPaths(sc, t, p, source_type, source_num, NULL, NULL); } } } @@ -640,7 +639,7 @@ void Inkscape::ObjectSnapper::constrainedSnap( SnappedConstraints &sc, Inkscape::SnapPreferences::PointType const &t, Geom::Point const &p, SnapSourceType const &source_type, - bool const &first_point, + long source_num, Geom::OptRect const &bbox_to_snap, ConstraintLine const &c, std::vector<SPItem const *> const *it) const @@ -650,9 +649,9 @@ void Inkscape::ObjectSnapper::constrainedSnap( SnappedConstraints &sc, } /* Get a list of all the SPItems that we will try to snap to */ - if (first_point) { + if (source_num == 0) { Geom::Rect const local_bbox_to_snap = bbox_to_snap ? *bbox_to_snap : Geom::Rect(p, p); - _findCandidates(sp_document_root(_snapmanager->getDocument()), it, first_point, local_bbox_to_snap, TRANSL_SNAP_XY, false, Geom::identity()); + _findCandidates(sp_document_root(_snapmanager->getDocument()), it, source_num == 0, local_bbox_to_snap, TRANSL_SNAP_XY, false, Geom::identity()); } // A constrained snap, is a snap in only one degree of freedom (specified by the constraint line). @@ -665,7 +664,7 @@ void Inkscape::ObjectSnapper::constrainedSnap( SnappedConstraints &sc, // so we will more or less snap to them anyhow. if (_snapmanager->snapprefs.getSnapToItemPath() || _snapmanager->snapprefs.getSnapToBBoxPath() || _snapmanager->snapprefs.getSnapToPageBorder()) { - _snapPathsConstrained(sc, t, p, source_type, first_point, c); + _snapPathsConstrained(sc, t, p, source_type, 0, c); } } @@ -723,13 +722,13 @@ void Inkscape::ObjectSnapper::guideConstrainedSnap(SnappedConstraints &sc, bool Inkscape::ObjectSnapper::ThisSnapperMightSnap() const { bool snap_to_something = _snapmanager->snapprefs.getSnapToItemPath() - || _snapmanager->snapprefs.getSnapToItemNode() - || _snapmanager->snapprefs.getSnapToBBoxPath() - || _snapmanager->snapprefs.getSnapToBBoxNode() - || _snapmanager->snapprefs.getSnapToPageBorder() - || _snapmanager->snapprefs.getSnapLineMidpoints() || _snapmanager->snapprefs.getSnapObjectMidpoints() - || _snapmanager->snapprefs.getSnapBBoxEdgeMidpoints() || _snapmanager->snapprefs.getSnapBBoxMidpoints() - || _snapmanager->snapprefs.getIncludeItemCenter(); + || _snapmanager->snapprefs.getSnapToItemNode() + || _snapmanager->snapprefs.getSnapToBBoxPath() + || _snapmanager->snapprefs.getSnapToBBoxNode() + || _snapmanager->snapprefs.getSnapToPageBorder() + || _snapmanager->snapprefs.getSnapLineMidpoints() || _snapmanager->snapprefs.getSnapObjectMidpoints() + || _snapmanager->snapprefs.getSnapBBoxEdgeMidpoints() || _snapmanager->snapprefs.getSnapBBoxMidpoints() + || _snapmanager->snapprefs.getIncludeItemCenter(); return (_snap_enabled && _snapmanager->snapprefs.getSnapModeBBoxOrNodes() && snap_to_something); } @@ -737,12 +736,12 @@ bool Inkscape::ObjectSnapper::ThisSnapperMightSnap() const bool Inkscape::ObjectSnapper::GuidesMightSnap() const // almost the same as ThisSnapperMightSnap above, but only looking at points (and not paths) { bool snap_to_something = _snapmanager->snapprefs.getSnapToItemNode() - || _snapmanager->snapprefs.getSnapToPageBorder() - || (_snapmanager->snapprefs.getSnapModeBBox() && _snapmanager->snapprefs.getSnapToBBoxNode()) - || (_snapmanager->snapprefs.getSnapModeBBox() && (_snapmanager->snapprefs.getSnapBBoxEdgeMidpoints() || _snapmanager->snapprefs.getSnapBBoxMidpoints())) - || (_snapmanager->snapprefs.getSnapModeNode() && (_snapmanager->snapprefs.getSnapLineMidpoints() || _snapmanager->snapprefs.getSnapObjectMidpoints())) - || (_snapmanager->snapprefs.getSnapModeNode() && _snapmanager->snapprefs.getIncludeItemCenter()) - || (_snapmanager->snapprefs.getSnapModeNode() && (_snapmanager->snapprefs.getSnapToItemPath() && _snapmanager->snapprefs.getSnapIntersectionCS())); + || _snapmanager->snapprefs.getSnapToPageBorder() + || (_snapmanager->snapprefs.getSnapModeBBox() && _snapmanager->snapprefs.getSnapToBBoxNode()) + || (_snapmanager->snapprefs.getSnapModeBBox() && (_snapmanager->snapprefs.getSnapBBoxEdgeMidpoints() || _snapmanager->snapprefs.getSnapBBoxMidpoints())) + || (_snapmanager->snapprefs.getSnapModeNode() && (_snapmanager->snapprefs.getSnapLineMidpoints() || _snapmanager->snapprefs.getSnapObjectMidpoints())) + || (_snapmanager->snapprefs.getSnapModeNode() && _snapmanager->snapprefs.getIncludeItemCenter()) + || (_snapmanager->snapprefs.getSnapModeNode() && (_snapmanager->snapprefs.getSnapToItemPath() && _snapmanager->snapprefs.getSnapIntersectionCS())); return (_snap_enabled && _snapmanager->snapprefs.getSnapModeGuide() && snap_to_something); } @@ -784,21 +783,21 @@ void Inkscape::ObjectSnapper::_getBorderNodes(std::vector<std::pair<Geom::Point, void Inkscape::getBBoxPoints(Geom::OptRect const bbox, std::vector<std::pair<Geom::Point, int> > *points, bool const isTarget, bool const includeCorners, bool const includeLineMidpoints, bool const includeObjectMidpoints) { - if (bbox) { - // collect the corners of the bounding box - for ( unsigned k = 0 ; k < 4 ; k++ ) { - if (includeCorners) { - points->push_back(std::make_pair((bbox->corner(k)), isTarget ? int(Inkscape::SNAPTARGET_BBOX_CORNER) : int(Inkscape::SNAPSOURCE_BBOX_CORNER))); - } - // optionally, collect the midpoints of the bounding box's edges too - if (includeLineMidpoints) { - points->push_back(std::make_pair((bbox->corner(k) + bbox->corner((k+1) % 4))/2, isTarget ? int(Inkscape::SNAPTARGET_BBOX_EDGE_MIDPOINT) : int(Inkscape::SNAPSOURCE_BBOX_EDGE_MIDPOINT))); - } - } - if (includeObjectMidpoints) { - points->push_back(std::make_pair(bbox->midpoint(), isTarget ? int(Inkscape::SNAPTARGET_BBOX_MIDPOINT) : int(Inkscape::SNAPSOURCE_BBOX_MIDPOINT))); - } - } + if (bbox) { + // collect the corners of the bounding box + for ( unsigned k = 0 ; k < 4 ; k++ ) { + if (includeCorners) { + points->push_back(std::make_pair((bbox->corner(k)), isTarget ? int(Inkscape::SNAPTARGET_BBOX_CORNER) : int(Inkscape::SNAPSOURCE_BBOX_CORNER))); + } + // optionally, collect the midpoints of the bounding box's edges too + if (includeLineMidpoints) { + points->push_back(std::make_pair((bbox->corner(k) + bbox->corner((k+1) % 4))/2, isTarget ? int(Inkscape::SNAPTARGET_BBOX_EDGE_MIDPOINT) : int(Inkscape::SNAPSOURCE_BBOX_EDGE_MIDPOINT))); + } + } + if (includeObjectMidpoints) { + points->push_back(std::make_pair(bbox->midpoint(), isTarget ? int(Inkscape::SNAPTARGET_BBOX_MIDPOINT) : int(Inkscape::SNAPSOURCE_BBOX_MIDPOINT))); + } + } } /* diff --git a/src/object-snapper.h b/src/object-snapper.h index baa60a096..2fcafb79a 100644 --- a/src/object-snapper.h +++ b/src/object-snapper.h @@ -46,47 +46,47 @@ class ObjectSnapper : public Snapper { public: - ObjectSnapper(SnapManager *sm, Geom::Coord const d); + ObjectSnapper(SnapManager *sm, Geom::Coord const d); ~ObjectSnapper(); - enum DimensionToSnap { - GUIDE_TRANSL_SNAP_X, // For snapping a vertical guide (normal in the X-direction) to objects, - GUIDE_TRANSL_SNAP_Y, // For snapping a horizontal guide (normal in the Y-direction) to objects - ANGLED_GUIDE_TRANSL_SNAP, // For snapping an angled guide, while translating it accross the desktop - TRANSL_SNAP_XY}; // All other cases; for snapping to objects, other than guides - - void guideFreeSnap(SnappedConstraints &sc, - Geom::Point const &p, - Geom::Point const &guide_normal) const; - - void guideConstrainedSnap(SnappedConstraints &sc, - Geom::Point const &p, - Geom::Point const &guide_normal, - ConstraintLine const &c) const; - - bool ThisSnapperMightSnap() const; - bool GuidesMightSnap() const; - - Geom::Coord getSnapperTolerance() const; //returns the tolerance of the snapper in screen pixels (i.e. independent of zoom) - bool getSnapperAlwaysSnap() const; //if true, then the snapper will always snap, regardless of its tolerance - - void freeSnap(SnappedConstraints &sc, - Inkscape::SnapPreferences::PointType const &t, - Geom::Point const &p, - SnapSourceType const &source_type, - bool const &first_point, - Geom::OptRect const &bbox_to_snap, - std::vector<SPItem const *> const *it, - std::vector<std::pair<Geom::Point, int> > *unselected_nodes) const; - - void constrainedSnap(SnappedConstraints &sc, - Inkscape::SnapPreferences::PointType const &t, - Geom::Point const &p, - SnapSourceType const &source_type, - bool const &first_point, - Geom::OptRect const &bbox_to_snap, - ConstraintLine const &c, - std::vector<SPItem const *> const *it) const; + enum DimensionToSnap { + GUIDE_TRANSL_SNAP_X, // For snapping a vertical guide (normal in the X-direction) to objects, + GUIDE_TRANSL_SNAP_Y, // For snapping a horizontal guide (normal in the Y-direction) to objects + ANGLED_GUIDE_TRANSL_SNAP, // For snapping an angled guide, while translating it accross the desktop + TRANSL_SNAP_XY}; // All other cases; for snapping to objects, other than guides + + void guideFreeSnap(SnappedConstraints &sc, + Geom::Point const &p, + Geom::Point const &guide_normal) const; + + void guideConstrainedSnap(SnappedConstraints &sc, + Geom::Point const &p, + Geom::Point const &guide_normal, + ConstraintLine const &c) const; + + bool ThisSnapperMightSnap() const; + bool GuidesMightSnap() const; + + Geom::Coord getSnapperTolerance() const; //returns the tolerance of the snapper in screen pixels (i.e. independent of zoom) + bool getSnapperAlwaysSnap() const; //if true, then the snapper will always snap, regardless of its tolerance + + void freeSnap(SnappedConstraints &sc, + Inkscape::SnapPreferences::PointType const &t, + Geom::Point const &p, + SnapSourceType const &source_type, + long source_num, + Geom::OptRect const &bbox_to_snap, + std::vector<SPItem const *> const *it, + std::vector<std::pair<Geom::Point, int> > *unselected_nodes) const; + + void constrainedSnap(SnappedConstraints &sc, + Inkscape::SnapPreferences::PointType const &t, + Geom::Point const &p, + SnapSourceType const &source_type, + long source_num, + Geom::OptRect const &bbox_to_snap, + ConstraintLine const &c, + std::vector<SPItem const *> const *it) const; private: //store some lists of candidates, points and paths, so we don't have to rebuild them for each point we want to snap @@ -106,7 +106,7 @@ private: Inkscape::SnapPreferences::PointType const &t, Geom::Point const &p, // in desktop coordinates SnapSourceType const &source_type, - bool const &first_point, + long source_num, std::vector<std::pair<Geom::Point, int> > *unselected_nodes) const; // in desktop coordinates void _snapTranslatingGuideToNodes(SnappedConstraints &sc, @@ -119,9 +119,9 @@ private: void _snapPaths(SnappedConstraints &sc, Inkscape::SnapPreferences::PointType const &t, - Geom::Point const &p, // in desktop coordinates + Geom::Point const &p, // in desktop coordinates SnapSourceType const &source_type, - bool const &first_point, + long source_num, std::vector<std::pair<Geom::Point, int> > *unselected_nodes, // in desktop coordinates SPPath const *selected_path) const; @@ -129,7 +129,7 @@ private: Inkscape::SnapPreferences::PointType const &t, Geom::Point const &p, // in desktop coordinates SnapSourceType const source_type, - bool const &first_point, + long source_num, ConstraintLine const &c) const; bool isUnselectedNode(Geom::Point const &point, std::vector<std::pair<Geom::Point, int> > const *unselected_nodes) const; diff --git a/src/path-chemistry.cpp b/src/path-chemistry.cpp index 99ee78ade..204c82aed 100644 --- a/src/path-chemistry.cpp +++ b/src/path-chemistry.cpp @@ -86,12 +86,20 @@ sp_selected_path_combine(SPDesktop *desktop) SPItem *first = NULL; Inkscape::XML::Node *parent = NULL; + if (did) { + selection->clear(); + } + for (GSList *i = items; i != NULL; i = i->next) { // going from top to bottom SPItem *item = (SPItem *) i->data; if (!SP_IS_PATH(item)) continue; - did = true; + + if (!did) { + selection->clear(); + did = true; + } SPCurve *c = sp_path_get_curve_for_edit(SP_PATH(item)); if (first == NULL) { // this is the topmost path @@ -124,11 +132,7 @@ sp_selected_path_combine(SPDesktop *desktop) g_slist_free(items); if (did) { - selection->clear(); - - // delete the topmost one so that its clones don't get alerted; this object will be - // restored shortly, with the same id - SP_OBJECT(first)->deleteObject(false); + // delete the topmost. Inkscape::XML::Document *xml_doc = sp_document_repr_doc(desktop->doc()); Inkscape::XML::Node *repr = xml_doc->createElement("svg:path"); diff --git a/src/persp3d-reference.h b/src/persp3d-reference.h index 43b0e82b1..7c2ce31bf 100644 --- a/src/persp3d-reference.h +++ b/src/persp3d-reference.h @@ -12,9 +12,9 @@ #include "uri-references.h" #include <sigc++/sigc++.h> +#include "persp3d.h" class SPObject; -class Persp3D; namespace Inkscape { namespace XML { @@ -28,7 +28,7 @@ public: ~Persp3DReference(); Persp3D *getObject() const { - return (Persp3D *)URIReference::getObject(); + return SP_PERSP3D(URIReference::getObject()); } SPObject *owner; diff --git a/src/persp3d.cpp b/src/persp3d.cpp index 916e9f25f..6a697ec9b 100644 --- a/src/persp3d.cpp +++ b/src/persp3d.cpp @@ -24,7 +24,7 @@ #include <glibmm/i18n.h> static void persp3d_class_init(Persp3DClass *klass); -static void persp3d_init(Persp3D *stop); +static void persp3d_init(Persp3D *persp); static void persp3d_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr); static void persp3d_release(SPObject *object); @@ -34,10 +34,22 @@ static Inkscape::XML::Node *persp3d_write(SPObject *object, Inkscape::XML::Docum static void persp3d_on_repr_attr_changed (Inkscape::XML::Node * repr, const gchar *key, const gchar *oldval, const gchar *newval, bool is_interactive, void * data); +static void persp3d_update_with_point (Persp3DImpl *persp_impl, Proj::Axis const axis, Proj::Pt2 const &new_image); +static gchar * persp3d_pt_to_str (Persp3DImpl *persp_impl, Proj::Axis const axis); + static SPObjectClass *persp3d_parent_class; static int global_counter = 0; +/* Constructor/destructor for the internal class */ + +Persp3DImpl::Persp3DImpl() { + tmat = Proj::TransfMat3x4 (); + document = NULL; + + my_counter = global_counter++; +} + /** * Registers Persp3d class and returns its type. */ @@ -91,13 +103,7 @@ static void persp3d_class_init(Persp3DClass *klass) static void persp3d_init(Persp3D *persp) { - persp->tmat = Proj::TransfMat3x4 (); - - persp->boxes_transformed = new std::map<SPBox3D *, bool>; - persp->boxes_transformed->clear(); - persp->document = NULL; - - persp->my_counter = global_counter++; + persp->perspective_impl = new Persp3DImpl(); } /** @@ -124,8 +130,8 @@ static void persp3d_build(SPObject *object, SPDocument *document, Inkscape::XML: * Virtual release of Persp3D members before destruction. */ static void persp3d_release(SPObject *object) { - Persp3D *persp = SP_PERSP3D (object); - delete persp->boxes_transformed; + Persp3D *persp = SP_PERSP3D(object); + delete persp->perspective_impl; SP_OBJECT_REPR(object)->removeListenerByData(object); } @@ -138,34 +144,34 @@ static void persp3d_release(SPObject *object) { static void persp3d_set(SPObject *object, unsigned key, gchar const *value) { - Persp3D *persp = SP_PERSP3D (object); + Persp3DImpl *persp_impl = SP_PERSP3D(object)->perspective_impl; switch (key) { case SP_ATTR_INKSCAPE_PERSP3D_VP_X: { if (value) { Proj::Pt2 new_image (value); - persp3d_update_with_point (persp, Proj::X, new_image); + persp3d_update_with_point (persp_impl, Proj::X, new_image); } break; } case SP_ATTR_INKSCAPE_PERSP3D_VP_Y: { if (value) { Proj::Pt2 new_image (value); - persp3d_update_with_point (persp, Proj::Y, new_image); + persp3d_update_with_point (persp_impl, Proj::Y, new_image); break; } } case SP_ATTR_INKSCAPE_PERSP3D_VP_Z: { if (value) { Proj::Pt2 new_image (value); - persp3d_update_with_point (persp, Proj::Z, new_image); + persp3d_update_with_point (persp_impl, Proj::Z, new_image); break; } } case SP_ATTR_INKSCAPE_PERSP3D_ORIGIN: { if (value) { Proj::Pt2 new_image (value); - persp3d_update_with_point (persp, Proj::W, new_image); + persp3d_update_with_point (persp_impl, Proj::W, new_image); break; } } @@ -201,36 +207,40 @@ persp3d_update(SPObject *object, SPCtx *ctx, guint flags) } Persp3D * -persp3d_create_xml_element (SPDocument *document, Persp3D *dup) {// if dup is given, copy the attributes over +persp3d_create_xml_element (SPDocument *document, Persp3DImpl *dup) {// if dup is given, copy the attributes over SPDefs *defs = (SPDefs *) SP_DOCUMENT_DEFS(document); Inkscape::XML::Document *xml_doc = sp_document_repr_doc(document); Inkscape::XML::Node *repr; - if (dup) { - repr = SP_OBJECT_REPR(dup)->duplicate (xml_doc); - } else { - /* if no perspective is given, create a default one */ - repr = xml_doc->createElement("inkscape:perspective"); - repr->setAttribute("sodipodi:type", "inkscape:persp3d"); - Proj::Pt2 proj_vp_x = Proj::Pt2 (0.0, sp_document_height(document)/2, 1.0); - Proj::Pt2 proj_vp_y = Proj::Pt2 ( 0.0,1000.0, 0.0); - Proj::Pt2 proj_vp_z = Proj::Pt2 (sp_document_width(document), sp_document_height(document)/2, 1.0); - Proj::Pt2 proj_origin = Proj::Pt2 (sp_document_width(document)/2, sp_document_height(document)/3, 1.0); + /* if no perspective is given, create a default one */ + repr = xml_doc->createElement("inkscape:perspective"); + repr->setAttribute("sodipodi:type", "inkscape:persp3d"); - gchar *str = NULL; - str = proj_vp_x.coord_string(); - repr->setAttribute("inkscape:vp_x", str); - g_free (str); - str = proj_vp_y.coord_string(); - repr->setAttribute("inkscape:vp_y", str); - g_free (str); - str = proj_vp_z.coord_string(); - repr->setAttribute("inkscape:vp_z", str); - g_free (str); - str = proj_origin.coord_string(); - repr->setAttribute("inkscape:persp3d-origin", str); - g_free (str); - } + Proj::Pt2 proj_vp_x = Proj::Pt2 (0.0, sp_document_height(document)/2, 1.0); + Proj::Pt2 proj_vp_y = Proj::Pt2 (0.0, 1000.0, 0.0); + Proj::Pt2 proj_vp_z = Proj::Pt2 (sp_document_width(document), sp_document_height(document)/2, 1.0); + Proj::Pt2 proj_origin = Proj::Pt2 (sp_document_width(document)/2, sp_document_height(document)/3, 1.0); + + if (dup) { + proj_vp_x = dup->tmat.column (Proj::X); + proj_vp_y = dup->tmat.column (Proj::Y); + proj_vp_z = dup->tmat.column (Proj::Z); + proj_origin = dup->tmat.column (Proj::W); + } + + gchar *str = NULL; + str = proj_vp_x.coord_string(); + repr->setAttribute("inkscape:vp_x", str); + g_free (str); + str = proj_vp_y.coord_string(); + repr->setAttribute("inkscape:vp_y", str); + g_free (str); + str = proj_vp_z.coord_string(); + repr->setAttribute("inkscape:vp_z", str); + g_free (str); + str = proj_origin.coord_string(); + repr->setAttribute("inkscape:persp3d-origin", str); + g_free (str); /* Append the new persp3d to defs */ SP_OBJECT_REPR(defs)->addChild(repr, NULL); @@ -258,7 +268,7 @@ persp3d_document_first_persp (SPDocument *document) { static Inkscape::XML::Node * persp3d_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags) { - Persp3D *persp = SP_PERSP3D(object); + Persp3DImpl *persp_impl = SP_PERSP3D(object)->perspective_impl; if ((flags & SP_OBJECT_WRITE_BUILD & SP_OBJECT_WRITE_EXT) && !repr) { // this is where we end up when saving as plain SVG (also in other circumstances?); @@ -268,16 +278,16 @@ persp3d_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML: if (flags & SP_OBJECT_WRITE_EXT) { gchar *str = NULL; // FIXME: Should this be freed each time we set an attribute or only in the end or at all? - str = persp3d_pt_to_str (persp, Proj::X); + str = persp3d_pt_to_str (persp_impl, Proj::X); repr->setAttribute("inkscape:vp_x", str); - str = persp3d_pt_to_str (persp, Proj::Y); + str = persp3d_pt_to_str (persp_impl, Proj::Y); repr->setAttribute("inkscape:vp_y", str); - str = persp3d_pt_to_str (persp, Proj::Z); + str = persp3d_pt_to_str (persp_impl, Proj::Z); repr->setAttribute("inkscape:vp_z", str); - str = persp3d_pt_to_str (persp, Proj::W); + str = persp3d_pt_to_str (persp_impl, Proj::W); repr->setAttribute("inkscape:persp3d-origin", str); } @@ -289,7 +299,7 @@ persp3d_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML: /* convenience wrapper around persp3d_get_finite_dir() and persp3d_get_infinite_dir() */ Geom::Point persp3d_get_PL_dir_from_pt (Persp3D *persp, Geom::Point const &pt, Proj::Axis axis) { - if (persp3d_VP_is_finite(persp, axis)) { + if (persp3d_VP_is_finite(persp->perspective_impl, axis)) { return persp3d_get_finite_dir(persp, pt, axis); } else { return persp3d_get_infinite_dir(persp, axis); @@ -314,17 +324,17 @@ persp3d_get_infinite_dir (Persp3D *persp, Proj::Axis axis) { double persp3d_get_infinite_angle (Persp3D *persp, Proj::Axis axis) { - return persp->tmat.get_infinite_angle(axis); + return persp->perspective_impl->tmat.get_infinite_angle(axis); } bool -persp3d_VP_is_finite (Persp3D *persp, Proj::Axis axis) { - return persp->tmat.has_finite_image(axis); +persp3d_VP_is_finite (Persp3DImpl *persp_impl, Proj::Axis axis) { + return persp_impl->tmat.has_finite_image(axis); } void persp3d_toggle_VP (Persp3D *persp, Proj::Axis axis, bool set_undo) { - persp->tmat.toggle_finite(axis); + persp->perspective_impl->tmat.toggle_finite(axis); // FIXME: Remove this repr update and rely on vp_drag_sel_modified() to do this for us // On the other hand, vp_drag_sel_modified() would update all boxes; // here we can confine ourselves to the boxes of this particular perspective. @@ -348,7 +358,7 @@ persp3d_toggle_VPs (std::list<Persp3D *> p, Proj::Axis axis) { void persp3d_set_VP_state (Persp3D *persp, Proj::Axis axis, Proj::VPState state) { - if (persp3d_VP_is_finite(persp, axis) != (state == Proj::VP_FINITE)) { + if (persp3d_VP_is_finite(persp->perspective_impl, axis) != (state == Proj::VP_FINITE)) { persp3d_toggle_VP(persp, axis); } } @@ -356,62 +366,67 @@ persp3d_set_VP_state (Persp3D *persp, Proj::Axis axis, Proj::VPState state) { void persp3d_rotate_VP (Persp3D *persp, Proj::Axis axis, double angle, bool alt_pressed) { // angle is in degrees // FIXME: Most of this functionality should be moved to trans_mat_3x4.(h|cpp) - if (persp->tmat.has_finite_image(axis)) { + if (persp->perspective_impl->tmat.has_finite_image(axis)) { // don't rotate anything for finite VPs return; } - Proj::Pt2 v_dir_proj (persp->tmat.column(axis)); + Proj::Pt2 v_dir_proj (persp->perspective_impl->tmat.column(axis)); Geom::Point v_dir (v_dir_proj[0], v_dir_proj[1]); double a = Geom::atan2 (v_dir) * 180/M_PI; a += alt_pressed ? 0.5 * ((angle > 0 ) - (angle < 0)) : angle; // the r.h.s. yields +/-0.5 or angle - persp->tmat.set_infinite_direction (axis, a); + persp->perspective_impl->tmat.set_infinite_direction (axis, a); persp3d_update_box_reprs (persp); SP_OBJECT(persp)->updateRepr(SP_OBJECT_WRITE_EXT); } void -persp3d_update_with_point (Persp3D *persp, Proj::Axis const axis, Proj::Pt2 const &new_image) { - persp->tmat.set_image_pt (axis, new_image); +persp3d_update_with_point (Persp3DImpl *persp_impl, Proj::Axis const axis, Proj::Pt2 const &new_image) { + persp_impl->tmat.set_image_pt (axis, new_image); } void persp3d_apply_affine_transformation (Persp3D *persp, Geom::Matrix const &xform) { - persp->tmat *= xform; + persp->perspective_impl->tmat *= xform; persp3d_update_box_reprs(persp); SP_OBJECT(persp)->updateRepr(SP_OBJECT_WRITE_EXT); } gchar * -persp3d_pt_to_str (Persp3D *persp, Proj::Axis const axis) +persp3d_pt_to_str (Persp3DImpl *persp_impl, Proj::Axis const axis) { - return persp->tmat.pt_to_str(axis); + return persp_impl->tmat.pt_to_str(axis); } void persp3d_add_box (Persp3D *persp, SPBox3D *box) { + Persp3DImpl *persp_impl = persp->perspective_impl; + if (!box) { return; } - if (std::find (persp->boxes.begin(), persp->boxes.end(), box) != persp->boxes.end()) { + if (std::find (persp_impl->boxes.begin(), persp_impl->boxes.end(), box) != persp_impl->boxes.end()) { return; } - persp->boxes.push_back(box); + persp_impl->boxes.push_back(box); } void persp3d_remove_box (Persp3D *persp, SPBox3D *box) { - std::vector<SPBox3D *>::iterator i = std::find (persp->boxes.begin(), persp->boxes.end(), box); - if (i != persp->boxes.end()) { - persp->boxes.erase(i); - } + Persp3DImpl *persp_impl = persp->perspective_impl; + + std::vector<SPBox3D *>::iterator i = std::find (persp_impl->boxes.begin(), persp_impl->boxes.end(), box); + if (i != persp_impl->boxes.end()) + persp_impl->boxes.erase(i); } bool persp3d_has_box (Persp3D *persp, SPBox3D *box) { + Persp3DImpl *persp_impl = persp->perspective_impl; + // FIXME: For some reason, std::find() does not seem to compare pointers "correctly" (or do we need to // provide a proper comparison function?), so we manually traverse the list. - for (std::vector<SPBox3D *>::iterator i = persp->boxes.begin(); i != persp->boxes.end(); ++i) { + for (std::vector<SPBox3D *>::iterator i = persp_impl->boxes.begin(); i != persp_impl->boxes.end(); ++i) { if ((*i) == box) { return true; } @@ -420,86 +435,27 @@ persp3d_has_box (Persp3D *persp, SPBox3D *box) { } void -persp3d_add_box_transform (Persp3D *persp, SPBox3D *box) { - std::map<SPBox3D *, bool>::iterator i = persp->boxes_transformed->find(box); - if (i != persp->boxes_transformed->end() && (*i).second == true) { - g_print ("Warning! In %s (%d): trying to add transform status for box %d twice when it's already listed as true.\n", SP_OBJECT_REPR(persp)->attribute("id"), persp->my_counter, box->my_counter); - return; - } - - (*persp->boxes_transformed)[box] = false; -} - -void -persp3d_remove_box_transform (Persp3D *persp, SPBox3D *box) { - persp->boxes_transformed->erase(box); -} +persp3d_update_box_displays (Persp3D *persp) { + Persp3DImpl *persp_impl = persp->perspective_impl; -void -persp3d_set_box_transformed (Persp3D *persp, SPBox3D *box, bool transformed) { - if (persp->boxes_transformed->find(box) == persp->boxes_transformed->end()) { - g_print ("Warning! In %s (%d): trying to set transform status for box %d, but it is not listed in the perspective!! Aborting.\n", - SP_OBJECT_REPR(persp)->attribute("id"), persp->my_counter, - box->my_counter); + if (persp_impl->boxes.empty()) return; - } - - (*persp->boxes_transformed)[box] = transformed; -} - -bool -persp3d_was_transformed (Persp3D *persp) { - if (persp->boxes_transformed->size() == 1) { - /* either the transform has not been applied to the single box associated to this perspective yet - or the transform was already reset; in both cases we need to return false because upcoming - transforms need to be applied */ - (*persp->boxes_transformed->begin()).second = false; // make sure the box is marked as untransformed (in case more boxes are added later) - return false; - } - - for (std::map<SPBox3D *, bool>::iterator i = persp->boxes_transformed->begin(); - i != persp->boxes_transformed->end(); ++i) { - if ((*i).second == true) { - // at least one of the boxes in the perspective has already been transformed; - return true; - } - } - return false; // all boxes in the perspective are still untransformed; a pending transformation should be applied -} - -bool -persp3d_all_transformed(Persp3D *persp) { - for (std::map<SPBox3D *, bool>::iterator i = persp->boxes_transformed->begin(); - i != persp->boxes_transformed->end(); ++i) { - if ((*i).second == false) { - return false; - } - } - return true; -} - -void -persp3d_unset_transforms(Persp3D *persp) { - for (std::map<SPBox3D *, bool>::iterator i = persp->boxes_transformed->begin(); - i != persp->boxes_transformed->end(); ++i) { - (*i).second = false; + for (std::vector<SPBox3D *>::iterator i = persp_impl->boxes.begin(); i != persp_impl->boxes.end(); ++i) { + box3d_position_set(*i); } } void -persp3d_update_box_displays (Persp3D *persp) { - if (persp->boxes.empty()) +persp3d_update_box_reprs (Persp3D *persp) { + if (!persp) { + // Hmm, is it an error if this happens? return; - for (std::vector<SPBox3D *>::iterator i = persp->boxes.begin(); i != persp->boxes.end(); ++i) { - box3d_position_set(*i); } -} + Persp3DImpl *persp_impl = persp->perspective_impl; -void -persp3d_update_box_reprs (Persp3D *persp) { - if (persp->boxes.empty()) + if (persp_impl->boxes.empty()) return; - for (std::vector<SPBox3D *>::iterator i = persp->boxes.begin(); i != persp->boxes.end(); ++i) { + for (std::vector<SPBox3D *>::iterator i = persp_impl->boxes.begin(); i != persp_impl->boxes.end(); ++i) { SP_OBJECT(*i)->updateRepr(SP_OBJECT_WRITE_EXT); box3d_set_z_orders(*i); } @@ -507,9 +463,11 @@ persp3d_update_box_reprs (Persp3D *persp) { void persp3d_update_z_orders (Persp3D *persp) { - if (persp->boxes.empty()) + Persp3DImpl *persp_impl = persp->perspective_impl; + + if (persp_impl->boxes.empty()) return; - for (std::vector<SPBox3D *>::iterator i = persp->boxes.begin(); i != persp->boxes.end(); ++i) { + for (std::vector<SPBox3D *>::iterator i = persp_impl->boxes.begin(); i != persp_impl->boxes.end(); ++i) { box3d_set_z_orders(*i); } } @@ -519,8 +477,10 @@ persp3d_update_z_orders (Persp3D *persp) { // obsolete. We should do this. std::list<SPBox3D *> persp3d_list_of_boxes(Persp3D *persp) { + Persp3DImpl *persp_impl = persp->perspective_impl; + std::list<SPBox3D *> bx_lst; - for (std::vector<SPBox3D *>::iterator i = persp->boxes.begin(); i != persp->boxes.end(); ++i) { + for (std::vector<SPBox3D *>::iterator i = persp_impl->boxes.begin(); i != persp_impl->boxes.end(); ++i) { bx_lst.push_back(*i); } return bx_lst; @@ -529,7 +489,7 @@ persp3d_list_of_boxes(Persp3D *persp) { bool persp3d_perspectives_coincide(const Persp3D *lhs, const Persp3D *rhs) { - return lhs->tmat == rhs->tmat; + return lhs->perspective_impl->tmat == rhs->perspective_impl->tmat; } void @@ -566,10 +526,12 @@ persp3d_on_repr_attr_changed ( Inkscape::XML::Node * /*repr*/, /* checks whether all boxes linked to this perspective are currently selected */ bool -persp3d_has_all_boxes_in_selection (Persp3D *persp) { - std::list<SPBox3D *> selboxes = sp_desktop_selection(inkscape_active_desktop())->box3DList(); +persp3d_has_all_boxes_in_selection (Persp3D *persp, Inkscape::Selection *selection) { + Persp3DImpl *persp_impl = persp->perspective_impl; + + std::list<SPBox3D *> selboxes = selection->box3DList(); - for (std::vector<SPBox3D *>::iterator i = persp->boxes.begin(); i != persp->boxes.end(); ++i) { + for (std::vector<SPBox3D *>::iterator i = persp_impl->boxes.begin(); i != persp_impl->boxes.end(); ++i) { if (std::find(selboxes.begin(), selboxes.end(), *i) == selboxes.end()) { // we have an unselected box in the perspective return false; @@ -578,62 +540,12 @@ persp3d_has_all_boxes_in_selection (Persp3D *persp) { return true; } -/** - * For each perspective having a box in \a selection, determine all its unselected boxes. - */ -// TODO: Check where we can use pass-by-reference (or so) instead of recreating all the lists afresh. -std::map<Persp3D *, std::list<SPBox3D *> > -persp3d_unselected_boxes(Inkscape::Selection *selection) { - std::list<Persp3D *> plist = selection->perspList(); - std::map<Persp3D *, std::list<SPBox3D *> > punsel; - - std::list<Persp3D *>::iterator i; - std::vector<SPBox3D *>::iterator j; - // for all perspectives in the list ... - for (i = plist.begin(); i != plist.end(); ++i) { - Persp3D *persp = *i; - // ... and each box associated to it ... - for (j = persp->boxes.begin(); j != persp->boxes.end(); ++j) { - SPBox3D *box = *j; - // ... check whether it is unselected, and if so add it to the list - if (persp->boxes_transformed->find(box) == persp->boxes_transformed->end()) { - punsel[persp].push_back(box); - } - } - } - return punsel; -} - -/** - * Split all perspectives with a box in \a selection by moving their unselected boxes to newly - * created perspectives. - */ -void -persp3d_split_perspectives_according_to_selection(Inkscape::Selection *selection) { - std::map<Persp3D *, std::list<SPBox3D *> > punsel = persp3d_unselected_boxes(selection); - - std::map<Persp3D *, std::list<SPBox3D *> >::iterator i; - std::list<SPBox3D *>::iterator j; - // for all perspectives in the list ... - for (i = punsel.begin(); i != punsel.end(); ++i) { - Persp3D *persp = (*i).first; - // ... if the perspective has unselected boxes ... - if (!(*i).second.empty()) { - // create a new perspective and move these boxes over - Persp3D * new_persp = persp3d_create_xml_element (SP_OBJECT_DOCUMENT(persp), persp); - for (j = (*i).second.begin(); j != (*i).second.end(); ++j) { - SPBox3D *box = *j; - box3d_switch_perspectives(box, persp, new_persp); - } - } - } -} - /* some debugging stuff follows */ void persp3d_print_debugging_info (Persp3D *persp) { - g_print ("=== Info for Persp3D %d ===\n", persp->my_counter); + Persp3DImpl *persp_impl = persp->perspective_impl; + g_print ("=== Info for Persp3D %d ===\n", persp_impl->my_counter); gchar * cstr; for (int i = 0; i < 4; ++i) { cstr = persp3d_get_VP(persp, Proj::axes[i]).coord_string(); @@ -645,8 +557,8 @@ persp3d_print_debugging_info (Persp3D *persp) { g_free(cstr); g_print (" Boxes: "); - for (std::vector<SPBox3D *>::iterator i = persp->boxes.begin(); i != persp->boxes.end(); ++i) { - g_print ("%d (%d) ", (*i)->my_counter, box3d_get_perspective(*i)->my_counter); + for (std::vector<SPBox3D *>::iterator i = persp_impl->boxes.begin(); i != persp_impl->boxes.end(); ++i) { + g_print ("%d (%d) ", (*i)->my_counter, box3d_get_perspective(*i)->perspective_impl->my_counter); } g_print ("\n"); g_print ("========================\n"); @@ -674,16 +586,23 @@ persp3d_print_all_selected() { for (std::list<Persp3D *>::iterator j = sel_persps.begin(); j != sel_persps.end(); ++j) { Persp3D *persp = SP_PERSP3D(*j); - g_print (" %s (%d): ", SP_OBJECT_REPR(persp)->attribute("id"), persp->my_counter); - for (std::map<SPBox3D *, bool>::iterator i = persp->boxes_transformed->begin(); - i != persp->boxes_transformed->end(); ++i) { - g_print ("<%d,%d> ", (*i).first->my_counter, (*i).second); + Persp3DImpl *persp_impl = persp->perspective_impl; + g_print (" %s (%d): ", SP_OBJECT_REPR(persp)->attribute("id"), persp->perspective_impl->my_counter); + for (std::vector<SPBox3D *>::iterator i = persp_impl->boxes.begin(); + i != persp_impl->boxes.end(); ++i) { + g_print ("%d ", (*i)->my_counter); } g_print ("\n"); } g_print ("======================================\n\n"); } +void print_current_persp3d(gchar *func_name, Persp3D *persp) { + g_print ("%s: current_persp3d is now %s\n", + func_name, + persp ? SP_OBJECT_REPR(persp)->attribute("id") : "NULL"); +} + /* Local Variables: mode:c++ diff --git a/src/persp3d.h b/src/persp3d.h index 79bec0232..62cc586ef 100644 --- a/src/persp3d.h +++ b/src/persp3d.h @@ -29,16 +29,25 @@ class SPBox3D; class Box3DContext; -struct Persp3D : public SPObject { +class Persp3DImpl { +public: + Persp3DImpl(); + +//private: Proj::TransfMat3x4 tmat; // Also write the list of boxes into the xml repr and vice versa link boxes to their persp3d? std::vector<SPBox3D *> boxes; - std::map<SPBox3D *, bool>* boxes_transformed; // TODO: eventually we should merge this with 'boxes' - SPDocument *document; // should this rather be the SPDesktop? + SPDocument *document; // for debugging only int my_counter; + +// friend class Persp3D; +}; + +struct Persp3D : public SPObject { + Persp3DImpl *perspective_impl; }; struct Persp3DClass { @@ -54,52 +63,43 @@ inline Persp3D * persp3d_get_from_repr (Inkscape::XML::Node *repr) { return SP_PERSP3D(SP_ACTIVE_DOCUMENT->getObjectByRepr(repr)); } inline Proj::Pt2 persp3d_get_VP (Persp3D *persp, Proj::Axis axis) { - return persp->tmat.column(axis); + return persp->perspective_impl->tmat.column(axis); } Geom::Point persp3d_get_PL_dir_from_pt (Persp3D *persp, Geom::Point const &pt, Proj::Axis axis); // convenience wrapper around the following two Geom::Point persp3d_get_finite_dir (Persp3D *persp, Geom::Point const &pt, Proj::Axis axis); Geom::Point persp3d_get_infinite_dir (Persp3D *persp, Proj::Axis axis); double persp3d_get_infinite_angle (Persp3D *persp, Proj::Axis axis); -bool persp3d_VP_is_finite (Persp3D *persp, Proj::Axis axis); +bool persp3d_VP_is_finite (Persp3DImpl *persp_impl, Proj::Axis axis); void persp3d_toggle_VP (Persp3D *persp, Proj::Axis axis, bool set_undo = true); void persp3d_toggle_VPs (std::list<Persp3D *>, Proj::Axis axis); void persp3d_set_VP_state (Persp3D *persp, Proj::Axis axis, Proj::VPState state); void persp3d_rotate_VP (Persp3D *persp, Proj::Axis axis, double angle, bool alt_pressed); // angle is in degrees -void persp3d_update_with_point (Persp3D *persp, Proj::Axis const axis, Proj::Pt2 const &new_image); void persp3d_apply_affine_transformation (Persp3D *persp, Geom::Matrix const &xform); -gchar * persp3d_pt_to_str (Persp3D *persp, Proj::Axis const axis); void persp3d_add_box (Persp3D *persp, SPBox3D *box); void persp3d_remove_box (Persp3D *persp, SPBox3D *box); bool persp3d_has_box (Persp3D *persp, SPBox3D *box); -void persp3d_add_box_transform (Persp3D *persp, SPBox3D *box); -void persp3d_remove_box_transform (Persp3D *persp, SPBox3D *box); -void persp3d_set_box_transformed (Persp3D *persp, SPBox3D *box, bool transformed = true); -bool persp3d_was_transformed (Persp3D *persp); -bool persp3d_all_transformed(Persp3D *persp); -void persp3d_unset_transforms(Persp3D *persp); - void persp3d_update_box_displays (Persp3D *persp); void persp3d_update_box_reprs (Persp3D *persp); void persp3d_update_z_orders (Persp3D *persp); -inline unsigned int persp3d_num_boxes (Persp3D *persp) { return persp->boxes.size(); } +inline unsigned int persp3d_num_boxes (Persp3D *persp) { return persp->perspective_impl->boxes.size(); } std::list<SPBox3D *> persp3d_list_of_boxes(Persp3D *persp); bool persp3d_perspectives_coincide(const Persp3D *lhs, const Persp3D *rhs); void persp3d_absorb(Persp3D *persp1, Persp3D *persp2); -Persp3D * persp3d_create_xml_element (SPDocument *document, Persp3D *dup = NULL); +Persp3D * persp3d_create_xml_element (SPDocument *document, Persp3DImpl *dup = NULL); Persp3D * persp3d_document_first_persp (SPDocument *document); -bool persp3d_has_all_boxes_in_selection (Persp3D *persp); -std::map<Persp3D *, std::list<SPBox3D *> > persp3d_unselected_boxes(Inkscape::Selection *selection); -void persp3d_split_perspectives_according_to_selection(Inkscape::Selection *selection); +bool persp3d_has_all_boxes_in_selection (Persp3D *persp, Inkscape::Selection *selection); void persp3d_print_debugging_info (Persp3D *persp); void persp3d_print_debugging_info_all(SPDocument *doc); void persp3d_print_all_selected(); +void print_current_persp3d(gchar *func_name, Persp3D *persp); + #endif /* __PERSP3D_H__ */ /* diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp index e55bba2a5..31e899d8a 100644 --- a/src/selection-chemistry.cpp +++ b/src/selection-chemistry.cpp @@ -76,6 +76,7 @@ #include "helper/units.h" #include "sp-item.h" #include "box3d.h" +#include "persp3d.h" #include "unit-constants.h" #include "xml/simple-document.h" #include "sp-filter-reference.h" @@ -1171,7 +1172,6 @@ selection_contains_both_clone_and_original(Inkscape::Selection *selection) return clone_with_original; } - /** Apply matrix to the selection. \a set_i2d is normally true, which means objects are in the original transform, synced with their reprs, and need to jump to the new transform in one go. A value of set_i2d==false is only used by seltrans when it's dragging objects live (not outlines); in @@ -1183,6 +1183,29 @@ void sp_selection_apply_affine(Inkscape::Selection *selection, Geom::Matrix cons if (selection->isEmpty()) return; + // For each perspective with a box in selection, check whether all boxes are selected and + // unlink all non-selected boxes. + Persp3D *persp; + Persp3D *transf_persp; + std::list<Persp3D *> plist = selection->perspList(); + for (std::list<Persp3D *>::iterator i = plist.begin(); i != plist.end(); ++i) { + persp = (Persp3D *) (*i); + + if (!persp3d_has_all_boxes_in_selection (persp, selection)) { + std::list<SPBox3D *> selboxes = selection->box3DList(persp); + + // create a new perspective as a copy of the current one and link the selected boxes to it + transf_persp = persp3d_create_xml_element (SP_OBJECT_DOCUMENT(persp), persp->perspective_impl); + + for (std::list<SPBox3D *>::iterator b = selboxes.begin(); b != selboxes.end(); ++b) + box3d_switch_perspectives(*b, persp, transf_persp); + } else { + transf_persp = persp; + } + + persp3d_apply_affine_transformation(transf_persp, affine); + } + for (GSList const *l = selection->itemList(); l != NULL; l = l->next) { SPItem *item = SP_ITEM(l->data); diff --git a/src/selection.cpp b/src/selection.cpp index 4d92a18df..7b936587c 100644 --- a/src/selection.cpp +++ b/src/selection.cpp @@ -162,24 +162,12 @@ void Selection::add(SPObject *obj, bool persist_selection_context/* = false */) _emitChanged(persist_selection_context); } -void Selection::add_box_perspective(SPBox3D *box) { - Persp3D *persp = box3d_get_perspective(box); - std::map<Persp3D *, unsigned int>::iterator p = _persps.find(persp); - if (p != _persps.end()) { - (*p).second++; - } else { - _persps[persp] = 1; - } -} - void Selection::add_3D_boxes_recursively(SPObject *obj) { std::list<SPBox3D *> boxes = box3d_extract_boxes(obj); for (std::list<SPBox3D *>::iterator i = boxes.begin(); i != boxes.end(); ++i) { SPBox3D *box = *i; - box3d_add_to_selection(box); _3dboxes.push_back(box); - add_box_perspective(box); } } @@ -220,33 +208,17 @@ void Selection::remove(SPObject *obj) { _emitChanged(); } -void Selection::remove_box_perspective(SPBox3D *box) { - Persp3D *persp = box3d_get_perspective(box); - std::map<Persp3D *, unsigned int>::iterator p = _persps.find(persp); - if (p == _persps.end()) { - g_print ("Warning! Trying to remove unselected perspective from selection!\n"); - return; - } - if ((*p).second > 1) { - _persps[persp]--; - } else { - _persps.erase(p); - } -} - void Selection::remove_3D_boxes_recursively(SPObject *obj) { std::list<SPBox3D *> boxes = box3d_extract_boxes(obj); for (std::list<SPBox3D *>::iterator i = boxes.begin(); i != boxes.end(); ++i) { SPBox3D *box = *i; - box3d_remove_from_selection(box); std::list<SPBox3D *>::iterator b = std::find(_3dboxes.begin(), _3dboxes.end(), box); if (b == _3dboxes.end()) { g_print ("Warning! Trying to remove unselected box from selection.\n"); return; } _3dboxes.erase(b); - remove_box_perspective(box); } } @@ -344,14 +316,27 @@ GSList const *Selection::reprList() { std::list<Persp3D *> const Selection::perspList() { std::list<Persp3D *> pl; - for (std::map<Persp3D *, unsigned int>::iterator p = _persps.begin(); p != _persps.end(); ++p) { - pl.push_back((*p).first); + for (std::list<SPBox3D *>::iterator i = _3dboxes.begin(); i != _3dboxes.end(); ++i) { + Persp3D *persp = box3d_get_perspective(*i); + if (std::find(pl.begin(), pl.end(), persp) == pl.end()) + pl.push_back(persp); } return pl; } -std::list<SPBox3D *> const Selection::box3DList() { - return _3dboxes; +std::list<SPBox3D *> const Selection::box3DList(Persp3D *persp) { + std::list<SPBox3D *> boxes; + if (persp) { + SPBox3D *box; + for (std::list<SPBox3D *>::iterator i = _3dboxes.begin(); i != _3dboxes.end(); ++i) { + box = *i; + if (persp == box3d_get_perspective(box)) + boxes.push_back(box); + } + } else { + boxes = _3dboxes; + } + return boxes; } SPObject *Selection::single() { @@ -455,7 +440,7 @@ std::vector<std::pair<Geom::Point, int> > Selection::getSnapPoints(SnapPreferenc //Include the transformation origin for snapping //For a selection or group only the overall origin is considered if (snapprefs != NULL && snapprefs->getIncludeItemCenter()) { - p.push_back(std::make_pair(this_item->getCenter(), SNAPSOURCE_ROTATION_CENTER)); + p.push_back(std::make_pair(this_item->getCenter(), SNAPSOURCE_ROTATION_CENTER)); } } @@ -467,12 +452,12 @@ std::vector<std::pair<Geom::Point, int> > Selection::getSnapPointsConvexHull(Sna std::vector<std::pair<Geom::Point, int> > p; for (GSList const *iter = items; iter != NULL; iter = iter->next) { - sp_item_snappoints(SP_ITEM(iter->data), false, p, snapprefs); + sp_item_snappoints(SP_ITEM(iter->data), false, p, snapprefs); } std::vector<std::pair<Geom::Point, int> > pHull; if (!p.empty()) { - std::vector<std::pair<Geom::Point, int> >::iterator i; + std::vector<std::pair<Geom::Point, int> >::iterator i; Geom::RectHull cvh((p.front()).first); for (i = p.begin(); i != p.end(); i++) { // these are the points we get back diff --git a/src/selection.h b/src/selection.h index 8eacf0d46..a0734c299 100644 --- a/src/selection.h +++ b/src/selection.h @@ -229,11 +229,14 @@ public: /// method for that GSList const *reprList(); - /* list of all perspectives which have a 3D box in the current selection + /** @brief Returns a list of all perspectives which have a 3D box in the current selection (these may also be nested in groups) */ std::list<Persp3D *> const perspList(); - std::list<SPBox3D *> const box3DList(); + /** @brief Returns a list of all 3D boxes in the current selection which are associated to @c + persp. If @c pers is @c NULL, return all selected boxes. + */ + std::list<SPBox3D *> const box3DList(Persp3D *persp = NULL); /** @brief Returns the number of layers in which there are selected objects */ guint numberOfLayers(); @@ -353,7 +356,6 @@ private: void remove_box_perspective(SPBox3D *box); void remove_3D_boxes_recursively(SPObject *obj); - std::map<Persp3D *, unsigned int> _persps; std::list<SPBox3D *> _3dboxes; GC::soft_ptr<SPDesktop> _desktop; diff --git a/src/snap.cpp b/src/snap.cpp index 545607889..2e38e4f14 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -164,18 +164,18 @@ bool SnapManager::gridSnapperMightSnap() const * \param point_type Category of points to which the source point belongs: node, guide or bounding box * \param p Current position of the snap source; will be overwritten by the position of the snap target if snapping has occurred * \param source_type Detailed description of the source type, will be used by the snap indicator - * \param first_point If true then this point is the first one from a set of points, all from the same selection and having the same transformation + * \param source_num Sequence number of the source point within the set of points that is to be snapped. Starting at zero * \param bbox_to_snap Bounding box hulling the set of points, all from the same selection and having the same transformation */ void SnapManager::freeSnapReturnByRef(Inkscape::SnapPreferences::PointType point_type, Geom::Point &p, Inkscape::SnapSourceType const source_type, - bool first_point, + long source_num, Geom::OptRect const &bbox_to_snap) const { //TODO: PointType and source_type are somewhat redundant; can't we get rid of the point_type parameter? - Inkscape::SnappedPoint const s = freeSnap(point_type, p, source_type, first_point, bbox_to_snap); + Inkscape::SnappedPoint const s = freeSnap(point_type, p, source_type, source_num, bbox_to_snap); s.getPoint(p); } @@ -194,7 +194,7 @@ void SnapManager::freeSnapReturnByRef(Inkscape::SnapPreferences::PointType point * \param point_type Category of points to which the source point belongs: node, guide or bounding box * \param p Current position of the snap source * \param source_type Detailed description of the source type, will be used by the snap indicator - * \param first_point If true then this point is the first one from a set of points, all from the same selection and having the same transformation + * \param source_num Sequence number of the source point within the set of points that is to be snapped. Starting at zero * \param bbox_to_snap Bounding box hulling the set of points, all from the same selection and having the same transformation * \return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics */ @@ -203,11 +203,11 @@ void SnapManager::freeSnapReturnByRef(Inkscape::SnapPreferences::PointType point Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::SnapPreferences::PointType point_type, Geom::Point const &p, Inkscape::SnapSourceType const &source_type, - bool first_point, + long source_num, Geom::OptRect const &bbox_to_snap) const { - if (!someSnapperMightSnap()) { - return Inkscape::SnappedPoint(p, source_type, Inkscape::SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false); + if (!someSnapperMightSnap()) { + return Inkscape::SnappedPoint(p, source_type, 0, Inkscape::SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false); } std::vector<SPItem const *> *items_to_ignore; @@ -224,7 +224,7 @@ Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::SnapPreferences::PointTyp SnapperList const snappers = getSnappers(); for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) { - (*i)->freeSnap(sc, point_type, p, source_type, first_point, bbox_to_snap, items_to_ignore, _unselected_nodes); + (*i)->freeSnap(sc, point_type, p, source_type, source_num, bbox_to_snap, items_to_ignore, _unselected_nodes); } if (_item_to_ignore) { @@ -280,7 +280,7 @@ Geom::Point SnapManager::multipleOfGridPitch(Geom::Point const &t) const Geom::Point const t_offset = t + grid->origin; SnappedConstraints sc; // Only the first three parameters are being used for grid snappers - snapper->freeSnap(sc, Inkscape::SnapPreferences::SNAPPOINT_NODE, t_offset, Inkscape::SNAPSOURCE_UNDEFINED, TRUE, Geom::OptRect(), NULL, NULL); + snapper->freeSnap(sc, Inkscape::SnapPreferences::SNAPPOINT_NODE, t_offset, Inkscape::SNAPSOURCE_UNDEFINED, 0, Geom::OptRect(), NULL, NULL); // Find the best snap for this grid, including intersections of the grid-lines Inkscape::SnappedPoint s = findBestSnap(t_offset, Inkscape::SNAPSOURCE_UNDEFINED, sc, false); if (s.getSnapped() && (s.getSnapDistance() < nearest_distance)) { @@ -323,7 +323,7 @@ Geom::Point SnapManager::multipleOfGridPitch(Geom::Point const &t) const * \param p Current position of the snap source; will be overwritten by the position of the snap target if snapping has occurred * \param source_type Detailed description of the source type, will be used by the snap indicator * \param constraint The direction or line along which snapping must occur - * \param first_point If true then this point is the first one from a set of points, all from the same selection and having the same transformation + * \param source_num Sequence number of the source point within the set of points that is to be snapped. Starting at zero * \param bbox_to_snap Bounding box hulling the set of points, all from the same selection and having the same transformation */ @@ -331,10 +331,10 @@ void SnapManager::constrainedSnapReturnByRef(Inkscape::SnapPreferences::PointTyp Geom::Point &p, Inkscape::SnapSourceType const source_type, Inkscape::Snapper::ConstraintLine const &constraint, - bool first_point, + long source_num, Geom::OptRect const &bbox_to_snap) const { - Inkscape::SnappedPoint const s = constrainedSnap(point_type, p, source_type, constraint, first_point, bbox_to_snap); + Inkscape::SnappedPoint const s = constrainedSnap(point_type, p, source_type, constraint, source_num, bbox_to_snap); s.getPoint(p); } @@ -353,7 +353,7 @@ void SnapManager::constrainedSnapReturnByRef(Inkscape::SnapPreferences::PointTyp * \param p Current position of the snap source * \param source_type Detailed description of the source type, will be used by the snap indicator * \param constraint The direction or line along which snapping must occur - * \param first_point If true then this point is the first one from a set of points, all from the same selection and having the same transformation + * \param source_num Sequence number of the source point within the set of points that is to be snapped. Starting at zero * \param bbox_to_snap Bounding box hulling the set of points, all from the same selection and having the same transformation */ @@ -361,11 +361,11 @@ Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::SnapPreferences::P Geom::Point const &p, Inkscape::SnapSourceType const &source_type, Inkscape::Snapper::ConstraintLine const &constraint, - bool first_point, + long source_num, Geom::OptRect const &bbox_to_snap) const { if (!someSnapperMightSnap()) { - return Inkscape::SnappedPoint(p, source_type, Inkscape::SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false); + return Inkscape::SnappedPoint(p, source_type, 0, Inkscape::SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false); } std::vector<SPItem const *> *items_to_ignore; @@ -386,7 +386,7 @@ Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::SnapPreferences::P SnappedConstraints sc; SnapperList const snappers = getSnappers(); for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) { - (*i)->constrainedSnap(sc, point_type, pp, source_type, first_point, bbox_to_snap, constraint, items_to_ignore); + (*i)->constrainedSnap(sc, point_type, pp, source_type, source_num, bbox_to_snap, constraint, items_to_ignore); } if (_item_to_ignore) { @@ -420,7 +420,7 @@ void SnapManager::guideFreeSnap(Geom::Point &p, Geom::Point const &guide_normal, Inkscape::SnapSourceType source_type = Inkscape::SNAPSOURCE_GUIDE_ORIGIN; if (drag_type == SP_DRAG_ROTATE) { - source_type = Inkscape::SNAPSOURCE_GUIDE; + source_type = Inkscape::SNAPSOURCE_GUIDE; } // Snap to nodes @@ -432,9 +432,9 @@ void SnapManager::guideFreeSnap(Geom::Point &p, Geom::Point const &guide_normal, // Snap to guides & grid lines SnapperList snappers = getGridSnappers(); snappers.push_back(&guide); - for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) { - (*i)->freeSnap(sc, Inkscape::SnapPreferences::SNAPPOINT_GUIDE, p, source_type, true, Geom::OptRect(), NULL, NULL); - } + for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) { + (*i)->freeSnap(sc, Inkscape::SnapPreferences::SNAPPOINT_GUIDE, p, source_type, 0, Geom::OptRect(), NULL, NULL); + } // Snap to intersections of curves, but not to the curves themselves! (see _snapTranslatingGuideToNodes in object-snapper.cpp) Inkscape::SnappedPoint const s = findBestSnap(p, source_type, sc, false, true); @@ -458,7 +458,7 @@ void SnapManager::guideFreeSnap(Geom::Point &p, Geom::Point const &guide_normal, void SnapManager::guideConstrainedSnap(Geom::Point &p, SPGuide const &guideline) const { - if (!snapprefs.getSnapEnabledGlobally() || snapprefs.getSnapPostponedGlobally()) { + if (!snapprefs.getSnapEnabledGlobally() || snapprefs.getSnapPostponedGlobally()) { return; } @@ -472,15 +472,15 @@ void SnapManager::guideConstrainedSnap(Geom::Point &p, SPGuide const &guideline) SnappedConstraints sc; Inkscape::Snapper::ConstraintLine cl(guideline.point_on_line, Geom::rot90(guideline.normal_to_line)); if (object.ThisSnapperMightSnap()) { - object.constrainedSnap(sc, Inkscape::SnapPreferences::SNAPPOINT_GUIDE, p, source_type, true, Geom::OptRect(), cl, NULL); + object.constrainedSnap(sc, Inkscape::SnapPreferences::SNAPPOINT_GUIDE, p, source_type, 0, Geom::OptRect(), cl, NULL); } // Snap to guides & grid lines - SnapperList snappers = getGridSnappers(); - snappers.push_back(&guide); - for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) { - (*i)->constrainedSnap(sc, Inkscape::SnapPreferences::SNAPPOINT_GUIDE, p, source_type, true, Geom::OptRect(), cl, NULL); - } + SnapperList snappers = getGridSnappers(); + snappers.push_back(&guide); + for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) { + (*i)->constrainedSnap(sc, Inkscape::SnapPreferences::SNAPPOINT_GUIDE, p, source_type, 0, Geom::OptRect(), cl, NULL); + } Inkscape::SnappedPoint const s = findBestSnap(p, source_type, sc, false); s.getPoint(p); @@ -566,6 +566,7 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed( g_assert(best_snapped_point.getAtIntersection() == false); std::vector<std::pair<Geom::Point, int> >::const_iterator j = transformed_points.begin(); + long source_num = 0; // std::cout << std::endl; for (std::vector<std::pair<Geom::Point, int> >::const_iterator i = points.begin(); i != points.end(); i++) { @@ -593,7 +594,7 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed( if (transformation_type == SCALE && !uniform) { g_warning("Non-uniform constrained scaling is not supported!"); } - snapped_point = constrainedSnap(type, (*j).first, static_cast<Inkscape::SnapSourceType>((*j).second), dedicated_constraint, i == points.begin(), bbox); + snapped_point = constrainedSnap(type, (*j).first, static_cast<Inkscape::SnapSourceType>((*j).second), dedicated_constraint, source_num, bbox); } else { bool const c1 = fabs(b[Geom::X]) < 1e-6; bool const c2 = fabs(b[Geom::Y]) < 1e-6; @@ -602,9 +603,9 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed( // move in that specific direction; therefore it should only snap in that direction, otherwise // we will get snapped points with an invalid transformation dedicated_constraint = Inkscape::Snapper::ConstraintLine(origin, component_vectors[c1]); - snapped_point = constrainedSnap(type, (*j).first, static_cast<Inkscape::SnapSourceType>((*j).second), dedicated_constraint, i == points.begin(), bbox); + snapped_point = constrainedSnap(type, (*j).first, static_cast<Inkscape::SnapSourceType>((*j).second), dedicated_constraint, source_num, bbox); } else { - snapped_point = freeSnap(type, (*j).first, static_cast<Inkscape::SnapSourceType>((*j).second), i == points.begin(), bbox); + snapped_point = freeSnap(type, (*j).first, static_cast<Inkscape::SnapSourceType>((*j).second), source_num, bbox); } } // std::cout << "dist = " << snapped_point.getSnapDistance() << std::endl; @@ -715,6 +716,7 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed( } j++; + source_num++; } Geom::Coord best_metric; @@ -917,10 +919,10 @@ Inkscape::SnappedPoint SnapManager::constrainedSnapSkew(Inkscape::SnapPreference */ Inkscape::SnappedPoint SnapManager::findBestSnap(Geom::Point const &p, - Inkscape::SnapSourceType const source_type, - SnappedConstraints &sc, - bool constrained, - bool noCurves) const + Inkscape::SnapSourceType const source_type, + SnappedConstraints &sc, + bool constrained, + bool noCurves) const { /* @@ -943,10 +945,10 @@ Inkscape::SnappedPoint SnapManager::findBestSnap(Geom::Point const &p, // search for the closest snapped curve if (!noCurves) { - Inkscape::SnappedCurve closestCurve; - if (getClosestCurve(sc.curves, closestCurve)) { - sp_list.push_back(Inkscape::SnappedPoint(closestCurve)); - } + Inkscape::SnappedCurve closestCurve; + if (getClosestCurve(sc.curves, closestCurve)) { + sp_list.push_back(Inkscape::SnappedPoint(closestCurve)); + } } if (snapprefs.getSnapIntersectionCS()) { @@ -1005,7 +1007,7 @@ Inkscape::SnappedPoint SnapManager::findBestSnap(Geom::Point const &p, } // now let's see which snapped point gets a thumbs up - Inkscape::SnappedPoint bestSnappedPoint = Inkscape::SnappedPoint(p, Inkscape::SNAPSOURCE_UNDEFINED, Inkscape::SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false); + Inkscape::SnappedPoint bestSnappedPoint = Inkscape::SnappedPoint(p, Inkscape::SNAPSOURCE_UNDEFINED, 0, Inkscape::SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false); // std::cout << "Finding the best snap..." << std::endl; for (std::list<Inkscape::SnappedPoint>::const_iterator i = sp_list.begin(); i != sp_list.end(); i++) { // first find out if this snapped point is within snapping range diff --git a/src/snap.h b/src/snap.h index e621bdb60..a3e463092 100644 --- a/src/snap.h +++ b/src/snap.h @@ -52,14 +52,14 @@ class SPNamedView; class SnapManager { public: - enum Transformation { + enum Transformation { TRANSLATION, SCALE, STRETCH, SKEW }; - SnapManager(SPNamedView const *v); + SnapManager(SPNamedView const *v); typedef std::list<const Inkscape::Snapper*> SnapperList; @@ -67,30 +67,30 @@ public: bool gridSnapperMightSnap() const; void setup(SPDesktop const *desktop, - bool snapindicator = true, - SPItem const *item_to_ignore = NULL, - std::vector<std::pair<Geom::Point, int> > *unselected_nodes = NULL, - SPGuide *guide_to_ignore = NULL); + bool snapindicator = true, + SPItem const *item_to_ignore = NULL, + std::vector<std::pair<Geom::Point, int> > *unselected_nodes = NULL, + SPGuide *guide_to_ignore = NULL); void setup(SPDesktop const *desktop, - bool snapindicator, - std::vector<SPItem const *> &items_to_ignore, - std::vector<std::pair<Geom::Point, int> > *unselected_nodes = NULL, - SPGuide *guide_to_ignore = NULL); + bool snapindicator, + std::vector<SPItem const *> &items_to_ignore, + std::vector<std::pair<Geom::Point, int> > *unselected_nodes = NULL, + SPGuide *guide_to_ignore = NULL); // freeSnapReturnByRef() is preferred over freeSnap(), because it only returns a // point if snapping has occurred (by overwriting p); otherwise p is untouched void freeSnapReturnByRef(Inkscape::SnapPreferences::PointType point_type, - Geom::Point &p, - Inkscape::SnapSourceType const source_type, - bool first_point = true, - Geom::OptRect const &bbox_to_snap = Geom::OptRect()) const; + Geom::Point &p, + Inkscape::SnapSourceType const source_type, + long source_num = 0, + Geom::OptRect const &bbox_to_snap = Geom::OptRect()) const; Inkscape::SnappedPoint freeSnap(Inkscape::SnapPreferences::PointType point_type, - Geom::Point const &p, - Inkscape::SnapSourceType const &source_type, - bool first_point = true, + Geom::Point const &p, + Inkscape::SnapSourceType const &source_type, + long source_num = 0, Geom::OptRect const &bbox_to_snap = Geom::OptRect() ) const; Geom::Point multipleOfGridPitch(Geom::Point const &t) const; @@ -98,17 +98,17 @@ public: // constrainedSnapReturnByRef() is preferred over constrainedSnap(), because it only returns a // point, by overwriting p, if snapping has occurred; otherwise p is untouched void constrainedSnapReturnByRef(Inkscape::SnapPreferences::PointType point_type, - Geom::Point &p, - Inkscape::SnapSourceType const source_type, - Inkscape::Snapper::ConstraintLine const &constraint, - bool first_point = true, - Geom::OptRect const &bbox_to_snap = Geom::OptRect()) const; + Geom::Point &p, + Inkscape::SnapSourceType const source_type, + Inkscape::Snapper::ConstraintLine const &constraint, + long source_num = 0, + Geom::OptRect const &bbox_to_snap = Geom::OptRect()) const; Inkscape::SnappedPoint constrainedSnap(Inkscape::SnapPreferences::PointType point_type, - Geom::Point const &p, - Inkscape::SnapSourceType const &source_type, - Inkscape::Snapper::ConstraintLine const &constraint, - bool first_point = true, + Geom::Point const &p, + Inkscape::SnapSourceType const &source_type, + Inkscape::Snapper::ConstraintLine const &constraint, + long source_num = 0, Geom::OptRect const &bbox_to_snap = Geom::OptRect()) const; void guideFreeSnap(Geom::Point &p, Geom::Point const &guide_normal, SPGuideDragType drag_type) const; diff --git a/src/snapped-curve.cpp b/src/snapped-curve.cpp index 4da2d4c7d..334038638 100644 --- a/src/snapped-curve.cpp +++ b/src/snapped-curve.cpp @@ -12,7 +12,7 @@ #include <2geom/crossing.h> #include <2geom/path-intersection.h> -Inkscape::SnappedCurve::SnappedCurve(Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, Geom::Coord const &snapped_tolerance, bool const &always_snap, bool const &fully_constrained, Geom::Curve const *curve, SnapSourceType source, SnapTargetType target) +Inkscape::SnappedCurve::SnappedCurve(Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, Geom::Coord const &snapped_tolerance, bool const &always_snap, bool const &fully_constrained, Geom::Curve const *curve, SnapSourceType source, long source_num, SnapTargetType target) { _distance = snapped_distance; _tolerance = std::max(snapped_tolerance, 1.0); @@ -25,6 +25,7 @@ Inkscape::SnappedCurve::SnappedCurve(Geom::Point const &snapped_point, Geom::Coo _at_intersection = false; _fully_constrained = fully_constrained; _source = source; + _source_num = source_num; _target = target; } @@ -41,6 +42,7 @@ Inkscape::SnappedCurve::SnappedCurve() _at_intersection = false; _fully_constrained = false; _source = SNAPSOURCE_UNDEFINED; + _source_num = 0; _target = SNAPTARGET_UNDEFINED; } @@ -83,12 +85,12 @@ Inkscape::SnappedPoint Inkscape::SnappedCurve::intersect(SnappedCurve const &cur // TODO: Investigate whether it is possible to use document coordinates everywhere // in the snapper code. Only the mouse position should be in desktop coordinates, I guess. // All paths are already in document coords and we are certainly not going to change THAT. - return SnappedPoint(best_p, Inkscape::SNAPSOURCE_UNDEFINED, Inkscape::SNAPTARGET_PATH_INTERSECTION, primaryDist, primaryC->getTolerance(), primaryC->getAlwaysSnap(), true, true, + return SnappedPoint(best_p, Inkscape::SNAPSOURCE_UNDEFINED, primaryC->getSourceNum(), Inkscape::SNAPTARGET_PATH_INTERSECTION, primaryDist, primaryC->getTolerance(), primaryC->getAlwaysSnap(), true, true, secondaryDist, secondaryC->getTolerance(), secondaryC->getAlwaysSnap()); } // No intersection - return SnappedPoint(Geom::Point(NR_HUGE, NR_HUGE), SNAPSOURCE_UNDEFINED, SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false, false, NR_HUGE, 0, false); + return SnappedPoint(Geom::Point(NR_HUGE, NR_HUGE), SNAPSOURCE_UNDEFINED, 0, SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false, false, NR_HUGE, 0, false); } // search for the closest snapped line diff --git a/src/snapped-curve.h b/src/snapped-curve.h index 0a1fe2431..4eea6e734 100644 --- a/src/snapped-curve.h +++ b/src/snapped-curve.h @@ -24,7 +24,7 @@ class SnappedCurve : public SnappedPoint { public: SnappedCurve(); - SnappedCurve(Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, Geom::Coord const &snapped_tolerance, bool const &always_snap, bool const &fully_constrained, Geom::Curve const *curve, SnapSourceType source, SnapTargetType target); + SnappedCurve(Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, Geom::Coord const &snapped_tolerance, bool const &always_snap, bool const &fully_constrained, Geom::Curve const *curve, SnapSourceType source, long source_num, SnapTargetType target); ~SnappedCurve(); Inkscape::SnappedPoint intersect(SnappedCurve const &curve, Geom::Point const &p, Geom::Matrix dt2doc) const; //intersect with another SnappedCurve diff --git a/src/snapped-line.cpp b/src/snapped-line.cpp index 3ebbeaf70..9dde22a4e 100644 --- a/src/snapped-line.cpp +++ b/src/snapped-line.cpp @@ -11,12 +11,13 @@ #include "snapped-line.h" #include <2geom/line.h> -Inkscape::SnappedLineSegment::SnappedLineSegment(Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, SnapSourceType const &source, SnapTargetType const &target, Geom::Coord const &snapped_tolerance, bool const &always_snap, Geom::Point const &start_point_of_line, Geom::Point const &end_point_of_line) +Inkscape::SnappedLineSegment::SnappedLineSegment(Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, SnapSourceType const &source, long source_num, SnapTargetType const &target, Geom::Coord const &snapped_tolerance, bool const &always_snap, Geom::Point const &start_point_of_line, Geom::Point const &end_point_of_line) : _start_point_of_line(start_point_of_line), _end_point_of_line(end_point_of_line) { _point = snapped_point; _source = source; - _target = target; + _source_num = source_num; + _target = target; _distance = snapped_distance; _tolerance = std::max(snapped_tolerance, 1.0); _always_snap = always_snap; @@ -32,7 +33,8 @@ Inkscape::SnappedLineSegment::SnappedLineSegment() _end_point_of_line = Geom::Point(0,0); _point = Geom::Point(0,0); _source = SNAPSOURCE_UNDEFINED; - _target = SNAPTARGET_UNDEFINED; + _source_num = 0; + _target = SNAPTARGET_UNDEFINED; _distance = NR_HUGE; _tolerance = 1; _always_snap = false; @@ -50,19 +52,19 @@ Inkscape::SnappedLineSegment::~SnappedLineSegment() Inkscape::SnappedPoint Inkscape::SnappedLineSegment::intersect(SnappedLineSegment const &line) const { Geom::OptCrossing inters = Geom::OptCrossing(); // empty by default - try - { - inters = Geom::intersection(getLineSegment(), line.getLineSegment()); - } - catch (Geom::InfiniteSolutions e) - { - // We're probably dealing with parallel lines, so they don't really cross - inters = Geom::OptCrossing(); - } + try + { + inters = Geom::intersection(getLineSegment(), line.getLineSegment()); + } + catch (Geom::InfiniteSolutions e) + { + // We're probably dealing with parallel lines, so they don't really cross + inters = Geom::OptCrossing(); + } if (inters) { Geom::Point inters_pt = getLineSegment().pointAt((*inters).ta); - /* If a snapper has been told to "always snap", then this one should be preferred + /* If a snapper has been told to "always snap", then this one should be preferred * over the other, if that other one has not been told so. (The preferred snapper * will be labeled "primary" below) */ @@ -78,22 +80,23 @@ Inkscape::SnappedPoint Inkscape::SnappedLineSegment::intersect(SnappedLineSegmen Inkscape::SnappedLineSegment const *secondarySLS = use_this_as_primary ? &line : this; Geom::Coord primaryDist = use_this_as_primary ? Geom::L2(inters_pt - this->getPoint()) : Geom::L2(inters_pt - line.getPoint()); Geom::Coord secondaryDist = use_this_as_primary ? Geom::L2(inters_pt - line.getPoint()) : Geom::L2(inters_pt - this->getPoint()); - return SnappedPoint(inters_pt, SNAPSOURCE_UNDEFINED, SNAPTARGET_PATH_INTERSECTION, primaryDist, primarySLS->getTolerance(), primarySLS->getAlwaysSnap(), true, true, + return SnappedPoint(inters_pt, SNAPSOURCE_UNDEFINED, primarySLS->getSourceNum(), SNAPTARGET_PATH_INTERSECTION, primaryDist, primarySLS->getTolerance(), primarySLS->getAlwaysSnap(), true, true, secondaryDist, secondarySLS->getTolerance(), secondarySLS->getAlwaysSnap()); } // No intersection - return SnappedPoint(Geom::Point(NR_HUGE, NR_HUGE), SNAPSOURCE_UNDEFINED, SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false, false, NR_HUGE, 0, false); + return SnappedPoint(Geom::Point(NR_HUGE, NR_HUGE), SNAPSOURCE_UNDEFINED, 0, SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false, false, NR_HUGE, 0, false); }; -Inkscape::SnappedLine::SnappedLine(Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, SnapSourceType const &source, SnapTargetType const &target, Geom::Coord const &snapped_tolerance, bool const &always_snap, Geom::Point const &normal_to_line, Geom::Point const &point_on_line) +Inkscape::SnappedLine::SnappedLine(Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, SnapSourceType const &source, long source_num, SnapTargetType const &target, Geom::Coord const &snapped_tolerance, bool const &always_snap, Geom::Point const &normal_to_line, Geom::Point const &point_on_line) : _normal_to_line(normal_to_line), _point_on_line(point_on_line) { - _source = source; - _target = target; - _distance = snapped_distance; + _source = source; + _source_num = source_num; + _target = target; + _distance = snapped_distance; _tolerance = std::max(snapped_tolerance, 1.0); _always_snap = always_snap; _second_distance = NR_HUGE; @@ -108,7 +111,8 @@ Inkscape::SnappedLine::SnappedLine() _normal_to_line = Geom::Point(0,0); _point_on_line = Geom::Point(0,0); _source = SNAPSOURCE_UNDEFINED; - _target = SNAPTARGET_UNDEFINED; + _source_num = 0; + _target = SNAPTARGET_UNDEFINED; _distance = NR_HUGE; _tolerance = 1; _always_snap = false; @@ -130,19 +134,19 @@ Inkscape::SnappedPoint Inkscape::SnappedLine::intersect(SnappedLine const &line) // The point of intersection should be considered for snapping, but might be outside the snapping range Geom::OptCrossing inters = Geom::OptCrossing(); // empty by default - try - { - inters = Geom::intersection(getLine(), line.getLine()); - } - catch (Geom::InfiniteSolutions e) - { - // We're probably dealing with parallel lines, so they don't really cross - inters = Geom::OptCrossing(); - } + try + { + inters = Geom::intersection(getLine(), line.getLine()); + } + catch (Geom::InfiniteSolutions e) + { + // We're probably dealing with parallel lines, so they don't really cross + inters = Geom::OptCrossing(); + } if (inters) { - Geom::Point inters_pt = getLine().pointAt((*inters).ta); - /* If a snapper has been told to "always snap", then this one should be preferred + Geom::Point inters_pt = getLine().pointAt((*inters).ta); + /* If a snapper has been told to "always snap", then this one should be preferred * over the other, if that other one has not been told so. (The preferred snapper * will be labelled "primary" below) */ @@ -157,14 +161,14 @@ Inkscape::SnappedPoint Inkscape::SnappedLine::intersect(SnappedLine const &line) Inkscape::SnappedLine const *secondarySL = use_this_as_primary ? &line : this; Geom::Coord primaryDist = use_this_as_primary ? Geom::L2(inters_pt - this->getPoint()) : Geom::L2(inters_pt - line.getPoint()); Geom::Coord secondaryDist = use_this_as_primary ? Geom::L2(inters_pt - line.getPoint()) : Geom::L2(inters_pt - this->getPoint()); - return SnappedPoint(inters_pt, Inkscape::SNAPSOURCE_UNDEFINED, Inkscape::SNAPTARGET_UNDEFINED, primaryDist, primarySL->getTolerance(), primarySL->getAlwaysSnap(), true, true, + return SnappedPoint(inters_pt, Inkscape::SNAPSOURCE_UNDEFINED, primarySL->getSourceNum(), Inkscape::SNAPTARGET_UNDEFINED, primaryDist, primarySL->getTolerance(), primarySL->getAlwaysSnap(), true, true, secondaryDist, secondarySL->getTolerance(), secondarySL->getAlwaysSnap()); // The type of the snap target is yet undefined, as we cannot tell whether // we're snapping to grid or the guide lines; must be set by on a higher level } // No intersection - return SnappedPoint(Geom::Point(NR_HUGE, NR_HUGE), SNAPSOURCE_UNDEFINED, SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false, false, NR_HUGE, 0, false); + return SnappedPoint(Geom::Point(NR_HUGE, NR_HUGE), SNAPSOURCE_UNDEFINED, 0, SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false, false, NR_HUGE, 0, false); } // search for the closest snapped line segment diff --git a/src/snapped-line.h b/src/snapped-line.h index 3dec432e7..b95e7a7df 100644 --- a/src/snapped-line.h +++ b/src/snapped-line.h @@ -23,7 +23,7 @@ class SnappedLineSegment : public SnappedPoint { public: SnappedLineSegment(); - SnappedLineSegment(Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, SnapSourceType const &source, SnapTargetType const &target, Geom::Coord const &snapped_tolerance,bool const &always_snap, Geom::Point const &start_point_of_line, Geom::Point const &end_point_of_line); + SnappedLineSegment(Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, SnapSourceType const &source, long source_num, SnapTargetType const &target, Geom::Coord const &snapped_tolerance,bool const &always_snap, Geom::Point const &start_point_of_line, Geom::Point const &end_point_of_line); ~SnappedLineSegment(); Inkscape::SnappedPoint intersect(SnappedLineSegment const &line) const; //intersect with another SnappedLineSegment Geom::LineSegment getLineSegment() const {return Geom::LineSegment(_start_point_of_line, _end_point_of_line);} @@ -39,7 +39,7 @@ class SnappedLine : public SnappedPoint { public: SnappedLine(); - SnappedLine(Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, SnapSourceType const &source, SnapTargetType const &target, Geom::Coord const &snapped_tolerance, bool const &always_snap, Geom::Point const &normal_to_line, Geom::Point const &point_on_line); + SnappedLine(Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, SnapSourceType const &source, long source_num, SnapTargetType const &target, Geom::Coord const &snapped_tolerance, bool const &always_snap, Geom::Point const &normal_to_line, Geom::Point const &point_on_line); ~SnappedLine(); Inkscape::SnappedPoint intersect(SnappedLine const &line) const; //intersect with another SnappedLine // This line is described by this equation: diff --git a/src/snapped-point.cpp b/src/snapped-point.cpp index 2d028c6d7..102e761b9 100644 --- a/src/snapped-point.cpp +++ b/src/snapped-point.cpp @@ -14,8 +14,8 @@ #include "preferences.h" // overloaded constructor -Inkscape::SnappedPoint::SnappedPoint(Geom::Point const &p, SnapSourceType const &source, SnapTargetType const &target, Geom::Coord const &d, Geom::Coord const &t, bool const &a, bool const &fully_constrained) - : _point(p), _source(source), _target(target), _distance(d), _tolerance(std::max(t,1.0)), _always_snap(a) +Inkscape::SnappedPoint::SnappedPoint(Geom::Point const &p, SnapSourceType const &source, long source_num, SnapTargetType const &target, Geom::Coord const &d, Geom::Coord const &t, bool const &a, bool const &fully_constrained) + : _point(p), _source(source), _source_num(source_num), _target(target), _distance(d), _tolerance(std::max(t,1.0)), _always_snap(a) { // tolerance should never be smaller than 1 px, as it is used for normalization in isOtherSnapBetter. We don't want a division by zero. _at_intersection = false; @@ -27,8 +27,8 @@ Inkscape::SnappedPoint::SnappedPoint(Geom::Point const &p, SnapSourceType const _pointer_distance = NR_HUGE; } -Inkscape::SnappedPoint::SnappedPoint(Geom::Point const &p, SnapSourceType const &source, SnapTargetType const &target, Geom::Coord const &d, Geom::Coord const &t, bool const &a, bool const &at_intersection, bool const &fully_constrained, Geom::Coord const &d2, Geom::Coord const &t2, bool const &a2) - : _point(p), _source(source), _target(target), _at_intersection(at_intersection), _fully_constrained(fully_constrained), _distance(d), _tolerance(std::max(t,1.0)), _always_snap(a), +Inkscape::SnappedPoint::SnappedPoint(Geom::Point const &p, SnapSourceType const &source, long source_num, SnapTargetType const &target, Geom::Coord const &d, Geom::Coord const &t, bool const &a, bool const &at_intersection, bool const &fully_constrained, Geom::Coord const &d2, Geom::Coord const &t2, bool const &a2) + : _point(p), _source(source), _source_num(source_num), _target(target), _at_intersection(at_intersection), _fully_constrained(fully_constrained), _distance(d), _tolerance(std::max(t,1.0)), _always_snap(a), _second_distance(d2), _second_tolerance(std::max(t2,1.0)), _second_always_snap(a2) { // tolerance should never be smaller than 1 px, as it is used for normalization in @@ -41,6 +41,7 @@ Inkscape::SnappedPoint::SnappedPoint() { _point = Geom::Point(0,0); _source = SNAPSOURCE_UNDEFINED, + _source_num = 0, _target = SNAPTARGET_UNDEFINED, _at_intersection = false; _fully_constrained = false; @@ -73,7 +74,7 @@ bool getClosestSP(std::list<Inkscape::SnappedPoint> &list, Inkscape::SnappedPoin bool success = false; for (std::list<Inkscape::SnappedPoint>::const_iterator i = list.begin(); i != list.end(); i++) { - if ((i == list.begin()) || (*i).getSnapDistance() < result.getSnapDistance()) { + if ((i == list.begin()) || (*i).getSnapDistance() < result.getSnapDistance()) { result = *i; success = true; } @@ -93,9 +94,9 @@ bool Inkscape::SnappedPoint::isOtherSnapBetter(Inkscape::SnappedPoint const &oth // (both the snap distance and the pointer distance are measured in document pixels, not in screen pixels) if (weighted) { - Geom::Coord const dist_pointer_other = other_one.getPointerDistance(); - Geom::Coord const dist_pointer_this = getPointerDistance(); - // Weight factor: controls which node should be preferred for snapping, which is either + Geom::Coord const dist_pointer_other = other_one.getPointerDistance(); + Geom::Coord const dist_pointer_this = getPointerDistance(); + // Weight factor: controls which node should be preferred for snapping, which is either // the node with the closest snap (w = 0), or the node closest to the mousepointer (w = 1) Inkscape::Preferences *prefs = Inkscape::Preferences::get(); double w = prefs->getDoubleLimited("/options/snapweight/value", 0.5, 0, 1); @@ -103,21 +104,21 @@ bool Inkscape::SnappedPoint::isOtherSnapBetter(Inkscape::SnappedPoint const &oth w = 1; } if (w > 0) { - if (!(w == 1 && dist_pointer_this == dist_pointer_other)) { - // When accounting for the distance to the mouse pointer, then at least one of the snapped points should - // have that distance set. If not, then this is a bug. Either "weighted" must be set to false, or the - // mouse pointer distance must be set. - g_assert(dist_pointer_this != NR_HUGE || dist_pointer_other != NR_HUGE); - // The snap distance will always be smaller than the tolerance set for the snapper. The pointer distance can - // however be very large. To compare these in a fair way, we will have to normalize these metrics first - // The closest pointer distance will be normalized to 1.0; the other one will be > 1.0 - // The snap distance will be normalized to 1.0 if it's equal to the snapper tolerance - double const norm_p = std::min(dist_pointer_this, dist_pointer_other); - double const norm_t_other = std::min(50.0, other_one.getTolerance()); - double const norm_t_this = std::min(50.0, getTolerance()); - dist_other = w * dist_pointer_other / norm_p + (1-w) * dist_other / norm_t_other; - dist_this = w * dist_pointer_this / norm_p + (1-w) * dist_this / norm_t_this; - } + if (!(w == 1 && dist_pointer_this == dist_pointer_other)) { + // When accounting for the distance to the mouse pointer, then at least one of the snapped points should + // have that distance set. If not, then this is a bug. Either "weighted" must be set to false, or the + // mouse pointer distance must be set. + g_assert(dist_pointer_this != NR_HUGE || dist_pointer_other != NR_HUGE); + // The snap distance will always be smaller than the tolerance set for the snapper. The pointer distance can + // however be very large. To compare these in a fair way, we will have to normalize these metrics first + // The closest pointer distance will be normalized to 1.0; the other one will be > 1.0 + // The snap distance will be normalized to 1.0 if it's equal to the snapper tolerance + double const norm_p = std::min(dist_pointer_this, dist_pointer_other); + double const norm_t_other = std::min(50.0, other_one.getTolerance()); + double const norm_t_this = std::min(50.0, getTolerance()); + dist_other = w * dist_pointer_other / norm_p + (1-w) * dist_other / norm_t_other; + dist_this = w * dist_pointer_this / norm_p + (1-w) * dist_this / norm_t_this; + } } } diff --git a/src/snapped-point.h b/src/snapped-point.h index 70d16b0be..70d353a73 100644 --- a/src/snapped-point.h +++ b/src/snapped-point.h @@ -78,8 +78,8 @@ class SnappedPoint public: SnappedPoint(); - SnappedPoint(Geom::Point const &p, SnapSourceType const &source, SnapTargetType const &target, Geom::Coord const &d, Geom::Coord const &t, bool const &a, bool const &at_intersection, bool const &fully_constrained, Geom::Coord const &d2, Geom::Coord const &t2, bool const &a2); - SnappedPoint(Geom::Point const &p, SnapSourceType const &source, SnapTargetType const &target, Geom::Coord const &d, Geom::Coord const &t, bool const &a, bool const &fully_constrained); + SnappedPoint(Geom::Point const &p, SnapSourceType const &source, long source_num, SnapTargetType const &target, Geom::Coord const &d, Geom::Coord const &t, bool const &a, bool const &at_intersection, bool const &fully_constrained, Geom::Coord const &d2, Geom::Coord const &t2, bool const &a2); + SnappedPoint(Geom::Point const &p, SnapSourceType const &source, long source_num, SnapTargetType const &target, Geom::Coord const &d, Geom::Coord const &t, bool const &a, bool const &fully_constrained); ~SnappedPoint(); Geom::Coord getSnapDistance() const {return _distance;} @@ -115,13 +115,15 @@ public: void setTarget(SnapTargetType const target) {_target = target;} SnapTargetType getTarget() const {return _target;} void setSource(SnapSourceType const source) {_source = source;} - SnapSourceType getSource() const {return _source;} + SnapSourceType getSource() const {return _source;} + long getSourceNum() const {return _source_num;} bool isOtherSnapBetter(SnappedPoint const &other_one, bool weighted) const; /*void dump() const { std::cout << "_point = " << _point << std::endl; std::cout << "_source = " << _source << std::endl; + std::cout << "_source_num = " << _source_num << std::endl; std::cout << "_target = " << _target << std::endl; std::cout << "_at_intersection = " << _at_intersection << std::endl; std::cout << "_fully_constrained = " << _fully_constrained << std::endl; @@ -138,6 +140,7 @@ public: protected: Geom::Point _point; // Location of the snapped point SnapSourceType _source; // Describes what snapped + long _source_num; // Sequence number of the source point that snapped, if that point is part of a set of points. (starting at zero) SnapTargetType _target; // Describes to what we've snapped to bool _at_intersection; // If true, the snapped point is at an intersection bool _fully_constrained; // When snapping for example to a node, then the snap will be "fully constrained". @@ -153,7 +156,7 @@ protected: bool _always_snap; /* If the snapped point is at an intersection of e.g. two lines, then this is - the distance to the fartest line */ + the distance to the farthest line */ Geom::Coord _second_distance; /* The snapping tolerance in screen pixels (depends on zoom)*/ Geom::Coord _second_tolerance; diff --git a/src/snapper.h b/src/snapper.h index 110b3d36a..dcc0fbb81 100644 --- a/src/snapper.h +++ b/src/snapper.h @@ -39,9 +39,9 @@ namespace Inkscape class Snapper { public: - Snapper() {} - Snapper(SnapManager *sm, ::Geom::Coord const t); - virtual ~Snapper() {} + Snapper() {} + Snapper(SnapManager *sm, ::Geom::Coord const t); + virtual ~Snapper() {} virtual Geom::Coord getSnapperTolerance() const = 0; //returns the tolerance of the snapper in screen pixels (i.e. independent of zoom) virtual bool getSnapperAlwaysSnap() const = 0; //if true, then the snapper will always snap, regardless of its tolerance @@ -53,7 +53,7 @@ public: // These four methods are only used for grids, for which snapping can be enabled individually void setEnabled(bool s); - void setSnapVisibleOnly(bool s); + void setSnapVisibleOnly(bool s); bool getEnabled() const {return _snap_enabled;} bool getSnapVisibleOnly() const {return _snap_visible_only;} @@ -61,7 +61,7 @@ public: SnapPreferences::PointType const &/*t*/, Geom::Point const &/*p*/, SnapSourceType const &/*source_type*/, - bool const &/*first_point*/, + long /*source_num*/, Geom::OptRect const &/*bbox_to_snap*/, std::vector<SPItem const *> const */*it*/, std::vector<std::pair<Geom::Point, int> > */*unselected_nodes*/) const {}; @@ -90,9 +90,9 @@ public: } Geom::Point projection(Geom::Point const &p) const { // returns the projection of p on this constraintline - Geom::Point const p1_on_cl = _has_point ? _point : p; - Geom::Point const p2_on_cl = p1_on_cl + _direction; - return Geom::projection(p, Geom::Line(p1_on_cl, p2_on_cl)); + Geom::Point const p1_on_cl = _has_point ? _point : p; + Geom::Point const p2_on_cl = p1_on_cl + _direction; + return Geom::projection(p, Geom::Line(p1_on_cl, p2_on_cl)); } private: @@ -103,20 +103,20 @@ public: }; virtual void constrainedSnap(SnappedConstraints &/*sc*/, - SnapPreferences::PointType const &/*t*/, + SnapPreferences::PointType const &/*t*/, Geom::Point const &/*p*/, SnapSourceType const &/*source_type*/, - bool const &/*first_point*/, + long /*source_num*/, Geom::OptRect const &/*bbox_to_snap*/, ConstraintLine const &/*c*/, std::vector<SPItem const *> const */*it*/) const {}; protected: - SnapManager *_snapmanager; + SnapManager *_snapmanager; - // This is only used for grids, for which snapping can be enabled individually - bool _snap_enabled; ///< true if this snapper is enabled, otherwise false - bool _snap_visible_only; + // This is only used for grids, for which snapping can be enabled individually + bool _snap_enabled; ///< true if this snapper is enabled, otherwise false + bool _snap_visible_only; }; } diff --git a/src/sp-conn-end.cpp b/src/sp-conn-end.cpp index 2287a182d..224442eb8 100644 --- a/src/sp-conn-end.cpp +++ b/src/sp-conn-end.cpp @@ -48,12 +48,12 @@ static bool try_get_intersect_point_with_item_recursive(Geom::PathVector& conn_p SPGroup* group = SP_GROUP(item); // consider all first-order children - double child_pos = std::numeric_limits<double>::max(); + double child_pos = 0.0; for (GSList const* i = sp_item_group_item_list(group); i != NULL; i = i->next) { SPItem* child_item = SP_ITEM(i->data); try_get_intersect_point_with_item_recursive(conn_pv, child_item, item_transform * child_item->transform, child_pos); - if (intersect_pos > child_pos) + if (intersect_pos < child_pos) intersect_pos = child_pos; } return intersect_pos != initial_pos; @@ -77,7 +77,7 @@ static bool try_get_intersect_point_with_item_recursive(Geom::PathVector& conn_p for (Geom::Crossings::const_iterator i = cr.begin(); i != cr.end(); i++) { const Geom::Crossing& cr_pt = *i; - if ( intersect_pos > cr_pt.ta) + if ( intersect_pos < cr_pt.ta) intersect_pos = cr_pt.ta; } } @@ -109,8 +109,8 @@ static bool try_get_intersect_point_with_item(SPPath* conn, SPItem* item, conn_pv[0] = conn_pv[0].reverse(); } - // We start with the intersection point at the end of the path - intersect_pos = conn_pv[0].size(); + // We start with the intersection point at the beginning of the path + intersect_pos = 0.0; // Find the intersection. bool result = try_get_intersect_point_with_item_recursive(conn_pv, item, item_transform, intersect_pos); diff --git a/src/sp-flowtext.cpp b/src/sp-flowtext.cpp index e4259e52c..53bcd425d 100644 --- a/src/sp-flowtext.cpp +++ b/src/sp-flowtext.cpp @@ -27,7 +27,7 @@ #include "sp-rect.h" #include "text-tag-attributes.h" #include "text-chemistry.h" - +#include "text-editing.h" #include "livarot/Shape.h" @@ -49,6 +49,7 @@ static void sp_flowtext_set(SPObject *object, unsigned key, gchar const *value); static void sp_flowtext_bbox(SPItem const *item, NRRect *bbox, Geom::Matrix const &transform, unsigned const flags); static void sp_flowtext_print(SPItem *item, SPPrintContext *ctx); static gchar *sp_flowtext_description(SPItem *item); +static void sp_flowtext_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs); static NRArenaItem *sp_flowtext_show(SPItem *item, NRArena *arena, unsigned key, unsigned flags); static void sp_flowtext_hide(SPItem *item, unsigned key); @@ -98,6 +99,7 @@ sp_flowtext_class_init(SPFlowtextClass *klass) item_class->bbox = sp_flowtext_bbox; item_class->print = sp_flowtext_print; item_class->description = sp_flowtext_description; + item_class->snappoints = sp_flowtext_snappoints; item_class->show = sp_flowtext_show; item_class->hide = sp_flowtext_hide; } @@ -373,15 +375,27 @@ static gchar *sp_flowtext_description(SPItem *item) Inkscape::Text::Layout const &layout = SP_FLOWTEXT(item)->layout; int const nChars = layout.iteratorToCharIndex(layout.end()); - char *trunc = ""; - if (layout.inputTruncated()) { - trunc = _(" [truncated]"); - } + char const *trunc = (layout.inputTruncated()) ? _(" [truncated]") : ""; - if (SP_FLOWTEXT(item)->has_internal_frame()) + if (SP_FLOWTEXT(item)->has_internal_frame()) { return g_strdup_printf(ngettext("<b>Flowed text</b> (%d character%s)", "<b>Flowed text</b> (%d characters%s)", nChars), nChars, trunc); - else + } else { return g_strdup_printf(ngettext("<b>Linked flowed text</b> (%d character%s)", "<b>Linked flowed text</b> (%d characters%s)", nChars), nChars, trunc); + } +} + +static void sp_flowtext_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const */*snapprefs*/) +{ + // Choose a point on the baseline for snapping from or to, with the horizontal position + // of this point depending on the text alignment (left vs. right) + Inkscape::Text::Layout const *layout = te_get_layout((SPItem *) item); + if (layout != NULL && layout->outputExists()) { + boost::optional<Geom::Point> pt = layout->baselineAnchorPoint(); + if (pt) { + int type = target ? int(Inkscape::SNAPTARGET_TEXT_BASELINE) : int(Inkscape::SNAPSOURCE_TEXT_BASELINE); + p.push_back(std::make_pair((*pt) * sp_item_i2d_affine(item), type)); + } + } } static NRArenaItem * diff --git a/src/sp-item-group.cpp b/src/sp-item-group.cpp index 7ac7880a7..3845be232 100644 --- a/src/sp-item-group.cpp +++ b/src/sp-item-group.cpp @@ -67,7 +67,6 @@ static void sp_group_set(SPObject *object, unsigned key, char const *value); static void sp_group_bbox(SPItem const *item, NRRect *bbox, Geom::Matrix const &transform, unsigned const flags); static void sp_group_print (SPItem * item, SPPrintContext *ctx); static gchar * sp_group_description (SPItem * item); -static Geom::Matrix sp_group_set_transform(SPItem *item, Geom::Matrix const &xform); static NRArenaItem *sp_group_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags); static void sp_group_hide (SPItem * item, unsigned int key); static void sp_group_snappoints (SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs); @@ -129,7 +128,6 @@ sp_group_class_init (SPGroupClass *klass) item_class->bbox = sp_group_bbox; item_class->print = sp_group_print; item_class->description = sp_group_description; - item_class->set_transform = sp_group_set_transform; item_class->show = sp_group_show; item_class->hide = sp_group_hide; item_class->snappoints = sp_group_snappoints; @@ -291,24 +289,6 @@ static gchar * sp_group_description (SPItem * item) return SP_GROUP(item)->group->getDescription(); } -static Geom::Matrix -sp_group_set_transform(SPItem *item, Geom::Matrix const &xform) -{ - Inkscape::Selection *selection = sp_desktop_selection(inkscape_active_desktop()); - persp3d_split_perspectives_according_to_selection(selection); - - Geom::Matrix last_trans; - sp_svg_transform_read(SP_OBJECT_REPR(item)->attribute("transform"), &last_trans); - Geom::Matrix inc_trans = last_trans.inverse()*xform; - - std::list<Persp3D *> plist = selection->perspList(); - for (std::list<Persp3D *>::iterator i = plist.begin(); i != plist.end(); ++i) { - persp3d_apply_affine_transformation(*i, inc_trans); - } - - return xform; -} - static void sp_group_set(SPObject *object, unsigned key, char const *value) { SPGroup *group = SP_GROUP(object); diff --git a/src/sp-item.cpp b/src/sp-item.cpp index 9f7157b99..1a5ca6f77 100644 --- a/src/sp-item.cpp +++ b/src/sp-item.cpp @@ -962,11 +962,11 @@ static void sp_item_private_snappoints(SPItem const *item, bool const target, Sn Geom::Point p1, p2; p1 = bbox->min(); p2 = bbox->max(); - int type = target ? int(Inkscape::SNAPSOURCE_CONVEX_HULL_CORNER) : int(Inkscape::SNAPSOURCE_CONVEX_HULL_CORNER); + int type = target ? int(Inkscape::SNAPTARGET_BBOX_CORNER) : int(Inkscape::SNAPSOURCE_BBOX_CORNER); p.push_back(std::make_pair(p1, type)); p.push_back(std::make_pair(Geom::Point(p1[Geom::X], p2[Geom::Y]), type)); p.push_back(std::make_pair(p2, type)); - p.push_back(std::make_pair(Geom::Point(p1[Geom::Y], p2[Geom::X]), type)); + p.push_back(std::make_pair(Geom::Point(p2[Geom::X], p1[Geom::Y]), type)); } } diff --git a/src/spray-context.cpp b/src/spray-context.cpp index b70e16467..b68fe0818 100644 --- a/src/spray-context.cpp +++ b/src/spray-context.cpp @@ -39,14 +39,14 @@ #include "desktop-style.h" #include "message-context.h" #include "pixmaps/cursor-spray.xpm" -#include "pixmaps/cursor-spray-move.xpm" -#include "pixmaps/cursor-thin.xpm" -#include "pixmaps/cursor-thicken.xpm" -#include "pixmaps/cursor-attract.xpm" -#include "pixmaps/cursor-repel.xpm" -#include "pixmaps/cursor-push.xpm" -#include "pixmaps/cursor-roughen.xpm" -#include "pixmaps/cursor-color.xpm" +//#include "pixmaps/cursor-spray-move.xpm" +//#include "pixmaps/cursor-thin.xpm" +//#include "pixmaps/cursor-thicken.xpm" +//#include "pixmaps/cursor-attract.xpm" +//#include "pixmaps/cursor-repel.xpm" +//#include "pixmaps/cursor-push.xpm" +//#include "pixmaps/cursor-roughen.xpm" +//#include "pixmaps/cursor-color.xpm" #include <boost/optional.hpp> #include "libnr/nr-matrix-ops.h" #include "libnr/nr-scale-translate-ops.h" @@ -107,10 +107,6 @@ static gint sp_spray_context_root_handler(SPEventContext *ec, GdkEvent *event); static SPEventContextClass *parent_class = 0; - -// The following code implements NormalDistribution wich is used for the density of the spray - - /* RAND is a macro which returns a pseudo-random numbers from a uniform distribution on the interval [0 1] @@ -118,7 +114,7 @@ static SPEventContextClass *parent_class = 0; #define RAND ((double) rand())/((double) RAND_MAX) /* - TWOPI = 2.0*pi + TWOPI = 2.0*pi */ #define TWOPI 2.0*3.141592653589793238462643383279502884197169399375 @@ -141,7 +137,6 @@ double NormalDistribution(double mu,double sigma) } -//Fin de la création de NormalDistribution GtkType sp_spray_context_get_type(void) { @@ -176,19 +171,16 @@ static void sp_spray_context_class_init(SPSprayContextClass *klass) event_context_class->root_handler = sp_spray_context_root_handler; } -/*Method to rotate items*/ +/* Method to rotate items */ void sp_spray_rotate_rel(Geom::Point c,SPDesktop */*desktop*/,SPItem *item, Geom::Rotate const &rotation) { - Geom::Point center = c; Geom::Translate const s(c); Geom::Matrix affine = Geom::Matrix(s).inverse() * Geom::Matrix(rotation) * Geom::Matrix(s); - // Rotate item. sp_item_set_i2d_affine(item, sp_item_i2d_affine(item) * (Geom::Matrix)affine); // Use each item's own transform writer, consistent with sp_selection_apply_affine() sp_item_write_transform(item, SP_OBJECT_REPR(item), item->transform); - // Restore the center position (it's changed because the bbox center changed) if (item->isCenterSet()) { item->setCenter(c); @@ -196,23 +188,17 @@ void sp_spray_rotate_rel(Geom::Point c,SPDesktop */*desktop*/,SPItem *item, Geom } } -/*Method to scale items*/ +/* Method to scale items */ void sp_spray_scale_rel(Geom::Point c, SPDesktop */*desktop*/, SPItem *item, Geom::Scale const &scale) { - Geom::Translate const s(c); - - - sp_item_set_i2d_affine(item, sp_item_i2d_affine(item) * s.inverse() * scale * s ); - sp_item_write_transform(item, SP_OBJECT_REPR(item), item->transform); - - + Geom::Translate const s(c); + sp_item_set_i2d_affine(item, sp_item_i2d_affine(item) * s.inverse() * scale * s ); + sp_item_write_transform(item, SP_OBJECT_REPR(item), item->transform); } static void sp_spray_context_init(SPSprayContext *tc) { - SPEventContext *event_context = SP_EVENT_CONTEXT(tc); - - + SPEventContext *event_context = SP_EVENT_CONTEXT(tc); event_context->cursor_shape = cursor_spray_xpm; event_context->hot_x = 4; @@ -226,12 +212,10 @@ static void sp_spray_context_init(SPSprayContext *tc) tc->ratio = 0; tc->tilt=0; tc->mean = 0.2; - tc->rot_min=0; - tc->rot_max=0; + tc->rotation_variation=0; tc->standard_deviation=0.2; tc->scale=1; - tc->scale_min = 1; - tc->scale_max=1; + tc->scale_variation = 1; tc->pressure = TC_DEFAULT_PRESSURE; tc->is_dilating = false; @@ -297,8 +281,7 @@ void sp_spray_update_cursor(SPSprayContext *tc, bool /*with_shift*/) case SPRAY_MODE_SINGLE_PATH: tc->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag, click or scroll to spray in a <b>single path</b> of the initial selection"), sel_message); break; - case SPRAY_OPTION: - tc->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Modify <b>spray</b> options"), sel_message); + default: break; } sp_event_context_update_cursor(event_context); @@ -337,18 +320,14 @@ static void sp_spray_context_setup(SPEventContext *ec) sp_event_context_read(ec, "width"); sp_event_context_read(ec, "ratio"); sp_event_context_read(ec, "tilt"); - sp_event_context_read(ec, "rot_min"); - sp_event_context_read(ec, "rot_max"); - sp_event_context_read(ec, "scale_min"); - sp_event_context_read(ec, "scale_max"); + sp_event_context_read(ec, "rotation_variation"); + sp_event_context_read(ec, "scale_variation"); sp_event_context_read(ec, "mode"); sp_event_context_read(ec, "population"); sp_event_context_read(ec, "force"); sp_event_context_read(ec, "mean"); sp_event_context_read(ec, "standard_deviation"); sp_event_context_read(ec, "usepressure"); - sp_event_context_read(ec, "Rotation min"); - sp_event_context_read(ec, "Rotation max"); sp_event_context_read(ec, "Scale"); sp_event_context_read(ec, "doh"); sp_event_context_read(ec, "dol"); @@ -387,14 +366,10 @@ static void sp_spray_context_set(SPEventContext *ec, Inkscape::Preferences::Entr tc->ratio = CLAMP(val->getDouble(), 0.0, 0.9); } else if (path == "force") { tc->force = CLAMP(val->getDouble(1.0), 0, 1.0); - } else if (path == "rot_min") { - tc->rot_min = CLAMP(val->getDouble(0), 0, 10.0); - } else if (path == "rot_max") { - tc->rot_max = CLAMP(val->getDouble(0), 0, 10.0); - } else if (path == "scale_min") { - tc->scale_min = CLAMP(val->getDouble(1.0), 0, 10.0); - } else if (path == "scale_max") { - tc->scale_max = CLAMP(val->getDouble(1.0), 0, 10.0); + } else if (path == "rotation_variation") { + tc->rotation_variation = CLAMP(val->getDouble(0.0), 0, 100.0); + } else if (path == "scale_variation") { + tc->scale_variation = CLAMP(val->getDouble(1.0), 0, 100.0); } else if (path == "mean") { tc->mean = 0.01 * CLAMP(val->getInt(10), 1, 100); } else if (path == "standard_deviation") { @@ -464,32 +439,24 @@ double get_move_standard_deviation(SPSprayContext *tc) return tc->standard_deviation; } -/* Method to handle the distribution of the items */ - - -void random_position( double &r, double &p, double &a, double &s, int choix) +/** Method to handle the distribution of the items */ +void random_position( double &r, double &p, double &a, double &s, int choice) { - if (choix == 0) // Mode 1 : uniform repartition + if (choice == 0) // 1 : uniform repartition { r = (1-pow(g_random_double_range(0, 1),2)); p = g_random_double_range(0, M_PI*2); } - if (choix == 1) //Mode 0 : gaussian repartition + if (choice == 1) // 0 : gaussian repartition { double r_temp =-1; -while(!((r_temp>=0)&&(r_temp<=1))) -{ - r_temp = NormalDistribution(a,s/4); -} -// generates a number following a normal distribution + while(!((r_temp>=0)&&(r_temp<=1))) + { + r_temp = NormalDistribution(a,s/4); + } + // generates a number following a normal distribution p = g_random_double_range(0, M_PI*2); r=r_temp; - /* if (r_temp<=0) r=0; - else - { - if (r_temp>1) r=1; - else r = r_temp; - }*/ } } @@ -497,7 +464,7 @@ while(!((r_temp>=0)&&(r_temp<=1))) -bool sp_spray_dilate_recursive(SPDesktop *desktop, +bool sp_spray_recursive(SPDesktop *desktop, Inkscape::Selection *selection, SPItem *item, Geom::Point p, @@ -507,202 +474,155 @@ bool sp_spray_dilate_recursive(SPDesktop *desktop, double /*force*/, double population, double &scale, - double scale_min, - double scale_max, + double scale_variation, bool /*reverse*/, double mean, double standard_deviation, double ratio, double tilt, - double rot_min, - double rot_max, + double rotation_variation, gint _distrib ) { - - - bool did = false; - - if (SP_IS_BOX3D(item) /*&& !is_transform_modes(mode)*/) { + + if (SP_IS_BOX3D(item) ) { // convert 3D boxes to ordinary groups before spraying their shapes item = SP_ITEM(box3d_convert_to_group(SP_BOX3D(item))); selection->add(item); } -/*if (SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item)) { - GSList *items = g_slist_prepend (NULL, item); - GSList *selected = NULL; - GSList *to_select = NULL; - SPDocument *doc = SP_OBJECT_DOCUMENT(item); - sp_item_list_to_curves (items, &selected, &to_select); - g_slist_free (items); - SPObject* newObj = doc->getObjectByRepr((Inkscape::XML::Node *) to_select->data); - g_slist_free (to_select); - item = (SPItem *) newObj; - // selection->add(item); - } -*/ - /*if (SP_IS_GROUP(item) && !SP_IS_BOX3D(item)) { - for (SPObject *child = sp_object_first_child(SP_OBJECT(item)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) { - if (SP_IS_ITEM(child)) { - if (sp_spray_dilate_recursive (desktop,selection, SP_ITEM(child), p, vector, mode, radius, force, population, scale, scale_min, scale_max, reverse, mean, standard_deviation,ratio,tilt, rot_min, rot_max,_distrib)) - did = true; + double _fid = g_random_double_range(0,1); + double angle = g_random_double_range( - rotation_variation / 100.0 * M_PI , rotation_variation / 100.0 * M_PI ); + double _scale = g_random_double_range( 1.0 - scale_variation / 100.0, 1.0 + scale_variation / 100.0 ); + double dr; double dp; + random_position(dr,dp,mean,standard_deviation,_distrib); + dr=dr*radius; + + if (mode == SPRAY_MODE_COPY) { + Geom::OptRect a = item->getBounds(sp_item_i2doc_affine(item)); + if (a) { + SPItem *item_copied; + if(_fid<=population) + { + // duplicate + SPDocument *doc = SP_OBJECT_DOCUMENT(item); + Inkscape::XML::Document* xml_doc = sp_document_repr_doc(doc); + Inkscape::XML::Node *old_repr = SP_OBJECT_REPR(item); + Inkscape::XML::Node *parent = old_repr->parent(); + Inkscape::XML::Node *copy = old_repr->duplicate(xml_doc); + parent->appendChild(copy); + + SPObject *new_obj = doc->getObjectByRepr(copy); + item_copied = (SPItem *) new_obj; //convertion object->item + Geom::Point center=item->getCenter(); + sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(_scale,_scale)); + sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(scale,scale)); + + sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle)); + //Move the cursor p + Geom::Point move = (Geom::Point(cos(tilt)*cos(dp)*dr/(1-ratio)+sin(tilt)*sin(dp)*dr/(1+ratio),-sin(tilt)*cos(dp)*dr/(1-ratio)+cos(tilt)*sin(dp)*dr/(1+ratio)))+(p-a->midpoint()); + sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y])); + did = true; } } + } else if (mode == SPRAY_MODE_SINGLE_PATH) { - } else {*/ - if (mode == SPRAY_MODE_COPY) { - - Geom::OptRect a = item->getBounds(sp_item_i2doc_affine(item)); - if (a) { - double dr; double dp; - random_position(dr,dp,mean,standard_deviation,_distrib); - dr=dr*radius; - double _fid = g_random_double_range(0,1); - SPItem *item_copied; - double angle = g_random_double_range(rot_min, rot_max); - double _scale = g_random_double_range(scale_min, scale_max); - if(_fid<=population) - { - // duplicate - SPDocument *doc = SP_OBJECT_DOCUMENT(item); - Inkscape::XML::Document* xml_doc = sp_document_repr_doc(doc); - Inkscape::XML::Node *old_repr = SP_OBJECT_REPR(item); - Inkscape::XML::Node *parent = old_repr->parent(); - Inkscape::XML::Node *copy = old_repr->duplicate(xml_doc); - parent->appendChild(copy); - - SPObject *new_obj = doc->getObjectByRepr(copy); - item_copied = (SPItem *) new_obj; //convertion object->item - Geom::Point center=item->getCenter(); - sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(_scale,_scale)); - sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(scale,scale)); - - sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle)); - Geom::Point move = (Geom::Point(cos(tilt)*cos(dp)*dr/(1-ratio)+sin(tilt)*sin(dp)*dr/(1+ratio),-sin(tilt)*cos(dp)*dr/(1-ratio)+cos(tilt)*sin(dp)*dr/(1+ratio)))+(p-a->midpoint());//Move the cursor p - sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y])); + SPItem *father; //initial Object + SPItem *item_copied; //Projected Object + SPItem *unionResult; //previous union + SPItem *son; //father copy + int i=1; + for (GSList *items = g_slist_copy((GSList *) selection->itemList()); + items != NULL; + items = items->next) { - - - - did = true; - } + SPItem *item1 = (SPItem *) items->data; + if (i==1) { + father=item1; } - - } else if (mode == SPRAY_MODE_SINGLE_PATH) { - - - SPItem *Pere; //Objet initial - SPItem *item_copied;//Objet projeté - SPItem *Union;//Union précédente - SPItem *fils;//Copie du père - - // GSList *items = g_slist_copy((GSList *) selection->itemList()); //Récupère la liste des objects sélectionnés -//Pere = (SPItem *) items->data;//Le premier objet est le père du spray - - int i=1; - for (GSList *items = g_slist_copy((GSList *) selection->itemList()); - items != NULL; - items = items->next) { - - SPItem *item1 = (SPItem *) items->data; - if (i==1) { - Pere=item1; - } - if (i==2) { - Union=item1; - } - i++; + if (i==2) { + unionResult=item1; } - SPDocument *doc = SP_OBJECT_DOCUMENT(Pere); - Inkscape::XML::Document* xml_doc = sp_document_repr_doc(doc); - Inkscape::XML::Node *old_repr = SP_OBJECT_REPR(Pere); - //SPObject *old_obj = doc->getObjectByRepr(old_repr); - Inkscape::XML::Node *parent = old_repr->parent(); - - Geom::OptRect a = Pere->getBounds(sp_item_i2doc_affine(Pere)); - if (a) { - double dr; double dp; //initialisation des variables - random_position(dr,dp,mean,standard_deviation,_distrib); - dr=dr*radius; - double _fid = g_random_double_range(0,1); - double angle = (g_random_double_range(rot_min, rot_max)); - double _scale = g_random_double_range(scale_min, scale_max); - if (i==2) { - Inkscape::XML::Node *copy1 = old_repr->duplicate(xml_doc); - parent->appendChild(copy1); - SPObject *new_obj1 = doc->getObjectByRepr(copy1); - fils = (SPItem *) new_obj1; //conversion object->item - Union=fils; - Inkscape::GC::release(copy1); - } - - if (_fid<=population) { //Rules the population of objects sprayed - // duplicates the father - Inkscape::XML::Node *copy2 = old_repr->duplicate(xml_doc); - parent->appendChild(copy2); - SPObject *new_obj2 = doc->getObjectByRepr(copy2); - item_copied = (SPItem *) new_obj2; - - Geom::Point move = (Geom::Point(cos(tilt)*cos(dp)*dr/(1-ratio)+sin(tilt)*sin(dp)*dr/(1+ratio),-sin(tilt)*cos(dp)*dr/(1-ratio)+cos(tilt)*sin(dp)*dr/(1+ratio)))+(p-a->midpoint());//Move around the cursor - - Geom::Point center=Pere->getCenter(); - sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(_scale,_scale)); - sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(scale,scale)); - sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle)); - sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y])); - -//UNION et surduplication - selection->clear(); - selection->add(item_copied); - selection->add(Union); - sp_selected_path_union(selection->desktop()); - selection->add(Pere); - Inkscape::GC::release(copy2); - did = true; - } + i++; + } + SPDocument *doc = SP_OBJECT_DOCUMENT(father); + Inkscape::XML::Document* xml_doc = sp_document_repr_doc(doc); + Inkscape::XML::Node *old_repr = SP_OBJECT_REPR(father); + Inkscape::XML::Node *parent = old_repr->parent(); + + Geom::OptRect a = father->getBounds(sp_item_i2doc_affine(father)); + if (a) { + if (i==2) { + Inkscape::XML::Node *copy1 = old_repr->duplicate(xml_doc); + parent->appendChild(copy1); + SPObject *new_obj1 = doc->getObjectByRepr(copy1); + son = (SPItem *) new_obj1; // conversion object->item + unionResult=son; + Inkscape::GC::release(copy1); + } + if (_fid<=population) { // Rules the population of objects sprayed + // duplicates the father + Inkscape::XML::Node *copy2 = old_repr->duplicate(xml_doc); + parent->appendChild(copy2); + SPObject *new_obj2 = doc->getObjectByRepr(copy2); + item_copied = (SPItem *) new_obj2; + + // Move around the cursor + Geom::Point move = (Geom::Point(cos(tilt)*cos(dp)*dr/(1-ratio)+sin(tilt)*sin(dp)*dr/(1+ratio),-sin(tilt)*cos(dp)*dr/(1-ratio)+cos(tilt)*sin(dp)*dr/(1+ratio)))+(p-a->midpoint()); + + Geom::Point center=father->getCenter(); + sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(_scale,_scale)); + sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(scale,scale)); + sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle)); + sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y])); + + // union and duplication + selection->clear(); + selection->add(item_copied); + selection->add(unionResult); + sp_selected_path_union(selection->desktop()); + selection->add(father); + Inkscape::GC::release(copy2); + did = true; } - } else if (mode == SPRAY_MODE_CLONE) { - + } + } else if (mode == SPRAY_MODE_CLONE) { Geom::OptRect a = item->getBounds(sp_item_i2doc_affine(item)); - if (a) { - double dr; double dp; - random_position(dr,dp,mean,standard_deviation,_distrib); - dr=dr*radius; - double _fid = g_random_double_range(0,1); - double angle = (g_random_double_range(rot_min, rot_max)); - double _scale = g_random_double_range(scale_min, scale_max); - - if(_fid<=population) - { - SPItem *item_copied; - SPDocument *doc = SP_OBJECT_DOCUMENT(item); - Inkscape::XML::Document* xml_doc = sp_document_repr_doc(doc); - Inkscape::XML::Node *old_repr = SP_OBJECT_REPR(item); - Inkscape::XML::Node *parent = old_repr->parent(); - - //Creation of the clone - Inkscape::XML::Node *clone = xml_doc->createElement("svg:use"); - parent->appendChild(clone); //Ajout du clone à la liste d'enfants du père (selection initiale - clone->setAttribute("xlink:href", g_strdup_printf("#%s", old_repr->attribute("id")), false); //Génère le lien entre les attributs du père et du fils - - SPObject *clone_object = doc->getObjectByRepr(clone); - item_copied = (SPItem *) clone_object;//conversion object->item - Geom::Point center=item->getCenter(); - sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(_scale,_scale)); - sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(scale,scale)); - sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle)); - Geom::Point move = (Geom::Point(cos(tilt)*cos(dp)*dr/(1-ratio)+sin(tilt)*sin(dp)*dr/(1+ratio),-sin(tilt)*cos(dp)*dr/(1-ratio)+cos(tilt)*sin(dp)*dr/(1+ratio)))+(p-a->midpoint()); - sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y])); - - Inkscape::GC::release(clone); - - did = true; - } }} - return did; + if (a) { + if(_fid<=population) { + SPItem *item_copied; + SPDocument *doc = SP_OBJECT_DOCUMENT(item); + Inkscape::XML::Document* xml_doc = sp_document_repr_doc(doc); + Inkscape::XML::Node *old_repr = SP_OBJECT_REPR(item); + Inkscape::XML::Node *parent = old_repr->parent(); + + // Creation of the clone + Inkscape::XML::Node *clone = xml_doc->createElement("svg:use"); + // Ad the clone to the list of the father's sons + parent->appendChild(clone); + // Generates the link between father and son attributes + clone->setAttribute("xlink:href", g_strdup_printf("#%s", old_repr->attribute("id")), false); + + SPObject *clone_object = doc->getObjectByRepr(clone); + // conversion object->item + item_copied = (SPItem *) clone_object; + Geom::Point center=item->getCenter(); + sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(_scale,_scale)); + sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(scale,scale)); + sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle)); + Geom::Point move = (Geom::Point(cos(tilt)*cos(dp)*dr/(1-ratio)+sin(tilt)*sin(dp)*dr/(1+ratio),-sin(tilt)*cos(dp)*dr/(1-ratio)+cos(tilt)*sin(dp)*dr/(1+ratio)))+(p-a->midpoint()); + sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y])); + + Inkscape::GC::release(clone); + did = true; + } + } + } + + return did; } @@ -751,23 +671,6 @@ bool sp_spray_dilate(SPSprayContext *tc, Geom::Point /*event_p*/, Geom::Point p, guint32 stroke_goal = sp_desktop_get_color_tool(desktop, "/tools/spray", false, &do_stroke); double opacity_goal = sp_desktop_get_master_opacity_tool(desktop, "/tools/spray", &do_opacity); if (reverse) { -#if 0 - // HSL inversion - float hsv[3]; - float rgb[3]; - sp_color_rgb_to_hsv_floatv (hsv, - SP_RGBA32_R_F(fill_goal), - SP_RGBA32_G_F(fill_goal), - SP_RGBA32_B_F(fill_goal)); - sp_color_hsv_to_rgb_floatv (rgb, hsv[0]<.5? hsv[0]+.5 : hsv[0]-.5, 1 - hsv[1], 1 - hsv[2]); - fill_goal = SP_RGBA32_F_COMPOSE(rgb[0], rgb[1], rgb[2], 1); - sp_color_rgb_to_hsv_floatv (hsv, - SP_RGBA32_R_F(stroke_goal), - SP_RGBA32_G_F(stroke_goal), - SP_RGBA32_B_F(stroke_goal)); - sp_color_hsv_to_rgb_floatv (rgb, hsv[0]<.5? hsv[0]+.5 : hsv[0]-.5, 1 - hsv[1], 1 - hsv[2]); - stroke_goal = SP_RGBA32_F_COMPOSE(rgb[0], rgb[1], rgb[2], 1); -#else // RGB inversion fill_goal = SP_RGBA32_U_COMPOSE( (255 - SP_RGBA32_R_U(fill_goal)), @@ -779,7 +682,6 @@ bool sp_spray_dilate(SPSprayContext *tc, Geom::Point /*event_p*/, Geom::Point p, (255 - SP_RGBA32_G_U(stroke_goal)), (255 - SP_RGBA32_B_U(stroke_goal)), (255 - SP_RGBA32_A_U(stroke_goal))); -#endif opacity_goal = 1 - opacity_goal; } @@ -806,21 +708,11 @@ bool sp_spray_dilate(SPSprayContext *tc, Geom::Point /*event_p*/, Geom::Point p, SPItem *item = (SPItem *) items->data; - /*if (is_color_modes (tc->mode)) { - if (do_fill || do_stroke || do_opacity) { - if (sp_spray_color_recursive (tc->mode, item, item_at_point, - fill_goal, do_fill, - stroke_goal, do_stroke, - opacity_goal, do_opacity, - tc->mode == SPRAY_MODE_BLUR, reverse, - p, radius, color_force, tc->do_h, tc->do_s, tc->do_l, tc->do_o)) - did = true; - } - }else*/ if (is_transform_modes(tc->mode)) { - if (sp_spray_dilate_recursive (desktop,selection, item, p, vector, tc->mode, radius, move_force, tc->population,tc->scale, tc->scale_min, tc->scale_max, reverse, move_mean, move_standard_deviation,tc->ratio,tc->tilt, tc->rot_min, tc->rot_max, tc->distrib)) + if (is_transform_modes(tc->mode)) { + if (sp_spray_recursive (desktop,selection, item, p, vector, tc->mode, radius, move_force, tc->population,tc->scale, tc->scale_variation, reverse, move_mean, move_standard_deviation,tc->ratio,tc->tilt, tc->rotation_variation, tc->distrib)) did = true; } else { - if (sp_spray_dilate_recursive (desktop,selection, item, p, vector, tc->mode, radius, path_force, tc->population,tc->scale, tc->scale_min, tc->scale_max, reverse, path_mean, path_standard_deviation,tc->ratio,tc->tilt, tc->rot_min, tc->rot_max, tc->distrib)) + if (sp_spray_recursive (desktop,selection, item, p, vector, tc->mode, radius, path_force, tc->population,tc->scale, tc->scale_variation, reverse, path_mean, path_standard_deviation,tc->ratio,tc->tilt, tc->rotation_variation, tc->distrib)) did = true; } } diff --git a/src/spray-context.h b/src/spray-context.h index f8822ce39..ab2434223 100644 --- a/src/spray-context.h +++ b/src/spray-context.h @@ -71,12 +71,10 @@ struct SPSprayContext double width; double ratio; double tilt; - double rot_min; - double rot_max; + double rotation_variation; double force; double population; - double scale_min; - double scale_max; + double scale_variation; double scale; double mean; double standard_deviation; diff --git a/src/svg/svg-color.cpp b/src/svg/svg-color.cpp index ff5a6c2d3..7d43b68ee 100644 --- a/src/svg/svg-color.cpp +++ b/src/svg/svg-color.cpp @@ -464,6 +464,7 @@ sp_svg_create_color_hash() return colors; } +#if ENABLE_LCMS //helper function borrowed from src/widgets/sp-color-icc-selector.cpp: void getThings( DWORD space, gchar const**& namers, gchar const**& tippies, guint const*& scalies ); @@ -497,6 +498,7 @@ g_message("transform to sRGB done"); } } } +#endif //ENABLE_LCMS /* * Some discussion at http://markmail.org/message/bhfvdfptt25kgtmj diff --git a/src/transf_mat_3x4.cpp b/src/transf_mat_3x4.cpp index b7cd278d4..6b49dc44a 100644 --- a/src/transf_mat_3x4.cpp +++ b/src/transf_mat_3x4.cpp @@ -115,6 +115,7 @@ TransfMat3x4::pt_to_str (Proj::Axis axis) { return g_strdup(os.str().c_str()); } +/* Check for equality (with a small tolerance epsilon) */ bool TransfMat3x4::operator==(const TransfMat3x4 &rhs) const { @@ -129,29 +130,16 @@ TransfMat3x4::operator==(const TransfMat3x4 &rhs) const return true; } -/* multiply a projective matrix by an affine matrix */ +/* Multiply a projective matrix by an affine matrix (by only multiplying the 'affine part' of the + * projective matrix) */ TransfMat3x4 TransfMat3x4::operator*(Geom::Matrix const &A) const { TransfMat3x4 ret; - // Is it safe to always use the currently active document? - double h = sp_document_height(inkscape_active_document()); - - /* - * Note: The strange multiplication involving the document height is due to the buggy - * intertwining of SVG and document coordinates. Essentially, what we do is first - * convert from "real-world" to SVG coordinates, then apply the transformation A - * (by multiplying with the Geom::Matrix) and then convert back from SVG to real-world - * coordinates. Maybe there is even a more Inkscape-ish way to achieve this? - * Once Inkscape has gotton rid of the two different coordiate systems, we can change - * this function to an ordinary matrix multiplication. - */ for (int j = 0; j < 4; ++j) { - ret.tmat[0][j] = A[0]*tmat[0][j] + A[2]*(h*tmat[2][j] - tmat[1][j]) + A[4]*tmat[2][j]; - ret.tmat[1][j] = A[1]*tmat[0][j] + A[3]*(h*tmat[2][j] - tmat[1][j]) + A[5]*tmat[2][j]; + ret.tmat[0][j] = A[0]*tmat[0][j] + A[2]*tmat[1][j] + A[4]*tmat[2][j]; + ret.tmat[1][j] = A[1]*tmat[0][j] + A[3]*tmat[1][j] + A[5]*tmat[2][j]; ret.tmat[2][j] = tmat[2][j]; - - ret.tmat[1][j] = h*ret.tmat[2][j] - ret.tmat[1][j]; // switch back from SVG to desktop coordinates } return ret; diff --git a/src/ui/dialog/align-and-distribute.cpp b/src/ui/dialog/align-and-distribute.cpp index 024d4b2f1..2bba0a0f8 100644 --- a/src/ui/dialog/align-and-distribute.cpp +++ b/src/ui/dialog/align-and-distribute.cpp @@ -705,14 +705,16 @@ private : { if (SP_IS_TEXT (*it) || SP_IS_FLOWTEXT (*it)) { Inkscape::Text::Layout const *layout = te_get_layout(*it); - Geom::Point base = layout->characterAnchorPoint(layout->begin()) * sp_item_i2d_affine(*it); - if (base[Geom::X] < b_min[Geom::X]) b_min[Geom::X] = base[Geom::X]; - if (base[Geom::Y] < b_min[Geom::Y]) b_min[Geom::Y] = base[Geom::Y]; - if (base[Geom::X] > b_max[Geom::X]) b_max[Geom::X] = base[Geom::X]; - if (base[Geom::Y] > b_max[Geom::Y]) b_max[Geom::Y] = base[Geom::Y]; - - Baselines b (*it, base, _orientation); - sorted.push_back(b); + boost::optional<Geom::Point> pt = layout->baselineAnchorPoint(); + if (pt) { + Geom::Point base = *pt * sp_item_i2d_affine(*it); + if (base[Geom::X] < b_min[Geom::X]) b_min[Geom::X] = base[Geom::X]; + if (base[Geom::Y] < b_min[Geom::Y]) b_min[Geom::Y] = base[Geom::Y]; + if (base[Geom::X] > b_max[Geom::X]) b_max[Geom::X] = base[Geom::X]; + if (base[Geom::Y] > b_max[Geom::Y]) b_max[Geom::Y] = base[Geom::Y]; + Baselines b (*it, base, _orientation); + sorted.push_back(b); + } } } @@ -746,11 +748,14 @@ private : { if (SP_IS_TEXT (*it) || SP_IS_FLOWTEXT (*it)) { Inkscape::Text::Layout const *layout = te_get_layout(*it); - Geom::Point base = layout->characterAnchorPoint(layout->begin()) * sp_item_i2d_affine(*it); - Geom::Point t(0.0, 0.0); - t[_orientation] = b_min[_orientation] - base[_orientation]; - sp_item_move_rel(*it, Geom::Translate(t)); - changed = true; + boost::optional<Geom::Point> pt = layout->baselineAnchorPoint(); + if (pt) { + Geom::Point base = *pt * sp_item_i2d_affine(*it); + Geom::Point t(0.0, 0.0); + t[_orientation] = b_min[_orientation] - base[_orientation]; + sp_item_move_rel(*it, Geom::Translate(t)); + changed = true; + } } } diff --git a/src/ui/dialog/document-properties.cpp b/src/ui/dialog/document-properties.cpp index 982fb3415..a7241ea40 100644 --- a/src/ui/dialog/document-properties.cpp +++ b/src/ui/dialog/document-properties.cpp @@ -353,6 +353,7 @@ DocumentProperties::populate_available_profiles(){ while ((filename = (gchar *)g_dir_read_name(directory)) != NULL) { gchar* full = g_build_filename(it->c_str(), filename, NULL); if ( !Inkscape::IO::file_test( full, G_FILE_TEST_IS_DIR ) ) { + cmsErrorAction( LCMS_ERROR_SHOW ); cmsHPROFILE hProfile = cmsOpenProfileFromFile(full, "r"); if (hProfile != NULL){ const gchar* name; diff --git a/src/vanishing-point.cpp b/src/vanishing-point.cpp index ab46b21a6..78ceec467 100644 --- a/src/vanishing-point.cpp +++ b/src/vanishing-point.cpp @@ -103,7 +103,7 @@ vp_knot_moved_handler (SPKnot */*knot*/, Geom::Point const *ppointer, guint stat sel_boxes = (*vp)->selectedBoxes(sp_desktop_selection(inkscape_active_desktop())); // we create a new perspective ... - Persp3D *new_persp = persp3d_create_xml_element (dragger->parent->document, old_persp); + Persp3D *new_persp = persp3d_create_xml_element (dragger->parent->document, old_persp->perspective_impl); /* ... unlink the boxes from the old one and FIXME: We need to unlink the _un_selected boxes of each VP so that @@ -230,7 +230,7 @@ unsigned int VanishingPoint::global_counter = 0; void VanishingPoint::set_pos(Proj::Pt2 const &pt) { g_return_if_fail (_persp); - _persp->tmat.set_image_pt (_axis, pt); + _persp->perspective_impl->tmat.set_image_pt (_axis, pt); } std::list<SPBox3D *> diff --git a/src/widgets/sp-color-notebook.cpp b/src/widgets/sp-color-notebook.cpp index 56b60d120..3ba39dd30 100644 --- a/src/widgets/sp-color-notebook.cpp +++ b/src/widgets/sp-color-notebook.cpp @@ -374,8 +374,10 @@ void ColorNotebook::init() sp_set_font_size_smaller (rgbabox); gtk_widget_show_all (rgbabox); +#if ENABLE_LCMS //the "too much ink" icon is initially hidden gtk_widget_hide(GTK_WIDGET(_box_toomuchink)); +#endif //ENABLE_LCMS gtk_table_attach (GTK_TABLE (table), rgbabox, 0, 2, row, row + 1, GTK_FILL, GTK_SHRINK, XPAD, YPAD); diff --git a/src/widgets/toolbox.cpp b/src/widgets/toolbox.cpp index 436213946..9fc077778 100644 --- a/src/widgets/toolbox.cpp +++ b/src/widgets/toolbox.cpp @@ -304,19 +304,19 @@ static gchar const * ui_descr = " <toolitem action='TweakDoO' />" " </toolbar>" - " <toolbar name='SprayToolbar'>" + " <toolbar name='SprayToolbar'>" " <toolitem action='SprayModeAction' />" " <separator />" - " <toolitem action='SprayWidthAction' />" " <separator />" + " <toolitem action='SprayWidthAction' />" " <toolitem action='SprayPressureAction' />" - " <separator />" " <toolitem action='SprayPopulationAction' />" " <separator />" - " <toolitem action='SprayMeanAction' />" - " <toolitem action='SprayStandard_deviationAction' />" + " <toolitem action='SprayRotationAction' />" + " <toolitem action='SprayScaleAction' />" " <separator />" - " <toolitem action='DialogSprayOption' />" + " <toolitem action='SprayStandard_deviationAction' />" + " <toolitem action='SprayMeanAction' />" " </toolbar>" " <toolbar name='ZoomToolbar'>" @@ -743,7 +743,6 @@ Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop ) //SP_VERB_EDIT_TILE, //SP_VERB_EDIT_UNTILE, SP_VERB_DIALOG_ALIGN_DISTRIBUTE, - SP_VERB_DIALOG_SPRAY_OPTION, SP_VERB_DIALOG_DISPLAY, SP_VERB_DIALOG_FILL_STROKE, SP_VERB_DIALOG_NAMEDVIEW, @@ -926,11 +925,11 @@ sp_commands_toolbox_new() GtkWidget * sp_snap_toolbox_new() { - GtkWidget *tb = gtk_vbox_new(FALSE, 0); - gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING); - g_object_set_data(G_OBJECT(tb), "desktop", NULL); + GtkWidget *tb = gtk_vbox_new(FALSE, 0); + gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING); + g_object_set_data(G_OBJECT(tb), "desktop", NULL); - //GtkWidget *tb = gtk_toolbar_new(); + //GtkWidget *tb = gtk_toolbar_new(); //g_object_set_data(G_OBJECT(tb), "desktop", NULL); gtk_widget_set_sensitive(tb, FALSE); @@ -1904,314 +1903,314 @@ update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/ void toggle_snap_callback (GtkToggleAction *act, gpointer data) { //data points to the toolbox - if (g_object_get_data(G_OBJECT(data), "freeze" )) { - return; - } - - gpointer ptr = g_object_get_data(G_OBJECT(data), "desktop"); - g_assert(ptr != NULL); - - SPDesktop *dt = reinterpret_cast<SPDesktop*>(ptr); - SPNamedView *nv = sp_desktop_namedview(dt); - SPDocument *doc = SP_OBJECT_DOCUMENT(nv); - - if (dt == NULL || nv == NULL) { - g_warning("No desktop or namedview specified (in toggle_snap_callback)!"); - return; - } - - Inkscape::XML::Node *repr = SP_OBJECT_REPR(nv); - - if (repr == NULL) { - g_warning("This namedview doesn't have a xml representation attached!"); - return; - } - - bool saved = sp_document_get_undo_sensitive(doc); - sp_document_set_undo_sensitive(doc, false); - - bool v = false; - SPAttributeEnum attr = (SPAttributeEnum) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(act), "SP_ATTR_INKSCAPE")); - - switch (attr) { - case SP_ATTR_INKSCAPE_SNAP_GLOBAL: - dt->toggleSnapGlobal(); - break; - case SP_ATTR_INKSCAPE_SNAP_BBOX: - v = nv->snap_manager.snapprefs.getSnapModeBBox(); - sp_repr_set_boolean(repr, "inkscape:snap-bbox", !v); - break; - case SP_ATTR_INKSCAPE_BBOX_PATHS: - v = nv->snap_manager.snapprefs.getSnapToBBoxPath(); - sp_repr_set_boolean(repr, "inkscape:bbox-paths", !v); - break; - case SP_ATTR_INKSCAPE_BBOX_NODES: - v = nv->snap_manager.snapprefs.getSnapToBBoxNode(); - sp_repr_set_boolean(repr, "inkscape:bbox-nodes", !v); - break; - case SP_ATTR_INKSCAPE_SNAP_NODES: - v = nv->snap_manager.snapprefs.getSnapModeNode(); - sp_repr_set_boolean(repr, "inkscape:snap-nodes", !v); - break; - case SP_ATTR_INKSCAPE_OBJECT_PATHS: - v = nv->snap_manager.snapprefs.getSnapToItemPath(); - sp_repr_set_boolean(repr, "inkscape:object-paths", !v); - break; - case SP_ATTR_INKSCAPE_OBJECT_NODES: - v = nv->snap_manager.snapprefs.getSnapToItemNode(); - sp_repr_set_boolean(repr, "inkscape:object-nodes", !v); - break; - case SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES: - v = nv->snap_manager.snapprefs.getSnapSmoothNodes(); - sp_repr_set_boolean(repr, "inkscape:snap-smooth-nodes", !v); - break; - case SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS: - v = nv->snap_manager.snapprefs.getSnapIntersectionCS(); - sp_repr_set_boolean(repr, "inkscape:snap-intersection-paths", !v); - break; - case SP_ATTR_INKSCAPE_SNAP_CENTER: - v = nv->snap_manager.snapprefs.getIncludeItemCenter(); - sp_repr_set_boolean(repr, "inkscape:snap-center", !v); - break; - case SP_ATTR_INKSCAPE_SNAP_GRIDS: - v = nv->snap_manager.snapprefs.getSnapToGrids(); - sp_repr_set_boolean(repr, "inkscape:snap-grids", !v); - break; - case SP_ATTR_INKSCAPE_SNAP_TO_GUIDES: - v = nv->snap_manager.snapprefs.getSnapToGuides(); - sp_repr_set_boolean(repr, "inkscape:snap-to-guides", !v); - break; - case SP_ATTR_INKSCAPE_SNAP_PAGE: - v = nv->snap_manager.snapprefs.getSnapToPageBorder(); - sp_repr_set_boolean(repr, "inkscape:snap-page", !v); - break; - /*case SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE: - v = nv->snap_manager.snapprefs.getSnapIntersectionGG(); - sp_repr_set_boolean(repr, "inkscape:snap-intersection-grid-guide", !v); - break;*/ - case SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS: - v = nv->snap_manager.snapprefs.getSnapLineMidpoints(); - sp_repr_set_boolean(repr, "inkscape:snap-midpoints", !v); - break; - case SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS: - v = nv->snap_manager.snapprefs.getSnapObjectMidpoints(); - sp_repr_set_boolean(repr, "inkscape:snap-object-midpoints", !v); - break; - case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS: - v = nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints(); - sp_repr_set_boolean(repr, "inkscape:snap-bbox-edge-midpoints", !v); - break; - case SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS: - v = nv->snap_manager.snapprefs.getSnapBBoxMidpoints(); - sp_repr_set_boolean(repr, "inkscape:snap-bbox-midpoints", !v); - break; - default: - g_warning("toggle_snap_callback has been called with an ID for which no action has been defined"); - break; - } - - // The snapping preferences are stored in the document, and therefore toggling makes the document dirty - doc->setModifiedSinceSave(); - - sp_document_set_undo_sensitive(doc, saved); + if (g_object_get_data(G_OBJECT(data), "freeze" )) { + return; + } + + gpointer ptr = g_object_get_data(G_OBJECT(data), "desktop"); + g_assert(ptr != NULL); + + SPDesktop *dt = reinterpret_cast<SPDesktop*>(ptr); + SPNamedView *nv = sp_desktop_namedview(dt); + SPDocument *doc = SP_OBJECT_DOCUMENT(nv); + + if (dt == NULL || nv == NULL) { + g_warning("No desktop or namedview specified (in toggle_snap_callback)!"); + return; + } + + Inkscape::XML::Node *repr = SP_OBJECT_REPR(nv); + + if (repr == NULL) { + g_warning("This namedview doesn't have a xml representation attached!"); + return; + } + + bool saved = sp_document_get_undo_sensitive(doc); + sp_document_set_undo_sensitive(doc, false); + + bool v = false; + SPAttributeEnum attr = (SPAttributeEnum) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(act), "SP_ATTR_INKSCAPE")); + + switch (attr) { + case SP_ATTR_INKSCAPE_SNAP_GLOBAL: + dt->toggleSnapGlobal(); + break; + case SP_ATTR_INKSCAPE_SNAP_BBOX: + v = nv->snap_manager.snapprefs.getSnapModeBBox(); + sp_repr_set_boolean(repr, "inkscape:snap-bbox", !v); + break; + case SP_ATTR_INKSCAPE_BBOX_PATHS: + v = nv->snap_manager.snapprefs.getSnapToBBoxPath(); + sp_repr_set_boolean(repr, "inkscape:bbox-paths", !v); + break; + case SP_ATTR_INKSCAPE_BBOX_NODES: + v = nv->snap_manager.snapprefs.getSnapToBBoxNode(); + sp_repr_set_boolean(repr, "inkscape:bbox-nodes", !v); + break; + case SP_ATTR_INKSCAPE_SNAP_NODES: + v = nv->snap_manager.snapprefs.getSnapModeNode(); + sp_repr_set_boolean(repr, "inkscape:snap-nodes", !v); + break; + case SP_ATTR_INKSCAPE_OBJECT_PATHS: + v = nv->snap_manager.snapprefs.getSnapToItemPath(); + sp_repr_set_boolean(repr, "inkscape:object-paths", !v); + break; + case SP_ATTR_INKSCAPE_OBJECT_NODES: + v = nv->snap_manager.snapprefs.getSnapToItemNode(); + sp_repr_set_boolean(repr, "inkscape:object-nodes", !v); + break; + case SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES: + v = nv->snap_manager.snapprefs.getSnapSmoothNodes(); + sp_repr_set_boolean(repr, "inkscape:snap-smooth-nodes", !v); + break; + case SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS: + v = nv->snap_manager.snapprefs.getSnapIntersectionCS(); + sp_repr_set_boolean(repr, "inkscape:snap-intersection-paths", !v); + break; + case SP_ATTR_INKSCAPE_SNAP_CENTER: + v = nv->snap_manager.snapprefs.getIncludeItemCenter(); + sp_repr_set_boolean(repr, "inkscape:snap-center", !v); + break; + case SP_ATTR_INKSCAPE_SNAP_GRIDS: + v = nv->snap_manager.snapprefs.getSnapToGrids(); + sp_repr_set_boolean(repr, "inkscape:snap-grids", !v); + break; + case SP_ATTR_INKSCAPE_SNAP_TO_GUIDES: + v = nv->snap_manager.snapprefs.getSnapToGuides(); + sp_repr_set_boolean(repr, "inkscape:snap-to-guides", !v); + break; + case SP_ATTR_INKSCAPE_SNAP_PAGE: + v = nv->snap_manager.snapprefs.getSnapToPageBorder(); + sp_repr_set_boolean(repr, "inkscape:snap-page", !v); + break; + /*case SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE: + v = nv->snap_manager.snapprefs.getSnapIntersectionGG(); + sp_repr_set_boolean(repr, "inkscape:snap-intersection-grid-guide", !v); + break;*/ + case SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS: + v = nv->snap_manager.snapprefs.getSnapLineMidpoints(); + sp_repr_set_boolean(repr, "inkscape:snap-midpoints", !v); + break; + case SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS: + v = nv->snap_manager.snapprefs.getSnapObjectMidpoints(); + sp_repr_set_boolean(repr, "inkscape:snap-object-midpoints", !v); + break; + case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS: + v = nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints(); + sp_repr_set_boolean(repr, "inkscape:snap-bbox-edge-midpoints", !v); + break; + case SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS: + v = nv->snap_manager.snapprefs.getSnapBBoxMidpoints(); + sp_repr_set_boolean(repr, "inkscape:snap-bbox-midpoints", !v); + break; + default: + g_warning("toggle_snap_callback has been called with an ID for which no action has been defined"); + break; + } + + // The snapping preferences are stored in the document, and therefore toggling makes the document dirty + doc->setModifiedSinceSave(); + + sp_document_set_undo_sensitive(doc, saved); } void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop) { - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop); - - gchar const * descr = - "<ui>" - " <toolbar name='SnapToolbar'>" - " <toolitem action='ToggleSnapGlobal' />" - " <separator />" - " <toolitem action='ToggleSnapFromBBoxCorner' />" - " <toolitem action='ToggleSnapToBBoxPath' />" - " <toolitem action='ToggleSnapToBBoxNode' />" - " <toolitem action='ToggleSnapToFromBBoxEdgeMidpoints' />" - " <toolitem action='ToggleSnapToFromBBoxCenters' />" - " <separator />" - " <toolitem action='ToggleSnapFromNode' />" - " <toolitem action='ToggleSnapToItemPath' />" - " <toolitem action='ToggleSnapToPathIntersections' />" - " <toolitem action='ToggleSnapToItemNode' />" - " <toolitem action='ToggleSnapToSmoothNodes' />" - " <toolitem action='ToggleSnapToFromLineMidpoints' />" - " <toolitem action='ToggleSnapToFromObjectCenters' />" - " <toolitem action='ToggleSnapToFromRotationCenter' />" - " <separator />" - " <toolitem action='ToggleSnapToPageBorder' />" - " <toolitem action='ToggleSnapToGrids' />" - " <toolitem action='ToggleSnapToGuides' />" - //" <toolitem action='ToggleSnapToGridGuideIntersections' />" - " </toolbar>" - "</ui>"; - - Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1); - - { - InkToggleAction* act = ink_toggle_action_new("ToggleSnapGlobal", - _("Snap"), _("Enable snapping"), INKSCAPE_ICON_SNAP, secondarySize, - SP_ATTR_INKSCAPE_SNAP_GLOBAL); - - gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); - g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); - } - - { - InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromBBoxCorner", - _("Bounding box"), _("Snap bounding box corners"), INKSCAPE_ICON_SNAP_BOUNDING_BOX, - secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX); - - gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); - g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); - } - - { - InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxPath", - _("Bounding box edges"), _("Snap to edges of a bounding box"), - INKSCAPE_ICON_SNAP_BOUNDING_BOX_EDGES, secondarySize, SP_ATTR_INKSCAPE_BBOX_PATHS); - - gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); - g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); - } - - { - InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxNode", - _("Bounding box corners"), _("Snap to bounding box corners"), - INKSCAPE_ICON_SNAP_BOUNDING_BOX_CORNERS, secondarySize, SP_ATTR_INKSCAPE_BBOX_NODES); - - gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); - g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); - } - - { - InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxEdgeMidpoints", - _("BBox Edge Midpoints"), _("Snap from and to midpoints of bounding box edges"), - INKSCAPE_ICON_SNAP_BOUNDING_BOX_MIDPOINTS, secondarySize, - SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS); - - gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); - g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); - } - - { - InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxCenters", - _("BBox Centers"), _("Snapping from and to centers of bounding boxes"), - INKSCAPE_ICON_SNAP_BOUNDING_BOX_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS); - - gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); - g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); - } - - { - InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromNode", - _("Nodes"), _("Snap nodes or handles"), INKSCAPE_ICON_SNAP_NODES, secondarySize, SP_ATTR_INKSCAPE_SNAP_NODES); - - gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); - g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); - } - - { - InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemPath", - _("Paths"), _("Snap to paths"), INKSCAPE_ICON_SNAP_NODES_PATH, secondarySize, - SP_ATTR_INKSCAPE_OBJECT_PATHS); - - gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); - g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); - } - - { - InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPathIntersections", - _("Path intersections"), _("Snap to path intersections"), - INKSCAPE_ICON_SNAP_NODES_INTERSECTION, secondarySize, SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS); - - gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); - g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); - } - - { - InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemNode", - _("To nodes"), _("Snap to cusp nodes"), INKSCAPE_ICON_SNAP_NODES_CUSP, secondarySize, - SP_ATTR_INKSCAPE_OBJECT_NODES); - - gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); - g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); - } - - { - InkToggleAction* act = ink_toggle_action_new("ToggleSnapToSmoothNodes", - _("Smooth nodes"), _("Snap to smooth nodes"), INKSCAPE_ICON_SNAP_NODES_SMOOTH, - secondarySize, SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES); - - gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); - g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); - } - - { - InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromLineMidpoints", - _("Line Midpoints"), _("Snap from and to midpoints of line segments"), - INKSCAPE_ICON_SNAP_NODES_MIDPOINT, secondarySize, SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS); - - gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); - g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); - } - - { - InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromObjectCenters", - _("Object Centers"), _("Snap from and to centers of objects"), - INKSCAPE_ICON_SNAP_NODES_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS); - - gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); - g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); - } - - { - InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromRotationCenter", - _("Rotation Centers"), _("Snap from and to an item's rotation center"), - INKSCAPE_ICON_SNAP_NODES_ROTATION_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_CENTER); - - gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); - g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); - } - - { - InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPageBorder", - _("Page border"), _("Snap to the page border"), INKSCAPE_ICON_SNAP_PAGE, - secondarySize, SP_ATTR_INKSCAPE_SNAP_PAGE); - - gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); - g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); - } - - { - InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGrids", - _("Grids"), _("Snap to grids"), INKSCAPE_ICON_GRID_RECTANGULAR, secondarySize, - SP_ATTR_INKSCAPE_SNAP_GRIDS); - - gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); - g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); - } - - { - InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGuides", - _("Guides"), _("Snap to guides"), INKSCAPE_ICON_GUIDES, secondarySize, - SP_ATTR_INKSCAPE_SNAP_TO_GUIDES); - - gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); - g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); - } - - /*{ - InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGridGuideIntersections", - _("Grid/guide intersections"), _("Snap to intersections of a grid with a guide"), - INKSCAPE_ICON_SNAP_GRID_GUIDE_INTERSECTIONS, secondarySize, - SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE); - - gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); - g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); - }*/ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop); + + gchar const * descr = + "<ui>" + " <toolbar name='SnapToolbar'>" + " <toolitem action='ToggleSnapGlobal' />" + " <separator />" + " <toolitem action='ToggleSnapFromBBoxCorner' />" + " <toolitem action='ToggleSnapToBBoxPath' />" + " <toolitem action='ToggleSnapToBBoxNode' />" + " <toolitem action='ToggleSnapToFromBBoxEdgeMidpoints' />" + " <toolitem action='ToggleSnapToFromBBoxCenters' />" + " <separator />" + " <toolitem action='ToggleSnapFromNode' />" + " <toolitem action='ToggleSnapToItemPath' />" + " <toolitem action='ToggleSnapToPathIntersections' />" + " <toolitem action='ToggleSnapToItemNode' />" + " <toolitem action='ToggleSnapToSmoothNodes' />" + " <toolitem action='ToggleSnapToFromLineMidpoints' />" + " <toolitem action='ToggleSnapToFromObjectCenters' />" + " <toolitem action='ToggleSnapToFromRotationCenter' />" + " <separator />" + " <toolitem action='ToggleSnapToPageBorder' />" + " <toolitem action='ToggleSnapToGrids' />" + " <toolitem action='ToggleSnapToGuides' />" + //" <toolitem action='ToggleSnapToGridGuideIntersections' />" + " </toolbar>" + "</ui>"; + + Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1); + + { + InkToggleAction* act = ink_toggle_action_new("ToggleSnapGlobal", + _("Snap"), _("Enable snapping"), INKSCAPE_ICON_SNAP, secondarySize, + SP_ATTR_INKSCAPE_SNAP_GLOBAL); + + gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); + g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); + } + + { + InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromBBoxCorner", + _("Bounding box"), _("Snap bounding box corners"), INKSCAPE_ICON_SNAP_BOUNDING_BOX, + secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX); + + gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); + g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); + } + + { + InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxPath", + _("Bounding box edges"), _("Snap to edges of a bounding box"), + INKSCAPE_ICON_SNAP_BOUNDING_BOX_EDGES, secondarySize, SP_ATTR_INKSCAPE_BBOX_PATHS); + + gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); + g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); + } + + { + InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxNode", + _("Bounding box corners"), _("Snap to bounding box corners"), + INKSCAPE_ICON_SNAP_BOUNDING_BOX_CORNERS, secondarySize, SP_ATTR_INKSCAPE_BBOX_NODES); + + gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); + g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); + } + + { + InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxEdgeMidpoints", + _("BBox Edge Midpoints"), _("Snap from and to midpoints of bounding box edges"), + INKSCAPE_ICON_SNAP_BOUNDING_BOX_MIDPOINTS, secondarySize, + SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS); + + gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); + g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); + } + + { + InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxCenters", + _("BBox Centers"), _("Snapping from and to centers of bounding boxes"), + INKSCAPE_ICON_SNAP_BOUNDING_BOX_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS); + + gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); + g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); + } + + { + InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromNode", + _("Nodes"), _("Snap nodes or handles"), INKSCAPE_ICON_SNAP_NODES, secondarySize, SP_ATTR_INKSCAPE_SNAP_NODES); + + gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); + g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); + } + + { + InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemPath", + _("Paths"), _("Snap to paths"), INKSCAPE_ICON_SNAP_NODES_PATH, secondarySize, + SP_ATTR_INKSCAPE_OBJECT_PATHS); + + gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); + g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); + } + + { + InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPathIntersections", + _("Path intersections"), _("Snap to path intersections"), + INKSCAPE_ICON_SNAP_NODES_INTERSECTION, secondarySize, SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS); + + gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); + g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); + } + + { + InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemNode", + _("To nodes"), _("Snap to cusp nodes"), INKSCAPE_ICON_SNAP_NODES_CUSP, secondarySize, + SP_ATTR_INKSCAPE_OBJECT_NODES); + + gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); + g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); + } + + { + InkToggleAction* act = ink_toggle_action_new("ToggleSnapToSmoothNodes", + _("Smooth nodes"), _("Snap to smooth nodes"), INKSCAPE_ICON_SNAP_NODES_SMOOTH, + secondarySize, SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES); + + gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); + g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); + } + + { + InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromLineMidpoints", + _("Line Midpoints"), _("Snap from and to midpoints of line segments"), + INKSCAPE_ICON_SNAP_NODES_MIDPOINT, secondarySize, SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS); + + gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); + g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); + } + + { + InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromObjectCenters", + _("Object Centers"), _("Snap from and to centers of objects"), + INKSCAPE_ICON_SNAP_NODES_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS); + + gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); + g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); + } + + { + InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromRotationCenter", + _("Rotation Centers"), _("Snap from and to an item's rotation center"), + INKSCAPE_ICON_SNAP_NODES_ROTATION_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_CENTER); + + gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); + g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); + } + + { + InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPageBorder", + _("Page border"), _("Snap to the page border"), INKSCAPE_ICON_SNAP_PAGE, + secondarySize, SP_ATTR_INKSCAPE_SNAP_PAGE); + + gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); + g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); + } + + { + InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGrids", + _("Grids"), _("Snap to grids"), INKSCAPE_ICON_GRID_RECTANGULAR, secondarySize, + SP_ATTR_INKSCAPE_SNAP_GRIDS); + + gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); + g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); + } + + { + InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGuides", + _("Guides"), _("Snap to guides"), INKSCAPE_ICON_GUIDES, secondarySize, + SP_ATTR_INKSCAPE_SNAP_TO_GUIDES); + + gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); + g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); + } + + /*{ + InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGridGuideIntersections", + _("Grid/guide intersections"), _("Snap to intersections of a grid with a guide"), + INKSCAPE_ICON_SNAP_GRID_GUIDE_INTERSECTIONS, secondarySize, + SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE); + + gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); + g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); + }*/ GtkUIManager* mgr = gtk_ui_manager_new(); GError* errVal = 0; @@ -2243,94 +2242,94 @@ void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop) void update_snap_toolbox(SPDesktop *desktop, SPEventContext */*eventcontext*/, GtkWidget *toolbox) { - g_assert(desktop != NULL); - g_assert(toolbox != NULL); - - SPNamedView *nv = sp_desktop_namedview(desktop); - if (nv == NULL) { - g_warning("Namedview cannot be retrieved (in update_snap_toolbox)!"); - return; - } - - Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop); - - Glib::RefPtr<Gtk::Action> act1 = mainActions->get_action("ToggleSnapGlobal"); - Glib::RefPtr<Gtk::Action> act2 = mainActions->get_action("ToggleSnapFromBBoxCorner"); - Glib::RefPtr<Gtk::Action> act3 = mainActions->get_action("ToggleSnapToBBoxPath"); - Glib::RefPtr<Gtk::Action> act4 = mainActions->get_action("ToggleSnapToBBoxNode"); - Glib::RefPtr<Gtk::Action> act4b = mainActions->get_action("ToggleSnapToFromBBoxEdgeMidpoints"); - Glib::RefPtr<Gtk::Action> act4c = mainActions->get_action("ToggleSnapToFromBBoxCenters"); - Glib::RefPtr<Gtk::Action> act5 = mainActions->get_action("ToggleSnapFromNode"); - Glib::RefPtr<Gtk::Action> act6 = mainActions->get_action("ToggleSnapToItemPath"); - Glib::RefPtr<Gtk::Action> act6b = mainActions->get_action("ToggleSnapToPathIntersections"); - Glib::RefPtr<Gtk::Action> act7 = mainActions->get_action("ToggleSnapToItemNode"); - Glib::RefPtr<Gtk::Action> act8 = mainActions->get_action("ToggleSnapToSmoothNodes"); - Glib::RefPtr<Gtk::Action> act9 = mainActions->get_action("ToggleSnapToFromLineMidpoints"); - Glib::RefPtr<Gtk::Action> act10 = mainActions->get_action("ToggleSnapToFromObjectCenters"); - Glib::RefPtr<Gtk::Action> act11 = mainActions->get_action("ToggleSnapToFromRotationCenter"); - Glib::RefPtr<Gtk::Action> act12 = mainActions->get_action("ToggleSnapToPageBorder"); - //Glib::RefPtr<Gtk::Action> act13 = mainActions->get_action("ToggleSnapToGridGuideIntersections"); - Glib::RefPtr<Gtk::Action> act14 = mainActions->get_action("ToggleSnapToGrids"); - Glib::RefPtr<Gtk::Action> act15 = mainActions->get_action("ToggleSnapToGuides"); - - - if (!act1) { - return; // The snap actions haven't been defined yet (might be the case during startup) - } - - // The ..._set_active calls below will toggle the buttons, but this shouldn't lead to - // changes in our document because we're only updating the UI; - // Setting the "freeze" parameter to true will block the code in toggle_snap_callback() - g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(TRUE)); - - bool const c1 = nv->snap_manager.snapprefs.getSnapEnabledGlobally(); - gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act1->gobj()), c1); - - bool const c2 = nv->snap_manager.snapprefs.getSnapModeBBox(); - gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act2->gobj()), c2); - gtk_action_set_sensitive(GTK_ACTION(act2->gobj()), c1); - - gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act3->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxPath()); - gtk_action_set_sensitive(GTK_ACTION(act3->gobj()), c1 && c2); - gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxNode()); - gtk_action_set_sensitive(GTK_ACTION(act4->gobj()), c1 && c2); - gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4b->gobj()), nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints()); - gtk_action_set_sensitive(GTK_ACTION(act4b->gobj()), c1 && c2); - gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4c->gobj()), nv->snap_manager.snapprefs.getSnapBBoxMidpoints()); - gtk_action_set_sensitive(GTK_ACTION(act4c->gobj()), c1 && c2); - - bool const c3 = nv->snap_manager.snapprefs.getSnapModeNode(); - gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act5->gobj()), c3); - gtk_action_set_sensitive(GTK_ACTION(act5->gobj()), c1); - - bool const c4 = nv->snap_manager.snapprefs.getSnapToItemPath(); - gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6->gobj()), c4); - gtk_action_set_sensitive(GTK_ACTION(act6->gobj()), c1 && c3); - gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6b->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionCS()); - gtk_action_set_sensitive(GTK_ACTION(act6b->gobj()), c1 && c3 && c4); - gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act7->gobj()), nv->snap_manager.snapprefs.getSnapToItemNode()); - gtk_action_set_sensitive(GTK_ACTION(act7->gobj()), c1 && c3); - gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act8->gobj()), nv->snap_manager.snapprefs.getSnapSmoothNodes()); - gtk_action_set_sensitive(GTK_ACTION(act8->gobj()), c1 && c3); - gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act9->gobj()), nv->snap_manager.snapprefs.getSnapLineMidpoints()); - gtk_action_set_sensitive(GTK_ACTION(act9->gobj()), c1 && c3); - gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act10->gobj()), nv->snap_manager.snapprefs.getSnapObjectMidpoints()); - gtk_action_set_sensitive(GTK_ACTION(act10->gobj()), c1 && c3); - gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11->gobj()), nv->snap_manager.snapprefs.getIncludeItemCenter()); - gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c3); - - gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.snapprefs.getSnapToPageBorder()); - gtk_action_set_sensitive(GTK_ACTION(act12->gobj()), c1); - //gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act13->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionGG()); - //gtk_action_set_sensitive(GTK_ACTION(act13->gobj()), c1); - - gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act14->gobj()), nv->snap_manager.snapprefs.getSnapToGrids()); - gtk_action_set_sensitive(GTK_ACTION(act14->gobj()), c1); - gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act15->gobj()), nv->snap_manager.snapprefs.getSnapToGuides()); - gtk_action_set_sensitive(GTK_ACTION(act15->gobj()), c1); - - - g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(FALSE)); // unfreeze (see above) + g_assert(desktop != NULL); + g_assert(toolbox != NULL); + + SPNamedView *nv = sp_desktop_namedview(desktop); + if (nv == NULL) { + g_warning("Namedview cannot be retrieved (in update_snap_toolbox)!"); + return; + } + + Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop); + + Glib::RefPtr<Gtk::Action> act1 = mainActions->get_action("ToggleSnapGlobal"); + Glib::RefPtr<Gtk::Action> act2 = mainActions->get_action("ToggleSnapFromBBoxCorner"); + Glib::RefPtr<Gtk::Action> act3 = mainActions->get_action("ToggleSnapToBBoxPath"); + Glib::RefPtr<Gtk::Action> act4 = mainActions->get_action("ToggleSnapToBBoxNode"); + Glib::RefPtr<Gtk::Action> act4b = mainActions->get_action("ToggleSnapToFromBBoxEdgeMidpoints"); + Glib::RefPtr<Gtk::Action> act4c = mainActions->get_action("ToggleSnapToFromBBoxCenters"); + Glib::RefPtr<Gtk::Action> act5 = mainActions->get_action("ToggleSnapFromNode"); + Glib::RefPtr<Gtk::Action> act6 = mainActions->get_action("ToggleSnapToItemPath"); + Glib::RefPtr<Gtk::Action> act6b = mainActions->get_action("ToggleSnapToPathIntersections"); + Glib::RefPtr<Gtk::Action> act7 = mainActions->get_action("ToggleSnapToItemNode"); + Glib::RefPtr<Gtk::Action> act8 = mainActions->get_action("ToggleSnapToSmoothNodes"); + Glib::RefPtr<Gtk::Action> act9 = mainActions->get_action("ToggleSnapToFromLineMidpoints"); + Glib::RefPtr<Gtk::Action> act10 = mainActions->get_action("ToggleSnapToFromObjectCenters"); + Glib::RefPtr<Gtk::Action> act11 = mainActions->get_action("ToggleSnapToFromRotationCenter"); + Glib::RefPtr<Gtk::Action> act12 = mainActions->get_action("ToggleSnapToPageBorder"); + //Glib::RefPtr<Gtk::Action> act13 = mainActions->get_action("ToggleSnapToGridGuideIntersections"); + Glib::RefPtr<Gtk::Action> act14 = mainActions->get_action("ToggleSnapToGrids"); + Glib::RefPtr<Gtk::Action> act15 = mainActions->get_action("ToggleSnapToGuides"); + + + if (!act1) { + return; // The snap actions haven't been defined yet (might be the case during startup) + } + + // The ..._set_active calls below will toggle the buttons, but this shouldn't lead to + // changes in our document because we're only updating the UI; + // Setting the "freeze" parameter to true will block the code in toggle_snap_callback() + g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(TRUE)); + + bool const c1 = nv->snap_manager.snapprefs.getSnapEnabledGlobally(); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act1->gobj()), c1); + + bool const c2 = nv->snap_manager.snapprefs.getSnapModeBBox(); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act2->gobj()), c2); + gtk_action_set_sensitive(GTK_ACTION(act2->gobj()), c1); + + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act3->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxPath()); + gtk_action_set_sensitive(GTK_ACTION(act3->gobj()), c1 && c2); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxNode()); + gtk_action_set_sensitive(GTK_ACTION(act4->gobj()), c1 && c2); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4b->gobj()), nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints()); + gtk_action_set_sensitive(GTK_ACTION(act4b->gobj()), c1 && c2); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4c->gobj()), nv->snap_manager.snapprefs.getSnapBBoxMidpoints()); + gtk_action_set_sensitive(GTK_ACTION(act4c->gobj()), c1 && c2); + + bool const c3 = nv->snap_manager.snapprefs.getSnapModeNode(); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act5->gobj()), c3); + gtk_action_set_sensitive(GTK_ACTION(act5->gobj()), c1); + + bool const c4 = nv->snap_manager.snapprefs.getSnapToItemPath(); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6->gobj()), c4); + gtk_action_set_sensitive(GTK_ACTION(act6->gobj()), c1 && c3); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6b->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionCS()); + gtk_action_set_sensitive(GTK_ACTION(act6b->gobj()), c1 && c3 && c4); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act7->gobj()), nv->snap_manager.snapprefs.getSnapToItemNode()); + gtk_action_set_sensitive(GTK_ACTION(act7->gobj()), c1 && c3); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act8->gobj()), nv->snap_manager.snapprefs.getSnapSmoothNodes()); + gtk_action_set_sensitive(GTK_ACTION(act8->gobj()), c1 && c3); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act9->gobj()), nv->snap_manager.snapprefs.getSnapLineMidpoints()); + gtk_action_set_sensitive(GTK_ACTION(act9->gobj()), c1 && c3); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act10->gobj()), nv->snap_manager.snapprefs.getSnapObjectMidpoints()); + gtk_action_set_sensitive(GTK_ACTION(act10->gobj()), c1 && c3); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11->gobj()), nv->snap_manager.snapprefs.getIncludeItemCenter()); + gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c3); + + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.snapprefs.getSnapToPageBorder()); + gtk_action_set_sensitive(GTK_ACTION(act12->gobj()), c1); + //gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act13->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionGG()); + //gtk_action_set_sensitive(GTK_ACTION(act13->gobj()), c1); + + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act14->gobj()), nv->snap_manager.snapprefs.getSnapToGrids()); + gtk_action_set_sensitive(GTK_ACTION(act14->gobj()), c1); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act15->gobj()), nv->snap_manager.snapprefs.getSnapToGuides()); + gtk_action_set_sensitive(GTK_ACTION(act15->gobj()), c1); + + + g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(FALSE)); // unfreeze (see above) } void show_aux_toolbox(GtkWidget *toolbox_toplevel) @@ -2412,8 +2411,8 @@ static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKl if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) { if (!IS_NAN(adj->value)) { - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - prefs->setDouble("/tools/shapes/star/proportion", adj->value); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->setDouble("/tools/shapes/star/proportion", adj->value); } } @@ -3226,7 +3225,7 @@ box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis, // TODO: Take all selected perspectives into account but don't touch the state button if not all of them // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states // are reset). - bool is_infinite = !persp3d_VP_is_finite(persp, axis); + bool is_infinite = !persp3d_VP_is_finite(persp->perspective_impl, axis); if (is_infinite) { gtk_toggle_action_set_active(tact, TRUE); @@ -3254,6 +3253,10 @@ box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) { GtkAction *act = 0; GtkToggleAction *tact = 0; Persp3D *persp = persp3d_get_from_repr(persp_repr); + if (!persp) { + // Hmm, is it an error if this happens? + return; + } { adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x")); act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action")); @@ -3340,7 +3343,7 @@ box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl) sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl); } - inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr); + inkscape_active_document()->setCurrentPersp3D(persp3d_get_from_repr(persp_repr)); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); prefs->setString("/tools/shapes/3dbox/persp", persp_repr->attribute("id")); @@ -3364,7 +3367,6 @@ box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis ax // in turn, prevent listener from responding g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE)); - //Persp3D *persp = document->current_persp3d; std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList(); if (sel_persps.empty()) { // this can happen when the document is created; we silently ignore it @@ -3372,7 +3374,7 @@ box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis ax } Persp3D *persp = sel_persps.front(); - persp->tmat.set_infinite_direction (axis, adj->value); + persp->perspective_impl->tmat.set_infinite_direction (axis, adj->value); SP_OBJECT(persp)->updateRepr(); // TODO: use the correct axis here, too @@ -3435,7 +3437,7 @@ static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, Inkscape::Preferences *prefs = Inkscape::Preferences::get(); EgeAdjustmentAction* eact = 0; SPDocument *document = sp_desktop_document (desktop); - Persp3D *persp = document->current_persp3d; + Persp3DImpl *persp_impl = document->getCurrentPersp3DImpl(); EgeAdjustmentAction* box3d_angle_x = 0; EgeAdjustmentAction* box3d_angle_y = 0; @@ -3459,7 +3461,7 @@ static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, box3d_angle_x = eact; } - if (!persp3d_VP_is_finite(persp, Proj::X)) { + if (!persp3d_VP_is_finite(persp_impl, Proj::X)) { gtk_action_set_sensitive( GTK_ACTION(eact), TRUE ); } else { gtk_action_set_sensitive( GTK_ACTION(eact), FALSE ); @@ -3499,7 +3501,7 @@ static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, box3d_angle_y = eact; } - if (!persp3d_VP_is_finite(persp, Proj::Y)) { + if (!persp3d_VP_is_finite(persp_impl, Proj::Y)) { gtk_action_set_sensitive( GTK_ACTION(eact), TRUE ); } else { gtk_action_set_sensitive( GTK_ACTION(eact), FALSE ); @@ -3538,7 +3540,7 @@ static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, box3d_angle_z = eact; } - if (!persp3d_VP_is_finite(persp, Proj::Z)) { + if (!persp3d_VP_is_finite(persp_impl, Proj::Z)) { gtk_action_set_sensitive( GTK_ACTION(eact), TRUE ); } else { gtk_action_set_sensitive( GTK_ACTION(eact), FALSE ); @@ -3988,6 +3990,7 @@ sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl) g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); } +/* class PencilToleranceObserver : public Inkscape::Preferences::Observer { public: PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x) @@ -3996,7 +3999,7 @@ public: } virtual ~PencilToleranceObserver() { if (g_object_get_data(_obj, "prefobserver") == this) { - g_object_set_data(_obj, "prefobserver", NULL); + g_object_set_data(_obj, "prefobserver", NULL); } } virtual void notify(Inkscape::Preferences::Entry const &val) { @@ -4015,7 +4018,7 @@ public: private: GObject *_obj; }; - +*/ static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder) { @@ -4040,9 +4043,6 @@ static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActio 1, 2); ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT ); gtk_action_group_add_action( mainActions, GTK_ACTION(eact) ); - - PencilToleranceObserver *obs = - new PencilToleranceObserver("/tools/freehand/pencil/tolerance", G_OBJECT(holder)); } /* advanced shape options */ @@ -4408,14 +4408,6 @@ static void sp_spray_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ ) prefs->setDouble( "/tools/spray/width", adj->value ); } -/* -static void sp_spray_force_value_changed( GtkAdjustment * / *adj* /, GObject * / *tbl* / ) -{ - //Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - //prefs->setDouble( "/tools/spray/force", adj->value * 0.01 ); -} -*/ - static void sp_spray_mean_value_changed( GtkAdjustment *adj, GObject */*tbl*/ ) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); @@ -4447,23 +4439,19 @@ static void sp_spray_population_value_changed( GtkAdjustment *adj, GObject */*tb prefs->setDouble( "/tools/spray/population", adj->value ); } -/*static void spray_toggle_doh (GtkToggleAction *act, gpointer ) { - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - prefs->setBool("/tools/spray/doh", gtk_toggle_action_get_active(act)); -} -static void spray_toggle_dos (GtkToggleAction *act, gpointer ) { - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - prefs->setBool("/tools/spray/dos", gtk_toggle_action_get_active(act)); -} -static void spray_toggle_dol (GtkToggleAction *act, gpointer ) { +static void sp_spray_rotation_value_changed( GtkAdjustment *adj, GObject */*tbl*/ ) +{ Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - prefs->setBool("/tools/spray/dol", gtk_toggle_action_get_active(act)); + prefs->setDouble( "/tools/spray/rotation_variation", adj->value ); } -static void spray_toggle_doo (GtkToggleAction *act, gpointer ) { + +static void sp_spray_scale_value_changed( GtkAdjustment *adj, GObject */*tbl*/ ) +{ Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - prefs->setBool("/tools/spray/doo", gtk_toggle_action_get_active(act)); + prefs->setDouble( "/tools/spray/scale_variation", adj->value ); } -*/ + + static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder) { Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1); @@ -4490,10 +4478,10 @@ static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainAction gchar const* labels[] = {_("(minimum mean)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum mean)")}; gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100}; EgeAdjustmentAction *eact = create_adjustment_action( "SprayMeanAction", - _("Mean"), _("Mean:"), _("The mean of the spray action"), - "/tools/spray/mean", 20, + _("Focus"), _("Focus:"), _("0 to spray a spot. Increase to enlarge the ring radius."), + "/tools/spray/mean", 0, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-mean", - 1, 100, 1.0, 10.0, + 0, 100, 1.0, 10.0, labels, values, G_N_ELEMENTS(labels), sp_spray_mean_value_changed, 1, 0 ); ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT ); @@ -4503,11 +4491,11 @@ static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainAction { /* Standard_deviation */ - gchar const* labels[] = {_("(minimum standard_deviation)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum standard_deviation)")}; + gchar const* labels[] = {_("(minimum scatter)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum scatter)")}; gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100}; EgeAdjustmentAction *eact = create_adjustment_action( "SprayStandard_deviationAction", - _("SD"), _("SD:"), _("The standard deviation of the spray action"), - "/tools/spray/standard_deviation", 20, + _("Scatter"), _("Scatter:"), _("Increase to scatter sprayed objects."), + "/tools/spray/standard_deviation", 70, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-standard_deviation", 1, 100, 1.0, 10.0, labels, values, G_N_ELEMENTS(labels), @@ -4555,7 +4543,7 @@ static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainAction ege_select_one_action_set_icon_size( act, secondarySize ); ege_select_one_action_set_tooltip_column( act, 1 ); - gint mode = prefs->getInt("/tools/spray/mode", 0); + gint mode = prefs->getInt("/tools/spray/mode", 1); ege_select_one_action_set_active( act, mode ); g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_spray_mode_changed), holder ); @@ -4566,9 +4554,9 @@ static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainAction gchar const* labels[] = {_("(low population)"), 0, 0, _("(default)"), 0, 0, _("(high population)")}; gdouble values[] = {10, 25, 35, 50, 60, 80, 100}; EgeAdjustmentAction *eact = create_adjustment_action( "SprayPopulationAction", - _("Population"), _("Population:"), - _("This setting adjusts the number of items sprayed"), - "/tools/spray/population", 50, + _("Amount"), _("Amount:"), + _("Adjusts the number of items sprayed per clic."), + "/tools/spray/population", 70, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-population", 1, 100, 1.0, 10.0, labels, values, G_N_ELEMENTS(labels), @@ -4582,13 +4570,48 @@ static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainAction { InkToggleAction* act = ink_toggle_action_new( "SprayPressureAction", _("Pressure"), - _("Use the pressure of the input device to alter the force of spray action"), + _("Use the pressure of the input device to alter the amount of sprayed objects."), "use_pressure", Inkscape::ICON_SIZE_DECORATION ); gtk_action_group_add_action( mainActions, GTK_ACTION( act ) ); g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_spray_pressure_state_changed), NULL); gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/spray/usepressure", true) ); } + + { /* Rotation */ + gchar const* labels[] = {_("(low rotation variation)"), 0, 0, _("(default)"), 0, 0, _("(high rotation variation)")}; + gdouble values[] = {10, 25, 35, 50, 60, 80, 100}; + EgeAdjustmentAction *eact = create_adjustment_action( "SprayRotationAction", + _("Rotation"), _("Rotation:"), + _("Variation of the rotation of the sprayed objects. 0% for the same rotation than the original object."), + "/tools/spray/rotation_variation", 0, + GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-rotation", + 0, 100, 1.0, 10.0, + labels, values, G_N_ELEMENTS(labels), + sp_spray_rotation_value_changed, 1, 0 ); + gtk_action_group_add_action( mainActions, GTK_ACTION(eact) ); + gtk_action_set_sensitive( GTK_ACTION(eact), TRUE ); + g_object_set_data( holder, "spray_rotation", eact ); + } + + { /* Scale */ + gchar const* labels[] = {_("(low scale variation)"), 0, 0, _("(default)"), 0, 0, _("(high scale variation)")}; + gdouble values[] = {10, 25, 35, 50, 60, 80, 100}; + EgeAdjustmentAction *eact = create_adjustment_action( "SprayScaleAction", + _("Scale"), _("Scale:"), + _("Variation in the scale of the sprayed objects. 0% for the same scale than the original object."), + "/tools/spray/scale_variation", 0, + GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-scale", + 0, 100, 1.0, 10.0, + labels, values, G_N_ELEMENTS(labels), + sp_spray_scale_value_changed, 1, 0 ); + gtk_action_group_add_action( mainActions, GTK_ACTION(eact) ); + gtk_action_set_sensitive( GTK_ACTION(eact), TRUE ); + g_object_set_data( holder, "spray_scale", eact ); + } + + + } @@ -4639,9 +4662,9 @@ static void update_presets_list (GObject *tbl) } } } - } + } - if (match) { + if (match) { // newly added item is at the same index as the // save command, so we need to change twice for it to take effect ege_select_one_action_set_active(sel, 0); @@ -4770,7 +4793,7 @@ static void sp_dcc_build_presets_list(GObject *tbl) int ii=1; for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) { - GtkTreeIter iter; + GtkTreeIter iter; Glib::ustring preset_name = prefs->getString(*i + "/name"); gtk_list_store_append( model, &iter ); gtk_list_store_set( model, &iter, 0, _(preset_name.data()), 1, ii++, -1 ); @@ -4822,12 +4845,12 @@ static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl) int temp_index = 0; for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) { - Glib::ustring name = prefs->getString(*i + "/name"); - if (!name.empty() && profile_name == name) { - new_index = temp_index; + Glib::ustring name = prefs->getString(*i + "/name"); + if (!name.empty() && profile_name == name) { + new_index = temp_index; save_path = *i; break; - } + } } if (new_index == -1) { @@ -6065,9 +6088,9 @@ sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject * if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) { // there are no texts in selection, read from prefs - sp_style_read_from_prefs(query, "/tools/text"); + sp_style_read_from_prefs(query, "/tools/text"); - if (g_object_get_data(tbl, "text_style_from_prefs")) { + if (g_object_get_data(tbl, "text_style_from_prefs")) { // do not reset the toolbar style from prefs if we already did it last time sp_style_unref(query); g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); @@ -6218,7 +6241,7 @@ sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flag } void -sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl) +sp_text_toolbox_subselection_changed (gpointer /*tc*/, GObject *tbl) { sp_text_toolbox_selection_changed (NULL, tbl); } @@ -6344,8 +6367,85 @@ sp_text_toolbox_anchoring_toggled (GtkRadioButton *button, int prop = GPOINTER_TO_INT(data); SPDesktop *desktop = SP_ACTIVE_DESKTOP; - SPCSSAttr *css = sp_repr_css_attr_new (); + // move the x of all texts to preserve the same bbox + Inkscape::Selection *selection = sp_desktop_selection(desktop); + for (GSList const *items = selection->itemList(); items != NULL; items = items->next) { + if (SP_IS_TEXT((SPItem *) items->data)) { + SPItem *item = SP_ITEM(items->data); + + unsigned writing_mode = SP_OBJECT_STYLE(item)->writing_mode.value; + // below, variable names suggest horizontal move, but we check the writing direction + // and move in the corresponding axis + int axis; + if (writing_mode == SP_CSS_WRITING_MODE_LR_TB || writing_mode == SP_CSS_WRITING_MODE_RL_TB) { + axis = NR::X; + } else { + axis = NR::Y; + } + + Geom::OptRect bbox + = item->getBounds(Geom::identity(), SPItem::GEOMETRIC_BBOX); + if (!bbox) + continue; + double width = bbox->dimensions()[axis]; + // If you want to align within some frame, other than the text's own bbox, calculate + // the left and right (or top and bottom for tb text) slacks of the text inside that + // frame (currently unused) + double left_slack = 0; + double right_slack = 0; + unsigned old_align = SP_OBJECT_STYLE(item)->text_align.value; + double move = 0; + if (old_align == SP_CSS_TEXT_ALIGN_START || old_align == SP_CSS_TEXT_ALIGN_LEFT) { + switch (prop) { + case 0: + move = -left_slack; + break; + case 1: + move = width/2 + (right_slack - left_slack)/2; + break; + case 2: + move = width + right_slack; + break; + } + } else if (old_align == SP_CSS_TEXT_ALIGN_CENTER) { + switch (prop) { + case 0: + move = -width/2 - left_slack; + break; + case 1: + move = (right_slack - left_slack)/2; + break; + case 2: + move = width/2 + right_slack; + break; + } + } else if (old_align == SP_CSS_TEXT_ALIGN_END || old_align == SP_CSS_TEXT_ALIGN_RIGHT) { + switch (prop) { + case 0: + move = -width - left_slack; + break; + case 1: + move = -width/2 + (right_slack - left_slack)/2; + break; + case 2: + move = right_slack; + break; + } + } + Geom::Point XY = SP_TEXT(item)->attributes.firstXY(); + if (axis == NR::X) { + XY = XY + Geom::Point (move, 0); + } else { + XY = XY + Geom::Point (0, move); + } + SP_TEXT(item)->attributes.setFirstXY(XY); + SP_OBJECT(item)->updateRepr(); + SP_OBJECT(item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + } + } + + SPCSSAttr *css = sp_repr_css_attr_new (); switch (prop) { case 0: @@ -6384,8 +6484,8 @@ sp_text_toolbox_anchoring_toggled (GtkRadioButton *button, // If querying returned nothing, read the style from the text tool prefs (default style for new texts) if (result_numbers == QUERY_STYLE_NOTHING) { - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - prefs->mergeStyle("/tools/text/style", css); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->mergeStyle("/tools/text/style", css); } sp_style_unref(query); @@ -6488,8 +6588,8 @@ sp_text_toolbox_style_toggled (GtkToggleButton *button, // If querying returned nothing, read the style from the text tool prefs (default style for new texts) if (result_fontspec == QUERY_STYLE_NOTHING) { - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - prefs->mergeStyle("/tools/text/style", css); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->mergeStyle("/tools/text/style", css); } sp_style_unref(query); @@ -6538,7 +6638,7 @@ sp_text_toolbox_orientation_toggled (GtkRadioButton *button, if (result_numbers == QUERY_STYLE_NOTHING) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - prefs->mergeStyle("/tools/text/style", css); + prefs->mergeStyle("/tools/text/style", css); } sp_desktop_set_style (desktop, css, true, true); @@ -6650,7 +6750,7 @@ sp_text_toolbox_size_changed (GtkComboBox *cbox, if (result_numbers == QUERY_STYLE_NOTHING) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - prefs->mergeStyle("/tools/text/style", css); + prefs->mergeStyle("/tools/text/style", css); } sp_style_unref(query); |
