blob: e3eb6dadbcbfc492a3df816980ca215e2dcad19b [file] [log] [blame]
/*
* Copyright 2000-2014 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.actions;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.openapi.ui.popup.PopupChooserBuilder;
import com.intellij.openapi.vcs.AbstractVcs;
import com.intellij.openapi.vcs.FilePathImpl;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.intellij.openapi.vcs.VcsBundle;
import com.intellij.openapi.vcs.diff.DiffProvider;
import com.intellij.openapi.vcs.history.*;
import com.intellij.openapi.vcs.impl.VcsBackgroundableActions;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.ui.SpeedSearchBase;
import com.intellij.ui.TableUtil;
import com.intellij.ui.dualView.TreeTableView;
import com.intellij.ui.table.TableView;
import com.intellij.ui.treeStructure.treetable.ListTreeTableModelOnColumns;
import com.intellij.util.Consumer;
import com.intellij.util.TreeItem;
import com.intellij.util.text.DateFormatUtil;
import com.intellij.util.ui.ColumnInfo;
import com.intellij.util.ui.ListTableModel;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import java.awt.*;
import java.util.Date;
import java.util.List;
public class CompareWithSelectedRevisionAction extends AbstractVcsAction {
private static final ColumnInfo<TreeNodeAdapter,String> BRANCH_COLUMN = new ColumnInfo<TreeNodeAdapter, String>(VcsBundle.message("column.name.revisions.list.branch")){
public String valueOf(final TreeNodeAdapter object) {
return object.getRevision().getBranchName();
}
};
private static final ColumnInfo<TreeNodeAdapter,String> REVISION_COLUMN = new ColumnInfo<TreeNodeAdapter, String>(VcsBundle.message("column.name.revision.list.revision")){
public String valueOf(final TreeNodeAdapter object) {
return object.getRevision().getRevisionNumber().asString();
}
};
private static final ColumnInfo<TreeNodeAdapter,String> DATE_COLUMN = new ColumnInfo<TreeNodeAdapter, String>(VcsBundle.message("column.name.revisions.list.filter")){
public String valueOf(final TreeNodeAdapter object) {
return DateFormatUtil.formatPrettyDateTime(object.getRevision().getRevisionDate());
}
};
private static final ColumnInfo<TreeNodeAdapter,String> AUTHOR_COLUMN = new ColumnInfo<TreeNodeAdapter, String>(VcsBundle.message("column.name.revision.list.author")){
public String valueOf(final TreeNodeAdapter object) {
return object.getRevision().getAuthor();
}
};
private static final ColumnInfo<VcsFileRevision, String> REVISION_TABLE_COLUMN = new ColumnInfo<VcsFileRevision, String>(VcsBundle.message("column.name.revision.list.revision")) {
public String valueOf(final VcsFileRevision vcsFileRevision) {
return vcsFileRevision.getRevisionNumber().asString();
}
};
private static final ColumnInfo<VcsFileRevision, String> DATE_TABLE_COLUMN = new ColumnInfo<VcsFileRevision, String>(VcsBundle.message("column.name.revision.list.revision")) {
public String valueOf(final VcsFileRevision vcsFileRevision) {
final Date date = vcsFileRevision.getRevisionDate();
return date == null ? "" : DateFormatUtil.formatPrettyDateTime(date);
}
};
private static final ColumnInfo<VcsFileRevision,String> AUTHOR_TABLE_COLUMN = new ColumnInfo<VcsFileRevision, String>(VcsBundle.message("column.name.revision.list.author")){
public String valueOf(final VcsFileRevision vcsFileRevision) {
return vcsFileRevision.getAuthor();
}
};
private static final ColumnInfo<VcsFileRevision,String> BRANCH_TABLE_COLUMN = new ColumnInfo<VcsFileRevision, String>(VcsBundle.message("column.name.revisions.list.branch")){
public String valueOf(final VcsFileRevision vcsFileRevision) {
return vcsFileRevision.getBranchName();
}
};
public void update(VcsContext e, Presentation presentation) {
AbstractShowDiffAction.updateDiffAction(presentation, e, VcsBackgroundableActions.COMPARE_WITH);
}
protected boolean forceSyncUpdate(final AnActionEvent e) {
return true;
}
protected void actionPerformed(VcsContext vcsContext) {
final VirtualFile file = vcsContext.getSelectedFiles()[0];
final Project project = vcsContext.getProject();
final AbstractVcs vcs = ProjectLevelVcsManager.getInstance(project).getVcsFor(file);
final VcsHistoryProvider vcsHistoryProvider = vcs.getVcsHistoryProvider();
new VcsHistoryProviderBackgroundableProxy(vcs, vcsHistoryProvider, vcs.getDiffProvider()).
createSessionFor(vcs.getKeyInstanceMethod(), new FilePathImpl(file),
new Consumer<VcsHistorySession>() {
public void consume(VcsHistorySession session) {
if (session == null) return;
final List<VcsFileRevision> revisions = session.getRevisionList();
final HistoryAsTreeProvider treeHistoryProvider = session.getHistoryAsTreeProvider();
if (treeHistoryProvider != null) {
showTreePopup(treeHistoryProvider.createTreeOn(revisions), file, project, vcs.getDiffProvider());
}
else {
showListPopup(revisions, project, new Consumer<VcsFileRevision>() {
public void consume(final VcsFileRevision revision) {
DiffActionExecutor.showDiff(vcs.getDiffProvider(), revision.getRevisionNumber(), file, project,
VcsBackgroundableActions.COMPARE_WITH);
}
}, true);
}
}
}, VcsBackgroundableActions.COMPARE_WITH, false, null);
}
private static void showTreePopup(final List<TreeItem<VcsFileRevision>> roots, final VirtualFile file, final Project project, final DiffProvider diffProvider) {
final TreeTableView treeTable = new TreeTableView(new ListTreeTableModelOnColumns(new TreeNodeAdapter(null, null, roots),
new ColumnInfo[]{BRANCH_COLUMN, REVISION_COLUMN,
DATE_COLUMN, AUTHOR_COLUMN}));
Runnable runnable = new Runnable() {
public void run() {
int index = treeTable.getSelectionModel().getMinSelectionIndex();
if (index == -1) {
return;
}
VcsFileRevision revision = getRevisionAt(treeTable, index);
if (revision != null) {
DiffActionExecutor.showDiff(diffProvider, revision.getRevisionNumber(), file, project, VcsBackgroundableActions.COMPARE_WITH);
}
}
};
treeTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
new PopupChooserBuilder(treeTable).
setTitle(VcsBundle.message("lookup.title.vcs.file.revisions")).
setItemChoosenCallback(runnable).
setSouthComponent(createCommentsPanel(treeTable)).
setResizable(true).
setDimensionServiceKey("Vcs.CompareWithSelectedRevision.Popup").
createPopup().
showCenteredInCurrentWindow(project);
final int lastRow = treeTable.getRowCount() - 1;
if (lastRow < 0) return;
treeTable.getSelectionModel().addSelectionInterval(lastRow, lastRow);
treeTable.scrollRectToVisible(treeTable.getCellRect(lastRow, 0, true));
}
@Nullable private static VcsFileRevision getRevisionAt(final TreeTableView treeTable, final int index) {
final List items = treeTable.getItems();
if (items.size() <= index) {
return null;
} else {
return ((TreeNodeAdapter)items.get(index)).getRevision();
}
}
private static JPanel createCommentsPanel(final TreeTableView treeTable) {
JPanel panel = new JPanel(new BorderLayout());
final JTextArea textArea = createTextArea();
treeTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
final int index = treeTable.getSelectionModel().getMinSelectionIndex();
if (index == -1) {
textArea.setText("");
} else {
final VcsFileRevision revision = getRevisionAt(treeTable, index);
if (revision != null) {
textArea.setText(revision.getCommitMessage());
} else {
textArea.setText("");
}
}
}
});
final JScrollPane textScrollPane = ScrollPaneFactory.createScrollPane(textArea);
panel.add(textScrollPane, BorderLayout.CENTER);
textScrollPane.setBorder(IdeBorderFactory.createTitledBorder(VcsBundle.message("border.selected.revision.commit.message"), false
));
return panel;
}
private static JTextArea createTextArea() {
final JTextArea textArea = new JTextArea();
textArea.setRows(5);
textArea.setEditable(false);
textArea.setWrapStyleWord(true);
textArea.setLineWrap(true);
return textArea;
}
public static void showListPopup(final List<VcsFileRevision> revisions, final Project project, final Consumer<VcsFileRevision> selectedRevisionConsumer,
final boolean showComments) {
ColumnInfo[] columns = new ColumnInfo[] { REVISION_TABLE_COLUMN, DATE_TABLE_COLUMN, AUTHOR_TABLE_COLUMN };
for(VcsFileRevision revision: revisions) {
if (revision.getBranchName() != null) {
columns = new ColumnInfo[] { REVISION_TABLE_COLUMN, BRANCH_TABLE_COLUMN, DATE_TABLE_COLUMN, AUTHOR_TABLE_COLUMN };
break;
}
}
final TableView<VcsFileRevision> table = new TableView<VcsFileRevision>(new ListTableModel<VcsFileRevision>(columns, revisions, 0));
table.setShowHorizontalLines(false);
table.setTableHeader(null);
Runnable runnable = new Runnable() {
public void run() {
VcsFileRevision revision = table.getSelectedObject();
if (revision != null) {
selectedRevisionConsumer.consume(revision);
}
}
};
if (table.getModel().getRowCount() == 0) {
table.clearSelection();
}
new SpeedSearchBase<TableView>(table) {
protected int getSelectedIndex() {
return table.getSelectedRow();
}
@Override
protected int convertIndexToModel(int viewIndex) {
return table.convertRowIndexToModel(viewIndex);
}
protected Object[] getAllElements() {
return revisions.toArray();
}
protected String getElementText(Object element) {
VcsFileRevision revision = (VcsFileRevision) element;
return revision.getRevisionNumber().asString() + " " + revision.getBranchName() + " " + revision.getAuthor();
}
protected void selectElement(Object element, String selectedText) {
VcsFileRevision revision = (VcsFileRevision) element;
TableUtil.selectRows(myComponent, new int[] {myComponent.convertRowIndexToView(revisions.indexOf(revision))});
TableUtil.scrollSelectionToVisible(myComponent);
}
};
table.setMinimumSize(new Dimension(300, 50));
final PopupChooserBuilder builder = new PopupChooserBuilder(table);
if (showComments) {
builder.setSouthComponent(createCommentsPanel(table));
}
builder.setTitle(VcsBundle.message("lookup.title.vcs.file.revisions")).
setItemChoosenCallback(runnable).
setResizable(true).
setDimensionServiceKey("Vcs.CompareWithSelectedRevision.Popup").setMinSize(new Dimension(300, 300));
final JBPopup popup = builder.createPopup();
popup.showCenteredInCurrentWindow(project);
}
private static JPanel createCommentsPanel(final TableView<VcsFileRevision> table) {
final JTextArea textArea = createTextArea();
table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
final VcsFileRevision revision = table.getSelectedObject();
if (revision == null) {
textArea.setText("");
} else {
textArea.setText(revision.getCommitMessage());
textArea.select(0, 0);
}
}
});
JPanel jPanel = new JPanel(new BorderLayout());
final JScrollPane textScrollPane = ScrollPaneFactory.createScrollPane(textArea);
textScrollPane.setBorder(IdeBorderFactory.createTitledBorder(VcsBundle.message("border.selected.revision.commit.message"), false
));
jPanel.add(textScrollPane, BorderLayout.SOUTH);
jPanel.setPreferredSize(new Dimension(300, 100));
return jPanel;
}
private static class TreeNodeAdapter extends DefaultMutableTreeNode {
private final TreeItem<VcsFileRevision> myRevision;
public TreeNodeAdapter(TreeNodeAdapter parent, TreeItem<VcsFileRevision> revision, List<TreeItem<VcsFileRevision>> children) {
if (parent != null) {
parent.add(this);
}
myRevision = revision;
for (TreeItem<VcsFileRevision> treeItem : children) {
new TreeNodeAdapter(this, treeItem, treeItem.getChildren());
}
}
public VcsFileRevision getRevision() {
return myRevision.getData();
}
}
}