0d1183ebf92a8bde407d2bafe705097555b7cfdf
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();
102 }
104 void CurveDragPoint::_ungrabbedHandler()
105 {
106 _pm._updateDragPoint(_desktop->d2w(position()));
107 _pm._commit(_("Drag curve"));
108 _pm._selection.restoreTransformHandles();
109 }
111 bool CurveDragPoint::_clickedHandler(GdkEventButton *event)
112 {
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;
136 }
138 bool CurveDragPoint::_doubleclickedHandler(GdkEventButton *event)
139 {
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;
153 }
155 Glib::ustring CurveDragPoint::_getTip(unsigned state)
156 {
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 }
172 }
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 :