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(/* ICON(preferences/) */ "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 selection listener synchronizing map display with table of default providers **/ 208 private final DefListSelectionListener defaultTableListener; 209 /** The map displaying imagery bounds of selected default providers **/ 210 public final JMapViewer defaultMap; 211 212 // Public models 213 /** The model of active providers **/ 214 public final ImageryLayerTableModel activeModel; 215 /** The model of default providers **/ 216 public final ImageryDefaultLayerTableModel defaultModel; 217 218 // Public JToolbars 219 /** The toolbar on the right of active providers **/ 220 public final JToolBar activeToolbar; 221 /** The toolbar on the middle of the panel **/ 222 public final JToolBar middleToolbar; 223 /** The toolbar on the right of default providers **/ 224 public final JToolBar defaultToolbar; 225 226 // Private members 227 private final PreferenceTabbedPane gui; 228 private final ImageryLayerInfo layerInfo; 229 230 private static class ImageryTableCellRenderer extends DefaultTableCellRenderer { 231 232 private List<ImageryInfo> layers; 233 234 public ImageryTableCellRenderer(List<ImageryInfo> layers) { 235 this.layers = layers; 236 } 237 238 @Override 239 public Component getTableCellRendererComponent(JTable table, Object value, boolean 240 isSelected, boolean hasFocus, int row, int column) { 241 JLabel label = (JLabel) super.getTableCellRendererComponent( 242 table, value, isSelected, hasFocus, row, column); 243 label.setBackground(Main.pref.getUIColor("Table.background")); 244 if (isSelected) { 245 label.setForeground(Main.pref.getUIColor("Table.foreground")); 246 } 247 if (value != null) { // Fix #8159 248 String t = value.toString(); 249 for (ImageryInfo l : layers) { 250 if (l.getExtendedUrl().equals(t)) { 251 label.setBackground(Main.pref.getColor( 252 marktr("Imagery Background: Default"), 253 new Color(200,255,200))); 254 break; 255 } 256 } 257 } 258 return label; 259 } 260 } 261 262 /** 263 * Constructs a new {@code ImageryProvidersPanel}. 264 * @param gui The parent preference tab pane 265 * @param layerInfoArg The list of imagery entries to display 266 */ 267 public ImageryProvidersPanel(final PreferenceTabbedPane gui, ImageryLayerInfo layerInfoArg) { 268 super(new GridBagLayout()); 269 this.gui = gui; 270 this.layerInfo = layerInfoArg; 271 this.activeModel = new ImageryLayerTableModel(); 272 273 activeTable = new JTable(activeModel) { 274 @Override 275 public String getToolTipText(MouseEvent e) { 276 java.awt.Point p = e.getPoint(); 277 return activeModel.getValueAt(rowAtPoint(p), columnAtPoint(p)).toString(); 278 } 279 }; 280 activeTable.putClientProperty("terminateEditOnFocusLost", true); 281 282 defaultModel = new ImageryDefaultLayerTableModel(); 283 defaultTable = new JTable(defaultModel) { 284 @Override 285 public String getToolTipText(MouseEvent e) { 286 java.awt.Point p = e.getPoint(); 287 return (String) defaultModel.getValueAt(rowAtPoint(p), columnAtPoint(p)); 288 } 289 }; 290 291 defaultModel.addTableModelListener( 292 new TableModelListener() { 293 @Override 294 public void tableChanged(TableModelEvent e) { 295 activeTable.repaint(); 296 } 297 } 298 ); 299 300 activeModel.addTableModelListener( 301 new TableModelListener() { 302 @Override 303 public void tableChanged(TableModelEvent e) { 304 defaultTable.repaint(); 305 } 306 } 307 ); 308 309 TableColumnModel mod = defaultTable.getColumnModel(); 310 mod.getColumn(2).setPreferredWidth(800); 311 mod.getColumn(2).setCellRenderer(new ImageryTableCellRenderer(layerInfo.getLayers())); 312 mod.getColumn(1).setPreferredWidth(400); 313 mod.getColumn(0).setPreferredWidth(50); 314 315 mod = activeTable.getColumnModel(); 316 mod.getColumn(1).setPreferredWidth(800); 317 mod.getColumn(1).setCellRenderer(new ImageryTableCellRenderer(layerInfo.getDefaultLayers())); 318 mod.getColumn(0).setPreferredWidth(200); 319 320 RemoveEntryAction remove = new RemoveEntryAction(); 321 activeTable.getSelectionModel().addListSelectionListener(remove); 322 323 add(new JLabel(tr("Available default entries:")), GBC.eol().insets(5, 5, 0, 0)); 324 // Add default item list 325 JScrollPane scrolldef = new JScrollPane(defaultTable); 326 scrolldef.setPreferredSize(new Dimension(200, 200)); 327 add(scrolldef, GBC.std().insets(0, 5, 0, 0).fill(GridBagConstraints.BOTH).weight(1.0, 0.6).insets(5, 0, 0, 0)); 328 329 // Add default item map 330 defaultMap = new JMapViewer(); 331 defaultMap.setZoomContolsVisible(false); 332 defaultMap.setMinimumSize(new Dimension(100, 200)); 333 add(defaultMap, GBC.std().insets(5, 5, 0, 0).fill(GridBagConstraints.BOTH).weight(0.33, 0.6).insets(5, 0, 0, 0)); 334 335 defaultTableListener = new DefListSelectionListener(); 336 defaultTable.getSelectionModel().addListSelectionListener(defaultTableListener); 337 338 defaultToolbar = new JToolBar(JToolBar.VERTICAL); 339 defaultToolbar.setFloatable(false); 340 defaultToolbar.setBorderPainted(false); 341 defaultToolbar.setOpaque(false); 342 defaultToolbar.add(new ReloadAction()); 343 add(defaultToolbar, GBC.eol().anchor(GBC.SOUTH).insets(0, 0, 5, 0)); 344 345 ActivateAction activate = new ActivateAction(); 346 defaultTable.getSelectionModel().addListSelectionListener(activate); 347 JButton btnActivate = new JButton(activate); 348 349 middleToolbar = new JToolBar(JToolBar.HORIZONTAL); 350 middleToolbar.setFloatable(false); 351 middleToolbar.setBorderPainted(false); 352 middleToolbar.setOpaque(false); 353 middleToolbar.add(btnActivate); 354 add(middleToolbar, GBC.eol().anchor(GBC.CENTER).insets(5, 15, 5, 0)); 355 356 add(Box.createHorizontalGlue(), GBC.eol().fill(GridBagConstraints.HORIZONTAL)); 357 358 add(new JLabel(tr("Selected entries:")), GBC.eol().insets(5, 0, 0, 0)); 359 JScrollPane scroll = new JScrollPane(activeTable); 360 add(scroll, GBC.std().fill(GridBagConstraints.BOTH).span(GridBagConstraints.RELATIVE).weight(1.0, 0.4).insets(5, 0, 0, 5)); 361 scroll.setPreferredSize(new Dimension(200, 200)); 362 363 activeToolbar = new JToolBar(JToolBar.VERTICAL); 364 activeToolbar.setFloatable(false); 365 activeToolbar.setBorderPainted(false); 366 activeToolbar.setOpaque(false); 367 activeToolbar.add(new NewEntryAction(ImageryInfo.ImageryType.WMS)); 368 activeToolbar.add(new NewEntryAction(ImageryInfo.ImageryType.TMS)); 369 //activeToolbar.add(edit); TODO 370 activeToolbar.add(remove); 371 add(activeToolbar, GBC.eol().anchor(GBC.NORTH).insets(0, 0, 5, 5)); 372 } 373 374 // Listener of default providers list selection 375 private final class DefListSelectionListener implements ListSelectionListener { 376 // The current drawn rectangles and polygons 377 private final Map<Integer, MapRectangle> mapRectangles; 378 private final Map<Integer, List<MapPolygon>> mapPolygons; 379 380 private DefListSelectionListener() { 381 this.mapRectangles = new HashMap<>(); 382 this.mapPolygons = new HashMap<>(); 383 } 384 385 private void clearMap() { 386 defaultMap.removeAllMapRectangles(); 387 defaultMap.removeAllMapPolygons(); 388 mapRectangles.clear(); 389 mapPolygons.clear(); 390 } 391 392 @Override 393 public void valueChanged(ListSelectionEvent e) { 394 // First index can be set to -1 when the list is refreshed, so discard all map rectangles and polygons 395 if (e.getFirstIndex() == -1) { 396 clearMap(); 397 } else if (!e.getValueIsAdjusting()) { 398 // Only process complete (final) selection events 399 for (int i = e.getFirstIndex(); i<=e.getLastIndex(); i++) { 400 updateBoundsAndShapes(i); 401 } 402 // If needed, adjust map to show all map rectangles and polygons 403 if (!mapRectangles.isEmpty() || !mapPolygons.isEmpty()) { 404 defaultMap.setDisplayToFitMapElements(false, true, true); 405 defaultMap.zoomOut(); 406 } 407 } 408 } 409 410 private void updateBoundsAndShapes(int i) { 411 ImageryBounds bounds = defaultModel.getRow(i).getBounds(); 412 if (bounds != null) { 413 List<Shape> shapes = bounds.getShapes(); 414 if (shapes != null && !shapes.isEmpty()) { 415 if (defaultTable.getSelectionModel().isSelectedIndex(i)) { 416 if (!mapPolygons.containsKey(i)) { 417 List<MapPolygon> list = new ArrayList<>(); 418 mapPolygons.put(i, list); 419 // Add new map polygons 420 for (Shape shape : shapes) { 421 MapPolygon polygon = new MapPolygonImpl(shape.getPoints()); 422 list.add(polygon); 423 defaultMap.addMapPolygon(polygon); 424 } 425 } 426 } else if (mapPolygons.containsKey(i)) { 427 // Remove previously drawn map polygons 428 for (MapPolygon polygon : mapPolygons.get(i)) { 429 defaultMap.removeMapPolygon(polygon); 430 } 431 mapPolygons.remove(i); 432 } 433 // Only display bounds when no polygons (shapes) are defined for this provider 434 } else { 435 if (defaultTable.getSelectionModel().isSelectedIndex(i)) { 436 if (!mapRectangles.containsKey(i)) { 437 // Add new map rectangle 438 Coordinate topLeft = new Coordinate(bounds.getMaxLat(), bounds.getMinLon()); 439 Coordinate bottomRight = new Coordinate(bounds.getMinLat(), bounds.getMaxLon()); 440 MapRectangle rectangle = new MapRectangleImpl(topLeft, bottomRight); 441 mapRectangles.put(i, rectangle); 442 defaultMap.addMapRectangle(rectangle); 443 } 444 } else if (mapRectangles.containsKey(i)) { 445 // Remove previously drawn map rectangle 446 defaultMap.removeMapRectangle(mapRectangles.get(i)); 447 mapRectangles.remove(i); 448 } 449 } 450 } 451 } 452 } 453 454 private class NewEntryAction extends AbstractAction { 455 456 private final ImageryInfo.ImageryType type; 457 458 public NewEntryAction(ImageryInfo.ImageryType type) { 459 putValue(NAME, type.toString()); 460 putValue(SHORT_DESCRIPTION, tr("Add a new {0} entry by entering the URL", type.toString())); 461 String icon = /* ICON(dialogs/) */ "add"; 462 if(ImageryInfo.ImageryType.WMS.equals(type)) 463 icon = /* ICON(dialogs/) */ "add_wms"; 464 else if(ImageryInfo.ImageryType.TMS.equals(type)) 465 icon = /* ICON(dialogs/) */ "add_tms"; 466 putValue(SMALL_ICON, ImageProvider.get("dialogs", icon)); 467 this.type = type; 468 } 469 470 @Override 471 public void actionPerformed(ActionEvent evt) { 472 final AddImageryPanel p; 473 if (ImageryInfo.ImageryType.WMS.equals(type)) { 474 p = new AddWMSLayerPanel(); 475 } else if (ImageryInfo.ImageryType.TMS.equals(type)) { 476 p = new AddTMSLayerPanel(); 477 } else { 478 throw new IllegalStateException("Type " + type + " not supported"); 479 } 480 481 final AddImageryDialog addDialog = new AddImageryDialog(gui, p); 482 addDialog.showDialog(); 483 484 if (addDialog.getValue() == 1) { 485 try { 486 activeModel.addRow(p.getImageryInfo()); 487 } catch (IllegalArgumentException ex) { 488 if (ex.getMessage() == null || ex.getMessage().isEmpty()) 489 throw ex; 490 else { 491 JOptionPane.showMessageDialog(Main.parent, 492 ex.getMessage(), tr("Error"), 493 JOptionPane.ERROR_MESSAGE); 494 } 495 } 496 } 497 } 498 } 499 500 private class RemoveEntryAction extends AbstractAction implements ListSelectionListener { 501 502 /** 503 * Constructs a new {@code RemoveEntryAction}. 504 */ 505 public RemoveEntryAction() { 506 putValue(NAME, tr("Remove")); 507 putValue(SHORT_DESCRIPTION, tr("Remove entry")); 508 putValue(SMALL_ICON, ImageProvider.get("dialogs", "delete")); 509 updateEnabledState(); 510 } 511 512 protected final void updateEnabledState() { 513 setEnabled(activeTable.getSelectedRowCount() > 0); 514 } 515 516 @Override 517 public void valueChanged(ListSelectionEvent e) { 518 updateEnabledState(); 519 } 520 521 @Override 522 public void actionPerformed(ActionEvent e) { 523 Integer i; 524 while ((i = activeTable.getSelectedRow()) != -1) { 525 activeModel.removeRow(i); 526 } 527 } 528 } 529 530 private class ActivateAction extends AbstractAction implements ListSelectionListener { 531 532 /** 533 * Constructs a new {@code ActivateAction}. 534 */ 535 public ActivateAction() { 536 putValue(NAME, tr("Activate")); 537 putValue(SHORT_DESCRIPTION, tr("copy selected defaults")); 538 putValue(SMALL_ICON, ImageProvider.get("preferences", "activate-down")); 539 } 540 541 protected void updateEnabledState() { 542 setEnabled(defaultTable.getSelectedRowCount() > 0); 543 } 544 545 @Override 546 public void valueChanged(ListSelectionEvent e) { 547 updateEnabledState(); 548 } 549 550 @Override 551 public void actionPerformed(ActionEvent e) { 552 int[] lines = defaultTable.getSelectedRows(); 553 if (lines.length == 0) { 554 JOptionPane.showMessageDialog( 555 gui, 556 tr("Please select at least one row to copy."), 557 tr("Information"), 558 JOptionPane.INFORMATION_MESSAGE); 559 return; 560 } 561 562 Set<String> acceptedEulas = new HashSet<>(); 563 564 outer: 565 for (int line : lines) { 566 ImageryInfo info = defaultModel.getRow(line); 567 568 // Check if an entry with exactly the same values already exists 569 for (int j = 0; j < activeModel.getRowCount(); j++) { 570 if (info.equalsBaseValues(activeModel.getRow(j))) { 571 // Select the already existing row so the user has 572 // some feedback in case an entry exists 573 activeTable.getSelectionModel().setSelectionInterval(j, j); 574 activeTable.scrollRectToVisible(activeTable.getCellRect(j, 0, true)); 575 continue outer; 576 } 577 } 578 579 String eulaURL = info.getEulaAcceptanceRequired(); 580 // If set and not already accepted, ask for EULA acceptance 581 if (eulaURL != null && !acceptedEulas.contains(eulaURL)) { 582 if (confirmEulaAcceptance(gui, eulaURL)) { 583 acceptedEulas.add(eulaURL); 584 } else { 585 continue outer; 586 } 587 } 588 589 activeModel.addRow(new ImageryInfo(info)); 590 int lastLine = activeModel.getRowCount() - 1; 591 activeTable.getSelectionModel().setSelectionInterval(lastLine, lastLine); 592 activeTable.scrollRectToVisible(activeTable.getCellRect(lastLine, 0, true)); 593 } 594 } 595 } 596 597 private class ReloadAction extends AbstractAction { 598 599 /** 600 * Constructs a new {@code ReloadAction}. 601 */ 602 public ReloadAction() { 603 putValue(SHORT_DESCRIPTION, tr("reload defaults")); 604 putValue(SMALL_ICON, ImageProvider.get("dialogs", "refresh")); 605 } 606 607 @Override 608 public void actionPerformed(ActionEvent evt) { 609 layerInfo.loadDefaults(true); 610 defaultModel.fireTableDataChanged(); 611 defaultTable.getSelectionModel().clearSelection(); 612 defaultTableListener.clearMap(); 613 /* loading new file may change active layers */ 614 activeModel.fireTableDataChanged(); 615 } 616 } 617 618 /** 619 * The table model for imagery layer list 620 */ 621 public class ImageryLayerTableModel extends DefaultTableModel { 622 /** 623 * Constructs a new {@code ImageryLayerTableModel}. 624 */ 625 public ImageryLayerTableModel() { 626 setColumnIdentifiers(new String[] { tr("Menu Name"), tr("Imagery URL")}); 627 } 628 629 /** 630 * Returns the imagery info at the given row number. 631 * @param row The row number 632 * @return The imagery info at the given row number 633 */ 634 public ImageryInfo getRow(int row) { 635 return layerInfo.getLayers().get(row); 636 } 637 638 /** 639 * Adds a new imagery info as the last row. 640 * @param i The imagery info to add 641 */ 642 public void addRow(ImageryInfo i) { 643 layerInfo.add(i); 644 int p = getRowCount() - 1; 645 fireTableRowsInserted(p, p); 646 } 647 648 @Override 649 public void removeRow(int i) { 650 layerInfo.remove(getRow(i)); 651 fireTableRowsDeleted(i, i); 652 } 653 654 @Override 655 public int getRowCount() { 656 return layerInfo.getLayers().size(); 657 } 658 659 @Override 660 public Object getValueAt(int row, int column) { 661 ImageryInfo info = layerInfo.getLayers().get(row); 662 switch (column) { 663 case 0: 664 return info.getName(); 665 case 1: 666 return info.getExtendedUrl(); 667 default: 668 throw new ArrayIndexOutOfBoundsException(); 669 } 670 } 671 672 @Override 673 public void setValueAt(Object o, int row, int column) { 674 if (layerInfo.getLayers().size() <= row) return; 675 ImageryInfo info = layerInfo.getLayers().get(row); 676 switch (column) { 677 case 0: 678 info.setName((String) o); 679 info.clearId(); 680 break; 681 case 1: 682 info.setExtendedUrl((String)o); 683 info.clearId(); 684 break; 685 default: 686 throw new ArrayIndexOutOfBoundsException(); 687 } 688 } 689 690 @Override 691 public boolean isCellEditable(int row, int column) { 692 return true; 693 } 694 } 695 696 /** 697 * The table model for the default imagery layer list 698 */ 699 public class ImageryDefaultLayerTableModel extends DefaultTableModel { 700 /** 701 * Constructs a new {@code ImageryDefaultLayerTableModel}. 702 */ 703 public ImageryDefaultLayerTableModel() { 704 setColumnIdentifiers(new String[]{"", tr("Menu Name (Default)"), tr("Imagery URL (Default)")}); 705 } 706 707 /** 708 * Returns the imagery info at the given row number. 709 * @param row The row number 710 * @return The imagery info at the given row number 711 */ 712 public ImageryInfo getRow(int row) { 713 return layerInfo.getDefaultLayers().get(row); 714 } 715 716 @Override 717 public int getRowCount() { 718 return layerInfo.getDefaultLayers().size(); 719 } 720 721 @Override 722 public Object getValueAt(int row, int column) { 723 ImageryInfo info = layerInfo.getDefaultLayers().get(row); 724 switch (column) { 725 case 0: 726 return info.getCountryCode(); 727 case 1: 728 return info.getName(); 729 case 2: 730 return info.getExtendedUrl(); 731 } 732 return null; 733 } 734 735 @Override 736 public boolean isCellEditable(int row, int column) { 737 return false; 738 } 739 } 740 741 private boolean confirmEulaAcceptance(PreferenceTabbedPane gui, String eulaUrl) { 742 URL url = null; 743 try { 744 url = new URL(eulaUrl.replaceAll("\\{lang\\}", LanguageInfo.getWikiLanguagePrefix())); 745 JosmEditorPane htmlPane = null; 746 try { 747 htmlPane = new JosmEditorPane(url); 748 } catch (IOException e1) { 749 // give a second chance with a default Locale 'en' 750 try { 751 url = new URL(eulaUrl.replaceAll("\\{lang\\}", "")); 752 htmlPane = new JosmEditorPane(url); 753 } catch (IOException e2) { 754 JOptionPane.showMessageDialog(gui ,tr("EULA license URL not available: {0}", eulaUrl)); 755 return false; 756 } 757 } 758 Box box = Box.createVerticalBox(); 759 htmlPane.setEditable(false); 760 JScrollPane scrollPane = new JScrollPane(htmlPane); 761 scrollPane.setPreferredSize(new Dimension(400, 400)); 762 box.add(scrollPane); 763 int option = JOptionPane.showConfirmDialog(Main.parent, box, tr("Please abort if you are not sure"), 764 JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); 765 if (option == JOptionPane.YES_OPTION) 766 return true; 767 } catch (MalformedURLException e2) { 768 JOptionPane.showMessageDialog(gui ,tr("Malformed URL for the EULA licence: {0}", eulaUrl)); 769 } 770 return false; 771 } 772 } 773 774 static class OffsetBookmarksPanel extends JPanel { 775 List<OffsetBookmark> bookmarks = OffsetBookmark.allBookmarks; 776 OffsetsBookmarksModel model = new OffsetsBookmarksModel(); 777 778 public OffsetBookmarksPanel(final PreferenceTabbedPane gui) { 779 super(new GridBagLayout()); 780 final JTable list = new JTable(model) { 781 @Override 782 public String getToolTipText(MouseEvent e) { 783 java.awt.Point p = e.getPoint(); 784 return model.getValueAt(rowAtPoint(p), columnAtPoint(p)).toString(); 785 } 786 }; 787 JScrollPane scroll = new JScrollPane(list); 788 add(scroll, GBC.eol().fill(GridBagConstraints.BOTH)); 789 scroll.setPreferredSize(new Dimension(200, 200)); 790 791 TableColumnModel mod = list.getColumnModel(); 792 mod.getColumn(0).setPreferredWidth(150); 793 mod.getColumn(1).setPreferredWidth(200); 794 mod.getColumn(2).setPreferredWidth(300); 795 mod.getColumn(3).setPreferredWidth(150); 796 mod.getColumn(4).setPreferredWidth(150); 797 798 JPanel buttonPanel = new JPanel(new FlowLayout()); 799 800 JButton add = new JButton(tr("Add")); 801 buttonPanel.add(add, GBC.std().insets(0, 5, 0, 0)); 802 add.addActionListener(new ActionListener() { 803 @Override 804 public void actionPerformed(ActionEvent e) { 805 OffsetBookmark b = new OffsetBookmark(Main.getProjection().toCode(),"","",0,0); 806 model.addRow(b); 807 } 808 }); 809 810 JButton delete = new JButton(tr("Delete")); 811 buttonPanel.add(delete, GBC.std().insets(0, 5, 0, 0)); 812 delete.addActionListener(new ActionListener() { 813 @Override 814 public void actionPerformed(ActionEvent e) { 815 if (list.getSelectedRow() == -1) { 816 JOptionPane.showMessageDialog(gui, tr("Please select the row to delete.")); 817 } else { 818 Integer i; 819 while ((i = list.getSelectedRow()) != -1) { 820 model.removeRow(i); 821 } 822 } 823 } 824 }); 825 826 add(buttonPanel,GBC.eol()); 827 } 828 829 /** 830 * The table model for imagery offsets list 831 */ 832 private class OffsetsBookmarksModel extends DefaultTableModel { 833 834 /** 835 * Constructs a new {@code OffsetsBookmarksModel}. 836 */ 837 public OffsetsBookmarksModel() { 838 setColumnIdentifiers(new String[] { tr("Projection"), tr("Layer"), tr("Name"), tr("Easting"), tr("Northing"),}); 839 } 840 841 public OffsetBookmark getRow(int row) { 842 return bookmarks.get(row); 843 } 844 845 public void addRow(OffsetBookmark i) { 846 bookmarks.add(i); 847 int p = getRowCount() - 1; 848 fireTableRowsInserted(p, p); 849 } 850 851 @Override 852 public void removeRow(int i) { 853 bookmarks.remove(getRow(i)); 854 fireTableRowsDeleted(i, i); 855 } 856 857 @Override 858 public int getRowCount() { 859 return bookmarks.size(); 860 } 861 862 @Override 863 public Object getValueAt(int row, int column) { 864 OffsetBookmark info = bookmarks.get(row); 865 switch (column) { 866 case 0: 867 if (info.projectionCode == null) return ""; 868 return info.projectionCode.toString(); 869 case 1: 870 return info.layerName; 871 case 2: 872 return info.name; 873 case 3: 874 return info.dx; 875 case 4: 876 return info.dy; 877 default: 878 throw new ArrayIndexOutOfBoundsException(); 879 } 880 } 881 882 @Override 883 public void setValueAt(Object o, int row, int column) { 884 OffsetBookmark info = bookmarks.get(row); 885 switch (column) { 886 case 1: 887 info.layerName = o.toString(); 888 break; 889 case 2: 890 info.name = o.toString(); 891 break; 892 case 3: 893 info.dx = Double.parseDouble((String) o); 894 break; 895 case 4: 896 info.dy = Double.parseDouble((String) o); 897 break; 898 default: 899 throw new ArrayIndexOutOfBoundsException(); 900 } 901 } 902 903 @Override 904 public boolean isCellEditable(int row, int column) { 905 return column >= 1; 906 } 907 } 908 } 909 910 /** 911 * Initializes imagery preferences. 912 */ 913 public static void initialize() { 914 ImageryLayerInfo.instance.load(); 915 OffsetBookmark.loadBookmarks(); 916 Main.main.menu.imageryMenu.refreshImageryMenu(); 917 Main.main.menu.imageryMenu.refreshOffsetMenu(); 918 } 919}