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