/*
* Copyright 2000-2010 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 org.community.intellij.plugins.communitycase.checkout.branches;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.RemoteRevisionsCache;
import com.intellij.openapi.vcs.changes.ui.ChangeNodeDecorator;
import com.intellij.openapi.vcs.changes.ui.ChangesBrowserNode;
import com.intellij.openapi.vcs.changes.ui.ChangesTreeList;
import com.intellij.openapi.vcs.changes.ui.TreeModelBuilder;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.ColoredTableCellRenderer;
import com.intellij.ui.DocumentAdapter;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.ui.table.JBTable;
import com.intellij.util.ui.AbstractTableCellEditor;
import org.community.intellij.plugins.communitycase.Branch;
import org.community.intellij.plugins.communitycase.Util;
import org.community.intellij.plugins.communitycase.validators.BranchNameValidator;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.plaf.basic.BasicComboBoxRenderer;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.tree.DefaultTreeModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.*;
import java.util.List;
/**
* The switch branches dialog
*/
public class SwitchBranchesDialog extends DialogWrapper {
/**
* The prefix for remote references
*/
public static final String REMOTES_PREFIX = "remotes/";
/**
* The branch configuration name text field
*/
private JTextField myNameTextField;
/**
* The changes panel
*/
private JPanel myChangesPanel;
/**
* The root panel
*/
private JPanel myRoot;
/**
* The branches table
*/
private JBTable myBranchesTable;
/**
* Changes to transfer to new configurations label
*/
private JLabel myChangesLabel;
/**
* The changes tree
*/
private final ChangesTreeList<Change> myChangesTree;
/**
* The project to use
*/
private final Project myProject;
/**
* The target branch configuration
*/
private final BranchConfiguration myTarget;
/**
* The configuration settings object
*/
private final BranchConfigurations myConfig;
/**
* If true, the dialog was invoked to modify the current configuration
*/
private final boolean myModify;
/**
* The list of branches to use
*/
private final List<BranchDescriptor> myBranches;
/**
* The existing configuration names (used to simplify validation)
*/
private Set<String> myExistingConfigNames;
/**
* Base project directory
*/
private File myBaseFile;
/**
* The constructor
*
* @param project the project
* @param target the target configuration
* @param allChanges the all changes
* @param roots the collection of roots
* @param remoteBranch the remote branch
* @param config the configuration
* @param isModify the modify flag
* @throws VcsException if there is a problem with detecting the current state
*/
protected SwitchBranchesDialog(Project project,
final BranchConfiguration target,
final List<Change> allChanges,
List<VirtualFile> roots,
String remoteBranch, final BranchConfigurations config, boolean isModify) throws VcsException {
super(project, true);
setTitle(isModify ? "Modify Branch Configuration" : "Checkout Branch Configuration");
assert (remoteBranch == null) || (target == null) : "There should be no target for remote branch";
myTarget = target;
myConfig = config;
myModify = isModify;
myProject = project;
VirtualFile baseDir = project.getBaseDir();
myBaseFile = baseDir == null ? null : new File(baseDir.getPath());
myExistingConfigNames = myConfig.getConfigurationNames();
myChangesTree = new ChangesTreeList<Change>(myProject,
Collections.<Change>emptyList(),
!myModify,
true,
null,
RemoteRevisionsCache.getInstance(project).getChangesNodeDecorator()) {
protected DefaultTreeModel buildTreeModel(final List<Change> changes, ChangeNodeDecorator changeNodeDecorator) {
TreeModelBuilder builder = new TreeModelBuilder(myProject, false);
return builder.buildModel(changes, changeNodeDecorator);
}
protected List<Change> getSelectedObjects(final ChangesBrowserNode<Change> node) {
return node.getAllChangesUnder();
}
@Nullable
protected Change getLeadSelectedObject(final ChangesBrowserNode node) {
final Object o = node.getUserObject();
if (o instanceof Change) {
return (Change)o;
}
return null;
}
};
if (remoteBranch != null) {
myBranches = prepareBranchesForRemote(remoteBranch, roots);
}
else {
myBranches = prepareBranchDescriptors(target, roots);
}
Collections.sort(myBranches, new Comparator<BranchDescriptor>() {
@Override
public int compare(BranchDescriptor o1, BranchDescriptor o2) {
return o1.getRoot().compareTo(o2.getRoot());
}
});
if (target == null) {
myNameTextField.setText(generateNewConfigurationName());
}
else {
myNameTextField.setText(target.getName());
}
myChangesTree.setChangesToDisplay(allChanges);
myChangesTree.setIncludedChanges(Collections.<Change>emptyList());
myChangesPanel.add(myChangesTree, BorderLayout.CENTER);
myChangesLabel.setLabelFor(myChangesTree);
if (myModify) {
myChangesLabel.setText("Changes in the current configuration");
}
RootTableModel tableModel = new RootTableModel();
myBranchesTable.setModel(tableModel);
myBranchesTable.setDefaultRenderer(Pair.class, new PairTableRenderer());
final TableColumnModel columns = myBranchesTable.getColumnModel();
final PairTableRenderer renderer = new PairTableRenderer();
for (Enumeration<TableColumn> cs = columns.getColumns(); cs.hasMoreElements();) {
cs.nextElement().setCellRenderer(renderer);
}
TableColumn revisionColumn = columns.getColumn(RootTableModel.REVISION_COLUMN);
revisionColumn.setCellEditor(new ReferenceEditor());
TableColumn branchColumn = columns.getColumn(RootTableModel.NEW_BRANCH_COLUMN);
branchColumn.setCellEditor(new BranchNameEditor());
myNameTextField.getDocument().addDocumentListener(new DocumentAdapter() {
@Override
protected void textChanged(DocumentEvent e) {
verify();
}
});
tableModel.addTableModelListener(new TableModelListener() {
@Override
public void tableChanged(TableModelEvent e) {
verify();
}
});
verify();
init();
}
/**
* The show configuration dialog
*
* @param project the project to use
* @param target the target configuration
* @param allChanges the collection of changes
* @param roots the vcs roots
* @param remoteBranch the remote branch
* @param config the configuration
* @param isModify the modify mode flag
* @return the dialog result object
* @throws VcsException if there is a problem with accessing
*/
@Nullable
public static Result showDialog(Project project,
@Nullable BranchConfiguration target,
final List<Change> allChanges,
List<VirtualFile> roots,
@Nullable String remoteBranch,
final BranchConfigurations config,
boolean isModify) throws VcsException {
SwitchBranchesDialog d = new SwitchBranchesDialog(project, target, allChanges, roots, remoteBranch, config, isModify);
d.show();
if (d.isOK()) {
return d.createResult();
}
else {
return null;
}
}
/**
* @return create dialog result object basing on the dialog state
*/
private Result createResult() {
Result rc = new Result();
String name = myNameTextField.getText().trim();
if (myTarget == null) {
rc.target = myConfig.createConfiguration(name);
}
else {
rc.target = myTarget;
rc.target.setName(name);
}
rc.changes = new ArrayList<Change>(myChangesTree.getIncludedChanges());
for (BranchDescriptor d : myBranches) {
if (d.root != null) {
if (!StringUtil.isEmpty(d.newBranchName)) {
final String ref = d.referenceToCheckout.trim();
rc.referencesToUse.put(d.root, Pair.create(ref, d.referencesToSelect.contains(ref)));
rc.target.setReference(d.root.getPath(), d.newBranchName.trim());
rc.checkoutNeeded.add(d.root);
}
else {
String ref = d.referenceToCheckout.trim();
if (!d.referencesToSelect.contains(ref)) {
ref = myConfig.detectTag(d.root, ref);
}
rc.target.setReference(d.root.getPath(), ref);
if (!d.referenceToCheckout.equals(d.currentReference)) {
rc.checkoutNeeded.add(d.root);
}
}
}
}
return rc;
}
/**
* Verify dialog state
*/
private void verify() {
String text = myNameTextField.getText().trim();
if (text.length() == 0) {
setError("Empty configuration name is not allowed.");
return;
}
else if (myTarget != null && text.equals(myTarget.getName())) {
}
else if (myExistingConfigNames.contains(text)) {
setError("There is another configuration with the same name");
return;
}
for (BranchDescriptor d : myBranches) {
switch (d.status) {
case BRANCH_NAME_EXISTS:
setError("Duplicate branch name for root " + d.getRoot());
return;
case INVALID_BRANCH_NAME:
setError("Invalid branch name for root " + d.getRoot());
return;
case BAD_REVISION:
setError("Invalid revision for root " + d.getRoot());
return;
case MISSING_REVISION:
setError("The revision must be specified for root " + d.getRoot());
return;
case CHECKOUT_NEEDED:
case NO_ACTION:
case REMOVED_ROOT:
break;
default:
throw new RuntimeException("Unexpected status: " + d.status);
}
}
setError(null);
}
private void setError(String s) {
setErrorText(s);
setOKActionEnabled(s == null);
}
/**
* Generate new configuration name basing on descriptor
*
* @return the generated configuration name
*/
private String generateNewConfigurationName() {
String name = null;
for (BranchDescriptor d : myBranches) {
if (d.newBranchName != null) {
name = d.newBranchName;
break;
}
if (d.existingBranches.contains(d.currentReference)) {
name = d.currentReference;
}
}
if (name == null) {
name = "untitled";
}
if (myExistingConfigNames.contains(name)) {
for (int i = 2; i < Integer.MAX_VALUE; i++) {
String t = name + i;
if (!myExistingConfigNames.contains(t)) {
name = t;
break;
}
}
}
return name;
}
/**
* Prepare branches for the case of remote checkout
*
* @param remoteBranch the remote branch to checkout
* @param roots the collection of vcs roots
* @return the list of descriptors for the remote
* @throws VcsException if failed
*/
private List<BranchDescriptor> prepareBranchesForRemote(String remoteBranch, List<VirtualFile> roots)
throws VcsException {
assert roots.size() > 0;
List<BranchDescriptor> rc = new ArrayList<BranchDescriptor>();
HashSet<String> allBranches = new HashSet<String>();
allBranches.addAll(myConfig.getConfigurationNames());
final String qualifiedBranch = "remotes/" + remoteBranch;
String firstRemote = remoteBranch.endsWith("/HEAD") ? null : qualifiedBranch;
for (VirtualFile root : roots) {
BranchDescriptor d = new BranchDescriptor();
d.root = root;
d.currentReference = myConfig.describeRoot(root);
if (firstRemote == null) {
firstRemote = resolveHead(qualifiedBranch, d.root.getPath());
}
d.referenceToCheckout = qualifiedBranch;
Branch.listAsStrings(myProject, root, false, true, d.existingBranches, null);
Branch.listAsStrings(myProject, root, true, true, d.referencesToSelect, null);
allBranches.addAll(d.existingBranches);
rc.add(d);
}
String candidate;
if (firstRemote == null) {
candidate = "untitled";
}
else {
int p = firstRemote.indexOf('/', REMOTES_PREFIX.length() + 1);
assert p > 0 && p < firstRemote.length() - 1 : "Unexpected format for remote branch: " + firstRemote;
candidate = firstRemote.substring(p + 1);
}
String actual = null;
if (!allBranches.contains(candidate)) {
actual = candidate;
}
else {
for (int i = 2; i < Integer.MAX_VALUE; i++) {
String t = candidate + i;
if (!allBranches.contains(t)) {
actual = t;
break;
}
}
assert actual != null : "Unexpected number of branches: " + remoteBranch;
}
for (BranchDescriptor d : rc) {
d.newBranchName = actual;
d.updateStatus();
}
return rc;
}
/**
* Get candidate new branch name from remote branch
*
* @param value the value used to guess new reference
* @param d a description to use
* @return the candidate branch name
*/
@Nullable
private String getCandidateLocal(String value, BranchDescriptor d) {
if (StringUtil.isEmpty(value) || !value.startsWith(REMOTES_PREFIX)) {
return null;
}
int p = value.indexOf('/', REMOTES_PREFIX.length() + 1);
String candidate = null;
if (p != -1) {
String c = value.substring(p + 1);
if (!d.existingBranches.contains(c)) {
candidate = c;
}
else {
for (int i = 2; i < Integer.MAX_VALUE; i++) {
String cn = c + i;
if (!d.existingBranches.contains(cn)) {
candidate = cn;
break;
}
}
}
if ("HEAD".equals(candidate)) {
final String rootPath = d.root.getPath();
String newRef = resolveHead(value, rootPath);
candidate = newRef == null ? null : getCandidateLocal(newRef, d);
}
}
return candidate;
}
/**
* Resolve remote had reference
*
* @param value the reference to resolve
* @param rootPath the root path
* @return the resolved reference or null
*/
@Nullable
private static String resolveHead(String value, String rootPath) {
if (!value.startsWith("remotes/")) {
return null;
}
String newRef;
try {
final String refText =
new String(FileUtil.loadFileText(new File(rootPath, "./refs/" + value), Util.UTF8_ENCODING)).trim();
String refsPrefix = "ref: refs/";
if (refText.endsWith("/HEAD") || !refText.startsWith(refsPrefix)) {
newRef = null;
}
else {
newRef = refText.substring(refsPrefix.length());
}
}
catch (Exception e) {
newRef = null;
}
return newRef;
}
/**
* Prepare branch descriptors for existing configuration
*
* @param target the target
* @param roots the vcs root
* @return the list of branch descriptors
* @throws VcsException in case of error
*/
private List<BranchDescriptor> prepareBranchDescriptors(
BranchConfiguration target,
List<VirtualFile> roots)
throws VcsException {
Map<String, String> map = target == null ? Collections.<String, String>emptyMap() : target.getReferences();
List<BranchDescriptor> rc = new ArrayList<BranchDescriptor>();
for (VirtualFile root : roots) {
BranchDescriptor d = new BranchDescriptor();
d.root = root;
d.storedReference = map.remove(root.getPath());
if (d.storedReference != null) {
d.storedRoot = d.root.getPath();
}
d.currentReference = myConfig.describeRoot(root);
if (d.storedReference != null && !myModify) {
d.referenceToCheckout = d.storedReference;
}
else {
d.referenceToCheckout = d.currentReference;
}
Branch.listAsStrings(myProject, root, false, true, d.existingBranches, null);
Branch.listAsStrings(myProject, root, true, true, d.referencesToSelect, null);
d.updateStatus();
rc.add(d);
}
for (Map.Entry<String, String> m : map.entrySet()) {
String root = m.getKey();
String ref = m.getValue();
BranchDescriptor d = new BranchDescriptor();
d.storedReference = ref;
d.storedRoot = root;
d.referenceToCheckout = ref;
d.updateStatus();
rc.add(d);
}
return rc;
}
/**
* {@inheritDoc}
*/
@Override
protected JComponent createCenterPanel() {
return myRoot;
}
/**
* The table model that displays mapping for the vcs roots
*/
class RootTableModel extends AbstractTableModel {
/**
* The vcs root
*/
static final int ROOT_COLUMN = 0;
/**
* The revision
*/
static final int REVISION_COLUMN = 1;
/**
* The name of branch to checkout
*/
static final int NEW_BRANCH_COLUMN = 2;
/**
* The status
*/
static final int STATUS_COLUMN = 3;
/**
* The total number of columns
*/
static final int COLUMNS = STATUS_COLUMN + 1;
/**
* {@inheritDoc}
*/
@Override
public int getRowCount() {
return myBranches.size();
}
/**
* {@inheritDoc}
*/
@Override
public int getColumnCount() {
return COLUMNS;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
BranchDescriptor d = myBranches.get(rowIndex);
if (d.root == null) {
return false;
}
return columnIndex == REVISION_COLUMN || columnIndex == NEW_BRANCH_COLUMN;
}
/**
* {@inheritDoc}
*/
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
String t = (String)aValue;
BranchDescriptor d = myBranches.get(rowIndex);
if (d.root == null) {
return;
}
if (columnIndex == REVISION_COLUMN) {
String currentCandidate = getCandidateLocal(d.referenceToCheckout, d);
boolean isCurrentMatchCandidate = currentCandidate != null && currentCandidate.equals(d.newBranchName);
d.referenceToCheckout = t;
if ((StringUtil.isEmpty(d.newBranchName) || isCurrentMatchCandidate) &&
t.startsWith(REMOTES_PREFIX) &&
d.referencesToSelect.contains(t)) {
String candidate = getCandidateLocal(t, d);
if (candidate != null) {
d.newBranchName = candidate;
}
}
}
else if (columnIndex == NEW_BRANCH_COLUMN) {
d.newBranchName = t;
}
d.updateStatus();
fireTableRowsUpdated(rowIndex, rowIndex);
}
/**
* {@inheritDoc}
*/
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
BranchDescriptor d = myBranches.get(rowIndex);
switch (columnIndex) {
case ROOT_COLUMN:
return Pair.create(d.getRoot(), d.root != null);
case REVISION_COLUMN:
return Pair.create(d.referenceToCheckout, d.isReferenceValid);
case NEW_BRANCH_COLUMN:
return Pair.create(d.newBranchName == null ? "" : d.newBranchName, d.isNewBranchValid);
case STATUS_COLUMN:
switch (d.status) {
case INVALID_BRANCH_NAME:
return Pair.create("Invalid new branch name", false);
case BAD_REVISION:
return Pair.create("Invalid revision", false);
case MISSING_REVISION:
return Pair.create("Missing revision", false);
case CHECKOUT_NEEDED:
return Pair.create("Checkout", true);
case REMOVED_ROOT:
return Pair.create("Removed root", true);
case NO_ACTION:
return Pair.create("", true);
case BRANCH_NAME_EXISTS:
return Pair.create("Branch name exists", false);
default:
throw new IllegalStateException("Unknown status:" + d.status);
}
default:
throw new IllegalStateException("Unknown column: " + columnIndex);
}
}
/**
* {@inheritDoc}
*/
@Override
public String getColumnName(int column) {
switch (column) {
case ROOT_COLUMN:
return "Vcs Root";
case REVISION_COLUMN:
return "Checkout";
case NEW_BRANCH_COLUMN:
return "As New Branch";
case STATUS_COLUMN:
return "Status";
default:
throw new IllegalStateException("Unknown column: " + column);
}
}
}
/**
* The object representing row entry
*/
class BranchDescriptor {
/**
* The root to checkout, if null means that the old root is missing.
*/
VirtualFile root;
/**
* Stored root path
*/
String storedRoot;
/**
* Stored reference
*/
String storedReference;
/**
* The commit expression to checkout
*/
String referenceToCheckout;
/**
* if true, branch name is valid
*/
boolean isReferenceValid;
/**
* True if commit expression is valid
*/
RootStatus status;
/**
* The name of of branch
*/
String newBranchName;
/**
* if true, branch name is valid
*/
boolean isNewBranchValid;
/**
* The current type
*/
String currentReference;
/**
* The existing branches
*/
HashSet<String> existingBranches = new HashSet<String>();
/**
* The existing branches
*/
TreeSet<String> referencesToSelect = new TreeSet<String>();
/**
* Update status of the entry
*/
void updateStatus() { //todo wc check that this still works after removing RevisionNumber.resolve
if (root == null) {
status = RootStatus.REMOVED_ROOT;
return;
}
status = branchNameStatus(newBranchName);
isNewBranchValid = status == null;
isReferenceValid = true;
if (referenceToCheckout == null) {
status = RootStatus.MISSING_REVISION;
isReferenceValid = false;
return;
}
if (status == null) {
status = StringUtil.isEmpty(newBranchName) && currentReference.equals(storedReference)
? RootStatus.NO_ACTION
: RootStatus.CHECKOUT_NEEDED;
}
}
/**
* Get branch name status
*
* @param name the name to check
* @return null if branch name is ok, or status describing the problem
*/
@Nullable
private RootStatus branchNameStatus(final String name) {
RootStatus b = null;
if (!StringUtil.isEmpty(name)) {
if (!BranchNameValidator.INSTANCE.checkInput(name)) {
b = RootStatus.INVALID_BRANCH_NAME;
}
if (existingBranches.contains(name)) {
b = RootStatus.BRANCH_NAME_EXISTS;
}
}
return b;
}
public String getRoot() {
String path = root == null ? storedRoot : root.getPath();
String relative = myBaseFile == null ? path : FileUtil.getRelativePath(myBaseFile, new File(path));
return relative == null ? path : relative;
}
}
/**
* The root status
*/
enum RootStatus {
/**
* The checkout is needed for the vcs root. Non-error status.
*/
CHECKOUT_NEEDED,
/**
* No action needed for vcs root. Non-error status.
*/
NO_ACTION,
/**
* The branch information does not represents a vcs root anymore. Non-error status.
*/
REMOVED_ROOT,
/**
* The bad revision expression
*/
MISSING_REVISION,
/**
* The revision expression is invalid or could not be evaluated
*/
BAD_REVISION,
/**
* The bad name for the branch
*/
INVALID_BRANCH_NAME,
/**
* The bad name for the branch
*/
BRANCH_NAME_EXISTS
}
/**
* Pair text renderer
*/
static class PairTableRenderer extends ColoredTableCellRenderer {
@Override
protected void customizeCellRenderer(JTable table, Object value, boolean selected, boolean hasFocus, int row, int column) {
@SuppressWarnings({"unchecked"}) Pair<String, Boolean> p = (Pair<String, Boolean>)value;
String t = p.first == null ? "" : p.first;
if (p.second) {
append(t);
}
else {
append(t, SimpleTextAttributes.ERROR_ATTRIBUTES);
}
}
}
/**
* The editor for references
*/
class ReferenceEditor extends AbstractTableCellEditor {
/**
* The root panel
*/
private final JPanel myPanel = new JPanel(new GridBagLayout());
/**
* Combobox for the panel
*/
private final JComboBox myComboBox = new JComboBox();
/**
* The constructor
*/
private ReferenceEditor() {
myComboBox.setEditable(true);
myComboBox.setRenderer(new BasicComboBoxRenderer());
myComboBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
stopCellEditing();
}
});
myPanel.add(myComboBox,
new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0),
0,
0));
}
/**
* {@inheritDoc}
*/
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
BranchDescriptor d = myBranches.get(row);
myComboBox.removeAllItems();
for (String s : d.referencesToSelect) {
myComboBox.addItem(s);
}
myComboBox.setSelectedItem(d.referenceToCheckout);
return myPanel;
}
/**
* {@inheritDoc}
*/
public Object getCellEditorValue() {
return myComboBox.getSelectedItem();
}
}
/**
* The editor for branch names
*/
class BranchNameEditor extends AbstractTableCellEditor {
/**
* The root panel
*/
private final JPanel myPanel = new JPanel(new GridBagLayout());
/**
* Combobox for the panel
*/
private final JTextField myTextField = new JTextField();
/**
* The values that considered invalid
*/
private Set<String> myInvalidValues;
/**
* Default foregorund color (likely black one)
*/
private Color myDefaultForeground;
/**
* The constructor
*/
private BranchNameEditor() {
myDefaultForeground = myTextField.getForeground();
myTextField.getDocument().addDocumentListener(new DocumentAdapter() {
@Override
protected void textChanged(DocumentEvent e) {
String s = myTextField.getText();
if ((myInvalidValues == null || !myInvalidValues.contains(s)) &&
(s.length() == 0 || BranchNameValidator.INSTANCE.checkInput(s))) {
myTextField.setForeground(myDefaultForeground);
}
else {
myTextField.setForeground(Color.RED);
}
}
});
myTextField.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
stopCellEditing();
}
});
myPanel.add(myTextField,
new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0),
0,
0));
}
/**
* {@inheritDoc}
*/
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
BranchDescriptor d = myBranches.get(row);
myInvalidValues = d.existingBranches;
myTextField.setText(d.newBranchName == null ? "" : d.newBranchName);
return myPanel;
}
/**
* {@inheritDoc}
*/
@Nullable
public Object getCellEditorValue() {
String s = myTextField.getText().trim();
return s.length() == 0 ? null : s;
}
}
/**
* The result of the dialog
*/
public static class Result {
/**
* The roots for which checkout is needed
*/
Collection<VirtualFile> checkoutNeeded = new ArrayList<VirtualFile>();
/**
* The set of selected changes to transfer to new configuration
*/
List<Change> changes;
/**
* References to use for new branches
*/
HashMap<VirtualFile, Pair<String, Boolean>> referencesToUse = new HashMap<VirtualFile, Pair<String, Boolean>>();
/**
* The target configuration
*/
BranchConfiguration target;
}
}