/*
* Copyright 2003-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jetbrains.mps.nodeEditor;
import jetbrains.mps.editor.runtime.impl.CellUtil;
import jetbrains.mps.editor.runtime.impl.cellActions.CellAction_DeleteEasily;
import jetbrains.mps.editor.runtime.impl.cellActions.CellAction_DeleteSPropertyOrNode;
import jetbrains.mps.editor.runtime.impl.cellActions.CellAction_DeleteSmart;
import jetbrains.mps.editor.runtime.impl.cellMenu.EnumSPropertySubstituteInfo;
import jetbrains.mps.editor.runtime.style.StyleAttributes;
import jetbrains.mps.internal.collections.runtime.IterableUtils;
import jetbrains.mps.lang.editor.cellProviders.SChildListHandler;
import jetbrains.mps.lang.editor.cellProviders.SingleRoleCellProvider;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.AttributeOperations;
import jetbrains.mps.nodeEditor.cellActions.CellAction_DeleteNode;
import jetbrains.mps.nodeEditor.cellActions.CellAction_DeleteNode.DeleteDirection;
import jetbrains.mps.nodeEditor.cellActions.CellAction_DeleteOnErrorReference;
import jetbrains.mps.nodeEditor.cellActions.CellAction_DeleteSReference;
import jetbrains.mps.nodeEditor.cellLayout.CellLayout_Indent;
import jetbrains.mps.nodeEditor.cellMenu.BooleanSPropertySubstituteInfo;
import jetbrains.mps.nodeEditor.cellMenu.DefaultReferenceSubstituteInfo;
import jetbrains.mps.nodeEditor.cellMenu.DefaultSChildSubstituteInfo;
import jetbrains.mps.nodeEditor.cellMenu.DefaultSReferenceSubstituteInfo;
import jetbrains.mps.nodeEditor.cellProviders.AbstractCellListHandler;
import jetbrains.mps.nodeEditor.cells.EditorCell_Constant;
import jetbrains.mps.nodeEditor.cells.EditorCell_Error;
import jetbrains.mps.nodeEditor.cells.EditorCell_Property;
import jetbrains.mps.nodeEditor.cells.SPropertyAccessor;
import jetbrains.mps.openapi.editor.EditorContext;
import jetbrains.mps.openapi.editor.cells.CellActionType;
import jetbrains.mps.openapi.editor.cells.EditorCell;
import jetbrains.mps.openapi.editor.menus.transformation.SNodeLocation.FromNode;
import jetbrains.mps.openapi.editor.menus.transformation.SNodeLocation.FromParentAndLink;
import jetbrains.mps.openapi.editor.update.AttributeKind;
import jetbrains.mps.smodel.SNodePointer;
import jetbrains.mps.smodel.SNodeUtil;
import jetbrains.mps.smodel.action.NodeFactoryManager;
import jetbrains.mps.smodel.presentation.ReferenceConceptUtil;
import jetbrains.mps.util.Computable;
import jetbrains.mps.util.Pair;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.mps.openapi.language.SConcept;
import org.jetbrains.mps.openapi.language.SContainmentLink;
import org.jetbrains.mps.openapi.language.SDataType;
import org.jetbrains.mps.openapi.language.SPrimitiveDataType;
import org.jetbrains.mps.openapi.language.SProperty;
import org.jetbrains.mps.openapi.language.SReferenceLink;
import org.jetbrains.mps.openapi.model.SNode;
import org.jetbrains.mps.openapi.model.SReference;
/**
* Semen Alperovich
* 04 04, 2013
*/
public class DefaultEditor extends AbstractDefaultEditor {
public DefaultEditor(@NotNull SConcept concept) {
super(concept);
}
protected void init() {
assert getNode() != null && getConcept() != null;
for (SProperty sProperty : getConcept().getProperties()) {
if (!sProperty.getOwner().equals(SNodeUtil.concept_BaseConcept)) {
addProperty(sProperty);
}
}
for (SReferenceLink sReferenceLink : getConcept().getReferenceLinks()) {
if (!sReferenceLink.getOwner().equals(SNodeUtil.concept_BaseConcept)) {
addReferenceLink(sReferenceLink);
}
}
for (SContainmentLink sContainmentLink : getConcept().getContainmentLinks()) {
if (!sContainmentLink.getOwner().equals(SNodeUtil.concept_BaseConcept)) {
addContainmentLink(sContainmentLink);
}
}
super.init();
}
@Override
protected void addPropertyCell(SProperty property) {
EditorCell_Property editorCell = new EditorCell_Property(getEditorContext(), new SPropertyAccessor(getNode(), property, false, false), getNode());
getUpdateSession().registerCleanDependency(editorCell, new Pair<>(new SNodePointer(getNode()), property.getName()));
editorCell.setDefaultText("<no " + property.getName() + ">");
if (editorCell.getCellId() == null) {
editorCell.setCellId("property_" + property);
}
editorCell.setAction(CellActionType.DELETE, new CellAction_DeleteSPropertyOrNode(getNode(), property, DeleteDirection.FORWARD));
editorCell.setAction(CellActionType.BACKSPACE, new CellAction_DeleteSPropertyOrNode(getNode(), property, DeleteDirection.BACKWARD));
SDataType type = property.getType();
if (type instanceof SPrimitiveDataType) {
if (((SPrimitiveDataType) type).getType() == SPrimitiveDataType.BOOL) {
editorCell.setSubstituteInfo(new BooleanSPropertySubstituteInfo(getNode(), property, getEditorContext()));
}
} else {
editorCell.setSubstituteInfo(new EnumSPropertySubstituteInfo(getNode(), property, getEditorContext()));
}
//todo generate property data type
// if (type instanceof SEnumeration) {
// editorCell.setSubstituteInfo(new EnumSPropertySubstituteInfo(mySNode, property, myEditorContext));
// }
addCellWithRole(IterableUtils.first(AttributeOperations.getPropertyAttributes(getNode(), property)), AttributeKind.PROPERTY, editorCell);
}
@Override
protected void addChildCell(final SContainmentLink link) {
if (link.isMultiple()) {
AbstractCellListHandler handler = new ListHandler(getNode(), link, getEditorContext());
EditorCell editorCell = handler.createCells(new CellLayout_Indent(), false);
editorCell.setRole(handler.getElementRole());
addStyle(editorCell, StyleAttributes.INDENT_LAYOUT_CHILDREN_NEWLINE);
setIndent(editorCell);
addCell(editorCell);
} else {
SingleRoleCellProvider provider = new SingleRoleCellProvider(link, getEditorContext()) {
@Override
protected EditorCell createEmptyCell() {
getCellFactory().pushCellContext();
getCellFactory().setNodeLocation(new FromParentAndLink(getNode(), myContainmentLink));
try {
EditorCell emptyCell = super.createEmptyCell();
emptyCell.setSubstituteInfo(new DefaultSChildSubstituteInfo(getNode(), link, getEditorContext()));
emptyCell.setRole(link.getName());
emptyCell.setCellId("empty_" + link.getName());
return emptyCell;
} finally {
getCellFactory().popCellContext();
}
}
@Override
public EditorCell createChildCell(SNode child) {
getCellFactory().pushCellContext();
getCellFactory().setNodeLocation(new FromNode(child));
try {
EditorCell cell = super.createChildCell(child);
cell.setAction(CellActionType.DELETE, new CellAction_DeleteSmart(getNode(), myContainmentLink, child));
cell.setAction(CellActionType.BACKSPACE, new CellAction_DeleteSmart(getNode(), myContainmentLink, child));
cell.setSubstituteInfo(new DefaultSChildSubstituteInfo(getNode(), child, link, getEditorContext()));
if (cell.getRole() == null) {
cell.setRole(link.getName());
}
return cell;
} finally {
getCellFactory().popCellContext();
}
}
@NotNull
@Override
public SNode getNode() {
return DefaultEditor.this.getNode();
}
};
EditorCell cell = provider.createCell();
setIndent(cell);
addCell(cell);
}
}
@Override
protected void addReferenceCell(final SReferenceLink referenceLink) {
SReference reference = getNode().getReference(referenceLink);
if (reference == null) {
String noTargetText = "<no " + referenceLink.getName() + ">";
jetbrains.mps.nodeEditor.cells.EditorCell_Label noRefCell = referenceLink.isOptional() ?
new EditorCell_Constant(getEditorContext(), getNode(), "") : new EditorCell_Error(getEditorContext(), getNode(), noTargetText);
noRefCell.setText("");
noRefCell.setEditable(true);
noRefCell.setDefaultText(noTargetText);
noRefCell.setAction(CellActionType.DELETE, new CellAction_DeleteEasily(getNode(), DeleteDirection.FORWARD));
noRefCell.setAction(CellActionType.BACKSPACE, new CellAction_DeleteEasily(getNode(), DeleteDirection.BACKWARD));
noRefCell.setCellId("empty_" + referenceLink.getName());
noRefCell.setRole(referenceLink.getName());
noRefCell.setReferenceCell(true);
noRefCell.setSubstituteInfo(new DefaultSReferenceSubstituteInfo(getNode(), referenceLink, getEditorContext()));
noRefCell.setRole(referenceLink.getName());
setIndent(noRefCell);
addCell(noRefCell);
} else {
final SNode referentNode = reference.getTargetNode();
if (referentNode == null || referentNode.getModel() == null) {
//todo do we need this?
String resolveInfo = ((jetbrains.mps.smodel.SReference) reference).getResolveInfo();
EditorCell errorCell = createErrorCell(resolveInfo != null ? resolveInfo : "?" + referenceLink.getName() + "?", referenceLink);
errorCell.setCellId("error_" + referenceLink.getName());
addCell(errorCell);
} else {
EditorCell cell = getUpdateSession().updateReferencedNodeCell(new Computable<EditorCell>() {
@Override
public EditorCell compute() {
return createReferentEditorCell(getEditorContext(), referenceLink, referentNode);
}
}, referentNode, referenceLink.getName());
//todo what is that?
CellUtil.setupIDeprecatableStyles(referentNode, cell);
setSemanticNodeToCells(cell, getNode());
//todo rewrite cell actions
cell.setAction(CellActionType.DELETE, new CellAction_DeleteSReference(getNode(), referenceLink));
cell.setAction(CellActionType.BACKSPACE, new CellAction_DeleteSReference(getNode(), referenceLink));
cell.setSubstituteInfo(new DefaultSReferenceSubstituteInfo(getNode(), referenceLink, getEditorContext()));
if (cell.getCellId() == null) {
cell.setCellId("reference_" + referenceLink.getName());
}
//todo attributes
addCellWithRole(IterableUtils.first(AttributeOperations.getLinkAttributes(getNode(), referenceLink)), AttributeKind.REFERENCE, cell);
}
}
}
protected EditorCell createErrorCell(String error, SReferenceLink link) {
EditorCell_Error errorCell = new EditorCell_Error(getEditorContext(), getNode(), error, true);
if (!link.isOptional()) {
if (ReferenceConceptUtil.getCharacteristicReference(getNode().getConcept()) != null) {
errorCell.setAction(CellActionType.DELETE, new CellAction_DeleteNode(getNode(), DeleteDirection.FORWARD));
errorCell.setAction(CellActionType.BACKSPACE, new CellAction_DeleteNode(getNode(), DeleteDirection.BACKWARD));
return errorCell;
}
}
//todo rewrite cell actions
errorCell.setAction(CellActionType.DELETE, new CellAction_DeleteOnErrorReference(getNode(), link.getName()));
errorCell.setAction(CellActionType.BACKSPACE, new CellAction_DeleteOnErrorReference(getNode(), link.getName()));
return errorCell;
}
private void addCellWithRole(SNode attributeConcept, AttributeKind attributeKind, EditorCell editorCell) {
EditorCell roleAttributeCell = createRoleAttributeCell(attributeConcept, attributeKind, editorCell);
if (roleAttributeCell != null) {
addCell(roleAttributeCell);
} else {
addCell(editorCell);
}
}
private EditorCell createRoleAttributeCell(SNode attributeConcept, AttributeKind attributeKind, EditorCell editorCell) {
if (attributeConcept != null) {
EditorManager manager = EditorManager.getInstanceFromContext(getEditorContext());
if (manager != null) {
return manager.createNodeRoleAttributeCell(attributeConcept, attributeKind, editorCell);
}
}
return null;
}
private static class ListHandler extends SChildListHandler {
ListHandler(SNode ownerNode, SContainmentLink link, EditorContext context) {
super(ownerNode, link, context, false);
}
public SNode createNodeToInsert(EditorContext editorContext) {
SNode listOwner = getNode();
return NodeFactoryManager.createNode(myLink.getTargetConcept(), null, listOwner, listOwner.getModel());
}
public EditorCell createNodeCell(SNode elementNode) {
EditorCell elementCell = getUpdateSession().updateChildNodeCell(elementNode);
this.installElementCellActions(getNode(), elementNode, elementCell);
return elementCell;
}
public EditorCell createEmptyCell() {
getCellFactory().pushCellContext();
getCellFactory().setNodeLocation(new FromParentAndLink(getNode(), myLink));
try {
EditorCell emptyCell = super.createEmptyCell();
this.installElementCellActions(getNode(), null, emptyCell);
return emptyCell;
} finally {
getCellFactory().popCellContext();
}
}
public void installElementCellActions(SNode listOwner, SNode elementNode, EditorCell elementCell) {
if (elementCell.getUserObject(AbstractCellListHandler.ELEMENT_CELL_ACTIONS_SET) == null) {
elementCell.putUserObject(AbstractCellListHandler.ELEMENT_CELL_ACTIONS_SET, AbstractCellListHandler.ELEMENT_CELL_ACTIONS_SET);
if (elementNode != null) {
elementCell.setAction(CellActionType.DELETE, new CellAction_DeleteNode(elementNode, DeleteDirection.FORWARD));
elementCell.setAction(CellActionType.BACKSPACE, new CellAction_DeleteNode(elementNode, DeleteDirection.BACKWARD));
}
if (elementCell.getSubstituteInfo() == null || elementCell.getSubstituteInfo() instanceof DefaultReferenceSubstituteInfo) {
elementCell.setSubstituteInfo(new DefaultSChildSubstituteInfo(listOwner, elementNode, myLink, getEditorContext()));
}
}
}
}
}