I have experimented with making the VectorModifiedEvent immediate and found the following:
I tried to set the “immediate” parameter to true in VVectorLayer#getFeatureModifiedListener():
private GwtOlHandler getFeatureModifiedListener() {
if (_fModifiedListener == null) {
_fModifiedListener = new GwtOlHandler() {
public void onEvent(JsArray arguments) {
if (!updating && drawingMode != "NONE") {
...
if (vector == modifiedFeature) {
client.updateVariable(paintableId, "modifiedVector", next, true); // << this call is set immediate
break;
}
}
}
}
}
};
}
return _fModifiedListener;
}
However, this does not work in itself, because there is - I think - a bug in the current version (0.9.1): when the UIDL response is returned after handling the VectorModifiedEvent on the server-side, VAbstractVector does the following:
public void updateFromUIDL(UIDL childUIDL, final ApplicationConnection client) {
...
if (vector != null) {
getLayer().removeFeature(vector); // (1)
}
//
updateAttribuets(childUIDL, client);
updateVector(childUIDL, client); // (2)
if (childUIDL.hasAttribute("style")) {
intent = childUIDL.getStringAttribute("style");
getVector().setRenderIntent(intent);
}
updateStyle(childUIDL, client);
getLayer().addFeature(vector); // (3)
}
It issues a VectorLayer#removeFeature(…) in (1), which - as far as I know - removes the Vector from the layer and from the layer’s selectedFeatures array but does not destroy the Vector. I.e. the Vector disappears from the map but the object remains around. In (2) updateVector(…) creates a new Vector and line (3) adds the new Vector to the layer. As a result, the new Vector appears on the map. However, when I click on another Vector, OpenLayers wants to unselect the removed Vector and its SelectFeature#clickFeature(…) method crashes with a “NullPointerException”.
I think it would be better to separate the “create” and “update” logic in VAbstractVector#updateFromUIDL(…). Something like this:
public void updateFromUIDL(UIDL childUIDL,
final ApplicationConnection client) {
...
boolean update = true;
if (vector != null) {
getLayer().eraseFeature(vector);
} else {
update = false;
}
//
updateAttribuets(childUIDL, client);
createOrUpdateVector(childUIDL, client);
if (childUIDL.hasAttribute("style")) {
intent = childUIDL.getStringAttribute("style");
getVector().setRenderIntent(intent);
}
updateStyle(childUIDL, client);
if (update) {
getLayer().drawFeature(vector);
} else {
getLayer().addFeature(vector);
}
}
I.e., the old logic would remain when a new Vector is to be created, but when we are updating an existing one then we would just erase it from the map, update its properties and then redraw it. For VPointVector, createOrUpdateVector(…) may look like the one below:
@Override
protected void createOrUpdateVector(UIDL childUIDL, ApplicationConnection client) {
Projection mapProjection = getMap().getProjection();
Point p = Point.create(childUIDL.getStringArrayAttribute("points")[0]
);
Util.browserDebugger();
VConsole.log(getProjection().toString() +" -> "+ mapProjection.toString());
VConsole.log(p.toString());
p.transform(getProjection(), mapProjection);
VConsole.log(p.toString());
JavaScriptObject style = null;
ValueMap attributes = getAttributes();
if (vector == null) {
vector = Vector.create(p, attributes, style);
} else {
vector.setGeometry(p);
vector.setStyle(style);
vector.setAttributes(attributes);
}
}
In addition we need some trivial JSNI methods for VectorLayer#eraseFeature(…),VectorLayer#drawFeature(…), Vector#setgeometry(…) and Vector#setAttributes(…).
Obviously, this is just a quick and dirty modification example of the existing code to make immediate VectorModifiedEvents work.
Do you think it is OK? Or am I overlooking something?
Cheers,
Richie.