//$HeadURL$ /*---------------- FILE HEADER ------------------------------------------ This file is part of deegree. Copyright (C) 2001-2012 by: Department of Geography, University of Bonn http://www.giub.uni-bonn.de/deegree/ lat/lon GmbH http://www.lat-lon.de This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Contact: lat/lon GmbH Aennchenstr. 19 53177 Bonn Germany E-Mail: info@lat-lon.de Prof. Dr. Klaus Greve Department of Geography University of Bonn Meckenheimer Allee 166 53115 Bonn Germany E-Mail: greve@giub.uni-bonn.de ---------------------------------------------------------------------------*/ package org.deegree.igeo.views.swing.objectinfo; import static java.awt.Cursor.DEFAULT_CURSOR; import static java.awt.Cursor.HAND_CURSOR; import static javax.swing.JOptionPane.QUESTION_MESSAGE; import static javax.swing.JOptionPane.showInputDialog; import static org.deegree.framework.log.LoggerFactory.getLogger; import static org.deegree.framework.util.StringTools.countString; import static org.deegree.igeo.i18n.Messages.get; import java.awt.BorderLayout; import java.awt.Container; import java.awt.Cursor; import java.awt.Point; import java.awt.Toolkit; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.ClipboardOwner; import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.Transferable; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import javax.swing.Icon; import javax.swing.JButton; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.JTable; import javax.swing.JTextArea; import javax.swing.JToolBar; import javax.swing.JTree; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.DefaultTableModel; import javax.swing.table.JTableHeader; import javax.swing.table.TableColumn; import org.deegree.framework.log.ILogger; import org.deegree.framework.xml.XMLFragment; import org.deegree.igeo.ApplicationContainer; import org.deegree.igeo.commands.SelectFeatureCommand; import org.deegree.igeo.commands.model.ZoomCommand; import org.deegree.igeo.config.ViewFormType; import org.deegree.igeo.dataadapter.DataAccessAdapter; import org.deegree.igeo.dataadapter.FeatureAdapter; import org.deegree.igeo.i18n.Messages; import org.deegree.igeo.mapmodel.Layer; import org.deegree.igeo.mapmodel.MapModel; import org.deegree.igeo.views.DialogFactory; import org.deegree.igeo.views.FeatureTable; import org.deegree.igeo.views.HelpManager; import org.deegree.igeo.views.swing.DefaultPanel; import org.deegree.igeo.views.swing.HelpFrame; import org.deegree.igeo.views.swing.util.IconRegistry; import org.deegree.igeo.views.swing.util.table.HeaderListener; import org.deegree.igeo.views.swing.util.table.SortButtonRenderer; import org.deegree.kernel.Command; import org.deegree.model.Identifier; import org.deegree.model.feature.Feature; import org.deegree.model.feature.FeatureCollection; import org.deegree.model.feature.GMLFeatureAdapter; import org.deegree.model.filterencoding.Filter; import org.deegree.model.spatialschema.Envelope; import org.deegree.model.spatialschema.GeometryException; /** * * The <code>FeatureTablePanel</code> class. TODO add class documentation here. * * @author <a href="mailto:wanhoff@lat-lon.de">Jeronimo Wanhoff</a> * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> * @author last edited by: $Author$ * * @version $Revision$, $Date$ * */ public class FeatureTablePanel extends DefaultPanel implements FeatureTable, ClipboardOwner, ListSelectionListener { static final Cursor defaultCursor = new Cursor( DEFAULT_CURSOR ); static final Cursor handCursor = new Cursor( HAND_CURSOR ); private static final long serialVersionUID = -3251085544494165934L; static final ILogger LOG = getLogger( FeatureTablePanel.class ); private static final String LINEBREAK = System.getProperty( "line.separator" ); private JToolBar jToolBar; private JPanel pnTable; private JButton btCopySelected; private JButton btCopyAll; private JButton btHelp; private JTabbedPane layersTabbedPane; private JTextArea taGML; private JScrollPane scGML; @SuppressWarnings("unused") private JTree treeView; private JButton btZoomto; @SuppressWarnings("unused") private JPanel pnTree; private JPanel pnGML; private JTable tabFeat; private JScrollPane featTableSC; private JTabbedPane viewsTabbedPane; private Layer layer; private FeatureCollection featureCollection; /** * */ public FeatureTablePanel() { initGUI(); } /* * (non-Javadoc) * * @see org.deegree.igeo.views.IView#init(org.deegree.igeo.config.ViewFormType) */ public void init( ViewFormType viewForm ) throws Exception { ApplicationContainer<Container> appContainer = this.owner.getApplicationContainer(); MapModel mapModel = appContainer.getMapModel( null ); List<Layer> layers = mapModel.getLayersSelectedForAction( MapModel.SELECTION_ACTION ); if ( layers.size() > 0 ) { Layer layer = layers.get( 0 ); List<DataAccessAdapter> data = layer.getDataAccess(); for ( DataAccessAdapter dataAccessAdapter : data ) { if ( dataAccessAdapter instanceof FeatureAdapter ) { FeatureCollection fc = ( (FeatureAdapter) dataAccessAdapter ).getFeatureCollection(); setFeatureCollection( layer, fc ); } } } } private void initGUI() { try { BorderLayout thisLayout = new BorderLayout(); this.setLayout( thisLayout ); this.setPreferredSize( new java.awt.Dimension( 814, 460 ) ); { jToolBar = new JToolBar(); this.add( jToolBar, BorderLayout.NORTH ); jToolBar.setPreferredSize( new java.awt.Dimension( 814, 35 ) ); { btCopySelected = new JButton( IconRegistry.getIcon( "copy.gif" ) ); btCopySelected.setToolTipText( Messages.getMessage( getLocale(), "$MD10541a" ) ); jToolBar.add( btCopySelected ); btCopySelected.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { int[] rows = tabFeat.getSelectedRows(); if ( rows != null && rows.length > 0 ) { copyToClipboard( false ); } } } ); } { btCopyAll = new JButton( IconRegistry.getIcon( "copy_all.gif" ) ); btCopyAll.setToolTipText( Messages.getMessage( getLocale(), "$MD10541b" ) ); jToolBar.add( btCopyAll ); btCopyAll.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { copyToClipboard( true ); } } ); } { btZoomto = new JButton( getImageIcon( "zoom.gif" ) ); btZoomto.setToolTipText( Messages.getMessage( getLocale(), "$MD10540" ) ); jToolBar.add( btZoomto ); btZoomto.addActionListener( new ZoomToActionListener() ); } { btHelp = new JButton( IconRegistry.getIcon( "help.png" ) ); jToolBar.add( btHelp ); btHelp.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { HelpFrame hf = HelpFrame.getInstance( new HelpManager( owner.getApplicationContainer() ) ); hf.setVisible( true ); hf.gotoModule( "LayerInfo" ); } } ); } } { layersTabbedPane = new JTabbedPane(); this.add( layersTabbedPane, BorderLayout.CENTER ); { viewsTabbedPane = new JTabbedPane(); layersTabbedPane.addTab( Messages.getMessage( getLocale(), "$MD11343" ), null, viewsTabbedPane, null ); viewsTabbedPane.setTabPlacement( JTabbedPane.LEFT ); { pnTable = new JPanel(); BorderLayout tablePanelLayout = new BorderLayout(); viewsTabbedPane.addTab( Messages.getMessage( getLocale(), "$MD11342" ), null, pnTable, null ); pnTable.setLayout( tablePanelLayout ); { { tabFeat = new JTable(); featTableSC = new JScrollPane( tabFeat ); tabFeat.setAutoResizeMode( JTable.AUTO_RESIZE_OFF ); tabFeat.getSelectionModel().addListSelectionListener( this ); tabFeat.addMouseMotionListener( new MouseMotionAdapter() { @Override public void mouseMoved( MouseEvent e ) { updateCursor( e ); } } ); tabFeat.addMouseListener( new MouseAdapter() { @Override public void mouseClicked( MouseEvent e ) { String val = getCellValue( e.getPoint() ); int count = 0; if ( val == null || ( count = countString( val, "http://" ) ) == 0 ) { return; } if ( count > 1 ) { String[] vals = val.split( "[ ,]" ); val = (String) showInputDialog( null, get( "$MD10896" ), get( "$DI10019" ), QUESTION_MESSAGE, null, vals, vals[0] ); if ( val == null ) { return; } } LinkedList<String> cmd = new LinkedList<String>(); if ( System.getProperty( "os.name" ).equalsIgnoreCase( "linux" ) ) { cmd.add( "firefox" ); } else { cmd.add( "cmd" ); cmd.add( "/c" ); cmd.add( "start" ); } cmd.add( val ); // leave the process alone, whatever happens ProcessBuilder pb = new ProcessBuilder( cmd ); try { pb.start(); } catch ( IOException e1 ) { LOG.logError( "Unknown error", e1 ); } } @Override public void mouseEntered( MouseEvent e ) { updateCursor( e ); } @Override public void mouseExited( MouseEvent e ) { updateCursor( null ); } } ); } pnTable.add( featTableSC, BorderLayout.CENTER ); } } { pnGML = new JPanel(); BorderLayout gmlPanelLayout = new BorderLayout(); pnGML.setLayout( gmlPanelLayout ); viewsTabbedPane.addTab( Messages.getMessage( getLocale(), "$MD11341" ), null, pnGML, null ); { scGML = new JScrollPane(); pnGML.add( scGML, BorderLayout.CENTER ); { taGML = new JTextArea(); taGML.setEditable( false ); scGML.setViewportView( taGML ); } } } { /* * pnTree = new JPanel(); BorderLayout treePanelLayout = new BorderLayout(); pnTree.setLayout( * treePanelLayout ); viewsTabbedPane.addTab( "tree view", null, pnTree, null ); { treeView = * new JTree(); pnTree.add( treeView, BorderLayout.CENTER ); } */ } } } } catch ( Exception e ) { e.printStackTrace(); } } String getCellValue( Point p ) { int column = tabFeat.columnAtPoint( p ); int row = tabFeat.rowAtPoint( p ); if ( column == -1 || row == -1 ) { return null; } Object val = tabFeat.getModel().getValueAt( row, column ); if ( val instanceof String ) { return (String) val; } return null; } void updateCursor( MouseEvent e ) { if ( e == null ) { setCursor( defaultCursor ); return; } String val = getCellValue( e.getPoint() ); if ( val != null && val.startsWith( "http://" ) ) { setCursor( handCursor ); } else { setCursor( defaultCursor ); } } /* * (non-Javadoc) * * @see java.awt.datatransfer.ClipboardOwner#lostOwnership(java.awt.datatransfer.Clipboard, * java.awt.datatransfer.Transferable) */ public void lostOwnership( Clipboard clipboard, Transferable contents ) { // TODO Auto-generated method stub } /** * sets a new FeatureCollection to be displayed with a {@link FeatureTablePanel} * * @param layer * @param featureCollection * must not be <code>null</code> */ public void setFeatureCollection( Layer layer, final FeatureCollection featureCollection ) { this.featureCollection = featureCollection; this.layer = layer; viewsTabbedPane.setName( layer.getTitle() ); DefaultTableModel ftm; if ( featureCollection.size() == 1 ) { ftm = new WMSFeatureTableModel( featureCollection ); } else { ftm = new FeatureTableModel( featureCollection ); } this.tabFeat.setModel( ftm ); markSelectedFeatures( layer, featureCollection ); this.tabFeat.invalidate(); for ( int i = 0; i < this.tabFeat.getColumnCount(); i++ ) { TableColumn column = this.tabFeat.getColumnModel().getColumn( i ); column.setPreferredWidth( 120 ); } SortButtonRenderer renderer = new SortButtonRenderer(); for ( int i = 0; i < this.tabFeat.getColumnCount(); i++ ) { TableColumn column = this.tabFeat.getColumnModel().getColumn( i ); column.setHeaderRenderer( renderer ); } JTableHeader header = tabFeat.getTableHeader(); header.addMouseListener( new FeatHeaderListener( header, renderer ) ); this.invalidate(); this.repaint(); Thread th = new Thread() { @Override public void run() { GMLFeatureAdapter ada = new GMLFeatureAdapter( true ); try { XMLFragment xml = ada.export( featureCollection ); taGML.setText( xml.getAsPrettyString() ); } catch ( Exception e ) { LOG.logDebug( "Stack trace", e ); DialogFactory.openErrorDialog( "Application", null, Messages.get( "$MD10538" ), Messages.get( "$MD10539" ), e ); } } }; th.start(); } private void markSelectedFeatures( Layer layer, final FeatureCollection featureCollection ) { // for reaster layers / datasource feature collection ist null if ( featureCollection != null ) { FeatureCollection fc = layer.getSelectedFeatures(); List<Integer> list = new ArrayList<Integer>(); Iterator<Feature> iterator = fc.iterator(); Feature[] features = featureCollection.toArray(); while ( iterator.hasNext() ) { Feature feature = (Feature) iterator.next(); String id = feature.getId(); for ( int i = 0; i < features.length; i++ ) { if ( features[i].getId().equals( id ) ) { list.add( i ); break; } } } for ( Integer c : list ) { this.tabFeat.getSelectionModel().addSelectionInterval( c, c ); } } } /** * @return list of features assigned to selected rows */ public List<Feature> getSelected() { int[] rows = tabFeat.getSelectedRows(); FeatureTableModel ftm = (FeatureTableModel) tabFeat.getModel(); FeatureCollection fc = ftm.getFeatureCollection(); List<Feature> list = new ArrayList<Feature>( rows.length ); for ( int i : rows ) { list.add( fc.getFeature( rows[i] ) ); } return list; } /* * (non-Javadoc) * * @see org.deegree.igeo.views.FeatureTable#select(org.deegree.model.filter.Filter) */ public void select( Filter filter ) { // TODO Auto-generated method stub } /* * (non-Javadoc) * * @see org.deegree.igeo.views.FeatureTable#select(org.deegree.model.feature.FeatureCollection) */ public void select( FeatureCollection featureCollection ) { if ( tabFeat.getModel() instanceof FeatureTableModel ) { FeatureCollection fc = ( (FeatureTableModel) tabFeat.getModel() ).getFeatureCollection(); // if selected layer/datasource is of raster type fc will be null if ( fc != null ) { Feature[] features = fc.toArray(); Iterator<Feature> iter = featureCollection.iterator(); tabFeat.getSelectionModel().removeListSelectionListener( this ); tabFeat.clearSelection(); while ( iter.hasNext() ) { String id = iter.next().getId(); for ( int i = 0; i < features.length; i++ ) { if ( features[i].getId().equals( id ) ) { tabFeat.addRowSelectionInterval( i, i ); break; } } } tabFeat.getSelectionModel().addListSelectionListener( this ); } } } /* * (non-Javadoc) * * @see javax.swing.event.ListSelectionListener#valueChanged(javax.swing.event.ListSelectionEvent) */ public void valueChanged( ListSelectionEvent e ) { // e.getValueIsAdjusting() is required to ensure that a selecting event just will be processed one time if ( e.getSource() == tabFeat.getSelectionModel() && tabFeat.getRowSelectionAllowed() && e.getValueIsAdjusting() ) { // Row selection changed int[] rows = tabFeat.getSelectedRows(); if ( tabFeat.getModel() instanceof FeatureTableModel ) { FeatureTableModel ftm = (FeatureTableModel) tabFeat.getModel(); FeatureCollection fc = ftm.getFeatureCollection(); List<Identifier> list = new ArrayList<Identifier>( rows.length ); for ( int i = 0; i < rows.length; i++ ) { list.add( new Identifier( fc.getFeature( rows[i] ).getId() ) ); } Command cmd = new SelectFeatureCommand( layer, list, false ); ApplicationContainer<Container> appContainer = owner.getApplicationContainer(); try { appContainer.getCommandProcessor().executeSychronously( cmd, true ); } catch ( Exception ex ) { LOG.logError( ex.getMessage(), ex ); } } } } private Icon getImageIcon( String image ) { URL url = FeatureTablePanel.class.getResource( image ); return IconRegistry.getIcon( url ); } /** * */ public void refresh() { setFeatureCollection( layer, featureCollection ); } /* * (non-Javadoc) * * @see org.deegree.igeo.views.FeatureTable#clear() */ public void clear() { this.tabFeat.setModel( new DefaultTableModel() ); } void copyToClipboard( boolean all ) { int[] rows = tabFeat.getSelectedRows(); if ( rows == null || rows.length == 0 || all ) { int rowCnt = tabFeat.getRowCount(); rows = new int[rowCnt]; for ( int i = 0; i < rows.length; i++ ) { rows[i] = i; } } int cols = tabFeat.getColumnCount(); StringBuilder sb = new StringBuilder( 10000 ); for ( int j = 0; j < cols; j++ ) { sb.append( tabFeat.getColumnName( j ) ); if ( j < cols ) { sb.append( "\t" ); } } sb.append( LINEBREAK ); for ( int i = 0; i < rows.length; i++ ) { for ( int j = 0; j < cols; j++ ) { sb.append( tabFeat.getValueAt( rows[i], j ) ); if ( j < cols ) { sb.append( "\t" ); } } if ( i < rows.length ) { sb.append( LINEBREAK ); } } StringSelection stringSelection = new StringSelection( sb.toString() ); Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); clipboard.setContents( stringSelection, FeatureTablePanel.this ); } // //////////////////////////////////////////////////////////////////////////////// // inner classes // //////////////////////////////////////////////////////////////////////////////// class FeatHeaderListener extends HeaderListener { /** * @param header * @param renderer */ public FeatHeaderListener( JTableHeader header, SortButtonRenderer renderer ) { super( header, renderer ); } public void mousePressed( MouseEvent e ) { JTable table = header.getTable(); if ( table.getModel() instanceof FeatureTableModel && e.getButton() == 1 ) { // just feature tables can be sorted int col = header.columnAtPoint( e.getPoint() ); int sortCol = table.convertColumnIndexToModel( col ); renderer.setPressedColumn( col ); renderer.setSelectedColumn( col ); header.repaint(); if ( header.getTable().isEditing() ) { header.getTable().getCellEditor().stopCellEditing(); } boolean isAscent; if ( SortButtonRenderer.DOWN == renderer.getState( col ) ) { isAscent = true; } else { isAscent = false; } ( (FeatureTableModel) table.getModel() ).sortByColumn( sortCol, isAscent ); FeatureTablePanel.this.invalidate(); FeatureTablePanel.this.getParent().repaint(); } } } class ZoomToActionListener implements ActionListener { public void actionPerformed( ActionEvent e ) { Envelope env = null; if ( tabFeat.getModel() instanceof FeatureTableModel ) { env = getEnvelopeFromFeatureTableModel( (FeatureTableModel) tabFeat.getModel() ); } else if ( tabFeat.getModel() instanceof WMSFeatureTableModel ) { env = getEnevelopeFromWmsFeatureTableModel( (WMSFeatureTableModel) tabFeat.getModel() ); } if ( env != null ) { zoomToSelected( env ); } } private Envelope getEnvelopeFromFeatureTableModel( FeatureTableModel ftm ) { int[] rows = tabFeat.getSelectedRows(); if ( rows != null && rows.length > 0 ) { FeatureCollection fc = ftm.getFeatureCollection(); try { Envelope env = fc.getFeature( rows[0] ).getBoundedBy(); for ( int i = 1; i < rows.length; i++ ) { env = env.merge( fc.getFeature( rows[i] ).getBoundedBy() ); } return expandEnvelopeIfItIsAPoint( fc, env ); } catch ( GeometryException e ) { LOG.logError( "Could not zoom to selected features: " + e.getMessage() ); LOG.logDebug( "Could not zoom to selected features!", e ); } } return null; } private Envelope getEnevelopeFromWmsFeatureTableModel( WMSFeatureTableModel tableModel ) { FeatureCollection fc = tableModel.getFeatureCollection(); try { Envelope env = fc.getBoundedBy(); return expandEnvelopeIfItIsAPoint( fc, env ); } catch ( GeometryException e ) { LOG.logError( "Could not zoom to selected features: " + e.getMessage() ); LOG.logDebug( "Could not zoom to selected features!", e ); } return null; } private Envelope expandEnvelopeIfItIsAPoint( FeatureCollection fc, Envelope env ) throws GeometryException { if ( env.getWidth() < 0.0001 ) { // selected object must be a Point; set bbox width/height to 1% // of the overall map width env = env.getBuffer( fc.getBoundedBy().getWidth() / 200d ); } if ( env.getWidth() < 0.0001 ) { // feature collection just contains one point; set fix bbox width/height // 25 map units env = env.getBuffer( 25 ); } return env; } private void zoomToSelected( Envelope env ) { ApplicationContainer<Container> appContainer = owner.getApplicationContainer(); MapModel mm = appContainer.getMapModel( null ); ZoomCommand cmd = new ZoomCommand( mm ); cmd.setZoomBox( env, mm.getTargetDevice().getPixelWidth(), mm.getTargetDevice().getPixelHeight() ); try { appContainer.getCommandProcessor().executeSychronously( cmd, true ); } catch ( Exception ex ) { LOG.logError( ex.getMessage(), ex ); } } } }