75#include <glibmm/i18n.h>
76#include <glibmm/stringutils.h>
77#include <gdk/gdkkeysyms.h>
117 auto const name = g_quark_to_string(name_);
119 if (!strcmp(
name,
"d") || !strcmp(
name,
"x") || !strcmp(
name,
"y") ||
120 !strcmp(
name,
"width") || !strcmp(
name,
"height") ||
121 !strcmp(
name,
"transform")) {
122 if (&repr == tool->active_shape_repr) {
124 tool->cc_clear_active_shape();
125 }
else if (&repr == tool->active_conn_repr) {
128 tool->cc_set_active_conn(tool->active_conn);
137 if (&
child == tool->active_shape_repr) {
185 if (prefs->
getBool(
"/tools/connector/selcue",
false)) {
220 this->
shref =
nullptr;
225 this->
shref =
nullptr;
237 if (
name ==
"curvature") {
239 }
else if (
name ==
"orthogonal") {
274 for (
auto & it : k) {
306 if (this->
active_handle && (this->
knots.find(this->active_handle) != this->knots.end())) {
312 *subhref = g_strdup_printf(
"#%s",
id);
326 knot->
ctrl->set_selected(
true);
334 knot->
ctrl->set_selected(
false);
346 if (event.button == 1) {
347 if ((this->state == SP_CONNECTOR_CONTEXT_DRAGGING) && this->within_tolerance) {
348 this->_resetColors();
349 this->state = SP_CONNECTOR_CONTEXT_IDLE;
377 auto const item = _desktop->getItemAtPoint(event.pos,
false);
379 _setActiveShape(
item);
394 if (event.num_press == 1) {
395 ret = _handleButtonPress(event);
399 ret = _handleMotionNotify(event);
402 ret = _handleButtonRelease(event);
410 return ret || ToolBase::root_handler(event);
426 auto const event_w = bevent.
pos;
428 saveDragOrigin(event_w);
430 Geom::Point const event_dt = _desktop->w2d(event_w);
434 switch (this->state) {
440 if ( this->npoints == 0 ) {
441 this->cc_clear_active_conn();
450 bool found = this->_ptHandleTest(p, &this->shref, &this->sub_shref);
459 this->_setInitialPoint(p);
473 this->_setSubsequentPoint(p);
474 this->_finishSegment(p);
476 this->_ptHandleTest(p, &this->ehref, &this->sub_ehref);
477 if (this->npoints != 0) {
480 this->cc_set_active_conn(this->newconn);
487 g_warning(
"Button down in CLOSE state");
493 }
else if (bevent.
button == 3) {
497 this->_reroutingFinish(&p);
503 }
else if (this->npoints != 0) {
517 if (mevent.
modifiers & (GDK_BUTTON2_MASK | GDK_BUTTON3_MASK)) {
522 auto const event_w = mevent.
pos;
524 if (!checkDragMoved(event_w)) {
533 switch (this->state) {
538 if ( this->npoints > 0 ) {
542 this->selection->clear();
543 this->_setSubsequentPoint(p);
551 g_assert(is<SPPath>(clickeditem));
558 auto i2d = clickeditem->i2dt_affine();
559 auto d2i = i2d.inverse();
560 auto path = cast<SPPath>(clickeditem);
561 auto curve = *path->curve();
562 if (clickedhandle == endpt_handle[0]) {
563 auto o = endpt_handle[1]->pos;
564 curve.stretch_endpoints(p * d2i, o * d2i);
566 auto o = endpt_handle[0]->pos;
567 curve.stretch_endpoints(o * d2i, p * d2i);
569 path->setCurve(std::move(
curve));
573 red_curve = path->curveForEdit()->transformed(i2d);
574 red_bpath->set_bpath(&*red_curve);
583 if (!this->sp_event_context_knot_mouseover()) {
601 auto const event_w = revent.
pos;
606 switch (this->state) {
614 if (this->within_tolerance) {
615 this->_finishSegment(p);
619 this->_setSubsequentPoint(p);
620 this->_finishSegment(p);
622 this->_ptHandleTest(p, &this->ehref, &this->sub_ehref);
623 if (this->npoints != 0) {
626 this->cc_set_active_conn(this->newconn);
635 this->_reroutingFinish(&p);
653bool ConnectorTool::_handleKeyPress(guint
const keyval)
659 case GDK_KEY_KP_Enter:
660 if (this->npoints != 0) {
670 this->_reroutingFinish(
nullptr);
676 _(
"Connector endpoint drag cancelled."));
678 }
else if (this->npoints != 0) {
681 this->_resetColors();
696 this->red_curve->reset();
697 red_bpath->set_bpath(
nullptr);
703 bool found = this->_ptHandleTest(*p, &shape_label, &sub_label);
706 if (this->clickedhandle == this->endpt_handle[0]) {
707 this->clickeditem->setAttribute(
"inkscape:connection-start", shape_label);
708 this->clickeditem->setAttribute(
"inkscape:connection-start-point", sub_label);
710 this->clickeditem->setAttribute(
"inkscape:connection-end", shape_label);
711 this->clickeditem->setAttribute(
"inkscape:connection-end-point", sub_label);
719 this->clickeditem->setHidden(
false);
721 this->clickeditem->updateRepr();
723 this->cc_set_active_conn(this->clickeditem);
727void ConnectorTool::_resetColors()
730 this->red_curve->reset();
731 red_bpath->set_bpath(
nullptr);
733 this->green_curve->reset();
739 g_assert( this->npoints == 0 );
744 red_bpath->set_bpath(
nullptr);
749 g_assert( this->npoints != 0 );
756 if (!this->newConnRef) {
757 Avoid::Router *router = _desktop->getDocument()->getRouter();
760 if (this->isOrthogonal) {
769 this->newConnRef->makePathInvalid();
770 this->newConnRef->router()->processTransaction();
773 red_curve->transform(_desktop->doc2dt());
774 red_bpath->set_bpath(&*red_curve,
true);
783void ConnectorTool::_concatColorsAndFlush()
785 auto c = std::make_optional<SPCurve>();
786 std::swap(
c, green_curve);
789 red_bpath->set_bpath(
nullptr);
810 c->transform(_desktop->dt2doc());
815 if ( !
c->is_empty() ) {
825 auto layer = currentLayer();
826 this->newconn = cast<SPItem>(layer->appendChildRepr(repr));
827 this->newconn->transform = layer->i2doc_affine().inverse();
829 bool connection =
false;
830 this->newconn->setAttribute(
"inkscape:connector-type",
831 this->isOrthogonal ?
"orthogonal" :
"polyline");
832 this->newconn->setAttribute(
"inkscape:connector-curvature",
833 Glib::Ascii::dtostr(this->
curvature).c_str());
836 this->newconn->setAttribute(
"inkscape:connection-start", this->shref);
837 if(this->sub_shref) {
838 this->newconn->setAttribute(
"inkscape:connection-start-point", this->sub_shref);
844 this->newconn->setAttribute(
"inkscape:connection-end", this->ehref);
845 if(this->sub_ehref) {
846 this->newconn->setAttribute(
"inkscape:connection-end-point", this->sub_ehref);
850 this->newconn->updateRepr();
856 this->newconn->updateRepr();
859 this->newconn->doWriteTransform(this->newconn->transform,
nullptr,
true);
865 this->selection->set(repr);
875 if (!this->red_curve->is_empty()) {
876 green_curve->append_continuous(*red_curve);
878 this->p[0] = this->p[3];
879 this->p[1] = this->p[4];
882 this->red_curve->reset();
886void ConnectorTool::_finish()
890 this->red_curve->reset();
891 this->_concatColorsAndFlush();
895 if (this->newConnRef) {
896 this->newConnRef->router()->deleteConnector(this->newConnRef);
897 this->newConnRef =
nullptr;
910 bool consumed =
false;
916 cc->active_handle = knot;
928 cc->active_handle =
nullptr;
945 bool consumed =
false;
992 auto use = cast<SPUse>(
item);
993 g_assert(use !=
nullptr);
1015 this->knots[knot] = 1;
1020 g_assert(
item !=
nullptr );
1022 if (this->active_shape !=
item) {
1025 this->active_shape =
item;
1027 if (this->active_shape_repr) {
1028 this->active_shape_repr->removeObserver(shapeNodeObserver());
1031 this->active_shape_layer_repr->removeObserver(layerNodeObserver());
1037 if (this->active_shape_repr) {
1039 this->active_shape_repr->addObserver(shapeNodeObserver());
1041 this->active_shape_layer_repr = this->active_shape_repr->parent();
1043 this->active_shape_layer_repr->addObserver(layerNodeObserver());
1051 if(
child.getAttribute(
"inkscape:connector")) {
1052 this->_activeShapeAddKnot((
SPItem *) &
child,
nullptr);
1056 if (
auto use = cast<SPUse>(
item)) {
1060 if(
child.getAttribute(
"inkscape:connector")) {
1066 this->_activeShapeAddKnot(
item,
nullptr);
1077 g_assert( is<SPPath>(
item) );
1082 if (this->active_conn ==
item) {
1083 if (
curve->is_empty()) {
1086 this->endpt_handle[0]->hide();
1087 this->endpt_handle[1]->hide();
1091 this->endpt_handle[0]->setPosition(startpt, 0);
1094 this->endpt_handle[1]->setPosition(endpt, 0);
1100 this->active_conn =
item;
1103 if (this->active_conn_repr) {
1104 this->active_conn_repr->removeObserver(shapeNodeObserver());
1106 this->active_conn_repr =
nullptr;
1111 if (this->active_conn_repr) {
1113 this->active_conn_repr->addObserver(shapeNodeObserver());
1116 for (
int i = 0; i < 2; ++i) {
1118 if ( this->endpt_handle[i] ==
nullptr ) {
1120 _(
"<b>Connector endpoint</b>: drag to reroute or connect to new shapes"),
1133 this->endpt_handle[i] = knot;
1137 this->endpt_handler_connection[i].disconnect();
1138 this->endpt_handler_connection[i] =
1139 this->endpt_handle[i]->
ctrl->connect_event(sigc::bind(sigc::ptr_fun(
endpt_handler),
this));
1142 if (
curve->is_empty()) {
1149 this->endpt_handle[0]->setPosition(startpt, 0);
1152 this->endpt_handle[1]->setPosition(endpt, 0);
1154 this->endpt_handle[0]->show();
1155 this->endpt_handle[1]->show();
1166 "CanvasItemCtrl::ConnectorTool:ConnectionPoint");
1180 if (
auto path = cast<SPPath>(
item)) {
1186 }
else if (is<SPText>(
item) || is<SPFlowtext>(
item)) {
1188 if (prefs->
getBool(
"/tools/connector/ignoretext",
true)) {
1199 if (
auto path = cast<SPPath>(
item)) {
1200 bool closed = path->curveForEdit()->is_closed();
1201 if (path->connEndPair.isAutoRoutingConn() && !closed) {
1225 char const *value = (set_avoid) ?
"true" :
nullptr;
1236 _(
"Select <b>at least one non-connector object</b>."));
1240 char *event_desc = (set_avoid) ?
1241 _(
"Make connectors avoid selected objects") :
1242 _(
"Make connectors ignore selected objects");
1249 if (this->active_conn ==
item) {
1254 if (
item ==
nullptr) {
1255 this->cc_clear_active_conn();
1260 this->cc_set_active_conn(
item);
The ConnRef class represents a connector object.
The Point class defines a point in the plane.
The Router class represents a libavoid router instance.
static const unsigned short tar
static const unsigned short src
3x3 matrix representing an affine transformation.
Two-dimensional point that doubles as a vector.
void set_bpath(SPCurve const *curve, bool phantom_line=false)
Set a control bpath.
void set_fill(uint32_t rgba, SPWindRule fill_rule)
Set the fill color and fill rule.
virtual void set_stroke(uint32_t rgba)
static void done(SPDocument *document, Glib::ustring const &event_description, Glib::ustring const &undo_icon, unsigned int object_modified_tag=0)
static gboolean undo(SPDocument *document)
void clear()
removes our current message from the stack
MessageId flash(MessageType type, char const *message)
Temporarily pushes a message onto the stack.
SPItemRange items()
Returns a range of selected SPItems.
void toggle(SPObject *obj)
Removes an item if selected, adds otherwise.
SPItem * singleItem()
Returns a single selected item.
Data type representing a typeless value of a preference.
Glib::ustring getEntryName() const
Get the last component of the preference's path.
bool getBool(bool def=false) const
Interpret the preference as a Boolean value.
double getDoubleLimited(double def=0.0, double min=DBL_MIN, double max=DBL_MAX, Glib::ustring const &unit="") const
Interpret the preference as a limited floating point value.
Preference storage class.
bool getBool(Glib::ustring const &pref_path, bool def=false)
Retrieve a Boolean value.
static Preferences * get()
Access the singleton Preferences object.
The set of selected SPObjects for a given document and layer model.
void set(XML::Node *repr)
Set the selection to an XML node's SPObject.
sigc::connection connectChanged(sigc::slot< void(Selection *)> slot)
Connects a slot to be notified of selection changes.
Class to store data for points which are snap candidates, either as a source or as a target.
Interface for refcounted XML nodes.
void setAttribute(Util::const_char_ptr key, Util::const_char_ptr value)
Change an attribute of this node.
virtual void removeObserver(NodeObserver &observer)=0
Remove an object from the list of observers.
Geom::Point getConnectionPointPos()
void handleSettingChange()
static SPCurve createCurve(Avoid::ConnRef *connRef, double curvature)
Wrapper around a Geom::PathVector object.
To do: update description of desktop.
Inkscape::UI::Widget::Canvas * getCanvas() const
SPDocument * getDocument() const
Inkscape::CanvasItemGroup * getCanvasSketch() const
Inkscape::MessageStack * messageStack() const
Inkscape::Selection * getSelection() const
Inkscape::UI::Tools::ToolBase * getTool() const
Typed SVG document implementation.
Inkscape::XML::Document * getReprDoc()
Our Inkscape::XML::Document.
int ensureUpToDate(unsigned int object_modified_tag=0)
Repeatedly works on getting the document updated, since sometimes it takes more than one pass to get ...
Base class for visual SVG elements.
Geom::Affine i2dt_affine() const
Returns the transformation from item to desktop coords.
void setHidden(bool hidden)
SPAvoidRef & getAvoidRef()
Desktop-bound visual control object.
static void ref(SPKnot *knot)
SPItem * owner
Optional Owner Item.
void setPosition(Geom::Point const &p, unsigned int state)
Move knot to new position and emits "moved" signal.
Geom::Point pos
Our desktop coordinates.
void setSize(Inkscape::HandleSize size)
CanvasItemPtr< Inkscape::CanvasItemCtrl > ctrl
Our CanvasItemCtrl.
static void unref(SPKnot *knot)
void setAnchor(unsigned int i)
sigc::scoped_connection _event_connection
void show()
Show knot on its canvas.
void setFlag(unsigned int flag, bool set)
Set flag in knot, with side effects.
void updateCtrl()
Update knot's control state.
SPItem * sub_owner
Optional SubOwner Item.
SPDesktop * desktop
Desktop we are on.
void setAttribute(Inkscape::Util::const_char_ptr key, Inkscape::Util::const_char_ptr value)
char const * getId() const
Returns the objects current ID string.
Inkscape::XML::Node * getRepr()
Returns the XML representation of tree.
char const * getAttribute(char const *name) const
SVG <path> implementation.
Class to coordinate snapping operations.
void setup(SPDesktop const *desktop, bool snapindicator=true, SPObject const *item_to_ignore=nullptr, std::vector< Inkscape::SnapCandidatePoint > *unselected_nodes=nullptr)
Convenience shortcut when there is only one item to ignore.
void freeSnapReturnByRef(Geom::Point &p, Inkscape::SnapSourceType const source_type, Geom::OptRect const &bbox_to_snap=Geom::OptRect()) const
Try to snap a point to grids, guides or objects.
void preSnap(Inkscape::SnapCandidatePoint const &p, bool to_path_only=false)
SPNamedView const * getNamedView() const
void sp_desktop_apply_style_tool(SPDesktop *desktop, Inkscape::XML::Node *repr, Glib::ustring const &tool_path, bool with_text)
Apply the desktop's current style or the tool style to repr.
Editable view implementation.
TODO: insert short description here.
Macro for icon names used in Inkscape.
Declarations for SPKnot: Desktop-bound visual control object.
Interface for locally managing a current status message.
Raw stack of active status messages.
@ ConnType_Orthogonal
The connector path will be a shortest-path orthogonal poly-line (only vertical and horizontal line se...
@ ConnType_PolyLine
The connector path will be a shortest-path poly-line that routes around obstacles.
static R & anchor(R &r)
Increments the reference count of a anchored object.
static R & release(R &r)
Decrements the reference count of a anchored object.
void inspect_event(E &&event, Fs... funcs)
Perform pattern-matching on a CanvasEvent.
@ SNAPSOURCE_OTHER_HANDLE
@ CANVAS_ITEM_CTRL_TYPE_SHAPER
bool have_viable_layer(SPDesktop *desktop, MessageContext *message)
Check to see if the current layer is both unhidden and unlocked.
Contains the interface for the Router class.
double curvature(Point const &a, Point const &b, Point const &c)
void sp_conn_reroute_path_immediate(SPPath *const path)
void sp_conn_end_detach(SPObject *const owner, unsigned const handle_ix)
TODO: insert short description here.
TODO: insert short description here.
Abstract base class for events.
unsigned modifiers
The modifiers mask immediately before the event.
The pointer has entered a widget or item.
The pointer has exited a widget or item.
Movement of the mouse pointer.
Geom::Point pos
Location of the cursor.
Interface for XML documents.
virtual Node * createElement(char const *name)=0
static void sp_svg_write_path(Inkscape::SVG::PathString &str, Geom::Path const &p, bool normalize=false)