001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.widgets; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.Dimension; 007import java.awt.GridBagConstraints; 008import java.awt.GridBagLayout; 009import java.awt.Insets; 010 011import javax.swing.BorderFactory; 012import javax.swing.JLabel; 013import javax.swing.JPanel; 014import javax.swing.event.DocumentEvent; 015import javax.swing.event.DocumentListener; 016import javax.swing.text.JTextComponent; 017 018import org.openstreetmap.josm.data.Bounds; 019import org.openstreetmap.josm.data.coor.CoordinateFormat; 020import org.openstreetmap.josm.data.coor.LatLon; 021import org.openstreetmap.josm.tools.GBC; 022import org.openstreetmap.josm.tools.OsmUrlToBounds; 023 024/** 025 * 026 * 027 */ 028public class BoundingBoxSelectionPanel extends JPanel { 029 030 private JosmTextField[] tfLatLon = null; 031 private final JosmTextField tfOsmUrl = new JosmTextField(); 032 033 protected void buildInputFields() { 034 tfLatLon = new JosmTextField[4]; 035 for(int i=0; i< 4; i++) { 036 tfLatLon[i] = new JosmTextField(11); 037 tfLatLon[i].setMinimumSize(new Dimension(100,new JosmTextField().getMinimumSize().height)); 038 SelectAllOnFocusGainedDecorator.decorate(tfLatLon[i]); 039 } 040 LatitudeValidator.decorate(tfLatLon[0]); 041 LatitudeValidator.decorate(tfLatLon[2]); 042 LongitudeValidator.decorate(tfLatLon[1]); 043 LongitudeValidator.decorate(tfLatLon[3]); 044 } 045 046 protected void build() { 047 buildInputFields(); 048 setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 049 setLayout(new GridBagLayout()); 050 tfOsmUrl.getDocument().addDocumentListener(new OsmUrlRefresher()); 051 052 // select content on receiving focus. this seems to be the default in the 053 // windows look+feel but not for others. needs invokeLater to avoid strange 054 // side effects that will cancel out the newly made selection otherwise. 055 tfOsmUrl.addFocusListener(new SelectAllOnFocusGainedDecorator()); 056 057 add(new JLabel(tr("Min. latitude")), GBC.std().insets(0,0,3,5)); 058 add(tfLatLon[0], GBC.std().insets(0,0,3,5)); 059 add(new JLabel(tr("Min. longitude")), GBC.std().insets(0,0,3,5)); 060 add(tfLatLon[1], GBC.eol()); 061 add(new JLabel(tr("Max. latitude")), GBC.std().insets(0,0,3,5)); 062 add(tfLatLon[2], GBC.std().insets(0,0,3,5)); 063 add(new JLabel(tr("Max. longitude")), GBC.std().insets(0,0,3,5)); 064 add(tfLatLon[3], GBC.eol()); 065 066 GridBagConstraints gc = new GridBagConstraints(); 067 gc.gridx = 0; 068 gc.gridy = 2; 069 gc.gridwidth = 4; 070 gc.fill = GridBagConstraints.HORIZONTAL; 071 gc.weightx = 1.0; 072 gc.insets = new Insets(10,0,0,3); 073 add(new JMultilineLabel(tr("URL from www.openstreetmap.org (you can paste a download URL here to specify a bounding box)")), gc); 074 075 gc.gridy = 3; 076 gc.insets = new Insets(3,0,0,3); 077 add(tfOsmUrl, gc); 078 } 079 080 public BoundingBoxSelectionPanel() { 081 build(); 082 } 083 084 public void setBoundingBox(Bounds area) { 085 updateBboxFields(area); 086 } 087 088 public Bounds getBoundingBox() { 089 double minlon, minlat, maxlon,maxlat; 090 try { 091 minlat = Double.parseDouble(tfLatLon[0].getText().trim()); 092 minlon = Double.parseDouble(tfLatLon[1].getText().trim()); 093 maxlat = Double.parseDouble(tfLatLon[2].getText().trim()); 094 maxlon = Double.parseDouble(tfLatLon[3].getText().trim()); 095 } catch(NumberFormatException e) { 096 return null; 097 } 098 if (!LatLon.isValidLon(minlon) || !LatLon.isValidLon(maxlon) 099 || !LatLon.isValidLat(minlat) || ! LatLon.isValidLat(maxlat)) 100 return null; 101 if (minlon > maxlon) 102 return null; 103 if (minlat > maxlat) 104 return null; 105 return new Bounds(minlon,minlat,maxlon,maxlat); 106 } 107 108 private boolean parseURL() { 109 Bounds b = OsmUrlToBounds.parse(tfOsmUrl.getText()); 110 if(b == null) return false; 111 updateBboxFields(b); 112 return true; 113 } 114 115 private void updateBboxFields(Bounds area) { 116 if (area == null) return; 117 tfLatLon[0].setText(area.getMin().latToString(CoordinateFormat.DECIMAL_DEGREES)); 118 tfLatLon[1].setText(area.getMin().lonToString(CoordinateFormat.DECIMAL_DEGREES)); 119 tfLatLon[2].setText(area.getMax().latToString(CoordinateFormat.DECIMAL_DEGREES)); 120 tfLatLon[3].setText(area.getMax().lonToString(CoordinateFormat.DECIMAL_DEGREES)); 121 } 122 123 static private class LatitudeValidator extends AbstractTextComponentValidator { 124 125 public static void decorate(JTextComponent tc) { 126 new LatitudeValidator(tc); 127 } 128 129 public LatitudeValidator(JTextComponent tc) { 130 super(tc); 131 } 132 133 @Override 134 public void validate() { 135 double value = 0; 136 try { 137 value = Double.parseDouble(getComponent().getText()); 138 } catch(NumberFormatException ex) { 139 feedbackInvalid(tr("The string ''{0}'' is not a valid double value.", getComponent().getText())); 140 return; 141 } 142 if (!LatLon.isValidLat(value)) { 143 feedbackInvalid(tr("Value for latitude in range [-90,90] required.", getComponent().getText())); 144 return; 145 } 146 feedbackValid(""); 147 } 148 149 @Override 150 public boolean isValid() { 151 double value = 0; 152 try { 153 value = Double.parseDouble(getComponent().getText()); 154 } catch(NumberFormatException ex) { 155 return false; 156 } 157 if (!LatLon.isValidLat(value)) 158 return false; 159 return true; 160 } 161 } 162 163 static private class LongitudeValidator extends AbstractTextComponentValidator{ 164 165 public static void decorate(JTextComponent tc) { 166 new LongitudeValidator(tc); 167 } 168 169 public LongitudeValidator(JTextComponent tc) { 170 super(tc); 171 } 172 173 @Override 174 public void validate() { 175 double value = 0; 176 try { 177 value = Double.parseDouble(getComponent().getText()); 178 } catch(NumberFormatException ex) { 179 feedbackInvalid(tr("The string ''{0}'' is not a valid double value.", getComponent().getText())); 180 return; 181 } 182 if (!LatLon.isValidLon(value)) { 183 feedbackInvalid(tr("Value for longitude in range [-180,180] required.", getComponent().getText())); 184 return; 185 } 186 feedbackValid(""); 187 } 188 189 @Override 190 public boolean isValid() { 191 double value = 0; 192 try { 193 value = Double.parseDouble(getComponent().getText()); 194 } catch(NumberFormatException ex) { 195 return false; 196 } 197 if (!LatLon.isValidLon(value)) 198 return false; 199 return true; 200 } 201 } 202 203 class OsmUrlRefresher implements DocumentListener { 204 @Override 205 public void changedUpdate(DocumentEvent e) { parseURL(); } 206 @Override 207 public void insertUpdate(DocumentEvent e) { parseURL(); } 208 @Override 209 public void removeUpdate(DocumentEvent e) { parseURL(); } 210 } 211}