blob: 83d895b93ffc9bf8ff296a21942054920e65360a [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 org.jetbrains.idea.svn.dialogs;
import com.intellij.CommonBundle;
import com.intellij.icons.AllIcons;
import com.intellij.ide.CommonActionsManager;
import com.intellij.ide.TreeExpander;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileChooser.FileChooser;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.ide.CopyPasteManager;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.vcs.*;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.ui.ChangeListViewerDialog;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.ui.PopupHandler;
import com.intellij.util.IconUtil;
import com.intellij.util.WaitForProgressToShow;
import com.intellij.vcsUtil.VcsUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.SvnApplicationSettings;
import org.jetbrains.idea.svn.SvnBundle;
import org.jetbrains.idea.svn.SvnUtil;
import org.jetbrains.idea.svn.SvnVcs;
import org.jetbrains.idea.svn.actions.BrowseRepositoryAction;
import org.jetbrains.idea.svn.browse.DirectoryEntry;
import org.jetbrains.idea.svn.checkout.SvnCheckoutProvider;
import org.jetbrains.idea.svn.commandLine.SvnBindException;
import org.jetbrains.idea.svn.dialogs.browser.*;
import org.jetbrains.idea.svn.dialogs.browserCache.Expander;
import org.jetbrains.idea.svn.dialogs.browserCache.KeepingExpandedExpander;
import org.jetbrains.idea.svn.dialogs.browserCache.SyntheticWorker;
import org.jetbrains.idea.svn.history.SvnRepositoryLocation;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc2.SvnTarget;
import javax.swing.*;
import javax.swing.tree.TreeNode;
import java.awt.*;
import java.awt.datatransfer.StringSelection;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.io.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class RepositoryBrowserDialog extends DialogWrapper {
private static final Logger LOG = Logger.getInstance(RepositoryBrowserDialog.class);
private final Project myProject;
protected final SvnVcs myVCS;
private RepositoryBrowserComponent myRepositoryBrowser;
private final DeleteAction myDeleteAction;
private AnAction copyUrlAction;
private AnAction mkDirAction;
private final boolean myShowFiles;
@NonNls public static final String PLACE_TOOLBAR = "RepositoryBrowser.Toolbar";
@NonNls private static final String PLACE_MENU = "RepositoryBrowser.Menu";
private final String myRepositoriesLabelText;
protected JLabel myRepositoriesLabel;
public RepositoryBrowserDialog(Project project) {
this(project, true, null);
}
public RepositoryBrowserDialog(Project project, final boolean showFiles, @Nullable final String repositoriesLabelText) {
super(project, true);
myRepositoriesLabelText = repositoriesLabelText == null ? "Repositories:" : repositoriesLabelText;
myShowFiles = showFiles;
myProject = project;
myVCS = SvnVcs.getInstance(project);
setTitle("SVN Repository Browser");
setResizable(true);
setOKButtonText(CommonBundle.getCloseButtonText());
getHelpAction().setEnabled(true);
Disposer.register(project, getDisposable());
myDeleteAction = new DeleteAction(getRepositoryBrowser());
init();
}
protected String getHelpId() {
return "reference.svn.repository";
}
@NotNull
protected Action[] createActions() {
return new Action[] {getOKAction(), getHelpAction()};
}
protected String getDimensionServiceKey() {
return "svn.repositoryBrowser";
}
protected boolean showImportAction() {
return true;
}
public JComponent createToolbar(final boolean horizontal, final AnAction... additionalActions) {
DefaultActionGroup group = new DefaultActionGroup();
final RepositoryBrowserComponent browser = getRepositoryBrowser();
group.add(new AddLocationAction(browser));
group.add(new EditLocationAction(browser));
group.add(new DiscardLocationAction(browser));
group.add(new DetailsAction());
group.addSeparator();
final RefreshAction refreshAction = new RefreshAction(browser);
refreshAction.registerCustomShortcutSet(CommonShortcuts.getRerun(), browser);
group.add(refreshAction);
copyUrlAction = new CopyUrlAction();
copyUrlAction.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT,
InputEvent.CTRL_MASK | InputEvent.CTRL_DOWN_MASK |
InputEvent.ALT_MASK | InputEvent.ALT_DOWN_MASK)), browser);
mkDirAction = new MkDirAction(browser);
mkDirAction.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT,
InputEvent.ALT_MASK | InputEvent.ALT_DOWN_MASK)), browser);
AnAction action = CommonActionsManager.getInstance().createCollapseAllAction(new TreeExpander() {
public void expandAll() {
}
public boolean canExpand() {
return false;
}
public void collapseAll() {
JTree tree = browser.getRepositoryTree();
int row = tree.getRowCount() - 1;
while (row >= 0) {
tree.collapseRow(row);
row--;
}
}
public boolean canCollapse() {
return true;
}
}, browser);
group.add(action);
if ((additionalActions != null) || (! horizontal)) {
group.addSeparator();
}
if (additionalActions != null) {
for (AnAction anAction : additionalActions) {
group.add(anAction);
}
}
if (! horizontal) {
group.add(new CloseToolWindowAction());
}
return ActionManager.getInstance().createActionToolbar(PLACE_TOOLBAR, group, horizontal).getComponent();
}
protected JPopupMenu createPopup(boolean toolWindow) {
DefaultActionGroup group = new DefaultActionGroup();
DefaultActionGroup newGroup = new DefaultActionGroup("_New", true);
final RepositoryBrowserComponent browser = getRepositoryBrowser();
newGroup.add(new AddLocationAction(browser));
newGroup.add(new MkDirAction(browser));
group.add(newGroup);
group.addSeparator();
if (toolWindow) {
group.add(new OpenAction());
group.add(new HistoryAction());
}
group.add(new CheckoutAction());
group.add(new DiffAction());
group.add(new BrowseChangesAction());
group.addSeparator();
group.add(new ImportAction());
group.add(new ExportAction());
group.addSeparator();
group.add(new CopyOrMoveAction("Branch or Tag...", "copy.dialog.title", false));
group.add(new CopyOrMoveAction("_Move or Rename...", "move.dialog.title", true));
group.add(myDeleteAction);
group.add(copyUrlAction);
group.addSeparator();
group.add(new RefreshAction(browser));
group.add(new EditLocationAction(browser));
group.add(new DiscardLocationAction(browser));
ActionPopupMenu menu = ActionManager.getInstance().createActionPopupMenu(PLACE_MENU, group);
return menu.getComponent();
}
public JComponent createCenterPanel() {
JPanel parentPanel = new JPanel(new BorderLayout());
JPanel top = new JPanel();
final BoxLayout layout = new BoxLayout(top, BoxLayout.X_AXIS);
top.setLayout(layout);
myRepositoriesLabel = new JLabel(myRepositoriesLabelText);
top.add(myRepositoriesLabel);
top.add(createToolbar(true, null));
parentPanel.add(top, BorderLayout.NORTH);
JComponent panel = createBrowserComponent(false);
parentPanel.add(panel, BorderLayout.CENTER);
return parentPanel;
}
public JComponent createBrowserComponent(final boolean toolWindow) {
JPanel panel = new JPanel();
panel.setLayout(new GridBagLayout());
GridBagConstraints gc = new GridBagConstraints();
gc.gridx = 0;
gc.gridwidth = 1;
gc.gridy = 0;
gc.gridheight = 1;
gc.gridx = 0;
gc.gridwidth = 2;
gc.gridy += 1;
gc.gridheight = 1;
gc.weightx = 1;
gc.weighty = 1;
gc.fill = GridBagConstraints.BOTH;
gc.anchor = GridBagConstraints.WEST;
panel.add(getRepositoryBrowser(), gc);
gc.gridy += 1;
gc.weighty = 0;
gc.fill = GridBagConstraints.HORIZONTAL;
panel.add(new JLabel(), gc);
Collection<String> urls = SvnApplicationSettings.getInstance().getCheckoutURLs();
ArrayList<SVNURL> svnURLs = new ArrayList<SVNURL>();
for (final String url : urls) {
try {
svnURLs.add(SVNURL.parseURIEncoded(url));
}
catch (SVNException e) {
//
}
}
getRepositoryBrowser().setRepositoryURLs(svnURLs.toArray(new SVNURL[svnURLs.size()]), myShowFiles);
getRepositoryBrowser().getRepositoryTree().addMouseListener(new PopupHandler() {
@Override
public void invokePopup(Component comp, int x, int y) {
JTree tree = getRepositoryBrowser().getRepositoryTree();
int row = tree.getRowForLocation(x, y);
if (row >= 0) {
tree.setSelectionRow(row);
}
JPopupMenu popupMenu = createPopup(toolWindow);
if (popupMenu != null) {
popupMenu.show(comp, x, y);
}
}
});
return panel;
}
protected RepositoryBrowserComponent getRepositoryBrowser() {
if (myRepositoryBrowser == null) {
myRepositoryBrowser = new RepositoryBrowserComponent(SvnVcs.getInstance(myProject));
myRepositoryBrowser.setPreferredSize(new Dimension(300, 300));
}
return myRepositoryBrowser;
}
public void disposeRepositoryBrowser() {
if (myRepositoryBrowser != null) {
Disposer.dispose(myRepositoryBrowser);
myRepositoryBrowser = null;
}
}
protected void dispose() {
super.dispose();
disposeRepositoryBrowser();
}
public JComponent getPreferredFocusedComponent() {
return (JComponent) getRepositoryBrowser().getPreferredFocusedComponent();
}
public boolean shouldCloseOnCross() {
return true;
}
public boolean isOKActionEnabled() {
return true;
}
public String getSelectedURL() {
return getRepositoryBrowser().getSelectedURL();
}
@Nullable
protected RepositoryTreeNode getSelectedNode() {
return getRepositoryBrowser().getSelectedNode();
}
protected class HistoryAction extends AnAction {
public void update(AnActionEvent e) {
e.getPresentation().setText(SvnBundle.message("repository.browser.history.action"));
e.getPresentation().setDescription(SvnBundle.message("repository.browser.history.action"));
final RepositoryTreeNode node = getRepositoryBrowser().getSelectedNode();
e.getPresentation().setEnabled(node != null && node.getURL() != null && !myProject.isDefault());
}
public void actionPerformed(AnActionEvent e) {
final RepositoryTreeNode node = getSelectedNode();
if (node == null) {
return;
}
boolean isDirectory =
node.getUserObject() instanceof SVNURL || (node.getSVNDirEntry() != null && node.getSVNDirEntry().isDirectory());
String url = node.getURL().toDecodedString();
AbstractVcsHelper.getInstance(myProject)
.showFileHistory(myVCS.getVcsHistoryProvider(), VcsUtil.getFilePathOnNonLocal(url, isDirectory), myVCS, url);
node.reload(false);
}
}
public static class RefreshAction extends AnAction {
private final RepositoryBrowserComponent myBrowserComponent;
public RefreshAction(final RepositoryBrowserComponent browserComponent) {
super(SvnBundle.message("action.name.refresh"), SvnBundle.message("repository.browser.refresh.action"), AllIcons.Actions.Refresh);
myBrowserComponent = browserComponent;
}
public void update(AnActionEvent e) {
e.getPresentation().setText(SvnBundle.message("action.name.refresh"));
e.getPresentation().setDescription(SvnBundle.message("repository.browser.refresh.action"));
e.getPresentation().setIcon(AllIcons.Actions.Refresh);
e.getPresentation().setEnabled(myBrowserComponent.getSelectedNode() != null);
}
public void actionPerformed(AnActionEvent e) {
final RepositoryTreeNode selectedNode = myBrowserComponent.getSelectedNode();
if (selectedNode != null) {
selectedNode.reload(true);
}
}
}
protected static class AddLocationAction extends AnAction {
private final RepositoryBrowserComponent myBrowserComponent;
public AddLocationAction(final RepositoryBrowserComponent browserComponent) {
super(SvnBundle.message("repository.browser.add.location.menu.item"));
myBrowserComponent = browserComponent;
}
public void update(AnActionEvent e) {
if (e.getPlace().equals(PLACE_TOOLBAR)) {
e.getPresentation().setDescription(SvnBundle.message("repository.browser.add.location.action"));
e.getPresentation().setText(SvnBundle.message("repository.browser.add.location.action"));
e.getPresentation().setIcon(IconUtil.getAddIcon());
}
}
public void actionPerformed(AnActionEvent e) {
final SvnApplicationSettings settings = SvnApplicationSettings.getInstance();
final AddRepositoryLocationDialog dialog = new AddRepositoryLocationDialog(myBrowserComponent.getProject(), settings.getTypedUrlsListCopy());
dialog.show();
if (dialog.getExitCode() == DialogWrapper.OK_EXIT_CODE) {
final String url = dialog.getSelected();
if (url != null && url.length() > 0) {
settings.addTypedUrl(url);
settings.addCheckoutURL(url);
myBrowserComponent.addURL(url);
}
}
}
}
protected static class EditLocationAction extends AnAction {
@NotNull private final RepositoryBrowserComponent myBrowserComponent;
public EditLocationAction(@NotNull RepositoryBrowserComponent browserComponent) {
super(SvnBundle.message("repository.browser.edit.location.menu.item"));
myBrowserComponent = browserComponent;
}
public void update(AnActionEvent e) {
RepositoryTreeNode node = myBrowserComponent.getSelectedNode();
if (e.getPlace().equals(PLACE_TOOLBAR)) {
e.getPresentation().setDescription(SvnBundle.message("repository.browser.edit.location.menu.item"));
e.getPresentation().setText(SvnBundle.message("repository.browser.edit.location.menu.item"));
e.getPresentation().setIcon(AllIcons.Actions.EditSource);
}
e.getPresentation().setEnabled(node != null && node.getParent() instanceof RepositoryTreeRootNode);
}
public void actionPerformed(AnActionEvent e) {
RepositoryTreeNode node = myBrowserComponent.getSelectedNode();
if (node == null || (! (node.getParent() instanceof RepositoryTreeRootNode))) {
return;
}
final String oldUrl = node.getURL().toString();
final SvnApplicationSettings settings = SvnApplicationSettings.getInstance();
final AddRepositoryLocationDialog dialog =
new AddRepositoryLocationDialog(myBrowserComponent.getProject(), settings.getTypedUrlsListCopy()) {
@Override
protected String initText() {
return oldUrl;
}
@Override
public String getTitle() {
return SvnBundle.message("repository.browser.edit.location.dialog.title");
}
};
dialog.show();
if (dialog.getExitCode() == DialogWrapper.OK_EXIT_CODE) {
final String url = dialog.getSelected();
if (url != null && url.length() > 0) {
settings.addTypedUrl(url);
settings.removeCheckoutURL(oldUrl);
settings.addCheckoutURL(url);
myBrowserComponent.removeURL(oldUrl);
myBrowserComponent.addURL(url);
}
}
}
}
protected static class DiscardLocationAction extends AnAction {
private final RepositoryBrowserComponent myBrowserComponent;
public DiscardLocationAction(final RepositoryBrowserComponent browserComponent) {
super(SvnBundle.message("repository.browser.discard.location.action"), SvnBundle.message("repository.browser.discard.location.action"), AllIcons.General.Remove);
myBrowserComponent = browserComponent;
}
public void update(AnActionEvent e) {
RepositoryTreeNode node = myBrowserComponent.getSelectedNode();
e.getPresentation().setText(SvnBundle.message("repository.browser.discard.location.action"), true);
e.getPresentation().setIcon(AllIcons.General.Remove);
e.getPresentation().setEnabled(node != null && node.getParent() instanceof RepositoryTreeRootNode);
}
public void actionPerformed(AnActionEvent e) {
RepositoryTreeNode node = myBrowserComponent.getSelectedNode();
if (node == null) {
return;
}
SVNURL url = node.getURL();
if (url != null) {
int rc = Messages.showYesNoDialog(myBrowserComponent.getProject(), SvnBundle.message("repository.browser.discard.location.prompt", url.toString()),
SvnBundle.message("repository.browser.discard.location.title"), Messages.getQuestionIcon());
if (rc != Messages.YES) {
return;
}
SvnApplicationSettings.getInstance().removeCheckoutURL(url.toString());
myBrowserComponent.removeURL(url.toString());
}
}
}
public static class MkDirAction extends AnAction {
private final RepositoryBrowserComponent myBrowserComponent;
public MkDirAction(final RepositoryBrowserComponent browserComponent) {
super(SvnBundle.message("repository.browser.new.folder.action"));
myBrowserComponent = browserComponent;
}
public void update(AnActionEvent e) {
//e.getPresentation().setText(SvnBundle.message("repository.browser.new.folder.action"), true);
setEnabled(e, myBrowserComponent.getSelectedNode());
}
public void actionPerformed(AnActionEvent e) {
// show dialog for comment and folder name, then create folder
// then refresh selected node.
final RepositoryTreeNode node = myBrowserComponent.getSelectedNode();
if (node == null) {
return;
}
final Project project = myBrowserComponent.getProject();
MkdirOptionsDialog dialog = new MkdirOptionsDialog(project, node.getURL());
dialog.show();
VcsConfiguration.getInstance(project).saveCommitMessage(dialog.getCommitMessage());
if (dialog.isOK()) {
SVNURL url = dialog.getURL();
String message = dialog.getCommitMessage();
doMkdir(url, message, project);
final SVNURL repositoryUrl = (node.getSVNDirEntry() == null) ? node.getURL() : node.getSVNDirEntry().getRepositoryRoot();
final SyntheticWorker worker = new SyntheticWorker(node.getURL());
worker.addSyntheticChildToSelf(url, repositoryUrl, dialog.getName(), true);
node.reload(false);
}
}
}
protected class DiffAction extends AnAction {
public void update(AnActionEvent e) {
e.getPresentation().setText("Compare With...", true);
setEnabled(e, getRepositoryBrowser().getSelectedNode());
}
public void actionPerformed(AnActionEvent e) {
// show dialog for comment and folder name, then create folder
// then refresh selected node.
SVNURL root;
RepositoryTreeNode node = getRepositoryBrowser().getSelectedNode();
if (node == null) {
return;
}
while (node.getSVNDirEntry() != null) {
node = (RepositoryTreeNode) node.getParent();
}
root = node.getURL();
final RepositoryTreeNode selectedNode = getSelectedNode();
if (selectedNode == null) {
return;
}
SVNURL sourceURL = selectedNode.getURL();
DiffOptionsDialog dialog = new DiffOptionsDialog(myProject, root, sourceURL);
dialog.show();
if (dialog.isOK()) {
SVNURL targetURL = dialog.getTargetURL();
if (dialog.isReverseDiff()) {
targetURL = sourceURL;
sourceURL = dialog.getTargetURL();
}
final SVNURL sURL = sourceURL;
final SVNURL tURL = targetURL;
Runnable command;
boolean cancelable;
if (dialog.isUnifiedDiff()) {
final File targetFile = dialog.getTargetFile();
command = new Runnable() {
public void run() {
targetFile.getParentFile().mkdirs();
doUnifiedDiff(targetFile, sURL, tURL);
}
};
cancelable = false;
} else {
command = new Runnable() {
public void run() {
try {
doGraphicalDiff(sURL, tURL);
}
catch (final VcsException ex) {
//noinspection InstanceofCatchParameter
boolean isCancelled = ex instanceof SvnBindException && ((SvnBindException)ex).contains(SVNErrorCode.CANCELLED);
if (!isCancelled) {
WaitForProgressToShow.runOrInvokeLaterAboveProgress(new Runnable() {
public void run() {
Messages.showErrorDialog(myProject, ex.getMessage(), "Error");
}
}, null, myProject);
}
}
}
};
cancelable = true;
}
ProgressManager.getInstance().runProcessWithProgressSynchronously(command, SvnBundle.message("progress.computing.difference"),
cancelable, myProject);
}
}
}
protected class CopyOrMoveAction extends AnAction {
private final String myActionName;
private final String myDialogTitleKey;
private final boolean myMove;
public CopyOrMoveAction(final String actionName, final String dialogTitleKey, final boolean move) {
myActionName = actionName;
myDialogTitleKey = dialogTitleKey;
myMove = move;
}
public void update(AnActionEvent e) {
e.getPresentation().setText(myActionName);
RepositoryTreeNode node = getRepositoryBrowser().getSelectedNode();
e.getPresentation().setEnabled(node != null && node.getSVNDirEntry() != null);
}
public void actionPerformed(final AnActionEvent e) {
final RepositoryTreeNode node = getSelectedNode();
if (node == null) {
return;
}
RepositoryTreeNode rootNode = node;
while (! rootNode.isRepositoryRoot()) {
rootNode = (RepositoryTreeNode) rootNode.getParent();
}
CopyOptionsDialog dialog = new CopyOptionsDialog(SvnBundle.message(myDialogTitleKey), myProject, rootNode, node, ! myMove);
dialog.show();
VcsConfiguration.getInstance(myProject).saveCommitMessage(dialog.getCommitMessage());
if (dialog.isOK()) {
SVNURL dst = dialog.getTargetURL();
SVNURL src = dialog.getSourceURL();
final String path = src.getPath();
final int folder = path.replace('\\', '/').lastIndexOf('/');
if (folder != -1) {
final String lastFolder = path.substring(folder + 1, path.length());
if (myMove && "trunk".equalsIgnoreCase(lastFolder)) {
final int result =
Messages.showOkCancelDialog(myProject, "You are about to move folder named '" + lastFolder +
"'. Are you sure?", SvnBundle.message(myDialogTitleKey), Messages.getWarningIcon());
if (Messages.OK == result) return;
}
}
String message = dialog.getCommitMessage();
doCopy(src, dst, myMove, message);
final CopyMoveReloadHelper sourceReloader = myMove ? new MoveSourceReloader(node) : CopyMoveReloadHelper.EMPTY;
final TargetReloader destinationReloader = new TargetReloader(dialog, node, rootNode, myRepositoryBrowser);
sourceReloader.doSynthetic();
destinationReloader.doSynthetic();
if ((! myMove) || (! Comparing.equal(sourceReloader.parent(), destinationReloader.parent()))) {
destinationReloader.doRefresh();
}
sourceReloader.doRefresh();
}
}
}
private static class TargetReloader implements CopyMoveReloadHelper {
private final RepositoryTreeNode myDialogParent;
private final SVNURL myDst;
private final RepositoryTreeNode mySourceNode;
private final RepositoryTreeNode myRoot;
private final RepositoryBrowserComponent myBrowserComponent;
private final String myNewName;
private TargetReloader(final CopyOptionsDialog dialog, final RepositoryTreeNode node,
final RepositoryTreeNode root, final RepositoryBrowserComponent browserComponent) {
myDialogParent = dialog.getTargetParentNode();
myDst = dialog.getTargetURL();
mySourceNode = node;
myRoot = root;
myBrowserComponent = browserComponent;
myNewName = dialog.getName();
}
public void doRefresh() {
final TreeNode[] oldPath = myDialogParent.getSelfPath();
final TreeNode[] correctedPath = new TreeNode[oldPath.length + 1];
System.arraycopy(oldPath, 0, correctedPath, 1, oldPath.length);
myRoot.reload(new OpeningExpander(oldPath, myBrowserComponent, myDialogParent), false);
}
public void doSynthetic() {
final SyntheticWorker parentWorker = new SyntheticWorker(myDialogParent.getURL());
parentWorker.addSyntheticChildToSelf(myDst, myRoot.getURL(), myNewName, ! mySourceNode.isLeaf());
parentWorker.copyTreeToSelf(mySourceNode);
}
public SVNURL parent() {
return myDialogParent.getURL();
}
}
private static class MoveSourceReloader implements CopyMoveReloadHelper {
private final RepositoryTreeNode mySource;
private final RepositoryTreeNode myParent;
private MoveSourceReloader(final RepositoryTreeNode source) {
mySource = source;
myParent = (RepositoryTreeNode) source.getParent();
}
public void doRefresh() {
myParent.reload(false);
}
public void doSynthetic() {
final SyntheticWorker worker = new SyntheticWorker(mySource.getURL());
worker.removeSelf();
}
public SVNURL parent() {
return myParent.getURL();
}
}
private interface CopyMoveReloadHelper {
void doRefresh();
void doSynthetic();
@Nullable
SVNURL parent();
CopyMoveReloadHelper EMPTY = new CopyMoveReloadHelper() {
public void doRefresh() {
}
public void doSynthetic() {
}
@Nullable
public SVNURL parent() {
return null;
}
};
}
protected class CopyUrlAction extends AnAction {
public void update(AnActionEvent e) {
e.getPresentation().setText("Copy URL...");
RepositoryTreeNode node = getRepositoryBrowser().getSelectedNode();
e.getPresentation().setEnabled(node != null);
}
public void actionPerformed(final AnActionEvent e) {
final RepositoryTreeNode treeNode = getRepositoryBrowser().getSelectedNode();
if (treeNode != null) {
final String url = treeNode.getURL().toString();
CopyPasteManager.getInstance().setContents(new StringSelection(url));
}
}
}
public static class DeleteAction extends AnAction {
private final RepositoryBrowserComponent myBrowserComponent;
public DeleteAction(final RepositoryBrowserComponent browserComponent) {
super("_Delete...");
myBrowserComponent = browserComponent;
registerCustomShortcutSet(CommonShortcuts.getDelete(), myBrowserComponent);
}
public void update(AnActionEvent e) {
RepositoryTreeNode node = myBrowserComponent.getSelectedNode();
e.getPresentation().setEnabled(node != null && node.getSVNDirEntry() != null);
}
public void actionPerformed(AnActionEvent e) {
DeleteOptionsDialog dialog = new DeleteOptionsDialog(myBrowserComponent.getProject());
RepositoryTreeNode node = myBrowserComponent.getSelectedNode();
dialog.show();
VcsConfiguration.getInstance(myBrowserComponent.getProject()).saveCommitMessage(dialog.getCommitMessage());
if (dialog.isOK()) {
SVNURL url = node.getURL();
String message = dialog.getCommitMessage();
final boolean successful = doDelete(url, message);
if (successful) {
final SyntheticWorker worker = new SyntheticWorker(url);
worker.removeSelf();
final RepositoryTreeNode parentNode = (RepositoryTreeNode) node.getParent();
parentNode.reload(new KeepingExpandedExpander(myBrowserComponent, new AfterDeletionSelectionInstaller(node, myBrowserComponent)), false);
}
}
}
private boolean doDelete(final SVNURL url, final String comment) {
final Ref<Exception> exception = new Ref<Exception>();
final Project project = myBrowserComponent.getProject();
Runnable command = new Runnable() {
public void run() {
ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator();
if (progress != null) {
progress.setText(SvnBundle.message("progres.text.deleting", url.toString()));
}
SvnVcs vcs = SvnVcs.getInstance(project);
try {
vcs.getFactoryFromSettings().createDeleteClient().delete(url, comment);
}
catch (VcsException e) {
exception.set(e);
}
}
};
ProgressManager.getInstance().runProcessWithProgressSynchronously(command, SvnBundle.message("progress.title.browser.delete"), false, project);
if (!exception.isNull()) {
Messages.showErrorDialog(exception.get().getMessage(), SvnBundle.message("message.text.error"));
}
return exception.isNull();
}
}
private static class AfterDeletionSelectionInstaller implements Expander {
private final RepositoryTreeNode myParentNode;
private final String myDeletedNodeName;
private final boolean myIsFolder;
private final RepositoryBrowserComponent myBrowserComponent;
private AfterDeletionSelectionInstaller(final RepositoryTreeNode deletedNode, final RepositoryBrowserComponent browserComponent) {
myBrowserComponent = browserComponent;
myParentNode = (RepositoryTreeNode) deletedNode.getParent();
myDeletedNodeName = deletedNode.toString();
myIsFolder = ! deletedNode.isLeaf();
}
public void onBeforeRefresh(final RepositoryTreeNode node) {
}
public void onAfterRefresh(final RepositoryTreeNode node) {
TreeNode nodeToSelect = myParentNode.getNextChildByKey(myDeletedNodeName, myIsFolder);
nodeToSelect = (nodeToSelect == null) ? myParentNode : nodeToSelect;
myBrowserComponent.setSelectedNode(nodeToSelect);
}
}
protected class ImportAction extends AnAction {
public void update(AnActionEvent e) {
e.getPresentation().setVisible(showImportAction());
e.getPresentation().setText(SvnBundle.message("repository.browser.import.action"));
setEnabled(e, getRepositoryBrowser().getSelectedNode(),
ProjectLevelVcsManager.getInstance(myProject).isBackgroundVcsOperationRunning());
}
public void actionPerformed(AnActionEvent e) {
// get directory, then import.
doImport();
}
}
protected class ExportAction extends AnAction {
public void update(AnActionEvent e) {
e.getPresentation().setText("_Export...");
e.getPresentation().setEnabled(getRepositoryBrowser().getSelectedNode() != null);
}
public void actionPerformed(AnActionEvent e) {
final RepositoryTreeNode selectedNode = getSelectedNode();
if (selectedNode == null) {
return;
}
SVNURL url = selectedNode.getURL();
final File dir = selectFile("Destination directory", "Select export destination directory");
if (dir == null) {
return;
}
Project p = e.getData(CommonDataKeys.PROJECT);
ExportOptionsDialog dialog = new ExportOptionsDialog(p, url, dir);
dialog.show();
if (dialog.isOK()) {
SvnCheckoutProvider.doExport(myProject, dir, url, dialog.getDepth(),
dialog.isIgnoreExternals(), dialog.isForce(), dialog.getEOLStyle());
}
}
}
protected class CheckoutAction extends AnAction {
public void update(AnActionEvent e) {
e.getPresentation().setText("_Checkout...", true);
setEnabled(e, getRepositoryBrowser().getSelectedNode());
}
public void actionPerformed(AnActionEvent e) {
final RepositoryTreeNode selectedNode = getSelectedNode();
if (! ModalityState.NON_MODAL.equals(ModalityState.current())) {
doCancelAction();
}
doCheckout(ProjectLevelVcsManager.getInstance(myProject).getCompositeCheckoutListener(), selectedNode);
}
}
private static void setEnabled(@NotNull AnActionEvent e, @Nullable RepositoryTreeNode node) {
setEnabled(e, node, false);
}
private static void setEnabled(@NotNull AnActionEvent e, @Nullable RepositoryTreeNode node, boolean isRunning) {
e.getPresentation().setEnabled(node != null && (node.getSVNDirEntry() == null || node.getSVNDirEntry().isDirectory()) && !isRunning);
}
protected class BrowseChangesAction extends AnAction {
public BrowseChangesAction() {
super(SvnBundle.message("repository.browser.browse.changes.action"),
SvnBundle.message("repository.browser.browse.changes.description"), null);
}
public void actionPerformed(AnActionEvent e) {
RepositoryTreeNode node = getSelectedNode();
if (node == null) {
return;
}
SVNURL url = node.getURL();
AbstractVcsHelper.getInstance(myProject).showChangesBrowser(myVCS.getCommittedChangesProvider(),
new SvnRepositoryLocation(url.toString()),
"Changes in " + url.toString(), null);
}
public void update(final AnActionEvent e) {
e.getPresentation().setEnabled(getRepositoryBrowser().getSelectedNode() != null);
}
}
protected class OpenAction extends AnAction {
public void update(AnActionEvent e) {
e.getPresentation().setEnabled(false);
if (myVCS == null) {
return;
}
e.getPresentation().setText("_Open", true);
e.getPresentation().setEnabled(getRepositoryBrowser().getSelectedVcsFile() != null);
}
public void actionPerformed(AnActionEvent e) {
VirtualFile vcsVF = getRepositoryBrowser().getSelectedVcsFile();
if (vcsVF != null) {
FileEditorManager.getInstance(myVCS.getProject()).openFile(vcsVF, true);
}
}
}
protected class DetailsAction extends ToggleAction {
private boolean myIsSelected;
public void update(final AnActionEvent e) {
e.getPresentation().setDescription(SvnBundle.message("repository.browser.details.action"));
e.getPresentation().setText(SvnBundle.message("repository.browser.details.action"));
e.getPresentation().setIcon(AllIcons.Actions.Annotate);
super.update(e);
}
public boolean isSelected(AnActionEvent e) {
return myIsSelected;
}
public void setSelected(AnActionEvent e, boolean state) {
myIsSelected = state;
SvnRepositoryTreeCellRenderer r = new SvnRepositoryTreeCellRenderer();
r.setShowDetails(state);
getRepositoryBrowser().getRepositoryTree().setCellRenderer(r);
}
}
@Nullable
private File selectFile(String title, String description) {
FileChooserDescriptor fcd = FileChooserDescriptorFactory.createSingleFolderDescriptor();
fcd.setShowFileSystemRoots(true);
fcd.setTitle(title);
fcd.setDescription(description);
fcd.setHideIgnored(false);
VirtualFile file = FileChooser.chooseFile(fcd, myProject, null);
if (file == null) {
return null;
}
final String path = file.getPath();
if (path.endsWith(":")) { // workaround for VFS oddities with drive root (IDEADEV-20870)
return new File(path + "/");
}
return new File(path);
}
protected static void doMkdir(final SVNURL url, final String comment, final Project project) {
final Ref<Exception> exception = new Ref<Exception>();
Runnable command = new Runnable() {
public void run() {
ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator();
if (progress != null) {
progress.setText(SvnBundle.message("progress.text.browser.creating", url.toString()));
}
SvnVcs vcs = SvnVcs.getInstance(project);
SvnTarget target = SvnTarget.fromURL(url);
try {
vcs.getFactoryFromSettings().createBrowseClient().createDirectory(target, comment, false);
}
catch (VcsException e) {
exception.set(e);
}
}
};
ProgressManager.getInstance().runProcessWithProgressSynchronously(command, SvnBundle.message("progress.text.create.remote.folder"), false, project);
if (!exception.isNull()) {
Messages.showErrorDialog(exception.get().getMessage(), SvnBundle.message("message.text.error"));
}
}
private void doCopy(final SVNURL src, final SVNURL dst, final boolean move, final String comment) {
final Ref<Exception> exception = new Ref<Exception>();
Runnable command = new Runnable() {
public void run() {
ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator();
if (progress != null) {
progress.setText((move ? SvnBundle.message("progress.text.browser.moving", src) : SvnBundle.message("progress.text.browser.copying", src)));
progress.setText2(SvnBundle.message("progress.text.browser.remote.destination", dst));
}
SvnVcs vcs = SvnVcs.getInstance(myProject);
try {
vcs.getFactoryFromSettings().createCopyMoveClient().copy(SvnTarget.fromURL(src), SvnTarget.fromURL(dst), SVNRevision.HEAD, true,
move, comment, null);
}
catch (VcsException e) {
exception.set(e);
}
}
};
String progressTitle = move ? SvnBundle.message("progress.title.browser.move") : SvnBundle.message("progress.title.browser.copy");
ProgressManager.getInstance().runProcessWithProgressSynchronously(command, progressTitle, false, myProject);
if (!exception.isNull()) {
Messages.showErrorDialog(exception.get().getMessage(), SvnBundle.message("message.text.error"));
}
}
protected void doCheckout(@Nullable final CheckoutProvider.Listener listener, final RepositoryTreeNode selectedNode) {
if (selectedNode == null) {
return;
}
SVNURL url = selectedNode.getURL();
String relativePath = "";
final DirectoryEntry entry = selectedNode.getSVNDirEntry();
if (entry != null) {
if (entry.getRepositoryRoot() != null) {
if (! entry.getRepositoryRoot().equals(url)) {
relativePath = SVNPathUtil.getRelativePath(entry.getRepositoryRoot().toString(), url.toDecodedString());
}
} else {
relativePath = entry.getRelativePath();
}
} else {
relativePath = url.getPath();
}
File dir = selectFile(SvnBundle.message("svn.checkout.destination.directory.title"),
SvnBundle.message("svn.checkout.destination.directory.description"));
if (dir == null) {
return;
}
Project p = myProject;
CheckoutOptionsDialog dialog = new CheckoutOptionsDialog(p, url, dir, SvnUtil.getVirtualFile(dir.getAbsolutePath()), relativePath);
dialog.show();
dir = dialog.getTarget();
if (dialog.isOK() && dir != null) {
final SVNRevision revision;
try {
revision = dialog.getRevision();
} catch (ConfigurationException e) {
Messages.showErrorDialog(SvnBundle.message("message.text.cannot.checkout", e.getMessage()), SvnBundle.message("message.title.check.out"));
return;
}
SvnCheckoutProvider.doCheckout(myProject, dir, url.toString(), revision, dialog.getDepth(), dialog.isIgnoreExternals(), listener);
}
}
/**
* @return true only if import was called
*/
protected boolean doImport() {
File dir = selectFile("Import Directory", "Select directory to import into repository");
if (dir == null) {
return false;
}
final RepositoryTreeNode selectedNode = getSelectedNode();
if (selectedNode == null) {
return false;
}
SVNURL url = selectedNode.getURL();
ImportOptionsDialog dialog = new ImportOptionsDialog(myProject, url, dir);
dialog.show();
VcsConfiguration.getInstance(myProject).saveCommitMessage(dialog.getCommitMessage());
if (dialog.isOK()) {
File src = dialog.getTarget();
boolean ignored = dialog.isIncludeIgnored();
String message = dialog.getCommitMessage();
SvnCheckoutProvider.doImport(myProject, src, url, dialog.getDepth(), ignored, message);
selectedNode.reload(false);
}
return dialog.isOK();
}
private void doUnifiedDiff(File targetFile, SVNURL sourceURL, SVNURL targetURL) {
OutputStream os = null;
try {
os = new BufferedOutputStream(new FileOutputStream(targetFile));
myVCS.getFactoryFromSettings().createDiffClient().unifiedDiff(SvnTarget.fromURL(sourceURL, SVNRevision.HEAD),
SvnTarget.fromURL(targetURL, SVNRevision.HEAD), os);
}
catch (IOException e) {
LOG.info(e);
}
catch (VcsException e) {
LOG.info(e);
}
finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
LOG.info(e);
}
}
}
}
private void doGraphicalDiff(SVNURL sourceURL, SVNURL targetURL) throws VcsException {
List<Change> changes =
myVCS.getFactoryFromSettings().createDiffClient().compare(SvnTarget.fromURL(sourceURL), SvnTarget.fromURL(targetURL));
showDiffEditorResults(changes, SVNPathUtil.tail(sourceURL.toString()), SVNPathUtil.tail(targetURL.toString()));
}
private void showDiffEditorResults(final Collection<Change> changes, String sourceTitle, String targetTitle) {
final String title = SvnBundle.message("repository.browser.compare.title", sourceTitle, targetTitle);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
final ChangeListViewerDialog dlg = new ChangeListViewerDialog(myRepositoryBrowser, myProject, changes, true);
dlg.setTitle(title);
dlg.show();
}
});
}
private class CloseToolWindowAction extends AnAction {
public void actionPerformed(AnActionEvent e) {
disposeRepositoryBrowser();
Project p = e.getData(CommonDataKeys.PROJECT);
ToolWindowManager.getInstance(p).unregisterToolWindow(BrowseRepositoryAction.REPOSITORY_BROWSER_TOOLWINDOW);
}
public void update(AnActionEvent e) {
e.getPresentation().setText("Close");
e.getPresentation().setDescription("Close this tool window");
e.getPresentation().setIcon(AllIcons.Actions.Cancel);
}
}
}