Code: Select all
/**
* \defgroup ecma_draw_text TextAlong Drawing Tool
* \ingroup ecma_draw
*
* \brief This module contains the ECMAScript implementation of the text along an entity
* drawing tool.
*/
include("scripts/Draw/Draw.js");
include("scripts/WidgetFactory.js");
include("scripts/Draw/Text/TextDialog/TextDialog.js");
/**
* \class TextAlong
* \brief Insert text along an entity.
* \ingroup ecma_misc_draw
*/
function TextAlong(guiAction, mode) {
if (isNull(mode)) {
mode = TextDialog.Mode.Text;
}
Draw.call(this, guiAction);
this.textData = undefined;
this.mode = mode;
this.entity = undefined;
this.text = undefined;
this.height = 1.0;
this.spacing = 0.0;
this.pos = undefined;
this.ccw = false;
this.fit = false;
this.CharWidths = [];
this.totalCharWidth = 0.0;
this.setUiOptions("TextAlong.ui");
}
TextAlong.prototype = new Draw();
TextAlong.State = {
ChoosingEntity : 0,
SettingPosition : 1
};
TextAlong.prototype.beginEvent = function() {
Draw.prototype.beginEvent.call(this);
var dialog = new TextDialog(this.mode);
this.textData = dialog.show();
if (isNull(this.textData)) {
this.terminate();
return;
}
var optionsToolBar = EAction.getOptionsToolBar();
var contentsEdit = optionsToolBar.findChild("Contents");
WidgetFactory.initLineEdit(contentsEdit, false);
contentsEdit.text = this.textData.getEscapedText();
var heightEdit = optionsToolBar.findChild("Height");
var ht = heightEdit.getValue();
this.textData.setTextHeight(ht);
// generate list of char widths
this.getCharWidths();
this.setState(TextAlong.State.ChoosingEntity);
};
TextAlong.prototype.setState = function(state) {
Draw.prototype.setState.call(this, state);
var appWin = RMainWindowQt.getMainWindow();
this.setCrosshairCursor();
switch (this.state) {
case TextAlong.State.ChoosingEntity :
this.getDocumentInterface().setClickMode(RAction.PickEntity);
this.setCommandPrompt(qsTr("Select Entity"));
this.setLeftMouseTip(qsTr("Select Entity"));
this.setRightMouseTip(EAction.trCancel);
EAction.showSnapTools();
break;
case TextAlong.State.SettingPosition :
this.getDocumentInterface().setClickMode(RAction.PickCoordinate);
this.setCommandPrompt(qsTr("Position"));
this.setLeftMouseTip(qsTr("Position"));
this.setRightMouseTip(EAction.trCancel);
EAction.showSnapTools();
break;
}
};
TextAlong.prototype.pickEntity = function(event, preview) {
var di = this.getDocumentInterface();
var doc = this.getDocument();
var entityId = event.getEntityId();
var entity = doc.queryEntity(entityId);
this.pos = event.getModelPosition();
if (isNull(entity)) {
this.entity = undefined;
return;
} else {
if (!preview) {
var type = entity.getType();
if (type !== RS.EntityLine && type !== RS.EntityArc && type !== RS.EntityCircle) {
EAction.handleUserWarning(qsTr("Selected entity is not a Line, Arc or Circle!"));
return;
}
this.entity = entity;
this.setState(TextAlong.State.SettingPosition);
}
}
};
TextAlong.prototype.pickCoordinate = function(event, preview) {
var di = this.getDocumentInterface();
this.pos = event.getModelPosition();
if (!preview) {
di.setRelativeZero(this.pos);
}
this.textData.setAlignmentPoint(this.pos);
this.text = new RTextEntity(this.getDocument(), this.textData);
var op = this.getOperation(preview);
if (preview) {
di.previewOperation(op);
}
else {
if (!isNull(op)) {
di.applyOperation(op);
}
}
};
TextAlong.prototype.getOperation = function(preview) {
var di = this.getDocumentInterface();
var type;
if (!isNull(this.entity)) {
type = this.entity.getType();
} else {
return undefined;
}
var op = undefined;
switch(type) {
case RS.EntityLine :
op = this.alongLine();
break;
case RS.EntityArc :
// 'this.entity' is a pointer to the arc, so get the actual arc object
var arc = this.entity.getData().getArc();
op = this.alongArc(arc);
break;
case RS.EntityCircle :
// create a circular arc and use 'alongArc'.
// when using 'fit', add one or more spaces to the start or end of the text
var arc = new RArc(this.entity.getCenter(), this.entity.getRadius(), 0.0, 0.0);
op = this.alongArc(arc);
break;
default :
break;
}
return op;
}
TextAlong.prototype.getCharWidths = function() {
this.text = new RTextEntity(this.getDocument(), this.textData);
var txt = this.text.getPlainText();
var num = txt.length;
this.CharWidths = [];
this.totalCharWidth = 0.0;
for (var i = 0; i < num; i++) {
var char = txt[i];
// A space character is returned as zero width
if (char === " ") {
char = "-";
}
var td = this.getTextData(char);
var wid = td.getWidth();
this.CharWidths.push(wid + this.spacing);
this.totalCharWidth += wid + this.spacing;
}
}
TextAlong.prototype.getTextData = function(txt) {
var td = new RTextData();
if (this.pos !== undefined) {
td.setAlignmentPoint(this.pos);
}
td.setVAlign(this.textData.getVAlign());
td.setHAlign(this.textData.getHAlign());
td.setText(txt);
td.setTextHeight(this.textData.getTextHeight());
td.setTextWidth(this.textData.getTextWidth());
td.setFontName(this.textData.getFontName());
td.setAngle(this.textData.getAngle());
td.setBold(this.textData.isBold());
td.setItalic(this.textData.isItalic());
td.setLineSpacingFactor(this.textData.getLineSpacingFactor());
return td;
}
TextAlong.prototype.slotContentsChanged = function(value) {
if (isNull(this.textData)) {
return;
}
this.textData.setText(value);
// change list of char widths
this.getCharWidths();
};
TextAlong.prototype.slotHeightChanged = function(value) {
if (isNull(this.textData)) {
return;
}
this.height = value;
this.textData.setTextHeight(this.height);
// change list of char widths
this.getCharWidths();
};
TextAlong.prototype.slotSpacingChanged = function(value) {
this.spacing = value;
this.getCharWidths();
};
TextAlong.prototype.slotDirectionChanged = function(button) {
if (!isQObject(button) || button.checked !== true) {
return;
}
if (button.objectName === "Clockwise") {
this.ccw = false;
}
else {
this.ccw = true;
}
};
TextAlong.prototype.slotFitChanged = function(value) {
this.fit = value;
};
TextAlong.prototype.alongLine = function() {
var ang, len, op, h, v;
ang = this.entity.getAngle();
len = this.entity.getLength();
if (!isNull(this.textData)) {
if (this.ccw) {
this.textData.setAngle(ang + Math.PI);
} else {
this.textData.setAngle(ang);
}
}
op = new RAddObjectsOperation();
if (this.fit) {
var txt = this.text.getPlainText();
var num = txt.length;
var firstCharWidth = this.CharWidths[0];
var lastCharWidth = this.CharWidths[num - 1];
var dist = (len - firstCharWidth) / (num - 1);
// now position 'this.pos'
v = new RVector();
h = this.textData.getHAlign();
if (h === RS.HAlignRight) {
v.setPolar(len, this.textData.getAngle() + Math.PI);
this.pos = this.pos.operator_add(v);
} else if (h === RS.HAlignCenter) {
v.setPolar(len / 2.0, this.textData.getAngle() + Math.PI);
this.pos = this.pos.operator_add(v);
}
for (var i = 0; i < num; i++) {
var td = this.getTextData(txt[i]);
var te = new RTextEntity(this.getDocument(), td);
te.setSimple(true); // This is required so 'base' and 'bottom' points are correct
te.setVAlign(this.textData.getVAlign());
te.setHAlign(RS.HAlignCenter);
v.setPolar((dist * i) + (firstCharWidth / 2.0), te.getAngle());
var pt = this.pos.operator_add(v);
te.setAlignmentPoint(pt);
op.addObject(te);
}
} else {
op.addObject(this.text);
}
return op;
}
TextAlong.prototype.alongArc = function(arc) {
var ang, op;
op = new RAddObjectsOperation();
var txt = this.text.getPlainText();
var num = txt.length;
// Fix bug: Fit option doesn't work on reversed arcs
if (arc.isReversed()) {
arc.reverse();
}
// use distance from 'this.pos' to centre point of arc, as radius
arc.setRadius(arc.center.getDistanceTo(this.pos));
var pt = this.pos;
ang = arc.center.getAngleTo(this.pos);
var arcrad = arc.getRadius();
// calculate spacing for 'Fit' option
if (this.fit) {
// subtract half the width of first character and last character
var firstcharAngle = ((this.CharWidths[0]) / arcrad) / 2.0;
var lastcharAngle = ((this.CharWidths[num - 1]) / arcrad) / 2.0;
var fitAngle = (arc.getSweep() - firstcharAngle - lastcharAngle) / (num - 1);
}
var td = this.getTextData(txt[0]);
var te = new RTextEntity(this.getDocument(), td);
var h = te.getHAlign();
//var v = te.getVAlign();
// set rotation angle for first character
var rotateAngle, sweep;
var width = this.totalCharWidth;
if (this.fit) {
sweep = arc.getSweep();
} else {
sweep = width / arcrad;
}
if (h === RS.HAlignLeft) {
rotateAngle = 0.0;
} else if (h === RS.HAlignCenter) {
rotateAngle = sweep / 2.0;
} else {
rotateAngle = sweep;
}
// get angle for half the first characters width
// and subtract from rotateAngle
var halfAngle = ((this.CharWidths[0]) / arcrad) / 2.0;
rotateAngle -= halfAngle;
if (this.ccw) {
rotateAngle = -rotateAngle;
}
for (var i = 0; i < num; i++) {
td = this.getTextData(txt[i]);
te = new RTextEntity(this.getDocument(), td);
h = te.getHAlign();
//v = te.getVAlign();
te.setHAlign(RS.HAlignCenter);
te.setSimple(true); // This is required so 'base' and 'bottom' points are correct
te.setAlignmentPoint(pt);
if (this.ccw) {
te.setAngle(ang + (Math.PI / 2.0));
} else {
te.setAngle(ang - (Math.PI / 2.0));
}
// draw each character at 'this.pos', and rotate to match alignment point
te.rotate(rotateAngle, arc.center);
op.addObject(te);
// adjust point and angle for next character
// function 'getCharWidths' handles spacing
var currentcharAngle = (this.CharWidths[i]) / arcrad;
var nextcharAngle = (this.CharWidths[i + 1]) / arcrad;
var charAngle = (currentcharAngle / 2.0) + (nextcharAngle / 2.0);
if (this.ccw) {
if (this.fit) {
rotateAngle = rotateAngle + fitAngle;
} else {
rotateAngle = rotateAngle + charAngle;
}
} else {
if (this.fit) {
rotateAngle = rotateAngle - fitAngle;
} else {
rotateAngle = rotateAngle - charAngle;
}
}
}
return op;
}