diff --git a/platform/platform-impl/src/com/intellij/util/ui/LocalPathCellEditor.java b/platform/platform-impl/src/com/intellij/util/ui/LocalPathCellEditor.java new file mode 100644 index 000000000000..f46845347a69 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/util/ui/LocalPathCellEditor.java @@ -0,0 +1,69 @@ +/* + * 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 com.intellij.util.ui; + +import com.intellij.openapi.fileChooser.FileChooser; +import com.intellij.openapi.fileChooser.FileChooserDescriptor; +import com.intellij.openapi.ui.TextFieldWithBrowseButton; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.ui.AbstractTableCellEditor; +import com.intellij.util.ui.CellEditorComponentWithBrowseButton; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class LocalPathCellEditor extends AbstractTableCellEditor { + private final String myTitle; + + private CellEditorComponentWithBrowseButton myComponent; + + public LocalPathCellEditor(String title) { + myTitle = title; + } + + public Object getCellEditorValue() { + return myComponent.getChildComponent().getText(); + } + + public Component getTableCellEditorComponent(final JTable table, Object value, boolean isSelected, final int row, int column) { + ActionListener listener = new ActionListener() { + public void actionPerformed(ActionEvent e) { + FileChooserDescriptor d = new FileChooserDescriptor(false, true, false, true, false, false); + if (myTitle != null) { + d.setTitle(myTitle); + } + d.setShowFileSystemRoots(true); + + VirtualFile initialFile = LocalFileSystem.getInstance().findFileByPath((String)getCellEditorValue()); + VirtualFile[] files = FileChooser.chooseFiles(table, d, initialFile); + if (files.length == 1 && files[0] != null) { + String path = files[0].getPresentableUrl(); + if (SystemInfo.isWindows && path.length() == 2 && Character.isLetter(path.charAt(0)) && path.charAt(1) == ':') { + path += "\\"; // make path absolute + } + myComponent.getChildComponent().setText(path); + } + } + }; + myComponent = new CellEditorComponentWithBrowseButton(new TextFieldWithBrowseButton(listener), this); + myComponent.getChildComponent().setText((String)value); + return myComponent; + } +} diff --git a/platform/platform-impl/src/com/intellij/util/ui/ValidatingTableEditor.form b/platform/platform-impl/src/com/intellij/util/ui/ValidatingTableEditor.form new file mode 100644 index 000000000000..3c0af0f68318 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/util/ui/ValidatingTableEditor.form @@ -0,0 +1,83 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/platform/platform-impl/src/com/intellij/util/ui/ValidatingTableEditor.java b/platform/platform-impl/src/com/intellij/util/ui/ValidatingTableEditor.java new file mode 100644 index 000000000000..0ffa6dce00e0 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/util/ui/ValidatingTableEditor.java @@ -0,0 +1,337 @@ +/* + * 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 com.intellij.util.ui; + +import com.intellij.openapi.util.NullableComputable; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.wm.IdeFocusManager; +import com.intellij.ui.HoverHyperlinkLabel; +import com.intellij.ui.table.TableView; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.event.HyperlinkEvent; +import javax.swing.event.HyperlinkListener; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.JTableHeader; +import javax.swing.table.TableCellEditor; +import javax.swing.table.TableCellRenderer; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public abstract class ValidatingTableEditor { + + private static final Icon WARNING_ICON = UIUtil.getBalloonWarningIcon(); + private static final Icon EMPTY_ICON = new EmptyIcon(WARNING_ICON); + private static final String REMOVE_KEY = "REMOVE_SELECTED"; + + public interface RowHeightProvider { + int getRowHeight(); + } + + public interface Fix extends Runnable { + String getTitle(); + } + + private class ColumnInfoWrapper extends ColumnInfo { + private final ColumnInfo myDelegate; + + public ColumnInfoWrapper(ColumnInfo delegate) { + super(delegate.getName()); + myDelegate = delegate; + } + + @Override + public String valueOf(Item item) { + return myDelegate.valueOf(item); + } + + @Override + public boolean isCellEditable(Item item) { + return myDelegate.isCellEditable(item); + } + + @Override + public void setValue(Item item, String value) { + myDelegate.setValue(item, value); + updateMessage(-1, null); + } + + @Override + public TableCellEditor getEditor(Item item) { + return myDelegate.getEditor(item); + } + } + + private JPanel myContentPane; + private TableView myTable; + private JButton myAddButton; + private JButton myRemoveButton; + private JLabel myMessageLabel; + private HoverHyperlinkLabel myFixLink; + private final List myWarnings = new ArrayList(); + private Fix myFixRunnable; + + protected abstract Item cloneOf(Item item); + + @Nullable + protected abstract Pair validate(List current, List warnings); + + protected abstract Item createItem(); + + private class IconColumn extends ColumnInfo implements RowHeightProvider { + public IconColumn() { + super(" "); + } + + public String valueOf(Item item) { + return null; + } + + @Override + public int getWidth(JTable table) { + return WARNING_ICON.getIconWidth() + 2; + } + + public int getRowHeight() { + return WARNING_ICON.getIconHeight(); + } + + @Override + public TableCellRenderer getRenderer(final Item item) { + return new WarningIconCellRenderer(new NullableComputable() { + public String compute() { + return myWarnings.get(doGetItems().indexOf(item)); + } + }); + } + } + + + private void createUIComponents() { + myTable = new ChangesTrackingTableView() { + protected void onTextChanged(int row, int column, String value) { + final Item original = getItems().get(row); + Item override = cloneOf(original); + final ColumnInfo columnInfo = getTableModel().getColumnInfos()[column]; + columnInfo.setValue(override, value); + updateMessage(row, override); + } + + @Override + protected void onEditingStopped() { + updateMessage(-1, null); + } + }; + + myFixLink = new HoverHyperlinkLabel(null); + } + + protected ValidatingTableEditor() { + myTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { + public void valueChanged(ListSelectionEvent e) { + updateButtons(); + } + }); + + myTable.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), REMOVE_KEY); + myTable.getActionMap().put(REMOVE_KEY, new AbstractAction() { + public void actionPerformed(final ActionEvent e) { + removeSelected(); + } + }); + + myFixLink.addHyperlinkListener(new HyperlinkListener() { + public void hyperlinkUpdate(HyperlinkEvent e) { + if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED && myFixRunnable != null) { + myFixRunnable.run(); + } + } + }); + + myAddButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + addItem(); + } + }); + + myRemoveButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + removeSelected(); + } + }); + } + + private void removeSelected() { + myTable.stopEditing(); + List items = new ArrayList(doGetItems()); + final int[] rows = myTable.getSelectedRows(); + for (int i = rows.length - 1; i >= 0; i--) { + items.remove(rows[i]); + } + + setItems(items); + updateMessage(-1, null); + + if (!items.isEmpty()) { + int index = Math.min(rows[0], items.size() - 1); + myTable.getSelectionModel().addSelectionInterval(index, index); + } + } + + protected void addItem() { + List items = new ArrayList(doGetItems()); + items.add(createItem()); + + setItems(items); + + final int row = items.size() - 1; + myTable.getSelectionModel().setSelectionInterval(row, row); + myTable.scrollRectToVisible(myTable.getCellRect(row, 0, true)); + if (getTableModel().getColumnInfos()[1].isCellEditable(items.get(row))) { + myTable.editCellAt(row, 1); + IdeFocusManager.findInstanceByComponent(myContentPane).requestFocus(myTable.getEditorComponent(), true); + } + updateMessage(-1, null); + } + + private ListTableModel getTableModel() { + return (ListTableModel)myTable.getModel(); + } + + public void setModel(ColumnInfo[] valueColumns, List items) { + ColumnInfo[] columns = new ColumnInfo[valueColumns.length + 1]; + IconColumn iconColumn = new IconColumn(); + int maxHeight = iconColumn.getRowHeight(); + + columns[0] = iconColumn; + for (int i = 0; i < valueColumns.length; i++) { + columns[i + 1] = new ColumnInfoWrapper(valueColumns[i]); + if (valueColumns[i] instanceof RowHeightProvider) { + maxHeight = Math.max(maxHeight, ((RowHeightProvider)valueColumns[i]).getRowHeight()); + } + } + + myTable.stopEditing(); + myTable.setModel(new ListTableModel(columns)); + if (maxHeight > 0) { + myTable.setRowHeight(maxHeight); + } + + setItems(items); + updateMessage(-1, null); + } + + public List getItems() { + return Collections.unmodifiableList(doGetItems()); + } + + private List doGetItems() { + List items = new ArrayList(getTableModel().getItems()); + if (myTable.isEditing()) { + Component c = myTable.getEditorComponent(); + final JTextField textField; + if (c instanceof CellEditorComponentWithBrowseButton) { + textField = (JTextField)((CellEditorComponentWithBrowseButton)c).getChildComponent(); + } + else { + textField = (JTextField)c; + } + String value = textField.getText(); + ColumnInfo column = ((ListTableModel)myTable.getModel()).getColumnInfos()[myTable.getEditingColumn()]; + ((ColumnInfoWrapper)column).myDelegate.setValue(items.get(myTable.getEditingRow()), value); + } + return items; + } + + private void setItems(List items) { + if (items.isEmpty()) { + getTableModel().setItems(Collections.emptyList()); + myWarnings.clear(); + } + else { + getTableModel().setItems(new ArrayList(items)); + for (Item item : items) { + myWarnings.add(null); + } + } + updateButtons(); + } + + public void setTableHeader(JTableHeader header) { + myTable.setTableHeader(header); + } + + private void updateButtons() { + myRemoveButton.setEnabled(myTable.getSelectedRow() != -1); + } + + public void updateMessage(int index, @Nullable Item override) { + List current = new ArrayList(doGetItems()); + if (override != null) { + current.set(index, override); + } + + Pair messageAndFix = validate(current, myWarnings); + if (messageAndFix != null) { + myMessageLabel.setText(messageAndFix.first); + myMessageLabel.setIcon(WARNING_ICON); + myMessageLabel.setVisible(true); + myFixRunnable = messageAndFix.second; + myFixLink.setVisible(myFixRunnable != null); + myFixLink.setText(myFixRunnable != null ? myFixRunnable.getTitle() : null); + } + else { + myMessageLabel.setText(" "); + myMessageLabel.setIcon(EMPTY_ICON); + myFixLink.setVisible(false); + myFixRunnable = null; + } + myTable.repaint(); + } + + public JComponent getPreferredFocusedComponent() { + return myTable; + } + + + private static class WarningIconCellRenderer extends DefaultTableCellRenderer { + private final NullableComputable myWarningProvider; + + public WarningIconCellRenderer(NullableComputable warningProvider) { + myWarningProvider = warningProvider; + } + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + String message = myWarningProvider.compute(); + label.setIcon(message != null ? WARNING_ICON : null); + label.setToolTipText(message); + label.setHorizontalAlignment(CENTER); + label.setVerticalAlignment(CENTER); + return label; + } + } + +}