blob: 0343651bfc691090ec44a5249f908a790516c6ed [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.vcs.changes.committed;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.AbstractVcs;
import com.intellij.openapi.vcs.CachingCommittedChangesProvider;
import com.intellij.openapi.vcs.VcsBundle;
import com.intellij.openapi.vcs.changes.issueLinks.IssueLinkRenderer;
import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
import com.intellij.ui.ColoredTreeCellRenderer;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.util.text.DateFormatUtil;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.plaf.TreeUI;
import javax.swing.plaf.basic.BasicTreeUI;
import javax.swing.tree.DefaultMutableTreeNode;
import java.awt.*;
import java.util.Date;
import java.util.List;
public class CommittedChangeListRenderer extends ColoredTreeCellRenderer {
private final IssueLinkRenderer myRenderer;
private final List<CommittedChangeListDecorator> myDecorators;
private final Project myProject;
private int myDateWidth;
private int myFontSize;
public CommittedChangeListRenderer(final Project project, final List<CommittedChangeListDecorator> decorators) {
myProject = project;
myRenderer = new IssueLinkRenderer(project, this);
myDecorators = decorators;
myDateWidth = 0;
myFontSize = -1;
}
public static String getDateOfChangeList(final Date date) {
return DateFormatUtil.formatPrettyDateTime(date);
}
public static Pair<String, Boolean> getDescriptionOfChangeList(final String text) {
return new Pair<String, Boolean>(text.replaceAll("\n", " // "), text.contains("\n"));
}
public static String truncateDescription(final String initDescription, final FontMetrics fontMetrics, int maxWidth) {
String description = initDescription;
int descWidth = fontMetrics.stringWidth(description);
while(description.length() > 0 && (descWidth > maxWidth)) {
description = trimLastWord(description);
descWidth = fontMetrics.stringWidth(description + " ");
}
return description;
}
public void customizeCellRenderer(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
customize(tree, value, selected, expanded, leaf, row, hasFocus);
}
public void customize(JComponent tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
if (node.getUserObject() instanceof CommittedChangeList) {
CommittedChangeList changeList = (CommittedChangeList) node.getUserObject();
renderChangeList(tree, changeList);
}
else if (node.getUserObject() != null) {
append(node.getUserObject().toString(), SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES);
}
}
public void renderChangeList(JComponent tree, CommittedChangeList changeList) {
final Container parent = tree.getParent();
final int rowX = getRowX(myTree, 2);
int availableWidth = parent == null ? 100 : parent.getWidth() - rowX;
String date = ", " + getDateOfChangeList(changeList.getCommitDate());
final FontMetrics fontMetrics = tree.getFontMetrics(tree.getFont());
final FontMetrics boldMetrics = tree.getFontMetrics(tree.getFont().deriveFont(Font.BOLD));
final FontMetrics italicMetrics = tree.getFontMetrics(tree.getFont().deriveFont(Font.ITALIC));
if (myDateWidth <= 0 || (fontMetrics.getFont().getSize() != myFontSize)) {
myDateWidth = Math.max(fontMetrics.stringWidth(", Yesterday 00:00 PM "), fontMetrics.stringWidth(", 00/00/00 00:00 PM "));
myDateWidth = Math.max(myDateWidth, fontMetrics.stringWidth(getDateOfChangeList(new Date(2000, 11, 31))));
myFontSize = fontMetrics.getFont().getSize();
}
int dateCommitterSize = myDateWidth + boldMetrics.stringWidth(changeList.getCommitterName());
final Pair<String, Boolean> descriptionInfo = getDescriptionOfChangeList(changeList.getName().trim());
boolean truncated = descriptionInfo.getSecond().booleanValue();
String description = descriptionInfo.getFirst();
for (CommittedChangeListDecorator decorator : myDecorators) {
final Icon icon = decorator.decorate(changeList);
if (icon != null) {
setIcon(icon);
}
}
int descMaxWidth = availableWidth - dateCommitterSize - 8;
boolean partial = (changeList instanceof ReceivedChangeList) && ((ReceivedChangeList)changeList).isPartial();
int descWidth = 0;
if (partial) {
final String partialMarker = VcsBundle.message("committed.changes.partial.list") + " ";
append(partialMarker, SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES);
descWidth += boldMetrics.stringWidth(partialMarker);
}
descWidth += fontMetrics.stringWidth(description);
int numberWidth = 0;
final AbstractVcs vcs = changeList.getVcs();
if (vcs != null) {
final CachingCommittedChangesProvider provider = vcs.getCachingCommittedChangesProvider();
if (provider != null && provider.getChangelistTitle() != null) {
String number = "#" + changeList.getNumber() + " ";
numberWidth = fontMetrics.stringWidth(number);
descWidth += numberWidth;
append(number, SimpleTextAttributes.GRAY_ATTRIBUTES);
}
}
int branchWidth = 0;
String branch = changeList.getBranch();
if (branch != null) {
branch += " ";
branchWidth = italicMetrics.stringWidth(branch);
descWidth += branchWidth;
append(branch, SimpleTextAttributes.GRAY_ITALIC_ATTRIBUTES);
}
if (description.isEmpty() && !truncated) {
append(VcsBundle.message("committed.changes.empty.comment"), SimpleTextAttributes.GRAYED_ATTRIBUTES);
appendFixedTextFragmentWidth(descMaxWidth);
}
else if (descMaxWidth < 0) {
myRenderer.appendTextWithLinks(description);
}
else if (descWidth < descMaxWidth && !truncated) {
myRenderer.appendTextWithLinks(description);
appendFixedTextFragmentWidth(descMaxWidth);
}
else {
final String moreMarker = VcsBundle.message("changes.browser.details.marker");
int moreWidth = fontMetrics.stringWidth(moreMarker);
int remainingWidth = descMaxWidth - moreWidth - numberWidth - branchWidth;
description = truncateDescription(description, fontMetrics, remainingWidth);
myRenderer.appendTextWithLinks(description);
if (!StringUtil.isEmpty(description)) {
append(" ", SimpleTextAttributes.REGULAR_ATTRIBUTES);
append(moreMarker, SimpleTextAttributes.LINK_ATTRIBUTES, new CommittedChangesTreeBrowser.MoreLauncher(myProject, changeList));
} else if (remainingWidth > 0) {
append(moreMarker, SimpleTextAttributes.LINK_ATTRIBUTES, new CommittedChangesTreeBrowser.MoreLauncher(myProject, changeList));
}
// align value is for the latest added piece
appendFixedTextFragmentWidth(descMaxWidth);
}
append(changeList.getCommitterName(), SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES);
append(date, SimpleTextAttributes.REGULAR_ATTRIBUTES);
}
private static String trimLastWord(final String description) {
int pos = description.trim().lastIndexOf(' ');
if (pos >= 0) {
return description.substring(0, pos).trim();
}
return description.substring(0, description.length()-1);
}
@NotNull
public Dimension getPreferredSize() {
return new Dimension(2000, super.getPreferredSize().height);
}
public static int getRowX(JTree tree, int depth) {
if (tree == null) return 0;
final TreeUI ui = tree.getUI();
if (ui instanceof BasicTreeUI) {
final BasicTreeUI treeUI = ((BasicTreeUI)ui);
return (treeUI.getLeftChildIndent() + treeUI.getRightChildIndent()) * depth;
}
final int leftIndent = UIUtil.getTreeLeftChildIndent();
final int rightIndent = UIUtil.getTreeRightChildIndent();
return (leftIndent + rightIndent) * depth;
}
}