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.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.emptyList()); } else { myLoadingPanel.stopLoading(); myDataPanel.setData(commitData); myRefsPanel.setRefs(sortRefs(hash, commitData.getRoot())); } List 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 sortRefs(@NotNull Hash hash, @NotNull VirtualFile root) { Collection refs = myDataPack.getRefsModel().refsToCommit(hash); return myLogDataHolder.getLogProvider(root).getReferenceManager().sort(refs); } 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) + "
" + getAuthorText(commit) + "

" + getMessageText(commit) + "

"; setText("" + UIUtil.getCssFontDeclaration(UIUtil.getLabelFont()) + "" + body + ""); 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 "" + IssueLinkHtmlRenderer.formatTextWithLinks(myProject, subject) + "" + 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 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 branches) { return StringUtil.join(branches, ", "); } } private static class RefsPanel extends JPanel { @NotNull private final RefPainter myRefPainter; @NotNull private List 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 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); } } }