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

Last change on this file since 8568 was 8568, checked in by wiktorn, 9 years ago

Basic WMTS support.

  • added information about units and to_meter to EPSG projection definitions (needed for WMTS)
  • added WMTSTileSource and WMTSLayer classes
  • a bit of cleanup of AbstractTileSourceLayer and align so it will work properly with WMTS tile definitions
  • added Imagery Preferences panel for WMTS and icon for button
  • added removal of wms: / tms: / wmts: prefix, if user will paste them into the field
  • CachedFile - added possibility to send custom headers with request
  • added support for unit and to_meter in CustomProjection
  • AbstractTMSTileSource cleanups (change of Coordinate to ICoordinate)
  • moved JCSCachedTileLoaderJob.read() to Utils

Addresses: #10623

Tested with Polish WMTS service proivders, Walonnie needs still some debugging, as it is not working right now.

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