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