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