[vcs-log] reference painters get ref manager themselves
[idea/community.git] / platform / vcs-log / impl / src / com / intellij / vcs / log / ui / render / GraphCommitCellRenderer.java
1 package com.intellij.vcs.log.ui.render;
2
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;
24
25 import javax.swing.*;
26 import javax.swing.table.TableColumn;
27 import java.awt.*;
28 import java.awt.image.BufferedImage;
29 import java.util.Collection;
30
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);
34
35   @NotNull private final VcsLogData myLogData;
36   @NotNull private final VcsLogGraphTable myGraphTable;
37
38   @Nullable private final FadeOutPainter myFadeOutPainter;
39   @Nullable private final ReferencePainter myTooltipPainter;
40
41   @NotNull private final MyComponent myComponent;
42
43   private boolean myExpanded;
44
45   public GraphCommitCellRenderer(@NotNull VcsLogData logData,
46                                  @NotNull GraphCellPainter painter,
47                                  @NotNull VcsLogGraphTable table) {
48     myLogData = logData;
49     myGraphTable = table;
50
51     myFadeOutPainter = isRedesignedLabels() ? new FadeOutPainter() : null;
52     myTooltipPainter = isRedesignedLabels() ? new LabelPainter(myLogData) : null;
53
54     myComponent = new MyComponent(logData, painter, table) {
55       @Override
56       public void paintComponent(Graphics g) {
57         super.paintComponent(g);
58
59         if (myFadeOutPainter != null) {
60           if (!myExpanded) {
61             int start = Math.max(myGraphImage.getWidth(), getWidth() - myFadeOutPainter.getWidth());
62             myFadeOutPainter.paint((Graphics2D)g, start, 0, getHeight());
63           }
64         }
65       }
66     };
67   }
68
69   @Override
70   protected Component getTableCellRendererComponentImpl(@NotNull JTable table,
71                                                         @NotNull GraphCommitCell value,
72                                                         boolean isSelected,
73                                                         boolean hasFocus,
74                                                         int row,
75                                                         int column) {
76     myComponent.customize(value, isSelected, hasFocus, row, column);
77
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*/);
81     }
82     return myComponent;
83   }
84
85   public static boolean isRedesignedLabels() {
86     return Registry.is("vcs.log.labels.redesign");
87   }
88
89   @Nullable
90   public JComponent getTooltip(@NotNull Object value, @NotNull Point point, int width) {
91     if (myTooltipPainter == null) return null;
92
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);
99       }
100     }
101     return null;
102   }
103
104   public int getPreferredHeight() {
105     return myComponent.getPreferredHeight();
106   }
107
108   public int getTooltipXCoordinate(int row) {
109     return myComponent.getTooltipXCoordinate(getValue(myGraphTable.getModel().getValueAt(row, GraphTableModel.COMMIT_COLUMN)));
110   }
111
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;
118
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;
122
123     public MyComponent(@NotNull VcsLogData data, @NotNull GraphCellPainter painter, @NotNull VcsLogGraphTable table) {
124       myLogData = data;
125       myPainter = painter;
126       myGraphTable = table;
127
128       myReferencePainter = isRedesignedLabels() ? new LabelPainter(myLogData) : new RectangleReferencePainter(myLogData);
129
130       myIssueLinkRenderer = new IssueLinkRenderer(myLogData.getProject(), this);
131       myFont = RectanglePainter.getFont();
132       myHeight = calculateHeight();
133     }
134
135     @NotNull
136     @Override
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());
142     }
143
144     @Override
145     public void paintComponent(Graphics g) {
146       super.paintComponent(g);
147
148       int graphImageWidth = myGraphImage.getWidth();
149
150       if (!myReferencePainter.isLeftAligned()) {
151         int start = Math.max(graphImageWidth, getWidth() - myReferencePainter.getSize().width);
152         myReferencePainter.paint((Graphics2D)g, start, 0, getHeight());
153       }
154       else {
155         myReferencePainter.paint((Graphics2D)g, graphImageWidth, 0, getHeight());
156       }
157
158       UIUtil.drawImage(g, myGraphImage.getImage(), 0, 0, null);
159     }
160
161     public void customize(@NotNull GraphCommitCell cell, boolean isSelected, boolean hasFocus, int row, int column) {
162       clear();
163       setPaintFocusBorder(hasFocus && myGraphTable.getCellSelectionEnabled());
164       acquireState(myGraphTable, isSelected, hasFocus, row, column);
165       getCellState().updateRenderer(this);
166
167       myGraphImage = getGraphImage(cell.getPrintElements());
168
169       SimpleTextAttributes style = myGraphTable.applyHighlighters(this, row, column, hasFocus, isSelected);
170
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);
174
175       setBorder(null);
176       append("");
177       appendTextPadding(myGraphImage.getWidth() + (myReferencePainter.isLeftAligned() ? myReferencePainter.getSize().width : 0));
178       myIssueLinkRenderer.appendTextWithLinks(cell.getText(), style);
179     }
180
181     private int calculateHeight() {
182       return Math.max(myReferencePainter.getSize().height, getFontMetrics(myFont).getHeight() + VERTICAL_PADDING);
183     }
184
185     public int getPreferredHeight() {
186       Font font = RectanglePainter.getFont();
187       if (myFont != font) {
188         myFont = font;
189         myHeight = calculateHeight();
190       }
191       return myHeight;
192     }
193
194     @NotNull
195     private PaintInfo getGraphImage(@NotNull Collection<? extends PrintElement> printElements) {
196       double maxIndex = 0;
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);
202         }
203       }
204       maxIndex++;
205
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);
212
213       int width = (int)(maxIndex * PaintParameters.getNodeWidth(myGraphTable.getRowHeight()));
214       return new PaintInfo(image, width);
215     }
216
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;
223       }
224       return -1;
225     }
226   }
227
228   private static class PaintInfo {
229     private final int myWidth;
230     @NotNull private final Image myImage;
231
232     PaintInfo(@NotNull Image image, int width) {
233       myImage = image;
234       myWidth = width;
235     }
236
237     @NotNull
238     Image getImage() {
239       return myImage;
240     }
241
242     /**
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.)
246      */
247     int getWidth() {
248       return myWidth;
249     }
250   }
251
252   private class FadeOutPainter {
253     @NotNull private final LabelPainter myEmptyPainter = new LabelPainter(myLogData);
254     private int myWidth = LabelPainter.GRADIENT_WIDTH;
255
256     public void customize(@NotNull Collection<VcsRef> currentRefs, int row, int column, @NotNull JTable table) {
257       myWidth = 0;
258
259       if (currentRefs.isEmpty()) {
260         int prevWidth = 0;
261         if (row > 0) {
262           GraphCommitCell commitCell = getValue(table.getValueAt(row - 1, column));
263           myEmptyPainter.customizePainter(myComponent, commitCell.getRefsToThisCommit(), myComponent.getBackground(), JBColor.black);
264           prevWidth = myEmptyPainter.getSize().width;
265         }
266
267         int nextWidth = 0;
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;
272         }
273
274         myWidth = Math.max(Math.max(prevWidth, nextWidth), LabelPainter.GRADIENT_WIDTH);
275       }
276     }
277
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());
281       config.restore();
282     }
283
284     public int getWidth() {
285       return myWidth;
286     }
287   }
288 }