node-tool.cpp revision 70d31ae8a7a27e57cfcdc921ea0d2f47c92442a4
/** @file
* @brief New node tool - implementation
*/
/* Authors:
* Krzysztof KosiĆski <tweenk@gmail.com>
*
* Copyright (C) 2009 Authors
* Released under GNU GPL, read the file 'COPYING' for more information
*/
#include <glib.h>
#include "desktop.h"
#include "desktop-handles.h"
#include "display/canvas-bpath.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!
#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 "ui/tool/shape-record.h"
#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 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
* - 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 {
} // anonymous namespace
{
if (!type) {
sizeof(InkNodeToolClass),
sizeof(InkNodeTool),
4,
NULL, /* value_table */
};
}
return type;
}
namespace {
{
return reinterpret_cast<SPCanvasGroup*>(sp_canvas_item_new(
}
void destroy_group(SPCanvasGroup *g)
{
}
{
}
{
//new (&nt->_mgroup) Inkscape::UI::ManipulatorGroup(nt->desktop);
}
{
nt->enableGrDrag(false);
if (nt->_node_message_context) {
delete nt->_node_message_context;
}
if (nt->shape_editor) {
delete nt->shape_editor;
}
}
{
SPEventContextClass *parent = (SPEventContextClass *) g_type_class_peek(g_type_parent(INK_TYPE_NODE_TOOL));
// selector has to be created here, so that its hidden control point is on the bottom
// Prepare canvas groups for controls. This guarantees correct z-order, so that
// for example a dragpoint won't obscure a node
nt));
/*nt->_selection_modified_connection.disconnect();
nt->_selection_modified_connection =
selection->connectModified(
sigc::hide(sigc::bind<0>(
sigc::ptr_fun(&ink_node_tool_selection_modified),
nt)));*/
nt));
nt));
nt));
(void*) 0));
(GdkEvent*)0),
nt))));
nt->cursor_drag = false;
nt->show_transform_handles = true;
nt->single_node_transform_handles = false;
// TODO remove this!
// read prefs before adding items to selection to prevent momentarily showing the outline
ec->enableSelectionCue();
}
ec->enableGrDrag();
}
}
{
if (entry_name == "show_handles") {
} else if (entry_name == "show_outline") {
} else if (entry_name == "show_path_direction") {
} else if (entry_name == "show_transform_handles") {
} else if (entry_name == "single_node_transform_handles") {
} else if (entry_name == "edit_clipping_paths") {
} else if (entry_name == "edit_masks") {
} else {
if (parent_class->set)
}
}
/** Recursively collect ShapeRecords */
{
if (!obj) return;
ShapeRecord r;
r.role = SHAPE_ROLE_LPE_PARAM;
s.insert(r);
}
} else if (SP_IS_ITEM(obj)) {
ShapeRecord r;
// TODO add support for objectBoundingBox
// this item was encountered the first time
}
}
}
}
}
struct IsPath {
bool operator()(SPItem *i) const { return SP_IS_PATH(i); }
};
{
if (SP_IS_ITEM(obj)) {
}
}
// 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;
ShapeRecord const &r = *i;
something_set = true;
break;
}
}
if (!something_set) {
}
}
{
/* things to handle here:
* 1. selection of items
* 2. passing events to manipulators
* 3. some keybindings
*/
{
case GDK_MOTION_NOTIFY:
// create outline
if (nt->flash_tempitem) {
}
c->unref();
}
return true;
case GDK_KEY_PRESS:
{
case GDK_Escape: // deselect everything
} else {
}
return TRUE;
case GDK_a:
} else {
// select all nodes in subpaths that have something selected
// if nothing is selected, select everything
}
return TRUE;
}
break;
default:
break;
}
break;
case GDK_KEY_RELEASE:
break;
default: break;
}
SPEventContextClass *parent_class = (SPEventContextClass *) g_type_class_peek(g_type_parent(INK_TYPE_NODE_TOOL));
if (parent_class->root_handler)
return FALSE;
}
{
if (state_held_shift(new_state)) {
"click to toggle object selection"));
return;
}
}
if (sz != 0) {
"Selected <b>%d nodes</b>. Drag to select nodes, click to select a single object "
"or unselect all objects"), sz);
} else {
"or clear the selection"));
}
}
{
if (parent_class->item_handler)
return FALSE;
}
{
// if multipath is empty, select rubberbanded items rather than nodes
} else {
}
}
{
if (!event) return;
// if no Shift, deselect
}
return;
}
if (held_shift(*event)) {
} else {
}
}
{
nt->cursor_drag = true;
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 :