blob: 2872699c80120c0d6f5d4a843005a4c5c3cfa31c [file] [log] [blame]
/*
* Copyright 2000-2009 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 com.intellij.openapi.editor.impl;
import com.intellij.openapi.editor.ex.MarkupModelEx;
import com.intellij.openapi.editor.ex.RangeHighlighterEx;
import com.intellij.openapi.editor.highlighter.EditorHighlighter;
import com.intellij.openapi.editor.markup.EffectType;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.TextRange;
import com.intellij.util.Processor;
import com.intellij.util.ui.UIUtil;
import gnu.trove.Equality;
import java.awt.*;
public class BorderEffect {
private final Graphics myGraphics;
private final int myStartOffset;
private final int myEndOffset;
private final TextRange myRange;
private final EditorImpl myEditor;
private static final Equality<TextAttributes> SAME_COLOR_BOXES = new Equality<TextAttributes>() {
@Override
public boolean equals(final TextAttributes attributes1, final TextAttributes attributes2) {
Color effectColor = attributes1.getEffectColor();
EffectType effectType = attributes1.getEffectType();
return effectColor != null
&& effectColor.equals(attributes2.getEffectColor())
&& (EffectType.BOXED == effectType || EffectType.ROUNDED_BOX == effectType) &&
effectType == attributes2.getEffectType();
}
};
private static final Condition<TextAttributes> BOX_FILTER = new Condition<TextAttributes>() {
@Override
public boolean value(TextAttributes attributes) {
return isBorder(attributes);
}
};
public BorderEffect(EditorImpl editor, Graphics graphics, int clipStartOffset, int clipEndOffset) {
myEditor = editor;
myGraphics = graphics;
myStartOffset = clipStartOffset;
myEndOffset = clipEndOffset;
myRange = new TextRange(myStartOffset, myEndOffset);
}
private static boolean isBorder(TextAttributes textAttributes) {
return textAttributes != null &&
textAttributes.getEffectColor() != null &&
(EffectType.BOXED == textAttributes.getEffectType() || EffectType.ROUNDED_BOX == textAttributes.getEffectType());
}
private void paintBorder(RangeHighlighterEx rangeHighlighter, TextAttributes textAttributes) {
paintBorder(textAttributes.getEffectColor(),
rangeHighlighter.getAffectedAreaStartOffset(),
rangeHighlighter.getAffectedAreaEndOffset(),
textAttributes.getEffectType());
}
private void paintBorder(Color color, int startOffset, int endOffset, EffectType effectType) {
paintBorder(myGraphics, myEditor, startOffset, endOffset, color, effectType);
}
private boolean intersectsRange(RangeHighlighterEx rangeHighlighter) {
return myRange.contains(rangeHighlighter.getAffectedAreaStartOffset()) ||
myRange.contains(rangeHighlighter.getAffectedAreaEndOffset());
}
public void paintHighlighters(MarkupModelEx markupModel) {
markupModel.processRangeHighlightersOverlappingWith(myStartOffset, myEndOffset, new Processor<RangeHighlighterEx>() {
@Override
public boolean process(RangeHighlighterEx rangeHighlighter) {
if (!rangeHighlighter.getEditorFilter().avaliableIn(myEditor)) return true;
TextAttributes textAttributes = rangeHighlighter.getTextAttributes();
if (isBorder(textAttributes) && intersectsRange(rangeHighlighter)) {
paintBorder(rangeHighlighter, textAttributes);
}
return true;
}
});
}
public void paintHighlighters(EditorHighlighter highlighter) {
int startOffset = startOfLineByOffset(myStartOffset);
if (startOffset < 0 || startOffset >= myEditor.getDocument().getTextLength()) return;
RangeIterator iterator = new RangeIterator(new FoldingOrNewLineGaps(myEditor), SAME_COLOR_BOXES,
highlighter.createIterator(startOffset),
BOX_FILTER);
iterator.init(myRange);
while (!iterator.atEnd()) {
iterator.advance();
paintBorder(myGraphics, myEditor, iterator.getStart(), iterator.getEnd(),
iterator.getTextAttributes().getEffectColor(), iterator.getTextAttributes().getEffectType());
}
}
private int startOfLineByOffset(int offset) {
int line = myEditor.offsetToLogicalLine(offset);
if (line >= myEditor.getDocument().getLineCount()) return -1;
return myEditor.getDocument().getLineStartOffset(line);
}
private static void paintBorder(Graphics g, EditorImpl editor, int startOffset, int endOffset, Color color, EffectType effectType) {
Color savedColor = g.getColor();
g.setColor(color);
paintBorder(g, editor, startOffset, endOffset, effectType);
g.setColor(savedColor);
}
private static void paintBorder(Graphics g, EditorImpl editor, int startOffset, int endOffset, EffectType effectType) {
Point startPoint = offsetToXY(editor, startOffset);
Point endPoint = offsetToXY(editor, endOffset);
int height = endPoint.y - startPoint.y;
int startX = startPoint.x;
int startY = startPoint.y;
int endX = endPoint.x;
if (height == 0) {
int width = endX == startX ? 1 : endX - startX - 1;
if (effectType == EffectType.ROUNDED_BOX) {
UIUtil.drawRectPickedOut((Graphics2D)g, startX, startY, width, editor.getLineHeight() - 1);
} else {
g.drawRect(startX, startY, width, editor.getLineHeight() - 1);
}
return;
}
BorderGraphics border = new BorderGraphics(g, startX, startY, effectType);
border.horizontalTo(editor.getMaxWidthInRange(startOffset, endOffset) - 1);
border.verticalRel(height - 1);
border.horizontalTo(endX);
border.verticalRel(editor.getLineHeight());
border.horizontalTo(0);
border.verticalRel(-height + 1);
border.horizontalTo(startX);
border.verticalTo(startY);
}
private static Point offsetToXY(EditorImpl editor, int offset) {
return editor.logicalPositionToXY(editor.offsetToLogicalPosition(offset));
}
public static void paintFoldedEffect(Graphics g, int foldingXStart,
int y, int foldingXEnd, int lineHeight, Color effectColor,
EffectType effectType) {
if (effectColor == null || effectType != EffectType.BOXED) return;
g.setColor(effectColor);
g.drawRect(foldingXStart, y, foldingXEnd - foldingXStart, lineHeight - 1);
}
private static class FoldingOrNewLineGaps implements RangeIterator.Gaps {
private final RangeIterator.FoldingGaps myFoldingGaps;
private final CharSequence myChars;
public FoldingOrNewLineGaps(CharSequence chars, RangeIterator.FoldingGaps foldingGaps) {
myChars = chars;
myFoldingGaps = foldingGaps;
}
public FoldingOrNewLineGaps(EditorImpl editor) {
this(editor.getDocument().getCharsSequence(), new RangeIterator.FoldingGaps(editor.getFoldingModel()));
}
@Override
public boolean isGapAt(int offset) {
return myChars.charAt(offset) == '\n' || myFoldingGaps.isGapAt(offset);
}
}
public static class BorderGraphics {
private final Graphics myGraphics;
private int myX;
private int myY;
private EffectType myEffectType;
public BorderGraphics(Graphics graphics, int startX, int stIntY, EffectType effectType) {
myGraphics = graphics;
myX = startX;
myY = stIntY;
myEffectType = effectType;
}
public void horizontalTo(int x) {
lineTo(x, myY);
}
public void horizontalRel(int width) {
lineTo(myX + width, myY);
}
private void lineTo(int x, int y) {
if (myEffectType == EffectType.ROUNDED_BOX) {
UIUtil.drawLinePickedOut(myGraphics, myX, myY, x, y);
} else {
UIUtil.drawLine(myGraphics, myX, myY, x, y);
}
myX = x;
myY = y;
}
public void verticalRel(int height) {
lineTo(myX, myY + height);
}
public void verticalTo(int y) {
lineTo(myX, y);
}
}
}