blob: 437e7f57e4642bce1139f734f277ed876bc7d67d [file] [log] [blame]
/*
* Copyright 2000-2009 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.icons.AllIcons;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.keymap.KeymapManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager;
import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.ui.PopupHandler;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.ui.table.JBTable;
import com.intellij.util.IconUtil;
import com.intellij.util.containers.HashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.SvnPropertyKeys;
import org.jetbrains.idea.svn.SvnVcs;
import org.jetbrains.idea.svn.api.Depth;
import org.jetbrains.idea.svn.properties.PropertyConsumer;
import org.jetbrains.idea.svn.properties.PropertyData;
import org.jetbrains.idea.svn.properties.PropertyValue;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc2.SvnTarget;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.io.File;
import java.util.Map;
import java.util.TreeMap;
/**
* Created by IntelliJ IDEA.
* User: alex
* Date: Jun 20, 2006
* Time: 4:39:46 PM
*/
public class PropertiesComponent extends JPanel {
public static final String ID = "SVN Properties";
private JTable myTable;
private JTextArea myTextArea;
private boolean myIsFollowSelection;
private File myFile;
private SvnVcs myVcs;
private JSplitPane mySplitPane;
private static final String CONTEXT_ID = "context";
private final CloseAction myCloseAction = new CloseAction();
private final RefreshAction myRefreshAction = new RefreshAction();
private ActionGroup myPopupActionGroup;
public PropertiesComponent() {
// register toolwindow and add listener to the selection.
myIsFollowSelection = true;
init();
}
public void init() {
setLayout(new BorderLayout());
myTable = new JBTable();
myTextArea = new JTextArea(0, 0);
myTextArea.setEditable(false);
JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(myTable);
mySplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, scrollPane, ScrollPaneFactory.createScrollPane(myTextArea));
add(mySplitPane, BorderLayout.CENTER);
add(createToolbar(), BorderLayout.WEST);
final DefaultTableModel model = new DefaultTableModel(createTableModel(new HashMap<String, String>()), new Object[]{"Name", "Value"}) {
public boolean isCellEditable(final int row, final int column) {
return false;
}
};
myTable.setModel(model);
myTable.setShowVerticalLines(true);
myTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
myTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
int index = myTable.getSelectedRow();
if (index >= 0) {
Object value = myTable.getValueAt(index, 1);
if (value instanceof String) {
myTextArea.setText(((String) value));
} else {
myTextArea.setText("");
}
} else {
myTextArea.setText("");
}
}
});
myPopupActionGroup = createPopup();
PopupHandler.installPopupHandler(myTable, myPopupActionGroup, ActionPlaces.UNKNOWN, ActionManager.getInstance());
PopupHandler.installPopupHandler(scrollPane, myPopupActionGroup, ActionPlaces.UNKNOWN, ActionManager.getInstance());
final Shortcut[] shortcuts = KeymapManager.getInstance().getActiveKeymap().getShortcuts(IdeActions.ACTION_CLOSE_ACTIVE_TAB);
myCloseAction.registerCustomShortcutSet(new CustomShortcutSet(shortcuts), this);
myRefreshAction.registerCustomShortcutSet(CommonShortcuts.getRerun(), this);
}
public void setFile(SvnVcs vcs, File file) {
final Map<String, String> props = new TreeMap<String, String>();
boolean firstTime = myFile == null;
if (file != null) {
myFile = file;
myVcs = vcs;
collectProperties(vcs, file, props);
}
DefaultTableModel model = (DefaultTableModel) myTable.getModel();
model.setDataVector(createTableModel(props), new Object[] {"Name", "Value"});
myTable.getColumnModel().setColumnSelectionAllowed(false);
myTable.getColumnModel().getColumn(1).setCellRenderer(new DefaultTableCellRenderer() {
protected void setValue(Object value) {
if (value != null) {
if (value.toString().indexOf('\r') >= 0) {
value = value.toString().substring(0, value.toString().indexOf('\r')) + " [...]";
}
if (value.toString().indexOf('\n') >= 0) {
value = value.toString().substring(0, value.toString().indexOf('\n')) + " [...]";
}
}
super.setValue(value);
}
});
if (firstTime) {
mySplitPane.setDividerLocation(.5);
}
if (myTable.getRowCount() > 0) {
myTable.getSelectionModel().setSelectionInterval(0, 0);
}
}
private static void collectProperties(@NotNull SvnVcs vcs, @NotNull File file, @NotNull final Map<String, String> props) {
try {
PropertyConsumer handler = new PropertyConsumer() {
public void handleProperty(File path, PropertyData property) throws SVNException {
final PropertyValue value = property.getValue();
if (value != null) {
props.put(property.getName(), PropertyValue.toString(property.getValue()));
}
}
public void handleProperty(SVNURL url, PropertyData property) throws SVNException {
}
public void handleProperty(long revision, PropertyData property) throws SVNException {
}
};
vcs.getFactory(file).createPropertyClient().list(SvnTarget.fromFile(file, SVNRevision.UNDEFINED), SVNRevision.WORKING, Depth.EMPTY,
handler);
}
catch (VcsException e) {
props.clear();
}
}
private static Object[][] createTableModel(Map<String, String> model) {
Object[][] result = new Object[model.size()][2];
int index = 0;
for (final String name : model.keySet()) {
String value = model.get(name);
if (value == null) {
value = "";
}
result[index][0] = name;
result[index][1] = value;
index++;
}
return result;
}
private JComponent createToolbar() {
DefaultActionGroup group = new DefaultActionGroup();
group.add(new AddPropertyAction());
group.add(new EditPropertyAction());
group.add(new DeletePropertyAction());
group.addSeparator();
group.add(new SetKeywordsAction());
group.addSeparator();
group.add(new FollowSelectionAction());
group.add(myRefreshAction);
group.add(myCloseAction);
return ActionManager.getInstance().createActionToolbar("", group, false).getComponent();
}
private DefaultActionGroup createPopup() {
DefaultActionGroup group = new DefaultActionGroup();
group.add(new AddPropertyAction());
group.add(new EditPropertyAction());
group.add(new DeletePropertyAction());
group.addSeparator();
group.add(new SetKeywordsAction());
group.addSeparator();
group.add(myRefreshAction);
return group;
}
private String getSelectedPropertyName() {
int row = myTable.getSelectedRow();
if (row < 0) {
return null;
}
return (String) myTable.getValueAt(row, 0);
}
private void updateFileStatus(boolean recursive) {
if (myFile != null && myVcs != null) {
String url = "file://" + myFile.getPath().replace(File.separatorChar, '/');
VirtualFile file = VirtualFileManager.getInstance().findFileByUrl(url);
if (file != null) {
VcsDirtyScopeManager dirtyScopeManager = VcsDirtyScopeManager.getInstance(myVcs.getProject());
if (recursive && file.isDirectory()) {
dirtyScopeManager.dirDirtyRecursively(file);
} else {
dirtyScopeManager.fileDirty(file);
}
}
}
}
private static class CloseAction extends AnAction {
public void update(AnActionEvent e) {
e.getPresentation().setText("Close");
e.getPresentation().setDescription("Close this tool window");
e.getPresentation().setIcon(AllIcons.Actions.Cancel);
}
public void actionPerformed(AnActionEvent e) {
Project p = e.getData(CommonDataKeys.PROJECT);
ToolWindowManager.getInstance(p).unregisterToolWindow(ID);
}
}
private class RefreshAction extends AnAction {
public void update(AnActionEvent e) {
e.getPresentation().setText("Refresh");
e.getPresentation().setDescription("Reload properties");
e.getPresentation().setIcon(AllIcons.Actions.Refresh);
e.getPresentation().setEnabled(myFile != null);
}
public void actionPerformed(AnActionEvent e) {
setFile(myVcs, myFile);
updateFileStatus(false);
}
}
private abstract class BasePropertyAction extends AnAction {
protected void setProperty(@Nullable String property, @Nullable String value, boolean recursive, boolean force) {
if (!StringUtil.isEmpty(property)) {
try {
myVcs.getFactory(myFile).createPropertyClient()
.setProperty(myFile, property, PropertyValue.create(value), Depth.allOrEmpty(recursive), force);
}
catch (VcsException error) {
VcsBalloonProblemNotifier
.showOverChangesView(myVcs.getProject(), "Can not set property: " + error.getMessage(), MessageType.ERROR);
// show error message.
}
}
}
protected void updateFileView(boolean recursive) {
setFile(myVcs, myFile);
updateFileStatus(recursive);
}
}
private class SetKeywordsAction extends BasePropertyAction {
public void update(AnActionEvent e) {
e.getPresentation().setText("Edit Keywords");
e.getPresentation().setDescription("Manage svn:keywords property");
if (!CONTEXT_ID.equals(e.getPlace())) {
e.getPresentation().setIcon(AllIcons.Actions.Properties);
}
e.getPresentation().setEnabled(myFile != null && myFile.isFile());
}
public void actionPerformed(AnActionEvent e) {
Project project = CommonDataKeys.PROJECT.getData(e.getDataContext());
PropertyValue propValue = null;
try {
propValue = myVcs.getFactory(myFile).createPropertyClient()
.getProperty(SvnTarget.fromFile(myFile), SvnPropertyKeys.SVN_KEYWORDS, false, SVNRevision.WORKING);
}
catch (VcsException e1) {
// show erorr message
}
SetKeywordsDialog dialog = new SetKeywordsDialog(project, PropertyValue.toString(propValue));
dialog.show();
if (dialog.isOK()) {
setProperty(SvnPropertyKeys.SVN_KEYWORDS, dialog.getKeywords(), false, false);
}
updateFileView(false);
}
}
private class DeletePropertyAction extends BasePropertyAction {
public void update(AnActionEvent e) {
e.getPresentation().setText("Delete Property");
e.getPresentation().setDescription("Delete selected property");
if (!CONTEXT_ID.equals(e.getPlace())) {
e.getPresentation().setIcon(AllIcons.General.Remove);
}
e.getPresentation().setEnabled(myFile != null && getSelectedPropertyName() != null);
}
public void actionPerformed(AnActionEvent e) {
setProperty(getSelectedPropertyName(), null, false, true);
updateFileView(false);
}
}
private class AddPropertyAction extends BasePropertyAction {
public void update(AnActionEvent e) {
e.getPresentation().setText("Add Property");
e.getPresentation().setDescription("Add new property");
if (!CONTEXT_ID.equals(e.getPlace())) {
e.getPresentation().setIcon(IconUtil.getAddIcon());
}
e.getPresentation().setEnabled(myFile != null);
}
public void actionPerformed(AnActionEvent e) {
Project project = CommonDataKeys.PROJECT.getData(e.getDataContext());
SetPropertyDialog dialog = new SetPropertyDialog(project, new File[] {myFile}, null,
myFile.isDirectory());
dialog.show();
boolean recursive = false;
if (dialog.isOK()) {
recursive = dialog.isRecursive();
setProperty(dialog.getPropertyName(), dialog.getPropertyValue(), recursive, false);
}
updateFileView(recursive);
}
}
private class EditPropertyAction extends BasePropertyAction {
public void update(AnActionEvent e) {
e.getPresentation().setText("Edit Property");
e.getPresentation().setDescription("Edit selected property value");
if (!CONTEXT_ID.equals(e.getPlace())) {
e.getPresentation().setIcon(AllIcons.Actions.EditSource);
}
e.getPresentation().setEnabled(myFile != null && getSelectedPropertyName() != null);
}
public void actionPerformed(AnActionEvent e) {
Project project = CommonDataKeys.PROJECT.getData(e.getDataContext());
SetPropertyDialog dialog = new SetPropertyDialog(project, new File[] {myFile}, getSelectedPropertyName(), myFile.isDirectory());
dialog.show();
boolean recursive = false;
if (dialog.isOK()) {
recursive = dialog.isRecursive();
setProperty(dialog.getPropertyName(), dialog.getPropertyValue(), recursive, false);
}
updateFileView(recursive);
}
}
private class FollowSelectionAction extends ToggleAction {
public boolean isSelected(AnActionEvent e) {
return myIsFollowSelection;
}
public void setSelected(AnActionEvent e, boolean state) {
if (state && !myIsFollowSelection) {
updateSelection(e);
}
myIsFollowSelection = state;
}
public void update(final AnActionEvent e) {
super.update(e);
e.getPresentation().setIcon(AllIcons.General.AutoscrollFromSource);
e.getPresentation().setText("Follow Selection");
e.getPresentation().setDescription("Follow Selection");
// change file
if (myIsFollowSelection) {
updateSelection(e);
}
}
private void updateSelection(AnActionEvent e) {
if (myVcs == null) {
return;
}
VirtualFile vf = CommonDataKeys.VIRTUAL_FILE.getData(e.getDataContext());
if (vf != null) {
File f = new File(vf.getPath());
if (!f.equals(myFile)) {
setFile(myVcs, f);
Project p = CommonDataKeys.PROJECT.getData(e.getDataContext());
ToolWindowManager.getInstance(p).getToolWindow(ID).setTitle(f.getName());
}
}
}
}
}