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

Last change on this file since 11959 was 11959, checked in by stoecker, 7 years ago

add a link to the wiki in imagery preferences

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