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