package com.revolsys.swing.map.form; import java.awt.Dimension; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.swing.DefaultRowSorter; import javax.swing.DropMode; import javax.swing.JButton; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.ListModel; import javax.swing.ListSelectionModel; import javax.swing.RowFilter; import javax.swing.RowSorter; import javax.swing.SortOrder; import javax.swing.SwingConstants; import org.jdesktop.swingx.HorizontalLayout; import org.jdesktop.swingx.JXList; import org.jdesktop.swingx.VerticalLayout; import com.revolsys.swing.SwingUtil; import com.revolsys.swing.component.ValueField; import com.revolsys.swing.dnd.transferhandler.ListReorderableTransferHandler; import com.revolsys.swing.field.ArrayListComboBoxModel; import com.revolsys.swing.field.ComboBox; import com.revolsys.swing.field.SearchField; import com.revolsys.swing.list.ArrayListModel; import com.revolsys.swing.list.filter.StringContainsRowFilter; import com.revolsys.swing.map.layer.record.AbstractRecordLayer; import com.revolsys.swing.toolbar.ToolBar; import com.revolsys.util.Property; public class FieldNamesSetPanel extends ValueField implements ActionListener, PropertyChangeListener { private static final long serialVersionUID = 1L; public static String showDialog(final AbstractRecordLayer layer) { final FieldNamesSetPanel dialog = new FieldNamesSetPanel(layer); return dialog.showDialog(); } private final JButton addButton; private final JXList allFieldNames; private final ArrayListModel<String> allFieldNamesModel; private final StringContainsRowFilter allFieldNamesTextFilter; private final JButton deleteButton; private final ComboBox<String> fieldNamesSetNamesField; private final ArrayListComboBoxModel<String> fieldNamesSetNamesModel; private final Map<String, List<String>> fieldNamesSets; private final JPanel fieldsPanel; private final JPanel filterPanel; private final AbstractRecordLayer layer; private final JButton moveDownButton; private final JButton moveUpButton; private final JButton removeButton; private final JButton renameButton; private final JXList selectedFieldNames; private final ArrayListModel<String> selectedFieldNamesModel; public FieldNamesSetPanel(final AbstractRecordLayer layer) { super(new VerticalLayout(5), "fieldNamesSetName", layer.getFieldNamesSetName()); this.layer = layer; this.fieldNamesSets = new HashMap<>(layer.getFieldNamesSets()); final List<String> fieldNamesSetNames = this.layer.getFieldNamesSetNames(); this.fieldNamesSetNamesField = ComboBox.newComboBox("fieldNamesSetName", fieldNamesSetNames); this.fieldNamesSetNamesModel = this.fieldNamesSetNamesField.getComboBoxModel(); int maxLength = 3; for (final String name : fieldNamesSetNames) { maxLength = Math.max(maxLength, name.length()); } this.fieldNamesSetNamesField .setMaximumSize(new Dimension(Math.max(300, maxLength * 11 + 40), 22)); Property.addListener(this.fieldNamesSetNamesField, "fieldNamesSetName", this); final ToolBar toolBar = new ToolBar(); toolBar.setOpaque(false); toolBar.addComponent("default", this.fieldNamesSetNamesField); this.renameButton = toolBar.addButtonTitleIcon("default", "Rename Field Set", "fields_filter_edit", () -> actionRename()); this.deleteButton = toolBar.addButtonTitleIcon("default", "Delete Field Set", "fields_filter_delete", () -> actionDelete()); toolBar.addButtonTitleIcon("default", "Add Field Set", "fields_filter_add", () -> actionAdd()); add(toolBar); this.filterPanel = new JPanel(new HorizontalLayout(46)); this.filterPanel.setOpaque(false); add(this.filterPanel); final SearchField allFieldNamesFilterField = new SearchField("allFieldNamesFilter"); allFieldNamesFilterField.setPreferredSize(new Dimension(350, 25)); allFieldNamesFilterField.addActionListener(this); this.filterPanel.add(allFieldNamesFilterField); this.fieldsPanel = new JPanel(new HorizontalLayout(5)); this.fieldsPanel.setOpaque(false); this.allFieldNamesModel = new ArrayListModel<>(layer.getFieldNames()); this.allFieldNames = new JXList(this.allFieldNamesModel); this.allFieldNames.setAutoCreateRowSorter(true); this.allFieldNames.setSortable(true); this.allFieldNames.setSortOrder(SortOrder.ASCENDING); this.allFieldNames.addListSelectionListener(event -> updateEnabledState()); final JScrollPane layerPathsScrollPane = new JScrollPane(this.allFieldNames); layerPathsScrollPane.setPreferredSize(new Dimension(350, 400)); this.fieldsPanel.add(layerPathsScrollPane); final ToolBar fieldsToolBar = new ToolBar(SwingConstants.VERTICAL); fieldsToolBar.setOpaque(false); fieldsToolBar.setMinimumSize(new Dimension(25, 25)); this.fieldsPanel.add(fieldsToolBar); this.addButton = fieldsToolBar.addButtonTitleIcon("default", "Add", "add", () -> actionAddSelected()); this.removeButton = fieldsToolBar.addButtonTitleIcon("default", "Remove", "delete", () -> actionRemoveSelected()); this.moveUpButton = fieldsToolBar.addButtonTitleIcon("default", "Move Up", "arrow_up", () -> actionMoveSelectedUp()); this.moveDownButton = fieldsToolBar.addButtonTitleIcon("default", "Move Down", "arrow_down", () -> actionMoveSelectedDown()); this.selectedFieldNamesModel = new ArrayListModel<>(); this.selectedFieldNames = new JXList(this.selectedFieldNamesModel); this.selectedFieldNames.setAutoCreateRowSorter(false); this.selectedFieldNames.setSortable(false); this.selectedFieldNames.addListSelectionListener(event -> updateEnabledState()); this.selectedFieldNames.setDragEnabled(true); this.selectedFieldNames.setDropMode(DropMode.INSERT); this.selectedFieldNames .setTransferHandler(new ListReorderableTransferHandler(this.selectedFieldNames)); final JScrollPane snapScrollPane = new JScrollPane(this.selectedFieldNames); snapScrollPane.setPreferredSize(new Dimension(350, 400)); this.fieldsPanel.add(snapScrollPane); add(this.fieldsPanel); this.allFieldNamesTextFilter = new StringContainsRowFilter(); final RowFilter<ListModel, Integer> allFieldNamesFilter = RowFilter.andFilter(Arrays.asList( new CollectionRowFilter(this.selectedFieldNamesModel, false), this.allFieldNamesTextFilter)); this.allFieldNames.setRowFilter(allFieldNamesFilter); final String fieldNamesSetName = layer.getFieldNamesSetName(); setFieldNamesSetName(fieldNamesSetName); updateEnabledState(); } private void actionAdd() { final String name = JOptionPane.showInputDialog(SwingUtil.getActiveWindow(), "Enter the name of the new field set.", "Add Field Set", JOptionPane.PLAIN_MESSAGE); if (Property.hasValue(name)) { boolean found = false; for (int i = 0; i < this.fieldNamesSetNamesModel.size(); i++) { final String name2 = this.fieldNamesSetNamesModel.get(i); if (name2.equalsIgnoreCase(name)) { this.fieldNamesSetNamesModel.set(i, name); final List<String> names = this.fieldNamesSets.remove(name2); this.fieldNamesSets.put(name, names); found = true; } } if (!found) { this.fieldNamesSetNamesModel.add(name); this.fieldNamesSetNamesModel.setSelectedItem(name); } } } private void actionAddSelected() { this.selectedFieldNames.clearSelection(); int firstIndex = Integer.MAX_VALUE; for (final Object selectedValue : this.allFieldNames.getSelectedValues()) { int allIndex = this.allFieldNamesModel.indexOf(selectedValue); if (allIndex >= 0) { allIndex = this.allFieldNames.convertIndexToView(allIndex); if (allIndex < firstIndex) { firstIndex = allIndex; } } final String fieldName = (String)selectedValue; if (!this.selectedFieldNamesModel.contains(fieldName)) { this.selectedFieldNamesModel.add(fieldName); final int index = this.selectedFieldNames .convertIndexToView(this.selectedFieldNamesModel.indexOf(fieldName)); this.selectedFieldNames.addSelectionInterval(index, index); } this.allFieldNamesModel.remove(fieldName); } if (firstIndex == Integer.MAX_VALUE) { firstIndex = 0; } else if (firstIndex >= this.allFieldNamesModel.size()) { firstIndex = this.allFieldNamesModel.size() - 1; } this.allFieldNames.setSelectedIndex(firstIndex); updateEnabledState(); } private void actionDelete() { final String fieldSetName = this.fieldNamesSetNamesModel.getSelectedItem(); if ("All".equalsIgnoreCase(fieldSetName)) { Toolkit.getDefaultToolkit().beep(); } else { final int result = JOptionPane.showConfirmDialog(SwingUtil.getActiveWindow(), "Delete field set " + fieldSetName + ".", "Delete Field Set", JOptionPane.YES_NO_OPTION); if (result == JOptionPane.OK_OPTION) { for (int i = 0; i < this.fieldNamesSetNamesModel.size(); i++) { final String name2 = this.fieldNamesSetNamesModel.get(i); if (fieldSetName.equalsIgnoreCase(name2)) { this.fieldNamesSetNamesModel.remove(i); this.fieldNamesSets.remove(name2); } } this.fieldNamesSetNamesField.setSelectedItem("All"); } } } private void actionMoveSelectedDown() { final int selectedIndex = this.selectedFieldNames.getSelectedIndex(); if (selectedIndex < this.selectedFieldNamesModel.getSize() - 1) { final int newStartIndex = selectedIndex + 1; int newIndex = newStartIndex; final Object[] selectedValues = this.selectedFieldNames.getSelectedValues(); this.selectedFieldNamesModel.removeAll(selectedValues); for (final Object selectedValue : selectedValues) { final String fieldName = (String)selectedValue; this.selectedFieldNamesModel.add(newIndex++, fieldName); } final ListSelectionModel selectionModel = this.selectedFieldNames.getSelectionModel(); selectionModel.setSelectionInterval(newStartIndex, newIndex - 1); } updateEnabledState(); } private void actionMoveSelectedUp() { final int selectedIndex = this.selectedFieldNames.getSelectedIndex(); if (selectedIndex > 0) { final int newStartIndex = selectedIndex - 1; int newIndex = newStartIndex; final Object[] selectedValues = this.selectedFieldNames.getSelectedValues(); this.selectedFieldNamesModel.removeAll(selectedValues); for (final Object selectedValue : selectedValues) { final String fieldName = (String)selectedValue; this.selectedFieldNamesModel.add(newIndex++, fieldName); } final ListSelectionModel selectionModel = this.selectedFieldNames.getSelectionModel(); selectionModel.setSelectionInterval(newStartIndex, newIndex - 1); } updateEnabledState(); } @Override public void actionPerformed(final ActionEvent event) { final Object source = event.getSource(); if (source instanceof SearchField) { final SearchField field = (SearchField)source; final String fieldName = field.getFieldName(); final String text = field.getText(); if (fieldName.equals("allFieldNamesFilter")) { this.allFieldNamesTextFilter.setFilterText(text); sort(this.allFieldNames); } } } private void actionRemoveSelected() { final Object[] selectedValues = this.selectedFieldNames.getSelectedValues(); int firstIndex = Integer.MAX_VALUE; for (final Object selectedValue : selectedValues) { final int index = this.selectedFieldNamesModel.indexOf(selectedValue); if (index != -1) { if (index < firstIndex) { firstIndex = index; } } } this.selectedFieldNamesModel.removeAll(selectedValues); if (firstIndex == Integer.MAX_VALUE) { firstIndex = 0; } else if (firstIndex >= this.selectedFieldNamesModel.size()) { firstIndex = this.selectedFieldNamesModel.size() - 1; } this.selectedFieldNames.setSelectedIndex(firstIndex); for (final Object selectedValue : selectedValues) { final String fieldName = (String)selectedValue; this.allFieldNamesModel.add(fieldName); } sort(this.allFieldNames); updateEnabledState(); } private void actionRename() { final String oldName = this.fieldNamesSetNamesModel.getSelectedItem(); if ("All".equalsIgnoreCase(oldName)) { Toolkit.getDefaultToolkit().beep(); } else { final String newName = (String)JOptionPane.showInputDialog(SwingUtil.getActiveWindow(), "Enter the new name for the field set.", "Rename Field Set", JOptionPane.PLAIN_MESSAGE, null, null, oldName); if (Property.hasValue(newName)) { int index = -1; for (int i = 0; i < this.fieldNamesSetNamesModel.size(); i++) { final String name = this.fieldNamesSetNamesModel.get(i); if (oldName.equalsIgnoreCase(name)) { index = i; } else if (newName.equalsIgnoreCase(name)) { JOptionPane.showMessageDialog(SwingUtil.getActiveWindow(), "New name already in use: " + newName, "Rename Field Set", JOptionPane.ERROR_MESSAGE); return; } } this.fieldNamesSets.put(newName, new ArrayList<>(this.selectedFieldNamesModel)); this.fieldNamesSetNamesModel.set(index, newName); this.fieldNamesSetNamesModel.setSelectedItem(newName); } } } @Override public void propertyChange(final PropertyChangeEvent event) { final String propertyName = event.getPropertyName(); if (propertyName.equals("fieldNamesSetName")) { final String oldFieldNamesSetName = (String)event.getOldValue(); if (oldFieldNamesSetName != null) { this.fieldNamesSets.put(oldFieldNamesSetName, new ArrayList<>(this.selectedFieldNamesModel)); } final String newFieldNamesSetName = (String)event.getNewValue(); setFieldNamesSetName(newFieldNamesSetName); updateEnabledState(); } } @Override public void save() { super.save(); final List<String> namesToSave = new ArrayList<>(); for (int i = 0; i < this.selectedFieldNamesModel.size(); i++) { final String fieldName = this.selectedFieldNamesModel.get(i); namesToSave.add(fieldName); } final String fieldNamesSetName = this.fieldNamesSetNamesField.getSelectedItem(); this.fieldNamesSets.put(fieldNamesSetName, namesToSave); final Map<String, List<String>> fieldNamesSets = new LinkedHashMap<>(); for (final String name : this.fieldNamesSetNamesModel) { final List<String> fieldNames = this.fieldNamesSets.get(name); if (Property.hasValue(fieldNames)) { fieldNamesSets.put(name, fieldNames); } } this.layer.setFieldNamesSets(fieldNamesSets); } public void setFieldNamesSetName(String fieldNamesSetName) { if (Property.isEmpty(fieldNamesSetName) || !this.fieldNamesSetNamesModel.contains(fieldNamesSetName)) { fieldNamesSetName = "All"; } super.setFieldValue(fieldNamesSetName); final List<String> allFieldNames = new ArrayList<>(this.layer.getFieldNames()); List<String> selectedFieldNames = this.fieldNamesSets.get(fieldNamesSetName); if (selectedFieldNames == null) { selectedFieldNames = new ArrayList<>(); } allFieldNames.removeAll(selectedFieldNames); this.allFieldNamesModel.setAll(allFieldNames); this.allFieldNamesTextFilter.setFilterText(""); this.allFieldNames.setSelectedIndex(0); this.allFieldNames.setRowFilter(this.allFieldNamesTextFilter); this.selectedFieldNamesModel.setAll(selectedFieldNames); this.selectedFieldNames.setSelectedIndex(0); this.fieldNamesSetNamesField.setSelectedItem(fieldNamesSetName); } @SuppressWarnings("rawtypes") public void sort(final JXList list) { final RowSorter<? extends ListModel> rowSorter = list.getRowSorter(); if (rowSorter instanceof DefaultRowSorter) { final DefaultRowSorter<?, ?> sorter = (DefaultRowSorter<?, ?>)rowSorter; sorter.sort(); } } public void updateEnabledState() { final String fieldSetName = this.fieldNamesSetNamesModel.getSelectedItem(); final boolean editEnabled = !"All".equalsIgnoreCase(fieldSetName); this.deleteButton.setEnabled(editEnabled); this.renameButton.setEnabled(editEnabled); this.addButton.setEnabled(this.allFieldNames.getSelectedIndex() > -1); final int selectedFieldIndex = this.selectedFieldNames.getSelectedIndex(); final int lastSelectedFieldIndex = this.selectedFieldNames.getSelectionModel() .getMaxSelectionIndex(); this.removeButton.setEnabled(editEnabled && selectedFieldIndex > -1); this.moveUpButton.setEnabled(selectedFieldIndex > 0); this.moveDownButton.setEnabled(selectedFieldIndex > -1 && lastSelectedFieldIndex < this.selectedFieldNamesModel.getSize() - 1); } }