blob: 9963289e1a2d118949e1f5e8d9173c6f7cd6e475 [file] [log] [blame]
package com.intellij.vcs.log.ui.render;
import com.intellij.ui.JBColor;
import com.intellij.vcs.log.VcsRef;
import com.intellij.vcs.log.ui.VcsLogColorManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.geom.RoundRectangle2D;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import static com.intellij.vcs.log.printer.idea.PrintParameters.HEIGHT_CELL;
/**
* @author erokhins
*/
public class RefPainter {
private static final int RECTANGLE_X_PADDING = 4;
private static final int RECTANGLE_Y_PADDING = 3;
private static final int REF_PADDING = 13;
private static final int LABEL_PADDING = 3;
private static final int FLAG_WIDTH = 8;
private static final int FLAG_PADDING = 8;
private static final int FLAG_TOP_INDENT = 2;
private static final int ROUND_RADIUS = 5;
public static final Font DEFAULT_FONT = new Font("Arial", Font.PLAIN, 12);
private static final Color DEFAULT_FONT_COLOR = JBColor.BLACK;
@NotNull private final VcsLogColorManager myColorManager;
private final boolean myDrawMultiRepoIndicator;
public RefPainter(@NotNull VcsLogColorManager manager, boolean drawMultiRepoIndicator) {
myColorManager = manager;
myDrawMultiRepoIndicator = drawMultiRepoIndicator;
}
private double paddingStr(@NotNull String str, @NotNull FontRenderContext renderContext) {
return getStringWidth(str, renderContext) + REF_PADDING + flagWidth();
}
private static double getStringWidth(@NotNull String str, @NotNull FontRenderContext renderContext) {
return DEFAULT_FONT.getStringBounds(str, renderContext).getWidth();
}
private int flagWidth() {
return myColorManager.isMultipleRoots() && myDrawMultiRepoIndicator ? FLAG_WIDTH + FLAG_PADDING : 0;
}
private static void drawText(@NotNull Graphics2D g2, @NotNull String str, int padding) {
FontMetrics metrics = g2.getFontMetrics();
g2.setColor(DEFAULT_FONT_COLOR);
int x = padding + REF_PADDING / 2;
int y = HEIGHT_CELL / 2 + (metrics.getAscent() - metrics.getDescent()) / 2;
g2.drawString(str, x, y);
}
private int draw(@NotNull Graphics2D g2, @NotNull VcsRef ref, int padding) {
Rectangle rectangle = drawLabel(g2, ref.getName(), padding, ref.getType().getBackgroundColor(),
myColorManager.getRootColor(ref.getRoot()));
return rectangle.x;
}
private void drawRootIndicator(@NotNull Graphics2D g2, int x, int y, int height, @NotNull Color rootIndicatorColor) {
g2.setColor(rootIndicatorColor);
int x0 = x + FLAG_PADDING;
int xMid = x0 + FLAG_WIDTH / 2;
int xRight = x0 + FLAG_WIDTH;
int y0 = y - FLAG_TOP_INDENT;
int yMid = y0 + 2 * height / 3 - 2;
int yBottom = y0 + height - 4;
// something like a knight flag
Polygon polygon = new Polygon(new int[]{ x0, xRight, xRight, xMid, x0 },
new int[]{ y0, y0, yMid, yBottom, yMid }, 5);
g2.fillPolygon(polygon);
g2.setColor(myColorManager.getReferenceBorderColor());
g2.drawPolygon(polygon);
}
public int padding(@NotNull Collection<String> refs, FontMetrics fontMetrics) {
int p = 0;
for (String ref : refs) {
XAndWidth xAndWidth = getXAndWidth(ref, 0, fontMetrics);
p += xAndWidth.x + xAndWidth.width + LABEL_PADDING;
}
if (p > 0) { // additional padding after all references looks better
p += LABEL_PADDING;
}
return p;
}
public Map<Integer, VcsRef> draw(@NotNull Graphics2D g2, @NotNull Collection<VcsRef> refs, int startPadding, int maxWidth) {
float currentPadding = startPadding;
setupGraphics(g2);
FontRenderContext renderContext = g2.getFontRenderContext();
Map<Integer, VcsRef> positions = new HashMap<Integer, VcsRef>();
for (VcsRef ref : refs) {
int x = draw(g2, ref, (int)currentPadding);
positions.put(x, ref);
currentPadding += paddingStr(ref.getName(), renderContext);
if (maxWidth > 0 && x >= maxWidth) {
break;
}
}
return positions;
}
private static void setupGraphics(Graphics2D g2) {
g2.setFont(DEFAULT_FONT);
g2.setStroke(new BasicStroke(1.5f));
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
}
public void drawLabels(@NotNull Graphics2D g2, @NotNull Map<String, Color> labels, int startPadding) {
int padding = startPadding;
for (Map.Entry<String, Color> entry : labels.entrySet()) {
Rectangle rectangle = drawLabel(g2, entry.getKey(), padding, entry.getValue(), null);
padding += rectangle.width + LABEL_PADDING;
}
}
private XAndWidth getXAndWidth(String label, int paddingX, FontMetrics metrics) {
int x = paddingX + REF_PADDING / 2 - RECTANGLE_X_PADDING;
int width = metrics.stringWidth(label) + 2 * RECTANGLE_X_PADDING + flagWidth();
return new XAndWidth(x, width);
}
public Rectangle drawLabel(@NotNull Graphics2D g2, @NotNull String label, int paddingX, @NotNull Color bgColor,
@Nullable Color rootIndicatorColor) {
setupGraphics(g2);
FontMetrics metrics = g2.getFontMetrics();
XAndWidth xAndWidth = getXAndWidth(label, paddingX, metrics);
int x = xAndWidth.x;
int width = xAndWidth.width;
int y = RECTANGLE_Y_PADDING;
int height = HEIGHT_CELL - 2 * RECTANGLE_Y_PADDING;
RoundRectangle2D rectangle2D = new RoundRectangle2D.Double(x, y, width, height, ROUND_RADIUS, ROUND_RADIUS);
g2.setColor(bgColor);
g2.fill(rectangle2D);
g2.setColor(myColorManager.getReferenceBorderColor());
g2.draw(rectangle2D);
g2.setColor(JBColor.BLACK);
drawText(g2, label, paddingX + flagWidth());
if (rootIndicatorColor != null && myColorManager.isMultipleRoots() && myDrawMultiRepoIndicator) {
drawRootIndicator(g2, paddingX, y, height, rootIndicatorColor);
}
return new Rectangle(x, y, width, height);
}
private static class XAndWidth {
private final int x;
private final int width;
private XAndWidth(int x, int width) {
this.x = x;
this.width = width;
}
}
}