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 */ 028package edu.wisc.ssec.mcidasv.data; 029 030import static javax.swing.BorderFactory.createTitledBorder; 031import static javax.swing.GroupLayout.Alignment.BASELINE; 032import static javax.swing.GroupLayout.Alignment.LEADING; 033import static javax.swing.GroupLayout.Alignment.TRAILING; 034import static javax.swing.LayoutStyle.ComponentPlacement.RELATED; 035 036import java.awt.Container; 037import java.awt.Dimension; 038import java.awt.EventQueue; 039import java.awt.Font; 040import java.awt.event.WindowEvent; 041import java.awt.event.WindowAdapter; 042import java.io.ByteArrayInputStream; 043import java.io.ByteArrayOutputStream; 044import java.io.File; 045import java.io.IOException; 046 047import java.util.ArrayList; 048import java.util.List; 049import java.util.stream.Collectors; 050 051import javax.swing.BoxLayout; 052import javax.swing.GroupLayout; 053import javax.swing.JButton; 054import javax.swing.JComboBox; 055import javax.swing.JDialog; 056import javax.swing.JEditorPane; 057import javax.swing.JFrame; 058import javax.swing.JLabel; 059import javax.swing.JOptionPane; 060import javax.swing.JPanel; 061import javax.swing.JRadioButton; 062import javax.swing.JScrollPane; 063import javax.swing.JSeparator; 064import javax.swing.JTextField; 065import javax.swing.JTextPane; 066import javax.swing.JToolBar; 067import javax.swing.ScrollPaneConstants; 068import javax.swing.UIManager; 069import javax.swing.WindowConstants; 070import javax.swing.border.EmptyBorder; 071import javax.swing.event.HyperlinkEvent; 072import javax.swing.text.PlainDocument; 073 074import org.slf4j.Logger; 075import org.slf4j.LoggerFactory; 076 077import net.miginfocom.swing.MigLayout; 078 079import edu.wisc.ssec.mcidasv.util.McVGuiUtils; 080import edu.wisc.ssec.mcidasv.util.McVGuiUtils.IconPanel; 081import edu.wisc.ssec.mcidasv.util.McVGuiUtils.Prefer; 082import edu.wisc.ssec.mcidasv.util.McVGuiUtils.Width; 083import edu.wisc.ssec.mcidasv.util.WebBrowser; 084import edu.wisc.ssec.mcidasv.Constants; 085 086import ucar.ma2.Array; 087import ucar.nc2.Variable; 088import ucar.nc2.dataset.NetcdfDataset; 089import ucar.nc2.ncml.NcMLReader; 090import ucar.nc2.dt.grid.GridDataset; 091 092import ucar.unidata.util.FileManager; 093import ucar.unidata.util.GuiUtils; 094import ucar.unidata.util.IOUtil; 095import ucar.unidata.util.LayoutUtil; 096 097import ucar.unidata.data.DataSourceDescriptor; 098import ucar.unidata.data.grid.GeoGridDataSource; 099import ucar.unidata.idv.IntegratedDataViewer; 100 101import visad.ConstantMap; 102import visad.Display; 103import visad.FlatField; 104import visad.python.JPythonMethods; 105import visad.ss.BasicSSCell; 106import visad.ss.FancySSCell; 107 108/** 109 * GUI widget that allows users to attempt to specify an {@literal "NCML"} 110 * if a given NetCDF is not CF-compliant. 111 */ 112public class BadNetCDFWidget implements Constants { 113 114 private static final Logger logger = 115 LoggerFactory.getLogger(BadNetCDFWidget.class); 116 117 private IntegratedDataViewer idv; 118 119 private NetcdfDataset ncFile; 120 private List<Variable> varList; 121 private List<String> varNames; 122 123 // For NcML Editor 124 private JEditorPane NcMLeditor; 125 126 // For variable display 127 BasicSSCell display; 128 ConstantMap[] cmaps; 129 130 // For nav specification 131 private JRadioButton radioLatLonVars = new JRadioButton("Variables", true); 132 private JRadioButton radioLatLonBounds = new JRadioButton("Bounds", false); 133 134 private JComboBox refComboBox = new JComboBox(); 135 private JComboBox latComboBox = new JComboBox(); 136 private JComboBox lonComboBox = new JComboBox(); 137 138 private JPanel panelLatLonVars = new JPanel(); 139 private JPanel panelLatLonBounds = new JPanel(); 140 141 private JTextField textLatUL = new JTextField(); 142 private JTextField textLonUL = new JTextField(); 143 private JTextField textLatLR = new JTextField(); 144 private JTextField textLonLR = new JTextField(); 145 146 /** 147 * Handles problems from {@code openDataset}. 148 * 149 * @param ncFile NetCDF that caused a problem. Cannot be {@code null}. 150 * @param idv Reference to the IDV. Cannot be {@code null}. 151 */ 152 public BadNetCDFWidget(NetcdfDataset ncFile, IntegratedDataViewer idv) { 153 this.idv = idv; 154 this.ncFile = ncFile; 155 varList = ncFile.getVariables(); 156 varNames = new ArrayList<>(varList.size()); 157 varNames.addAll(varList.stream().map(Variable::getFullName).collect(Collectors.toList())); 158 } 159 160 /** 161 * Passes through any exception from openDataset - this function 162 * doesn't provide an IDV and should only be used for testing. 163 * 164 * (Some functionality using the rest of the IDV won't work.) 165 * 166 * @param filepath Path to a NetCDF file. 167 * 168 * @throws IOException if there was a problem. 169 */ 170 public BadNetCDFWidget(String filepath) throws IOException { 171 this(NetcdfDataset.openDataset(filepath), null); 172 } 173 174 /** 175 * Displays our "main menu" of choices to fix the given file. Everything 176 * else needed can get called from here. 177 */ 178 public void showChoices() { 179 EventQueue.invokeLater(() -> { 180 try { 181 BadNetCDFDialog dialog = new BadNetCDFDialog(); 182 dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); 183 dialog.setVisible(true); 184 dialog.toFront(); 185 } catch (Exception e) { 186 logger.error("Could not show choices", e); 187 } 188 }); 189 } 190 191 /** 192 * Creates an editor for NcML and displays it in a window. This includes 193 * buttons for saving just the NcML and the full NetCDF file with the 194 * changes made. 195 */ 196 private void showNcMLEditor() { 197 NcMLeditor = new JEditorPane(); 198 199 // We use this to store the actual ncml - 10000 is just the number 200 // toolsUI used 201 ByteArrayOutputStream bos = new ByteArrayOutputStream(10000); 202 try { 203 ncFile.writeNcML(bos, null); 204 NcMLeditor.setText(bos.toString()); 205 NcMLeditor.setCaretPosition(0); 206 } catch (IOException ioe) { 207 logger.error("Could not write ncml", ioe); 208 // DataSourceImpl - doesn't work if we're not a data source 209 // setInError(true, false, ""); 210 return; 211 } 212 213 NcMLeditor.setEditable(true); 214 215 // Set the font style. 216 NcMLeditor.setFont(new Font("Courier", Font.PLAIN, 12)); 217 218 // Set the tab size 219 NcMLeditor.getDocument().putProperty(PlainDocument.tabSizeAttribute, 2); 220 221 // Button to save NcML as text, 222 // popup allows them to specify where. 223 JButton saveNcMLBtn = new JButton("Save NcML as text"); 224 saveNcMLBtn.addActionListener(e -> { 225 // Begin with getting the filename we want to write to. 226 String ncLocation = ncFile.getLocation(); 227 228 if (ncLocation == null) { 229 ncLocation = "test"; 230 } 231 int pos = ncLocation.lastIndexOf("."); 232 if (pos > 0) { 233 ncLocation = ncLocation.substring(0, pos); 234 } 235 String filename = FileManager.getWriteFile(ncLocation + ".ncml"); 236 if (filename == null) { 237 return; 238 } 239 240 // Once we have that, we can actually write to the file! 241 try { 242 IOUtil.writeFile(new File(filename), NcMLeditor.getText()); 243 } catch (Exception exc) { 244 logger.error("Could not write to '"+filename+'\'', exc); 245 } 246 }); 247 248 // Button to merge the NcML with NetCDF 249 // a'la ToolsUI and write it back out as NetCDF3. 250 JButton saveNetCDFBtn = new JButton("Merge and save NetCDF"); 251 saveNetCDFBtn.addActionListener(e -> { 252 // Begin with getting the filename we want to write to. 253 String ncLocation = ncFile.getLocation(); 254 255 if (ncLocation == null) { 256 ncLocation = "test"; 257 } 258 int pos = ncLocation.lastIndexOf("."); 259 if (pos > 0) { 260 ncLocation = ncLocation.substring(0, pos); 261 } 262 String filename = FileManager.getWriteFile(ncLocation + ".nc"); 263 if (filename == null) { 264 return; 265 } 266 267 // Once we have that, we can actually write to the file! 268 try { 269 ByteArrayInputStream bis = 270 new ByteArrayInputStream(NcMLeditor.getText().getBytes()); 271 NcMLReader.writeNcMLToFile(bis, filename); 272 } catch (Exception exc) { 273 logger.error("Could not write to '"+filename+'\'', exc); 274 } 275 }); 276 277 // Button to load this data into McV from NcML 278 JButton sendToMcVBtn = new JButton("Attempt to load with this NcML"); 279 sendToMcVBtn.addActionListener(ae -> { 280 // TODO: save the current NcML into the NetcdfDataSource 281 createIDVdisplay(); 282 }); 283 284 JToolBar toolbar = new JToolBar("NcML Editor Controls"); 285 286 toolbar.add(saveNcMLBtn); 287 toolbar.add(saveNetCDFBtn); 288 toolbar.add(sendToMcVBtn); 289 290 JScrollPane scrollPane = 291 new JScrollPane(NcMLeditor, 292 ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, 293 ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); 294 295 // TODO: PREFERRED SIZE? 296 scrollPane.setPreferredSize(new Dimension(600, 600)); 297 298 JPanel panel = LayoutUtil.topCenter(toolbar, scrollPane); 299 JFrame editorWindow = 300 GuiUtils.makeWindow("NcML Editor", LayoutUtil.inset(panel, 10), 0, 0); 301 editorWindow.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); 302 editorWindow.setVisible(true); 303 editorWindow.toFront(); 304 } 305 306 /** 307 * Takes our ncFile and puts it back into IDV (McV). 308 */ 309 private void createIDVdisplay() { 310 311 // Make a NetcdfDataset from our NcML 312 ByteArrayInputStream bis = 313 new ByteArrayInputStream(NcMLeditor.getText().getBytes()); 314 315 try { 316 ncFile = NcMLReader.readNcML(bis, null); 317 } catch (IOException e1) { 318 logger.error("Could not read ncml", e1); 319 return; 320 } 321 322 // Now try to turn that NetcdfDataset into a legitimate DataSource! 323 GridDataset gd; 324 325 try { 326 gd = new GridDataset(ncFile); 327 } catch (IOException e) { 328 logger.error("could not create grid dataset from netcdf file", e); 329 return; 330 } 331 332 ncFile.getLocation(); 333 DataSourceDescriptor dsd = new DataSourceDescriptor(); 334 dsd.setLabel("NcML DS Label"); 335 GeoGridDataSource ggds = 336 new GeoGridDataSource(dsd, gd, "NcML Data Source", ncFile.getLocation()); 337 ggds.initAfterCreation(); 338 idv.getDataManager().addDataSource(ggds); 339 } 340 341 /** 342 * Shows a window that gives a choice of variables. 343 */ 344 private void showVarPicker() { 345 final JComboBox<String> varDD = new JComboBox<>(); 346 GuiUtils.setListData(varDD, varNames); 347 348 varDD.addActionListener(ae -> { 349 JComboBox<String> cb = (JComboBox<String>)ae.getSource(); 350 Variable plotVar = varList.get(cb.getSelectedIndex()); 351 String varName = (String) cb.getSelectedItem(); 352 353 float [] varVals; 354 try { 355 // TODO: Is there a better way to convert this? 356 // Is there another function like reshape? 357 Array varArray = plotVar.read(); 358 varVals = (float[])varArray.get1DJavaArray(float.class); 359 } catch (IOException IOexe) { 360 logger.error("error while reading from variable '"+plotVar+'\'', IOexe); 361 return; 362 } 363 364 int size = plotVar.getDimensions().size(); 365 if( size != 2) { 366 JOptionPane.showMessageDialog(null, 367 ("<html>Variables must have 2 dimensions to be displayed here.<br><br>\"" + varName + "\" has " + size + ".</html>"), 368 "Invalid Dimensions", 369 JOptionPane.ERROR_MESSAGE); 370 return; 371 } 372 373 int xdim = plotVar.getDimensions().get(0).getLength(); 374 int ydim = plotVar.getDimensions().get(1).getLength(); 375 376 float[][] var2D = reshape(varVals, ydim, xdim); 377 378 //JPythonMethods.plot(varVals); 379 //JPythonMethods.plot(var2D); 380 381 try { 382 FlatField varField = JPythonMethods.field(var2D); 383 384 ConstantMap[] cmaps1 = { 385 new ConstantMap(1.0, Display.Red), 386 new ConstantMap(1.0, Display.Green), 387 new ConstantMap(1.0, Display.Blue) 388 }; 389 390 // Clear out the display or we get some weird stuff going on. 391 display.clearCell(); 392 display.clearMaps(); 393 display.clearDisplay(); 394 395 display.addData(varField, cmaps1); 396 } catch (Exception exe) { 397 logger.error("problem encountered", exe); 398 } 399 }); 400 401 //BasicSSCell display = new FancySSCell("Variable!"); 402 //display.setDimension(BasicSSCell.JAVA3D_3D); 403 404 try { 405 // Heavily borrowed from VISAD's JPythonMethods 406 display = new FancySSCell("Variable Viewer"); 407 display.setDimension(BasicSSCell.JAVA3D_3D); 408 display.setPreferredSize(new Dimension(256, 256)); 409 JFrame frame = new JFrame("Variable Viewer"); 410 JPanel pane = new JPanel(); 411 pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS)); 412 frame.setContentPane(pane); 413 pane.add(varDD); 414 pane.add(display); 415 JButton controls = new JButton("Controls"); 416 JPanel buttons = new JPanel(); 417 buttons.setLayout(new BoxLayout(buttons, BoxLayout.X_AXIS)); 418 buttons.add(controls); 419 pane.add(buttons); 420 final FancySSCell fdisp = (FancySSCell) display; 421 fdisp.setAutoShowControls(false); 422 423 controls.addActionListener(e -> fdisp.showWidgetFrame()); 424 425 frame.pack(); 426 frame.setVisible(true); 427 frame.toFront(); 428 } catch (Exception exe) { 429 logger.error("problem displaying", exe); 430 } 431 } 432 433 /** 434 * Reshape a 1D float array into a 2D float array. 435 * 436 * @param arr Array to reshape. 437 * @param m First dimension. 438 * @param n Second dimension. 439 * 440 * @return {@code arr} reshaped into a new 2D array. 441 */ 442 private static float[][] reshape(float[] arr, int m, int n) { 443 float[][] newArr = new float[m][n]; 444 int index = 0; 445 for (int i = 0; i < n; i++) { 446 for (int j = 0; j< m; j++) { 447 newArr[j][i] = arr[index++]; 448 } 449 } 450 return newArr; 451 } 452 453 /** 454 * Shows a window that gives the opportunity to either define coordinate 455 * variables or specify corner points. 456 * 457 * Borrowed heavily from FlatFileChooser's makeNavigationPanel for style. 458 */ 459 private void showNavChooser() { 460 JPanel midPanel = new JPanel(); 461 midPanel.setBorder(createTitledBorder("Navigation")); 462 463 GuiUtils.setListData(refComboBox, varNames); 464 GuiUtils.setListData(latComboBox, varNames); 465 GuiUtils.setListData(lonComboBox, varNames); 466 467 McVGuiUtils.setComponentWidth(latComboBox, Width.QUADRUPLE); 468 McVGuiUtils.setComponentWidth(lonComboBox, Width.QUADRUPLE); 469 McVGuiUtils.setComponentWidth(refComboBox, Width.QUADRUPLE); 470 471 panelLatLonVars = 472 McVGuiUtils.topBottom( 473 McVGuiUtils.makeLabeledComponent("Latitude:",latComboBox), 474 McVGuiUtils.makeLabeledComponent("Longitude:",lonComboBox), 475 Prefer.NEITHER); 476 477 GuiUtils.buttonGroup(radioLatLonVars, radioLatLonBounds); 478 479 // Images to make the bounds more clear 480 IconPanel urPanel = 481 new IconPanel("/edu/wisc/ssec/mcidasv/images/upper_right.gif"); 482 IconPanel llPanel = 483 new IconPanel("/edu/wisc/ssec/mcidasv/images/lower_left.gif"); 484 485 McVGuiUtils.setComponentWidth(textLatUL); 486 McVGuiUtils.setComponentWidth(textLonUL); 487 McVGuiUtils.setComponentWidth(textLatLR); 488 McVGuiUtils.setComponentWidth(textLonLR); 489 panelLatLonBounds = McVGuiUtils.topBottom( 490 McVGuiUtils.makeLabeledComponent("UL Lat/Lon:", LayoutUtil.leftRight(LayoutUtil.hbox(textLatUL, textLonUL), urPanel)), 491 McVGuiUtils.makeLabeledComponent("LR Lat/Lon:", LayoutUtil.leftRight(llPanel, LayoutUtil.hbox(textLatLR, textLonLR))), 492 Prefer.NEITHER); 493 494 panelLatLonBounds = 495 McVGuiUtils.topBottom( 496 panelLatLonBounds, 497 McVGuiUtils.makeLabeledComponent("Reference:", refComboBox), 498 Prefer.NEITHER); 499 500 McVGuiUtils.setComponentWidth(radioLatLonVars); 501 McVGuiUtils.setComponentWidth(radioLatLonBounds); 502 503 // Add a bit of a buffer to both 504 panelLatLonVars = LayoutUtil.inset(panelLatLonVars, 5); 505 panelLatLonBounds = LayoutUtil.inset(panelLatLonBounds, 5); 506 507 GroupLayout layout = new GroupLayout(midPanel); 508 midPanel.setLayout(layout); 509 layout.setHorizontalGroup( 510 layout.createParallelGroup(LEADING) 511 .addGroup(layout.createSequentialGroup() 512 .addContainerGap() 513 .addGroup(layout.createParallelGroup(LEADING) 514 .addGroup(layout.createSequentialGroup() 515 .addComponent(radioLatLonVars) 516 .addGap(GAP_RELATED) 517 .addComponent(panelLatLonVars)) 518 .addGroup(layout.createSequentialGroup() 519 .addComponent(radioLatLonBounds) 520 .addGap(GAP_RELATED) 521 .addComponent(panelLatLonBounds))) 522 .addContainerGap()) 523 ); 524 layout.setVerticalGroup( 525 layout.createParallelGroup(LEADING) 526 .addGroup(TRAILING, layout.createSequentialGroup() 527 .addContainerGap() 528 .addGroup(layout.createParallelGroup(BASELINE) 529 .addComponent(radioLatLonVars) 530 .addComponent(panelLatLonVars)) 531 .addPreferredGap(RELATED) 532 .addGroup(layout.createParallelGroup(BASELINE) 533 .addComponent(radioLatLonBounds) 534 .addComponent(panelLatLonBounds)) 535 .addPreferredGap(RELATED) 536 .addContainerGap()) 537 ); 538 539 540 541 radioLatLonVars.addActionListener(e -> checkSetLatLon()); 542 543 radioLatLonBounds.addActionListener(e -> checkSetLatLon()); 544 545 JButton goBtn = new JButton("Go!"); 546 goBtn.addActionListener(ae -> { 547 boolean isVar = radioLatLonVars.isSelected(); 548 if (isVar) { 549 navVarAction(); 550 } else { 551 navCornersAction(); 552 } 553 }); 554 555 JPanel wholePanel = 556 McVGuiUtils.topBottom(midPanel, goBtn, Prefer.NEITHER); 557 558 JFrame myWindow = 559 GuiUtils.makeWindow( 560 "Pick Your Navigation!", 561 LayoutUtil.inset(wholePanel, 10), 562 0, 0); 563 564 checkSetLatLon(); 565 myWindow.setVisible(true); 566 myWindow.toFront(); 567 } 568 569 570 /** 571 * Enable or disable widgets for navigation. 572 */ 573 private void checkSetLatLon() { 574 boolean isVar = radioLatLonVars.isSelected(); 575 GuiUtils.enableTree(panelLatLonVars, isVar); 576 GuiUtils.enableTree(panelLatLonBounds, !isVar); 577 } 578 579 /** 580 * One of the two workhorses of our nav chooser, it alters the chosen 581 * (existing) variables so they can be used as lat/lon pairs. 582 */ 583 private void navVarAction() { 584 } 585 586 /** 587 * One of the two workhorses of our nav chooser, it creates new 588 * variables for lat/lon based on the specified cornerpoints and 589 * reference variable (for dimensions). 590 */ 591 private void navCornersAction() { 592 } 593 594 public class BadNetCDFDialog extends JDialog { 595 596 /** 597 * Create the dialog. 598 */ 599 public BadNetCDFDialog() { 600 setTitle("Non-Compliant NetCDF Tool"); 601 setMinimumSize(new Dimension(705, 320)); 602 setBounds(100, 100, 705, 320); 603 Container contentPane = getContentPane(); 604 605 JLabel headerLabel = 606 new JLabel("McIDAS-V is unable to read your file."); 607 headerLabel.setFont(UIManager.getFont("OptionPane.font")); 608 headerLabel.setBorder(new EmptyBorder(0, 0, 4, 0)); 609 610 JTextPane messageTextPane = new JTextPane(); 611 Font textPaneFont = UIManager.getFont("TextPane.font"); 612 String fontCss = 613 String.format("style=\"font-family: '%s'; font-size: %d;\"", textPaneFont.getFamily(), textPaneFont.getSize()); 614 messageTextPane.setBackground(UIManager.getColor("Label.background")); 615 messageTextPane.setContentType("text/html"); 616 messageTextPane.setDragEnabled(false); 617 messageTextPane.setText("<html>\n<body "+ 618 fontCss + 619 ">To verify if your file is CF-compliant, you can run your file through an online compliance checker " + 620 "(<a href=\"http://cfconventions.org/compliance-checker.html\">example CF-compliance utility</a>). " + 621 "<br/><br/> \n\nIf the checker indicates that your file is not compliant you can attempt to fix it using " + 622 "the NcML Editor provided in this window.<br/><br/>\n\nIn a future release of McIDAS-V, this interface will " + 623 "present you with choices for the variables necessary for McIDAS-V to display your data.<br/></font></body></html>"); 624 messageTextPane.setEditable(false); 625 messageTextPane.addHyperlinkListener(e -> { 626 HyperlinkEvent.EventType type = e.getEventType(); 627 if (HyperlinkEvent.EventType.ACTIVATED.equals(type)) { 628 String url = (e.getURL() == null) 629 ? e.getDescription() 630 : e.getURL().toString(); 631 WebBrowser.browse(url); 632 } 633 }); 634 635 JSeparator separator = new JSeparator(); 636 // seems pretty dumb to have to do this sizing business in order 637 // to get the separator to appear, right? 638 // check out the following: 639 // http://docs.oracle.com/javase/tutorial/uiswing/components/separator.html 640 // 641 // "Separators have almost no API and are extremely easy to use as 642 // long as you keep one thing in mind: In most implementations, 643 // a vertical separator has a preferred height of 0, and a 644 // horizontal separator has a preferred width of 0. This means a 645 // separator is not visible unless you either set its preferred 646 // size or put it in under the control of a layout manager such as 647 // BorderLayout or BoxLayout that stretches it to fill its 648 // available display area." 649 // WHO ON EARTH DECIDED THAT WAS SENSIBLE DEFAULT BEHAVIOR FOR A 650 // SEPARATOR WIDGET!? 651 separator.setMinimumSize(new Dimension(1, 12)); 652 separator.setPreferredSize(new Dimension(1, 12)); 653 654 JLabel editorLabel = 655 new JLabel("Open the file in the NcML editor:"); 656 657 JButton editorButton = new JButton("NcML Editor"); 658 editorButton.addActionListener(e -> showNcMLEditor()); 659 660 JLabel viewLabel = 661 new JLabel("I just want to view one of the variables:"); 662 663 JButton viewButton = new JButton("View Variable"); 664 viewButton.addActionListener(e -> showVarPicker()); 665 666 JLabel noncompliantLabel = 667 new JLabel("I have navigation variables, they just aren't CF-compliant: (FEATURE INCOMPLETE)"); 668 669 JButton noncompliantButton = new JButton("Choose Nav"); 670 noncompliantButton.addActionListener(e -> showNavChooser()); 671 this.addWindowListener(new WindowAdapter() { 672 @Override public void windowClosing(WindowEvent e) { 673 BadNetCDFDialog.this.dispose(); 674 } 675 }); 676 677 contentPane.setLayout(new MigLayout( 678 "", 679 "[grow][]", 680 "[][grow][][][][][][]")); 681 contentPane.add(headerLabel, "spanx 2, alignx left, aligny top, wrap"); 682 contentPane.add(messageTextPane, "spanx 2, grow, wrap"); 683 contentPane.add(separator, "spanx 2, growx, aligny top, wrap"); 684 contentPane.add(editorLabel, "alignx left, aligny baseline"); 685 contentPane.add(editorButton, "growx, aligny baseline, wrap"); 686 contentPane.add(viewLabel, "alignx left, aligny baseline"); 687 contentPane.add(viewButton, "growx, aligny baseline, wrap"); 688 contentPane.add(noncompliantLabel, "alignx left, aligny baseline"); 689 contentPane.add(noncompliantButton, "growx, aligny baseline, wrap"); 690 691 } 692 } 693 694 /** 695 * Tester function to pick a file and send it through the paces. 696 * 697 * @param args Incoming arguments. 698 */ 699 public static void main(String... args) { 700 String testfile = FileManager.getReadFile(); 701 BadNetCDFWidget bfReader; 702 try { 703 bfReader = new BadNetCDFWidget(testfile); 704 bfReader.showChoices(); 705 //bfReader.showNavChooser(); 706 } catch (Exception exe) { 707 logger.error("Could not read '"+testfile+'\'', exe); 708 } 709 } 710}