001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2005-2006, Geotools Project Managment Committee (PMC)
005: * (C) 2005, Institut de Recherche pour le Développement
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: */
017: package org.geotools.gui.swing.referencing;
018:
019: // J2SE dependencies
020: import java.util.Locale;
021: import java.util.List;
022: import java.util.ArrayList;
023: import java.util.Iterator;
024: import java.util.Collection;
025: import java.awt.BorderLayout;
026: import java.awt.CardLayout;
027: import java.awt.Component;
028: import java.awt.Dimension;
029: import java.awt.event.ActionEvent;
030: import java.awt.event.ActionListener;
031: import javax.swing.ComboBoxModel;
032: import javax.swing.DefaultComboBoxModel;
033: import javax.swing.JButton;
034: import javax.swing.JComboBox;
035: import javax.swing.JComponent;
036: import javax.swing.JFrame;
037: import javax.swing.JInternalFrame;
038: import javax.swing.JPanel;
039: import javax.swing.JTextField;
040:
041: // OpenGIS dependencies
042: import org.opengis.referencing.AuthorityFactory;
043: import org.opengis.referencing.FactoryException;
044: import org.opengis.referencing.IdentifiedObject;
045: import org.opengis.referencing.crs.CRSAuthorityFactory;
046: import org.opengis.referencing.crs.CoordinateReferenceSystem;
047:
048: // Geotools dependencies
049: import org.geotools.resources.Arguments;
050: import org.geotools.resources.Utilities;
051: import org.geotools.resources.SwingUtilities;
052: import org.geotools.resources.i18n.Vocabulary;
053: import org.geotools.resources.i18n.VocabularyKeys;
054: import org.geotools.metadata.iso.citation.Citations;
055: import org.geotools.referencing.ReferencingFactoryFinder;
056: import org.geotools.referencing.factory.FallbackAuthorityFactory;
057: import org.geotools.factory.FactoryRegistryException;
058: import org.geotools.gui.swing.IconFactory;
059:
060: /**
061: * A combox box for selecting a coordinate reference system from a list. This component also
062: * provides a search button (for filtering the CRS name that contain the specified keywords)
063: * and a info button displaying the CRS {@linkplain PropertiesSheet properties sheet}.
064: *
065: * @since 2.3
066: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/extension/widgets-swing/src/main/java/org/geotools/gui/swing/referencing/AuthorityCodesComboBox.java $
067: * @version $Id: AuthorityCodesComboBox.java 25671 2007-05-29 16:52:56Z desruisseaux $
068: * @author Martin Desruisseaux
069: */
070: public class AuthorityCodesComboBox extends JComponent {
071: /**
072: * The authority factory responsible for creating objects from a list of codes.
073: */
074: private final AuthorityFactory factory;
075:
076: /**
077: * The list of authority codes, as a combo box model.
078: */
079: private final CodeList codeList;
080:
081: /**
082: * The type of CRS object to includes in the list.
083: */
084: private Class type;
085:
086: /**
087: * The list of CRS objects.
088: */
089: private final JComboBox list;
090:
091: /**
092: * The text field for searching item.
093: */
094: private final JTextField search;
095:
096: /**
097: * The {@link #search} or {@link #list} field.
098: */
099: private final JPanel searchOrList;
100:
101: /**
102: * The card layout showing either {@link #list} or {@link #search}.
103: */
104: private final CardLayout cards;
105:
106: /**
107: * The button to press for showing properties.
108: */
109: private final JButton showProperties;
110:
111: /**
112: * Info about the currently selected item.
113: */
114: private PropertiesSheet properties;
115:
116: /**
117: * The window that contains {@link #properties}.
118: */
119: private Component propertiesWindow;
120:
121: /**
122: * Creates a CRS chooser backed by the EPSG authority factory.
123: *
124: * @throws FactoryRegistryException if no EPSG authority factory has been found.
125: * @throws FactoryException if the factory can't provide CRS codes.
126: */
127: public AuthorityCodesComboBox() throws FactoryRegistryException,
128: FactoryException {
129: this ("EPSG");
130: }
131:
132: /**
133: * Creates a CRS chooser backed by the specified authority factory.
134: *
135: * @param authority The authority identifier (e.g. {@code "EPSG"}).
136: * @throws FactoryRegistryException if no authority factory has been found.
137: * @throws FactoryException if the factory can't provide CRS codes.
138: *
139: * @since 2.4
140: */
141: public AuthorityCodesComboBox(final String authority)
142: throws FactoryRegistryException, FactoryException {
143: // TODO: remove the cast when we will be allowed to compile for J2SE 1.5.
144: this ((CRSAuthorityFactory) FallbackAuthorityFactory.create(
145: CRSAuthorityFactory.class, filter(
146: ReferencingFactoryFinder
147: .getCRSAuthorityFactories(null),
148: authority)));
149: }
150:
151: /**
152: * Returns a collection containing only the factories of the specified authority.
153: */
154: private static Collection filter(final Collection factories,
155: final String authority) {
156: final List filtered = new ArrayList();
157: for (final Iterator it = factories.iterator(); it.hasNext();) {
158: final AuthorityFactory factory = (AuthorityFactory) it
159: .next();
160: if (Citations.identifierMatches(factory.getAuthority(),
161: authority)) {
162: filtered.add(factory);
163: }
164: }
165: return filtered;
166: }
167:
168: /**
169: * Creates a CRS chooser backed by the specified authority factory.
170: *
171: * @param factory The authority factory responsible for creating objects from a list of codes.
172: * @throws FactoryException if the factory can't provide CRS codes.
173: */
174: public AuthorityCodesComboBox(final AuthorityFactory factory)
175: throws FactoryException {
176: this (factory, CoordinateReferenceSystem.class);
177: }
178:
179: /**
180: * Creates a CRS chooser backed by the specified authority factory.
181: *
182: * @param factory The authority factory responsible for creating objects from a list of codes.
183: * @param type The type of CRS object to includes in the list.
184: * @throws FactoryException if the factory can't provide CRS codes.
185: */
186: public AuthorityCodesComboBox(final AuthorityFactory factory,
187: final Class type) throws FactoryException {
188: this .factory = factory;
189: this .type = type;
190: final Locale locale = SwingUtilities.getLocale(this );
191: final Vocabulary resources = Vocabulary.getResources(locale);
192:
193: setLayout(new BorderLayout());
194: cards = new CardLayout();
195: searchOrList = new JPanel(cards);
196: codeList = new CodeList(factory, type);
197: list = new JComboBox(codeList);
198: list
199: .setPrototypeDisplayValue("Unknown datum based upon the Average Terrestrial System 1977 ellipsoid");
200: search = new JTextField();
201: search.addActionListener(new ActionListener() {
202: public void actionPerformed(final ActionEvent event) {
203: search(false);
204: }
205: });
206: searchOrList.add(list, "List");
207: searchOrList.add(search, "Search");
208: add(searchOrList, BorderLayout.CENTER);
209: /*
210: * Adds the "Info" button.
211: */
212: JButton button;
213: final Dimension size = new Dimension(24, 20);
214: final IconFactory icons = IconFactory.DEFAULT;
215: String label = resources.getString(VocabularyKeys.INFORMATIONS);
216: button = icons.getButton(
217: "toolbarButtonGraphics/general/Information16.gif",
218: label, label);
219: button.setFocusable(false);
220: button.setPreferredSize(size);
221: button.addActionListener(new ActionListener() {
222: public void actionPerformed(final ActionEvent event) {
223: showProperties();
224: }
225: });
226: add(button, BorderLayout.WEST);
227: showProperties = button;
228: /*
229: * Adds the "Search" button.
230: */
231: label = resources.getString(VocabularyKeys.SEARCH);
232: button = icons.getButton(
233: "toolbarButtonGraphics/general/Find16.gif", label,
234: label);
235: button.setFocusable(false);
236: button.setPreferredSize(size);
237: button.addActionListener(new ActionListener() {
238: public void actionPerformed(final ActionEvent event) {
239: search(true);
240: }
241: });
242: add(button, BorderLayout.EAST);
243: }
244:
245: /**
246: * Returns the authority name. Useful for providing a window title for example.
247: */
248: public String getAuthority() {
249: final Locale locale = SwingUtilities.getLocale(this );
250: return factory.getAuthority().getTitle().toString(locale);
251: }
252:
253: /**
254: * Returns the code for the selected object, or {@code null} if none.
255: */
256: public String getSelectedCode() {
257: final Code code = (Code) list.getModel().getSelectedItem();
258: return (code != null) ? code.code : null;
259: }
260:
261: /**
262: * Returns the selected object, usually as a {@link CoordinateReferenceSystem}.
263: *
264: * @throws FactoryException if the factory can't create the selected object.
265: */
266: public IdentifiedObject getSelectedItem() throws FactoryException {
267: final String code = getSelectedCode();
268: return (code != null) ? factory.createObject(code) : null;
269: }
270:
271: /**
272: * Display information about the currently selected item in a separated window.
273: * The default implementation show the <cite>Well Know Text</cite>.
274: */
275: public void showProperties() {
276: if (properties == null) {
277: properties = new PropertiesSheet();
278: }
279: IdentifiedObject item;
280: try {
281: item = getSelectedItem();
282: } catch (FactoryException e) {
283: String message = e.getLocalizedMessage();
284: if (message == null) {
285: message = Utilities.getShortClassName(e);
286: }
287: properties.setErrorMessage(message);
288: return;
289: }
290: final String title = item.getName().getCode();
291: if (propertiesWindow == null) {
292: propertiesWindow = SwingUtilities.toFrame(this , properties,
293: title, null);
294: if (propertiesWindow instanceof JFrame) {
295: ((JFrame) propertiesWindow)
296: .setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
297: } else if (propertiesWindow instanceof JInternalFrame) {
298: ((JInternalFrame) propertiesWindow)
299: .setDefaultCloseOperation(JInternalFrame.HIDE_ON_CLOSE);
300: }
301: propertiesWindow.setSize(600, 500);
302: } else {
303: SwingUtilities.setTitle(propertiesWindow, title);
304: }
305: properties.setIdentifiedObject(item);
306: propertiesWindow.setVisible(true);
307: }
308:
309: /**
310: * Enable or disable the search field.
311: */
312: private void search(final boolean enable) {
313: final JComponent component;
314: final String name;
315: if (enable) {
316: component = search;
317: name = "Search";
318: } else {
319: component = list;
320: name = "List";
321: filter(search.getText());
322: }
323: showProperties.setEnabled(!enable);
324: cards.show(searchOrList, name);
325: component.requestFocus();
326: }
327:
328: /**
329: * Display only the CRS name that contains the specified keywords. The {@code keywords}
330: * argument is a space-separated list, usually provided by the user after he pressed the
331: * "Search" button.
332: *
333: * @param keywords space-separated list of keywords to look for.
334: */
335: public void filter(String keywords) {
336: ComboBoxModel model = codeList;
337: if (keywords != null) {
338: final Locale locale = SwingUtilities.getLocale(this );
339: keywords = keywords.toLowerCase(locale).trim();
340: final String[] tokens = keywords.split("\\s+");
341: if (tokens.length != 0) {
342: final DefaultComboBoxModel filtered;
343: model = filtered = new DefaultComboBoxModel();
344: final int size = codeList.getSize();
345: scan: for (int i = 0; i < size; i++) {
346: final Code code = (Code) codeList.getElementAt(i);
347: final String name = code.toString().toLowerCase(
348: locale);
349: for (int j = 0; j < tokens.length; j++) {
350: if (name.indexOf(tokens[j]) < 0) {
351: continue scan;
352: }
353: }
354: filtered.addElement(code);
355: }
356: }
357: }
358: list.setModel(model);
359: }
360:
361: /**
362: * Display the chooser. This method is provided mainly for testing purpose.
363: * <p>
364: * If the {@code -prototype} argument is provided on the command line, then this method
365: * display the longuest CRS name found in the database. This is useful for setting the
366: * combo box {@linkplain JComboBox#setPrototypeDisplayValue prototype display value}.
367: *
368: * @throws FactoryRegistryException if no EPSG authority factory has been found.
369: * @throws FactoryException if the factory can't provide CRS codes.
370: */
371: public static void main(String[] args)
372: throws FactoryRegistryException, FactoryException {
373: final Arguments arguments = new Arguments(args);
374: final boolean prototype = arguments.getFlag("-prototype");
375: args = arguments.getRemainingArguments(0);
376: final AuthorityCodesComboBox chooser = new AuthorityCodesComboBox();
377: if (prototype) {
378: System.out.println(((CodeList) chooser.list.getModel())
379: .getPrototypeItem());
380: }
381: final JFrame frame = new JFrame(chooser.getAuthority());
382: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
383: frame.add(chooser, BorderLayout.CENTER);
384:
385: frame.pack();
386: frame.setVisible(true);
387: }
388: }
|