/******************************************************************************* * Copyright (c) 2010 Stefan A. Tzeggai. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Lesser Public License v2.1 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * * Contributors: * Stefan A. Tzeggai - initial API and implementation ******************************************************************************/ package org.geopublishing.atlasViewer.swing; import java.awt.Component; import java.beans.PropertyChangeListener; import javax.swing.event.ListSelectionListener; import org.apache.log4j.Logger; import org.geopublishing.atlasViewer.GpCoreUtil; import org.geotools.map.MapLayer; import org.geotools.map.event.MapLayerEvent; import de.schmitzm.geotools.feature.AttributeTypeFilter; import de.schmitzm.geotools.gui.FeatureTablePane; import de.schmitzm.geotools.gui.SelectableFeatureTablePane; import de.schmitzm.geotools.gui.SelectableXMapPane; import de.schmitzm.geotools.map.event.MapLayerAdapter; import de.schmitzm.geotools.selection.StyledFeatureLayerSelectionModel; import de.schmitzm.geotools.selection.StyledLayerSelectionModel; import de.schmitzm.geotools.selection.StyledLayerSelectionModelSynchronizer; import de.schmitzm.geotools.selection.TableSelectionSynchronizer; import de.schmitzm.geotools.styling.StyledFeatureCollectionTableModel; import de.schmitzm.geotools.styling.StyledFeaturesInterface; import de.schmitzm.geotools.styling.StyledLayerInterface; import de.schmitzm.swing.AtlasDialog; import de.schmitzm.swing.SortableJTable; import de.schmitzm.swing.SwingUtil; /** * A dialog to show the attribute table of a vector layer. This class implements * a {@link PropertyChangeListener} which is connected to the * {@link StyledLayerInterface StyledLayerInterface's} * {@link StyledLayerSelectionModel} to keep the table selection synchronized to * other component's selection (e.g. Map or chart). */ public class AttributeTableJDialog extends AtlasDialog { static final Logger LOGGER = Logger.getLogger(AttributeTableJDialog.class); /** If a table will contain more than that many cells, the user will be warned **/ public static final int WARN_CELLS = 15000; private StyledFeatureCollectionTableModel model; /** Holds the table and preview of the dialog. */ private FeatureTablePane featureTablePane; /** * The external maplayer that shows the features. May be <code>null</code> */ protected final MapLayer mapLayer; private final MapLegend mapLegend; private StyledLayerSelectionModelSynchronizer synchronizer; private StyledFeatureLayerSelectionModel selectionModel; private MapLayerAdapter mapLayerChangedListener; private final Component owner; private final StyledFeaturesInterface<?> styledObj; /** * AtlasViewer.R("AttributeTable.dialog.title", styledObj .getTitle()) * * @param owner * Parent component * @param mapLayer * may be <code>null</code> * @param mapLegend * may be <code>null</code> */ public AttributeTableJDialog(Component owner, final StyledFeaturesInterface<?> styledObj, final MapLegend mapLegend) { super(owner, GpCoreUtil.R("AttributeTable.dialog.title", styledObj .getTitle())); this.owner = owner; this.styledObj = styledObj; this.mapLegend = mapLegend; this.mapLayer = mapLegend == null ? null : mapLegend .getMapLayerFor(styledObj.getId()); // make a check on howmany feateurs we have an print a warning if too // many int numCells = styledObj.getFeatureCollectionFiltered().size() * styledObj.getAttributeMetaDataMap().sortedValuesVisibleOnly() .size(); if (numCells > WARN_CELLS) { if (SwingUtil.askYesNo(owner, SwingUtil.R( "AttributeTable.dialog.warnTooManyCells", numCells)) == false){ dispose(); return; } } // SK: Sadly has no effect :-( .. Only for JFrames? // setIconImage(AtlasTaskPaneUI.ICON_TABLE.getImage()); getModel().setAttributeFilter(AttributeTypeFilter.NO_GEOMETRY); if (mapLegend != null) { /** * Add Synchronizer for selection id mapLegend != null. Will be * removed in close() */ StyledLayerSelectionModel<?> anySelectionModel = mapLegend .getRememberSelection(mapLayer.getTitle()); if (anySelectionModel instanceof StyledFeatureLayerSelectionModel) { selectionModel = (StyledFeatureLayerSelectionModel) anySelectionModel; // create a synchronizer to keep the feature table selection // synchronized with the other components connected to the // DpLayerSelectionModel final SortableJTable table = getFeatureTablePane().getTable(); synchronizer = new TableSelectionSynchronizer( selectionModel, table); selectionModel.addSelectionListener(synchronizer); table.getSelectionModel().addListSelectionListener( (ListSelectionListener) synchronizer); selectionModel.refreshSelection(); } /** * Listen for FILTER changes of the target layer and update the * table accordingly */ mapLayerChangedListener = new MapLayerAdapter() { @Override public void layerChanged(MapLayerEvent arg0) { if (arg0.getReason() == MapLayerEvent.FILTER_CHANGED) { // TODO compare to old filter to see if anything has final StyledLayerSelectionModel<?> styledLayerSelectionModel = mapLegend.rememberSelectionModel .get(arg0.getSource()); if (styledLayerSelectionModel != null) { // If there exists a SelectionModel for this // MapLayer, // we have two options: Clear all selections, or // unselect the excluded elements.. We do the first // here. styledLayerSelectionModel.setValueIsAdjusting(true); styledLayerSelectionModel.clearSelection(); } // we can cast here, because we checked above getModel().setStyledFeatures( styledObj); if (styledLayerSelectionModel != null) { styledLayerSelectionModel .setValueIsAdjusting(false); } } } }; mapLayer.addMapLayerListener(mapLayerChangedListener); } // (mapLegend != null) initialize(); } @Override public boolean close() { /** * Remove Synchronizer for selection id mapLegend != null */ if (selectionModel != null && synchronizer != null) { selectionModel.removeSelectionListener(synchronizer); getFeatureTablePane().getTable().getSelectionModel() .removeListSelectionListener( (ListSelectionListener) synchronizer); } if (mapLegend != null) { if (mapLayerChangedListener != null) mapLayer.removeMapLayerListener(mapLayerChangedListener); } return super.close(); } private void initialize() { setContentPane(getFeatureTablePane()); pack(); SwingUtil.setRelativeFramePosition(this, owner, SwingUtil.BOUNDS_OUTER, SwingUtil.EAST); } private FeatureTablePane getFeatureTablePane() { if (featureTablePane == null) { // The attribute table can also be used without any // layerPanel or MapPane just from the DatapoolJTable SelectableXMapPane mapPane = mapLegend == null ? null : mapLegend .getGeoMapPane() != null ? mapLegend.getGeoMapPane() .getMapPane() : null; featureTablePane = new SelectableFeatureTablePane(getModel(), true, mapPane); } return featureTablePane; } public StyledFeatureCollectionTableModel getModel() { if (model == null) { model = new StyledFeatureCollectionTableModel(styledObj); } return model; } @Override public void dispose() { if (isDisposed) return; super.dispose(); } }