001    /*
002     * This file is part of McIDAS-V
003     *
004     * Copyright 2007-2013
005     * Space Science and Engineering Center (SSEC)
006     * University of Wisconsin - Madison
007     * 1225 W. Dayton Street, Madison, WI 53706, USA
008     * https://www.ssec.wisc.edu/mcidas
009     * 
010     * All Rights Reserved
011     * 
012     * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
013     * some McIDAS-V source code is based on IDV and VisAD source code.  
014     * 
015     * McIDAS-V is free software; you can redistribute it and/or modify
016     * it under the terms of the GNU Lesser Public License as published by
017     * the Free Software Foundation; either version 3 of the License, or
018     * (at your option) any later version.
019     * 
020     * McIDAS-V is distributed in the hope that it will be useful,
021     * but WITHOUT ANY WARRANTY; without even the implied warranty of
022     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
023     * GNU Lesser Public License for more details.
024     * 
025     * You should have received a copy of the GNU Lesser Public License
026     * along with this program.  If not, see http://www.gnu.org/licenses.
027     */
028    
029    package edu.wisc.ssec.mcidasv.chooser;
030    
031    
032    import static javax.swing.GroupLayout.DEFAULT_SIZE;
033    import static javax.swing.GroupLayout.Alignment.BASELINE;
034    import static javax.swing.GroupLayout.Alignment.LEADING;
035    import static javax.swing.GroupLayout.Alignment.TRAILING;
036    import static javax.swing.LayoutStyle.ComponentPlacement.RELATED;
037    import static javax.swing.LayoutStyle.ComponentPlacement.UNRELATED;
038    
039    import java.awt.Image;
040    import java.awt.MediaTracker;
041    import java.awt.Toolkit;
042    import java.awt.event.ActionEvent;
043    import java.awt.event.ActionListener;
044    import java.awt.event.FocusEvent;
045    import java.awt.event.FocusListener;
046    import java.io.File;
047    import java.io.FileNotFoundException;
048    import java.io.FileReader;
049    import java.io.InputStream;
050    import java.util.ArrayList;
051    import java.util.Hashtable;
052    import java.util.List;
053    
054    import javax.swing.GroupLayout;
055    import javax.swing.JButton;
056    import javax.swing.JCheckBox;
057    import javax.swing.JComboBox;
058    import javax.swing.JComponent;
059    import javax.swing.JFileChooser;
060    import javax.swing.JLabel;
061    import javax.swing.JPanel;
062    import javax.swing.JRadioButton;
063    import javax.swing.JTextField;
064    
065    import org.w3c.dom.Element;
066    
067    import ucar.unidata.idv.IntegratedDataViewer;
068    import ucar.unidata.idv.chooser.IdvChooser;
069    import ucar.unidata.idv.chooser.IdvChooserManager;
070    import ucar.unidata.util.GuiUtils;
071    import ucar.unidata.util.IOUtil;
072    import ucar.unidata.util.Misc;
073    import ucar.unidata.util.TwoFacedObject;
074    import ucar.unidata.xml.XmlUtil;
075    import visad.util.ImageHelper;
076    import edu.wisc.ssec.mcidasv.Constants;
077    import edu.wisc.ssec.mcidasv.data.AxformInfo;
078    import edu.wisc.ssec.mcidasv.data.EnviInfo;
079    import edu.wisc.ssec.mcidasv.data.HeaderInfo;
080    import edu.wisc.ssec.mcidasv.util.McVGuiUtils;
081    import edu.wisc.ssec.mcidasv.util.McVGuiUtils.Position;
082    import edu.wisc.ssec.mcidasv.util.McVGuiUtils.Prefer;
083    import edu.wisc.ssec.mcidasv.util.McVGuiUtils.TextColor;
084    import edu.wisc.ssec.mcidasv.util.McVGuiUtils.Width;
085    import edu.wisc.ssec.mcidasv.util.McVGuiUtils.IconPanel;
086    
087    /**
088     * @author SSEC Development Team
089     */
090    
091    public class FlatFileChooser extends IdvChooser implements Constants {
092    
093        /** Set default stride to keep dimensions within this */
094        private int maxDefDim = 1000;
095        
096        // Properties associated with the button selector
097        private File dataFile;
098        private JTextField dataFileText = new JTextField();
099        private JButton dataFileButton = new JButton();
100        private JLabel dataFileDescription = new JLabel();
101        private JLabel textDescription = new JLabel();
102        
103        // Dimensions
104        // elements, lines, bands
105        private JTextField textElements = new JTextField();
106        private JTextField textLines = new JTextField();
107        private JTextField textBands = new JTextField();
108        private JTextField textUnit = new JTextField();
109        private JTextField textStride = new JTextField();
110        private JCheckBox checkTranspose = new JCheckBox("Transpose elements/lines");
111        private List bandNames = new ArrayList();
112        private List bandFiles = new ArrayList();
113    
114        // Navigation
115        // lat/lon files or bounds
116        private JRadioButton radioLatLonFiles = new JRadioButton("Files", true);
117        private JRadioButton radioLatLonBounds = new JRadioButton("Bounds", false);
118        private File latFile, lonFile;
119        private JLabel textLatFile = new JLabel();
120        private JButton buttonLatFile = new JButton();
121        private JLabel textLonFile = new JLabel();
122        private JButton buttonLonFile = new JButton();
123        private JPanel panelLatLonFiles = new JPanel();
124        private JTextField textLatUL = new JTextField();
125        private JTextField textLonUL = new JTextField();
126        private JTextField textLatLR = new JTextField();
127        private JTextField textLonLR = new JTextField();
128        private JPanel panelLatLonBounds = new JPanel();
129        private JTextField textLatLonScale = new JTextField();
130        private JCheckBox checkEastPositive = new JCheckBox("East positive");
131    
132    
133        // Properties associated with the data file
134        // bytes/pixel, ASCII delimiter, endianness, interleave, offset, missing
135        private JRadioButton radioBinary = new JRadioButton("Binary", true);
136        private JRadioButton radioASCII = new JRadioButton("ASCII", false);
137        private JRadioButton radioImage = new JRadioButton("Image", false);
138        private JRadioButton radioEndianLittle = new JRadioButton("Little", true);
139        private JRadioButton radioEndianBig = new JRadioButton("Big", false);
140        private JComboBox comboByteFormat = new JComboBox();
141        private JComboBox comboInterleave = new JComboBox();
142        private JTextField textOffset = new JTextField("0");
143        private JPanel panelBinary = new JPanel();
144        private JTextField textDelimiter = new JTextField();
145        private JPanel panelASCII = new JPanel();
146        private JPanel panelImage = new JPanel();
147        private JTextField textMissing = new JTextField();
148        
149        private List<TwoFacedObject> listByteFormat = Misc.newList(new TwoFacedObject[] {
150                new TwoFacedObject("1-byte unsigned integer", HeaderInfo.kFormat1ByteUInt),
151                new TwoFacedObject("2-byte signed integer", HeaderInfo.kFormat2ByteSInt),
152                new TwoFacedObject("4-byte signed integer", HeaderInfo.kFormat4ByteSInt),
153                new TwoFacedObject("4-byte float", HeaderInfo.kFormat4ByteFloat),
154                new TwoFacedObject("8-byte double", HeaderInfo.kFormat8ByteDouble),
155                new TwoFacedObject("2x8-byte complex number", HeaderInfo.kFormat2x8Byte),
156                new TwoFacedObject("2-byte unsigned integer", HeaderInfo.kFormat2ByteUInt)
157        });
158    
159        private List<TwoFacedObject> listInterleave = Misc.newList(
160                new TwoFacedObject("Sequential", HeaderInfo.kInterleaveSequential),
161                new TwoFacedObject("By line", HeaderInfo.kInterleaveByLine),
162                new TwoFacedObject("By pixel", HeaderInfo.kInterleaveByPixel));
163    
164        private JLabel statusLabel = new JLabel("Status");
165        
166        /**
167         * Super setStatus() takes a second string to enable "simple" mode
168         * which highlights the required component.  We don't really care
169         * about that feature, and we don't want getStatusLabel() to
170         * change the label background color.
171         */
172        @Override
173        public void setStatus(String statusString, String foo) {
174            if (statusString == null)
175                statusString = "";
176            statusLabel.setText(statusString);
177        }
178    
179        /**
180         * Get a handle on the IDV
181         */
182        protected IntegratedDataViewer idv = getIdv();
183    
184        /**
185         * Create the FileChooser, passing in the manager and the xml element
186         * from choosers.xml
187         *
188         * @param mgr The manager
189         * @param root The xml root
190         *
191         */
192        public FlatFileChooser(IdvChooserManager mgr, Element root) {
193            super(mgr, root);
194            
195            loadButton = McVGuiUtils.makeImageTextButton(ICON_ACCEPT_SMALL, getLoadCommandName());
196            loadButton.setActionCommand(getLoadCommandName());
197            loadButton.addActionListener(this);
198    
199            dataFileButton = McVGuiUtils.makeImageButton(ICON_OPEN, "Open file");
200            dataFileButton.addActionListener(new ActionListener(){
201                public void actionPerformed(ActionEvent e) {
202                    dataFile = getDataFile(dataFile);
203                    if (dataFile!=null) {
204                        dataFileText.setText(dataFile.getAbsolutePath());
205                        inspectDataFile(dataFile);
206                    }
207                }
208            });
209            dataFileText.addActionListener(new ActionListener(){
210                public void actionPerformed(ActionEvent e) {
211                    dataFile = new File(dataFileText.getText());
212                    inspectDataFile(dataFile);
213                }
214            });
215            dataFileText.addFocusListener(new FocusListener() {
216                public void focusGained(FocusEvent e) {}
217                public void focusLost(FocusEvent e) {
218                    dataFile = new File(dataFileText.getText());
219                    inspectDataFile(dataFile);
220                }
221            });
222            
223            radioLatLonFiles.addActionListener(new ActionListener(){
224                public void actionPerformed(ActionEvent e) {
225                    checkSetLatLon();
226                }
227            });
228            radioLatLonBounds.addActionListener(new ActionListener(){
229                public void actionPerformed(ActionEvent e) {
230                    checkSetLatLon();
231                }
232            });
233    
234            buttonLatFile = McVGuiUtils.makeImageButton(ICON_OPEN, "Select latitude file");
235            buttonLatFile.addActionListener(new ActionListener(){
236                public void actionPerformed(ActionEvent e) {
237                    latFile = getDataFile(latFile);
238                    if (latFile!=null)
239                        textLatFile.setText(latFile.getName());
240                }
241            });
242            buttonLonFile = McVGuiUtils.makeImageButton(ICON_OPEN, "Select longitude file");
243            buttonLonFile.addActionListener(new ActionListener(){
244                public void actionPerformed(ActionEvent e) {
245                    lonFile = getDataFile(lonFile);
246                    if (lonFile!=null)
247                        textLonFile.setText(lonFile.getName());
248                }
249            });
250            GuiUtils.buttonGroup(radioLatLonFiles, radioLatLonBounds);
251            
252            radioBinary.addActionListener(new ActionListener(){
253                public void actionPerformed(ActionEvent e) {
254                    checkSetBinaryASCIIImage();
255                }
256            });
257            radioASCII.addActionListener(new ActionListener(){
258                public void actionPerformed(ActionEvent e) {
259                    checkSetBinaryASCIIImage();
260                }
261            });
262            radioImage.addActionListener(new ActionListener(){
263                public void actionPerformed(ActionEvent e) {
264                    checkSetBinaryASCIIImage();
265                }
266            });
267            GuiUtils.buttonGroup(radioBinary, radioASCII, radioImage);
268            GuiUtils.buttonGroup(radioEndianLittle, radioEndianBig);
269    
270            GuiUtils.setListData(comboByteFormat, listByteFormat);
271            GuiUtils.setListData(comboInterleave, listInterleave);
272    
273            setHaveData(false);
274        }
275        
276        /**
277         * enable/disable widgets for navigation
278         */
279        private void checkSetLatLon() {
280            boolean isFile = radioLatLonFiles.isSelected();
281            GuiUtils.enableTree(panelLatLonFiles, isFile);
282            GuiUtils.enableTree(panelLatLonBounds, !isFile);
283        }
284    
285        /**
286         * enable/disable widgets for binary/ASCII
287         */
288        private void checkSetBinaryASCIIImage() {
289            GuiUtils.enableTree(panelBinary, radioBinary.isSelected());
290            GuiUtils.enableTree(panelASCII, radioASCII.isSelected());
291            GuiUtils.enableTree(panelImage, radioImage.isSelected());
292        }
293    
294        /**
295         * Set whether the user has made a selection that contains data.
296         *
297         * @param have   true to set the haveData property.  Enables the
298         *               loading button
299         */
300        public void setHaveData(boolean have) {
301            super.setHaveData(have);
302            updateStatus();
303        }
304        
305        /**
306         * Set the status message appropriately
307         */
308        protected void updateStatus() {
309            super.updateStatus();
310            checkSetLatLon();
311            checkSetBinaryASCIIImage();
312            if(!getHaveData()) {
313                setStatus("Select a file"); 
314            }
315        }
316    
317        /**
318         * Inspect the selected data file
319         * Determine if it is a known header file type
320         * 
321         * @param thisFile
322         * @throws FileNotFoundException 
323         */
324        private void inspectDataFile(File thisFile) {
325            if (thisFile == null || thisFile.getName()=="") {
326                dataFileDescription.setText("");
327                setHaveData(false);
328                return;
329            }
330            if (!thisFile.exists()) {
331                dataFileDescription.setText("File does not exist");
332                setHaveData(false);
333                return;
334            }
335            try {
336                FileReader fr = new FileReader(thisFile);
337                char first80c[] = new char[80];
338                fr.read(first80c, 0, 80);
339                fr.close();
340                String first80 = new String(first80c);
341                clearValues();
342                boolean doStride = false;
343                if (IOUtil.hasSuffix(thisFile.getName(), ".gif") ||
344                        IOUtil.hasSuffix(thisFile.getName(), ".jpg") ||
345                        IOUtil.hasSuffix(thisFile.getName(), ".png")) {
346                    dataFileDescription.setText("Image file");
347                    processImageFile(thisFile);
348                }
349                else if (IOUtil.hasSuffix(thisFile.getName(), ".xml") ||
350                        IOUtil.hasSuffix(thisFile.getName(), ".ximg")) {
351                    dataFileDescription.setText("XML image header file");
352                    processXmlHeaderFile(thisFile);
353                }
354                else if (first80.indexOf("                     Space Science & Engineering Center") >= 0) {
355                    dataFileDescription.setText("McIDAS-X AXFORM header file");
356                    processAxformHeaderFile(thisFile);
357                    doStride = true;
358                }
359                else if (first80.indexOf("ENVI") >= 0) {
360                    dataFileDescription.setText("ENVI header file");
361                    processEnviHeaderFile(thisFile);
362                    doStride = true;
363                }
364                else {
365                    dataFileDescription.setText("Binary, ASCII or Image data");
366                    processGenericFile(thisFile);
367                    doStride = true;
368                }
369                
370                // Default the stride
371                int newStride = 1;
372                if (doStride) {
373                    String textLinesText = textLines.getText();
374                    String textElementsText = textElements.getText();
375                    if (!(textLinesText.equalsIgnoreCase("") || textElementsText.equalsIgnoreCase(""))) {
376                        int myLines = Integer.parseInt(textLinesText);
377                        int myElements = Integer.parseInt(textElementsText);
378                        if (myLines > maxDefDim || myElements > maxDefDim) {
379                            newStride = Math.max((int)Math.ceil((float)myLines/(float)maxDefDim), (int)Math.ceil((float)myElements/(float)maxDefDim));
380                        }
381                    }
382                }
383                textStride.setText(Integer.toString(newStride));
384                
385                setHaveData(true);
386            }
387            catch (Exception e) {
388                e.printStackTrace();
389            }
390        }
391            
392        /**
393         * Special processing for a known data type
394         * This deals specifically with AXFORM header files
395         */
396        private void processAxformHeaderFile(File thisFile) {
397            try {
398                AxformInfo axformInfo = new AxformInfo(thisFile);
399                
400                // Set the properties in the GUI
401                textDescription.setText(axformInfo.getParameter(HeaderInfo.DESCRIPTION, ""));
402                textElements.setText((axformInfo.getParameter(HeaderInfo.ELEMENTS, 0)).toString());
403                textLines.setText((axformInfo.getParameter(HeaderInfo.LINES, 0)).toString());
404                textUnit.setText((axformInfo.getParameter(HeaderInfo.UNIT, "")).toString());
405                bandNames = (List)axformInfo.getParameter(HeaderInfo.BANDNAMES, new ArrayList());
406                bandFiles = (List)axformInfo.getParameter(HeaderInfo.BANDFILES, new ArrayList());
407                textBands.setText(Integer.toString(bandNames.size()));
408                textOffset.setText((axformInfo.getParameter(HeaderInfo.OFFSET, 0)).toString());
409                textMissing.setText((axformInfo.getParameter(HeaderInfo.MISSINGVALUE, (float)0)).toString());
410                
411                Integer dataType = (Integer)axformInfo.getParameter(HeaderInfo.DATATYPE, HeaderInfo.kFormatUnknown);
412                Boolean bigEndian = (Boolean)axformInfo.getParameter(HeaderInfo.BIGENDIAN, false);
413                if (dataType==HeaderInfo.kFormatASCII) {
414                    radioASCII.setSelected(true);
415                }
416                else if (dataType==HeaderInfo.kFormatImage) {
417                    radioImage.setSelected(true);
418                }
419                else {
420                    radioBinary.setSelected(true);
421                    TwoFacedObject tfo = TwoFacedObject.findId(dataType.intValue(), listByteFormat);
422                    if (tfo!=null)
423                        comboByteFormat.setSelectedItem(tfo);
424                    tfo = TwoFacedObject.findId(HeaderInfo.kInterleaveSequential, listInterleave);
425                    if (tfo!=null)
426                        comboInterleave.setSelectedItem(tfo);
427                }
428                
429                radioEndianLittle.setSelected(!bigEndian.booleanValue());
430                radioEndianBig.setSelected(bigEndian.booleanValue());
431    
432                List latlonFiles = axformInfo.getParameter(HeaderInfo.NAVFILES, new ArrayList());
433                if (latlonFiles.size() == 2) {
434                    latFile = new File((String)latlonFiles.get(0));
435                    lonFile = new File((String)latlonFiles.get(1));
436                }
437    
438                if (latFile==null || lonFile==null) {
439                    radioLatLonBounds.setSelected(true);
440                }
441                else {
442                    textLatFile.setText(latFile.getName());
443                    textLonFile.setText(lonFile.getName());
444                    radioLatLonFiles.setSelected(true);
445                }
446                
447                textLatLonScale.setText("100");
448                checkEastPositive.setSelected(true);
449                
450            }
451            catch (Exception e) {
452                e.printStackTrace();
453            }
454        }
455    
456        /**
457         * Special processing for a known data type
458         * This deals specifically with ENVI header files
459         */
460        private void processEnviHeaderFile(File thisFile) {
461            try {
462                EnviInfo enviInfo = new EnviInfo(thisFile);
463                
464                // Set the properties in the GUI
465                textDescription.setText(enviInfo.getParameter(HeaderInfo.DESCRIPTION, ""));
466                textElements.setText((enviInfo.getParameter(HeaderInfo.ELEMENTS, 0)).toString());
467                textLines.setText((enviInfo.getParameter(HeaderInfo.LINES, 0)).toString());
468                textUnit.setText((enviInfo.getParameter(HeaderInfo.UNIT, "")).toString());
469                bandNames = (List)enviInfo.getParameter(HeaderInfo.BANDNAMES, new ArrayList());
470                bandFiles = (List)enviInfo.getParameter(HeaderInfo.BANDFILES, new ArrayList());
471                textBands.setText(Integer.toString(bandNames.size()));
472                textOffset.setText((enviInfo.getParameter(HeaderInfo.OFFSET, 0)).toString());
473                textMissing.setText((enviInfo.getParameter(HeaderInfo.MISSINGVALUE, (float)0)).toString());
474    
475                Integer dataType = (Integer)enviInfo.getParameter(HeaderInfo.DATATYPE, HeaderInfo.kFormatUnknown);
476                String interleaveType = (enviInfo.getParameter(HeaderInfo.INTERLEAVE, HeaderInfo.kInterleaveSequential)).toString();
477                Boolean bigEndian = (Boolean)enviInfo.getParameter(HeaderInfo.BIGENDIAN, false);
478                radioBinary.setSelected(true);
479                TwoFacedObject tfo = TwoFacedObject.findId(dataType.intValue(), listByteFormat);
480                if (tfo!=null)
481                    comboByteFormat.setSelectedItem(tfo);
482                tfo = TwoFacedObject.findId(interleaveType, listInterleave);
483                if (tfo!=null)
484                    comboInterleave.setSelectedItem(tfo);
485                
486                radioEndianLittle.setSelected(!bigEndian.booleanValue());
487                radioEndianBig.setSelected(bigEndian.booleanValue());
488    
489                // Look for a geo.hdr file that contains Latitude and Longitude bands
490                String parent = thisFile.getParent();
491                if (parent==null) parent=".";
492                String navFile = thisFile.getName().replace(".hdr", "");
493                int lastDot = navFile.lastIndexOf(".");
494                if (lastDot >= 0) {
495                    navFile = navFile.substring(0, lastDot) + ".geo.hdr";
496                }
497                navFile = parent + "/" + navFile;
498                EnviInfo navInfo = new EnviInfo(navFile);
499                if (navInfo.isNavHeader()) {
500                    latFile = new File(navFile);
501                    lonFile = new File(navFile);
502                }
503                
504                if (latFile==null || lonFile==null) {
505                    radioLatLonBounds.setSelected(true);
506                }
507                else {
508                    textLatFile.setText(latFile.getName());
509                    textLonFile.setText(lonFile.getName());
510                    radioLatLonFiles.setSelected(true);
511                }
512                
513                textLatLonScale.setText("1");
514                checkEastPositive.setSelected(false);
515    
516            }
517            catch (Exception e) {
518                e.printStackTrace();
519            }
520        }
521    
522        /**
523         * Special processing for a known data type
524         * This deals specifically with XML header files
525         */
526        private void processXmlHeaderFile(File thisFile) {
527            try {
528                
529                String description = "";
530                int lines = 0;
531                int elements = 0;
532                float missingVal = -1;
533                
534                bandFiles = new ArrayList();
535                bandNames = new ArrayList();
536    
537                Element root = XmlUtil.getRoot(thisFile.getAbsolutePath(), getClass());
538                if (!root.getTagName().equals("image")) {
539                    processGenericFile(thisFile);
540                    return;
541                }
542                
543                description = XmlUtil.getAttribute(root, "name", (String) null);
544                String url = "";
545                if (XmlUtil.hasAttribute(root, "url")) {
546                    url = XmlUtil.getAttribute(root, "url");
547                    if (description == "") {
548                        description = url;
549                    }
550                    String parent = thisFile.getParent();
551                    if (parent==null) parent=".";
552                    url = parent + "/" + url;
553                }
554                else {
555                    processGenericFile(thisFile);
556                    return;
557                }
558                if (XmlUtil.hasAttribute(root, "ullat")) {
559                    radioLatLonBounds.setSelected(true);
560                    textLatUL.setText(XmlUtil.getAttribute(root, "ullat"));
561                }
562                if (XmlUtil.hasAttribute(root, "ullon")) {
563                    radioLatLonBounds.setSelected(true);
564                    textLonUL.setText(XmlUtil.getAttribute(root, "ullon"));
565                }
566                if (XmlUtil.hasAttribute(root, "lrlat")) {
567                    radioLatLonBounds.setSelected(true);
568                    textLatLR.setText(XmlUtil.getAttribute(root, "lrlat"));
569                }
570                if (XmlUtil.hasAttribute(root, "lrlon")) {
571                    radioLatLonBounds.setSelected(true);
572                    textLonLR.setText(XmlUtil.getAttribute(root, "lrlon"));
573                }
574    
575                // Try to read the referenced image to get lines and elements
576                setStatus("Loading image");
577                InputStream is   = null;
578                is = IOUtil.getInputStream(url, getClass());
579                byte[] imageContent = IOUtil.readBytes(is);
580                Image image = Toolkit.getDefaultToolkit().createImage(imageContent);
581                MediaTracker tracker = new MediaTracker(this); 
582                tracker.addImage(image, 0);
583                try { 
584                    tracker.waitForAll(); 
585                } 
586                catch(InterruptedException e) {}
587                ImageHelper ih = new ImageHelper();
588                image.getWidth(ih);
589                if (ih.badImage) {
590                    throw new IllegalStateException("Bad image: " + url);
591                }
592                elements = image.getWidth(ih);
593                lines = image.getHeight(ih);
594                
595                // Bands
596                bandFiles.add(url);
597                bandNames.add("XML image file");
598                
599                // Set the properties in the GUI
600                textDescription.setText(description);
601                textElements.setText(Integer.toString(elements));
602                textLines.setText(Integer.toString(lines));
603                textBands.setText(Integer.toString(bandNames.size()));
604    
605                radioImage.setSelected(true);
606                textMissing.setText(Float.toString(missingVal));
607                
608                textLatLonScale.setText("1");
609                checkEastPositive.setSelected(false);
610    
611            }
612            catch (Exception e) {
613                e.printStackTrace();
614            }
615        }
616    
617        /**
618         * Special processing for a known data type
619         * This deals specifically with XML header files
620         */
621        private void processImageFile(File thisFile) {
622            try {
623                
624                String description = "";
625                int lines = 0;
626                int elements = 0;
627                float missingVal = -1;
628                
629                bandFiles = new ArrayList();
630                bandNames = new ArrayList();
631    
632                description = thisFile.getName();
633                String url = thisFile.getAbsolutePath();
634    
635                // Try to read the referenced image to get lines and elements
636                setStatus("Loading image");
637                InputStream is   = null;
638                is = IOUtil.getInputStream(url, getClass());
639                byte[] imageContent = IOUtil.readBytes(is);
640                Image image = Toolkit.getDefaultToolkit().createImage(imageContent);
641                MediaTracker tracker = new MediaTracker(this); 
642                tracker.addImage(image, 0);
643                try { 
644                    tracker.waitForAll(); 
645                } 
646                catch(InterruptedException e) {}
647                ImageHelper ih = new ImageHelper();
648                image.getWidth(ih);
649                if (ih.badImage) {
650                    throw new IllegalStateException("Bad image: " + url);
651                }
652                elements = image.getWidth(ih);
653                lines = image.getHeight(ih);
654    
655                // Bands
656                bandFiles.add(url);
657                bandNames.add("Image file");
658    
659                // Set the properties in the GUI
660                textDescription.setText(description);
661                textElements.setText(Integer.toString(elements));
662                textLines.setText(Integer.toString(lines));
663                textBands.setText(Integer.toString(bandNames.size()));
664                
665                radioImage.setSelected(true);
666                textMissing.setText(Float.toString(missingVal));
667                
668                textLatLonScale.setText("1");
669                checkEastPositive.setSelected(false);
670    
671            }
672            catch (Exception e) {
673                e.printStackTrace();
674            }
675        }
676    
677        /**
678         * Special processing for an unknown data type
679         * Can we glean anything about the file by inspecting it more?
680         */
681        private void processGenericFile(File thisFile) {
682    
683            clearValues();
684    
685            // Set appropriate defaults
686            // Bands
687            bandFiles.clear();
688            bandFiles.add(thisFile.getAbsolutePath());
689            bandNames.clear();
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("0");
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