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

Last change on this file since 12620 was 12620, checked in by Don-vip, 7 years ago

see #15182 - deprecate all Main logging methods and introduce suitable replacements in Logging for most of them

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