package nxm.sys.libg;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;

import nxm.sys.inc.MessageHandler;
import nxm.sys.inc.ProvisionalUseOnly;
import nxm.sys.lib.Convert;
import nxm.sys.lib.Format;
import nxm.sys.lib.Message;
import nxm.sys.lib.Time;

/**
   A class to implement a graphical numerical valuator widget. This widget holds
   a value of one of four types: T-Time, D-Double (default), F-Float, or L-Long.

   @author Jeff Schoen
 */
public class GValue extends GWidget implements ActionListener {

  /** widget flags  */
  public static final String flagsList = GWidget.flagsList+
    ",NoNumb,Slider,Gauge,VuMeter,Minimum,Maximum,Percent,Hex,Discrete,MsgOnDrag,RightJustify";

  /** flags mask: do not display the numeric value text */
  public static final int NOTEXT = 0x0100;

  /** flags mask: display as slider bar */
  public static final int SLIDER = 0x0200;

  /** flags mask: display has empty/full gauge */
  public static final int GAUGE = 0x0400;

  /** flags mask: display as blocked gauge (VuMeter) */
  public static final int VUM = 0x0800;
  /** flag for Minimum */
  public static final int MINIMUM = 0x1000;
  /** flag for Maximum */
  public static final int MAXIMUM = 0x2000;
  /** flag for Percent */
  public static final int PERCENT = 0x4000;

  /** flags mask: display text in hexidecimal format */
  public static final int HEX     = 0x8000;

  /** flags mask: only allow discrete values at increment */
  public static final int DISCRETE= 0x10000;

  /** Send messages for incremental slider movements */
  public static final int MSGONDRAG = 0x20000;

  /** Allow users to right justify DVAL, FVAL, LVAL, TVAL, and XVAL values*/
  public static final int RIGHTJUSTIFY = 0x40000;

  private MJTextField jtexti;
  private String  sval;          // Current value (with UNITS) as a string
  private double  val;           // value val=current
  private double  minVal;        // minimum allowed value
  private double  maxVal;        // maximum allowed value
  private double  deltaVal;        // value vald=delta
  private boolean edit;
  private boolean dragValue;
  private char    datatype='D';
  private MBox    bp;            // box for prompt
  private MBox    bt;            // box for text
  private MBox    bc;            // box for control
  private int     digits;
  private int     pw;
  private Format  nf;
  private int     mouseMode=0;
  private boolean waiting = false; // waiting for slider to stop moving

  /**
     Construct a new GValue with the value type indicated.
     @param parent the parent object
     @param name the name of this GValue
     @param val the current value
     @param val1 the minimum value
     @param val2 the maximum value
     @param vald the delta
     @param type the value's data type
     @param flags the widget flags
     @param mh the MessageHandler
  */
  public GValue (Object parent, String name, double val, double val1,
                double val2, double vald, char type, int flags, MessageHandler mh) {
    super("GValue",parent,name,flags,mh);
    open (name, val, val1, val2, vald, type);
    addTo (parent);
  }

  /**
     Construct a new GValue with a double value type.
     @param parent the parent object
     @param name the name of this GValue
     @param val the current value
     @param val1 the minimum value
     @param val2 the maximum value
     @param vald the delta
     @param flags the widget flags
     @param mh the MessageHandler
   */
  public GValue (Object parent, String name, double val, double val1,
                 double val2, double vald, int flags, MessageHandler mh) {
    this (parent,name,val,val1,val2,vald,'D',flags,mh);
  }

  /**
     Construct a new GValue with a float value type.
     @param parent the parent object
     @param name the name of this GValue
     @param val the current value
     @param val1 the minimum value
     @param val2 the maximum value
     @param vald the delta
     @param flags the widget flags
     @param mh the MessageHandler
   */
  public GValue (Object parent, String name, float val, float val1,
                 float val2, float vald, int flags, MessageHandler mh) {
    this (parent,name,(double)val,(double)val1,
        (double)val2,(double)vald,'F',flags,mh);
  }

  /**
     Construct a new GValue with a long value type.
     @param parent the parent object
     @param name the name of this GValue
     @param val the current value
     @param val1 the minimum value
     @param val2 the maximum value
     @param vald the delta
     @param flags the widget flags
     @param mh the MessageHandler
   */
  public GValue (Object parent, String name, int val, int val1,
                 int val2, int vald, int flags, MessageHandler mh) {
    this (parent,name,(double)val,(double)val1,
        (double)val2,(double)vald,'L',flags,mh);
  }

  /**
     Opens the graphics window for this GValue and initializes parameters.
     @param name the name of this GValue
     @param val the current value
     @param val1 the minimum value
     @param val2 the maximum value
     @param vald the delta
     @param type the value's data type
   */
  public void open (String name, double val, double val1, double val2, double vald, char type) {
    super.open();
    this.minVal   = val1;
    this.maxVal   = val2;
    this.deltaVal = vald;
    this.val      = val;
    datatype      = type;
    pw = stringWidth(title);
    setupTextField();
    pos.w = 0;
    nf = new Format();
    if (datatype=='T') setFormat("HH:MM:SS");
    else if (datatype=='L') setFormat("#0");
    else setFormat("##0.###E0");
  }

  /** Init the text field size, font, foreground, background and listener. */
  public void setupTextField () {
    if (!is(HEADLESS)) { // Since NeXtMidas 3.5.0. Skip if in Headless state. BUG 2817
      jtexti = new MJTextField ("",20);
      jtexti.setFont(font);
      // The widget theme is not yet set here, so I override the setWidgetTheme(..)
      // method in GWidget to affect the input text colors
      jtexti.setForeground(theme.cwfg);
      jtexti.setBackground(theme.cwbg);
      jtexti.addActionListener(this);
    }
  }

  /** Set this widget and the input component theme.
    @param o The theme in String, Table or Object form
  */
  public void setWidgetTheme (Object o) {
    // jph - I added this method because -- Seems that the widget theme is not yet set in the open()
    //       method below , so this MAY have no thus I override the setWidgetTheme(..) method in GWidget
    //       to affect the input text colors
    super.setWidgetTheme(o);
    if (!is(HEADLESS)) { // Since NeXtMidas 3.5.0. Skip if in Headless state. BUG 2817
      jtexti.setForeground(theme.cwfg);
      jtexti.setBackground(theme.cwbg);
    }
  }

  /** Sets the decimal format pattern used to display a textual representation of this GValue's
      current value.
    @param format the decimal format pattern string
                  (See the {@link Format} class for valid pattern Strings.)
  */
  public void setFormat (String format) {
    setFormat(format, null);
  }

  /** Sets the decimal format pattern used to display a textual representation of this GValue's
      current value.
    @param format the decimal format pattern string (See {@link Format} for valid pattern Strings.)
    @param unitMult Unit multiplier character or number
  */
  public void setFormat (String format, Object unitMult) {
    if (format==null || format.length()==0) return;
//    this.format = format;
    digits = format.length();
    digits += 2;  // leave romm for negative mantissa and exponents
    nf.setFormat(format);
    if (unitMult !=null) {
      nf.setExp(unitMult);
    } else {
      nf.setExp(minVal,maxVal);
    }
    setup();
    sval=null;
    setValue(val,0);
  }

  /** Get the Format pattern string
   *  @return the Format pattern string. */
  public String getFormat () {
    return nf.getFormat();
  }

  public void setup () {
    if (is(INLINE)) bw=0; else bw=DEFBW;
    int h = th+ta-(edit?0:compression);
    int w = tw*digits;
    if (units!=null) w += tw*(1+units.length());
    bp = new MBox (bw+tw, bw, pw, h);
    bc = new MBox (bp.x+bp.w, bw, th+tw, h);
    bt = new MBox (bc.x+bc.w, bw+h/2-th/2, w, th);
    pos.w = bt.x+bt.w+tw+bw;
    pos.h = h+bw+bw;
    if (fixWidth>pos.w) { pos.w=fixWidth; bt.w=pos.w-bt.x-bw-tw; }
    layout();
  }

  public void resize (int mode) {
    super.resize(mode);
    bt.w = pos.w-bt.x-bw-tw;
    placeTextInput (bc,jtexti);
  }

  /**
   * @deprecated Since NeXtMidas 3.9.0 to avoid graphical timing issues
   * use {@link #paintComponent(Graphics)} (for NXM-3213)
   */
  @Deprecated
  public void paint() {
    refresh();
  }

  @Override
  @ProvisionalUseOnly("Treat this as protected and should only be called by subclass paintComponent methods")
  public void paintComponent (Graphics g) {
    // Significant reworking of this code that used to be in paint to avoid
    // graphical timing issues (in 3.9.0 for NXM-3213)
    //if (g==null) return; // J. Czechowski got rid of this 3.9.0
    super.paintComponent(g);

    int ty = pos.h/2 + ta/2;
    shadowbox(g,0,0,pos.w,pos.h,SB_FLAT,SB_DEF);
    setColor(g,theme.cwti);
    setTextClip(g, panel, title, bp.x);  // added for Bug 2473
    g.drawString(title,bp.x,ty);
    g.setColor(theme.cwfg); // reset color back
    if (edit) return;
    boolean sat = is(SLIDER) && !is(NOTEXT);
    setValueBox(bt.x-3,bt.y,bt.w+5,bt.h+(sat? 3:0));
    if (!is(SLIDER|GAUGE)) drawValueBox(g,1);
    if (is(GAUGE) && maxVal>minVal) {
      shadowbox(g,bt.x-2,bt.y+1,bt.w+4,bt.h,SB_DEF);
      int ip = (int)(bt.w*(val-minVal)/(maxVal-minVal));
      setColor(g,theme.cwfh);
      g.fillRect(bt.x,bt.y+3,ip,bt.h-4);
    }
    if (is(SLIDER)) {
      double p = (maxVal>minVal)?  (val-minVal)/(maxVal-minVal) : 0.5;
      sliderbox(g,bt.x,bt.y+(sat? bt.h:bt.h/2),bt.w,0, p, 0);
    }
    if (is(VUM) && maxVal>minVal) {
      shadowbox(g,bt.x-2,bt.y+1,bt.w+4,bt.h,SB_DEF);
      double f = (val-minVal)/(maxVal-minVal);
      int i1 = (int)(bt.w*.08);
      int ip = (int)(bt.w*f);
      int i2 = (int)(bt.w*.8);
      int i3 = (int)(bt.w*.9); // Jeff's addition since 3.9.0
      int vw = 5;
      setColor(g,MColor.getColor((ip<i1)?0xa0a000:0x008000)); g.fillRect(bt.x,bt.y+3,ip,bt.h-4);
      // Jeff's addition since 3.9.0
      if (ip>i2) { setColor(g,MColor.getColor((ip>i3)?0x801000:0xa0a000)); g.fillRect (bt.x+i2,bt.y+3,ip-i2,bt.h-4); }
      if (ip<i1) g.drawRect(bt.x+1,bt.y+3,bt.w-2,bt.h-4);
      setColor(g,theme.cwbs); for (int i=bt.x+vw-1; i<bt.x+bt.w; i+=vw) g.drawLine(i,bt.y+4,i,bt.y+bt.h-2);
    }
    if (is(NOTEXT)) nf.setDigitPos(-1);
    else {
      String text = sval;
      int xLocation = 0;
      if (is(MINIMUM)) text = ""+nf.form(minVal)+" / "+text;
      if (is(MAXIMUM)) text = text+" / "+nf.form(maxVal);
      if (units!=null && units.length()>0) text += " "+units;
      if (is(VUM)) text = "";
      if (is(PERCENT)) text = text+" | "+Convert.l2s((int)(100*(val-minVal)/(maxVal-minVal)))+"%";
      // Since Nxm 3.7.0 Bug 2969
      if (is(RIGHTJUSTIFY)) {
        int textPixelLength = g.getFontMetrics().stringWidth(text);
        xLocation =  fixWidth - textPixelLength - 5;  // -5 gives a small buffer zone between the text and border
      } else {
        xLocation = bt.x;
      }
      if (sat) ty -= 2;
      setColor(g,theme.cwfg);
      setTextClip(g, panel, text, bt.x);  // keep text within shadowbox (Bug 2473)
      g.drawString(text,xLocation,ty);
      int digit = nf.getDigitPos();
      if (digit>=0 && digit<sval.length()) {
        setColor(g,theme.cwfh);
        char c = sval.charAt(digit);
        if (c>='0' && c<='9') {
          g.drawRect(bt.x+digit*tw,ty+2,tw-1,1);
          String tmpstr = sval.substring(digit,digit+1);
          setTextClip(g, panel, tmpstr, bt.x);  // keep text within shadowbox (Bug 2473)
          g.drawString(tmpstr,bt.x+digit*tw,ty);
        }
        setColor(g,theme.cwfg);
      }
    }
    drawDiamond(g,bc.x+bc.w/2, bc.y+bc.h/2, tw*2/3, mouseMode);
  }

  /** Sets the color of the selected text in the Gvalue
      @param c The color to set to
      @since NeXtMidas 2.9.0
   */
  public void setSelectedTextColor (Color c) {
    jtexti.setSelectedTextColor(c);
  }

  /** Gets the color of the selected text in the Gvalue
      @return The color
      @since neXtMidas 2.9.0
   */
  public Color getSelectedTextColor () {
    return jtexti.getSelectedTextColor();
  }

  /** Sets the background selection color of the textfield
      @param c The Color to set
      @since NeXtMidas 2.9.0
   */
  public void setSelectionColor (Color c) {
    jtexti.setSelectionColor(c);
  }

  /** Gets the background color of the text field selection
      @return The color
      @since NeXtMidas 2.9.0
   */
  public Color getSelectionColor () {
    return jtexti.getSelectionColor();
  }

  /** Sets the color of the selected text
      @param color The color to set to
      @since NeXtMidas 2.9.0
   */
  public void setSelectedTextColor (String color) {
    setSelectedTextColor(MColor.getColor(color));
  }

  /** Gets the color of the selected text
      @return The color
      @since neXtMidas 2.9.0
   */
  public String getSelectedTextColorString () {
    return MColor.toString(getSelectedTextColor());
  }

  /** Sets the background selection color of the selection
      @param c The Color to set
      @since NeXtMidas 2.9.0
   */
  public void setSelectionColor (String c) {
    setSelectionColor(MColor.getColor(c));
  }

  /** Gets the background color of the selection
      @return The color
      @since NeXtMidas 2.9.0
   */
  public String getSelectionColorString () {
    return MColor.toString(getSelectionColor());
  }

  public void actionPerformed (ActionEvent e) {
    String text = getTextFieldText();
    double value = val;
    try {
      if (!nf.isSimpleDateFormat()) {
        value = Convert.s2d(text);
      }
      else {
        String pattern = nf.getFormat().substring(4);
        try {
          value = new Time(text,pattern).getSec();
        }
        catch (Exception ex) {
          value = Convert.s2d(text); // allow STD time format entry too
        }
      }
    } catch (Exception ex) {
      M.warning("You entered an invalid number = '"+text+"'");
    }
    panel.remove(jtexti);
    panel.requestFocus();
    edit=false;
    setup();
    setValue(value,1);
  }

  private void sendMessage () {
    Object value = getValue();
    if (is(MONITOR)) M.results.put(monitorName,value);
    if (!waiting) {
      setActionType();   // BUG 2188
      sendWidgetMessage (label,actionType,value);
    }
  }

  public Object getValue () {
    Object data=null;
         if (datatype=='T') data = Double.valueOf(val);
    else if (datatype=='D') data = Double.valueOf(val);
    else if (datatype=='F') data = Float.valueOf((float)val);
    else if (datatype=='L') data = Integer.valueOf((int)val);
    return data;
  }

  /** Returns a string representation of the value of this widget, including units.*/
  public String getValueString () {
    return sval;
  }

  /** Poll for change in monitor.
     @return true - if there is a monitor for this GValue and the current value has changed
   */
  public boolean poll () {
    String monitorName = this.monitorName; // keep local reference to fix possible NPE for Bug 2118
    if (monitorName==null || !is(MONITOR)) return false;
    double newval = M.results.getD(monitorName);
    if (newval==val) return false;
    setActionType(CHANGED_BY_POLL);
    setValue(newval,0);

    // Wayne Dengel change - 08APR06 - To distinguish between a change due to action versus monitor
    sendWidgetMessage (label,CHANGED_BY_POLL,getValue());
    return true;
  }

  public String getLegend () {
    String rm = "";
    if (!is(PERM)) rm = ",RM=Cancel";
    if (mouseMode==0) {
      if (!is(PERM)) return "(MM=Configure,RM=Cancel)";
      else return "(MM=Configure)";
    }
    else if (mouseMode==1) return "(LM=Inc" + rm + ")";
    else if (mouseMode==2) return "(LM=Dec" + rm + ")";
    else if (mouseMode==3) return "(MM=Configure" + rm + ")";
    else if (is(GAUGE|SLIDER)) return "(LM=Set,RM=Edit)";
    else return "(LM=SelectDigit,RM=Edit)";
  }

  /** Process event messages for this widget locally.
   *  @return 1 after processing BUTTON and KEYPRESS messages
   *  -1 otherwise
   */
  @Override
  public int processEventMessage (String name, int info, Object data) {

    if (name.equals("POINTER")) {
      int mm=0;
      if (info<0) mm=0;  // 4.1.2 hover readouts allowed with NOEDIT
      else if (px>bc.x+bc.w) mm=8;
      //else if (px<=bc.x/* && is(PERM)*/) mm=3;
      else if (px<=bc.x) mm=0;
      else if (py>bc.y+bc.h/2) mm=2;
      else mm=1;
      if (mm!=mouseMode) {
        mouseMode=mm;
        refresh(bc);
        if (info==0) info=1;
      }
      if (is(VBOXMO) && Math.abs(info)==9) refreshValue();
      formatReadOut(info);
    }
    if (dragValue && name.equals("DRAGBOX")) { dragValue=false; return 1; }
    int drag = (name.equals("DRAG"))? info: 0;
    int button = (name.equals("BUTTON"))? info: 0;
    int keycode = (name.equals("KEYPRESS"))? info: 0;
    if (button==0 && keycode==0 && drag==0) return -1;
    if (button==2 && px<bc.x) configure();
    else if (is(NOEDIT));
    else if (button>=2 && px>bt.x && px<bt.x+bt.w) {  // ... the user selected the box...
      String str = sval;                              // ..."sval" contains the value & units,
      if (datatype!='T') {                            // ... we just want to display the value
        if ( datatype=='L') str = Convert.l2s((int)val);
        else                str = Convert.d2s(val);
      }
      else if (nf.isSimpleDateFormat()){ // NXM-3664 SimpleDateFormat support
        str = new Time(val).toString(nf.getSdfDisplayPattern());
      }
      jtexti.setText(str);
      edit=true;
      setup();
      panel.add(jtexti,0);
      jtexti.requestFocus();
      placeTextInput (bc,jtexti);
      nf.setDigitPos(-1);
    }
    else if (button==3 && !is(PERM)) {
      close();
    }
    else if (button>0 && mouseMode!=0 && mouseMode!=8) {
      double dv = (nf.isDigitSel()) ? getUpDownDelta() : deltaVal;
      if (button==1 && mouseMode==2) dv=-dv;
      if (button==3 && mouseMode!=2) dv=-dv;
      if (button==1 && mouseMode!=3) setValue(val+dv,1); //NEW
    }
    else if (button==1 && mouseMode==8) {
      if (is(GAUGE) || is(VUM) || (is(SLIDER) && (is(NOTEXT) || py>bt.y+bt.h-4)) ) {
        userAction = true;  // BUG 2188
        setValue(minVal + deltaVal*(int)((maxVal-minVal)*(px-bt.x)/bt.w/deltaVal+.5), 1);
      }
      else if (!is(NOTEXT)) {
        nf.setDigitPos( (px-bt.x)/tw );
        refresh(bbox);
      }
    }
    else if (is(GAUGE|SLIDER) && drag==1 && mouseMode==8) {
      userAction = true;  // BUG 2188
      setValue(minVal + deltaVal*(int)((maxVal-minVal)*(px-bt.x)/bt.w/deltaVal+.5), 1);
      dragValue = true;
    }
    else if (keycode == KeyEvent.VK_UP) {
      double dv = (nf.isDigitSel()) ? getUpDownDelta() : deltaVal;
      setValue(val+dv,1);
    }
    else if (keycode == KeyEvent.VK_DOWN) {
      double dv = (nf.isDigitSel()) ? getUpDownDelta() : deltaVal;
      setValue(val-dv,1);
    }
    else if (keycode == KeyEvent.VK_LEFT) {
      nf.incDigitPos();
      refresh(bbox);
    }
    else if (keycode == KeyEvent.VK_RIGHT) {
      nf.decDigitPos();
      refresh(bbox);
    }
    else if (keycode == KeyEvent.VK_ENTER && !is(PERM)) { // Close on ENTER
      close();
    }
    formatReadOut(1);
    return 1;
  }

  /** Get the delta for the up or down arrow
   *  If have fractional seconds, need to adjust for number of decimal places plus the dot
   *  If have SimpleDateFormat, the calculation is more complex
   *  @return delta value
   */
  private double getUpDownDelta () {
    if (!nf.isSimpleDateFormat()) {
      int ndp = nf.getDecPlacesHMS();
      if (ndp==0) {
        return nf.getDigitInc();
      }
      else {
        int selected = nf.getDigitSel();
        if (selected==ndp) return 0; // the dot was selected
        int dotAdjustment = (selected > ndp) ? 1 : 0;
        return nf.getDigitInc(selected-ndp-dotAdjustment);
      }
    }
    else {
      return deltaSimpleDateFormat();
    }
  }

  /** Get the value in seconds of the selected position
   *  in the SimpleDateFormatPattern
   *
   *  @return the value in seconds of this position
   */
  private double deltaSimpleDateFormat () {
    int pos = nf.getDigitPos();
    String charAtPos = "" + sval.charAt(pos);
    if (!charAtPos.matches("[0-9]")) return 0; // position obviously shifted to non-digit
    boolean digitsAfter = pos < sval.length();
    String ending = (digitsAfter) ? sval.substring(pos+1) : "";
    String val0 = sval.substring(0,pos) + 0 + ending;
    String val1 = sval.substring(0,pos) + 1 + ending;
    Time val0Time = new Time(val0, nf.getFormat().substring(4));
    Time val1Time = new Time(val1, nf.getFormat().substring(4));
    double deltaTime = val1Time.diff(val0Time);
    return deltaTime;
  }

  /**
     Sets the value of this widget.
     @param value the new value
   */
  public void setValue (double value) {
    setValue(value,0);
  }

  /**
     Sets the value of this widget and if the flag is set, sends a widget message.
     @param value the new value
     @param flag if <CODE>(flag&gt;=1)</CODE> a widget message is sent<BR>
   */
  public void setValue (double value, int flag) {
    if (maxVal>minVal) value = Math.max(minVal, (maxVal==-1) ? value : Math.min(maxVal,value)); // if maxVal=-1 there is no max. Always just use value as the max value.
    if (value==val && sval!=null && flag<2)  return;
    if (is(DISCRETE)) value = minVal + Math.round((value-minVal)/deltaVal)*deltaVal;
    if (datatype=='L') value = Math.round(value);
    val = value;
    if (is(HEX)) sval = Convert.l2x((int)value);
    else sval = nf.form(val);
    refresh(bbox);
    if (dragging==1 && !is(MSGONDRAG) && flag>=1) waiting = true;
    if (flag>=1) sendMessage();
  }

  /** Over-ride of {@link MWindow#mouseReleased(MouseEvent)} to capture when a mouse
      was released so that we do not send messages needlessly when a SLIDER is
      still being dragged.
   */
  public void mouseReleased (MouseEvent e) {
    super.mouseReleased(e);
    if (waiting) {
      waiting = false;
      sendMessage();
    }
  }

  /**
     Sets the value of this GPrompt.
     @param value the new value
   */
  public void setValue (String value) {
    setValue(Convert.s2d(value),0);
  }

  /** Sets the value of this GPrompt and sends a widget message. This is
      the same as <CODE>setValue(value, 1)</CODE>.
      @param value the new value
   */
  public void setAction (String value) {
    setActionType(); // check whether mouse click triggered action
         if (value.equals("Inc")) setValue(val+deltaVal,2);
    else if (value.equals("Dec")) setValue(val-deltaVal,2);
    else if (value.equals("Min")) setValue(minVal,2);
    else if (value.equals("Max")) setValue(maxVal,2);
    else if (value.equals("Mid")) setValue((minVal+maxVal)/2,2);
    else setValue(Convert.s2d(value),2);
  }

  /** Get the type of data
   *  @return The type of data (i.e. 'D' for double, 'L' for int */
  public char getType () { return datatype; }

  /** Get the minimum value for this GPrompt.
      @return the minimum value
   */
  public double getMin () { return minVal; }

  /** Get the maximum value for this GPrompt.
      @return the maximum value
   */
  public double getMax () { return maxVal; }

  /** Get the delta value for this GPrompt.
      @return the delta
   */
  public double getDelta () { return deltaVal; }

  /** Set the minimum value for this GPrompt.
      @param value the new minimum value
   */
  public void setMin (double value) { minVal=value; if (is(MINIMUM)||is(PERCENT)) refreshValue(); }

  /** Set the maximum value for this GPrompt.
      @param value the new maximum value
   */
  public void setMax (double value) { maxVal=value; if (is(MAXIMUM)||is(PERCENT)) refreshValue(); }

  /** Set the delta value for this GPrompt.
      @param value the new delta value
   */
  public void setDelta (double value) { deltaVal=value; }

  /** Returns a formatted textual representation of this GValue's current value.
      @return the string representation of the current value
   */
  public String getText () { return sval; }

  private String getTextFieldText () {
    return jtexti.getText();
  }

  /** Process GValue config messages.
      @param msg {@inheritDoc}
      @return 1
   */
  public int processMessage (Message msg) {
    if (msg.name.equals("GVALUE.CFG")) {
      flags = msg.info;
      setup();
      refresh();
    }
    return 1;
  }

  /** Returns a string representation of this widget's current value in list format.
      @return the string representation of this widget
   */
  public String toString () {
    return toList(""+val,""+minVal+","+deltaVal+"<,"+sval+",>"+deltaVal+","+maxVal);
  }

}
