1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
4  *
5  * Released under GNU GPL v2+, read the file 'COPYING' for more information.
6  */
7 
8 #include "live_effects/parameter/point.h"
9 #include "live_effects/effect.h"
10 
11 #include "inkscape.h"
12 #include "verbs.h"
13 
14 #include "svg/stringstream.h"
15 #include "svg/svg.h"
16 #include "ui/knot/knot-holder.h"
17 #include "ui/knot/knot-holder-entity.h"
18 #include "ui/widget/point.h"
19 
20 #include <glibmm/i18n.h>
21 
22 namespace Inkscape {
23 
24 namespace LivePathEffect {
25 
PointParam(const Glib::ustring & label,const Glib::ustring & tip,const Glib::ustring & key,Inkscape::UI::Widget::Registry * wr,Effect * effect,const gchar * htip,Geom::Point default_value,bool live_update)26 PointParam::PointParam( const Glib::ustring& label, const Glib::ustring& tip,
27                         const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr,
28                         Effect* effect, const gchar *htip, Geom::Point default_value,
29                         bool live_update )
30     : Parameter(label, tip, key, wr, effect)
31     , defvalue(default_value)
32     , liveupdate(live_update)
33 {
34     handle_tip = g_strdup(htip);
35 }
36 
~PointParam()37 PointParam::~PointParam()
38 {
39     if (handle_tip)
40         g_free(handle_tip);
41 }
42 
43 void
param_set_default()44 PointParam::param_set_default()
45 {
46     param_setValue(defvalue,true);
47 }
48 
49 void
param_set_liveupdate(bool live_update)50 PointParam::param_set_liveupdate( bool live_update)
51 {
52     liveupdate = live_update;
53 }
54 
55 Geom::Point
param_get_default() const56 PointParam::param_get_default() const{
57     return defvalue;
58 }
59 
60 void
param_update_default(Geom::Point default_point)61 PointParam::param_update_default(Geom::Point default_point)
62 {
63     defvalue = default_point;
64 }
65 
66 void
param_update_default(const gchar * default_point)67 PointParam::param_update_default(const gchar * default_point)
68 {
69     gchar ** strarray = g_strsplit(default_point, ",", 2);
70     double newx, newy;
71     unsigned int success = sp_svg_number_read_d(strarray[0], &newx);
72     success += sp_svg_number_read_d(strarray[1], &newy);
73     g_strfreev (strarray);
74     if (success == 2) {
75         param_update_default( Geom::Point(newx, newy) );
76     }
77 }
78 
79 void
param_hide_knot(bool hide)80 PointParam::param_hide_knot(bool hide) {
81     if (_knot_entity) {
82         bool update = false;
83         if (hide && _knot_entity->knot->flags & SP_KNOT_VISIBLE) {
84             update = true;
85             _knot_entity->knot->hide();
86         } else if(!hide && !(_knot_entity->knot->flags & SP_KNOT_VISIBLE)) {
87             update = true;
88             _knot_entity->knot->show();
89         }
90         if (update) {
91             _knot_entity->update_knot();
92         }
93     }
94 }
95 
96 void
param_setValue(Geom::Point newpoint,bool write)97 PointParam::param_setValue(Geom::Point newpoint, bool write)
98 {
99     *dynamic_cast<Geom::Point *>( this ) = newpoint;
100     if(write){
101         Inkscape::SVGOStringStream os;
102         os << newpoint;
103         gchar * str = g_strdup(os.str().c_str());
104         param_write_to_repr(str);
105         g_free(str);
106     }
107     if(_knot_entity && liveupdate){
108         _knot_entity->update_knot();
109     }
110 }
111 
112 bool
param_readSVGValue(const gchar * strvalue)113 PointParam::param_readSVGValue(const gchar * strvalue)
114 {
115     gchar ** strarray = g_strsplit(strvalue, ",", 2);
116     double newx, newy;
117     unsigned int success = sp_svg_number_read_d(strarray[0], &newx);
118     success += sp_svg_number_read_d(strarray[1], &newy);
119     g_strfreev (strarray);
120     if (success == 2) {
121         param_setValue( Geom::Point(newx, newy) );
122         return true;
123     }
124     return false;
125 }
126 
127 Glib::ustring
param_getSVGValue() const128 PointParam::param_getSVGValue() const
129 {
130     Inkscape::SVGOStringStream os;
131     os << *dynamic_cast<Geom::Point const *>( this );
132     return os.str();
133 }
134 
135 Glib::ustring
param_getDefaultSVGValue() const136 PointParam::param_getDefaultSVGValue() const
137 {
138     Inkscape::SVGOStringStream os;
139     os << defvalue;
140     return os.str();
141 }
142 
143 void
param_transform_multiply(Geom::Affine const & postmul,bool)144 PointParam::param_transform_multiply(Geom::Affine const& postmul, bool /*set*/)
145 {
146     param_setValue( (*this) * postmul, true);
147 }
148 
149 Gtk::Widget *
param_newWidget()150 PointParam::param_newWidget()
151 {
152     Inkscape::UI::Widget::RegisteredTransformedPoint * pointwdg = Gtk::manage(
153         new Inkscape::UI::Widget::RegisteredTransformedPoint( param_label,
154                                                               param_tooltip,
155                                                               param_key,
156                                                               *param_wr,
157                                                               param_effect->getRepr(),
158                                                               param_effect->getSPDoc() ) );
159     Geom::Affine transf = SP_ACTIVE_DESKTOP->doc2dt();
160     pointwdg->setTransform(transf);
161     pointwdg->setValue( *this );
162     pointwdg->clearProgrammatically();
163     pointwdg->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change point parameter"));
164     pointwdg->signal_button_release_event().connect(sigc::mem_fun (*this, &PointParam::on_button_release));
165 
166     Gtk::Box * hbox = Gtk::manage( new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL) );
167     hbox->pack_start(*pointwdg, true, true);
168     hbox->show_all_children();
169     return dynamic_cast<Gtk::Widget *> (hbox);
170 }
171 
on_button_release(GdkEventButton * button_event)172 bool PointParam::on_button_release(GdkEventButton* button_event) {
173     param_effect->refresh_widgets = true;
174     return false;
175 }
176 
177 void
set_oncanvas_looks(Inkscape::CanvasItemCtrlShape shape,Inkscape::CanvasItemCtrlMode mode,guint32 color)178 PointParam::set_oncanvas_looks(Inkscape::CanvasItemCtrlShape shape,
179                                Inkscape::CanvasItemCtrlMode mode,
180                                guint32 color)
181 {
182     knot_shape = shape;
183     knot_mode  = mode;
184     knot_color = color;
185 }
186 
187 class PointParamKnotHolderEntity : public KnotHolderEntity {
188 public:
PointParamKnotHolderEntity(PointParam * p)189     PointParamKnotHolderEntity(PointParam *p) { this->pparam = p; }
~PointParamKnotHolderEntity()190     ~PointParamKnotHolderEntity() override { this->pparam->_knot_entity = nullptr;}
191 
192     void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state) override;
193     Geom::Point knot_get() const override;
194     void knot_ungrabbed(Geom::Point const &p, Geom::Point const &origin, guint state) override;
195     void knot_click(guint state) override;
196 
197 private:
198     PointParam *pparam;
199 };
200 
201 void
knot_set(Geom::Point const & p,Geom::Point const & origin,guint state)202 PointParamKnotHolderEntity::knot_set(Geom::Point const &p, Geom::Point const &origin, guint state)
203 {
204     Geom::Point s = snap_knot_position(p, state);
205     if (state & GDK_CONTROL_MASK) {
206         Geom::Point A(origin[Geom::X],p[Geom::Y]);
207         Geom::Point B(p[Geom::X],origin[Geom::Y]);
208         double distanceA = Geom::distance(A,p);
209         double distanceB = Geom::distance(B,p);
210         if(distanceA > distanceB){
211             s = B;
212         } else {
213             s = A;
214         }
215     }
216     if(this->pparam->liveupdate){
217         pparam->param_setValue(s, true);
218     } else {
219         pparam->param_setValue(s);
220     }
221 }
222 
223 void
knot_ungrabbed(Geom::Point const & p,Geom::Point const & origin,guint state)224 PointParamKnotHolderEntity::knot_ungrabbed(Geom::Point const &p, Geom::Point const &origin, guint state)
225 {
226     pparam->param_effect->refresh_widgets = true;
227     pparam->write_to_SVG();
228 }
229 
230 Geom::Point
knot_get() const231 PointParamKnotHolderEntity::knot_get() const
232 {
233     return *pparam;
234 }
235 
236 void
knot_click(guint state)237 PointParamKnotHolderEntity::knot_click(guint state)
238 {
239     if (state & GDK_CONTROL_MASK) {
240         if (state & GDK_MOD1_MASK) {
241             this->pparam->param_set_default();
242             pparam->param_setValue(*pparam,true);
243         }
244     }
245 }
246 
247 void
addKnotHolderEntities(KnotHolder * knotholder,SPItem * item)248 PointParam::addKnotHolderEntities(KnotHolder *knotholder, SPItem *item)
249 {
250     _knot_entity = new PointParamKnotHolderEntity(this);
251     // TODO: can we ditch handleTip() etc. because we have access to handle_tip etc. itself???
252     _knot_entity->create(nullptr, item, knotholder, Inkscape::CANVAS_ITEM_CTRL_TYPE_LPE, "LPE:Point",
253                          handleTip(), knot_color);
254     knotholder->add(_knot_entity);
255 }
256 
257 } /* namespace LivePathEffect */
258 
259 } /* namespace Inkscape */
260 
261 /*
262   Local Variables:
263   mode:c++
264   c-file-style:"stroustrup"
265   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
266   indent-tabs-mode:nil
267   fill-column:99
268   End:
269 */
270 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
271