001    /*
002     * $Id: FlatFileChooser.java,v 1.12 2012/02/19 17:35:36 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    
034    import static javax.swing.GroupLayout.DEFAULT_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.Image;
042    import java.awt.MediaTracker;
043    import java.awt.Toolkit;
044    import java.awt.event.ActionEvent;
045    import java.awt.event.ActionListener;
046    import java.awt.event.FocusEvent;
047    import java.awt.event.FocusListener;
048    import java.io.File;
049    import java.io.FileNotFoundException;
050    import java.io.FileReader;
051    import java.io.InputStream;
052    import java.util.ArrayList;
053    import java.util.Hashtable;
054    import java.util.List;
055    
056    import javax.swing.GroupLayout;
057    import javax.swing.JButton;
058    import javax.swing.JCheckBox;
059    import javax.swing.JComboBox;
060    import javax.swing.JComponent;
061    import javax.swing.JFileChooser;
062    import javax.swing.JLabel;
063    import javax.swing.JPanel;
064    import javax.swing.JRadioButton;
065    import javax.swing.JTextField;
066    
067    import org.w3c.dom.Element;
068    
069    import ucar.unidata.idv.IntegratedDataViewer;
070    import ucar.unidata.idv.chooser.IdvChooser;
071    import ucar.unidata.idv.chooser.IdvChooserManager;
072    import ucar.unidata.util.GuiUtils;
073    import ucar.unidata.util.IOUtil;
074    import ucar.unidata.util.Misc;
075    import ucar.unidata.util.TwoFacedObject;
076    import ucar.unidata.xml.XmlUtil;
077    import visad.util.ImageHelper;
078    import edu.wisc.ssec.mcidasv.Constants;
079    import edu.wisc.ssec.mcidasv.data.AxformInfo;
080    import edu.wisc.ssec.mcidasv.data.EnviInfo;
081    import edu.wisc.ssec.mcidasv.data.HeaderInfo;
082    import edu.wisc.ssec.mcidasv.util.McVGuiUtils;
083    import edu.wisc.ssec.mcidasv.util.McVGuiUtils.Position;
084    import edu.wisc.ssec.mcidasv.util.McVGuiUtils.Prefer;
085    import edu.wisc.ssec.mcidasv.util.McVGuiUtils.TextColor;
086    import edu.wisc.ssec.mcidasv.util.McVGuiUtils.Width;
087    import edu.wisc.ssec.mcidasv.util.McVGuiUtils.IconPanel;
088    
089    /**
090     * @author SSEC Development Team
091     */
092    
093    public class FlatFileChooser extends IdvChooser implements Constants {
094    
095        /** Set default stride to keep dimensions within this */
096        private int maxDefDim = 1000;
097        
098        // Properties associated with the button selector
099        private File dataFile;
100        private JTextField dataFileText = new JTextField();
101        private JButton dataFileButton = new JButton();
102        private JLabel dataFileDescription = new JLabel();
103        private JLabel textDescription = new JLabel();
104        
105        // Dimensions
106        // elements, lines, bands
107        private JTextField textElements = new JTextField();
108        private JTextField textLines = new JTextField();
109        private JTextField textBands = new JTextField();
110        private JTextField textUnit = new JTextField();
111        private JTextField textStride = new JTextField();
112        private JCheckBox checkTranspose = new JCheckBox("Transpose elements/lines");
113        private List bandNames = new ArrayList();
114        private List bandFiles = new ArrayList();
115    
116        // Navigation
117        // lat/lon files or bounds
118        private JRadioButton radioLatLonFiles = new JRadioButton("Files", true);
119        private JRadioButton radioLatLonBounds = new JRadioButton("Bounds", false);
120        private File latFile, lonFile;
121        private JLabel textLatFile = new JLabel();
122        private JButton buttonLatFile = new JButton();
123        private JLabel textLonFile = new JLabel();
124        private JButton buttonLonFile = new JButton();
125        private JPanel panelLatLonFiles = new JPanel();
126        private JTextField textLatUL = new JTextField();
127        private JTextField textLonUL = new JTextField();
128        private JTextField textLatLR = new JTextField();
129        private JTextField textLonLR = new JTextField();
130        private JPanel panelLatLonBounds = new JPanel();
131        private JTextField textLatLonScale = new JTextField();
132        private JCheckBox checkEastPositive = new JCheckBox("East positive");
133    
134    
135        // Properties associated with the data file
136        // bytes/pixel, ASCII delimiter, endianness, interleave, offset, missing
137        private JRadioButton radioBinary = new JRadioButton("Binary", true);
138        private JRadioButton radioASCII = new JRadioButton("ASCII", false);
139        private JRadioButton radioImage = new JRadioButton("Image", false);
140        private JRadioButton radioEndianLittle = new JRadioButton("Little", true);
141        private JRadioButton radioEndianBig = new JRadioButton("Big", false);
142        private JComboBox comboByteFormat = new JComboBox();
143        private JComboBox comboInterleave = new JComboBox();
144        private JTextField textOffset = new JTextField();
145        private JPanel panelBinary = new JPanel();
146        private JTextField textDelimiter = new JTextField();
147        private JPanel panelASCII = new JPanel();
148        private JPanel panelImage = new JPanel();
149        private JTextField textMissing = new JTextField();
150        
151        private List<TwoFacedObject> listByteFormat = Misc.newList(new TwoFacedObject[] {
152                new TwoFacedObject("1-byte unsigned integer", HeaderInfo.kFormat1ByteUInt),
153                new TwoFacedObject("2-byte signed integer", HeaderInfo.kFormat2ByteSInt),
154                new TwoFacedObject("4-byte signed integer", HeaderInfo.kFormat4ByteSInt),
155                new TwoFacedObject("4-byte float", HeaderInfo.kFormat4ByteFloat),
156                new TwoFacedObject("8-byte double", HeaderInfo.kFormat8ByteDouble),
157                new TwoFacedObject("2x8-byte complex number", HeaderInfo.kFormat2x8Byte),
158                new TwoFacedObject("2-byte unsigned integer", HeaderInfo.kFormat2ByteUInt)
159        });
160    
161        private List<TwoFacedObject> listInterleave = Misc.newList(
162                new TwoFacedObject("Sequential", HeaderInfo.kInterleaveSequential),
163                new TwoFacedObject("By line", HeaderInfo.kInterleaveByLine),
164                new TwoFacedObject("By pixel", HeaderInfo.kInterleaveByPixel));
165    
166        private JLabel statusLabel = new JLabel("Status");
167        
168        /**
169         * Super setStatus() takes a second string to enable "simple" mode
170         * which highlights the required component.  We don't really care
171         * about that feature, and we don't want getStatusLabel() to
172         * change the label background color.
173         */
174        @Override
175        public void setStatus(String statusString, String foo) {
176            if (statusString == null)
177                statusString = "";
178            statusLabel.setText(statusString);
179        }
180    
181        /**
182         * Get a handle on the IDV
183         */
184        protected IntegratedDataViewer idv = getIdv();
185    
186        /**
187         * Create the FileChooser, passing in the manager and the xml element
188         * from choosers.xml
189         *
190         * @param mgr The manager
191         * @param root The xml root
192         *
193         */
194        public FlatFileChooser(IdvChooserManager mgr, Element root) {
195            super(mgr, root);
196            
197            loadButton = McVGuiUtils.makeImageTextButton(ICON_ACCEPT_SMALL, getLoadCommandName());
198            loadButton.setActionCommand(getLoadCommandName());
199            loadButton.addActionListener(this);
200    
201            dataFileButton = McVGuiUtils.makeImageButton(ICON_OPEN, "Open file");
202            dataFileButton.addActionListener(new ActionListener(){
203                public void actionPerformed(ActionEvent e) {
204                    dataFile = getDataFile(dataFile);
205                    if (dataFile!=null) {
206                        dataFileText.setText(dataFile.getAbsolutePath());
207                        inspectDataFile(dataFile);
208                    }
209                }
210            });
211            dataFileText.addActionListener(new ActionListener(){
212                public void actionPerformed(ActionEvent e) {
213                    dataFile = new File(dataFileText.getText());
214                    inspectDataFile(dataFile);
215                }
216            });
217            dataFileText.addFocusListener(new FocusListener() {
218                public void focusGained(FocusEvent e) {}
219                public void focusLost(FocusEvent e) {
220                    dataFile = new File(dataFileText.getText());
221                    inspectDataFile(dataFile);
222                }
223            });
224            
225            radioLatLonFiles.addActionListener(new ActionListener(){
226                public void actionPerformed(ActionEvent e) {
227                    checkSetLatLon();
228                }
229            });
230            radioLatLonBounds.addActionListener(new ActionListener(){
231                public void actionPerformed(ActionEvent e) {
232                    checkSetLatLon();
233                }
234            });
235    
236            buttonLatFile = McVGuiUtils.makeImageButton(ICON_OPEN, "Select latitude file");
237            buttonLatFile.addActionListener(new ActionListener(){
238                public void actionPerformed(ActionEvent e) {
239                    latFile = getDataFile(latFile);
240                    if (latFile!=null)
241                        textLatFile.setText(latFile.getName());
242                }
243            });
244            buttonLonFile = McVGuiUtils.makeImageButton(ICON_OPEN, "Select longitude file");
245            buttonLonFile.addActionListener(new ActionListener(){
246                public void actionPerformed(ActionEvent e) {
247                    lonFile = getDataFile(lonFile);
248                    if (lonFile!=null)
249                        textLonFile.setText(lonFile.getName());
250                }
251            });
252            GuiUtils.buttonGroup(radioLatLonFiles, radioLatLonBounds);
253            
254            radioBinary.addActionListener(new ActionListener(){
255                public void actionPerformed(ActionEvent e) {
256                    checkSetBinaryASCIIImage();
257                }
258            });
259            radioASCII.addActionListener(new ActionListener(){
260                public void actionPerformed(ActionEvent e) {
261                    checkSetBinaryASCIIImage();
262                }
263            });
264            radioImage.addActionListener(new ActionListener(){
265                public void actionPerformed(ActionEvent e) {
266                    checkSetBinaryASCIIImage();
267                }
268            });
269            GuiUtils.buttonGroup(radioBinary, radioASCII, radioImage);
270            GuiUtils.buttonGroup(radioEndianLittle, radioEndianBig);
271    
272            GuiUtils.setListData(comboByteFormat, listByteFormat);
273            GuiUtils.setListData(comboInterleave, listInterleave);
274    
275            setHaveData(false);
276        }
277        
278        /**
279         * enable/disable widgets for navigation
280         */
281        private void checkSetLatLon() {
282            boolean isFile = radioLatLonFiles.isSelected();
283            GuiUtils.enableTree(panelLatLonFiles, isFile);
284            GuiUtils.enableTree(panelLatLonBounds, !isFile);
285        }
286    
287        /**
288         * enable/disable widgets for binary/ASCII
289         */
290        private void checkSetBinaryASCIIImage() {
291            GuiUtils.enableTree(panelBinary, radioBinary.isSelected());
292            GuiUtils.enableTree(panelASCII, radioASCII.isSelected());
293            GuiUtils.enableTree(panelImage, radioImage.isSelected());
294        }
295    
296        /**
297         * Set whether the user has made a selection that contains data.
298         *
299         * @param have   true to set the haveData property.  Enables the
300         *               loading button
301         */
302        public void setHaveData(boolean have) {
303            super.setHaveData(have);
304            updateStatus();
305        }
306        
307        /**
308         * Set the status message appropriately
309         */
310        protected void updateStatus() {
311            super.updateStatus();
312            checkSetLatLon();
313            checkSetBinaryASCIIImage();
314            if(!getHaveData()) {
315                setStatus("Select a file"); 
316            }
317        }
318    
319        /**
320         * Inspect the selected data file
321         * Determine if it is a known header file type
322         * 
323         * @param thisFile
324         * @throws FileNotFoundException 
325         */
326        private void inspectDataFile(File thisFile) {
327            if (thisFile == null || thisFile.getName()=="") {
328                dataFileDescription.setText("");
329                setHaveData(false);
330                return;
331            }
332            if (!thisFile.exists()) {
333                dataFileDescription.setText("File does not exist");
334                setHaveData(false);
335                return;
336            }
337            try {
338                FileReader fr = new FileReader(thisFile);
339                char first80c[] = new char[80];
340                fr.read(first80c, 0, 80);
341                fr.close();
342                String first80 = new String(first80c);
343                clearValues();
344                boolean doStride = false;
345                if (IOUtil.hasSuffix(thisFile.getName(), ".gif") ||
346                        IOUtil.hasSuffix(thisFile.getName(), ".jpg") ||
347                        IOUtil.hasSuffix(thisFile.getName(), ".png")) {
348                    dataFileDescription.setText("Image file");
349                    processImageFile(thisFile);
350                }
351                else if (IOUtil.hasSuffix(thisFile.getName(), ".xml") ||
352                        IOUtil.hasSuffix(thisFile.getName(), ".ximg")) {
353                    dataFileDescription.setText("XML image header file");
354                    processXmlHeaderFile(thisFile);
355                }
356                else if (first80.indexOf("                     Space Science & Engineering Center") >= 0) {
357                    dataFileDescription.setText("McIDAS-X AXFORM header file");
358                    processAxformHeaderFile(thisFile);
359                    doStride = true;
360                }
361                else if (first80.indexOf("ENVI") >= 0) {
362                    dataFileDescription.setText("ENVI header file");
363                    processEnviHeaderFile(thisFile);
364                    doStride = true;
365                }
366                else {
367                    dataFileDescription.setText("Binary, ASCII or Image data");
368                    processGenericFile(thisFile);
369                    doStride = true;
370                }
371                
372                // Default the stride
373                int newStride = 1;
374                if (doStride) {
375                    String textLinesText = textLines.getText();
376                    String textElementsText = textElements.getText();
377                    if (!(textLinesText.equalsIgnoreCase("") || textElementsText.equalsIgnoreCase(""))) {
378                        int myLines = Integer.parseInt(textLinesText);
379                        int myElements = Integer.parseInt(textElementsText);
380                        if (myLines > maxDefDim || myElements > maxDefDim) {
381                            newStride = Math.max((int)Math.ceil((float)myLines/(float)maxDefDim), (int)Math.ceil((float)myElements/(float)maxDefDim));
382                        }
383                    }
384                }
385                textStride.setText(Integer.toString(newStride));
386                
387                setHaveData(true);
388            }
389            catch (Exception e) {
390                e.printStackTrace();
391            }
392        }
393            
394        /**
395         * Special processing for a known data type
396         * This deals specifically with AXFORM header files
397         */
398        private void processAxformHeaderFile(File thisFile) {
399            try {
400                AxformInfo axformInfo = new AxformInfo(thisFile);
401                
402                // Set the properties in the GUI
403                textDescription.setText(axformInfo.getParameter(HeaderInfo.DESCRIPTION, ""));
404                textElements.setText((axformInfo.getParameter(HeaderInfo.ELEMENTS, 0)).toString());
405                textLines.setText((axformInfo.getParameter(HeaderInfo.LINES, 0)).toString());
406                textUnit.setText((axformInfo.getParameter(HeaderInfo.UNIT, "")).toString());
407                bandNames = (List)axformInfo.getParameter(HeaderInfo.BANDNAMES, new ArrayList());
408                bandFiles = (List)axformInfo.getParameter(HeaderInfo.BANDFILES, new ArrayList());
409                textBands.setText(Integer.toString(bandNames.size()));
410                textOffset.setText((axformInfo.getParameter(HeaderInfo.OFFSET, 0)).toString());
411                textMissing.setText((axformInfo.getParameter(HeaderInfo.MISSINGVALUE, (float)0)).toString());
412                
413                Integer dataType = (Integer)axformInfo.getParameter(HeaderInfo.DATATYPE, HeaderInfo.kFormatUnknown);
414                Boolean bigEndian = (Boolean)axformInfo.getParameter(HeaderInfo.BIGENDIAN, false);
415                if (dataType==HeaderInfo.kFormatASCII) {
416                    radioASCII.setSelected(true);
417                }
418                else if (dataType==HeaderInfo.kFormatImage) {
419                    radioImage.setSelected(true);
420                }
421                else {
422                    radioBinary.setSelected(true);
423                    TwoFacedObject tfo = TwoFacedObject.findId(dataType.intValue(), listByteFormat);
424                    if (tfo!=null)
425                        comboByteFormat.setSelectedItem(tfo);
426                    tfo = TwoFacedObject.findId(HeaderInfo.kInterleaveSequential, listInterleave);
427                    if (tfo!=null)
428                        comboInterleave.setSelectedItem(tfo);
429                }
430                
431                radioEndianLittle.setSelected(!bigEndian.booleanValue());
432                radioEndianBig.setSelected(bigEndian.booleanValue());
433    
434                List latlonFiles = axformInfo.getParameter(HeaderInfo.NAVFILES, new ArrayList());
435                if (latlonFiles.size() == 2) {
436                    latFile = new File((String)latlonFiles.get(0));
437                    lonFile = new File((String)latlonFiles.get(1));
438                }
439    
440                if (latFile==null || lonFile==null) {
441                    radioLatLonBounds.setSelected(true);
442                }
443                else {
444                    textLatFile.setText(latFile.getName());
445                    textLonFile.setText(lonFile.getName());
446                    radioLatLonFiles.setSelected(true);
447                }
448                
449                textLatLonScale.setText("100");
450                checkEastPositive.setSelected(true);
451                
452            }
453            catch (Exception e) {
454                e.printStackTrace();
455            }
456        }
457    
458        /**
459         * Special processing for a known data type
460         * This deals specifically with ENVI header files
461         */
462        private void processEnviHeaderFile(File thisFile) {
463            try {
464                EnviInfo enviInfo = new EnviInfo(thisFile);
465                
466                // Set the properties in the GUI
467                textDescription.setText(enviInfo.getParameter(HeaderInfo.DESCRIPTION, ""));
468                textElements.setText((enviInfo.getParameter(HeaderInfo.ELEMENTS, 0)).toString());
469                textLines.setText((enviInfo.getParameter(HeaderInfo.LINES, 0)).toString());
470                textUnit.setText((enviInfo.getParameter(HeaderInfo.UNIT, "")).toString());
471                bandNames = (List)enviInfo.getParameter(HeaderInfo.BANDNAMES, new ArrayList());
472                bandFiles = (List)enviInfo.getParameter(HeaderInfo.BANDFILES, new ArrayList());
473                textBands.setText(Integer.toString(bandNames.size()));
474                textOffset.setText((enviInfo.getParameter(HeaderInfo.OFFSET, 0)).toString());
475                textMissing.setText((enviInfo.getParameter(HeaderInfo.MISSINGVALUE, (float)0)).toString());
476    
477                Integer dataType = (Integer)enviInfo.getParameter(HeaderInfo.DATATYPE, HeaderInfo.kFormatUnknown);
478                String interleaveType = (enviInfo.getParameter(HeaderInfo.INTERLEAVE, HeaderInfo.kInterleaveSequential)).toString();
479                Boolean bigEndian = (Boolean)enviInfo.getParameter(HeaderInfo.BIGENDIAN, false);
480                radioBinary.setSelected(true);
481                TwoFacedObject tfo = TwoFacedObject.findId(dataType.intValue(), listByteFormat);
482                if (tfo!=null)
483                    comboByteFormat.setSelectedItem(tfo);
484                tfo = TwoFacedObject.findId(interleaveType, listInterleave);
485                if (tfo!=null)
486                    comboInterleave.setSelectedItem(tfo);
487                
488                radioEndianLittle.setSelected(!bigEndian.booleanValue());
489                radioEndianBig.setSelected(bigEndian.booleanValue());
490    
491                // Look for a geo.hdr file that contains Latitude and Longitude bands
492                String parent = thisFile.getParent();
493                if (parent==null) parent=".";
494                String navFile = thisFile.getName().replace(".hdr", "");
495                int lastDot = navFile.lastIndexOf(".");
496                if (lastDot >= 0) {
497                    navFile = navFile.substring(0, lastDot) + ".geo.hdr";
498                }
499                navFile = parent + "/" + navFile;
500                EnviInfo navInfo = new EnviInfo(navFile);
501                if (navInfo.isNavHeader()) {
502                    latFile = new File(navFile);
503                    lonFile = new File(navFile);
504                }
505                
506                if (latFile==null || lonFile==null) {
507                    radioLatLonBounds.setSelected(true);
508                }
509                else {
510                    textLatFile.setText(latFile.getName());
511                    textLonFile.setText(lonFile.getName());
512                    radioLatLonFiles.setSelected(true);
513                }
514                
515                textLatLonScale.setText("1");
516                checkEastPositive.setSelected(false);
517    
518            }
519            catch (Exception e) {
520                e.printStackTrace();
521            }
522        }
523    
524        /**
525         * Special processing for a known data type
526         * This deals specifically with XML header files
527         */
528        private void processXmlHeaderFile(File thisFile) {
529            try {
530                
531                String description = "";
532                int lines = 0;
533                int elements = 0;
534                float missingVal = -1;
535                
536                bandFiles = new ArrayList();
537                bandNames = new ArrayList();
538    
539                Element root = XmlUtil.getRoot(thisFile.getAbsolutePath(), getClass());
540                if (!root.getTagName().equals("image")) {
541                    processGenericFile(thisFile);
542                    return;
543                }
544                
545                description = XmlUtil.getAttribute(root, "name", (String) null);
546                String url = "";
547                if (XmlUtil.hasAttribute(root, "url")) {
548                    url = XmlUtil.getAttribute(root, "url");
549                    if (description == "") {
550                        description = url;
551                    }
552                    String parent = thisFile.getParent();
553                    if (parent==null) parent=".";
554                    url = parent + "/" + url;
555                }
556                else {
557                    processGenericFile(thisFile);
558                    return;
559                }
560                if (XmlUtil.hasAttribute(root, "ullat")) {
561                    radioLatLonBounds.setSelected(true);
562                    textLatUL.setText(XmlUtil.getAttribute(root, "ullat"));
563                }
564                if (XmlUtil.hasAttribute(root, "ullon")) {
565                    radioLatLonBounds.setSelected(true);
566                    textLonUL.setText(XmlUtil.getAttribute(root, "ullon"));
567                }
568                if (XmlUtil.hasAttribute(root, "lrlat")) {
569                    radioLatLonBounds.setSelected(true);
570                    textLatLR.setText(XmlUtil.getAttribute(root, "lrlat"));
571                }
572                if (XmlUtil.hasAttribute(root, "lrlon")) {
573                    radioLatLonBounds.setSelected(true);
574                    textLonLR.setText(XmlUtil.getAttribute(root, "lrlon"));
575                }
576    
577                // Try to read the referenced image to get lines and elements
578                setStatus("Loading image");
579                InputStream is   = null;
580                is = IOUtil.getInputStream(url, getClass());
581                byte[] imageContent = IOUtil.readBytes(is);
582                Image image = Toolkit.getDefaultToolkit().createImage(imageContent);
583                MediaTracker tracker = new MediaTracker(this); 
584                tracker.addImage(image, 0);
585                try { 
586                    tracker.waitForAll(); 
587                } 
588                catch(InterruptedException e) {}
589                ImageHelper ih = new ImageHelper();
590                image.getWidth(ih);
591                if (ih.badImage) {
592                    throw new IllegalStateException("Bad image: " + url);
593                }
594                elements = image.getWidth(ih);
595                lines = image.getHeight(ih);
596                
597                // Bands
598                bandFiles.add(url);
599                bandNames.add("XML image file");
600                
601                // Set the properties in the GUI
602                textDescription.setText(description);
603                textElements.setText(Integer.toString(elements));
604                textLines.setText(Integer.toString(lines));
605                textBands.setText(Integer.toString(bandNames.size()));
606    
607                radioImage.setSelected(true);
608                textMissing.setText(Float.toString(missingVal));
609                
610                textLatLonScale.setText("1");
611                checkEastPositive.setSelected(false);
612    
613            }
614            catch (Exception e) {
615                e.printStackTrace();
616            }
617        }
618    
619        /**
620         * Special processing for a known data type
621         * This deals specifically with XML header files
622         */
623        private void processImageFile(File thisFile) {
624            try {
625                
626                String description = "";
627                int lines = 0;
628                int elements = 0;
629                float missingVal = -1;
630                
631                bandFiles = new ArrayList();
632                bandNames = new ArrayList();
633    
634                description = thisFile.getName();
635                String url = thisFile.getAbsolutePath();
636    
637                // Try to read the referenced image to get lines and elements
638                setStatus("Loading image");
639                InputStream is   = null;
640                is = IOUtil.getInputStream(url, getClass());
641                byte[] imageContent = IOUtil.readBytes(is);
642                Image image = Toolkit.getDefaultToolkit().createImage(imageContent);
643                MediaTracker tracker = new MediaTracker(this); 
644                tracker.addImage(image, 0);
645                try { 
646                    tracker.waitForAll(); 
647                } 
648                catch(InterruptedException e) {}
649                ImageHelper ih = new ImageHelper();
650                image.getWidth(ih);
651                if (ih.badImage) {
652                    throw new IllegalStateException("Bad image: " + url);
653                }
654                elements = image.getWidth(ih);
655                lines = image.getHeight(ih);
656    
657                // Bands
658                bandFiles.add(url);
659                bandNames.add("Image file");
660    
661                // Set the properties in the GUI
662                textDescription.setText(description);
663                textElements.setText(Integer.toString(elements));
664                textLines.setText(Integer.toString(lines));
665                textBands.setText(Integer.toString(bandNames.size()));
666                
667                radioImage.setSelected(true);
668                textMissing.setText(Float.toString(missingVal));
669                
670                textLatLonScale.setText("1");
671                checkEastPositive.setSelected(false);
672    
673            }
674            catch (Exception e) {
675                e.printStackTrace();
676            }
677        }
678    
679        /**
680         * Special processing for an unknown data type
681         * Can we glean anything about the file by inspecting it more?
682         */
683        private void processGenericFile(File thisFile) {
684    
685            clearValues();
686    
687            // Set appropriate defaults
688            // Bands
689            bandFiles.add(thisFile.getAbsolutePath());
690            bandNames.add("Flat data");
691    
692            // Set the properties in the GUI
693            textDescription.setText(thisFile.getName());
694    //      textElements.setText(Integer.toString(elements));
695    //      textLines.setText(Integer.toString(lines));
696            textBands.setText("1");
697            
698            radioBinary.setSelected(true);
699            
700            textLatLonScale.setText("1");
701            checkEastPositive.setSelected(false);
702    
703        }
704        
705        /**
706         * Clear out any data values presented to the user
707         */
708        private void clearValues() {
709            textDescription.setText("");
710            textElements.setText("");
711            textLines.setText("");
712            textBands.setText("");
713            textUnit.setText("");
714            textStride.setText("");
715            checkTranspose.setSelected(false);
716    
717            textLatFile.setText("");
718            textLonFile.setText("");
719            textLatUL.setText("");
720            textLonUL.setText("");
721            textLatLR.setText("");
722            textLonLR.setText("");
723    
724            textLatLonScale.setText("");
725            checkEastPositive.setSelected(false);
726    
727            textOffset.setText("");
728            textMissing.setText("");
729        }
730        
731        /**
732         * Ask the user for a data file
733         */
734        private File getDataFile(File thisFile) {
735            JFileChooser fileChooser = new JFileChooser(thisFile);
736            fileChooser.setMultiSelectionEnabled(false);
737            int status = fileChooser.showOpenDialog(null);
738            if (status == JFileChooser.APPROVE_OPTION) {
739                thisFile = fileChooser.getSelectedFile();
740            }
741            return(thisFile);
742        }
743        
744        /**
745         * Get the name of the dataset.
746         *
747         * @return descriptive name of the dataset.
748         */
749        public String getDatasetName() {
750            return "Data Set Name";
751        }
752        
753        /**
754         * Get the properties from the datasource
755         *
756         * @param ht  a Hashtable of properties
757         */
758        protected void getDataSourceProperties(Hashtable ht) {
759            super.getDataSourceProperties(ht);
760            ht.put("FLAT.NAME", textDescription.getText());
761            ht.put("FLAT.ELEMENTS", textElements.getText());
762            ht.put("FLAT.LINES", textLines.getText());
763            ht.put("FLAT.BANDNAMES", bandNames);
764            ht.put("FLAT.BANDFILES", bandFiles);
765            ht.put("FLAT.UNIT", textUnit.getText());
766            ht.put("FLAT.STRIDE", textStride.getText());
767            ht.put("FLAT.TRANSPOSE", checkTranspose.isSelected());
768            ht.put("FLAT.MISSING", textMissing.getText());
769            
770            // Navigation
771            if (radioLatLonFiles.isSelected()) {
772                ht.put("NAV.TYPE", "FILES");
773                ht.put("FILE.LAT", latFile.getAbsolutePath());
774                ht.put("FILE.LON", lonFile.getAbsolutePath());
775            }
776            else if (radioLatLonBounds.isSelected()) {
777                ht.put("NAV.TYPE", "BOUNDS");
778                ht.put("BOUNDS.ULLAT", textLatUL.getText());
779                ht.put("BOUNDS.ULLON", textLonUL.getText());
780                ht.put("BOUNDS.LRLAT", textLatLR.getText());
781                ht.put("BOUNDS.LRLON", textLonLR.getText());
782            }
783            else {
784                ht.put("NAV.TYPE", "UNKNOWN");
785            }
786            ht.put("NAV.SCALE", textLatLonScale.getText());
787            ht.put("NAV.EASTPOS", checkEastPositive.isSelected());
788    
789            // Data type
790            if (radioBinary.isSelected()) {
791                TwoFacedObject format = (TwoFacedObject) comboByteFormat.getSelectedItem();
792                TwoFacedObject interleave = (TwoFacedObject) comboInterleave.getSelectedItem();
793                ht.put("FORMAT.TYPE", "BINARY");
794                ht.put("BINARY.FORMAT", format.getId());
795                ht.put("BINARY.INTERLEAVE", interleave.getId());
796                ht.put("BINARY.BIGENDIAN", radioEndianBig.isSelected());
797                ht.put("BINARY.OFFSET", textOffset.getText());
798            }
799            else if (radioASCII.isSelected()) {
800                ht.put("FORMAT.TYPE", "ASCII");
801                ht.put("ASCII.DELIMITER", textDelimiter.getText());
802            }
803            else if (radioImage.isSelected()) {
804                ht.put("FORMAT.TYPE", "IMAGE");         
805            }
806            else {
807                ht.put("FORMAT.TYPE", "UNKNOWN");
808            }
809    
810        }
811            
812        /**
813         * User said go, we go. Simply get the list of images
814         * from the imageChooser and create the FILE.FLAT
815         * DataSource
816         *
817         */
818        public void doLoadInThread() {
819            String definingObject = dataFileText.getText();
820            String dataType = "FILE.FLAT";
821            
822            Hashtable properties = new Hashtable();
823            getDataSourceProperties(properties);
824            
825            makeDataSource(definingObject, dataType, properties);
826        }
827        
828        /**
829         * The dimensions inner panel
830         */
831        protected JPanel makeDimensionsPanel() {
832            JPanel myPanel = new JPanel();
833            myPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Dimensions"));
834            
835            JLabel elementsLabel = McVGuiUtils.makeLabelRight("Elements:");
836            McVGuiUtils.setComponentWidth(textElements);
837            
838            JLabel linesLabel = McVGuiUtils.makeLabelRight("Lines:");
839            McVGuiUtils.setComponentWidth(textLines);
840            
841            JLabel bandsLabel = McVGuiUtils.makeLabelRight("Bands:");
842            McVGuiUtils.setComponentWidth(textBands);
843    
844            JLabel unitLabel = McVGuiUtils.makeLabelRight("Units:");
845            McVGuiUtils.setComponentWidth(textUnit);
846    
847            JLabel strideLabel = McVGuiUtils.makeLabelRight("Sampling:");
848            McVGuiUtils.setComponentWidth(textStride);
849    
850    //      JLabel transposeLabel = McVGuiUtils.makeLabelRight("");
851            
852            GroupLayout layout = new GroupLayout(myPanel);
853            myPanel.setLayout(layout);
854            layout.setHorizontalGroup(
855                layout.createParallelGroup(LEADING)
856                .addGroup(layout.createSequentialGroup()
857                    .addContainerGap()
858                    .addGroup(layout.createParallelGroup(LEADING)
859                        .addGroup(layout.createSequentialGroup()
860                            .addComponent(elementsLabel)
861                            .addGap(GAP_RELATED)
862                            .addComponent(textElements))
863                        .addGroup(layout.createSequentialGroup()
864                            .addComponent(linesLabel)
865                            .addGap(GAP_RELATED)
866                            .addComponent(textLines))
867                        .addGroup(layout.createSequentialGroup()
868                            .addComponent(bandsLabel)
869                            .addGap(GAP_RELATED)
870                            .addComponent(textBands))
871                        .addGroup(layout.createSequentialGroup()
872                            .addComponent(unitLabel)
873                            .addGap(GAP_RELATED)
874                            .addComponent(textUnit))
875                        .addGroup(layout.createSequentialGroup()
876                            .addComponent(strideLabel)
877                            .addGap(GAP_RELATED)
878                            .addComponent(textStride)))
879                    .addContainerGap())
880            );
881            layout.setVerticalGroup(
882                layout.createParallelGroup(LEADING)
883                .addGroup(TRAILING, layout.createSequentialGroup()
884                    .addContainerGap()
885                    .addGroup(layout.createParallelGroup(BASELINE)
886                        .addComponent(textElements)
887                        .addComponent(elementsLabel))
888                    .addPreferredGap(RELATED)
889                    .addGroup(layout.createParallelGroup(BASELINE)
890                        .addComponent(textLines)
891                        .addComponent(linesLabel))
892                    .addPreferredGap(RELATED)
893                    .addGroup(layout.createParallelGroup(BASELINE)
894                        .addComponent(textBands)
895                        .addComponent(bandsLabel))
896                    .addPreferredGap(RELATED)
897                    .addGroup(layout.createParallelGroup(BASELINE)
898                        .addComponent(textUnit)
899                        .addComponent(unitLabel))
900                    .addPreferredGap(RELATED)
901                    .addGroup(layout.createParallelGroup(BASELINE)
902                        .addComponent(textStride)
903                        .addComponent(strideLabel))
904                    .addContainerGap())
905            );
906            
907            return myPanel;
908        }
909    
910        /**
911         * The navigation inner panel
912         */
913        protected JPanel makeNavigationPanel() {
914            JPanel myPanel = new JPanel();
915            myPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Navigation"));
916            
917            McVGuiUtils.setComponentWidth(textLatFile, Width.DOUBLE);
918            McVGuiUtils.setComponentWidth(textLonFile, Width.DOUBLE);
919            panelLatLonFiles = McVGuiUtils.topBottom(
920                    GuiUtils.leftRight(McVGuiUtils.makeLabeledComponent("Latitude:",textLatFile), buttonLatFile),
921                    GuiUtils.leftRight(McVGuiUtils.makeLabeledComponent("Longitude:",textLonFile), buttonLonFile),
922                    Prefer.NEITHER);
923            
924            // Images to make the bounds more clear
925            IconPanel urPanel = new IconPanel("/edu/wisc/ssec/mcidasv/images/upper_right.gif");
926            IconPanel llPanel = new IconPanel("/edu/wisc/ssec/mcidasv/images/lower_left.gif");
927            
928            McVGuiUtils.setComponentWidth(textLatUL);
929            McVGuiUtils.setComponentWidth(textLonUL);
930            McVGuiUtils.setComponentWidth(textLatLR);
931            McVGuiUtils.setComponentWidth(textLonLR);
932            panelLatLonBounds = McVGuiUtils.topBottom(
933                    McVGuiUtils.makeLabeledComponent("UL Lat/Lon:", GuiUtils.leftRight(GuiUtils.hbox(textLatUL, textLonUL), urPanel)),
934                    McVGuiUtils.makeLabeledComponent("LR Lat/Lon:", GuiUtils.leftRight(llPanel, GuiUtils.hbox(textLatLR, textLonLR))),
935                    Prefer.NEITHER);
936            
937            McVGuiUtils.setComponentWidth(radioLatLonFiles);
938            McVGuiUtils.setComponentWidth(radioLatLonBounds);
939            
940            JLabel labelScale = McVGuiUtils.makeLabelRight("Scale:");
941            McVGuiUtils.setComponentWidth(textLatLonScale);
942            
943            JPanel panelScaleEastPositive = GuiUtils.hbox(textLatLonScale, checkEastPositive);
944    
945            JLabel labelEastPositive = McVGuiUtils.makeLabelRight("");
946            
947            GroupLayout layout = new GroupLayout(myPanel);
948            myPanel.setLayout(layout);
949            layout.setHorizontalGroup(
950                layout.createParallelGroup(LEADING)
951                .addGroup(layout.createSequentialGroup()
952                    .addContainerGap()
953                    .addGroup(layout.createParallelGroup(LEADING)
954                        .addGroup(layout.createSequentialGroup()
955                            .addComponent(radioLatLonFiles)
956                            .addGap(GAP_RELATED)
957                            .addComponent(panelLatLonFiles))
958                        .addGroup(layout.createSequentialGroup()
959                            .addComponent(radioLatLonBounds)
960                            .addGap(GAP_RELATED)
961                            .addComponent(panelLatLonBounds))
962                        .addGroup(layout.createSequentialGroup()
963                            .addComponent(labelScale)
964                            .addGap(GAP_RELATED)
965                            .addComponent(panelScaleEastPositive)))
966                    .addContainerGap())
967            );
968            layout.setVerticalGroup(
969                layout.createParallelGroup(LEADING)
970                .addGroup(TRAILING, layout.createSequentialGroup()
971                    .addContainerGap()
972                    .addGroup(layout.createParallelGroup(BASELINE)
973                        .addComponent(radioLatLonFiles)
974                        .addComponent(panelLatLonFiles))
975                    .addPreferredGap(RELATED)
976                    .addGroup(layout.createParallelGroup(BASELINE)
977                        .addComponent(radioLatLonBounds)
978                        .addComponent(panelLatLonBounds))
979                    .addPreferredGap(RELATED)
980                    .addGroup(layout.createParallelGroup(BASELINE)
981                        .addComponent(labelScale)
982                        .addComponent(panelScaleEastPositive))
983                    .addContainerGap())
984            );
985            
986            return myPanel;
987        }
988    
989        /**
990         * The format inner panel
991         */
992        protected JPanel makeFormatPanel() {
993            JPanel myPanel = new JPanel();
994            myPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Format"));
995    
996            McVGuiUtils.setComponentWidth(radioBinary);
997            McVGuiUtils.setComponentWidth(radioASCII);
998            McVGuiUtils.setComponentWidth(radioImage);
999            McVGuiUtils.setComponentWidth(radioEndianLittle);
1000            McVGuiUtils.setComponentWidth(radioEndianBig);
1001    
1002            McVGuiUtils.setComponentWidth(comboByteFormat, Width.TRIPLE);
1003            McVGuiUtils.setComponentWidth(comboInterleave, Width.DOUBLE);
1004            McVGuiUtils.setComponentWidth(textOffset, Width.HALF);
1005    
1006            panelBinary = McVGuiUtils.topBottom(
1007                    McVGuiUtils.topBottom(
1008                            McVGuiUtils.makeLabeledComponent("Byte format:", comboByteFormat),
1009                            McVGuiUtils.makeLabeledComponent("Interleave:", comboInterleave),
1010                            Prefer.NEITHER),
1011                    McVGuiUtils.topBottom(
1012                            McVGuiUtils.makeLabeledComponent("Endian:", GuiUtils.hbox(radioEndianLittle, radioEndianBig, GAP_RELATED)),
1013                            McVGuiUtils.makeLabeledComponent("Offset:", McVGuiUtils.makeComponentLabeled(textOffset, "bytes")),
1014                            Prefer.NEITHER),
1015                    Prefer.NEITHER);
1016            
1017            McVGuiUtils.setComponentWidth(textDelimiter, Width.HALF);
1018            panelASCII = McVGuiUtils.makeLabeledComponent("Delimiter:", textDelimiter);
1019            panelImage = new JPanel();
1020            
1021            JLabel missingLabel = McVGuiUtils.makeLabelRight("Missing value:");
1022            McVGuiUtils.setComponentWidth(textMissing);
1023            JPanel missingPanel = McVGuiUtils.makeComponentLabeled(textMissing, "");
1024    
1025            GroupLayout layout = new GroupLayout(myPanel);
1026            myPanel.setLayout(layout);
1027            layout.setHorizontalGroup(
1028                layout.createParallelGroup(LEADING)
1029                .addGroup(layout.createSequentialGroup()
1030                    .addContainerGap()
1031                    .addGroup(layout.createParallelGroup(LEADING)
1032                        .addGroup(layout.createSequentialGroup()
1033                            .addComponent(radioBinary)
1034                            .addGap(GAP_RELATED)
1035                            .addComponent(panelBinary))
1036                        .addGroup(layout.createSequentialGroup()
1037                            .addComponent(radioASCII)
1038                            .addGap(GAP_RELATED)
1039                            .addComponent(panelASCII))
1040                        .addGroup(layout.createSequentialGroup()
1041                            .addComponent(radioImage)
1042                            .addGap(GAP_RELATED)
1043                            .addComponent(panelImage))
1044                        .addGroup(layout.createSequentialGroup()
1045                            .addComponent(missingLabel)
1046                            .addGap(GAP_RELATED)
1047                            .addComponent(missingPanel)))
1048                    .addContainerGap())
1049            );
1050            layout.setVerticalGroup(
1051                layout.createParallelGroup(LEADING)
1052                .addGroup(TRAILING, layout.createSequentialGroup()
1053                    .addContainerGap()
1054                    .addGroup(layout.createParallelGroup(BASELINE)
1055                        .addComponent(radioBinary)
1056                        .addComponent(panelBinary))
1057                    .addPreferredGap(RELATED)
1058                    .addGroup(layout.createParallelGroup(BASELINE)
1059                        .addComponent(radioASCII)
1060                        .addComponent(panelASCII))
1061                    .addPreferredGap(RELATED)
1062                    .addGroup(layout.createParallelGroup(BASELINE)
1063                        .addComponent(radioImage)
1064                        .addComponent(panelImage))
1065                    .addPreferredGap(RELATED)
1066                    .addGroup(layout.createParallelGroup(BASELINE)
1067                        .addComponent(missingLabel)
1068                        .addComponent(missingPanel))
1069                    .addContainerGap())
1070            );
1071    
1072            return myPanel;
1073        }
1074    
1075        /**
1076         * The main panel properties panel
1077         */
1078        protected JPanel makePropertiesPanel() {
1079            JPanel thisPanel = new JPanel();
1080            JPanel topPanel = McVGuiUtils.sideBySide(makeDimensionsPanel(), makeNavigationPanel());
1081            JPanel bottomPanel = makeFormatPanel();
1082            return McVGuiUtils.topBottom(topPanel, bottomPanel, Prefer.NEITHER);
1083        }
1084        
1085        /**
1086         * @return The gui of this chooser
1087         */
1088        protected JComponent doMakeContents() {
1089    
1090            Element chooserNode = getXmlNode();
1091            String path = (String) idv.getPreference(PREF_DEFAULTDIR + getId());
1092            if (path == null) {
1093                path = XmlUtil.getAttribute(chooserNode, "path", (String) null);
1094            }
1095    
1096            JPanel myPanel = new JPanel();
1097            
1098            // File
1099            JLabel fileLabel = McVGuiUtils.makeLabelRight("File:");
1100            McVGuiUtils.setComponentWidth(dataFileText, Width.DOUBLEDOUBLE);
1101            
1102            JLabel typeLabel = McVGuiUtils.makeLabelRight("Type:");
1103            McVGuiUtils.setLabelBold(dataFileDescription, true);
1104    
1105            JLabel descriptionLabel = McVGuiUtils.makeLabelRight("Description:");
1106            McVGuiUtils.setLabelBold(textDescription, true);
1107    
1108            JLabel propertiesLabel = McVGuiUtils.makeLabelRight("Properties:");
1109            JPanel propertiesPanel = makePropertiesPanel();
1110            
1111            JLabel statusLabelLabel = McVGuiUtils.makeLabelRight("");
1112            McVGuiUtils.setLabelPosition(statusLabel, Position.RIGHT);
1113            McVGuiUtils.setComponentColor(statusLabel, TextColor.STATUS);
1114                    
1115            JButton helpButton = McVGuiUtils.makeImageButton(ICON_HELP, "Show help");
1116            helpButton.setActionCommand(GuiUtils.CMD_HELP);
1117            helpButton.addActionListener(this);
1118            
1119            McVGuiUtils.setComponentWidth(loadButton, Width.DOUBLE);
1120            
1121            GroupLayout layout = new GroupLayout(myPanel);
1122            myPanel.setLayout(layout);
1123            layout.setHorizontalGroup(
1124                layout.createParallelGroup(LEADING)
1125                .addGroup(layout.createSequentialGroup()
1126                    .addContainerGap()
1127                    .addGroup(layout.createParallelGroup(LEADING)
1128                        .addGroup(layout.createSequentialGroup()
1129                            .addComponent(fileLabel)
1130                            .addGap(GAP_RELATED)
1131                            .addComponent(dataFileText)
1132                            .addGap(GAP_RELATED)
1133                            .addComponent(dataFileButton))
1134                        .addGroup(layout.createSequentialGroup()
1135                            .addComponent(typeLabel)
1136                            .addGap(GAP_RELATED)
1137                            .addComponent(dataFileDescription))
1138                        .addGroup(layout.createSequentialGroup()
1139                            .addComponent(descriptionLabel)
1140                            .addGap(GAP_RELATED)
1141                            .addComponent(textDescription))
1142                        .addGroup(layout.createSequentialGroup()
1143                            .addComponent(propertiesLabel)
1144                            .addGap(GAP_RELATED)
1145                            .addComponent(propertiesPanel))
1146                        .addGroup(TRAILING, layout.createSequentialGroup()
1147                            .addComponent(helpButton)
1148                            .addPreferredGap(RELATED)
1149                            .addComponent(loadButton))
1150                        .addGroup(layout.createSequentialGroup()
1151                            .addComponent(statusLabelLabel)
1152                            .addGap(GAP_RELATED)
1153                            .addComponent(statusLabel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)))
1154                    .addContainerGap())
1155            );
1156            layout.setVerticalGroup(
1157                layout.createParallelGroup(LEADING)
1158                .addGroup(TRAILING, layout.createSequentialGroup()
1159                    .addContainerGap()
1160                    .addGroup(layout.createParallelGroup(BASELINE)
1161                        .addComponent(dataFileButton)
1162                        .addComponent(dataFileText)
1163                        .addComponent(fileLabel))
1164                    .addPreferredGap(RELATED)
1165                    .addGroup(layout.createParallelGroup(BASELINE)
1166                        .addComponent(dataFileDescription)
1167                        .addComponent(typeLabel))
1168                    .addPreferredGap(RELATED)
1169                    .addGroup(layout.createParallelGroup(BASELINE)
1170                        .addComponent(textDescription)
1171                        .addComponent(descriptionLabel))
1172                    .addPreferredGap(RELATED)
1173                    .addGroup(layout.createParallelGroup(BASELINE)
1174                        .addComponent(propertiesPanel)
1175                        .addComponent(propertiesLabel))
1176                    .addPreferredGap(RELATED, DEFAULT_SIZE, Short.MAX_VALUE)
1177                    .addGroup(layout.createParallelGroup(BASELINE)
1178                        .addComponent(statusLabelLabel)
1179                        .addComponent(statusLabel))
1180                    .addPreferredGap(UNRELATED)
1181                    .addGroup(layout.createParallelGroup(BASELINE)
1182                        .addComponent(loadButton)
1183                        .addComponent(helpButton))
1184                    .addContainerGap())
1185            );
1186            
1187            return myPanel;
1188            
1189        }
1190    
1191    }
1192