From: buliabyak Date: Tue, 13 Feb 2007 17:18:22 +0000 (+0000) Subject: isolate the nodepath-or-knotholder unit into the new ShapeEditor class which handles... X-Git-Url: https://git.tokkee.org/?a=commitdiff_plain;h=2f8a505c173a704ab6fb25538bb3c1afe5ce9e4e;p=inkscape.git isolate the nodepath-or-knotholder unit into the new ShapeEditor class which handles listeners etc. and provides a single interface to work with nodepath or knotholder; later will add another layer, ShapeEditorsCollective, which will allow us to have any number of nodepaths and knotholders simultaneously --- diff --git a/src/Makefile_insert b/src/Makefile_insert index 3af6748c3..acccdc327 100644 --- a/src/Makefile_insert +++ b/src/Makefile_insert @@ -128,6 +128,7 @@ libinkpre_a_SOURCES = \ selection.cpp selection.h \ seltrans-handles.cpp seltrans-handles.h \ seltrans.cpp seltrans.h \ + shape-editor.cpp shape-editor.h \ shortcuts.cpp shortcuts.h \ slideshow.cpp slideshow.h \ snap.cpp snap.h \ diff --git a/src/node-context.cpp b/src/node-context.cpp index a4a51f676..000818161 100644 --- a/src/node-context.cpp +++ b/src/node-context.cpp @@ -7,8 +7,7 @@ * Lauris Kaplinski * bulia byak * - * This code is in public domain, except stamping code, - * which is Copyright (C) Masatake Yamato 2002 + * This code is in public domain */ #ifdef HAVE_CONFIG_H @@ -33,6 +32,7 @@ #include "xml/node-event-vector.h" #include "style.h" #include "splivarot.h" +#include "shape-editor.h" static void sp_node_context_class_init(SPNodeContextClass *klass); static void sp_node_context_init(SPNodeContext *node_context); @@ -43,18 +43,6 @@ static gint sp_node_context_root_handler(SPEventContext *event_context, GdkEvent static gint sp_node_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event); -static void nodepath_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name, - gchar const *old_value, gchar const *new_value, - bool is_interactive, gpointer data); - -static Inkscape::XML::NodeEventVector nodepath_repr_events = { - NULL, /* child_added */ - NULL, /* child_removed */ - nodepath_event_attr_changed, - NULL, /* content_changed */ - NULL /* order_changed */ -}; - static SPEventContextClass *parent_class; GType @@ -120,29 +108,7 @@ sp_node_context_dispose(GObject *object) nc->sel_changed_connection.disconnect(); nc->sel_changed_connection.~connection(); - Inkscape::XML::Node *repr = NULL; - if (nc->nodepath) { - repr = nc->nodepath->repr; - } - if (!repr && ec->shape_knot_holder) { - repr = ec->shape_knot_holder->repr; - } - - if (repr) { - sp_repr_remove_listener_by_data(repr, ec); - Inkscape::GC::release(repr); - } - - if (nc->nodepath) { - nc->grab_node = -1; - sp_nodepath_destroy(nc->nodepath); - nc->nodepath = NULL; - } - - if (ec->shape_knot_holder) { - sp_knot_holder_destroy(ec->shape_knot_holder); - ec->shape_knot_holder = NULL; - } + delete nc->shape_editor; if (nc->_node_message_context) { delete nc->_node_message_context; @@ -165,9 +131,7 @@ sp_node_context_setup(SPEventContext *ec) Inkscape::Selection *selection = sp_desktop_selection(ec->desktop); SPItem *item = selection->singleItem(); - nc->grab_node = -1; - nc->nodepath = NULL; - ec->shape_knot_holder = NULL; + nc->shape_editor = new ShapeEditor(ec->desktop); nc->rb_escaped = false; @@ -178,25 +142,7 @@ sp_node_context_setup(SPEventContext *ec) nc->current_state = SP_NODE_CONTEXT_INACTIVE; if (item) { - nc->nodepath = sp_nodepath_new(ec->desktop, item, (prefs_get_int_attribute("tools.nodes", "show_handles", 1) != 0)); - if ( nc->nodepath) { - //point pack to parent in case nodepath is deleted - nc->nodepath->nodeContext = nc; - } - ec->shape_knot_holder = sp_item_knot_holder(item, ec->desktop); - - if (nc->nodepath || ec->shape_knot_holder) { - // setting listener - Inkscape::XML::Node *repr; - if (ec->shape_knot_holder) - repr = ec->shape_knot_holder->repr; - else - repr = SP_OBJECT_REPR(item); - if (repr) { - Inkscape::GC::anchor(repr); - sp_repr_add_listener(repr, &nodepath_repr_events, ec); - } - } + nc->shape_editor->set_item(item); } if (prefs_get_int_attribute("tools.nodes", "selcue", 0) != 0) { @@ -208,7 +154,8 @@ sp_node_context_setup(SPEventContext *ec) } nc->_node_message_context = new Inkscape::MessageContext((ec->desktop)->messageStack()); - sp_nodepath_update_statusbar(nc->nodepath); + + nc->shape_editor->update_statusbar(); } /** @@ -219,143 +166,13 @@ void sp_node_context_selection_changed(Inkscape::Selection *selection, gpointer data) { SPNodeContext *nc = SP_NODE_CONTEXT(data); - SPEventContext *ec = SP_EVENT_CONTEXT(nc); - - Inkscape::XML::Node *old_repr = NULL; - if (nc->nodepath) { - old_repr = nc->nodepath->repr; - nc->grab_node = -1; - sp_nodepath_destroy(nc->nodepath); - nc->nodepath = NULL; - } - - if (ec->shape_knot_holder) { - old_repr = ec->shape_knot_holder->repr; - sp_knot_holder_destroy(ec->shape_knot_holder); - } + // TODO: update ShapeEditorsCollective instead + nc->shape_editor->unset_item(); + SPItem *item = selection->singleItem(); + nc->shape_editor->set_item(item); - if (old_repr) { // remove old listener - sp_repr_remove_listener_by_data(old_repr, ec); - Inkscape::GC::release(old_repr); - } - - SPItem *item = selection->singleItem(); - - SPDesktop *desktop = selection->desktop(); - nc->grab_node = -1; - nc->nodepath = NULL; - ec->shape_knot_holder = NULL; - if (item) { - nc->nodepath = sp_nodepath_new(desktop, item, (prefs_get_int_attribute("tools.nodes", "show_handles", 1) != 0)); - if (nc->nodepath) { - nc->nodepath->nodeContext = nc; - } - ec->shape_knot_holder = sp_item_knot_holder(item, desktop); - - if (nc->nodepath || ec->shape_knot_holder) { - // setting new listener - Inkscape::XML::Node *repr; - if (ec->shape_knot_holder) - repr = ec->shape_knot_holder->repr; - else - repr = SP_OBJECT_REPR(item); - if (repr) { - Inkscape::GC::anchor(repr); - sp_repr_add_listener(repr, &nodepath_repr_events, ec); - } - } - } - sp_nodepath_update_statusbar(nc->nodepath); -} - -/** -\brief Regenerates nodepath when the item's repr was change outside of node edit -(e.g. by undo, or xml editor, or edited in another view). The item is assumed to be the same -(otherwise sp_node_context_selection_changed() would have been called), so repr and listeners -are not changed. -*/ -void -sp_nodepath_update_from_item(SPNodeContext *nc, SPItem *item) -{ - g_assert(nc); - SPEventContext *ec = ((SPEventContext *) nc); - - SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(SP_EVENT_CONTEXT(nc)); - g_assert(desktop); - - if (nc->nodepath) { - nc->grab_node = -1; - sp_nodepath_destroy(nc->nodepath); - nc->nodepath = NULL; - } - - if (ec->shape_knot_holder) { - sp_knot_holder_destroy(ec->shape_knot_holder); - ec->shape_knot_holder = NULL; - } - - Inkscape::Selection *selection = sp_desktop_selection(desktop); - item = selection->singleItem(); - - if (item) { - nc->nodepath = sp_nodepath_new(desktop, item, (prefs_get_int_attribute("tools.nodes", "show_handles", 1) != 0)); - if (nc->nodepath) { - nc->nodepath->nodeContext = nc; - } - ec->shape_knot_holder = sp_item_knot_holder(item, desktop); - } - sp_nodepath_update_statusbar(nc->nodepath); -} - -/** -\brief Callback that is fired whenever an attribute of the selected item (which we have in the nodepath) changes -*/ -static void -nodepath_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name, - gchar const *old_value, gchar const *new_value, - bool is_interactive, gpointer data) -{ - SPItem *item = NULL; - gboolean changed = FALSE; - - g_assert(data); - SPNodeContext *nc = ((SPNodeContext *) data); - SPEventContext *ec = ((SPEventContext *) data); - g_assert(nc); - Inkscape::NodePath::Path *np = nc->nodepath; - SPKnotHolder *kh = ec->shape_knot_holder; - - if (np) { - item = SP_ITEM(np->path); - if (!strcmp(name, "d") || !strcmp(name, "sodipodi:nodetypes")) { // With paths, we only need to act if one of the path-affecting attributes has changed. - changed = (np->local_change == 0); - if (np->local_change > 0) - np->local_change--; - } - - } else if (kh) { - item = SP_ITEM(kh->item); - changed = !(kh->local_change); - kh->local_change = FALSE; - } - - if (np && changed) { - GList *saved = NULL; - SPDesktop *desktop = np->desktop; - g_assert(desktop); - Inkscape::Selection *selection = desktop->selection; - g_assert(selection); - - saved = save_nodepath_selection(nc->nodepath); - sp_nodepath_update_from_item(nc, item); - if (nc->nodepath && saved) restore_nodepath_selection(nc->nodepath, saved); - - } else if (kh && changed) { - sp_nodepath_update_from_item(nc, item); - } - - sp_nodepath_update_statusbar(nc->nodepath); + nc->shape_editor->update_statusbar(); } void @@ -368,44 +185,6 @@ sp_node_context_show_modifier_tip(SPEventContext *event_context, GdkEvent *event _("Alt: lock handle length; Ctrl+Alt: move along handles")); } -bool -sp_node_context_is_over_stroke (SPNodeContext *nc, SPItem *item, NR::Point event_p, bool remember) -{ - SPDesktop *desktop = SP_EVENT_CONTEXT (nc)->desktop; - - //Translate click point into proper coord system - nc->curvepoint_doc = desktop->w2d(event_p); - nc->curvepoint_doc *= sp_item_dt2i_affine(item); - nc->curvepoint_doc *= sp_item_i2doc_affine(item); - - sp_nodepath_ensure_livarot_path(nc->nodepath); - NR::Maybe position = get_nearest_position_on_Path(nc->nodepath->livarot_path, nc->curvepoint_doc); - NR::Point nearest = get_point_on_Path(nc->nodepath->livarot_path, position.assume().piece, position.assume().t); - NR::Point delta = nearest - nc->curvepoint_doc; - - delta = desktop->d2w(delta); - - double stroke_tolerance = - (SP_OBJECT_STYLE (item)->stroke.type != SP_PAINT_TYPE_NONE? - desktop->current_zoom() * - SP_OBJECT_STYLE (item)->stroke_width.computed * - sp_item_i2d_affine (item).expansion() * 0.5 - : 0.0) - + (double) SP_EVENT_CONTEXT(nc)->tolerance; - - bool close = (NR::L2 (delta) < stroke_tolerance); - - if (remember && close) { - nc->curvepoint_event[NR::X] = (gint) event_p [NR::X]; - nc->curvepoint_event[NR::Y] = (gint) event_p [NR::Y]; - nc->hit = true; - nc->grab_t = position.assume().t; - nc->grab_node = position.assume().piece; - } - - return close; -} - static gint sp_node_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event) @@ -432,8 +211,8 @@ sp_node_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEve NR::Point(event->button.x, event->button.y)); bool over_stroke = false; - if (item_over && nc->nodepath) { - over_stroke = sp_node_context_is_over_stroke (nc, item_over, NR::Point(event->button.x, event->button.y), false); + if (item_over && nc->shape_editor->has_nodepath()) { + over_stroke = nc->shape_editor->is_over_stroke(NR::Point(event->button.x, event->button.y), false); } if (over_stroke || nc->added_node) { @@ -441,7 +220,7 @@ sp_node_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEve case GDK_BUTTON_RELEASE: if (event->button.state & GDK_CONTROL_MASK && event->button.state & GDK_MOD1_MASK) { //add a node - sp_nodepath_add_node_near_point(nc->nodepath, nc->curvepoint_doc); + nc->shape_editor->add_node_near_point(); } else { if (nc->added_node) { // we just received double click, ignore release nc->added_node = false; @@ -449,16 +228,16 @@ sp_node_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEve } //select the segment if (event->button.state & GDK_SHIFT_MASK) { - sp_nodepath_select_segment_near_point(nc->nodepath, nc->curvepoint_doc, true); + nc->shape_editor->select_segment_near_point(true); } else { - sp_nodepath_select_segment_near_point(nc->nodepath, nc->curvepoint_doc, false); + nc->shape_editor->select_segment_near_point(false); } desktop->updateNow(); } break; case GDK_2BUTTON_PRESS: //add a node - sp_nodepath_add_node_near_point(nc->nodepath, nc->curvepoint_doc); + nc->shape_editor->add_node_near_point(); nc->added_node = true; break; default: @@ -483,20 +262,20 @@ sp_node_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEve event_context->xp = (gint) event->button.x; event_context->yp = (gint) event->button.y; event_context->within_tolerance = true; - nc->hit = false; + nc->shape_editor->cancel_hit(); if (!nc->drag) { // find out if we're over the selected item, disregarding groups SPItem *item_over = sp_event_context_over_item (desktop, selection->singleItem(), NR::Point(event->button.x, event->button.y)); - if (nc->nodepath && selection->single() && item_over) { + if (nc->shape_editor->has_nodepath() && selection->single() && item_over) { // save drag origin - bool over_stroke = sp_node_context_is_over_stroke (nc, item_over, NR::Point(event->button.x, event->button.y), true); + bool over_stroke = nc->shape_editor->is_over_stroke(NR::Point(event->button.x, event->button.y), true); //only dragging curves if (over_stroke) { - sp_nodepath_select_segment_near_point(nc->nodepath, nc->curvepoint_doc, false); + nc->shape_editor->select_segment_near_point(false); ret = TRUE; } else { break; @@ -528,11 +307,6 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event) SPDesktop *desktop = event_context->desktop; Inkscape::Selection *selection = sp_desktop_selection (desktop); - // fixme: nc->nodepath can potentially become NULL after retrieving nc. - // A general method for handling this possibility should be created. - // For now, the number of checks for a NULL nc->nodepath have been - // increased, both here and in the called sp_nodepath_* functions. - SPNodeContext *nc = SP_NODE_CONTEXT(event_context); double const nudge = prefs_get_double_attribute_limited("options.nudgedistance", "value", 2, 0, 1000); // in px event_context->tolerance = prefs_get_int_attribute_limited("options.dragtolerance", "value", 0, 0, 100); // read every time, to make prefs changes really live @@ -548,7 +322,7 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event) event_context->xp = (gint) event->button.x; event_context->yp = (gint) event->button.y; event_context->within_tolerance = true; - nc->hit = false; + nc->shape_editor->cancel_hit(); NR::Point const button_w(event->button.x, event->button.y); @@ -570,7 +344,8 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event) // The path went away while dragging; throw away any further motion // events until the mouse pointer is released. - if (nc->hit && (nc->nodepath == NULL)) { + + if (nc->shape_editor->hits_curve() && !nc->shape_editor->has_nodepath()) { break; } @@ -583,7 +358,7 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event) // selection rubberband), make sure we continue to perform that operation // until the mouse pointer is lifted. if (nc->current_state == SP_NODE_CONTEXT_INACTIVE) { - if (nc->nodepath && nc->hit) { + if (nc->shape_editor->hits_curve() && nc->shape_editor->has_nodepath()) { nc->current_state = SP_NODE_CONTEXT_NODE_DRAGGING; } else { nc->current_state = SP_NODE_CONTEXT_RUBBERBAND_DRAGGING; @@ -593,30 +368,8 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event) switch (nc->current_state) { case SP_NODE_CONTEXT_NODE_DRAGGING: { - if (nc->grab_node == -1) // don't know which segment to drag - break; + nc->shape_editor->curve_drag (event->motion.x, event->motion.y); - // We round off the extra precision in the motion coordinates provided - // by some input devices (like tablets). As we'll store the coordinates - // as integers in curvepoint_event we need to do this rounding before - // comparing them with the last coordinates from curvepoint_event. - // See bug #1593499 for details. - - gint x = (gint) Inkscape::round(event->motion.x); - gint y = (gint) Inkscape::round(event->motion.y); - - // The coordinates hasn't changed since the last motion event, abort - if (nc->curvepoint_event[NR::X] == x && - nc->curvepoint_event[NR::Y] == y) - break; - - NR::Point const delta_w(event->motion.x - nc->curvepoint_event[NR::X], - event->motion.y - nc->curvepoint_event[NR::Y]); - NR::Point const delta_dt(desktop->w2d(delta_w)); - - sp_nodepath_curve_drag (nc->grab_node, nc->grab_t, delta_dt); - nc->curvepoint_event[NR::X] = x; - nc->curvepoint_event[NR::Y] = y; gobble_motion_events(GDK_BUTTON1_MASK); break; } @@ -633,15 +386,15 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event) nc->drag = TRUE; ret = TRUE; } else { - if (!nc->nodepath || selection->singleItem() == NULL) { + if (!nc->shape_editor->has_nodepath() || selection->singleItem() == NULL) { break; } SPItem *item_over = sp_event_context_over_item (desktop, selection->singleItem(), NR::Point(event->motion.x, event->motion.y)); bool over_stroke = false; - if (item_over && nc->nodepath) { - over_stroke = sp_node_context_is_over_stroke (nc, item_over, NR::Point(event->motion.x, event->motion.y), false); + if (item_over && nc->shape_editor->has_nodepath()) { + over_stroke = nc->shape_editor->is_over_stroke(NR::Point(event->motion.x, event->motion.y), false); } if (nc->cursor_drag && !over_stroke) { @@ -665,18 +418,14 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event) NR::Maybe b = Inkscape::Rubberband::get()->getRectangle(); - if (nc->hit && !event_context->within_tolerance) { //drag curve - if (nc->nodepath) { - sp_nodepath_update_repr (nc->nodepath, _("Drag curve")); - } + if (nc->shape_editor->hits_curve() && !event_context->within_tolerance) { //drag curve + nc->shape_editor->finish_drag(); } else if (b != NR::Nothing() && !event_context->within_tolerance) { // drag to select - if (nc->nodepath) { - sp_nodepath_select_rect(nc->nodepath, b.assume(), event->button.state & GDK_SHIFT_MASK); - } + nc->shape_editor->select_rect(b.assume(), event->button.state & GDK_SHIFT_MASK); } else { if (!(nc->rb_escaped)) { // unless something was cancelled - if (nc->nodepath && nc->nodepath->selected) - sp_nodepath_deselect(nc->nodepath); + if (nc->shape_editor->has_selection()) + nc->shape_editor->deselect(); else sp_desktop_selection(desktop)->clear(); } @@ -686,7 +435,7 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event) desktop->updateNow(); nc->rb_escaped = false; nc->drag = FALSE; - nc->hit = false; + nc->shape_editor->cancel_hit(); nc->current_state = SP_NODE_CONTEXT_INACTIVE; break; } @@ -696,74 +445,72 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event) case GDK_Insert: case GDK_KP_Insert: // with any modifiers - sp_node_selected_add_node(); + nc->shape_editor->add_node(); ret = TRUE; break; case GDK_Delete: case GDK_KP_Delete: case GDK_BackSpace: if (MOD__CTRL_ONLY) { - sp_node_selected_delete(); + nc->shape_editor->delete_nodes(); } else { - if (nc->nodepath && nc->nodepath->selected) { - sp_node_delete_preserve(g_list_copy(nc->nodepath->selected)); - } + nc->shape_editor->delete_nodes_preserving_shape(); } ret = TRUE; break; case GDK_C: case GDK_c: if (MOD__SHIFT_ONLY) { - sp_node_selected_set_type(Inkscape::NodePath::NODE_CUSP); + nc->shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP); ret = TRUE; } break; case GDK_S: case GDK_s: if (MOD__SHIFT_ONLY) { - sp_node_selected_set_type(Inkscape::NodePath::NODE_SMOOTH); + nc->shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH); ret = TRUE; } break; case GDK_Y: case GDK_y: if (MOD__SHIFT_ONLY) { - sp_node_selected_set_type(Inkscape::NodePath::NODE_SYMM); + nc->shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM); ret = TRUE; } break; case GDK_B: case GDK_b: if (MOD__SHIFT_ONLY) { - sp_node_selected_break(); + nc->shape_editor->break_at_nodes(); ret = TRUE; } break; case GDK_J: case GDK_j: if (MOD__SHIFT_ONLY) { - sp_node_selected_join(); + nc->shape_editor->join_nodes(); ret = TRUE; } break; case GDK_D: case GDK_d: if (MOD__SHIFT_ONLY) { - sp_node_selected_duplicate(); + nc->shape_editor->duplicate_nodes(); ret = TRUE; } break; case GDK_L: case GDK_l: if (MOD__SHIFT_ONLY) { - sp_node_selected_set_line_type(NR_LINETO); + nc->shape_editor->set_type_of_segments(NR_LINETO); ret = TRUE; } break; case GDK_U: case GDK_u: if (MOD__SHIFT_ONLY) { - sp_node_selected_set_line_type(NR_CURVETO); + nc->shape_editor->set_type_of_segments(NR_CURVETO); ret = TRUE; } break; @@ -780,12 +527,12 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event) case GDK_KP_4: if (!MOD__CTRL) { // not ctrl if (MOD__ALT) { // alt - if (MOD__SHIFT) sp_node_selected_move_screen(-10, 0); // shift - else sp_node_selected_move_screen(-1, 0); // no shift + if (MOD__SHIFT) nc->shape_editor->move_nodes_screen(-10, 0); // shift + else nc->shape_editor->move_nodes_screen(-1, 0); // no shift } else { // no alt - if (MOD__SHIFT) sp_node_selected_move(-10*nudge, 0); // shift - else sp_node_selected_move(-nudge, 0); // no shift + if (MOD__SHIFT) nc->shape_editor->move_nodes(-10*nudge, 0); // shift + else nc->shape_editor->move_nodes(-nudge, 0); // no shift } ret = TRUE; } @@ -795,12 +542,12 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event) case GDK_KP_8: if (!MOD__CTRL) { // not ctrl if (MOD__ALT) { // alt - if (MOD__SHIFT) sp_node_selected_move_screen(0, 10); // shift - else sp_node_selected_move_screen(0, 1); // no shift + if (MOD__SHIFT) nc->shape_editor->move_nodes_screen(0, 10); // shift + else nc->shape_editor->move_nodes_screen(0, 1); // no shift } else { // no alt - if (MOD__SHIFT) sp_node_selected_move(0, 10*nudge); // shift - else sp_node_selected_move(0, nudge); // no shift + if (MOD__SHIFT) nc->shape_editor->move_nodes(0, 10*nudge); // shift + else nc->shape_editor->move_nodes(0, nudge); // no shift } ret = TRUE; } @@ -810,12 +557,12 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event) case GDK_KP_6: if (!MOD__CTRL) { // not ctrl if (MOD__ALT) { // alt - if (MOD__SHIFT) sp_node_selected_move_screen(10, 0); // shift - else sp_node_selected_move_screen(1, 0); // no shift + if (MOD__SHIFT) nc->shape_editor->move_nodes_screen(10, 0); // shift + else nc->shape_editor->move_nodes_screen(1, 0); // no shift } else { // no alt - if (MOD__SHIFT) sp_node_selected_move(10*nudge, 0); // shift - else sp_node_selected_move(nudge, 0); // no shift + if (MOD__SHIFT) nc->shape_editor->move_nodes(10*nudge, 0); // shift + else nc->shape_editor->move_nodes(nudge, 0); // no shift } ret = TRUE; } @@ -825,12 +572,12 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event) case GDK_KP_2: if (!MOD__CTRL) { // not ctrl if (MOD__ALT) { // alt - if (MOD__SHIFT) sp_node_selected_move_screen(0, -10); // shift - else sp_node_selected_move_screen(0, -1); // no shift + if (MOD__SHIFT) nc->shape_editor->move_nodes_screen(0, -10); // shift + else nc->shape_editor->move_nodes_screen(0, -1); // no shift } else { // no alt - if (MOD__SHIFT) sp_node_selected_move(0, -10*nudge); // shift - else sp_node_selected_move(0, -nudge); // no shift + if (MOD__SHIFT) nc->shape_editor->move_nodes(0, -10*nudge); // shift + else nc->shape_editor->move_nodes(0, -nudge); // no shift } ret = TRUE; } @@ -843,8 +590,8 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event) nc->current_state = SP_NODE_CONTEXT_INACTIVE; nc->rb_escaped = true; } else { - if (nc->nodepath && nc->nodepath->selected) { - sp_nodepath_deselect(nc->nodepath); + if (nc->shape_editor->has_selection()) { + nc->shape_editor->deselect(); } else { sp_desktop_selection(desktop)->clear(); } @@ -856,40 +603,40 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event) case GDK_bracketleft: if ( MOD__CTRL && !MOD__ALT && ( snaps != 0 ) ) { if (nc->leftctrl) - sp_nodepath_selected_nodes_rotate (nc->nodepath, M_PI/snaps, -1, false); + nc->shape_editor->rotate_nodes (M_PI/snaps, -1, false); if (nc->rightctrl) - sp_nodepath_selected_nodes_rotate (nc->nodepath, M_PI/snaps, 1, false); + nc->shape_editor->rotate_nodes (M_PI/snaps, 1, false); } else if ( MOD__ALT && !MOD__CTRL ) { if (nc->leftalt && nc->rightalt) - sp_nodepath_selected_nodes_rotate (nc->nodepath, 1, 0, true); + nc->shape_editor->rotate_nodes (1, 0, true); else { if (nc->leftalt) - sp_nodepath_selected_nodes_rotate (nc->nodepath, 1, -1, true); + nc->shape_editor->rotate_nodes (1, -1, true); if (nc->rightalt) - sp_nodepath_selected_nodes_rotate (nc->nodepath, 1, 1, true); + nc->shape_editor->rotate_nodes (1, 1, true); } } else if ( snaps != 0 ) { - sp_nodepath_selected_nodes_rotate (nc->nodepath, M_PI/snaps, 0, false); + nc->shape_editor->rotate_nodes (M_PI/snaps, 0, false); } ret = TRUE; break; case GDK_bracketright: if ( MOD__CTRL && !MOD__ALT && ( snaps != 0 ) ) { if (nc->leftctrl) - sp_nodepath_selected_nodes_rotate (nc->nodepath, -M_PI/snaps, -1, false); + nc->shape_editor->rotate_nodes (-M_PI/snaps, -1, false); if (nc->rightctrl) - sp_nodepath_selected_nodes_rotate (nc->nodepath, -M_PI/snaps, 1, false); + nc->shape_editor->rotate_nodes (-M_PI/snaps, 1, false); } else if ( MOD__ALT && !MOD__CTRL ) { if (nc->leftalt && nc->rightalt) - sp_nodepath_selected_nodes_rotate (nc->nodepath, -1, 0, true); + nc->shape_editor->rotate_nodes (-1, 0, true); else { if (nc->leftalt) - sp_nodepath_selected_nodes_rotate (nc->nodepath, -1, -1, true); + nc->shape_editor->rotate_nodes (-1, -1, true); if (nc->rightalt) - sp_nodepath_selected_nodes_rotate (nc->nodepath, -1, 1, true); + nc->shape_editor->rotate_nodes (-1, 1, true); } } else if ( snaps != 0 ) { - sp_nodepath_selected_nodes_rotate (nc->nodepath, -M_PI/snaps, 0, false); + nc->shape_editor->rotate_nodes (-M_PI/snaps, 0, false); } ret = TRUE; break; @@ -897,20 +644,20 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event) case GDK_comma: if (MOD__CTRL) { if (nc->leftctrl) - sp_nodepath_selected_nodes_scale(nc->nodepath, -offset, -1); + nc->shape_editor->scale_nodes(-offset, -1); if (nc->rightctrl) - sp_nodepath_selected_nodes_scale(nc->nodepath, -offset, 1); + nc->shape_editor->scale_nodes(-offset, 1); } else if (MOD__ALT) { if (nc->leftalt && nc->rightalt) - sp_nodepath_selected_nodes_scale_screen(nc->nodepath, -1, 0); + nc->shape_editor->scale_nodes_screen (-1, 0); else { if (nc->leftalt) - sp_nodepath_selected_nodes_scale_screen(nc->nodepath, -1, -1); + nc->shape_editor->scale_nodes_screen (-1, -1); if (nc->rightalt) - sp_nodepath_selected_nodes_scale_screen(nc->nodepath, -1, 1); + nc->shape_editor->scale_nodes_screen (-1, 1); } } else { - sp_nodepath_selected_nodes_scale(nc->nodepath, -offset, 0); + nc->shape_editor->scale_nodes (-offset, 0); } ret = TRUE; break; @@ -918,20 +665,20 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event) case GDK_period: if (MOD__CTRL) { if (nc->leftctrl) - sp_nodepath_selected_nodes_scale(nc->nodepath, offset, -1); + nc->shape_editor->scale_nodes (offset, -1); if (nc->rightctrl) - sp_nodepath_selected_nodes_scale(nc->nodepath, offset, 1); + nc->shape_editor->scale_nodes (offset, 1); } else if (MOD__ALT) { if (nc->leftalt && nc->rightalt) - sp_nodepath_selected_nodes_scale_screen(nc->nodepath, 1, 0); + nc->shape_editor->scale_nodes_screen (1, 0); else { if (nc->leftalt) - sp_nodepath_selected_nodes_scale_screen(nc->nodepath, 1, -1); + nc->shape_editor->scale_nodes_screen (1, -1); if (nc->rightalt) - sp_nodepath_selected_nodes_scale_screen(nc->nodepath, 1, 1); + nc->shape_editor->scale_nodes_screen (1, 1); } } else { - sp_nodepath_selected_nodes_scale(nc->nodepath, offset, 0); + nc->shape_editor->scale_nodes (offset, 0); } ret = TRUE; break; diff --git a/src/node-context.h b/src/node-context.h index fae95cf43..cf53635d1 100644 --- a/src/node-context.h +++ b/src/node-context.h @@ -36,7 +36,7 @@ struct SPNodeContext { guint drag : 1; - Inkscape::NodePath::Path *nodepath; + ShapeEditor* shape_editor; gboolean leftalt; gboolean rightalt; @@ -50,16 +50,11 @@ struct SPNodeContext { Inkscape::MessageContext *_node_message_context; - double grab_t; - int grab_node; // number of node grabbed by sp_node_context_is_over_stroke - bool hit; - NR::Point curvepoint_event; // int coords from event - NR::Point curvepoint_doc; // same, in doc coords bool cursor_drag; - bool added_node; + bool added_node; - unsigned int current_state; + unsigned int current_state; }; struct SPNodeContextClass { diff --git a/src/nodepath.cpp b/src/nodepath.cpp index 69827bd84..78d76404d 100644 --- a/src/nodepath.cpp +++ b/src/nodepath.cpp @@ -31,6 +31,7 @@ #include "message-stack.h" #include "message-context.h" #include "node-context.h" +#include "shape-editor.h" #include "selection-chemistry.h" #include "selection.h" #include "xml/repr.h" @@ -184,7 +185,7 @@ Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPItem *item, bool np->path = path; np->subpaths = NULL; np->selected = NULL; - np->nodeContext = NULL; //Let the context that makes this set it + np->shape_editor = NULL; //Let the shapeeditor that makes this set it np->livarot_path = NULL; np->local_change = 0; np->show_handles = show_handles; @@ -217,7 +218,7 @@ Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPItem *item, bool } /** - * Destroys nodepath's subpaths, then itself, also tell context about it. + * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it. */ void sp_nodepath_destroy(Inkscape::NodePath::Path *np) { @@ -228,9 +229,9 @@ void sp_nodepath_destroy(Inkscape::NodePath::Path *np) { sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data); } - //Inform the context that made me, if any, that I am gone. - if (np->nodeContext) - np->nodeContext->nodepath = NULL; + //Inform the ShapeEditor that made me, if any, that I am gone. + if (np->shape_editor) + np->shape_editor->nodepath_destroyed(); g_assert(!np->selected); @@ -662,7 +663,7 @@ static gchar *create_typestr(Inkscape::NodePath::Path *np) } /** - * Returns current path in context. + * Returns current path in context. // later eliminate this function at all! */ static Inkscape::NodePath::Path *sp_nodepath_current() { @@ -676,7 +677,7 @@ static Inkscape::NodePath::Path *sp_nodepath_current() return NULL; } - return SP_NODE_CONTEXT(event_context)->nodepath; + return SP_NODE_CONTEXT(event_context)->shape_editor->get_nodepath(); } @@ -4233,7 +4234,7 @@ static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node) * Handles content of statusbar as long as node tool is active. */ void -sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath) +sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection { gchar const *when_selected = _("Drag nodes or node handles; Alt+drag nodes to sculpt; arrow keys to move nodes, < > to scale, [ ] to rotate"); gchar const *when_selected_one = _("Drag the node or its handles; arrow keys to move the node"); diff --git a/src/nodepath.h b/src/nodepath.h index b675863d5..46d768b63 100644 --- a/src/nodepath.h +++ b/src/nodepath.h @@ -73,8 +73,7 @@ operator NR::Point() const }; -/** defined in node-context.h */ -class SPNodeContext; +class ShapeEditor; namespace Inkscape { namespace NodePath { @@ -96,7 +95,6 @@ class NodeSide; */ class Node; - /** * This is a collection of subpaths which contain nodes * @@ -115,7 +113,7 @@ class Path { /** The parent path of this nodepath */ SPPath * path; /** The context which created this nodepath. Important if this nodepath is deleted */ - SPNodeContext * nodeContext; + ShapeEditor *shape_editor; /** The subpaths which comprise this NodePath */ GList * subpaths; /** A list of nodes which are currently selected */ diff --git a/src/shape-editor.cpp b/src/shape-editor.cpp new file mode 100644 index 000000000..19b9ea64d --- /dev/null +++ b/src/shape-editor.cpp @@ -0,0 +1,435 @@ +#define __SHAPE_EDITOR_CPP__ + +/* + * Inkscape::ShapeEditor + * + * Authors: + * bulia byak + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "sp-object.h" +#include "sp-item.h" +#include "selection.h" +#include "desktop.h" +#include "desktop-handles.h" +#include "knotholder.h" +#include "node-context.h" +#include "xml/node-event-vector.h" +#include "prefs-utils.h" +#include "object-edit.h" +#include "splivarot.h" +#include "style.h" + +#include "shape-editor.h" + + +ShapeEditorsCollective::ShapeEditorsCollective(SPDesktop *dt) { +} + +ShapeEditorsCollective::~ShapeEditorsCollective() { +} + + +void ShapeEditorsCollective::update_statusbar() { + +//!!! move from nodepath: sp_nodepath_update_statusbar but summing for all nodepaths + +} + +ShapeEditor::ShapeEditor(SPDesktop *dt) { + this->desktop = dt; + this->grab_node = -1; + this->nodepath = NULL; + this->knotholder = NULL; + this->hit = false; +} + +ShapeEditor::~ShapeEditor() { + unset_item(); +} + +void ShapeEditor::unset_item() { + + Inkscape::XML::Node *old_repr = NULL; + + if (this->nodepath) { + old_repr = this->nodepath->repr; + } + + if (!old_repr && this->knotholder) { + old_repr = this->knotholder->repr; + } + + if (old_repr) { // remove old listener + sp_repr_remove_listener_by_data(old_repr, this); + Inkscape::GC::release(old_repr); + } + + if (this->nodepath) { + this->grab_node = -1; + sp_nodepath_destroy(this->nodepath); + this->nodepath = NULL; + } + + if (this->knotholder) { + sp_knot_holder_destroy(this->knotholder); + this->knotholder = NULL; + } +} + +bool ShapeEditor::has_nodepath () { + return (this->nodepath != NULL); +} + +bool ShapeEditor::has_knotholder () { + return (this->knotholder != NULL); +} + +bool ShapeEditor::has_local_change () { + if (this->nodepath) + return (this->nodepath->local_change != 0); + else if (this->knotholder) + return (this->knotholder->local_change != 0); + else + return false; +} + +void ShapeEditor::decrement_local_change () { + if (this->nodepath) { + if (this->nodepath->local_change > 0) + this->nodepath->local_change--; + } else if (this->knotholder) { + this->knotholder->local_change = FALSE; + } +} + +SPItem *ShapeEditor::get_item () { + SPItem *item = NULL; + if (this->has_nodepath()) { + item = SP_ITEM(this->nodepath->path); + } else if (this->has_knotholder()) { + item = SP_ITEM(this->knotholder->item); + } + return item; +} + +GList *ShapeEditor::save_nodepath_selection () { + if (this->nodepath) + return ::save_nodepath_selection (this->nodepath); + return NULL; +} + +void ShapeEditor::restore_nodepath_selection (GList *saved) { + if (this->nodepath && saved) + ::restore_nodepath_selection (this->nodepath, saved); +} + + +static void shapeeditor_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name, + gchar const *old_value, gchar const *new_value, + bool is_interactive, gpointer data) +{ + SPItem *item = NULL; + gboolean changed = FALSE; + + g_assert(data); + ShapeEditor *sh = ((ShapeEditor *) data); + + item = sh->get_item(); + + if ( + ((sh->has_nodepath()) && (!strcmp(name, "d") || !strcmp(name, "sodipodi:nodetypes"))) // With paths, we only need to act if one of the path-affecting attributes has changed. + || sh->has_knotholder()) { + changed = !sh->has_local_change(); + sh->decrement_local_change(); + } + + if (changed) { + GList *saved = NULL; + if (sh->has_nodepath()) { + saved = sh->save_nodepath_selection(); + } + + sh->set_item (item); + + if (sh->has_nodepath() && saved) { + sh->restore_nodepath_selection(saved); + g_list_free (saved); + } + } + + sh->update_statusbar(); //TODO: sh->get_container()->update_statusbar(); +} + +static Inkscape::XML::NodeEventVector shapeeditor_repr_events = { + NULL, /* child_added */ + NULL, /* child_removed */ + shapeeditor_event_attr_changed, + NULL, /* content_changed */ + NULL /* order_changed */ +}; + + +void ShapeEditor::set_item(SPItem *item) { + + unset_item(); + + this->grab_node = -1; + + if (item) { + this->nodepath = sp_nodepath_new(desktop, item, (prefs_get_int_attribute("tools.nodes", "show_handles", 1) != 0)); + if (this->nodepath) { + this->nodepath->shape_editor = this; + } + this->knotholder = sp_item_knot_holder(item, desktop); + + if (this->nodepath || this->knotholder) { + // setting new listener + Inkscape::XML::Node *repr; + if (this->knotholder) + repr = this->knotholder->repr; + else + repr = SP_OBJECT_REPR(item); + if (repr) { + Inkscape::GC::anchor(repr); + sp_repr_add_listener(repr, &shapeeditor_repr_events, this); + } + } + } +} + +void ShapeEditor::nodepath_destroyed () { + this->nodepath = NULL; +} + +void ShapeEditor::update_statusbar () { + if (this->nodepath) + sp_nodepath_update_statusbar(this->nodepath); +} + +bool ShapeEditor::is_over_stroke (NR::Point event_p, bool remember) { + + if (!this->nodepath) + return false; // no stroke in knotholder + + SPItem *item = get_item(); + + //Translate click point into proper coord system + this->curvepoint_doc = desktop->w2d(event_p); + this->curvepoint_doc *= sp_item_dt2i_affine(item); + this->curvepoint_doc *= sp_item_i2doc_affine(item); + + sp_nodepath_ensure_livarot_path(this->nodepath); + + NR::Maybe position = get_nearest_position_on_Path(this->nodepath->livarot_path, this->curvepoint_doc); + NR::Point nearest = get_point_on_Path(this->nodepath->livarot_path, position.assume().piece, position.assume().t); + NR::Point delta = nearest - this->curvepoint_doc; + + delta = desktop->d2w(delta); + + double stroke_tolerance = + (SP_OBJECT_STYLE (item)->stroke.type != SP_PAINT_TYPE_NONE? + desktop->current_zoom() * + SP_OBJECT_STYLE (item)->stroke_width.computed * + sp_item_i2d_affine (item).expansion() * 0.5 + : 0.0) + + prefs_get_int_attribute_limited("options.dragtolerance", "value", 0, 0, 100); //(double) SP_EVENT_CONTEXT(nc)->tolerance; + + bool close = (NR::L2 (delta) < stroke_tolerance); + + if (remember && close) { + this->curvepoint_event[NR::X] = (gint) event_p [NR::X]; + this->curvepoint_event[NR::Y] = (gint) event_p [NR::Y]; + this->hit = true; + this->grab_t = position.assume().t; + this->grab_node = position.assume().piece; + } + + return close; +} + +void ShapeEditor::add_node_near_point() { + if (this->nodepath) { + sp_nodepath_add_node_near_point(this->nodepath, this->curvepoint_doc); + } else if (this->knotholder) { + // we do not add nodes in knotholder... yet + } +} + +void ShapeEditor::select_segment_near_point(bool toggle) { + if (this->nodepath) { + sp_nodepath_select_segment_near_point(this->nodepath, this->curvepoint_doc, toggle); + } else if (this->knotholder) { + // we do not select segments in knotholder... yet? + } +} + +void ShapeEditor::cancel_hit() { + this->hit = false; +} + +bool ShapeEditor::hits_curve() { + return (this->hit); +} + + +void ShapeEditor::curve_drag(gdouble eventx, gdouble eventy) { + if (this->nodepath) { + + if (this->grab_node == -1) // don't know which segment to drag + return; + + // We round off the extra precision in the motion coordinates provided + // by some input devices (like tablets). As we'll store the coordinates + // as integers in curvepoint_event we need to do this rounding before + // comparing them with the last coordinates from curvepoint_event. + // See bug #1593499 for details. + + gint x = (gint) Inkscape::round(eventx); + gint y = (gint) Inkscape::round(eventy); + + + // The coordinates hasn't changed since the last motion event, abort + if (this->curvepoint_event[NR::X] == x && + this->curvepoint_event[NR::Y] == y) + return; + + NR::Point const delta_w(eventx - this->curvepoint_event[NR::X], + eventy - this->curvepoint_event[NR::Y]); + NR::Point const delta_dt(this->desktop->w2d(delta_w)); + + sp_nodepath_curve_drag (this->grab_node, this->grab_t, delta_dt); //!!! FIXME: which nodepath?!!! also uses current!!! + this->curvepoint_event[NR::X] = x; + this->curvepoint_event[NR::Y] = y; + + } else if (this->knotholder) { + // we do not drag curve in knotholder + } + +} + +void ShapeEditor::finish_drag() { + if (this->nodepath && this->hit) { + sp_nodepath_update_repr (this->nodepath, _("Drag curve")); + } +} + +void ShapeEditor::select_rect(NR::Rect const &rect, bool add) { + if (this->nodepath) { + sp_nodepath_select_rect(this->nodepath, rect, add); + } +} + +bool ShapeEditor::has_selection() { + if (this->nodepath) + return this->nodepath->selected; + return false; // so far, knotholder cannot have selection +} + +void ShapeEditor::deselect() { + if (this->nodepath) + sp_nodepath_deselect(this->nodepath); +} + +void ShapeEditor::add_node () { + sp_node_selected_add_node(); // FIXME fix that function by removing nodepath_current lookup, and pass it this->nodepath instead (needs fixing verbs/buttons first) +} + +void ShapeEditor::delete_nodes () { + sp_node_selected_delete(); // FIXME fix that function by removing nodepath_current lookup, and pass it this->nodepath instead (needs fixing verbs/buttons first) +} + +void ShapeEditor::delete_nodes_preserving_shape () { + if (this->nodepath && this->nodepath->selected) { + sp_node_delete_preserve(g_list_copy(this->nodepath->selected)); + } +} + +void ShapeEditor::set_node_type(Inkscape::NodePath::NodeType type) { + sp_node_selected_set_type(type); // FIXME fix that function by removing nodepath_current lookup, and pass it this->nodepath instead (needs fixing verbs/buttons first) +} + +void ShapeEditor::break_at_nodes() { + sp_node_selected_break(); // FIXME fix that function by removing nodepath_current lookup, and pass it this->nodepath instead (needs fixing verbs/buttons first) +} + +void ShapeEditor::join_nodes() { + sp_node_selected_join(); // FIXME fix that function by removing nodepath_current lookup, and pass it this->nodepath instead (needs fixing verbs/buttons first) +} + +void ShapeEditor::duplicate_nodes() { + sp_node_selected_duplicate(); // FIXME fix that function by removing nodepath_current lookup, and pass it this->nodepath instead (needs fixing verbs/buttons first) +} + +void ShapeEditor::set_type_of_segments(NRPathcode code) { + sp_node_selected_set_line_type(code); // FIXME fix that function by removing nodepath_current lookup, and pass it this->nodepath instead (needs fixing verbs/buttons first) +} + +void ShapeEditor::move_nodes_screen(gdouble dx, gdouble dy) { + sp_node_selected_move_screen(dx, dy); // FIXME fix that function by removing nodepath_current lookup, and pass it this->nodepath instead (needs fixing verbs/buttons first) +} +void ShapeEditor::move_nodes(gdouble dx, gdouble dy) { + sp_node_selected_move(dx, dy); // FIXME fix that function by removing nodepath_current lookup, and pass it this->nodepath instead (needs fixing verbs/buttons first) +} + +void ShapeEditor::rotate_nodes(gdouble angle, int which, bool screen) { + if (this->nodepath) + sp_nodepath_selected_nodes_rotate (this->nodepath, angle, which, screen); +} + +void ShapeEditor::scale_nodes(gdouble const grow, int const which) { + sp_nodepath_selected_nodes_scale(this->nodepath, grow, which); +} +void ShapeEditor::scale_nodes_screen(gdouble const grow, int const which) { + sp_nodepath_selected_nodes_scale_screen(this->nodepath, grow, which); +} + +void ShapeEditor::select_all (bool invert) { + if (this->nodepath) + sp_nodepath_select_all (this->nodepath, invert); +} +void ShapeEditor::select_all_from_subpath (bool invert) { + if (this->nodepath) + sp_nodepath_select_all_from_subpath (this->nodepath, invert); +} +void ShapeEditor::select_next () { + if (this->nodepath) + sp_nodepath_select_next (this->nodepath); +} +void ShapeEditor::select_prev () { + if (this->nodepath) + sp_nodepath_select_prev (this->nodepath); +} + +void ShapeEditor::flip (NR::Dim2 axis) { + if (this->nodepath) + sp_nodepath_flip (this->nodepath, axis); +} + +void ShapeEditor::distribute (NR::Dim2 axis) { + if (this->nodepath) + sp_nodepath_selected_distribute (this->nodepath, axis); +} +void ShapeEditor::align (NR::Dim2 axis) { + if (this->nodepath) + sp_nodepath_selected_align (this->nodepath, axis); +} + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/shape-editor.h b/src/shape-editor.h new file mode 100644 index 000000000..bd93668f7 --- /dev/null +++ b/src/shape-editor.h @@ -0,0 +1,160 @@ +#ifndef __SHAPE_EDITOR_H__ +#define __SHAPE_EDITOR_H__ + +/* + * Inkscape::ShapeEditor + * + * This is a container class which contains either knotholder (for shapes) or nodepath (for + * paths). It is attached to a single item so only one of these is active at a time. + * + * Authors: + * bulia byak + * + */ + +#include + +namespace Inkscape { +namespace NodePath { +class Path; +typedef enum NodeType; +} +} + +#include "libnr/nr-path-code.h" +#include "libnr/nr-point.h" + +class SPKnotHolder; +class SPDesktop; +class SPNodeContext; +class ShapeEditorsCollective; + +class ShapeEditor { +public: + + ShapeEditor(SPDesktop *desktop); + ~ShapeEditor(); + + void set_item (SPItem *item); + void unset_item (); + + SPItem *get_item (); + + bool has_nodepath (); + bool has_knotholder (); + + bool has_local_change (); + void decrement_local_change (); + + GList *save_nodepath_selection (); + void restore_nodepath_selection (GList *saved); + + void nodepath_destroyed (); + + void update_statusbar (); + + bool is_over_stroke (NR::Point event_p, bool remember); + + void add_node_near_point(); // uses the shapeeditor's remembered point, if any + + void select_segment_near_point(bool toggle); // uses the shapeeditor's remembered point, if any + + void cancel_hit (); + + bool hits_curve (); + + void curve_drag (gdouble eventx, gdouble eventy); + + void finish_drag (); + + void select_rect (NR::Rect const &rect, bool add); + + bool has_selection (); + void deselect (); + + Inkscape::NodePath::Path *get_nodepath() {return nodepath;} //((deprecated)) + ShapeEditorsCollective *get_container() {return container;} + + void add_node(); + + void delete_nodes(); + void delete_nodes_preserving_shape(); + + void set_node_type(Inkscape::NodePath::NodeType type); + + void break_at_nodes(); + void join_nodes(); + + void duplicate_nodes(); + + void set_type_of_segments(NRPathcode code); + + void move_nodes(gdouble dx, gdouble dy); + void move_nodes_screen(gdouble dx, gdouble dy); + + void rotate_nodes(gdouble angle, int which, bool screen); + + void scale_nodes(gdouble const grow, int const which); + void scale_nodes_screen(gdouble const grow, int const which); + + void select_all (bool invert); + void select_all_from_subpath (bool invert); + void select_next (); + void select_prev (); + + void flip (NR::Dim2 axis); + + void distribute (NR::Dim2 axis); + void align (NR::Dim2 axis); + +private: + SPDesktop *desktop; + + Inkscape::NodePath::Path *nodepath; + SPKnotHolder *knotholder; + + ShapeEditorsCollective *container; + + //Inkscape::XML::Node *lidtened_repr; + + double grab_t; + int grab_node; // number of node grabbed by sp_node_context_is_over_stroke + bool hit; + NR::Point curvepoint_event; // int coords from event + NR::Point curvepoint_doc; // same, in doc coords +}; + + +/* As the next stage, this will be a collection of multiple ShapeEditors, +with the same interface as the single ShapeEditor, passing the actions to all its +contained ShapeEditors. Thus it should be easy to switch node context from +using a single ShapeEditor to using a ShapeEditorsCollective. */ + +class ShapeEditorsCollective { +public: + + ShapeEditorsCollective(SPDesktop *desktop); + ~ShapeEditorsCollective(); + + void update_statusbar(); + +private: + std::vector editors; + + SPNodeContext *nc; // who holds us +}; + +#endif + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : + diff --git a/src/ui/dialog/align-and-distribute.cpp b/src/ui/dialog/align-and-distribute.cpp index 81bce14c3..b8f840520 100644 --- a/src/ui/dialog/align-and-distribute.cpp +++ b/src/ui/dialog/align-and-distribute.cpp @@ -46,7 +46,8 @@ #include "sp-flowtext.h" #include "text-editing.h" -#include "node-context.h" //For node align/distribute function +#include "node-context.h" //For access to ShapeEditor +#include "shape-editor.h" //For node align/distribute methods #include "tools-switch.h" @@ -413,12 +414,10 @@ private : SPEventContext *event_context = sp_desktop_event_context(SP_ACTIVE_DESKTOP); if (!SP_IS_NODE_CONTEXT (event_context)) return ; - Inkscape::NodePath::Path *nodepath = SP_NODE_CONTEXT (event_context)->nodepath; - if (!nodepath) return; if (_distribute) - sp_nodepath_selected_distribute(nodepath, _orientation); + SP_NODE_CONTEXT (event_context)->shape_editor->distribute(_orientation); else - sp_nodepath_selected_align(nodepath, _orientation); + SP_NODE_CONTEXT (event_context)->shape_editor->align(_orientation); } }; diff --git a/src/verbs.cpp b/src/verbs.cpp index 123ccf96c..a68e09356 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -77,6 +77,7 @@ #include "layer-fns.h" #include "node-context.h" #include "gradient-context.h" +#include "shape-editor.h" /** @@ -915,28 +916,28 @@ EditVerb::perform(SPAction *action, void *data, void *pdata) break; case SP_VERB_EDIT_SELECT_ALL: if (tools_isactive(dt, TOOLS_NODES)) { - sp_nodepath_select_all_from_subpath(SP_NODE_CONTEXT(ec)->nodepath, false); + SP_NODE_CONTEXT(ec)->shape_editor->select_all_from_subpath(false); } else { sp_edit_select_all(); } break; case SP_VERB_EDIT_INVERT: if (tools_isactive(dt, TOOLS_NODES)) { - sp_nodepath_select_all_from_subpath(SP_NODE_CONTEXT(ec)->nodepath, true); + SP_NODE_CONTEXT(ec)->shape_editor->select_all_from_subpath(true); } else { sp_edit_invert(); } break; case SP_VERB_EDIT_SELECT_ALL_IN_ALL_LAYERS: if (tools_isactive(dt, TOOLS_NODES)) { - sp_nodepath_select_all(SP_NODE_CONTEXT(ec)->nodepath, false); + SP_NODE_CONTEXT(ec)->shape_editor->select_all(false); } else { sp_edit_select_all_in_all_layers(); } break; case SP_VERB_EDIT_INVERT_IN_ALL_LAYERS: if (tools_isactive(dt, TOOLS_NODES)) { - sp_nodepath_select_all(SP_NODE_CONTEXT(ec)->nodepath, true); + SP_NODE_CONTEXT(ec)->shape_editor->select_all(true); } else { sp_edit_invert_in_all_layers(); } @@ -944,7 +945,7 @@ EditVerb::perform(SPAction *action, void *data, void *pdata) case SP_VERB_EDIT_SELECT_NEXT: if (tools_isactive(dt, TOOLS_NODES)) { - sp_nodepath_select_next(SP_NODE_CONTEXT(ec)->nodepath); + SP_NODE_CONTEXT(ec)->shape_editor->select_next(); } else if (tools_isactive(dt, TOOLS_GRADIENT)) { sp_gradient_context_select_next (ec); } else { @@ -953,7 +954,7 @@ EditVerb::perform(SPAction *action, void *data, void *pdata) break; case SP_VERB_EDIT_SELECT_PREV: if (tools_isactive(dt, TOOLS_NODES)) { - sp_nodepath_select_prev(SP_NODE_CONTEXT(ec)->nodepath); + SP_NODE_CONTEXT(ec)->shape_editor->select_prev(); } else if (tools_isactive(dt, TOOLS_GRADIENT)) { sp_gradient_context_select_prev (ec); } else { @@ -963,7 +964,7 @@ EditVerb::perform(SPAction *action, void *data, void *pdata) case SP_VERB_EDIT_DESELECT: if (tools_isactive(dt, TOOLS_NODES)) { - sp_nodepath_deselect(SP_NODE_CONTEXT(ec)->nodepath); + SP_NODE_CONTEXT(ec)->shape_editor->deselect(); } else { sp_desktop_selection(dt)->clear(); } @@ -1282,7 +1283,7 @@ ObjectVerb::perform( SPAction *action, void *data, void *pdata ) break; case SP_VERB_OBJECT_FLIP_HORIZONTAL: if (tools_isactive(dt, TOOLS_NODES)) { - sp_nodepath_flip(SP_NODE_CONTEXT(ec)->nodepath, NR::X); + SP_NODE_CONTEXT(ec)->shape_editor->flip(NR::X); } else { sp_selection_scale_relative(sel, center, NR::scale(-1.0, 1.0)); } @@ -1291,7 +1292,7 @@ ObjectVerb::perform( SPAction *action, void *data, void *pdata ) break; case SP_VERB_OBJECT_FLIP_VERTICAL: if (tools_isactive(dt, TOOLS_NODES)) { - sp_nodepath_flip(SP_NODE_CONTEXT(ec)->nodepath, NR::Y); + SP_NODE_CONTEXT(ec)->shape_editor->flip(NR::Y); } else { sp_selection_scale_relative(sel, center, NR::scale(1.0, -1.0)); }