001    /*
002     * $Id: McIdasImageSequenceControl.java,v 1.22 2012/02/19 17:35:38 davep Exp $
003     *
004     * This file is part of McIDAS-V
005     *
006     * Copyright 2007-2012
007     * Space Science and Engineering Center (SSEC)
008     * University of Wisconsin - Madison
009     * 1225 W. Dayton Street, Madison, WI 53706, USA
010     * https://www.ssec.wisc.edu/mcidas
011     * 
012     * All Rights Reserved
013     * 
014     * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
015     * some McIDAS-V source code is based on IDV and VisAD source code.  
016     * 
017     * McIDAS-V is free software; you can redistribute it and/or modify
018     * it under the terms of the GNU Lesser Public License as published by
019     * the Free Software Foundation; either version 3 of the License, or
020     * (at your option) any later version.
021     * 
022     * McIDAS-V is distributed in the hope that it will be useful,
023     * but WITHOUT ANY WARRANTY; without even the implied warranty of
024     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
025     * GNU Lesser Public License for more details.
026     * 
027     * You should have received a copy of the GNU Lesser Public License
028     * along with this program.  If not, see http://www.gnu.org/licenses.
029     */
030    
031    package edu.wisc.ssec.mcidasv.control;
032    
033    import java.awt.Color;
034    import java.awt.Component;
035    import java.awt.Dimension;
036    import java.awt.Font;
037    import java.awt.FontMetrics;
038    import java.awt.GridBagConstraints;
039    import java.awt.GridBagLayout;
040    import java.awt.Image;
041    import java.awt.event.ActionEvent;
042    import java.awt.event.ActionListener;
043    import java.awt.event.ItemEvent;
044    import java.awt.event.ItemListener;
045    import java.awt.event.KeyAdapter;
046    import java.awt.event.KeyEvent;
047    import java.awt.event.MouseAdapter;
048    import java.awt.event.MouseEvent;
049    import java.io.BufferedReader;
050    import java.io.DataInputStream;
051    import java.io.InputStreamReader;
052    import java.net.URLEncoder;
053    import java.rmi.RemoteException;
054    import java.util.ArrayList;
055    import java.util.Hashtable;
056    import java.util.List;
057    import java.util.StringTokenizer;
058    
059    import javax.swing.BorderFactory;
060    import javax.swing.JCheckBox;
061    import javax.swing.JComponent;
062    import javax.swing.JLabel;
063    import javax.swing.JPanel;
064    import javax.swing.JScrollBar;
065    import javax.swing.JScrollPane;
066    import javax.swing.JTabbedPane;
067    import javax.swing.JTextField;
068    import javax.swing.JTextPane;
069    import javax.swing.ScrollPaneConstants;
070    import javax.swing.SwingUtilities;
071    import javax.swing.border.EmptyBorder;
072    import javax.swing.text.BadLocationException;
073    import javax.swing.text.Style;
074    import javax.swing.text.StyleConstants;
075    import javax.swing.text.StyledDocument;
076    
077    import visad.VisADException;
078    import visad.georef.MapProjection;
079    
080    import ucar.unidata.data.DataChoice;
081    import ucar.unidata.data.DataContext;
082    import ucar.unidata.data.DataSource;
083    import ucar.unidata.data.DataSourceImpl;
084    import ucar.unidata.idv.ControlContext;
085    import ucar.unidata.idv.IntegratedDataViewer;
086    import ucar.unidata.idv.MapViewManager;
087    import ucar.unidata.idv.control.ControlWidget;
088    import ucar.unidata.idv.control.ImageSequenceControl;
089    import ucar.unidata.idv.control.WrapperWidget;
090    import ucar.unidata.ui.colortable.ColorTableManager;
091    import ucar.unidata.util.ColorTable;
092    import ucar.unidata.util.GuiUtils;
093    import ucar.unidata.util.Misc;
094    
095    import edu.wisc.ssec.mcidasv.data.FrameDirtyInfo;
096    import edu.wisc.ssec.mcidasv.data.McIdasFrame;
097    import edu.wisc.ssec.mcidasv.data.McIdasXDataSource;
098    import edu.wisc.ssec.mcidasv.data.McIdasXInfo;
099    import edu.wisc.ssec.mcidasv.ui.McIdasFrameDisplay;
100    
101    /**
102     * A DisplayControl for handling McIDAS-X image sequences
103     */
104    public class McIdasImageSequenceControl extends ImageSequenceControl {
105            
106        private JLabel runningThreads;
107        private JCheckBox navigatedCbx;
108        private JPanel frameNavigatedContent;
109        private McIdasFrameDisplay frameDisplay;
110        private Dimension frameSize;
111        private JTextField inputText;
112        private JScrollPane outputPane;
113        private StyledDocument outputText;
114        private Font outputFont = new Font("Monospaced", Font.BOLD, 12);
115        private ArrayList commandHistory = new ArrayList();
116        private int commandHistoryIdx = -1;
117        private boolean commandHistoryMode = true;
118        
119        /** McIDAS-X handles */
120        private McIdasXInfo mcidasxInfo;
121        private McIdasXDataSource mcidasxDS;
122    
123        private int threadCount = 0;
124    
125        private static DataChoice dc=null;
126        private static Integer frmI;
127    
128        /** Holds frame component information */
129        private FrameComponentInfo frameComponentInfo;
130        private List frameDirtyInfoList = new ArrayList();
131        private List frameNumbers = new ArrayList();
132    
133        /**
134         * Default ctor; sets the attribute flags
135         */
136        public McIdasImageSequenceControl() {
137            setAttributeFlags(FLAG_COLORTABLE | FLAG_DISPLAYUNIT);
138            initFrameComponentInfo();
139            this.mcidasxInfo = null;
140            this.mcidasxDS = null;
141            
142            setDisplayId("bridgecontrol");
143            setHelpUrl("idv.controls.bridgecontrol");
144            
145            setExpandedInTabs(true);
146        }
147    
148        /**
149         * Creates, if needed, the frameComponentInfo member.
150         */
151        private void initFrameComponentInfo() {
152            if (this.frameComponentInfo == null) {
153                this.frameComponentInfo = new FrameComponentInfo(true, true, true, false, true, false);
154            }
155        }
156        
157        /**
158         * Initializes the frameDirtyInfoList member.
159         */
160        private void initFrameDirtyInfoList() {
161            this.frameDirtyInfoList.clear();
162            Integer frameNumber;
163            FrameDirtyInfo frameDirtyInfo;
164            for (int i=0; i<this.frameNumbers.size(); i++) {
165                    frameNumber = (Integer)this.frameNumbers.get(i);
166                    frameDirtyInfo = new FrameDirtyInfo(frameNumber.intValue(), false, false, false);
167                    this.frameDirtyInfoList.add(frameDirtyInfo);
168            }
169        }
170        
171        /**
172         * Sets the frameDirtyInfoList member based on frame number
173         */
174        private void setFrameDirtyInfoList(int frameNumber, boolean dirtyImage, boolean dirtyGraphics, boolean dirtyColorTable) {
175            FrameDirtyInfo frameDirtyInfo;
176            for (int i=0; i<this.frameDirtyInfoList.size(); i++) {
177                    frameDirtyInfo = (FrameDirtyInfo)frameDirtyInfoList.get(i);
178                    if (frameDirtyInfo.getFrameNumber() == frameNumber) {
179                            frameDirtyInfo.setDirtyImage(dirtyImage);
180                            frameDirtyInfo.setDirtyGraphics(dirtyGraphics);
181                            frameDirtyInfo.setDirtyColorTable(dirtyColorTable);
182                            this.frameDirtyInfoList.set(i, frameDirtyInfo);
183                    }
184            }
185        }
186        
187        /**
188         * Override the base class method that creates request properties
189         * and add in the appropriate frame component request parameters.
190         * @return  table of properties
191         */
192        protected Hashtable getRequestProperties() {
193            Hashtable props = super.getRequestProperties();
194            props.put(McIdasComponents.IMAGE, new Boolean(this.frameComponentInfo.getIsImage()));
195            props.put(McIdasComponents.GRAPHICS, new Boolean(this.frameComponentInfo.getIsGraphics()));
196            props.put(McIdasComponents.COLORTABLE, new Boolean(this.frameComponentInfo.getIsColorTable()));
197            props.put(McIdasComponents.ANNOTATION, new Boolean(this.frameComponentInfo.getIsAnnotation()));
198            props.put(McIdasComponents.FAKEDATETIME, new Boolean(this.frameComponentInfo.getFakeDateTime()));
199            props.put(McIdasComponents.DIRTYINFO, this.frameDirtyInfoList);
200            return props;
201        }
202    
203        /**
204         * A helper method for constructing the ui.
205         * This fills up a list of {@link ControlWidget}
206         * (e.g., ColorTableWidget) and creates a gridded
207         * ui  with them.
208         *
209         * @return The ui for the widgets
210         */
211        protected JComponent doMakeWidgetComponent() {
212    
213            JPanel framePanel = new JPanel();
214            try {
215                    framePanel = GuiUtils.top(doMakeFramePanel());
216            } catch (Exception e) {
217                    System.err.println("doMakeContents exception: " + e);
218            }
219            
220            JComponent settingsPanel = GuiUtils.top(super.doMakeWidgetComponent());
221            
222            JTabbedPane tabbedPane = new JTabbedPane();
223            tabbedPane.add("Frames", framePanel);
224            tabbedPane.add("Settings", settingsPanel);
225    
226            return tabbedPane;
227     
228        }
229        
230        /**
231         * Get control widgets specific to this control.
232         *
233         * @param controlWidgets   list of control widgets from other places
234         *
235         * @throws RemoteException  Java RMI error
236         * @throws VisADException   VisAD Error
237         */
238        public void getControlWidgets(List controlWidgets)
239            throws VisADException, RemoteException {
240    
241            super.getControlWidgets(controlWidgets);
242            
243            // Navigated checkbox
244            navigatedCbx = new JCheckBox("Display data in main 3D panel", false);
245            navigatedCbx.setToolTipText("Set to send navigated data to the main 3D display in addition to this 2D display");
246            navigatedCbx.addItemListener(new ItemListener() {
247                public void itemStateChanged(ItemEvent e) {
248                    JCheckBox myself = (JCheckBox)e.getItemSelectable();
249                    GuiUtils.enableTree(frameNavigatedContent, myself.isSelected());
250                    updateVImage();
251                }
252             });
253            JPanel frameNavigatedCbx =
254                    GuiUtils.hflow(Misc.newList(navigatedCbx), 2, 0);
255            controlWidgets.add(
256                            new WrapperWidget( this, GuiUtils.rLabel("Data:"), frameNavigatedCbx));
257            
258            // Navigated options
259            JPanel frameComponentsPanel =
260                GuiUtils.hflow(Misc.newList(doMakeImageBox(), doMakeGraphicsBox(), doMakeAnnotationBox()), 2, 0);
261            JPanel frameOrderPanel =
262                    GuiUtils.hflow(Misc.newList(doMakeFakeDateTimeBox()), 2, 0);
263            JPanel frameProjectionPanel =
264                    GuiUtils.hflow(Misc.newList(doMakeResetProjectionBox()), 2, 0);
265            frameNavigatedContent = 
266                    GuiUtils.vbox(frameComponentsPanel, frameOrderPanel, frameProjectionPanel);
267            GuiUtils.enableTree(frameNavigatedContent, false);
268            controlWidgets.add(
269                            new WrapperWidget( this, GuiUtils.rLabel(""), frameNavigatedContent));
270    
271        }
272        
273        /**
274         * Get frame control widgets specific to this control.
275         *
276         * @throws RemoteException  Java RMI error
277         * @throws VisADException   VisAD Error
278         */
279        private JPanel doMakeFramePanel() 
280            throws VisADException, RemoteException {
281            frameSize = new Dimension(640, 480);
282            
283            JPanel framePanel = new JPanel(new GridBagLayout());
284            GridBagConstraints c = new GridBagConstraints();
285            c.gridwidth = GridBagConstraints.REMAINDER;
286            
287            frmI = new Integer(0);
288            ControlContext controlContext = getControlContext();
289            List dss = ((IntegratedDataViewer)controlContext).getDataSources();
290            for (int i=0; i<dss.size(); i++) {
291                    DataSourceImpl ds = (DataSourceImpl)dss.get(i);
292                    if (ds instanceof McIdasXDataSource) {
293                            frameNumbers.clear();
294                            ds.setProperty(DataSource.PROP_AUTOCREATEDISPLAY, false);
295                            mcidasxDS = (McIdasXDataSource)ds;
296                            DataContext dataContext = mcidasxDS.getDataContext();
297                            ColorTableManager colorTableManager = 
298                                    ((IntegratedDataViewer)dataContext).getColorTableManager();
299                            ColorTable ct = colorTableManager.getColorTable("McIDAS-X");
300                            setColorTable(ct);
301                            this.mcidasxInfo = mcidasxDS.getMcIdasXInfo();
302                            this.dc = getDataChoice();
303                            String choiceStr = this.dc.toString();
304                            if (choiceStr.equals("Frame Sequence")) {
305                                    frameNumbers = mcidasxDS.getFrameNumbers();
306                            } else {
307                                    StringTokenizer tok = new StringTokenizer(choiceStr);
308                                    String str = tok.nextToken();
309                                    if (str.equals("Frame")) {
310                                            frmI = new Integer(tok.nextToken());
311                                            frameNumbers.add(frmI);
312                                    } else {
313                                            frmI = new Integer(1);
314                                            frameNumbers.add(frmI);
315                                    }
316                            }
317                            break;
318                    }
319            }
320            initFrameDirtyInfoList();
321            
322            // McIDAS-X frame display
323            frameDisplay = new McIdasFrameDisplay(frameNumbers, frameSize);
324            for (int i=0; i<frameNumbers.size(); i++) {
325                    updateXImage((Integer)frameNumbers.get(i));
326                    if (i==0) showXImage((Integer)frameNumbers.get(i));
327            }
328            framePanel.add(GuiUtils.hflow(Misc.newList(frameDisplay)), c);
329            
330            // McIDAS-X text stuff
331            outputPane = doMakeOutputText();
332            inputText = doMakeCommandLine();
333            JPanel commandLinePanel =
334                    GuiUtils.vbox(outputPane, doMakeSpacer(), inputText);
335            commandLinePanel.setBorder(BorderFactory.createLineBorder(Color.black));
336            framePanel.add(GuiUtils.hflow(Misc.newList(commandLinePanel)), c);
337            
338            // McIDAS-X commands that are running
339            runningThreads = GuiUtils.lLabel("Running: " + this.threadCount);
340            framePanel.add(GuiUtils.hflow(Misc.newList(runningThreads)), c);
341           
342            // Create a sensible title and tell McIDAS-X to stop looping and go to the first frame
343            String title = "";
344            if (frameNumbers.size() == 1) {
345                    title = "McIDAS-X Frame " + (Integer)frameNumbers.get(0);
346            }
347            else {
348                    Integer first = (Integer)frameNumbers.get(0);
349                    Integer last = (Integer)frameNumbers.get(frameNumbers.size() - 1);
350                    if (last - first == frameNumbers.size() - 1) {
351                            title = "McIDAS-X Frames " + first + "-" + last;
352                    }
353                    else {
354                            title = "McIDAS-X Frames " + (Integer)frameNumbers.get(0);
355                            for (int i=1; i<frameNumbers.size(); i++) {
356                                    title += ", " + (Integer)frameNumbers.get(i);
357                            }
358                    }
359            }
360            sendCommandLine("TERM L OFF; SF " + (Integer)frameNumbers.get(0), false);
361               
362            setNameFromUser(title);
363    
364            // Give inputText the focus whenever anything is clicked on...
365            framePanel.addMouseListener(new MouseAdapter() {
366                    public void mouseClicked(MouseEvent me) {
367                            if (me.getButton() == me.BUTTON1) {
368                                    inputText.requestFocus();
369                            }
370                    }
371            });
372            
373            return framePanel;
374        }
375    
376        /**
377         * Make the frame component check boxes.
378         * @return Check box for Images
379         */
380            protected Component doMakeImageBox() {
381            JCheckBox newBox = new JCheckBox("Image", frameComponentInfo.getIsImage());
382            newBox.setToolTipText("Set to import image data");
383            newBox.addItemListener(new ItemListener() {
384                    public void itemStateChanged(ItemEvent e) {
385                            JCheckBox myself = (JCheckBox)e.getItemSelectable();
386                            frameComponentInfo.setIsImage(myself.isSelected());
387                            updateVImage();
388                    }
389            });
390            return newBox;
391            }
392    
393        /**
394         * Make the frame component check boxes.
395         * @return Check box for Graphics
396         */
397        protected Component doMakeGraphicsBox() {
398            JCheckBox newBox = new JCheckBox("Graphics", frameComponentInfo.getIsGraphics());
399            newBox.setToolTipText("Set to import graphics data");
400            newBox.addItemListener(new ItemListener() {
401                    public void itemStateChanged(ItemEvent e) {
402                            JCheckBox myself = (JCheckBox)e.getItemSelectable();
403                            frameComponentInfo.setIsGraphics(myself.isSelected());
404                            updateVImage();
405                    }
406            });
407            return newBox;
408            }
409    
410        /**
411         * Make the frame component check boxes.
412         * @return Check box for Color table
413         */
414        protected Component doMakeColorTableBox() {
415            JCheckBox newBox = new JCheckBox("Color table", frameComponentInfo.getIsColorTable());
416            newBox.setToolTipText("Set to import color table data");
417            newBox.addItemListener(new ItemListener() {
418                    public void itemStateChanged(ItemEvent e) {
419                            JCheckBox myself = (JCheckBox)e.getItemSelectable();
420                            frameComponentInfo.setIsColorTable(myself.isSelected());
421                            updateVImage();
422                    }
423            });
424            return newBox;
425            }
426        
427        /**
428         * Make the frame component check boxes.
429         * @return Check box for Annotation line
430         */
431        protected Component doMakeAnnotationBox() {
432            JCheckBox newBox = new JCheckBox("Annotation line", frameComponentInfo.getIsAnnotation());
433            newBox.setToolTipText("Set to import annotation line");
434            newBox.addItemListener(new ItemListener() {
435                    public void itemStateChanged(ItemEvent e) {
436                            JCheckBox myself = (JCheckBox)e.getItemSelectable();
437                            frameComponentInfo.setIsAnnotation(myself.isSelected());
438                            updateVImage();
439                    }
440            });
441            return newBox;
442            }
443        
444        /**
445         * Make the frame behavior check boxes.
446         * @return Check box for Fake date/time
447         */
448        protected Component doMakeFakeDateTimeBox() {
449            JCheckBox newBox =
450                    new JCheckBox("Use McIDAS-X frame order to override data time with frame number",
451                            frameComponentInfo.getFakeDateTime());
452            newBox.setToolTipText("Set to preserve frame order");
453            newBox.addItemListener(new ItemListener() {
454                    public void itemStateChanged(ItemEvent e) {
455                            JCheckBox myself = (JCheckBox)e.getItemSelectable();
456                            frameComponentInfo.setFakeDateTime(myself.isSelected());
457                            updateVImage();
458                    }
459            });
460            return newBox;
461            }
462        
463        /**
464         * Make the frame behavior check boxes.
465         * @return Check box for Projection reset
466         */
467        protected Component doMakeResetProjectionBox() {
468            JCheckBox newBox =
469                    new JCheckBox("Use McIDAS-X data projection",
470                            frameComponentInfo.getResetProjection());
471            newBox.setToolTipText("Set to reset projection when data is refreshed");
472            newBox.addItemListener(new ItemListener() {
473                    public void itemStateChanged(ItemEvent e) {
474                            JCheckBox myself = (JCheckBox)e.getItemSelectable();
475                            frameComponentInfo.setResetProjection(myself.isSelected());
476                            updateVImage();
477                    }
478            });
479            return newBox;
480            }
481        
482        private void resetCommandHistory() {
483            commandHistory = new ArrayList();
484            resetCommandHistoryIdx();
485        }
486        
487        private void resetCommandHistoryIdx() {
488            commandHistoryIdx = -1;
489        }
490        
491        protected JTextField doMakeCommandLine() {
492                    final JTextField commandLine = new JTextField(0);
493                    commandLine.setFont(outputFont);
494                    commandLine.setBackground(Color.black);
495                    commandLine.setForeground(Color.cyan);
496                    commandLine.setCaretColor(Color.cyan);
497                    
498                    FontMetrics metrics = commandLine.getFontMetrics(outputFont);
499            Dimension d = new Dimension(frameSize.width, metrics.getHeight());
500            commandLine.setSize(d);
501            commandLine.setPreferredSize(d);
502            commandLine.setMinimumSize(d);
503            commandLine.setMaximumSize(d);
504            
505            resetCommandHistory();
506                    
507                    commandLine.addActionListener(new ActionListener() {
508                            public void actionPerformed(ActionEvent ae) {
509                                    String line = commandLine.getText().trim();
510                                    if (line.equals("")) return;
511                                    commandLine.setText("");                        
512                                    sendCommandLineThread(line, true);
513                                    
514                                    // Add it to the head of commandHistory list
515                                    commandHistory.add(0, line);
516                                    resetCommandHistoryIdx();
517                            }
518                    });
519                    commandLine.addKeyListener(new KeyAdapter() {
520                            public void keyTyped(KeyEvent ke) {
521                                    char keyChar = ke.getKeyChar();
522                                    if (commandLine.getText().trim().equals("") && commandHistory.size() > 0) {
523                                            commandHistoryMode = true;
524                                    }
525                                    if (commandHistoryMode) {
526                                            if (keyChar == '&') {
527                                                    commandHistoryIdx = Math.min(commandHistoryIdx+1,commandHistory.size()-1);
528                                                    if (commandHistoryIdx < 0) {
529                                                            resetCommandHistoryIdx();
530                                                            commandLine.setText("");
531                                                    }
532                                                    else {
533                                                            commandLine.setText((String)commandHistory.get(commandHistoryIdx));
534                                                    }
535                                                    ke.consume();
536                                            }
537                                            else if (keyChar == '^') {
538                                                    commandHistoryIdx--;
539                                                    if (commandHistoryIdx < 0) {
540                                                            resetCommandHistoryIdx();
541                                                            commandLine.setText("");
542                                                    }
543                                                    else {
544                                                            commandLine.setText((String)commandHistory.get(commandHistoryIdx));
545                                                    }
546                                                    ke.consume();
547                                            }
548                                            else {
549                                                    commandHistoryMode = false;
550                                            }
551                                    }
552                    if (Character.isLowerCase(keyChar))
553                            keyChar = Character.toUpperCase(keyChar);
554                    else
555                            keyChar = Character.toLowerCase(keyChar);
556                    ke.setKeyChar(keyChar);
557                }
558            });
559                    commandLine.setBorder(new EmptyBorder(0,0,0,0));
560                    return commandLine;
561        }
562        
563        private Component doMakeSpacer() {
564            JPanel spacer = new JPanel();
565            Color backgroundColor = new Color(0, 128, 128);
566            spacer.setBackground(backgroundColor);
567                    FontMetrics metrics = inputText.getFontMetrics(outputFont);
568            Dimension d = new Dimension(frameSize.width, metrics.getHeight());
569            spacer.setSize(d);
570            spacer.setPreferredSize(d);
571            spacer.setMinimumSize(d);
572            spacer.setMaximumSize(d);
573            spacer.setBorder(new EmptyBorder(0,0,0,0));
574            return spacer;
575        }
576        
577        protected JScrollPane doMakeOutputText() {          
578            JTextPane outputPane = new JTextPane() {
579                    public void setSize(Dimension d) {
580                                    if (d.width < getParent().getSize().width)
581                                            d.width = getParent().getSize().width;
582                                    super.setSize(d);
583                            }
584                            public boolean getScrollableTracksViewportWidth() {
585                                    return false;
586                            }
587            };
588            outputPane.setFont(outputFont);
589            outputPane.setEditable(false);
590            outputPane.setBackground(Color.black);
591            outputPane.setForeground(Color.lightGray);
592            JScrollPane outputScrollPane = new JScrollPane(outputPane,
593                            ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
594                            ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
595            
596                    FontMetrics metrics = outputPane.getFontMetrics(outputFont);
597            Dimension d = new Dimension(frameSize.width, metrics.getHeight() * 8);
598            outputScrollPane.setSize(d);
599            outputScrollPane.setPreferredSize(d);
600            outputScrollPane.setMinimumSize(d);
601            outputScrollPane.setMaximumSize(d);
602            
603            outputText = (StyledDocument)outputPane.getDocument();
604            outputScrollPane.setBorder(new EmptyBorder(0,0,0,0));
605            
606            return outputScrollPane;
607        }
608    
609        /**
610         * Send the given commandline to McIDAS-X over the bridge
611         * @param line
612         * @param showprocess
613         */
614            private void sendCommandLine(String line, boolean showprocess) {
615                    
616            // The user might have moved to another frame...
617            // Ask the image display which frame we are on
618                    int frameCur = 1;
619                    if (frameDisplay != null)
620                            frameCur = frameDisplay.getFrameNumber();
621                    
622            line = line.trim();
623            if (line.length() < 1) return;
624            String encodedLine = line;
625            try {
626                    encodedLine = URLEncoder.encode(line,"UTF-8");
627            } catch (Exception e) {
628                    System.out.println("sendCommandLine URLEncoder exception: " + e);
629            }
630            
631            DataInputStream inputStream = mcidasxInfo.getCommandInputStream(encodedLine, frameCur);
632            if (!showprocess) {
633                try { inputStream.close(); }
634                catch (Exception e) {}
635                    return;
636            }
637    //        appendTextLineItalics(line);
638            appendTextLineCommand(line);
639            try {
640                    BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
641                    String responseType = null;
642                    String ULine = null;
643                    StringTokenizer tok;
644                    boolean inList = false;
645                    boolean doUpdate = false;
646                    boolean dirtyImage, dirtyGraphics, dirtyColorTable;
647                    // Burn the session key header line
648                    String lineOut = br.readLine();
649                    lineOut = br.readLine();
650                    while (lineOut != null) {
651    //                      System.out.println("sendCommandLine processing: " + lineOut);
652                            tok = new StringTokenizer(lineOut, " ");
653                            responseType = tok.nextToken();
654                            if (responseType.equals("U")) {
655                            Integer frameInt = (Integer)Integer.parseInt(tok.nextToken());
656                            inList = false;
657                            dirtyImage = dirtyGraphics = dirtyColorTable = false;
658                            // Don't pay attention to anything outside of our currently loaded frame list
659                                    for (int i=0; i<frameNumbers.size(); i++) {
660                                            if (frameInt.compareTo((Integer)frameNumbers.get(i)) == 0) inList = true;
661                                    }
662                                    if (inList) {
663                                            ULine = tok.nextToken();
664    //                                      System.out.println("  Frame " + frameInt + " status line: " + ULine);
665                                            if (Integer.parseInt(ULine.substring(1,2)) != 0) dirtyImage = true;
666                                            if (Integer.parseInt(ULine.substring(3,4)) != 0) dirtyGraphics = true;
667                                            if (Integer.parseInt(ULine.substring(5,6)) != 0) dirtyColorTable = true;
668                                            if (dirtyImage || dirtyGraphics || dirtyColorTable) {
669                                                    doUpdate = true;
670                                                    updateXImage(frameInt);
671                                            }
672                                            setFrameDirtyInfoList(frameInt, dirtyImage, dirtyGraphics, dirtyColorTable);
673                                    }
674                            } else if (responseType.equals("C")) {
675                                    appendTextLineCommand(lineOut.substring(6));
676                    } else if (responseType.equals("T")) {
677    //                      appendTextLine("   " + lineOut.substring(6));
678                            appendTextLineNormal(lineOut.substring(6));
679                            
680                    } else if (responseType.equals("M") ||
681                               responseType.equals("S")) {
682    //                      appendTextLine(" * " + lineOut.substring(6));
683                            appendTextLineError(lineOut.substring(6));
684                            
685                    } else if (responseType.equals("R")) {
686    //                      appendTextLine(" ! " + lineOut.substring(6));
687                            appendTextLineError(lineOut.substring(6));
688                    } else if (responseType.equals("V")) {
689    //                      System.out.println("Viewing frame status line: " + lineOut);
690                            frameCur = Integer.parseInt(tok.nextToken());
691                            } else if (responseType.equals("H") ||
692                                           responseType.equals("K")) {
693                                    /* Don't do anything with these response types */
694                            } else {
695                                    /* Catch any unparsed line... */
696                                    System.err.println("Could not parse bridge response: " + lineOut);
697                            }
698                            lineOut = br.readLine();
699                    }
700                    showXImage(frameCur);
701                            if (doUpdate) {
702                                    updateVImage();
703                            }
704                            
705                } catch (Exception e) {
706                    System.out.println("sendCommandLine exception: " + e);
707                try { inputStream.close(); }
708                catch (Exception ee) {}
709                }
710    
711            }
712    
713    /*
714            private void appendTextLine(String line) {
715                    outputText.append(line + "\n");
716                    outputText.setCaretPosition(outputText.getDocument().getLength());
717            }
718    */
719            
720            private void appendTextLineNormal(String line) {
721            Style style = outputText.addStyle("Normal", null);
722            StyleConstants.setForeground(style, Color.lightGray);
723            try {
724                    outputText.insertString(outputText.getLength(), line + "\n", style);
725            } catch (BadLocationException e) { }
726            scrollTextLineToBottom();
727            }
728            
729            private void appendTextLineCommand(String line) {
730            Style style = outputText.addStyle("Command", null);
731            StyleConstants.setForeground(style, Color.green);
732            try {
733                    outputText.insertString(outputText.getLength(), line + "\n", style);
734            } catch (BadLocationException e) { }
735            scrollTextLineToBottom();
736            }
737            
738            private void appendTextLineError(String line) {
739            Style style = outputText.addStyle("Error", null);
740            StyleConstants.setForeground(style, Color.yellow);
741            try {
742                    outputText.insertString(outputText.getLength(), line + "\n", style);
743            } catch (BadLocationException e) { }
744            scrollTextLineToBottom();
745            }
746    
747            private void scrollTextLineToBottom() {
748                    SwingUtilities.invokeLater(new Runnable() {
749                            public void run() {
750                                    JScrollBar vBar = outputPane.getVerticalScrollBar();
751                                    vBar.setValue(vBar.getMaximum());
752                            }
753                    });
754            }
755            
756            private void updateXImage(int inFrame) {
757                    if (mcidasxDS == null || frameDisplay == null) return;
758                    try {
759                            McIdasFrame frm = mcidasxDS.getFrame(inFrame);
760                            Image imageGIF = frm.getGIF();
761                            frameDisplay.setFrameImage(inFrame, imageGIF);
762                    } catch (Exception e) {
763                            System.out.println("updateXImage exception: " + e);
764                    }
765            }
766            
767            private void showXImage(int inFrame) {
768                    if (frameDisplay == null) return;
769                    try {
770                            frameDisplay.showFrameNumber(inFrame);
771                    } catch (Exception e) {
772                            System.out.println("showXImage exception: " + e);
773                    }
774            }
775            
776        private void updateVImage() {
777            try {
778                    getRequestProperties();
779                resetData();
780            } catch (Exception e) {
781                System.out.println("updateVImage exception: " + e);
782            }
783        }
784    
785        public boolean init(DataChoice choice)
786                            throws VisADException, RemoteException {
787            setShowProgressBar(false);
788            boolean ret = super.init(choice, false);
789            return ret;
790        }
791        
792        /**
793         * This gets called when the control has received notification of a
794         * dataChange event.
795         * 
796         * @throws RemoteException   Java RMI problem
797         * @throws VisADException    VisAD problem
798         */
799        protected void resetData() throws VisADException, RemoteException {
800            // Do not attempt to load any data unless the checkbox is set...
801            if (!navigatedCbx.isSelected()) return;
802            
803            super.resetData();
804    
805            if (frameComponentInfo.getResetProjection()) {
806                    MapProjection mp = getDataProjection();
807                    if (mp != null) {
808                            MapViewManager mvm = getMapViewManager();
809                            mvm.setMapProjection(mp, false); 
810                    }
811            }
812        }
813        
814        /**
815         * Try my hand at creating a thread
816         */
817        private class McIdasCommandLine implements Runnable {
818            private String line;
819            private boolean showprocess;
820            public McIdasCommandLine() {
821                    this.line = "";
822                    this.showprocess = true;
823            }
824            public McIdasCommandLine(String line, boolean showprocess) {
825                    this.line = line;
826                    this.showprocess = showprocess;
827            }
828            public void run() {
829                    notifyThreadStart();
830                    sendCommandLine(this.line, this.showprocess);
831                    notifyThreadStop();
832            }
833        }
834        
835        /**
836         * Threaded sendCommandLine
837         * @param line
838         * @param showprocess
839         */
840        private void sendCommandLineThread(String line, boolean showprocess) {
841            McIdasCommandLine mcCmdLine = new McIdasCommandLine(line, showprocess);
842            Thread t = new Thread(mcCmdLine);
843            t.start();
844        }
845        
846        private void notifyThreadStart() {
847            this.threadCount++;
848            notifyThreadCount();
849        }
850        private void notifyThreadStop() {
851            this.threadCount--;
852            notifyThreadCount();
853        }
854        private void notifyThreadCount() {
855            runningThreads.setText("Running: " + this.threadCount);
856        }
857    
858    }