分析一下线实体的添加操作,两点划线,下面是Line2P.js中的部分代码:
function Line2P(guiAction) {
Line.call(this, guiAction);
//点的列表
// list of points drawn:
this.pointList = [];
// index of point that was drawn last, used for tool undo / redo:
//保存点的列表,用于撤销和重做
this.pointListIndex = -1;
// list of entities drawn:
//绘制的实体列表
this.entityIdList = [];
//点
this.point1 = undefined;
this.point2 = undefined;
this.setUiOptions(["../Line.ui", "Line2P.ui"]);
}
//设置Line2P的原型为Line
Line2P.prototype = new Line();
//状态
Line2P.State = {
SettingFirstPoint : 0,
SettingNextPoint : 1
};
//当action激活时自动调用
Line2P.prototype.beginEvent = function() {
Line.prototype.beginEvent.call(this);
this.setState(Line2P.State.SettingFirstPoint);
this.updateButtonStates();
};
//设置状态
Line2P.prototype.setState = function(state) {
Line.prototype.setState.call(this, state);
//文档接口会根据这个模式选择调用相应的交互事件【例如是调用pickCoordinate还是调用鼠标的其他事件】
this.getDocumentInterface().setClickMode(RAction.PickCoordinate);
//设置鼠标样式
this.setCrosshairCursor();
var appWin = RMainWindowQt.getMainWindow();
switch (this.state) {
//如果是第一次点击鼠标
case Line2P.State.SettingFirstPoint:
var trFirstPoint = qsTr("First point");
//设置命令行中的提示信息
this.setCommandPrompt(trFirstPoint);
this.setLeftMouseTip(trFirstPoint);
this.setRightMouseTip(EAction.trCancel);
this.pointList = [];
this.pointListIndex = -1;
this.entityIdList = [];
break;
case Line2P.State.SettingNextPoint:
//如果是第二次点击鼠标
var trNextPoint = qsTr("Next point");
this.setCommandPrompt(trNextPoint);
this.setLeftMouseTip(trNextPoint);
this.setRightMouseTip(qsTr("Done"));
break;
}
EAction.showSnapTools();
};
//拾取坐标
Line2P.prototype.pickCoordinate = function(event, preview) {
var di = this.getDocumentInterface();
switch (this.state) {
//如果状态是第一个点,
case Line2P.State.SettingFirstPoint:
if (!preview) {
this.point1 = event.getModelPosition();
//添加第一个点
this.pointList.splice(0, 0, this.point1);
//设置索引
this.pointListIndex = 0;
//应该是将第一点设置为相对位置,使用矩阵???
di.setRelativeZero(this.point1);
//将状态改为第二个点
this.setState(Line2P.State.SettingNextPoint);
}
break;
case Line2P.State.SettingNextPoint:
this.point2 = event.getModelPosition();
if (preview) {
this.updatePreview();
}
else {
//获取一个操作
var op = this.getOperation(preview);
if (!isNull(op)) {
if (!this.isRayOrXLine()) {
//如果不是射线或者X线就++
this.pointListIndex++;
}
//获取文档
var doc = this.getDocument();
//添加到文档接口当中【两个点构成一个实体】
var trans = di.applyOperation(op);
var id = this.getLineEntityId(trans);
if (!this.isRayOrXLine()) {
//添加到相应的位置
this.pointList.splice(this.pointListIndex, 0, this.point2);
//将实体存储起来
this.entityIdList.splice(this.pointListIndex-1, 0, id);
//将相对位置重置为第二个点
di.setRelativeZero(this.point2);
//第二个点作为第一个点,然后创建另一个线实体
this.point1 = this.point2;
}
// qDebug("this.pointList: ", this.pointList);
// qDebug("this.entityIdList: ", this.entityIdList);
// qDebug("this.pointListIndex: ", this.pointListIndex);
}
}
break;
}
if (!preview) {
this.updateButtonStates();
}
};
Line2P.prototype.getOperation = function(preview) {
if (!isVector(this.point1) || !isVector(this.point2)) {
return undefined;
}
//创建线实体
var e = this.createLineEntity(this.getDocument(), this.point1, this.point2);
//将实体封装到操作当中
return new RAddObjectOperation(e, this.getToolTitle());
};
当一个Action被触发后会调用Action的beginEvent事件,Line2P重写了RAction的beginEvent方法,在这个函数中调用
this.setState(Line2P.State.SettingFirstPoint);
这个函数设置了一个自定义的标志Line2P.State.SettingFirstPoint,代表这是第一个按下的点。在setState()函数中
//文档接口会根据这个模式选择调用相应的交互事件【例如是调用pickCoordinate还是调用鼠标的其他事件】
this.getDocumentInterface().setClickMode(RAction.PickCoordinate);
设置了点击模式后,下次鼠标点击后会在documentinterface中判断模式,根据这个模式调用Line2P类的函数
//拾取坐标
Line2P.prototype.pickCoordinate = function(event, preview)
在上面的函数中会调用下面的代码
//获取一个操作
var op = this.getOperation(preview);
if (!isNull(op)) {
if (!this.isRayOrXLine()) {
//如果不是射线或者X线就++
this.pointListIndex++;
}
//获取文档
var doc = this.getDocument();
//添加到文档接口当中【两个点构成一个实体】
var trans = di.applyOperation(op);
var id = this.getLineEntityId(trans);
if (!this.isRayOrXLine()) {
//添加到相应的位置
this.pointList.splice(this.pointListIndex, 0, this.point2);
//将实体存储起来
this.entityIdList.splice(this.pointListIndex-1, 0, id);
//将相对位置重置为第二个点
di.setRelativeZero(this.point2);
//第二个点作为第一个点,然后创建另一个线实体
this.point1 = this.point2;
}
// qDebug("this.pointList: ", this.pointList);
// qDebug("this.entityIdList: ", this.entityIdList);
// qDebug("this.pointListIndex: ", this.pointListIndex);
}
这个函数中的过程伪代码如下:
documentinterface.applyOperation(new AddOperation(new LineEntity(new LineEntityData())));
在documentinterface中
RTransaction RDocumentInterface::applyOperation(ROperation* operation) {
//如果当前的操作是null就返回
if (operation==NULL) {
qWarning() << "RDocumentInterface::applyOperation: operation is NULL";
return RTransaction();
}
//??
if (document.getAutoTransactionGroup()) {
operation->setTransactionGroup(document.getTransactionGroup());
}
//将具体的数据库操作放到ROperation完成【功能独立】
RTransaction transaction = operation->apply(document, false);
if (transaction.isFailed()) {
qWarning() << "RDocumentInterface::applyOperation: "
"transaction failed";
if (RMainWindow::hasMainWindow()) {
RMainWindow::getMainWindow()->handleUserWarning("#transaction_failed");
}
}
//获取被影响的id
QList<RObject::Id> objectIds = transaction.getAffectedObjects();
//清空之前的视图
clearPreview();
objectChangeEvent(objectIds);
if (RMainWindow::hasMainWindow() && notifyGlobalListeners) {
RMainWindow::getMainWindow()->postTransactionEvent(transaction,
transaction.hasOnlyChanges(), operation->getEntityTypeFilter());
}
delete operation;
notifyTransactionListeners(&transaction);
return transaction;
}
在documentinterface中将文档放入到operation中完成对文档的操作RTransaction transaction = operation->apply(document, false);
RTransaction RAddObjectsOperation::apply(RDocument& document, bool preview) {
//将文档封装成一个事务,通过事务处理文档中的实体
RTransaction transaction(document.getStorage(), text, undoable);
transaction.setRecordAffectedObjects(recordAffectedObjects);
transaction.setSpatialIndexDisabled(spatialIndexDisabled);
transaction.setAllowAll(allowAll);
transaction.setAllowInvisible(allowInvisible);
transaction.setGroup(transactionGroup);
for (int i=0; i<addedObjects.size(); ++i) {
if (limitPreview && preview && i>RSettings::getPreviewEntities()) {
break;
}
RModifiedObjects modObj = addedObjects[i];
if (modObj.object.isNull()) {
transaction.endCycle();
//qWarning() << "RAddObjectsOperation::apply: "
// "list contains NULL object";
continue;
}
if (modObj.getDelete()) {
transaction.deleteObject(modObj.object);
continue;
}
QSet<RPropertyTypeId> props;
if (modObj.getGeometryOnly()) {
// we know that only geometry was modified for this object:
props = modObj.object->getPropertyTypeIds(RPropertyAttributes::Geometry);
}
transaction.addObject(
modObj.object,
!modObj.getUseAttributes(),
modObj.getForceNew(),
props
);
}
transaction.end();
return transaction;
}
通过事务将实体添加到文档的RStorage当中。