001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2025 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 https://www.gnu.org/licenses/. 027 */ 028 029package edu.wisc.ssec.mcidasv.control; 030 031import java.awt.BorderLayout; 032import java.awt.Color; 033import java.awt.Component; 034import java.awt.Container; 035import java.awt.Dimension; 036import java.awt.FlowLayout; 037import java.awt.event.ActionEvent; 038import java.awt.event.ActionListener; 039import java.awt.event.MouseEvent; 040import java.rmi.RemoteException; 041import java.util.ArrayList; 042import java.util.Hashtable; 043import java.util.List; 044 045import javax.swing.JButton; 046import javax.swing.JComponent; 047import javax.swing.JFrame; 048import javax.swing.JLabel; 049import javax.swing.JMenu; 050import javax.swing.JMenuItem; 051import javax.swing.JPanel; 052import javax.swing.JPopupMenu; 053import javax.swing.JTabbedPane; 054import javax.swing.JTextField; 055import javax.swing.event.ChangeEvent; 056import javax.swing.event.ChangeListener; 057 058import org.slf4j.Logger; 059import org.slf4j.LoggerFactory; 060import org.w3c.dom.Document; 061import org.w3c.dom.Element; 062import org.w3c.dom.Node; 063 064import ucar.unidata.data.DataChoice; 065import ucar.unidata.data.DataSelection; 066import ucar.unidata.data.DataSourceImpl; 067import ucar.unidata.data.DerivedDataChoice; 068import ucar.unidata.data.imagery.AddeImageDescriptor; 069import ucar.unidata.data.imagery.BandInfo; 070import ucar.unidata.idv.ControlContext; 071import ucar.unidata.idv.IdvResourceManager; 072import ucar.unidata.idv.control.DisplayControlImpl; 073import ucar.unidata.idv.control.McVHistogramWrapper; 074import ucar.unidata.ui.XmlTree; 075import ucar.unidata.util.ColorTable; 076import ucar.unidata.util.GuiUtils; 077import ucar.unidata.util.Range; 078import ucar.unidata.xml.XmlResourceCollection; 079import ucar.unidata.xml.XmlUtil; 080 081import visad.Data; 082import visad.DateTime; 083import visad.FieldImpl; 084import visad.FlatField; 085import visad.VisADException; 086import visad.meteorology.ImageSequenceImpl; 087 088import edu.wisc.ssec.mcidasv.PersistenceManager; 089import edu.wisc.ssec.mcidasv.chooser.ImageParameters; 090import edu.wisc.ssec.mcidasv.data.ComboDataChoice; 091import edu.wisc.ssec.mcidasv.data.adde.AddeImageParameterDataSource; 092 093/** 094 * {@link ucar.unidata.idv.control.ImagePlanViewControl} with some McIDAS-V 095 * specific extensions. Namely parameter sets and support for inverted 096 * parameter defaults. 097 */ 098 099public class ImagePlanViewControl extends ucar.unidata.idv.control.ImagePlanViewControl { 100 101 private static final Logger logger = LoggerFactory.getLogger(ImagePlanViewControl.class); 102 103 private static final String TAG_FOLDER = "folder"; 104 private static final String TAG_DEFAULT = "default"; 105 private static final String ATTR_NAME = "name"; 106 private static final String ATTR_SERVER = "server"; 107 private static final String ATTR_POS = "POS"; 108 private static final String ATTR_DAY = "DAY"; 109 private static final String ATTR_TIME = "TIME"; 110 private static final String ATTR_UNIT = "UNIT"; 111 112 /** Command for connecting */ 113 protected static final String CMD_NEWFOLDER = "cmd.newfolder"; 114 protected static final String CMD_NEWPARASET = "cmd.newparaset"; 115 116 /** save parameter set */ 117 private JFrame saveWindow; 118 119 private static String newFolder; 120 121 private XmlTree xmlTree; 122 123 /** Holds the current save set tree */ 124 private JPanel treePanel; 125 126 /** The user imagedefaults xml root */ 127 private static Element imageDefaultsRoot; 128 129 /** The user imagedefaults xml document */ 130 private static Document imageDefaultsDocument; 131 132 /** Holds the ADDE servers and groups*/ 133 private static XmlResourceCollection imageDefaults; 134 135 private Node lastCat; 136 137 private static Element lastClicked; 138 139 private JButton newFolderBtn; 140 141 private JButton newSetBtn; 142 143 private String newCompName = ""; 144 145 /** Shows the status */ 146 private JLabel statusLabel; 147 148 /** Status bar component */ 149 private JComponent statusComp; 150 151 private JPanel contents; 152 153 private DataSourceImpl dataSource; 154 155 private FlatField image; 156 157 final JTextField minBox = new JTextField(6); 158 final JTextField maxBox = new JTextField(6); 159 160 private float rangeMin; 161 private float rangeMax; 162 163 private float origRangeMin; 164 private float origRangeMax; 165 166 private MyTabbedPane histogramPane; 167 168 public ImagePlanViewControl() { 169 super(); 170 logger.trace("created new imageplanviewcontrol={}", Integer.toHexString(hashCode())); 171 ImagePlanViewControl.imageDefaults = getImageDefaults(); 172 } 173 174 @Override public boolean init(DataChoice dataChoice) 175 throws VisADException, RemoteException 176 { 177 // TJJ Jul 2014 178 // by sharing a property via the active View Manager, we can signal any 179 // preview windows they are part of an in-progress Formula. If so, it 180 // appears we need to use a shared HydraContext so our geographic coverage 181 // subset applies across channels, and so we use full res data. 182 183 Hashtable ht = getIdv().getViewManager().getProperties(); 184 ht.put(RGBCompositeControl.FORMULA_IN_PROGRESS_FLAG, true); 185 boolean result = super.init((DataChoice)this.getDataChoices().get(0)); 186 ht.put(RGBCompositeControl.FORMULA_IN_PROGRESS_FLAG, false); 187 return result; 188 } 189 190 /** 191 * Get the xml resource collection that defines the image default xml 192 * 193 * @return Image defaults resources 194 */ 195 protected XmlResourceCollection getImageDefaults() { 196 XmlResourceCollection ret = null; 197 try { 198 ControlContext controlContext = getControlContext(); 199 if (controlContext != null) { 200 IdvResourceManager irm = controlContext.getResourceManager(); 201 ret = irm.getXmlResources( IdvResourceManager.RSC_IMAGEDEFAULTS); 202 if (ret.hasWritableResource()) { 203 imageDefaultsDocument = 204 ret.getWritableDocument("<imagedefaults></imagedefaults>"); 205 imageDefaultsRoot = 206 ret.getWritableRoot("<imagedefaults></imagedefaults>"); 207 } 208 } 209 } catch (Exception e) { 210 logger.error("problem trying to set up xml document", e); 211 } 212 return ret; 213 } 214 215 /** 216 * Called by doMakeWindow in DisplayControlImpl, which then calls its 217 * doMakeMainButtonPanel(), which makes more buttons. 218 * 219 * @return container of contents 220 */ 221 public Container doMakeContents() { 222 try { 223 JTabbedPane tab = new MyTabbedPane(); 224 tab.add("Settings", 225 GuiUtils.inset(GuiUtils.top(doMakeWidgetComponent()), 5)); 226 227 // MH: just add a dummy component to this tab for now.. 228 // don't init histogram until the tab is clicked. 229 230 // DO NOT DO THIS 231// if (!(this.datachoice instanceof DerivedDataChoice)) { 232// tab.add("Histogram", getHistogramTabComponent()); 233// } 234 235 tab.add("Histogram", new JLabel("Histogram not yet initialized")); 236 237 histogramPane = (MyTabbedPane) tab; 238 return tab; 239 } catch (Exception exc) { 240 logException("doMakeContents", exc); 241 } 242 return null; 243 } 244 245 @Override public void doRemove() throws RemoteException, VisADException { 246 super.doRemove(); 247 } 248 249 /** 250 * Take out the histogram-related stuff that was in doMakeContents and put it 251 * in a standalone method, so we can wait and call it only after the 252 * histogram is actually initialized. 253 */ 254 private void setInitialHistogramRange() { 255 try { 256 org.jfree.data.Range range = histoWrapper.getRange(); 257 rangeMin = (float) range.getLowerBound(); 258 rangeMax = (float) range.getUpperBound(); 259 origRangeMin = rangeMin; 260 origRangeMax = rangeMax; 261 minBox.setText(Integer.toString((int) origRangeMin)); 262 maxBox.setText(Integer.toString((int) origRangeMax)); 263 histoWrapper.setHigh(rangeMax); 264 histoWrapper.setLow(rangeMin); 265 } catch (Exception exc) { 266 logException("setInitialHistogramRange", exc); 267 } 268 } 269 270 protected JComponent getHistogramTabComponent() { 271 List choices = new ArrayList(); 272 if (datachoice == null) { 273 datachoice = getDataChoice(); 274 } 275 choices.add(datachoice); 276 histoWrapper = new McVHistogramWrapper("Histogram", choices, (DisplayControlImpl) this); 277 dataSource = getDataSource(); 278 279 if (dataSource == null) { 280 try { 281 image = (FlatField) ((ComboDataChoice) datachoice).getData(); 282 histoWrapper.loadData(image); 283 } catch (IllegalArgumentException e) { 284 logger.trace("Could not create histogram: nothing to show!", e); 285 } catch (RemoteException | VisADException e) { 286 logger.error("Could not create histogram!", e); 287 } 288 } else { 289 Hashtable props = dataSource.getProperties(); 290 try { 291 DataSelection testSelection = datachoice.getDataSelection(); 292 DataSelection realSelection = getDataSelection(); 293 if (testSelection == null) { 294 datachoice.setDataSelection(realSelection); 295 } 296 ImageSequenceImpl seq = null; 297 if (dataSelection == null) 298 dataSelection = dataSource.getDataSelection(); 299 if (dataSelection == null) { 300 image = (FlatField)dataSource.getData(datachoice, null, props); 301 if (image == null) { 302 image = (FlatField)datachoice.getData(null); 303 } 304 } else { 305 Data data = dataSource.getData(datachoice, null, dataSelection, props); 306 if (data instanceof ImageSequenceImpl) { 307 seq = (ImageSequenceImpl) data; 308 } else if (data instanceof FlatField) { 309 image = (FlatField) data; 310 } else if (data instanceof FieldImpl) { 311 image = (FlatField) ((FieldImpl)data).getSample(0, false); 312 } else { 313 throw new Exception("Histogram must be made from a FlatField"); 314 } 315 } 316 if ((seq != null) && (seq.getImageCount() > 0)) { 317 image = (FlatField)seq.getImage(0); 318 } 319 try { 320 histoWrapper.loadData(image); 321 } catch (IllegalArgumentException e) { 322 logger.trace("Could not create histogram: nothing to show!", e); 323 } 324 } catch (Exception e) { 325 logger.error("attempting to set up histogram", e); 326 } 327 } 328 329 JLabel minLabel = new JLabel("Range Min"); 330 JLabel maxLabel = new JLabel("Range Max"); 331 332 minBox.addActionListener(ae -> { 333 rangeMin = Float.valueOf(minBox.getText().trim()); 334 rangeMax = Float.valueOf(maxBox.getText().trim()); 335 histoWrapper.modifyRange((int) rangeMin, (int) rangeMax); 336 }); 337 maxBox.addActionListener(ae -> { 338 rangeMin = Float.valueOf(minBox.getText().trim()); 339 rangeMax = Float.valueOf(maxBox.getText().trim()); 340 histoWrapper.modifyRange((int) rangeMin, (int) rangeMax); 341 }); 342 343 JComponent histoComp = histoWrapper.doMakeContents(); 344 JButton resetButton = new JButton("Reset"); 345 resetButton.addActionListener(new ActionListener() { 346 public void actionPerformed(ActionEvent ae) { 347 resetColorTable(); 348 } 349 }); 350 JPanel resetPanel = new JPanel(new FlowLayout()); 351 resetPanel.add(minLabel); 352 resetPanel.add(minBox); 353 resetPanel.add(maxLabel); 354 resetPanel.add(maxBox); 355 resetPanel.add(resetButton); 356 return GuiUtils.centerBottom(histoComp, resetPanel); 357 } 358 359 protected void contrastStretch(double low, double high) { 360 ColorTable ct = getColorTable(); 361 if (ct != null) { 362 Range range = new Range(low, high); 363 try { 364 setRange(ct.getName(), range); 365 } catch (Exception e) { 366 logger.error("problem stretching contrast", e); 367 } 368 } 369 } 370 371 @Override public boolean setData(DataChoice dataChoice) throws VisADException, RemoteException { 372 boolean result = super.setData(dataChoice); 373// logger.trace("result: {}, dataChoice: {}", result, dataChoice); 374 return result; 375 } 376 377 @Override public void setRange(final Range newRange) throws RemoteException, VisADException { 378// logger.trace("newRange: {} [avoiding NPE!]", newRange); 379 super.setRange(newRange); 380 if (histoWrapper != null) { 381 histoWrapper.modifyRange(newRange.getMin(), newRange.getMax(), false); 382 } 383 } 384 385 public void resetColorTable() { 386 try { 387 revertToDefaultColorTable(); 388 revertToDefaultRange(); 389 histoWrapper.doReset(); 390 histoWrapper.modifyRange((int) origRangeMin, (int) origRangeMax); 391 minBox.setText(Integer.toString((int) origRangeMin)); 392 maxBox.setText(Integer.toString((int) origRangeMax)); 393 } catch (Exception e) { 394 logger.error("problem resetting color table", e); 395 } 396 } 397 398 protected void getSaveMenuItems(List items, boolean forMenuBar) { 399 super.getSaveMenuItems(items, forMenuBar); 400 401 // DAVEP: Remove the parameter set save options for now... 402// items.add(GuiUtils.makeMenuItem("Save Image Parameter Set (TEST)", this, 403// "popupPersistImageParameters")); 404// 405// items.add(GuiUtils.makeMenuItem("Save Image Parameter Set", this, 406// "popupSaveImageParameters")); 407 408 items.add(GuiUtils.makeMenuItem("Save As Local Data Source", this, 409 "saveDataToLocalDisk")); 410 } 411 412 public void popupPersistImageParameters() { 413 PersistenceManager pm = (PersistenceManager)getIdv().getPersistenceManager(); 414 pm.saveParameterSet("addeimagery", makeParameterValues()); 415 } 416 417 private Hashtable makeParameterValues() { 418 Hashtable parameterValues = new Hashtable(); 419// Document doc = XmlUtil.makeDocument(); 420// Element newChild = doc.createElement(TAG_DEFAULT); 421 422 if (datachoice == null) { 423 datachoice = getDataChoice(); 424 } 425 dataSource = getDataSource(); 426 if (!(dataSource.getClass().isInstance(new AddeImageParameterDataSource()))) { 427 logger.trace("dataSource not a AddeImageParameterDataSource; it is: {}", dataSource.getClass().toString()); 428 return parameterValues; 429 } 430 AddeImageParameterDataSource testDataSource = (AddeImageParameterDataSource)dataSource; 431 List imageList = testDataSource.getDescriptors(datachoice, this.dataSelection); 432 int numImages = imageList.size(); 433 List dateTimes = new ArrayList(); 434 DateTime thisDT = null; 435 if (!(imageList == null)) { 436 AddeImageDescriptor aid = null; 437 for (int imageNo=0; imageNo<numImages; imageNo++) { 438 aid = (AddeImageDescriptor) imageList.get(imageNo); 439 thisDT = aid.getImageTime(); 440 if (!dateTimes.contains(thisDT)) { 441 if (thisDT != null) { 442 dateTimes.add(thisDT); 443 } 444 } 445 } 446 447 // Set the date and time for later reference 448 String dateS = ""; 449 String timeS = ""; 450 if (!dateTimes.isEmpty()) { 451 thisDT = (DateTime)dateTimes.get(0); 452 dateS = thisDT.dateString(); 453 timeS = thisDT.timeString(); 454 if (dateTimes.size() > 1) { 455 for (int img=1; img<dateTimes.size(); img++) { 456 thisDT = (DateTime)dateTimes.get(img); 457 String str = "," + thisDT.dateString(); 458 String newString = dateS + str; 459 dateS = newString; 460 str = "," + thisDT.timeString(); 461 newString = timeS + str; 462 timeS = newString; 463 } 464 } 465 } 466 467 // Set the unit for later reference 468 String unitS = ""; 469 if (!(datachoice.getId() instanceof BandInfo)) { 470 logger.trace("dataChoice ID not a BandInfo; it is: {}", datachoice.getId().getClass().toString()); 471 return parameterValues; 472 } 473 BandInfo bi = (BandInfo)datachoice.getId(); 474 unitS = bi.getPreferredUnit(); 475 476 if (aid != null) { 477 String displayUrl = testDataSource.getDisplaySource(); 478 ImageParameters ip = new ImageParameters(displayUrl); 479 List props = ip.getProperties(); 480 List vals = ip.getValues(); 481 String server = ip.getServer(); 482 parameterValues.put(ATTR_SERVER, server); 483// newChild.setAttribute(ATTR_SERVER, server); 484 int num = props.size(); 485 if (num > 0) { 486 String attr = ""; 487 String val = ""; 488 for (int i=0; i<num; i++) { 489 attr = (String)(props.get(i)); 490 if (attr.equals(ATTR_POS)) { 491 val = Integer.valueOf(numImages - 1).toString(); 492 } else if (attr.equals(ATTR_DAY)) { 493 val = dateS; 494 } else if (attr.equals(ATTR_TIME)) { 495 val = timeS; 496 } else if (attr.equals(ATTR_UNIT)) { 497 val = unitS; 498 } else { 499 val = (String)(vals.get(i)); 500 } 501 parameterValues.put(attr, val); 502 } 503 } 504 } 505 } 506 return parameterValues; 507 } 508 509 public void saveDataToLocalDisk() { 510 getDataSource().saveDataToLocalDisk(); 511 } 512 513 public void popupSaveImageParameters() { 514 if (saveWindow == null) { 515 showSaveDialog(); 516 return; 517 } 518 saveWindow.setVisible(true); 519 GuiUtils.toFront(saveWindow); 520 } 521 522 private void showSaveDialog() { 523 if (saveWindow == null) { 524 saveWindow = GuiUtils.createFrame("Save Image Parameter Set"); 525 } 526 if (statusComp == null) { 527 statusLabel = new JLabel(); 528 statusComp = GuiUtils.inset(statusLabel, 2); 529 statusComp.setBackground(new Color(255, 255, 204)); 530 statusLabel.setOpaque(true); 531 statusLabel.setBackground(new Color(255, 255, 204)); 532 } 533 JPanel statusPanel = GuiUtils.inset(GuiUtils.top( GuiUtils.vbox(new JLabel(" "), 534 GuiUtils.hbox(GuiUtils.rLabel("Status: "), statusComp), 535 new JLabel(" "))), 6); 536 JPanel sPanel = GuiUtils.topCenter(statusPanel, GuiUtils.filler()); 537 538 List newComps = new ArrayList(); 539 final JTextField newName = new JTextField(20); 540 newName.addActionListener(new ActionListener() { 541 public void actionPerformed(ActionEvent ae) { 542 setStatus("Click New Folder or New ParameterSet button"); 543 newCompName = newName.getText().trim(); 544 } 545 }); 546 newComps.add(newName); 547 newComps.add(GuiUtils.filler()); 548 newFolderBtn = new JButton("New Folder"); 549 newFolderBtn.addActionListener(new ActionListener() { 550 public void actionPerformed(ActionEvent ae) { 551 newFolder = newName.getText().trim(); 552 if (newFolder.length() == 0) { 553 newComponentError("folder"); 554 return; 555 } 556 Element exists = XmlUtil.findElement(imageDefaultsRoot, "folder", ATTR_NAME, newFolder); 557 if (!(exists == null)) { 558 if (!GuiUtils.askYesNo("Verify Replace Folder", 559 "Do you want to replace the folder " + 560 "\"" + newFolder + "\"?" + 561 "\nNOTE: All parameter sets it contains will be deleted.")) return; 562 imageDefaultsRoot.removeChild(exists); 563 } 564 newName.setText(""); 565 Node newEle = makeNewFolder(); 566 makeXmlTree(); 567 xmlTree.selectElement((Element)newEle); 568 lastCat = newEle; 569 lastClicked = null; 570 newSetBtn.setEnabled(true); 571 setStatus("Please enter a name for the new parameter set"); 572 } 573 }); 574 newComps.add(newFolderBtn); 575 newComps.add(GuiUtils.filler()); 576 newName.setEnabled(true); 577 newFolderBtn.setEnabled(true); 578 newSetBtn = new JButton("New Parameter Set"); 579 newSetBtn.setActionCommand(CMD_NEWPARASET); 580 newSetBtn.addActionListener(new ActionListener() { 581 public void actionPerformed(ActionEvent ae) { 582 newCompName = newName.getText().trim(); 583 if (newCompName.length() == 0) { 584 newComponentError("parameter set"); 585 return; 586 } 587 newName.setText(""); 588 Element newEle = saveParameterSet(); 589 if (newEle == null) return; 590 xmlTree.selectElement(newEle); 591 lastClicked = newEle; 592 } 593 }); 594 newComps.add(newSetBtn); 595 newSetBtn.setEnabled(false); 596 597 JPanel newPanel = GuiUtils.top(GuiUtils.left(GuiUtils.hbox(newComps))); 598 JPanel topPanel = GuiUtils.topCenter(sPanel, newPanel); 599 600 treePanel = new JPanel(); 601 treePanel.setLayout(new BorderLayout()); 602 makeXmlTree(); 603 ActionListener listener = new ActionListener() { 604 public void actionPerformed(ActionEvent event) { 605 String cmd = event.getActionCommand(); 606 if (cmd.equals(GuiUtils.CMD_CANCEL)) { 607 if (lastClicked != null) { 608 removeNode(lastClicked); 609 lastClicked = null; 610 } 611 saveWindow.setVisible(false); 612 saveWindow = null; 613 } else { 614 saveWindow.setVisible(false); 615 saveWindow = null; 616 } 617 } 618 }; 619 JPanel bottom = 620 GuiUtils.inset(GuiUtils.makeApplyCancelButtons(listener), 5); 621 contents = 622 GuiUtils.topCenterBottom(topPanel, treePanel, bottom); 623 624 saveWindow.getContentPane().add(contents); 625 saveWindow.pack(); 626 saveWindow.setLocation(200, 200); 627 628 saveWindow.setVisible(true); 629 GuiUtils.toFront(saveWindow); 630 setStatus("Please select a folder from tree, or create a new folder"); 631 } 632 633 private void newComponentError(String comp) { 634 JLabel label = new JLabel("Please enter " + comp +" name"); 635 JPanel contents = GuiUtils.top(GuiUtils.inset(label, 24)); 636 GuiUtils.showOkCancelDialog(null, "Make Component Error", contents, null); 637 } 638 639 private void setStatus(String msg) { 640 statusLabel.setText(msg); 641 contents.paintImmediately(0,0,contents.getWidth(), 642 contents.getHeight()); 643 } 644 645 private void removeNode(Element node) { 646 if (imageDefaults == null) { 647 imageDefaults = getImageDefaults(); 648 } 649 Node parent = node.getParentNode(); 650 parent.removeChild(node); 651 makeXmlTree(); 652 try { 653 imageDefaults.writeWritable(); 654 } catch (Exception e) { 655 logger.error("write error!", e); 656 } 657 imageDefaults.setWritableDocument(imageDefaultsDocument, 658 imageDefaultsRoot); 659 } 660 661 private Node makeNewFolder() { 662 if (imageDefaults == null) { 663 imageDefaults = getImageDefaults(); 664 } 665 if (newFolder.length() == 0) { 666 return null; 667 } 668 List newChild = new ArrayList(); 669 Node newEle = imageDefaultsDocument.createElement(TAG_FOLDER); 670 lastCat = newEle; 671 String[] newAttrs = { ATTR_NAME, newFolder }; 672 XmlUtil.setAttributes((Element)newEle, newAttrs); 673 newChild.add(newEle); 674 XmlUtil.addChildren(imageDefaultsRoot, newChild); 675 try { 676 imageDefaults.writeWritable(); 677 } catch (Exception e) { 678 logger.error("write error!", e); 679 } 680 imageDefaults.setWritableDocument(imageDefaultsDocument, 681 imageDefaultsRoot); 682 return newEle; 683 } 684 685 /** 686 * Just creates an empty XmlTree 687 */ 688 private void makeXmlTree() { 689 if (imageDefaults == null) { 690 imageDefaults = getImageDefaults(); 691 } 692 xmlTree = new XmlTree(imageDefaultsRoot, true, "") { 693 public void doClick(XmlTree theTree, XmlTree.XmlTreeNode node, 694 Element element) { 695 Element clicked = xmlTree.getSelectedElement(); 696 String lastTag = clicked.getTagName(); 697 if ("folder".equals(lastTag)) { 698 lastCat = clicked; 699 lastClicked = null; 700 setStatus("Please enter a name for the new parameter set"); 701 newSetBtn.setEnabled(true); 702 } else { 703 lastCat = clicked.getParentNode(); 704 lastClicked = clicked; 705 } 706 } 707 708 public void doRightClick(XmlTree theTree, 709 XmlTree.XmlTreeNode node, 710 Element element, MouseEvent event) { 711 JPopupMenu popup = new JPopupMenu(); 712 if (makePopupMenu(theTree, element, popup)) { 713 popup.show((Component) event.getSource(), event.getX(), 714 event.getY()); 715 } 716 } 717 }; 718 List tagList = new ArrayList(); 719 tagList.add(TAG_FOLDER); 720 tagList.add(TAG_DEFAULT); 721 xmlTree.addTagsToProcess(tagList); 722 xmlTree.defineLabelAttr(TAG_FOLDER, ATTR_NAME); 723 addToContents(GuiUtils.inset(GuiUtils.topCenter(new JPanel(), 724 xmlTree.getScroller()), 5)); 725 return; 726 } 727 728 private List getFolders() { 729 return XmlUtil.findChildren(imageDefaultsRoot, TAG_FOLDER); 730 } 731 732 733 private void doDeleteRequest(Node node) { 734 if (node == null) { 735 return; 736 } 737 Element ele = (Element)node; 738 String tagName = ele.getTagName(); 739 if (tagName.equals("folder")) { 740 if (!GuiUtils.askYesNo("Verify Delete Folder", 741 "Do you want to delete the folder " + 742 "\"" + ele.getAttribute("name") + "\"?" + 743 "\nNOTE: All parameter sets it contains will be deleted.")) return; 744 XmlUtil.removeChildren(ele); 745 } else if (tagName.equals("default")) { 746 if (!GuiUtils.askYesNo("Verify Delete", "Do you want to delete " + 747 "\"" + ele.getAttribute(ATTR_NAME) + "\"?")) return; 748 } else { return; } 749 removeNode(ele); 750 } 751 /** 752 * Create and popup a command menu for when the user has clicked on the given xml node. 753 * 754 * @param theTree The XmlTree object displaying the current xml document. 755 * @param node The xml node the user clicked on. 756 * @param popup The popup menu to put the menu items in. 757 * @return Did we add any items into the menu 758 */ 759 private boolean makePopupMenu(final XmlTree theTree, final Element node, 760 JPopupMenu popup) 761 { 762 theTree.selectElement(node); 763 String tagName = node.getTagName(); 764 final Element parent = (Element)node.getParentNode(); 765 boolean didone = false; 766 JMenuItem mi; 767 768 if (tagName.equals("default")) { 769 lastClicked = node; 770 JMenu moveMenu = new JMenu("Move to"); 771 List folders = getFolders(); 772 for (int i = 0; i < folders.size(); i++) { 773 final Element newFolder = (Element)folders.get(i); 774 if (!newFolder.isSameNode(parent)) { 775 String name = newFolder.getAttribute(ATTR_NAME); 776 mi = new JMenuItem(name); 777 mi.addActionListener(new ActionListener() { 778 public void actionPerformed(ActionEvent ae) { 779 moveParameterSet(parent, newFolder); 780 } 781 }); 782 moveMenu.add(mi); 783 } 784 } 785 popup.add(moveMenu); 786 popup.addSeparator(); 787 didone = true; 788 } 789 790 mi = new JMenuItem("Rename..."); 791 mi.addActionListener(new ActionListener() { 792 public void actionPerformed(ActionEvent ae) { 793 doRename(node); 794 } 795 }); 796 popup.add(mi); 797 didone = true; 798 799 mi = new JMenuItem("Delete"); 800 mi.addActionListener(new ActionListener() { 801 public void actionPerformed(ActionEvent ae) { 802 doDeleteRequest(node); 803 } 804 }); 805 popup.add(mi); 806 didone = true; 807 808 return didone; 809 } 810 811 public void moveParameterSet(Element parent, Element newFolder) { 812 if (imageDefaults == null) { 813 imageDefaults = getImageDefaults(); 814 } 815 if (lastClicked == null) { 816 return; 817 } 818 Node copyNode = lastClicked.cloneNode(true); 819 newFolder.appendChild(copyNode); 820 parent.removeChild(lastClicked); 821 lastCat = newFolder; 822 makeXmlTree(); 823 try { 824 imageDefaults.writeWritable(); 825 } catch (Exception e) { 826 logger.error("write error!", e); 827 } 828 imageDefaults.setWritableDocument(imageDefaultsDocument, imageDefaultsRoot); 829 } 830 831 private void doRename(Element node) { 832 if (imageDefaults == null) { 833 imageDefaults = getImageDefaults(); 834 } 835 if (!node.hasAttribute(ATTR_NAME)) return; 836 JTextField nameFld = new JTextField("", 20); 837 JComponent contents = GuiUtils.doLayout(new Component[] { 838 GuiUtils.rLabel("New name: "), nameFld, }, 2, 839 GuiUtils.WT_N, GuiUtils.WT_N); 840 contents = GuiUtils.center(contents); 841 contents = GuiUtils.inset(contents, 10); 842 if (!GuiUtils.showOkCancelDialog(null, "Rename \"" + 843 node.getAttribute("name") + "\"", contents, null)) return; 844 String newName = nameFld.getText().trim(); 845 String tagName = node.getTagName(); 846 Element root = imageDefaultsRoot; 847 if (tagName.equals("default")) { 848 root = (Element)node.getParentNode(); 849 } 850 Element exists = XmlUtil.findElement(root, tagName, ATTR_NAME, newName); 851 if (!(exists == null)) { 852 if (!GuiUtils.askYesNo("Name Already Exists", 853 "Do you want to replace " + node.getAttribute("name") + " with" + 854 "\"" + newName + "\"?")) return; 855 } 856 node.removeAttribute(ATTR_NAME); 857 node.setAttribute(ATTR_NAME, newName); 858 makeXmlTree(); 859 try { 860 imageDefaults.writeWritable(); 861 } catch (Exception e) { 862 logger.error("write error!", e); 863 } 864 imageDefaults.setWritableDocument(imageDefaultsDocument, 865 imageDefaultsRoot); 866 } 867 868 /** 869 * Remove the currently display gui and insert the given one. 870 * 871 * @param comp The new gui. 872 */ 873 private void addToContents(JComponent comp) { 874 treePanel.removeAll(); 875 comp.setPreferredSize(new Dimension(200, 300)); 876 treePanel.add(comp, BorderLayout.CENTER); 877 if (contents != null) { 878 contents.invalidate(); 879 contents.validate(); 880 contents.repaint(); 881 } 882 } 883 884 public DataSourceImpl getDataSource() { 885 DataSourceImpl ds = null; 886 List dataSources = getDataSources(); 887 if (!dataSources.isEmpty()) { 888 ds = (DataSourceImpl)dataSources.get(0); 889 } 890 return ds; 891 } 892 893 public Element saveParameterSet() { 894 if (imageDefaults == null) { 895 imageDefaults = getImageDefaults(); 896 } 897 if (newCompName.length() == 0) { 898 newComponentError("parameter set"); 899 return null; 900 } 901 Element newChild = imageDefaultsDocument.createElement(TAG_DEFAULT); 902 newChild.setAttribute(ATTR_NAME, newCompName); 903 904 if (datachoice == null) { 905 datachoice = getDataChoice(); 906 } 907 dataSource = getDataSource(); 908 if (!(dataSource.getClass().isInstance(new AddeImageParameterDataSource()))) { 909 return newChild; 910 } 911 AddeImageParameterDataSource testDataSource = (AddeImageParameterDataSource)dataSource; 912 List imageList = testDataSource.getDescriptors(datachoice, this.dataSelection); 913 int numImages = imageList.size(); 914 List dateTimes = new ArrayList(); 915 DateTime thisDT = null; 916 if (!(imageList == null)) { 917 AddeImageDescriptor aid = null; 918 for (int imageNo = 0; imageNo < numImages; imageNo++) { 919 aid = (AddeImageDescriptor)(imageList.get(imageNo)); 920 thisDT = aid.getImageTime(); 921 if (!(dateTimes.contains(thisDT))) { 922 if (thisDT != null) { 923 dateTimes.add(thisDT); 924 } 925 } 926 } 927 String dateS = ""; 928 String timeS = ""; 929 if (!(dateTimes.isEmpty())) { 930 thisDT = (DateTime)dateTimes.get(0); 931 dateS = thisDT.dateString(); 932 timeS = thisDT.timeString(); 933 if (dateTimes.size() > 1) { 934 for (int img = 1; img < dateTimes.size(); img++) { 935 thisDT = (DateTime)dateTimes.get(img); 936 String str = ',' + thisDT.dateString(); 937 String newString = new String(dateS + str); 938 dateS = newString; 939 str = ',' + thisDT.timeString(); 940 newString = new String(timeS + str); 941 timeS = newString; 942 } 943 } 944 } 945 if (aid != null) { 946 String displayUrl = testDataSource.getDisplaySource(); 947 ImageParameters ip = new ImageParameters(displayUrl); 948 List props = ip.getProperties(); 949 List vals = ip.getValues(); 950 String server = ip.getServer(); 951 newChild.setAttribute(ATTR_SERVER, server); 952 int num = props.size(); 953 if (num > 0) { 954 String attr = ""; 955 String val = ""; 956 for (int i = 0; i < num; i++) { 957 attr = (String)(props.get(i)); 958 if (attr.equals(ATTR_POS)) { 959 val = Integer.valueOf(numImages - 1).toString(); 960 } else if (attr.equals(ATTR_DAY)) { 961 val = dateS; 962 } else if (attr.equals(ATTR_TIME)) { 963 val = timeS; 964 } else { 965 val = (String)(vals.get(i)); 966 } 967 newChild.setAttribute(attr, val); 968 } 969 } 970 } 971 } 972 Element parent = xmlTree.getSelectedElement(); 973 if (parent == null) { 974 parent = (Element)lastCat; 975 } 976 if (parent != null) { 977 Element exists = XmlUtil.findElement(parent, "default", ATTR_NAME, newCompName); 978 if (!(exists == null)) { 979 JLabel label = new JLabel("Replace \"" + newCompName + "\"?"); 980 JPanel contents = GuiUtils.top(GuiUtils.inset(label, newCompName.length()+12)); 981 if (!GuiUtils.showOkCancelDialog(null, "Parameter Set Exists", contents, null)) { 982 return newChild; 983 } 984 parent.removeChild(exists); 985 } 986 parent.appendChild(newChild); 987 makeXmlTree(); 988 } 989 try { 990 imageDefaults.writeWritable(); 991 } catch (Exception e) { 992 logger.error("write error!", e); 993 } 994 imageDefaults.setWritableDocument(imageDefaultsDocument, imageDefaultsRoot); 995 return newChild; 996 } 997 998 protected void getViewMenuItems(List items, boolean forMenuBar) { 999 super.getViewMenuItems(items, forMenuBar); 1000 items.add(GuiUtils.MENU_SEPARATOR); 1001 1002 JMenuItem saveMenuItem = new JMenuItem("Save Chart Image..."); 1003 saveMenuItem.addActionListener(e -> { 1004 if (!histogramPane.haveDoneHistogramInit) { 1005 histogramPane.loadHistogram(); 1006 histogramPane.haveDoneHistogramInit = true; 1007 } 1008 getChart().saveImage();}); 1009 items.add(saveMenuItem); 1010 } 1011 1012 /** 1013 * Holds a JFreeChart histogram of image values. 1014 */ 1015 private class MyTabbedPane extends JTabbedPane implements ChangeListener { 1016 /** Have we been painted */ 1017 boolean painted = false; 1018 1019 public boolean haveDoneHistogramInit = false; 1020 1021 /** 1022 * Creates a new {@code MyTabbedPane} that gets immediately registered 1023 * as a {@link javax.swing.event.ChangeListener} for its own events. 1024 */ 1025 public MyTabbedPane() { 1026 addChangeListener(this); 1027 } 1028 /** 1029 * The histogram isn't created unless the user selects the histogram 1030 * tab (this is done in an effort to avoid a spike in memory usage). 1031 * 1032 * @param e The event. Ignored for now. 1033 */ 1034 public void stateChanged(ChangeEvent e) { 1035 // MH: don't make the histogram until user clicks the tab. 1036 int index = getSelectedIndex(); 1037 if (index >= 0 && getTitleAt(index).equals("Histogram") && !haveDoneHistogramInit) { 1038 getIdv().showWaitCursor(); 1039 this.setComponentAt(index, 1040 GuiUtils.inset(getHistogramTabComponent(),5)); 1041 setInitialHistogramRange(); 1042 getIdv().clearWaitCursor(); 1043// haveDoneHistogramInit = true; 1044 } 1045 } 1046 public void loadHistogram() { 1047 int index = -1; 1048 for (int i = 0; i < getTabCount(); i++) { 1049 if (getTitleAt(i).equals("Histogram")) { 1050 index = i; 1051 break; 1052 } 1053 } 1054 1055 getIdv().showWaitCursor(); 1056 this.setComponentAt(index, 1057 GuiUtils.inset(getHistogramTabComponent(),5)); 1058 setInitialHistogramRange(); 1059 getIdv().clearWaitCursor(); 1060// haveDoneHistogramInit = true; 1061 } 1062 1063 /** 1064 * MH: Not really doing anything useful...but will leave it here for now... 1065 * 1066 * @param g graphics 1067 */ 1068 public void paint(java.awt.Graphics g) { 1069 if (!painted) { 1070 painted = true; 1071 } 1072 super.paint(g); 1073 } 1074 } 1075}