source: josm/trunk/src/org/openstreetmap/josm/gui/preferences/imagery/ImageryProvidersPanel.java

Last change on this file was 18801, checked in by taylor.smock, 14 months ago

Fix #22832: Code cleanup and some simplification, documentation fixes (patch by gaben)

There should not be any functional changes in this patch; it is intended to do
the following:

  • Simplify and cleanup code (example: Arrays.asList(item) -> Collections.singletonList(item))
  • Fix typos in documentation (which also corrects the documentation to match what actually happens, in some cases)
  • Property svn:eol-style set to native
File size: 31.3 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.BorderLayout;
8import java.awt.Color;
9import java.awt.Component;
10import java.awt.Dimension;
11import java.awt.GridBagConstraints;
12import java.awt.GridBagLayout;
13import java.awt.event.ActionEvent;
14import java.awt.event.MouseAdapter;
15import java.awt.event.MouseEvent;
16import java.io.IOException;
17import java.net.MalformedURLException;
18import java.net.URL;
19import java.util.ArrayList;
20import java.util.HashMap;
21import java.util.HashSet;
22import java.util.Iterator;
23import java.util.List;
24import java.util.Map;
25import java.util.Map.Entry;
26import java.util.Objects;
27import java.util.Optional;
28import java.util.Set;
29import java.util.function.BiConsumer;
30import java.util.function.Consumer;
31import java.util.function.Function;
32
33import javax.swing.AbstractAction;
34import javax.swing.Box;
35import javax.swing.JButton;
36import javax.swing.JLabel;
37import javax.swing.JOptionPane;
38import javax.swing.JPanel;
39import javax.swing.JScrollPane;
40import javax.swing.JTable;
41import javax.swing.JToolBar;
42import javax.swing.UIManager;
43import javax.swing.event.ListSelectionEvent;
44import javax.swing.event.ListSelectionListener;
45import javax.swing.table.DefaultTableCellRenderer;
46import javax.swing.table.DefaultTableModel;
47import javax.swing.table.TableColumnModel;
48
49import org.openstreetmap.gui.jmapviewer.Coordinate;
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.josm.data.imagery.ImageryInfo;
55import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryBounds;
56import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryCategory;
57import org.openstreetmap.josm.data.imagery.ImageryLayerInfo;
58import org.openstreetmap.josm.data.imagery.Shape;
59import org.openstreetmap.josm.data.preferences.NamedColorProperty;
60import org.openstreetmap.josm.gui.MainApplication;
61import org.openstreetmap.josm.gui.bbox.JosmMapViewer;
62import org.openstreetmap.josm.gui.bbox.SlippyMapBBoxChooser;
63import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane;
64import org.openstreetmap.josm.gui.util.GuiHelper;
65import org.openstreetmap.josm.gui.util.TableHelper;
66import org.openstreetmap.josm.gui.widgets.FilterField;
67import org.openstreetmap.josm.gui.widgets.HtmlPanel;
68import org.openstreetmap.josm.gui.widgets.JosmEditorPane;
69import org.openstreetmap.josm.spi.preferences.Config;
70import org.openstreetmap.josm.tools.GBC;
71import org.openstreetmap.josm.tools.ImageProvider;
72import org.openstreetmap.josm.tools.ImageProvider.ImageSizes;
73import org.openstreetmap.josm.tools.LanguageInfo;
74import org.openstreetmap.josm.tools.Logging;
75import org.openstreetmap.josm.tools.Utils;
76
77/**
78 * A panel displaying imagery providers.
79 * @since 15115 (extracted from ImageryPreferences)
80 */
81public class ImageryProvidersPanel extends JPanel {
82 // Public JTables and JosmMapViewer
83 /** The table of active providers **/
84 public final JTable activeTable;
85 /** The table of default providers **/
86 public final JTable defaultTable;
87 /** The filter of default providers **/
88 private final FilterField defaultFilter;
89 /** The selection listener synchronizing map display with table of default providers **/
90 private final transient DefListSelectionListener defaultTableListener;
91 /** The map displaying imagery bounds of selected default providers **/
92 public final JosmMapViewer defaultMap;
93
94 // Public models
95 /** The model of active providers **/
96 public final ImageryLayerTableModel activeModel;
97 /** The model of default providers **/
98 public final ImageryDefaultLayerTableModel defaultModel;
99
100 // Public JToolbars
101 /** The toolbar on the right of active providers **/
102 public final JToolBar activeToolbar;
103 /** The toolbar on the middle of the panel **/
104 public final JToolBar middleToolbar;
105
106 // Private members
107 private final PreferenceTabbedPane gui;
108 private final transient ImageryLayerInfo layerInfo;
109
110 /**
111 * class to render the URL information of Imagery source
112 * @since 8065
113 */
114 private static class ImageryURLTableCellRenderer extends DefaultTableCellRenderer {
115
116 private static final NamedColorProperty IMAGERY_BACKGROUND_COLOR = new NamedColorProperty(
117 marktr("Imagery Background: Default"),
118 new Color(200, 255, 200));
119
120 private final transient List<ImageryInfo> layers;
121
122 ImageryURLTableCellRenderer(List<ImageryInfo> layers) {
123 this.layers = layers;
124 }
125
126 @Override
127 public Component getTableCellRendererComponent(JTable table, Object value, boolean
128 isSelected, boolean hasFocus, int row, int column) {
129 JLabel label = (JLabel) super.getTableCellRendererComponent(
130 table, value, isSelected, hasFocus, row, column);
131 GuiHelper.setBackgroundReadable(label, UIManager.getColor("Table.background"));
132 if (value != null) { // Fix #8159
133 if (layers.stream().anyMatch(l -> Objects.equals(l.getExtendedUrl(), value.toString()))) {
134 GuiHelper.setBackgroundReadable(label, IMAGERY_BACKGROUND_COLOR.get());
135 }
136 label.setToolTipText((String) value);
137 }
138 return label;
139 }
140 }
141
142 /**
143 * class to render an information of Imagery source
144 * @param <T> type of information
145 */
146 private static class ImageryTableCellRenderer<T> extends DefaultTableCellRenderer {
147 private final Function<T, Object> mapper;
148 private final Function<T, String> tooltip;
149 private final BiConsumer<T, JLabel> decorator;
150
151 ImageryTableCellRenderer(Function<T, Object> mapper, Function<T, String> tooltip, BiConsumer<T, JLabel> decorator) {
152 this.mapper = mapper;
153 this.tooltip = tooltip;
154 this.decorator = decorator;
155 }
156
157 @Override
158 @SuppressWarnings("unchecked")
159 public final Component getTableCellRendererComponent(JTable table, Object value, boolean
160 isSelected, boolean hasFocus, int row, int column) {
161 T obj = (T) value;
162 JLabel label = (JLabel) super.getTableCellRendererComponent(
163 table, mapper.apply(obj), isSelected, hasFocus, row, column);
164 GuiHelper.setBackgroundReadable(label,
165 isSelected ? UIManager.getColor("Table.selectionBackground") : UIManager.getColor("Table.background"));
166 if (obj != null) {
167 label.setToolTipText(tooltip.apply(obj));
168 if (decorator != null) {
169 decorator.accept(obj, label);
170 }
171 }
172 return label;
173 }
174 }
175
176 /**
177 * class to render the category information of Imagery source
178 */
179 private static class ImageryCategoryTableCellRenderer extends ImageryProvidersPanel.ImageryTableCellRenderer<ImageryCategory> {
180 ImageryCategoryTableCellRenderer() {
181 super(cat -> null, cat -> tr("Imagery category: {0}", cat.getDescription()),
182 (cat, label) -> label.setIcon(cat.getIcon(ImageSizes.TABLE)));
183 }
184 }
185
186 /**
187 * class to render the country information of Imagery source
188 */
189 private static class ImageryCountryTableCellRenderer extends ImageryProvidersPanel.ImageryTableCellRenderer<String> {
190 ImageryCountryTableCellRenderer() {
191 super(code -> code, ImageryInfo::getLocalizedCountry, null);
192 }
193 }
194
195 /**
196 * class to render the name information of Imagery source
197 */
198 private static class ImageryNameTableCellRenderer extends ImageryProvidersPanel.ImageryTableCellRenderer<ImageryInfo> {
199 ImageryNameTableCellRenderer() {
200 super(info -> info == null ? null : info.getName(), ImageryInfo::getToolTipText, null);
201 }
202 }
203
204 /**
205 * Constructs a new {@code ImageryProvidersPanel}.
206 * @param gui The parent preference tab pane
207 * @param layerInfoArg The list of imagery entries to display
208 */
209 public ImageryProvidersPanel(final PreferenceTabbedPane gui, ImageryLayerInfo layerInfoArg) {
210 super(new GridBagLayout());
211 this.gui = gui;
212 this.layerInfo = layerInfoArg;
213 this.activeModel = new ImageryLayerTableModel();
214
215 activeTable = new JTable(activeModel) {
216 @Override
217 public String getToolTipText(MouseEvent e) {
218 java.awt.Point p = e.getPoint();
219 try {
220 return activeModel.getValueAt(rowAtPoint(p), columnAtPoint(p)).toString();
221 } catch (ArrayIndexOutOfBoundsException ex) {
222 Logging.debug(ex);
223 return null;
224 }
225 }
226 };
227 TableHelper.setFont(activeTable, getClass());
228 activeTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
229
230 defaultModel = new ImageryDefaultLayerTableModel();
231 defaultTable = new JTable(defaultModel);
232 TableHelper.setFont(defaultTable, getClass());
233 defaultTable.setAutoCreateRowSorter(true);
234 defaultFilter = new FilterField().filter(defaultTable, defaultModel);
235
236 defaultModel.addTableModelListener(e -> activeTable.repaint());
237 activeModel.addTableModelListener(e -> defaultTable.repaint());
238
239 TableColumnModel mod = defaultTable.getColumnModel();
240 mod.getColumn(3).setPreferredWidth(775);
241 mod.getColumn(3).setCellRenderer(new ImageryURLTableCellRenderer(layerInfo.getLayers()));
242 mod.getColumn(2).setPreferredWidth(475);
243 mod.getColumn(2).setCellRenderer(new ImageryNameTableCellRenderer());
244 mod.getColumn(1).setCellRenderer(new ImageryCountryTableCellRenderer());
245 mod.getColumn(0).setPreferredWidth(50);
246 mod.getColumn(0).setCellRenderer(new ImageryCategoryTableCellRenderer());
247 mod.getColumn(0).setPreferredWidth(50);
248
249 mod = activeTable.getColumnModel();
250 mod.getColumn(1).setPreferredWidth(800);
251 mod.getColumn(1).setCellRenderer(new ImageryURLTableCellRenderer(layerInfo.getAllDefaultLayers()));
252 mod.getColumn(0).setPreferredWidth(200);
253
254 RemoveEntryAction remove = new RemoveEntryAction();
255 activeTable.getSelectionModel().addListSelectionListener(remove);
256
257 add(new JLabel(tr("Available default entries:")), GBC.std().insets(5, 5, 0, 0));
258 add(new JLabel(tr("Boundaries of selected imagery entries:")), GBC.eol().insets(5, 5, 0, 0));
259
260 // Add default item list
261 JPanel defaultPane = new JPanel(new GridBagLayout());
262 JScrollPane scrolldef = new JScrollPane(defaultTable);
263 defaultPane.add(defaultFilter, GBC.eol().insets(0, 0, 0, 0).fill(GridBagConstraints.HORIZONTAL));
264 defaultPane.add(scrolldef, GBC.eol().insets(0, 0, 0, 0).fill(GridBagConstraints.BOTH));
265 add(defaultPane, GBC.std().fill(GridBagConstraints.BOTH).weight(1.0, 0.6).insets(5, 0, 0, 0));
266
267 // Add default item map
268 defaultMap = new JosmMapViewer();
269 defaultMap.setTileSource(SlippyMapBBoxChooser.DefaultOsmTileSourceProvider.get()); // for attribution
270 defaultMap.addMouseListener(new MouseAdapter() {
271 @Override
272 public void mouseClicked(MouseEvent e) {
273 if (e.getButton() == MouseEvent.BUTTON1) {
274 defaultMap.getAttribution().handleAttribution(e.getPoint(), true);
275 }
276 }
277 });
278 defaultMap.setZoomControlsVisible(false);
279 defaultMap.setMinimumSize(new Dimension(100, 200));
280 add(defaultMap, GBC.eol().fill(GridBagConstraints.BOTH).weight(0.33, 0.6).insets(5, 0, 0, 0));
281
282 defaultTableListener = new DefListSelectionListener();
283 defaultTable.getSelectionModel().addListSelectionListener(defaultTableListener);
284
285 HtmlPanel help = new HtmlPanel(tr("New default entries can be added in the <a href=\"{0}\">Wiki</a>.",
286 Config.getUrls().getJOSMWebsite()+"/wiki/Maps"));
287 help.enableClickableHyperlinks();
288 add(help, GBC.eol().insets(5, 0, 0, 0).fill(GBC.HORIZONTAL));
289
290 ActivateAction activate = new ActivateAction();
291 defaultTable.getSelectionModel().addListSelectionListener(activate);
292 JButton btnActivate = new JButton(activate);
293
294 middleToolbar = new JToolBar(JToolBar.HORIZONTAL);
295 middleToolbar.setFloatable(false);
296 middleToolbar.setBorderPainted(false);
297 middleToolbar.setOpaque(false);
298 middleToolbar.add(new ReloadAction());
299 middleToolbar.add(btnActivate);
300 add(middleToolbar, GBC.eol().anchor(GBC.CENTER).insets(5, 5, 5, 0));
301
302 add(Box.createHorizontalGlue(), GBC.eol().fill(GridBagConstraints.HORIZONTAL));
303
304 JPanel activePanel = new JPanel(new BorderLayout());
305 activePanel.add(new JLabel(tr("Selected entries:")), BorderLayout.NORTH);
306 JScrollPane scroll = new JScrollPane(activeTable);
307 activePanel.add(scroll, BorderLayout.CENTER);
308
309 activeToolbar = new JToolBar(JToolBar.VERTICAL);
310 activeToolbar.setFloatable(false);
311 activeToolbar.setBorderPainted(false);
312 activeToolbar.setOpaque(false);
313 activeToolbar.add(new NewEntryAction(ImageryInfo.ImageryType.WMS));
314 activeToolbar.add(new NewEntryAction(ImageryInfo.ImageryType.TMS));
315 activeToolbar.add(new NewEntryAction(ImageryInfo.ImageryType.WMTS));
316 activeToolbar.add(new NewEntryAction(ImageryInfo.ImageryType.MVT));
317 activeToolbar.add(remove);
318 activePanel.add(activeToolbar, BorderLayout.EAST);
319 add(activePanel, GBC.eol().fill(GridBagConstraints.BOTH).weight(2.0, 0.4).insets(5, 0, 0, 5));
320 }
321
322 // Listener of default providers list selection
323 private final class DefListSelectionListener implements ListSelectionListener {
324 // The current drawn rectangles and polygons
325 private final Map<Integer, MapRectangle> mapRectangles;
326 private final Map<Integer, List<MapPolygon>> mapPolygons;
327
328 private DefListSelectionListener() {
329 this.mapRectangles = new HashMap<>();
330 this.mapPolygons = new HashMap<>();
331 }
332
333 private void clearMap() {
334 defaultMap.removeAllMapRectangles();
335 defaultMap.removeAllMapPolygons();
336 mapRectangles.clear();
337 mapPolygons.clear();
338 }
339
340 @Override
341 public void valueChanged(ListSelectionEvent e) {
342 // First index can be set to -1 when the list is refreshed, so discard all map rectangles and polygons
343 if (e.getFirstIndex() == -1) {
344 clearMap();
345 } else if (!e.getValueIsAdjusting()) {
346 // Only process complete (final) selection events
347 for (int i = e.getFirstIndex(); i <= e.getLastIndex(); i++) {
348 if (i < defaultTable.getRowCount()) {
349 updateBoundsAndShapes(defaultTable.convertRowIndexToModel(i));
350 }
351 }
352 // Cleanup residual selected bounds which may have disappeared after a filter
353 cleanupResidualBounds();
354 // If needed, adjust map to show all map rectangles and polygons
355 if (!mapRectangles.isEmpty() || !mapPolygons.isEmpty()) {
356 defaultMap.setDisplayToFitMapElements(false, true, true);
357 defaultMap.zoomOut();
358 }
359 }
360 }
361
362 /**
363 * update bounds and shapes for a new entry
364 * @param i model index
365 */
366 private void updateBoundsAndShapes(int i) {
367 ImageryBounds bounds = defaultModel.getRow(i).getBounds();
368 if (bounds != null) {
369 int viewIndex = defaultTable.convertRowIndexToView(i);
370 List<Shape> shapes = bounds.getShapes();
371 if (!Utils.isEmpty(shapes)) {
372 if (defaultTable.getSelectionModel().isSelectedIndex(viewIndex)) {
373 if (!mapPolygons.containsKey(i)) {
374 List<MapPolygon> list = new ArrayList<>();
375 mapPolygons.put(i, list);
376 // Add new map polygons
377 for (Shape shape : shapes) {
378 MapPolygon polygon = new MapPolygonImpl(shape.getPoints());
379 list.add(polygon);
380 defaultMap.addMapPolygon(polygon);
381 }
382 }
383 } else if (mapPolygons.containsKey(i)) {
384 // Remove previously drawn map polygons
385 for (MapPolygon polygon : mapPolygons.get(i)) {
386 defaultMap.removeMapPolygon(polygon);
387 }
388 mapPolygons.remove(i);
389 }
390 // Only display bounds when no polygons (shapes) are defined for this provider
391 } else {
392 if (defaultTable.getSelectionModel().isSelectedIndex(viewIndex)) {
393 if (!mapRectangles.containsKey(i)) {
394 // Add new map rectangle
395 Coordinate topLeft = new Coordinate(bounds.getMaxLat(), bounds.getMinLon());
396 Coordinate bottomRight = new Coordinate(bounds.getMinLat(), bounds.getMaxLon());
397 MapRectangle rectangle = new MapRectangleImpl(topLeft, bottomRight);
398 mapRectangles.put(i, rectangle);
399 defaultMap.addMapRectangle(rectangle);
400 }
401 } else if (mapRectangles.containsKey(i)) {
402 // Remove previously drawn map rectangle
403 defaultMap.removeMapRectangle(mapRectangles.get(i));
404 mapRectangles.remove(i);
405 }
406 }
407 }
408 }
409
410 private <T> void doCleanupResidualBounds(Map<Integer, T> map, Consumer<T> removalEffect) {
411 for (Iterator<Entry<Integer, T>> it = map.entrySet().iterator(); it.hasNext();) {
412 Entry<Integer, T> e = it.next();
413 int viewIndex = defaultTable.convertRowIndexToView(e.getKey());
414 if (!defaultTable.getSelectionModel().isSelectedIndex(viewIndex)) {
415 it.remove();
416 removalEffect.accept(e.getValue());
417 }
418 }
419 }
420
421 private void cleanupResidualBounds() {
422 doCleanupResidualBounds(mapPolygons, l -> l.forEach(defaultMap::removeMapPolygon));
423 doCleanupResidualBounds(mapRectangles, defaultMap::removeMapRectangle);
424 }
425 }
426
427 private class NewEntryAction extends AbstractAction {
428
429 private final ImageryInfo.ImageryType type;
430
431 NewEntryAction(ImageryInfo.ImageryType type) {
432 putValue(NAME, type.toString());
433 putValue(SHORT_DESCRIPTION, tr("Add a new {0} entry by entering the URL", type.toString()));
434 String icon = /* ICON(dialogs/) */ "add";
435 switch (type) {
436 case WMS:
437 icon = /* ICON(dialogs/) */ "add_wms";
438 break;
439 case TMS:
440 icon = /* ICON(dialogs/) */ "add_tms";
441 break;
442 case WMTS:
443 icon = /* ICON(dialogs/) */ "add_wmts";
444 break;
445 case MVT:
446 icon = /* ICON(dialogs/) */ "add_mvt";
447 break;
448 default:
449 break;
450 }
451 new ImageProvider("dialogs", icon).getResource().attachImageIcon(this, true);
452 this.type = type;
453 }
454
455 @Override
456 public void actionPerformed(ActionEvent evt) {
457 final AddImageryPanel p;
458 switch (type) {
459 case WMS:
460 p = new AddWMSLayerPanel();
461 break;
462 case TMS:
463 p = new AddTMSLayerPanel();
464 break;
465 case WMTS:
466 p = new AddWMTSLayerPanel();
467 break;
468 case MVT:
469 p = new AddMVTLayerPanel();
470 break;
471 default:
472 throw new IllegalStateException("Type " + type + " not supported");
473 }
474
475 final AddImageryDialog addDialog = new AddImageryDialog(gui, p);
476 addDialog.showDialog();
477
478 if (addDialog.getValue() == 1) {
479 try {
480 activeModel.addRow(p.getImageryInfo());
481 } catch (IllegalArgumentException ex) {
482 if (Utils.isEmpty(ex.getMessage()))
483 throw ex;
484 else {
485 JOptionPane.showMessageDialog(MainApplication.getMainFrame(),
486 ex.getMessage(), tr("Error"),
487 JOptionPane.ERROR_MESSAGE);
488 }
489 }
490 }
491 }
492 }
493
494 private class RemoveEntryAction extends AbstractAction implements ListSelectionListener {
495
496 /**
497 * Constructs a new {@code RemoveEntryAction}.
498 */
499 RemoveEntryAction() {
500 putValue(NAME, tr("Remove"));
501 putValue(SHORT_DESCRIPTION, tr("Remove entry"));
502 new ImageProvider("dialogs", "delete").getResource().attachImageIcon(this, true);
503 updateEnabledState();
504 }
505
506 protected final void updateEnabledState() {
507 setEnabled(activeTable.getSelectedRowCount() > 0);
508 }
509
510 @Override
511 public void valueChanged(ListSelectionEvent e) {
512 updateEnabledState();
513 }
514
515 @Override
516 public void actionPerformed(ActionEvent e) {
517 int i;
518 while ((i = activeTable.getSelectedRow()) != -1) {
519 activeModel.removeRow(i);
520 }
521 }
522 }
523
524 private class ActivateAction extends AbstractAction implements ListSelectionListener {
525
526 /**
527 * Constructs a new {@code ActivateAction}.
528 */
529 ActivateAction() {
530 putValue(NAME, tr("Activate"));
531 putValue(SHORT_DESCRIPTION, tr("Copy selected default entries from the list above into the list below."));
532 new ImageProvider("preferences", "activate-down").getResource().attachImageIcon(this, true);
533 }
534
535 protected void updateEnabledState() {
536 setEnabled(defaultTable.getSelectedRowCount() > 0);
537 }
538
539 @Override
540 public void valueChanged(ListSelectionEvent e) {
541 updateEnabledState();
542 }
543
544 @Override
545 public void actionPerformed(ActionEvent e) {
546 int[] lines = defaultTable.getSelectedRows();
547 if (lines.length == 0) {
548 JOptionPane.showMessageDialog(
549 gui,
550 tr("Please select at least one row to copy."),
551 tr("Information"),
552 JOptionPane.INFORMATION_MESSAGE);
553 return;
554 }
555
556 Set<String> acceptedEulas = new HashSet<>();
557
558 outer:
559 for (int line : lines) {
560 ImageryInfo info = defaultModel.getRow(defaultTable.convertRowIndexToModel(line));
561
562 // Check if an entry with exactly the same values already exists
563 for (int j = 0; j < activeModel.getRowCount(); j++) {
564 if (info.equalsBaseValues(activeModel.getRow(j))) {
565 // Select the already existing row so the user has
566 // some feedback in case an entry exists
567 activeTable.getSelectionModel().setSelectionInterval(j, j);
568 activeTable.scrollRectToVisible(activeTable.getCellRect(j, 0, true));
569 continue outer;
570 }
571 }
572
573 String eulaURL = info.getEulaAcceptanceRequired();
574 // If set and not already accepted, ask for EULA acceptance
575 if (eulaURL != null && !acceptedEulas.contains(eulaURL)) {
576 if (confirmEulaAcceptance(gui, eulaURL)) {
577 acceptedEulas.add(eulaURL);
578 } else {
579 continue outer;
580 }
581 }
582
583 activeModel.addRow(new ImageryInfo(info));
584 int lastLine = activeModel.getRowCount() - 1;
585 activeTable.getSelectionModel().setSelectionInterval(lastLine, lastLine);
586 activeTable.scrollRectToVisible(activeTable.getCellRect(lastLine, 0, true));
587 }
588 }
589 }
590
591 private class ReloadAction extends AbstractAction {
592
593 /**
594 * Constructs a new {@code ReloadAction}.
595 */
596 ReloadAction() {
597 putValue(SHORT_DESCRIPTION, tr("Update default entries"));
598 new ImageProvider("dialogs", "refresh").getResource().attachImageIcon(this, true);
599 }
600
601 @Override
602 public void actionPerformed(ActionEvent evt) {
603 layerInfo.loadDefaults(true, MainApplication.worker, false);
604 this.updateTableArea();
605 // Ensure that the table is updated properly if someone cancels the download. See JOSM #21797.
606 MainApplication.worker.execute(() -> GuiHelper.runInEDTAndWait(this::updateTableArea));
607 }
608
609 private void updateTableArea() {
610 defaultModel.fireTableDataChanged();
611 defaultTable.getSelectionModel().clearSelection();
612 defaultTableListener.clearMap();
613 /* loading new file may change active layers */
614 activeModel.fireTableDataChanged();
615 }
616 }
617
618 /**
619 * The table model for imagery layer list
620 */
621 public class ImageryLayerTableModel extends DefaultTableModel {
622 /**
623 * Constructs a new {@code ImageryLayerTableModel}.
624 */
625 public ImageryLayerTableModel() {
626 setColumnIdentifiers(new String[] {tr("Menu Name"), tr("Imagery URL")});
627 }
628
629 /**
630 * Returns the imagery info at the given row number.
631 * @param row The row number
632 * @return The imagery info at the given row number
633 */
634 public ImageryInfo getRow(int row) {
635 return layerInfo.getLayers().get(row);
636 }
637
638 /**
639 * Adds a new imagery info as the last row.
640 * @param i The imagery info to add
641 */
642 public void addRow(ImageryInfo i) {
643 layerInfo.add(i);
644 int p = getRowCount() - 1;
645 fireTableRowsInserted(p, p);
646 }
647
648 @Override
649 public void removeRow(int i) {
650 layerInfo.remove(getRow(i));
651 fireTableRowsDeleted(i, i);
652 }
653
654 @Override
655 public int getRowCount() {
656 return layerInfo.getLayers().size();
657 }
658
659 @Override
660 public Object getValueAt(int row, int column) {
661 ImageryInfo info = layerInfo.getLayers().get(row);
662 switch (column) {
663 case 0:
664 return info.getName();
665 case 1:
666 return info.getExtendedUrl();
667 default:
668 throw new ArrayIndexOutOfBoundsException(Integer.toString(column));
669 }
670 }
671
672 @Override
673 public void setValueAt(Object o, int row, int column) {
674 if (layerInfo.getLayers().size() <= row) return;
675 ImageryInfo info = layerInfo.getLayers().get(row);
676 switch (column) {
677 case 0:
678 info.setName((String) o);
679 info.clearId();
680 break;
681 case 1:
682 info.setExtendedUrl((String) o);
683 info.clearId();
684 break;
685 default:
686 throw new ArrayIndexOutOfBoundsException(Integer.toString(column));
687 }
688 }
689 }
690
691 /**
692 * The table model for the default imagery layer list
693 */
694 public class ImageryDefaultLayerTableModel extends DefaultTableModel {
695 /**
696 * Constructs a new {@code ImageryDefaultLayerTableModel}.
697 */
698 public ImageryDefaultLayerTableModel() {
699 setColumnIdentifiers(new String[]{"", "", tr("Menu Name (Default)"), tr("Imagery URL (Default)")});
700 }
701
702 /**
703 * Returns the imagery info at the given row number.
704 * @param row The row number
705 * @return The imagery info at the given row number
706 */
707 public ImageryInfo getRow(int row) {
708 return layerInfo.getAllDefaultLayers().get(row);
709 }
710
711 @Override
712 public int getRowCount() {
713 return layerInfo.getAllDefaultLayers().size();
714 }
715
716 @Override
717 public Class<?> getColumnClass(int columnIndex) {
718 switch (columnIndex) {
719 case 0:
720 return ImageryCategory.class;
721 case 1:
722 return String.class;
723 case 2:
724 return ImageryInfo.class;
725 case 3:
726 return String.class;
727 default:
728 return super.getColumnClass(columnIndex);
729 }
730 }
731
732 @Override
733 public Object getValueAt(int row, int column) {
734 ImageryInfo info = layerInfo.getAllDefaultLayers().get(row);
735 switch (column) {
736 case 0:
737 return Optional.ofNullable(info.getImageryCategory()).orElse(ImageryCategory.OTHER);
738 case 1:
739 return info.getCountryCode();
740 case 2:
741 return info;
742 case 3:
743 return info.getExtendedUrl();
744 default:
745 return null;
746 }
747 }
748
749 @Override
750 public boolean isCellEditable(int row, int column) {
751 return false;
752 }
753 }
754
755 private static boolean confirmEulaAcceptance(PreferenceTabbedPane gui, String eulaUrl) {
756 URL url;
757 try {
758 url = new URL(eulaUrl.replaceAll("\\{lang}", LanguageInfo.getWikiLanguagePrefix()));
759 JosmEditorPane htmlPane;
760 try {
761 htmlPane = new JosmEditorPane(url);
762 } catch (IOException e1) {
763 Logging.trace(e1);
764 // give a second chance with a default Locale 'en'
765 try {
766 url = new URL(eulaUrl.replaceAll("\\{lang}", ""));
767 htmlPane = new JosmEditorPane(url);
768 } catch (IOException e2) {
769 Logging.debug(e2);
770 JOptionPane.showMessageDialog(gui, tr("EULA license URL not available: {0}", eulaUrl));
771 return false;
772 }
773 }
774 Box box = Box.createVerticalBox();
775 htmlPane.setEditable(false);
776 JScrollPane scrollPane = new JScrollPane(htmlPane);
777 scrollPane.setPreferredSize(new Dimension(400, 400));
778 box.add(scrollPane);
779 int option = JOptionPane.showConfirmDialog(MainApplication.getMainFrame(), box, tr("Please abort if you are not sure"),
780 JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
781 if (option == JOptionPane.YES_OPTION)
782 return true;
783 } catch (MalformedURLException e2) {
784 JOptionPane.showMessageDialog(gui, tr("Malformed URL for the EULA licence: {0}", eulaUrl));
785 }
786 return false;
787 }
788}
Note: See TracBrowser for help on using the repository browser.