/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2005-2008, Open Source Geospatial Foundation (OSGeo) * * 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; * version 2.1 of the License. * * 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. */ package org.geotools.gui.swing.referencing; import java.util.Locale; import java.util.List; import java.util.ArrayList; import java.util.Collection; import java.awt.BorderLayout; import java.awt.CardLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.ComboBoxModel; import javax.swing.DefaultComboBoxModel; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JInternalFrame; import javax.swing.JPanel; import javax.swing.JTextField; import org.opengis.referencing.AuthorityFactory; import org.opengis.referencing.FactoryException; import org.opengis.referencing.IdentifiedObject; import org.opengis.referencing.crs.CRSAuthorityFactory; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.geotools.resources.Classes; import org.geotools.resources.Arguments; import org.geotools.resources.SwingUtilities; import org.geotools.resources.i18n.Vocabulary; import org.geotools.resources.i18n.VocabularyKeys; import org.geotools.metadata.iso.citation.Citations; import org.geotools.referencing.ReferencingFactoryFinder; import org.geotools.referencing.factory.FallbackAuthorityFactory; import org.geotools.factory.FactoryRegistryException; import org.geotools.gui.swing.IconFactory; /** * A combox box for selecting a coordinate reference system from a list. This component also * provides a search button (for filtering the CRS name that contain the specified keywords) * and a info button displaying the CRS {@linkplain PropertiesSheet properties sheet}. * * @since 2.3 * @source $URL$ * @version $Id$ * @author Martin Desruisseaux (IRD) */ @SuppressWarnings("serial") public class AuthorityCodesComboBox extends JComponent { /** * The authority factory responsible for creating objects from a list of codes. */ private final AuthorityFactory factory; /** * The list of authority codes, as a combo box model. */ private final CodeList codeList; /** * The type of CRS object to includes in the list. */ private Class type; /** * The list of CRS objects. */ private final JComboBox list; /** * The text field for searching item. */ private final JTextField search; /** * The {@link #search} or {@link #list} field. */ private final JPanel searchOrList; /** * The card layout showing either {@link #list} or {@link #search}. */ private final CardLayout cards; /** * The button to press for showing properties. */ private final JButton showProperties; /** * Info about the currently selected item. */ private PropertiesSheet properties; /** * The window that contains {@link #properties}. */ private Component propertiesWindow; /** * Creates a CRS chooser backed by the EPSG authority factory. * * @throws FactoryRegistryException if no EPSG authority factory has been found. * @throws FactoryException if the factory can't provide CRS codes. */ public AuthorityCodesComboBox() throws FactoryRegistryException, FactoryException { this("EPSG"); } /** * Creates a CRS chooser backed by the specified authority factory. * * @param authority The authority identifier (e.g. {@code "EPSG"}). * @throws FactoryRegistryException if no authority factory has been found. * @throws FactoryException if the factory can't provide CRS codes. * * @since 2.4 */ public AuthorityCodesComboBox(final String authority) throws FactoryRegistryException, FactoryException { this(FallbackAuthorityFactory.create(CRSAuthorityFactory.class, filter(ReferencingFactoryFinder.getCRSAuthorityFactories(null), authority))); } /** * Returns a collection containing only the factories of the specified authority. */ private static Collection<CRSAuthorityFactory> filter( final Collection<? extends CRSAuthorityFactory> factories, final String authority) { final List<CRSAuthorityFactory> filtered = new ArrayList<CRSAuthorityFactory>(); for (final CRSAuthorityFactory factory : factories) { if (Citations.identifierMatches(factory.getAuthority(), authority)) { filtered.add(factory); } } return filtered; } /** * Creates a CRS chooser backed by the specified authority factory. * * @param factory The authority factory responsible for creating objects from a list of codes. * @throws FactoryException if the factory can't provide CRS codes. */ public AuthorityCodesComboBox(final AuthorityFactory factory) throws FactoryException { this(factory, CoordinateReferenceSystem.class); } /** * Creates a CRS chooser backed by the specified authority factory. * * @param factory The authority factory responsible for creating objects from a list of codes. * @param type The type of CRS object to includes in the list. * @throws FactoryException if the factory can't provide CRS codes. */ public AuthorityCodesComboBox(final AuthorityFactory factory, final Class type) throws FactoryException { this.factory = factory; this.type = type; final Locale locale = SwingUtilities.getLocale(this); final Vocabulary resources = Vocabulary.getResources(locale); setLayout(new BorderLayout()); cards = new CardLayout(); searchOrList = new JPanel(cards); codeList = new CodeList(factory, type); list = new JComboBox(codeList); list.setPrototypeDisplayValue("Unknown datum based upon the Average Terrestrial System 1977 ellipsoid"); search = new JTextField(); search.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent event) { search(false); } }); searchOrList.add(list, "List"); searchOrList.add(search, "Search"); add(searchOrList, BorderLayout.CENTER); /* * Adds the "Info" button. */ JButton button; final Dimension size = new Dimension(24, 20); final IconFactory icons = IconFactory.DEFAULT; String label = resources.getString(VocabularyKeys.INFORMATIONS); button = icons.getButton("toolbarButtonGraphics/general/Information16.gif", label, label); button.setFocusable(false); button.setPreferredSize(size); button.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent event) { showProperties(); } }); add(button, BorderLayout.WEST); showProperties = button; /* * Adds the "Search" button. */ label = resources.getString(VocabularyKeys.SEARCH); button = icons.getButton("toolbarButtonGraphics/general/Find16.gif", label, label); button.setFocusable(false); button.setPreferredSize(size); button.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent event) { search(true); } }); add(button, BorderLayout.EAST); } /** * Returns the authority name. Useful for providing a window title for example. */ public String getAuthority() { final Locale locale = SwingUtilities.getLocale(this); return factory.getAuthority().getTitle().toString(locale); } /** * Returns the code for the selected object, or {@code null} if none. */ public String getSelectedCode() { final Code code = (Code) list.getModel().getSelectedItem(); return (code != null) ? code.code : null; } /** * Returns the selected object, usually as a {@link CoordinateReferenceSystem}. * * @throws FactoryException if the factory can't create the selected object. */ public IdentifiedObject getSelectedItem() throws FactoryException { final String code = getSelectedCode(); return (code != null) ? factory.createObject(code) : null; } /** * Display information about the currently selected item in a separated window. * The default implementation show the <cite>Well Know Text</cite>. */ public void showProperties() { if (properties == null) { properties = new PropertiesSheet(); } IdentifiedObject item; try { item = getSelectedItem(); } catch (FactoryException e) { String message = e.getLocalizedMessage(); if (message == null) { message = Classes.getShortClassName(e); } properties.setErrorMessage(message); return; } final String title = item.getName().getCode(); if (propertiesWindow == null) { propertiesWindow = SwingUtilities.toFrame(this, properties, title, null); if (propertiesWindow instanceof JFrame) { ((JFrame) propertiesWindow).setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); } else if (propertiesWindow instanceof JInternalFrame) { ((JInternalFrame) propertiesWindow).setDefaultCloseOperation(JInternalFrame.HIDE_ON_CLOSE); } propertiesWindow.setSize(600, 500); } else { SwingUtilities.setTitle(propertiesWindow, title); } properties.setIdentifiedObject(item); propertiesWindow.setVisible(true); } /** * Enable or disable the search field. */ private void search(final boolean enable) { final JComponent component; final String name; if (enable) { component = search; name = "Search"; } else { component = list; name = "List"; filter(search.getText()); } showProperties.setEnabled(!enable); cards.show(searchOrList, name); component.requestFocus(); } /** * Display only the CRS name that contains the specified keywords. The {@code keywords} * argument is a space-separated list, usually provided by the user after he pressed the * "Search" button. * * @param keywords space-separated list of keywords to look for. */ public void filter(String keywords) { ComboBoxModel model = codeList; if (keywords != null) { final Locale locale = SwingUtilities.getLocale(this); keywords = keywords.toLowerCase(locale).trim(); final String[] tokens = keywords.split("\\s+"); if (tokens.length != 0) { final DefaultComboBoxModel filtered; model = filtered = new DefaultComboBoxModel(); final int size = codeList.getSize(); scan: for (int i=0; i<size; i++) { final Code code = (Code) codeList.getElementAt(i); final String name = code.toString().toLowerCase(locale); for (int j=0; j<tokens.length; j++) { if (name.indexOf(tokens[j]) < 0) { continue scan; } } filtered.addElement(code); } } } list.setModel(model); } /** * Display the chooser. This method is provided mainly for testing purpose. * <p> * If the {@code -prototype} argument is provided on the command line, then this method * display the longuest CRS name found in the database. This is useful for setting the * combo box {@linkplain JComboBox#setPrototypeDisplayValue prototype display value}. * * @throws FactoryRegistryException if no EPSG authority factory has been found. * @throws FactoryException if the factory can't provide CRS codes. */ public static void main(String[] args) throws FactoryRegistryException, FactoryException { final Arguments arguments = new Arguments(args); final boolean prototype = arguments.getFlag("-prototype"); args = arguments.getRemainingArguments(0); final AuthorityCodesComboBox chooser = new AuthorityCodesComboBox(); if (prototype) { System.out.println(((CodeList) chooser.list.getModel()).getPrototypeItem()); } final JFrame frame = new JFrame(chooser.getAuthority()); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.add(chooser, BorderLayout.CENTER); frame.pack(); frame.setVisible(true); } }