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}