001/*
002 * $Id: McIdasImageSequenceControl.java,v 1.21 2011/03/24 16:06:32 davep Exp $
003 *
004 * This file is part of McIDAS-V
005 *
006 * Copyright 2007-2011
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
031package edu.wisc.ssec.mcidasv.control;
032
033import java.awt.Color;
034import java.awt.Component;
035import java.awt.Dimension;
036import java.awt.Font;
037import java.awt.FontMetrics;
038import java.awt.GridBagConstraints;
039import java.awt.GridBagLayout;
040import java.awt.Image;
041import java.awt.event.ActionEvent;
042import java.awt.event.ActionListener;
043import java.awt.event.ItemEvent;
044import java.awt.event.ItemListener;
045import java.awt.event.KeyAdapter;
046import java.awt.event.KeyEvent;
047import java.awt.event.MouseAdapter;
048import java.awt.event.MouseEvent;
049import java.io.BufferedReader;
050import java.io.DataInputStream;
051import java.io.InputStreamReader;
052import java.net.URLEncoder;
053import java.rmi.RemoteException;
054import java.util.ArrayList;
055import java.util.Hashtable;
056import java.util.List;
057import java.util.StringTokenizer;
058
059import javax.swing.BorderFactory;
060import javax.swing.JCheckBox;
061import javax.swing.JComponent;
062import javax.swing.JLabel;
063import javax.swing.JPanel;
064import javax.swing.JScrollBar;
065import javax.swing.JScrollPane;
066import javax.swing.JTabbedPane;
067import javax.swing.JTextField;
068import javax.swing.JTextPane;
069import javax.swing.ScrollPaneConstants;
070import javax.swing.SwingUtilities;
071import javax.swing.border.EmptyBorder;
072import javax.swing.text.BadLocationException;
073import javax.swing.text.Style;
074import javax.swing.text.StyleConstants;
075import javax.swing.text.StyledDocument;
076
077import visad.VisADException;
078import visad.georef.MapProjection;
079
080import ucar.unidata.data.DataChoice;
081import ucar.unidata.data.DataContext;
082import ucar.unidata.data.DataSource;
083import ucar.unidata.data.DataSourceImpl;
084import ucar.unidata.idv.ControlContext;
085import ucar.unidata.idv.IntegratedDataViewer;
086import ucar.unidata.idv.MapViewManager;
087import ucar.unidata.idv.control.ControlWidget;
088import ucar.unidata.idv.control.ImageSequenceControl;
089import ucar.unidata.idv.control.WrapperWidget;
090import ucar.unidata.ui.colortable.ColorTableManager;
091import ucar.unidata.util.ColorTable;
092import ucar.unidata.util.GuiUtils;
093import ucar.unidata.util.Misc;
094
095import edu.wisc.ssec.mcidasv.data.FrameDirtyInfo;
096import edu.wisc.ssec.mcidasv.data.McIdasFrame;
097import edu.wisc.ssec.mcidasv.data.McIdasXDataSource;
098import edu.wisc.ssec.mcidasv.data.McIdasXInfo;
099import edu.wisc.ssec.mcidasv.ui.McIdasFrameDisplay;
100
101/**
102 * A DisplayControl for handling McIDAS-X image sequences
103 */
104public 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}