001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.preferences.imagery; 003 004import static org.openstreetmap.josm.tools.I18n.marktr; 005import static org.openstreetmap.josm.tools.I18n.tr; 006 007import java.awt.Color; 008import java.awt.Component; 009import java.awt.Dimension; 010import java.awt.FlowLayout; 011import java.awt.Font; 012import java.awt.GridBagConstraints; 013import java.awt.GridBagLayout; 014import java.awt.event.ActionEvent; 015import java.awt.event.ActionListener; 016import java.awt.event.MouseEvent; 017import java.io.IOException; 018import java.net.MalformedURLException; 019import java.net.URL; 020import java.util.ArrayList; 021import java.util.HashMap; 022import java.util.HashSet; 023import java.util.List; 024import java.util.Map; 025import java.util.Set; 026 027import javax.swing.AbstractAction; 028import javax.swing.BorderFactory; 029import javax.swing.Box; 030import javax.swing.JButton; 031import javax.swing.JLabel; 032import javax.swing.JOptionPane; 033import javax.swing.JPanel; 034import javax.swing.JScrollPane; 035import javax.swing.JSeparator; 036import javax.swing.JTabbedPane; 037import javax.swing.JTable; 038import javax.swing.JToolBar; 039import javax.swing.event.ListSelectionEvent; 040import javax.swing.event.ListSelectionListener; 041import javax.swing.event.TableModelEvent; 042import javax.swing.event.TableModelListener; 043import javax.swing.table.DefaultTableCellRenderer; 044import javax.swing.table.DefaultTableModel; 045import javax.swing.table.TableColumnModel; 046 047import org.openstreetmap.gui.jmapviewer.Coordinate; 048import org.openstreetmap.gui.jmapviewer.JMapViewer; 049import org.openstreetmap.gui.jmapviewer.MapPolygonImpl; 050import org.openstreetmap.gui.jmapviewer.MapRectangleImpl; 051import org.openstreetmap.gui.jmapviewer.interfaces.MapPolygon; 052import org.openstreetmap.gui.jmapviewer.interfaces.MapRectangle; 053import org.openstreetmap.josm.Main; 054import org.openstreetmap.josm.data.imagery.ImageryInfo; 055import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryBounds; 056import org.openstreetmap.josm.data.imagery.ImageryLayerInfo; 057import org.openstreetmap.josm.data.imagery.OffsetBookmark; 058import org.openstreetmap.josm.data.imagery.Shape; 059import org.openstreetmap.josm.gui.download.DownloadDialog; 060import org.openstreetmap.josm.gui.preferences.DefaultTabPreferenceSetting; 061import org.openstreetmap.josm.gui.preferences.PreferenceSetting; 062import org.openstreetmap.josm.gui.preferences.PreferenceSettingFactory; 063import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane; 064import org.openstreetmap.josm.gui.widgets.JosmEditorPane; 065import org.openstreetmap.josm.tools.GBC; 066import org.openstreetmap.josm.tools.ImageProvider; 067import org.openstreetmap.josm.tools.LanguageInfo; 068 069/** 070 * Imagery preferences, including imagery providers, settings and offsets. 071 */ 072public final class ImageryPreference extends DefaultTabPreferenceSetting { 073 074 /** 075 * Factory used to create a new {@code ImageryPreference}. 076 */ 077 public static class Factory implements PreferenceSettingFactory { 078 @Override 079 public PreferenceSetting createPreferenceSetting() { 080 return new ImageryPreference(); 081 } 082 } 083 084 private ImageryPreference() { 085 super("imagery", tr("Imagery Preferences"), tr("Modify list of imagery layers displayed in the Imagery menu"), false, new JTabbedPane()); 086 } 087 088 private ImageryProvidersPanel imageryProviders; 089 private ImageryLayerInfo layerInfo; 090 091 private CommonSettingsPanel commonSettings; 092 private WMSSettingsPanel wmsSettings; 093 private TMSSettingsPanel tmsSettings; 094 095 private void addSettingsSection(final JPanel p, String name, JPanel section) { 096 addSettingsSection(p, name, section, GBC.eol()); 097 } 098 099 private void addSettingsSection(final JPanel p, String name, JPanel section, GBC gbc) { 100 final JLabel lbl = new JLabel(name); 101 lbl.setFont(lbl.getFont().deriveFont(Font.BOLD)); 102 p.add(lbl,GBC.std()); 103 p.add(new JSeparator(), GBC.eol().fill(GBC.HORIZONTAL).insets(5, 0, 0, 0)); 104 p.add(section, gbc.insets(20,5,0,10)); 105 } 106 107 private Component buildSettingsPanel() { 108 final JPanel p = new JPanel(new GridBagLayout()); 109 p.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 110 111 addSettingsSection(p, tr("Common Settings"), commonSettings = new CommonSettingsPanel()); 112 addSettingsSection(p, tr("WMS Settings"), wmsSettings = new WMSSettingsPanel(), 113 GBC.eol().fill(GBC.HORIZONTAL)); 114 addSettingsSection(p, tr("TMS Settings"), tmsSettings = new TMSSettingsPanel(), 115 GBC.eol().fill(GBC.HORIZONTAL)); 116 117 p.add(new JPanel(),GBC.eol().fill(GBC.BOTH)); 118 return new JScrollPane(p); 119 } 120 121 @Override 122 public void addGui(final PreferenceTabbedPane gui) { 123 JPanel p = gui.createPreferenceTab(this); 124 JTabbedPane pane = getTabPane(); 125 layerInfo = new ImageryLayerInfo(ImageryLayerInfo.instance); 126 imageryProviders = new ImageryProvidersPanel(gui, layerInfo); 127 pane.addTab(tr("Imagery providers"), imageryProviders); 128 pane.addTab(tr("Settings"), buildSettingsPanel()); 129 pane.addTab(tr("Offset bookmarks"), new OffsetBookmarksPanel(gui)); 130 loadSettings(); 131 p.add(pane,GBC.std().fill(GBC.BOTH)); 132 } 133 134 /** 135 * Returns the imagery providers panel. 136 * @return The imagery providers panel. 137 */ 138 public ImageryProvidersPanel getProvidersPanel() { 139 return imageryProviders; 140 } 141 142 private void loadSettings() { 143 commonSettings.loadSettings(); 144 wmsSettings.loadSettings(); 145 tmsSettings.loadSettings(); 146 } 147 148 @Override 149 public boolean ok() { 150 layerInfo.save(); 151 ImageryLayerInfo.instance.clear(); 152 ImageryLayerInfo.instance.load(); 153 Main.main.menu.imageryMenu.refreshOffsetMenu(); 154 OffsetBookmark.saveBookmarks(); 155 156 DownloadDialog.getInstance().refreshTileSources(); 157 158 boolean commonRestartRequired = commonSettings.saveSettings(); 159 boolean wmsRestartRequired = wmsSettings.saveSettings(); 160 boolean tmsRestartRequired = tmsSettings.saveSettings(); 161 162 return commonRestartRequired || wmsRestartRequired || tmsRestartRequired; 163 } 164 165 /** 166 * Updates a server URL in the preferences dialog. Used by plugins. 167 * 168 * @param server 169 * The server name 170 * @param url 171 * The server URL 172 */ 173 public void setServerUrl(String server, String url) { 174 for (int i = 0; i < imageryProviders.activeModel.getRowCount(); i++) { 175 if (server.equals(imageryProviders.activeModel.getValueAt(i, 0).toString())) { 176 imageryProviders.activeModel.setValueAt(url, i, 1); 177 return; 178 } 179 } 180 imageryProviders.activeModel.addRow(new String[] { server, url }); 181 } 182 183 /** 184 * Gets a server URL in the preferences dialog. Used by plugins. 185 * 186 * @param server 187 * The server name 188 * @return The server URL 189 */ 190 public String getServerUrl(String server) { 191 for (int i = 0; i < imageryProviders.activeModel.getRowCount(); i++) { 192 if (server.equals(imageryProviders.activeModel.getValueAt(i, 0).toString())) 193 return imageryProviders.activeModel.getValueAt(i, 1).toString(); 194 } 195 return null; 196 } 197 198 /** 199 * A panel displaying imagery providers. 200 */ 201 public static class ImageryProvidersPanel extends JPanel { 202 // Public JTables and JMapViewer 203 /** The table of active providers **/ 204 public final JTable activeTable; 205 /** The table of default providers **/ 206 public final JTable defaultTable; 207 /** The map displaying imagery bounds of selected default providers **/ 208 public final JMapViewer defaultMap; 209 210 // Public models 211 /** The model of active providers **/ 212 public final ImageryLayerTableModel activeModel; 213 /** The model of default providers **/ 214 public final ImageryDefaultLayerTableModel defaultModel; 215 216 // Public JToolbars 217 /** The toolbar on the right of active providers **/ 218 public final JToolBar activeToolbar; 219 /** The toolbar on the middle of the panel **/ 220 public final JToolBar middleToolbar; 221 /** The toolbar on the right of default providers **/ 222 public final JToolBar defaultToolbar; 223 224 // Private members 225 private final PreferenceTabbedPane gui; 226 private final ImageryLayerInfo layerInfo; 227 228 private static class ImageryTableCellRenderer extends DefaultTableCellRenderer { 229 230 private List<ImageryInfo> layers; 231 232 public ImageryTableCellRenderer(List<ImageryInfo> layers) { 233 this.layers = layers; 234 } 235 236 @Override 237 public Component getTableCellRendererComponent(JTable table, Object value, boolean 238 isSelected, boolean hasFocus, int row, int column) { 239 JLabel label = (JLabel) super.getTableCellRendererComponent( 240 table, value, isSelected, hasFocus, row, column); 241 label.setBackground(Main.pref.getUIColor("Table.background")); 242 if (isSelected) { 243 label.setForeground(Main.pref.getUIColor("Table.foreground")); 244 } 245 if (value != null) { // Fix #8159 246 String t = value.toString(); 247 for (ImageryInfo l : layers) { 248 if (l.getExtendedUrl().equals(t)) { 249 label.setBackground(Main.pref.getColor( 250 marktr("Imagery Background: Default"), 251 new Color(200,255,200))); 252 break; 253 } 254 } 255 } 256 return label; 257 } 258 } 259 260 /** 261 * Constructs a new {@code ImageryProvidersPanel}. 262 * @param gui The parent preference tab pane 263 * @param layerInfoArg The list of imagery entries to display 264 */ 265 public ImageryProvidersPanel(final PreferenceTabbedPane gui, ImageryLayerInfo layerInfoArg) { 266 super(new GridBagLayout()); 267 this.gui = gui; 268 this.layerInfo = layerInfoArg; 269 this.activeModel = new ImageryLayerTableModel(); 270 271 activeTable = new JTable(activeModel) { 272 @Override 273 public String getToolTipText(MouseEvent e) { 274 java.awt.Point p = e.getPoint(); 275 return activeModel.getValueAt(rowAtPoint(p), columnAtPoint(p)).toString(); 276 } 277 }; 278 activeTable.putClientProperty("terminateEditOnFocusLost", true); 279 280 defaultModel = new ImageryDefaultLayerTableModel(); 281 defaultTable = new JTable(defaultModel) { 282 @Override 283 public String getToolTipText(MouseEvent e) { 284 java.awt.Point p = e.getPoint(); 285 return (String) defaultModel.getValueAt(rowAtPoint(p), columnAtPoint(p)); 286 } 287 }; 288 289 defaultModel.addTableModelListener( 290 new TableModelListener() { 291 @Override 292 public void tableChanged(TableModelEvent e) { 293 activeTable.repaint(); 294 } 295 } 296 ); 297 298 activeModel.addTableModelListener( 299 new TableModelListener() { 300 @Override 301 public void tableChanged(TableModelEvent e) { 302 defaultTable.repaint(); 303 } 304 } 305 ); 306 307 TableColumnModel mod = defaultTable.getColumnModel(); 308 mod.getColumn(2).setPreferredWidth(800); 309 mod.getColumn(2).setCellRenderer(new ImageryTableCellRenderer(layerInfo.getLayers())); 310 mod.getColumn(1).setPreferredWidth(400); 311 mod.getColumn(0).setPreferredWidth(50); 312 313 mod = activeTable.getColumnModel(); 314 mod.getColumn(1).setPreferredWidth(800); 315 mod.getColumn(1).setCellRenderer(new ImageryTableCellRenderer(layerInfo.getDefaultLayers())); 316 mod.getColumn(0).setPreferredWidth(200); 317 318 RemoveEntryAction remove = new RemoveEntryAction(); 319 activeTable.getSelectionModel().addListSelectionListener(remove); 320 321 add(new JLabel(tr("Available default entries:")), GBC.eol().insets(5, 5, 0, 0)); 322 // Add default item list 323 JScrollPane scrolldef = new JScrollPane(defaultTable); 324 scrolldef.setPreferredSize(new Dimension(200, 200)); 325 add(scrolldef, GBC.std().insets(0, 5, 0, 0).fill(GridBagConstraints.BOTH).weight(1.0, 0.6).insets(5, 0, 0, 0)); 326 327 // Add default item map 328 defaultMap = new JMapViewer(); 329 defaultMap.setZoomContolsVisible(false); 330 defaultMap.setMinimumSize(new Dimension(100, 200)); 331 add(defaultMap, GBC.std().insets(5, 5, 0, 0).fill(GridBagConstraints.BOTH).weight(0.33, 0.6).insets(5, 0, 0, 0)); 332 333 defaultTable.getSelectionModel().addListSelectionListener(new DefListSelectionListener()); 334 335 defaultToolbar = new JToolBar(JToolBar.VERTICAL); 336 defaultToolbar.setFloatable(false); 337 defaultToolbar.setBorderPainted(false); 338 defaultToolbar.setOpaque(false); 339 defaultToolbar.add(new ReloadAction()); 340 add(defaultToolbar, GBC.eol().anchor(GBC.SOUTH).insets(0, 0, 5, 0)); 341 342 ActivateAction activate = new ActivateAction(); 343 defaultTable.getSelectionModel().addListSelectionListener(activate); 344 JButton btnActivate = new JButton(activate); 345 346 middleToolbar = new JToolBar(JToolBar.HORIZONTAL); 347 middleToolbar.setFloatable(false); 348 middleToolbar.setBorderPainted(false); 349 middleToolbar.setOpaque(false); 350 middleToolbar.add(btnActivate); 351 add(middleToolbar, GBC.eol().anchor(GBC.CENTER).insets(5, 15, 5, 0)); 352 353 add(Box.createHorizontalGlue(), GBC.eol().fill(GridBagConstraints.HORIZONTAL)); 354 355 add(new JLabel(tr("Selected entries:")), GBC.eol().insets(5, 0, 0, 0)); 356 JScrollPane scroll = new JScrollPane(activeTable); 357 add(scroll, GBC.std().fill(GridBagConstraints.BOTH).span(GridBagConstraints.RELATIVE).weight(1.0, 0.4).insets(5, 0, 0, 5)); 358 scroll.setPreferredSize(new Dimension(200, 200)); 359 360 activeToolbar = new JToolBar(JToolBar.VERTICAL); 361 activeToolbar.setFloatable(false); 362 activeToolbar.setBorderPainted(false); 363 activeToolbar.setOpaque(false); 364 activeToolbar.add(new NewEntryAction(ImageryInfo.ImageryType.WMS)); 365 activeToolbar.add(new NewEntryAction(ImageryInfo.ImageryType.TMS)); 366 //activeToolbar.add(edit); TODO 367 activeToolbar.add(remove); 368 add(activeToolbar, GBC.eol().anchor(GBC.NORTH).insets(0, 0, 5, 5)); 369 370 } 371 372 // Listener of default providers list selection 373 private final class DefListSelectionListener implements ListSelectionListener { 374 // The current drawn rectangles and polygons 375 private final Map<Integer, MapRectangle> mapRectangles; 376 private final Map<Integer, List<MapPolygon>> mapPolygons; 377 378 private DefListSelectionListener() { 379 this.mapRectangles = new HashMap<>(); 380 this.mapPolygons = new HashMap<>(); 381 } 382 383 @Override 384 public void valueChanged(ListSelectionEvent e) { 385 // First index is set to -1 when the list is refreshed, so discard all map rectangles and polygons 386 if (e.getFirstIndex() == -1) { 387 defaultMap.removeAllMapRectangles(); 388 defaultMap.removeAllMapPolygons(); 389 mapRectangles.clear(); 390 mapPolygons.clear(); 391 // Only process complete (final) selection events 392 } else if (!e.getValueIsAdjusting()) { 393 for (int i = e.getFirstIndex(); i<=e.getLastIndex(); i++) { 394 updateBoundsAndShapes(i); 395 } 396 // If needed, adjust map to show all map rectangles and polygons 397 if (!mapRectangles.isEmpty() || !mapPolygons.isEmpty()) { 398 defaultMap.setDisplayToFitMapElements(false, true, true); 399 defaultMap.zoomOut(); 400 } 401 } 402 } 403 404 private void updateBoundsAndShapes(int i) { 405 ImageryBounds bounds = defaultModel.getRow(i).getBounds(); 406 if (bounds != null) { 407 List<Shape> shapes = bounds.getShapes(); 408 if (shapes != null && !shapes.isEmpty()) { 409 if (defaultTable.getSelectionModel().isSelectedIndex(i)) { 410 if (!mapPolygons.containsKey(i)) { 411 List<MapPolygon> list = new ArrayList<>(); 412 mapPolygons.put(i, list); 413 // Add new map polygons 414 for (Shape shape : shapes) { 415 MapPolygon polygon = new MapPolygonImpl(shape.getPoints()); 416 list.add(polygon); 417 defaultMap.addMapPolygon(polygon); 418 } 419 } 420 } else if (mapPolygons.containsKey(i)) { 421 // Remove previously drawn map polygons 422 for (MapPolygon polygon : mapPolygons.get(i)) { 423 defaultMap.removeMapPolygon(polygon); 424 } 425 mapPolygons.remove(i); 426 } 427 // Only display bounds when no polygons (shapes) are defined for this provider 428 } else { 429 if (defaultTable.getSelectionModel().isSelectedIndex(i)) { 430 if (!mapRectangles.containsKey(i)) { 431 // Add new map rectangle 432 Coordinate topLeft = new Coordinate(bounds.getMaxLat(), bounds.getMinLon()); 433 Coordinate bottomRight = new Coordinate(bounds.getMinLat(), bounds.getMaxLon()); 434 MapRectangle rectangle = new MapRectangleImpl(topLeft, bottomRight); 435 mapRectangles.put(i, rectangle); 436 defaultMap.addMapRectangle(rectangle); 437 } 438 } else if (mapRectangles.containsKey(i)) { 439 // Remove previously drawn map rectangle 440 defaultMap.removeMapRectangle(mapRectangles.get(i)); 441 mapRectangles.remove(i); 442 } 443 } 444 } 445 } 446 } 447 448 private class NewEntryAction extends AbstractAction { 449 450 private final ImageryInfo.ImageryType type; 451 452 public NewEntryAction(ImageryInfo.ImageryType type) { 453 putValue(NAME, type.toString()); 454 putValue(SHORT_DESCRIPTION, tr("Add a new {0} entry by entering the URL", type.toString())); 455 putValue(SMALL_ICON, ImageProvider.get("dialogs", 456 "add" + (ImageryInfo.ImageryType.WMS.equals(type) ? "_wms" : ImageryInfo.ImageryType.TMS.equals(type) ? "_tms" : ""))); 457 this.type = type; 458 } 459 460 @Override 461 public void actionPerformed(ActionEvent evt) { 462 final AddImageryPanel p; 463 if (ImageryInfo.ImageryType.WMS.equals(type)) { 464 p = new AddWMSLayerPanel(); 465 } else if (ImageryInfo.ImageryType.TMS.equals(type)) { 466 p = new AddTMSLayerPanel(); 467 } else { 468 throw new IllegalStateException("Type " + type + " not supported"); 469 } 470 471 final AddImageryDialog addDialog = new AddImageryDialog(gui, p); 472 addDialog.showDialog(); 473 474 if (addDialog.getValue() == 1) { 475 try { 476 activeModel.addRow(p.getImageryInfo()); 477 } catch (IllegalArgumentException ex) { 478 if (ex.getMessage() == null || ex.getMessage().isEmpty()) 479 throw ex; 480 else { 481 JOptionPane.showMessageDialog(Main.parent, 482 ex.getMessage(), tr("Error"), 483 JOptionPane.ERROR_MESSAGE); 484 } 485 } 486 } 487 } 488 } 489 490 private class RemoveEntryAction extends AbstractAction implements ListSelectionListener { 491 492 public RemoveEntryAction() { 493 putValue(NAME, tr("Remove")); 494 putValue(SHORT_DESCRIPTION, tr("Remove entry")); 495 putValue(SMALL_ICON, ImageProvider.get("dialogs", "delete")); 496 updateEnabledState(); 497 } 498 499 protected final void updateEnabledState() { 500 setEnabled(activeTable.getSelectedRowCount() > 0); 501 } 502 503 @Override 504 public void valueChanged(ListSelectionEvent e) { 505 updateEnabledState(); 506 } 507 508 @Override 509 public void actionPerformed(ActionEvent e) { 510 Integer i; 511 while ((i = activeTable.getSelectedRow()) != -1) { 512 activeModel.removeRow(i); 513 } 514 } 515 } 516 517 private class ActivateAction extends AbstractAction implements ListSelectionListener { 518 public ActivateAction() { 519 putValue(NAME, tr("Activate")); 520 putValue(SHORT_DESCRIPTION, tr("copy selected defaults")); 521 putValue(SMALL_ICON, ImageProvider.get("preferences", "activate-down")); 522 } 523 524 protected void updateEnabledState() { 525 setEnabled(defaultTable.getSelectedRowCount() > 0); 526 } 527 528 @Override 529 public void valueChanged(ListSelectionEvent e) { 530 updateEnabledState(); 531 } 532 533 @Override 534 public void actionPerformed(ActionEvent e) { 535 int[] lines = defaultTable.getSelectedRows(); 536 if (lines.length == 0) { 537 JOptionPane.showMessageDialog( 538 gui, 539 tr("Please select at least one row to copy."), 540 tr("Information"), 541 JOptionPane.INFORMATION_MESSAGE); 542 return; 543 } 544 545 Set<String> acceptedEulas = new HashSet<>(); 546 547 outer: 548 for (int line : lines) { 549 ImageryInfo info = defaultModel.getRow(line); 550 551 // Check if an entry with exactly the same values already exists 552 for (int j = 0; j < activeModel.getRowCount(); j++) { 553 if (info.equalsBaseValues(activeModel.getRow(j))) { 554 // Select the already existing row so the user has 555 // some feedback in case an entry exists 556 activeTable.getSelectionModel().setSelectionInterval(j, j); 557 activeTable.scrollRectToVisible(activeTable.getCellRect(j, 0, true)); 558 continue outer; 559 } 560 } 561 562 String eulaURL = info.getEulaAcceptanceRequired(); 563 // If set and not already accepted, ask for EULA acceptance 564 if (eulaURL != null && !acceptedEulas.contains(eulaURL)) { 565 if (confirmEulaAcceptance(gui, eulaURL)) { 566 acceptedEulas.add(eulaURL); 567 } else { 568 continue outer; 569 } 570 } 571 572 activeModel.addRow(new ImageryInfo(info)); 573 int lastLine = activeModel.getRowCount() - 1; 574 activeTable.getSelectionModel().setSelectionInterval(lastLine, lastLine); 575 activeTable.scrollRectToVisible(activeTable.getCellRect(lastLine, 0, true)); 576 } 577 } 578 } 579 580 private class ReloadAction extends AbstractAction { 581 public ReloadAction() { 582 putValue(SHORT_DESCRIPTION, tr("reload defaults")); 583 putValue(SMALL_ICON, ImageProvider.get("dialogs", "refresh")); 584 } 585 586 @Override 587 public void actionPerformed(ActionEvent evt) { 588 layerInfo.loadDefaults(true); 589 defaultModel.fireTableDataChanged(); 590 } 591 } 592 593 /** 594 * The table model for imagery layer list 595 */ 596 public class ImageryLayerTableModel extends DefaultTableModel { 597 /** 598 * Constructs a new {@code ImageryLayerTableModel}. 599 */ 600 public ImageryLayerTableModel() { 601 setColumnIdentifiers(new String[] { tr("Menu Name"), tr("Imagery URL")}); 602 } 603 604 /** 605 * Returns the imagery info at the given row number. 606 * @param row The row number 607 * @return The imagery info at the given row number 608 */ 609 public ImageryInfo getRow(int row) { 610 return layerInfo.getLayers().get(row); 611 } 612 613 /** 614 * Adds a new imagery info as the last row. 615 * @param i The imagery info to add 616 */ 617 public void addRow(ImageryInfo i) { 618 layerInfo.add(i); 619 int p = getRowCount() - 1; 620 fireTableRowsInserted(p, p); 621 } 622 623 @Override 624 public void removeRow(int i) { 625 layerInfo.remove(getRow(i)); 626 fireTableRowsDeleted(i, i); 627 } 628 629 @Override 630 public int getRowCount() { 631 return layerInfo.getLayers().size(); 632 } 633 634 @Override 635 public Object getValueAt(int row, int column) { 636 ImageryInfo info = layerInfo.getLayers().get(row); 637 switch (column) { 638 case 0: 639 return info.getName(); 640 case 1: 641 return info.getExtendedUrl(); 642 default: 643 throw new ArrayIndexOutOfBoundsException(); 644 } 645 } 646 647 @Override 648 public void setValueAt(Object o, int row, int column) { 649 if (layerInfo.getLayers().size() <= row) return; 650 ImageryInfo info = layerInfo.getLayers().get(row); 651 switch (column) { 652 case 0: 653 info.setName((String) o); 654 info.clearId(); 655 break; 656 case 1: 657 info.setExtendedUrl((String)o); 658 info.clearId(); 659 break; 660 default: 661 throw new ArrayIndexOutOfBoundsException(); 662 } 663 } 664 665 @Override 666 public boolean isCellEditable(int row, int column) { 667 return true; 668 } 669 } 670 671 /** 672 * The table model for the default imagery layer list 673 */ 674 public class ImageryDefaultLayerTableModel extends DefaultTableModel { 675 /** 676 * Constructs a new {@code ImageryDefaultLayerTableModel}. 677 */ 678 public ImageryDefaultLayerTableModel() { 679 setColumnIdentifiers(new String[]{"", tr("Menu Name (Default)"), tr("Imagery URL (Default)")}); 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.getDefaultLayers().get(row); 689 } 690 691 @Override 692 public int getRowCount() { 693 return layerInfo.getDefaultLayers().size(); 694 } 695 696 @Override 697 public Object getValueAt(int row, int column) { 698 ImageryInfo info = layerInfo.getDefaultLayers().get(row); 699 switch (column) { 700 case 0: 701 return info.getCountryCode(); 702 case 1: 703 return info.getName(); 704 case 2: 705 return info.getExtendedUrl(); 706 } 707 return null; 708 } 709 710 @Override 711 public boolean isCellEditable(int row, int column) { 712 return false; 713 } 714 } 715 716 private boolean confirmEulaAcceptance(PreferenceTabbedPane gui, String eulaUrl) { 717 URL url = null; 718 try { 719 url = new URL(eulaUrl.replaceAll("\\{lang\\}", LanguageInfo.getWikiLanguagePrefix())); 720 JosmEditorPane htmlPane = null; 721 try { 722 htmlPane = new JosmEditorPane(url); 723 } catch (IOException e1) { 724 // give a second chance with a default Locale 'en' 725 try { 726 url = new URL(eulaUrl.replaceAll("\\{lang\\}", "")); 727 htmlPane = new JosmEditorPane(url); 728 } catch (IOException e2) { 729 JOptionPane.showMessageDialog(gui ,tr("EULA license URL not available: {0}", eulaUrl)); 730 return false; 731 } 732 } 733 Box box = Box.createVerticalBox(); 734 htmlPane.setEditable(false); 735 JScrollPane scrollPane = new JScrollPane(htmlPane); 736 scrollPane.setPreferredSize(new Dimension(400, 400)); 737 box.add(scrollPane); 738 int option = JOptionPane.showConfirmDialog(Main.parent, box, tr("Please abort if you are not sure"), JOptionPane.YES_NO_OPTION, 739 JOptionPane.WARNING_MESSAGE); 740 if (option == JOptionPane.YES_OPTION) 741 return true; 742 } catch (MalformedURLException e2) { 743 JOptionPane.showMessageDialog(gui ,tr("Malformed URL for the EULA licence: {0}", eulaUrl)); 744 } 745 return false; 746 } 747 } 748 749 static class OffsetBookmarksPanel extends JPanel { 750 List<OffsetBookmark> bookmarks = OffsetBookmark.allBookmarks; 751 OffsetsBookmarksModel model = new OffsetsBookmarksModel(); 752 753 public OffsetBookmarksPanel(final PreferenceTabbedPane gui) { 754 super(new GridBagLayout()); 755 final JTable list = new JTable(model) { 756 @Override 757 public String getToolTipText(MouseEvent e) { 758 java.awt.Point p = e.getPoint(); 759 return model.getValueAt(rowAtPoint(p), columnAtPoint(p)).toString(); 760 } 761 }; 762 JScrollPane scroll = new JScrollPane(list); 763 add(scroll, GBC.eol().fill(GridBagConstraints.BOTH)); 764 scroll.setPreferredSize(new Dimension(200, 200)); 765 766 TableColumnModel mod = list.getColumnModel(); 767 mod.getColumn(0).setPreferredWidth(150); 768 mod.getColumn(1).setPreferredWidth(200); 769 mod.getColumn(2).setPreferredWidth(300); 770 mod.getColumn(3).setPreferredWidth(150); 771 mod.getColumn(4).setPreferredWidth(150); 772 773 JPanel buttonPanel = new JPanel(new FlowLayout()); 774 775 JButton add = new JButton(tr("Add")); 776 buttonPanel.add(add, GBC.std().insets(0, 5, 0, 0)); 777 add.addActionListener(new ActionListener() { 778 @Override 779 public void actionPerformed(ActionEvent e) { 780 OffsetBookmark b = new OffsetBookmark(Main.getProjection().toCode(),"","",0,0); 781 model.addRow(b); 782 } 783 }); 784 785 JButton delete = new JButton(tr("Delete")); 786 buttonPanel.add(delete, GBC.std().insets(0, 5, 0, 0)); 787 delete.addActionListener(new ActionListener() { 788 @Override 789 public void actionPerformed(ActionEvent e) { 790 if (list.getSelectedRow() == -1) { 791 JOptionPane.showMessageDialog(gui, tr("Please select the row to delete.")); 792 } else { 793 Integer i; 794 while ((i = list.getSelectedRow()) != -1) { 795 model.removeRow(i); 796 } 797 } 798 } 799 }); 800 801 add(buttonPanel,GBC.eol()); 802 } 803 804 /** 805 * The table model for imagery offsets list 806 */ 807 class OffsetsBookmarksModel extends DefaultTableModel { 808 public OffsetsBookmarksModel() { 809 setColumnIdentifiers(new String[] { tr("Projection"), tr("Layer"), tr("Name"), tr("Easting"), tr("Northing"),}); 810 } 811 812 public OffsetBookmark getRow(int row) { 813 return bookmarks.get(row); 814 } 815 816 public void addRow(OffsetBookmark i) { 817 bookmarks.add(i); 818 int p = getRowCount() - 1; 819 fireTableRowsInserted(p, p); 820 } 821 822 @Override 823 public void removeRow(int i) { 824 bookmarks.remove(getRow(i)); 825 fireTableRowsDeleted(i, i); 826 } 827 828 @Override 829 public int getRowCount() { 830 return bookmarks.size(); 831 } 832 833 @Override 834 public Object getValueAt(int row, int column) { 835 OffsetBookmark info = bookmarks.get(row); 836 switch (column) { 837 case 0: 838 if (info.projectionCode == null) return ""; 839 return info.projectionCode.toString(); 840 case 1: 841 return info.layerName; 842 case 2: 843 return info.name; 844 case 3: 845 return info.dx; 846 case 4: 847 return info.dy; 848 default: 849 throw new ArrayIndexOutOfBoundsException(); 850 } 851 } 852 853 @Override 854 public void setValueAt(Object o, int row, int column) { 855 OffsetBookmark info = bookmarks.get(row); 856 switch (column) { 857 case 1: 858 info.layerName = o.toString(); 859 break; 860 case 2: 861 info.name = o.toString(); 862 break; 863 case 3: 864 info.dx = Double.parseDouble((String) o); 865 break; 866 case 4: 867 info.dy = Double.parseDouble((String) o); 868 break; 869 default: 870 throw new ArrayIndexOutOfBoundsException(); 871 } 872 } 873 874 @Override 875 public boolean isCellEditable(int row, int column) { 876 return column >= 1; 877 } 878 } 879 } 880 881 /** 882 * Initializes imagery preferences. 883 */ 884 public static void initialize() { 885 ImageryLayerInfo.instance.load(); 886 OffsetBookmark.loadBookmarks(); 887 Main.main.menu.imageryMenu.refreshImageryMenu(); 888 Main.main.menu.imageryMenu.refreshOffsetMenu(); 889 } 890}