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