Code

0d1183ebf92a8bde407d2bafe705097555b7cfdf
[inkscape.git] / curve-drag-point.cpp
1 /** @file
2  * Control point that is dragged during path drag
3  */
4 /* Authors:
5  *   Krzysztof KosiƄski <tweenk.pl@gmail.com>
6  *
7  * Copyright (C) 2009 Authors
8  * Released under GNU GPL, read the file 'COPYING' for more information
9  */
11 #include <glib/gi18n.h>
12 #include <2geom/bezier-curve.h>
13 #include "desktop.h"
14 #include "ui/tool/control-point-selection.h"
15 #include "ui/tool/curve-drag-point.h"
16 #include "ui/tool/event-utils.h"
17 #include "ui/tool/multi-path-manipulator.h"
18 #include "ui/tool/path-manipulator.h"
19 #include "ui/tool/node.h"
21 namespace Inkscape {
22 namespace UI {
24 /**
25  * @class CurveDragPoint
26  * An invisible point used to drag curves. This point is used by PathManipulator to allow editing
27  * of path segments by dragging them. It is defined in a separate file so that the node tool
28  * can check if the mouseovered control point is a curve drag point and update the cursor
29  * accordingly, without the need to drag in the full PathManipulator header.
30  */
32 // This point should be invisible to the user - use the invisible_cset from control-point.h
33 // TODO make some methods from path-manipulator.cpp public so that this point doesn't have
34 // to be declared as a friend
36 bool CurveDragPoint::_drags_stroke = false;
38 CurveDragPoint::CurveDragPoint(PathManipulator &pm)
39     : ControlPoint(pm._multi_path_manipulator._path_data.node_data.desktop, Geom::Point(),
40         Gtk::ANCHOR_CENTER, SP_CTRL_SHAPE_CIRCLE, 1.0, &invisible_cset,
41         pm._multi_path_manipulator._path_data.dragpoint_group)
42     , _pm(pm)
43 {
44     setVisible(false);
45     signal_grabbed.connect(
46         sigc::bind_return(
47             sigc::mem_fun(*this, &CurveDragPoint::_grabbedHandler),
48             false));
49     signal_dragged.connect(
50             sigc::hide(
51                 sigc::mem_fun(*this, &CurveDragPoint::_draggedHandler)));
52     signal_ungrabbed.connect(
53         sigc::hide(
54             sigc::mem_fun(*this, &CurveDragPoint::_ungrabbedHandler)));
55     signal_clicked.connect(
56         sigc::mem_fun(*this, &CurveDragPoint::_clickedHandler));
57     signal_doubleclicked.connect(
58         sigc::mem_fun(*this, &CurveDragPoint::_doubleclickedHandler));
59 }
61 void CurveDragPoint::_grabbedHandler(GdkEventMotion */*event*/)
62 {
63     _pm._selection.hideTransformHandles();
64     NodeList::iterator second = first.next();
66     // move the handles to 1/3 the length of the segment for line segments
67     if (first->front()->isDegenerate() && second->back()->isDegenerate()) {
69         // delta is a vector equal 1/3 of distance from first to second
70         Geom::Point delta = (second->position() - first->position()) / 3.0;
71         first->front()->move(first->front()->position() + delta);
72         second->back()->move(second->back()->position() - delta);
74         signal_update.emit();
75     }
76 }
78 void CurveDragPoint::_draggedHandler(Geom::Point const &old_pos, Geom::Point const &new_pos)
79 {
80     if (_drags_stroke) {
81         // TODO
82     } else {
83         NodeList::iterator second = first.next();
84         // Magic Bezier Drag Equations follow!
85         // "weight" describes how the influence of the drag should be distributed
86         // among the handles; 0 = front handle only, 1 = back handle only.
87         double weight, t = _t;
88         if (t <= 1.0 / 6.0) weight = 0;
89         else if (t <= 0.5) weight = (pow((6 * t - 1) / 2.0, 3)) / 2;
90         else if (t <= 5.0 / 6.0) weight = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
91         else weight = 1;
93         Geom::Point delta = new_pos - old_pos;
94         Geom::Point offset0 = ((1-weight)/(3*t*(1-t)*(1-t))) * delta;
95         Geom::Point offset1 = (weight/(3*t*t*(1-t))) * delta;
97         first->front()->move(first->front()->position() + offset0);
98         second->back()->move(second->back()->position() + offset1);
99     }
101     signal_update.emit();
104 void CurveDragPoint::_ungrabbedHandler()
106     _pm._updateDragPoint(_desktop->d2w(position()));
107     _pm._commit(_("Drag curve"));
108     _pm._selection.restoreTransformHandles();
111 bool CurveDragPoint::_clickedHandler(GdkEventButton *event)
113     // This check is probably redundant
114     if (!first || event->button != 1) return false;
115     // the next iterator can be invalid if we click very near the end of path
116     NodeList::iterator second = first.next();
117     if (!second) return false;
119     if (held_shift(*event)) {
120         // if both nodes of the segment are selected, deselect;
121         // otherwise add to selection
122         if (first->selected() && second->selected())  {
123             _pm._selection.erase(first.ptr());
124             _pm._selection.erase(second.ptr());
125         } else {
126             _pm._selection.insert(first.ptr());
127             _pm._selection.insert(second.ptr());
128         }
129     } else {
130         // without Shift, take selection
131         _pm._selection.clear();
132         _pm._selection.insert(first.ptr());
133         _pm._selection.insert(second.ptr());
134     }
135     return true;
138 bool CurveDragPoint::_doubleclickedHandler(GdkEventButton *event)
140     if (event->button != 1 || !first || !first.next()) return false;
142     // The purpose of this call is to make way for the just created node.
143     // Otherwise clicks on the new node would only work after the user moves the mouse a bit.
144     // PathManipulator will restore visibility when necessary.
145     setVisible(false);
146     NodeList::iterator inserted = _pm.subdivideSegment(first, _t);
147     _pm._selection.clear();
148     _pm._selection.insert(inserted.ptr());
150     signal_update.emit();
151     _pm._commit(_("Add node"));
152     return true;
155 Glib::ustring CurveDragPoint::_getTip(unsigned state)
157     if (!first || !first.next()) return "";
158     bool linear = first->front()->isDegenerate() && first.next()->back()->isDegenerate();
159     if (state_held_shift(state)) {
160         return C_("Path segment statusbar tip",
161             "<b>Shift:</b> click to toggle segment selection");
162     }
163     if (linear) {
164         return C_("Path segment statusbar tip",
165             "<b>Linear segment:</b> drag to convert to a Bezier segment, "
166             "doubleclick to insert node, click to select this segment");
167     } else {
168         return C_("Path segment statusbar tip",
169             "<b>Bezier segment:</b> drag to shape the segment, doubleclick to insert node, "
170             "click to select this segment");
171     }
174 } // namespace UI
175 } // namespace Inkscape
177 /*
178   Local Variables:
179   mode:c++
180   c-file-style:"stroustrup"
181   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
182   indent-tabs-mode:nil
183   fill-column:99
184   End:
185 */
186 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :