summaryrefslogtreecommitdiffstats
path: root/src/ui/tool
diff options
context:
space:
mode:
authorMartin Owens <doctormo@gmail.com>2014-03-27 01:33:44 +0000
committerMartin Owens <doctormo@gmail.com>2014-03-27 01:33:44 +0000
commit5a4fb2325f60d292b47330f540b26a3279341c90 (patch)
treed2aa7967be25450b83e625025366c618101ae49f /src/ui/tool
parentThe Polar Arrange Tab of the Arrange Dialog now hides the parametric (diff)
parentRemove Snap menu item and improve grid menu item text (diff)
downloadinkscape-5a4fb2325f60d292b47330f540b26a3279341c90.tar.gz
inkscape-5a4fb2325f60d292b47330f540b26a3279341c90.zip
Commit a merge to trunk, with probabal errors
(bzr r11073.1.36)
Diffstat (limited to 'src/ui/tool')
-rw-r--r--src/ui/tool/Makefile_insert2
-rw-r--r--src/ui/tool/control-point-selection.cpp17
-rw-r--r--src/ui/tool/control-point-selection.h2
-rw-r--r--src/ui/tool/control-point.cpp340
-rw-r--r--src/ui/tool/control-point.h293
-rw-r--r--src/ui/tool/curve-drag-point.cpp29
-rw-r--r--src/ui/tool/curve-drag-point.h23
-rw-r--r--src/ui/tool/event-utils.cpp16
-rw-r--r--src/ui/tool/manipulator.h6
-rw-r--r--src/ui/tool/modifier-tracker.cpp4
-rw-r--r--src/ui/tool/multi-path-manipulator.cpp47
-rw-r--r--src/ui/tool/multi-path-manipulator.h3
-rw-r--r--src/ui/tool/node-tool.cpp695
-rw-r--r--src/ui/tool/node-tool.h95
-rw-r--r--src/ui/tool/node.cpp302
-rw-r--r--src/ui/tool/node.h177
-rw-r--r--src/ui/tool/path-manipulator.cpp40
-rw-r--r--src/ui/tool/path-manipulator.h9
-rw-r--r--src/ui/tool/selectable-control-point.cpp72
-rw-r--r--src/ui/tool/selectable-control-point.h47
-rw-r--r--src/ui/tool/selector.cpp35
-rw-r--r--src/ui/tool/selector.h2
-rw-r--r--src/ui/tool/transform-handle-set.cpp152
-rw-r--r--src/ui/tool/transform-handle-set.h35
24 files changed, 1018 insertions, 1425 deletions
diff --git a/src/ui/tool/Makefile_insert b/src/ui/tool/Makefile_insert
index 2a72dd645..f46f48b72 100644
--- a/src/ui/tool/Makefile_insert
+++ b/src/ui/tool/Makefile_insert
@@ -19,8 +19,6 @@ ink_common_sources += \
ui/tool/node.cpp \
ui/tool/node.h \
ui/tool/node-types.h \
- ui/tool/node-tool.cpp \
- ui/tool/node-tool.h \
ui/tool/path-manipulator.cpp \
ui/tool/path-manipulator.h \
ui/tool/selectable-control-point.cpp \
diff --git a/src/ui/tool/control-point-selection.cpp b/src/ui/tool/control-point-selection.cpp
index 1c66b91b6..d10ed0f0d 100644
--- a/src/ui/tool/control-point-selection.cpp
+++ b/src/ui/tool/control-point-selection.cpp
@@ -21,10 +21,6 @@
#include <gdk/gdkkeysyms.h>
-#if !GTK_CHECK_VERSION(2,22,0)
-#include "compat-key-syms.h"
-#endif
-
namespace Inkscape {
namespace UI {
@@ -198,6 +194,8 @@ void ControlPointSelection::align(Geom::Dim2 axis)
bound.unionWith(Geom::OptInterval((*i)->position()[d]));
}
+ if (!bound) { return; }
+
double new_coord = bound->middle();
for (iterator i = _points.begin(); i != _points.end(); ++i) {
Geom::Point pos = (*i)->position();
@@ -224,6 +222,8 @@ void ControlPointSelection::distribute(Geom::Dim2 d)
bound.unionWith(Geom::OptInterval(pos[d]));
}
+ if (!bound) { return; }
+
// now we iterate over the multimap and set aligned positions.
double step = size() == 1 ? 0 : bound->extent() / (size() - 1);
double start = bound->min();
@@ -268,7 +268,9 @@ void ControlPointSelection::toggleTransformHandlesMode()
{
if (_handles->mode() == TransformHandleSet::MODE_SCALE) {
_handles->setMode(TransformHandleSet::MODE_ROTATE_SKEW);
- if (size() == 1) _handles->rotationCenter().setVisible(false);
+ if (size() == 1) {
+ _handles->rotationCenter().setVisible(false);
+ }
} else {
_handles->setMode(TransformHandleSet::MODE_SCALE);
}
@@ -383,8 +385,9 @@ void ControlPointSelection::_pointChanged(SelectableControlPoint *p, bool select
{
_updateBounds();
_updateTransformHandles(false);
- if (_bounds)
+ if (_bounds) {
_handles->rotationCenter().move(_bounds->midpoint());
+ }
signal_point_changed.emit(p, selected);
}
@@ -583,7 +586,7 @@ void ControlPointSelection::_commitHandlesTransform(CommitEvent ce)
signal_commit.emit(ce);
}
-bool ControlPointSelection::event(SPEventContext * /*event_context*/, GdkEvent *event)
+bool ControlPointSelection::event(Inkscape::UI::Tools::ToolBase * /*event_context*/, GdkEvent *event)
{
// implement generic event handling that should apply for all control point selections here;
// for example, keyboard moves and transformations. This way this functionality doesn't need
diff --git a/src/ui/tool/control-point-selection.h b/src/ui/tool/control-point-selection.h
index f5eb47026..a087e0455 100644
--- a/src/ui/tool/control-point-selection.h
+++ b/src/ui/tool/control-point-selection.h
@@ -90,7 +90,7 @@ public:
void invertSelection();
void spatialGrow(SelectableControlPoint *origin, int dir);
- virtual bool event(SPEventContext *, GdkEvent *);
+ virtual bool event(Inkscape::UI::Tools::ToolBase *, GdkEvent *);
void transform(Geom::Affine const &m);
void align(Geom::Dim2 d);
diff --git a/src/ui/tool/control-point.cpp b/src/ui/tool/control-point.cpp
index 68749cdff..e98c7b2a2 100644
--- a/src/ui/tool/control-point.cpp
+++ b/src/ui/tool/control-point.cpp
@@ -1,7 +1,3 @@
-/**
- * @file
- * Desktop-bound visual control object - implementation.
- */
/* Authors:
* Krzysztof Kosiński <tweenk.pl@gmail.com>
* Jon A. Cruz <jon@joncruz.org>
@@ -11,103 +7,46 @@
*/
#include <iostream>
+#include <gdk/gdkkeysyms.h>
#include <gdkmm.h>
-#include <gtkmm.h>
#include <2geom/point.h>
#include "desktop.h"
#include "desktop-handles.h"
#include "display/sp-canvas.h"
#include "display/snap-indicator.h"
-#include "event-context.h"
+#include "ui/tools/tool-base.h"
#include "message-context.h"
#include "preferences.h"
#include "snap-preferences.h"
#include "sp-namedview.h"
+#include "ui/control-manager.h"
#include "ui/tool/control-point.h"
#include "ui/tool/event-utils.h"
#include "ui/tool/transform-handle-set.h"
-#if !GTK_CHECK_VERSION(2,22,0)
-#include "compat-key-syms.h"
-#endif
-
namespace Inkscape {
namespace UI {
-// class and member documentation goes here...
-
-/**
- * @class ControlPoint
- * Draggable point, the workhorse of on-canvas editing.
- *
- * Control points (formerly known as knots) are graphical representations of some significant
- * point in the drawing. The drawing can be changed by dragging the point and the things that are
- * attached to it with the mouse. Example things that could be edited with draggable points
- * are gradient stops, the place where text is attached to a path, text kerns, nodes and handles
- * in a path, and many more.
- *
- * @par Control point event handlers
- * @par
- * The control point has several virtual methods which allow you to react to things that
- * happen to it. The most important ones are the grabbed, dragged, ungrabbed and moved functions.
- * When a drag happens, the order of calls is as follows:
- * - <tt>grabbed()</tt>
- * - <tt>dragged()</tt>
- * - <tt>dragged()</tt>
- * - <tt>dragged()</tt>
- * - ...
- * - <tt>dragged()</tt>
- * - <tt>ungrabbed()</tt>
- *
- * The control point can also respond to clicks and double clicks. On a double click,
- * clicked() is called, followed by doubleclicked(). When deriving from SelectableControlPoint,
- * you need to manually call the superclass version at the appropriate point in your handler.
- *
- * @par Which method to override?
- * @par
- * You might wonder which hook to use when you want to do things when the point is relocated.
- * Here are some tips:
- * - If the point is used to edit an object, override the move() method.
- * - If the point can usually be dragged wherever you like but can optionally be constrained
- * to axes or the like, add a handler for <tt>signal_dragged</tt> that modifies its new
- * position argument.
- * - If the point has additional canvas items tied to it (like handle lines), override
- * the setPosition() method.
- */
-
-/**
- * @enum ControlPoint::State
- * Enumeration representing the possible states of the control point, used to determine
- * its appearance.
- * @var ControlPoint::STATE_NORMAL
- * Normal state
- * @var ControlPoint::STATE_MOUSEOVER
- * Mouse is hovering over the control point
- * @var ControlPoint::STATE_CLICKED
- * First mouse button pressed over the control point
- */
// Default colors for control points
-static ControlPoint::ColorSet default_color_set = {
+ControlPoint::ColorSet ControlPoint::_default_color_set = {
{0xffffff00, 0x01000000}, // normal fill, stroke
{0xff0000ff, 0x01000000}, // mouseover fill, stroke
- {0x0000ffff, 0x01000000} // clicked fill, stroke
+ {0x0000ffff, 0x01000000}, // clicked fill, stroke
+ //
+ {0x0000ffff, 0x000000ff}, // normal fill, stroke when selected
+ {0xff000000, 0x000000ff}, // mouseover fill, stroke when selected
+ {0xff000000, 0x000000ff} // clicked fill, stroke when selected
};
-/** Holds the currently mouseovered control point. */
ControlPoint *ControlPoint::mouseovered_point = 0;
-/** Emitted when the mouseovered point changes. The parameter is the new mouseovered point.
- * When a point ceases to be mouseovered, the parameter will be NULL. */
sigc::signal<void, ControlPoint*> ControlPoint::signal_mouseover_change;
-/** Stores the window point over which the cursor was during the last mouse button press */
Geom::Point ControlPoint::_drag_event_origin(Geom::infinity(), Geom::infinity());
-/** Stores the desktop point from which the last drag was initiated */
Geom::Point ControlPoint::_drag_origin(Geom::infinity(), Geom::infinity());
-/** Events which should be captured when a handle is being dragged. */
int const ControlPoint::_grab_event_mask = (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_KEY_PRESS_MASK |
GDK_KEY_RELEASE_MASK);
@@ -115,69 +54,51 @@ int const ControlPoint::_grab_event_mask = (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_R
bool ControlPoint::_drag_initiated = false;
bool ControlPoint::_event_grab = false;
-/** A color set which you can use to create an invisible control that can still receive events.
- * @relates ControlPoint */
-ControlPoint::ColorSet invisible_cset = {
+ControlPoint::ColorSet ControlPoint::invisible_cset = {
+ {0x00000000, 0x00000000},
+ {0x00000000, 0x00000000},
+ {0x00000000, 0x00000000},
{0x00000000, 0x00000000},
{0x00000000, 0x00000000},
{0x00000000, 0x00000000}
};
-/**
- * Create a regular control point.
- * Derive to have constructors with a reasonable number of parameters.
- *
- * @param d Desktop for this control
- * @param initial_pos Initial position of the control point in desktop coordinates
- * @param anchor Where is the control point rendered relative to its desktop coordinates
- * @param shape Shape of the control point: square, diamond, circle...
- * @param size Pixel size of the visual representation
- * @param cset Colors of the point
- * @param group The canvas group the point's canvas item should be created in
- */
-ControlPoint::ControlPoint(SPDesktop *d, Geom::Point const &initial_pos,
- SPAnchorType anchor, SPCtrlShapeType shape,
- unsigned int size, ColorSet *cset, SPCanvasGroup *group)
- : _desktop (d)
- , _canvas_item (NULL)
- , _cset (cset ? cset : &default_color_set)
- , _state (STATE_NORMAL)
- , _position (initial_pos)
+ControlPoint::ControlPoint(SPDesktop *d, Geom::Point const &initial_pos, SPAnchorType anchor,
+ Glib::RefPtr<Gdk::Pixbuf> pixbuf,
+ ColorSet const &cset, SPCanvasGroup *group) :
+ _desktop(d),
+ _canvas_item(NULL),
+ _cset(cset),
+ _state(STATE_NORMAL),
+ _position(initial_pos),
+ _lurking(false)
{
_canvas_item = sp_canvas_item_new(
- group ? group : sp_desktop_controls (_desktop), SP_TYPE_CTRL,
- "anchor", (SPAnchorType) anchor, "size", (gdouble) size, "shape", shape,
- "filled", TRUE, "fill_color", _cset->normal.fill,
- "stroked", TRUE, "stroke_color", _cset->normal.stroke,
+ group ? group : sp_desktop_controls(_desktop), SP_TYPE_CTRL,
+ "anchor", (SPAnchorType) anchor, "size", (gdouble) pixbuf->get_width(),
+ "shape", SP_CTRL_SHAPE_BITMAP, "pixbuf", pixbuf->gobj(),
+ "filled", TRUE, "fill_color", _cset.normal.fill,
+ "stroked", TRUE, "stroke_color", _cset.normal.stroke,
"mode", SP_CTRL_MODE_XOR, NULL);
_commonInit();
}
-/**
- * Create a control point with a pixbuf-based visual representation.
- *
- * @param d Desktop for this control
- * @param initial_pos Initial position of the control point in desktop coordinates
- * @param anchor Where is the control point rendered relative to its desktop coordinates
- * @param pixbuf Pixbuf to be used as the visual representation
- * @param cset Colors of the point
- * @param group The canvas group the point's canvas item should be created in
- */
-ControlPoint::ControlPoint(SPDesktop *d, Geom::Point const &initial_pos,
- SPAnchorType anchor, Glib::RefPtr<Gdk::Pixbuf> pixbuf,
- ColorSet *cset, SPCanvasGroup *group)
- : _desktop (d)
- , _canvas_item (NULL)
- , _cset(cset ? cset : &default_color_set)
- , _position (initial_pos)
+ControlPoint::ControlPoint(SPDesktop *d, Geom::Point const &initial_pos, SPAnchorType anchor,
+ ControlType type,
+ ColorSet const &cset, SPCanvasGroup *group) :
+ _desktop(d),
+ _canvas_item(NULL),
+ _cset(cset),
+ _state(STATE_NORMAL),
+ _position(initial_pos),
+ _lurking(false)
{
- _canvas_item = sp_canvas_item_new(
- group ? group : sp_desktop_controls(_desktop), SP_TYPE_CTRL,
- "anchor", (SPAnchorType) anchor, "size", (gdouble) pixbuf->get_width(),
- "shape", SP_CTRL_SHAPE_BITMAP, "pixbuf", pixbuf->gobj(),
- "filled", TRUE, "fill_color", _cset->normal.fill,
- "stroked", TRUE, "stroke_color", _cset->normal.stroke,
- "mode", SP_CTRL_MODE_XOR, NULL);
+ _canvas_item = ControlManager::getManager().createControl(group ? group : sp_desktop_controls(_desktop), type);
+ g_object_set(_canvas_item,
+ "anchor", anchor,
+ "filled", TRUE, "fill_color", _cset.normal.fill,
+ "stroked", TRUE, "stroke_color", _cset.normal.stroke,
+ "mode", SP_CTRL_MODE_XOR, NULL);
_commonInit();
}
@@ -190,7 +111,7 @@ ControlPoint::~ControlPoint()
g_signal_handler_disconnect(G_OBJECT(_canvas_item), _event_handler_connection);
//sp_canvas_item_hide(_canvas_item);
- gtk_object_destroy(_canvas_item);
+ sp_canvas_item_destroy(_canvas_item);
}
void ControlPoint::_commonInit()
@@ -200,29 +121,17 @@ void ControlPoint::_commonInit()
G_CALLBACK(_event_handler), this);
}
-/** Relocate the control point without side effects.
- * Overload this method only if there is an additional graphical representation
- * that must be updated (like the lines that connect handles to nodes). If you override it,
- * you must also call the superclass implementation of the method.
- * @todo Investigate whether this method should be protected */
void ControlPoint::setPosition(Geom::Point const &pos)
{
_position = pos;
SP_CTRL(_canvas_item)->moveto(pos);
}
-/** Move the control point to new position with side effects.
- * This is called after each drag. Override this method if only some positions make sense
- * for a control point (like a point that must always be on a path and can't modify it),
- * or when moving a control point changes the positions of other points. */
void ControlPoint::move(Geom::Point const &pos)
{
setPosition(pos);
}
-/** Apply an arbitrary affine transformation to a control point. This is used
- * by ControlPointSelection, and is important for things like nodes with handles.
- * The default implementation simply moves the point according to the transform. */
void ControlPoint::transform(Geom::Affine const &m) {
move(position() * m);
}
@@ -232,9 +141,6 @@ bool ControlPoint::visible() const
return sp_canvas_item_is_visible(_canvas_item);
}
-/** Set the visibility of the control point. An invisible point is not drawn on the canvas
- * and cannot receive any events. If you want to have an invisible point that can respond
- * to events, use <tt>invisible_cset</tt> as its color set. */
void ControlPoint::setVisible(bool v)
{
if (v) sp_canvas_item_show(_canvas_item);
@@ -287,9 +193,9 @@ void ControlPoint::_setSize(unsigned int size)
g_object_set(_canvas_item, "size", (gdouble) size, NULL);
}
-void ControlPoint::_setShape(SPCtrlShapeType shape)
+bool ControlPoint::_setControlType(Inkscape::ControlType type)
{
- g_object_set(_canvas_item, "shape", shape, NULL);
+ return ControlManager::getManager().setControlType(_canvas_item, type);
}
void ControlPoint::_setAnchor(SPAnchorType anchor)
@@ -305,15 +211,36 @@ void ControlPoint::_setPixbuf(Glib::RefPtr<Gdk::Pixbuf> p)
// re-routes events into the virtual function
int ControlPoint::_event_handler(SPCanvasItem */*item*/, GdkEvent *event, ControlPoint *point)
{
+ if ((point == NULL) || (point->_desktop == NULL)) {
+ return FALSE;
+ }
return point->_eventHandler(point->_desktop->event_context, event) ? TRUE : FALSE;
}
// main event callback, which emits all other callbacks.
-bool ControlPoint::_eventHandler(SPEventContext *event_context, GdkEvent *event)
+bool ControlPoint::_eventHandler(Inkscape::UI::Tools::ToolBase *event_context, GdkEvent *event)
{
// NOTE the static variables below are shared for all points!
// TODO handle clicks and drags from other buttons too
+ if (event == NULL)
+ {
+ return false;
+ }
+
+ if (event_context == NULL)
+ {
+ return false;
+ }
+ if (_desktop == NULL)
+ {
+ return false;
+ }
+ if(event_context->desktop !=_desktop)
+ {
+ g_warning ("ControlPoint: desktop pointers not equal!");
+ //return false;
+ }
// offset from the pointer hotspot to the center of the grabbed knot in desktop coords
static Geom::Point pointer_offset;
// number of last doubleclicked button
@@ -321,7 +248,8 @@ bool ControlPoint::_eventHandler(SPEventContext *event_context, GdkEvent *event)
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
int drag_tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
-
+ GdkEventMotion em;
+ SPCanvas* Ca;
switch(event->type)
{
case GDK_BUTTON_PRESS:
@@ -347,18 +275,22 @@ bool ControlPoint::_eventHandler(SPEventContext *event_context, GdkEvent *event)
return true;
case GDK_MOTION_NOTIFY:
- combine_motion_events(_desktop->canvas, event->motion, 0);
- if (_event_grab && !_desktop->event_context->space_panning) {
+ Ca = _desktop->canvas;
+ em = event->motion;
+ combine_motion_events(Ca, em, 0);
+ if (_event_grab && ! event_context->space_panning) {
_desktop->snapindicator->remove_snaptarget();
bool transferred = false;
if (!_drag_initiated) {
- bool t = fabs(event->motion.x - _drag_event_origin[Geom::X]) <= drag_tolerance &&
- fabs(event->motion.y - _drag_event_origin[Geom::Y]) <= drag_tolerance;
- if (t) return true;
+ bool t = fabs(em.x - _drag_event_origin[Geom::X]) <= drag_tolerance &&
+ fabs(em.y - _drag_event_origin[Geom::Y]) <= drag_tolerance;
+ if (t){
+ return true;
+ }
// if we are here, it means the tolerance was just exceeded.
_drag_origin = _position;
- transferred = grabbed(&event->motion);
+ transferred = grabbed(&em);
// _drag_initiated might change during the above virtual call
if (!_drag_initiated) {
// this guarantees smooth redraws while dragging
@@ -369,17 +301,16 @@ bool ControlPoint::_eventHandler(SPEventContext *event_context, GdkEvent *event)
if (!transferred) {
// dragging in progress
Geom::Point new_pos = _desktop->w2d(event_point(event->motion)) + pointer_offset;
-
// the new position is passed by reference and can be changed in the handlers.
- dragged(new_pos, &event->motion);
+ dragged(new_pos, &em);
move(new_pos);
- _updateDragTip(&event->motion); // update dragging tip after moving to new position
+ _updateDragTip(&em); // update dragging tip after moving to new position
_desktop->scroll_to_point(new_pos);
_desktop->set_coordinate_status(_position);
- sp_event_context_snap_delay_handler(_desktop->event_context, NULL,
+ sp_event_context_snap_delay_handler(event_context, NULL,
(gpointer) this, &event->motion,
- DelayedSnapEvent::CONTROL_POINT_HANDLER);
+ Inkscape::UI::Tools::DelayedSnapEvent::CONTROL_POINT_HANDLER);
}
return true;
}
@@ -393,8 +324,9 @@ bool ControlPoint::_eventHandler(SPEventContext *event_context, GdkEvent *event)
// We must snap at some point in time though, and this is our last chance)
// PS: For other contexts this is handled already in sp_event_context_item_handler or
// sp_event_context_root_handler
- if (_desktop->event_context->_delayed_snap_event) {
- sp_event_context_snap_watchdog_callback(_desktop->event_context->_delayed_snap_event);
+ //if (_desktop && _desktop->event_context && _desktop->event_context->_delayed_snap_event) {
+ if (event_context->_delayed_snap_event) {
+ sp_event_context_snap_watchdog_callback(event_context->_delayed_snap_event);
}
sp_canvas_item_ungrab(_canvas_item, event->button.time);
@@ -429,8 +361,9 @@ bool ControlPoint::_eventHandler(SPEventContext *event_context, GdkEvent *event)
if (_event_grab && !event->grab_broken.keyboard) {
{
ungrabbed(NULL);
- if (_drag_initiated)
+ if (_drag_initiated) {
_desktop->canvas->endForcedFullRedraws();
+ }
}
_setState(STATE_NORMAL);
_event_grab = false;
@@ -442,14 +375,14 @@ bool ControlPoint::_eventHandler(SPEventContext *event_context, GdkEvent *event)
// update tips on modifier state change
// TODO add ESC keybinding as drag cancel
case GDK_KEY_PRESS:
- switch (get_group0_keyval(&event->key))
+ switch (Inkscape::UI::Tools::get_group0_keyval(&event->key))
{
case GDK_KEY_Escape: {
// ignore Escape if this is not a drag
if (!_drag_initiated) break;
// temporarily disable snapping - we might snap to a different place than we were initially
- sp_event_context_discard_delayed_snap_event(_desktop->event_context);
+ sp_event_context_discard_delayed_snap_event(event_context);
SnapPreferences &snapprefs = _desktop->namedview->snap_manager.snapprefs;
bool snap_save = snapprefs.getSnapEnabledGlobally();
snapprefs.setSnapEnabledGlobally(false);
@@ -483,7 +416,8 @@ bool ControlPoint::_eventHandler(SPEventContext *event_context, GdkEvent *event)
ungrabbed(NULL); // ungrabbed handlers can handle a NULL event
snapprefs.setSnapEnabledGlobally(snap_save);
- } return true;
+ }
+ return true;
case GDK_KEY_Tab:
{// Downcast from ControlPoint to TransformHandle, if possible
// This is an ugly hack; we should have the transform handle intercept the keystrokes itself
@@ -509,7 +443,9 @@ bool ControlPoint::_eventHandler(SPEventContext *event_context, GdkEvent *event)
}
// Do not break here, to allow for updating tooltips and such
case GDK_KEY_RELEASE:
- if (mouseovered_point != this) return false;
+ if (mouseovered_point != this){
+ return false;
+ }
if (_drag_initiated) {
return true; // this prevents the tool from overwriting the drag tip
} else {
@@ -559,7 +495,9 @@ bool ControlPoint::_updateTip(unsigned state)
bool ControlPoint::_updateDragTip(GdkEventMotion *event)
{
- if (!_hasDragTips()) return false;
+ if (!_hasDragTips()) {
+ return false;
+ }
Glib::ustring tip = _getDragTip(event);
if (!tip.empty()) {
_desktop->event_context->defaultMessageContext()->set(Inkscape::NORMAL_MESSAGE,
@@ -581,16 +519,6 @@ void ControlPoint::_clearMouseover()
}
}
-/** Transfer the grab to another point. This method allows one to create a draggable point
- * that should be dragged instead of the one that received the grabbed signal.
- * This is used to implement dragging out handles in the new node tool, for example.
- *
- * This method will NOT emit the ungrab signal of @c prev_point, because this would complicate
- * using it with selectable control points. If you use this method while dragging, you must emit
- * the ungrab signal yourself.
- *
- * Note that this will break horribly if you try to transfer grab between points in different
- * desktops, which doesn't make much sense anyway. */
void ControlPoint::transferGrab(ControlPoint *prev_point, GdkEventMotion *event)
{
if (!_event_grab) return;
@@ -608,42 +536,80 @@ void ControlPoint::transferGrab(ControlPoint *prev_point, GdkEventMotion *event)
_setMouseover(this, event->state);
}
-/**
- * Change the state of the knot.
- * Alters the appearance of the knot to match one of the states: normal, mouseover
- * or clicked.
- */
void ControlPoint::_setState(State state)
{
ColorEntry current = {0, 0};
+ ColorSet const &activeCset = (_isLurking()) ? invisible_cset : _cset;
switch(state) {
- case STATE_NORMAL:
- current = _cset->normal; break;
- case STATE_MOUSEOVER:
- current = _cset->mouseover; break;
- case STATE_CLICKED:
- current = _cset->clicked; break;
+ case STATE_NORMAL:
+ current = activeCset.normal;
+ break;
+ case STATE_MOUSEOVER:
+ current = activeCset.mouseover;
+ break;
+ case STATE_CLICKED:
+ current = activeCset.clicked;
+ break;
};
_setColors(current);
_state = state;
}
+
+void ControlPoint::_handleControlStyling()
+{
+ if (_canvas_item->ctrlType != CTRL_TYPE_UNKNOWN) {
+ ControlManager::getManager().updateItem(_canvas_item);
+ }
+}
+
void ControlPoint::_setColors(ColorEntry colors)
{
g_object_set(_canvas_item, "fill_color", colors.fill, "stroke_color", colors.stroke, NULL);
}
+bool ControlPoint::_isLurking()
+{
+ return _lurking;
+}
+
+void ControlPoint::_setLurking(bool lurking)
+{
+ if (lurking != _lurking) {
+ _lurking = lurking;
+ _setState(_state); // TODO refactor out common part
+ }
+}
+
+
bool ControlPoint::_is_drag_cancelled(GdkEventMotion *event)
{
return !event || event->x_root == -1;
}
// dummy implementations for handlers
-// they are here to avoid unused param warnings
-bool ControlPoint::grabbed(GdkEventMotion *) { return false; }
-void ControlPoint::dragged(Geom::Point &, GdkEventMotion *) {}
-void ControlPoint::ungrabbed(GdkEventButton *) {}
-bool ControlPoint::clicked(GdkEventButton *) { return false; }
-bool ControlPoint::doubleclicked(GdkEventButton *) { return false; }
+
+bool ControlPoint::grabbed(GdkEventMotion * /*event*/)
+{
+ return false;
+}
+
+void ControlPoint::dragged(Geom::Point &/*new_pos*/, GdkEventMotion * /*event*/)
+{
+}
+
+void ControlPoint::ungrabbed(GdkEventButton * /*event*/)
+{
+}
+
+bool ControlPoint::clicked(GdkEventButton * /*event*/)
+{
+ return false;
+}
+
+bool ControlPoint::doubleclicked(GdkEventButton * /*event*/)
+{
+ return false;
+}
} // namespace UI
} // namespace Inkscape
diff --git a/src/ui/tool/control-point.h b/src/ui/tool/control-point.h
index 8d1b778a7..b3ed9545e 100644
--- a/src/ui/tool/control-point.h
+++ b/src/ui/tool/control-point.h
@@ -1,9 +1,8 @@
-/** @file
- * Desktop-bound visual control object
- */
/* Authors:
* Krzysztof Kosiński <tweenk.pl@gmail.com>
+ * Jon A. Cruz <jon@joncruz.org>
*
+ * Copyright (C) 2012 Authors
* Copyright (C) 2009 Authors
* Released under GNU GPL, read the file 'COPYING' for more information
*/
@@ -17,59 +16,149 @@
#include <sigc++/signal.h>
#include <sigc++/trackable.h>
#include <2geom/point.h>
+
+#include "ui/control-types.h"
#include "util/accumulators.h"
#include "display/sodipodi-ctrl.h"
#include "enums.h"
class SPDesktop;
-class SPEventContext;
namespace Inkscape {
namespace UI {
+namespace Tools {
+
+class ToolBase;
+
+}
+}
+}
-// most of the documentation is in the .cpp file
+namespace Inkscape {
+namespace UI {
+/**
+ * Draggable point, the workhorse of on-canvas editing.
+ *
+ * Control points (formerly known as knots) are graphical representations of some significant
+ * point in the drawing. The drawing can be changed by dragging the point and the things that are
+ * attached to it with the mouse. Example things that could be edited with draggable points
+ * are gradient stops, the place where text is attached to a path, text kerns, nodes and handles
+ * in a path, and many more.
+ *
+ * @par Control point event handlers
+ * @par
+ * The control point has several virtual methods which allow you to react to things that
+ * happen to it. The most important ones are the grabbed, dragged, ungrabbed and moved functions.
+ * When a drag happens, the order of calls is as follows:
+ * - <tt>grabbed()</tt>
+ * - <tt>dragged()</tt>
+ * - <tt>dragged()</tt>
+ * - <tt>dragged()</tt>
+ * - ...
+ * - <tt>dragged()</tt>
+ * - <tt>ungrabbed()</tt>
+ *
+ * The control point can also respond to clicks and double clicks. On a double click,
+ * clicked() is called, followed by doubleclicked(). When deriving from SelectableControlPoint,
+ * you need to manually call the superclass version at the appropriate point in your handler.
+ *
+ * @par Which method to override?
+ * @par
+ * You might wonder which hook to use when you want to do things when the point is relocated.
+ * Here are some tips:
+ * - If the point is used to edit an object, override the move() method.
+ * - If the point can usually be dragged wherever you like but can optionally be constrained
+ * to axes or the like, add a handler for <tt>signal_dragged</tt> that modifies its new
+ * position argument.
+ * - If the point has additional canvas items tied to it (like handle lines), override
+ * the setPosition() method.
+ */
class ControlPoint : boost::noncopyable, public sigc::trackable {
public:
typedef Inkscape::Util::ReverseInterruptible RInt;
typedef Inkscape::Util::Interruptible Int;
- // these have to be public, because GCC doesn't allow protected types in constructors,
- // even if the constructors are protected themselves.
- struct ColorEntry {
- guint32 fill;
- guint32 stroke;
- };
- struct ColorSet {
- ColorEntry normal;
- ColorEntry mouseover;
- ColorEntry clicked;
- };
+
+ /**
+ * Enumeration representing the possible states of the control point, used to determine
+ * its appearance.
+ *
+ * @todo resolve this to be in sync with the five standard GTK states.
+ */
enum State {
+ /** Normal state. */
STATE_NORMAL,
+
+ /** Mouse is hovering over the control point. */
STATE_MOUSEOVER,
+
+ /** First mouse button pressed over the control point. */
STATE_CLICKED
};
+ /**
+ * Destructor
+ */
virtual ~ControlPoint();
/// @name Adjust the position of the control point
/// @{
/** Current position of the control point. */
Geom::Point const &position() const { return _position; }
+
operator Geom::Point const &() { return _position; }
+
+ /**
+ * Move the control point to new position with side effects.
+ * This is called after each drag. Override this method if only some positions make sense
+ * for a control point (like a point that must always be on a path and can't modify it),
+ * or when moving a control point changes the positions of other points.
+ */
virtual void move(Geom::Point const &pos);
+
+ /**
+ * Relocate the control point without side effects.
+ * Overload this method only if there is an additional graphical representation
+ * that must be updated (like the lines that connect handles to nodes). If you override it,
+ * you must also call the superclass implementation of the method.
+ * @todo Investigate whether this method should be protected
+ */
virtual void setPosition(Geom::Point const &pos);
+
+ /**
+ * Apply an arbitrary affine transformation to a control point. This is used
+ * by ControlPointSelection, and is important for things like nodes with handles.
+ * The default implementation simply moves the point according to the transform.
+ */
virtual void transform(Geom::Affine const &m);
/// @}
/// @name Toggle the point's visibility
/// @{
bool visible() const;
+
+ /**
+ * Set the visibility of the control point. An invisible point is not drawn on the canvas
+ * and cannot receive any events. If you want to have an invisible point that can respond
+ * to events, use <tt>invisible_cset</tt> as its color set.
+ */
virtual void setVisible(bool v);
/// @}
/// @name Transfer grab from another event handler
/// @{
+ /**
+ * Transfer the grab to another point. This method allows one to create a draggable point
+ * that should be dragged instead of the one that received the grabbed signal.
+ * This is used to implement dragging out handles in the new node tool, for example.
+ *
+ * This method will NOT emit the ungrab signal of @c prev_point, because this would complicate
+ * using it with selectable control points. If you use this method while dragging, you must emit
+ * the ungrab signal yourself.
+ *
+ * Note that this will break horribly if you try to transfer grab between points in different
+ * desktops, which doesn't make much sense anyway.
+ */
void transferGrab(ControlPoint *from, GdkEventMotion *event);
/// @}
@@ -84,109 +173,233 @@ public:
/// @name Inspect the state of the control point
/// @{
- State state() { return _state; }
- bool mouseovered() { return this == mouseovered_point; }
+ State state() const { return _state; }
+
+ bool mouseovered() const { return this == mouseovered_point; }
/// @}
+ /** Holds the currently mouseovered control point. */
static ControlPoint *mouseovered_point;
+
+ /**
+ * Emitted when the mouseovered point changes. The parameter is the new mouseovered point.
+ * When a point ceases to be mouseovered, the parameter will be NULL.
+ */
static sigc::signal<void, ControlPoint*> signal_mouseover_change;
+
static Glib::ustring format_tip(char const *format, ...) G_GNUC_PRINTF(1,2);
// temporarily public, until snap delay is refactored a little
- virtual bool _eventHandler(SPEventContext *event_context, GdkEvent *event);
+ virtual bool _eventHandler(Inkscape::UI::Tools::ToolBase *event_context, GdkEvent *event);
+ SPDesktop *const _desktop; ///< The desktop this control point resides on.
protected:
+
+ struct ColorEntry {
+ guint32 fill;
+ guint32 stroke;
+ };
+
+ /**
+ * Color entries for each possible state.
+ * @todo resolve this to be in sync with the five standard GTK states.
+ */
+ struct ColorSet {
+ ColorEntry normal;
+ ColorEntry mouseover;
+ ColorEntry clicked;
+ ColorEntry selected_normal;
+ ColorEntry selected_mouseover;
+ ColorEntry selected_clicked;
+ };
+
+ /**
+ * A color set which you can use to create an invisible control that can still receive events.
+ */
+ static ColorSet invisible_cset;
+
+ /**
+ * Create a regular control point.
+ * Derive to have constructors with a reasonable number of parameters.
+ *
+ * @param d Desktop for this control
+ * @param initial_pos Initial position of the control point in desktop coordinates
+ * @param anchor Where is the control point rendered relative to its desktop coordinates
+ * @param type Logical type of the control point.
+ * @param cset Colors of the point
+ * @param group The canvas group the point's canvas item should be created in
+ */
ControlPoint(SPDesktop *d, Geom::Point const &initial_pos, SPAnchorType anchor,
- SPCtrlShapeType shape, unsigned int size, ColorSet *cset = 0, SPCanvasGroup *group = 0);
+ ControlType type,
+ ColorSet const &cset = _default_color_set, SPCanvasGroup *group = 0);
+
+ /**
+ * Create a control point with a pixbuf-based visual representation.
+ *
+ * @param d Desktop for this control
+ * @param initial_pos Initial position of the control point in desktop coordinates
+ * @param anchor Where is the control point rendered relative to its desktop coordinates
+ * @param pixbuf Pixbuf to be used as the visual representation
+ * @param cset Colors of the point
+ * @param group The canvas group the point's canvas item should be created in
+ */
ControlPoint(SPDesktop *d, Geom::Point const &initial_pos, SPAnchorType anchor,
- Glib::RefPtr<Gdk::Pixbuf> pixbuf, ColorSet *cset = 0, SPCanvasGroup *group = 0);
+ Glib::RefPtr<Gdk::Pixbuf> pixbuf,
+ ColorSet const &cset = _default_color_set, SPCanvasGroup *group = 0);
/// @name Handle control point events in subclasses
/// @{
/**
* Called when the user moves the point beyond the drag tolerance with the first button held
- * down. Return true if you called transferGrab() during this method.
- * @param event Motion event when drag tolerance was exceeded */
+ * down.
+ *
+ * @param event Motion event when drag tolerance was exceeded.
+ * @return true if you called transferGrab() during this method.
+ */
virtual bool grabbed(GdkEventMotion *event);
+
/**
* Called while dragging, but before moving the knot to new position.
+ *
* @param pos Old position, always equal to position()
* @param new_pos New position (after drag). This is passed as a non-const reference,
* so you can change it from the handler - that's how constrained dragging is implemented.
- * @param event Motion event */
+ * @param event Motion event.
+ */
virtual void dragged(Geom::Point &new_pos, GdkEventMotion *event);
+
/**
- * @var ControlPoint::signal_ungrabbed
- * Emitted when the control point finishes a drag.
+ * Called when the control point finishes a drag.
+ *
* @param event Button release event
*/
virtual void ungrabbed(GdkEventButton *event);
+
/**
- * Called when the control point is clicked, at mouse button release. Your override should
- * return true if the click had some effect. If it did nothing, return false. Improperly
- * implementing this method can cause the default context menu not to appear when a control
+ * Called when the control point is clicked, at mouse button release.
+ * Improperly implementing this method can cause the default context menu not to appear when a control
* point is right-clicked.
- * @param event Button release event */
+ *
+ * @param event Button release event
+ * @return true if the click had some effect, false if it did nothing.
+ */
virtual bool clicked(GdkEventButton *event);
+
/**
* Called when the control point is doubleclicked, at mouse button release.
- * @param event Button release event */
- virtual bool doubleclicked(GdkEventButton *);
+ *
+ * @param event Button release event
+ */
+ virtual bool doubleclicked(GdkEventButton *event);
/// @}
/// @name Manipulate the control point's appearance in subclasses
/// @{
+
+ /**
+ * Change the state of the knot.
+ * Alters the appearance of the knot to match one of the states: normal, mouseover
+ * or clicked.
+ */
virtual void _setState(State state);
+
+ void _handleControlStyling();
+
void _setColors(ColorEntry c);
unsigned int _size() const;
+
SPCtrlShapeType _shape() const;
+
SPAnchorType _anchor() const;
+
Glib::RefPtr<Gdk::Pixbuf> _pixbuf();
void _setSize(unsigned int size);
- void _setShape(SPCtrlShapeType shape);
+
+ bool _setControlType(Inkscape::ControlType type);
+
void _setAnchor(SPAnchorType anchor);
+
void _setPixbuf(Glib::RefPtr<Gdk::Pixbuf>);
+
+ /**
+ * Determins if the control point is not visible yet still reacting to events.
+ *
+ * @return true if non-visible, false otherwise.
+ */
+ bool _isLurking();
+
+ /**
+ * Sets the control point to be non-visible yet still reacting to events.
+ *
+ * @param lurking true to make non-visible, false otherwise.
+ */
+ void _setLurking(bool lurking);
+
/// @}
- virtual Glib::ustring _getTip(unsigned /*state*/) { return ""; }
- virtual Glib::ustring _getDragTip(GdkEventMotion */*event*/) { return ""; }
- virtual bool _hasDragTips() { return false; }
+ virtual Glib::ustring _getTip(unsigned /*state*/) const { return ""; }
+
+ virtual Glib::ustring _getDragTip(GdkEventMotion */*event*/) const { return ""; }
+
+ virtual bool _hasDragTips() const { return false; }
+
- SPDesktop *const _desktop; ///< The desktop this control point resides on.
SPCanvasItem * _canvas_item; ///< Visual representation of the control point.
- ColorSet *_cset; ///< Colors used to represent the point
+
+ ColorSet const &_cset; ///< Colors used to represent the point
+
State _state;
static Geom::Point const &_last_click_event_point() { return _drag_event_origin; }
+
static Geom::Point const &_last_drag_origin() { return _drag_origin; }
+
static bool _is_drag_cancelled(GdkEventMotion *event);
+
+ /** Events which should be captured when a handle is being dragged. */
static int const _grab_event_mask;
private:
+
ControlPoint(ControlPoint const &other);
+
void operator=(ControlPoint const &other);
static int _event_handler(SPCanvasItem *item, GdkEvent *event, ControlPoint *point);
+
static void _setMouseover(ControlPoint *, unsigned state);
+
static void _clearMouseover();
+
bool _updateTip(unsigned state);
+
bool _updateDragTip(GdkEventMotion *event);
+
void _setDefaultColors();
+
void _commonInit();
Geom::Point _position; ///< Current position in desktop coordinates
+
gulong _event_handler_connection;
+ bool _lurking;
+
+ static ColorSet _default_color_set;
+
+ /** Stores the window point over which the cursor was during the last mouse button press. */
static Geom::Point _drag_event_origin;
+
+ /** Stores the desktop point from which the last drag was initiated. */
static Geom::Point _drag_origin;
+
static bool _event_grab;
+
static bool _drag_initiated;
};
-extern ControlPoint::ColorSet invisible_cset;
-
} // namespace UI
} // namespace Inkscape
diff --git a/src/ui/tool/curve-drag-point.cpp b/src/ui/tool/curve-drag-point.cpp
index 8754681df..4ca736f80 100644
--- a/src/ui/tool/curve-drag-point.cpp
+++ b/src/ui/tool/curve-drag-point.cpp
@@ -1,8 +1,6 @@
-/** @file
- * Control point that is dragged during path drag
- */
/* Authors:
* Krzysztof Kosiński <tweenk.pl@gmail.com>
+ * Jon A. Cruz <jon@joncruz.org>
*
* Copyright (C) 2009 Authors
* Released under GNU GPL, read the file 'COPYING' for more information
@@ -21,31 +19,20 @@
namespace Inkscape {
namespace UI {
-/**
- * @class CurveDragPoint
- * An invisible point used to drag curves. This point is used by PathManipulator to allow editing
- * of path segments by dragging them. It is defined in a separate file so that the node tool
- * can check if the mouseovered control point is a curve drag point and update the cursor
- * accordingly, without the need to drag in the full PathManipulator header.
- */
-
-// This point should be invisible to the user - use the invisible_cset from control-point.h
-// TODO make some methods from path-manipulator.cpp public so that this point doesn't have
-// to be declared as a friend
bool CurveDragPoint::_drags_stroke = false;
bool CurveDragPoint::_segment_was_degenerate = false;
-CurveDragPoint::CurveDragPoint(PathManipulator &pm)
- : ControlPoint(pm._multi_path_manipulator._path_data.node_data.desktop, Geom::Point(),
- SP_ANCHOR_CENTER, SP_CTRL_SHAPE_CIRCLE, 1.0, &invisible_cset,
- pm._multi_path_manipulator._path_data.dragpoint_group)
- , _pm(pm)
+CurveDragPoint::CurveDragPoint(PathManipulator &pm) :
+ ControlPoint(pm._multi_path_manipulator._path_data.node_data.desktop, Geom::Point(), SP_ANCHOR_CENTER,
+ CTRL_TYPE_INVISIPOINT,
+ invisible_cset, pm._multi_path_manipulator._path_data.dragpoint_group),
+ _pm(pm)
{
setVisible(false);
}
-bool CurveDragPoint::_eventHandler(SPEventContext *event_context, GdkEvent *event)
+bool CurveDragPoint::_eventHandler(Inkscape::UI::Tools::ToolBase *event_context, GdkEvent *event)
{
// do not process any events when the manipulator is empty
if (_pm.empty()) {
@@ -170,7 +157,7 @@ void CurveDragPoint::_insertNode(bool take_selection)
_pm._commit(_("Add node"));
}
-Glib::ustring CurveDragPoint::_getTip(unsigned state)
+Glib::ustring CurveDragPoint::_getTip(unsigned state) const
{
if (_pm.empty()) return "";
if (!first || !first.next()) return "";
diff --git a/src/ui/tool/curve-drag-point.h b/src/ui/tool/curve-drag-point.h
index 939047634..ea83978e0 100644
--- a/src/ui/tool/curve-drag-point.h
+++ b/src/ui/tool/curve-drag-point.h
@@ -1,8 +1,6 @@
-/** @file
- * Control point that is dragged during path drag
- */
/* Authors:
* Krzysztof Kosiński <tweenk.pl@gmail.com>
+ * Jon A. Cruz <jon@joncruz.org>
*
* Copyright (C) 2009 Authors
* Released under GNU GPL, read the file 'COPYING' for more information
@@ -21,15 +19,27 @@ namespace UI {
class PathManipulator;
struct PathSharedData;
+// This point should be invisible to the user - use the invisible_cset from control-point.h
+// TODO make some methods from path-manipulator.cpp public so that this point doesn't have
+// to be declared as a friend
+/**
+ * An invisible point used to drag curves. This point is used by PathManipulator to allow editing
+ * of path segments by dragging them. It is defined in a separate file so that the node tool
+ * can check if the mouseovered control point is a curve drag point and update the cursor
+ * accordingly, without the need to drag in the full PathManipulator header.
+ */
class CurveDragPoint : public ControlPoint {
public:
+
CurveDragPoint(PathManipulator &pm);
void setSize(double sz) { _setSize(sz); }
void setTimeValue(double t) { _t = t; }
void setIterator(NodeList::iterator i) { first = i; }
- virtual bool _eventHandler(SPEventContext *event_context, GdkEvent *event);
+ virtual bool _eventHandler(Inkscape::UI::Tools::ToolBase *event_context, GdkEvent *event);
+
protected:
- virtual Glib::ustring _getTip(unsigned state);
+
+ virtual Glib::ustring _getTip(unsigned state) const;
virtual void dragged(Geom::Point &, GdkEventMotion *);
virtual bool grabbed(GdkEventMotion *);
virtual void ungrabbed(GdkEventButton *);
@@ -37,10 +47,13 @@ protected:
virtual bool doubleclicked(GdkEventButton *);
private:
+
void _insertNode(bool take_selection);
+
double _t;
PathManipulator &_pm;
NodeList::iterator first;
+
static bool _drags_stroke;
static bool _segment_was_degenerate;
static Geom::Point _stroke_drag_origin;
diff --git a/src/ui/tool/event-utils.cpp b/src/ui/tool/event-utils.cpp
index 6b067a3ce..079275d63 100644
--- a/src/ui/tool/event-utils.cpp
+++ b/src/ui/tool/event-utils.cpp
@@ -14,10 +14,6 @@
#include "display/sp-canvas.h"
#include "ui/tool/event-utils.h"
-#if !GTK_CHECK_VERSION(2,22,0)
-#include "compat-key-syms.h"
-#endif
-
namespace Inkscape {
namespace UI {
@@ -59,6 +55,9 @@ unsigned combine_key_events(guint keyval, gint mask)
unsigned combine_motion_events(SPCanvas *canvas, GdkEventMotion &event, gint mask)
{
+ if (canvas == NULL) {
+ return false;
+ }
GdkEvent *event_next;
gint i = 0;
event.x -= canvas->x0;
@@ -66,7 +65,7 @@ unsigned combine_motion_events(SPCanvas *canvas, GdkEventMotion &event, gint mas
event_next = gdk_event_get();
// while the next event is also a motion notify
- while (event_next && event_next->type == GDK_MOTION_NOTIFY
+ while (event_next && (event_next->type == GDK_MOTION_NOTIFY)
&& (!mask || event_next->motion.state & mask))
{
if (event_next->motion.device == event.device) {
@@ -80,11 +79,7 @@ unsigned combine_motion_events(SPCanvas *canvas, GdkEventMotion &event, gint mas
event.x_root = next.x_root;
event.y_root = next.y_root;
if (event.axes && next.axes) {
-#if GTK_CHECK_VERSION(2,22,0)
memcpy(event.axes, next.axes, gdk_device_get_n_axes(event.device));
-#else
- memcpy(event.axes, next.axes, event.device->num_axes);
-#endif
}
}
@@ -94,8 +89,9 @@ unsigned combine_motion_events(SPCanvas *canvas, GdkEventMotion &event, gint mas
i++;
}
// otherwise, put it back onto the queue
- if (event_next)
+ if (event_next) {
gdk_event_put(event_next);
+ }
event.x += canvas->x0;
event.y += canvas->y0;
diff --git a/src/ui/tool/manipulator.h b/src/ui/tool/manipulator.h
index ffed370d3..07e01a656 100644
--- a/src/ui/tool/manipulator.h
+++ b/src/ui/tool/manipulator.h
@@ -18,7 +18,7 @@
#include <glib.h>
#include <gdk/gdk.h>
#include <boost/shared_ptr.hpp>
-#include "event-context.h"
+#include "ui/tools/tool-base.h"
class SPDesktop;
namespace Inkscape {
@@ -40,7 +40,7 @@ public:
virtual ~Manipulator() {}
/// Handle input event. Returns true if handled.
- virtual bool event(SPEventContext *, GdkEvent *)=0;
+ virtual bool event(Inkscape::UI::Tools::ToolBase *, GdkEvent *)=0;
SPDesktop *const _desktop;
};
@@ -145,7 +145,7 @@ public:
}
}
- virtual bool event(SPEventContext *event_context, GdkEvent *event) {
+ virtual bool event(Inkscape::UI::Tools::ToolBase *event_context, GdkEvent *event) {
for (typename MapType::iterator i = _mmap.begin(); i != _mmap.end(); ++i) {
if ((*i).second->event(event_context, event)) return true;
}
diff --git a/src/ui/tool/modifier-tracker.cpp b/src/ui/tool/modifier-tracker.cpp
index 601c8334f..cc4e4d0b2 100644
--- a/src/ui/tool/modifier-tracker.cpp
+++ b/src/ui/tool/modifier-tracker.cpp
@@ -14,10 +14,6 @@
#include "ui/tool/modifier-tracker.h"
#include <gtk/gtk.h>
-#if !GTK_CHECK_VERSION(2,22,0)
-#include "compat-key-syms.h"
-#endif
-
namespace Inkscape {
namespace UI {
diff --git a/src/ui/tool/multi-path-manipulator.cpp b/src/ui/tool/multi-path-manipulator.cpp
index 3fc202b69..65987ad52 100644
--- a/src/ui/tool/multi-path-manipulator.cpp
+++ b/src/ui/tool/multi-path-manipulator.cpp
@@ -11,9 +11,8 @@
*/
#include <boost/shared_ptr.hpp>
-#include <glib.h>
-#include <glibmm/i18n.h>
#include "node.h"
+#include <glibmm/i18n.h>
#include "desktop.h"
#include "desktop-handles.h"
#include "document.h"
@@ -31,21 +30,6 @@
#include <gdk/gdkkeysyms.h>
-#if !GTK_CHECK_VERSION(2,22,0)
-#include "compat-key-syms.h"
-#endif
-
-#ifdef USE_GNU_HASHES
-namespace __gnu_cxx {
-template<>
-struct hash<Inkscape::UI::NodeList::iterator> {
- size_t operator()(Inkscape::UI::NodeList::iterator const &n) const {
- return reinterpret_cast<size_t>(n.ptr());
- }
-};
-} // namespace __gnu_cxx
-#endif // USE_GNU_HASHES
-
namespace Inkscape {
namespace UI {
@@ -69,7 +53,6 @@ typedef std::pair<double, IterPair> DistanceMapItem;
void find_join_iterators(ControlPointSelection &sel, IterPairList &pairs)
{
IterSet join_iters;
- DistanceMap dists;
// find all endnodes in selection
for (ControlPointSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
@@ -230,10 +213,12 @@ void MultiPathManipulator::shiftSelection(int dir)
SubpathList::iterator last_j;
NodeList::iterator last_k;
bool anything_found = false;
+ bool anynode_found = false;
for (MapType::iterator i = _mmap.begin(); i != _mmap.end(); ++i) {
SubpathList &sp = i->second->subpathList();
for (SubpathList::iterator j = sp.begin(); j != sp.end(); ++j) {
+ anynode_found = true;
for (NodeList::iterator k = (*j)->begin(); k != (*j)->end(); ++k) {
if (k->selected()) {
last_i = i;
@@ -255,10 +240,12 @@ void MultiPathManipulator::shiftSelection(int dir)
if (!anything_found) {
// select first / last node
// this should never fail because there must be at least 1 non-empty manipulator
- if (dir == 1) {
+ if (anynode_found) {
+ if (dir == 1) {
_selection.insert((*_mmap.begin()->second->subpathList().begin())->begin().ptr());
- } else {
+ } else {
_selection.insert((--(*--(--_mmap.end())->second->subpathList().end())->end()).ptr());
+ }
}
return;
}
@@ -341,23 +328,27 @@ void MultiPathManipulator::setSegmentType(SegmentType type)
void MultiPathManipulator::insertNodes()
{
+ if (_selection.empty()) return;
invokeForAll(&PathManipulator::insertNodes);
_done(_("Add nodes"));
}
void MultiPathManipulator::insertNodesAtExtrema(ExtremumType extremum)
{
+ if (_selection.empty()) return;
invokeForAll(&PathManipulator::insertNodeAtExtremum, extremum);
_done(_("Add extremum nodes"));
}
void MultiPathManipulator::duplicateNodes()
{
+ if (_selection.empty()) return;
invokeForAll(&PathManipulator::duplicateNodes);
_done(_("Duplicate nodes"));
}
void MultiPathManipulator::joinNodes()
{
+ if (_selection.empty()) return;
invokeForAll(&PathManipulator::hideDragPoint);
// Node join has two parts. In the first one we join two subpaths by fusing endpoints
// into one. In the second we fuse nodes in each subpath.
@@ -435,6 +426,7 @@ void MultiPathManipulator::deleteNodes(bool keep_shape)
/** Join selected endpoints to create segments. */
void MultiPathManipulator::joinSegments()
{
+ if (_selection.empty()) return;
IterPairList joins;
find_join_iterators(_selection, joins);
@@ -467,6 +459,7 @@ void MultiPathManipulator::deleteSegments()
void MultiPathManipulator::alignNodes(Geom::Dim2 d)
{
+ if (_selection.empty()) return;
_selection.align(d);
if (d == Geom::X) {
_done("Align nodes to a horizontal line");
@@ -477,6 +470,7 @@ void MultiPathManipulator::alignNodes(Geom::Dim2 d)
void MultiPathManipulator::distributeNodes(Geom::Dim2 d)
{
+ if (_selection.empty()) return;
_selection.distribute(d);
if (d == Geom::X) {
_done("Distrubute nodes horizontally");
@@ -498,6 +492,7 @@ void MultiPathManipulator::reverseSubpaths()
void MultiPathManipulator::move(Geom::Point const &delta)
{
+ if (_selection.empty()) return;
_selection.transform(Geom::Translate(delta));
_done("Move nodes");
}
@@ -554,7 +549,12 @@ void MultiPathManipulator::updateOutlineColors()
//}
}
-bool MultiPathManipulator::event(SPEventContext *event_context, GdkEvent *event)
+void MultiPathManipulator::updateHandles()
+{
+ invokeForAll(&PathManipulator::updateHandles);
+}
+
+bool MultiPathManipulator::event(Inkscape::UI::Tools::ToolBase *event_context, GdkEvent *event)
{
_tracker.event(event);
guint key = 0;
@@ -671,6 +671,9 @@ bool MultiPathManipulator::event(SPEventContext *event_context, GdkEvent *event)
// b) ctrl+del preserves shape (del_preserves_shape is false), and control is pressed
// Hence xor
deleteNodes(del_preserves_shape ^ held_control(event->key));
+
+ // Delete any selected gradient nodes as well
+ event_context->deleteSelectedDrag(held_control(event->key));
}
return true;
case GDK_KEY_c:
@@ -723,7 +726,7 @@ bool MultiPathManipulator::event(SPEventContext *event_context, GdkEvent *event)
case GDK_KEY_u:
case GDK_KEY_U:
if (held_only_shift(event->key)) {
- // Shift+L - make segments curves
+ // Shift+U - make segments curves
setSegmentType(SEGMENT_CUBIC_BEZIER);
return true;
}
diff --git a/src/ui/tool/multi-path-manipulator.h b/src/ui/tool/multi-path-manipulator.h
index 2c05a1252..1328372c6 100644
--- a/src/ui/tool/multi-path-manipulator.h
+++ b/src/ui/tool/multi-path-manipulator.h
@@ -36,7 +36,7 @@ class MultiPathManipulator : public PointManipulator {
public:
MultiPathManipulator(PathSharedData &data, sigc::connection &chg);
virtual ~MultiPathManipulator();
- virtual bool event(SPEventContext *, GdkEvent *event);
+ virtual bool event(Inkscape::UI::Tools::ToolBase *, GdkEvent *event);
bool empty() { return _mmap.empty(); }
unsigned size() { return _mmap.empty(); }
@@ -70,6 +70,7 @@ public:
void setLiveOutline(bool set);
void setLiveObjects(bool set);
void updateOutlineColors();
+ void updateHandles();
sigc::signal<void> signal_coords_changed; /// Emitted whenever the coordinates
/// shown in the status bar need updating
diff --git a/src/ui/tool/node-tool.cpp b/src/ui/tool/node-tool.cpp
deleted file mode 100644
index 7127ae968..000000000
--- a/src/ui/tool/node-tool.cpp
+++ /dev/null
@@ -1,695 +0,0 @@
-/**
- * @file
- * New node tool - implementation.
- */
-/* Authors:
- * Krzysztof Kosiński <tweenk@gmail.com>
- * Abhishek Sharma
- *
- * Copyright (C) 2009 Authors
- * Released under GNU GPL, read the file 'COPYING' for more information
- */
-
-#include "curve-drag-point.h"
-#include <glib/gi18n.h>
-#include "desktop.h"
-#include "desktop-handles.h"
-#include "display/sp-canvas-group.h"
-#include "display/canvas-bpath.h"
-#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!
-#include "sp-clippath.h"
-#include "sp-item-group.h"
-#include "sp-mask.h"
-#include "sp-object-group.h"
-#include "sp-path.h"
-#include "sp-text.h"
-#include "ui/tool/node-tool.h"
-#include "ui/tool/control-point-selection.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"
-
-#include <gdk/gdkkeysyms.h>
-
-#if !GTK_CHECK_VERSION(2,22,0)
-#include "compat-key-syms.h"
-#endif
-
-/** @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 Functionality that resides in weird places
- * @par
- *
- * This list is probably incomplete.
- * - Curve dragging: CurveDragPoint, controlled by PathManipulator
- * - Single handle shortcuts: MultiPathManipulator::event(), ModifierTracker
- * - Linear and spatial grow: Node, spatial grow routed to ControlPointSelection
- * - Committing handle actions performed with the mouse: PathManipulator
- * - Sculpting: ControlPointSelection
- *
- * @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);
-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<SPCanvasGroup*>(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->_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();
- new (&nt->_multipath) MultiPathPtr();
- new (&nt->_selector) SelectorPtr();
- new (&nt->_path_data) PathSharedDataPtr();
- new (&nt->_shape_editors) ShapeEditors();
-}
-
-void ink_node_tool_dispose(GObject *object)
-{
- InkNodeTool *nt = INK_NODE_TOOL(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();
- nt->_selector.~SelectorPtr();
- nt->_shape_editors.~ShapeEditors();
-
- 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->_selection_modified_connection.~connection();
- nt->_mouseover_changed_connection.~connection();
-
- if (nt->_node_message_context) {
- delete nt->_node_message_context;
- }
-
- 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->_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->_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;
- nt->_last_over = NULL;
-
- // 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, "live_outline");
- sp_event_context_read(nt, "live_objects");
- 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->show_handles = value->getBool(true);
- nt->_multipath->showHandles(nt->show_handles);
- } else if (entry_name == "show_outline") {
- nt->show_outline = value->getBool();
- nt->_multipath->showOutline(nt->show_outline);
- } else if (entry_name == "live_outline") {
- nt->live_outline = value->getBool();
- nt->_multipath->setLiveOutline(nt->live_outline);
- } else if (entry_name == "live_objects") {
- nt->live_objects = value->getBool();
- nt->_multipath->setLiveObjects(nt->live_objects);
- } 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);
- }
-}
-
-/** Recursively collect ShapeRecords */
-void gather_items(InkNodeTool *nt, SPItem *base, SPObject *obj, Inkscape::UI::ShapeRole role,
- std::set<Inkscape::UI::ShapeRecord> &s)
-{
- using namespace Inkscape::UI;
- if (!obj) return;
-
- //XML Tree being used directly here while it shouldn't be.
- if (SP_IS_PATH(obj) && obj->getRepr()->attribute("inkscape:original-d") != NULL) {
- ShapeRecord r;
- r.item = static_cast<SPItem*>(obj);
- r.edit_transform = Geom::identity(); // TODO wrong?
- r.role = role;
- 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);
- }
- } else if (SP_IS_ITEM(obj)) {
- SPItem *item = static_cast<SPItem*>(obj);
- ShapeRecord r;
- r.item = item;
- // TODO add support for objectBoundingBox
- r.edit_transform = base ? base->i2doc_affine() : Geom::identity();
- r.role = role;
- 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);
- }
- }
- }
-}
-
-void ink_node_tool_selection_changed(InkNodeTool *nt, Inkscape::Selection *sel)
-{
- using namespace Inkscape::UI;
-
- std::set<ShapeRecord> shapes;
-
- GSList const *ilist = sel->itemList();
-
- for (GSList *i = const_cast<GSList*>(ilist); i; i = i->next) {
- SPObject *obj = static_cast<SPObject*>(i->data);
- if (SP_IS_ITEM(obj)) {
- gather_items(nt, NULL, static_cast<SPItem*>(obj), SHAPE_ROLE_NORMAL, shapes);
- }
- }
-
- // use multiple ShapeEditors for now, to allow editing many shapes at once
- // needs to be rethought
- for (ShapeEditors::iterator i = nt->_shape_editors.begin();
- i != nt->_shape_editors.end(); )
- {
- ShapeRecord s;
- s.item = i->first;
- if (shapes.find(s) == shapes.end()) {
- nt->_shape_editors.erase(i++);
- } else {
- ++i;
- }
- }
-
- for (std::set<ShapeRecord>::iterator i = shapes.begin(); i != shapes.end(); ++i) {
- ShapeRecord const &r = *i;
- if ((SP_IS_SHAPE(r.item) || SP_IS_TEXT(r.item)) &&
- nt->_shape_editors.find(r.item) == nt->_shape_editors.end())
- {
- ShapeEditor *si = new ShapeEditor(nt->desktop);
- si->set_item(r.item, SH_KNOTHOLDER);
- nt->_shape_editors.insert(const_cast<SPItem*&>(r.item), si);
- }
- }
-
- nt->_multipath->setItems(shapes);
- 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<InkNodeTool*>(event_context);
- static Inkscape::Preferences *prefs = Inkscape::Preferences::get();
-
- if (nt->_multipath->event(event_context, event)) return true;
- if (nt->_selector->event(event_context, event)) return true;
- if (nt->_selected_nodes->event(event_context, event)) return true;
-
- switch (event->type)
- {
- case GDK_MOTION_NOTIFY: {
- combine_motion_events(desktop->canvas, event->motion, 0);
- SPItem *over_item = sp_event_context_find_item (desktop, event_point(event->button),
- FALSE, TRUE);
- if (over_item != nt->_last_over) {
- nt->_last_over = over_item;
- ink_node_tool_update_tip(nt, event);
- }
-
- // create pathflash outline
- if (prefs->getBool("/tools/nodes/pathflash_enabled")) {
- 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;
- nt->flashed_item = NULL;
- }
- if (!SP_IS_SHAPE(over_item)) break; // for now, handle only shapes
-
- nt->flashed_item = over_item;
- SPCurve *c = SP_SHAPE(over_item)->getCurveBeforeLPE();
- if (!c) break; // break out when curve doesn't exist
- c->transform(over_item->i2dt_affine());
- 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();
- }
- } break; // do not return true, because we need to pass this event to the parent context
- // otherwise some features cease to work
-
- case GDK_KEY_PRESS:
- switch (get_group0_keyval(&event->key))
- {
- case GDK_KEY_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_KEY_a:
- case GDK_KEY_A:
- if (held_control(event->key) && held_alt(event->key)) {
- nt->_selected_nodes->selectAll();
- // Ctrl+A is handled in selection-chemistry.cpp via verb
- ink_node_tool_update_tip(nt, event);
- return TRUE;
- }
- break;
- case GDK_KEY_h:
- case GDK_KEY_H:
- if (held_only_control(event->key)) {
- Inkscape::Preferences *prefs = Inkscape::Preferences::get();
- prefs->setBool("/tools/nodes/show_handles", !nt->show_handles);
- 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)) {
- if (nt->_last_over) {
- nt->_node_message_context->set(Inkscape::NORMAL_MESSAGE,
- C_("Node tool tip", "<b>Shift</b>: drag to add nodes to the selection, "
- "click to toggle object selection"));
- } else {
- nt->_node_message_context->set(Inkscape::NORMAL_MESSAGE,
- C_("Node tool tip", "<b>Shift</b>: drag to add nodes to the selection"));
- }
- return;
- }
- }
- unsigned sz = nt->_selected_nodes->size();
- unsigned total = nt->_selected_nodes->allPoints().size();
- if (sz != 0) {
- char *nodestring = g_strdup_printf(
- ngettext("<b>%u of %u</b> node selected.", "<b>%u of %u</b> nodes selected.", total),
- sz, total);
- if (nt->_last_over) {
- // TRANSLATORS: The %s below is where the "%u of %u nodes selected" sentence gets put
- char *dyntip = g_strdup_printf(C_("Node tool tip",
- "%s Drag to select nodes, click to edit only this object (more: Shift)"),
- nodestring);
- nt->_node_message_context->set(Inkscape::NORMAL_MESSAGE, dyntip);
- g_free(dyntip);
- } else {
- char *dyntip = g_strdup_printf(C_("Node tool tip",
- "%s Drag to select nodes, click clear the selection"),
- nodestring);
- nt->_node_message_context->set(Inkscape::NORMAL_MESSAGE, dyntip);
- g_free(dyntip);
- }
- g_free(nodestring);
- } else if (!nt->_multipath->empty()) {
- if (nt->_last_over) {
- nt->_node_message_context->set(Inkscape::NORMAL_MESSAGE, C_("Node tool tip",
- "Drag to select nodes, click to edit only this object"));
- } else {
- nt->_node_message_context->set(Inkscape::NORMAL_MESSAGE, C_("Node tool tip",
- "Drag to select nodes, click to clear the selection"));
- }
- } else {
- if (nt->_last_over) {
- nt->_node_message_context->set(Inkscape::NORMAL_MESSAGE, C_("Node tool tip",
- "Drag to select objects to edit, click to edit this object (more: Shift)"));
- } else {
- nt->_node_message_context->set(Inkscape::NORMAL_MESSAGE, C_("Node tool tip",
- "Drag to select objects to edit"));
- }
- }
-}
-
-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_desktop_document(nt->desktop)->getItemsInBox(nt->desktop->dkey, sel);
- selection->setList(items);
- g_slist_free(items);
- } else {
- if (!held_shift(*event)) nt->_selected_nodes->clear();
- nt->_selected_nodes->selectArea(sel);
- }
-}
-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 there are nodes selected, the first click should deselect the nodes
- // and the second should deselect the items
- if (!state_held_shift(event->state)) {
- if (nt->_selected_nodes->empty()) {
- selection->clear();
- } else {
- nt->_selected_nodes->clear();
- }
- }
- } else {
- 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<CurveDragPoint*>(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:fileencoding=utf-8:textwidth=99 :
diff --git a/src/ui/tool/node-tool.h b/src/ui/tool/node-tool.h
deleted file mode 100644
index 6f7ab01d4..000000000
--- a/src/ui/tool/node-tool.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/** @file
- * @brief New node tool with support for multiple path editing
- */
-/* Authors:
- * Krzysztof Kosiński <tweenk@gmail.com>
- *
- * Copyright (C) 2009 Authors
- * Released under GNU GPL, read the file 'COPYING' for more information
- */
-
-#ifndef SEEN_UI_TOOL_NODE_TOOL_H
-#define SEEN_UI_TOOL_NODE_TOOL_H
-
-#include <memory>
-#include <boost/ptr_container/ptr_map.hpp>
-#include <glib.h>
-#include <stddef.h>
-#include <sigc++/sigc++.h>
-#include "event-context.h"
-#include "ui/tool/node-types.h"
-
-#define INK_TYPE_NODE_TOOL (ink_node_tool_get_type ())
-#define INK_NODE_TOOL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), INK_TYPE_NODE_TOOL, InkNodeTool))
-#define INK_NODE_TOOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), INK_TYPE_NODE_TOOL, InkNodeToolClass))
-#define INK_IS_NODE_TOOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), INK_TYPE_NODE_TOOL))
-#define INK_IS_NODE_TOOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), INK_TYPE_NODE_TOOL))
-
-class InkNodeTool;
-class InkNodeToolClass;
-
-namespace Inkscape {
-
-namespace Display {
-class TemporaryItem;
-} // namespace Display
-namespace UI {
-class MultiPathManipulator;
-class ControlPointSelection;
-class Selector;
-struct PathSharedData;
-} // namespace UI
-} // namespace Inkscape
-
-typedef std::auto_ptr<Inkscape::UI::MultiPathManipulator> MultiPathPtr;
-typedef std::auto_ptr<Inkscape::UI::ControlPointSelection> CSelPtr;
-typedef std::auto_ptr<Inkscape::UI::Selector> SelectorPtr;
-typedef std::auto_ptr<Inkscape::UI::PathSharedData> PathSharedDataPtr;
-typedef boost::ptr_map<SPItem*, ShapeEditor> ShapeEditors;
-
-struct InkNodeTool : public SPEventContext
-{
- sigc::connection _selection_changed_connection;
- sigc::connection _mouseover_changed_connection;
- sigc::connection _selection_modified_connection;
- Inkscape::MessageContext *_node_message_context;
- SPItem *flashed_item;
- Inkscape::Display::TemporaryItem *flash_tempitem;
- CSelPtr _selected_nodes;
- MultiPathPtr _multipath;
- SelectorPtr _selector;
- PathSharedDataPtr _path_data;
- SPCanvasGroup *_transform_handle_group;
- SPItem *_last_over;
- ShapeEditors _shape_editors;
-
- unsigned cursor_drag : 1;
- unsigned show_handles : 1;
- unsigned show_outline : 1;
- unsigned live_outline : 1;
- unsigned live_objects : 1;
- unsigned show_path_direction : 1;
- unsigned show_transform_handles : 1;
- unsigned single_node_transform_handles : 1;
- unsigned edit_clipping_paths : 1;
- unsigned edit_masks : 1;
-};
-
-struct InkNodeToolClass {
- SPEventContextClass parent_class;
-};
-
-GType ink_node_tool_get_type (void);
-
-#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:fileencoding=utf-8:textwidth=99 :
diff --git a/src/ui/tool/node.cpp b/src/ui/tool/node.cpp
index ad48b523b..fbbc4be64 100644
--- a/src/ui/tool/node.cpp
+++ b/src/ui/tool/node.cpp
@@ -1,7 +1,3 @@
-/**
- * @file
- * Editable node - implementation.
- */
/* Authors:
* Krzysztof Kosiński <tweenk.pl@gmail.com>
* Jon A. Cruz <jon@joncruz.org>
@@ -26,33 +22,57 @@
#include "preferences.h"
#include "snap.h"
#include "snap-preferences.h"
-#include "sp-metrics.h"
#include "sp-namedview.h"
+#include "ui/control-manager.h"
#include "ui/tool/control-point-selection.h"
#include "ui/tool/event-utils.h"
#include "ui/tool/node.h"
#include "ui/tool/path-manipulator.h"
#include <gdk/gdkkeysyms.h>
-#if !GTK_CHECK_VERSION(2,22,0)
-#include "compat-key-syms.h"
-#endif
+namespace {
+
+Inkscape::ControlType nodeTypeToCtrlType(Inkscape::UI::NodeType type)
+{
+ Inkscape::ControlType result = Inkscape::CTRL_TYPE_NODE_CUSP;
+ switch(type) {
+ case Inkscape::UI::NODE_SMOOTH:
+ result = Inkscape::CTRL_TYPE_NODE_SMOOTH;
+ break;
+ case Inkscape::UI::NODE_AUTO:
+ result = Inkscape::CTRL_TYPE_NODE_AUTO;
+ break;
+ case Inkscape::UI::NODE_SYMMETRIC:
+ result = Inkscape::CTRL_TYPE_NODE_SYMETRICAL;
+ break;
+ case Inkscape::UI::NODE_CUSP:
+ default:
+ result = Inkscape::CTRL_TYPE_NODE_CUSP;
+ break;
+ }
+ return result;
+}
+
+} // namespace
namespace Inkscape {
namespace UI {
-static SelectableControlPoint::ColorSet node_colors = {
- {
- {0xbfbfbf00, 0x000000ff}, // normal fill, stroke
- {0xff000000, 0x000000ff}, // mouseover fill, stroke
- {0xff000000, 0x000000ff} // clicked fill, stroke
- },
+ControlPoint::ColorSet Node::node_colors = {
+ {0xbfbfbf00, 0x000000ff}, // normal fill, stroke
+ {0xff000000, 0x000000ff}, // mouseover fill, stroke
+ {0xff000000, 0x000000ff}, // clicked fill, stroke
+ //
{0x0000ffff, 0x000000ff}, // normal fill, stroke when selected
{0xff000000, 0x000000ff}, // mouseover fill, stroke when selected
{0xff000000, 0x000000ff} // clicked fill, stroke when selected
};
-static ControlPoint::ColorSet handle_colors = {
+ControlPoint::ColorSet Handle::_handle_colors = {
+ {0xffffffff, 0x000000ff}, // normal fill, stroke
+ {0xff000000, 0x000000ff}, // mouseover fill, stroke
+ {0xff000000, 0x000000ff}, // clicked fill, stroke
+ //
{0xffffffff, 0x000000ff}, // normal fill, stroke
{0xff000000, 0x000000ff}, // mouseover fill, stroke
{0xff000000, 0x000000ff} // clicked fill, stroke
@@ -75,30 +95,27 @@ static Geom::Point direction(Geom::Point const &first, Geom::Point const &second
return Geom::unit_vector(second - first);
}
-/**
- * Control point of a cubic Bezier curve in a path.
- *
- * Handle keeps the node type invariant only for the opposite handle of the same node.
- * Keeping the invariant on node moves is left to the %Node class.
- */
Geom::Point Handle::_saved_other_pos(0, 0);
+
double Handle::_saved_length = 0.0;
+
bool Handle::_drag_out = false;
-Handle::Handle(NodeSharedData const &data, Geom::Point const &initial_pos, Node *parent)
- : ControlPoint(data.desktop, initial_pos, SP_ANCHOR_CENTER, SP_CTRL_SHAPE_CIRCLE, 7.0,
- &handle_colors, data.handle_group)
- , _parent(parent)
- , _degenerate(true)
+Handle::Handle(NodeSharedData const &data, Geom::Point const &initial_pos, Node *parent) :
+ ControlPoint(data.desktop, initial_pos, SP_ANCHOR_CENTER,
+ CTRL_TYPE_ADJ_HANDLE,
+ _handle_colors, data.handle_group),
+ _parent(parent),
+ _handle_line(ControlManager::getManager().createControlLine(data.handle_line_group)),
+ _degenerate(true)
{
- _cset = &handle_colors;
- _handle_line = SP_CTRLLINE(sp_canvas_item_new(data.handle_line_group, SP_TYPE_CTRLLINE, NULL));
setVisible(false);
}
+
Handle::~Handle()
{
//sp_canvas_item_hide(_handle_line);
- gtk_object_destroy(_handle_line);
+ sp_canvas_item_destroy(_handle_line);
}
void Handle::setVisible(bool v)
@@ -233,7 +250,7 @@ char const *Handle::handle_type_to_localized_string(NodeType type)
}
}
-bool Handle::_eventHandler(SPEventContext *event_context, GdkEvent *event)
+bool Handle::_eventHandler(Inkscape::UI::Tools::ToolBase *event_context, GdkEvent *event)
{
switch (event->type)
{
@@ -275,7 +292,7 @@ void Handle::dragged(Geom::Point &new_pos, GdkEventMotion *event)
Geom::Point parent_pos = _parent->position();
Geom::Point origin = _last_drag_origin();
SnapManager &sm = _desktop->namedview->snap_manager;
- bool snap = sm.someSnapperMightSnap();
+ bool snap = held_shift(*event) ? false : sm.someSnapperMightSnap();
boost::optional<Inkscape::Snapper::SnapConstraint> ctrl_constraint;
// with Alt, preserve length
@@ -387,10 +404,18 @@ bool Handle::clicked(GdkEventButton *event)
return true;
}
+Handle const *Handle::other() const
+{
+ return const_cast<Handle *>(this)->other();
+}
+
Handle *Handle::other()
{
- if (this == &_parent->_front) return &_parent->_back;
- return &_parent->_front;
+ if (this == &_parent->_front) {
+ return &_parent->_back;
+ } else {
+ return &_parent->_front;
+ }
}
static double snap_increment_degrees() {
@@ -399,7 +424,7 @@ static double snap_increment_degrees() {
return 180.0 / snaps;
}
-Glib::ustring Handle::_getTip(unsigned state)
+Glib::ustring Handle::_getTip(unsigned state) const
{
char const *more;
bool can_shift_rotate = _parent->type() == NODE_CUSP && !other()->isDegenerate();
@@ -457,16 +482,20 @@ Glib::ustring Handle::_getTip(unsigned state)
}
}
-Glib::ustring Handle::_getDragTip(GdkEventMotion */*event*/)
+Glib::ustring Handle::_getDragTip(GdkEventMotion */*event*/) const
{
Geom::Point dist = position() - _last_drag_origin();
// report angle in mathematical convention
double angle = Geom::angle_between(Geom::Point(-1,0), position() - _parent->position());
angle += M_PI; // angle is (-M_PI...M_PI] - offset by +pi and scale to 0...360
angle *= 360.0 / (2 * M_PI);
- GString *x = SP_PX_TO_METRIC_STRING(dist[Geom::X], _desktop->namedview->getDefaultMetric());
- GString *y = SP_PX_TO_METRIC_STRING(dist[Geom::Y], _desktop->namedview->getDefaultMetric());
- GString *len = SP_PX_TO_METRIC_STRING(length(), _desktop->namedview->getDefaultMetric());
+
+ Inkscape::Util::Quantity x_q = Inkscape::Util::Quantity(dist[Geom::X], "px");
+ Inkscape::Util::Quantity y_q = Inkscape::Util::Quantity(dist[Geom::Y], "px");
+ Inkscape::Util::Quantity len_q = Inkscape::Util::Quantity(length(), "px");
+ GString *x = g_string_new(x_q.string(_desktop->namedview->doc_units).c_str());
+ GString *y = g_string_new(y_q.string(_desktop->namedview->doc_units).c_str());
+ GString *len = g_string_new(len_q.string(_desktop->namedview->doc_units).c_str());
Glib::ustring ret = format_tip(C_("Path handle tip",
"Move handle by %s, %s; angle %.2f°, length %s"), x->str, y->str, angle, len->str);
g_string_free(x, TRUE);
@@ -475,34 +504,48 @@ Glib::ustring Handle::_getDragTip(GdkEventMotion */*event*/)
return ret;
}
-/**
- * Curve endpoint in an editable path.
- *
- * The method move() keeps node type invariants during translations.
- */
-Node::Node(NodeSharedData const &data, Geom::Point const &initial_pos)
- : SelectableControlPoint(data.desktop, initial_pos, SP_ANCHOR_CENTER,
- SP_CTRL_SHAPE_DIAMOND, 9.0, *data.selection, &node_colors, data.node_group)
- , _front(data, initial_pos, this)
- , _back(data, initial_pos, this)
- , _type(NODE_CUSP)
- , _handles_shown(false)
+Node::Node(NodeSharedData const &data, Geom::Point const &initial_pos) :
+ SelectableControlPoint(data.desktop, initial_pos, SP_ANCHOR_CENTER,
+ CTRL_TYPE_NODE_CUSP,
+ *data.selection,
+ node_colors, data.node_group),
+ _front(data, initial_pos, this),
+ _back(data, initial_pos, this),
+ _type(NODE_CUSP),
+ _handles_shown(false)
{
// NOTE we do not set type here, because the handles are still degenerate
}
+Node const *Node::_next() const
+{
+ return const_cast<Node*>(this)->_next();
+}
+
// NOTE: not using iterators won't make this much quicker because iterators can be 100% inlined.
Node *Node::_next()
{
NodeList::iterator n = NodeList::get_iterator(this).next();
- if (n) return n.ptr();
- return NULL;
+ if (n) {
+ return n.ptr();
+ } else {
+ return NULL;
+ }
+}
+
+Node const *Node::_prev() const
+{
+ return const_cast<Node *>(this)->_prev();
}
+
Node *Node::_prev()
{
NodeList::iterator p = NodeList::get_iterator(this).prev();
- if (p) return p.ptr();
- return NULL;
+ if (p) {
+ return p.ptr();
+ } else {
+ return NULL;
+ }
}
void Node::move(Geom::Point const &new_pos)
@@ -531,7 +574,7 @@ void Node::transform(Geom::Affine const &m)
_fixNeighbors(old_pos, position());
}
-Geom::Rect Node::bounds()
+Geom::Rect Node::bounds() const
{
Geom::Rect b(position(), position());
b.expandTo(_front.position());
@@ -608,17 +651,23 @@ void Node::_updateAutoHandles()
void Node::showHandles(bool v)
{
_handles_shown = v;
- if (!_front.isDegenerate()) _front.setVisible(v);
- if (!_back.isDegenerate()) _back.setVisible(v);
+ if (!_front.isDegenerate()) {
+ _front.setVisible(v);
+ }
+ if (!_back.isDegenerate()) {
+ _back.setVisible(v);
+ }
}
-/** Sets the node type and optionally restores the invariants associated with the given type.
- * @param type The type to set
- * @param update_handles Whether to restore invariants associated with the given type.
- * Passing false is useful e.g. wen initially creating the path,
- * and when making cusp nodes during some node algorithms.
- * Pass true when used in response to an UI node type button.
- */
+void Node::updateHandles()
+{
+ _handleControlStyling();
+
+ _front._handleControlStyling();
+ _back._handleControlStyling();
+}
+
+
void Node::setType(NodeType type, bool update_handles)
{
if (type == NODE_PICK_BEST) {
@@ -711,12 +760,10 @@ void Node::setType(NodeType type, bool update_handles)
}
}
_type = type;
- _setShape(_node_type_to_shape(type));
+ _setControlType(nodeTypeToCtrlType(_type));
updateState();
}
-/** Pick the best type for this node, based on the position of its handles.
- * This is what assigns types to nodes created using the pen tool. */
void Node::pickBestType()
{
_type = NODE_CUSP;
@@ -762,17 +809,15 @@ void Node::pickBestType()
}
}
} while (false);
- _setShape(_node_type_to_shape(_type));
+ _setControlType(nodeTypeToCtrlType(_type));
updateState();
}
-bool Node::isEndNode()
+bool Node::isEndNode() const
{
return !_prev() || !_next();
}
-/** Move the node to the bottom of its canvas group. Useful for node break, to ensure that
- * the selected nodes are above the unselected ones. */
void Node::sink()
{
sp_canvas_item_move_to_z(_canvas_item, 0);
@@ -789,8 +834,7 @@ NodeType Node::parse_nodetype(char x)
}
}
-/** Customized event handler to catch scroll events needed for selection grow/shrink. */
-bool Node::_eventHandler(SPEventContext *event_context, GdkEvent *event)
+bool Node::_eventHandler(Inkscape::UI::Tools::ToolBase *event_context, GdkEvent *event)
{
int dir = 0;
@@ -834,8 +878,6 @@ bool Node::_eventHandler(SPEventContext *event_context, GdkEvent *event)
return ControlPoint::_eventHandler(event_context, event);
}
-/** Select or deselect a node in this node's subpath based on its path distance from this node.
- * @param dir If negative, shrink selection by one node; if positive, grow by one node */
void Node::_linearGrow(int dir)
{
// Interestingly, we do not need any help from PathManipulator when doing linear grow.
@@ -948,29 +990,36 @@ void Node::_linearGrow(int dir)
void Node::_setState(State state)
{
// change node size to match type and selection state
- switch (_type) {
- case NODE_AUTO:
- case NODE_CUSP:
- if (selected()) _setSize(11);
- else _setSize(9);
- break;
- default:
- if(selected()) _setSize(9);
- else _setSize(7);
- break;
+ ControlManager &mgr = ControlManager::getManager();
+ mgr.setSelected(_canvas_item, selected());
+ switch (state) {
+ case STATE_NORMAL:
+ mgr.setActive(_canvas_item, false);
+ mgr.setPrelight(_canvas_item, false);
+ break;
+ case STATE_MOUSEOVER:
+ mgr.setActive(_canvas_item, false);
+ mgr.setPrelight(_canvas_item, true);
+ break;
+ case STATE_CLICKED:
+ mgr.setActive(_canvas_item, true);
+ mgr.setPrelight(_canvas_item, false);
+ break;
}
SelectableControlPoint::_setState(state);
}
bool Node::grabbed(GdkEventMotion *event)
{
- if (SelectableControlPoint::grabbed(event))
+ if (SelectableControlPoint::grabbed(event)) {
return true;
+ }
// Dragging out handles with Shift + drag on a node.
- if (!held_shift(*event)) return false;
+ if (!held_shift(*event)) {
+ return false;
+ }
- Handle *h;
Geom::Point evp = event_point(*event);
Geom::Point rel_evp = evp - _last_click_event_point();
@@ -991,8 +1040,11 @@ bool Node::grabbed(GdkEventMotion *event)
angle_prev = fabs(Geom::angle_between(rel_evp, prev_relpos));
has_degenerate = true;
}
- if (!has_degenerate) return false;
- h = angle_next < angle_prev ? &_front : &_back;
+ if (!has_degenerate) {
+ return false;
+ }
+
+ Handle *h = angle_next < angle_prev ? &_front : &_back;
h->setPosition(_desktop->w2d(evp));
h->setVisible(true);
@@ -1138,13 +1190,13 @@ bool Node::clicked(GdkEventButton *event)
return SelectableControlPoint::clicked(event);
}
-Inkscape::SnapSourceType Node::_snapSourceType()
+Inkscape::SnapSourceType Node::_snapSourceType() const
{
if (_type == NODE_SMOOTH || _type == NODE_AUTO)
return SNAPSOURCE_NODE_SMOOTH;
return SNAPSOURCE_NODE_CUSP;
}
-Inkscape::SnapTargetType Node::_snapTargetType()
+Inkscape::SnapTargetType Node::_snapTargetType() const
{
if (_type == NODE_SMOOTH || _type == NODE_AUTO)
return SNAPTARGET_NODE_SMOOTH;
@@ -1156,10 +1208,6 @@ Inkscape::SnapCandidatePoint Node::snapCandidatePoint()
return SnapCandidatePoint(position(), _snapSourceType(), _snapTargetType());
}
-/**
- * Gets the handle that faces the given adjacent node.
- * Will abort with error if the given node is not adjacent.
- */
Handle *Node::handleToward(Node *to)
{
if (_next() == to) {
@@ -1169,12 +1217,9 @@ Handle *Node::handleToward(Node *to)
return back();
}
g_error("Node::handleToward(): second node is not adjacent!");
+ return NULL;
}
-/**
- * Gets the node in the direction of the given handle.
- * Will abort with error if the handle doesn't belong to this node.
- */
Node *Node::nodeToward(Handle *dir)
{
if (front() == dir) {
@@ -1184,12 +1229,9 @@ Node *Node::nodeToward(Handle *dir)
return _prev();
}
g_error("Node::nodeToward(): handle is not a child of this node!");
+ return NULL;
}
-/**
- * Gets the handle that goes in the direction opposite to the given adjacent node.
- * Will abort with error if the given node is not adjacent.
- */
Handle *Node::handleAwayFrom(Node *to)
{
if (_next() == to) {
@@ -1199,12 +1241,9 @@ Handle *Node::handleAwayFrom(Node *to)
return front();
}
g_error("Node::handleAwayFrom(): second node is not adjacent!");
+ return NULL;
}
-/**
- * Gets the node in the direction opposite to the given handle.
- * Will abort with error if the handle doesn't belong to this node.
- */
Node *Node::nodeAwayFrom(Handle *h)
{
if (front() == h) {
@@ -1214,9 +1253,10 @@ Node *Node::nodeAwayFrom(Handle *h)
return _next();
}
g_error("Node::nodeAwayFrom(): handle is not a child of this node!");
+ return NULL;
}
-Glib::ustring Node::_getTip(unsigned state)
+Glib::ustring Node::_getTip(unsigned state) const
{
if (state_held_shift(state)) {
bool can_drag_out = (_next() && _front.isDegenerate()) || (_prev() && _back.isDegenerate());
@@ -1258,13 +1298,15 @@ Glib::ustring Node::_getTip(unsigned state)
"<b>%s</b>: drag to shape the path, click to select only this node (more: Shift, Ctrl, Alt)"), nodetype);
}
-Glib::ustring Node::_getDragTip(GdkEventMotion */*event*/)
+Glib::ustring Node::_getDragTip(GdkEventMotion */*event*/) const
{
Geom::Point dist = position() - _last_drag_origin();
- GString *x = SP_PX_TO_METRIC_STRING(dist[Geom::X], _desktop->namedview->getDefaultMetric());
- GString *y = SP_PX_TO_METRIC_STRING(dist[Geom::Y], _desktop->namedview->getDefaultMetric());
- Glib::ustring ret = format_tip(C_("Path node tip", "Move node by %s, %s"),
- x->str, y->str);
+
+ Inkscape::Util::Quantity x_q = Inkscape::Util::Quantity(dist[Geom::X], "px");
+ Inkscape::Util::Quantity y_q = Inkscape::Util::Quantity(dist[Geom::Y], "px");
+ GString *x = g_string_new(x_q.string(_desktop->namedview->doc_units).c_str());
+ GString *y = g_string_new(y_q.string(_desktop->namedview->doc_units).c_str());
+ Glib::ustring ret = format_tip(C_("Path node tip", "Move node by %s, %s"), x->str, y->str);
g_string_free(x, TRUE);
g_string_free(y, TRUE);
return ret;
@@ -1281,7 +1323,6 @@ char const *Node::node_type_to_localized_string(NodeType type)
}
}
-/** Determine whether two nodes are joined by a linear segment. */
bool Node::_is_line_segment(Node *first, Node *second)
{
if (!first || !second) return false;
@@ -1292,25 +1333,6 @@ bool Node::_is_line_segment(Node *first, Node *second)
return false;
}
-SPCtrlShapeType Node::_node_type_to_shape(NodeType type)
-{
- switch(type) {
- case NODE_CUSP: return SP_CTRL_SHAPE_DIAMOND;
- case NODE_SMOOTH: return SP_CTRL_SHAPE_SQUARE;
- case NODE_AUTO: return SP_CTRL_SHAPE_CIRCLE;
- case NODE_SYMMETRIC: return SP_CTRL_SHAPE_SQUARE;
- default: return SP_CTRL_SHAPE_DIAMOND;
- }
-}
-
-
-/**
- * An editable list of nodes representing a subpath.
- *
- * It can optionally be cyclic to represent a closed path.
- * The list has iterators that act like plain node iterators, but can also be used
- * to obtain shared pointers to nodes.
- */
NodeList::NodeList(SubpathList &splist)
: _list(splist)
, _closed(false)
@@ -1342,8 +1364,6 @@ bool NodeList::closed()
return _closed;
}
-/** A subpath is degenerate if it has no segments - either one node in an open path
- * or no nodes in a closed path */
bool NodeList::degenerate()
{
return closed() ? empty() : ++begin() == end();
@@ -1360,10 +1380,9 @@ NodeList::iterator NodeList::before(double t, double *fracpart)
return ret;
}
-// insert a node before i
-NodeList::iterator NodeList::insert(iterator i, Node *x)
+NodeList::iterator NodeList::insert(iterator pos, Node *x)
{
- ListNode *ins = i._node;
+ ListNode *ins = pos._node;
x->ln_next = ins;
x->ln_prev = ins->ln_prev;
ins->ln_prev->ln_next = x;
@@ -1469,11 +1488,6 @@ NodeList &NodeList::get(iterator const &i) {
}
-/**
- * @class SubpathList
- * Editable path composed of one or more subpaths.
- */
-
} // namespace UI
} // namespace Inkscape
diff --git a/src/ui/tool/node.h b/src/ui/tool/node.h
index f2e31e018..b874949f5 100644
--- a/src/ui/tool/node.h
+++ b/src/ui/tool/node.h
@@ -3,6 +3,7 @@
*/
/* Authors:
* Krzysztof Kosiński <tweenk.pl@gmail.com>
+ * Jon A. Cruz <jon@joncruz.org>
*
* Copyright (C) 2009 Authors
* Released under GNU GPL, read the file 'COPYING' for more information
@@ -15,7 +16,13 @@
#include <iosfwd>
#include <stdexcept>
#include <cstddef>
+
+#if __cplusplus >= 201103L
+#include <functional>
+#else
#include <tr1/functional>
+#endif
+
#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>
#include "ui/tool/selectable-control-point.h"
@@ -30,11 +37,13 @@ template <typename> class NodeIterator;
}
}
+#if __cplusplus < 201103L
namespace std {
namespace tr1 {
template <typename N> struct hash< Inkscape::UI::NodeIterator<N> >;
}
}
+#endif
namespace Inkscape {
namespace UI {
@@ -80,10 +89,11 @@ struct NodeSharedData {
class Handle : public ControlPoint {
public:
+
virtual ~Handle();
- inline Geom::Point relativePos();
- inline double length();
- bool isDegenerate() { return _degenerate; } // True if the handle is retracted, i.e. has zero length.
+ inline Geom::Point relativePos() const;
+ inline double length() const;
+ bool isDegenerate() const { return _degenerate; } // True if the handle is retracted, i.e. has zero length.
virtual void setVisible(bool);
virtual void move(Geom::Point const &p);
@@ -96,80 +106,161 @@ public:
void setDirection(Geom::Point const &dir);
Node *parent() { return _parent; }
Handle *other();
+ Handle const *other() const;
static char const *handle_type_to_localized_string(NodeType type);
+
protected:
+
Handle(NodeSharedData const &data, Geom::Point const &initial_pos, Node *parent);
- virtual bool _eventHandler(SPEventContext *event_context, GdkEvent *event);
- virtual void dragged(Geom::Point &, GdkEventMotion *);
- virtual bool grabbed(GdkEventMotion *);
- virtual void ungrabbed(GdkEventButton *);
- virtual bool clicked(GdkEventButton *);
+ virtual bool _eventHandler(Inkscape::UI::Tools::ToolBase *event_context, GdkEvent *event);
+ virtual void dragged(Geom::Point &new_pos, GdkEventMotion *event);
+ virtual bool grabbed(GdkEventMotion *event);
+ virtual void ungrabbed(GdkEventButton *event);
+ virtual bool clicked(GdkEventButton *event);
+
+ virtual Glib::ustring _getTip(unsigned state) const;
+ virtual Glib::ustring _getDragTip(GdkEventMotion *event) const;
+ virtual bool _hasDragTips() const { return true; }
- virtual Glib::ustring _getTip(unsigned state);
- virtual Glib::ustring _getDragTip(GdkEventMotion *event);
- virtual bool _hasDragTips() { return true; }
private:
+
inline PathManipulator &_pm();
Node *_parent; // the handle's lifetime does not extend beyond that of the parent node,
// so a naked pointer is OK and allows setting it during Node's construction
SPCtrlLine *_handle_line;
bool _degenerate; // True if the handle is retracted, i.e. has zero length. This is used often internally so it makes sense to cache this
+ /**
+ * Control point of a cubic Bezier curve in a path.
+ *
+ * Handle keeps the node type invariant only for the opposite handle of the same node.
+ * Keeping the invariant on node moves is left to the %Node class.
+ */
static Geom::Point _saved_other_pos;
+
static double _saved_length;
static bool _drag_out;
+ static ColorSet _handle_colors;
friend class Node;
};
class Node : ListNode, public SelectableControlPoint {
public:
+
+ /**
+ * Curve endpoint in an editable path.
+ *
+ * The method move() keeps node type invariants during translations.
+ */
Node(NodeSharedData const &data, Geom::Point const &pos);
+
virtual void move(Geom::Point const &p);
virtual void transform(Geom::Affine const &m);
- virtual Geom::Rect bounds();
-
- NodeType type() { return _type; }
+ virtual Geom::Rect bounds() const;
+
+ NodeType type() const { return _type; }
+
+ /**
+ * Sets the node type and optionally restores the invariants associated with the given type.
+ * @param type The type to set.
+ * @param update_handles Whether to restore invariants associated with the given type.
+ * Passing false is useful e.g. wen initially creating the path,
+ * and when making cusp nodes during some node algorithms.
+ * Pass true when used in response to an UI node type button.
+ */
void setType(NodeType type, bool update_handles = true);
+
void showHandles(bool v);
+
+ void updateHandles();
+
+
+ /**
+ * Pick the best type for this node, based on the position of its handles.
+ * This is what assigns types to nodes created using the pen tool.
+ */
void pickBestType(); // automatically determine the type from handle positions
- bool isDegenerate() { return _front.isDegenerate() && _back.isDegenerate(); }
- bool isEndNode();
+
+ bool isDegenerate() const { return _front.isDegenerate() && _back.isDegenerate(); }
+ bool isEndNode() const;
Handle *front() { return &_front; }
Handle *back() { return &_back; }
+
+ /**
+ * Gets the handle that faces the given adjacent node.
+ * Will abort with error if the given node is not adjacent.
+ */
Handle *handleToward(Node *to);
+
+ /**
+ * Gets the node in the direction of the given handle.
+ * Will abort with error if the handle doesn't belong to this node.
+ */
Node *nodeToward(Handle *h);
+
+ /**
+ * Gets the handle that goes in the direction opposite to the given adjacent node.
+ * Will abort with error if the given node is not adjacent.
+ */
Handle *handleAwayFrom(Node *to);
+
+ /**
+ * Gets the node in the direction opposite to the given handle.
+ * Will abort with error if the handle doesn't belong to this node.
+ */
Node *nodeAwayFrom(Handle *h);
+
NodeList &nodeList() { return *(static_cast<ListNode*>(this)->ln_list); }
+
+ /**
+ * Move the node to the bottom of its canvas group.
+ * Useful for node break, to ensure that the selected nodes are above the unselected ones.
+ */
void sink();
static NodeType parse_nodetype(char x);
static char const *node_type_to_localized_string(NodeType type);
+
// temporarily public
- virtual bool _eventHandler(SPEventContext *event_context, GdkEvent *event);
+ /** Customized event handler to catch scroll events needed for selection grow/shrink. */
+ virtual bool _eventHandler(Inkscape::UI::Tools::ToolBase *event_context, GdkEvent *event);
+
Inkscape::SnapCandidatePoint snapCandidatePoint();
+
protected:
- virtual void dragged(Geom::Point &, GdkEventMotion *);
- virtual bool grabbed(GdkEventMotion *);
- virtual bool clicked(GdkEventButton *);
+
+ virtual void dragged(Geom::Point &new_pos, GdkEventMotion *event);
+ virtual bool grabbed(GdkEventMotion *event);
+ virtual bool clicked(GdkEventButton *event);
virtual void _setState(State state);
- virtual Glib::ustring _getTip(unsigned state);
- virtual Glib::ustring _getDragTip(GdkEventMotion *event);
- virtual bool _hasDragTips() { return true; }
+ virtual Glib::ustring _getTip(unsigned state) const;
+ virtual Glib::ustring _getDragTip(GdkEventMotion *event) const;
+ virtual bool _hasDragTips() const { return true; }
+
private:
+
Node(Node const &);
void _fixNeighbors(Geom::Point const &old_pos, Geom::Point const &new_pos);
void _updateAutoHandles();
+
+ /**
+ * Select or deselect a node in this node's subpath based on its path distance from this node.
+ * @param dir If negative, shrink selection by one node; if positive, grow by one node.
+ */
void _linearGrow(int dir);
+
Node *_next();
+ Node const *_next() const;
Node *_prev();
- Inkscape::SnapSourceType _snapSourceType();
- Inkscape::SnapTargetType _snapTargetType();
+ Node const *_prev() const;
+ Inkscape::SnapSourceType _snapSourceType() const;
+ Inkscape::SnapTargetType _snapTargetType() const;
inline PathManipulator &_pm();
- static SPCtrlShapeType _node_type_to_shape(NodeType type);
+
+ /** Determine whether two nodes are joined by a linear segment. */
static bool _is_line_segment(Node *first, Node *second);
// Handles are always present, but are not visible if they coincide with the node
@@ -179,6 +270,8 @@ private:
Handle _back; ///< Node handle in the forward direction of the path
NodeType _type; ///< Type of node - cusp, smooth...
bool _handles_shown;
+ static ColorSet node_colors;
+
friend class Handle;
friend class NodeList;
friend class NodeIterator<Node>;
@@ -224,14 +317,14 @@ public:
// default copy, default assign
self &operator++() {
- _node = _node->ln_next;
+ _node = (_node?_node->ln_next:NULL);
return *this;
}
self &operator--() {
- _node = _node->ln_prev;
+ _node = (_node?_node->ln_prev:NULL);
return *this;
}
- bool operator==(self const &other) const { return _node == other._node; }
+ bool operator==(self const &other) const { if(&other){return _node == other._node;} else{return false;} }
N &operator*() const { return *static_cast<N*>(_node); }
inline operator bool() const; // define after NodeList
/// Get a pointer to the underlying node. Equivalent to <code>&*i</code>.
@@ -271,7 +364,15 @@ public:
typedef NodeIterator<value_type const> const_iterator;
// TODO Lame. Make this private and make SubpathList a factory
+ /**
+ * An editable list of nodes representing a subpath.
+ *
+ * It can optionally be cyclic to represent a closed path.
+ * The list has iterators that act like plain node iterators, but can also be used
+ * to obtain shared pointers to nodes.
+ */
NodeList(SubpathList &_list);
+
~NodeList();
// iterators
@@ -286,7 +387,13 @@ public:
// extra node-specific methods
bool closed();
+
+ /**
+ * A subpath is degenerate if it has no segments - either one node in an open path
+ * or no nodes in a closed path.
+ */
bool degenerate();
+
void setClosed(bool c) { _closed = c; }
iterator before(double t, double *fracpart = NULL);
const_iterator before(double t, double *fracpart = NULL) const {
@@ -294,7 +401,10 @@ public:
}
// list operations
+
+ /** insert a node before pos. */
iterator insert(iterator pos, Node *x);
+
template <class InputIterator>
void insert(iterator pos, InputIterator first, InputIterator last) {
for (; first != last; ++first) insert(pos, *first);
@@ -342,7 +452,10 @@ private:
friend class NodeIterator<Node const>;
};
-/** List of node lists. Represents an editable path. */
+/**
+ * List of node lists. Represents an editable path.
+ * Editable path composed of one or more subpaths.
+ */
class SubpathList : public std::list< boost::shared_ptr<NodeList> > {
public:
typedef std::list< boost::shared_ptr<NodeList> > list_type;
@@ -361,13 +474,13 @@ private:
// define inline Handle funcs after definition of Node
-inline Geom::Point Handle::relativePos() {
+inline Geom::Point Handle::relativePos() const {
return position() - _parent->position();
}
inline void Handle::setRelativePos(Geom::Point const &p) {
setPosition(_parent->position() + p);
}
-inline double Handle::length() {
+inline double Handle::length() const {
return relativePos().length();
}
inline PathManipulator &Handle::_pm() {
diff --git a/src/ui/tool/path-manipulator.cpp b/src/ui/tool/path-manipulator.cpp
index 97e25069f..338499672 100644
--- a/src/ui/tool/path-manipulator.cpp
+++ b/src/ui/tool/path-manipulator.cpp
@@ -18,8 +18,7 @@
#include <boost/shared_ptr.hpp>
#include <2geom/bezier-curve.h>
#include <2geom/bezier-utils.h>
-#include <2geom/svg-path.h>
-#include <glibmm.h>
+#include <2geom/path-sink.h>
#include <glibmm/i18n.h>
#include "ui/tool/path-manipulator.h"
#include "desktop.h"
@@ -152,13 +151,13 @@ PathManipulator::~PathManipulator()
{
delete _dragpoint;
delete _observer;
- gtk_object_destroy(_outline);
+ sp_canvas_item_destroy(_outline);
_spcurve->unref();
clear();
}
/** Handle motion events to update the position of the curve drag point. */
-bool PathManipulator::event(SPEventContext * /*event_context*/, GdkEvent *event)
+bool PathManipulator::event(Inkscape::UI::Tools::ToolBase * /*event_context*/, GdkEvent *event)
{
if (empty()) return false;
@@ -204,7 +203,7 @@ void PathManipulator::writeXML()
sp_object_ref(_path);
_path->deleteObject(true, true);
sp_object_unref(_path);
- _path = 0;
+ _path = NULL;
}
_observer->unblock();
}
@@ -460,7 +459,10 @@ void PathManipulator::weldSegments()
if (j->selected()) ++num_selected;
else ++num_unselected;
}
- if (num_selected < 3) continue;
+
+ // if 2 or fewer nodes are selected, there can't be any middle points to remove.
+ if (num_selected <= 2) continue;
+
if (num_unselected == 0 && sp->closed()) {
// if all nodes in a closed subpath are selected, the operation doesn't make much sense
continue;
@@ -490,14 +492,16 @@ void PathManipulator::weldSegments()
}
if (num_points > 2) {
// remove nodes in the middle
+ // TODO: fit bezier to the former shape
sel_beg = sel_beg.next();
while (sel_beg != sel_end.prev()) {
NodeList::iterator next = sel_beg.next();
sp->erase(sel_beg);
sel_beg = next;
}
- sel_beg = sel_end;
}
+ sel_beg = sel_end;
+ // decrease num_selected by the number of points processed
num_selected -= num_points;
}
}
@@ -915,6 +919,15 @@ void PathManipulator::setLiveObjects(bool set)
_live_objects = set;
}
+void PathManipulator::updateHandles()
+{
+ for (SubpathList::iterator i = _subpaths.begin(); i != _subpaths.end(); ++i) {
+ for (NodeList::iterator j = (*i)->begin(); j != (*i)->end(); ++j) {
+ j->updateHandles();
+ }
+ }
+}
+
void PathManipulator::setControlsTransform(Geom::Affine const &tnew)
{
Geom::Affine delta = _i2d_transform.inverse() * _edit_transform.inverse() * tnew * _i2d_transform;
@@ -1189,12 +1202,13 @@ void PathManipulator::_createGeometryFromControlPoints(bool alert_LPE)
}
++spi;
}
- builder.finish();
+ builder.flush();
Geom::PathVector pathv = builder.peek() * (_edit_transform * _i2d_transform).inverse();
_spcurve->set_pathvector(pathv);
if (alert_LPE) {
- if (SP_IS_LPE_ITEM(_path) && sp_lpe_item_has_path_effect(SP_LPE_ITEM(_path))) {
- PathEffectList effect_list = sp_lpe_item_get_effect_list(SP_LPE_ITEM(_path));
+ /// \todo note that _path can be an Inkscape::LivePathEffect::Effect* too, kind of confusing, rework member naming?
+ if (SP_IS_LPE_ITEM(_path) && _path->hasPathEffect()) {
+ PathEffectList effect_list = _path->getEffectList();
LivePathEffect::LPEPowerStroke *lpe_pwr = dynamic_cast<LivePathEffect::LPEPowerStroke*>( effect_list.front()->lpeobject->get_lpe() );
if (lpe_pwr) {
lpe_pwr->adjustForNewPath(pathv);
@@ -1296,6 +1310,10 @@ void PathManipulator::_getGeometry()
} else {
_spcurve->unref();
_spcurve = _path->get_curve_for_edit();
+ // never allow NULL to sneak in here!
+ if (_spcurve == NULL) {
+ _spcurve = new SPCurve();
+ }
}
}
@@ -1320,7 +1338,7 @@ void PathManipulator::_setGeometry()
if (_path->getRepr()->attribute("inkscape:original-d"))
_path->set_original_curve(_spcurve, false, false);
else
- SP_SHAPE(_path)->setCurve(_spcurve, false);
+ _path->setCurve(_spcurve, false);
}
}
diff --git a/src/ui/tool/path-manipulator.h b/src/ui/tool/path-manipulator.h
index fe73555c8..7a13ce723 100644
--- a/src/ui/tool/path-manipulator.h
+++ b/src/ui/tool/path-manipulator.h
@@ -21,8 +21,8 @@
#include "ui/tool/manipulator.h"
struct SPCanvasItem;
-struct SPCurve;
-struct SPPath;
+class SPCurve;
+class SPPath;
namespace Inkscape {
namespace XML { class Node; }
@@ -56,7 +56,7 @@ public:
PathManipulator(MultiPathManipulator &mpm, SPPath *path, Geom::Affine const &edit_trans,
guint32 outline_color, Glib::ustring lpe_key);
~PathManipulator();
- virtual bool event(SPEventContext *, GdkEvent *);
+ virtual bool event(Inkscape::UI::Tools::ToolBase *, GdkEvent *);
bool empty();
void writeXML();
@@ -86,6 +86,7 @@ public:
void showPathDirection(bool show);
void setLiveOutline(bool set);
void setLiveObjects(bool set);
+ void updateHandles();
void setControlsTransform(Geom::Affine const &);
void hideDragPoint();
MultiPathManipulator &mpm() { return _multi_path_manipulator; }
@@ -130,7 +131,7 @@ private:
SubpathList _subpaths;
MultiPathManipulator &_multi_path_manipulator;
- SPPath *_path;
+ SPPath *_path; ///< can be an SPPath or an Inkscape::LivePathEffect::Effect !!!
SPCurve *_spcurve; // in item coordinates
SPCanvasItem *_outline;
CurveDragPoint *_dragpoint; // an invisible control point hoverng over curve
diff --git a/src/ui/tool/selectable-control-point.cpp b/src/ui/tool/selectable-control-point.cpp
index cec2e7b07..f3f9c0e1e 100644
--- a/src/ui/tool/selectable-control-point.cpp
+++ b/src/ui/tool/selectable-control-point.cpp
@@ -1,8 +1,6 @@
-/** @file
- * Desktop-bound selectable control object - implementation
- */
/* Authors:
* Krzysztof Kosiński <tweenk.pl@gmail.com>
+ * Jon A. Cruz <jon@joncruz.org>
*
* Copyright (C) 2009 Authors
* Released under GNU GPL, read the file 'COPYING' for more information
@@ -15,34 +13,32 @@
namespace Inkscape {
namespace UI {
-static SelectableControlPoint::ColorSet default_scp_color_set = {
- {
- {0xffffff00, 0x01000000}, // normal fill, stroke
- {0xff0000ff, 0x01000000}, // mouseover fill, stroke
- {0x0000ffff, 0x01000000} // clicked fill, stroke
- },
+ControlPoint::ColorSet SelectableControlPoint::_default_scp_color_set = {
+ {0xffffff00, 0x01000000}, // normal fill, stroke
+ {0xff0000ff, 0x01000000}, // mouseover fill, stroke
+ {0x0000ffff, 0x01000000}, // clicked fill, stroke
+ //
{0x0000ffff, 0x000000ff}, // normal fill, stroke when selected
{0xff000000, 0x000000ff}, // mouseover fill, stroke when selected
{0xff000000, 0x000000ff} // clicked fill, stroke when selected
};
-SelectableControlPoint::SelectableControlPoint(SPDesktop *d, Geom::Point const &initial_pos,
- SPAnchorType anchor, SPCtrlShapeType shape, unsigned int size,
- ControlPointSelection &sel, ColorSet *cset, SPCanvasGroup *group)
- : ControlPoint (d, initial_pos, anchor, shape, size,
- cset ? reinterpret_cast<ControlPoint::ColorSet*>(cset)
- : reinterpret_cast<ControlPoint::ColorSet*>(&default_scp_color_set), group)
- , _selection (sel)
+SelectableControlPoint::SelectableControlPoint(SPDesktop *d, Geom::Point const &initial_pos, SPAnchorType anchor,
+ Inkscape::ControlType type,
+ ControlPointSelection &sel,
+ ColorSet const &cset, SPCanvasGroup *group) :
+ ControlPoint(d, initial_pos, anchor, type, cset, group),
+ _selection(sel)
{
_selection.allPoints().insert(this);
}
-SelectableControlPoint::SelectableControlPoint(SPDesktop *d, Geom::Point const &initial_pos,
- SPAnchorType anchor, Glib::RefPtr<Gdk::Pixbuf> pixbuf,
- ControlPointSelection &sel, ColorSet *cset, SPCanvasGroup *group)
- : ControlPoint (d, initial_pos, anchor, pixbuf,
- cset ? reinterpret_cast<ControlPoint::ColorSet*>(cset)
- : reinterpret_cast<ControlPoint::ColorSet*>(&default_scp_color_set), group)
- , _selection (sel)
+
+SelectableControlPoint::SelectableControlPoint(SPDesktop *d, Geom::Point const &initial_pos, SPAnchorType anchor,
+ Glib::RefPtr<Gdk::Pixbuf> pixbuf,
+ ControlPointSelection &sel,
+ ColorSet const &cset, SPCanvasGroup *group) :
+ ControlPoint(d, initial_pos, anchor, pixbuf, cset, group),
+ _selection (sel)
{
_selection.allPoints().insert(this);
}
@@ -107,21 +103,23 @@ void SelectableControlPoint::_setState(State state)
{
if (!selected()) {
ControlPoint::_setState(state);
- return;
- }
-
- ColorSet *cset = reinterpret_cast<ColorSet*>(_cset);
- ColorEntry current = {0, 0};
- switch (state) {
- case STATE_NORMAL:
- current = cset->selected_normal; break;
- case STATE_MOUSEOVER:
- current = cset->selected_mouseover; break;
- case STATE_CLICKED:
- current = cset->selected_clicked; break;
+ } else {
+ ColorEntry current = {0, 0};
+ ColorSet const &activeCset = (_isLurking()) ? invisible_cset : _cset;
+ switch (state) {
+ case STATE_NORMAL:
+ current = activeCset.selected_normal;
+ break;
+ case STATE_MOUSEOVER:
+ current = activeCset.selected_mouseover;
+ break;
+ case STATE_CLICKED:
+ current = activeCset.selected_clicked;
+ break;
+ }
+ _setColors(current);
+ _state = state;
}
- _setColors(current);
- _state = state;
}
} // namespace UI
diff --git a/src/ui/tool/selectable-control-point.h b/src/ui/tool/selectable-control-point.h
index 8ecb39a79..8acfc1168 100644
--- a/src/ui/tool/selectable-control-point.h
+++ b/src/ui/tool/selectable-control-point.h
@@ -1,8 +1,6 @@
-/** @file
- * Desktop-bound selectable control object
- */
/* Authors:
* Krzysztof Kosiński <tweenk.pl@gmail.com>
+ * Jon A. Cruz <jon@joncruz.org>
*
* Copyright (C) 2009 Authors
* Released under GNU GPL, read the file 'COPYING' for more information
@@ -18,40 +16,45 @@ namespace UI {
class ControlPointSelection;
+/**
+ * Desktop-bound selectable control object.
+ */
class SelectableControlPoint : public ControlPoint {
public:
- struct ColorSet {
- ControlPoint::ColorSet cpset;
- ColorEntry selected_normal;
- ColorEntry selected_mouseover;
- ColorEntry selected_clicked;
- };
~SelectableControlPoint();
bool selected() const;
- void updateState() const { const_cast<SelectableControlPoint*>(this)->_setState(_state); }
- virtual Geom::Rect bounds() {
+ void updateState() { _setState(_state); }
+ virtual Geom::Rect bounds() const {
return Geom::Rect(position(), position());
}
+
protected:
- SelectableControlPoint(SPDesktop *d, Geom::Point const &initial_pos,
- SPAnchorType anchor, SPCtrlShapeType shape,
- unsigned int size, ControlPointSelection &sel, ColorSet *cset = 0,
- SPCanvasGroup *group = 0);
- SelectableControlPoint(SPDesktop *d, Geom::Point const &initial_pos,
- SPAnchorType anchor, Glib::RefPtr<Gdk::Pixbuf> pixbuf,
- ControlPointSelection &sel, ColorSet *cset = 0, SPCanvasGroup *group = 0);
+
+ SelectableControlPoint(SPDesktop *d, Geom::Point const &initial_pos, SPAnchorType anchor,
+ Inkscape::ControlType type,
+ ControlPointSelection &sel,
+ ColorSet const &cset = _default_scp_color_set, SPCanvasGroup *group = 0);
+
+ SelectableControlPoint(SPDesktop *d, Geom::Point const &initial_pos, SPAnchorType anchor,
+ Glib::RefPtr<Gdk::Pixbuf> pixbuf,
+ ControlPointSelection &sel,
+ ColorSet const &cset = _default_scp_color_set, SPCanvasGroup *group = 0);
virtual void _setState(State state);
- virtual void dragged(Geom::Point &, GdkEventMotion *);
- virtual bool grabbed(GdkEventMotion *);
- virtual void ungrabbed(GdkEventButton *);
- virtual bool clicked(GdkEventButton *);
+ virtual void dragged(Geom::Point &new_pos, GdkEventMotion *event);
+ virtual bool grabbed(GdkEventMotion *event);
+ virtual void ungrabbed(GdkEventButton *event);
+ virtual bool clicked(GdkEventButton *event);
ControlPointSelection &_selection;
+
private:
+
void _takeSelection();
+
+ static ColorSet _default_scp_color_set;
};
} // namespace UI
diff --git a/src/ui/tool/selector.cpp b/src/ui/tool/selector.cpp
index 364776ce4..bdeacadc9 100644
--- a/src/ui/tool/selector.cpp
+++ b/src/ui/tool/selector.cpp
@@ -3,6 +3,7 @@
*/
/* Authors:
* Krzysztof Kosiński <tweenk.pl@gmail.com>
+ * Jon A. Cruz <jon@joncruz.org>
*
* Copyright (C) 2009 Authors
* Released under GNU GPL, read the file 'COPYING' for more information
@@ -12,17 +13,13 @@
#include "desktop.h"
#include "desktop-handles.h"
#include "display/sodipodi-ctrlrect.h"
-#include "event-context.h"
+#include "ui/tools/tool-base.h"
#include "preferences.h"
#include "ui/tool/event-utils.h"
#include "ui/tool/selector.h"
#include <gdk/gdkkeysyms.h>
-#if !GTK_CHECK_VERSION(2,22,0)
-#include "compat-key-syms.h"
-#endif
-
namespace Inkscape {
namespace UI {
@@ -32,27 +29,31 @@ namespace UI {
* is pressed, it grabs events and handles drags and clicks in the usual way. */
class SelectorPoint : public ControlPoint {
public:
- SelectorPoint(SPDesktop *d, SPCanvasGroup *group, Selector *s)
- : ControlPoint(d, Geom::Point(0,0), SP_ANCHOR_CENTER, SP_CTRL_SHAPE_SQUARE,
- 1, &invisible_cset, group)
- , _selector(s)
- , _cancel(false)
+ SelectorPoint(SPDesktop *d, SPCanvasGroup *group, Selector *s) :
+ ControlPoint(d, Geom::Point(0,0), SP_ANCHOR_CENTER,
+ CTRL_TYPE_INVISIPOINT,
+ invisible_cset, group),
+ _selector(s),
+ _cancel(false)
{
setVisible(false);
_rubber = static_cast<CtrlRect*>(sp_canvas_item_new(sp_desktop_controls(_desktop),
SP_TYPE_CTRLRECT, NULL));
sp_canvas_item_hide(_rubber);
}
+
~SelectorPoint() {
- gtk_object_destroy(_rubber);
+ sp_canvas_item_destroy(_rubber);
}
+
SPDesktop *desktop() { return _desktop; }
- bool event(SPEventContext *ec, GdkEvent *e) {
+
+ bool event(Inkscape::UI::Tools::ToolBase *ec, GdkEvent *e) {
return _eventHandler(ec, e);
}
protected:
- virtual bool _eventHandler(SPEventContext *event_context, GdkEvent *event) {
+ virtual bool _eventHandler(Inkscape::UI::Tools::ToolBase *event_context, GdkEvent *event) {
if (event->type == GDK_KEY_PRESS && shortcut_key(event->key) == GDK_KEY_Escape &&
sp_canvas_item_is_visible(_rubber))
{
@@ -70,22 +71,26 @@ private:
sp_canvas_item_show(_rubber);
return false;
}
+
virtual void dragged(Geom::Point &new_pos, GdkEventMotion *) {
if (_cancel) return;
Geom::Rect sel(_start, new_pos);
_rubber->setRectangle(sel);
}
+
virtual void ungrabbed(GdkEventButton *event) {
if (_cancel) return;
sp_canvas_item_hide(_rubber);
Geom::Rect sel(_start, position());
_selector->signal_area.emit(sel, event);
}
+
virtual bool clicked(GdkEventButton *event) {
if (event->button != 1) return false;
_selector->signal_point.emit(position(), event);
return true;
}
+
CtrlRect *_rubber;
Selector *_selector;
Geom::Point _start;
@@ -105,7 +110,7 @@ Selector::~Selector()
delete _dragger;
}
-bool Selector::event(SPEventContext *event_context, GdkEvent *event)
+bool Selector::event(Inkscape::UI::Tools::ToolBase *event_context, GdkEvent *event)
{
// The hidden control point will capture all events after it obtains the grab,
// but it relies on this function to initiate it. If we pass only first button
@@ -113,7 +118,7 @@ bool Selector::event(SPEventContext *event_context, GdkEvent *event)
switch (event->type) {
case GDK_BUTTON_PRESS:
// Do not pass button presses other than left button to the control point.
- // This way middle click and right click can be handled in SPEventContext.
+ // This way middle click and right click can be handled in ToolBase.
if (event->button.button == 1 && !event_context->space_panning) {
_dragger->setPosition(_desktop->w2d(event_point(event->motion)));
return _dragger->event(event_context, event);
diff --git a/src/ui/tool/selector.h b/src/ui/tool/selector.h
index 7e2759b69..dbe751ede 100644
--- a/src/ui/tool/selector.h
+++ b/src/ui/tool/selector.h
@@ -28,7 +28,7 @@ class Selector : public Manipulator {
public:
Selector(SPDesktop *d);
virtual ~Selector();
- virtual bool event(SPEventContext *, GdkEvent *);
+ virtual bool event(Inkscape::UI::Tools::ToolBase *, GdkEvent *);
sigc::signal<void, Geom::Rect const &, GdkEventButton*> signal_area;
sigc::signal<void, Geom::Point const &, GdkEventButton*> signal_point;
diff --git a/src/ui/tool/transform-handle-set.cpp b/src/ui/tool/transform-handle-set.cpp
index b4a27170a..7d5c9bf0c 100644
--- a/src/ui/tool/transform-handle-set.cpp
+++ b/src/ui/tool/transform-handle-set.cpp
@@ -3,6 +3,7 @@
*/
/* Authors:
* Krzysztof Kosiński <tweenk.pl@gmail.com>
+ * Jon A. Cruz <jon@joncruz.org>
*
* Copyright (C) 2009 Authors
* Released under GNU GPL, read the file 'COPYING' for more information
@@ -25,7 +26,7 @@
#include "ui/tool/selectable-control-point.h"
#include "ui/tool/event-utils.h"
#include "ui/tool/transform-handle-set.h"
-#include "ui/tool/node-tool.h"
+#include "ui/tools/node-tool.h"
#include "ui/tool/node.h"
#include "seltrans.h"
@@ -38,6 +39,7 @@ namespace Inkscape {
namespace UI {
namespace {
+
SPAnchorType corner_to_anchor(unsigned c) {
switch (c % 4) {
case 0: return SP_ANCHOR_NE;
@@ -46,6 +48,7 @@ SPAnchorType corner_to_anchor(unsigned c) {
default: return SP_ANCHOR_SE;
}
}
+
SPAnchorType side_to_anchor(unsigned s) {
switch (s % 4) {
case 0: return SP_ANCHOR_N;
@@ -62,30 +65,30 @@ double snap_angle(double a) {
double unit_angle = M_PI / snaps;
return CLAMP(unit_angle * round(a / unit_angle), -M_PI, M_PI);
}
+
double snap_increment_degrees() {
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
int snaps = prefs->getIntLimited("/options/rotationsnapsperpi/value", 12, 1, 1000);
return 180.0 / snaps;
}
-ControlPoint::ColorSet thandle_cset = {
+} // anonymous namespace
+
+ControlPoint::ColorSet TransformHandle::thandle_cset = {
+ {0x000000ff, 0x000000ff},
+ {0x00ff6600, 0x000000ff},
+ {0x00ff6600, 0x000000ff},
+ //
{0x000000ff, 0x000000ff},
{0x00ff6600, 0x000000ff},
{0x00ff6600, 0x000000ff}
};
-ControlPoint::ColorSet center_cset = {
- {0x00000000, 0x000000ff},
- {0x00000000, 0xff0000b0},
- {0x00000000, 0xff0000b0}
-};
-} // anonymous namespace
-
-/** Base class for node transform handles to simplify implementation */
-TransformHandle::TransformHandle(TransformHandleSet &th, SPAnchorType anchor, Glib::RefPtr<Gdk::Pixbuf> pb)
- : ControlPoint(th._desktop, Geom::Point(), anchor, pb, &thandle_cset,
- th._transform_handle_group)
- , _th(th)
+TransformHandle::TransformHandle(TransformHandleSet &th, SPAnchorType anchor, Glib::RefPtr<Gdk::Pixbuf> pb) :
+ ControlPoint(th._desktop, Geom::Point(), anchor,
+ pb,
+ thandle_cset, th._transform_handle_group),
+ _th(th)
{
setVisible(false);
}
@@ -121,12 +124,13 @@ bool TransformHandle::grabbed(GdkEventMotion *)
startTransform();
_th._setActiveHandle(this);
- _cset = &invisible_cset;
+ _setLurking(true);
_setState(_state);
// Collect the snap-candidates, one for each selected node. These will be stored in the _snap_points vector.
- InkNodeTool *nt = INK_NODE_TOOL(_th._desktop->event_context);
- ControlPointSelection *selection = nt->_selected_nodes.get();
+ Inkscape::UI::Tools::NodeTool *nt = INK_NODE_TOOL(_th._desktop->event_context);
+ //ControlPointSelection *selection = nt->_selected_nodes.get();
+ ControlPointSelection* selection = nt->_selected_nodes;
selection->setOriginalPoints();
selection->getOriginalPoints(_snap_points);
@@ -171,7 +175,7 @@ void TransformHandle::ungrabbed(GdkEventButton *)
{
_snap_points.clear();
_th._clearActiveHandle();
- _cset = &thandle_cset;
+ _setLurking(false);
_setState(_state);
endTransform();
_th.signal_commit.emit(getCommitEvent());
@@ -184,7 +188,7 @@ public:
: TransformHandle(th, anchor, pb)
{}
protected:
- virtual Glib::ustring _getTip(unsigned state) {
+ virtual Glib::ustring _getTip(unsigned state) const {
if (state_held_control(state)) {
if (state_held_shift(state)) {
return C_("Transform handle tip",
@@ -205,31 +209,36 @@ protected:
return C_("Transform handle tip", "<b>Scale handle</b>: drag to scale the selection");
}
- virtual Glib::ustring _getDragTip(GdkEventMotion */*event*/) {
+ virtual Glib::ustring _getDragTip(GdkEventMotion */*event*/) const {
return format_tip(C_("Transform handle tip",
"Scale by %.2f%% x %.2f%%"), _last_scale_x * 100, _last_scale_y * 100);
}
- virtual bool _hasDragTips() { return true; }
+ virtual bool _hasDragTips() const { return true; }
static double _last_scale_x, _last_scale_y;
};
double ScaleHandle::_last_scale_x = 1.0;
double ScaleHandle::_last_scale_y = 1.0;
-/// Corner scaling handle for node transforms
+/**
+ * Corner scaling handle for node transforms.
+ */
class ScaleCornerHandle : public ScaleHandle {
public:
- ScaleCornerHandle(TransformHandleSet &th, unsigned corner)
- : ScaleHandle(th, corner_to_anchor(corner), _corner_to_pixbuf(corner))
- , _corner(corner)
+
+ ScaleCornerHandle(TransformHandleSet &th, unsigned corner) :
+ ScaleHandle(th, corner_to_anchor(corner), _corner_to_pixbuf(corner)),
+ _corner(corner)
{}
+
protected:
virtual void startTransform() {
_sc_center = _th.rotationCenter();
_sc_opposite = _th.bounds().corner(_corner + 2);
_last_scale_x = _last_scale_y = 1.0;
}
+
virtual Geom::Affine computeTransform(Geom::Point const &new_pos, GdkEventMotion *event) {
Geom::Point scc = held_shift(*event) ? _sc_center : _sc_opposite;
Geom::Point vold = _origin - scc, vnew = new_pos - scc;
@@ -275,25 +284,35 @@ protected:
* Geom::Translate(scc);
return t;
}
+
virtual CommitEvent getCommitEvent() {
return _last_transform.isUniformScale()
? COMMIT_MOUSE_SCALE_UNIFORM
: COMMIT_MOUSE_SCALE;
}
+
private:
+
static Glib::RefPtr<Gdk::Pixbuf> _corner_to_pixbuf(unsigned c) {
- sp_select_context_get_type();
+ //sp_select_context_get_type();
switch (c % 2) {
- case 0: return Glib::wrap(handles[1], true);
- default: return Glib::wrap(handles[0], true);
+ case 0:
+ return Glib::wrap(handles[1], true);
+ break;
+ default:
+ return Glib::wrap(handles[0], true);
+ break;
}
}
+
Geom::Point _sc_center;
Geom::Point _sc_opposite;
unsigned _corner;
};
-/// Side scaling handle for node transforms
+/**
+ * Side scaling handle for node transforms.
+ */
class ScaleSideHandle : public ScaleHandle {
public:
ScaleSideHandle(TransformHandleSet &th, unsigned side)
@@ -358,7 +377,7 @@ protected:
}
private:
static Glib::RefPtr<Gdk::Pixbuf> _side_to_pixbuf(unsigned c) {
- sp_select_context_get_type();
+ //sp_select_context_get_type();
switch (c % 2) {
case 0: return Glib::wrap(handles[3], true);
default: return Glib::wrap(handles[2], true);
@@ -369,7 +388,9 @@ private:
unsigned _side;
};
-/// Rotation handle for node transforms
+/**
+ * Rotation handle for node transforms.
+ */
class RotateHandle : public TransformHandle {
public:
RotateHandle(TransformHandleSet &th, unsigned corner)
@@ -410,7 +431,7 @@ protected:
virtual CommitEvent getCommitEvent() { return COMMIT_MOUSE_ROTATE; }
- virtual Glib::ustring _getTip(unsigned state) {
+ virtual Glib::ustring _getTip(unsigned state) const {
if (state_held_shift(state)) {
if (state_held_control(state)) {
return format_tip(C_("Transform handle tip",
@@ -427,20 +448,20 @@ protected:
"the selection around the rotation center");
}
- virtual Glib::ustring _getDragTip(GdkEventMotion */*event*/) {
+ virtual Glib::ustring _getDragTip(GdkEventMotion */*event*/) const {
return format_tip(C_("Transform handle tip", "Rotate by %.2f°"),
- _last_angle * 360.0);
+ _last_angle * 180.0 / M_PI);
}
- virtual bool _hasDragTips() { return true; }
+ virtual bool _hasDragTips() const { return true; }
private:
static Glib::RefPtr<Gdk::Pixbuf> _corner_to_pixbuf(unsigned c) {
- sp_select_context_get_type();
+ //sp_select_context_get_type();
switch (c % 4) {
- case 0: return Glib::wrap(handles[10], true);
- case 1: return Glib::wrap(handles[8], true);
- case 2: return Glib::wrap(handles[6], true);
+ case 0: return Glib::wrap(handles[7], true);
+ case 1: return Glib::wrap(handles[6], true);
+ case 2: return Glib::wrap(handles[5], true);
default: return Glib::wrap(handles[4], true);
}
}
@@ -550,7 +571,7 @@ protected:
: COMMIT_MOUSE_SKEW_X;
}
- virtual Glib::ustring _getTip(unsigned state) {
+ virtual Glib::ustring _getTip(unsigned state) const {
if (state_held_shift(state)) {
if (state_held_control(state)) {
return format_tip(C_("Transform handle tip",
@@ -568,7 +589,7 @@ protected:
"the opposite handle");
}
- virtual Glib::ustring _getDragTip(GdkEventMotion */*event*/) {
+ virtual Glib::ustring _getDragTip(GdkEventMotion */*event*/) const {
if (_last_horizontal) {
return format_tip(C_("Transform handle tip", "Skew horizontally by %.2f°"),
_last_angle * 360.0);
@@ -578,16 +599,16 @@ protected:
}
}
- virtual bool _hasDragTips() { return true; }
+ virtual bool _hasDragTips() const { return true; }
private:
static Glib::RefPtr<Gdk::Pixbuf> _side_to_pixbuf(unsigned s) {
- sp_select_context_get_type();
+ //sp_select_context_get_type();
switch (s % 4) {
- case 0: return Glib::wrap(handles[9], true);
- case 1: return Glib::wrap(handles[7], true);
- case 2: return Glib::wrap(handles[5], true);
+ case 0: return Glib::wrap(handles[10], true);
+ case 1: return Glib::wrap(handles[9], true);
+ case 2: return Glib::wrap(handles[8], true);
default: return Glib::wrap(handles[11], true);
}
}
@@ -601,11 +622,13 @@ bool SkewHandle::_last_horizontal = false;
double SkewHandle::_last_angle = 0;
class RotationCenter : public ControlPoint {
+
public:
- RotationCenter(TransformHandleSet &th)
- : ControlPoint(th._desktop, Geom::Point(), SP_ANCHOR_CENTER, _get_pixbuf(),
- &center_cset, th._transform_handle_group)
- , _th(th)
+ RotationCenter(TransformHandleSet &th) :
+ ControlPoint(th._desktop, Geom::Point(), SP_ANCHOR_CENTER,
+ _get_pixbuf(),
+ _center_cset, th._transform_handle_group),
+ _th(th)
{
setVisible(false);
}
@@ -628,7 +651,7 @@ protected:
}
sm.unSetup();
}
- virtual Glib::ustring _getTip(unsigned /*state*/) {
+ virtual Glib::ustring _getTip(unsigned /*state*/) const {
return C_("Transform handle tip",
"<b>Rotation center</b>: drag to change the origin of transforms");
}
@@ -636,13 +659,25 @@ protected:
private:
static Glib::RefPtr<Gdk::Pixbuf> _get_pixbuf() {
- sp_select_context_get_type();
+ //sp_select_context_get_type();
return Glib::wrap(handles[12], true);
}
+ static ColorSet _center_cset;
TransformHandleSet &_th;
};
+ControlPoint::ColorSet RotationCenter::_center_cset = {
+ {0x00000000, 0x000000ff},
+ {0x00000000, 0xff0000b0},
+ {0x00000000, 0xff0000b0},
+ //
+ {0x00000000, 0x000000ff},
+ {0x00000000, 0xff0000b0},
+ {0x00000000, 0xff0000b0}
+};
+
+
TransformHandleSet::TransformHandleSet(SPDesktop *d, SPCanvasGroup *th_group)
: Manipulator(d)
, _active(0)
@@ -674,18 +709,22 @@ TransformHandleSet::~TransformHandleSet()
}
}
-/** Sets the mode of transform handles (scale or rotate). */
void TransformHandleSet::setMode(Mode m)
{
_mode = m;
_updateVisibility(_visible);
}
-Geom::Rect TransformHandleSet::bounds()
+Geom::Rect TransformHandleSet::bounds() const
{
return Geom::Rect(*_scale_corners[0], *_scale_corners[2]);
}
+ControlPoint const &TransformHandleSet::rotationCenter() const
+{
+ return *_center;
+}
+
ControlPoint &TransformHandleSet::rotationCenter()
{
return *_center;
@@ -715,7 +754,7 @@ void TransformHandleSet::setBounds(Geom::Rect const &r, bool preserve_center)
}
}
-bool TransformHandleSet::event(SPEventContext *, GdkEvent*)
+bool TransformHandleSet::event(Inkscape::UI::Tools::ToolBase *, GdkEvent*)
{
return false;
}
@@ -746,9 +785,6 @@ void TransformHandleSet::_clearActiveHandle()
_updateVisibility(_visible);
}
-/** Update the visibility of transformation handles according to settings and the dimensions
- * of the bounding box. It hides the handles that would have no effect or lead to
- * discontinuities. Additionally, side handles for which there is no space are not shown. */
void TransformHandleSet::_updateVisibility(bool v)
{
if (v) {
diff --git a/src/ui/tool/transform-handle-set.h b/src/ui/tool/transform-handle-set.h
index cd1a61da2..54f81adf5 100644
--- a/src/ui/tool/transform-handle-set.h
+++ b/src/ui/tool/transform-handle-set.h
@@ -3,6 +3,7 @@
*/
/* Authors:
* Krzysztof Kosiński <tweenk.pl@gmail.com>
+ * Jon A. Cruz <jon@joncruz.org>
*
* Copyright (C) 2009 Authors
* Released under GNU GPL, read the file 'COPYING' for more information
@@ -23,7 +24,6 @@ class CtrlRect;
namespace Inkscape {
namespace UI {
-//class TransformHandle;
class RotateHandle;
class SkewHandle;
class ScaleCornerHandle;
@@ -32,6 +32,7 @@ class RotationCenter;
class TransformHandleSet : public Manipulator {
public:
+
enum Mode {
MODE_SCALE,
MODE_ROTATE_SKEW
@@ -39,25 +40,39 @@ public:
TransformHandleSet(SPDesktop *d, SPCanvasGroup *th_group);
virtual ~TransformHandleSet();
- virtual bool event(SPEventContext *, GdkEvent *);
+ virtual bool event(Inkscape::UI::Tools::ToolBase *, GdkEvent *);
- bool visible() { return _visible; }
- Mode mode() { return _mode; }
- Geom::Rect bounds();
+ bool visible() const { return _visible; }
+ Mode mode() const { return _mode; }
+ Geom::Rect bounds() const;
void setVisible(bool v);
- void setMode(Mode);
+
+ /** Sets the mode of transform handles (scale or rotate). */
+ void setMode(Mode m);
+
void setBounds(Geom::Rect const &, bool preserve_center = false);
bool transforming() { return _in_transform; }
+
+ ControlPoint const &rotationCenter() const;
ControlPoint &rotationCenter();
sigc::signal<void, Geom::Affine const &> signal_transform;
sigc::signal<void, CommitEvent> signal_commit;
+
private:
+
void _emitTransform(Geom::Affine const &);
void _setActiveHandle(ControlPoint *h);
void _clearActiveHandle();
+
+ /** Update the visibility of transformation handles according to settings and the dimensions
+ * of the bounding box. It hides the handles that would have no effect or lead to
+ * discontinuities. Additionally, side handles for which there is no space are not shown.
+ */
void _updateVisibility(bool v);
+
+ // TODO unions must GO AWAY:
union {
ControlPoint *_handles[17];
struct {
@@ -68,6 +83,7 @@ private:
RotationCenter *_center;
};
};
+
ControlPoint *_active;
SPCanvasGroup *_transform_handle_group;
CtrlRect *_trans_outline;
@@ -79,8 +95,9 @@ private:
friend class RotationCenter;
};
-/** Base class for node transform handles to simplify implementation */
-class TransformHandle : public ControlPoint {
+/** Base class for node transform handles to simplify implementation. */
+class TransformHandle : public ControlPoint
+{
public:
TransformHandle(TransformHandleSet &th, SPAnchorType anchor, Glib::RefPtr<Gdk::Pixbuf> pb);
void getNextClosestPoint(bool reverse);
@@ -103,6 +120,8 @@ private:
virtual bool grabbed(GdkEventMotion *);
virtual void dragged(Geom::Point &new_pos, GdkEventMotion *event);
virtual void ungrabbed(GdkEventButton *);
+
+ static ColorSet thandle_cset;
};
} // namespace UI