// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.gui.preferences.imagery; import static org.openstreetmap.josm.tools.I18n.marktr; import static org.openstreetmap.josm.tools.I18n.tr; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Font; import java.awt.GraphicsEnvironment; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.event.ActionEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSeparator; import javax.swing.JTabbedPane; import javax.swing.JTable; import javax.swing.JToolBar; import javax.swing.UIManager; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableColumnModel; import org.openstreetmap.gui.jmapviewer.Coordinate; import org.openstreetmap.gui.jmapviewer.JMapViewer; import org.openstreetmap.gui.jmapviewer.MapPolygonImpl; import org.openstreetmap.gui.jmapviewer.MapRectangleImpl; import org.openstreetmap.gui.jmapviewer.interfaces.MapPolygon; import org.openstreetmap.gui.jmapviewer.interfaces.MapRectangle; import org.openstreetmap.gui.jmapviewer.tilesources.OsmTileSource; import org.openstreetmap.josm.Main; import org.openstreetmap.josm.data.imagery.ImageryInfo; import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryBounds; import org.openstreetmap.josm.data.imagery.ImageryLayerInfo; import org.openstreetmap.josm.data.imagery.OffsetBookmark; import org.openstreetmap.josm.data.imagery.Shape; import org.openstreetmap.josm.data.preferences.ColorProperty; import org.openstreetmap.josm.gui.download.DownloadDialog; import org.openstreetmap.josm.gui.preferences.DefaultTabPreferenceSetting; import org.openstreetmap.josm.gui.preferences.PreferenceSetting; import org.openstreetmap.josm.gui.preferences.PreferenceSettingFactory; import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane; import org.openstreetmap.josm.gui.util.GuiHelper; import org.openstreetmap.josm.gui.widgets.JosmEditorPane; import org.openstreetmap.josm.tools.GBC; import org.openstreetmap.josm.tools.ImageProvider; import org.openstreetmap.josm.tools.LanguageInfo; /** * Imagery preferences, including imagery providers, settings and offsets. * @since 3715 */ public final class ImageryPreference extends DefaultTabPreferenceSetting { private ImageryProvidersPanel imageryProviders; private ImageryLayerInfo layerInfo; private final CommonSettingsPanel commonSettings = new CommonSettingsPanel(); private final WMSSettingsPanel wmsSettings = new WMSSettingsPanel(); private final TMSSettingsPanel tmsSettings = new TMSSettingsPanel(); /** * Factory used to create a new {@code ImageryPreference}. */ public static class Factory implements PreferenceSettingFactory { @Override public PreferenceSetting createPreferenceSetting() { return new ImageryPreference(); } } private ImageryPreference() { super(/* ICON(preferences/) */ "imagery", tr("Imagery Preferences"), tr("Modify list of imagery layers displayed in the Imagery menu"), false, new JTabbedPane()); } private static void addSettingsSection(final JPanel p, String name, JPanel section) { addSettingsSection(p, name, section, GBC.eol()); } private static void addSettingsSection(final JPanel p, String name, JPanel section, GBC gbc) { final JLabel lbl = new JLabel(name); lbl.setFont(lbl.getFont().deriveFont(Font.BOLD)); lbl.setLabelFor(section); p.add(lbl, GBC.std()); p.add(new JSeparator(), GBC.eol().fill(GBC.HORIZONTAL).insets(5, 0, 0, 0)); p.add(section, gbc.insets(20, 5, 0, 10)); } private Component buildSettingsPanel() { final JPanel p = new JPanel(new GridBagLayout()); p.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); addSettingsSection(p, tr("Common Settings"), commonSettings); addSettingsSection(p, tr("WMS Settings"), wmsSettings, GBC.eol().fill(GBC.HORIZONTAL)); addSettingsSection(p, tr("TMS Settings"), tmsSettings, GBC.eol().fill(GBC.HORIZONTAL)); p.add(new JPanel(), GBC.eol().fill(GBC.BOTH)); return GuiHelper.setDefaultIncrement(new JScrollPane(p)); } @Override public void addGui(final PreferenceTabbedPane gui) { JPanel p = gui.createPreferenceTab(this); JTabbedPane pane = getTabPane(); layerInfo = new ImageryLayerInfo(ImageryLayerInfo.instance); imageryProviders = new ImageryProvidersPanel(gui, layerInfo); pane.addTab(tr("Imagery providers"), imageryProviders); pane.addTab(tr("Settings"), buildSettingsPanel()); pane.addTab(tr("Offset bookmarks"), new OffsetBookmarksPanel(gui)); pane.addTab(tr("Cache contents"), new CacheContentsPanel()); loadSettings(); p.add(pane, GBC.std().fill(GBC.BOTH)); } /** * Returns the imagery providers panel. * @return The imagery providers panel. */ public ImageryProvidersPanel getProvidersPanel() { return imageryProviders; } private void loadSettings() { commonSettings.loadSettings(); wmsSettings.loadSettings(); tmsSettings.loadSettings(); } @Override public boolean ok() { layerInfo.save(); ImageryLayerInfo.instance.clear(); ImageryLayerInfo.instance.load(false); Main.main.menu.imageryMenu.refreshOffsetMenu(); OffsetBookmark.saveBookmarks(); if (!GraphicsEnvironment.isHeadless()) { DownloadDialog.getInstance().refreshTileSources(); } boolean commonRestartRequired = commonSettings.saveSettings(); boolean wmsRestartRequired = wmsSettings.saveSettings(); boolean tmsRestartRequired = tmsSettings.saveSettings(); return commonRestartRequired || wmsRestartRequired || tmsRestartRequired; } /** * Updates a server URL in the preferences dialog. Used by plugins. * * @param server * The server name * @param url * The server URL */ public void setServerUrl(String server, String url) { for (int i = 0; i < imageryProviders.activeModel.getRowCount(); i++) { if (server.equals(imageryProviders.activeModel.getValueAt(i, 0).toString())) { imageryProviders.activeModel.setValueAt(url, i, 1); return; } } imageryProviders.activeModel.addRow(new String[] {server, url}); } /** * Gets a server URL in the preferences dialog. Used by plugins. * * @param server The server name * @return The server URL */ public String getServerUrl(String server) { for (int i = 0; i < imageryProviders.activeModel.getRowCount(); i++) { if (server.equals(imageryProviders.activeModel.getValueAt(i, 0).toString())) return imageryProviders.activeModel.getValueAt(i, 1).toString(); } return null; } /** * A panel displaying imagery providers. */ public static class ImageryProvidersPanel extends JPanel { // Public JTables and JMapViewer /** The table of active providers **/ public final JTable activeTable; /** The table of default providers **/ public final JTable defaultTable; /** The selection listener synchronizing map display with table of default providers **/ private final transient DefListSelectionListener defaultTableListener; /** The map displaying imagery bounds of selected default providers **/ public final JMapViewer defaultMap; // Public models /** The model of active providers **/ public final ImageryLayerTableModel activeModel; /** The model of default providers **/ public final ImageryDefaultLayerTableModel defaultModel; // Public JToolbars /** The toolbar on the right of active providers **/ public final JToolBar activeToolbar; /** The toolbar on the middle of the panel **/ public final JToolBar middleToolbar; /** The toolbar on the right of default providers **/ public final JToolBar defaultToolbar; // Private members private final PreferenceTabbedPane gui; private final transient ImageryLayerInfo layerInfo; /** * class to render the URL information of Imagery source * @since 8065 */ private static class ImageryURLTableCellRenderer extends DefaultTableCellRenderer { private static final ColorProperty IMAGERY_BACKGROUND_COLOR = new ColorProperty( marktr("Imagery Background: Default"), new Color(200, 255, 200)); private final transient List<ImageryInfo> layers; ImageryURLTableCellRenderer(List<ImageryInfo> layers) { this.layers = layers; } @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { JLabel label = (JLabel) super.getTableCellRendererComponent( table, value, isSelected, hasFocus, row, column); GuiHelper.setBackgroundReadable(label, UIManager.getColor("Table.background")); if (value != null) { // Fix #8159 String t = value.toString(); for (ImageryInfo l : layers) { if (l.getExtendedUrl().equals(t)) { GuiHelper.setBackgroundReadable(label, IMAGERY_BACKGROUND_COLOR.get()); break; } } label.setToolTipText((String) value); } return label; } } /** * class to render the name information of Imagery source * @since 8064 */ private static class ImageryNameTableCellRenderer extends DefaultTableCellRenderer { @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { ImageryInfo info = (ImageryInfo) value; JLabel label = (JLabel) super.getTableCellRendererComponent( table, info == null ? null : info.getName(), isSelected, hasFocus, row, column); GuiHelper.setBackgroundReadable(label, UIManager.getColor("Table.background")); if (info != null) { label.setToolTipText(info.getToolTipText()); } return label; } } /** * Constructs a new {@code ImageryProvidersPanel}. * @param gui The parent preference tab pane * @param layerInfoArg The list of imagery entries to display */ public ImageryProvidersPanel(final PreferenceTabbedPane gui, ImageryLayerInfo layerInfoArg) { super(new GridBagLayout()); this.gui = gui; this.layerInfo = layerInfoArg; this.activeModel = new ImageryLayerTableModel(); activeTable = new JTable(activeModel) { @Override public String getToolTipText(MouseEvent e) { java.awt.Point p = e.getPoint(); return activeModel.getValueAt(rowAtPoint(p), columnAtPoint(p)).toString(); } }; activeTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE); defaultModel = new ImageryDefaultLayerTableModel(); defaultTable = new JTable(defaultModel); defaultModel.addTableModelListener(e -> activeTable.repaint()); activeModel.addTableModelListener(e -> defaultTable.repaint()); TableColumnModel mod = defaultTable.getColumnModel(); mod.getColumn(2).setPreferredWidth(800); mod.getColumn(2).setCellRenderer(new ImageryURLTableCellRenderer(layerInfo.getLayers())); mod.getColumn(1).setPreferredWidth(400); mod.getColumn(1).setCellRenderer(new ImageryNameTableCellRenderer()); mod.getColumn(0).setPreferredWidth(50); mod = activeTable.getColumnModel(); mod.getColumn(1).setPreferredWidth(800); mod.getColumn(1).setCellRenderer(new ImageryURLTableCellRenderer(layerInfo.getAllDefaultLayers())); mod.getColumn(0).setPreferredWidth(200); RemoveEntryAction remove = new RemoveEntryAction(); activeTable.getSelectionModel().addListSelectionListener(remove); add(new JLabel(tr("Available default entries:")), GBC.eol().insets(5, 5, 0, 0)); // Add default item list JScrollPane scrolldef = new JScrollPane(defaultTable); scrolldef.setPreferredSize(new Dimension(200, 200)); add(scrolldef, GBC.std().insets(0, 5, 0, 0).fill(GridBagConstraints.BOTH).weight(1.0, 0.6).insets(5, 0, 0, 0)); // Add default item map defaultMap = new JMapViewer(); defaultMap.setTileSource(new OsmTileSource.Mapnik()); // for attribution defaultMap.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON1) { defaultMap.getAttribution().handleAttribution(e.getPoint(), true); } } }); defaultMap.setZoomContolsVisible(false); defaultMap.setMinimumSize(new Dimension(100, 200)); add(defaultMap, GBC.std().insets(5, 5, 0, 0).fill(GridBagConstraints.BOTH).weight(0.33, 0.6).insets(5, 0, 0, 0)); defaultTableListener = new DefListSelectionListener(); defaultTable.getSelectionModel().addListSelectionListener(defaultTableListener); defaultToolbar = new JToolBar(JToolBar.VERTICAL); defaultToolbar.setFloatable(false); defaultToolbar.setBorderPainted(false); defaultToolbar.setOpaque(false); defaultToolbar.add(new ReloadAction()); add(defaultToolbar, GBC.eol().anchor(GBC.SOUTH).insets(0, 0, 5, 0)); ActivateAction activate = new ActivateAction(); defaultTable.getSelectionModel().addListSelectionListener(activate); JButton btnActivate = new JButton(activate); middleToolbar = new JToolBar(JToolBar.HORIZONTAL); middleToolbar.setFloatable(false); middleToolbar.setBorderPainted(false); middleToolbar.setOpaque(false); middleToolbar.add(btnActivate); add(middleToolbar, GBC.eol().anchor(GBC.CENTER).insets(5, 15, 5, 0)); add(Box.createHorizontalGlue(), GBC.eol().fill(GridBagConstraints.HORIZONTAL)); add(new JLabel(tr("Selected entries:")), GBC.eol().insets(5, 0, 0, 0)); JScrollPane scroll = new JScrollPane(activeTable); add(scroll, GBC.std().fill(GridBagConstraints.BOTH).span(GridBagConstraints.RELATIVE).weight(1.0, 0.4).insets(5, 0, 0, 5)); scroll.setPreferredSize(new Dimension(200, 200)); activeToolbar = new JToolBar(JToolBar.VERTICAL); activeToolbar.setFloatable(false); activeToolbar.setBorderPainted(false); activeToolbar.setOpaque(false); activeToolbar.add(new NewEntryAction(ImageryInfo.ImageryType.WMS)); activeToolbar.add(new NewEntryAction(ImageryInfo.ImageryType.TMS)); activeToolbar.add(new NewEntryAction(ImageryInfo.ImageryType.WMTS)); //activeToolbar.add(edit); TODO activeToolbar.add(remove); add(activeToolbar, GBC.eol().anchor(GBC.NORTH).insets(0, 0, 5, 5)); } // Listener of default providers list selection private final class DefListSelectionListener implements ListSelectionListener { // The current drawn rectangles and polygons private final Map<Integer, MapRectangle> mapRectangles; private final Map<Integer, List<MapPolygon>> mapPolygons; private DefListSelectionListener() { this.mapRectangles = new HashMap<>(); this.mapPolygons = new HashMap<>(); } private void clearMap() { defaultMap.removeAllMapRectangles(); defaultMap.removeAllMapPolygons(); mapRectangles.clear(); mapPolygons.clear(); } @Override public void valueChanged(ListSelectionEvent e) { // First index can be set to -1 when the list is refreshed, so discard all map rectangles and polygons if (e.getFirstIndex() == -1) { clearMap(); } else if (!e.getValueIsAdjusting()) { // Only process complete (final) selection events for (int i = e.getFirstIndex(); i <= e.getLastIndex(); i++) { updateBoundsAndShapes(i); } // If needed, adjust map to show all map rectangles and polygons if (!mapRectangles.isEmpty() || !mapPolygons.isEmpty()) { defaultMap.setDisplayToFitMapElements(false, true, true); defaultMap.zoomOut(); } } } private void updateBoundsAndShapes(int i) { ImageryBounds bounds = defaultModel.getRow(i).getBounds(); if (bounds != null) { List<Shape> shapes = bounds.getShapes(); if (shapes != null && !shapes.isEmpty()) { if (defaultTable.getSelectionModel().isSelectedIndex(i)) { if (!mapPolygons.containsKey(i)) { List<MapPolygon> list = new ArrayList<>(); mapPolygons.put(i, list); // Add new map polygons for (Shape shape : shapes) { MapPolygon polygon = new MapPolygonImpl(shape.getPoints()); list.add(polygon); defaultMap.addMapPolygon(polygon); } } } else if (mapPolygons.containsKey(i)) { // Remove previously drawn map polygons for (MapPolygon polygon : mapPolygons.get(i)) { defaultMap.removeMapPolygon(polygon); } mapPolygons.remove(i); } // Only display bounds when no polygons (shapes) are defined for this provider } else { if (defaultTable.getSelectionModel().isSelectedIndex(i)) { if (!mapRectangles.containsKey(i)) { // Add new map rectangle Coordinate topLeft = new Coordinate(bounds.getMaxLat(), bounds.getMinLon()); Coordinate bottomRight = new Coordinate(bounds.getMinLat(), bounds.getMaxLon()); MapRectangle rectangle = new MapRectangleImpl(topLeft, bottomRight); mapRectangles.put(i, rectangle); defaultMap.addMapRectangle(rectangle); } } else if (mapRectangles.containsKey(i)) { // Remove previously drawn map rectangle defaultMap.removeMapRectangle(mapRectangles.get(i)); mapRectangles.remove(i); } } } } } private class NewEntryAction extends AbstractAction { private final ImageryInfo.ImageryType type; NewEntryAction(ImageryInfo.ImageryType type) { putValue(NAME, type.toString()); putValue(SHORT_DESCRIPTION, tr("Add a new {0} entry by entering the URL", type.toString())); String icon = /* ICON(dialogs/) */ "add"; switch (type) { case WMS: icon = /* ICON(dialogs/) */ "add_wms"; break; case TMS: icon = /* ICON(dialogs/) */ "add_tms"; break; case WMTS: icon = /* ICON(dialogs/) */ "add_wmts"; break; default: break; } putValue(SMALL_ICON, ImageProvider.get("dialogs", icon)); this.type = type; } @Override public void actionPerformed(ActionEvent evt) { final AddImageryPanel p; switch (type) { case WMS: p = new AddWMSLayerPanel(); break; case TMS: p = new AddTMSLayerPanel(); break; case WMTS: p = new AddWMTSLayerPanel(); break; default: throw new IllegalStateException("Type " + type + " not supported"); } final AddImageryDialog addDialog = new AddImageryDialog(gui, p); addDialog.showDialog(); if (addDialog.getValue() == 1) { try { activeModel.addRow(p.getImageryInfo()); } catch (IllegalArgumentException ex) { if (ex.getMessage() == null || ex.getMessage().isEmpty()) throw ex; else { JOptionPane.showMessageDialog(Main.parent, ex.getMessage(), tr("Error"), JOptionPane.ERROR_MESSAGE); } } } } } private class RemoveEntryAction extends AbstractAction implements ListSelectionListener { /** * Constructs a new {@code RemoveEntryAction}. */ RemoveEntryAction() { putValue(NAME, tr("Remove")); putValue(SHORT_DESCRIPTION, tr("Remove entry")); putValue(SMALL_ICON, ImageProvider.get("dialogs", "delete")); updateEnabledState(); } protected final void updateEnabledState() { setEnabled(activeTable.getSelectedRowCount() > 0); } @Override public void valueChanged(ListSelectionEvent e) { updateEnabledState(); } @Override public void actionPerformed(ActionEvent e) { Integer i; while ((i = activeTable.getSelectedRow()) != -1) { activeModel.removeRow(i); } } } private class ActivateAction extends AbstractAction implements ListSelectionListener { /** * Constructs a new {@code ActivateAction}. */ ActivateAction() { putValue(NAME, tr("Activate")); putValue(SHORT_DESCRIPTION, tr("copy selected defaults")); putValue(SMALL_ICON, ImageProvider.get("preferences", "activate-down")); } protected void updateEnabledState() { setEnabled(defaultTable.getSelectedRowCount() > 0); } @Override public void valueChanged(ListSelectionEvent e) { updateEnabledState(); } @Override public void actionPerformed(ActionEvent e) { int[] lines = defaultTable.getSelectedRows(); if (lines.length == 0) { JOptionPane.showMessageDialog( gui, tr("Please select at least one row to copy."), tr("Information"), JOptionPane.INFORMATION_MESSAGE); return; } Set<String> acceptedEulas = new HashSet<>(); outer: for (int line : lines) { ImageryInfo info = defaultModel.getRow(line); // Check if an entry with exactly the same values already exists for (int j = 0; j < activeModel.getRowCount(); j++) { if (info.equalsBaseValues(activeModel.getRow(j))) { // Select the already existing row so the user has // some feedback in case an entry exists activeTable.getSelectionModel().setSelectionInterval(j, j); activeTable.scrollRectToVisible(activeTable.getCellRect(j, 0, true)); continue outer; } } String eulaURL = info.getEulaAcceptanceRequired(); // If set and not already accepted, ask for EULA acceptance if (eulaURL != null && !acceptedEulas.contains(eulaURL)) { if (confirmEulaAcceptance(gui, eulaURL)) { acceptedEulas.add(eulaURL); } else { continue outer; } } activeModel.addRow(new ImageryInfo(info)); int lastLine = activeModel.getRowCount() - 1; activeTable.getSelectionModel().setSelectionInterval(lastLine, lastLine); activeTable.scrollRectToVisible(activeTable.getCellRect(lastLine, 0, true)); } } } private class ReloadAction extends AbstractAction { /** * Constructs a new {@code ReloadAction}. */ ReloadAction() { putValue(SHORT_DESCRIPTION, tr("Update default entries")); putValue(SMALL_ICON, ImageProvider.get("dialogs", "refresh")); } @Override public void actionPerformed(ActionEvent evt) { layerInfo.loadDefaults(true, false, false); defaultModel.fireTableDataChanged(); defaultTable.getSelectionModel().clearSelection(); defaultTableListener.clearMap(); /* loading new file may change active layers */ activeModel.fireTableDataChanged(); } } /** * The table model for imagery layer list */ public class ImageryLayerTableModel extends DefaultTableModel { /** * Constructs a new {@code ImageryLayerTableModel}. */ public ImageryLayerTableModel() { setColumnIdentifiers(new String[] {tr("Menu Name"), tr("Imagery URL")}); } /** * Returns the imagery info at the given row number. * @param row The row number * @return The imagery info at the given row number */ public ImageryInfo getRow(int row) { return layerInfo.getLayers().get(row); } /** * Adds a new imagery info as the last row. * @param i The imagery info to add */ public void addRow(ImageryInfo i) { layerInfo.add(i); int p = getRowCount() - 1; fireTableRowsInserted(p, p); } @Override public void removeRow(int i) { layerInfo.remove(getRow(i)); fireTableRowsDeleted(i, i); } @Override public int getRowCount() { return layerInfo.getLayers().size(); } @Override public Object getValueAt(int row, int column) { ImageryInfo info = layerInfo.getLayers().get(row); switch (column) { case 0: return info.getName(); case 1: return info.getExtendedUrl(); default: throw new ArrayIndexOutOfBoundsException(); } } @Override public void setValueAt(Object o, int row, int column) { if (layerInfo.getLayers().size() <= row) return; ImageryInfo info = layerInfo.getLayers().get(row); switch (column) { case 0: info.setName((String) o); info.clearId(); break; case 1: info.setExtendedUrl((String) o); info.clearId(); break; default: throw new ArrayIndexOutOfBoundsException(); } } } /** * The table model for the default imagery layer list */ public class ImageryDefaultLayerTableModel extends DefaultTableModel { /** * Constructs a new {@code ImageryDefaultLayerTableModel}. */ public ImageryDefaultLayerTableModel() { setColumnIdentifiers(new String[]{"", tr("Menu Name (Default)"), tr("Imagery URL (Default)")}); } /** * Returns the imagery info at the given row number. * @param row The row number * @return The imagery info at the given row number */ public ImageryInfo getRow(int row) { return layerInfo.getAllDefaultLayers().get(row); } @Override public int getRowCount() { return layerInfo.getAllDefaultLayers().size(); } @Override public Object getValueAt(int row, int column) { ImageryInfo info = layerInfo.getAllDefaultLayers().get(row); switch (column) { case 0: return info.getCountryCode(); case 1: return info; case 2: return info.getExtendedUrl(); } return null; } @Override public boolean isCellEditable(int row, int column) { return false; } } private static boolean confirmEulaAcceptance(PreferenceTabbedPane gui, String eulaUrl) { URL url = null; try { url = new URL(eulaUrl.replaceAll("\\{lang\\}", LanguageInfo.getWikiLanguagePrefix())); JosmEditorPane htmlPane = null; try { htmlPane = new JosmEditorPane(url); } catch (IOException e1) { Main.trace(e1); // give a second chance with a default Locale 'en' try { url = new URL(eulaUrl.replaceAll("\\{lang\\}", "")); htmlPane = new JosmEditorPane(url); } catch (IOException e2) { Main.debug(e2); JOptionPane.showMessageDialog(gui, tr("EULA license URL not available: {0}", eulaUrl)); return false; } } Box box = Box.createVerticalBox(); htmlPane.setEditable(false); JScrollPane scrollPane = new JScrollPane(htmlPane); scrollPane.setPreferredSize(new Dimension(400, 400)); box.add(scrollPane); int option = JOptionPane.showConfirmDialog(Main.parent, box, tr("Please abort if you are not sure"), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); if (option == JOptionPane.YES_OPTION) return true; } catch (MalformedURLException e2) { JOptionPane.showMessageDialog(gui, tr("Malformed URL for the EULA licence: {0}", eulaUrl)); } return false; } } static class OffsetBookmarksPanel extends JPanel { private final OffsetsBookmarksModel model = new OffsetsBookmarksModel(); /** * Constructs a new {@code OffsetBookmarksPanel}. * @param gui the preferences tab pane */ OffsetBookmarksPanel(final PreferenceTabbedPane gui) { super(new GridBagLayout()); final JTable list = new JTable(model) { @Override public String getToolTipText(MouseEvent e) { java.awt.Point p = e.getPoint(); return model.getValueAt(rowAtPoint(p), columnAtPoint(p)).toString(); } }; JScrollPane scroll = new JScrollPane(list); add(scroll, GBC.eol().fill(GridBagConstraints.BOTH)); scroll.setPreferredSize(new Dimension(200, 200)); TableColumnModel mod = list.getColumnModel(); mod.getColumn(0).setPreferredWidth(150); mod.getColumn(1).setPreferredWidth(200); mod.getColumn(2).setPreferredWidth(300); mod.getColumn(3).setPreferredWidth(150); mod.getColumn(4).setPreferredWidth(150); JPanel buttonPanel = new JPanel(new FlowLayout()); JButton add = new JButton(tr("Add")); buttonPanel.add(add, GBC.std().insets(0, 5, 0, 0)); add.addActionListener(e -> model.addRow(new OffsetBookmark(Main.getProjection().toCode(), "", "", 0, 0))); JButton delete = new JButton(tr("Delete")); buttonPanel.add(delete, GBC.std().insets(0, 5, 0, 0)); delete.addActionListener(e -> { if (list.getSelectedRow() == -1) { JOptionPane.showMessageDialog(gui, tr("Please select the row to delete.")); } else { Integer i; while ((i = list.getSelectedRow()) != -1) { model.removeRow(i); } } }); add(buttonPanel, GBC.eol()); } /** * The table model for imagery offsets list */ private static class OffsetsBookmarksModel extends DefaultTableModel { /** * Constructs a new {@code OffsetsBookmarksModel}. */ OffsetsBookmarksModel() { setColumnIdentifiers(new String[] {tr("Projection"), tr("Layer"), tr("Name"), tr("Easting"), tr("Northing")}); } private static OffsetBookmark getRow(int row) { return OffsetBookmark.getBookmarkByIndex(row); } private void addRow(OffsetBookmark i) { OffsetBookmark.addBookmark(i); int p = getRowCount() - 1; fireTableRowsInserted(p, p); } @Override public void removeRow(int i) { OffsetBookmark.removeBookmark(getRow(i)); fireTableRowsDeleted(i, i); } @Override public int getRowCount() { return OffsetBookmark.getBookmarksSize(); } @Override public Object getValueAt(int row, int column) { OffsetBookmark info = OffsetBookmark.getBookmarkByIndex(row); switch (column) { case 0: if (info.projectionCode == null) return ""; return info.projectionCode; case 1: return info.layerName; case 2: return info.name; case 3: return info.dx; case 4: return info.dy; default: throw new ArrayIndexOutOfBoundsException(); } } @Override public void setValueAt(Object o, int row, int column) { OffsetBookmark info = OffsetBookmark.getBookmarkByIndex(row); switch (column) { case 1: info.layerName = o.toString(); break; case 2: info.name = o.toString(); break; case 3: info.dx = Double.parseDouble((String) o); break; case 4: info.dy = Double.parseDouble((String) o); break; default: throw new ArrayIndexOutOfBoundsException(); } } @Override public boolean isCellEditable(int row, int column) { return column >= 1; } } } /** * Initializes imagery preferences. */ public static void initialize() { ImageryLayerInfo.instance.load(false); OffsetBookmark.loadBookmarks(); Main.main.menu.imageryMenu.refreshImageryMenu(); Main.main.menu.imageryMenu.refreshOffsetMenu(); } }