// License: GPL. Copyright 2007 by Immanuel Scholz and others package org.openstreetmap.josm.gui.widgets; import static org.openstreetmap.josm.tools.I18n.tr; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.Toolkit; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.FlavorEvent; import java.awt.datatransfer.FlavorListener; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.event.ActionEvent; import java.awt.event.MouseEvent; import java.io.IOException; import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JTextField; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.text.JTextComponent; import org.openstreetmap.josm.data.Bounds; import org.openstreetmap.josm.data.coor.CoordinateFormat; import org.openstreetmap.josm.data.coor.LatLon; import org.openstreetmap.josm.gui.JMultilineLabel; import org.openstreetmap.josm.tools.GBC; import org.openstreetmap.josm.tools.ImageProvider; import org.openstreetmap.josm.tools.OsmUrlToBounds; /** * * */ public class BoundingBoxSelectionPanel extends JPanel { private JTextField[] tfLatLon = null; private final JTextField tfOsmUrl = new JTextField(); protected void buildInputFields() { tfLatLon = new JTextField[4]; for(int i=0; i< 4; i++) { tfLatLon[i] = new JTextField(11); tfLatLon[i].setMinimumSize(new Dimension(100,new JTextField().getMinimumSize().height)); SelectAllOnFocusGainedDecorator.decorate(tfLatLon[i]); } LatitudeValidator.decorate(tfLatLon[0]); LatitudeValidator.decorate(tfLatLon[2]); LongitudeValidator.decorate(tfLatLon[1]); LongitudeValidator.decorate(tfLatLon[3]); } protected void build() { buildInputFields(); setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); setLayout(new GridBagLayout()); tfOsmUrl.getDocument().addDocumentListener(new OsmUrlRefresher()); // select content on receiving focus. this seems to be the default in the // windows look+feel but not for others. needs invokeLater to avoid strange // side effects that will cancel out the newly made selection otherwise. tfOsmUrl.addFocusListener(new SelectAllOnFocusGainedDecorator()); add(new JLabel(tr("Min. latitude")), GBC.std().insets(0,0,3,5)); add(tfLatLon[0], GBC.std().insets(0,0,3,5)); add(new JLabel(tr("Min. longitude")), GBC.std().insets(0,0,3,5)); add(tfLatLon[1], GBC.eol()); add(new JLabel(tr("Max. latitude")), GBC.std().insets(0,0,3,5)); add(tfLatLon[2], GBC.std().insets(0,0,3,5)); add(new JLabel(tr("Max. longitude")), GBC.std().insets(0,0,3,5)); add(tfLatLon[3], GBC.eol()); GridBagConstraints gc = new GridBagConstraints(); gc.gridx = 0; gc.gridy = 2; gc.gridwidth = 4; gc.fill = GridBagConstraints.HORIZONTAL; gc.weightx = 1.0; gc.insets = new Insets(10,0,0,3); add(new JMultilineLabel(tr("URL from www.openstreetmap.org (you can paste a download URL here to specify a bounding box)")), gc); gc.gridy = 3; gc.insets = new Insets(3,0,0,3); add(tfOsmUrl, gc); tfOsmUrl.addMouseListener(new PopupMenuLauncher() { @Override public void launch(MouseEvent e) { OsmUrlPopup popup = new OsmUrlPopup(); popup.show(tfOsmUrl, e.getX(), e.getY()); } }); } public BoundingBoxSelectionPanel() { build(); } public void setBoundingBox(Bounds area) { updateBboxFields(area); } public Bounds getBoundingBox() { double minlon, minlat, maxlon,maxlat; try { minlon = Double.parseDouble(tfLatLon[0].getText().trim()); minlat = Double.parseDouble(tfLatLon[1].getText().trim()); maxlon = Double.parseDouble(tfLatLon[2].getText().trim()); maxlat = Double.parseDouble(tfLatLon[3].getText().trim()); } catch(NumberFormatException e) { return null; } if (!LatLon.isValidLon(minlon) || !LatLon.isValidLon(maxlon) || !LatLon.isValidLat(minlat) || ! LatLon.isValidLat(maxlat)) return null; if (minlon > maxlon) return null; if (minlat > maxlat) return null; return new Bounds(minlon,minlat,maxlon,maxlat); } private boolean parseURL() { Bounds b = OsmUrlToBounds.parse(tfOsmUrl.getText()); if(b == null) return false; updateBboxFields(b); return true; } private void updateBboxFields(Bounds area) { if (area == null) return; tfLatLon[0].setText(area.getMin().latToString(CoordinateFormat.DECIMAL_DEGREES)); tfLatLon[1].setText(area.getMin().lonToString(CoordinateFormat.DECIMAL_DEGREES)); tfLatLon[2].setText(area.getMax().latToString(CoordinateFormat.DECIMAL_DEGREES)); tfLatLon[3].setText(area.getMax().lonToString(CoordinateFormat.DECIMAL_DEGREES)); } static private class LatitudeValidator extends AbstractTextComponentValidator { public static void decorate(JTextComponent tc) { new LatitudeValidator(tc); } public LatitudeValidator(JTextComponent tc) { super(tc); } @Override public void validate() { double value = 0; try { value = Double.parseDouble(getComponent().getText()); } catch(NumberFormatException ex) { feedbackInvalid(tr("The string ''{0}'' is not a valid double value.", getComponent().getText())); return; } if (!LatLon.isValidLat(value)) { feedbackInvalid(tr("Value for latitude in range [-90,90] required.", getComponent().getText())); return; } feedbackValid(""); } @Override public boolean isValid() { double value = 0; try { value = Double.parseDouble(getComponent().getText()); } catch(NumberFormatException ex) { return false; } if (!LatLon.isValidLat(value)) return false; return true; } } static private class LongitudeValidator extends AbstractTextComponentValidator{ public static void decorate(JTextComponent tc) { new LongitudeValidator(tc); } public LongitudeValidator(JTextComponent tc) { super(tc); } @Override public void validate() { double value = 0; try { value = Double.parseDouble(getComponent().getText()); } catch(NumberFormatException ex) { feedbackInvalid(tr("The string ''{0}'' is not a valid double value.", getComponent().getText())); return; } if (!LatLon.isValidLon(value)) { feedbackInvalid(tr("Value for longitude in range [-180,180] required.", getComponent().getText())); return; } feedbackValid(""); } @Override public boolean isValid() { double value = 0; try { value = Double.parseDouble(getComponent().getText()); } catch(NumberFormatException ex) { return false; } if (!LatLon.isValidLon(value)) return false; return true; } } class OsmUrlRefresher implements DocumentListener { public void changedUpdate(DocumentEvent e) { parseURL(); } public void insertUpdate(DocumentEvent e) { parseURL(); } public void removeUpdate(DocumentEvent e) { parseURL(); } } class PasteUrlAction extends AbstractAction implements FlavorListener { public PasteUrlAction() { putValue(NAME, tr("Paste")); putValue(SMALL_ICON, ImageProvider.get("paste")); putValue(SHORT_DESCRIPTION, tr("Paste URL from clipboard")); Toolkit.getDefaultToolkit().getSystemClipboard().addFlavorListener(this); } protected String getClipboardContent() { Transferable t = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null); try { if (t != null && t.isDataFlavorSupported(DataFlavor.stringFlavor)) { String text = (String)t.getTransferData(DataFlavor.stringFlavor); return text; } } catch (UnsupportedFlavorException ex) { ex.printStackTrace(); return null; } catch (IOException ex) { ex.printStackTrace(); return null; } return null; } public void actionPerformed(ActionEvent e) { String content = getClipboardContent(); if (content != null) { tfOsmUrl.setText(content); } } protected void updateEnabledState() { setEnabled(getClipboardContent() != null); } public void flavorsChanged(FlavorEvent e) { updateEnabledState(); } } class OsmUrlPopup extends JPopupMenu { public OsmUrlPopup() { add(new PasteUrlAction()); } } }