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