/* * Copyright 2003-2014 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.sidetransform; import com.intellij.ui.LightColors; import jetbrains.mps.editor.runtime.SideTransformInfoUtil; import jetbrains.mps.editor.runtime.cells.AbstractCellAction; import jetbrains.mps.editor.runtime.cells.KeyMapActionImpl; import jetbrains.mps.editor.runtime.cells.KeyMapImpl; import jetbrains.mps.editor.runtime.style.StyleAttributes; import jetbrains.mps.nodeEditor.CellSide; import jetbrains.mps.nodeEditor.cellActions.SideTransformSubstituteInfo; import jetbrains.mps.nodeEditor.cellActions.SideTransformSubstituteInfo.Side; import jetbrains.mps.nodeEditor.cells.DefaultCellInfo; import jetbrains.mps.nodeEditor.cells.EditorCell_Constant; import jetbrains.mps.nodeEditor.cells.EditorCell_Label; import jetbrains.mps.nodeEditor.cells.SynchronizeableEditorCell; import jetbrains.mps.openapi.editor.EditorComponent; import jetbrains.mps.openapi.editor.EditorContext; import jetbrains.mps.openapi.editor.cells.CellActionType; import jetbrains.mps.openapi.editor.cells.CellInfo; import jetbrains.mps.openapi.editor.cells.EditorCell; import jetbrains.mps.openapi.editor.cells.EditorCellContext; import jetbrains.mps.openapi.editor.cells.EditorCell_Collection; import jetbrains.mps.openapi.editor.cells.KeyMap; import jetbrains.mps.util.annotation.ToRemove; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.mps.openapi.model.SNode; /** * User: shatalin * Date: 27/02/14 */ public class EditorCell_STHint extends EditorCell_Constant { private static final String CELL_ID = "STHint"; @Nullable private final CellInfo myRestoreSelectionCellInfo; @NotNull private final EditorCell myBigCell; @NotNull private final EditorCell myAnchorCell; @NotNull private final Side mySide; private boolean myInstalled; public static EditorCell_STHint getSTHintCell(SNode node, @NotNull EditorComponent editorComponent) { EditorCell stHintCell = editorComponent.findCellWithId(node, CELL_ID); return stHintCell instanceof EditorCell_STHint ? (EditorCell_STHint) stHintCell : null; } /** * @deprecated after MPS 3.4 side transform actions will be migrated from actions aspect to editor aspect * so the will be referenced directly from editor and anchor tag will not be used. * Use {@link EditorCell_STHint#EditorCell_STHint(EditorCell, EditorCell, Side, CellInfo)} } */ @Deprecated @ToRemove(version = 2017.2) public EditorCell_STHint(@NotNull EditorCell bigCell, @NotNull EditorCell anchorCell, @NotNull CellSide oldSide, @NotNull String sideTransformTag, @Nullable CellInfo restoreSelectionCellInto) { this(bigCell, anchorCell, oldSide == CellSide.LEFT ? Side.LEFT : Side.RIGHT, restoreSelectionCellInto); } public EditorCell_STHint(@NotNull EditorCell bigCell, @NotNull EditorCell anchorCell, @NotNull Side side, @Nullable CellInfo restoreSelectionCellInto) { super(anchorCell.getContext(), anchorCell.getSNode(), ""); assert bigCell.isBig(); myBigCell = bigCell; myAnchorCell = anchorCell; mySide = side; myRestoreSelectionCellInfo = restoreSelectionCellInto; init(); } private void init() { setCellId(CELL_ID); setDefaultText(" "); setEditable(true); getStyle().set(StyleAttributes.BACKGROUND_COLOR, LightColors.BLUE); getStyle().set(StyleAttributes.PUNCTUATION_LEFT, true); getStyle().set(StyleAttributes.PUNCTUATION_RIGHT, true); getStyle().set(StyleAttributes.FIRST_POSITION_ALLOWED, true); getStyle().set(StyleAttributes.LAST_POSITION_ALLOWED, true); RemoveSTHintAction removeSTHintAction = new RemoveSTHintAction(); // delete the hint when pressed ctrl-delete, delete or backspace setAction(CellActionType.DELETE, removeSTHintAction); setAction(CellActionType.BACKSPACE, removeSTHintAction); // delete the hint when double press 'space' setAction(CellActionType.RIGHT_TRANSFORM, removeSTHintAction); setAction(CellActionType.LEFT_TRANSFORM, removeSTHintAction); // delete the hint when double press 'esc' KeyMap keyMap = new KeyMapImpl(); keyMap.putAction(KeyMap.KEY_MODIFIERS_NONE, "VK_ESCAPE", new RemoveSTHintKeyMapAction()); addKeyMap(keyMap); // create the hint's auto-completion menu setSubstituteInfo(new SideTransformSubstituteInfo(myAnchorCell, mySide)); } @NotNull @Override public CellInfo getCellInfo() { return new STHintCellInfo(EditorCell_STHint.this, myAnchorCell); } @Override public void changeText(String text) { super.changeText(text); if ("".equals(getText())) { SideTransformInfoUtil.removeTransformInfo(getSNode()); } } @Override public void setCaretPosition(int position, boolean selection) { if (position != getText().length() && !isRight()) { validate(true, false); } super.setCaretPosition(position, selection); } @Override public void synchronizeViewWithModel() { } private void removeSTHintAndChangeSelection(final EditorContext context) { SideTransformInfoUtil.removeTransformInfo(getSNode()); context.flushEvents(); if (myRestoreSelectionCellInfo == null) { return; } jetbrains.mps.nodeEditor.EditorComponent editorComponent = (jetbrains.mps.nodeEditor.EditorComponent) context.getEditorComponent(); EditorCell newlySelectedCell = myRestoreSelectionCellInfo.findCell(editorComponent); if (newlySelectedCell == null) { return; } editorComponent.changeSelection(newlySelectedCell); if (newlySelectedCell instanceof EditorCell_Label) { newlySelectedCell.end(); } } public EditorCell install() { assert !myInstalled; myInstalled = true; if (myAnchorCell == myBigCell) { // Creating wrapper collection to hold both nodeCell & STHintCell jetbrains.mps.nodeEditor.cells.EditorCell_Collection wrapperCell = jetbrains.mps.nodeEditor.cells.EditorCell_Collection.createHorizontal(getContext(), getSNode()); wrapperCell.setSelectable(false); wrapperCell.setBig(true); EditorCellContext cellContext = myBigCell.getCellContext(); assert cellContext != null; wrapperCell.setCellContext(cellContext); wrapperCell.setCanBeSynchronized(myBigCell instanceof SynchronizeableEditorCell && ((SynchronizeableEditorCell) myBigCell).canBeSynchronized()); myBigCell.setBig(false); if (!isRight()) { wrapperCell.addEditorCell(this); } wrapperCell.addEditorCell(myBigCell); if (isRight()) { wrapperCell.addEditorCell(this); } return wrapperCell; } EditorCell_Collection cellCollection = myAnchorCell.getParent(); if (isRight()) { cellCollection.addEditorCellAfter(this, myAnchorCell); } else { cellCollection.addEditorCellBefore(this, myAnchorCell); } return myBigCell; } private boolean isRight() { return mySide == Side.RIGHT; } @NotNull public Side getSide() { return mySide; } public EditorCell uninstall() { assert myInstalled; if (myAnchorCell == myBigCell) { // not necessary to remove EditorCell_STHint from wrapper // collection - just returning myBigCell in the end myBigCell.setBig(true); } else { this.getParent().removeCell(this); } return myBigCell; } private class RemoveSTHintAction extends AbstractCellAction { @Override public void execute(EditorContext context) { removeSTHintAndChangeSelection(context); } } private class RemoveSTHintKeyMapAction extends KeyMapActionImpl { @Override public void execute(EditorContext context) { removeSTHintAndChangeSelection(context); } } private static class STHintCellInfo extends DefaultCellInfo { CellInfo myAnchorCellInfo; STHintCellInfo(EditorCell_STHint rightTransformHintCell, EditorCell anchorCell) { super(rightTransformHintCell); myAnchorCellInfo = anchorCell.getCellInfo(); } @Override public EditorCell findCell(@NotNull EditorComponent editorComponent) { EditorCell anchorCell = myAnchorCellInfo.findCell(editorComponent); return anchorCell != null ? getSTHintCell(anchorCell.getSNode(), editorComponent) : super.findCell(editorComponent); } @Override public EditorCell findClosestCell(@NotNull EditorComponent editorComponent) { EditorCell anchorCell = myAnchorCellInfo.findCell(editorComponent); if (anchorCell == null) { return super.findCell(editorComponent); } EditorCell_Label rtHint = getSTHintCell(anchorCell.getSNode(), editorComponent); return rtHint != null ? rtHint : (jetbrains.mps.nodeEditor.cells.EditorCell) anchorCell; } } }