source: josm/trunk/src/org/openstreetmap/josm/gui/preferences/projection/CodeProjectionChoice.java@ 9609

Last change on this file since 9609 was 9609, checked in by bastiK, 8 years ago

rewrite of ProjectionRefTest - now covers all projections (see #12186)

  • Property svn:eol-style set to native
File size: 7.9 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.preferences.projection;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Dimension;
7import java.awt.GridBagLayout;
8import java.awt.event.ActionListener;
9import java.io.Serializable;
10import java.util.ArrayList;
11import java.util.Collection;
12import java.util.Collections;
13import java.util.Comparator;
14import java.util.List;
15import java.util.Locale;
16import java.util.regex.Matcher;
17import java.util.regex.Pattern;
18
19import javax.swing.AbstractListModel;
20import javax.swing.JList;
21import javax.swing.JPanel;
22import javax.swing.JScrollPane;
23import javax.swing.event.DocumentEvent;
24import javax.swing.event.DocumentListener;
25import javax.swing.event.ListSelectionEvent;
26import javax.swing.event.ListSelectionListener;
27
28import org.openstreetmap.josm.data.projection.Projection;
29import org.openstreetmap.josm.data.projection.Projections;
30import org.openstreetmap.josm.gui.widgets.JosmTextField;
31import org.openstreetmap.josm.tools.GBC;
32
33/**
34 * Projection choice that lists all known projects by code.
35 */
36public class CodeProjectionChoice extends AbstractProjectionChoice implements SubPrefsOptions {
37
38 private String code;
39
40 /**
41 * Constructs a new {@code CodeProjectionChoice}.
42 */
43 public CodeProjectionChoice() {
44 super(tr("By Code (EPSG)"), /* NO-ICON */ "core:code");
45 }
46
47 private static class CodeSelectionPanel extends JPanel implements ListSelectionListener, DocumentListener {
48
49 public JosmTextField filter;
50 private ProjectionCodeListModel model;
51 public JList<String> selectionList;
52 private final List<String> data;
53 private final List<String> filteredData;
54 private static final String DEFAULT_CODE = "EPSG:3857";
55 private String lastCode = DEFAULT_CODE;
56 private final transient ActionListener listener;
57
58 CodeSelectionPanel(String initialCode, ActionListener listener) {
59 this.listener = listener;
60 data = new ArrayList<>(Projections.getAllProjectionCodes());
61 Collections.sort(data, new CodeComparator());
62 filteredData = new ArrayList<>(data);
63 build();
64 setCode(initialCode != null ? initialCode : DEFAULT_CODE);
65 selectionList.addListSelectionListener(this);
66 }
67
68 /**
69 * List model for the filtered view on the list of all codes.
70 */
71 private class ProjectionCodeListModel extends AbstractListModel<String> {
72 @Override
73 public int getSize() {
74 return filteredData.size();
75 }
76
77 @Override
78 public String getElementAt(int index) {
79 if (index >= 0 && index < filteredData.size())
80 return filteredData.get(index);
81 else
82 return null;
83 }
84
85 public void fireContentsChanged() {
86 fireContentsChanged(this, 0, this.getSize()-1);
87 }
88 }
89
90 private void build() {
91 filter = new JosmTextField(30);
92 filter.setColumns(10);
93 filter.getDocument().addDocumentListener(this);
94
95 selectionList = new JList<>(data.toArray(new String[0]));
96 selectionList.setModel(model = new ProjectionCodeListModel());
97 JScrollPane scroll = new JScrollPane(selectionList);
98 scroll.setPreferredSize(new Dimension(200, 214));
99
100 this.setLayout(new GridBagLayout());
101 this.add(filter, GBC.eol().weight(1.0, 0.0));
102 this.add(scroll, GBC.eol());
103 }
104
105 public String getCode() {
106 int idx = selectionList.getSelectedIndex();
107 if (idx == -1) return lastCode;
108 return filteredData.get(selectionList.getSelectedIndex());
109 }
110
111 public final void setCode(String code) {
112 int idx = filteredData.indexOf(code);
113 if (idx != -1) {
114 selectionList.setSelectedIndex(idx);
115 selectionList.ensureIndexIsVisible(idx);
116 }
117 }
118
119 @Override
120 public void valueChanged(ListSelectionEvent e) {
121 listener.actionPerformed(null);
122 lastCode = getCode();
123 }
124
125 @Override
126 public void insertUpdate(DocumentEvent e) {
127 updateFilter();
128 }
129
130 @Override
131 public void removeUpdate(DocumentEvent e) {
132 updateFilter();
133 }
134
135 @Override
136 public void changedUpdate(DocumentEvent e) {
137 updateFilter();
138 }
139
140 private void updateFilter() {
141 filteredData.clear();
142 String filterTxt = filter.getText().trim().toLowerCase(Locale.ENGLISH);
143 for (String code : data) {
144 if (code.toLowerCase(Locale.ENGLISH).contains(filterTxt)) {
145 filteredData.add(code);
146 }
147 }
148 model.fireContentsChanged();
149 int idx = filteredData.indexOf(lastCode);
150 if (idx == -1) {
151 selectionList.clearSelection();
152 if (selectionList.getModel().getSize() > 0) {
153 selectionList.ensureIndexIsVisible(0);
154 }
155 } else {
156 selectionList.setSelectedIndex(idx);
157 selectionList.ensureIndexIsVisible(idx);
158 }
159 }
160 }
161
162 /**
163 * Comparator that compares the number part of the code numerically.
164 */
165 public static class CodeComparator implements Comparator<String>, Serializable {
166 private static final long serialVersionUID = 1L;
167 private final Pattern codePattern = Pattern.compile("([a-zA-Z]+):(\\d+)");
168
169 @Override
170 public int compare(String c1, String c2) {
171 Matcher matcher1 = codePattern.matcher(c1);
172 Matcher matcher2 = codePattern.matcher(c2);
173 if (matcher1.matches()) {
174 if (matcher2.matches()) {
175 int cmp1 = matcher1.group(1).compareTo(matcher2.group(1));
176 if (cmp1 != 0) return cmp1;
177 int num1 = Integer.parseInt(matcher1.group(2));
178 int num2 = Integer.parseInt(matcher2.group(2));
179 return Integer.compare(num1, num2);
180 } else
181 return -1;
182 } else if (matcher2.matches())
183 return 1;
184 return c1.compareTo(c2);
185 }
186 }
187
188 @Override
189 public Projection getProjection() {
190 return Projections.getProjectionByCode(code);
191 }
192
193 @Override
194 public String getCurrentCode() {
195 // not needed - getProjection() is overridden
196 throw new UnsupportedOperationException();
197 }
198
199 @Override
200 public String getProjectionName() {
201 // not needed - getProjection() is overridden
202 throw new UnsupportedOperationException();
203 }
204
205 @Override
206 public void setPreferences(Collection<String> args) {
207 if (args != null && !args.isEmpty()) {
208 code = args.iterator().next();
209 }
210 }
211
212 @Override
213 public JPanel getPreferencePanel(ActionListener listener) {
214 return new CodeSelectionPanel(code, listener);
215 }
216
217 @Override
218 public Collection<String> getPreferences(JPanel panel) {
219 if (!(panel instanceof CodeSelectionPanel)) {
220 throw new IllegalArgumentException("Unsupported panel: "+panel);
221 }
222 CodeSelectionPanel csPanel = (CodeSelectionPanel) panel;
223 return Collections.singleton(csPanel.getCode());
224 }
225
226 /* don't return all possible codes - this projection choice it too generic */
227 @Override
228 public String[] allCodes() {
229 return new String[0];
230 }
231
232 /* not needed since allCodes() returns empty array */
233 @Override
234 public Collection<String> getPreferencesFromCode(String code) {
235 return null;
236 }
237
238 @Override
239 public boolean showProjectionCode() {
240 return true;
241 }
242
243 @Override
244 public boolean showProjectionName() {
245 return true;
246 }
247
248}
Note: See TracBrowser for help on using the repository browser.