/* * Copyright 2003-2011 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.leftHighlighter; import jetbrains.mps.nodeEditor.leftHighlighter.HighlighterBracket.BracketEdge; import jetbrains.mps.openapi.editor.cells.CellInfo; import jetbrains.mps.openapi.editor.cells.EditorCell; import java.awt.Color; import java.awt.Graphics; import java.awt.Rectangle; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import java.util.Stack; /** * User: Alexander Shatalin * Date: 02.03.2010 */ public class BracketsPainter extends AbstractFoldingAreaPainter { private int myAreaWidth = -1; private HashMap<CellInfo, HighlighterBracket> myBrackets = new HashMap<CellInfo, HighlighterBracket>(); private boolean myRightToLeft; public BracketsPainter(LeftEditorHighlighter leftEditorHighlighter, boolean rightToLeft) { super(leftEditorHighlighter); myRightToLeft = rightToLeft; } @Override public int getLeftAreaWidth() { return myRightToLeft ? super.getLeftAreaWidth() : getAreaWidth(); } @Override public int getRightAreaWidth() { return myRightToLeft ? getAreaWidth() : super.getRightAreaWidth(); } private int getAreaWidth() { if (myAreaWidth < 0) { myAreaWidth = relayoutBrackets(); } return myAreaWidth; } @Override public void relayout() { myAreaWidth = -1; } private int relayoutBrackets() { for (Iterator<Entry<CellInfo, HighlighterBracket>> it = myBrackets.entrySet().iterator(); it.hasNext(); ) { Entry<CellInfo, HighlighterBracket> nextEntry = it.next(); if (!nextEntry.getValue().relayout()) { // TODO: check if this code is useful it.remove(); } } List<BracketEdge> bracketEdges = new ArrayList<BracketEdge>(); for (HighlighterBracket bracket : myBrackets.values()) { bracket.setLevel(1); bracketEdges.add(bracket.getBeginningEdge()); bracketEdges.add(bracket.getEndingEdge()); } Collections.sort(bracketEdges); int maxLevel = 0; Stack<HighlighterBracket> myBracketsLayoutStack = new Stack<HighlighterBracket>(); for (int i = 0; i < bracketEdges.size(); i++) { BracketEdge edge = bracketEdges.get(i); HighlighterBracket bracket = edge.getBracket(); if (edge.isBeggining()) { myBracketsLayoutStack.push(bracket); } else { HighlighterBracket bracketFromStack = null; while (bracketFromStack != bracket) { if (bracketFromStack != null) { // layout error: last popped bracket corresponds to another ending edge. Skipping this bracket. bracketEdges.remove(bracketFromStack.getEndingEdge()); myBrackets.remove(bracketFromStack.getCell()); } if (!myBracketsLayoutStack.isEmpty()) { HighlighterBracket newBracketFromStack = myBracketsLayoutStack.pop(); if (bracketFromStack != null) { // last popped bracket was incorrect - see "layout error" comment above, so copying level informatin from there newBracketFromStack.setLevel(Math.max(newBracketFromStack.getLevel(), bracketFromStack.getLevel())); } bracketFromStack = newBracketFromStack; } else { break; } } if (bracketFromStack != bracket) { // layout error - no bracket corresponding to this endig edge was found in stack. Skipping this bracket. myBrackets.remove(bracket.getCell()); } else { if (!myBracketsLayoutStack.isEmpty()) { // setting level for next bracket on stack HighlighterBracket onTopOfStack = myBracketsLayoutStack.peek(); onTopOfStack.setLevel(Math.max(onTopOfStack.getLevel(), bracket.getLevel() + 1)); } maxLevel = Math.max(bracket.getLevel(), maxLevel); } } } return maxLevel == 0 ? 0 : HighlighterBracket.getBracketWidth(maxLevel); } @Override public void paintInLocalCoordinates(Graphics g) { Rectangle clipBounds = g.getClipBounds(); for (HighlighterBracket bracket : myBrackets.values()) { bracket.paint(g, clipBounds); } } @Override public int getWeight() { return 1; } public void removeBracket(EditorCell cell) { myBrackets.remove(cell.getCellInfo()); myAreaWidth = -1; } public void addBracket(EditorCell cell, EditorCell secondCell, Color c) { CellInfo info1 = cell.getCellInfo(); CellInfo info2 = secondCell.getCellInfo(); myBrackets.put(info1, new HighlighterBracket(info1, info2, c, getEditorComponent(), myRightToLeft)); myAreaWidth = -1; } }