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

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

see #16288 - replace similar i18n strings (removes 35 strings from Launchpad)

  • Property svn:eol-style set to native
File size: 39.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.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.ListSelectionEvent;
42import javax.swing.event.ListSelectionListener;
43import javax.swing.table.DefaultTableCellRenderer;
44import javax.swing.table.DefaultTableModel;
45import javax.swing.table.TableColumnModel;
46
47import org.openstreetmap.gui.jmapviewer.Coordinate;
48import org.openstreetmap.gui.jmapviewer.JMapViewer;
49import org.openstreetmap.gui.jmapviewer.MapPolygonImpl;
50import org.openstreetmap.gui.jmapviewer.MapRectangleImpl;
51import org.openstreetmap.gui.jmapviewer.interfaces.MapPolygon;
52import org.openstreetmap.gui.jmapviewer.interfaces.MapRectangle;
53import org.openstreetmap.gui.jmapviewer.tilesources.OsmTileSource;
54import org.openstreetmap.josm.Main;
55import org.openstreetmap.josm.data.coor.EastNorth;
56import org.openstreetmap.josm.data.imagery.ImageryInfo;
57import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryBounds;
58import org.openstreetmap.josm.data.imagery.ImageryLayerInfo;
59import org.openstreetmap.josm.data.imagery.OffsetBookmark;
60import org.openstreetmap.josm.data.imagery.Shape;
61import org.openstreetmap.josm.data.preferences.NamedColorProperty;
62import org.openstreetmap.josm.gui.MainApplication;
63import org.openstreetmap.josm.gui.download.DownloadDialog;
64import org.openstreetmap.josm.gui.help.HelpUtil;
65import org.openstreetmap.josm.gui.preferences.DefaultTabPreferenceSetting;
66import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
67import org.openstreetmap.josm.gui.preferences.PreferenceSettingFactory;
68import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane;
69import org.openstreetmap.josm.gui.util.GuiHelper;
70import org.openstreetmap.josm.gui.widgets.HtmlPanel;
71import org.openstreetmap.josm.gui.widgets.JosmEditorPane;
72import org.openstreetmap.josm.tools.GBC;
73import org.openstreetmap.josm.tools.ImageProvider;
74import org.openstreetmap.josm.tools.LanguageInfo;
75import org.openstreetmap.josm.tools.Logging;
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 MainApplication.getMenu().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 NamedColorProperty IMAGERY_BACKGROUND_COLOR = new NamedColorProperty(
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 try {
314 return activeModel.getValueAt(rowAtPoint(p), columnAtPoint(p)).toString();
315 } catch (ArrayIndexOutOfBoundsException ex) {
316 Logging.debug(ex);
317 return null;
318 }
319 }
320 };
321 activeTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
322
323 defaultModel = new ImageryDefaultLayerTableModel();
324 defaultTable = new JTable(defaultModel);
325
326 defaultModel.addTableModelListener(e -> activeTable.repaint());
327 activeModel.addTableModelListener(e -> defaultTable.repaint());
328
329 TableColumnModel mod = defaultTable.getColumnModel();
330 mod.getColumn(2).setPreferredWidth(800);
331 mod.getColumn(2).setCellRenderer(new ImageryURLTableCellRenderer(layerInfo.getLayers()));
332 mod.getColumn(1).setPreferredWidth(400);
333 mod.getColumn(1).setCellRenderer(new ImageryNameTableCellRenderer());
334 mod.getColumn(0).setPreferredWidth(50);
335
336 mod = activeTable.getColumnModel();
337 mod.getColumn(1).setPreferredWidth(800);
338 mod.getColumn(1).setCellRenderer(new ImageryURLTableCellRenderer(layerInfo.getAllDefaultLayers()));
339 mod.getColumn(0).setPreferredWidth(200);
340
341 RemoveEntryAction remove = new RemoveEntryAction();
342 activeTable.getSelectionModel().addListSelectionListener(remove);
343
344 add(new JLabel(tr("Available default entries:")), GBC.std().insets(5, 5, 0, 0));
345 add(new JLabel(tr("Boundaries of selected imagery entries:")), GBC.eol().insets(5, 5, 0, 0));
346
347 // Add default item list
348 JScrollPane scrolldef = new JScrollPane(defaultTable);
349 scrolldef.setPreferredSize(new Dimension(200, 200));
350 add(scrolldef, GBC.std().insets(0, 5, 0, 0).fill(GridBagConstraints.BOTH).weight(1.0, 0.6).insets(5, 0, 0, 0));
351
352 // Add default item map
353 defaultMap = new JMapViewer();
354 defaultMap.setTileSource(new OsmTileSource.Mapnik()); // for attribution
355 defaultMap.addMouseListener(new MouseAdapter() {
356 @Override
357 public void mouseClicked(MouseEvent e) {
358 if (e.getButton() == MouseEvent.BUTTON1) {
359 defaultMap.getAttribution().handleAttribution(e.getPoint(), true);
360 }
361 }
362 });
363 defaultMap.setZoomControlsVisible(false);
364 defaultMap.setMinimumSize(new Dimension(100, 200));
365 add(defaultMap, GBC.std().insets(5, 5, 0, 0).fill(GridBagConstraints.BOTH).weight(0.33, 0.6).insets(5, 0, 0, 0));
366
367 defaultTableListener = new DefListSelectionListener();
368 defaultTable.getSelectionModel().addListSelectionListener(defaultTableListener);
369
370 defaultToolbar = new JToolBar(JToolBar.VERTICAL);
371 defaultToolbar.setFloatable(false);
372 defaultToolbar.setBorderPainted(false);
373 defaultToolbar.setOpaque(false);
374 defaultToolbar.add(new ReloadAction());
375 add(defaultToolbar, GBC.eol().anchor(GBC.SOUTH).insets(0, 0, 5, 0));
376
377 HtmlPanel help = new HtmlPanel(tr("New default entries can be added in the <a href=\"{0}\">Wiki</a>.",
378 Main.getJOSMWebsite()+"/wiki/Maps"));
379 help.enableClickableHyperlinks();
380 add(help, GBC.eol().insets(10, 0, 0, 0).fill(GBC.HORIZONTAL));
381
382 ActivateAction activate = new ActivateAction();
383 defaultTable.getSelectionModel().addListSelectionListener(activate);
384 JButton btnActivate = new JButton(activate);
385
386 middleToolbar = new JToolBar(JToolBar.HORIZONTAL);
387 middleToolbar.setFloatable(false);
388 middleToolbar.setBorderPainted(false);
389 middleToolbar.setOpaque(false);
390 middleToolbar.add(btnActivate);
391 add(middleToolbar, GBC.eol().anchor(GBC.CENTER).insets(5, 5, 5, 0));
392
393 add(Box.createHorizontalGlue(), GBC.eol().fill(GridBagConstraints.HORIZONTAL));
394
395 add(new JLabel(tr("Selected entries:")), GBC.eol().insets(5, 0, 0, 0));
396 JScrollPane scroll = new JScrollPane(activeTable);
397 add(scroll, GBC.std().fill(GridBagConstraints.BOTH).span(GridBagConstraints.RELATIVE).weight(1.0, 0.4).insets(5, 0, 0, 5));
398 scroll.setPreferredSize(new Dimension(200, 200));
399
400 activeToolbar = new JToolBar(JToolBar.VERTICAL);
401 activeToolbar.setFloatable(false);
402 activeToolbar.setBorderPainted(false);
403 activeToolbar.setOpaque(false);
404 activeToolbar.add(new NewEntryAction(ImageryInfo.ImageryType.WMS));
405 activeToolbar.add(new NewEntryAction(ImageryInfo.ImageryType.TMS));
406 activeToolbar.add(new NewEntryAction(ImageryInfo.ImageryType.WMTS));
407 //activeToolbar.add(edit); TODO
408 activeToolbar.add(remove);
409 add(activeToolbar, GBC.eol().anchor(GBC.NORTH).insets(0, 0, 5, 5));
410 }
411
412 // Listener of default providers list selection
413 private final class DefListSelectionListener implements ListSelectionListener {
414 // The current drawn rectangles and polygons
415 private final Map<Integer, MapRectangle> mapRectangles;
416 private final Map<Integer, List<MapPolygon>> mapPolygons;
417
418 private DefListSelectionListener() {
419 this.mapRectangles = new HashMap<>();
420 this.mapPolygons = new HashMap<>();
421 }
422
423 private void clearMap() {
424 defaultMap.removeAllMapRectangles();
425 defaultMap.removeAllMapPolygons();
426 mapRectangles.clear();
427 mapPolygons.clear();
428 }
429
430 @Override
431 public void valueChanged(ListSelectionEvent e) {
432 // First index can be set to -1 when the list is refreshed, so discard all map rectangles and polygons
433 if (e.getFirstIndex() == -1) {
434 clearMap();
435 } else if (!e.getValueIsAdjusting()) {
436 // Only process complete (final) selection events
437 for (int i = e.getFirstIndex(); i <= e.getLastIndex(); i++) {
438 updateBoundsAndShapes(i);
439 }
440 // If needed, adjust map to show all map rectangles and polygons
441 if (!mapRectangles.isEmpty() || !mapPolygons.isEmpty()) {
442 defaultMap.setDisplayToFitMapElements(false, true, true);
443 defaultMap.zoomOut();
444 }
445 }
446 }
447
448 private void updateBoundsAndShapes(int i) {
449 ImageryBounds bounds = defaultModel.getRow(i).getBounds();
450 if (bounds != null) {
451 List<Shape> shapes = bounds.getShapes();
452 if (shapes != null && !shapes.isEmpty()) {
453 if (defaultTable.getSelectionModel().isSelectedIndex(i)) {
454 if (!mapPolygons.containsKey(i)) {
455 List<MapPolygon> list = new ArrayList<>();
456 mapPolygons.put(i, list);
457 // Add new map polygons
458 for (Shape shape : shapes) {
459 MapPolygon polygon = new MapPolygonImpl(shape.getPoints());
460 list.add(polygon);
461 defaultMap.addMapPolygon(polygon);
462 }
463 }
464 } else if (mapPolygons.containsKey(i)) {
465 // Remove previously drawn map polygons
466 for (MapPolygon polygon : mapPolygons.get(i)) {
467 defaultMap.removeMapPolygon(polygon);
468 }
469 mapPolygons.remove(i);
470 }
471 // Only display bounds when no polygons (shapes) are defined for this provider
472 } else {
473 if (defaultTable.getSelectionModel().isSelectedIndex(i)) {
474 if (!mapRectangles.containsKey(i)) {
475 // Add new map rectangle
476 Coordinate topLeft = new Coordinate(bounds.getMaxLat(), bounds.getMinLon());
477 Coordinate bottomRight = new Coordinate(bounds.getMinLat(), bounds.getMaxLon());
478 MapRectangle rectangle = new MapRectangleImpl(topLeft, bottomRight);
479 mapRectangles.put(i, rectangle);
480 defaultMap.addMapRectangle(rectangle);
481 }
482 } else if (mapRectangles.containsKey(i)) {
483 // Remove previously drawn map rectangle
484 defaultMap.removeMapRectangle(mapRectangles.get(i));
485 mapRectangles.remove(i);
486 }
487 }
488 }
489 }
490 }
491
492 private class NewEntryAction extends AbstractAction {
493
494 private final ImageryInfo.ImageryType type;
495
496 NewEntryAction(ImageryInfo.ImageryType type) {
497 putValue(NAME, type.toString());
498 putValue(SHORT_DESCRIPTION, tr("Add a new {0} entry by entering the URL", type.toString()));
499 String icon = /* ICON(dialogs/) */ "add";
500 switch (type) {
501 case WMS:
502 icon = /* ICON(dialogs/) */ "add_wms";
503 break;
504 case TMS:
505 icon = /* ICON(dialogs/) */ "add_tms";
506 break;
507 case WMTS:
508 icon = /* ICON(dialogs/) */ "add_wmts";
509 break;
510 default:
511 break;
512 }
513 new ImageProvider("dialogs", icon).getResource().attachImageIcon(this, true);
514 this.type = type;
515 }
516
517 @Override
518 public void actionPerformed(ActionEvent evt) {
519 final AddImageryPanel p;
520 switch (type) {
521 case WMS:
522 p = new AddWMSLayerPanel();
523 break;
524 case TMS:
525 p = new AddTMSLayerPanel();
526 break;
527 case WMTS:
528 p = new AddWMTSLayerPanel();
529 break;
530 default:
531 throw new IllegalStateException("Type " + type + " not supported");
532 }
533
534 final AddImageryDialog addDialog = new AddImageryDialog(gui, p);
535 addDialog.showDialog();
536
537 if (addDialog.getValue() == 1) {
538 try {
539 activeModel.addRow(p.getImageryInfo());
540 } catch (IllegalArgumentException ex) {
541 if (ex.getMessage() == null || ex.getMessage().isEmpty())
542 throw ex;
543 else {
544 JOptionPane.showMessageDialog(Main.parent,
545 ex.getMessage(), tr("Error"),
546 JOptionPane.ERROR_MESSAGE);
547 }
548 }
549 }
550 }
551 }
552
553 private class RemoveEntryAction extends AbstractAction implements ListSelectionListener {
554
555 /**
556 * Constructs a new {@code RemoveEntryAction}.
557 */
558 RemoveEntryAction() {
559 putValue(NAME, tr("Remove"));
560 putValue(SHORT_DESCRIPTION, tr("Remove entry"));
561 new ImageProvider("dialogs", "delete").getResource().attachImageIcon(this, true);
562 updateEnabledState();
563 }
564
565 protected final void updateEnabledState() {
566 setEnabled(activeTable.getSelectedRowCount() > 0);
567 }
568
569 @Override
570 public void valueChanged(ListSelectionEvent e) {
571 updateEnabledState();
572 }
573
574 @Override
575 public void actionPerformed(ActionEvent e) {
576 Integer i;
577 while ((i = activeTable.getSelectedRow()) != -1) {
578 activeModel.removeRow(i);
579 }
580 }
581 }
582
583 private class ActivateAction extends AbstractAction implements ListSelectionListener {
584
585 /**
586 * Constructs a new {@code ActivateAction}.
587 */
588 ActivateAction() {
589 putValue(NAME, tr("Activate"));
590 putValue(SHORT_DESCRIPTION, tr("Copy selected default entries from the list above into the list below."));
591 new ImageProvider("preferences", "activate-down").getResource().attachImageIcon(this, true);
592 }
593
594 protected void updateEnabledState() {
595 setEnabled(defaultTable.getSelectedRowCount() > 0);
596 }
597
598 @Override
599 public void valueChanged(ListSelectionEvent e) {
600 updateEnabledState();
601 }
602
603 @Override
604 public void actionPerformed(ActionEvent e) {
605 int[] lines = defaultTable.getSelectedRows();
606 if (lines.length == 0) {
607 JOptionPane.showMessageDialog(
608 gui,
609 tr("Please select at least one row to copy."),
610 tr("Information"),
611 JOptionPane.INFORMATION_MESSAGE);
612 return;
613 }
614
615 Set<String> acceptedEulas = new HashSet<>();
616
617 outer:
618 for (int line : lines) {
619 ImageryInfo info = defaultModel.getRow(line);
620
621 // Check if an entry with exactly the same values already exists
622 for (int j = 0; j < activeModel.getRowCount(); j++) {
623 if (info.equalsBaseValues(activeModel.getRow(j))) {
624 // Select the already existing row so the user has
625 // some feedback in case an entry exists
626 activeTable.getSelectionModel().setSelectionInterval(j, j);
627 activeTable.scrollRectToVisible(activeTable.getCellRect(j, 0, true));
628 continue outer;
629 }
630 }
631
632 String eulaURL = info.getEulaAcceptanceRequired();
633 // If set and not already accepted, ask for EULA acceptance
634 if (eulaURL != null && !acceptedEulas.contains(eulaURL)) {
635 if (confirmEulaAcceptance(gui, eulaURL)) {
636 acceptedEulas.add(eulaURL);
637 } else {
638 continue outer;
639 }
640 }
641
642 activeModel.addRow(new ImageryInfo(info));
643 int lastLine = activeModel.getRowCount() - 1;
644 activeTable.getSelectionModel().setSelectionInterval(lastLine, lastLine);
645 activeTable.scrollRectToVisible(activeTable.getCellRect(lastLine, 0, true));
646 }
647 }
648 }
649
650 private class ReloadAction extends AbstractAction {
651
652 /**
653 * Constructs a new {@code ReloadAction}.
654 */
655 ReloadAction() {
656 putValue(SHORT_DESCRIPTION, tr("Update default entries"));
657 new ImageProvider("dialogs", "refresh").getResource().attachImageIcon(this, true);
658 }
659
660 @Override
661 public void actionPerformed(ActionEvent evt) {
662 layerInfo.loadDefaults(true, MainApplication.worker, false);
663 defaultModel.fireTableDataChanged();
664 defaultTable.getSelectionModel().clearSelection();
665 defaultTableListener.clearMap();
666 /* loading new file may change active layers */
667 activeModel.fireTableDataChanged();
668 }
669 }
670
671 /**
672 * The table model for imagery layer list
673 */
674 public class ImageryLayerTableModel extends DefaultTableModel {
675 /**
676 * Constructs a new {@code ImageryLayerTableModel}.
677 */
678 public ImageryLayerTableModel() {
679 setColumnIdentifiers(new String[] {tr("Menu Name"), tr("Imagery URL")});
680 }
681
682 /**
683 * Returns the imagery info at the given row number.
684 * @param row The row number
685 * @return The imagery info at the given row number
686 */
687 public ImageryInfo getRow(int row) {
688 return layerInfo.getLayers().get(row);
689 }
690
691 /**
692 * Adds a new imagery info as the last row.
693 * @param i The imagery info to add
694 */
695 public void addRow(ImageryInfo i) {
696 layerInfo.add(i);
697 int p = getRowCount() - 1;
698 fireTableRowsInserted(p, p);
699 }
700
701 @Override
702 public void removeRow(int i) {
703 layerInfo.remove(getRow(i));
704 fireTableRowsDeleted(i, i);
705 }
706
707 @Override
708 public int getRowCount() {
709 return layerInfo.getLayers().size();
710 }
711
712 @Override
713 public Object getValueAt(int row, int column) {
714 ImageryInfo info = layerInfo.getLayers().get(row);
715 switch (column) {
716 case 0:
717 return info.getName();
718 case 1:
719 return info.getExtendedUrl();
720 default:
721 throw new ArrayIndexOutOfBoundsException(Integer.toString(column));
722 }
723 }
724
725 @Override
726 public void setValueAt(Object o, int row, int column) {
727 if (layerInfo.getLayers().size() <= row) return;
728 ImageryInfo info = layerInfo.getLayers().get(row);
729 switch (column) {
730 case 0:
731 info.setName((String) o);
732 info.clearId();
733 break;
734 case 1:
735 info.setExtendedUrl((String) o);
736 info.clearId();
737 break;
738 default:
739 throw new ArrayIndexOutOfBoundsException(Integer.toString(column));
740 }
741 }
742 }
743
744 /**
745 * The table model for the default imagery layer list
746 */
747 public class ImageryDefaultLayerTableModel extends DefaultTableModel {
748 /**
749 * Constructs a new {@code ImageryDefaultLayerTableModel}.
750 */
751 public ImageryDefaultLayerTableModel() {
752 setColumnIdentifiers(new String[]{"", tr("Menu Name (Default)"), tr("Imagery URL (Default)")});
753 }
754
755 /**
756 * Returns the imagery info at the given row number.
757 * @param row The row number
758 * @return The imagery info at the given row number
759 */
760 public ImageryInfo getRow(int row) {
761 return layerInfo.getAllDefaultLayers().get(row);
762 }
763
764 @Override
765 public int getRowCount() {
766 return layerInfo.getAllDefaultLayers().size();
767 }
768
769 @Override
770 public Object getValueAt(int row, int column) {
771 ImageryInfo info = layerInfo.getAllDefaultLayers().get(row);
772 switch (column) {
773 case 0:
774 return info.getCountryCode();
775 case 1:
776 return info;
777 case 2:
778 return info.getExtendedUrl();
779 }
780 return null;
781 }
782
783 @Override
784 public boolean isCellEditable(int row, int column) {
785 return false;
786 }
787 }
788
789 private static boolean confirmEulaAcceptance(PreferenceTabbedPane gui, String eulaUrl) {
790 URL url;
791 try {
792 url = new URL(eulaUrl.replaceAll("\\{lang\\}", LanguageInfo.getWikiLanguagePrefix()));
793 JosmEditorPane htmlPane;
794 try {
795 htmlPane = new JosmEditorPane(url);
796 } catch (IOException e1) {
797 Logging.trace(e1);
798 // give a second chance with a default Locale 'en'
799 try {
800 url = new URL(eulaUrl.replaceAll("\\{lang\\}", ""));
801 htmlPane = new JosmEditorPane(url);
802 } catch (IOException e2) {
803 Logging.debug(e2);
804 JOptionPane.showMessageDialog(gui, tr("EULA license URL not available: {0}", eulaUrl));
805 return false;
806 }
807 }
808 Box box = Box.createVerticalBox();
809 htmlPane.setEditable(false);
810 JScrollPane scrollPane = new JScrollPane(htmlPane);
811 scrollPane.setPreferredSize(new Dimension(400, 400));
812 box.add(scrollPane);
813 int option = JOptionPane.showConfirmDialog(Main.parent, box, tr("Please abort if you are not sure"),
814 JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
815 if (option == JOptionPane.YES_OPTION)
816 return true;
817 } catch (MalformedURLException e2) {
818 JOptionPane.showMessageDialog(gui, tr("Malformed URL for the EULA licence: {0}", eulaUrl));
819 }
820 return false;
821 }
822 }
823
824 static class OffsetBookmarksPanel extends JPanel {
825 private final OffsetsBookmarksModel model = new OffsetsBookmarksModel();
826
827 /**
828 * Constructs a new {@code OffsetBookmarksPanel}.
829 * @param gui the preferences tab pane
830 */
831 OffsetBookmarksPanel(final PreferenceTabbedPane gui) {
832 super(new GridBagLayout());
833 final JTable list = new JTable(model) {
834 @Override
835 public String getToolTipText(MouseEvent e) {
836 java.awt.Point p = e.getPoint();
837 return model.getValueAt(rowAtPoint(p), columnAtPoint(p)).toString();
838 }
839 };
840 JScrollPane scroll = new JScrollPane(list);
841 add(scroll, GBC.eol().fill(GridBagConstraints.BOTH));
842 scroll.setPreferredSize(new Dimension(200, 200));
843
844 TableColumnModel mod = list.getColumnModel();
845 mod.getColumn(0).setPreferredWidth(150);
846 mod.getColumn(1).setPreferredWidth(200);
847 mod.getColumn(2).setPreferredWidth(300);
848 mod.getColumn(3).setPreferredWidth(150);
849 mod.getColumn(4).setPreferredWidth(150);
850
851 JPanel buttonPanel = new JPanel(new FlowLayout());
852
853 JButton add = new JButton(tr("Add"));
854 buttonPanel.add(add, GBC.std().insets(0, 5, 0, 0));
855 add.addActionListener(e -> model.addRow(new OffsetBookmark(Main.getProjection().toCode(), "", "", 0, 0)));
856
857 JButton delete = new JButton(tr("Delete"));
858 buttonPanel.add(delete, GBC.std().insets(0, 5, 0, 0));
859 delete.addActionListener(e -> {
860 if (list.getSelectedRow() == -1) {
861 JOptionPane.showMessageDialog(gui, tr("Please select the row to delete."));
862 } else {
863 Integer i;
864 while ((i = list.getSelectedRow()) != -1) {
865 model.removeRow(i);
866 }
867 }
868 });
869
870 add(buttonPanel, GBC.eol());
871 }
872
873 /**
874 * The table model for imagery offsets list
875 */
876 private static class OffsetsBookmarksModel extends DefaultTableModel {
877
878 /**
879 * Constructs a new {@code OffsetsBookmarksModel}.
880 */
881 OffsetsBookmarksModel() {
882 setColumnIdentifiers(new String[] {tr("Projection"), tr("Layer"), tr("Name"), tr("Easting"), tr("Northing")});
883 }
884
885 private static OffsetBookmark getRow(int row) {
886 return OffsetBookmark.getBookmarkByIndex(row);
887 }
888
889 private void addRow(OffsetBookmark i) {
890 OffsetBookmark.addBookmark(i);
891 int p = getRowCount() - 1;
892 fireTableRowsInserted(p, p);
893 }
894
895 @Override
896 public void removeRow(int i) {
897 OffsetBookmark.removeBookmark(getRow(i));
898 fireTableRowsDeleted(i, i);
899 }
900
901 @Override
902 public int getRowCount() {
903 return OffsetBookmark.getBookmarksSize();
904 }
905
906 @Override
907 public Object getValueAt(int row, int column) {
908 OffsetBookmark info = OffsetBookmark.getBookmarkByIndex(row);
909 switch (column) {
910 case 0:
911 if (info.getProjectionCode() == null) return "";
912 return info.getProjectionCode();
913 case 1:
914 return info.getImageryName();
915 case 2:
916 return info.getName();
917 case 3:
918 return info.getDisplacement().east();
919 case 4:
920 return info.getDisplacement().north();
921 default:
922 throw new ArrayIndexOutOfBoundsException();
923 }
924 }
925
926 @Override
927 public void setValueAt(Object o, int row, int column) {
928 OffsetBookmark info = OffsetBookmark.getBookmarkByIndex(row);
929 switch (column) {
930 case 1:
931 info.setImageryName(o.toString());
932 break;
933 case 2:
934 info.setName(o.toString());
935 break;
936 case 3:
937 double dx = Double.parseDouble((String) o);
938 info.setDisplacement(new EastNorth(dx, info.getDisplacement().north()));
939 break;
940 case 4:
941 double dy = Double.parseDouble((String) o);
942 info.setDisplacement(new EastNorth(info.getDisplacement().east(), dy));
943 break;
944 default:
945 throw new ArrayIndexOutOfBoundsException();
946 }
947 }
948
949 @Override
950 public boolean isCellEditable(int row, int column) {
951 return column >= 1;
952 }
953 }
954 }
955
956 /**
957 * Initializes imagery preferences.
958 */
959 public static void initialize() {
960 ImageryLayerInfo.instance.load(false);
961 OffsetBookmark.loadBookmarks();
962 MainApplication.getMenu().imageryMenu.refreshImageryMenu();
963 MainApplication.getMenu().imageryMenu.refreshOffsetMenu();
964 }
965
966 @Override
967 public String getHelpContext() {
968 return HelpUtil.ht("/Preferences/Imagery");
969 }
970}
Note: See TracBrowser for help on using the repository browser.