2 * Copyright 2000-2015 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.diff.util;
18 import com.intellij.openapi.editor.Editor;
19 import com.intellij.openapi.editor.colors.ColorKey;
20 import com.intellij.openapi.editor.colors.EditorColors;
21 import com.intellij.openapi.editor.colors.EditorColorsManager;
22 import com.intellij.openapi.editor.colors.EditorColorsScheme;
23 import com.intellij.openapi.editor.ex.EditorEx;
24 import com.intellij.openapi.editor.ex.EditorGutterComponentEx;
25 import com.intellij.openapi.editor.markup.LineMarkerRenderer;
26 import com.intellij.openapi.editor.markup.LineSeparatorRenderer;
27 import com.intellij.openapi.ui.GraphicsConfig;
28 import com.intellij.openapi.util.BooleanGetter;
29 import com.intellij.ui.ColorUtil;
30 import com.intellij.ui.Gray;
31 import com.intellij.util.ui.GraphicsUtil;
32 import org.jetbrains.annotations.NotNull;
33 import org.jetbrains.annotations.Nullable;
36 import java.awt.geom.AffineTransform;
37 import java.util.Arrays;
39 public class DiffLineSeparatorRenderer implements LineMarkerRenderer, LineSeparatorRenderer {
40 @NotNull private final Editor myEditor;
41 @NotNull private final BooleanGetter myCondition;
43 public DiffLineSeparatorRenderer(@NotNull Editor editor) {
44 this(editor, BooleanGetter.TRUE);
47 public DiffLineSeparatorRenderer(@NotNull Editor editor, @NotNull BooleanGetter condition) {
49 myCondition = condition;
52 public static void drawConnectorLine(@NotNull Graphics2D g,
55 int start2, int end2) {
56 drawConnectorLine(g, x1, x2, start1, start2, end1 - end2, null);
62 public static void drawConnectorLine(@NotNull Graphics2D g,
66 @Nullable EditorColorsScheme scheme) {
67 int step = getStepSize(lineHeight);
68 int height = getHeight(lineHeight);
69 int dx = getDeltaX(lineHeight);
70 int dy = getDeltaY(lineHeight);
72 int start1 = y1 + (lineHeight - height - step) / 2 + step / 2;
73 int start2 = y2 + (lineHeight - height - step) / 2 + step / 2;
74 int end1 = start1 + height - 1;
75 int end2 = start2 + height - 1;
80 if (Math.abs(x2 - x1) < Math.abs(y2 - y1)) {
82 xPoints = new int[]{x1, x2 - dx, x2, x2, x1 + dx, x1};
83 yPoints = new int[]{start1, start2 + dy, start2, end2, end1 - dy, end1};
86 xPoints = new int[]{x1, x1 + dx, x2, x2, x2 - dx, x1};
87 yPoints = new int[]{start1, start1 + dy, start2, end2, end2 - dy, end1};
91 xPoints = new int[]{x1, x2, x2, x1};
92 yPoints = new int[]{start1, start2, end2, end1};
95 paintConnectorLine(g, xPoints, yPoints, lineHeight, scheme);
102 public void paint(Editor editor, Graphics g, Rectangle r) {
103 if (!myCondition.get()) return;
106 int lineHeight = myEditor.getLineHeight();
108 EditorGutterComponentEx gutter = ((EditorEx)editor).getGutterComponentEx();
109 int annotationsOffset = gutter.getAnnotationsAreaOffset();
110 int annotationsWidth = gutter.getAnnotationsAreaWidth();
111 if (annotationsWidth != 0) {
112 g.setColor(editor.getColorsScheme().getColor(EditorColors.GUTTER_BACKGROUND));
113 g.fillRect(annotationsOffset, y, annotationsWidth, lineHeight);
116 draw(g, 0, y, lineHeight, myEditor.getColorsScheme());
123 public void drawLine(Graphics g, int x1, int x2, int y) {
124 if (!myCondition.get()) return;
126 y++; // we want y to be line's top position
128 final int gutterWidth = ((EditorEx)myEditor).getGutterComponentEx().getWidth();
129 int lineHeight = myEditor.getLineHeight();
130 int interval = getStepSize(lineHeight) * 2;
132 int shiftX = -interval; // skip zero index painting
133 if (DiffUtil.isMirrored(myEditor)) {
134 int contentWidth = ((EditorEx)myEditor).getScrollPane().getViewport().getWidth();
135 shiftX += contentWidth % interval - interval;
136 shiftX += gutterWidth % interval - interval;
139 shiftX += -gutterWidth % interval - interval;
142 draw(g, shiftX, y, lineHeight, myEditor.getColorsScheme());
145 private static void draw(@NotNull Graphics g,
149 @Nullable EditorColorsScheme scheme) {
150 int step = getStepSize(lineHeight);
151 int height = getHeight(lineHeight);
153 Rectangle clip = g.getClipBounds();
154 if (clip.width <= 0) return;
155 int count = (clip.width / step + 3);
156 int shift = (clip.x - shiftX) / step;
158 int[] xPoints = new int[count];
159 int[] yPoints = new int[count];
161 shiftY += (lineHeight - height - step) / 2;
163 for (int index = 0; index < count; index++) {
164 int absIndex = index + shift;
166 int xPos = absIndex * step + shiftX;
170 yPos = step / 2 + shiftY;
172 else if (absIndex % 2 == 0) {
176 yPos = step + shiftY;
179 xPoints[index] = xPos;
180 yPoints[index] = yPos;
183 GraphicsConfig config = GraphicsUtil.disableAAPainting(g);
185 paintLine(g, xPoints, yPoints, lineHeight, scheme);
192 private static void paintLine(@NotNull Graphics g,
193 @NotNull int[] xPoints, @NotNull int[] yPoints,
195 @Nullable EditorColorsScheme scheme) {
196 int height = getHeight(lineHeight);
197 if (scheme == null) scheme = EditorColorsManager.getInstance().getGlobalScheme();
199 Graphics2D gg = ((Graphics2D)g);
200 AffineTransform oldTransform = gg.getTransform();
202 for (int i = 0; i < height; i++) {
203 Color color = getTopBorderColor(i, lineHeight, scheme);
204 if (color == null) color = getBottomBorderColor(i, lineHeight, scheme);
205 if (color == null) color = getBackgroundColor(scheme);
208 gg.drawPolyline(xPoints, yPoints, xPoints.length);
211 gg.setTransform(oldTransform);
214 private static void paintConnectorLine(@NotNull Graphics g,
215 @NotNull int[] xPoints, @NotNull int[] yPoints,
217 @Nullable EditorColorsScheme scheme) {
218 // TODO: shadow looks bad with big lineHeight and slope
219 int height = getHeight(lineHeight);
220 if (scheme == null) scheme = EditorColorsManager.getInstance().getGlobalScheme();
222 Graphics2D gg = ((Graphics2D)g);
223 AffineTransform oldTransform = gg.getTransform();
226 gg.setColor(getBackgroundColor(scheme));
227 gg.fillPolygon(xPoints, yPoints, xPoints.length);
229 if (scheme.getColor(TOP_BORDER) != null) {
230 for (int i = 0; i < height; i++) {
231 Color color = getTopBorderColor(i, lineHeight, scheme);
232 if (color == null) break;
235 gg.drawPolyline(xPoints, yPoints, xPoints.length / 2);
238 gg.setTransform(oldTransform);
241 if (scheme.getColor(BOTTOM_BORDER) != null) {
242 int[] xBottomPoints = Arrays.copyOfRange(xPoints, xPoints.length / 2, xPoints.length);
243 int[] yBottomPoints = Arrays.copyOfRange(yPoints, xPoints.length / 2, xPoints.length);
245 for (int i = height - 1; i >= 0; i--) {
246 Color color = getBottomBorderColor(i, lineHeight, scheme);
247 if (color == null) break;
250 gg.drawPolyline(xBottomPoints, yBottomPoints, xPoints.length / 2);
253 gg.setTransform(oldTransform);
261 private static final ColorKey BACKGROUND = ColorKey.createColorKey("DIFF_SEPARATORS_BACKGROUND");
262 private static final ColorKey TOP_BORDER = ColorKey.createColorKey("DIFF_SEPARATORS_TOP_BORDER");
263 private static final ColorKey BOTTOM_BORDER = ColorKey.createColorKey("DIFF_SEPARATORS_BOTTOM_BORDER");
265 private static int getStepSize(int lineHeight) {
266 return Math.max(lineHeight / 3, 1);
269 private static int getHeight(int lineHeight) {
270 return Math.max(lineHeight / 2, 1);
273 private static int getDeltaX(int lineHeight) {
274 return Math.max(lineHeight / 4, 1);
277 private static int getDeltaY(int lineHeight) {
278 return Math.max(lineHeight / 6, 1);
282 private static Color getBackgroundColor(@NotNull EditorColorsScheme scheme) {
283 Color color = scheme.getColor(BACKGROUND);
284 return color != null ? color : Gray._128;
288 private static Color getTopBorderColor(int i, int lineHeight, @NotNull EditorColorsScheme scheme) {
289 int border = Math.max(lineHeight / 4, 1);
290 double ratio = (double)i / border;
291 if (ratio > 1) return null;
293 Color top = scheme.getColor(TOP_BORDER);
294 if (top == null) return null;
296 Color background = getBackgroundColor(scheme);
297 return ColorUtil.mix(top, background, ratio);
301 private static Color getBottomBorderColor(int i, int lineHeight, @NotNull EditorColorsScheme scheme) {
302 int height = getHeight(lineHeight);
303 int border = Math.max(lineHeight / 12, 1);
305 int index = (height - i - 1);
306 double ratio = (double)index / border;
307 if (ratio > 1) return null;
309 Color bottom = scheme.getColor(BOTTOM_BORDER);
310 if (bottom == null) return null;
312 Color background = getBackgroundColor(scheme);
313 return ColorUtil.mix(bottom, background, ratio);