continue WEB-2093: fix editing

This commit is contained in:
Vladimir Krivosheev
2014-01-08 13:02:54 +01:00
parent 9e02c06277
commit 7ddf6a845d
4 changed files with 187 additions and 163 deletions
@@ -154,8 +154,6 @@ browsers.opera=Opera
browsers.firefox=Firefox
browsers.chrome=Chrome
browser.description=Open file in {0}
browser.active=Active
browser.default.settings=Default
browser.error=Browser Error
web.editor.configuration.title=Web
browser.select.in.default.name=Web Browser
@@ -24,7 +24,6 @@ import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.util.Function;
@@ -45,6 +44,85 @@ import static com.intellij.ide.browsers.BrowsersConfiguration.BrowserFamily;
import static com.intellij.ide.browsers.TableModelEditor.EditableColumnInfo;
public class BrowserSettingsPanel {
private static final FileChooserDescriptor APP_FILE_CHOOSER_DESCRIPTOR =
SystemInfo.isMac ? new FileChooserDescriptor(false, true, false, false, false, false) {
@Override
public boolean isFileSelectable(VirtualFile file) {
return file.getName().endsWith(".app");
}
} : FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor();
private static final ColumnInfo[] COLUMNS = {new EditableColumnInfo<ConfigurableWebBrowser, Boolean>() {
@Override
public Class getColumnClass() {
return Boolean.class;
}
@Override
public Boolean valueOf(ConfigurableWebBrowser item) {
return item.isActive();
}
@Override
public void setValue(ConfigurableWebBrowser item, Boolean value) {
item.setActive(value);
}
}, new EditableColumnInfo<ConfigurableWebBrowser, String>("Name") {
@Override
public String valueOf(ConfigurableWebBrowser item) {
return item.getName();
}
@Override
public void setValue(ConfigurableWebBrowser item, String value) {
item.setName(value);
}
}, new EditableColumnInfo<ConfigurableWebBrowser, BrowserFamily>("Family") {
@Override
public Class getColumnClass() {
return BrowserFamily.class;
}
@Override
public BrowserFamily valueOf(ConfigurableWebBrowser item) {
return item.getFamily();
}
@Override
public void setValue(ConfigurableWebBrowser item, BrowserFamily value) {
item.setFamily(value);
}
@Nullable
@Override
public TableCellRenderer getRenderer(ConfigurableWebBrowser item) {
return IconTableCellRenderer.ICONABLE;
}
@Nullable
@Override
public TableCellEditor getEditor(ConfigurableWebBrowser item) {
return ComboBoxTableCellEditor.INSTANCE;
}
}, new EditableColumnInfo<ConfigurableWebBrowser, String>("Path") {
@Override
public String valueOf(ConfigurableWebBrowser item) {
return item.getPath();
}
@Override
public void setValue(ConfigurableWebBrowser item, String value) {
item.setPath(value);
}
@Nullable
@Override
public TableCellEditor getEditor(ConfigurableWebBrowser item) {
return new LocalPathCellEditor().fileChooserDescriptor(APP_FILE_CHOOSER_DESCRIPTOR);
}
}};
private JPanel root;
private JRadioButton useSystemDefaultBrowser;
@@ -61,20 +139,11 @@ public class BrowserSettingsPanel {
private TableModelEditor<ConfigurableWebBrowser> browsersEditor;
private final FileChooserDescriptor appFileChooserDescriptor;
public BrowserSettingsPanel() {
defaultBrowserPanel.setBorder(IdeBorderFactory.createTitledBorder("Default Browser", true));
appFileChooserDescriptor = SystemInfo.isMac ?
new FileChooserDescriptor(false, true, false, false, false, false) {
@Override
public boolean isFileSelectable(VirtualFile file) {
return file.getName().endsWith(".app");
}
} : FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor();
alternativeBrowserPathField.addBrowseFolderListener(IdeBundle.message("title.select.path.to.browser"), null, null,
appFileChooserDescriptor);
APP_FILE_CHOOSER_DESCRIPTOR);
if (BrowserUtil.canStartDefaultBrowser()) {
ActionListener actionListener = new ActionListener() {
@@ -100,84 +169,7 @@ public class BrowserSettingsPanel {
}
private void createUIComponents() {
browsersEditor = new TableModelEditor<ConfigurableWebBrowser>(WebBrowserManager.getInstance().getList(), new ColumnInfo[]{new EditableColumnInfo<ConfigurableWebBrowser, Boolean>() {
@Override
public Class getColumnClass() {
return Boolean.class;
}
@Override
public Boolean valueOf(ConfigurableWebBrowser item) {
return browsersEditor.getEffective(item).isActive();
}
@Override
public void setValue(ConfigurableWebBrowser item, Boolean value) {
if (value != item.isActive()) {
browsersEditor.getMutable(item).setActive(value);
}
}
}, new EditableColumnInfo<ConfigurableWebBrowser, String>("Name") {
@Override
public String valueOf(ConfigurableWebBrowser item) {
return browsersEditor.getEffective(item).getName();
}
@Override
public void setValue(ConfigurableWebBrowser item, String value) {
if (!value.equals(item.getName())) {
browsersEditor.getMutable(item).setName(value);
}
}
}, new EditableColumnInfo<ConfigurableWebBrowser, BrowserFamily>("Family") {
@Override
public Class getColumnClass() {
return BrowserFamily.class;
}
@Override
public BrowserFamily valueOf(ConfigurableWebBrowser item) {
return browsersEditor.getEffective(item).getFamily();
}
@Override
public void setValue(ConfigurableWebBrowser item, BrowserFamily value) {
if (value != item.getFamily()) {
browsersEditor.getMutable(item).setFamily(value);
}
}
@Nullable
@Override
public TableCellRenderer getRenderer(ConfigurableWebBrowser item) {
return IconTableCellRenderer.ICONABLE;
}
@Nullable
@Override
public TableCellEditor getEditor(ConfigurableWebBrowser item) {
return ComboBoxTableCellEditor.INSTANCE;
}
}, new EditableColumnInfo<ConfigurableWebBrowser, String>("Path") {
@Override
public String valueOf(ConfigurableWebBrowser info) {
return browsersEditor.getEffective(info).getPath();
}
@Override
public void setValue(ConfigurableWebBrowser item, String value) {
String normalizedValue = StringUtil.nullize(value, true);
if (!Comparing.equal(normalizedValue, item.getPath())) {
browsersEditor.getMutable(item).setPath(normalizedValue);
}
}
@Nullable
@Override
public TableCellEditor getEditor(ConfigurableWebBrowser item) {
return new LocalPathCellEditor().fileChooserDescriptor(appFileChooserDescriptor);
}
}}, new Function<ConfigurableWebBrowser, ConfigurableWebBrowser>() {
browsersEditor = new TableModelEditor<ConfigurableWebBrowser>(WebBrowserManager.getInstance().getList(), COLUMNS, new Function<ConfigurableWebBrowser, ConfigurableWebBrowser>() {
@Override
public ConfigurableWebBrowser fun(ConfigurableWebBrowser browser) {
return new ConfigurableWebBrowser(browser.getId(), browser.getFamily(), browser.getName(), browser.getPath(), browser.isActive(), browser.getSpecificSettings());
@@ -103,8 +103,10 @@ final class ConfigurableWebBrowser extends WebBrowser {
ConfigurableWebBrowser browser = (ConfigurableWebBrowser)o;
return id.equals(browser.id) &&
family.equals(browser.family) &&
active == browser.active &&
Comparing.equal(path, browser.path) &&
Comparing.strEqual(name, browser.name) &&
Comparing.strEqual(path, browser.path) &&
Comparing.equal(specificSettings, browser.specificSettings);
}
@@ -15,43 +15,44 @@
*/
package com.intellij.ide.browsers;
import com.intellij.openapi.util.Comparing;
import com.intellij.ui.TableSpeedSearch;
import com.intellij.ui.TableUtil;
import com.intellij.ui.ToolbarDecorator;
import com.intellij.ui.table.TableView;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.ColumnInfo;
import com.intellij.util.ui.ElementProducer;
import com.intellij.util.ui.ListTableModel;
import gnu.trove.THashMap;
import gnu.trove.TObjectObjectProcedure;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class TableModelEditor<T> implements ElementProducer<T> {
private final List<T> items;
private final TableView<T> table;
private final THashMap<T, T> modifiedItems = new THashMap<T, T>();
private final Function<T, T> mutableFactory;
private final Class<T> itemClass;
private boolean isApplying;
private final MyListTableModel<T> model;
/**
* source will be copied, passed list will not be used directly
* itemClass must has empty constructor
*/
public TableModelEditor(@NotNull List<T> source, @NotNull ColumnInfo[] columns, @NotNull Function<T, T> mutableFactory, Class<T> itemClass) {
public TableModelEditor(@NotNull List<T> items, @NotNull ColumnInfo[] columns, @NotNull Function<T, T> mutableFactory, Class<T> itemClass) {
this.itemClass = itemClass;
items = new ArrayList<T>(source);
this.mutableFactory = mutableFactory;
table = new TableView<T>(new ListTableModel<T>(columns, items));
model = new MyListTableModel<T>(columns, new ArrayList<T>(items), this);
table = new TableView<T>(model);
table.setStriped(true);
new TableSpeedSearch(table);
if (columns[0].getColumnClass() == Boolean.class && columns[0].getName().isEmpty()) {
@@ -59,6 +60,95 @@ public class TableModelEditor<T> implements ElementProducer<T> {
}
}
private static final class MyListTableModel<T> extends ListTableModel<T> {
private final List<T> items;
private final TableModelEditor<T> editor;
private final THashMap<T, T> modifiedToOriginal = new THashMap<T, T>();
public MyListTableModel(ColumnInfo[] columns, List<T> items, TableModelEditor<T> editor) {
super(columns, items);
this.items = items;
this.editor = editor;
}
@Override
public void setItems(@NotNull List<T> items) {
throw new IncorrectOperationException();
}
@Override
public void setValueAt(Object newValue, int rowIndex, int columnIndex) {
if (rowIndex < getRowCount()) {
@SuppressWarnings("unchecked")
ColumnInfo<T, Object> column = (ColumnInfo<T, Object>)getColumnInfos()[columnIndex];
T item = getItem(rowIndex);
Object oldValue = column.valueOf(item);
if (column.getColumnClass() == String.class
? !Comparing.strEqual(((String)oldValue), ((String)newValue))
: !Comparing.equal(oldValue, newValue)) {
T mutable;
if (modifiedToOriginal.containsKey(item)) {
mutable = item;
}
else {
mutable = editor.mutableFactory.fun(item);
modifiedToOriginal.put(mutable, item);
items.set(rowIndex, mutable);
}
column.setValue(mutable, newValue);
}
}
}
public boolean isModified(@NotNull List<T> oldItems) {
if (items.size() == oldItems.size()) {
for (int i = 0, size = items.size(); i < size; i++) {
if (!items.get(i).equals(oldItems.get(i))) {
return true;
}
}
}
else {
return true;
}
return false;
}
@NotNull
public List<T> apply() {
if (modifiedToOriginal.isEmpty()) {
return items;
}
@SuppressWarnings("unchecked")
final ColumnInfo<T, Object>[] columns = getColumnInfos();
modifiedToOriginal.forEachEntry(new TObjectObjectProcedure<T, T>() {
@Override
public boolean execute(T newItem, @Nullable T item) {
if (item == null) {
// it is added item, we don't need to sync
return true;
}
for (ColumnInfo<T, Object> column : columns) {
if (column.isCellEditable(newItem)) {
column.setValue(item, column.valueOf(newItem));
}
}
items.set(ContainerUtil.indexOfIdentity(items, newItem), item);
return true;
}
});
modifiedToOriginal.clear();
return items;
}
}
public abstract static class EditableColumnInfo<Item, Aspect> extends ColumnInfo<Item, Aspect> {
public EditableColumnInfo(@NotNull String name) {
super(name);
@@ -82,7 +172,9 @@ public class TableModelEditor<T> implements ElementProducer<T> {
@Override
public T createElement() {
try {
return itemClass.newInstance();
T item = itemClass.newInstance();
model.modifiedToOriginal.put(item, null);
return item;
}
catch (InstantiationException e) {
throw new RuntimeException(e);
@@ -97,76 +189,16 @@ public class TableModelEditor<T> implements ElementProducer<T> {
return true;
}
@NotNull
public T getEffective(@NotNull T item) {
T mutable = isApplying || modifiedItems.isEmpty() ? null : modifiedItems.get(item);
return mutable == null ? item : mutable;
}
@NotNull
public T getMutable(@NotNull T item) {
if (isApplying) {
return item;
}
T mutable = modifiedItems.get(item);
if (mutable == null) {
mutable = mutableFactory.fun(item);
modifiedItems.put(item, mutable);
}
return mutable;
}
public boolean isModified(@NotNull List<T> oldItems) {
if (!modifiedItems.isEmpty()) {
for (Map.Entry<T, T> entry : modifiedItems.entrySet()) {
if (entry.getValue().equals(entry.getKey())) {
return true;
}
}
}
// is order changed or new items added?
if (items.size() == oldItems.size()) {
for (int i = 0, size = items.size(); i < size; i++) {
if (items.get(i) != oldItems.get(i)) {
return true;
}
}
}
else {
return true;
}
return false;
return model.isModified(oldItems);
}
@NotNull
public List<T> apply() {
if (!modifiedItems.isEmpty()) {
isApplying = true;
@SuppressWarnings("unchecked")
final ColumnInfo<T, Object>[] columns = ((ListTableModel)table.getModel()).getColumnInfos();
modifiedItems.forEachEntry(new TObjectObjectProcedure<T, T>() {
@Override
public boolean execute(T item, T newItem) {
for (ColumnInfo<T, Object> column : columns) {
if (column.isCellEditable(item)) {
column.setValue(item, column.valueOf(newItem));
}
}
return true;
}
});
isApplying = false;
modifiedItems.clear();
}
return items;
return model.apply();
}
public void clear() {
modifiedItems.clear();
model.modifiedToOriginal.clear();
}
}