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

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

fix #18142 - fix AIOOBE after changing imagery source filter in imagery preferences

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