@doc hierarchy="GMLDOM">
Aspect for drawing zoomable connection pins
(c) SAP AG 2003-2006. All rights reserved.
#INCLUDE[gml:defs.inc]
Aspect ZPin for ZDrawing;
constructor(shape)
this.id = base.id;
this.shapeID = shape.id;
this.board = shape.board;
this.canvas= this.board.canvas;
this.lines = {};
this.canvas.setWidget(this);
this.isInout = ((base.@dir & #[SVG_INOUT]) != 0);
this.isInward = ((base.@dir & #[SVG_INWARD]) != 0);
this.isOutward = ((base.@dir & #[SVG_OUTWARD]) != 0);
this.isDynamic = ((base.@symbol & #[SVG_DYNAMIC_PIN]) != 0);
this.isAutohide = ((base.@anchor & #[SVG_AUTOHIDE]) != 0);
this.isFilled = (!(base.@symbol & (#[SVG_HOLLOW|SVG_FILLED]))) ? this.isOutward : !(base.@symbol & #[SVG_HOLLOW]); // TODO: make inout pin half-hollow, half-filled
this.pinColor = base.@strokeColor || shape.base.@strokeColor;
this.fillColor = this.isFilled ? this.pinColor : 'white';
this.hilightColor = shape.getDiagram().base.@hilightColor
this.paint();
end
destructor
if (this.isAnimated) SVG.clearElement(this.pinNode);
SVG.removeElement(this.pinNode);
this.pinNode = null;
if (this.pinText) {
SVG.removeElement(this.pinText);
this.pinText = null;
this.pinTextClip = null;
}
this.canvas.removeWidget(this.id);
//TODO: delete this['base'];
end
virtual method getShape()
return this.canvas.getWidget(this.shapeID);
end
//////////////////////////////////////////////////////////////////////////////////////
// BASE PROPERTIES
<@doc type="ENUM" group="Instance Properties">Gets or sets the pin's anchor point
The ~anchor property is a bitwise combination of the following groups of flags:
Name | Description
SVG_LEFT | Anchor to left edge
SVG_RIGHT | Anchor to right edge
SVG_TOP | Anchor to top edge
SVG_BOTTOM | Anchor to bottom edge
|
SVG_START | Anchor to edge's start point
SVG_MIDDLE | Anchor to edge's middle point
SVG_END | Anchor to edge's end point
|
SVG_AUTO | Anchor point is calculated automatically
|
SVG_AUTOHIDE | The pin is hiden by default, but is displayed when selected
When SVG_AUTO flag is specified, the actual anchor point is calculated basing on SVG_START/SVG_MIDDLE/SVG_END flags
and the index of this pin among other pins with the same anchor settings. Typicially, pins created earlier
will be positioned before pins created later.
property anchor = 0;
<@doc type="n">Defines the pin's vertical offset from the anchor point
// TODO: We'd better create a way to define vertical and horizontal offset, if needed
property offset = 0;
<@doc type="Object">Defines the pin's direction
The ~dir property can be one of the following flags:
Name | Description
SVG_INOUT | In/out pin
SVG_INWARD | Inward pin
SVG_OUTWARD | Outward pin
readonly property dir = 0;
<@doc type="n">Defines the pin's connection radius
The connection ~radius is the distance between the exact point where lines connect to the pin and
the pin's origin. Use this property to override the default connection radius defined on the pin's
symbol, in order to accomodate for the geometry of the containing shape aspect.
readonly property radius = 0;
<@doc type="RGB">Defines the pin's stroke color
If the pin color is not specified, the stroke color of the containing shape will be used.
readonly property strokeColor = '';
<@doc>
Defines the behavior of the pin text when it becomes larger than approximatelly half of the parent shape.
This property is only used when the pin has text defined for it.
This property can have one of the following possible values:
Name | Description
SVG_OVERFLOW_VISIBLE | Entire text is visible (default)
SVG_OVERFLOW_FADEOUT | The overflowed part of the text is shown faded out
readonly property textOverflow = #[SVG_OVERFLOW_VISIBLE];
<@doc type="@PIN_SYMBOL">Defines the pin's graphical symbol
property symbol = 0;
<@struct name="PIN_SYMBOL" group="Structures">
A bitwise structure for defining pin symbols
A pin symbol can be any bitwise combination of the following groups of flags:
Name | Description
SVG_CLASSIC_PIN | Classic pin symbol (default)
SVG_DIAMOND_PIN | Diamond symbol
SVG_CIRCLE_PIN | Circle symbol
SVG_SQUARE_PIN | Square symbol
SVG_TRIANGLE_PIN | Triangle symbol
SVG_CUSTOM_PIN | Custom symbol defined by the @ZShape!pinSymbols property on the containing shape (using the first 8 bits as index)
SVG_DYNAMIC_PIN | Pin symbol is calculated dynamically, basing on @dynsymbol property
|
SVG_HOLLOW | The symbol is hollow, i.e. filled with white color (default for input pins).
SVG_FILLED | The symbol is filled with the primary color (default for output pins).
|
SVG_NOTEXT | The pin text label is hidden
SVG_NOSYMBOL | The pin has no symbol
SVG_HIDDEN | The pin has no text and no symbol, making it invisible (combines SVG_NOTEXT|SVG_NOSYMBOL)
<@doc>
Defines the dynamic formula that evaluates to pin symbol.
This property is evaluated only if @symbol property has SVG_DYNAMIC_PIN flag set.
A dynamic formula is a string containing a valid JavaScript expression that involves one or more GML property references.
A GML property reference is written by adding the \@ prefix (e.g., \@pinSymbol or \@Class.metadata.pinSymbol).
static readonly property dynsymbol = '';
//////////////////////////////////////////////////////////////////////////////////////
// ASPECT PROPERTIES
<@doc scope="private">Gets the pin's Id
virtual property id = '';
// Pin colors
<@doc type="RGB" scope="private">Gets the pin's color (inerited from the stroke color)
virtual property pinColor = '';
<@doc type="RGB" scope="private">Gets the pin's fill color (derived from the pin color and symbol type)
virtual property fillColor = '';
<@doc type="RGB" scope="private">Gets the pin's hilight color (inherited from the diagram hilight color)
virtual property hilightColor = '';
// Associated objects
<@doc type="bool[id]" scope="private">Gets the collection of lines connected to this pin
virtual property lines = null;
<@doc type="@ZShape!" scope="private">Gets the owner shape object
virtual property shape = null;
// Status Flags
<@doc type="b" scope="private">Indicates that this graphic is a connection pin (for quick tests)
virtual property isPin = true;
<@doc scope="private">Indicates whether the pin is animated
virtual property isAnimated = false;
<@doc scope="private">Indicates whether the pin is filled (otherwise, hollow)
virtual property isFilled = false;
<@doc type="b" scope="private">Indicates that the pin is hidden by default, and shown when selected
virtual property isAutohide = false;
<@doc type="b" scope="private">Indicates that the pin is facing inward and outward
virtual property isInout = false;
<@doc type="b" scope="private">Indicates that the pin is facing inward
virtual property isInward = false;
<@doc type="b" scope="private">Indicates that the pin is facing outward
virtual property isOutward = false;
<@doc scope="private">Indicates whether the pin is currently selected
virtual property isSelected = false;
<@doc type="b" scope="private">Indicates that this pin is candidate for wiring
virtual property isWireTarget = false;
<@doc scope="private">Indicates whether the pin symbol is dynamic
virtual property isDynamic = false;
// SVG painting objects
<@doc type="SVGNode" scope="private">Gets the pin's symbol svg node
virtual property pinNode = null;
<@doc type="SVGNode" scope="private">Gets the pin's text svg node
virtual property pinText = null;
<@doc type="SVGNode" scope="private">Gets the pin's text clipping svg node
virtual property pinTextClip = null;
// Geometry properties
<@doc scope="private">Gets the pin's effective radius (as defined on the symbol or overridden by the base object)
virtual property rad = 0;
<@doc scope="private">Gets the pin's rotation angle (in multiples of 90 degrees counterclockwise)
The pin's rotation angle is defined as the rotation angle of the imaginary line that
emanates from the pin's connection point outward, and is perpendicular to the side of
the shape on which the pin is placed (after accounting for the rotation angle and flip state
of the containing shape).
The resulting value of this property is: 0=right(0 degrees), 1=top(90 degrees), 2=left(180 degrees), 3=bottom(270 degrees)
virtual property rot = 0;
<@doc scope="private">Gets the x-coordinate of the pin's connection point
The pin's connection point is the precise point to which the pin's lines should be connected.
The coordinate of the connection point is relative to the containing shape's center point.
virtual property x = 0;
<@doc scope="private">Gets the y-coordinate of the pin's connection point
The pin's connection point is the precise point to which the pin's lines should be connected.
The coordinate of the connection point is relative to the containing shape's center point.
virtual property y = 0;
//////////////////////////////////////////////////////////////////////////////////////
// MODEL EVENTS
override virtual method onModelUpdate(evt)
this.repaint(evt.name);
//TODO: repaint also: strokeColor, symbol
end
override virtual method onModelInsert(evt)
// nothing to do (pin has no child graphics)
end
override virtual method onModelRemove(evt)
// nothing to do (pin has no child graphics)
end
//////////////////////////////////////////////////////////////////////////////////////
// PAINTING METHODS
<@doc scope="private">
Paints the pin
virtual method paint()
this.repaint();
end
<@doc scope="private">
Repaints the pin after a property has changed
virtual method repaint(prop)
var shape = this.getShape();
if (!prop || prop == '@symbol') this.updateSymbol();
if (!prop || prop == 'name') {
if (this.pinText) SVG.setText(this.pinText, base.name);
}
if (!prop || prop == '@anchor' || prop == '@offset' || prop == '@symbol') {
var edge = base.@anchor & ~(#[SVG_AUTOHIDE|SVG_AUTO]);
var offset = base.@offset, count = 0, total = 0, sa=0;
if (base.@anchor & #[SVG_AUTO]) {
// count all pins with same anchor, save index of this pin in the counting
var shapeBase = shape.base, pins = shapeBase[shapeBase.@pinsKey], found = false;
for (var pid in pins) {
if (!found && (pid == this.id)) found = true;
if (base.@anchor == pins[pid].@anchor) {
total++;
if (!found) count++;
}
}
total--; // make total zero based
}
var aoffset = 0, doff = (edge & (#[SVG_TOP|SVG_BOTTOM])) ? 30 : 10; // distance between same-anchor pins
switch (edge & 0xF0) {
case #[SVG_START]: sa=-1; aoffset += count*doff; break;
case #[SVG_MIDDLE]: sa=0; aoffset += (count - total/2)*doff; break;
case #[SVG_END]: sa=+1; aoffset += -(count*doff); break;
}
var px=0, py=0, pa=0, sx=0, sy=0, rot=0, dw=shape.w/2, dh=shape.h/2;
switch (edge & 0x0F) {
case #[SVG_LEFT]: rot=0; pa=0; px=-dw; py=sa*dh+aoffset+offset; sx=-1; break;
case #[SVG_RIGHT]: rot=2; pa=180; px=+dw; py=sa*dh+aoffset+offset; sx=+1; break;
case #[SVG_TOP]: rot=3; pa=90; px=sa*dw+aoffset; py=-dh+offset; sy=-1; break;
case #[SVG_BOTTOM]: rot=1; pa=270; px=sa*dw+aoffset; py=+dh+offset; sy=+1; break;
case 0: rot=0; pa=270; sy=+1; break;
}
this.rot = Math.round(rot + (2-shape.rot)) % 4;
if (this.isOutward) pa=180-pa;
if (this.pinNode) {
this.pinNode.setAttribute('transform', 'translate('+px+' '+py+')'+(pa ? ' rotate('+pa+') ' : ''));
}
if (this.pinText) {
var tx = (edge & (#[SVG_TOP|SVG_BOTTOM])) ? px : px-(sx*6||+3);
var ty = (edge & (#[SVG_TOP|SVG_BOTTOM])) ? py-(sy*12) : py-(sy*6||-3);
this.pinText.setAttribute('transform', 'translate('+tx+' '+ty+')');
this.pinText.setAttribute('text-anchor', (edge & #[SVG_RIGHT]) ? 'end' : (edge & #[SVG_LEFT]) ? 'start' : 'middle');
if (this.pinTextClip) {
// text clipping values - used for fadeout text before it reaches the icon in the center of the shape
// TODO: Fix clipping for top/bottom plugs because they now render text horizontally (without the rotation)
// TODO: Find why cx/cy of +16/-16 doesn't end at the center, and -32/+21 are necessary
// TODO: The heuristics surronding the icon (ABS..etc.) is not generic (only look good if there's an icon in the middle). Think if this is OK
var cx=0, cy=0, cstart=0, cend=0;
switch (edge & 0x0F) {
case #[SVG_LEFT]: cx=dw-32+((ABS(py)>16)?14:0); cy=0; cstart=0; cend=10; break;
case #[SVG_RIGHT]: cx=-(dw-21+((ABS(py)>16)?14:0)); cy=0; cstart=10; cend=0; break;
case #[SVG_TOP]: cx=0; cy=dh-32+((ABS(py)>16)?14:0); cstart=0; cend=10; break;
case #[SVG_BOTTOM]: cx=0; cy=-(dh-21+((ABS(py)>16)?14:0)); cstart=10; cend=0; break;
}
this.pinTextClip.setAttribute('x1', cstart);
this.pinTextClip.setAttribute('x2', cend);
this.pinTextClip.setAttribute('gradientTransform', 'translate('+cx+' '+cy+')');
}
}
px+=sx*this.rad, py+=sy*this.rad;
switch (shape.rot) {
case 0: this.x=+px; this.y=+py; break;
case 1: this.x=-py; this.y=+px; break;
case 2: this.x=-px; this.y=-py; break;
case 3: this.x=+py; this.y=-px; break;
}
if (this.rot < 0) this.rot += 4;
// Rerouting lines
var lines = this.lines, canvas = this.canvas;
if (!lines || ISEMPTY(lines)) return;
for (var m in lines) {
var line = canvas.getWidget(m);
if (line) line.reroute();
}
}
end
//////////////////////////////////////////////////////////////////////////////////////
// INTERACTIVE EFFECTS
<@doc scope="private">
Shows indication that the mouse is over the pin
virtual method showHoverEffect()
if (!this.pinNode || this.marked) return;
var shape = this.getShape(), diag=shape.getDiagram();
var color = (this.isSelected && !shape.isSelected ? diag.base.@hilightText : this.hilightColor);
SVG.setProperty(this.pinNode, 'color', color);
end
<@doc scope="private">
Hides indication that the mouse is over the pin
virtual method hideHoverEffect()
if (!this.pinNode || this.marked) return;
var shape=this.getShape(), diag=shape.getDiagram();
var color = (this.isSelected && !shape.isSelected ? this.hilightColor : shape.isSelected ? diag.base.@hilightText : base.@strokeColor);
SVG.setProperty(this.pinNode, 'color', this.isFilled ? color : 'white');
end
<@doc scope="private">
Shows indication that the pin is selected
virtual method showSelectionEffect(showAutohidden)
if (this.marked) return;
var shape = this.getShape(), diag=shape.getDiagram();
this.isSelected = true;
if (showAutohidden && this.isAutohide) SVG.visible(this.pinNode, true);
var color = shape.isSelected ? diag.base.@hilightText : this.hilightColor;
SVG.setProperty(this.pinNode, 'stroke', color);
SVG.setProperty(this.pinNode, 'color', this.isFilled ? color : 'white');
var color = shape.isSelected ? diag.base.@hilightText : base.@strokeColor;
SVG.setProperty(this.pinTextClip, 'color', color);
SVG.setProperty(this.pinText, 'color', color);
end
<@doc scope="private">
Hides the pin selection indication
virtual method hideSelectionEffect(hideAutohidden)
if (this.marked) return;
var shape=this.getShape(), diag=shape.getDiagram();
this.isSelected = false;
if (hideAutohidden && this.isAutohide) SVG.visible(this.pinNode, false);
var color = base.@strokeColor;
SVG.setProperty(this.pinNode, 'stroke', color);
SVG.setProperty(this.pinNode, 'color', this.isFilled ? color : 'white');
SVG.setProperty(this.pinTextClip, 'color', color);
SVG.setProperty(this.pinText, 'color', color);
end
<@doc scope="private">
Shows indication that the pin is in focus
virtual method showFocusEffect()
if (this.isAutohide) SVG.visible(this.pinNode, true);
end
<@doc scope="private">
Hides the pin focus indication
virtual method hideFocusEffect()
if (this.isAutohide) SVG.visible(this.pinNode, false);
end
<@doc scope="private">
Shows indication that the pin is a valid target for wiring operation
virtual method showWiringEffect(animate)
if (!this.pinNode) return;
this.isWireTarget = true;
if (this.isAutohide) SVG.visible(this.pinNode, true);
this.showHoverEffect();
if (this.isAnimated) {
SVG.clearElement(this.pinNode);
this.isAnimated = false;
}
if (animate) {
SVG.createFragment(this.pinNode, '');
this.isAnimated = true;
}
end
<@doc scope="private">
Hides the pin's wiring indication
virtual method hideWiringEffect()
if (!this.pinNode) return;
this.isWireTarget = false;
if (this.isAutohide) SVG.visible(this.pinNode, false);
this.hideHoverEffect();
if (this.isAnimated) {
SVG.clearElement(this.pinNode);
this.isAnimated = false;
}
end
//////////////////////////////////////////////////////////////////////////////////////
// GEOMETRIC CALCULATIONS
<@doc scope="private">
Gets the absolute position of the pin's connection point (relative to the canvas)
virtual method getCP()
var s=this.getShape();
return {
x: s.ox+(s.cx+this.x)*s.os,
y: s.oy+(s.cy+this.y)*s.os,
rot: this.rot,
scale: s.os,
shapeID: s.id
};
end
<@doc scope="private">
Gets the position of the pin's connection point relative to the given line's parent coordinates system
virtual method getLineCP(line, otherPin)
var s=this.getShape(), p=line.getParent(), newRot=this.rot;
var edge = base.@anchor & ~(#[SVG_AUTOHIDE|SVG_AUTO]);
var sx = 0;
if (edge == #[SVG_RIGHT|SVG_MIDDLE]) {
if (line.srcpinID == this.id)
newRot = 0;
else if (line.trgpinID == this.id) {
newRot = 2;
sx = -2;
}
}
return {
x: s.ox+(s.cx+this.x)*s.os-p.qx + sx*this.rad,
y: s.oy+(s.cy+this.y)*s.os-p.qy,
rot: newRot,
scale: s.os/p.qs,
shapeID: s.id
};
end
<@doc scope="private">
Gets the pin's tooltip positioning data
virtual method getTooltipPos()
var side=0;
switch (base.@anchor & 0x0F) {
case #[SVG_TOP]: side=0; break;
case #[SVG_RIGHT]: side=1; break;
case #[SVG_BOTTOM]: side=2; break;
case #[SVG_LEFT]: side=3; break;
}
var data=this.getCP();
switch ((side+this.getShape().rot)%4) {
case 0: data.dy = -1; break; // top
case 1: data.x -= 8; data.y += 4; data.dx = -1; break; // right
case 2: data.y -= 5; data.dy = -1; break; // bottom
case 3: data.x += 8; data.y += 4; data.dx = 1; break; // left
}
data.color = this.pinColor;
return data;
end
<@doc scope="private">
Tests whether this pin is connected to a given other pin
virtual method isConnectedTo(other)
var canvas = this.canvas, lines = this.lines;
for (var k in lines) {
var ln = canvas.getWidget(k);
if (!ln) continue;
if (ln.srcpinID == this.id && other && ln.trgpinID == other.id) return true;
if (other && ln.srcpinID == other.id && ln.trgpinID == this.id) return true;
}
return false;
end
//////////////////////////////////////////////////////////////////////////////////////
// EVENT HANDLERS
virtual method setupPointer(evt)
var shape=this.getShape(), diag=shape.getDiagram();
if (evt.detail>1) {
RULE('drillIntoShape', diag.base, shape.base);
} else if (diag.isEditable) {
if (RULE('canConnectFrom', diag.base, diag.boardAspect, base)) {
return new SvgPointer('wiringHandler', evt, this.id, this.canvas);
}
}
return null;
end
virtual method mouseover(evt, ptr)
var shape=this.getShape(), diag=shape.getDiagram();
if (!diag.isEditable) return;
if (ptr) {
if (ptr.method == 'wiringHandler' && this.isWireTarget) {
ptr.currpinID = this.id;
diag.showTooltip(this);
}
if (ptr.method == 'moveHandler') {
shape.mouseover(evt, ptr);
}
return;
}
diag.showTooltip(this);
if (!diag.hilightMode) return;
if (!RULE('canConnectFrom', diag.base, diag.boardAspect, base)) return;
this.showHoverEffect();
end
virtual method mouseout(evt, ptr)
var shape=this.getShape(), diag=shape.getDiagram();
if (!diag.isEditable) return;
if (ptr) {
if (ptr.method == 'wiringHandler' && this.isWireTarget) {
ptr.currpinID = '';
diag.hideTooltip();
}
if (ptr.method == 'moveHandler') {
shape.mouseout(evt, ptr);
}
return;
}
diag.hideTooltip();
this.hideHoverEffect();
end
virtual method wiringHandler(ptr)
var canvas = this.canvas;
var shape=this.getShape(), diag=shape.getDiagram(), lwidth=2/canvas.scale;
switch (ptr.phase) {
case #[PTR_START]:
diag.selectById(shape.id);
diag.activateBackground(true);
if (diag.isZoomable) diag.startZoomableAction(); // TODO; BlockZoom work
ptr.scrollMode = #[PTR_AUTOSCROLL];
ptr.visualCues = GETVAR('SVG_VISUAL_CUES');
ptr.wireTargetID = '';
ptr.visitedBlocks = {};
ptr.srcpinID = this.id;
ptr.trgpinID = '';
ptr.currpinID = '';
var x=shape.ox+(shape.cx+this.x)*shape.os;
var y=shape.oy+(shape.cy+this.y)*shape.os;
ptr.wire = SVG.createElement(diag.wiresLayer, 'line', {
x1:x, y1:y, x2:x, y2:y, fill:'none', 'stroke':this.hilightColor,
'stroke-width':lwidth, 'stroke-dasharray':lwidth+' '+lwidth,
'pointer-events':'none'
});
if (this.id != ptr.srcpinID) this.showWiringEffect(ptr.visualCues);
if (diag.rubberwireObj) {
SVG.removeElement(diag.rubberwireObj);
diag.rubberwireObj = null;
}
diag.hideTooltip(); // Hides the toolTip when a line created (phase 1)
break;
case #[PTR_MOVE]:
if (ptr.currpinID !== ptr.trgpinID) {
if (!ptr.currpinID) {
ptr.trgpinID = '';
ptr.wire.setAttribute('stroke-dasharray', lwidth+' '+lwidth);
} else {
ptr.trgpinID = ptr.currpinID;
//TODO: route the wire according to candidate link type
var P2=canvas.getWidget(ptr.trgpinID), S2=P2.getShape();
ptr.wire.setAttribute('x2', S2.ox+(S2.cx+P2.x)*S2.os);
ptr.wire.setAttribute('y2', S2.oy+(S2.cy+P2.y)*S2.os);
ptr.wire.setAttribute('stroke-dasharray', 'none');
}
}
if (!ptr.trgpinID) {
ptr.wire.setAttribute('x2', ptr.pos.x);
ptr.wire.setAttribute('y2', ptr.pos.y);
}
break;
case #[PTR_FINISH]:
diag.activateBackground(false);
var wireTarget = canvas.getWidget(ptr.wireTargetID);
if (wireTarget) wireTarget.hideWiringEffect(ptr);
this.hideWiringEffect();
var shapeBase = shape.base, pins = shapeBase[shapeBase.@pinsKey];
for (var k in pins) {
var pin = canvas.getWidget(pins[k].id);
if (pin) pin.showSelectionEffect(true);
}
if (!ptr.moved || ptr.srcpinID == ptr.trgpinID) {
SVG.removeElement(ptr.wire);
if (diag.isZoomable) diag.finishZoomableAction();
break;
}
// in case of dangling connection, open quick-connect menu (if defined)
// TODO: why calculating block if ptr.wireTarget seems to be the block we're looking for?
var pos = ptr.pos;
if (!ptr.trgpinID) {
var block = diag.findBlockAt(pos) || diag.getRoot();
var relpos = {x:pos.x-block.qx, y:pos.y-block.qy};
var menu = RULE('defineDanglingMenu', diag.base, diag.boardAspect, base, relpos ,(block && block.base || null));
if (menu) {
diag.rubberwireObj = ptr.wire;
diag.zoomTargetID = ptr.wireTargetID; // TODO: BlockZoom work
diag.openMenu(menu, pos, menu.callback);
} else {
SVG.removeElement(ptr.wire);
if (diag.isZoomable) diag.finishZoomableAction();
}
break;
}
// otherwise, open the connect menu (if defined). Figure direction first.
var ptrTrgpin = canvas.getWidget(ptr.trgpinID);
var srcpin = this.isInward ? ptrTrgpin : this;
var trgpin = this.isOutward ? ptrTrgpin : this;
if (this.isInout) { if (ptrTrgpin.isInward) trgpin = ptrTrgpin; else srcpin = ptrTrgpin; }
var srcplug=srcpin.base, trgplug=trgpin.base;
var menu = RULE('defineConnectMenu', diag.base, diag.boardAspect, srcplug, trgplug, pos);
if (menu) {
diag.rubberwireObj = ptr.wire;
diag.zoomTargetID = ptr.wireTargetID; // TODO: BlockZoom work
diag.openMenu(menu, pos, menu.callback);
break;
}
// otherwise, create new link based on candidate link type(s)
var linktypes = RULE('defineConnectionTypes', diag.base, diag.boardAspect, srcplug, trgplug);
// no valid link type
if (!linktypes || linktypes.length==0) {
if (diag.isZoomable) diag.finishZoomableAction(); // TODO: BlockZoom work
SVG.removeElement(ptr.wire);
break;
}
// exactly one valid link type, so create it immediately
if (linktypes.length==1) {
diag.rubberwireObj = ptr.wire;
var linkclass = CLASS(linktypes[0]);
if (linkclass) createLink(linkclass.fullname);
if (diag.isZoomable) diag.finishZoomableAction(wireTarget); // TODO: BlockZoom work
break;
}
// more than one valid link type, so open a selection menu
var menu = $ENV.defineMenubar();
diag.rubberwireObj = ptr.wire;
diag.zoomTargetID = ptr.wireTargetID;
for (var i=0, len=linktypes.length; i'
);
this.pinNode.peerID = this.id;
}
}
// delete old text, if any
if (this.pinText) {
SVG.removeElement(this.pinText);
this.pinText = null;
this.pinTextClip = null;
}
// create new text, if defined
if (!(base.@symbol & #[SVG_NOTEXT])) {
this.pinText = SVG.createElement(shape.pinsLabels, 'text', {'class':'pinText', fill: 'currentColor'});
if ((base.@textOverflow == #[SVG_OVERFLOW_FADEOUT]) && shape.getDiagram().handleTextOverflow) {
var id = 'gradient' + this.id;
this.pinTextClip = SVG.createFragment(this.pinText, '');
this.pinText.setAttribute('fill', 'url(#' + id + ')');
}
SVG.setText(this.pinText, base.name);
}
end