Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
node-toolbar.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
5/* Authors:
6 * MenTaLguY <mental@rydia.net>
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * bulia byak <buliabyak@users.sf.net>
9 * Frank Felfe <innerspace@iname.com>
10 * John Cliff <simarilius@yahoo.com>
11 * David Turner <novalis@gnu.org>
12 * Josh Andler <scislac@scislac.com>
13 * Jon A. Cruz <jon@joncruz.org>
14 * Maximilian Albert <maximilian.albert@gmail.com>
15 * Tavmjong Bah <tavmjong@free.fr>
16 * Abhishek Sharma
17 * Kris De Gussem <Kris.DeGussem@gmail.com>
18 * Vaibhav Malik <vaibhavmalik2018@gmail.com>
19 *
20 * Copyright (C) 2004 David Turner
21 * Copyright (C) 2003 MenTaLguY
22 * Copyright (C) 1999-2011 authors
23 * Copyright (C) 2001-2002 Ximian, Inc.
24 *
25 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
26 */
27
28#include "node-toolbar.h"
29
30#include <giomm/simpleactiongroup.h>
31#include <glibmm/i18n.h>
32#include <gtkmm/adjustment.h>
33#include <gtkmm/box.h>
34#include <gtkmm/button.h>
35#include <gtkmm/image.h>
36#include <gtkmm/menubutton.h>
37#include <gtkmm/togglebutton.h>
38#include <sigc++/functors/mem_fun.h>
39
40#include "desktop.h"
41#include "document-undo.h"
42#include "inkscape.h"
43#include "object/sp-namedview.h"
44#include "page-manager.h"
45#include "selection.h"
46#include "ui/builder-utils.h"
50#include "ui/tools/node-tool.h"
51#include "ui/util.h"
56
62
63namespace Inkscape::UI::Toolbar {
64
66 : NodeToolbar{create_builder("toolbar-node.ui")}
67{}
68
69NodeToolbar::NodeToolbar(Glib::RefPtr<Gtk::Builder> const &builder)
70 : Toolbar{get_widget<Gtk::Box>(builder, "node-toolbar")}
71 , _tracker{std::make_unique<UnitTracker>(Util::UNIT_TYPE_LINEAR)}
72 , _nodes_lpeedit_btn{get_widget<Gtk::Button>(builder, "_nodes_lpeedit_btn")}
73 , _show_helper_path_btn{&get_widget<Gtk::ToggleButton>(builder, "_show_helper_path_btn")}
74 , _show_handles_btn{&get_widget<Gtk::ToggleButton>(builder, "_show_handles_btn")}
75 , _show_transform_handles_btn{&get_widget<Gtk::ToggleButton>(builder, "_show_transform_handles_btn")}
76 , _object_edit_mask_path_btn{&get_widget<Gtk::ToggleButton>(builder, "_object_edit_mask_path_btn")}
77 , _object_edit_clip_path_btn{&get_widget<Gtk::ToggleButton>(builder, "_object_edit_clip_path_btn")}
78 , _nodes_x_item{get_derived_widget<UI::Widget::SpinButton>(builder, "_nodes_x_item")}
79 , _nodes_y_item{get_derived_widget<UI::Widget::SpinButton>(builder, "_nodes_y_item")}
80 , _nodes_d_item{get_derived_widget<UI::Widget::SpinButton>(builder, "_nodes_d_item")}
81 , _nodes_d_box{get_widget<Gtk::Box>(builder, "_nodes_d_box")}
82{
83 // Setup the derived spin buttons.
87
88 auto unit_menu = _tracker->create_tool_item(_("Units"), (""));
89 get_widget<Gtk::Box>(builder, "unit_menu_box").append(*unit_menu);
90
91 // Attach the signals.
92 struct ButtonMapping
93 {
94 char const *button_id;
95 void (NodeToolbar::*callback)();
96 };
97
98 static constexpr ButtonMapping button_mapping[] = {
99 {"insert_node_btn", &NodeToolbar::edit_add},
100 {"delete_btn", &NodeToolbar::edit_delete},
101 {"join_btn", &NodeToolbar::edit_join},
102 {"break_btn", &NodeToolbar::edit_break},
103 {"join_segment_btn", &NodeToolbar::edit_join_segment},
104 {"delete_segment_btn", &NodeToolbar::edit_delete_segment},
105 {"cusp_btn", &NodeToolbar::edit_cusp},
106 {"smooth_btn", &NodeToolbar::edit_smooth},
107 {"symmetric_btn", &NodeToolbar::edit_symmetrical},
108 {"auto_btn", &NodeToolbar::edit_auto},
109 {"line_btn", &NodeToolbar::edit_toline},
110 {"curve_btn", &NodeToolbar::edit_tocurve},
111 };
112
113 for (auto const &button_info : button_mapping) {
114 get_widget<Gtk::Button>(builder, button_info.button_id)
115 .signal_clicked()
116 .connect(sigc::mem_fun(*this, button_info.callback));
117 }
118
120
121 _pusher_show_outline = std::make_unique<SimplePrefPusher>(_show_helper_path_btn, "/tools/nodes/show_outline");
122 _show_helper_path_btn->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &NodeToolbar::on_pref_toggled),
123 _show_helper_path_btn, "/tools/nodes/show_outline"));
124
125 _pusher_show_handles = std::make_unique<SimplePrefPusher>(_show_handles_btn, "/tools/nodes/show_handles");
126 _show_handles_btn->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &NodeToolbar::on_pref_toggled),
127 _show_handles_btn, "/tools/nodes/show_handles"));
128
130 std::make_unique<SimplePrefPusher>(_show_transform_handles_btn, "/tools/nodes/show_transform_handles");
131 _show_transform_handles_btn->signal_toggled().connect(
132 sigc::bind(sigc::mem_fun(*this, &NodeToolbar::on_pref_toggled), _show_transform_handles_btn,
133 "/tools/nodes/show_transform_handles"));
134
135 _pusher_edit_masks = std::make_unique<SimplePrefPusher>(_object_edit_mask_path_btn, "/tools/nodes/edit_masks");
136 _object_edit_mask_path_btn->signal_toggled().connect(sigc::bind(
137 sigc::mem_fun(*this, &NodeToolbar::on_pref_toggled), _object_edit_mask_path_btn, "/tools/nodes/edit_masks"));
138
140 std::make_unique<SimplePrefPusher>(_object_edit_clip_path_btn, "/tools/nodes/edit_clipping_paths");
141 _object_edit_clip_path_btn->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &NodeToolbar::on_pref_toggled),
143 "/tools/nodes/edit_clipping_paths"));
144
146}
147
148NodeToolbar::~NodeToolbar() = default;
149
151{
152 if (_desktop) {
153 c_selection_changed.disconnect();
154 c_selection_modified.disconnect();
155 c_subselection_changed.disconnect();
156 }
157
159
160 if (_desktop) {
161 // watch selection
165 coord_changed(selection);
166 });
167
169 coord_changed(get_node_tool()->_selected_nodes);
170 }
171}
172
174{
175 _tracker->setActiveUnit(unit);
176}
177
179{
180 auto adj = btn.get_adjustment();
181 adj->set_value(0);
182 adj->signal_value_changed().connect(sigc::bind(sigc::mem_fun(*this, &NodeToolbar::value_changed), name, adj));
183
184 _tracker->addAdjustment(adj->gobj());
185 btn.addUnitTracker(_tracker.get());
186
187 btn.setDefocusTarget(this);
188}
189
191{
192 // insert_node_menu
193 auto const actions = Gio::SimpleActionGroup::create();
194 actions->add_action("insert-leftmost", sigc::mem_fun(*this, &NodeToolbar::edit_add_leftmost));
195 actions->add_action("insert-rightmost", sigc::mem_fun(*this, &NodeToolbar::edit_add_rightmost));
196 actions->add_action("insert-topmost", sigc::mem_fun(*this, &NodeToolbar::edit_add_topmost));
197 actions->add_action("insert-bottommost", sigc::mem_fun(*this, &NodeToolbar::edit_add_bottommost));
198 insert_action_group("node-toolbar", actions);
199}
200
201void NodeToolbar::value_changed(Glib::ustring const &name, Glib::RefPtr<Gtk::Adjustment> const &adj)
202{
203 // quit if run by the XML listener or a unit change
204 if (_blocker.pending() || _tracker->isUpdating()) {
205 return;
206 }
207
208 // in turn, prevent XML listener from responding
209 auto guard = _blocker.block();
210
211 auto const unit = _tracker->getActiveUnit();
212
213 auto nt = get_node_tool();
214 double val = Quantity::convert(adj->get_value(), unit, "px");
215 auto pwb = nt->_selected_nodes->pointwiseBounds();
216 auto fsp = nt->_selected_nodes->firstSelectedPoint();
217
218 if (name == "d") {
219 // Length has changed, not a coordinate...
220 double delta = val / pwb->diameter();
221
222 if (delta > 0) {
223 auto center = fsp ? *fsp : pwb->midpoint();
224 nt->_multipath->scale(center, {delta, delta});
225 }
226
227 } else if (nt && !nt->_selected_nodes->empty()) {
228 // Coordinate
229 auto d = name == "x" ? Geom::X : Geom::Y;
230 double oldval = pwb->midpoint()[d];
231
232 // Adjust the coordinate to the current page, if needed
233 auto &pm = _desktop->getDocument()->getPageManager();
235 auto page = pm.getSelectedPageRect();
236 oldval -= page.corner(0)[d];
237 }
238
240 delta[d] = val - oldval;
241 nt->_multipath->move(delta);
242 }
243}
244
246{
247 if (auto lpeitem = cast<SPLPEItem>(selection->singleItem())) {
248 _nodes_lpeedit_btn.set_sensitive(lpeitem->hasPathEffect());
249 } else {
250 _nodes_lpeedit_btn.set_sensitive(false);
251 }
252}
253
254void NodeToolbar::sel_modified(Selection *selection, guint /*flags*/)
255{
256 sel_changed(selection);
257}
258
259// is called when the node selection is modified
261{
262 // quit if run by the attr_changed listener
263 if (_blocker.pending()) {
264 return;
265 }
266
267 // in turn, prevent listener from responding
268 auto guard = _blocker.block();
269
270 if (!_tracker) {
271 return;
272 }
273 auto const unit = _tracker->getActiveUnit();
274
275 if (!selected_nodes || selected_nodes->empty()) {
276 // no path selected
277 _nodes_x_item.set_sensitive(false);
278 _nodes_y_item.set_sensitive(false);
279 } else {
280 _nodes_x_item.set_sensitive(true);
281 _nodes_y_item.set_sensitive(true);
282 auto adj_x = _nodes_x_item.get_adjustment();
283 auto adj_y = _nodes_y_item.get_adjustment();
284 Geom::Coord oldx = Quantity::convert(adj_x->get_value(), unit, "px");
285 Geom::Coord oldy = Quantity::convert(adj_y->get_value(), unit, "px");
286 Geom::Point mid = selected_nodes->pointwiseBounds()->midpoint();
287
288 // Adjust shown coordinate according to the selected page
290 auto &pm = _desktop->getDocument()->getPageManager();
291 mid *= pm.getSelectedPageAffine().inverse();
292 }
293
294 if (oldx != mid.x()) {
295 adj_x->set_value(Quantity::convert(mid.x(), "px", unit));
296 }
297 if (oldy != mid.y()) {
298 adj_y->set_value(Quantity::convert(mid.y(), "px", unit));
299 }
300 }
301
302 if (selected_nodes->size() == 2) {
303 // Length is only visible when exactly two nodes are selected
304 _nodes_d_box.set_visible(true);
305 auto adj_l = _nodes_d_item.get_adjustment();
306 Geom::Coord oldl = Quantity::convert(adj_l->get_value(), unit, "px");
307
308 Geom::Coord length = selected_nodes->pointwiseBounds()->diameter();
309 if (oldl != length) {
310 adj_l->set_value(Quantity::convert(length, "px", unit));
311 }
312 } else {
313 _nodes_d_box.set_visible(false);
314 }
315}
316
318{
319 if (auto nt = get_node_tool()) {
320 nt->_multipath->insertNodes();
321 }
322}
323
324/* add a node at the left-most point on selected path(s)*/
326{
327 if (auto nt = get_node_tool()) {
328 nt->_multipath->insertNodesAtExtrema(PointManipulator::EXTR_MIN_X);
329 }
330}
331
332/* add a node at the right-most point on selected path(s)*/
334{
335 if (auto nt = get_node_tool()) {
336 nt->_multipath->insertNodesAtExtrema(PointManipulator::EXTR_MAX_X);
337 }
338}
339
340/* add a node at the top-most point on selected path(s)*/
342{
343 const auto extrema = _desktop->is_yaxisdown()
346 if (auto nt = get_node_tool()) {
347 nt->_multipath->insertNodesAtExtrema(extrema);
348 }
349}
350
351/* add a node at the bottom-most point on selected path(s)*/
353{
354 const auto extrema = _desktop->is_yaxisdown()
357 if (auto nt = get_node_tool()) {
358 nt->_multipath->insertNodesAtExtrema(extrema);
359 }
360}
361
363{
364 if (auto nt = get_node_tool()) {
365 auto prefs = Preferences::get();
366 nt->_multipath->deleteNodes((NodeDeleteMode)prefs->getInt("/tools/node/delete-mode-default", (int)NodeDeleteMode::automatic));
367 }
368}
369
371{
372 if (auto nt = get_node_tool()) {
373 nt->_multipath->joinNodes();
374 }
375}
376
378{
379 if (auto nt = get_node_tool()) {
380 nt->_multipath->breakNodes();
381 }
382}
383
385{
386 if (auto nt = get_node_tool()) {
387 nt->_multipath->deleteSegments();
388 }
389}
390
392{
393
394 if (auto nt = get_node_tool()) {
395 nt->_multipath->joinSegments();
396 }
397}
398
400{
401 if (auto nt = get_node_tool()) {
402 nt->_multipath->setNodeType(NODE_CUSP);
403 }
404}
405
407{
408 if (auto nt = get_node_tool()) {
409 nt->_multipath->setNodeType(NODE_SMOOTH);
410 }
411}
412
414{
415 if (auto nt = get_node_tool()) {
416 nt->_multipath->setNodeType(NODE_SYMMETRIC);
417 }
418}
419
421{
422 if (auto nt = get_node_tool()) {
423 nt->_multipath->setNodeType(NODE_AUTO);
424 }
425}
426
428{
429 if (auto nt = get_node_tool()) {
430 nt->_multipath->setSegmentType(SEGMENT_STRAIGHT);
431 }
432}
433
435{
436 if (auto nt = get_node_tool()) {
437 nt->_multipath->setSegmentType(SEGMENT_CUBIC_BEZIER);
438 }
439}
440
441void NodeToolbar::on_pref_toggled(Gtk::ToggleButton *item, Glib::ustring const &path)
442{
443 Preferences::get()->setBool(path, item->get_active());
444}
445
447{
448 return dynamic_cast<NodeTool *>(_desktop->getTool());
449}
450
451} // namespace Inkscape::UI::Toolbar
452
453/*
454 Local Variables:
455 mode:c++
456 c-file-style:"stroustrup"
457 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
458 indent-tabs-mode:nil
459 fill-column:99
460 End:
461*/
462// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
Gtk builder utilities.
uint64_t page
Definition canvas.cpp:171
CPoint corner(unsigned i) const
Return the n-th corner of the rectangle.
Two-dimensional point that doubles as a vector.
Definition point.h:66
constexpr Coord y() const noexcept
Definition point.h:106
constexpr Coord x() const noexcept
Definition point.h:104
SPItem * singleItem()
Returns a single selected item.
Geom::Rect getSelectedPageRect() const
Returns the selected page rect, OR the viewbox rect.
static Preferences * get()
Access the singleton Preferences object.
void setBool(Glib::ustring const &pref_path, bool value)
Set a Boolean value.
The set of selected SPObjects for a given document and layer model.
Definition selection.h:80
sigc::connection connectChanged(sigc::slot< void(Selection *)> slot)
Connects a slot to be notified of selection changes.
Definition selection.h:179
sigc::connection connectModified(sigc::slot< void(Selection *, unsigned)> slot)
Connects a slot to be notified of selected object modifications.
Definition selection.h:232
Group of selected control points.
Geom::OptRect pointwiseBounds()
Get the bounds of the selection.
Gtk::ToggleButton * _object_edit_mask_path_btn
void setup_derived_spin_button(UI::Widget::SpinButton &btn, Glib::ustring const &name)
UI::Widget::SpinButton & _nodes_d_item
void value_changed(Glib::ustring const &name, Glib::RefPtr< Gtk::Adjustment > const &adj)
void sel_modified(Selection *selection, unsigned flags)
Gtk::ToggleButton * _object_edit_clip_path_btn
void setDesktop(SPDesktop *desktop) override
std::unique_ptr< UI::SimplePrefPusher > _pusher_show_transform_handles
sigc::connection c_subselection_changed
Gtk::ToggleButton * _show_helper_path_btn
Gtk::ToggleButton * _show_transform_handles_btn
void sel_changed(Selection *selection)
std::unique_ptr< UI::SimplePrefPusher > _pusher_edit_masks
UI::Widget::SpinButton & _nodes_x_item
std::unique_ptr< UI::SimplePrefPusher > _pusher_show_outline
UI::Widget::SpinButton & _nodes_y_item
void coord_changed(ControlPointSelection *selected_nodes)
Tools::NodeTool * get_node_tool() const
std::unique_ptr< UI::Widget::UnitTracker > _tracker
std::unique_ptr< UI::SimplePrefPusher > _pusher_edit_clipping_paths
void on_pref_toggled(Gtk::ToggleButton *item, Glib::ustring const &path)
std::unique_ptr< UI::SimplePrefPusher > _pusher_show_handles
Gtk::ToggleButton * _show_handles_btn
void setActiveUnit(Util::Unit const *unit) override
Base class for all tool toolbars.
Definition toolbar.h:72
virtual void setDesktop(SPDesktop *desktop)
Definition toolbar.h:76
SpinButton widget, that allows entry of simple math expressions (also units, when linked with UnitMen...
Definition spinbutton.h:52
void addUnitTracker(UnitTracker *ut)
Definition spinbutton.h:68
void setDefocusTarget(decltype(_defocus_target) target)
Definition spinbutton.h:127
scoped_block block()
To do: update description of desktop.
Definition desktop.h:149
SPDocument * getDocument() const
Definition desktop.h:189
Inkscape::Selection * getSelection() const
Definition desktop.h:188
Inkscape::UI::Tools::ToolBase * getTool() const
Definition desktop.h:187
sigc::connection connect_control_point_selected(sigc::slot< void(Inkscape::UI::ControlPointSelection *)> const &slot)
Definition desktop.cpp:1353
bool is_yaxisdown() const
Definition desktop.h:427
bool get_origin_follows_page()
Inkscape::PageManager & getPageManager()
Definition document.h:162
A combobox that can be displayed in a toolbar.
Control point selection - stores a set of control points and applies transformations to them.
Editable view implementation.
TODO: insert short description here.
double Coord
Floating point type used to store coordinates.
Definition coord.h:76
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
SPItem * item
Multi path manipulator - a tool component that edits multiple paths at once.
Definition desktop.h:50
W & get_widget(const Glib::RefPtr< Gtk::Builder > &builder, const char *id)
@ NODE_CUSP
Cusp node - no handle constraints.
Definition node-types.h:21
@ NODE_SYMMETRIC
Symmetric node - handles must be colinear and of equal length.
Definition node-types.h:24
@ NODE_AUTO
Auto node - handles adjusted automatically based on neighboring nodes.
Definition node-types.h:23
@ NODE_SMOOTH
Smooth node - handles must be colinear.
Definition node-types.h:22
W & get_derived_widget(const Glib::RefPtr< Gtk::Builder > &builder, const char *id, Args &&... args)
Glib::RefPtr< Gtk::Builder > create_builder(const char *filename)
@ SEGMENT_STRAIGHT
Straight linear segment.
Definition node-types.h:31
@ SEGMENT_CUBIC_BEZIER
Bezier curve with two control points.
Definition node-types.h:32
Miscellaneous supporting code.
Definition document.h:93
STL namespace.
New node tool with support for multiple path editing.
int delta
SPDesktop * desktop
Glib::ustring name
Definition toolbars.cpp:55
Glib::RefPtr< Gtk::Builder > builder
TODO: insert short description here.