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.chooser;
030    
031    import static javax.swing.GroupLayout.DEFAULT_SIZE;
032    import static javax.swing.GroupLayout.PREFERRED_SIZE;
033    import static javax.swing.GroupLayout.Alignment.BASELINE;
034    import static javax.swing.GroupLayout.Alignment.LEADING;
035    import static javax.swing.GroupLayout.Alignment.TRAILING;
036    import static javax.swing.LayoutStyle.ComponentPlacement.RELATED;
037    import static javax.swing.LayoutStyle.ComponentPlacement.UNRELATED;
038    
039    import java.awt.Component;
040    import java.awt.event.ActionEvent;
041    import java.awt.event.ActionListener;
042    import java.awt.event.ItemEvent;
043    import java.awt.event.ItemListener;
044    import java.io.IOException;
045    import java.net.URI;
046    import java.util.ArrayList;
047    import java.util.Date;
048    import java.util.Hashtable;
049    import java.util.List;
050    import java.util.Vector;
051    
052    import javax.swing.GroupLayout;
053    import javax.swing.JButton;
054    import javax.swing.JComboBox;
055    import javax.swing.JComponent;
056    import javax.swing.JLabel;
057    import javax.swing.JPanel;
058    import javax.swing.JTabbedPane;
059    
060    import org.jdom2.Document;
061    import org.jdom2.JDOMException;
062    import org.jdom2.Namespace;
063    import org.jdom2.input.SAXBuilder;
064    import org.w3c.dom.Element;
065    
066    import thredds.catalog.XMLEntityResolver;
067    import ucar.unidata.data.radar.TDSRadarDatasetCollection;
068    import ucar.nc2.units.DateUnit;
069    import ucar.unidata.data.radar.RadarQuery;
070    import ucar.unidata.geoloc.StationImpl;
071    import ucar.unidata.idv.chooser.IdvChooserManager;
072    import ucar.unidata.idv.chooser.TimesChooser;
073    import ucar.unidata.metdata.NamedStation;
074    import ucar.unidata.metdata.NamedStationImpl;
075    import ucar.unidata.util.DateSelection;
076    import ucar.unidata.util.DateUtil;
077    import ucar.unidata.util.DatedThing;
078    import ucar.unidata.util.GuiUtils;
079    import ucar.unidata.util.LogUtil;
080    import ucar.unidata.util.Misc;
081    import ucar.unidata.util.PreferenceList;
082    import ucar.unidata.util.Product;
083    import ucar.unidata.util.TwoFacedObject;
084    
085    import visad.CommonUnit;
086    import visad.DateTime;
087    
088    import edu.wisc.ssec.mcidasv.Constants;
089    import edu.wisc.ssec.mcidasv.util.McVGuiUtils;
090    import edu.wisc.ssec.mcidasv.util.McVGuiUtils.Position;
091    import edu.wisc.ssec.mcidasv.util.McVGuiUtils.TextColor;
092    import edu.wisc.ssec.mcidasv.util.McVGuiUtils.Width;
093    
094    
095    /**
096     * Created by IntelliJ IDEA.
097     * User: yuanho
098     * Date: Jan 16, 2008
099     * Time: 11:17:51 AM
100     * To change this template use File | Settings | File Templates.
101     */
102    public class TDSRadarChooser extends TimesChooser implements Constants {
103    
104        /** The collection */
105        private TDSRadarDatasetCollection collection;
106    
107    
108        /** The currently selected station */
109        private NamedStation selectedStation;
110    
111        /** The currently selected level3 product */
112        private String selectedProduct;
113    
114        /** Those urls we connect to */
115        //"http://motherlode.ucar.edu:8080/thredds/radarServer/catalog.xml";
116        private String serverUrl;
117    
118        /** Each dataset collection URL */
119        //"http://motherlode.ucar.edu:8080/thredds/radarServer/level2/idd/dataset.xml";
120        //private String collectionUrl;
121    
122        /** Component to hold collections */
123        private JComboBox collectionSelector;
124    
125        /** Component to hold product list */
126        private JComboBox productComboBox;
127        
128        /** Level 3 panel that can be hidden */
129        private JPanel productPanel;
130        
131        /** components that need a server for activation */
132        private List compsThatNeedServer = new ArrayList();
133    
134        /** components that need a server for activation */
135        private List level3CompsThatNeedServer = new ArrayList();
136    
137        /** persistent holder for catalog URLS */
138        private PreferenceList urlListHandler;
139    
140        /** catalog URL holder */
141        private JComboBox urlBox;
142    
143        /** ok flag */
144        private boolean okToDoUrlListEvents = true;
145    
146        /** dataset list */
147        private List datasetList;
148    
149        /** Command for connecting */
150        protected static final String CMD_CONNECT = "cmd.connect";
151    
152        /** _more_          */
153        private boolean isLevel3;
154    
155        /** _more_          */
156        public static final String[] level3_ExName = { "NVW", "DPA" };
157    
158    
159        /**
160         * Create the RadarChooser
161         *
162         * @param mgr The <code>IdvChooserManager</code>
163         * @param root  The xml root that defines this chooser
164         *
165         */
166        public TDSRadarChooser(IdvChooserManager mgr, Element root) {
167            super(mgr, root);
168            
169            loadButton = McVGuiUtils.makeImageTextButton(ICON_ACCEPT_SMALL, getLoadCommandName());
170            loadButton.setActionCommand(getLoadCommandName());
171            loadButton.addActionListener(this);
172            
173            cancelButton = McVGuiUtils.makeImageButton(ICON_CANCEL, "Cancel");
174            cancelButton.setActionCommand(GuiUtils.CMD_CANCEL);
175            cancelButton.addActionListener(this);
176            cancelButton.setEnabled(false);
177            
178        }
179    
180    
181    
182        /**
183         * Handle the update event. Just pass it through to the imageChooser
184         */
185        public void doUpdate() {
186            if ((serverUrl == null) || (datasetList == null)
187                    || (datasetList.size() == 0) || (selectedProduct == null)) {
188                if (urlBox != null) {
189                    setServer((String) urlBox.getSelectedItem());
190                }
191                return;
192            }
193            Misc.run(this, "stationOrProductChanged");
194        }
195    
196    
197    
198        /**
199         * Update the status of the gui
200         */
201        protected void updateStatus() {
202            super.updateStatus();
203            if (serverUrl == null) {
204                setHaveData(false);
205                setStatus("Please connect to the server");
206            }
207            else if (selectedStation == null) {
208                setHaveData(false);
209                setStatus("Please select a station", "stations");
210            }
211            else if (isLevel3 && (selectedProduct == null)) {
212                setHaveData(false);
213                setStatus("Please select a level 3 product", "products");
214            }
215            else {
216                boolean haveTimesSelected;
217                if (getDoAbsoluteTimes()) {
218                    haveTimesSelected = getSelectedAbsoluteTimes().size() > 0;
219                } else {
220                    haveTimesSelected = true;
221                }
222                setHaveData(haveTimesSelected);
223                if (haveTimesSelected) {
224                    setStatus("Press \"" + CMD_LOAD + "\" to load the selected radar data", "buttons");
225                } else {
226                    setStatus("Please select times", "timepanel");
227                }
228            }
229            GuiUtils.enableTree(loadButton, getHaveData());
230        }
231    
232    
233    
234        /**
235         * Handle when there are newly selected stations
236         *
237         * @param stations list of newly selected stations
238         */
239        protected void newSelectedStations(List stations) {
240            super.newSelectedStations(stations);
241            if ((stations == null) || (stations.size() == 0)) {
242                selectedStation = null;
243            } else {
244                NamedStation newStation = (NamedStation) stations.get(0);
245                if (Misc.equals(newStation, selectedStation)) {
246                    return;
247                }
248                selectedStation = newStation;
249            }
250            Misc.run(TDSRadarChooser.this, "stationOrProductChanged");
251        }
252    
253    
254        /** A widget for the list of dataset descriptors */
255    
256    
257        /** Flag to keep from infinite looping */
258        private boolean ignoreProductChange = false;
259    
260        /** Selection label text */
261        protected static final String LABEL_SELECT = " -- Select -- ";
262    
263        /**
264         * _more_
265         */
266        protected void productChanged() {
267            stationOrProductChanged();
268            // updateStatus();
269        }
270    
271        /**
272         * Reset the descriptor stuff
273         */
274        private void resetProductBox() {
275            ignoreProductChange = true;
276            productComboBox.setSelectedItem(LABEL_SELECT);
277            ignoreProductChange = false;
278        }
279    
280        /**
281         * Should we update on first display
282         *
283         * @return true
284         */
285        protected boolean shouldDoUpdateOnFirstDisplay() {
286            return false;
287        }
288    
289        /**
290         * Set the server
291         *
292         * @param s the server URL
293         */
294        private void setServer(String s) {
295            datasetList = new ArrayList();
296            serverUrl   = s;
297            try {
298                List collections = getRadarCollections(serverUrl);
299                GuiUtils.setListData(collectionSelector, collections);
300            } catch (Exception e) {
301                GuiUtils.setListData(collectionSelector, new ArrayList());
302            }
303        }
304    
305        /**
306         * Set the active collection
307         *
308         * @param s collection URL
309         */
310        private void setCollection(String s) {
311            isLevel3 = false;
312            GuiUtils.enableComponents(level3CompsThatNeedServer, false);
313            productPanel.setVisible(false);
314            GuiUtils.enableComponents(compsThatNeedServer, true);
315            setAbsoluteTimes(new ArrayList());
316            selectedProduct = null;
317            selectedStation = null;
318            Misc.run(this, "initializeCollection", s);
319        }
320    
321        /**
322         * _more_
323         *
324         * @param s _more_
325         */
326        private void setLevel3Collection(String s) {
327            isLevel3 = true;
328            GuiUtils.enableComponents(level3CompsThatNeedServer, true);
329            productPanel.setVisible(true);
330            GuiUtils.enableComponents(compsThatNeedServer, true);
331            setAbsoluteTimes(new ArrayList());
332            selectedProduct = null;
333            selectedStation = null;
334            Misc.run(this, "initializeLevel3Collection", s);
335        }
336    
337        /**
338         * Add a component that needs to have a valid server
339         *
340         * @param comp  the component
341         *
342         * @return  the component
343         */
344        protected JComponent addServerComp(JComponent comp) {
345            compsThatNeedServer.add(comp);
346            return comp;
347        }
348    
349        /**
350         * Add a component that needs to have a valid server
351         *
352         * @param comp  the component
353         *
354         * @return  the component
355         */
356        protected JComponent addLevel3ServerComp(JComponent comp) {
357            level3CompsThatNeedServer.add(comp);
358            return comp;
359        }
360    
361        /**
362         * Get  the radar collections for  the given server URL
363         *
364         * @param radarServerURL  server URL
365         *
366         * @return  a map of the collection names to URL
367         */
368        private List getRadarCollections(String radarServerURL) {
369            SAXBuilder        builder;
370            Document          doc  = null;
371            XMLEntityResolver jaxp = new XMLEntityResolver(true);
372            builder = jaxp.getSAXBuilder();
373            List collections = new ArrayList();
374    
375            try {
376                doc = builder.build(radarServerURL);
377            } catch (JDOMException e) {
378                userMessage("Invalid catalog");
379                //e.printStackTrace();
380            } catch (IOException e) {
381                userMessage("Unable to open catalog");
382                //e.printStackTrace();
383            }
384    
385            org.jdom2.Element rootElem    = doc.getRootElement();
386            org.jdom2.Element serviceElem = readElements(rootElem, "service");
387            String           uriBase     = serviceElem.getAttributeValue("base");
388            org.jdom2.Element dsElem      = readElements(rootElem, "dataset");
389            String           naming      = "catalogRef";
390            Namespace        nss         = rootElem.getNamespace("xlink");
391            List             children    = dsElem.getChildren();
392            for (int j = 0; j < children.size(); j++) {
393                org.jdom2.Element child     = (org.jdom2.Element) children.get(j);
394                String           childName = child.getName();
395                if (childName.equals(naming)) {
396                    //String id   = child.getAttributeValue("ID");
397                    String desc    = child.getAttributeValue("title", nss);
398                    String urlpath = child.getAttributeValue("href", nss);
399                    String[] c = radarServerURL.split(uriBase);  //.replaceFirst("catalog.xml", "");
400                    String         ul     = c[0] + uriBase + urlpath;
401                    TwoFacedObject twoObj = new TwoFacedObject(desc, ul);
402                    collections.add(twoObj);
403                    //collections.put(desc, ul);
404                }
405    
406            }
407    
408            return collections;
409        }
410    
411        /**
412         * Read the elements
413         *
414         * @param elem  element
415         * @param eleName element name
416         *
417         * @return an element
418         */
419        public org.jdom2.Element readElements(org.jdom2.Element elem,
420                                             String eleName) {
421            List children = elem.getChildren();
422            for (int j = 0; j < children.size(); j++) {
423                org.jdom2.Element child     = (org.jdom2.Element) children.get(j);
424                String           childName = child.getName();
425                if (childName.equals(eleName)) {
426                    return child;
427                }
428            }
429            return null;
430        }
431    
432        /**
433         * Make the collection.  If there is an error, pop up a user message.
434         *
435         * @param url   URL for the collection
436         */
437        public void initializeCollection(String url) {
438    
439            List<NamedStationImpl> stations = new ArrayList<NamedStationImpl>();
440            try {
441                StringBuffer errlog = new StringBuffer();
442                try {
443                    collection = TDSRadarDatasetCollection.factory("test", url,
444                            errlog);
445                } catch (Exception exc) {
446                    userMessage("Invalid catalog");
447    
448                    return;
449                }
450                List tdsStations = collection.getRadarStations();
451                for (int i = 0; i < tdsStations.size(); i++) {
452                    StationImpl stn = (StationImpl) tdsStations.get(i);
453                    // thredds.catalog.query.Location loc = stn.getLocation();
454                    //TODO: need better station  need to switch lat lon
455                    NamedStationImpl station =
456                        new NamedStationImpl(stn.getName(), stn.getName(),
457                                             stn.getLatitude(),
458                                             stn.getLongitude(),
459                                             stn.getAltitude(), CommonUnit.meter);
460                    stations.add(station);
461    
462                }
463    
464                getStationMap().setStations(stations);
465            } catch (Exception exc) {
466                userMessage("Unable to load stations");
467                return;
468            }
469            urlListHandler.saveState(urlBox);
470        }
471    
472        /**
473         * _more_
474         *
475         * @param url _more_
476         */
477        public void initializeLevel3Collection(String url) {
478    
479            List<NamedStationImpl> stations = new ArrayList<NamedStationImpl>();
480            List<Product>          products;
481            List<String>           exProducts = new ArrayList<String>();
482    
483            for(String ename: level3_ExName){
484                exProducts.add(ename);
485            }
486    
487            try {
488                StringBuffer errlog = new StringBuffer();
489                try {
490                    collection = TDSRadarDatasetCollection.factory("test", url,
491                            errlog);
492                } catch (Exception exc) {
493                    userMessage("Invalid catalog");
494                    return;
495                }
496                products = collection.getRadarProducts();
497                List tdsStations = collection.getRadarStations();
498                for (int i = 0; i < tdsStations.size(); i++) {
499                    StationImpl stn = (StationImpl) tdsStations.get(i);
500                    // thredds.catalog.query.Location loc = stn.getLocation();
501                    //TODO: need better station  need to switch lat lon
502                    NamedStationImpl station =
503                        new NamedStationImpl(stn.getName(), stn.getName(),
504                                             stn.getLatitude(),
505                                             stn.getLongitude(),
506                                             stn.getAltitude(), CommonUnit.meter);
507                    stations.add(station);
508    
509                }
510                List<TwoFacedObject> productNames = new ArrayList();
511                for (Product product : products) {
512                   // if ( !product.getID().contains("DPA")
513                     //       && !product.getID().contains("NVW")) {
514                    if ( !exProducts.contains(product.getID())) {
515                        String lable = product.getName() + " (" + product.getID()
516                                       + ")";
517                        TwoFacedObject twoObj = new TwoFacedObject(lable,
518                                                    product.getID());
519                        productNames.add(twoObj);
520                    }
521                }
522                GuiUtils.setListData(productComboBox, productNames);
523    
524                // GuiUtils.setListData(dataTypeComboBox, dataTypes);
525                getStationMap().setStations(stations);
526            } catch (Exception exc) {
527                userMessage("Unable to load stations");
528                return;
529            }
530            urlListHandler.saveState(urlBox);
531        }
532    
533    
534        /**
535         * Handle when the user has selected a new station
536         */
537        public void stationOrProductChanged() {
538            Vector times = new Vector();
539            setHaveData(false);
540            if ((!isLevel3 && selectedStation != null) ||
541                    (isLevel3 && selectedStation != null && selectedProduct != null)) {
542                List timeSpan = collection.getRadarTimeSpan();
543                Date fromDate =  DateUnit.getStandardOrISO((String) timeSpan.get(0));
544                //Date toDate = DateUnit.getStandardOrISO((String) timeSpan.get(1));
545                Date toDate = new Date(System.currentTimeMillis()
546                                       + DateUtil.daysToMillis(1));
547                //Go back 10 years (or so)
548                //Date fromDate = new Date(System.currentTimeMillis()
549                //                         - DateUtil.daysToMillis(365 * 10));
550                try {
551                    showWaitCursor();
552                    setAbsoluteTimes(new ArrayList());
553                    setStatus("Reading times for station: " + selectedStation,
554                              "");
555                    //                LogUtil.message("Reading times for station: "
556                    //                                + selectedStation);
557                    String pid = null;
558                    if(isLevel3)
559                        pid = TwoFacedObject.getIdString(
560                                     productComboBox.getSelectedItem());
561                    List allTimes =
562                        collection.getRadarStationTimes(selectedStation.getID(),
563                            pid, fromDate, toDate);
564    
565                 //   if(allTimes.size() == 0) {
566                 //       toDate = new Date(System.currentTimeMillis()
567                 //                + DateUtil.daysToMillis(1));
568                 //       allTimes =
569                 //       collection.getRadarStationTimes(selectedStation.getID(),
570                 //           pid, fromDate, toDate);
571                 //   }
572    
573                    for (int timeIdx = 0; timeIdx < allTimes.size(); timeIdx++) {
574                        Object timeObj = allTimes.get(timeIdx);
575                        Date   date;
576                        if (timeObj instanceof Date) {
577                            date = (Date) timeObj;
578                        } else {
579                            date = DateUnit.getStandardOrISO(timeObj.toString());
580                        }
581                        times.add(new DateTime(date));
582                    }
583                    //                LogUtil.message("");
584                    showNormalCursor();
585                } catch (Exception exc) {
586                    userMessage("Error reading times for station: "
587                                + selectedStation);
588                    //logException("Getting times for station: " + selectedStation,
589                    //             exc);
590                    setStatus("Select a different collection", "collections");
591                    showNormalCursor();
592                    return;
593                }
594            }
595            setAbsoluteTimes(times);
596            updateStatus();
597        }
598    
599    
600    
601    
602    
603        /**
604         * Load the data
605         */
606        public void doLoadInThread() {
607            // to the CDMRadarDataSource
608            Hashtable ht = new Hashtable();
609            if (selectedStation != null) {
610                ht.put(ucar.unidata.data.radar.RadarDataSource.STATION_LOCATION,
611                       selectedStation.getNamedLocation());
612            } else {
613                LogUtil.userMessage("No Station selected");
614            }
615    
616            if (isLevel3 && (selectedProduct == null)) {
617    
618                LogUtil.userMessage("No Product selected");
619            }
620    
621            try {
622                DateSelection dateSelection = new DateSelection();
623                String collectionUrl = TwoFacedObject.getIdString(
624                                           collectionSelector.getSelectedItem());
625                String     pid = null;
626                RadarQuery radarQuery;
627                if (isLevel3) {
628                    pid = TwoFacedObject.getIdString(
629                        productComboBox.getSelectedItem());
630                    radarQuery = new RadarQuery(collectionUrl,
631                                                selectedStation.getID(), pid,
632                                                dateSelection);
633                } else {
634                    radarQuery = new RadarQuery(collectionUrl,
635                                                selectedStation.getID(),
636                                                dateSelection);
637                }
638    
639                List urls = new ArrayList();
640    
641                if (getDoAbsoluteTimes()) {
642                    List times    = new ArrayList();
643                    List selected = makeDatedObjects(getSelectedAbsoluteTimes());
644                    for (int i = 0; i < selected.size(); i++) {
645                        DatedThing datedThing = (DatedThing) selected.get(i);
646                        Date       date       = datedThing.getDate();
647                        times.add(date);
648                        URI uri = null;
649                        try {
650                            uri = collection.getRadarDatasetURI(
651                                selectedStation.getID(), pid, date);
652                        } catch (Exception excp) {
653                            LogUtil.userMessage("incorrect times selected");
654                            return;
655                        }
656                        urls.add(uri.toString());
657                    }
658                    if (urls.size() == 0) {
659                        LogUtil.userMessage("No times selected");
660                        return;
661                    }
662                    dateSelection.setTimes(times);
663                } else {
664                    int count = getRelativeTimesList().getSelectedIndex() + 1;
665                    if (count == 0) {
666                        LogUtil.userMessage("No relative times selected");
667                        return;
668                    }
669                    Date toDate = new Date(System.currentTimeMillis()
670                                           + DateUtil.daysToMillis(365 * 100));
671                    //Go back 10 years (or so)
672                    Date fromDate = new Date(System.currentTimeMillis()
673                                             - DateUtil.daysToMillis(365 * 10));
674    
675                    dateSelection.setStartFixedTime(fromDate);
676                    dateSelection.setEndFixedTime(toDate);
677                    dateSelection.setCount(count);
678                }
679                makeDataSource(radarQuery, "FILE.RADAR", ht);
680            } catch (Exception exc) {
681                logException("Loading radar data", exc);
682            }
683        }
684        
685        protected int getNumTimesToSelect() {
686            return 5;
687        }
688        
689        /**
690         * Get the default selected index for the relative times list.
691         *
692         * @return default index
693         */
694        protected int getDefaultRelativeTimeIndex() {
695            return 4;
696        }
697        
698        /**
699         * Check the times lists
700         */
701        protected void checkTimesLists() {
702            super.checkTimesLists();
703            if (timesCardPanelExtra == null) {
704                return;
705            }
706            if (getDoAbsoluteTimes()) {
707                timesCardPanelExtra.show("absolute");
708            } else {
709                timesCardPanelExtra.show("relative");
710            }
711        }
712            
713        /** Card panel to hold extra relative and absolute time components */
714        private GuiUtils.CardLayoutPanel timesCardPanelExtra;
715        
716        /**
717         * Add the interval selector to the component.
718         * @return superclass component with extra stuff
719         */
720        protected JPanel makeTimesPanel() {
721            JComponent extra = getExtraTimeComponent();
722            GuiUtils.enableTree(extra, false);
723            JPanel timesPanel = makeTimesPanel(extra, null);
724            return timesPanel;
725        }
726        
727        /**
728         * Set the relative and absolute extra components
729         */
730        protected JPanel makeTimesPanel(JComponent relativeCard, JComponent absoluteCard) {
731            JPanel timesPanel = super.makeTimesPanel(false,true);
732                    
733            // Make a new timesPanel that has extra components tacked on the bottom, inside the tabs
734            Component[] comps = timesPanel.getComponents();
735            if (comps.length==2 && comps[0] instanceof JTabbedPane && comps[1] instanceof JLabel) {         
736                timesCardPanelExtra = new GuiUtils.CardLayoutPanel();
737                if (relativeCard == null) relativeCard = new JPanel();
738                if (absoluteCard == null) absoluteCard = new JPanel();
739                absoluteCard = GuiUtils.hbox(comps[1], GuiUtils.right(absoluteCard));
740                timesCardPanelExtra.add(relativeCard, "relative");
741                timesCardPanelExtra.add(absoluteCard, "absolute");
742                timesPanel = GuiUtils.centerBottom(comps[0], timesCardPanelExtra);
743            }
744            
745            return timesPanel;
746        }
747        
748        /**
749         * Get the time popup widget
750         *
751         * @return  a widget for selecting the day
752         */
753        protected JComponent getExtraTimeComponent() {
754            JPanel filler = new JPanel();
755            McVGuiUtils.setComponentHeight(filler, new JComboBox());
756            return filler;
757        }
758     
759        /**
760         * Make the contents
761         *
762         * @return  the contents
763         */
764        protected JPanel doMakeInnerPanel() {
765            JPanel myPanel = new JPanel();
766    
767            JLabel collectionLabel = McVGuiUtils.makeLabelRight("Collection:");
768    
769            collectionSelector = new JComboBox();
770            collectionSelector.addItemListener(new ItemListener() {
771                public void itemStateChanged(ItemEvent e) {
772                    newSelectedStations(new ArrayList());
773                    if (collectionSelector.getSelectedItem() == null) {
774                        return;
775                    }
776                    String collectionUrl =
777                        TwoFacedObject.getIdString(
778                            collectionSelector.getSelectedItem());
779    
780                    if (collectionUrl.contains("level3")) {
781                        setLevel3Collection(collectionUrl);
782                    } else {
783                        setCollection(collectionUrl);
784                    }
785                }
786    
787            });
788            addServerComp(collectionLabel);
789            addServerComp(collectionSelector);
790                    
791            productComboBox = new JComboBox();
792            productComboBox.addItemListener(new ItemListener() {
793                public void itemStateChanged(ItemEvent e) {
794                    if (productComboBox.getSelectedItem() == null) {
795                        return;
796                    }
797                    selectedProduct =
798                        productComboBox.getSelectedItem().toString();
799                    resetProductBox();
800                    productChanged();
801                }
802    
803            });
804            addLevel3ServerComp(productComboBox);
805                    
806            productPanel = McVGuiUtils.makeLabeledComponent("Product:", productComboBox);
807            
808            JLabel stationLabel = McVGuiUtils.makeLabelRight("Station:");
809            addServerComp(stationLabel);
810    
811            JComponent stationPanel = getStationMap();
812            registerStatusComp("stations", stationPanel);
813            addServerComp(stationPanel);
814            
815            JLabel timesLabel = McVGuiUtils.makeLabelRight("Times:");
816            addServerComp(timesLabel);
817            
818            JPanel timesPanel = makeTimesPanel();
819            timesPanel.setBorder(javax.swing.BorderFactory.createEtchedBorder());
820            addServerComp(timesPanel);
821    
822            GuiUtils.enableComponents(compsThatNeedServer, false);
823            GuiUtils.enableComponents(level3CompsThatNeedServer, false);
824            productPanel.setVisible(false);
825    
826            GroupLayout layout = new GroupLayout(myPanel);
827            myPanel.setLayout(layout);
828            layout.setHorizontalGroup(
829                layout.createParallelGroup(LEADING)
830                .addGroup(layout.createSequentialGroup()
831                    .addGroup(layout.createParallelGroup(LEADING)
832                        .addGroup(layout.createSequentialGroup()
833                            .addComponent(collectionLabel)
834                            .addGap(GAP_RELATED)
835                            .addComponent(collectionSelector, PREFERRED_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)
836                            .addComponent(productPanel, PREFERRED_SIZE, DEFAULT_SIZE, Short.MAX_VALUE))
837                        .addGroup(layout.createSequentialGroup()
838                            .addComponent(stationLabel)
839                            .addGap(GAP_RELATED)
840                            .addComponent(stationPanel, PREFERRED_SIZE, DEFAULT_SIZE, Short.MAX_VALUE))
841                        .addGroup(layout.createSequentialGroup()
842                            .addComponent(timesLabel)
843                            .addGap(GAP_RELATED)
844                            .addComponent(timesPanel, PREFERRED_SIZE, DEFAULT_SIZE, Short.MAX_VALUE))))
845            );
846            layout.setVerticalGroup(
847                layout.createParallelGroup(LEADING)
848                .addGroup(layout.createSequentialGroup()
849                    .addGroup(layout.createParallelGroup(BASELINE)
850                        .addComponent(collectionLabel)
851                        .addComponent(collectionSelector)
852                        .addComponent(productPanel))
853                    .addPreferredGap(RELATED)
854                    .addGroup(layout.createParallelGroup(LEADING)
855                        .addComponent(stationLabel)
856                        .addComponent(stationPanel, PREFERRED_SIZE, DEFAULT_SIZE, Short.MAX_VALUE))
857                    .addPreferredGap(RELATED)
858                    .addGroup(layout.createParallelGroup(LEADING)
859                        .addComponent(timesLabel)
860                        .addComponent(timesPanel, PREFERRED_SIZE, DEFAULT_SIZE, Short.MAX_VALUE))
861                    .addPreferredGap(RELATED))
862            );
863            
864            return myPanel;
865        }
866        
867        /**
868         * Make the UI for this selector.
869         * 
870         * Thank you NetBeans for helping with the layout!
871         * 
872         * @return The gui
873         */ 
874        private JPanel innerPanel = doMakeInnerPanel();
875            
876        private JLabel statusLabel = new JLabel("Status");
877    
878        @Override
879        public void setStatus(String statusString, String foo) {
880            if (statusString == null)
881                statusString = "";
882            statusLabel.setText(statusString);
883        }
884            
885        protected void setInnerPanel(JPanel newInnerPanel) {
886            innerPanel = newInnerPanel;
887        }
888    
889        public JComponent doMakeContents() {
890            JPanel outerPanel = new JPanel();
891    
892            JLabel serverLabel = McVGuiUtils.makeLabelRight("Catalog:");                
893    
894            //Get the list of catalogs but remove the old catalog.xml entry
895            urlListHandler = getPreferenceList(PREF_TDSRADARSERVER);
896    
897            ActionListener catListListener = new ActionListener() {
898                public void actionPerformed(ActionEvent ae) {
899                    if ( !okToDoUrlListEvents) {
900                        return;
901                    }
902                    setServer((String) urlBox.getSelectedItem());
903                }
904            };
905            
906            urlBox = urlListHandler.createComboBox(GuiUtils.CMD_UPDATE, catListListener, true);
907            McVGuiUtils.setComponentWidth(urlBox, Width.DOUBLEDOUBLE);
908            
909            // productComboBox gets created a little too tall--set to same height as urlBox
910            if (productComboBox!=null)
911                McVGuiUtils.setComponentHeight(productComboBox, urlBox);
912                    
913            JButton connectButton = McVGuiUtils.makeImageTextButton(ICON_CONNECT_SMALL, "Connect");
914            McVGuiUtils.setComponentWidth(connectButton, Width.DOUBLE);
915            connectButton.setActionCommand(GuiUtils.CMD_UPDATE);
916            connectButton.addActionListener(this);
917                    
918            JLabel statusLabelLabel = McVGuiUtils.makeLabelRight("");
919            
920            statusLabel.setText("Status");
921            McVGuiUtils.setLabelPosition(statusLabel, Position.RIGHT);
922            McVGuiUtils.setComponentColor(statusLabel, TextColor.STATUS);
923            
924            JButton helpButton = McVGuiUtils.makeImageButton(ICON_HELP, "Show help");
925            helpButton.setActionCommand(GuiUtils.CMD_HELP);
926            helpButton.addActionListener(this);
927            
928            JButton refreshButton = McVGuiUtils.makeImageButton(ICON_REFRESH, "Refresh");
929            refreshButton.setActionCommand(GuiUtils.CMD_UPDATE);
930            refreshButton.addActionListener(this);
931            
932            McVGuiUtils.setComponentWidth(loadButton, Width.DOUBLE);
933    
934            GroupLayout layout = new GroupLayout(outerPanel);
935            outerPanel.setLayout(layout);
936            layout.setHorizontalGroup(
937                layout.createParallelGroup(LEADING)
938                .addGroup(TRAILING, layout.createSequentialGroup()
939                    .addGroup(layout.createParallelGroup(TRAILING)
940                        .addGroup(layout.createSequentialGroup()
941                            .addContainerGap()
942                            .addComponent(helpButton)
943                            .addGap(GAP_RELATED)
944                            .addComponent(refreshButton)
945                            .addGap(GAP_RELATED)
946                            .addComponent(cancelButton)
947                            .addPreferredGap(RELATED)
948                            .addComponent(loadButton))
949                            .addGroup(LEADING, layout.createSequentialGroup()
950                            .addContainerGap()
951                            .addGroup(layout.createParallelGroup(LEADING)
952                                .addComponent(innerPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)
953                                .addGroup(layout.createSequentialGroup()
954                                    .addComponent(serverLabel)
955                                    .addGap(GAP_RELATED)
956                                    .addComponent(urlBox, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)
957                                    .addGap(GAP_UNRELATED)
958                                    .addComponent(connectButton))
959                                .addGroup(layout.createSequentialGroup()
960                                    .addComponent(statusLabelLabel)
961                                    .addGap(GAP_RELATED)
962                                    .addComponent(statusLabel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)))))
963                    .addContainerGap())
964            );
965            layout.setVerticalGroup(
966                layout.createParallelGroup(LEADING)
967                .addGroup(layout.createSequentialGroup()
968                    .addContainerGap()
969                    .addGroup(layout.createParallelGroup(BASELINE)
970                        .addComponent(serverLabel)
971                        .addComponent(urlBox)
972                        .addComponent(connectButton))
973                    .addPreferredGap(UNRELATED)
974                    .addComponent(innerPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)
975                    .addPreferredGap(UNRELATED)
976                    .addGroup(layout.createParallelGroup(BASELINE)
977                        .addComponent(statusLabelLabel)
978                        .addComponent(statusLabel))
979                    .addPreferredGap(UNRELATED)
980                    .addGroup(layout.createParallelGroup(BASELINE)
981                        .addComponent(loadButton)
982                        .addComponent(cancelButton)
983                        .addComponent(refreshButton)
984                        .addComponent(helpButton))
985                    .addContainerGap())
986            );
987        
988            return outerPanel;
989    
990        }
991    
992    }
993