From 272c0a04dbce8684149a3fdfaf6cf13eda76e990 Mon Sep 17 00:00:00 2001 From: cilix42 Date: Wed, 5 Dec 2007 04:09:29 +0000 Subject: [PATCH] Add entry fields for x/y coordinates of selected nodes (fixes bugs #170116 and #172113) --- src/node-context.cpp | 7 ++ src/nodepath.cpp | 67 ++++++++++-- src/nodepath.h | 3 + src/widgets/toolbox.cpp | 233 +++++++++++++++++++++++++++++++--------- 4 files changed, 254 insertions(+), 56 deletions(-) diff --git a/src/node-context.cpp b/src/node-context.cpp index 3e7903227..1cac6f164 100644 --- a/src/node-context.cpp +++ b/src/node-context.cpp @@ -482,6 +482,13 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event) ret = TRUE; } break; + case GDK_x: + case GDK_X: + if (MOD__ALT_ONLY) { + desktop->setToolboxFocusTo ("altx-nodes"); + ret = TRUE; + } + break; case GDK_Left: // move selection left case GDK_KP_Left: case GDK_KP_4: diff --git a/src/nodepath.cpp b/src/nodepath.cpp index 1239f961b..4c0727d15 100644 --- a/src/nodepath.cpp +++ b/src/nodepath.cpp @@ -1372,6 +1372,50 @@ sp_node_selected_move_screen(Inkscape::NodePath::Path *nodepath, gdouble dx, gdo } } +/** + * Move selected nodes to the absolute position given + */ +void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, NR::Coord val, NR::Dim2 axis) +{ + for (GList *l = nodepath->selected; l != NULL; l = l->next) { + Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data; + NR::Point npos(axis == NR::X ? val : n->pos[NR::X], axis == NR::Y ? val : n->pos[NR::Y]); + sp_node_moveto(n, npos); + } + + sp_nodepath_update_repr(nodepath, _("Move nodes")); +} + +/** + * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return NR::Nothing + */ +NR::Maybe sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis) +{ + g_return_val_if_fail(nodepath->selected, NR::Nothing()); + + // determine coordinate of first selected node + GList *nsel = nodepath->selected; + Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data; + NR::Coord coord = n->pos[axis]; + bool coincide = true; + + // compare it to the coordinates of all the other selected nodes + for (GList *l = nsel->next; l != NULL; l = l->next) { + n = (Inkscape::NodePath::Node *) l->data; + if (n->pos[axis] != coord) { + coincide = false; + } + } + if (coincide) { + return coord; + } else { + NR::Rect bbox = sp_node_selected_bbox(nodepath); + // currently we return the coordinate of the bounding box midpoint because I don't know how + // to erase the spin button entry field :), but maybe this can be useful behaviour anyway + return bbox.midpoint()[axis]; + } +} + /** If they don't yet exist, creates knot and line for the given side of the node */ static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side) { @@ -3933,13 +3977,7 @@ void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, NR::Ma } else { // scale nodes as an "object": - Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data; - NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node - for (GList *l = nodepath->selected; l != NULL; l = l->next) { - Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data; - box.expandTo (n->pos); // contain all selected nodes - } - + NR::Rect box = sp_node_selected_bbox (nodepath); if (!center) { center = box.midpoint(); } @@ -3960,6 +3998,19 @@ void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, NR::Ma sp_nodepath_update_repr(nodepath, _("Flip nodes")); } +NR::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath) +{ + g_assert (nodepath->selected); + + Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data; + NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node + for (GList *l = nodepath->selected; l != NULL; l = l->next) { + Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data; + box.expandTo (n->pos); // contain all selected nodes + } + return box; +} + //----------------------------------------------- /** * Return new subpath under given nodepath. @@ -4392,6 +4443,8 @@ sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to Sha Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context; if (!mc) return; + inkscape_active_desktop()->emitToolSubselectionChanged(NULL); + if (selected_nodes == 0) { Inkscape::Selection *sel = desktop->selection; if (!sel || sel->isEmpty()) { diff --git a/src/nodepath.h b/src/nodepath.h index ee828f4ac..8f17ecb14 100644 --- a/src/nodepath.h +++ b/src/nodepath.h @@ -302,6 +302,9 @@ void sp_node_selected_set_type (Inkscape::NodePath::Path *nodepath, Inkscape::No void sp_node_selected_set_line_type (Inkscape::NodePath::Path *nodepath, NRPathcode code); void sp_node_selected_move (Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy); void sp_node_selected_move_screen (Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy); +void sp_node_selected_move_absolute (Inkscape::NodePath::Path *nodepath, NR::Coord val, NR::Dim2 axis); +NR::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath); +NR::Maybe sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis); void sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show); diff --git a/src/widgets/toolbox.cpp b/src/widgets/toolbox.cpp index 3609a48dd..a4d24203f 100644 --- a/src/widgets/toolbox.cpp +++ b/src/widgets/toolbox.cpp @@ -242,6 +242,9 @@ static gchar const * ui_descr = " " " " " " + " " + " " + " " " " " " @@ -686,6 +689,53 @@ sp_commands_toolbox_new() return hb; } +static EgeAdjustmentAction * create_adjustment_action( gchar const *name, + gchar const *label, gchar const *shortLabel, gchar const *tooltip, + gchar const *path, gchar const *data, gdouble def, + GtkWidget *focusTarget, + GtkWidget *us, + GObject *dataKludge, + gboolean altx, gchar const *altx_mark, + gdouble lower, gdouble upper, gdouble step, gdouble page, + gchar const** descrLabels, gdouble const* descrValues, guint descrCount, + void (*callback)(GtkAdjustment *, GObject *), + gdouble climb = 0.1, guint digits = 3, double factor = 1.0 ) +{ + GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor, + lower, upper, step, page, page ) ); + if (us) { + sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj ); + } + + gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge ); + + EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits ); + if ( shortLabel ) { + g_object_set( act, "short_label", shortLabel, NULL ); + } + + if ( (descrCount > 0) && descrLabels && descrValues ) { + ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount ); + } + + if ( focusTarget ) { + ege_adjustment_action_set_focuswidget( act, focusTarget ); + } + + if ( altx && altx_mark ) { + g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL ); + } + + if ( dataKludge ) { + g_object_set_data( dataKludge, data, adj ); + } + + // Using a cast just to make sure we pass in the right kind of function pointer + g_object_set( G_OBJECT(act), "tool-post", static_cast(sp_set_font_size_smaller), NULL ); + + return act; +} + //#################################### //# node editing callbacks @@ -794,11 +844,104 @@ static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) { if (shape_editor) shape_editor->show_handles(show); } +/* is called when the node selection is modified */ +static void +sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl) +{ + GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) ); + GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) ); + GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact)); + GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact)); + + // quit if run by the attr_changed listener + if (g_object_get_data( tbl, "freeze" )) { + return; + } + + // in turn, prevent listener from responding + g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE)); + + ShapeEditor *shape_editor = get_current_shape_editor(); + if (shape_editor && shape_editor->has_nodepath()) { + Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath(); + int n_selected = 0; + if (nodepath) { + n_selected = nodepath->numSelected(); + } + + if (n_selected == 0) { + gtk_action_set_sensitive(xact, FALSE); + gtk_action_set_sensitive(yact, FALSE); + } else { + gtk_action_set_sensitive(xact, TRUE); + gtk_action_set_sensitive(yact, TRUE); + NR::Coord oldx = gtk_adjustment_get_value(xadj); + NR::Coord oldy = gtk_adjustment_get_value(xadj); + + if (n_selected == 1) { + NR::Point sel_node = nodepath->singleSelectedCoords(); + if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) { + gtk_adjustment_set_value(xadj, sel_node[NR::X]); + gtk_adjustment_set_value(yadj, sel_node[NR::Y]); + } + } else { + NR::Maybe x = sp_node_selected_common_coord(nodepath, NR::X); + NR::Maybe y = sp_node_selected_common_coord(nodepath, NR::Y); + if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) { + /* Note: Currently x and y will always have a value, even if the coordinates of the + selected nodes don't coincide (in this case we use the coordinates of the center + of the bounding box). So the entries are never set to zero. */ + gtk_adjustment_set_value(xadj, x ? (*x) : 0.0); // FIXME: Maybe we should clear the entry + gtk_adjustment_set_value(yadj, y ? (*y) : 0.0); // fields, not set them to zero. + } + } + } + } + + g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); +} + +static void +sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name) +{ + // quit if run by the attr_changed listener + if (g_object_get_data( tbl, "freeze" )) { + return; + } + + // in turn, prevent listener from responding + g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE)); + + ShapeEditor *shape_editor = get_current_shape_editor(); + if (shape_editor && shape_editor->has_nodepath()) { + if (!strcmp(value_name, "x")) { + sp_node_selected_move_absolute(shape_editor->get_nodepath(), gtk_adjustment_get_value(adj), NR::X); + } + if (!strcmp(value_name, "y")) { + sp_node_selected_move_absolute(shape_editor->get_nodepath(), gtk_adjustment_get_value(adj), NR::Y); + } + } + + g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); +} + +static void +sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl) +{ + sp_node_path_value_changed(adj, tbl, "x"); +} + +static void +sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl) +{ + sp_node_path_value_changed(adj, tbl, "y"); +} + //################################ //## Node Editing Toolbox ## //################################ -static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* /*holder*/) +static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder) { { InkAction* inky = ink_action_new( "NodeInsertAction", @@ -924,6 +1067,46 @@ static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) ); } + /* X coord of selected node(s) */ + { + EgeAdjustmentAction* eact = 0; + gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500}; + eact = create_adjustment_action( "NodeXAction", + _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"), + "tools.nodes", "Xcoord", 0, + GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes", + -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP, + labels, values, G_N_ELEMENTS(labels), + sp_node_path_x_value_changed ); + g_object_set_data( holder, "nodes_x_action", eact ); + gtk_action_set_sensitive( GTK_ACTION(eact), FALSE ); + gtk_action_group_add_action( mainActions, GTK_ACTION(eact) ); + } + + /* Y coord of selected node(s) */ + { + EgeAdjustmentAction* eact = 0; + gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500}; + eact = create_adjustment_action( "NodeYAction", + _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"), + "tools.nodes", "Ycoord", 0, + GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL, + -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP, + labels, values, G_N_ELEMENTS(labels), + sp_node_path_y_value_changed ); + g_object_set_data( holder, "nodes_y_action", eact ); + gtk_action_set_sensitive( GTK_ACTION(eact), FALSE ); + gtk_action_group_add_action( mainActions, GTK_ACTION(eact) ); + } + + sigc::connection *connection = new sigc::connection ( + desktop->connectToolSubselectionChanged(sigc::bind (sigc::ptr_fun(sp_node_toolbox_coord_changed), (GObject *)holder)) + ); + + g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection ); + g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder ); } // end of sp_node_toolbox_prep() @@ -1277,54 +1460,6 @@ sp_tb_spinbutton( return hb; } -static EgeAdjustmentAction * create_adjustment_action( gchar const *name, - gchar const *label, gchar const *shortLabel, gchar const *tooltip, - gchar const *path, gchar const *data, gdouble def, - GtkWidget *focusTarget, - GtkWidget *us, - GObject *dataKludge, - gboolean altx, gchar const *altx_mark, - gdouble lower, gdouble upper, gdouble step, gdouble page, - gchar const** descrLabels, gdouble const* descrValues, guint descrCount, - void (*callback)(GtkAdjustment *, GObject *), - gdouble climb = 0.1, guint digits = 3, double factor = 1.0 ) -{ - GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor, - lower, upper, step, page, page ) ); - if (us) { - sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj ); - } - - gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge ); - - EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits ); - if ( shortLabel ) { - g_object_set( act, "short_label", shortLabel, NULL ); - } - - if ( (descrCount > 0) && descrLabels && descrValues ) { - ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount ); - } - - if ( focusTarget ) { - ege_adjustment_action_set_focuswidget( act, focusTarget ); - } - - if ( altx && altx_mark ) { - g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL ); - } - - if ( dataKludge ) { - g_object_set_data( dataKludge, data, adj ); - } - - // Using a cast just to make sure we pass in the right kind of function pointer - g_object_set( G_OBJECT(act), "tool-post", static_cast(sp_set_font_size_smaller), NULL ); - - return act; -} - - #define MODE_LABEL_WIDTH 70 //######################## -- 2.30.2