/*
* Copyright 2003-2015 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.cells;
import com.intellij.util.ui.UIUtil;
import gnu.trove.TIntObjectHashMap;
import gnu.trove.TIntProcedure;
import jetbrains.mps.editor.runtime.TextBuilderImpl;
import jetbrains.mps.editor.runtime.commands.EditorCommand;
import jetbrains.mps.editor.runtime.impl.CellUtil;
import jetbrains.mps.editor.runtime.impl.LayoutConstraints;
import jetbrains.mps.editor.runtime.style.StyleAttributes;
import jetbrains.mps.editor.runtime.style.StyleImpl;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.AttributeOperations;
import jetbrains.mps.logging.Logger;
import jetbrains.mps.nodeEditor.EditorComponent;
import jetbrains.mps.nodeEditor.EditorManager;
import jetbrains.mps.nodeEditor.EditorMessage;
import jetbrains.mps.nodeEditor.EditorSettings;
import jetbrains.mps.nodeEditor.cellMenu.NodeSubstitutePatternEditor;
import jetbrains.mps.nodeEditor.cells.collections.Entry;
import jetbrains.mps.openapi.editor.EditorContext;
import jetbrains.mps.openapi.editor.TextBuilder;
import jetbrains.mps.openapi.editor.cells.CellAction;
import jetbrains.mps.openapi.editor.cells.CellActionType;
import jetbrains.mps.openapi.editor.cells.CellInfo;
import jetbrains.mps.openapi.editor.cells.CellMessagesUtil;
import jetbrains.mps.openapi.editor.cells.EditorCellContext;
import jetbrains.mps.openapi.editor.cells.KeyMap;
import jetbrains.mps.openapi.editor.cells.SubstituteAction;
import jetbrains.mps.openapi.editor.cells.SubstituteInfo;
import jetbrains.mps.openapi.editor.menus.transformation.TransformationMenuLookup;
import jetbrains.mps.openapi.editor.message.SimpleEditorMessage;
import jetbrains.mps.openapi.editor.style.Style;
import jetbrains.mps.smodel.IOperationContext;
import jetbrains.mps.smodel.ModelAccess;
import jetbrains.mps.smodel.SNodeLegacy;
import jetbrains.mps.smodel.adapter.MetaAdapterByDeclaration;
import jetbrains.mps.smodel.constraints.ModelConstraints;
import jetbrains.mps.util.Computable;
import jetbrains.mps.util.IterableUtil;
import jetbrains.mps.util.ListMap;
import org.apache.log4j.LogManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.mps.openapi.language.SConcept;
import org.jetbrains.mps.openapi.model.SNode;
import org.jetbrains.mps.openapi.model.SNodeId;
import org.jetbrains.mps.openapi.model.SNodeReference;
import org.jetbrains.mps.openapi.model.SNodeUtil;
import org.jetbrains.mps.util.Condition;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.InputMethodEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
/**
* Author: Sergey Dmitriev
* Created Sep 14, 2003
*/
public abstract class EditorCell_Basic implements EditorCell, Entry<jetbrains.mps.openapi.editor.cells.EditorCell> {
public static final Logger LOG = Logger.wrap(LogManager.getLogger(EditorCell_Basic.class));
public static final int BRACKET_WIDTH = 7;
private Map myUserObjects;
protected int myX = 0;
protected int myY = 0;
protected int myWidth = 0;
protected int myHeight = 0;
private int myCaretX = 0;
private boolean myErrorState;
private boolean mySelected;
@NotNull
private final EditorContext myEditorContext;
private EditorCell_Collection myParent = null;
private SNode myNode;
private SNodeId myNodeId;
private SubstituteInfo mySubstituteInfo;
private TransformationMenuLookup myTransformationMenuLookup;
private TIntObjectHashMap<CellAction> myActionMap = new TIntObjectHashMap<CellAction>();
private Style myStyle = new StyleImpl();
private KeyMap myKeyMap;
private String myCellId;
private String myRole;
private SNodeReference myLinkDeclarationPointer;
private boolean myInTree;
private boolean myIsReferenceCell = false;
protected int myGapLeft;
protected int myGapRight;
private boolean myIsNeedRelayout = true;
private boolean myBig;
private EditorCellContext myCellContext;
/**
* {@link Entry} fields
*/
private Entry<jetbrains.mps.openapi.editor.cells.EditorCell> myNext;
private Entry<jetbrains.mps.openapi.editor.cells.EditorCell> myPrev;
protected EditorCell_Basic(@NotNull EditorContext editorContext, SNode node) {
myEditorContext = editorContext;
myNode = node;
myNodeId = node == null ? null : node.getNodeId();
}
@Override
public EditorComponent getEditor() {
return (EditorComponent) getContext().getEditorComponent();
}
@Override
public jetbrains.mps.openapi.editor.EditorComponent getEditorComponent() {
return getContext().getEditorComponent();
}
@Override
public boolean isErrorState() {
return myErrorState;
}
@Override
public boolean isPunctuationLayout() {
return LayoutConstraints.PUNCTUATION_LAYOUT_CONSTRAINT.getName().equals(getStyle().get(StyleAttributes.LAYOUT_CONSTRAINT));
}
@Override
public void setErrorState(boolean errorState) {
boolean wasState = myErrorState;
myErrorState = errorState;
if (myInTree && wasState != myErrorState) {
if (myErrorState) {
getEditor().getCellTracker().addErrorCell(this);
} else {
getEditor().getCellTracker().removeErrorCell(this);
}
}
}
@Override
public boolean validate(boolean strict, boolean canActivatePopup) {
SubstituteInfo substituteInfo = getSubstituteInfo();
if (substituteInfo == null) {
return false;
}
String pattern = "";
if (this instanceof EditorCell_Label) {
pattern = ((EditorCell_Label) this).getText();
}
if (pattern.equals("")) {
return false;
}
List<SubstituteAction> matchingActions = substituteInfo.getMatchingActions(pattern, strict);
return APICellAdapter.substituteIfPossible(this, canActivatePopup, pattern, matchingActions);
}
public boolean isDrawBrackets() {
return getStyle().get(StyleAttributes.DRAW_BRACKETS);
}
/**
* @deprecated since MPS 3.4 use:
* <code>cell.getStyle().set(StyleAttributes.BACKGROUND_COLOR, color)</code>
*/
@Deprecated
@Override
public void setCellBackgroundColor(Color color) {
getStyle().set(StyleAttributes.BACKGROUND_COLOR, color);
}
/**
* @deprecated since MPS 3.4 use:
* <code>cell.getStyle().get(StyleAttributes.BACKGROUND_COLOR)</code>
*/
@Deprecated
@Override
public Color getCellBackgroundColor() {
return getStyle().get(StyleAttributes.BACKGROUND_COLOR);
}
public Color getBracketsColor() {
return getStyle().get(StyleAttributes.BRACKETS_COLOR);
}
@Override
public CellAction getAction(CellActionType type) {
return myActionMap.get(type.ordinal());
}
@Override
public Collection<CellActionType> getAvailableActions() {
final Collection<CellActionType> result = new ArrayList<CellActionType>(myActionMap.size());
myActionMap.forEachKey(new TIntProcedure() {
@Override
public boolean execute(int value) {
return result.add(CellActionType.values()[value]);
}
});
return result;
}
@Override
public void setAction(CellActionType type, CellAction action) {
myActionMap.put(type.ordinal(), action);
}
@Override
public void addKeyMap(KeyMap keyMap) {
if (myKeyMap != null) {
myKeyMap.addKeyMap(keyMap);
} else {
myKeyMap = keyMap;
}
}
@Override
public KeyMap getKeyMap() {
return myKeyMap;
}
@Override
public SNode getSNode() {
return myNode;
}
public final void setSNode(@NotNull SNode node) {
myNode = node;
myNodeId = node.getNodeId();
}
@NotNull
protected SNodeId getSNodeId() {
return myNodeId;
}
@Override
public int getHeight() {
return myHeight;
}
@Override
public void setHeight(int height) {
myHeight = height;
}
@Override
public int getBottom() {
return getY() + getHeight();
}
@Override
public int getRight() {
return getX() + getWidth();
}
@Override
public int getEffectiveWidth() {
return myWidth;
}
@Override
public int getLeftInset() {
return 0;
}
@Override
public int getRightInset() {
return 0;
}
@Override
public int getTopInset() {
return 0;
}
@Override
public int getBottomInset() {
return 0;
}
@Override
public int getWidth() {
return myWidth;
}
@Override
public void setWidth(int width) {
myWidth = width;
}
@Override
public int getY() {
return myY;
}
@Override
public void setY(int y) {
if (myY == y) {
return;
}
myY = y;
}
@Override
public int getX() {
return myX;
}
@Override
public void setX(int x) {
if (myX == x) {
return;
}
myX = x;
}
@Override
public boolean isSelected() {
return mySelected;
}
public boolean isWithinSelection() {
return mySelected && getEditor().getDeepestSelectedCell() == this;
}
@Override
public boolean isSelectable() {
return getStyle().get(StyleAttributes.SELECTABLE);
}
@Override
public void setSelectable(boolean selectable) {
getStyle().set(StyleAttributes.SELECTABLE, selectable);
}
@Override
public void setCellId(@NotNull String cellId) {
assert myCellId == null;
myCellId = cellId;
}
@Override
public String getCellId() {
return myCellId;
}
@Override
public String getRole() {
String role = getStyle().get(StyleAttributes.NAVIGATABLE_REFERENCE);
if (role != null) {
return role;
}
return myRole;
}
@Override
public void setRole(String role) {
myRole = role;
}
@Override
public void setSelected(boolean selected) {
mySelected = selected;
}
public boolean isDrawBorder() {
return getStyle().get(StyleAttributes.DRAW_BORDER);
}
@Override
public Object getUserObject(Object key) {
if (myUserObjects == null) {
return null;
}
return myUserObjects.get(key);
}
@Override
public void moveTo(int x, int y) {
myX = x;
myY = y;
}
@Override
public void putUserObject(Object key, Object value) {
if (myUserObjects == null) {
myUserObjects = new ListMap();
}
myUserObjects.put(key, value);
}
@Override
public EditorContext getContext() {
return myEditorContext;
}
public IOperationContext getOperationContext() {
return getContext().getOperationContext();
}
@Override
public final boolean processKeyPressed(KeyEvent e, boolean allowErrors) {
if (e.isConsumed()) {
return false;
}
return doProcessKeyPressed(e, allowErrors);
}
protected boolean doProcessKeyPressed(KeyEvent e, boolean allowErrors) {
return false;
}
@Override
public final boolean processKeyTyped(KeyEvent e, boolean allowErrors) {
if (e.isConsumed()) {
return false;
}
return doProcessKeyTyped(e, allowErrors);
}
protected boolean doProcessKeyTyped(final KeyEvent e, final boolean allowErrors) {
if (getSNode() == null || !isBig() || !isTextTypedEvent(e)) {
return false;
}
if (ModelAccess.instance().runReadAction(new Computable<Boolean>() {
@Override
public Boolean compute() {
return getSNode().getModel() != null && getSNode().getParent() == null;
}
})) {
return false;
}
getContext().getRepository().getModelAccess().executeCommand(new EditorCommand(getContext()) {
@Override
public void doExecute() {
EditorComponent editor = getEditor();
SNode oldNode = getSNode();
SNode newNode = replaceWithDefault();
if (newNode == null) {
EditorCell_Label editable = CellFinderUtil.findFirstEditable(EditorCell_Basic.this);
if (editable != null) {
editor.changeSelection(editable);
editor.processKeyTyped(e);
}
return;
}
newNode.putUserObject(EditorManager.OLD_NODE_FOR_SUBSTITUTION, oldNode);
EditorCell nodeCell = editor.findNodeCell(newNode);
if (nodeCell == null) {
return;
}
EditorCell_Label editable = CellFinderUtil.findFirstEditable(nodeCell);
if (editable != null) {
editor.changeSelection(editable);
editor.processKeyTyped(e);
} else {
editor.changeSelection(nodeCell);
editor.processKeyTyped(e);
}
}
});
return true;
}
@Override
public boolean processTextChanged(InputMethodEvent e) {
return false;
}
protected boolean isTextTypedEvent(KeyEvent e) {
return UIUtil.isReallyTypedEvent(e);
}
private SNode replaceWithDefault() {
SNode node = getSNode();
while (AttributeOperations.isAttribute(node)) {
node = node.getParent();
}
SNode link = new SNodeLegacy(node).getRoleLink();
SNode concept = CellUtil.getLinkDeclarationTarget(link);
SConcept concreteConcept = ModelConstraints.getDefaultConcreteConcept(MetaAdapterByDeclaration.getConcept(concept));
if (node.getConcept().equals(concreteConcept)) {
return null;
}
jetbrains.mps.smodel.SNode newNode = new jetbrains.mps.smodel.SNode(concreteConcept);
SNodeUtil.replaceWithAnother(node, newNode);
getContext().flushEvents();
return newNode;
}
@Override
public void setCaretX(int x) {
myCaretX = x;
}
@Override
public int getCaretX() {
return myCaretX;
}
@Override
public void home() {
}
@Override
public void end() {
}
@Override
public jetbrains.mps.openapi.editor.cells.EditorCell findLeaf(int x, int y) {
if (getX() <= x && x < getX() + getWidth() && getY() <= y && y < getY() + getHeight()) {
return this;
} else {
return null;
}
}
@Override
public jetbrains.mps.openapi.editor.cells.EditorCell findNearestLeafOnLine(int x, int y, Condition<jetbrains.mps.openapi.editor.cells.EditorCell> condition) {
if (getY() <= y && y < getY() + getHeight() && condition.met(this)) {
return this;
} else {
return null;
}
}
/**
* @deprecated since MPS 3.4 use {@link #findLeaf(int, int)} and check the condition upon returned cell
*/
@Deprecated
@Override
public EditorCell findLeaf(int x, int y, Condition<EditorCell> condition) {
if (myX <= x && x < myX + myWidth && myY <= y && y < myY + myHeight && condition.met(this)) {
return this;
}
return null;
}
/**
* @deprecated since MPS 3.4 use {@link #findNearestLeafOnLine(int, int, Condition)}
* using {@link com.intellij.openapi.util.Conditions#TRUE} as a parameter
*/
@Deprecated
@Override
public final EditorCell findCellWeak(int x, int y) {
return findCellWeak(x, y, Condition.TRUE_CONDITION);
}
/**
* @deprecated since MPS 3.4 use {@link #findNearestLeafOnLine(int, int, Condition)}
*/
@Deprecated
@Override
public EditorCell findCellWeak(int x, int y, Condition<EditorCell> condition) {
Set<EditorCell> candidates = new LinkedHashSet<EditorCell>();
collectCellsWithY(this, y, candidates);
EditorCell best = findClosestHorizontal(x, condition, candidates);
if (best == null) {
best = findClosestHorizontal(x, Condition.TRUE_CONDITION, candidates);
if (best != null) {
best = best.getPrevLeaf(condition);
}
}
return best;
}
private EditorCell findClosestHorizontal(int x, Condition<? super EditorCell> condition, Set<EditorCell> candidates) {
EditorCell best = null;
int bestDistance = -1;
for (EditorCell cell : candidates) {
if (!condition.met(cell)) {
continue;
}
int distance = horizontalDistance(x, cell);
if (bestDistance == -1 || distance < bestDistance) {
best = cell;
bestDistance = distance;
}
}
return best;
}
private int horizontalDistance(int x, EditorCell cell) {
if (x >= cell.getX() && x <= cell.getX() + cell.getWidth()) {
return 0;
}
return Math.min(Math.abs(x - cell.getX()), Math.abs(x - cell.getX() - cell.getWidth()));
}
private void collectCellsWithY(EditorCell current, int y, Set<EditorCell> cells) {
collectCellsWithY(current, y, cells, true);
}
private void collectCellsWithY(EditorCell current, int y, Set<EditorCell> cells, boolean leafsOnly) {
if (y >= current.getY() && y <= current.getY() + current.getHeight() && (!leafsOnly || current.isLeaf())) {
cells.add(current);
}
if (current instanceof EditorCell_Collection) {
for (EditorCell cell : ((EditorCell_Collection) current).getCells()) {
collectCellsWithY(cell, y, cells);
}
}
}
@Override
public EditorCell_Collection getParent() {
return myParent;
}
@Override
public boolean isSingleNodeCell() {
if (myParent == null) {
return true;
}
if (myParent.getSNode() != myNode) {
return true;
}
if (myParent.getCellsCount() == 1) {
return myParent.isSingleNodeCell();
}
return false;
}
void setParent(EditorCell_Collection parent) {
myParent = parent;
}
@Override
public boolean processMousePressed(MouseEvent e) {
return false;
}
@Override
public NodeSubstitutePatternEditor createSubstitutePatternEditor() {
return new NodeSubstitutePatternEditor();
}
@Override
public void setSubstituteInfo(SubstituteInfo info) {
mySubstituteInfo = info;
}
@Override
public SubstituteInfo getSubstituteInfo() {
return mySubstituteInfo;
}
@Nullable
public TransformationMenuLookup getTransformationMenuLookup() {
return myTransformationMenuLookup;
}
public void setTransformationMenuLookup(@Nullable TransformationMenuLookup transformationMenuLookup) {
myTransformationMenuLookup = transformationMenuLookup;
}
@Override
public void setBig(boolean big) {
myBig = big;
}
@Override
public boolean isBig() {
return myBig;
}
public void setReferenceCell(boolean isReference) {
myIsReferenceCell = isReference;
}
@Override
public boolean isReferenceCell() {
return myIsReferenceCell;
}
@Override
public void paint(Graphics g) {
paintCell(g, ParentSettings.createDefaultSetting());
paintDecorations(g);
}
@Override
public void paintCell(Graphics g, ParentSettings parentSettings) {
fillBackground(g, parentSettings);
paintContent(g, parentSettings);
}
@Override
public boolean isInClipRegion(Graphics g) {
// The +1 for width and height takes into account decorations such as selection or border, which may currently be drawn outside the cell.
return g.hitClip(getX(), getY(), getWidth() + 1, getHeight() + 1);
}
protected ParentSettings fillBackground(Graphics g, ParentSettings parentSettings) {
ParentSettings settings = isSelectionPaintedOnAncestor(parentSettings);
if (!settings.isSelectionPainted()) {
if (!parentSettings.isSkipBackground()) {
Color backgroundColor = getStyle().get(StyleAttributes.BACKGROUND_COLOR);
if (backgroundColor != null) {
g.setColor(backgroundColor);
paintBackground(g);
}
}
boolean hasMessages = false;
for (EditorMessage message : CellMessagesUtil.getMessages(this, EditorMessage.class)) {
if (message != null && message.showInEditor() && message.isBackground()) {
message.paint(g, getEditor(), this);
hasMessages = true;
}
}
settings = ParentSettings.createBackgroundlessSetting(hasMessages).combineWith(parentSettings);
}
paintSelectionIfRequired(g, parentSettings);
return settings;
}
protected ParentSettings isSelectionPaintedOnAncestor(ParentSettings parentSettings) {
return ParentSettings.createSelectedSetting(isSelectionPainted()).combineWith(parentSettings);
}
protected void paintBackground(Graphics g) {
g.fillRect(getX(), getY(), getWidth(), getHeight());
}
protected boolean isSelectionPainted() {
return isSelected();
}
protected void paintSelectionIfRequired(Graphics g, ParentSettings parentSettings) {
if (isSelectionPainted()) {
paintSelection(g, getSelectionColor(), true, parentSettings);
}
}
protected abstract void paintContent(Graphics g, ParentSettings parentSettings);
public void paintDecorations(Graphics g) {
int effectiveWidth = getEffectiveWidth();
if (isDrawBorder()) {
// COLORS: Remove hardcoded color
g.setColor(Color.lightGray);
g.drawRect(myX, myY, getWidth(), getHeight());
}
int leftInternalInset = getLeftInset();
if (isDrawBrackets()) {
g.setColor(getBracketsColor());
// opening bracket
g.fillRect(myX + leftInternalInset + 2, myY + 3, 2, myHeight - 5);
g.fillRect(myX + leftInternalInset + 3, myY + 2, BRACKET_WIDTH - 3, 2);
g.fillRect(myX + leftInternalInset + 3, myY + myHeight - 3, BRACKET_WIDTH - 3, 2);
// closing bracket
g.fillRect(myX + effectiveWidth - 3, myY + 3, 2, myHeight - 5);
g.fillRect(myX + effectiveWidth - BRACKET_WIDTH + 1, myY + 2, BRACKET_WIDTH - 3, 2);
g.fillRect(myX + effectiveWidth - BRACKET_WIDTH + 1, myY + myHeight - 3, BRACKET_WIDTH - 3, 2);
}
List<EditorMessage> messages = CellMessagesUtil.getMessages(this, EditorMessage.class);
for (EditorMessage message : messages) {
if (message != null && message.showInEditor() && !message.isBackground()) {
message.paint(g, getEditor(), this);
}
}
}
@Override
public List<SimpleEditorMessage> getMessages() {
return getEditor().getHighlightManager().getMessages(this);
}
@Override
public void synchronizeViewWithModel() {
}
@Override
public void setBaseline(int y) {
int relBaseline = getAscent();
moveTo(myX, y - relBaseline);
}
@Override
public int getBaseline() {
return myY + getAscent();
}
@Override
public int getAscent() {
return myHeight;
}
@Override
public int getDescent() {
return myHeight - getAscent();
}
@Override
public void paintSelection(Graphics g, Color c, boolean drawBorder) {
paintSelection(g, c, drawBorder, ParentSettings.createDefaultSetting());
}
@Override
public void paintSelection(Graphics g, Color c, boolean drawBorder, ParentSettings parentSettings) {
g.setColor(c);
g.fillRect(getX(), getY() /*+ getTopInset()*/, getWidth(), getHeight() - getTopInset() - getBottomInset());
if (getEditor().hasFocus() && drawBorder) {
g.setColor(c.darker());
g.drawRect(getX(), getY(), getWidth(), getHeight());
}
}
@Override
public TextBuilder renderText() {
return new TextBuilderImpl();
}
@Override
public final void relayout() {
if (!myIsNeedRelayout) {
return;
}
boolean drawBrackets = isDrawBrackets();
if (drawBrackets) {
myX += BRACKET_WIDTH;
}
myX += myGapLeft;
relayoutImpl();
if (drawBrackets) {
myX -= BRACKET_WIDTH;
myWidth += 2 * BRACKET_WIDTH;
}
myX -= myGapLeft;
myWidth += myGapLeft + myGapRight;
myIsNeedRelayout = false;
}
protected void relayoutImpl() {
}
/**
* @deprecated since MPS 3.4 some cells can implement {@link jetbrains.mps.openapi.editor.cells.optional.WithCaret}
* interface in order to have this method.
*/
@Deprecated
@Override
public void switchCaretVisible() {
}
@NotNull
@Override
public CellInfo getCellInfo() {
return new DefaultCellInfo(this);
}
/**
* @deprecated since MPS 3.4 not used
*/
@Deprecated
@Override
public boolean isAncestorOf(EditorCell cell) {
jetbrains.mps.openapi.editor.cells.EditorCell_Collection parent = cell.getParent();
while (parent != null) {
if (parent == this) {
return true;
}
parent = parent.getParent();
}
return false;
}
public Color getSelectionColor() {
return EditorSettings.getInstance().getSelectionBackgroundColor();
}
public static Color getRangeSelectionColor() {
return EditorSettings.getInstance().getRangeSelectionForegroundColor();
}
/**
* @deprecated since MPS 3.4 not used
*/
@Deprecated
@Override
public Iterator<EditorCell_Collection> parents() {
return new Iterator<EditorCell_Collection>() {
private EditorCell myCurrentCell = EditorCell_Basic.this;
@Override
public boolean hasNext() {
return myCurrentCell.getParent() != null;
}
@Override
public EditorCell_Collection next() {
EditorCell_Collection parent = (EditorCell_Collection) myCurrentCell.getParent();
if (parent == null) {
throw new NoSuchElementException();
}
myCurrentCell = parent;
return parent;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
/**
* @deprecated since MPS 3.4 not used
*/
@Deprecated
@Override
public EditorCell_Collection findParent(Condition<EditorCell_Collection> condition) {
if (this instanceof EditorCell_Collection && condition.met((EditorCell_Collection) this)) {
return (EditorCell_Collection) this;
}
for (EditorCell_Collection collection : IterableUtil.asIterable(parents())) {
if (condition.met(collection)) {
return collection;
}
}
return null;
}
@Override
public EditorCell getRootParent() {
EditorCell cell = this;
EditorCell prevCell = null;
while (cell != null) {
prevCell = cell;
cell = (EditorCell) cell.getParent();
}
return prevCell;
}
@Override
public boolean isFirstCaretPosition() {
return false;
}
@Override
public boolean isLastCaretPosition() {
return false;
}
@Override
public boolean isFirstPositionInBigCell() {
return false;
}
@Override
public boolean isLastPositionInBigCell() {
return false;
}
/**
* @deprecated since MPS 3.4 use {@link jetbrains.mps.openapi.editor.cells.CellTraversalUtil#getContainingBigCell(jetbrains.mps.openapi.editor.cells.EditorCell)}
*/
@Deprecated
@Override
public EditorCell getContainingBigCell() {
if (isBig() || getParent() == null) {
return this;
}
return getParent().getContainingBigCell();
}
/**
* @deprecated since MPS 3.4 use {@link GeometryUtil#isAbove(jetbrains.mps.openapi.editor.cells.EditorCell, jetbrains.mps.openapi.editor.cells.EditorCell)}
* like: isAbove(this, cell)
*/
@Deprecated
@Override
public boolean isAbove(EditorCell cell) {
return GeometryUtil.isAbove(this, cell);
}
/**
* @deprecated since MPS 3.4 use {@link GeometryUtil#isAbove(jetbrains.mps.openapi.editor.cells.EditorCell, jetbrains.mps.openapi.editor.cells.EditorCell)}
* like: isAbove(cell, this)
*/
@Deprecated
@Override
public boolean isBelow(EditorCell cell) {
return GeometryUtil.isAbove(cell, this);
}
@Override
public boolean isToLeft(EditorCell cell) {
return GeometryUtil.isLeftToRight(this, cell);
}
@Override
public boolean isToRight(EditorCell cell) {
return cell.isToLeft(this);
}
/**
* @deprecated since MPS 3.4 not used
*/
@Deprecated
@Override
public EditorCell getUpper(Condition<EditorCell> condition, int baseX) {
EditorCell bestMatch = null;
EditorCell current = getPrevLeaf(condition);
while (current != null) {
if (GeometryUtil.isAbove(current, this)) {
if (bestMatch != null && GeometryUtil.isAbove(current, bestMatch)) {
break;
}
if (bestMatch != null) {
if (GeometryUtil.getHorizontalDistance(bestMatch, baseX) > GeometryUtil.getHorizontalDistance(current, baseX)) {
bestMatch = current;
}
} else {
bestMatch = current;
}
}
current = current.getPrevLeaf(condition);
}
return bestMatch;
}
/**
* @deprecated since MPS 3.4 not used
*/
@Deprecated
@Override
public EditorCell getLower(Condition<EditorCell> condition, int baseX) {
EditorCell bestMatch = null;
EditorCell current = getNextLeaf(condition);
while (current != null) {
if (current.isBelow(this)) {
if (bestMatch != null && current.isBelow(bestMatch)) {
break;
}
if (bestMatch != null) {
if (GeometryUtil.getHorizontalDistance(bestMatch, baseX) > GeometryUtil.getHorizontalDistance(current, baseX)) {
bestMatch = current;
}
} else {
bestMatch = current;
}
}
current = current.getNextLeaf(condition);
}
return bestMatch;
}
/**
* @deprecated since MPS 3.4 not used
*/
@Deprecated
@Override
public EditorCell getEndCell(Condition<EditorCell> condition) {
EditorCell current = this;
while (current.getLeafToRight(condition) != null) {
current = current.getLeafToRight(condition);
}
return current.getLastLeaf(condition);
}
/**
* @deprecated since MPS 3.4 not used
*/
@Deprecated
@Override
public EditorCell getHomeCell(Condition<EditorCell> condition) {
EditorCell current = this;
while (current.getLeafToLeft(condition) != null) {
current = current.getLeafToLeft(condition);
}
return current.getFirstLeaf();
}
/**
* @deprecated since MPS 3.4 not used
*/
@Deprecated
@Override
public EditorCell getLeafToLeft(Condition<EditorCell> condition) {
return getPrevLeaf(new Condition<EditorCell>() {
@Override
public boolean met(EditorCell current) {
return current.isSelectable() && !GeometryUtil.isAbove(EditorCell_Basic.this, current) && !isBelow(current) && isToRight(current);
}
});
}
/**
* @deprecated since MPS 3.4 not used
*/
@Deprecated
@Override
public EditorCell getLeafToRight(Condition<EditorCell> condition) {
return getNextLeaf(new Condition<EditorCell>() {
@Override
public boolean met(EditorCell current) {
return current.isSelectable() && !GeometryUtil.isAbove(EditorCell_Basic.this, current) && !isBelow(current) && isToLeft(current);
}
});
}
@Override
public EditorCell getNextSibling() {
return myNext != null ? (EditorCell) myNext.getItem() : null;
}
@Override
public EditorCell getPrevSibling() {
return myPrev != null && myPrev.getNext() != null ? (EditorCell) myPrev.getItem() : null;
}
/**
* @deprecated since MPS 3.4 not used
*/
@Deprecated
@Override
public EditorCell getNextLeaf() {
if (getNextSibling() != null) {
return getNextSibling().getFirstLeaf();
}
if (myParent != null) {
return myParent.getNextLeaf();
}
return null;
}
/**
* @deprecated since MPS 3.4 not used
*/
@Deprecated
@Override
public EditorCell getNextLeaf(Condition<EditorCell> condition) {
EditorCell current = getNextLeaf();
while (current != null) {
if (condition.met(current)) {
return current;
}
current = current.getNextLeaf();
}
return null;
}
/**
* @deprecated since MPS 3.4 not used
*/
@Deprecated
@Override
public EditorCell getPrevLeaf() {
if (getPrevSibling() != null) {
return getPrevSibling().getLastLeaf();
}
if (myParent != null) {
return myParent.getPrevLeaf();
}
return null;
}
/**
* @deprecated since MPS 3.4 not used
*/
@Deprecated
@Override
public EditorCell getPrevLeaf(Condition<EditorCell> condition) {
EditorCell current = getPrevLeaf();
while (current != null) {
if (condition.met(current)) {
return current;
}
current = current.getPrevLeaf();
}
return null;
}
/**
* @deprecated since MPS 3.4 not used
*/
@Deprecated
@Override
public EditorCell getFirstLeaf() {
return this;
}
/**
* @deprecated since MPS 3.4 not used
*/
@Deprecated
@Override
public EditorCell getLastLeaf() {
return this;
}
/**
* @deprecated since MPS 3.4 not used
*/
@Deprecated
@Override
public EditorCell getFirstLeaf(final Condition<EditorCell> condition) {
EditorCell firstLeaf = getFirstLeaf();
if (condition.met(firstLeaf)) {
return firstLeaf;
}
return firstLeaf.getNextLeaf(new Condition<EditorCell>() {
@Override
public boolean met(EditorCell object) {
return isAncestorOf(object) && condition.met(object);
}
});
}
/**
* @deprecated since MPS 3.4 not used
*/
@Deprecated
@Override
public EditorCell getLastLeaf(final Condition<EditorCell> condition) {
EditorCell lastLeaf = getLastLeaf();
if (condition.met(lastLeaf)) {
return lastLeaf;
}
return lastLeaf.getPrevLeaf(new Condition<EditorCell>() {
@Override
public boolean met(EditorCell object) {
return isAncestorOf(object) && condition.met(object);
}
});
}
@Override
public EditorCell getLastChild() {
return this;
}
@Override
public EditorCell getFirstChild() {
return this;
}
@Override
public Style getStyle() {
return myStyle;
}
@Override
public boolean isLeaf() {
return true;
}
public boolean isInTree() {
return myInTree;
}
public void onAdd() {
myInTree = true;
if (isErrorState()) {
getEditor().getCellTracker().addErrorCell(this);
}
}
public void onRemove() {
myInTree = false;
if (isErrorState()) {
getEditor().getCellTracker().removeErrorCell(this);
}
}
@Override
public void setLeftGap(int gap) {
// TODO: remove this line and modify getEffectiveWidth() method in order to return
// getWidth() + myGapRight + myGapLeft
// most of getWidth() usages must be replaced with getEffectiveWidth() then.
myWidth = myWidth - myGapLeft + gap;
myGapLeft = gap;
}
public int getLeftGap() {
return myGapLeft;
}
@Override
public void setRightGap(int gap) {
// TODO: remove this line and modify getEffectiveWidth() method in order to return
// getWidth() + myGapRight + myGapLeft
// most of getWidth() usages must be replaced with getEffectiveWidth() then.
myWidth = myWidth - myGapRight + gap;
myGapRight = gap;
}
public int getRightGap() {
return myGapRight;
}
public void requestRelayout() {
if (myIsNeedRelayout) {
return;
}
myIsNeedRelayout = true;
if (getParent() != null) {
getParent().requestRelayout();
}
}
// Following methods are used from layout algorythms
@Override
public boolean wasRelayoutRequested() {
return myIsNeedRelayout;
}
public void unrequestLayout() {
myIsNeedRelayout = false;
}
@Override
public void setCellContext(EditorCellContext cellContext) {
myCellContext = cellContext;
}
@Override
public EditorCellContext getCellContext() {
return myCellContext;
}
/**
* {@link Entry} methods
*/
@NotNull
@Override
public jetbrains.mps.openapi.editor.cells.EditorCell getItem() {
return this;
}
@Override
public Entry<jetbrains.mps.openapi.editor.cells.EditorCell> getNext() {
return myNext;
}
@Override
public void setNext(Entry<jetbrains.mps.openapi.editor.cells.EditorCell> next) {
myNext = next;
}
@Override
public Entry<jetbrains.mps.openapi.editor.cells.EditorCell> getPrev() {
return myPrev;
}
@Override
public void setPrev(Entry<jetbrains.mps.openapi.editor.cells.EditorCell> prev) {
myPrev = prev;
}
}