[vcs-log] reference painters get ref manager themselves
[idea/community.git] / platform / vcs-log / impl / src / com / intellij / vcs / log / ui / render / LabelPainter.java
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.intellij.vcs.log.ui.render;
17 /*
18  * Copyright 2000-2015 JetBrains s.r.o.
19  *
20  * Licensed under the Apache License, Version 2.0 (the "License");
21  * you may not use this file except in compliance with the License.
22  * You may obtain a copy of the License at
23  *
24  * http://www.apache.org/licenses/LICENSE-2.0
25  *
26  * Unless required by applicable law or agreed to in writing, software
27  * distributed under the License is distributed on an "AS IS" BASIS,
28  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29  * See the License for the specific language governing permissions and
30  * limitations under the License.
31  */
32
33 import com.intellij.openapi.ui.GraphicsConfig;
34 import com.intellij.openapi.util.Pair;
35 import com.intellij.openapi.util.text.StringUtil;
36 import com.intellij.ui.SimpleColoredComponent;
37 import com.intellij.util.ObjectUtils;
38 import com.intellij.util.containers.ContainerUtil;
39 import com.intellij.util.containers.MultiMap;
40 import com.intellij.util.ui.GraphicsUtil;
41 import com.intellij.util.ui.JBUI;
42 import com.intellij.util.ui.UIUtil;
43 import com.intellij.vcs.log.RefGroup;
44 import com.intellij.vcs.log.VcsLogRefManager;
45 import com.intellij.vcs.log.VcsRef;
46 import com.intellij.vcs.log.VcsRefType;
47 import com.intellij.vcs.log.data.VcsLogData;
48 import org.jetbrains.annotations.NotNull;
49
50 import javax.swing.*;
51 import java.awt.*;
52 import java.util.Collection;
53 import java.util.List;
54 import java.util.Map;
55
56 public class LabelPainter implements ReferencePainter {
57   public static final int TOP_TEXT_PADDING = JBUI.scale(2);
58   public static final int BOTTOM_TEXT_PADDING = JBUI.scale(1);
59   public static final int GRADIENT_WIDTH = JBUI.scale(50);
60   public static final int RIGHT_PADDING = JBUI.scale(5);
61   public static final int MIDDLE_PADDING = JBUI.scale(5);
62   private static final int MAX_LENGTH = 22;
63   private static final String THREE_DOTS = "...";
64   private static final String TWO_DOTS = "..";
65   private static final String SEPARATOR = "/";
66
67   @NotNull private final VcsLogData myLogData;
68
69   @NotNull private List<Pair<String, LabelIcon>> myLabels = ContainerUtil.newArrayList();
70   private int myHeight = JBUI.scale(22);
71   private int myWidth = 0;
72   @NotNull
73   private Color myBackground = UIUtil.getTableBackground();
74   @NotNull
75   private Color myForeground = UIUtil.getTableForeground();
76
77   public LabelPainter(@NotNull VcsLogData data) {
78     myLogData = data;
79   }
80
81   public void customizePainter(@NotNull JComponent component,
82                                @NotNull Collection<VcsRef> references,
83                                @NotNull Color background,
84                                @NotNull Color foreground) {
85     myBackground = background;
86     myForeground = foreground;
87
88     FontMetrics metrics = component.getFontMetrics(getReferenceFont());
89     myHeight = metrics.getHeight() + TOP_TEXT_PADDING + BOTTOM_TEXT_PADDING;
90
91     VcsLogRefManager manager = ReferencePainter.getRefManager(myLogData, references);
92     List<RefGroup> refGroups = manager == null ? ContainerUtil.emptyList() : manager.groupForTable(references);
93     Pair<List<Pair<String, LabelIcon>>, Integer> presentation = calculatePresentation(refGroups, metrics, myHeight, myBackground);
94
95     myLabels = presentation.first;
96     myWidth = presentation.second;
97   }
98
99   @NotNull
100   public static Pair<List<Pair<String, LabelIcon>>, Integer> calculatePresentation(@NotNull List<RefGroup> refGroups,
101                                                                                    @NotNull FontMetrics fontMetrics,
102                                                                                    int height,
103                                                                                    @NotNull Color background) {
104     int width = GRADIENT_WIDTH + RIGHT_PADDING;
105
106     List<Pair<String, LabelIcon>> labels = ContainerUtil.newArrayList();
107     if (refGroups.isEmpty()) return Pair.create(labels, width);
108
109     for (RefGroup group : refGroups) {
110       if (group.isExpanded()) {
111         for (VcsRef ref : group.getRefs()) {
112           LabelIcon labelIcon = new LabelIcon(height, background, ref.getType().getBackgroundColor());
113           String text = shortenRefName(ref.getName());
114
115           labels.add(Pair.create(text, labelIcon));
116           width += labelIcon.getIconWidth() + fontMetrics.stringWidth(text) + MIDDLE_PADDING;
117         }
118       }
119       else {
120         LabelIcon labelIcon = new LabelIcon(height, background, getGroupColors(group));
121         String text = shortenRefName(group.getName());
122
123         labels.add(Pair.create(text, labelIcon));
124         width += labelIcon.getIconWidth() + fontMetrics.stringWidth(text) + MIDDLE_PADDING;
125       }
126     }
127
128     return Pair.create(labels, width);
129   }
130
131   @NotNull
132   private static String shortenRefName(@NotNull String refName) {
133     int textLength = refName.length();
134     if (textLength > MAX_LENGTH) {
135       int separatorIndex = refName.indexOf(SEPARATOR);
136       if (separatorIndex > TWO_DOTS.length()) {
137         refName = TWO_DOTS + refName.substring(separatorIndex);
138       }
139       return StringUtil.shortenTextWithEllipsis(refName, MAX_LENGTH, 0, THREE_DOTS);
140     }
141     return refName;
142   }
143
144   @NotNull
145   public static Color[] getGroupColors(@NotNull RefGroup group) {
146     MultiMap<VcsRefType, VcsRef> referencesByType = ContainerUtil.groupBy(group.getRefs(), VcsRef::getType);
147     Color[] colors;
148     if (referencesByType.size() == 1) {
149       Map.Entry<VcsRefType, Collection<VcsRef>> firstItem =
150         ObjectUtils.assertNotNull(ContainerUtil.getFirstItem(referencesByType.entrySet()));
151       boolean multiple = firstItem.getValue().size() > 1;
152       Color color = firstItem.getKey().getBackgroundColor();
153       colors = multiple ? new Color[]{color, color} : new Color[]{color};
154     }
155     else {
156       List<Color> colorsList = ContainerUtil.newArrayList();
157       for (VcsRefType type : referencesByType.keySet()) {
158         if (referencesByType.get(type).size() > 1) {
159           colorsList.add(type.getBackgroundColor());
160         }
161         colorsList.add(type.getBackgroundColor());
162       }
163       colors = colorsList.toArray(new Color[colorsList.size()]);
164     }
165     return colors;
166   }
167
168   public void paint(@NotNull Graphics2D g2, int x, int y, int height) {
169     if (myLabels.isEmpty()) return;
170
171     GraphicsConfig config = GraphicsUtil.setupAAPainting(g2);
172     g2.setFont(getReferenceFont());
173     g2.setStroke(new BasicStroke(1.5f));
174
175     x = paintFadeOut(g2, x, y, myWidth, height, myBackground);
176
177     FontMetrics fontMetrics = g2.getFontMetrics();
178     for (Pair<String, LabelIcon> label : myLabels) {
179       LabelIcon icon = label.second;
180       String text = label.first;
181
182       icon.paintIcon(null, g2, x, y + (height - icon.getIconHeight()) / 2);
183       x += icon.getIconWidth();
184
185       g2.setColor(myForeground);
186       g2.drawString(text, x, y + SimpleColoredComponent.getTextBaseLine(fontMetrics, height));
187       x += fontMetrics.stringWidth(text) + MIDDLE_PADDING;
188     }
189
190     config.restore();
191   }
192
193   public static int paintFadeOut(@NotNull Graphics2D g2, int x, int y, int width, int height, @NotNull Color background) {
194     //noinspection UseJBColor
195     g2.setPaint(new GradientPaint(x, y,
196                                   new Color(background.getRed(), background.getGreen(), background.getBlue(), 0),
197                                   x + GRADIENT_WIDTH, y,
198                                   background));
199     g2.fill(new Rectangle(x, y, GRADIENT_WIDTH, height));
200     x += GRADIENT_WIDTH;
201
202     g2.setColor(background);
203     g2.fillRect(x, y, width - GRADIENT_WIDTH, height);
204     return x;
205   }
206
207   public Dimension getSize() {
208     if (myLabels.isEmpty()) return new Dimension();
209     return new Dimension(myWidth, myHeight);
210   }
211
212   @Override
213   public boolean isLeftAligned() {
214     return false;
215   }
216 }
217