001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2017 005 * Space Science and Engineering Center (SSEC) 006 * University of Wisconsin - Madison 007 * 1225 W. Dayton Street, Madison, WI 53706, USA 008 * https://www.ssec.wisc.edu/mcidas 009 * 010 * All Rights Reserved 011 * 012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and 013 * some McIDAS-V source code is based on IDV and VisAD source code. 014 * 015 * McIDAS-V is free software; you can redistribute it and/or modify 016 * it under the terms of the GNU Lesser Public License as published by 017 * the Free Software Foundation; either version 3 of the License, or 018 * (at your option) any later version. 019 * 020 * McIDAS-V is distributed in the hope that it will be useful, 021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 023 * GNU Lesser Public License for more details. 024 * 025 * You should have received a copy of the GNU Lesser Public License 026 * along with this program. If not, see http://www.gnu.org/licenses. 027 */ 028 029package edu.wisc.ssec.mcidasv.chooser; 030 031import static javax.swing.GroupLayout.DEFAULT_SIZE; 032import static javax.swing.GroupLayout.PREFERRED_SIZE; 033import static javax.swing.GroupLayout.Alignment.BASELINE; 034import static javax.swing.GroupLayout.Alignment.LEADING; 035import static javax.swing.GroupLayout.Alignment.TRAILING; 036import static javax.swing.LayoutStyle.ComponentPlacement.RELATED; 037import static javax.swing.LayoutStyle.ComponentPlacement.UNRELATED; 038 039import java.awt.Component; 040import java.awt.Dimension; 041import java.awt.event.ActionListener; 042import java.awt.event.ItemEvent; 043import java.awt.event.ItemListener; 044import java.io.IOException; 045import java.net.URI; 046import java.util.ArrayList; 047import java.util.Date; 048import java.util.Hashtable; 049import java.util.List; 050import java.util.Vector; 051import java.util.regex.Pattern; 052 053import javax.swing.GroupLayout; 054import javax.swing.JButton; 055import javax.swing.JComboBox; 056import javax.swing.JComponent; 057import javax.swing.JLabel; 058import javax.swing.JPanel; 059import javax.swing.JScrollPane; 060import javax.swing.JTabbedPane; 061import javax.swing.JTextField; 062import javax.swing.event.DocumentEvent; 063import javax.swing.event.DocumentListener; 064import javax.swing.text.BadLocationException; 065 066import edu.wisc.ssec.mcidasv.chooser.adde.AddeChooser; 067import edu.wisc.ssec.mcidasv.util.McVTextField; 068import org.jdom2.Document; 069import org.jdom2.JDOMException; 070import org.jdom2.Namespace; 071import org.jdom2.input.SAXBuilder; 072import org.slf4j.Logger; 073import org.slf4j.LoggerFactory; 074import org.w3c.dom.Element; 075 076import thredds.catalog.XMLEntityResolver; 077import ucar.unidata.data.DataSelection; 078import ucar.unidata.data.radar.TDSRadarDatasetCollection; 079import ucar.nc2.units.DateUnit; 080import ucar.unidata.data.radar.RadarQuery; 081import ucar.unidata.geoloc.StationImpl; 082import ucar.unidata.idv.chooser.IdvChooserManager; 083import ucar.unidata.idv.chooser.TimesChooser; 084import ucar.unidata.metdata.NamedStation; 085import ucar.unidata.metdata.NamedStationImpl; 086import ucar.unidata.util.DateSelection; 087import ucar.unidata.util.DateUtil; 088import ucar.unidata.util.DatedThing; 089import ucar.unidata.util.GuiUtils; 090import ucar.unidata.util.LogUtil; 091import ucar.unidata.util.Misc; 092import ucar.unidata.util.PreferenceList; 093import ucar.unidata.util.Product; 094import ucar.unidata.util.TwoFacedObject; 095 096import visad.CommonUnit; 097import visad.DateTime; 098 099import edu.wisc.ssec.mcidasv.Constants; 100import edu.wisc.ssec.mcidasv.util.McVGuiUtils; 101import edu.wisc.ssec.mcidasv.util.McVGuiUtils.Position; 102import edu.wisc.ssec.mcidasv.util.McVGuiUtils.TextColor; 103import edu.wisc.ssec.mcidasv.util.McVGuiUtils.Width; 104 105/** 106 * This class is responsible for the {@literal "Remote Level II Radar"} 107 * chooser in McIDAS-V. 108 */ 109public class TDSRadarChooser extends TimesChooser implements Constants { 110 111 /** Loggging object. */ 112 private static final Logger logger = 113 LoggerFactory.getLogger(TDSRadarChooser.class); 114 115 /** The collection */ 116 private TDSRadarDatasetCollection collection; 117 118 /** The currently selected station */ 119 private NamedStation selectedStation; 120 121 /** The currently selected level3 product */ 122 private String selectedProduct; 123 124 /** Those urls we connect to */ 125 //"http://motherlode.ucar.edu:8080/thredds/radarServer/catalog.xml"; 126 private String serverUrl; 127 128 ///** Each dataset collection URL */ 129 //"http://motherlode.ucar.edu:8080/thredds/radarServer/level2/idd/dataset.xml"; 130 //private String collectionUrl; 131 132 /** Component to hold collections */ 133 private JComboBox collectionSelector; 134 135 /** Component to hold product list */ 136 private JComboBox productComboBox; 137 138 /** Level 3 panel that can be hidden */ 139 private JPanel productPanel; 140 141 /** components that need a server for activation */ 142 private List compsThatNeedServer = new ArrayList(); 143 144 /** components that need a server for activation */ 145 private List level3CompsThatNeedServer = new ArrayList(); 146 147 /** persistent holder for catalog URLS */ 148 private PreferenceList urlListHandler; 149 150 /** catalog URL holder */ 151 private JComboBox urlBox; 152 153 /** ok flag */ 154 private boolean okToDoUrlListEvents = true; 155 156 /** dataset list */ 157 private List datasetList; 158 159 /** Command for connecting */ 160 protected static final String CMD_CONNECT = "cmd.connect"; 161 162 /** _more_ */ 163 private boolean isLevel3; 164 165 /** _more_ */ 166 public static final String[] level3_ExName = { "NVW", "DPA" }; 167 168 /** Number of relative time steps to load */ 169 private int relativeTimes = 5; 170 171 /** 172 * Create the RadarChooser. 173 * 174 * @param mgr {@code IdvChooserManager} 175 * @param root XML root that defines this chooser. 176 * 177 */ 178 public TDSRadarChooser(IdvChooserManager mgr, Element root) { 179 super(mgr, root); 180 181 loadButton = McVGuiUtils.makeImageTextButton(ICON_ACCEPT_SMALL, getLoadCommandName()); 182 loadButton.setActionCommand(getLoadCommandName()); 183 loadButton.addActionListener(this); 184 185 cancelButton = McVGuiUtils.makeImageButton(ICON_CANCEL, "Cancel"); 186 cancelButton.setActionCommand(GuiUtils.CMD_CANCEL); 187 cancelButton.addActionListener(this); 188 cancelButton.setEnabled(false); 189 190 } 191 192 193 194 /** 195 * Handle the update event. Just pass it through to the imageChooser 196 */ 197 public void doUpdate() { 198 if ((serverUrl == null) || (datasetList == null) 199 || (datasetList.size() == 0) || (selectedProduct == null)) { 200 if (urlBox != null) { 201 setServer((String) urlBox.getSelectedItem()); 202 } 203 return; 204 } 205 Misc.run(this, "stationOrProductChanged"); 206 } 207 208 209 210 /** 211 * Update the status of the gui 212 */ 213 protected void updateStatus() { 214 super.updateStatus(); 215 if (serverUrl == null) { 216 setHaveData(false); 217 setStatus("Please connect to the server"); 218 } 219 else if (selectedStation == null) { 220 setHaveData(false); 221 setStatus("Please select a station", "stations"); 222 } 223 else if (isLevel3 && (selectedProduct == null)) { 224 setHaveData(false); 225 setStatus("Please select a level 3 product", "products"); 226 } 227 else { 228 boolean haveTimesSelected; 229 if (getDoAbsoluteTimes()) { 230 haveTimesSelected = getSelectedAbsoluteTimes().size() > 0; 231 } else { 232 haveTimesSelected = true; 233 } 234 setHaveData(haveTimesSelected); 235 if (haveTimesSelected) { 236 setStatus("Press \"" + CMD_LOAD + "\" to load the selected radar data", "buttons"); 237 } else { 238 setStatus("Please select times", "timepanel"); 239 } 240 } 241 GuiUtils.enableTree(loadButton, getHaveData()); 242 if (drivercbx != null) { 243 drivercbx.setEnabled(anyTimeDrivers() && getHaveData()); 244 } 245 } 246 247 248 249 /** 250 * Handle when there are newly selected stations 251 * 252 * @param stations list of newly selected stations 253 */ 254 protected void newSelectedStations(List stations) { 255 super.newSelectedStations(stations); 256 if ((stations == null) || (stations.size() == 0)) { 257 selectedStation = null; 258 } else { 259 NamedStation newStation = (NamedStation) stations.get(0); 260 if (Misc.equals(newStation, selectedStation)) { 261 return; 262 } 263 selectedStation = newStation; 264 } 265 Misc.run(TDSRadarChooser.this, "stationOrProductChanged"); 266 } 267 268 269 /** A widget for the list of dataset descriptors */ 270 271 272 /** Flag to keep from infinite looping */ 273 private boolean ignoreProductChange = false; 274 275 /** Selection label text */ 276 protected static final String LABEL_SELECT = " -- Select -- "; 277 278 /** 279 * _more_ 280 */ 281 protected void productChanged() { 282 stationOrProductChanged(); 283 // updateStatus(); 284 } 285 286 /** 287 * Reset the descriptor stuff 288 */ 289 private void resetProductBox() { 290 ignoreProductChange = true; 291 productComboBox.setSelectedItem(LABEL_SELECT); 292 ignoreProductChange = false; 293 } 294 295 /** 296 * Should we update on first display 297 * 298 * @return true 299 */ 300 protected boolean shouldDoUpdateOnFirstDisplay() { 301 return false; 302 } 303 304 /** 305 * Set the server 306 * 307 * @param s the server URL 308 */ 309 private void setServer(String s) { 310 datasetList = new ArrayList(); 311 serverUrl = s; 312 try { 313 datasetList = getRadarCollections(serverUrl); 314 GuiUtils.setListData(collectionSelector, datasetList); 315 for (int i = 0; i < datasetList.size(); i++) { 316 TwoFacedObject obj = (TwoFacedObject) datasetList.get(i); 317 String tmpStr = (String) (obj.getLabel()); 318 if ((tmpStr).contains("NEXRAD Level II Radar from IDD")) { 319 collectionSelector.setSelectedIndex(i); 320 } 321 } 322 } catch (Exception e) { 323 GuiUtils.setListData(collectionSelector, new ArrayList()); 324 } 325 } 326 327 /** 328 * Set the active collection 329 * 330 * @param s collection URL 331 */ 332 private void setCollection(String s) { 333 isLevel3 = false; 334 GuiUtils.enableComponents(level3CompsThatNeedServer, false); 335 productPanel.setVisible(false); 336 GuiUtils.enableComponents(compsThatNeedServer, true); 337 if (drivercbx != null) { 338 drivercbx.setEnabled(anyTimeDrivers() && getHaveData()); 339 } 340 setAbsoluteTimes(new ArrayList()); 341 selectedProduct = null; 342 selectedStation = null; 343 Misc.run(this, "initializeCollection", s); 344 } 345 346 /** 347 * _more_ 348 * 349 * @param s _more_ 350 */ 351 private void setLevel3Collection(String s) { 352 isLevel3 = true; 353 GuiUtils.enableComponents(level3CompsThatNeedServer, true); 354 productPanel.setVisible(true); 355 GuiUtils.enableComponents(compsThatNeedServer, true); 356 if (drivercbx != null) { 357 drivercbx.setEnabled(anyTimeDrivers() && getHaveData()); 358 } 359 setAbsoluteTimes(new ArrayList()); 360 selectedProduct = null; 361 selectedStation = null; 362 Misc.run(this, "initializeLevel3Collection", s); 363 } 364 365 /** 366 * Add a component that needs to have a valid server 367 * 368 * @param comp the component 369 * 370 * @return the component 371 */ 372 protected JComponent addServerComp(JComponent comp) { 373 compsThatNeedServer.add(comp); 374 return comp; 375 } 376 377 /** 378 * Add a component that needs to have a valid server 379 * 380 * @param comp the component 381 * 382 * @return the component 383 */ 384 protected JComponent addLevel3ServerComp(JComponent comp) { 385 level3CompsThatNeedServer.add(comp); 386 return comp; 387 } 388 389 /** 390 * Get the radar collections for the given server URL 391 * 392 * @param radarServerURL server URL 393 * 394 * @return a map of the collection names to URL 395 */ 396 private List getRadarCollections(String radarServerURL) { 397 SAXBuilder builder; 398 Document doc = null; 399 XMLEntityResolver jaxp = new XMLEntityResolver(true); 400 builder = jaxp.getSAXBuilder(); 401 List collections = new ArrayList(); 402 403 try { 404 doc = builder.build(radarServerURL); 405 } catch (JDOMException e) { 406 userMessage("Invalid catalog"); 407 //e.printStackTrace(); 408 } catch (IOException e) { 409 userMessage("Unable to open catalog"); 410 //e.printStackTrace(); 411 } 412 413 org.jdom2.Element rootElem = doc.getRootElement(); 414 org.jdom2.Element serviceElem = readElements(rootElem, "service"); 415 String uriBase = serviceElem.getAttributeValue("base"); 416 org.jdom2.Element dsElem = readElements(rootElem, "dataset"); 417 String naming = "catalogRef"; 418 Namespace nss = rootElem.getNamespace("xlink"); 419 List children = dsElem.getChildren(); 420 for (int j = 0; j < children.size(); j++) { 421 org.jdom2.Element child = (org.jdom2.Element) children.get(j); 422 String childName = child.getName(); 423 if (childName.equals(naming)) { 424 //String id = child.getAttributeValue("ID"); 425 String desc = child.getAttributeValue("title", nss); 426 String urlpath = child.getAttributeValue("href", nss); 427 String[] c = radarServerURL.split(uriBase); //.replaceFirst("catalog.xml", ""); 428 String ul = c[0] + uriBase + urlpath; 429 TwoFacedObject twoObj = new TwoFacedObject(desc, ul); 430 collections.add(twoObj); 431 //collections.put(desc, ul); 432 } 433 434 } 435 436 return collections; 437 } 438 439 /** 440 * Read the elements 441 * 442 * @param elem element 443 * @param eleName element name 444 * 445 * @return an element 446 */ 447 public org.jdom2.Element readElements(org.jdom2.Element elem, 448 String eleName) { 449 List children = elem.getChildren(); 450 for (int j = 0; j < children.size(); j++) { 451 org.jdom2.Element child = (org.jdom2.Element) children.get(j); 452 String childName = child.getName(); 453 if (childName.equals(eleName)) { 454 return child; 455 } 456 } 457 return null; 458 } 459 460 /** 461 * Make the collection. If there is an error, pop up a user message. 462 * 463 * @param url URL for the collection 464 */ 465 public void initializeCollection(String url) { 466 467 List<NamedStationImpl> stations = new ArrayList<NamedStationImpl>(); 468 try { 469 StringBuffer errlog = new StringBuffer(); 470 try { 471 collection = TDSRadarDatasetCollection.factory("test", url, 472 errlog); 473 } catch (Exception exc) { 474 userMessage("Invalid catalog"); 475 476 return; 477 } 478 List tdsStations = collection.getRadarStations(); 479 for (int i = 0; i < tdsStations.size(); i++) { 480 StationImpl stn = (StationImpl) tdsStations.get(i); 481 // thredds.catalog.query.Location loc = stn.getLocation(); 482 //TODO: need better station need to switch lat lon 483 NamedStationImpl station = 484 new NamedStationImpl(stn.getName(), stn.getName(), 485 stn.getLatitude(), 486 stn.getLongitude(), 487 stn.getAltitude(), CommonUnit.meter); 488 stations.add(station); 489 490 } 491 492 getStationMap().setStations(stations); 493 } catch (Exception exc) { 494 userMessage("Unable to load stations"); 495 return; 496 } 497 urlListHandler.saveState(urlBox); 498 } 499 500 /** 501 * _more_ 502 * 503 * @param url _more_ 504 */ 505 public void initializeLevel3Collection(String url) { 506 507 List<NamedStationImpl> stations = new ArrayList<NamedStationImpl>(); 508 List<Product> products; 509 List<String> exProducts = new ArrayList<String>(); 510 511 for(String ename: level3_ExName){ 512 exProducts.add(ename); 513 } 514 515 try { 516 StringBuffer errlog = new StringBuffer(); 517 try { 518 collection = TDSRadarDatasetCollection.factory("test", url, 519 errlog); 520 } catch (Exception exc) { 521 userMessage("Invalid catalog"); 522 return; 523 } 524 products = collection.getRadarProducts(); 525 List tdsStations = collection.getRadarStations(); 526 for (int i = 0; i < tdsStations.size(); i++) { 527 StationImpl stn = (StationImpl) tdsStations.get(i); 528 // thredds.catalog.query.Location loc = stn.getLocation(); 529 //TODO: need better station need to switch lat lon 530 NamedStationImpl station = 531 new NamedStationImpl(stn.getName(), stn.getName(), 532 stn.getLatitude(), 533 stn.getLongitude(), 534 stn.getAltitude(), CommonUnit.meter); 535 stations.add(station); 536 537 } 538 List<TwoFacedObject> productNames = new ArrayList(); 539 for (Product product : products) { 540 // if ( !product.getID().contains("DPA") 541 // && !product.getID().contains("NVW")) { 542 if ( !exProducts.contains(product.getID())) { 543 String lable = product.getName() + " (" + product.getID() 544 + ")"; 545 TwoFacedObject twoObj = new TwoFacedObject(lable, 546 product.getID()); 547 productNames.add(twoObj); 548 } 549 } 550 GuiUtils.setListData(productComboBox, productNames); 551 552 // GuiUtils.setListData(dataTypeComboBox, dataTypes); 553 getStationMap().setStations(stations); 554 } catch (Exception exc) { 555 userMessage("Unable to load stations"); 556 return; 557 } 558 urlListHandler.saveState(urlBox); 559 } 560 561 562 /** 563 * Handle when the user has selected a new station 564 */ 565 public void stationOrProductChanged() { 566 Vector times = new Vector(); 567 setHaveData(false); 568 if ((!isLevel3 && selectedStation != null) || 569 (isLevel3 && selectedStation != null && selectedProduct != null)) { 570 List timeSpan = collection.getRadarTimeSpan(); 571 Date fromDate = DateUnit.getStandardOrISO((String) timeSpan.get(0)); 572 //Date toDate = DateUnit.getStandardOrISO((String) timeSpan.get(1)); 573 Date toDate = new Date(System.currentTimeMillis() 574 + DateUtil.daysToMillis(1)); 575 //Go back 10 years (or so) 576 //Date fromDate = new Date(System.currentTimeMillis() 577 // - DateUtil.daysToMillis(365 * 10)); 578 try { 579 showWaitCursor(); 580 setAbsoluteTimes(new ArrayList()); 581 setStatus("Reading times for station: " + selectedStation, 582 ""); 583 // LogUtil.message("Reading times for station: " 584 // + selectedStation); 585 String pid = null; 586 if(isLevel3) 587 pid = TwoFacedObject.getIdString( 588 productComboBox.getSelectedItem()); 589 List allTimes = 590 collection.getRadarStationTimes(selectedStation.getID(), 591 pid, fromDate, toDate); 592 593 // if(allTimes.size() == 0) { 594 // toDate = new Date(System.currentTimeMillis() 595 // + DateUtil.daysToMillis(1)); 596 // allTimes = 597 // collection.getRadarStationTimes(selectedStation.getID(), 598 // pid, fromDate, toDate); 599 // } 600 601 for (int timeIdx = 0; timeIdx < allTimes.size(); timeIdx++) { 602 Object timeObj = allTimes.get(timeIdx); 603 Date date; 604 if (timeObj instanceof Date) { 605 date = (Date) timeObj; 606 } else { 607 date = DateUnit.getStandardOrISO(timeObj.toString()); 608 } 609 times.add(new DateTime(date)); 610 } 611 // LogUtil.message(""); 612 showNormalCursor(); 613 } catch (Exception exc) { 614 userMessage("Error reading times for station: " 615 + selectedStation); 616 //logException("Getting times for station: " + selectedStation, 617 // exc); 618 setStatus("Select a different collection", "collections"); 619 showNormalCursor(); 620 return; 621 } 622 } 623 setAbsoluteTimes(times); 624 updateStatus(); 625 } 626 627 628 629 630 631 /** 632 * Load the data 633 */ 634 public void doLoadInThread() { 635 // to the CDMRadarDataSource 636 Hashtable ht = new Hashtable(); 637 if (selectedStation != null) { 638 ht.put(ucar.unidata.data.radar.RadarDataSource.STATION_LOCATION, 639 selectedStation.getNamedLocation()); 640 ht.put(DataSelection.PROP_CHOOSERTIMEMATCHING, getDoTimeDrivers()); 641 } else { 642 LogUtil.userMessage("No Station selected"); 643 } 644 645 if (isLevel3 && (selectedProduct == null)) { 646 647 LogUtil.userMessage("No Product selected"); 648 } 649 650 try { 651 DateSelection dateSelection = new DateSelection(); 652 String collectionUrl = TwoFacedObject.getIdString( 653 collectionSelector.getSelectedItem()); 654 String pid = null; 655 RadarQuery radarQuery; 656 if (isLevel3) { 657 pid = TwoFacedObject.getIdString( 658 productComboBox.getSelectedItem()); 659 radarQuery = new RadarQuery(collectionUrl, 660 selectedStation.getID(), pid, 661 dateSelection); 662 } else { 663 radarQuery = new RadarQuery(collectionUrl, 664 selectedStation.getID(), 665 dateSelection); 666 } 667 668 List urls = new ArrayList(); 669 670 if (getDoAbsoluteTimes()) { 671 List times = new ArrayList(); 672 List selected = makeDatedObjects(getSelectedAbsoluteTimes()); 673 for (int i = 0; i < selected.size(); i++) { 674 DatedThing datedThing = (DatedThing) selected.get(i); 675 Date date = datedThing.getDate(); 676 times.add(date); 677 URI uri = null; 678 try { 679 uri = collection.getRadarDatasetURI( 680 selectedStation.getID(), pid, date); 681 } catch (Exception excp) { 682 LogUtil.userMessage("incorrect times selected"); 683 return; 684 } 685 urls.add(uri.toString()); 686 } 687 if (urls.size() == 0) { 688 LogUtil.userMessage("No times selected"); 689 return; 690 } 691 dateSelection.setTimes(times); 692 } else { 693// int count = getRelativeTimesList().getSelectedIndex() + 1; 694 if (relativeTimes == 0) { 695 LogUtil.userMessage("No relative times selected"); 696 return; 697 } 698 Date toDate = new Date(System.currentTimeMillis() 699 + DateUtil.daysToMillis(365 * 100)); 700 //Go back 10 years (or so) 701 Date fromDate = new Date(System.currentTimeMillis() 702 - DateUtil.daysToMillis(365 * 10)); 703 704 dateSelection.setStartFixedTime(fromDate); 705 dateSelection.setEndFixedTime(toDate); 706 dateSelection.setCount(relativeTimes); 707 } 708 makeDataSource(radarQuery, "FILE.RADAR", ht); 709 } catch (Exception exc) { 710 logException("Loading radar data", exc); 711 } 712 // uncheck the check box every time click the add source button 713 drivercbx.setSelected(false); 714 enableTimeWidgets(); 715 setDoTimeDrivers(false); 716 } 717 718 protected int getNumTimesToSelect() { 719 return 5; 720 } 721 722 /** 723 * Get the default selected index for the relative times list. 724 * 725 * @return default index 726 */ 727 protected int getDefaultRelativeTimeIndex() { 728 return 4; 729 } 730 731 /** 732 * Check the times lists 733 */ 734 protected void checkTimesLists() { 735 super.checkTimesLists(); 736 if (timesCardPanelExtra == null) { 737 return; 738 } 739 if (getDoAbsoluteTimes()) { 740 timesCardPanelExtra.show("absolute"); 741 } else { 742 timesCardPanelExtra.show("relative"); 743 } 744 } 745 746 /** Card panel to hold extra relative and absolute time components */ 747 private GuiUtils.CardLayoutPanel timesCardPanelExtra; 748 749 /** 750 * Add the interval selector to the component. 751 * @return superclass component with extra stuff 752 */ 753 protected JPanel makeTimesPanel() { 754 JComponent extra = getExtraTimeComponent(); 755 GuiUtils.enableTree(extra, false); 756 JPanel timesPanel = makeTimesPanel(extra, null); 757 return timesPanel; 758 } 759 760 /** 761 * Set the relative and absolute extra components 762 */ 763 protected JPanel makeTimesPanel(JComponent relativeCard, JComponent absoluteCard) { 764 JPanel timesPanel = super.makeTimesPanel(false, true, getIdv().getUseTimeDriver()); 765 766 // Make a new timesPanel that has extra components tacked on the bottom, inside the tabs 767 Component[] comps = timesPanel.getComponents(); 768 769 if ((comps.length == 2) && (comps[0] instanceof JTabbedPane) && (comps[1] instanceof JLabel)) { 770 timesCardPanelExtra = new GuiUtils.CardLayoutPanel(); 771 if (relativeCard == null) { 772 relativeCard = new JPanel(); 773 } 774 if (absoluteCard == null) { 775 absoluteCard = new JPanel(); 776 } 777 absoluteCard = GuiUtils.hbox(comps[1], GuiUtils.right(absoluteCard)); 778 timesCardPanelExtra.add(relativeCard, "relative"); 779 timesCardPanelExtra.add(absoluteCard, "absolute"); 780 timesPanel = GuiUtils.centerBottom(comps[0], timesCardPanelExtra); 781 } 782 return timesPanel; 783 } 784 785 /** 786 * Get the time popup widget 787 * 788 * @return a widget for selecting the day 789 */ 790 protected JComponent getExtraTimeComponent() { 791 JPanel filler = new JPanel(); 792 McVGuiUtils.setComponentHeight(filler, new JComboBox()); 793 return filler; 794 } 795 796 /** 797 * Make the contents 798 * 799 * @return the contents 800 */ 801 protected JPanel doMakeInnerPanel() { 802 JPanel myPanel = new JPanel(); 803 804 JLabel collectionLabel = McVGuiUtils.makeLabelRight("Collection:"); 805 806 collectionSelector = new JComboBox(); 807 collectionSelector.addItemListener(new ItemListener() { 808 public void itemStateChanged(ItemEvent e) { 809 newSelectedStations(new ArrayList()); 810 if (collectionSelector.getSelectedItem() == null) { 811 return; 812 } 813 String collectionUrl = 814 TwoFacedObject.getIdString( 815 collectionSelector.getSelectedItem()); 816 817 if (collectionUrl.contains("level3")) { 818 setLevel3Collection(collectionUrl); 819 } else { 820 setCollection(collectionUrl); 821 } 822 } 823 824 }); 825 addServerComp(collectionLabel); 826 addServerComp(collectionSelector); 827 828 productComboBox = new JComboBox(); 829 productComboBox.addItemListener(new ItemListener() { 830 public void itemStateChanged(ItemEvent e) { 831 if (productComboBox.getSelectedItem() == null) { 832 return; 833 } 834 selectedProduct = 835 productComboBox.getSelectedItem().toString(); 836 resetProductBox(); 837 productChanged(); 838 } 839 840 }); 841 addLevel3ServerComp(productComboBox); 842 843 productPanel = McVGuiUtils.makeLabeledComponent("Product:", productComboBox); 844 845 JLabel stationLabel = McVGuiUtils.makeLabelRight("Station:"); 846 addServerComp(stationLabel); 847 848 JComponent stationPanel = getStationMap(); 849 registerStatusComp("stations", stationPanel); 850 addServerComp(stationPanel); 851 852 JLabel timesLabel = McVGuiUtils.makeLabelRight("Times:"); 853 addServerComp(timesLabel); 854 855 JPanel timesPanel = makeTimesPanel(); 856 timesPanel.setBorder(javax.swing.BorderFactory.createEtchedBorder()); 857 addServerComp(timesPanel); 858 859 GuiUtils.enableComponents(compsThatNeedServer, false); 860 GuiUtils.enableComponents(level3CompsThatNeedServer, false); 861 productPanel.setVisible(false); 862 863 GroupLayout layout = new GroupLayout(myPanel); 864 myPanel.setLayout(layout); 865 layout.setHorizontalGroup( 866 layout.createParallelGroup(LEADING) 867 .addGroup(layout.createSequentialGroup() 868 .addGroup(layout.createParallelGroup(LEADING) 869 .addGroup(layout.createSequentialGroup() 870 .addComponent(collectionLabel) 871 .addGap(GAP_RELATED) 872 .addComponent(collectionSelector, PREFERRED_SIZE, DEFAULT_SIZE, Short.MAX_VALUE) 873 .addComponent(productPanel, PREFERRED_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)) 874 .addGroup(layout.createSequentialGroup() 875 .addComponent(stationLabel) 876 .addGap(GAP_RELATED) 877 .addComponent(stationPanel, PREFERRED_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)) 878 .addGroup(layout.createSequentialGroup() 879 .addComponent(timesLabel) 880 .addGap(GAP_RELATED) 881 .addComponent(timesPanel, PREFERRED_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)))) 882 ); 883 layout.setVerticalGroup( 884 layout.createParallelGroup(LEADING) 885 .addGroup(layout.createSequentialGroup() 886 .addGroup(layout.createParallelGroup(BASELINE) 887 .addComponent(collectionLabel) 888 .addComponent(collectionSelector) 889 .addComponent(productPanel)) 890 .addPreferredGap(RELATED) 891 .addGroup(layout.createParallelGroup(LEADING) 892 .addComponent(stationLabel) 893 .addComponent(stationPanel, PREFERRED_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)) 894 .addPreferredGap(RELATED) 895 .addGroup(layout.createParallelGroup(LEADING) 896 .addComponent(timesLabel) 897 .addComponent(timesPanel, PREFERRED_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)) 898 .addPreferredGap(RELATED)) 899 ); 900 901 return myPanel; 902 } 903 904 private JPanel innerPanel = doMakeInnerPanel(); 905 906 private JLabel statusLabel = new JLabel("Status"); 907 908 @Override public void setStatus(String statusString, String foo) { 909 if (statusString == null) { 910 statusString = ""; 911 } 912 statusLabel.setText(statusString); 913 } 914 915 protected void setInnerPanel(JPanel newInnerPanel) { 916 innerPanel = newInnerPanel; 917 } 918 919 /** 920 * Create the widget responsible for handling relative time selection. 921 * 922 * @return GUI widget. 923 */ 924 @Override public JComponent getRelativeTimesChooser() { 925 McVTextField relativeTimesField = 926 McVGuiUtils.makeTextFieldAllow(String.valueOf(relativeTimes), 927 4, 928 false, 929 '0', '1', '2', '3', '4', '5', '6', 930 '7','8','9'); 931 932 // need to keep *both* the ActionListener and DocumentListener around. 933 // removing the ActionListener results in strange behavior when you've 934 // accidentally cleared out the text field. 935 relativeTimesField.setAllow(Pattern.compile("^[1-9][0-9]*$"), true); 936// relativeTimesField.setDeny(Pattern.compile("^0$"), true); 937 relativeTimesField.setColumns(4); 938 relativeTimesField.addActionListener(e -> { 939 String text = ((JTextField)e.getSource()).getText(); 940 validateRelativeTimeInput(text); 941 }); 942 relativeTimesField.getDocument().addDocumentListener(new DocumentListener() { 943 @Override public void insertUpdate(DocumentEvent e) { 944 handleRelativeTimeChange(e); 945 } 946 947 @Override public void removeUpdate(DocumentEvent e) { 948 handleRelativeTimeChange(e); 949 } 950 951 @Override public void changedUpdate(DocumentEvent e) { 952 handleRelativeTimeChange(e); 953 } 954 }); 955 relativeTimesField.setToolTipText(AddeChooser.RELATIVE_TIMES_TOOLTIP); 956 957 JPanel panel = 958 GuiUtils.topLeft(GuiUtils.label(AddeChooser.RELATIVE_TIMES_LABEL, 959 relativeTimesField)); 960 JScrollPane scrollPane = new JScrollPane(panel); 961 scrollPane.setPreferredSize(new Dimension(150, 100)); 962 return scrollPane; 963 } 964 965 /** 966 * Validate the contents of the relative times text field. 967 * 968 * <p>This method overwrites {@link #relativeTimes} if {@code text} is an 969 * integer greater than zero.</p> 970 * 971 * @param text Contents of the text field. 972 */ 973 private void validateRelativeTimeInput(String text) { 974 try { 975 int value = Integer.valueOf(text); 976 if (value > 0) { 977 relativeTimes = value; 978 setHaveData(true); 979 updateStatus(); 980 } 981 } catch (NumberFormatException e) { 982 setHaveData(false); 983 setStatus("Please provide an integer value greater than zero."); 984 } 985 } 986 987 /** 988 * Handle {@link DocumentListener} events for the {@link JTextField} 989 * created by {@link #getRelativeTimesChooser()}. 990 * 991 * @param event Event to handle. Cannot be {@code null}. 992 */ 993 private void handleRelativeTimeChange(DocumentEvent event) { 994 int len = event.getDocument().getLength(); 995 try { 996 String text = event.getDocument().getText(0, len); 997 validateRelativeTimeInput(text); 998 } catch (BadLocationException ex) { 999 logger.warn("Could not get contents of text field!", ex); 1000 } 1001 } 1002 1003 /** 1004 * Get the relative time indices 1005 * 1006 * @return an array of indices 1007 */ 1008 @Override public int[] getRelativeTimeIndices() { 1009 int[] indices = new int[relativeTimes]; 1010 for (int i = 0; i < indices.length; i++) { 1011 indices[i] = i; 1012 } 1013 return indices; 1014 } 1015 1016 /** 1017 * Make the UI for this selector. 1018 * 1019 * Thank you NetBeans for helping with the layout! 1020 * 1021 * @return The GUI. 1022 */ 1023 public JComponent doMakeContents() { 1024 JPanel outerPanel = new JPanel(); 1025 1026 JLabel serverLabel = McVGuiUtils.makeLabelRight("Catalog:"); 1027 1028 //Get the list of catalogs but remove the old catalog.xml entry 1029 urlListHandler = getPreferenceList(PREF_TDSRADARSERVER); 1030 1031 ActionListener catListListener = ae -> { 1032 if (okToDoUrlListEvents) { 1033 setServer((String)urlBox.getSelectedItem()); 1034 } 1035 }; 1036 1037 urlBox = urlListHandler.createComboBox(GuiUtils.CMD_UPDATE, catListListener, true); 1038 McVGuiUtils.setComponentWidth(urlBox, Width.DOUBLEDOUBLE); 1039 1040 // productComboBox gets created a little too tall--set to same height as urlBox 1041 if (productComboBox != null) { 1042 McVGuiUtils.setComponentHeight(productComboBox, urlBox); 1043 } 1044 JButton connectButton = McVGuiUtils.makeImageTextButton(ICON_CONNECT_SMALL, "Connect"); 1045 McVGuiUtils.setComponentWidth(connectButton, Width.DOUBLE); 1046 connectButton.setActionCommand(GuiUtils.CMD_UPDATE); 1047 connectButton.addActionListener(this); 1048 1049 JLabel statusLabelLabel = McVGuiUtils.makeLabelRight(""); 1050 1051 statusLabel.setText("Status"); 1052 McVGuiUtils.setLabelPosition(statusLabel, Position.RIGHT); 1053 McVGuiUtils.setComponentColor(statusLabel, TextColor.STATUS); 1054 1055 JButton helpButton = McVGuiUtils.makeImageButton(ICON_HELP, "Show help"); 1056 helpButton.setActionCommand(GuiUtils.CMD_HELP); 1057 helpButton.addActionListener(this); 1058 1059 JButton refreshButton = McVGuiUtils.makeImageButton(ICON_REFRESH, "Refresh"); 1060 refreshButton.setActionCommand(GuiUtils.CMD_UPDATE); 1061 refreshButton.addActionListener(this); 1062 1063 McVGuiUtils.setComponentWidth(loadButton, Width.DOUBLE); 1064 1065 GroupLayout layout = new GroupLayout(outerPanel); 1066 outerPanel.setLayout(layout); 1067 layout.setHorizontalGroup( 1068 layout.createParallelGroup(LEADING) 1069 .addGroup(TRAILING, layout.createSequentialGroup() 1070 .addGroup(layout.createParallelGroup(TRAILING) 1071 .addGroup(layout.createSequentialGroup() 1072 .addContainerGap() 1073 .addComponent(helpButton) 1074 .addGap(GAP_RELATED) 1075 .addComponent(refreshButton) 1076 .addGap(GAP_RELATED) 1077 .addComponent(cancelButton) 1078 .addPreferredGap(RELATED) 1079 .addComponent(loadButton)) 1080 .addGroup(LEADING, layout.createSequentialGroup() 1081 .addContainerGap() 1082 .addGroup(layout.createParallelGroup(LEADING) 1083 .addComponent(innerPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE) 1084 .addGroup(layout.createSequentialGroup() 1085 .addComponent(serverLabel) 1086 .addGap(GAP_RELATED) 1087 .addComponent(urlBox, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE) 1088 .addGap(GAP_UNRELATED) 1089 .addComponent(connectButton)) 1090 .addGroup(layout.createSequentialGroup() 1091 .addComponent(statusLabelLabel) 1092 .addGap(GAP_RELATED) 1093 .addComponent(statusLabel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE))))) 1094 .addContainerGap()) 1095 ); 1096 layout.setVerticalGroup( 1097 layout.createParallelGroup(LEADING) 1098 .addGroup(layout.createSequentialGroup() 1099 .addContainerGap() 1100 .addGroup(layout.createParallelGroup(BASELINE) 1101 .addComponent(serverLabel) 1102 .addComponent(urlBox) 1103 .addComponent(connectButton)) 1104 .addPreferredGap(UNRELATED) 1105 .addComponent(innerPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE) 1106 .addPreferredGap(UNRELATED) 1107 .addGroup(layout.createParallelGroup(BASELINE) 1108 .addComponent(statusLabelLabel) 1109 .addComponent(statusLabel)) 1110 .addPreferredGap(UNRELATED) 1111 .addGroup(layout.createParallelGroup(BASELINE) 1112 .addComponent(loadButton) 1113 .addComponent(cancelButton) 1114 .addComponent(refreshButton) 1115 .addComponent(helpButton)) 1116 .addContainerGap()) 1117 ); 1118 return outerPanel; 1119 } 1120}