2 * Copyright 2000-2016 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.intellij.vcs.log.ui.render;
18 * Copyright 2000-2015 JetBrains s.r.o.
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
24 * http://www.apache.org/licenses/LICENSE-2.0
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.
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.ColorUtil;
37 import com.intellij.ui.JBColor;
38 import com.intellij.ui.SimpleColoredComponent;
39 import com.intellij.util.ObjectUtils;
40 import com.intellij.util.containers.ContainerUtil;
41 import com.intellij.util.containers.MultiMap;
42 import com.intellij.util.ui.GraphicsUtil;
43 import com.intellij.util.ui.JBUI;
44 import com.intellij.util.ui.UIUtil;
45 import com.intellij.vcs.log.RefGroup;
46 import com.intellij.vcs.log.VcsLogRefManager;
47 import com.intellij.vcs.log.VcsRef;
48 import com.intellij.vcs.log.VcsRefType;
49 import com.intellij.vcs.log.data.VcsLogData;
50 import org.jetbrains.annotations.NotNull;
51 import org.jetbrains.annotations.Nullable;
55 import java.util.Collection;
56 import java.util.List;
59 public class LabelPainter implements ReferencePainter {
60 public static final int TOP_TEXT_PADDING = JBUI.scale(1);
61 public static final int BOTTOM_TEXT_PADDING = JBUI.scale(2);
62 public static final int RIGHT_PADDING = JBUI.scale(2);
63 public static final int LEFT_PADDING = JBUI.scale(2);
64 public static final int MIDDLE_PADDING = JBUI.scale(2);
65 private static final int MAX_LENGTH = 22;
66 private static final String THREE_DOTS = "...";
67 private static final String TWO_DOTS = "..";
68 private static final String SEPARATOR = "/";
69 @SuppressWarnings("UseJBColor") private static final JBColor BACKGROUND = new JBColor(Color.BLACK, Color.WHITE);
70 private static final float BALANCE = 0.08f;
72 @NotNull private final VcsLogData myLogData;
74 @NotNull private List<Pair<String, LabelIcon>> myLabels = ContainerUtil.newArrayList();
75 private int myHeight = JBUI.scale(22);
76 private int myWidth = 0;
77 @NotNull private Color myBackground = UIUtil.getTableBackground();
78 @Nullable private Color myGreyBackground = null;
79 @NotNull private Color myForeground = UIUtil.getTableForeground();
81 public LabelPainter(@NotNull VcsLogData data) {
85 public void customizePainter(@NotNull JComponent component,
86 @NotNull Collection<VcsRef> references,
87 @NotNull Color background,
88 @NotNull Color foreground,
91 myBackground = background;
92 myForeground = foreground;
94 FontMetrics metrics = component.getFontMetrics(getReferenceFont());
95 myHeight = metrics.getHeight() + TOP_TEXT_PADDING + BOTTOM_TEXT_PADDING;
97 VcsLogRefManager manager = ReferencePainter.getRefManager(myLogData, references);
98 List<RefGroup> refGroups = manager == null ? ContainerUtil.emptyList() : manager.groupForTable(references);
100 myGreyBackground = calculateGreyBackground(refGroups, background, isSelected);
101 Pair<List<Pair<String, LabelIcon>>, Integer> presentation =
102 calculatePresentation(refGroups, metrics, myHeight, myGreyBackground != null ? myGreyBackground : myBackground, availableWidth);
104 myLabels = presentation.first;
105 myWidth = presentation.second;
109 private static Pair<List<Pair<String, LabelIcon>>, Integer> calculatePresentation(@NotNull List<RefGroup> refGroups,
110 @NotNull FontMetrics fontMetrics,
112 @NotNull Color background,
113 int availableWidth) {
114 int width = LEFT_PADDING + RIGHT_PADDING;
116 List<Pair<String, LabelIcon>> labels = ContainerUtil.newArrayList();
117 if (refGroups.isEmpty()) return Pair.create(labels, width);
119 for (RefGroup group : refGroups) {
120 if (group.isExpanded()) {
121 for (VcsRef ref : group.getRefs()) {
122 LabelIcon labelIcon = new LabelIcon(height, background, ref.getType().getBackgroundColor());
123 width += labelIcon.getIconWidth() + MIDDLE_PADDING;
125 String text = shortenRefName(ref.getName(), fontMetrics, availableWidth - width);
126 width += fontMetrics.stringWidth(text);
128 labels.add(Pair.create(text, labelIcon));
132 LabelIcon labelIcon = new LabelIcon(height, background, getGroupColors(group));
133 width += labelIcon.getIconWidth() + MIDDLE_PADDING;
135 String text = shortenRefName(group.getName(), fontMetrics, availableWidth - width);
136 width += fontMetrics.stringWidth(text);
138 labels.add(Pair.create(text, labelIcon));
142 return Pair.create(labels, width);
146 private static Color calculateGreyBackground(@NotNull List<RefGroup> refGroups, @NotNull Color background, boolean isSelected) {
147 if (isSelected) return null;
149 boolean paintGreyBackground;
150 for (RefGroup group : refGroups) {
151 if (group.isExpanded()) {
152 paintGreyBackground = ContainerUtil.find(group.getRefs(), ref -> !ref.getName().isEmpty()) != null;
155 paintGreyBackground = !group.getName().isEmpty();
158 if (paintGreyBackground) return ColorUtil.mix(background, BACKGROUND, BALANCE);
165 private static String shortenRefName(@NotNull String refName, @NotNull FontMetrics fontMetrics, int availableWidth) {
166 if (fontMetrics.stringWidth(refName) > availableWidth && refName.length() > MAX_LENGTH) {
167 int separatorIndex = refName.indexOf(SEPARATOR);
168 if (separatorIndex > TWO_DOTS.length()) {
169 refName = TWO_DOTS + refName.substring(separatorIndex);
172 if (fontMetrics.stringWidth(refName) <= availableWidth) return refName;
174 if (availableWidth > 0) {
175 for (int i = refName.length(); i > MAX_LENGTH; i--) {
176 String result = StringUtil.shortenTextWithEllipsis(refName, i, 0, THREE_DOTS);
177 if (fontMetrics.stringWidth(result) <= availableWidth) {
182 return StringUtil.shortenTextWithEllipsis(refName, MAX_LENGTH, 0, THREE_DOTS);
188 public static Color[] getGroupColors(@NotNull RefGroup group) {
189 MultiMap<VcsRefType, VcsRef> referencesByType = ContainerUtil.groupBy(group.getRefs(), VcsRef::getType);
191 if (referencesByType.size() == 1) {
192 Map.Entry<VcsRefType, Collection<VcsRef>> firstItem =
193 ObjectUtils.assertNotNull(ContainerUtil.getFirstItem(referencesByType.entrySet()));
194 boolean multiple = firstItem.getValue().size() > 1;
195 Color color = firstItem.getKey().getBackgroundColor();
196 colors = multiple ? new Color[]{color, color} : new Color[]{color};
199 List<Color> colorsList = ContainerUtil.newArrayList();
200 for (VcsRefType type : referencesByType.keySet()) {
201 if (referencesByType.get(type).size() > 1) {
202 colorsList.add(type.getBackgroundColor());
204 colorsList.add(type.getBackgroundColor());
206 colors = colorsList.toArray(new Color[colorsList.size()]);
211 public void paint(@NotNull Graphics2D g2, int x, int y, int height) {
212 if (myLabels.isEmpty()) return;
214 GraphicsConfig config = GraphicsUtil.setupAAPainting(g2);
215 g2.setFont(getReferenceFont());
216 g2.setStroke(new BasicStroke(1.5f));
218 FontMetrics fontMetrics = g2.getFontMetrics();
219 int baseLine = SimpleColoredComponent.getTextBaseLine(fontMetrics, height);
221 g2.setColor(myBackground);
222 g2.fillRect(x, y, myWidth, height);
224 if (myGreyBackground != null) {
225 g2.setColor(myGreyBackground);
226 g2.fillRect(x, y + baseLine - fontMetrics.getAscent() - TOP_TEXT_PADDING,
227 myWidth - RIGHT_PADDING + LEFT_PADDING,
228 fontMetrics.getHeight() + TOP_TEXT_PADDING + BOTTOM_TEXT_PADDING);
233 for (Pair<String, LabelIcon> label : myLabels) {
234 LabelIcon icon = label.second;
235 String text = label.first;
237 icon.paintIcon(null, g2, x, y + (height - icon.getIconHeight()) / 2);
238 x += icon.getIconWidth();
240 g2.setColor(myForeground);
241 g2.drawString(text, x, y + baseLine);
242 x += fontMetrics.stringWidth(text) + MIDDLE_PADDING;
248 public Dimension getSize() {
249 if (myLabels.isEmpty()) return new Dimension();
250 return new Dimension(myWidth, myHeight);
254 public boolean isLeftAligned() {
259 public Font getReferenceFont() {
260 Font font = RectanglePainter.getFont();
261 return font.deriveFont(font.getSize() - 1f);