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