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