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