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.openapi.vfs.VirtualFile;
7 import com.intellij.ui.JBColor;
8 import com.intellij.ui.SimpleColoredRenderer;
9 import com.intellij.ui.SimpleTextAttributes;
10 import com.intellij.ui.TableCell;
11 import com.intellij.util.ObjectUtils;
12 import com.intellij.util.containers.ContainerUtil;
13 import com.intellij.util.ui.GraphicsUtil;
14 import com.intellij.util.ui.JBUI;
15 import com.intellij.util.ui.UIUtil;
16 import com.intellij.vcs.log.VcsRef;
17 import com.intellij.vcs.log.data.VcsLogData;
18 import com.intellij.vcs.log.graph.EdgePrintElement;
19 import com.intellij.vcs.log.graph.PrintElement;
20 import com.intellij.vcs.log.paint.GraphCellPainter;
21 import com.intellij.vcs.log.paint.PaintParameters;
22 import com.intellij.vcs.log.ui.frame.VcsLogGraphTable;
23 import com.intellij.vcs.log.ui.tables.GraphTableModel;
24 import org.jetbrains.annotations.NotNull;
25 import org.jetbrains.annotations.Nullable;
28 import javax.swing.table.TableColumn;
30 import java.awt.image.BufferedImage;
31 import java.util.Collection;
33 public class GraphCommitCellRenderer extends TypeSafeTableCellRenderer<GraphCommitCell> {
34 private static final int MAX_GRAPH_WIDTH = 6;
35 private static final int VERTICAL_PADDING = JBUI.scale(7);
37 @NotNull private final VcsLogData myLogData;
38 @NotNull private final VcsLogGraphTable myGraphTable;
40 @Nullable private final FadeOutPainter myFadeOutPainter = isRedesignedLabels() ? new FadeOutPainter() : null;
41 @Nullable private final ReferencePainter myTooltipPainter = isRedesignedLabels() ? new LabelPainter() : null;
43 @NotNull private final MyComponent myComponent;
45 private boolean myExpanded;
47 public GraphCommitCellRenderer(@NotNull VcsLogData logData,
48 @NotNull GraphCellPainter painter,
49 @NotNull VcsLogGraphTable table) {
53 myComponent = new MyComponent(logData, painter, table) {
55 public void paintComponent(Graphics g) {
56 super.paintComponent(g);
58 if (myFadeOutPainter != null) {
60 int start = Math.max(myGraphImage.getWidth(), getWidth() - myFadeOutPainter.getWidth());
61 myFadeOutPainter.paint((Graphics2D)g, start, 0, getHeight());
69 protected Component getTableCellRendererComponentImpl(@NotNull JTable table,
70 @NotNull GraphCommitCell value,
75 myComponent.customize(value, isSelected, hasFocus, row, column);
77 myExpanded = myGraphTable.getExpandableItemsHandler().getExpandedItems().contains(new TableCell(row, column));
78 if (myFadeOutPainter != null) {
79 myFadeOutPainter.customize(value.getRefsToThisCommit(), row, column, table, JBColor.black /*any color fits here*/);
84 public static boolean isRedesignedLabels() {
85 return Registry.is("vcs.log.labels.redesign");
89 public JComponent getTooltip(@NotNull Object value, @NotNull Point point, int width) {
90 if (myTooltipPainter == null) return null;
92 GraphCommitCell cell = getValue(value);
93 Collection<VcsRef> refs = cell.getRefsToThisCommit();
94 if (!refs.isEmpty()) {
95 customizeRefsPainter(myTooltipPainter, refs, myComponent.getForeground(), myLogData, myComponent);
96 if (myTooltipPainter.getSize().getWidth() - LabelPainter.GRADIENT_WIDTH >= width - point.getX()) {
97 return new TooltipReferencesPanel(myLogData, myTooltipPainter, refs);
103 private static void customizeRefsPainter(@NotNull ReferencePainter painter,
104 @NotNull Collection<VcsRef> refs,
105 @NotNull Color foreground,
106 @NotNull VcsLogData logData,
107 @NotNull JComponent component) {
108 if (!refs.isEmpty()) {
109 VirtualFile root = ObjectUtils.assertNotNull(ContainerUtil.getFirstItem(refs)).getRoot();
110 painter.customizePainter(component, refs, logData.getLogProvider(root).getReferenceManager(),
111 component.getBackground(), foreground);
114 painter.customizePainter(component, refs, null, component.getBackground(), foreground);
118 public int getPreferredHeight() {
119 return myComponent.getPreferredHeight();
122 public int getTooltipXCoordinate(int row) {
123 return myComponent.getTooltipXCoordinate(getValue(myGraphTable.getModel().getValueAt(row, GraphTableModel.COMMIT_COLUMN)));
126 private static class MyComponent extends SimpleColoredRenderer {
127 @NotNull private final VcsLogData myLogData;
128 @NotNull private final VcsLogGraphTable myGraphTable;
129 @NotNull private final GraphCellPainter myPainter;
130 @NotNull private final IssueLinkRenderer myIssueLinkRenderer;
131 @NotNull private final ReferencePainter myReferencePainter =
132 isRedesignedLabels() ? new LabelPainter() : new RectangleReferencePainter();
134 @NotNull protected PaintInfo myGraphImage = new PaintInfo(UIUtil.createImage(1, 1, BufferedImage.TYPE_INT_ARGB), 0);
135 @NotNull private Font myFont;
136 private int myHeight;
138 public MyComponent(@NotNull VcsLogData data, @NotNull GraphCellPainter painter, @NotNull VcsLogGraphTable table) {
141 myGraphTable = table;
143 myIssueLinkRenderer = new IssueLinkRenderer(myLogData.getProject(), this);
144 myFont = RectanglePainter.getFont();
145 myHeight = calculateHeight();
150 public Dimension getPreferredSize() {
151 Dimension preferredSize = super.getPreferredSize();
152 int referencesSize = myReferencePainter.isLeftAligned() ? 0 : myReferencePainter.getSize().width;
153 if (referencesSize > 0) referencesSize -= LabelPainter.GRADIENT_WIDTH;
154 return new Dimension(preferredSize.width + referencesSize, getPreferredHeight());
158 public void paintComponent(Graphics g) {
159 super.paintComponent(g);
161 int graphImageWidth = myGraphImage.getWidth();
163 if (!myReferencePainter.isLeftAligned()) {
164 int start = Math.max(graphImageWidth, getWidth() - myReferencePainter.getSize().width);
165 myReferencePainter.paint((Graphics2D)g, start, 0, getHeight());
168 myReferencePainter.paint((Graphics2D)g, graphImageWidth, 0, getHeight());
171 UIUtil.drawImage(g, myGraphImage.getImage(), 0, 0, null);
174 public void customize(@NotNull GraphCommitCell cell, boolean isSelected, boolean hasFocus, int row, int column) {
176 setPaintFocusBorder(hasFocus && myGraphTable.getCellSelectionEnabled());
177 acquireState(myGraphTable, isSelected, hasFocus, row, column);
178 getCellState().updateRenderer(this);
180 myGraphImage = getGraphImage(cell.getPrintElements());
182 SimpleTextAttributes style = myGraphTable.applyHighlighters(this, row, column, hasFocus, isSelected);
184 Collection<VcsRef> refs = cell.getRefsToThisCommit();
185 Color foreground = ObjectUtils.assertNotNull(myGraphTable.getBaseStyle(row, column, hasFocus, isSelected).getForeground());
186 customizeRefsPainter(myReferencePainter, refs, foreground, myLogData, this);
190 appendTextPadding(myGraphImage.getWidth() + (myReferencePainter.isLeftAligned() ? myReferencePainter.getSize().width : 0));
191 myIssueLinkRenderer.appendTextWithLinks(cell.getText(), style);
194 private int calculateHeight() {
195 return Math.max(myReferencePainter.getSize().height, getFontMetrics(myFont).getHeight() + VERTICAL_PADDING);
198 public int getPreferredHeight() {
199 Font font = RectanglePainter.getFont();
200 if (myFont != font) {
202 myHeight = calculateHeight();
208 private PaintInfo getGraphImage(@NotNull Collection<? extends PrintElement> printElements) {
210 for (PrintElement printElement : printElements) {
211 maxIndex = Math.max(maxIndex, printElement.getPositionInCurrentRow());
212 if (printElement instanceof EdgePrintElement) {
213 maxIndex = Math.max(maxIndex,
214 (printElement.getPositionInCurrentRow() + ((EdgePrintElement)printElement).getPositionInOtherRow()) / 2.0);
219 maxIndex = Math.max(maxIndex, Math.min(MAX_GRAPH_WIDTH, myGraphTable.getVisibleGraph().getRecommendedWidth()));
220 BufferedImage image = UIUtil.createImage((int)(PaintParameters.getNodeWidth(myGraphTable.getRowHeight()) * (maxIndex + 2)),
221 myGraphTable.getRowHeight(),
222 BufferedImage.TYPE_INT_ARGB);
223 Graphics2D g2 = image.createGraphics();
224 myPainter.draw(g2, printElements);
226 int width = (int)(maxIndex * PaintParameters.getNodeWidth(myGraphTable.getRowHeight()));
227 return new PaintInfo(image, width);
230 public int getTooltipXCoordinate(@NotNull GraphCommitCell cell) {
231 Collection<VcsRef> refs = cell.getRefsToThisCommit();
232 if (!refs.isEmpty()) {
233 customizeRefsPainter(myReferencePainter, refs, getForeground(), myLogData, this);
234 TableColumn commitColumn = myGraphTable.getColumnModel().getColumn(GraphTableModel.COMMIT_COLUMN);
235 return commitColumn.getWidth() - (myReferencePainter.getSize().width - LabelPainter.GRADIENT_WIDTH) / 2;
241 private static class PaintInfo {
242 private final int myWidth;
243 @NotNull private final Image myImage;
245 PaintInfo(@NotNull Image image, int width) {
256 * Returns the "interesting" width of the painted image, i.e. the width which the text in the table should be offset by. <br/>
257 * It can be smaller than the width of {@link #getImage() the image}, because we allow the text to cover part of the graph
258 * (some diagonal edges, etc.)
265 private class FadeOutPainter {
266 @NotNull private final LabelPainter myEmptyPainter = new LabelPainter();
267 private int myWidth = LabelPainter.GRADIENT_WIDTH;
269 public void customize(@NotNull Collection<VcsRef> currentRefs, int row, int column, @NotNull JTable table, @NotNull Color foreground) {
272 if (currentRefs.isEmpty()) {
275 GraphCommitCell commitCell = getValue(table.getValueAt(row - 1, column));
276 customizeRefsPainter(myEmptyPainter, commitCell.getRefsToThisCommit(), foreground, myLogData, myComponent);
277 prevWidth = myEmptyPainter.getSize().width;
281 if (row < table.getRowCount() - 1) {
282 GraphCommitCell commitCell = getValue(table.getValueAt(row + 1, column));
283 customizeRefsPainter(myEmptyPainter, commitCell.getRefsToThisCommit(), foreground, myLogData, myComponent);
284 nextWidth = myEmptyPainter.getSize().width;
287 myWidth = Math.max(Math.max(prevWidth, nextWidth), LabelPainter.GRADIENT_WIDTH);
291 public void paint(@NotNull Graphics2D g2, int x, int y, int height) {
292 GraphicsConfig config = GraphicsUtil.setupAAPainting(g2);
293 LabelPainter.paintFadeOut(g2, x, y, myWidth, height, myComponent.getBackground());
297 public int getWidth() {