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