package nxm.sys.prim;

import java.awt.Button;
import java.awt.Color;
import java.awt.Frame;
import java.util.Vector;

import nxm.sys.inc.Keyable;
import nxm.sys.inc.MessageHandler;
import nxm.sys.lib.BaseFile;
import nxm.sys.lib.Convert;
import nxm.sys.lib.Data;
import nxm.sys.lib.DataFile;
import nxm.sys.lib.Message;
import nxm.sys.lib.Midas;
import nxm.sys.lib.Table;
import nxm.sys.libg.GMenu;
import nxm.sys.libg.GPrimitive;
import nxm.sys.libg.GPrompt;
import nxm.sys.libg.GridSheet;
import nxm.sys.libg.GridSheet.GridSheetReference;
import nxm.sys.libg.MColor;
import nxm.sys.libg.MPanel;
import nxm.sys.libg.MWindow;

/** Displays Midas data in the form of textual lists. This version uses no swing components and is
    based on GridSheet graphics library component.

    @see list2

    @author Jeff Schoen, Beth Hassler
*/
public class list extends GPrimitive implements Keyable, GridSheetReference {

  private int controls=1, scale=0, mode=0, view=0, type=0;
  private int realtime=0, hilite=1, exit=-1, poll=0;
  private int outtype=1, settings=0, curmode=0, colrIdx=1;
  private long polltime = System.currentTimeMillis();

  private DataFile hcb;
  private Data data;
  private double size;
  private double[] mrkd = new double[3];
  private Data mrkData = new Data (mrkd);
  private boolean rescale=false;
  private String currentFilename;
  private String templateFilename;
  private String currColumn;
  private int currRow;
  private int currColSize;
  private boolean warnNonScalar = true; // Warn when opening a non-scalar file.
  private boolean drawHidden = false;

  static final int EXIT_RETURN=0x1, EXIT_MENU=0x2, EXIT_MESSAGE=0x4, EXIT_ALL=0xF;
  static final String exitList = "RETURN,MENU,MESSAGE";
  static final int ONCLICK=0x1, ONMOTION=0x2;
  static final int T_ONTYPE=1,T_VECTOR=2,T_TABLE=3;
  static final int T_CELL=1,T_ROW=2,T_COL=3;
  static final int GRID=0x1,COLLABELS=0x2,ROWLABELS=0x4;
  static final String colorChoices = "THEME,Black,Blue,Cyan,Gray,Green,Magenta,Orange,Pink,Red,White,Yellow";

  /** the GridSheet library class.
   * @deprecated Since NeXtMidas 3.5.4, use {@link #getGridSheet()} instead.
   */
  @Deprecated
  public GridSheet gs;

  /** default constructor */
  public list () {}

  @Override
  public int open() {
    hilite = MA.getSelectionIndex("HILITE","BACK,FORE",1);
    controls = MA.getSelectionIndex("CNT=","CLICK,CONT",1);
    outtype = MA.getSelectionIndex("/OUTTYPE","AUTO,VECTOR,TABLE",T_ONTYPE);
    poll = MA.getI("/POLL",(short)0);
    if (outtype == -1) outtype = T_ONTYPE;
    curmode = MA.getSelectionIndex("CURSOR","CELL,ROW,COL",T_CELL);
    if (curmode == -1) curmode = T_CELL;
    realtime = MA.getL("/RT",0);
    if (M.pipeMode==Midas.PINIT) exit=0; else exit=-1;
    try {
      exit  = MA.getL("/EXIT",exit);
      if (MA.isPresent("/EXIT")) M.deprecate("Since NeXtMidas 2.5.0, the use of numbers for /EXIT.  Use "+exitList);
    } catch (Exception e) {
      exit = MA.getOptionMask("/EXIT",exitList,exit);
    }
    drawHidden = MA.getState("/DRAWHIDDEN", drawHidden);
    MW = new ListWindow("List",this);
    MW.open();  //set colors and fonts
    MW.setSize(800,500);
    MW.addTo(this);

    // Construct a gridsheet with drag and drop option
    gs = new GridSheet();
    //gs.setDragAndDrop(MA.getState("/DRAGDROP", false));  // changed to line below in 3.3.0 (Bug 2120)
    gs.setDragAndDrop(MA.getS("/DRAGDROP","false","true"));  // allow for State or String
    gs.addTo(MW);
    gs.setTheme();
    gs.setCursorMode(curmode);
    gs.setFgHilite(hilite==2);
    gs.setWayneMode(MA.getState("/WAYNE", false)); // Only Wayne is allowed to use /WAYNE mode.
    int refreshMode = MA.getSelectionIndex("/REFRESH",GridSheet.refreshChoices.toUpperCase(),GridSheet.RM_LEGACY);
    gs.setRefreshMode(refreshMode);
    String colr = MA.getSelection("HCOLOR",colorChoices.toUpperCase(),"THEME");
    if (colr.equals("THEME")) gs.setHighlight(MW.theme.cwfh);
    else gs.setHighlight(colr);
    gs.setAutoSelect( MA.getState("AUTOSELECT",true) );
    gs.setMultiSelMsgMode( MA.getS("MULTI","OFF") );
    setScrollEnd (MA.getState("/SCROLLEND",false));
    currentFilename = MA.getCS("FILE");
    openFile(currentFilename);
    templateFilename = MA.getCS("TEMPLATE");
    openTemplateFile(0, templateFilename);

    settings = 0;
    if (MA.getState("/GRID",gs.getGrid())) {
      gs.setGrid(true);
      settings = settings|GRID;
    } else
      gs.setGrid(false);
    if (MA.getState("/COLLABELS",gs.getColLabelsVisible())) {
      gs.setColLabels(true);
      settings = settings|COLLABELS;
    } else
      gs.setColLabels(false);
    if (MA.getState("/ROWLABELS",gs.getRowLabelsVisible())) {
      gs.setRowLabels(true);
      settings = settings|ROWLABELS;
    } else {
      gs.setRowLabels(false);
    }
    refresh(1);

    return NORMAL;
  }

  private boolean isVisible() {
    boolean visible = true;

    if(MW.panel != null) {
      java.awt.Frame frame = javax.swing.JOptionPane.getFrameForComponent(MW.panel);

      if(!MW.panel.isShowing()) {
        visible = false;
      } else if((frame != null) && (frame.getExtendedState() == Frame.ICONIFIED)) {
        visible = false;
      }
    } else {
      visible = false;
    }

    return visible;
  }

  @Override
  public int process () {
    if (MW.status==MWindow.INIT && (!drawHidden || !gs.isVisible()))  return NOOP; // list not up yet
    boolean doPoll = false;
    if (poll>0) { doPoll = checkPollTime(); }
    int stat = NORMAL;
    if (isVisible() || (drawHidden && gs.isVisible())) {  // drawHidden will get data even if hidden in a tab (BUG 2230)
      int nels = getData(doPoll);            // get available data
      if (nels > 0) { stat = NORMAL; }
      else          { stat = NOOP;   }
    } else          { stat = NOOP;   } // window not visible
    return stat;
  }

  @Override
  public int close() {
    if (hcb!=null) hcb.close();
    gs.close();
    MW.close();
    return NORMAL;
  }

  private boolean checkPollTime() {
    boolean retval = false;
    if ((System.currentTimeMillis()-polltime)>poll*1000)  {
      polltime = System.currentTimeMillis();
      retval = true;
    }
    return retval;
  }
  /** Open the file to list with a refresh on clear */
  private void openFile (String name) {
    openFile (name, true);
  }
  /** Open the file to list with option to refresh on clear */
  private void openFile (String name, boolean doRefresh) {
    int ty,i,nc,cm;
    String[] s = {};
    int numSubs;
    int tmpType=0;

    /* Since list deals with only one data file at a time, make sure we close any open file
       before opening a new one.  */
    if (hcb != null) hcb.close(); //jph -- DR500224-1
    hcb = new DataFile();
    hcb.init (M,name);
    if (!hcb.open(BaseFile.OPTIONAL)) return;
    boolean rt = realtime>0 || hcb.io.isStream();

    size=hcb.size;
    data = hcb.getDataBuffer((int)size);  //assumes no memory restrictions
    gs.clear(doRefresh);

    if (warnNonScalar && !hcb.getFormat().equals("NH") && (!Data.isString(hcb.getFormatType()))
                      && !((hcb.getFormatMode()==Data.SCALAR) || (hcb.getFormatMode()=='1'))) { // 3.9.4 include 'Z' 256 ASCII NXM-3570
      warnNonScalar = false;
      M.warning("LIST: LIST does not support files with non-scalar numbers, please use LIST2.");
    }

    tmpType = outtype;
    switch (hcb.typeClass) {
      case 1:
        s = new String[1];
        s[0] = "F1";
        if (outtype==T_ONTYPE) tmpType=T_VECTOR;
        break;
      case 2:
        int frameSize = hcb.getSubSize();
        s = new String[frameSize];
        for (int ii=0; ii<frameSize; ii++) s[ii] = "F"+(ii+1);
        if (outtype==T_ONTYPE) tmpType=T_VECTOR;
        break;
      case 3:
      case 6:
        numSubs = hcb.getSubSize();
        s = new String[numSubs];
        for (int ii=0; ii<numSubs; ii++) {
          s[ii] = hcb.getRecName(ii);
        }
        if (outtype==T_ONTYPE) tmpType=T_TABLE;
        break;
      case 4:
        M.warning ("File type not supported yet: "+hcb.getType());
        if (outtype==T_ONTYPE) tmpType=T_TABLE;
        break;
      case 5:
        numSubs = hcb.getSubSize();
        s = new String[numSubs];
        for (int ii=0; ii<numSubs; ii++)
          s[ii] = hcb.getCompName(ii);
        if (outtype==T_ONTYPE) tmpType=T_TABLE;
        break;
      default:
        M.warning ("File type not supported yet: "+hcb.getType());
        hcb.close();
        if (outtype==T_ONTYPE) tmpType=T_VECTOR;
        break;
    }

    if (s.length > 0) gs.setColLabels(s);
    gs.setOutputType(tmpType);
  }

  private void openTemplateFile (int n, String name) {
    DataFile templatehcb;

    templatehcb = new DataFile();
    templatehcb.init (M,name);
    if (!templatehcb.open(BaseFile.OPTIONAL)) return;
    gs.setTemplate(templatehcb.keywords);
    gs.applyTemplate();
    templatehcb.close();
    settings = 0;
    if (gs.getGrid()) settings = settings|GRID;
    if (gs.getRowLabelsVisible()) settings = settings|ROWLABELS;
    if (gs.getColLabelsVisible()) settings = settings|COLLABELS;
  }

  private void closeFile () {
    hcb.close();
    gs.clear();
    refresh();
  }

  private int getData(boolean doPoll) {
    int nelem = 0;
    Data subdata;
    Vector<Vector<Object>> rows;

    if (doPoll) { openFile(currentFilename, false);}

    if (hcb!=null && hcb.isOpen && hcb.avail()>=1) {
      nelem = hcb.read(data);
      if (nelem<=0) return nelem;
      switch (hcb.typeClass) {
        case 1:
          gs.addRows(data.toVector(),1);
          break;
        case 2:
          gs.addRows(data.toVector(),hcb.getSubSize());
          break;
        case 3:
        case 6:
        case 5:
          rows = new Vector<>();
          for (int j=0, ioff=0; j<size; j++) {
            Vector<Object> newRow = new Vector<>();
            int subsize = hcb.getSubSize();
            for (int i=0; i<subsize; i++) {
              if (hcb.typeClass==5) {
                subdata = data.getAtomAt(ioff,hcb.getCompFormat(i));
              } else {
                subdata = data.getAtomAt(ioff, hcb.getRecFormat(i));
              }
              if(subdata.toVector().size() == 1) {
                newRow.addElement(subdata.toVector().elementAt(0));
              } else {
                newRow.addElement(subdata.toString());
              }
              ioff += subdata.bpe;
            }
            rows.addElement(newRow);
          }
          gs.addRows(rows);
          break;
        default:
          M.info("Unknown file type");
          break;
      }
    }
    return nelem;
  }

  private void refresh () { refresh(0); }

  private void refresh (int flag) {
    if (gs == null) return;
    if (flag==1) gs.refresh(true);
    else gs.refresh();
  }

  private void resize () {
    gs.setSize(MW.panel.getSize());
  }

  /** Process a message */
  @Override
  public int processMessage (Message msg) {

    if (msg.name.equals("KEYPRESS")) {
      String text = (String)msg.data;
      if (text.equalsIgnoreCase("M")) msg.name = "MENU";
      if (text.equalsIgnoreCase("Enter") && (exit&EXIT_RETURN)!=0) msg.name = "EXIT";
    }
    if (msg.name.equals("BUTTON") && msg.info==2) msg.name = "MENU";
    if (msg.name.equals("BUTTON") && msg.info==3) gs.deselectAll(true);

    if (msg.name.equals("MENU")) {
      new GMenu (MW,"List","Files...,Template...,Settings...,"+
                "Control...,Window...,Options...,Other...,Exit",0,0,this);
    }
    else if (msg.name.equals("LIST")) {
      String text = (String)msg.data;
      if (text.equals("FILES...")) {
        new GMenu (MW,"Files","Open,Close,Reread",0,0,this);
      }
      else if (text.equals("TEMPLATE...")) {
        new GMenu (MW,"Template","Open,Default",0,0,this);
      }
      else if (text.equals("CONTROL...")) {
        new GMenu (MW,"Controls","Off,Click,Continuous,Assign...",controls+1,0,this);
      }
      else if (text.equals("WINDOW...")) {
        new GMenu (MW,"Window","Toggle,Push,Pop",0,0,this);
      }
      else if (text.equals("OTHER...")) {
        new GMenu (MW,"Other","Theme",0,0,this);
      }
      else if (text.equals("OPTIONS...")) {
        new GMenu (MW,"Options","Add Column,Cursor Granularity...,Highlight...,OutputType...",0,0,this);
      }
      else if (text.equals("SETTINGS...")) {
        new GMenu (MW,"Settings","Grid,Column Labels,Row Labels",settings,GMenu.TOGGLE,this);
      }
      else if (text.equals("EXIT")) {
        msg.name = "EXIT";
        if ((exit&EXIT_MENU)!=0) MQ.put(msg);
      }
    }
    else if (msg.name.equals("FILES")) {
      String text = (String)msg.data;
      if (text.equals("OPEN")) {
        new GPrompt (MW,"OpenFile","",0,this);
      }
      else if (text.equals("CLOSE")) {
        closeFile();
      }
      else if (text.equals("REREAD")) {
        if (hcb!=null) {
          // Since only one file can be open at a time, the open will make
          // sure previous file is closed
          //if (hcb.isOpen) hcb.close();
          openFile(currentFilename);
          openTemplateFile(0, templateFilename);
        }
      }
    }
    else if (msg.name.equals("TEMPLATE")) {
      String text = (String)msg.data;
      if (text.equals("OPEN")) {
        new GPrompt (MW,"OPENTEMPLATE","",0,this);
      }
      else if (text.equals("DEFAULT")) {
        // Since only one file can be open at a time, the open will make
        // sure previous file is closed
        //if (hcb.isOpen) hcb.close();
        openFile(currentFilename);
      }
    }
    else if (msg.name.equals("SETTINGS")) {
      settings = msg.info;
      if (!gs.getGrid() && (settings&GRID)!=0) gs.setGrid(true);
      if (gs.getGrid() && (settings&GRID)==0)  gs.setGrid(false);
      if (!gs.getColLabelsVisible() && (settings&COLLABELS)!=0) gs.setColLabels(true);
      if (gs.getColLabelsVisible() && (settings&COLLABELS)==0) gs.setColLabels(false);
      if (!gs.getRowLabelsVisible() && (settings&ROWLABELS)!=0) gs.setRowLabels(true);
      if (gs.getRowLabelsVisible() && (settings&ROWLABELS)==0) gs.setRowLabels(false);
    }
    else if (msg.name.equals("SETROW")) {
      gs.setOutputType(outtype);
      gs.setRow(msg.info, Convert.o2t(msg.data));
      MQ.put(msg,msg.from,this);
    }
    else if (msg.name.equals("GETROW")) {
      msg.data = gs.getRow(msg.info);
      MQ.put(msg,msg.from,this);
    }
    else if (msg.name.equals("GETROWVECTOR")) {
      msg.data = gs.getRowVector(msg.info);
      MQ.put(msg,msg.from,this);
    }
    else if (msg.name.equals("GETROWTABLE")) {
      msg.data = gs.getRowTable(msg.info);
      MQ.put(msg,msg.from,this);
    }
    else if (msg.name.equals("GETCOL")) {
      msg.data = gs.getColumn(msg.info);
      MQ.put(msg,msg.from,this);
    }
    else if (msg.name.equals("GETCOLUMNVECTOR")) {
      msg.data = gs.getColumnVector(msg.info);
      MQ.put(msg,msg.from,this);
    }
    else if (msg.name.equals("GETCOLUMNTABLE")) {
      msg.data = gs.getColumnTable(msg.info);
      MQ.put(msg,msg.from,this);
    }
    else if (msg.name.equals("CONTROLS")) {
      if (msg.info == 4);
      else controls = msg.info-1;
    }
    else if (msg.name.equals("WINDOW")) {
      //System.out.println("Action "+msg.data);
      ///*
      String text = (String)msg.data;
      if (text.equals("CLOSING")) {
        // put on message on MQ for processing in the proper thread for CLOSING message
        if (!thisIsMe()) { MQ.put(msg); return NOOP;   }
        else             {              return FINISH; }
      } else if (text.equals("TOGGLE")) {
        MW.pop(-1);
      } else if (text.equals("PUSH")) {
        MW.pop(0);
      } else if (text.equals("POP")) {
        MW.pop(1);
      }
      if (!text.equals("CLOSED")) { refresh(); }
      //*/
    }
    else if (msg.name.equals("OTHER")) {
      String text = (String)msg.data;
      if (text.equals("THEME")) {
        new GMenu (MW,"THEME",MWindow.themeList,0,0,this);
      }
    }
    else if (msg.name.equals("OPTIONS")) {
      String text = (String)msg.data;
      if (text.equals("ADD COLUMN"))
        new GPrompt (MW,"AddColumn","",0,this);
      else if (text.equals("CURSOR GRANULARITY..."))
        new GMenu (MW,"Cursor","Cell,Row,Column",curmode,0,this);
      else if (text.equals("HIGHLIGHT..."))
        new GMenu (MW,"HIGHLIGHT","Background,Foreground,Color",hilite,0,this);
      else if (text.equals("OUTPUTTYPE..."))
        new GMenu (MW,"OUTPUTTYPE","Auto,Vector,Table",outtype,0,this);
    }

    /* if initializing or this Thread is not me - queue for me */
    else if (MW==null || MW.status==0 || !thisIsMe()) {
      if (msg.name.equals("POINTER")) MQ.remove(msg.name);
      if (msg.name.equals("REFRESH") || msg.name.equals("RESIZE")
                || msg.name.startsWith("PAN")) {
        MQ.remove(msg.name);
      }
      MQ.put(msg);
      return NOOP;
    }

    else if (msg.name.equals("SELECT")) {
      //M.info("Sending select "+msg.data);
      sendMessage (msg);
    }
    else if (msg.name.equals("SELECTROW")) {
      //M.info("Sending select "+msg.data);
      gs.selectRow(Convert.o2l(msg.data));
      sendMessage(msg);
    }
    else if (msg.name.equals("DESELECT")) {
      //M.info("Sending deselect "+msg.data);
      sendMessage (msg);
    }
    /* the rest draw so main thread gets them from the queue */
    else if (msg.name.equals("POINTER")) {
      if (controls==ONMOTION) {
        msg.data = null;
        sendMessage (msg);
      }
    }
    else if (msg.name.equals("DROPDATA")) {
      sendMessage(msg);
    }
    else if (msg.name.equals("BUTTON") && msg.info == -1) {
      if (controls==ONCLICK) {
        msg.name = "MARK";
        msg.data = null;
        sendMessage (msg);
      }
    }
    else if (msg.name.equals("REFRESH")) {
      refresh(1);
    }
    else if (msg.name.equals("RESIZE")) {
      resize();
    }
    else if (msg.name.equals("OPENFILE")) {
      currentFilename = (String)msg.data;
      openFile (currentFilename);
    }
    else if (msg.name.equals("OPEN TEMPLATE") ||
             msg.name.equals("OPENTEMPLATE") ) {
      if (msg.name.equals("OPEN TEMPLATE")) {
        M.deprecate("OPEN TEMPLATE deprecated, use OPENTEMPLATE");
      }
      // Since only one file can be open at a time, the open will make
      // sure previous file is closed
      //if (hcb.isOpen) hcb.close();
      openFile(currentFilename);
      openTemplateFile ( msg.info,(String)msg.data );
    }
    else if (msg.name.equals("APPLYTEMPLATE")) {
      openTemplateFile ( msg.info,(String)msg.data );
    }
    else if (msg.name.equals("CLOSEFILE")) {
      closeFile ();
    }
    else if (msg.name.equals("THEME")) {
      MW.setTheme((String)msg.data);
      gs.setTheme();
      refresh();
    }
    else if (msg.name.equals("ADDCOLUMN")) {
      String text = (String)msg.data;
      if (text.length() > 0) gs.addColumns(text);
    }
    else if (msg.name.equals("OUTPUTTYPE")) {
      outtype = msg.info;
      if (outtype==T_ONTYPE) {
       switch (hcb.typeClass) {
         case 1:
         case 2:
           gs.setOutputType(T_VECTOR); break;
         default:
           gs.setOutputType(T_TABLE); break;
       }
      } else
        gs.setOutputType(outtype);
    }
    else if (msg.name.equals("HIGHLIGHT")) {
      if (msg.info == 3) {
        new GMenu (MW,"Color",colorChoices,colrIdx,0,this);
      } else {
        hilite = msg.info;
        gs.setFgHilite(hilite==2);
      }
    }
    else if (msg.name.equals("CURSOR")) {
      curmode = msg.info;
      gs.setCursorMode(curmode);
    }
    else if (msg.name.equals("COLOR")) {
      colrIdx = msg.info;
      if (colrIdx == 1)
        gs.setHighlight(MW.theme.cwfh.darker());
      else
        gs.setHighlight((String)msg.data);
    }
    else if (msg.name.equals("EXIT")) {
      if ((exit&EXIT_MESSAGE)!=0) return FINISH;
    }
    else if (msg.name.equals("POP")) {
      MW.pop(-1);
      //Refresh with flag==1 so that Graphics context will be renewed
      refresh(1);
    }
    return NORMAL;
  }

  @Override
  public void sendMessage (Message msg) {
    // This matches the super class, but is public rather than protected
    super.sendMessage(msg);
  }

  @Override
  public Object setKey (String name, Object value) {
           if (name.equals("ADDROW")) {gs.setOutputType(outtype); gs.addRow(Convert.o2t(value)); }
      else if (name.equals("ADDROWS")){
        gs.setOutputType(outtype);
        Table inTable = Convert.o2t(value);
        String keys[] = inTable.getKeys();
        //M.info("Keys is "+inTable);
        for (int z=0;z<keys.length;z++){
          Table tt = inTable.getTable(keys[z]);
          if (tt!=null){
            //M.info("Adding ro "+keys[z]+" "+tt);
            try{
               gs.addRow(tt);
            }catch(Exception ex){
               System.out.println("Exception "+ex);
               System.out.println("Adding "+tt);
            }
          }
        }
      }
      else if (name.equals("SETROW")) {gs.setOutputType(outtype); gs.setRow(-1, Convert.o2t(value)); } //append only, for now
      else if (name.equals("ROWBYVALUE")) currRow = gs.getRowByValue(Convert.o2t(value));
      else if (name.equals("CURRROW")) currRow = Convert.o2l(value);
      else if (name.equals("CURRCOL")) currColumn = Convert.o2s(value);
      else if (name.equals("REMOVEROW")) gs.removeRow(Convert.o2l(value));
      else if (name.equals("REMOVECURRROW")) gs.removeRow(currRow);
      else if (name.equals("SELECTROW")) gs.selectRow(Convert.o2l(value));
      else if (name.equals("SELECTMROW")) gs.selectRow(Convert.o2l(value),false);
      else if (name.equals("SELECTCELLS")) gs.selectCells(Convert.o2t(value));
      else if (name.equals("SORTA")) gs.sort(Convert.o2s(value),false);
      else if (name.equals("SORTD")) gs.sort(Convert.o2s(value),true);
      else if (name.equals("SHOWCOL")) gs.setColVisible(Convert.o2s(value),true);
      else if (name.equals("HIDECOL")) gs.setColVisible(Convert.o2s(value),false);
      else if (name.equals("SHOWALL")) gs.setColVisible("ALLCOLUMNS",true);
      else if (name.equals("HIDEALL")) gs.setColVisible("ALLCOLUMNS",false);
      else if (name.equals("SETCELL")) gs.setCell(Convert.o2t(value));
      else if (name.equals("COLRASTER")) {
        gs.changeColumnRender(Convert.o2s(value),3);
        gs.setColWidth(Convert.o2s(value));
      }
      else if (name.equals("COLSTRING")) {
        gs.changeColumnRender(Convert.o2s(value),1);
        gs.setColWidth(Convert.o2s(value));
      }

      //the following may only be temporary...
      else if (name.equals("COLTOG")) {
        gs.changeColumnRender(Convert.o2s(value),5);
        gs.setColWidth(Convert.o2s(value));
      }
      else if (name.equals("COLPANEL")) {
        Vector<Object> v = gs.changeColumnRender(Convert.o2s(value),2);
        if (v != null) {
          int maxWidth = 0;
          for (int i=0; i<v.size(); i++) {
            MPanel mp = (MPanel)v.elementAt(i);
            //Button test = new Button(""+(i+1));
            Button test = new Button("ON");
            test.setForeground(Color.green);
            test.setBackground(Color.black);
            test.setSize(50,gs.getRowHeight());
            maxWidth = Math.max(maxWidth, 50);
            mp.add(test);
          }
          gs.setColWidth(Convert.o2s(value), maxWidth);
        }
      }
      else if (name.equals("RASTERDATA")) {
        byte[] buf = new byte[60];
        for (int i=0; i<60; i++) buf[i] = (byte)(Math.random()*12);
        gs.setRasterData(Convert.o2t(value), buf);
      }
      else if (name.equals("SETCELLCOLORS"))  {
         Color fgColor, bgColor;
         Table t = Convert.o2t(value);
         if (t.get("FG") == null) fgColor = null; else fgColor = MColor.getColor(Convert.o2s(t.get("FG")));
         if (t.get("BG") == null) bgColor = null; else bgColor = MColor.getColor(Convert.o2s(t.get("BG")));
         gs.setCellColors(Convert.o2l(t.get("COLUMN")),
                          Convert.o2l(t.get("ROW")),
                          fgColor, bgColor);
      }
      else if (name.equals("SETROWCOLORS"))  {
        Table t = Convert.o2t(value);
        Color fgColor=null, bgColor=null;
        if (t.get("FG")!=null) {fgColor=MColor.getColor(Convert.o2s(t.get("FG")));}
        if (t.get("BG")!=null) {bgColor = MColor.getColor(Convert.o2s(t.get("BG")));}
        gs.setRowColors(Convert.o2l(t.get("ROW")), fgColor, bgColor);
      }
      else if (name.equals("SETCOLCOLORS")) {
        Table t = Convert.o2t(value);
        Color fgColor=null, bgColor=null;
        if (t.get("FG")!=null) {fgColor=MColor.getColor(Convert.o2s(t.get("FG")));}
        if (t.get("BG")!=null) {bgColor = MColor.getColor(Convert.o2s(t.get("BG")));}
        gs.setColColors(Convert.o2l(t.get("COLUMN")),fgColor, bgColor);
      }
      else return null;
      return value;
  }

  private static String[] keys = {"NUMROWS","NUMCOLS","COLVECSIZE","CURROW","CURCOL","SCELL","CELL","COLUMNVECTOR"};
  public String[] getKeys() { return keys; }

  @Override
  public Object getKey (String name) {
    if (name.equals("NUMROWS")) return Convert.l2o(gs.getRowCount());
    else if (name.equals("NUMCOLS")) return Convert.l2o(gs.getColCount());
    else if (name.equals("COLVECSIZE")) return Convert.l2o(currColSize);
    else if (name.equals("CURRROW")) return Convert.l2o(currRow);
    else if (name.equals("CURRCOL")) return currColumn;
    else if (name.equals("SCELL")) return Convert.o2s(gs.getCell(currRow, currColumn));
    else if (name.equals("CELL")) return gs.getCell(currRow, currColumn);
    else if (name.equals("COLUMNVECTOR")) {
      if (currColumn != null) {
        Object v = gs.getColumnVector(currColumn);
        currColSize = ((Vector<?>)v).size();
        return v;
      }
    }
    return null;
  }

  /** Get the poll interval in seconds
   *  @return poll interval in seconds
   */
  public int getPollInterval () {
    return poll;
  }
  /** Set the poll interval in seconds
   *  @param pollInterval poll interval in seconds
   */
  public void setPollInterval (int pollInterval) {
    poll = pollInterval;
  }
  /** Set the SCROLL end mode 0=OFF, 1=ON
   *  @param i SCROLL end mode 0=OFF, 1=ON
   */
  public void setScrollEnd (int i) {
    boolean scrollState = false;
    if (i == 1) scrollState = true;
    setScrollEnd(scrollState);
  }
  /** Set the SCROLL end mode FALSE=Do not scroll to end, TRUE=Scroll to end
   *  @param scrollToEnd FALSE=Do not scroll to end, TRUE=Scroll to end
   */
  public void setScrollEnd (boolean scrollToEnd) {
    gs.scrollEndSw = scrollToEnd;
  }

  /** For */
  public Object getNextLink() { //jph -- why is this here if chainable is not implemented??
    return gs;
  }
  /** Get the color choices
   *  @return color choices in String form
   */
  public static String getColorChoices() {
    return colorChoices;
  }

  /* Wrapper class around MWindow to override help data when the
     help key (F1) is pressed.
   */
  private class ListWindow extends MWindow {
    public ListWindow (String name, MessageHandler mh) {
      super(name, mh,true);
    }

    /** Return the Help Data for the parent list component as a Table.
     Sample Table return: {NAME="IDLIST",VALUE="-127.0",ROW=L:2,COL=L:4}
     */
    protected Table getHelpData() {
      Table data = new Table();
      data.put("NAME",  getID());
      data.put("VALUE", gs.getSelectedCellData());
      data.put("ROW",   gs.getSelectedRow());
      data.put("COL",   gs.getSelectedColumn());
      return data;
    }
  }

  /** @since NeXtMidas 3.5.4 */
  @Override
  public GridSheet getGridSheet () {
    return gs;
  }
}
