blob: b179b7d4dd0a0482a2520419fd7cea96e804cdc2 [file] [log] [blame]
/*
* Copyright 2000-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 com.intellij.openapi.diff.impl.highlighting;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.editor.impl.EditorImpl;
import com.intellij.openapi.editor.markup.LineMarkerRenderer;
import com.intellij.openapi.editor.markup.LineSeparatorRenderer;
import com.intellij.openapi.util.Couple;
import com.intellij.util.Consumer;
import com.intellij.util.containers.Convertor;
import com.intellij.util.ui.UIUtil;
import java.awt.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
/**
* @author irengrig
* Date: 7/6/11
* Time: 7:44 PM
*/
public class FragmentBoundRenderer implements LineMarkerRenderer, LineSeparatorRenderer {
private final int myLineHeight;
private final Editor myEditor;
private final Consumer<Integer> myOffsetsConsumer;
private final ShoeneLine myShoeneLine;
private final Color myMainColor;
public FragmentBoundRenderer(int lineHeight, final Editor editor, final Consumer<Integer> offsetsConsumer) {
myLineHeight = lineHeight;
myEditor = editor;
myOffsetsConsumer = offsetsConsumer;
myShoeneLine = new ShoeneLine(2);
myMainColor = darkerBorder();
}
public static Color darkerBorder() {
final Color borderColor = UIUtil.getBorderColor();
return new Color(borderColor.getRed() + 10, borderColor.getGreen() + 10, borderColor.getBlue() + 10);
}
// only top
@Override
public void paint(Editor editor, Graphics g, Rectangle r) {
final Graphics gr = g.create();
try {
((Graphics2D) gr).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
gr.setColor(getColor());
int y = r.y;
final int width = ((EditorEx)editor).getGutterComponentEx().getWidth();
final int editorWidth = editor.getScrollingModel().getVisibleArea().width;
myShoeneLine.ensureLastX(editorWidth + width + width);
if (((EditorImpl) editor).isMirrored()) {
// continue
List<Couple<Integer>> points = myShoeneLine.getPoints();
int i = 0;
for (; i < points.size(); i++) {
Couple<Integer> integerIntegerPair = points.get(i);
if (integerIntegerPair.getFirst() - width >= editorWidth) {
break;
}
}
// take previous
i = i == 0 ? 0 : i - 1;
points = points.subList(i, points.size());
drawCurved(gr, 0, r.y, TornLineParams.ourDark, points, width + editorWidth, true,width);
gr.setColor(getColor().darker());
drawCurved(gr, 0, r.y, TornLineParams.ourLight, points, width + editorWidth, true,width);
int j = points.size() - 1;
final int finalX = width + editorWidth;
for (; j > 0; j--) {
if (points.get(j).getFirst() >= finalX) break;
}
myOffsetsConsumer.consume(points.get(j).getSecond());
} else {
List<Couple<Integer>> points = myShoeneLine.getPoints();
drawCurved(gr, 0, r.y, TornLineParams.ourDark, points, 0, false,0);
gr.setColor(getColor().darker());
drawCurved(gr, 0, r.y, TornLineParams.ourLight, points, 0, false,0);
myOffsetsConsumer.consume(points.get(0).getSecond());
}
} finally {
gr.dispose();
}
}
public Color getColor() {
return myMainColor;
}
@Override
public void drawLine(Graphics g, int x1, int x2, int y) {
final int length = x2 - x1;
if (length == 0) return;
final int width = ((EditorEx) myEditor).getGutterComponentEx().getWidth();
myShoeneLine.ensureLastX(length + width);
final Graphics gr = g.create();
try {
((Graphics2D) gr).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
gr.setColor(getColor());
List<Couple<Integer>> points = myShoeneLine.getPoints();
int i = getLastPointInBeforeGutter(width, points);
points = points.subList(i, points.size());
drawCurved(gr, x1, y, TornLineParams.ourDark, points, width, false,0);
gr.setColor(getColor().darker());
drawCurved(gr, x1, y, TornLineParams.ourLight, points, width, false,0);
} finally {
gr.dispose();
}
}
private int getLastPointInBeforeGutter(int width, List<Couple<Integer>> points) {
int i = 0;
for (; i < points.size(); i++) {
if (points.get(i).getFirst() >= width) break;
}
i = i == 0 ? 0 : i - 1;
return i;
}
private void drawCurved(Graphics g,
final int x1,
int y,
final int offset,
final List<Couple<Integer>> points,
final int subtractX,
final boolean mirrorX, final int mirrorSize) {
final Iterator<Couple<Integer>> iterator = points.iterator();
assert iterator.hasNext();
final Convertor<Integer, Integer> c = new Convertor<Integer, Integer>() {
@Override
public Integer convert(Integer o) {
final int val = x1 + o - subtractX;
if (mirrorX) {
return mirrorSize - val;
}
return val;
}
};
Couple<Integer> previous = iterator.next();
while (iterator.hasNext()) {
final Couple<Integer> next = iterator.next();
UIUtil.drawLine(g, c.convert(previous.getFirst()), y + offset + previous.getSecond() - myLineHeight/2, c.convert(next.getFirst()),
y + offset + next.getSecond() - myLineHeight/2);
UIUtil.drawLine(g, c.convert(previous.getFirst()), y - offset + previous.getSecond() - myLineHeight/2, c.convert(next.getFirst()),
y - offset + next.getSecond() - myLineHeight/2);
previous = next;
}
}
private static class ShoeneLine {
private List<Couple<Integer>> myPoints;
private final int myYDiff;
private ShoeneLine(final int yDiff) {
myYDiff = yDiff;
myPoints = new ArrayList<Couple<Integer>>();
}
public void ensureLastX(final int x) {
if (myPoints.isEmpty() || myPoints.get(myPoints.size() - 1).getFirst() < x) {
if (myPoints.isEmpty()) {
myPoints.add(Couple.of(0, 0));
myPoints.addAll(generateLine(0,0,x,0,myYDiff,0));
} else {
final Couple<Integer> lastPoint = myPoints.get(myPoints.size() - 1);
int finalX = (x - lastPoint.getFirst()) < 5 ? x + 10 : x;
myPoints.addAll(generateLine(lastPoint.getFirst(),lastPoint.getSecond(),finalX,0,myYDiff,lastPoint.getSecond()));
}
//myPoints.set(myPoints.size() - 1, new Pair<Integer, Integer>(x, 0));
}
}
public List<Couple<Integer>> getPoints() {
return myPoints;
}
}
private final static int xVariation = 7;
private static List<Couple<Integer>> generateLine(final int startX, int startY, int finalX, final int yBase, final int yDiff,
final int wasPrevStep) {
int xCurrent = startX;
int yCurrent = startY;
final List<Couple<Integer>> result = new ArrayList<Couple<Integer>>();
final Random xRnd = new Random();
final Random yRnd = new Random();
int prevStep = wasPrevStep;
while (xCurrent < finalX) {
int yStep = prevStep;
while (prevStep == yStep) {
yStep = yRnd.nextInt(yDiff * 2) - yDiff;
}
prevStep = yStep;
int newY = yBase + yStep;
int newX = xCurrent + 4 + xRnd.nextInt(xVariation);
newX = Math.min(newX, finalX);
result.add(Couple.of(newX, newY));
xCurrent = newX;
}
return result;
}
}