From 31bb8269c26a781036448ed8f8cd93cc84fb2118 Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Sun, 29 Nov 2009 16:33:18 +0100 Subject: First GSoC node tool commit to Bazaar (bzr r8846.1.1) --- src/ui/tool/node-tool.cpp | 563 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 563 insertions(+) create mode 100644 src/ui/tool/node-tool.cpp (limited to 'src/ui/tool/node-tool.cpp') diff --git a/src/ui/tool/node-tool.cpp b/src/ui/tool/node-tool.cpp new file mode 100644 index 000000000..a57057c92 --- /dev/null +++ b/src/ui/tool/node-tool.cpp @@ -0,0 +1,563 @@ +/** @file + * @brief New node tool - implementation + */ +/* Authors: + * Krzysztof KosiƄski + * + * Copyright (C) 2009 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include +#include +#include "desktop.h" +#include "desktop-handles.h" +#include "display/canvas-bpath.h" +#include "display/curve.h" +#include "display/sp-canvas.h" +#include "document.h" +#include "message-context.h" +#include "selection.h" +#include "shape-editor.h" // temporary! +#include "sp-clippath.h" +#include "sp-item-group.h" +#include "sp-mask.h" +#include "sp-object-group.h" +#include "sp-path.h" +#include "ui/tool/node-tool.h" +#include "ui/tool/control-point-selection.h" +#include "ui/tool/curve-drag-point.h" +#include "ui/tool/event-utils.h" +#include "ui/tool/manipulator.h" +#include "ui/tool/multi-path-manipulator.h" +#include "ui/tool/path-manipulator.h" +#include "ui/tool/selector.h" + +#include "pixmaps/cursor-node.xpm" +#include "pixmaps/cursor-node-d.xpm" + +namespace { +SPCanvasGroup *create_control_group(SPDesktop *d); +void ink_node_tool_class_init(InkNodeToolClass *klass); +void ink_node_tool_init(InkNodeTool *node_context); +void ink_node_tool_dispose(GObject *object); + +void ink_node_tool_setup(SPEventContext *ec); +gint ink_node_tool_root_handler(SPEventContext *event_context, GdkEvent *event); +gint ink_node_tool_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event); +void ink_node_tool_set(SPEventContext *ec, Inkscape::Preferences::Entry *value); + +void ink_node_tool_update_tip(InkNodeTool *nt, GdkEvent *event); +void ink_node_tool_selection_changed(InkNodeTool *nt, Inkscape::Selection *sel); +void ink_node_tool_select_area(InkNodeTool *nt, Geom::Rect const &, GdkEventButton *); +void ink_node_tool_select_point(InkNodeTool *nt, Geom::Point const &, GdkEventButton *); +void ink_node_tool_mouseover_changed(InkNodeTool *nt, Inkscape::UI::ControlPoint *p); +} // anonymous namespace + +GType ink_node_tool_get_type() +{ + static GType type = 0; + if (!type) { + GTypeInfo info = { + sizeof(InkNodeToolClass), + NULL, NULL, + (GClassInitFunc) ink_node_tool_class_init, + NULL, NULL, + sizeof(InkNodeTool), + 4, + (GInstanceInitFunc) ink_node_tool_init, + NULL, /* value_table */ + }; + type = g_type_register_static(SP_TYPE_EVENT_CONTEXT, "InkNodeTool", &info, (GTypeFlags)0); + } + return type; +} + +namespace { + +SPCanvasGroup *create_control_group(SPDesktop *d) +{ + return reinterpret_cast(sp_canvas_item_new( + sp_desktop_controls(d), SP_TYPE_CANVAS_GROUP, NULL)); +} + +void destroy_group(SPCanvasGroup *g) +{ + gtk_object_destroy(GTK_OBJECT(g)); +} + +void ink_node_tool_class_init(InkNodeToolClass *klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + SPEventContextClass *event_context_class = (SPEventContextClass *) klass; + + object_class->dispose = ink_node_tool_dispose; + + event_context_class->setup = ink_node_tool_setup; + event_context_class->set = ink_node_tool_set; + event_context_class->root_handler = ink_node_tool_root_handler; + event_context_class->item_handler = ink_node_tool_item_handler; +} + +void ink_node_tool_init(InkNodeTool *nt) +{ + SPEventContext *event_context = SP_EVENT_CONTEXT(nt); + + event_context->cursor_shape = cursor_node_xpm; + event_context->hot_x = 1; + event_context->hot_y = 1; + + new (&nt->_selection_changed_connection) sigc::connection(); + new (&nt->_mouseover_changed_connection) sigc::connection(); + //new (&nt->_mgroup) Inkscape::UI::ManipulatorGroup(nt->desktop); + new (&nt->_selected_nodes) CSelPtr(); + new (&nt->_multipath) MultiPathPtr(); + new (&nt->_selector) SelectorPtr(); + new (&nt->_path_data) PathSharedDataPtr(); +} + +void ink_node_tool_dispose(GObject *object) +{ + InkNodeTool *nt = INK_NODE_TOOL(object); + + nt->enableGrDrag(false); + + nt->_selection_changed_connection.disconnect(); + nt->_mouseover_changed_connection.disconnect(); + nt->_multipath.~MultiPathPtr(); + nt->_selected_nodes.~CSelPtr(); + nt->_selector.~SelectorPtr(); + + Inkscape::UI::PathSharedData &data = *nt->_path_data; + destroy_group(data.node_data.node_group); + destroy_group(data.node_data.handle_group); + destroy_group(data.node_data.handle_line_group); + destroy_group(data.outline_group); + destroy_group(data.dragpoint_group); + destroy_group(nt->_transform_handle_group); + + nt->_path_data.~PathSharedDataPtr(); + nt->_selection_changed_connection.~connection(); + nt->_mouseover_changed_connection.~connection(); + + if (nt->_node_message_context) { + delete nt->_node_message_context; + } + if (nt->shape_editor) { + nt->shape_editor->unset_item(SH_KNOTHOLDER); + delete nt->shape_editor; + } + + G_OBJECT_CLASS(g_type_class_peek(g_type_parent(INK_TYPE_NODE_TOOL)))->dispose(object); +} + +void ink_node_tool_setup(SPEventContext *ec) +{ + InkNodeTool *nt = INK_NODE_TOOL(ec); + + SPEventContextClass *parent = (SPEventContextClass *) g_type_class_peek(g_type_parent(INK_TYPE_NODE_TOOL)); + if (parent->setup) parent->setup(ec); + + nt->_node_message_context = new Inkscape::MessageContext((ec->desktop)->messageStack()); + + nt->_path_data.reset(new Inkscape::UI::PathSharedData()); + Inkscape::UI::PathSharedData &data = *nt->_path_data; + data.node_data.desktop = nt->desktop; + + // selector has to be created here, so that its hidden control point is on the bottom + nt->_selector.reset(new Inkscape::UI::Selector(nt->desktop)); + + // Prepare canvas groups for controls. This guarantees correct z-order, so that + // for example a dragpoint won't obscure a node + data.outline_group = create_control_group(nt->desktop); + data.node_data.handle_line_group = create_control_group(nt->desktop); + data.dragpoint_group = create_control_group(nt->desktop); + nt->_transform_handle_group = create_control_group(nt->desktop); + data.node_data.node_group = create_control_group(nt->desktop); + data.node_data.handle_group = create_control_group(nt->desktop); + + Inkscape::Selection *selection = sp_desktop_selection (ec->desktop); + nt->_selection_changed_connection.disconnect(); + nt->_selection_changed_connection = + selection->connectChanged( + sigc::bind<0>( + sigc::ptr_fun(&ink_node_tool_selection_changed), + nt)); + nt->_mouseover_changed_connection.disconnect(); + nt->_mouseover_changed_connection = + Inkscape::UI::ControlPoint::signal_mouseover_change.connect( + sigc::bind<0>( + sigc::ptr_fun(&ink_node_tool_mouseover_changed), + nt)); + + nt->_selected_nodes.reset( + new Inkscape::UI::ControlPointSelection(nt->desktop, nt->_transform_handle_group)); + data.node_data.selection = nt->_selected_nodes.get(); + nt->_multipath.reset(new Inkscape::UI::MultiPathManipulator(data, + nt->_selection_changed_connection)); + + nt->_selector->signal_point.connect( + sigc::bind<0>( + sigc::ptr_fun(&ink_node_tool_select_point), + nt)); + nt->_selector->signal_area.connect( + sigc::bind<0>( + sigc::ptr_fun(&ink_node_tool_select_area), + nt)); + + nt->_multipath->signal_coords_changed.connect( + sigc::bind( + sigc::mem_fun(*nt->desktop, &SPDesktop::emitToolSubselectionChanged), + (void*) 0)); + nt->_selected_nodes->signal_point_changed.connect( + sigc::hide( sigc::hide( + sigc::bind( + sigc::bind( + sigc::ptr_fun(ink_node_tool_update_tip), + (GdkEvent*)0), + nt)))); + + nt->cursor_drag = false; + nt->show_transform_handles = true; + nt->single_node_transform_handles = false; + nt->flash_tempitem = NULL; + nt->flashed_item = NULL; + // TODO remove this! + nt->shape_editor = new ShapeEditor(nt->desktop); + + // read prefs before adding items to selection to prevent momentarily showing the outline + sp_event_context_read(nt, "show_handles"); + sp_event_context_read(nt, "show_outline"); + sp_event_context_read(nt, "show_path_direction"); + sp_event_context_read(nt, "show_transform_handles"); + sp_event_context_read(nt, "single_node_transform_handles"); + sp_event_context_read(nt, "edit_clipping_paths"); + sp_event_context_read(nt, "edit_masks"); + + ink_node_tool_selection_changed(nt, selection); + ink_node_tool_update_tip(nt, NULL); + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + if (prefs->getBool("/tools/nodes/selcue")) { + ec->enableSelectionCue(); + } + if (prefs->getBool("/tools/nodes/gradientdrag")) { + ec->enableGrDrag(); + } + + nt->desktop->emitToolSubselectionChanged(NULL); // sets the coord entry fields to inactive +} + +void ink_node_tool_set(SPEventContext *ec, Inkscape::Preferences::Entry *value) +{ + InkNodeTool *nt = INK_NODE_TOOL(ec); + Glib::ustring entry_name = value->getEntryName(); + + if (entry_name == "show_handles") { + nt->_multipath->showHandles(value->getBool(true)); + } else if (entry_name == "show_outline") { + nt->show_outline = value->getBool(); + nt->_multipath->showOutline(nt->show_outline); + } else if (entry_name == "show_path_direction") { + nt->show_path_direction = value->getBool(); + nt->_multipath->showPathDirection(nt->show_path_direction); + } else if (entry_name == "show_transform_handles") { + nt->show_transform_handles = value->getBool(true); + nt->_selected_nodes->showTransformHandles( + nt->show_transform_handles, nt->single_node_transform_handles); + } else if (entry_name == "single_node_transform_handles") { + nt->single_node_transform_handles = value->getBool(); + nt->_selected_nodes->showTransformHandles( + nt->show_transform_handles, nt->single_node_transform_handles); + } else if (entry_name == "edit_clipping_paths") { + nt->edit_clipping_paths = value->getBool(); + ink_node_tool_selection_changed(nt, nt->desktop->selection); + } else if (entry_name == "edit_masks") { + nt->edit_masks = value->getBool(); + ink_node_tool_selection_changed(nt, nt->desktop->selection); + } else { + SPEventContextClass *parent_class = + (SPEventContextClass *) g_type_class_peek(g_type_parent(INK_TYPE_NODE_TOOL)); + if (parent_class->set) + parent_class->set(ec, value); + } +} + +void store_clip_mask_items(SPItem *clipped, SPObject *obj, std::map > &s, Geom::Matrix const &postm, guint32 color) +{ + if (!obj) return; + if (SP_IS_GROUP(obj) || SP_IS_OBJECTGROUP(obj)) { + //TODO is checking for obj->children != NULL above better? + for (SPObject *c = obj->children; c; c = c->next) { + store_clip_mask_items(clipped, c, s, postm, color); + } + } else if (SP_IS_ITEM(obj)) { + s.insert(std::make_pair(SP_ITEM(obj), + std::make_pair(sp_item_i2d_affine(clipped) * postm, color))); + } +} + +struct IsPath { + bool operator()(SPItem *i) const { return SP_IS_PATH(i); } +}; + +void ink_node_tool_selection_changed(InkNodeTool *nt, Inkscape::Selection *sel) +{ + using namespace Inkscape::UI; + // TODO this is ugly!!! + typedef std::map > TransMap; + typedef std::map > PathMap; + GSList const *ilist = sel->itemList(); + TransMap items; + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + + for (GSList *i = const_cast(ilist); i; i = i->next) { + SPObject *obj = static_cast(i->data); + if (SP_IS_ITEM(obj)) { + items.insert(std::make_pair(SP_ITEM(obj), + std::make_pair(Geom::identity(), + prefs->getColor("/tools/nodes/outline_color", 0xff0000ff)))); + if (nt->edit_clipping_paths && SP_ITEM(i->data)->clip_ref) { + store_clip_mask_items(SP_ITEM(i->data), + SP_OBJECT(SP_ITEM(i->data)->clip_ref->getObject()), items, + nt->desktop->dt2doc(), + prefs->getColor("/tools/nodes/clipping_path_color", 0x00ff00ff)); + } + if (nt->edit_masks && SP_ITEM(i->data)->mask_ref) { + store_clip_mask_items(SP_ITEM(i->data), + SP_OBJECT(SP_ITEM(i->data)->mask_ref->getObject()), items, + nt->desktop->dt2doc(), + prefs->getColor("/tools/nodes/mask_color", 0x0000ffff)); + } + } + } + + // ugly hack: set the first editable non-path item for knotholder + // maybe use multiple ShapeEditors for now, to allow editing many shapes at once? + bool something_set = false; + for (TransMap::iterator i = items.begin(); i != items.end(); ++i) { + SPItem *obj = i->first; + if (SP_IS_SHAPE(obj) && !SP_IS_PATH(obj)) { + nt->shape_editor->set_item(obj, SH_KNOTHOLDER); + something_set = true; + break; + } + } + if (!something_set) { + nt->shape_editor->unset_item(SH_KNOTHOLDER); + } + + PathMap p; + for (TransMap::iterator i = items.begin(); i != items.end(); ++i) { + if (SP_IS_PATH(i->first)) { + p.insert(std::make_pair(SP_PATH(i->first), + std::make_pair(i->second.first, i->second.second))); + } + } + + nt->_multipath->setItems(p); + ink_node_tool_update_tip(nt, NULL); + nt->desktop->updateNow(); +} + +gint ink_node_tool_root_handler(SPEventContext *event_context, GdkEvent *event) +{ + /* things to handle here: + * 1. selection of items + * 2. passing events to manipulators + * 3. some keybindings + */ + using namespace Inkscape::UI; // pull in event helpers + + SPDesktop *desktop = event_context->desktop; + Inkscape::Selection *selection = desktop->selection; + InkNodeTool *nt = static_cast(event_context); + static Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + + if (nt->_multipath->event(event)) return true; + if (nt->_selector->event(event)) return true; + if (nt->_selected_nodes->event(event)) return true; + + switch (event->type) + { + case GDK_MOTION_NOTIFY: + // create outline + if (prefs->getBool("/tools/nodes/pathflash_enabled")) { + if (prefs->getBool("/tools/nodes/pathflash_unselected") && !nt->_multipath->empty()) + break; + + SPItem *over_item = sp_event_context_find_item (desktop, event_point(event->button), + FALSE, TRUE); + if (over_item == nt->flashed_item) break; + if (nt->flash_tempitem) { + desktop->remove_temporary_canvasitem(nt->flash_tempitem); + nt->flash_tempitem = NULL; + nt->flashed_item = NULL; + } + if (!SP_IS_PATH(over_item)) break; // for now, handle only paths + + nt->flashed_item = over_item; + SPCurve *c = sp_path_get_curve_for_edit(SP_PATH(over_item)); + c->transform(sp_item_i2d_affine(over_item)); + SPCanvasItem *flash = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), c); + sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(flash), + prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff), 1.0, + SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); + sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(flash), 0, SP_WIND_RULE_NONZERO); + nt->flash_tempitem = desktop->add_temporary_canvasitem(flash, + prefs->getInt("/tools/nodes/pathflash_timeout", 500)); + c->unref(); + } + return true; + case GDK_KEY_PRESS: + switch (get_group0_keyval(&event->key)) + { + case GDK_Escape: // deselect everything + if (nt->_selected_nodes->empty()) { + selection->clear(); + } else { + nt->_selected_nodes->clear(); + } + ink_node_tool_update_tip(nt, event); + return TRUE; + case GDK_a: + if (held_control(event->key)) { + if (held_alt(event->key)) { + nt->_multipath->selectAll(); + } else { + // select all nodes in subpaths that have something selected + // if nothing is selected, select everything + nt->_multipath->selectSubpaths(); + } + ink_node_tool_update_tip(nt, event); + return TRUE; + } + break; + default: + break; + } + ink_node_tool_update_tip(nt, event); + break; + case GDK_KEY_RELEASE: + ink_node_tool_update_tip(nt, event); + break; + default: break; + } + + SPEventContextClass *parent_class = (SPEventContextClass *) g_type_class_peek(g_type_parent(INK_TYPE_NODE_TOOL)); + if (parent_class->root_handler) + return parent_class->root_handler(event_context, event); + return FALSE; +} + +void ink_node_tool_update_tip(InkNodeTool *nt, GdkEvent *event) +{ + using namespace Inkscape::UI; + if (event && (event->type == GDK_KEY_PRESS || event->type == GDK_KEY_RELEASE)) { + unsigned new_state = state_after_event(event); + if (new_state == event->key.state) return; + if (state_held_shift(new_state)) { + nt->_node_message_context->set(Inkscape::NORMAL_MESSAGE, + C_("Node tool tip", "Shift: drag to add nodes to the selection, " + "click to toggle object selection")); + return; + } + } + unsigned sz = nt->_selected_nodes->size(); + if (sz != 0) { + char *dyntip = g_strdup_printf(C_("Node tool tip", + "Selected %d nodes. Drag to select nodes, click to select a single object " + "or unselect all objects"), sz); + nt->_node_message_context->set(Inkscape::NORMAL_MESSAGE, dyntip); + g_free(dyntip); + } else if (nt->_multipath->empty()) { + nt->_node_message_context->set(Inkscape::NORMAL_MESSAGE, + C_("Node tool tip", "Drag or click to select objects to edit")); + } else { + nt->_node_message_context->set(Inkscape::NORMAL_MESSAGE, + C_("Node tool tip", "Drag to select nodes, click to select an object " + "or clear the selection")); + } +} + +gint ink_node_tool_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event) +{ + SPEventContextClass *parent_class = + (SPEventContextClass *) g_type_class_peek(g_type_parent(INK_TYPE_NODE_TOOL)); + if (parent_class->item_handler) + return parent_class->item_handler(event_context, item, event); + return FALSE; +} + +void ink_node_tool_select_area(InkNodeTool *nt, Geom::Rect const &sel, GdkEventButton *event) +{ + using namespace Inkscape::UI; + if (nt->_multipath->empty()) { + // if multipath is empty, select rubberbanded items rather than nodes + Inkscape::Selection *selection = nt->desktop->selection; + GSList *items = sp_document_items_in_box( + sp_desktop_document(nt->desktop), nt->desktop->dkey, sel); + selection->setList(items); + g_slist_free(items); + } else { + nt->_multipath->selectArea(sel, !held_shift(*event)); + } +} +void ink_node_tool_select_point(InkNodeTool *nt, Geom::Point const &sel, GdkEventButton *event) +{ + using namespace Inkscape::UI; // pull in event helpers + if (!event) return; + if (event->button != 1) return; + + Inkscape::Selection *selection = nt->desktop->selection; + + SPItem *item_clicked = sp_event_context_find_item (nt->desktop, event_point(*event), + (event->state & GDK_MOD1_MASK) && !(event->state & GDK_CONTROL_MASK), TRUE); + + if (item_clicked == NULL) { // nothing under cursor + // if no Shift, deselect + if (!(event->state & GDK_SHIFT_MASK)) { + selection->clear(); + } + return; + } + if (held_shift(*event)) { + selection->toggle(item_clicked); + } else { + selection->set(item_clicked); + } + nt->desktop->updateNow(); +} + +void ink_node_tool_mouseover_changed(InkNodeTool *nt, Inkscape::UI::ControlPoint *p) +{ + using Inkscape::UI::CurveDragPoint; + CurveDragPoint *cdp = dynamic_cast(p); + if (cdp && !nt->cursor_drag) { + nt->cursor_shape = cursor_node_d_xpm; + nt->hot_x = 1; + nt->hot_y = 1; + sp_event_context_update_cursor(nt); + nt->cursor_drag = true; + } else if (!cdp && nt->cursor_drag) { + nt->cursor_shape = cursor_node_xpm; + nt->hot_x = 1; + nt->hot_y = 1; + sp_event_context_update_cursor(nt); + nt->cursor_drag = false; + } +} + +} // anonymous namespace + +/* + 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:encoding=utf-8:textwidth=99 : -- cgit v1.2.3 From a79eab7e518e7c1b3540075552ecb3e7aa62b0df Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Sat, 5 Dec 2009 03:48:07 +0100 Subject: Fix mask editing behavior on undo and outline display for masks/clips; prepare to fix LPE path parameters (bzr r8846.2.2) --- src/ui/tool/node-tool.cpp | 83 +++++++++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 32 deletions(-) (limited to 'src/ui/tool/node-tool.cpp') diff --git a/src/ui/tool/node-tool.cpp b/src/ui/tool/node-tool.cpp index a57057c92..31c722744 100644 --- a/src/ui/tool/node-tool.cpp +++ b/src/ui/tool/node-tool.cpp @@ -32,6 +32,7 @@ #include "ui/tool/multi-path-manipulator.h" #include "ui/tool/path-manipulator.h" #include "ui/tool/selector.h" +#include "ui/tool/shape-record.h" #include "pixmaps/cursor-node.xpm" #include "pixmaps/cursor-node-d.xpm" @@ -108,6 +109,7 @@ void ink_node_tool_init(InkNodeTool *nt) event_context->hot_y = 1; new (&nt->_selection_changed_connection) sigc::connection(); + new (&nt->_selection_modified_connection) sigc::connection(); new (&nt->_mouseover_changed_connection) sigc::connection(); //new (&nt->_mgroup) Inkscape::UI::ManipulatorGroup(nt->desktop); new (&nt->_selected_nodes) CSelPtr(); @@ -123,6 +125,7 @@ void ink_node_tool_dispose(GObject *object) nt->enableGrDrag(false); nt->_selection_changed_connection.disconnect(); + nt->_selection_modified_connection.disconnect(); nt->_mouseover_changed_connection.disconnect(); nt->_multipath.~MultiPathPtr(); nt->_selected_nodes.~CSelPtr(); @@ -138,6 +141,7 @@ void ink_node_tool_dispose(GObject *object) nt->_path_data.~PathSharedDataPtr(); nt->_selection_changed_connection.~connection(); + nt->_selection_modified_connection.~connection(); nt->_mouseover_changed_connection.~connection(); if (nt->_node_message_context) { @@ -183,6 +187,12 @@ void ink_node_tool_setup(SPEventContext *ec) sigc::bind<0>( sigc::ptr_fun(&ink_node_tool_selection_changed), nt)); + nt->_selection_modified_connection.disconnect(); + nt->_selection_modified_connection = + selection->connectModified( + sigc::hide(sigc::bind<0>( + sigc::ptr_fun(&ink_node_tool_selection_changed), + nt))); nt->_mouseover_changed_connection.disconnect(); nt->_mouseover_changed_connection = Inkscape::UI::ControlPoint::signal_mouseover_change.connect( @@ -298,6 +308,36 @@ void store_clip_mask_items(SPItem *clipped, SPObject *obj, std::map &s) +{ + using namespace Inkscape::UI; + if (!obj) return; + if (role != SHAPE_ROLE_NORMAL && (SP_IS_GROUP(obj) || SP_IS_OBJECTGROUP(obj))) { + for (SPObject *c = obj->children; c; c = c->next) { + gather_items(nt, base, c, role, s); + } + } else if (SP_IS_ITEM(obj)) { + SPItem *item = static_cast(obj); + ShapeRecord r; + r.item = item; + // TODO add support for objectBoundingBox + r.edit_transform = base ? sp_item_i2doc_affine(base) : Geom::identity(); + r.role = role; + r.edit_original = false; + if (s.insert(r).second) { + // this item was encountered the first time + if (nt->edit_clipping_paths && item->clip_ref) { + gather_items(nt, item, item->clip_ref->getObject(), SHAPE_ROLE_CLIPPING_PATH, s); + } + if (nt->edit_masks && item->mask_ref) { + gather_items(nt, item, item->mask_ref->getObject(), SHAPE_ROLE_MASK, s); + } + } + } +} + struct IsPath { bool operator()(SPItem *i) const { return SP_IS_PATH(i); } }; @@ -305,41 +345,28 @@ struct IsPath { void ink_node_tool_selection_changed(InkNodeTool *nt, Inkscape::Selection *sel) { using namespace Inkscape::UI; + + std::set shapes; + // TODO this is ugly!!! - typedef std::map > TransMap; - typedef std::map > PathMap; + //typedef std::map > TransMap; + //typedef std::map > PathMap; GSList const *ilist = sel->itemList(); - TransMap items; - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); for (GSList *i = const_cast(ilist); i; i = i->next) { SPObject *obj = static_cast(i->data); if (SP_IS_ITEM(obj)) { - items.insert(std::make_pair(SP_ITEM(obj), - std::make_pair(Geom::identity(), - prefs->getColor("/tools/nodes/outline_color", 0xff0000ff)))); - if (nt->edit_clipping_paths && SP_ITEM(i->data)->clip_ref) { - store_clip_mask_items(SP_ITEM(i->data), - SP_OBJECT(SP_ITEM(i->data)->clip_ref->getObject()), items, - nt->desktop->dt2doc(), - prefs->getColor("/tools/nodes/clipping_path_color", 0x00ff00ff)); - } - if (nt->edit_masks && SP_ITEM(i->data)->mask_ref) { - store_clip_mask_items(SP_ITEM(i->data), - SP_OBJECT(SP_ITEM(i->data)->mask_ref->getObject()), items, - nt->desktop->dt2doc(), - prefs->getColor("/tools/nodes/mask_color", 0x0000ffff)); - } + gather_items(nt, NULL, static_cast(obj), SHAPE_ROLE_NORMAL, shapes); } } // ugly hack: set the first editable non-path item for knotholder // maybe use multiple ShapeEditors for now, to allow editing many shapes at once? bool something_set = false; - for (TransMap::iterator i = items.begin(); i != items.end(); ++i) { - SPItem *obj = i->first; - if (SP_IS_SHAPE(obj) && !SP_IS_PATH(obj)) { - nt->shape_editor->set_item(obj, SH_KNOTHOLDER); + for (std::set::iterator i = shapes.begin(); i != shapes.end(); ++i) { + ShapeRecord const &r = *i; + if (SP_IS_SHAPE(r.item) && !SP_IS_PATH(r.item)) { + nt->shape_editor->set_item(r.item, SH_KNOTHOLDER); something_set = true; break; } @@ -347,16 +374,8 @@ void ink_node_tool_selection_changed(InkNodeTool *nt, Inkscape::Selection *sel) if (!something_set) { nt->shape_editor->unset_item(SH_KNOTHOLDER); } - - PathMap p; - for (TransMap::iterator i = items.begin(); i != items.end(); ++i) { - if (SP_IS_PATH(i->first)) { - p.insert(std::make_pair(SP_PATH(i->first), - std::make_pair(i->second.first, i->second.second))); - } - } - nt->_multipath->setItems(p); + nt->_multipath->setItems(shapes); ink_node_tool_update_tip(nt, NULL); nt->desktop->updateNow(); } -- cgit v1.2.3 From e2b9f78d271e5fea988138d49020e704e72c83b1 Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Tue, 8 Dec 2009 03:21:08 +0100 Subject: Fix LPEs and break mask transform undo (bzr r8846.2.3) --- src/ui/tool/node-tool.cpp | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) (limited to 'src/ui/tool/node-tool.cpp') diff --git a/src/ui/tool/node-tool.cpp b/src/ui/tool/node-tool.cpp index 31c722744..9103f066f 100644 --- a/src/ui/tool/node-tool.cpp +++ b/src/ui/tool/node-tool.cpp @@ -16,6 +16,7 @@ #include "display/curve.h" #include "display/sp-canvas.h" #include "document.h" +#include "live_effects/lpeobject.h" #include "message-context.h" #include "selection.h" #include "shape-editor.h" // temporary! @@ -187,12 +188,12 @@ void ink_node_tool_setup(SPEventContext *ec) sigc::bind<0>( sigc::ptr_fun(&ink_node_tool_selection_changed), nt)); - nt->_selection_modified_connection.disconnect(); + /*nt->_selection_modified_connection.disconnect(); nt->_selection_modified_connection = selection->connectModified( sigc::hide(sigc::bind<0>( - sigc::ptr_fun(&ink_node_tool_selection_changed), - nt))); + sigc::ptr_fun(&ink_node_tool_selection_modified), + nt)));*/ nt->_mouseover_changed_connection.disconnect(); nt->_mouseover_changed_connection = Inkscape::UI::ControlPoint::signal_mouseover_change.connect( @@ -293,28 +294,20 @@ void ink_node_tool_set(SPEventContext *ec, Inkscape::Preferences::Entry *value) } } -void store_clip_mask_items(SPItem *clipped, SPObject *obj, std::map > &s, Geom::Matrix const &postm, guint32 color) -{ - if (!obj) return; - if (SP_IS_GROUP(obj) || SP_IS_OBJECTGROUP(obj)) { - //TODO is checking for obj->children != NULL above better? - for (SPObject *c = obj->children; c; c = c->next) { - store_clip_mask_items(clipped, c, s, postm, color); - } - } else if (SP_IS_ITEM(obj)) { - s.insert(std::make_pair(SP_ITEM(obj), - std::make_pair(sp_item_i2d_affine(clipped) * postm, color))); - } -} - /** Recursively collect ShapeRecords */ void gather_items(InkNodeTool *nt, SPItem *base, SPObject *obj, Inkscape::UI::ShapeRole role, std::set &s) { using namespace Inkscape::UI; if (!obj) return; - if (role != SHAPE_ROLE_NORMAL && (SP_IS_GROUP(obj) || SP_IS_OBJECTGROUP(obj))) { + + if (SP_IS_PATH(obj) && obj->repr->attribute("inkscape:original-d") != NULL) { + ShapeRecord r; + r.item = static_cast(obj); + r.edit_transform = Geom::identity(); // TODO wrong? + r.role = SHAPE_ROLE_LPE_PARAM; + s.insert(r); + } else if (role != SHAPE_ROLE_NORMAL && (SP_IS_GROUP(obj) || SP_IS_OBJECTGROUP(obj))) { for (SPObject *c = obj->children; c; c = c->next) { gather_items(nt, base, c, role, s); } @@ -325,7 +318,6 @@ void gather_items(InkNodeTool *nt, SPItem *base, SPObject *obj, Inkscape::UI::Sh // TODO add support for objectBoundingBox r.edit_transform = base ? sp_item_i2doc_affine(base) : Geom::identity(); r.role = role; - r.edit_original = false; if (s.insert(r).second) { // this item was encountered the first time if (nt->edit_clipping_paths && item->clip_ref) { -- cgit v1.2.3 From a2a3b0d8ef5f15db7d6e8ba569766117e618de34 Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Tue, 8 Dec 2009 03:47:53 +0100 Subject: Fix the "show temporary outlines for selected paths" preference. (bzr r8846.2.4) --- src/ui/tool/node-tool.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src/ui/tool/node-tool.cpp') diff --git a/src/ui/tool/node-tool.cpp b/src/ui/tool/node-tool.cpp index 9103f066f..e310731bb 100644 --- a/src/ui/tool/node-tool.cpp +++ b/src/ui/tool/node-tool.cpp @@ -395,12 +395,10 @@ gint ink_node_tool_root_handler(SPEventContext *event_context, GdkEvent *event) case GDK_MOTION_NOTIFY: // create outline if (prefs->getBool("/tools/nodes/pathflash_enabled")) { - if (prefs->getBool("/tools/nodes/pathflash_unselected") && !nt->_multipath->empty()) - break; - SPItem *over_item = sp_event_context_find_item (desktop, event_point(event->button), FALSE, TRUE); if (over_item == nt->flashed_item) break; + if (!prefs->getBool("/tools/nodes/pathflash_selected") && selection->includes(over_item)) break; if (nt->flash_tempitem) { desktop->remove_temporary_canvasitem(nt->flash_tempitem); nt->flash_tempitem = NULL; -- cgit v1.2.3 From 6286e1b266d79742170df705ba6a1e6f94ca32d6 Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Sun, 27 Dec 2009 00:59:01 +0100 Subject: Implement selection linear grow (bzr r8846.2.8) --- src/ui/tool/node-tool.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'src/ui/tool/node-tool.cpp') diff --git a/src/ui/tool/node-tool.cpp b/src/ui/tool/node-tool.cpp index e310731bb..735ddf87e 100644 --- a/src/ui/tool/node-tool.cpp +++ b/src/ui/tool/node-tool.cpp @@ -38,6 +38,50 @@ #include "pixmaps/cursor-node.xpm" #include "pixmaps/cursor-node-d.xpm" +/** @struct InkNodeTool + * + * Node tool event context. + * + * @par Architectural overview of the tool + * @par + * Here's a breakdown of what each object does. + * - Handle: shows a handle and keeps the node type constraint (smooth / symmetric) by updating + * the other handle's position when dragged. Its move() method cannot violate the constraints. + * - Node: keeps node type constraints for auto nodes and smooth nodes at ends of linear segments. + * Its move() method cannot violate constraints. Handles linear grow and dispatches spatial grow + * to MultiPathManipulator. Keeps a reference to its NodeList. + * - NodeList: exposes an iterator-based interface to nodes. It is possible to obtain an iterator + * to a node from the node. Keeps a reference to its SubpathList. + * - SubpathList: list of NodeLists that represents an editable pathvector. Keeps a reference + * to its PathManipulator. + * - PathManipulator: performs most of the single-path actions like reverse subpaths, + * delete segment, shift selection, etc. Keeps a reference to MultiPathManipulator. + * - MultiPathManipulator: performs additional operations for actions that are not per-path, + * for example node joins and segment joins. Tracks the control transforms for PMs that edit + * clipping paths and masks. It is more or less equivalent to ShapeEditor and in the future + * it might handle all shapes. Handles XML commit of actions that affect all paths or + * the node selection and removes PathManipulators that have no nodes left after e.g. node + * deletes. + * - ControlPointSelection: keeps track of node selection. Performs actions that require no + * knowledge about the path, only about the nodes, like dragging and transforms. It is not + * specific to nodes and can accomodate any control point derived from SelectableControlPoint. + * Transforms nodes in response to transform handle events. + * - TransformHandleSet: displays nodeset transform handles and emits transform events. The aim + * is to eventually use a common class for object and control point transforms. + * + * @par Plans for the future + * @par + * - MultiPathManipulator should become a generic shape editor that manages all active manipulator, + * more or less like the old ShapeEditor. + * - Knotholder should be rewritten into one manipulator class per shape, using the control point + * classes. Interesting features like dragging rectangle sides could be added along the way. + * - Better handling of clip and mask editing, particularly in response to undo. + * - High level refactoring of the event context hierarchy. All aspects of tools, like toolbox + * controls, icons, event handling should be collected in one class, though each aspect + * of a tool might be in an separate class for better modularity. The long term goal is to allow + * tools to be defined in extensions or shared library plugins. + */ + namespace { SPCanvasGroup *create_control_group(SPDesktop *d); void ink_node_tool_class_init(InkNodeToolClass *klass); -- cgit v1.2.3 From b52865a71a9f83da9719a3ec5f50a4a2cd7cdace Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Sun, 10 Jan 2010 01:46:28 +0100 Subject: * Implement node snapping. * Fix minor bug in linear grow. * Add --fixes. * Move some node selection-related functions to ControlPointSelection. Fixed bugs: - https://launchpad.net/bugs/170561 - https://launchpad.net/bugs/171893 - https://launchpad.net/bugs/182585 - https://launchpad.net/bugs/446773 (bzr r8846.2.9) --- src/ui/tool/node-tool.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/ui/tool/node-tool.cpp') diff --git a/src/ui/tool/node-tool.cpp b/src/ui/tool/node-tool.cpp index 735ddf87e..c1ba3394e 100644 --- a/src/ui/tool/node-tool.cpp +++ b/src/ui/tool/node-tool.cpp @@ -62,12 +62,15 @@ * it might handle all shapes. Handles XML commit of actions that affect all paths or * the node selection and removes PathManipulators that have no nodes left after e.g. node * deletes. - * - ControlPointSelection: keeps track of node selection. Performs actions that require no + * - ControlPointSelection: keeps track of node selection and a set of nodes that can potentially + * be selected. There can be more than one selection. Performs actions that require no * knowledge about the path, only about the nodes, like dragging and transforms. It is not * specific to nodes and can accomodate any control point derived from SelectableControlPoint. * Transforms nodes in response to transform handle events. * - TransformHandleSet: displays nodeset transform handles and emits transform events. The aim * is to eventually use a common class for object and control point transforms. + * - SelectableControlPoint: base for any type of selectable point. It can belong to only one + * selection. * * @par Plans for the future * @par @@ -384,9 +387,6 @@ void ink_node_tool_selection_changed(InkNodeTool *nt, Inkscape::Selection *sel) std::set shapes; - // TODO this is ugly!!! - //typedef std::map > TransMap; - //typedef std::map > PathMap; GSList const *ilist = sel->itemList(); for (GSList *i = const_cast(ilist); i; i = i->next) { @@ -477,7 +477,7 @@ gint ink_node_tool_root_handler(SPEventContext *event_context, GdkEvent *event) case GDK_a: if (held_control(event->key)) { if (held_alt(event->key)) { - nt->_multipath->selectAll(); + nt->_selected_nodes->selectAll(); } else { // select all nodes in subpaths that have something selected // if nothing is selected, select everything @@ -554,7 +554,7 @@ void ink_node_tool_select_area(InkNodeTool *nt, Geom::Rect const &sel, GdkEventB selection->setList(items); g_slist_free(items); } else { - nt->_multipath->selectArea(sel, !held_shift(*event)); + nt->_selected_nodes->selectArea(sel); } } void ink_node_tool_select_point(InkNodeTool *nt, Geom::Point const &sel, GdkEventButton *event) -- cgit v1.2.3