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