source: josm/trunk/src/org/openstreetmap/josm/gui/preferences/ImageryPreference.java@ 4551

Last change on this file since 4551 was 4551, checked in by stoecker, 12 years ago

store projections for self-added WMS entries

  • Property svn:eol-style set to native
File size: 41.5 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.preferences;
3
4import static org.openstreetmap.josm.tools.I18n.marktr;
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.awt.Color;
8import java.awt.Component;
9import java.awt.Dialog;
10import java.awt.Dimension;
11import java.awt.FlowLayout;
12import java.awt.Font;
13import java.awt.GridBagConstraints;
14import java.awt.GridBagLayout;
15import java.awt.Window;
16import java.awt.event.ActionEvent;
17import java.awt.event.ActionListener;
18import java.awt.event.HierarchyEvent;
19import java.awt.event.HierarchyListener;
20import java.awt.event.MouseEvent;
21import java.io.IOException;
22import java.net.MalformedURLException;
23import java.net.URL;
24import java.util.ArrayList;
25import java.util.HashMap;
26import java.util.HashSet;
27import java.util.List;
28import java.util.Locale;
29import java.util.Map;
30import java.util.Set;
31
32import javax.swing.AbstractAction;
33import javax.swing.BorderFactory;
34import javax.swing.Box;
35import javax.swing.JButton;
36import javax.swing.JCheckBox;
37import javax.swing.JColorChooser;
38import javax.swing.JComboBox;
39import javax.swing.JEditorPane;
40import javax.swing.JLabel;
41import javax.swing.JOptionPane;
42import javax.swing.JPanel;
43import javax.swing.JScrollPane;
44import javax.swing.JSeparator;
45import javax.swing.JSlider;
46import javax.swing.JSpinner;
47import javax.swing.JTabbedPane;
48import javax.swing.JTable;
49import javax.swing.JTextField;
50import javax.swing.JToolBar;
51import javax.swing.SpinnerNumberModel;
52import javax.swing.SwingUtilities;
53import javax.swing.event.ChangeEvent;
54import javax.swing.event.ChangeListener;
55import javax.swing.event.ListSelectionEvent;
56import javax.swing.event.ListSelectionListener;
57import javax.swing.event.TableModelEvent;
58import javax.swing.event.TableModelListener;
59import javax.swing.table.DefaultTableCellRenderer;
60import javax.swing.table.DefaultTableModel;
61import javax.swing.table.TableColumnModel;
62
63import org.openstreetmap.gui.jmapviewer.Coordinate;
64import org.openstreetmap.gui.jmapviewer.JMapViewer;
65import org.openstreetmap.gui.jmapviewer.MapPolygonImpl;
66import org.openstreetmap.gui.jmapviewer.MapRectangleImpl;
67import org.openstreetmap.gui.jmapviewer.interfaces.MapPolygon;
68import org.openstreetmap.gui.jmapviewer.interfaces.MapRectangle;
69import org.openstreetmap.josm.Main;
70import org.openstreetmap.josm.data.imagery.ImageryInfo;
71import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryBounds;
72import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType;
73import org.openstreetmap.josm.data.imagery.ImageryLayerInfo;
74import org.openstreetmap.josm.data.imagery.OffsetBookmark;
75import org.openstreetmap.josm.data.imagery.Shape;
76import org.openstreetmap.josm.gui.layer.ImageryLayer;
77import org.openstreetmap.josm.gui.layer.TMSLayer;
78import org.openstreetmap.josm.gui.layer.WMSLayer;
79import org.openstreetmap.josm.io.imagery.HTMLGrabber;
80import org.openstreetmap.josm.io.imagery.OffsetServer;
81import org.openstreetmap.josm.io.imagery.OsmosnimkiOffsetServer;
82import org.openstreetmap.josm.tools.ColorHelper;
83import org.openstreetmap.josm.tools.GBC;
84import org.openstreetmap.josm.tools.ImageProvider;
85
86public class ImageryPreference implements PreferenceSetting {
87 public static class Factory implements PreferenceSettingFactory {
88 @Override
89 public PreferenceSetting createPreferenceSetting() {
90 return new ImageryPreference();
91 }
92 }
93 ImageryProvidersPanel imageryProviders;
94 ImageryLayerInfo layerInfo;
95
96 // Common settings
97 private Color colFadeColor;
98 private JButton btnFadeColor;
99 private JSlider fadeAmount = new JSlider(0, 100);
100 private JComboBox sharpen;
101 private JCheckBox useOffsetServer;
102 private JTextField offsetServerUrl;
103
104 // WMS Settings
105 private JComboBox browser;
106 JCheckBox overlapCheckBox;
107 JSpinner spinEast;
108 JSpinner spinNorth;
109 JSpinner spinSimConn;
110
111 //TMS settings controls
112 private JCheckBox autozoomActive = new JCheckBox();
113 private JCheckBox autoloadTiles = new JCheckBox();
114 private JSpinner minZoomLvl;
115 private JSpinner maxZoomLvl;
116 private JCheckBox addToSlippyMapChosser = new JCheckBox();
117 private JTextField tilecacheDir = new JTextField();
118
119 private JPanel buildCommonSettingsPanel(final PreferenceTabbedPane gui) {
120 final JPanel p = new JPanel(new GridBagLayout());
121
122 this.colFadeColor = ImageryLayer.getFadeColor();
123 this.btnFadeColor = new JButton();
124
125 this.btnFadeColor.addActionListener(new ActionListener() {
126 @Override
127 public void actionPerformed(ActionEvent e) {
128 JColorChooser chooser = new JColorChooser(colFadeColor);
129 int answer = JOptionPane.showConfirmDialog(
130 gui, chooser,
131 tr("Choose a color for {0}", tr("imagery fade")),
132 JOptionPane.OK_CANCEL_OPTION,
133 JOptionPane.PLAIN_MESSAGE);
134 if (answer == JOptionPane.OK_OPTION) {
135 colFadeColor = chooser.getColor();
136 btnFadeColor.setBackground(colFadeColor);
137 btnFadeColor.setText(ColorHelper.color2html(colFadeColor));
138 }
139 }
140 });
141
142 p.add(new JLabel(tr("Fade Color: ")), GBC.std());
143 p.add(GBC.glue(5, 0), GBC.std().fill(GBC.HORIZONTAL));
144 p.add(this.btnFadeColor, GBC.eol().fill(GBC.HORIZONTAL));
145
146 p.add(new JLabel(tr("Fade amount: ")), GBC.std());
147 p.add(GBC.glue(5, 0), GBC.std().fill(GBC.HORIZONTAL));
148 p.add(this.fadeAmount, GBC.eol().fill(GBC.HORIZONTAL));
149
150 this.sharpen = new JComboBox(new String[] {
151 tr("None"),
152 tr("Soft"),
153 tr("Strong")});
154 p.add(new JLabel(tr("Sharpen (requires layer re-add): ")));
155 p.add(GBC.glue(5, 0), GBC.std().fill(GBC.HORIZONTAL));
156 p.add(this.sharpen, GBC.eol().fill(GBC.HORIZONTAL));
157
158 this.useOffsetServer = new JCheckBox(tr("Use offset server: "));
159 this.offsetServerUrl = new JTextField();
160 this.useOffsetServer.addChangeListener(new ChangeListener() {
161 @Override
162 public void stateChanged(ChangeEvent e) {
163 offsetServerUrl.setEnabled(useOffsetServer.isSelected());
164 }
165 });
166 offsetServerUrl.setEnabled(useOffsetServer.isSelected());
167 p.add(this.useOffsetServer, GBC.eol().fill(GBC.HORIZONTAL));
168 p.add(this.offsetServerUrl, GBC.eol().fill(GBC.HORIZONTAL));
169 return p;
170 }
171
172 private JPanel buildWMSSettingsPanel() {
173 final JPanel p = new JPanel(new GridBagLayout());
174 browser = new JComboBox(new String[] {
175 "webkit-image {0}",
176 "gnome-web-photo --mode=photo --format=png {0} /dev/stdout",
177 "gnome-web-photo-fixed {0}",
178 "webkit-image-gtk {0}"});
179 browser.setEditable(true);
180 p.add(new JLabel(tr("Downloader:")), GBC.eol().fill(GBC.HORIZONTAL));
181 p.add(browser);
182
183 // Overlap
184 p.add(Box.createHorizontalGlue(), GBC.eol().fill(GBC.HORIZONTAL));
185
186 overlapCheckBox = new JCheckBox(tr("Overlap tiles"));
187 JLabel labelEast = new JLabel(tr("% of east:"));
188 JLabel labelNorth = new JLabel(tr("% of north:"));
189 spinEast = new JSpinner(new SpinnerNumberModel(WMSLayer.PROP_OVERLAP_EAST.get(), 1, 50, 1));
190 spinNorth = new JSpinner(new SpinnerNumberModel(WMSLayer.PROP_OVERLAP_NORTH.get(), 1, 50, 1));
191
192 JPanel overlapPanel = new JPanel(new FlowLayout());
193 overlapPanel.add(overlapCheckBox);
194 overlapPanel.add(labelEast);
195 overlapPanel.add(spinEast);
196 overlapPanel.add(labelNorth);
197 overlapPanel.add(spinNorth);
198
199 p.add(overlapPanel);
200
201 // Simultaneous connections
202 p.add(Box.createHorizontalGlue(), GBC.eol().fill(GBC.HORIZONTAL));
203 JLabel labelSimConn = new JLabel(tr("Simultaneous connections"));
204 spinSimConn = new JSpinner(new SpinnerNumberModel(WMSLayer.PROP_SIMULTANEOUS_CONNECTIONS.get(), 1, 30, 1));
205 JPanel overlapPanelSimConn = new JPanel(new FlowLayout(FlowLayout.LEFT));
206 overlapPanelSimConn.add(labelSimConn);
207 overlapPanelSimConn.add(spinSimConn);
208 p.add(overlapPanelSimConn, GBC.eol().fill(GBC.HORIZONTAL));
209
210 return p;
211 }
212
213 private JPanel buildTMSSettingsPanel() {
214 JPanel tmsTab = new JPanel(new GridBagLayout());
215
216 minZoomLvl = new JSpinner(new SpinnerNumberModel(TMSLayer.DEFAULT_MIN_ZOOM, TMSLayer.MIN_ZOOM, TMSLayer.MAX_ZOOM, 1));
217 maxZoomLvl = new JSpinner(new SpinnerNumberModel(TMSLayer.DEFAULT_MAX_ZOOM, TMSLayer.MIN_ZOOM, TMSLayer.MAX_ZOOM, 1));
218
219 tmsTab.add(new JLabel(tr("Auto zoom by default: ")), GBC.std());
220 tmsTab.add(GBC.glue(5, 0), GBC.std());
221 tmsTab.add(autozoomActive, GBC.eol().fill(GBC.HORIZONTAL));
222
223 tmsTab.add(new JLabel(tr("Autoload tiles by default: ")), GBC.std());
224 tmsTab.add(GBC.glue(5, 0), GBC.std());
225 tmsTab.add(autoloadTiles, GBC.eol().fill(GBC.HORIZONTAL));
226
227 tmsTab.add(new JLabel(tr("Min. zoom level: ")), GBC.std());
228 tmsTab.add(GBC.glue(5, 0), GBC.std());
229 tmsTab.add(this.minZoomLvl, GBC.eol());
230
231 tmsTab.add(new JLabel(tr("Max. zoom level: ")), GBC.std());
232 tmsTab.add(GBC.glue(5, 0), GBC.std());
233 tmsTab.add(this.maxZoomLvl, GBC.eol());
234
235 tmsTab.add(new JLabel(tr("Add to slippymap chooser: ")), GBC.std());
236 tmsTab.add(GBC.glue(5, 0), GBC.std());
237 tmsTab.add(addToSlippyMapChosser, GBC.eol().fill(GBC.HORIZONTAL));
238
239 tmsTab.add(new JLabel(tr("Tile cache directory: ")), GBC.std());
240 tmsTab.add(GBC.glue(5, 0), GBC.std());
241 tmsTab.add(tilecacheDir, GBC.eol().fill(GBC.HORIZONTAL));
242
243 return tmsTab;
244 }
245
246 private void addSettingsSection(final JPanel p, String name, JPanel section) {
247 addSettingsSection(p, name, section, GBC.eol());
248 }
249 private void addSettingsSection(final JPanel p, String name, JPanel section, GBC gbc) {
250 final JLabel lbl = new JLabel(name);
251 lbl.setFont(lbl.getFont().deriveFont(Font.BOLD));
252 p.add(lbl,GBC.std());
253 p.add(new JSeparator(), GBC.eol().fill(GBC.HORIZONTAL).insets(5, 0, 0, 0));
254 p.add(section, gbc.insets(20,5,0,10));
255 }
256
257 private Component buildSettingsPanel(final PreferenceTabbedPane gui) {
258 final JPanel p = new JPanel(new GridBagLayout());
259 p.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
260
261 addSettingsSection(p, tr("Common Settings"), buildCommonSettingsPanel(gui));
262 addSettingsSection(p, tr("WMS Settings"), buildWMSSettingsPanel());
263 addSettingsSection(p, tr("TMS Settings"), buildTMSSettingsPanel(),
264 GBC.eol().fill(GBC.HORIZONTAL));
265
266 p.add(new JPanel(),GBC.eol().fill(GBC.BOTH));
267 return new JScrollPane(p);
268 }
269
270 @Override
271 public void addGui(final PreferenceTabbedPane gui) {
272 JPanel p = gui.createPreferenceTab("imagery", tr("Imagery Preferences"), tr("Modify list of imagery layers displayed in the Imagery menu"));
273 JTabbedPane pane = new JTabbedPane();
274 layerInfo = new ImageryLayerInfo(ImageryLayerInfo.instance);
275 imageryProviders = new ImageryProvidersPanel(gui, layerInfo);
276 pane.add(imageryProviders);
277 pane.add(buildSettingsPanel(gui));
278 pane.add(new OffsetBookmarksPanel(gui));
279 loadSettings();
280 pane.setTitleAt(0, tr("Imagery providers"));
281 pane.setTitleAt(1, tr("Settings"));
282 pane.setTitleAt(2, tr("Offset bookmarks"));
283 p.add(pane,GBC.std().fill(GBC.BOTH));
284 }
285
286 private void loadSettings() {
287 // Common settings
288 this.btnFadeColor.setBackground(colFadeColor);
289 this.btnFadeColor.setText(ColorHelper.color2html(colFadeColor));
290 this.fadeAmount.setValue(ImageryLayer.PROP_FADE_AMOUNT.get());
291 this.sharpen.setSelectedIndex(Math.max(0, Math.min(2, ImageryLayer.PROP_SHARPEN_LEVEL.get())));
292 this.useOffsetServer.setSelected(OffsetServer.PROP_SERVER_ENABLED.get());
293 this.offsetServerUrl.setText(OsmosnimkiOffsetServer.PROP_SERVER_URL.get());
294
295 // WMS Settings
296 this.browser.setSelectedItem(HTMLGrabber.PROP_BROWSER.get());
297 this.overlapCheckBox.setSelected(WMSLayer.PROP_OVERLAP.get());
298 this.spinEast.setValue(WMSLayer.PROP_OVERLAP_EAST.get());
299 this.spinNorth.setValue(WMSLayer.PROP_OVERLAP_NORTH.get());
300 this.spinSimConn.setValue(WMSLayer.PROP_SIMULTANEOUS_CONNECTIONS.get());
301
302 // TMS Settings
303 this.autozoomActive.setSelected(TMSLayer.PROP_DEFAULT_AUTOZOOM.get());
304 this.autoloadTiles.setSelected(TMSLayer.PROP_DEFAULT_AUTOLOAD.get());
305 this.addToSlippyMapChosser.setSelected(TMSLayer.PROP_ADD_TO_SLIPPYMAP_CHOOSER.get());
306 this.maxZoomLvl.setValue(TMSLayer.getMaxZoomLvl(null));
307 this.minZoomLvl.setValue(TMSLayer.getMinZoomLvl(null));
308 this.tilecacheDir.setText(TMSLayer.PROP_TILECACHE_DIR.get());
309 }
310
311 @Override
312 public boolean ok() {
313 boolean restartRequired = false;
314 layerInfo.save();
315 ImageryLayerInfo.instance.clear();
316 ImageryLayerInfo.instance.load();
317 Main.main.menu.imageryMenu.refreshImageryMenu();
318 Main.main.menu.imageryMenu.refreshOffsetMenu();
319 OffsetBookmark.saveBookmarks();
320
321 WMSLayer.PROP_OVERLAP.put(overlapCheckBox.getModel().isSelected());
322 WMSLayer.PROP_OVERLAP_EAST.put((Integer) spinEast.getModel().getValue());
323 WMSLayer.PROP_OVERLAP_NORTH.put((Integer) spinNorth.getModel().getValue());
324 WMSLayer.PROP_SIMULTANEOUS_CONNECTIONS.put((Integer) spinSimConn.getModel().getValue());
325
326 HTMLGrabber.PROP_BROWSER.put(browser.getEditor().getItem().toString());
327 OffsetServer.PROP_SERVER_ENABLED.put(useOffsetServer.isSelected());
328 OsmosnimkiOffsetServer.PROP_SERVER_URL.put(offsetServerUrl.getText());
329
330 if (TMSLayer.PROP_ADD_TO_SLIPPYMAP_CHOOSER.get() != this.addToSlippyMapChosser.isSelected()) {
331 restartRequired = true;
332 }
333 TMSLayer.PROP_ADD_TO_SLIPPYMAP_CHOOSER.put(this.addToSlippyMapChosser.isSelected());
334 TMSLayer.PROP_DEFAULT_AUTOZOOM.put(this.autozoomActive.isSelected());
335 TMSLayer.PROP_DEFAULT_AUTOLOAD.put(this.autoloadTiles.isSelected());
336 TMSLayer.setMaxZoomLvl((Integer)this.maxZoomLvl.getValue());
337 TMSLayer.setMinZoomLvl((Integer)this.minZoomLvl.getValue());
338 TMSLayer.PROP_TILECACHE_DIR.put(this.tilecacheDir.getText());
339
340 ImageryLayer.PROP_FADE_AMOUNT.put(this.fadeAmount.getValue());
341 ImageryLayer.setFadeColor(this.colFadeColor);
342 ImageryLayer.PROP_SHARPEN_LEVEL.put(sharpen.getSelectedIndex());
343
344 return restartRequired;
345 }
346
347
348 /**
349 * Updates a server URL in the preferences dialog. Used by plugins.
350 *
351 * @param server
352 * The server name
353 * @param url
354 * The server URL
355 */
356 public void setServerUrl(String server, String url) {
357 for (int i = 0; i < imageryProviders.model.getRowCount(); i++) {
358 if (server.equals(imageryProviders.model.getValueAt(i, 0).toString())) {
359 imageryProviders.model.setValueAt(url, i, 1);
360 return;
361 }
362 }
363 imageryProviders.model.addRow(new String[] { server, url });
364 }
365
366 /**
367 * Gets a server URL in the preferences dialog. Used by plugins.
368 *
369 * @param server
370 * The server name
371 * @return The server URL
372 */
373 public String getServerUrl(String server) {
374 for (int i = 0; i < imageryProviders.model.getRowCount(); i++) {
375 if (server.equals(imageryProviders.model.getValueAt(i, 0).toString()))
376 return imageryProviders.model.getValueAt(i, 1).toString();
377 }
378 return null;
379 }
380
381 static class ImageryProvidersPanel extends JPanel {
382 final ImageryLayerTableModel model;
383 final ImageryDefaultLayerTableModel modeldef;
384 private final ImageryLayerInfo layerInfo;
385 private JTable listActive;
386 final JTable listdef;
387 final JMapViewer map;
388 final PreferenceTabbedPane gui;
389
390 private class ImageryTableCellRenderer extends DefaultTableCellRenderer {
391
392 private List<ImageryInfo> layers;
393
394 public ImageryTableCellRenderer(List<ImageryInfo> layers) {
395 this.layers = layers;
396 }
397
398 @Override
399 public Component getTableCellRendererComponent(JTable table, Object value, boolean
400 isSelected, boolean hasFocus, int row, int column) {
401 JLabel label = (JLabel) super.getTableCellRendererComponent(
402 table, value, isSelected, hasFocus, row, column);
403 String t = value.toString();
404 label.setBackground(Main.pref.getUIColor("Table.background"));
405 if (isSelected) {
406 label.setForeground(Main.pref.getUIColor("Table.foreground"));
407 }
408 for(ImageryInfo l : layers)
409 {
410 if(l.getExtendedUrl().equals(t)) {
411 label.setBackground(Main.pref.getColor(
412 marktr("Imagery Background: Default"),
413 new Color(200,255,200)));
414 break;
415 }
416 }
417 return label;
418 }
419 }
420
421 public ImageryProvidersPanel(final PreferenceTabbedPane gui, ImageryLayerInfo layerInfoArg) {
422 super(new GridBagLayout());
423 this.gui = gui;
424 this.layerInfo = layerInfoArg;
425 this.model = new ImageryLayerTableModel();
426
427 listActive = new JTable(model) {
428 @Override
429 public String getToolTipText(MouseEvent e) {
430 java.awt.Point p = e.getPoint();
431 return model.getValueAt(rowAtPoint(p), columnAtPoint(p)).toString();
432 }
433 };
434
435 modeldef = new ImageryDefaultLayerTableModel();
436 listdef = new JTable(modeldef) {
437 @Override
438 public String getToolTipText(MouseEvent e) {
439 java.awt.Point p = e.getPoint();
440 return (String) modeldef.getValueAt(rowAtPoint(p), columnAtPoint(p));
441 }
442 };
443
444 modeldef.addTableModelListener(
445 new TableModelListener() {
446 @Override
447 public void tableChanged(TableModelEvent e) {
448 listActive.repaint();
449 }
450 }
451 );
452
453 model.addTableModelListener(
454 new TableModelListener() {
455 @Override
456 public void tableChanged(TableModelEvent e) {
457 listdef.repaint();
458 }
459 }
460 );
461
462 TableColumnModel mod = listdef.getColumnModel();
463 mod.getColumn(2).setPreferredWidth(800);
464 mod.getColumn(2).setCellRenderer(new ImageryTableCellRenderer(layerInfo.getLayers()));
465 mod.getColumn(1).setPreferredWidth(400);
466 mod.getColumn(0).setPreferredWidth(50);
467
468 mod = listActive.getColumnModel();
469 mod.getColumn(1).setPreferredWidth(800);
470 mod.getColumn(1).setCellRenderer(new ImageryTableCellRenderer(layerInfo.getDefaultLayers()));
471 mod.getColumn(0).setPreferredWidth(200);
472
473 RemoveEntryAction remove = new RemoveEntryAction();
474 listActive.getSelectionModel().addListSelectionListener(remove);
475
476 add(new JLabel(tr("Available default entries:")), GBC.eol().insets(5, 5, 0, 0));
477 // Add default item list
478 JScrollPane scrolldef = new JScrollPane(listdef);
479 scrolldef.setPreferredSize(new Dimension(200, 200));
480 add(scrolldef, GBC.std().insets(0, 5, 0, 0).fill(GridBagConstraints.BOTH).weight(1.0, 0.6).insets(5, 0, 0, 0));
481
482 // Add default item map
483 map = new JMapViewer();
484 map.setZoomContolsVisible(false);
485 map.setMinimumSize(new Dimension(100, 200));
486 add(map, GBC.std().insets(5, 5, 0, 0).fill(GridBagConstraints.BOTH).weight(0.33, 0.6).insets(5, 0, 0, 0));
487
488 listdef.getSelectionModel().addListSelectionListener(new DefListSelectionListener());
489
490 JToolBar tb = new JToolBar(JToolBar.VERTICAL);
491 tb.setFloatable(false);
492 tb.setBorderPainted(false);
493 tb.setOpaque(false);
494 tb.add(new ReloadAction());
495 add(tb, GBC.eol().anchor(GBC.SOUTH).insets(0, 0, 5, 0));
496
497 ActivateAction activate = new ActivateAction();
498 listdef.getSelectionModel().addListSelectionListener(activate);
499 JButton btnActivate = new JButton(activate);
500
501 JToolBar tb2 = new JToolBar(JToolBar.VERTICAL);
502 tb2.setFloatable(false);
503 tb2.setBorderPainted(false);
504 tb2.setOpaque(false);
505 tb2.add(btnActivate);
506 add(tb2, GBC.eol().anchor(GBC.CENTER).insets(5, 15, 5, 0));
507
508 add(Box.createHorizontalGlue(), GBC.eol().fill(GridBagConstraints.HORIZONTAL));
509
510 add(new JLabel(tr("Selected entries:")), GBC.eol().insets(5, 0, 0, 0));
511 JScrollPane scroll = new JScrollPane(listActive);
512 add(scroll, GBC.std().fill(GridBagConstraints.BOTH).span(GridBagConstraints.RELATIVE).weight(1.0, 0.4).insets(5, 0, 0, 5));
513 scroll.setPreferredSize(new Dimension(200, 200));
514
515 JToolBar sideButtonTB = new JToolBar(JToolBar.VERTICAL);
516 sideButtonTB.setFloatable(false);
517 sideButtonTB.setBorderPainted(false);
518 sideButtonTB.setOpaque(false);
519 sideButtonTB.add(new NewEntryAction());
520 //sideButtonTB.add(edit); TODO
521 sideButtonTB.add(remove);
522 add(sideButtonTB, GBC.eol().anchor(GBC.NORTH).insets(0, 0, 5, 5));
523
524 }
525
526 // Listener of default providers list selection
527 private final class DefListSelectionListener implements ListSelectionListener {
528 // The current drawn rectangles and polygons
529 private final Map<Integer, MapRectangle> mapRectangles;
530 private final Map<Integer, List<MapPolygon>> mapPolygons;
531
532 private DefListSelectionListener() {
533 this.mapRectangles = new HashMap<Integer, MapRectangle>();
534 this.mapPolygons = new HashMap<Integer, List<MapPolygon>>();
535 }
536
537 @Override
538 public void valueChanged(ListSelectionEvent e) {
539 // First index is set to -1 when the list is refreshed, so discard all map rectangles and polygons
540 if (e.getFirstIndex() == -1) {
541 map.removeAllMapRectangles();
542 map.removeAllMapPolygons();
543 mapRectangles.clear();
544 mapPolygons.clear();
545 // Only process complete (final) selection events
546 } else if (!e.getValueIsAdjusting()) {
547 for (int i = e.getFirstIndex(); i<=e.getLastIndex(); i++) {
548 updateBoundsAndShapes(i);
549 }
550 // If needed, adjust map to show all map rectangles and polygons
551 if (!mapRectangles.isEmpty() || !mapPolygons.isEmpty()) {
552 map.setDisplayToFitMapElements(false, true, true);
553 map.zoomOut();
554 }
555 }
556 }
557
558 private void updateBoundsAndShapes(int i) {
559 ImageryBounds bounds = modeldef.getRow(i).getBounds();
560 if (bounds != null) {
561 List<Shape> shapes = bounds.getShapes();
562 if (shapes != null && !shapes.isEmpty()) {
563 if (listdef.getSelectionModel().isSelectedIndex(i)) {
564 if (!mapPolygons.containsKey(i)) {
565 List<MapPolygon> list = new ArrayList<MapPolygon>();
566 mapPolygons.put(i, list);
567 // Add new map polygons
568 for (Shape shape : shapes) {
569 MapPolygon polygon = new MapPolygonImpl(shape.getPoints());
570 list.add(polygon);
571 map.addMapPolygon(polygon);
572 }
573 }
574 } else if (mapPolygons.containsKey(i)) {
575 // Remove previously drawn map polygons
576 for (MapPolygon polygon : mapPolygons.get(i)) {
577 map.removeMapPolygon(polygon);
578 }
579 mapPolygons.remove(i);
580 }
581 // Only display bounds when no polygons (shapes) are defined for this provider
582 } else {
583 if (listdef.getSelectionModel().isSelectedIndex(i)) {
584 if (!mapRectangles.containsKey(i)) {
585 // Add new map rectangle
586 Coordinate topLeft = new Coordinate(bounds.getMax().lat(), bounds.getMin().lon());
587 Coordinate bottomRight = new Coordinate(bounds.getMin().lat(), bounds.getMax().lon());
588 MapRectangle rectangle = new MapRectangleImpl(topLeft, bottomRight);
589 mapRectangles.put(i, rectangle);
590 map.addMapRectangle(rectangle);
591 }
592 } else if (mapRectangles.containsKey(i)) {
593 // Remove previously drawn map rectangle
594 map.removeMapRectangle(mapRectangles.get(i));
595 mapRectangles.remove(i);
596 }
597 }
598 }
599 }
600 }
601
602 class NewEntryAction extends AbstractAction {
603 public NewEntryAction() {
604 putValue(NAME, tr("New"));
605 putValue(SHORT_DESCRIPTION, tr("Add a new WMS/TMS entry by entering the URL"));
606 putValue(SMALL_ICON, ImageProvider.get("dialogs", "add"));
607 }
608
609 public void actionPerformed(ActionEvent evt) {
610 final AddWMSLayerPanel p = new AddWMSLayerPanel();
611 // This code snippet allows to resize the JOptionPane (fix #6090)
612 p.addHierarchyListener(new HierarchyListener() {
613 public void hierarchyChanged(HierarchyEvent e) {
614 Window window = SwingUtilities.getWindowAncestor(p);
615 if (window instanceof Dialog) {
616 Dialog dialog = (Dialog)window;
617 if (!dialog.isResizable()) {
618 dialog.setResizable(true);
619 dialog.setMinimumSize(new Dimension(250, 350));
620 }
621 }
622 }
623 });
624 int answer = JOptionPane.showConfirmDialog(
625 gui, p,
626 tr("Add Imagery URL"),
627 JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);
628 if (answer == JOptionPane.OK_OPTION) {
629 try {
630 model.addRow(p.getImageryInfo());
631 } catch (IllegalArgumentException ex) {
632 if (ex.getMessage() == null || ex.getMessage().isEmpty()) {
633 throw ex;
634 } else {
635 JOptionPane.showMessageDialog(Main.parent,
636 ex.getMessage(), tr("Error"),
637 JOptionPane.ERROR_MESSAGE);
638 }
639 }
640 }
641 }
642 }
643
644 class RemoveEntryAction extends AbstractAction implements ListSelectionListener {
645
646 public RemoveEntryAction() {
647 putValue(NAME, tr("Remove"));
648 putValue(SHORT_DESCRIPTION, tr("Remove entry"));
649 putValue(SMALL_ICON, ImageProvider.get("dialogs", "delete"));
650 updateEnabledState();
651 }
652
653 protected void updateEnabledState() {
654 setEnabled(listActive.getSelectedRowCount() > 0);
655 }
656
657 @Override
658 public void valueChanged(ListSelectionEvent e) {
659 updateEnabledState();
660 }
661
662 @Override
663 public void actionPerformed(ActionEvent e) {
664 Integer i;
665 while ((i = listActive.getSelectedRow()) != -1) {
666 model.removeRow(i);
667 }
668 }
669 }
670
671 class ActivateAction extends AbstractAction implements ListSelectionListener {
672 public ActivateAction() {
673 putValue(NAME, tr("Activate"));
674 putValue(SHORT_DESCRIPTION, tr("copy selected defaults"));
675 putValue(SMALL_ICON, ImageProvider.get("preferences", "activate-down"));
676 }
677
678 protected void updateEnabledState() {
679 setEnabled(listdef.getSelectedRowCount() > 0);
680 }
681
682 @Override
683 public void valueChanged(ListSelectionEvent e) {
684 updateEnabledState();
685 }
686
687 @Override
688 public void actionPerformed(ActionEvent e) {
689 int[] lines = listdef.getSelectedRows();
690 if (lines.length == 0) {
691 JOptionPane.showMessageDialog(
692 gui,
693 tr("Please select at least one row to copy."),
694 tr("Information"),
695 JOptionPane.INFORMATION_MESSAGE);
696 return;
697 }
698
699 Set<String> acceptedEulas = new HashSet<String>();
700
701 outer: for (int i = 0; i < lines.length; i++) {
702 ImageryInfo info = modeldef.getRow(lines[i]);
703
704 // Check if an entry with exactly the same values already
705 // exists
706 for (int j = 0; j < model.getRowCount(); j++) {
707 if (info.equalsBaseValues(model.getRow(j))) {
708 // Select the already existing row so the user has
709 // some feedback in case an entry exists
710 listActive.getSelectionModel().setSelectionInterval(j, j);
711 listActive.scrollRectToVisible(listActive.getCellRect(j, 0, true));
712 continue outer;
713 }
714 }
715
716 String eulaURL = info.getEulaAcceptanceRequired();
717 // If set and not already accepted, ask for EULA acceptance
718 if (eulaURL != null && !acceptedEulas.contains(eulaURL)) {
719 if (confirmEulaAcceptance(gui, eulaURL)) {
720 acceptedEulas.add(eulaURL);
721 } else {
722 continue outer;
723 }
724 }
725
726 model.addRow(new ImageryInfo(info));
727 int lastLine = model.getRowCount() - 1;
728 listActive.getSelectionModel().setSelectionInterval(lastLine, lastLine);
729 listActive.scrollRectToVisible(listActive.getCellRect(lastLine, 0, true));
730 }
731 }
732 }
733
734 class ReloadAction extends AbstractAction {
735 public ReloadAction() {
736 putValue(SHORT_DESCRIPTION, tr("reload defaults"));
737 putValue(SMALL_ICON, ImageProvider.get("dialogs", "refresh"));
738 }
739
740 public void actionPerformed(ActionEvent evt) {
741 layerInfo.loadDefaults(true);
742 modeldef.fireTableDataChanged();
743 }
744 }
745
746 /**
747 * The table model for imagery layer list
748 */
749 class ImageryLayerTableModel extends DefaultTableModel {
750 public ImageryLayerTableModel() {
751 setColumnIdentifiers(new String[] { tr("Menu Name"), tr("Imagery URL")});
752 }
753
754 public ImageryInfo getRow(int row) {
755 return layerInfo.getLayers().get(row);
756 }
757
758 public void addRow(ImageryInfo i) {
759 layerInfo.add(i);
760 int p = getRowCount() - 1;
761 fireTableRowsInserted(p, p);
762 }
763
764 @Override
765 public void removeRow(int i) {
766 layerInfo.remove(getRow(i));
767 fireTableRowsDeleted(i, i);
768 }
769
770 @Override
771 public int getRowCount() {
772 return layerInfo.getLayers().size();
773 }
774
775 @Override
776 public Object getValueAt(int row, int column) {
777 ImageryInfo info = layerInfo.getLayers().get(row);
778 switch (column) {
779 case 0:
780 return info.getName();
781 case 1:
782 return info.getExtendedUrl();
783 default:
784 throw new ArrayIndexOutOfBoundsException();
785 }
786 }
787
788 @Override
789 public void setValueAt(Object o, int row, int column) {
790 ImageryInfo info = layerInfo.getLayers().get(row);
791 switch (column) {
792 case 0:
793 info.setName((String) o);
794 break;
795 case 1:
796 info.setExtendedUrl((String)o);
797 break;
798 default:
799 throw new ArrayIndexOutOfBoundsException();
800 }
801 }
802
803 @Override
804 public boolean isCellEditable(int row, int column) {
805 return true;
806 }
807 }
808
809 /**
810 * The table model for the default imagery layer list
811 */
812 class ImageryDefaultLayerTableModel extends DefaultTableModel {
813 public ImageryDefaultLayerTableModel() {
814 setColumnIdentifiers(new String[]{"", tr("Menu Name (Default)"), tr("Imagery URL (Default)")});
815 }
816
817 public ImageryInfo getRow(int row) {
818 return layerInfo.getDefaultLayers().get(row);
819 }
820
821 @Override
822 public int getRowCount() {
823 return layerInfo.getDefaultLayers().size();
824 }
825
826 @Override
827 public Object getValueAt(int row, int column) {
828 ImageryInfo info = layerInfo.getDefaultLayers().get(row);
829 switch (column) {
830 case 0:
831 return info.getCountryCode();
832 case 1:
833 return info.getName();
834 case 2:
835 return info.getExtendedUrl();
836 }
837 return null;
838 }
839
840 @Override
841 public boolean isCellEditable(int row, int column) {
842 return false;
843 }
844 }
845
846 private boolean confirmEulaAcceptance(PreferenceTabbedPane gui, String eulaUrl) {
847 URL url = null;
848 try {
849 url = new URL(eulaUrl.replaceAll("\\{lang\\}", Locale.getDefault().toString()));
850 JEditorPane htmlPane = null;
851 try {
852 htmlPane = new JEditorPane(url);
853 } catch (IOException e1) {
854 // give a second chance with a default Locale 'en'
855 try {
856 url = new URL(eulaUrl.replaceAll("\\{lang\\}", "en"));
857 htmlPane = new JEditorPane(url);
858 } catch (IOException e2) {
859 JOptionPane.showMessageDialog(gui ,tr("EULA license URL not available: {0}", eulaUrl));
860 return false;
861 }
862 }
863 Box box = Box.createVerticalBox();
864 htmlPane.setEditable(false);
865 JScrollPane scrollPane = new JScrollPane(htmlPane);
866 scrollPane.setPreferredSize(new Dimension(400, 400));
867 box.add(scrollPane);
868 int option = JOptionPane.showConfirmDialog(Main.parent, box, tr("Please abort if you are not sure"), JOptionPane.YES_NO_OPTION,
869 JOptionPane.WARNING_MESSAGE);
870 if (option == JOptionPane.YES_OPTION)
871 return true;
872 } catch (MalformedURLException e2) {
873 JOptionPane.showMessageDialog(gui ,tr("Malformed URL for the EULA licence: {0}", eulaUrl));
874 }
875 return false;
876 }
877 }
878
879 static class OffsetBookmarksPanel extends JPanel {
880 List<OffsetBookmark> bookmarks = OffsetBookmark.allBookmarks;
881 OffsetsBookmarksModel model = new OffsetsBookmarksModel();
882
883 public OffsetBookmarksPanel(final PreferenceTabbedPane gui) {
884 super(new GridBagLayout());
885 final JTable list = new JTable(model) {
886 @Override
887 public String getToolTipText(MouseEvent e) {
888 java.awt.Point p = e.getPoint();
889 return model.getValueAt(rowAtPoint(p), columnAtPoint(p)).toString();
890 }
891 };
892 JScrollPane scroll = new JScrollPane(list);
893 add(scroll, GBC.eol().fill(GridBagConstraints.BOTH));
894 scroll.setPreferredSize(new Dimension(200, 200));
895
896 TableColumnModel mod = list.getColumnModel();
897 mod.getColumn(0).setPreferredWidth(150);
898 mod.getColumn(1).setPreferredWidth(200);
899 mod.getColumn(2).setPreferredWidth(300);
900 mod.getColumn(3).setPreferredWidth(150);
901 mod.getColumn(4).setPreferredWidth(150);
902
903 JPanel buttonPanel = new JPanel(new FlowLayout());
904
905 JButton add = new JButton(tr("Add"));
906 buttonPanel.add(add, GBC.std().insets(0, 5, 0, 0));
907 add.addActionListener(new ActionListener() {
908 @Override
909 public void actionPerformed(ActionEvent e) {
910 OffsetBookmark b = new OffsetBookmark(Main.getProjection(),"","",0,0);
911 model.addRow(b);
912 }
913 });
914
915 JButton delete = new JButton(tr("Delete"));
916 buttonPanel.add(delete, GBC.std().insets(0, 5, 0, 0));
917 delete.addActionListener(new ActionListener() {
918 @Override
919 public void actionPerformed(ActionEvent e) {
920 if (list.getSelectedRow() == -1) {
921 JOptionPane.showMessageDialog(gui, tr("Please select the row to delete."));
922 } else {
923 Integer i;
924 while ((i = list.getSelectedRow()) != -1) {
925 model.removeRow(i);
926 }
927 }
928 }
929 });
930
931 add(buttonPanel,GBC.eol());
932 }
933
934 /**
935 * The table model for imagery offsets list
936 */
937 class OffsetsBookmarksModel extends DefaultTableModel {
938 public OffsetsBookmarksModel() {
939 setColumnIdentifiers(new String[] { tr("Projection"), tr("Layer"), tr("Name"), tr("Easting"), tr("Northing"),});
940 }
941
942 public OffsetBookmark getRow(int row) {
943 return bookmarks.get(row);
944 }
945
946 public void addRow(OffsetBookmark i) {
947 bookmarks.add(i);
948 int p = getRowCount() - 1;
949 fireTableRowsInserted(p, p);
950 }
951
952 @Override
953 public void removeRow(int i) {
954 bookmarks.remove(getRow(i));
955 fireTableRowsDeleted(i, i);
956 }
957
958 @Override
959 public int getRowCount() {
960 return bookmarks.size();
961 }
962
963 @Override
964 public Object getValueAt(int row, int column) {
965 OffsetBookmark info = bookmarks.get(row);
966 switch (column) {
967 case 0:
968 if (info.proj == null) return "";
969 return info.proj.toString();
970 case 1:
971 return info.layerName;
972 case 2:
973 return info.name;
974 case 3:
975 return info.dx;
976 case 4:
977 return info.dy;
978 default:
979 throw new ArrayIndexOutOfBoundsException();
980 }
981 }
982
983 @Override
984 public void setValueAt(Object o, int row, int column) {
985 OffsetBookmark info = bookmarks.get(row);
986 switch (column) {
987 case 1:
988 info.layerName = o.toString();
989 break;
990 case 2:
991 info.name = o.toString();
992 break;
993 case 3:
994 info.dx = Double.parseDouble((String) o);
995 break;
996 case 4:
997 info.dy = Double.parseDouble((String) o);
998 break;
999 default:
1000 throw new ArrayIndexOutOfBoundsException();
1001 }
1002 }
1003
1004 @Override
1005 public boolean isCellEditable(int row, int column) {
1006 return column >= 1;
1007 }
1008 }
1009 }
1010
1011 public static void initialize() {
1012 ImageryLayerInfo.instance.clear();
1013 ImageryLayerInfo.instance.loadDefaults(false);
1014 ImageryLayerInfo.instance.load();
1015 OffsetBookmark.loadBookmarks();
1016 Main.main.menu.imageryMenu.refreshImageryMenu();
1017 Main.main.menu.imageryMenu.refreshOffsetMenu();
1018 }
1019}
Note: See TracBrowser for help on using the repository browser.