package com.revolsys.swing.map.layer.record.table; import java.awt.Component; import java.awt.Font; import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.HashMap; import java.util.Map; import javax.swing.BorderFactory; import javax.swing.Icon; import javax.swing.JButton; import javax.swing.JCheckBoxMenuItem; import javax.swing.JLabel; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JTable; import javax.swing.JToggleButton; import javax.swing.SwingUtilities; import javax.swing.table.TableCellEditor; import org.jdesktop.swingx.VerticalLayout; import com.revolsys.collection.map.LinkedHashMapEx; import com.revolsys.collection.map.MapEx; import com.revolsys.collection.map.Maps; import com.revolsys.io.map.MapSerializer; import com.revolsys.record.query.Condition; import com.revolsys.record.schema.RecordDefinition; import com.revolsys.swing.Icons; import com.revolsys.swing.action.ConsumerAction; import com.revolsys.swing.action.RunnableAction; import com.revolsys.swing.action.enablecheck.EnableCheck; import com.revolsys.swing.action.enablecheck.ObjectPropertyEnableCheck; import com.revolsys.swing.map.MapPanel; import com.revolsys.swing.map.form.FieldNamesSetPanel; import com.revolsys.swing.map.layer.AbstractLayer; import com.revolsys.swing.map.layer.record.AbstractRecordLayer; import com.revolsys.swing.map.layer.record.LayerRecord; import com.revolsys.swing.map.layer.record.LayerRecordMenu; import com.revolsys.swing.map.layer.record.component.FieldCalculator; import com.revolsys.swing.map.layer.record.component.FieldFilterPanel; import com.revolsys.swing.map.layer.record.component.SetRecordsFieldValue; import com.revolsys.swing.map.layer.record.table.model.RecordLayerTableModel; import com.revolsys.swing.map.layer.record.table.model.TableRecordsMode; import com.revolsys.swing.menu.BaseJPopupMenu; import com.revolsys.swing.menu.MenuFactory; import com.revolsys.swing.table.TablePanel; import com.revolsys.swing.table.TableRowCount; import com.revolsys.swing.table.record.RecordRowTable; import com.revolsys.swing.table.record.editor.RecordTableCellEditor; import com.revolsys.swing.toolbar.ToolBar; import com.revolsys.util.Property; public class RecordLayerTablePanel extends TablePanel implements PropertyChangeListener, MapSerializer { public static final String FILTER_FIELD = "filter_field"; public static final String FILTER_GEOMETRY = "filter_geometry"; private static final long serialVersionUID = 1L; private final Map<String, JToggleButton> buttonByMode = new HashMap<>(); private FieldFilterPanel fieldFilterPanel; private JButton fieldSetsButton; private AbstractRecordLayer layer; private RecordLayerTableModel tableModel; private final PropertyChangeListener viewportListener; public RecordLayerTablePanel(final AbstractRecordLayer layer, final RecordLayerTable table, final Map<String, Object> config) { super(table); this.layer = layer; this.tableModel = getTableModel(); Property.addListenerNewValueSource(this.tableModel, "tableRecordsMode", this::setTableRecordsMode); Property.addListenerNewValueSource(this.tableModel, "geometryFilterMode", this::setGeometryFilterMode); final Map<String, Object> pluginConfig = layer.getPluginConfig(AbstractLayer.PLUGIN_TABLE_VIEW); table.getTableCellEditor().addMouseListener(this); table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE); final MenuFactory headerMenu = getHeaderMenu(); SetRecordsFieldValue.addMenuItem(headerMenu); FieldCalculator.addMenuItem(headerMenu); final LayerRecordMenu menu = this.layer.getRecordMenu(); final RecordTableCellEditor tableCellEditor = table.getTableCellEditor(); tableCellEditor.setPopupMenu(menu::newJPopupMenu); newToolBar(pluginConfig); setPluginConfig(pluginConfig); layer.setPluginConfig(AbstractLayer.PLUGIN_TABLE_VIEW, this); Property.addListener(layer, this); this.viewportListener = (e) -> { if (this.tableModel != null && this.tableModel.isFilterByBoundingBox()) { this.tableModel.refresh(); } }; MapPanel.getMapPanel(layer).getViewport().addPropertyChangeListener("boundingBox", this.viewportListener); this.tableModel.refresh(); } private void actionExportRecords() { final RecordDefinition recordDefinition = getRecordDefinition(); final String title = this.layer.getName(); final boolean hasGeometryField = recordDefinition.hasGeometryField(); AbstractRecordLayer.exportRecords(title, hasGeometryField, this.tableModel::exportRecords); } private void actionShowFieldSetsMenu() { final JPopupMenu menu = new JPopupMenu(); final JMenuItem editMenuItem = RunnableAction.newMenuItem("Edit Field Sets", "fields_filter_edit", () -> { final String fieldNamesSetName = FieldNamesSetPanel.showDialog(this.layer); if (Property.hasValue(fieldNamesSetName)) { this.tableModel.setFieldNamesSetName(fieldNamesSetName); } }); menu.add(editMenuItem); menu.addSeparator(); final AbstractRecordLayer layer = getLayer(); final String selectedFieldSetName = layer.getFieldNamesSetName(); for (final String fieldSetName : layer.getFieldNamesSetNames()) { final JCheckBoxMenuItem menuItem = RunnableAction.newCheckBoxMenuItem(fieldSetName, () -> this.tableModel.setFieldNamesSetName(fieldSetName)); if (fieldSetName.equalsIgnoreCase(selectedFieldSetName)) { menuItem.setSelected(true); } menu.add(menuItem); } MenuFactory.showMenu(menu, this.fieldSetsButton, 10, 10); } protected JToggleButton addGeometryFilterToggleButton(final ToolBar toolBar, final int index, final String title, final String icon, final String mode, final EnableCheck enableCheck) { final JToggleButton button = toolBar.addToggleButtonTitleIcon(FILTER_GEOMETRY, index, title, icon, enableCheck, () -> setGeometryFilterMode(mode)); this.buttonByMode.put(FILTER_GEOMETRY + "_" + mode, button); return button; } @Override public void close() { final RecordLayerTable table = getTable(); if (table != null) { final RecordTableCellEditor tableCellEditor = table.getTableCellEditor(); tableCellEditor.close(); table.dispose(); } if (this.layer != null) { MapPanel.getMapPanel(this.layer).removePropertyChangeListener("boundingBox", this.viewportListener); Property.removeListener(this.layer, this); this.layer.setPluginConfig(AbstractLayer.PLUGIN_TABLE_VIEW, toMap()); this.layer = null; } this.tableModel = null; if (this.fieldFilterPanel != null) { this.fieldFilterPanel.close(); this.fieldFilterPanel = null; } } @Override public JPopupMenu getHeaderMenu(final int columnIndex) { final JPopupMenu headerMenu = super.getHeaderMenu(columnIndex); final String columnName = this.tableModel.getColumnName(columnIndex); final JMenuItem menuItem = new JMenuItem(); final JLabel title = new JLabel(columnName); title.setFont(menuItem.getFont().deriveFont(Font.BOLD)); title.setBackground(menuItem.getBackground()); title.setHorizontalAlignment(JLabel.CENTER); title.setHorizontalTextPosition(JLabel.CENTER); final JPanel labelPanel = new JPanel(new VerticalLayout()); labelPanel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5)); labelPanel.setOpaque(false); labelPanel.add(title); headerMenu.add(labelPanel, 0); headerMenu.add(new JPopupMenu.Separator(), 1); return headerMenu; } @Override protected RecordLayerTable getHeaderMenuSource() { return getTable(); } public AbstractRecordLayer getLayer() { return this.layer; } @Override protected Object getMenuSource() { return this.layer; } public RecordDefinition getRecordDefinition() { return this.layer.getRecordDefinition(); } @SuppressWarnings("unchecked") @Override public RecordLayerTable getTable() { return (RecordLayerTable)super.getTable(); } @SuppressWarnings("unchecked") @Override public RecordLayerTableModel getTableModel() { final JTable table = getTable(); return (RecordLayerTableModel)table.getModel(); } @Override public boolean isCurrentCellEditable() { return super.isCurrentCellEditable() && this.layer.isCanEditRecords(); } protected boolean isRecordDeleted(final LayerRecord record) { return getLayer().isDeleted(record); } @Override public void mouseClicked(final MouseEvent e) { super.mouseClicked(e); if (e.getClickCount() == 2 && SwingUtilities.isLeftMouseButton(e)) { if (isEditing()) { final JTable table = getTable(); final TableCellEditor cellEditor = table.getCellEditor(); cellEditor.stopCellEditing(); } final LayerRecord record = RecordRowTable.getEventRecord(); this.layer.showForm(record); } } protected void newToolBar(final Map<String, Object> pluginConfig) { final ToolBar toolBar = getToolBar(); final RecordDefinition recordDefinition = getRecordDefinition(); final boolean hasGeometry = recordDefinition.hasGeometryField(); final MenuFactory layerMenuFactory = MenuFactory.findMenu(this.layer); if (layerMenuFactory != null) { toolBar.addButtonTitleIcon("menu", "Layer Menu", "menu", () -> layerMenuFactory.showMenu(this.layer, this, 10, 10)); } if (hasGeometry) { final EnableCheck hasSelectedRecords = new ObjectPropertyEnableCheck(this.layer, "hasSelectedRecords"); toolBar.addButton("layer", "Zoom to Selected", "magnifier_zoom_selected", hasSelectedRecords, this.layer::zoomToSelected); toolBar.addButton("layer", "Pan to Selected", "pan_selected", hasSelectedRecords, this.layer::panToSelected); } final RecordLayerTable table = getTable(); final TableRowCount tableRowCount = new TableRowCount(table); toolBar.addComponent("count", tableRowCount); toolBar.addButtonTitleIcon("table", "Refresh", "table_refresh", this.tableModel::refresh); toolBar.addButtonTitleIcon("table", "Export Records", "table_save", new ObjectPropertyEnableCheck(tableRowCount, "rowCount", 0, true), () -> actionExportRecords()); this.fieldSetsButton = toolBar.addButtonTitleIcon("table", "Field Sets", "fields_filter", () -> actionShowFieldSetsMenu()); this.fieldFilterPanel = new FieldFilterPanel(this, this.tableModel, pluginConfig); if (this.fieldFilterPanel.isVisible()) { toolBar.addComponent("search", this.fieldFilterPanel); toolBar.addButtonTitleIcon("search", "Advanced Search", "filter_edits", this.fieldFilterPanel::showAdvancedFilter); final EnableCheck hasFilter = new ObjectPropertyEnableCheck(this.tableModel, "hasFilter"); toolBar.addButton("search", "Clear Search", "filter_delete", hasFilter, this.fieldFilterPanel::clear); final EnableCheck hasFilterHistory = new ObjectPropertyEnableCheck(this.tableModel, "hasFilterHistory"); toolBar.addButton("search", ConsumerAction.action("Search History", Icons.getIconWithBadge("book", "filter"), hasFilterHistory, (event) -> { final Object source = event.getSource(); Component component = null; if (source instanceof Component) { component = (Component)source; } final BaseJPopupMenu menu = new BaseJPopupMenu(); for (final Condition filter : this.tableModel.getFilterHistory()) { menu.addMenuItem(filter.toString(), () -> this.fieldFilterPanel.setFilter(filter)); } menu.showMenu(component, 0, 20); })); } // Filter buttons boolean first = true; for (final TableRecordsMode fieldFilterMode : this.tableModel.getFieldFilterModes()) { final String key = fieldFilterMode.getKey(); final String title = fieldFilterMode.getTitle(); final Icon icon = fieldFilterMode.getIcon(); final EnableCheck enableCheck = fieldFilterMode.getEnableCheck(); final JToggleButton button = toolBar.addToggleButton(FILTER_FIELD, -1, null, title, icon, enableCheck, () -> { if (this.tableModel != null) { this.tableModel.setTableRecordsMode(fieldFilterMode); } }); this.buttonByMode.put(FILTER_FIELD + "_" + key, button); if (first) { button.doClick(); first = false; } } if (hasGeometry) { final JToggleButton showAllGeometries = addGeometryFilterToggleButton(toolBar, -1, "Show All Records ", "world_filter", "all", null); showAllGeometries.doClick(); addGeometryFilterToggleButton(toolBar, -1, "Show Records on Map", "map_filter", "boundingBox", new ObjectPropertyEnableCheck(this.tableModel, "filterByBoundingBoxSupported")); } } @Override public void propertyChange(final PropertyChangeEvent event) { final String propertyName = event.getPropertyName(); final Object source = event.getSource(); if (source instanceof LayerRecord) { repaint(); } else if (source == this.layer) { if (propertyName.endsWith("Changed")) { this.tableModel.refresh(); } else if ("selectedRecordsByBoundingBox".equals(propertyName)) { this.fieldFilterPanel.clear(); } else { repaint(); } } } public void setFieldFilterMode(final String key) { final TableRecordsMode tableRecordsMode = this.tableModel.getTableRecordsMode(key); setTableRecordsMode(tableRecordsMode); } public void setGeometryFilterMode(final String geometryFilterMode) { final String mode = this.tableModel.setGeometryFilterMode(geometryFilterMode); final JToggleButton button = this.buttonByMode.get(FILTER_GEOMETRY + "_" + mode); if (button != null) { if (!button.isSelected()) { button.doClick(); } } } protected void setPluginConfig(final Map<String, Object> config) { final Object orderBy = config.get("orderBy"); if (orderBy instanceof Map) { @SuppressWarnings("unchecked") final Map<String, Boolean> order = (Map<String, Boolean>)orderBy; this.tableModel.setOrderBy(order); } final String tableRecordsMode = Maps.getString(config, "fieldFilterMode"); setFieldFilterMode(tableRecordsMode); final String geometryFilterMode = Maps.getString(config, "geometryFilterMode"); setGeometryFilterMode(geometryFilterMode); } private void setTableRecordsMode(TableRecordsMode tableRecordsMode) { if (!Property.hasValue(tableRecordsMode)) { tableRecordsMode = this.tableModel.getTableRecordsMode(); } final JToggleButton button = this.buttonByMode .get(FILTER_FIELD + "_" + tableRecordsMode.getKey()); if (button != null) { if (!button.isSelected()) { button.doClick(); } if (this.tableModel != null) { this.tableModel.setTableRecordsMode(tableRecordsMode); } } } @Override public MapEx toMap() { final MapEx map = new LinkedHashMapEx(); final String tableRecordsMode = this.tableModel.getTableRecordsMode().getKey(); addToMap(map, "fieldFilterMode", tableRecordsMode); final String geometryFilterMode = this.tableModel.getGeometryFilterMode(); addToMap(map, "geometryFilterMode", geometryFilterMode); if (this.fieldFilterPanel != null) { addToMap(map, "searchField", this.fieldFilterPanel.getSearchFieldName()); } addToMap(map, "orderBy", this.tableModel.getOrderBy()); return map; } }