blob: dc876657c4283e278380bf4c21c606dbbbb9fd2d [file] [log] [blame]
package com.intellij.vcs.log.ui.frame;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.util.ProgressWindow;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.changes.issueLinks.IssueLinkHtmlRenderer;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.BrowserHyperlinkListener;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.components.JBLoadingPanel;
import com.intellij.ui.components.JBTextField;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.text.DateFormatUtil;
import com.intellij.util.ui.AsyncProcessIcon;
import com.intellij.util.ui.UIUtil;
import com.intellij.vcs.log.Hash;
import com.intellij.vcs.log.VcsFullCommitDetails;
import com.intellij.vcs.log.VcsRef;
import com.intellij.vcs.log.data.DataPack;
import com.intellij.vcs.log.data.LoadingDetails;
import com.intellij.vcs.log.data.VcsLogDataHolder;
import com.intellij.vcs.log.printer.idea.PrintParameters;
import com.intellij.vcs.log.ui.VcsLogColorManager;
import com.intellij.vcs.log.ui.render.RefPainter;
import com.intellij.vcs.log.ui.tables.AbstractVcsLogTableModel;
import net.miginfocom.swing.MigLayout;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* @author Kirill Likhodedov
*/
class DetailsPanel extends JPanel implements ListSelectionListener {
private static final Logger LOG = Logger.getInstance("Vcs.Log");
private static final String STANDARD_LAYER = "Standard";
private static final String MESSAGE_LAYER = "Message";
@NotNull private final VcsLogDataHolder myLogDataHolder;
@NotNull private final VcsLogGraphTable myGraphTable;
@NotNull private final RefsPanel myRefsPanel;
@NotNull private final DataPanel myDataPanel;
@NotNull private final ContainingBranchesPanel myContainingBranchesPanel;
@NotNull private final MessagePanel myMessagePanel;
@NotNull private final JBLoadingPanel myLoadingPanel;
@NotNull private DataPack myDataPack;
DetailsPanel(@NotNull VcsLogDataHolder logDataHolder, @NotNull VcsLogGraphTable graphTable, @NotNull VcsLogColorManager colorManager,
@NotNull DataPack initialDataPack) {
super(new CardLayout());
myLogDataHolder = logDataHolder;
myGraphTable = graphTable;
myDataPack = initialDataPack;
myRefsPanel = new RefsPanel(colorManager);
myDataPanel = new DataPanel(logDataHolder.getProject());
myContainingBranchesPanel = new ContainingBranchesPanel();
myMessagePanel = new MessagePanel();
final JScrollPane scrollPane = ScrollPaneFactory.createScrollPane();
JPanel content = new JPanel(new MigLayout("flowy, ins 0, fill, hidemode 3")) {
@Override
public Dimension getPreferredSize() {
Dimension size = super.getPreferredSize();
size.width = scrollPane.getViewport().getWidth() - 5;
return size;
}
};
content.setOpaque(false);
scrollPane.setViewportView(content);
content.add(myRefsPanel, "shrinky, pushx, growx");
content.add(myDataPanel, "growy, push");
content.add(myContainingBranchesPanel, "shrinky, pushx, growx");
content.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5));
myLoadingPanel = new JBLoadingPanel(new BorderLayout(), logDataHolder, ProgressWindow.DEFAULT_PROGRESS_DIALOG_POSTPONE_TIME_MILLIS);
myLoadingPanel.add(scrollPane);
add(myLoadingPanel, STANDARD_LAYER);
add(myMessagePanel, MESSAGE_LAYER);
setBackground(UIUtil.getTableBackground());
showMessage("No commits selected");
}
void updateDataPack(@NotNull DataPack dataPack) {
myDataPack = dataPack;
}
@Override
public void valueChanged(@Nullable ListSelectionEvent notUsed) {
int[] rows = myGraphTable.getSelectedRows();
if (rows.length < 1) {
showMessage("No commits selected");
}
else if (rows.length > 1) {
showMessage("Several commits selected");
}
else {
((CardLayout)getLayout()).show(this, STANDARD_LAYER);
int row = rows[0];
AbstractVcsLogTableModel<?> tableModel = (AbstractVcsLogTableModel)myGraphTable.getModel();
Hash hash = tableModel.getHashAtRow(row);
VcsFullCommitDetails commitData = myLogDataHolder.getCommitDetailsGetter().getCommitData(row, tableModel);
if (commitData == null || hash == null) {
showMessage("No commits selected");
return;
}
if (commitData instanceof LoadingDetails) {
myLoadingPanel.startLoading();
myDataPanel.setData(null);
myRefsPanel.setRefs(Collections.<VcsRef>emptyList());
}
else {
myLoadingPanel.stopLoading();
myDataPanel.setData(commitData);
myRefsPanel.setRefs(sortRefs(hash, commitData.getRoot()));
}
List<String> branches = null;
if (!(commitData instanceof LoadingDetails)) {
branches = myLogDataHolder.getContainingBranchesGetter().requestContainingBranches(commitData.getRoot(), hash);
}
myContainingBranchesPanel.setBranches(branches);
}
}
private void showMessage(String text) {
myLoadingPanel.stopLoading();
((CardLayout)getLayout()).show(this, MESSAGE_LAYER);
myMessagePanel.setText(text);
}
@NotNull
private List<VcsRef> sortRefs(@NotNull Hash hash, @NotNull VirtualFile root) {
Collection<VcsRef> refs = myDataPack.getRefsModel().refsToCommit(hash);
return ContainerUtil.sorted(refs, myLogDataHolder.getLogProvider(root).getReferenceManager().getComparator());
}
private static class DataPanel extends JEditorPane {
@NotNull private final Project myProject;
DataPanel(@NotNull Project project) {
super(UIUtil.HTML_MIME, "");
setEditable(false);
myProject = project;
addHyperlinkListener(new BrowserHyperlinkListener());
setOpaque(false);
}
void setData(@Nullable VcsFullCommitDetails commit) {
if (commit == null) {
setText("");
}
else {
String body = getHashText(commit) + "<br/>" + getAuthorText(commit) + "<p>" + getMessageText(commit) + "</p>";
setText("<html><head>" + UIUtil.getCssFontDeclaration(UIUtil.getLabelFont()) + "</head><body>" + body + "</body></html>");
setCaretPosition(0);
}
}
private String getMessageText(VcsFullCommitDetails commit) {
String fullMessage = commit.getFullMessage();
int separator = fullMessage.indexOf("\n\n");
String subject = separator > 0 ? fullMessage.substring(0, separator) : fullMessage;
String description = fullMessage.substring(subject.length());
return "<b>" + IssueLinkHtmlRenderer.formatTextWithLinks(myProject, subject) + "</b>" +
IssueLinkHtmlRenderer.formatTextWithLinks(myProject, description);
}
private static String getHashText(VcsFullCommitDetails commit) {
return commit.getId().asString();
}
private static String getAuthorText(VcsFullCommitDetails commit) {
String authorText = commit.getAuthor().getName() + " at " + DateFormatUtil.formatDateTime(commit.getAuthorTime());
if (!commit.getAuthor().equals(commit.getCommitter())) {
String commitTime;
if (commit.getAuthorTime() != commit.getTimestamp()) {
commitTime = " at " + DateFormatUtil.formatDateTime(commit.getTimestamp());
}
else {
commitTime = "";
}
authorText += " (committed by " + commit.getCommitter().getName() + commitTime + ")";
}
else if (commit.getAuthorTime() != commit.getTimestamp()) {
authorText += " (committed at " + DateFormatUtil.formatDateTime(commit.getTimestamp()) + ")";
}
return authorText;
}
}
private static class ContainingBranchesPanel extends JPanel {
private final JComponent myLoadingIcon;
private final JTextField myBranchesList;
ContainingBranchesPanel() {
JLabel label = new JBLabel("Contained in branches: ") {
@Override
public Font getFont() {
return UIUtil.getLabelFont().deriveFont(Font.ITALIC);
}
};
myLoadingIcon = new AsyncProcessIcon("Loading...");
myBranchesList = new JBTextField("");
myBranchesList.setEditable(false);
myBranchesList.setBorder(null);
setOpaque(false);
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
add(label);
add(myLoadingIcon);
add(myBranchesList);
add(Box.createHorizontalGlue());
}
void setBranches(@Nullable List<String> branches) {
if (branches == null) {
myLoadingIcon.setVisible(true);
myBranchesList.setVisible(false);
}
else {
myLoadingIcon.setVisible(false);
myBranchesList.setVisible(true);
myBranchesList.setText(getContainedBranchesText(branches));
}
}
@NotNull
private static String getContainedBranchesText(@NotNull List<String> branches) {
return StringUtil.join(branches, ", ");
}
}
private static class RefsPanel extends JPanel {
@NotNull private final RefPainter myRefPainter;
@NotNull private List<VcsRef> myRefs;
RefsPanel(@NotNull VcsLogColorManager colorManager) {
myRefPainter = new RefPainter(colorManager, false);
myRefs = Collections.emptyList();
setPreferredSize(new Dimension(-1, PrintParameters.HEIGHT_CELL + UIUtil.DEFAULT_VGAP));
setOpaque(false);
}
@Override
protected void paintComponent(Graphics g) {
// TODO when the right margin reaches, draw on the second line
myRefPainter.draw((Graphics2D)g, myRefs, 0, getWidth());
}
void setRefs(@NotNull List<VcsRef> refs) {
myRefs = refs;
setVisible(!myRefs.isEmpty());
repaint();
}
}
private static class MessagePanel extends JPanel {
private final JLabel myLabel;
MessagePanel() {
super(new BorderLayout());
myLabel = new JLabel();
myLabel.setForeground(UIUtil.getInactiveTextColor());
myLabel.setHorizontalAlignment(SwingConstants.CENTER);
myLabel.setVerticalAlignment(SwingConstants.CENTER);
add(myLabel);
}
void setText(String text) {
myLabel.setText(text);
}
}
}