/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2008-2011, 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.swing.dialog; import java.awt.Dialog.ModalityType; import java.awt.EventQueue; import java.awt.Point; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.concurrent.SynchronousQueue; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.JViewport; import javax.swing.ListSelectionModel; import javax.swing.SwingUtilities; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import net.miginfocom.layout.LC; import net.miginfocom.swing.MigLayout; import org.geotools.referencing.CRS; import org.geotools.util.logging.Logging; import org.opengis.referencing.crs.CoordinateReferenceSystem; /** * This class has a single static method that shows a dialog to prompt * the user to choose a coordinate reference system. * <p> * Example of use: * <pre><code> * CoordinateReferenceSystem crs = JCRSChooser.showDialog( * null, // parent component * "Choose a projection"); // title * * if (crs != null) { * // use the CRS... * } * </code></pre> * * @author Michael Bedward * @since 2.6 * @source $URL$ * @version $Id$ */ public class JCRSChooser { private static final Logger LOGGER = Logging.getLogger("org.geotools.swing"); /** Default authority name (EPSG). */ public static final String DEFAULT_AUTHORITY = "EPSG"; private JDialog dialog; private CRSListModel model; private CoordinateReferenceSystem crs; /** * Displays a dialog with a list of coordinate reference systems in the EPSG * database. This method can be called safely from any thread. * * @param parent the parent {@code Component}; may be {@code null} * * @param title dialog title; if {@code null} title will be "Choose Projection" * * @return a {@code CoordinateReferenceSystem} object or {@code null} if the user * cancelled the dialog */ public static CoordinateReferenceSystem showDialog( final Window parent, final String title) { return showDialog(parent, title, null); } public static CoordinateReferenceSystem showDialog( final Window parent, final String title, final String initialCode) { return showDialog(parent, title, initialCode, null); } public static CoordinateReferenceSystem showDialog( final Window parent, final String title, final String initialCode, final String authority) { CoordinateReferenceSystem selected = null; if (SwingUtilities.isEventDispatchThread()) { selected = runDialog(parent, title, initialCode, authority); } else { final SynchronousQueue<CoordinateReferenceSystem> sq = new SynchronousQueue<CoordinateReferenceSystem>(); final Thread currentThread = Thread.currentThread(); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { try { CoordinateReferenceSystem crs = runDialog(parent, title, initialCode, authority); if (crs == null) { currentThread.interrupt(); } else { sq.put(crs); } } catch (InterruptedException ex) { throw new RuntimeException(ex); } } }); try { selected = sq.take(); } catch (InterruptedException ex) { // dialog was cancelled } } return selected; } /** * Creates and displays the dialog. Both arguments may be {@code null}. * * @param parent the parent window * @param title custom title * * @return the selected reference system or {@code null} if the dialog * is cancelled by the user */ private static CoordinateReferenceSystem runDialog( Window parent, String title, String initialCode, String authority) { JCRSChooser chooser = new JCRSChooser(parent, title, initialCode, authority); DialogUtils.showCentred(chooser.dialog); return chooser.crs; } /** * Private constructor. * * @param parent parent window (may be {@code null}) * @param title custom title (may be {@code null}) */ private JCRSChooser(Window parent, String title, String initialCode, String authority) { if (!EventQueue.isDispatchThread()) { throw new IllegalStateException( "Constructor should only be called on the event dispatch thread"); } dialog = new JDialog(); dialog.setModalityType(ModalityType.APPLICATION_MODAL); dialog.setResizable(false); dialog.setTitle( title == null ? "Choose Projection" : title ); final int WIDTH = 400; final String MARGIN = "10"; JPanel panel = new JPanel(new MigLayout( new LC().insetsAll(MARGIN))); if (authority == null) { authority = DEFAULT_AUTHORITY; } model = new CRSListModel(authority); panel.add(new JLabel("Enter sub-string to filter list"), "wrap"); final JTextField filterFld = new JTextField(); filterFld.getDocument().addDocumentListener(new DocumentListener() { @Override public void insertUpdate(DocumentEvent e) { model.setFilter(filterFld.getText()); } @Override public void removeUpdate(DocumentEvent e) { model.setFilter(filterFld.getText()); } @Override public void changedUpdate(DocumentEvent e) { model.setFilter(filterFld.getText()); } }); panel.add(filterFld, String.format("w %d!, wrap", WIDTH)); final JList listBox = new JList(model); listBox.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (e.getClickCount() == 2) { selectCRS(listBox.getSelectedIndex()); } } }); JScrollPane listPane = new JScrollPane(listBox); listBox.setBorder(BorderFactory.createEtchedBorder()); listBox.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); int initialIndex = model.findCode(initialCode); if (initialIndex >= 0) { listBox.setSelectedIndex(initialIndex); Point p = listBox.indexToLocation(initialIndex); JViewport port = listPane.getViewport(); port.setViewPosition(p); } panel.add(listPane, String.format("w %d!, h %d!, wrap", WIDTH, WIDTH / 2)); Action okAction = new AbstractAction("OK") { @Override public void actionPerformed(ActionEvent ae) { if (model.getSize() > 0) { if (model.getSize() == 1) { selectCRS(0); } else { int index = listBox.getSelectedIndex(); if (index >= 0) { selectCRS(index); } } } } }; JButton okBtn = new JButton(okAction); filterFld.setAction(okAction); panel.add(okBtn, "align center, split 2"); JButton cancelBtn = new JButton("Cancel"); cancelBtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent ae) { crs = null; closeDialog(); } }); panel.add(cancelBtn); dialog.add(panel); dialog.pack(); } private void selectCRS(int index) { String code = model.getCodeAt(index); try { crs = CRS.decode(DEFAULT_AUTHORITY + ":" + code, true); } catch (Exception ex) { LOGGER.log(Level.SEVERE, "Failed to get coordinate reference system for code {0}", code); } finally { closeDialog(); } } private void closeDialog() { dialog.dispose(); } }