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

Last change on this file since 17862 was 17862, checked in by simon04, 3 years ago

fix #17177 - Add support for Mapbox Vector Tile (patch by taylor.smock)

Signed-off-by: Taylor Smock <tsmock@…>

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