    001package org.vishia.gral.base;
    002
    003
    004import java.util.Map;
    005
    006import org.vishia.gral.ifc.GralColor;
    007import org.vishia.gral.ifc.GralFont;
    008import org.vishia.gral.ifc.GralRectangle;
    009import org.vishia.gral.ifc.GralTextFieldUser_ifc;
    010import org.vishia.gral.ifc.GralUserAction;
    011import org.vishia.gral.ifc.GralTextField_ifc;
    012import org.vishia.gral.ifc.GralWidget_ifc;
    013import org.vishia.util.CalculatorExpr;
    014import org.vishia.util.DataAccess;
    015import org.vishia.util.Removeable;
    016import org.vishia.util.StringFunctions;
    017
    018/**This is the base class for all widgets which represents a simple text.
    019 * @author Hartmut Schorrig
    020 *
    021 */
    022public class GralTextField extends GralWidget implements GralTextField_ifc
    023{
    024  /**Version, history and license .
    025   * <ul>
    026   * <li>2015-05-04 Hartmut new: {@link #setBorderWidth(int)} to show the text field with a border.
    027   * <li>2015-05-04 Hartmut chg: {@link #setLongValue(long)} is more complexly, a calculation can result in a float value. Fixed.
    028   * <li>2015-05-02 Hartmut chg: Calculation of the {@link GraphicImplAccess#posPrompt} and ...posField is processed
    029   *   in this class commonly for SWT and AWT implementation.
    030   * <li>2014-02-10 Hartmut chg: Constructor with Parameter {@link Type}, supports password field.
    031   * <li>2013-12-22 Hartmut chg: Now {@link GralTextField} uses the new concept of instantiation: It is not
    032   *   the super class of the implementation class. But it provides {@link GralTextField.GraphicImplAccess}
    033   *   as the super class.
    034   * <li>2013-03-13 Hartmut new {@link #setText(CharSequence, int)}: supports {@link GralWidget_ifc#isNotEditableOrShouldInitialize()} to support
    035   *   edit field handling.
    036   * <li>2013-03-04 Hartmut chg: The {@link #setText(CharSequence, int)} overwriting concept is faulty.
    037   *   Because an accidentally change of text prevents setting the correct text. Check of {@link #bTextChg}
    038   *   removed. The preventing of overwriting an edit field should be prevented outside as condition of
    039   *   call of {@link #setText(CharSequence)}.
    040   * <li>2013-02-22 GralTextField: edit field now works with GralShowMethods syncVariableOnFocus,
    041   *   {@link #bTextChg} detects whether any input is taken. {@link #isChanged(boolean)} returns this information
    042   *   and resets {@link #bTextChg}. {@link #setText(CharSequence, int)} only sets if the text is unchanged.
    043   *   Therefore a changed text won't be over-written by an setText().
    044   * <li>2012-09-24 Hartmut new: {@link #setValue(double)} and {@link #setValue(Object[])} for double values.
    045   * <li>2012-04-17 Hartmut new: {@link #sFormat2} etc: Now format "int16AngleDegree" etc. are recognized
    046   *   as special format. Improved calling {@link #calculator} in {@link #setValue(float)}.
    047   * <li>2012-04-16 Hartmut new: {@link #isChanged()}, {@link #setUser(GralTextFieldUser_ifc)}
    048   * <li>2012-04-01 Hartmut new: {@link #setValue(float)} now supports formatting view.
    049   *   This algorithm was used for the {@link org.vishia.guiInspc.InspcGui} before.
    050   * <li>2011-11-18 Hartmut new {@link #setMouseAction(GralUserAction)}. This method should be
    051   * an abstract method of all {@link GralWidget} but it is used yet only here.
    052   * <li>2011-09-00 Hartmut Creation to build a platform-indenpenden representation of text field.
    053   * </ul>
    054   *
    055   * <b>Copyright/Copyleft</b>:
    056   * For this source the LGPL Lesser General Public License,
    057   * published by the Free Software Foundation is valid.
    058   * It means:
    059   * <ol>
    060   * <li> You can use this source without any restriction for any desired purpose.
    061   * <li> You can redistribute copies of this source to everybody.
    062   * <li> Every user of this source, also the user of redistribute copies
    063   *    with or without payment, must accept this license for further using.
    064   * <li> But the LPGL ist not appropriate for a whole software product,
    065   *    if this source is only a part of them. It means, the user
    066   *    must publish this part of source,
    067   *    but don't need to publish the whole source of the own product.
    068   * <li> You can study and modify (improve) this source
    069   *    for own using or for redistribution, but you have to license the
    070   *    modified sources likewise under this LGPL Lesser General Public License.
    071   *    You mustn't delete this Copyright/Copyleft inscription in this source file.
    072   * </ol>
    073   * If you are intent to use this sources without publishing its usage, you can get
    074   * a second license subscribing a special contract with the author.
    075   *
    076   * @author Hartmut Schorrig = hartmut.schorrig@vishia.de
    077   */
    078  //@SuppressWarnings("hiding")
    079  public static final String version = "2015-09-12";
    080
    081
    082  public enum Type{ password, editable};
    083
    084  protected int caretPos;
    085
    086  /**The width of an extra border arround the text field to mark it.
    087   * 0: initially, no border. Note: The color of the border is the {@link GralWidget.DynamicData#lineColor}
    088   */
    089  protected int borderwidth;
    090
    091  /**The prompt to the text field. */
    092  protected String sPrompt, sPromptStylePosition;
    093
    094  //protected GralColor colorBack = GralColor.getColor("wh"), colorText = GralColor.getColor("bk");
    095
    096  //protected GralFont fontText;
    097
    098  /**A calculator to show calculated float values. It is null if it isn't used. */
    099  private CalculatorExpr calculator;
    100
    101  private String sFormat2;
    102
    103  protected GralTextFieldUser_ifc user;
    104
    105  /**It is used for some operations. */
    106  protected final GralGraphicThread windowMng;
    107
    108  final boolean bPassword;
    109
    110  /**Constructs a text field with given properties
    111   * @param posName Position and Name of the field. Maybe null if it is not need in management by name
    112   *   See {@link GralWidget#GralWidget(String, char)}.
    113   * @param property password, editable, maybe left empty.
    114   */
    115  public GralTextField(String posName, Type... property){
    116    super(posName, 't');
    117    boolean bPassword1 = false;
    118    if(property !=null){
    119      for(int ii=0; ii<property.length; ++ii){
    120        switch(property[ii]){
    121          case password: bPassword1 = true; break;
    122          case editable: setEditable(true);
    123        }
    124      }
    125    }
    126    bPassword = bPassword1;
    127    windowMng = null;
    128    setBackColor(GralColor.getColor("wh"),0);
    129    setTextColor(GralColor.getColor("bk"));
    130  }
    131
    132  /**Constructs a text field with given properties
    133   * @param name Name of the field. Maybe null if it is not need in management by name
    134   * @param property password, editable, maybe left empty.
    135   * @deprecated since 2016-09,use {@link GralTextField#GralTextField(String, Type...)} with "@pos=name"
    136   */
    137  public GralTextField(String pos, String name, Type... property){
    138    this(pos !=null ? (pos.startsWith("@") ? "" : "@" + pos + "=" + name) : name, property);
    139  }
    140
    141
    142  @Deprecated public GralTextField(String name, char whatis, GralMng mng){
    143    super(name, whatis, mng);
    144    bPassword = false;
    145    setBackColor(GralColor.getColor("wh"),0);
    146    setTextColor(GralColor.getColor("bk"));
    147    this.windowMng = mng.gralDevice;
    148  }
    149
    150
    151
    152  public void setPrompt(String sPrompt){
    153    this.sPrompt = sPrompt;
    154    if(_wdgImpl !=null){
    155      dyda.setChanged(GraphicImplAccess.chgPrompt);
    156      repaint();
    157    } else {
    158      this.sPromptStylePosition = "t";
    159    }
    160  }
    161
    162
    163  public void setPrompt(String sPrompt, String sPromptPos){
    164    this.sPrompt = sPrompt;
    165    this.sPromptStylePosition = sPromptPos;
    166    if(_wdgImpl !=null){
    167      throw new IllegalStateException("setPrompt(prompt, style) can only applied on creation, use setPrompt(text) to change the prompt.");
    168    }
    169  }
    170
    171
    172  public void setUser(GralTextFieldUser_ifc user){
    173    this.user = user;
    174  }
    175
    176
    177
    178  @Override public void setFormat(String sFormat){
    179    super.setFormat(sFormat);
    180    calculator = null;  //set it on first usage.
    181    sFormat2 = null;
    182  }
    183
    184
    185
    186
    187  /**Sets a float value into a text field.
    188   * The float value may be calculated and formatted:
    189   * <ul>
    190   * <li>If the {@link #setFormat(String)} of this widget starts with '!' and contains a second '!',
    191   *   the String between that is used as expression to calculate the float value
    192   *   using {@link CalculatorExpr}. Therewith any calculation can be done.
    193   * <li>The rest after the "!expression!" or the given format is used for String.format(sFormat, value)
    194   *   to determine the output appearance.
    195   * <li>If no format is given, the value will be shown in proper readable appearance. That assures
    196   *   that the value is able to present in a short text field, using maximal 9 digits.
    197   * <li>If no format is given and the absolute of the value is less 0.000001 but not 0,
    198   *   a "0.000001" with the correct sign will be shown. It shows, there is a less value, but not null.
    199   * <li>If no format is given and the value is in range up to 1 Billion, it is shown with "k", "M"
    200   *   for kilo and Mega with max 3 digits before dot and 3 digits after the dot.
    201   * <li>if no format is given and the value is greater than 1 Billion, it is shown with exponent.
    202   * <ul>
    203   *
    204   * @see org.vishia.gral.base.GralWidget#setValue(float)
    205   */
    206  @Override public void setValue(final float valueP){
    207    final String sFormat1;
    208    float value;
    209    String sShow;
    210    if(calculator !=null){
    211      try {
    212        CalculatorExpr.Value value1 = calculator.calcDataAccess(null, valueP);
    213        value = value1.floatValue();
    214      } catch (Exception e) {
    215        value = 7777777.777f;
    216      }
    217      sFormat1 = this.sFormat2;
    218    } else if(sFormat !=null){
    219      if(sFormat.startsWith("!")){
    220        final String sFormat3;
    221        if(sFormat.startsWith("!16hi!") || sFormat.startsWith("!16lo!")) {
    222          sFormat3 = sFormat.substring(5);  //from !
    223        } else {
    224          sFormat3 = this.sFormat;
    225        }
    226        int posEnd = sFormat3.indexOf('!',1);
    227        if(posEnd >=0){
    228          String sExpr = sFormat3.substring(1, posEnd);
    229          this.sFormat2 = sFormat1 = sFormat3.substring(posEnd+1);
    230          if(calculator ==null){
    231            calculator = new CalculatorExpr();
    232            String sError = calculator.setExpr(sExpr);
    233            if(sError !=null){
    234              //console.writeError(sError);
    235              calculator = null;
    236            }
    237          }
    238          if(calculator !=null){
    239            value = calculator.calc(valueP);
    240          } else {
    241            value = valueP;
    242          }
    243        } else {
    244          sFormat1 = sFormat;
    245          value = valueP;
    246        }
    247      } else if(sFormat.startsWith("int16AngleDegree")){
    248        this.calculator = new CalculatorAngle16();
    249        sFormat1 = this.sFormat2 = "%3.3f";
    250        value = calculator.calc(valueP);
    251      } else if(sFormat.startsWith("int32AngleDegree")){
    252        this.calculator = new CalculatorAngle32();
    253        sFormat1 = this.sFormat2 = "%3.3f";
    254        value = calculator.calc(valueP);
    255      } else {
    256        sFormat1 = sFormat;
    257        value = valueP;
    258      }
    259
    260    } else { //sFormat == null
    261      sFormat1 = null;  //no expression
    262      value = valueP;
    263    }
    264    if(sFormat1 !=null && sFormat.length() >0){
    265      try{ sShow = String.format(sFormat1, new Float(value)); }
    266      catch(java.util.IllegalFormatException exc){
    267        sShow = null;  //maybe integer
    268      }
    269      if(sShow == null){
    270        long value1;
    271        if(sFormat.startsWith("+")){
    272          if(value <0){
    273            value1 = (long)(((int)value) + 0x80000000L);
    274          } else {
    275            value1 = (int)value;
    276          }
    277          //if(value1 <0) { value1 += 0x8000000000000000L; }
    278        } else {
    279          value1 = (int)value;
    280        }
    281        try{ sShow = String.format(sFormat, new Long(value1)); }
    282        catch(java.util.IllegalFormatException exc){
    283          sShow = "?format";
    284        }
    285      }
    286    } else { //no format given
    287      float valueAbs = Math.abs(value);
    288      if(value == 0.0f){ sShow = "0.0"; }
    289      else if(valueAbs < 1.0e-7f){ sShow = value < 0 ? "-0.0000001" : "0.0000001"; }  //shorten output, don't show exponent.
    290      else if(valueAbs < 1.0f){ sShow = String.format("%1.7f", new Float(value)); }  //shorten output, don't show exponent.
    291      else if(valueAbs < 1.0e3f){ sShow = String.format("%3.4f", new Float(value)); }  //shorten output, don't show exponent.
    292      else if(valueAbs < 1.0e6f){ sShow = String.format("%3.3f k", new Float(value/1000)); }  //shorten output, don't show exponent.
    293      else if(valueAbs < 1.0e9f){ sShow = String.format("%3.3f M", new Float(value/1000000)); }  //shorten output, don't show exponent.
    294      else if(valueAbs >= 1.0e9f){ sShow = String.format("%3.3g", new Float(value)); }  //shorten output, don't show exponent.
    295      //else if(valueAbs >= 1e6){ sShow = Float.toString(value/1000000) + " M"; }  //shorten output, don't show exponent.
    296      else { sShow = Float.toString(value); }
    297
    298    }
    299    setText(sShow);
    300  }
    301
    302
    303
    304  /**Sets a long value into a text field.
    305   * The long value may be calculated and may be formatted:
    306   * <ul>
    307   * <li>If the {@link #setFormat(String)} of this widget starts with '!' and contains a second '!',
    308   *   the String between that is used as expression to calculate the long value
    309   *   using {@link CalculatorExpr}. Therewith any calculation can be done. The result may be a float or double.
    310   * <li>The rest after the "!expression!" or the given format is used for String.format(sFormat, value)
    311   *   to determine the output appearance.
    312   * <li>If The sFormat has the form "!16lo!expr!format" ("!16lo" as prefix), then additional to the expression the 16 lo-Bits are filtered firstly.
    313   *   Adequate starting with "!!16hi.." filteres the Bits 31..16 to bit 15..0 before calculate the expression.
    314   *   This is especially if 2 * 16 bit for different values are accessed in one 32 bit address position of an processor which can only access 32 bit.
    315   * <li>As special feature a format <code>int32AngleDegree</code> and <code>int16AngleDegree</code>
    316   *   is supported, if that text is contained in the format string. The value should come from an integer,
    317   *   which contains an angle value with wrap-around-presentation: 0x7fffffff (32 bit) or 0x7fff (16 bit) and
    318   *   0x80000000 which is exactly 180 degree. This format helps to calculate with angle values in range
    319   *   of -180...+179.99999 degree. The presentation in the text field is shown as degree angle value.
    320   * <li>If no format is given, the value will be shown as normal decimal long value.
    321   * <ul>
    322   *
    323   * @see org.vishia.gral.base.GralWidget#setValue(float)
    324   */
    325  @Override public void setLongValue(final long valueP){
    326    final String sFormat1;
    327    //final long value;
    328    CalculatorExpr.Value value1;
    329    String sShow;
    330    if(sFormat !=null && (sFormat.startsWith("!") || sFormat.startsWith("int"))) { //should have a calculator.
    331      if(calculator == null){
    332        if(sFormat !=null && sFormat.startsWith("int16AngleDegree")){
    333          this.calculator = new CalculatorAngle16();
    334          sFormat1 = this.sFormat2 = "%3.3f";
    335        } else if(sFormat !=null && sFormat.startsWith("int32AngleDegree")){
    336          this.calculator = new CalculatorAngle32();
    337          sFormat1 = this.sFormat2 = "%3.3f";
    338        } else {
    339          final String sFormat3;
    340          if(sFormat.startsWith("!16hi!") || sFormat.startsWith("!16lo!")) {
    341            sFormat3 = sFormat.substring(5);  //from !
    342          } else {
    343            sFormat3 = this.sFormat;
    344          }
    345          int posEnd = sFormat3.indexOf('!',1);
    346          if(posEnd >=0){
    347            String sExpr = sFormat3.substring(1, posEnd);
    348            sFormat1 = this.sFormat2 = sFormat3.substring(posEnd+1);
    349            if(calculator ==null){
    350              calculator = new CalculatorExpr();
    351              String sError = calculator.setExpr(sExpr);
    352              if(sError !=null){
    353                //console.writeError(sError);
    354                calculator = null;
    355              }
    356            }
    357          } else {
    358            sFormat1 = this.sFormat2 = sFormat;  //!...error
    359          }
    360        }
    361      } //create calculator.
    362      else {
    363        sFormat1 = this.sFormat2;  //Should be correct
    364      }
    365    } else {
    366      sFormat1 = this.sFormat2 = sFormat; //maybe null or a normal format string
    367      //value = valueP;
    368    }
    369
    370    if(calculator !=null){ //use it.
    371      try {
    372        if(sFormat.startsWith("!16hi!")) {
    373          value1 = calculator.calcDataAccess(null, (float)(((int)valueP) >>16));
    374        }
    375        else if(sFormat.startsWith("!16lo!")) {
    376          value1 = calculator.calcDataAccess(null, (float)((short)valueP));
    377        } else {
    378          value1 = calculator.calcDataAccess(null, valueP);
    379        }
    380        //value = (long)value1.doubleValue();
    381      } catch (Exception e) {
    382        value1 = new CalculatorExpr.Value(777777L);  //a long value
    383        //value = 7777777L;
    384      }
    385    } else {
    386      value1 = new CalculatorExpr.Value(valueP);  //a long value
    387    }
    388
    389    if(sFormat1 !=null && sFormat.length() >0){
    390      char type = value1.type();
    391      if(type == 'J') {
    392        long value = value1.longValue();
    393        if(sFormat1.startsWith("+") && value <0) { //special handling. It is an unsigned int, convert it to long
    394          value = (value + 0x8000000000000000L) & 0x0ffffffffL;
    395          //sFormat1 = sFormat1.substring(1);
    396        }
    397        try{ sShow = String.format(sFormat1, new Long(value)); }
    398        catch(java.util.IllegalFormatException exc){
    399          sShow = "?format";
    400        }
    401      } else {
    402        double value = value1.doubleValue();
    403        try{ sShow = String.format(sFormat1, new Double(value)); }
    404        catch(java.util.IllegalFormatException exc){
    405          sShow = "?format";
    406        }
    407      }
    408
    409    } else { //no format given
    410      sShow = String.format("%d", new Long(value1.intValue()));
    411    }
    412    setText(sShow);
    413  }
    414
    415
    416
    417  /**Sets a float value into a text field.
    418   * The float value may be formatted:
    419   * <ul>
    420   * <li>If the {@link #setFormat(String)} of this widget starts with '!' and contains a second '!',
    421   *   the String between that is used as expression to calculate the float value
    422   *   using {@link CalculatorExpr}. Therewith any calculation can be done.
    423   * <li>The rest after the "!expression!" or the given format is used for String.format(sFormat, value)
    424   *   to determine the output appearance.
    425   * <li>If no format is given, the value will be shown in proper readable appearance. That assures
    426   *   that the value is able to present in a short text field, using maximal 9 digits.
    427   * <li>If no format is given and the absolute of the value is less 0.000001 but not 0,
    428   *   a "0.000001" with the correct sign will be shown. It shows, there is a less value, but not null.
    429   * <li>If no format is given and the value is in range up to 1 Billion, it is shown with "k", "M"
    430   *   for kilo and Mega with max 3 digits before dot and 3 digits after the dot.
    431   * <li>if no format is given and the value is greater than 1 Billion, it is shown with exponent.
    432   * <ul>
    433   *
    434   * @see org.vishia.gral.base.GralWidget#setValue(float)
    435   */
    436  public void setValue(final double valueP){
    437    final String sFormat1;
    438    double value;
    439    String sShow;
    440    if(calculator !=null){
    441      try {
    442        CalculatorExpr.Value value1 = calculator.calcDataAccess(null, valueP);
    443        value = value1.doubleValue();
    444      } catch (Exception e) {
    445        value = 7777777.777f;
    446      }
    447      sFormat1 = this.sFormat2;
    448    } else if(sFormat !=null){
    449      if(sFormat.startsWith("!")){
    450        int posEnd = sFormat.indexOf('!',1);
    451        if(posEnd >=0){
    452          String sExpr = sFormat.substring(1, posEnd);
    453          this.sFormat2 = sFormat1 = sFormat.substring(posEnd+1);
    454          if(calculator ==null){
    455            calculator = new CalculatorExpr();
    456            String sError = calculator.setExpr(sExpr);
    457            if(sError !=null){
    458              //console.writeError(sError);
    459              calculator = null;
    460            }
    461          }
    462          if(calculator !=null){
    463            try {
    464              CalculatorExpr.Value value1 = calculator.calcDataAccess(null, valueP);
    465              value = value1.doubleValue();
    466            } catch (Exception e) {
    467              value = 7777777.777f;
    468            }
    469          } else {
    470            value = valueP;
    471          }
    472        } else {
    473          sFormat1 = sFormat;
    474          value = valueP;
    475        }
    476      } else {
    477        sFormat1 = sFormat;
    478        value = valueP;
    479      }
    480
    481    } else { //sFormat == null
    482      sFormat1 = null;  //no expression
    483      value = valueP;
    484    }
    485    if(sFormat1 !=null && sFormat.length() >0){
    486      try{ sShow = String.format(sFormat1, new Double(value)); }
    487      catch(java.util.IllegalFormatException exc){
    488        sShow = null;  //maybe integer
    489      }
    490      if(sShow == null){
    491        try{ sShow = String.format(sFormat, new Integer((int)value)); }
    492        catch(java.util.IllegalFormatException exc){
    493          sShow = "?format";
    494        }
    495      }
    496    } else { //no format given
    497      double valueAbs = Math.abs(value);
    498      if(value == 0.0f){ sShow = "0.0"; }
    499      else if(valueAbs < 1.0e-7f){ sShow = value < 0 ? "-0.0000001" : "0.0000001"; }  //shorten output, don't show exponent.
    500      else if(valueAbs < 1.0f){ sShow = String.format("%1.12f", new Double(value)); }  //shorten output, don't show exponent.
    501      else if(valueAbs < 1.0e3f){ sShow = String.format("%3.9f", new Double(value)); }  //shorten output, don't show exponent.
    502      else if(valueAbs < 1.0e6f){ sShow = String.format("%3.8f k", new Double(value/1000)); }  //shorten output, don't show exponent.
    503      else if(valueAbs < 1.0e9f){ sShow = String.format("%3.8f M", new Double(value/1000000)); }  //shorten output, don't show exponent.
    504      else if(valueAbs >= 1.0e9f){ sShow = String.format("%3.8g", new Double(value)); }  //shorten output, don't show exponent.
    505      //else if(valueAbs >= 1e6){ sShow = Float.toString(value/1000000) + " M"; }  //shorten output, don't show exponent.
    506      else { sShow = Double.toString(value); }
    507
    508    }
    509    setText(sShow);
    510  }
    511
    512
    513
    514
    515  @Override public void setValue(Object[] value){
    516    if(value !=null && value.length ==1 && value[0] instanceof Double){
    517      double val = ((Double)value[0]).doubleValue();
    518      setValue(val);
    519    } else {
    520      setText("?" + value);
    521    }
    522  }
    523
    524
    525
    526
    527  @Override public void setText(CharSequence arg)
    528  { setText(arg, 0);
    529  }
    530
    531  /**Sets the textual content. This method sets the text and invokes a {@link #repaint(int, int)} in 100 ms
    532   * if the content is changed in another thread than the graphical thread. It invokes a {@link #repaintGthread()}
    533   * if the content was changed in the graphical thread.
    534   * Note: If the current content is equals with the new one, a repaint request is not forced.
    535   * Therewith the cursor can be positioned inside. But if the content is changed, it is set with this given one.
    536   * @see org.vishia.gral.ifc.GralTextField_ifc#setText(java.lang.CharSequence, int)
    537   */
    538  @Override public void setText(CharSequence arg, int caretPos)
    539  {
    540    bShouldInitialize= false;  //it is done.
    541    if(  dyda.displayedText == null   //set the text if no text is stored. Initially!
    542      //|| !bTextChanged                 //don't set the text if it is changed by user yet.
    543         //&&
    544        || (  !dyda.bTouchedField  //don't change the text if the field is in focus and anything was moved. Either any copy to clipboard is pending, or it is in editing.
    545           && !StringFunctions.equals(dyda.displayedText,arg) || caretPos != this.caretPos)  //set the text only if it is changed. Prevent effort.
    546      ){                               //prevent invocation of setText() on non changed values to help move cursor, select etc.
    547      dyda.displayedText = arg.toString();
    548      this.caretPos = caretPos;
    549      dyda.setChanged(GralWidget.ImplAccess.chgText);
    550      repaint();
    551    } //else: no change, do nothing. Therewith the field is able to edit on unchanged texts.
    552  }
    553
    554
    555
    556
    557  /**Sets the action which is invoked while a mouse button is pressed or release on this widget.
    558   * Implementation hint: It installs a mouse listener.
    559   * TODO: use GralMouseWidgetAction_ifc instead GralUserAction, use another action for mouse than change.
    560   */
    561  //abstract public void setMouseAction(GralUserAction action);
    562
    563  /**Gets the current value of the text field thread-independent.
    564   * It is the value stored in the {@link GralWidget.DynamicData#displayedText} which is updated while typing the text
    565   * or if the focus is changed (depending on implementation).
    566   * @return The value in String representation.
    567   */
    568  @Override public String getValue() { return dyda.displayedText; }
    569
    570
    571
    572
    573  @Override
    574  public int getCursorPos(){ return caretPos; }
    575
    576
    577  @Override
    578  public int setCursorPos(int pos)
    579  { int pos9 = caretPos;
    580    if(pos != caretPos){
    581      caretPos = pos;
    582      dyda.setChanged(GraphicImplAccess.chgCursor);
    583      repaint(repaintDelay, repaintDelayMax);
    584    }
    585    return pos9;  //the old
    586  }
    587
    588
    589  @Override public void setTextStyle(GralColor color, GralFont font)
    590  {
    591    dyda.textFont = font;
    592    dyda.textColor = color;
    593    dyda.setChanged(GralWidget.ImplAccess.chgColorText);
    594    if(_wdgImpl !=null){
    595      repaint();
    596    }
    597  }
    598
    599
    600
    601  /**Sets a new border width and returns the old one.
    602   * If the width is the same as the old one, nothing else is done.
    603   * If the widht is another, a repaint request is registered to show it in graphic.
    604   * This routine can be invoked in any thread.
    605   * @param width 0: No border, 1... number of pixel for border.
    606   * @return last width to quest, store and restore.
    607   */
    608  public int setBorderWidth(int width){
    609    if(borderwidth == width) return width; //no action if nothing is change.
    610    int widthLast = width;
    611    borderwidth = width;
    612    dyda.setChanged(GralWidget.ImplAccess.chgColorLine);
    613    if(_wdgImpl !=null){
    614      repaint();
    615    }
    616    return widthLast;
    617  }
    618
    619
    620
    621  /**Returns the Label for a prompt or null if there isn't used a prompt
    622   */
    623  public final String getPromptLabelImpl(){ return sPrompt; }
    624
    625
    626
    627  private static class CalculatorAngle16 extends CalculatorExpr
    628  { CalculatorAngle16(){}
    629    @Override public float calc(float input){
    630      return input * (180.0f / 32768.0f);
    631    }
    632    @Override public Value calcDataAccess(Map<String, DataAccess.Variable<Object>> javaVariables, Object... args) throws Exception{
    633      Float value;
    634      if(args[0] instanceof Float){
    635        value = (Float)args[0];
    636      } else {
    637        Long lval = (Long)args[0];
    638        value = new Float((int)(lval.longValue())); //really an int if it is an angle.
    639      }
    640      CalculatorExpr.Value valueRet = new CalculatorExpr.Value(calc(value.floatValue()));
    641      return valueRet;
    642    }
    643  } //class CalculatorAngle16
    644
    645
    646  private static class CalculatorAngle32 extends CalculatorExpr
    647  { CalculatorAngle32(){}
    648    @Override public float calc(float input){
    649      return input * (180.0f / 0x7fffffff);
    650    }
    651    @Override public Value calcDataAccess(Map<String, DataAccess.Variable<Object>> javaVariables, Object... args) throws Exception{
    652      Float value;
    653      if(args[0] instanceof Float){
    654        value = (Float)args[0];
    655      } else {
    656        Long lval = (Long)args[0];
    657        value = new Float((int)(lval.longValue())); //really an int if it is an angle.
    658      }
    659      //TODO use long value or int for calculation
    660      CalculatorExpr.Value valueRet = new CalculatorExpr.Value(calc(value.floatValue()));
    661      return valueRet;
    662    }
    663
    664  } //class CalculatorAngle32
    665
    666
    667
    668  protected GralKeyListener gralKeyListener = new GralKeyListener(itsMng)
    669  {
    670    @Override public boolean specialKeysOfWidgetType(int key, GralWidget_ifc widgg, Object widgImpl){ return false; }
    671  };
    672
    673
    674
    675  public abstract class GraphicImplAccess extends GralWidget.ImplAccess
    676  implements GralWidgImpl_ifc
    677  {
    678
    679    public static final int chgPrompt = 0x100, chgCursor = 0x200;
    680
    681    /**The {@link GralWidget#pos()} is the summary position for prompt and field.
    682     * This is the part for field and prompt.
    683     */
    684    protected final GralPos posPrompt, posField;
    685
    686
    687
    688    protected GraphicImplAccess(GralWidget widgg)
    689    {
    690      super(widgg);
    691      if(prompt() != null && promptStylePosition() !=null && promptStylePosition().startsWith("t")) {
    692        //mng.setNextPosition();  //deprecated, done in Widget constructor.
    693        char sizeFontPrompt;
    694        posPrompt = new GralPos();
    695        posField = new GralPos();
    696
    697        //boundsAll = mng.calcWidgetPosAndSize(this.pos, 800, 600, 100, 20);
    698        float ySize = widgg.pos().height();
    699        //float xSize = pos.width();
    700        //posPrompt from top,
    701        float yPosPrompt, heightPrompt, heightText;
    702        //switch(promptStylePosition){
    703          //case 't':{
    704            if(ySize <= 2.5){ //it is very small for top-prompt:
    705              yPosPrompt = 1.0f;  //no more less than 1/2 normal line.
    706              heightPrompt = 1.0f;
    707              heightText = ySize - 0.7f;  //max. 1.8
    708              if(heightText < 1.0f){ heightText = 1.0f; }
    709            } else if(ySize <=3.3){ //it is normally 2.5..4
    710              heightPrompt = ySize - 2.0f + 0.5f;   //1 to 1.8
    711              yPosPrompt = ySize - heightPrompt - 0.1f;  //no more less than 1/2 normal line.
    712              heightText = 2.0f;
    713            } else if(ySize <=4.0){ //it is normally 2.5..4
    714              heightPrompt = ySize - 2.0f + (4.0f - ySize) * 0.5f;
    715              if(heightPrompt < 1.0f){ heightPrompt = 1.0f; }
    716              yPosPrompt = ySize - heightPrompt + 0.2f;  //no more less than 1/2 normal line.
    717              heightText = 2.0f;
    718            } else { //greater then 4.0
    719              yPosPrompt = ySize * 0.5f;
    720              heightPrompt = ySize * 0.4f;;
    721              heightText = ySize * 0.5f;
    722            }
    723            //from top, size of prompt
    724            posPrompt.setPosition(widgg.pos(), GralPos.same - ySize + yPosPrompt, GralPos.size - heightPrompt, GralPos.same, GralPos.same, 0, '.');
    725            //from bottom line, size of text
    726            posField.setPosition(widgg.pos(), GralPos.same, GralPos.size - heightText, GralPos.same, GralPos.same, 0, '.');
    727          //} break;
    728        //}
    729
    730      } else { //no prompt given
    731        posPrompt = null;
    732        posField = widgg.pos();
    733      }
    734    }
    735
    736    //protected GralFont fontText(){ return GralTextField.this.fontText; }
    737
    738    protected String prompt(){ return GralTextField.this.sPrompt; }
    739
    740    protected String promptStylePosition(){ return GralTextField.this.sPromptStylePosition; }
    741
    742    protected int borderwidth(){ return GralTextField.this.borderwidth; }
    743
    744    protected int caretPos(){ return GralTextField.this.caretPos; }
    745
    746    protected void caretPos(int newPos){ GralTextField.this.caretPos = newPos; }
    747
    748    protected GralTextFieldUser_ifc user(){ return GralTextField.this.user; }
    749
    750    protected boolean isPasswordField(){ return GralTextField.this.bPassword; }
    751
    752    protected void setTouched(boolean bTouched) { widgg.dyda.bTouchedField = bTouched; }
    753
    754  }
    755
    756
    757}