/* * 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())); } } } } }