blob: 6e6a607025952f71dba675d1603967cea2b8f9b3 [file] [log] [blame]
/*
* Copyright 2000-2012 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.splitter;
import com.intellij.openapi.diff.impl.DiffUtil;
import com.intellij.openapi.diff.impl.EditingSides;
import com.intellij.openapi.diff.impl.highlighting.FragmentSide;
import com.intellij.openapi.diff.impl.util.TextDiffType;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.LogicalPosition;
import com.intellij.openapi.util.Comparing;
import com.intellij.util.ui.GraphicsUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.awt.geom.CubicCurve2D;
import java.awt.geom.Path2D;
import java.util.ArrayList;
/**
* A polygon, which is drawn between editors in merge or diff dialogs, and which indicates the change flow from one editor to another.
*/
public class DividerPolygon {
@Nullable private final Color myColor;
private final int myStart1;
private final int myStart2;
private final int myEnd1;
private final int myEnd2;
private final boolean myApplied;
public static final double CTRL_PROXIMITY_X = 0.3;
public DividerPolygon(int start1, int start2, int end1, int end2, @Nullable Color color, boolean applied) {
myApplied = applied;
myStart1 = advance(start1);
myStart2 = advance(start2);
myEnd1 = advance(end1);
myEnd2 = advance(end2);
myColor = color;
}
private int advance(int y) {
return y == 0 ? y : y + 1;
}
private void paint(Graphics2D g, int width) {
GraphicsUtil.setupAntialiasing(g);
if (!myApplied) {
Shape upperCurve = makeCurve(width, myStart1, myStart2, true);
Shape lowerCurve = makeCurve(width, myEnd1, myEnd2, false);
Path2D path = new Path2D.Double();
path.append(upperCurve, true);
path.append(lowerCurve, true);
g.setColor(myColor);
g.fill(path);
g.setColor(DiffUtil.getFramingColor(myColor));
g.draw(upperCurve);
g.draw(lowerCurve);
}
else {
g.setColor(myColor);
g.draw(makeCurve(width, myStart1 + 1, myStart2 + 1, true));
g.draw(makeCurve(width, myStart1 + 2, myStart2 + 2, true));
g.draw(makeCurve(width, myEnd1 + 1, myEnd2 + 1, false));
g.draw(makeCurve(width, myEnd1 + 2, myEnd2 + 2, false));
}
}
private static Shape makeCurve(int width, int y1, int y2, boolean forward) {
if (forward) {
return new CubicCurve2D.Double(0, y1,
width * CTRL_PROXIMITY_X, y1,
width * (1.0 - CTRL_PROXIMITY_X), y2,
width, y2);
}
else {
return new CubicCurve2D.Double(width, y2,
width * (1.0 - CTRL_PROXIMITY_X), y2,
width * CTRL_PROXIMITY_X, y1,
0, y1);
}
}
public int hashCode() {
return myStart1 ^ myStart2 ^ myEnd1 ^ myEnd2;
}
public boolean equals(Object obj) {
if (!(obj instanceof DividerPolygon)) return false;
DividerPolygon other = (DividerPolygon)obj;
return myStart1 == other.myStart1 &&
myStart2 == other.myStart2 &&
myEnd1 == other.myEnd1 &&
myEnd2 == other.myEnd2 && Comparing.equal(myColor, other.myColor);
}
public String toString() {
return "<" + myStart1 + ", " + myEnd1 + " : " + myStart2 + ", " + myEnd2 + "> " + myColor;
}
public Color getColor() {
return myColor;
}
public int getTopLeftY() {
return myStart1;
}
public int getTopRightY() {
return myStart2;
}
public int getBottomLeftY() {
return myEnd1;
}
public int getBottomRightY() {
return myEnd2;
}
public boolean isApplied() {
return myApplied;
}
public static void paintPolygons(ArrayList<DividerPolygon> polygons, Graphics2D g, int width) {
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
//Composite composite = g.getComposite();
//g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.4f));
for (DividerPolygon polygon : polygons) {
polygon.paint(g, width);
}
//g.setComposite(composite);
}
public static ArrayList<DividerPolygon> createVisiblePolygons(@NotNull EditingSides sides,
@NotNull FragmentSide left,
int diffDividerPolygonsOffset) {
Editor editor1 = sides.getEditor(left);
Editor editor2 = sides.getEditor(left.otherSide());
LineBlocks lineBlocks = sides.getLineBlocks();
Trapezium visibleArea = new Trapezium(getVisibleInterval(editor1),
getVisibleInterval(editor2));
Interval indices = lineBlocks.getVisibleIndices(visibleArea);
Transformation[] transformations = new Transformation[]{getTransformation(editor1),
getTransformation(editor2)};
ArrayList<DividerPolygon> polygons = new ArrayList<DividerPolygon>();
for (int i = indices.getStart(); i < indices.getEnd(); i++) {
Trapezium trapezium = lineBlocks.getTrapezium(i);
final TextDiffType type = lineBlocks.getType(i);
Color color = type.getPolygonColor(editor1);
polygons.add(createPolygon(transformations, trapezium, color, left, diffDividerPolygonsOffset, type.isApplied()));
}
return polygons;
}
private static Transformation getTransformation(Editor editor) {
// return new LinearTransformation(editor.getScrollingModel().getVerticalScrollOffset(), editor.getLineHeight());
return new FoldingTransformation(editor);
}
private static DividerPolygon createPolygon(@NotNull Transformation[] transformations,
@NotNull Trapezium trapezium,
@Nullable Color color,
@NotNull FragmentSide left,
int diffDividerPolygonsOffset, boolean applied) {
Interval base1 = trapezium.getBase(left);
Interval base2 = trapezium.getBase(left.otherSide());
Transformation leftTransform = transformations[left.getIndex()];
Transformation rightTransform = transformations[left.otherSide().getIndex()];
int start1 = leftTransform.transform(base1.getStart());
int end1 = leftTransform.transform(base1.getEnd());
int start2 = rightTransform.transform(base2.getStart());
int end2 = rightTransform.transform(base2.getEnd());
return new DividerPolygon(start1 - diffDividerPolygonsOffset, start2 - diffDividerPolygonsOffset,
end1 - diffDividerPolygonsOffset, end2 - diffDividerPolygonsOffset,
color, applied);
}
static Interval getVisibleInterval(Editor editor) {
int offset = editor.getScrollingModel().getVerticalScrollOffset();
LogicalPosition logicalPosition = editor.xyToLogicalPosition(new Point(0, offset));
int line = logicalPosition.line;
return new Interval(line, editor.getComponent().getHeight() / editor.getLineHeight() + 1);
}
}