/* * 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.lang.editor.cellProviders; import jetbrains.mps.editor.runtime.cells.AbstractCellAction; import jetbrains.mps.editor.runtime.descriptor.AbstractEditorBuilder; import jetbrains.mps.editor.runtime.descriptor.EditorBuilderEnvironment; import jetbrains.mps.editor.runtime.impl.cellActions.CellAction_DeleteSimple; import jetbrains.mps.lang.smodel.generator.smodelAdapter.AttributeOperations; import jetbrains.mps.nodeEditor.SNodeEditorUtil; import jetbrains.mps.nodeEditor.cellActions.CellAction_DeleteNode.DeleteDirection; import jetbrains.mps.nodeEditor.cells.EditorCell_Constant; import jetbrains.mps.nodeEditor.cells.EditorCell_Error; import jetbrains.mps.nodeEditor.cells.EditorCell_Label; 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.cells.EditorCell_Collection; import jetbrains.mps.smodel.action.NodeFactoryManager; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.mps.openapi.language.SContainmentLink; import org.jetbrains.mps.openapi.model.SNode; import java.util.Iterator; /** * @author simon */ public abstract class SingleRoleCellProvider extends AbstractEditorBuilder implements EditorBuilderEnvironment { protected final SContainmentLink myContainmentLink; /** * @deprecated since MPS 3.5 use {@link #getNode()} method, this field will be removed in the next release */ @Deprecated protected final SNode myOwnerNode; /** * @deprecated since MPS 3.5 use {@link #getEditorContext()} method, this field will be removed in the next release */ @Deprecated protected final EditorContext myEditorContext; /** * @deprecated since MPS 3.5 use {@link #SingleRoleCellProvider(SContainmentLink, EditorContext)} constructor */ @Deprecated public SingleRoleCellProvider(final SNode ownerNode, final SContainmentLink containmentLink, EditorContext editorContext) { super(editorContext); myOwnerNode = ownerNode; myContainmentLink = containmentLink; myEditorContext = editorContext; } public SingleRoleCellProvider(final SContainmentLink containmentLink, EditorContext editorContext) { super(editorContext); myOwnerNode = null; myContainmentLink = containmentLink; myEditorContext = editorContext; } /** * This is a compatibility implementation of corresponding method from super-class. * Since MPS 3.5 this method will be generated in sub-classes. * After MPS 3.5 this method should be removed, so sub-classes will implement it directly. * * @return */ @NotNull @Override public SNode getNode() { return myOwnerNode; } protected EditorCell createChildCell(SNode child) { return createChildCell_internal(child); } @NotNull private EditorCell createChildCell_internal(SNode child) { return getEditorContext().getEditorComponent().getUpdater().getCurrentUpdateSession().updateChildNodeCell(child); } public EditorCell createCell() { if (areAttributesEmpty()) { return createSingleCell(); } else { EditorCell_Collection resultCell = jetbrains.mps.nodeEditor.cells.EditorCell_Collection.createIndent2(getEditorContext(), getNode()); addInnerCells(resultCell); return resultCell; } } private void addInnerCells(EditorCell_Collection parentCell) { SNode realChild = getRealChild(); for (SNode node : getNodesToPresent()) { EditorCell cell; if (node == realChild) { cell = createChildCell(node); } else { cell = createChildCell_internal(node); cell.setAction(CellActionType.BACKSPACE, new CellAction_DeleteSimple(node, DeleteDirection.BACKWARD)); cell.setAction(CellActionType.DELETE, new CellAction_DeleteSimple(node, DeleteDirection.FORWARD)); } parentCell.addEditorCell(cell); } if (realChild == null) { parentCell.addEditorCell(createEmptyCellCollection()); } } @Nullable private SNode getRealChild() { Iterator<? extends SNode> childIterator = getNode().getChildren(myContainmentLink).iterator(); return childIterator.hasNext() ? childIterator.next() : null; } private EditorCell createSingleCell() { SNode child = getRealChild(); if (child != null) { return createChildCell(child); } else { return createEmptyCellCollection(); } } private boolean areAttributesEmpty() { return !AttributeOperations.getChildAttributes(getNode(), myContainmentLink).iterator().hasNext(); } private EditorCell createEmptyCellCollection() { EditorCell_Collection collection = jetbrains.mps.nodeEditor.cells.EditorCell_Collection.createIndent2(getEditorContext(), getNode()); collection.setSelectable(false); collection.addEditorCell(createEmptyCell()); collection.setAction(CellActionType.INSERT, new CellAction_InsertInSingleRole()); collection.setAction(CellActionType.INSERT_BEFORE, new CellAction_InsertInSingleRole()); return collection; } protected SNode createNodeToInsert() { return NodeFactoryManager.createNode(myContainmentLink.getTargetConcept(), null, getNode(), getNode().getModel()); } protected EditorCell createEmptyCell() { EditorCell_Label result = myContainmentLink.isOptional() ? new EditorCell_Constant(getEditorContext(), getNode(), "") : new EditorCell_Error(getEditorContext(), getNode(), getNoTargetText()); result.setDefaultText(getNoTargetText()); result.setEditable(true); return result; } protected String getNoTargetText() { return "<no " + myContainmentLink.getName() + ">"; } protected Iterable<SNode> getNodesToPresent() { return AttributeOperations.getChildNodesAndAttributes(getNode(), myContainmentLink); } private class CellAction_InsertInSingleRole extends AbstractCellAction { @Override public void execute(EditorContext context) { final SNode nodeToInsert = createNode(); SNodeEditorUtil.setSingleChild(getNode(), myContainmentLink, nodeToInsert); } private SNode createNode() { return SingleRoleCellProvider.this.createNodeToInsert(); } } }