1 package com.intellij.vcs.log.ui.render;
3 import com.intellij.openapi.ui.GraphicsConfig;
4 import com.intellij.openapi.util.registry.Registry;
5 import com.intellij.openapi.vcs.changes.issueLinks.IssueLinkRenderer;
6 import com.intellij.ui.JBColor;
7 import com.intellij.ui.SimpleColoredRenderer;
8 import com.intellij.ui.SimpleTextAttributes;
9 import com.intellij.ui.TableCell;
10 import com.intellij.util.ObjectUtils;
11 import com.intellij.util.ui.GraphicsUtil;
12 import com.intellij.util.ui.JBUI;
13 import com.intellij.util.ui.UIUtil;
14 import com.intellij.vcs.log.VcsRef;
15 import com.intellij.vcs.log.data.VcsLogData;
16 import com.intellij.vcs.log.graph.EdgePrintElement;
17 import com.intellij.vcs.log.graph.PrintElement;
18 import com.intellij.vcs.log.paint.GraphCellPainter;
19 import com.intellij.vcs.log.paint.PaintParameters;
20 import com.intellij.vcs.log.ui.frame.VcsLogGraphTable;
21 import com.intellij.vcs.log.ui.tables.GraphTableModel;
22 import org.jetbrains.annotations.NotNull;
23 import org.jetbrains.annotations.Nullable;
26 import javax.swing.table.TableColumn;
28 import java.awt.image.BufferedImage;
29 import java.util.Collection;
31 public class GraphCommitCellRenderer extends TypeSafeTableCellRenderer<GraphCommitCell> {
32 private static final int MAX_GRAPH_WIDTH = 6;
33 private static final int VERTICAL_PADDING = JBUI.scale(7);
35 @NotNull private final VcsLogData myLogData;
36 @NotNull private final VcsLogGraphTable myGraphTable;
38 @Nullable private final FadeOutPainter myFadeOutPainter;
39 @Nullable private final ReferencePainter myTooltipPainter;
41 @NotNull private final MyComponent myComponent;
43 private boolean myExpanded;
45 public GraphCommitCellRenderer(@NotNull VcsLogData logData,
46 @NotNull GraphCellPainter painter,
47 @NotNull VcsLogGraphTable table) {
51 myFadeOutPainter = isRedesignedLabels() ? new FadeOutPainter() : null;
52 myTooltipPainter = isRedesignedLabels() ? new LabelPainter(myLogData) : null;
54 myComponent = new MyComponent(logData, painter, table) {
56 public void paintComponent(Graphics g) {
57 super.paintComponent(g);
59 if (myFadeOutPainter != null) {
61 int start = Math.max(myGraphImage.getWidth(), getWidth() - myFadeOutPainter.getWidth());
62 myFadeOutPainter.paint((Graphics2D)g, start, 0, getHeight());
70 protected Component getTableCellRendererComponentImpl(@NotNull JTable table,
71 @NotNull GraphCommitCell value,
76 myComponent.customize(value, isSelected, hasFocus, row, column);
78 myExpanded = myGraphTable.getExpandableItemsHandler().getExpandedItems().contains(new TableCell(row, column));
79 if (myFadeOutPainter != null) {
80 myFadeOutPainter.customize(value.getRefsToThisCommit(), row, column, table /*any color fits here*/);
85 public static boolean isRedesignedLabels() {
86 return Registry.is("vcs.log.labels.redesign");
90 public JComponent getTooltip(@NotNull Object value, @NotNull Point point, int width) {
91 if (myTooltipPainter == null) return null;
93 GraphCommitCell cell = getValue(value);
94 Collection<VcsRef> refs = cell.getRefsToThisCommit();
95 if (!refs.isEmpty()) {
96 myTooltipPainter.customizePainter(myComponent, refs, myComponent.getBackground(), myComponent.getForeground());
97 if (myTooltipPainter.getSize().getWidth() - LabelPainter.GRADIENT_WIDTH >= width - point.getX()) {
98 return new TooltipReferencesPanel(myLogData, myTooltipPainter, refs);
104 public int getPreferredHeight() {
105 return myComponent.getPreferredHeight();
108 public int getTooltipXCoordinate(int row) {
109 return myComponent.getTooltipXCoordinate(getValue(myGraphTable.getModel().getValueAt(row, GraphTableModel.COMMIT_COLUMN)));
112 private static class MyComponent extends SimpleColoredRenderer {
113 @NotNull private final VcsLogData myLogData;
114 @NotNull private final VcsLogGraphTable myGraphTable;
115 @NotNull private final GraphCellPainter myPainter;
116 @NotNull private final IssueLinkRenderer myIssueLinkRenderer;
117 @NotNull private final ReferencePainter myReferencePainter;
119 @NotNull protected PaintInfo myGraphImage = new PaintInfo(UIUtil.createImage(1, 1, BufferedImage.TYPE_INT_ARGB), 0);
120 @NotNull private Font myFont;
121 private int myHeight;
123 public MyComponent(@NotNull VcsLogData data, @NotNull GraphCellPainter painter, @NotNull VcsLogGraphTable table) {
126 myGraphTable = table;
128 myReferencePainter = isRedesignedLabels() ? new LabelPainter(myLogData) : new RectangleReferencePainter(myLogData);
130 myIssueLinkRenderer = new IssueLinkRenderer(myLogData.getProject(), this);
131 myFont = RectanglePainter.getFont();
132 myHeight = calculateHeight();
137 public Dimension getPreferredSize() {
138 Dimension preferredSize = super.getPreferredSize();
139 int referencesSize = myReferencePainter.isLeftAligned() ? 0 : myReferencePainter.getSize().width;
140 if (referencesSize > 0) referencesSize -= LabelPainter.GRADIENT_WIDTH;
141 return new Dimension(preferredSize.width + referencesSize, getPreferredHeight());
145 public void paintComponent(Graphics g) {
146 super.paintComponent(g);
148 int graphImageWidth = myGraphImage.getWidth();
150 if (!myReferencePainter.isLeftAligned()) {
151 int start = Math.max(graphImageWidth, getWidth() - myReferencePainter.getSize().width);
152 myReferencePainter.paint((Graphics2D)g, start, 0, getHeight());
155 myReferencePainter.paint((Graphics2D)g, graphImageWidth, 0, getHeight());
158 UIUtil.drawImage(g, myGraphImage.getImage(), 0, 0, null);
161 public void customize(@NotNull GraphCommitCell cell, boolean isSelected, boolean hasFocus, int row, int column) {
163 setPaintFocusBorder(hasFocus && myGraphTable.getCellSelectionEnabled());
164 acquireState(myGraphTable, isSelected, hasFocus, row, column);
165 getCellState().updateRenderer(this);
167 myGraphImage = getGraphImage(cell.getPrintElements());
169 SimpleTextAttributes style = myGraphTable.applyHighlighters(this, row, column, hasFocus, isSelected);
171 Collection<VcsRef> refs = cell.getRefsToThisCommit();
172 Color baseForeground = ObjectUtils.assertNotNull(myGraphTable.getBaseStyle(row, column, hasFocus, isSelected).getForeground());
173 myReferencePainter.customizePainter(this, refs, getBackground(), baseForeground);
177 appendTextPadding(myGraphImage.getWidth() + (myReferencePainter.isLeftAligned() ? myReferencePainter.getSize().width : 0));
178 myIssueLinkRenderer.appendTextWithLinks(cell.getText(), style);
181 private int calculateHeight() {
182 return Math.max(myReferencePainter.getSize().height, getFontMetrics(myFont).getHeight() + VERTICAL_PADDING);
185 public int getPreferredHeight() {
186 Font font = RectanglePainter.getFont();
187 if (myFont != font) {
189 myHeight = calculateHeight();
195 private PaintInfo getGraphImage(@NotNull Collection<? extends PrintElement> printElements) {
197 for (PrintElement printElement : printElements) {
198 maxIndex = Math.max(maxIndex, printElement.getPositionInCurrentRow());
199 if (printElement instanceof EdgePrintElement) {
200 maxIndex = Math.max(maxIndex,
201 (printElement.getPositionInCurrentRow() + ((EdgePrintElement)printElement).getPositionInOtherRow()) / 2.0);
206 maxIndex = Math.max(maxIndex, Math.min(MAX_GRAPH_WIDTH, myGraphTable.getVisibleGraph().getRecommendedWidth()));
207 BufferedImage image = UIUtil.createImage((int)(PaintParameters.getNodeWidth(myGraphTable.getRowHeight()) * (maxIndex + 2)),
208 myGraphTable.getRowHeight(),
209 BufferedImage.TYPE_INT_ARGB);
210 Graphics2D g2 = image.createGraphics();
211 myPainter.draw(g2, printElements);
213 int width = (int)(maxIndex * PaintParameters.getNodeWidth(myGraphTable.getRowHeight()));
214 return new PaintInfo(image, width);
217 public int getTooltipXCoordinate(@NotNull GraphCommitCell cell) {
218 Collection<VcsRef> refs = cell.getRefsToThisCommit();
219 if (!refs.isEmpty()) {
220 myReferencePainter.customizePainter(this, refs, getBackground(), getForeground());
221 TableColumn commitColumn = myGraphTable.getColumnModel().getColumn(GraphTableModel.COMMIT_COLUMN);
222 return commitColumn.getWidth() - (myReferencePainter.getSize().width - LabelPainter.GRADIENT_WIDTH) / 2;
228 private static class PaintInfo {
229 private final int myWidth;
230 @NotNull private final Image myImage;
232 PaintInfo(@NotNull Image image, int width) {
243 * Returns the "interesting" width of the painted image, i.e. the width which the text in the table should be offset by. <br/>
244 * It can be smaller than the width of {@link #getImage() the image}, because we allow the text to cover part of the graph
245 * (some diagonal edges, etc.)
252 private class FadeOutPainter {
253 @NotNull private final LabelPainter myEmptyPainter = new LabelPainter(myLogData);
254 private int myWidth = LabelPainter.GRADIENT_WIDTH;
256 public void customize(@NotNull Collection<VcsRef> currentRefs, int row, int column, @NotNull JTable table) {
259 if (currentRefs.isEmpty()) {
262 GraphCommitCell commitCell = getValue(table.getValueAt(row - 1, column));
263 myEmptyPainter.customizePainter(myComponent, commitCell.getRefsToThisCommit(), myComponent.getBackground(), JBColor.black);
264 prevWidth = myEmptyPainter.getSize().width;
268 if (row < table.getRowCount() - 1) {
269 GraphCommitCell commitCell = getValue(table.getValueAt(row + 1, column));
270 myEmptyPainter.customizePainter(myComponent, commitCell.getRefsToThisCommit(), myComponent.getBackground(), JBColor.black);
271 nextWidth = myEmptyPainter.getSize().width;
274 myWidth = Math.max(Math.max(prevWidth, nextWidth), LabelPainter.GRADIENT_WIDTH);
278 public void paint(@NotNull Graphics2D g2, int x, int y, int height) {
279 GraphicsConfig config = GraphicsUtil.setupAAPainting(g2);
280 LabelPainter.paintFadeOut(g2, x, y, myWidth, height, myComponent.getBackground());
284 public int getWidth() {