    001package org.vishia.gral.base;
    002
    003import java.lang.ref.Reference;
    004
    005import org.vishia.gral.ifc.GralColor;
    006import org.vishia.gral.ifc.GralImageBase;
    007import org.vishia.gral.ifc.GralUserAction;
    008import org.vishia.gral.ifc.GralWidget_ifc;
    009import org.vishia.util.KeyCode;
    010
    011public class GralButton extends GralWidget
    012{
    013  /**Version, history and license.
    014   * <ul>
    015   * <li>2016-05-03 Hartmut chg: Now supports {@link GralWidget#setVisible(boolean)}
    016   * <li>2015-06-21 Hartmut chg: {@link #setSwitchMode(GralColor, GralColor, GralColor)} with null as 3. color
    017   *   sets to 2-times switch like {@link #setSwitchMode(GralColor, GralColor)}.
    018   * <li>2015-05-31 Hartmut new: Now a key listener and a traverse listener was added for the SWT implementation.
    019   *   This base class provides the {@link #activate()} method which should be invoked on ENTER-key.
    020   * <li>2013-12-22 Hartmut chg: Now {@link GralButton} uses the new concept of instantiation: It is not
    021   *   the super class of the implementation class. But it provides {@link GralButton.GraphicImplAccess}
    022   *   as the super class.
    023   * <li>2013-05-23 Hartmut new color of text able to change (not only black)
    024   * <li>2013-05-23 Hartmut chg: {@link #setState(State)} with an enum, not int, used for showing active/inactive
    025   * <li>2012-08-22 Hartmut new implements {@link #setBackColor(GralColor, int)}
    026   * <li>2012-03-09 Hartmut new Some functionality from SwtButton moved to this, some enhancement of
    027   *   functionality: Three states,
    028   * <li>2011 Hartmut created
    029   * </ul>
    030   *
    031   * <b>Copyright/Copyleft</b>:
    032   * For this source the LGPL Lesser General Public License,
    033   * published by the Free Software Foundation is valid.
    034   * It means:
    035   * <ol>
    036   * <li> You can use this source without any restriction for any desired purpose.
    037   * <li> You can redistribute copies of this source to everybody.
    038   * <li> Every user of this source, also the user of redistribute copies
    039   *    with or without payment, must accept this license for further using.
    040   * <li> But the LPGL ist not appropriate for a whole software product,
    041   *    if this source is only a part of them. It means, the user
    042   *    must publish this part of source,
    043   *    but don't need to publish the whole source of the own product.
    044   * <li> You can study and modify (improve) this source
    045   *    for own using or for redistribution, but you have to license the
    046   *    modified sources likewise under this LGPL Lesser General Public License.
    047   *    You mustn't delete this Copyright/Copyleft inscription in this source file.
    048   * </ol>
    049   * If you are intent to use this sources without publishing its usage, you can get
    050   * a second license subscribing a special contract with the author.
    051   *
    052   * @author Hartmut Schorrig = hartmut.schorrig@vishia.de
    053   *
    054   *
    055   */
    056  public final static int version = 0x20120303;
    057
    058
    059  //final GralWindowMng_ifc mainWindow;
    060
    061  protected boolean pressed;
    062
    063  /**The state of switch and check button
    064   * 1=on 2=off 3=disabled. 0=initial, see {@link #kOn} etc.
    065   */
    066  //protected int switchState;
    067  //protected boolean switchedOn;
    068
    069
    070  public enum State{ On, Off, Disabled}
    071
    072  protected State switchState;
    073
    074  //public static final int kOn = 1, kOff=2, kDisabled=3;
    075
    076  protected String sButtonTextOff, sButtonTextOn, sButtonTextDisabled;
    077
    078  protected GralImageBase imageOff, imageOn, imageDisabled;
    079
    080  /**Color of button. If colorOff is null, then it is not a switch button. */
    081  protected GralColor colorBackOn, colorBackDisabled = GralColor.getColor("gr");
    082
    083  /**Color of button. If colorOff is null, then it is not a switch button. */
    084  protected GralColor colorLineOff = GralColor.getColor("bk"), colorLineOn = GralColor.getColor("bk"), colorLineDisabled = GralColor.getColor("lgr");
    085
    086  /**Currently pressed, show it in graphic. */
    087  protected boolean isPressed;
    088
    089
    090  /**True if it is a switch or a check button. */
    091  protected boolean shouldSwitched;
    092
    093  /**True if the switch is in disable state. Show it in graphic. */
    094  //protected boolean isDisabled;
    095
    096  /**True if the button has three states: on, off, disabled. */
    097  protected boolean bThreeStateSwitch;
    098
    099  public GralButton(String sName)
    100  {
    101    super(sName, 'B');  //GralWidget
    102  }
    103
    104  /**Creates a button
    105   * @param sPosName If given with "@pos = name" then it is a position with name, see {@link GralWidget#GralWidget(String, char)}
    106   * @param sText The button text
    107   * @param action The action on release mouse
    108   */
    109  public GralButton(String sPosName, String sText, GralUserAction action)
    110  { super(sPosName, 'B');
    111    setText(sText);
    112    specifyActionChange(null, action, null);
    113  }
    114
    115  @Deprecated public GralButton(String position, String sName, String sText, GralUserAction action)
    116  {
    117    super(position, sName, 'B');  //GralWidget
    118    setText(sText);
    119    setActionChange(action);
    120  }
    121
    122  /**Declares the button to a switch button (switching on/off) with the given texts.
    123   * Different colors for on and off may be set with {@link #setSwitchMode(GralColor, GralColor)}.
    124   * See {@link #setSwitchMode(GralImageBase, GralImageBase)}.
    125   * @param sButtonTextOff
    126   * @param sButtonTextOn
    127   */
    128  public void setSwitchMode(String sButtonTextOff, String sButtonTextOn){
    129    this.sButtonTextOn = sButtonTextOn;
    130    this.sButtonTextOff = sButtonTextOff;
    131    shouldSwitched = true;
    132    bThreeStateSwitch = false;
    133  }
    134
    135
    136  /**Declares the button to a check button (switching on/off/disable) with the given texts.
    137   * Different colors for on, off and disable may be set with {@link #setSwitchMode(GralColor, GralColor, GralColor)}.
    138   * See {@link #setSwitchMode(GralImageBase, GralImageBase, GralImageBase)}.
    139   * @param sButtonTextOff
    140   * @param sButtonTextOn
    141   * @param sDisabled
    142   */
    143  public void setSwitchMode(String sButtonTextOff, String sButtonTextOn, String sDisabled){
    144    this.sButtonTextOn = sButtonTextOn;
    145    this.sButtonTextOff = sButtonTextOff;
    146    this.sButtonTextDisabled = sDisabled;
    147    shouldSwitched = true;
    148    bThreeStateSwitch = true;
    149  }
    150
    151
    152  /**Declares the button to a switch button (switching on/off) with the given colors.
    153   * Different texts for on and off may be set with {@link #setSwitchMode(String, String)}.
    154   * See {@link #setSwitchMode(GralImageBase, GralImageBase)}.
    155   * @param colorOff
    156   * @param colorOn
    157   */
    158  public void setSwitchMode(GralColor colorOff, GralColor colorOn){
    159    this.colorBackOn = colorOn;
    160    dyda.backColor = colorOff;
    161    bThreeStateSwitch = false;
    162    shouldSwitched = true;
    163  }
    164
    165
    166  /**Declares the button to a check button (switching on/off/disable) with the given texts.
    167   * Different texts for on and off may be set with {@link #setSwitchMode(String, String, String)}.
    168   * See {@link #setSwitchMode(GralImageBase, GralImageBase, GralImageBase)}.
    169   * @param colorOff
    170   * @param colorOn
    171   */
    172  public void setSwitchMode(GralColor colorOff, GralColor colorOn, GralColor colorDisabled){
    173    this.colorBackOn = colorOn;
    174    dyda.backColor = colorOff;
    175    this.colorBackDisabled = colorDisabled;
    176    bThreeStateSwitch = colorBackDisabled !=null;
    177    shouldSwitched = true;
    178  }
    179
    180
    181  /**Declares the button to a switch button (switching on/off) with the given images.
    182   * see {@link #setSwitchMode(String, String)}.
    183   * @param imageOff
    184   * @param imageOn
    185   */
    186  public void setSwitchMode(GralImageBase imageOff, GralImageBase imageOn){
    187    this.imageOn = imageOn;
    188    this.imageOff = imageOff;
    189    bThreeStateSwitch = false;
    190    shouldSwitched = true;
    191  }
    192
    193
    194  /**Declares the button to a check button (switching on/off/disable) with the given texts.
    195   * see {@link #setSwitchMode(String, String, String)}.
    196   * @param imageOff
    197   * @param imageOn
    198   */
    199  public void setSwitchMode(GralImageBase imageOff, GralImageBase imageOn, GralImageBase imageDisabled){
    200    this.imageOn = imageOn;
    201    this.imageOff = imageOff;
    202    this.imageDisabled = imageDisabled;
    203    bThreeStateSwitch = true;
    204    shouldSwitched = true;
    205  }
    206
    207
    208
    209
    210  /**Implementing for
    211   * @see org.vishia.gral.base.GralWidget#setBackColor(org.vishia.gral.ifc.GralColor, int)
    212   * @param ix=0: off-color ix=1: on-color, ix=2 or ix=-1: disable color.
    213   */
    214  @Override public void setBackColor(GralColor color, int ix){
    215    GralColor[] ref = new GralColor[1];
    216    boolean bChanged;
    217    switch(ix){
    218      case 0:          bChanged = !color.equals(dyda.backColor);     if(bChanged){ dyda.backColor = color; } break;
    219      case 1:          bChanged = !color.equals(colorBackOn);        if(bChanged){ colorBackOn = color; } break;
    220      case -1: case 2: bChanged = !color.equals(colorBackDisabled);  if(bChanged){ colorBackDisabled = color; } break;
    221      default: throw new IllegalArgumentException("fault ix, allowed -1, 0, 1, 2, ix=" + ix);
    222    }//switch
    223    if(bChanged){
    224      dyda.setChanged(ImplAccess.chgColorBack);
    225      repaint();
    226    }
    227  }
    228
    229
    230  /**Sets the disable text and color. A Button can be shown disabled anytime, independent of its role
    231   * of switch button or check button.
    232   * @param color
    233   * @param text
    234   */
    235  public void setDisableColorText(GralColor color, String text){
    236    this.colorBackDisabled = color;
    237    this.sButtonTextDisabled = text;
    238  }
    239
    240
    241  /**Sets the text of the button.
    242   * The text can be changed in any thread any time.
    243   * A repaint is forced automatically after a short time (100 ms).
    244   * @param sButtonText
    245   */
    246  public void setText(String sButtonText){
    247    this.sButtonTextOff = sButtonText;
    248    sButtonTextDisabled = sButtonText;
    249    sButtonTextOff = sButtonText;
    250
    251    if(sButtonTextDisabled == null){ sButtonTextDisabled = sButtonText; }
    252    if(sButtonTextOn == null){ sButtonTextOn = sButtonText; }
    253    repaint(100, 100);
    254  }
    255
    256  /**Show the button in an activated state. This method is called especially
    257   * in its mouse press and release events.
    258   * In the activated state the button looks like pressed.*/
    259  protected void setActivated(boolean value){
    260    isPressed = value;
    261    repaint(100, 100);
    262  }
    263
    264
    265  /**This is the same action like release the left mouse button. It can be called from the application
    266   * to activate the function of the button. Especially it is used for [Enter] on the focused button.
    267   * It invokes the {@link GralWidget#specifyActionChange(String, GralUserAction, String[], org.vishia.gral.ifc.GralWidget_ifc.ActionChangeWhen...)}
    268   * with {@link GralWidget_ifc.ActionChangeWhen#onMouse1Up}
    269   */
    270  public void activate() {
    271    if(shouldSwitched){
    272      if( switchState == State.On) { switchState = State.Off; }
    273      else if(bThreeStateSwitch && switchState == State.Off){ switchState = State.Disabled; }
    274      else { switchState = State.On; }
    275    }
    276    GralWidget_ifc.ActionChange action = getActionChange(GralWidget_ifc.ActionChangeWhen.onMouse1Up);
    277    if(action !=null){
    278      action.action().exec(KeyCode.enter, this, action.args());
    279    }
    280    repaint();
    281  }
    282
    283
    284
    285  @Override public void setValue(int cmd, int ident, Object visibleInfo, Object userData){
    286    if(visibleInfo instanceof Integer){
    287      int value = ((Integer)visibleInfo).intValue();
    288      if(shouldSwitched){
    289        switch(value){
    290          case 0: switchState = State.Off; break;
    291          case 1: switchState = State.On; break;
    292          default: switchState = State.Disabled; break;
    293        }
    294      }
    295    }
    296  }
    297
    298
    299
    300  public void XXXsetDisabled(boolean value){
    301    this.switchState = value ? State.Disabled: State.On; //kDisabled : kOn;
    302    repaint(100, 100);
    303  }
    304
    305
    306  public boolean isDisabled(){ return switchState == State.Disabled; }
    307
    308
    309  public boolean isOn(){ return switchState == State.On; }
    310
    311  /**Sets the appearance of the button
    312   * <ul>
    313   * <li>val instance of GralColor, change the color appropriate to the current state.
    314   * <li>val instance of Text, changes the switch state.
    315   *   <ul>
    316   *   <li>"1", "true", "on"; switches to on
    317   *   <li>"0", "false", "off"; switches to off
    318   *   </ul>
    319   * </ul>
    320   * <li>val instance of Integer:
    321   *   <ul>
    322   *   <li>0 switches off.
    323   *   <li>not 0 switches on
    324   *   </ul>
    325   * </ul>
    326   *   instance
    327   * @param val
    328   */
    329  public void setState(Object val)
    330  {
    331    if(val instanceof GralColor){
    332      if(switchState == State.On){
    333        colorBackOn = (GralColor)val;
    334      } else {
    335        dyda.backColor = (GralColor)val;
    336      }
    337    } else {
    338      String sVal = (val instanceof String) ? (String)val : null;
    339      int nVal = val instanceof Integer ? ((Integer)val).intValue(): -1;
    340      if(sVal !=null && (sVal.equals("1") || sVal.equals("true") || sVal.equals("on"))
    341        || sVal == null && nVal !=0){
    342        switchState = State.On;
    343      } else if(sVal !=null && (sVal.equals("0") || sVal.equals("false") || sVal.equals("off"))
    344          || nVal == 0){
    345        switchState = State.Off;
    346      }
    347    }
    348    repaint(100,100);
    349  }
    350
    351
    352
    353  /**Sets the state of the button
    354   * @param state one of {@link #kOn}, {@value #kOff}, {@value #kDisabled}.
    355   * @throws IllegalArgumentException if the state is faulty.
    356   */
    357  public void setState(State state){
    358    //if(state == kOn || state == kOff || state ==kDisabled){
    359      switchState = state;
    360      repaint(100,100);
    361    //} else {
    362    //  throw new IllegalArgumentException("faulty state: " + state);
    363    //}
    364  }
    365
    366
    367  public State getState(){ return switchState; }
    368
    369
    370  /**Inner class to implement the mouse actions called from the gral implementing layer.
    371   * If the mouse was moved from the widget's area while a button is pressed, the mouse action on release
    372   * is not invoked.
    373   */
    374  class MouseActionButton implements GralMouseWidgetAction_ifc
    375  {
    376
    377    @Override public boolean mouseMoved(int xMousePixel, int yMousePixel, int sizex, int sizey){
    378      if(  xMousePixel < 0 || xMousePixel > sizex
    379          || yMousePixel < 0 || yMousePixel > sizey
    380          ){ //the mouse cursor has left the area of the widget:
    381        setActivated(false);
    382        return false;
    383      }
    384      else return true;
    385    }
    386
    387
    388    @Override public void mouse1Down(int keyCode, int xMousePixel, int yMousePixel, int xWidgetSizePixel, int yWidgetSizePixel, GralWidget widgg)
    389    {
    390      setActivated(true);
    391      /*
    392      GralWidget_ifc.ActionChange action = widgg.getActionChange(GralWidget_ifc.ActionChangeWhen.onMouse1Dn);
    393      if(action !=null){
    394        Object[] args = action.args();
    395        if(args == null){ action.action().exec(keyCode, widgg, new Integer(xMousePixel), new Integer(yMousePixel)); }
    396        else {
    397          //additional 2 arguments: copy in one args2.
    398          Object[] args2 = new Object[args.length +2];
    399          System.arraycopy(args, 0, args2, 0, args.length);
    400          args2[args.length] = new Integer(xMousePixel);
    401          args2[args.length+1] = new Integer(yMousePixel);
    402          action.action().exec(keyCode, widgg, args2);
    403        }
    404      }
    405      */
    406    }
    407
    408
    409
    410    /**It will be called only if the mouse up is pressed inside the button's area.
    411     * @see org.vishia.gral.base.GralMouseWidgetAction_ifc#mouse1Up(int, int, int, int, int, org.vishia.gral.base.GralWidget)
    412     */
    413    @Override public void mouse1Up(int keyCode, int xMousePixel, int yMousePixel, int xWidgetSizePixel, int yWidgetSizePixel, GralWidget widgg)
    414    {
    415      setActivated(false);
    416      //On -> Off -> Disabled -> On
    417      if(shouldSwitched){
    418        if( switchState == State.On) { switchState = State.Off; }
    419        else if(bThreeStateSwitch && switchState == State.Off){ switchState = State.Disabled; }
    420        else { switchState = State.On; }
    421      }
    422      /*
    423      GralWidget_ifc.ActionChange action = getActionChange(GralWidget_ifc.ActionChangeWhen.onMouse1Up);
    424      if(action !=null){
    425
    426        Object[] args = action.args();
    427        if(args == null){ action.action().exec(keyCode, widgg, new Integer(xMousePixel), new Integer(yMousePixel)); }
    428        else {
    429          //additional 2 arguments: copy in one args2.
    430          Object[] args2 = new Object[args.length +2];
    431          System.arraycopy(args, 0, args2, 0, args.length);
    432          args2[args.length] = new Integer(xMousePixel);
    433          args2[args.length+1] = new Integer(yMousePixel);
    434          action.action().exec(keyCode, widgg, args2);
    435        }
    436      }*/
    437      widgg.repaint(); //100, 200);
    438    }
    439
    440
    441    @Override public void mouse2Down(int keyCode, int xMousePixel, int yMousePixel, int xWidgetSizePixel, int yWidgetSizePixel, GralWidget widgg)
    442    {
    443    }
    444
    445
    446    @Override public void mouse2Up(int keyCode, int xMousePixel, int yMousePixel, int xWidgetSizePixel, int yWidgetSizePixel, GralWidget widgg)
    447    {
    448      /*
    449      GralWidget_ifc.ActionChange action = getActionChange(GralWidget_ifc.ActionChangeWhen.onMouse2Up);
    450
    451      if(action !=null){
    452        Object[] args = action.args();
    453        if(args == null){ action.action().exec(keyCode, widgg, new Integer(xMousePixel), new Integer(yMousePixel)); }
    454        else {
    455          //additional 2 arguments: copy in one args2.
    456          Object[] args2 = new Object[args.length +2];
    457          System.arraycopy(args, 0, args2, 0, args.length);
    458          args2[args.length] = new Integer(xMousePixel);
    459          args2[args.length+1] = new Integer(yMousePixel);
    460          action.action().exec(keyCode, widgg, args2);
    461        }
    462      }
    463      */
    464    }
    465
    466    @Override public void mouse1Double(int keyCode, int xMousePixel, int yMousePixel, int xWidgetSizePixel, int yWidgetSizePixel, GralWidget widgg)
    467    {
    468      /*
    469      GralWidget_ifc.ActionChange action = getActionChange(GralWidget_ifc.ActionChangeWhen.onMouse1Doublc);
    470      if(action !=null){
    471        Object[] args = action.args();
    472        if(args == null){ action.action().exec(keyCode, widgg, new Integer(xMousePixel), new Integer(yMousePixel)); }
    473        else {
    474          //additional 2 arguments: copy in one args2.
    475          Object[] args2 = new Object[args.length +2];
    476          System.arraycopy(args, 0, args2, 0, args.length);
    477          args2[args.length] = new Integer(xMousePixel);
    478          args2[args.length+1] = new Integer(yMousePixel);
    479          action.action().exec(keyCode, widgg, args2);
    480        }
    481      }
    482      */
    483    }
    484
    485
    486
    487  }
    488
    489
    490
    491  public abstract class GraphicImplAccess extends GralWidget.ImplAccess
    492  implements GralWidgImpl_ifc
    493  {
    494
    495    /**That action effects that the internal variable {@link #switchedOn} is set for a switching button.
    496     * The user action will be called by the standard mouse action of the implementation graphic
    497     * AWT: {@link org.vishia.gral.awt.AwtGralMouseListener.MouseListenerUserAction}.
    498     */
    499    final protected GralMouseWidgetAction_ifc mouseWidgetAction = new MouseActionButton();
    500
    501
    502    /**Set in {@link #paint1()}, used for paint routine. */
    503    protected String sButtonText;
    504    /**Set in {@link #paint1()}, used for paint routine. */
    505    protected GralColor colorgback, colorgline;
    506
    507    /**Covers the GralWidget#widgg, but it is the same reference. This is the necessary type. */
    508    protected GralButton widgg;
    509
    510    protected GraphicImplAccess(GralButton widgg, GralMng mng)
    511    { super(widgg);
    512      this.widgg = widgg;
    513    }
    514
    515
    516    protected boolean isPressed(){ return GralButton.this.isPressed; }
    517
    518
    519    protected void prepareWidget(){
    520      int state1 = -1;
    521      int chg = dyda().getChanged();  //don't handle a bit twice if re-read.
    522      if((chg & chgVisibleInfo) !=0 && dyda().visibleInfo !=null){
    523        if(dyda().visibleInfo instanceof Integer){
    524          state1 = ((Integer)dyda().visibleInfo).intValue();
    525        }
    526      }
    527      if((chg & chgIntg) !=0 ){
    528        state1 = 0; //TODO dyda.intValue
    529      }
    530      dyda.acknChanged(chg & (chgVisibleInfo | chgIntg));
    531      switch(state1){
    532        case 0: switchState = State.Off; break;
    533        case 1: switchState = State.On; break;
    534        case 2: switchState = State.Disabled; break;
    535        default: ; //don't change state.
    536      }
    537    }
    538
    539
    540
    541    protected void paint1(){
    542      if(switchState == State.On){
    543        sButtonText = sButtonTextOn != null ? sButtonTextOn : sButtonTextOff;
    544        colorgback = colorBackOn !=null ? colorBackOn: dyda().backColor;
    545        colorgline = colorLineOn !=null ? colorLineOn: colorLineOff;
    546      } else if(switchState == State.Disabled){
    547        sButtonText = sButtonTextDisabled;
    548        colorgback = colorBackDisabled;
    549        colorgline = colorLineDisabled;
    550      } else {
    551        sButtonText = sButtonTextOff;
    552        colorgback = dyda().backColor;
    553        colorgline = colorLineOff;
    554      }
    555
    556    }
    557
    558
    559  }
    560
    561
    562
    563
    564}