001package org.vishia.gral.swt; 002 003import org.eclipse.swt.SWT; 004import org.eclipse.swt.dnd.DropTarget; 005import org.eclipse.swt.events.DragDetectEvent; 006import org.eclipse.swt.events.DragDetectListener; 007import org.eclipse.swt.events.FocusEvent; 008import org.eclipse.swt.events.KeyListener; 009import org.eclipse.swt.events.ModifyEvent; 010import org.eclipse.swt.events.ModifyListener; 011import org.eclipse.swt.events.PaintEvent; 012import org.eclipse.swt.events.PaintListener; 013import org.eclipse.swt.graphics.Color; 014import org.eclipse.swt.graphics.Font; 015import org.eclipse.swt.graphics.GC; 016import org.eclipse.swt.graphics.Point; 017import org.eclipse.swt.graphics.Rectangle; 018import org.eclipse.swt.widgets.Composite; 019import org.eclipse.swt.widgets.Listener; 020import org.eclipse.swt.widgets.Text; 021import org.vishia.byteData.VariableAccessWithIdx; 022import org.vishia.gral.base.GralButton.GraphicImplAccess; 023import org.vishia.gral.base.GralKeyListener; 024import org.vishia.gral.base.GralMouseWidgetAction_ifc; 025import org.vishia.gral.base.GralPos; 026import org.vishia.gral.base.GralWidget; 027import org.vishia.gral.base.GralMng; 028import org.vishia.gral.base.GralTextField; 029import org.vishia.gral.ifc.GralColor; 030import org.vishia.gral.ifc.GralFont; 031import org.vishia.gral.ifc.GralRectangle; 032import org.vishia.gral.ifc.GralTextFieldUser_ifc; 033import org.vishia.gral.ifc.GralUserAction; 034import org.vishia.gral.ifc.GralWidget_ifc; 035import org.vishia.util.KeyCode; 036 037public class SwtTextFieldWrapper extends GralTextField.GraphicImplAccess 038{ 039 040 /**Version, history and license. 041 * <ul> 042 * <li>2015-05-04 Hartmut new: {@link #setBorderWidth(int)} to show the text field with a border. That is not a property 043 * of an SWT Text, therefore a new {@link #paintListener} was added to draw the border. 044 * <li>2013-12-22 Hartmut chg: Now {@link GralTextField} uses the new concept of instantiation: It is not 045 * the super class of the implementation class. But it provides {@link GralTextField.GraphicImplAccess} 046 * as the super class. 047 * <li>2012-06-30 Hartmut new actionChange called on typing inside a field. 048 * <li>2012-06-30 Hartmut new {@link #swtKeyListener}. The [Enter] key will be send to the User 049 * in opposite to {@link SwtTextBox}. 050 * <li>2012-06-08 Hartmut chg: {@link #repaintGthread()} does not do anything if the textFieldSwt is removed 051 * because the widget was removed. Prevent null-Pointer exception. 052 * <li>2012-04-10 Hartmut chg: A key listener, only for experience 053 * <li>2012-03-17 Hartmut bugfix: adjustment of prompt for top prompt 054 * <li>2012-03-10 Hartmut chg: Minor for top-level prompt. 055 * <li>2011-06-00 Hartmut creation 056 * </ul> 057 * 058 * <b>Copyright/Copyleft</b>: 059 * For this source the LGPL Lesser General Public License, 060 * published by the Free Software Foundation is valid. 061 * It means: 062 * <ol> 063 * <li> You can use this source without any restriction for any desired purpose. 064 * <li> You can redistribute copies of this source to everybody. 065 * <li> Every user of this source, also the user of redistribute copies 066 * with or without payment, must accept this license for further using. 067 * <li> But the LPGL is not appropriate for a whole software product, 068 * if this source is only a part of them. It means, the user 069 * must publish this part of source, 070 * but doesn't need to publish the whole source of the own product. 071 * <li> You can study and modify (improve) this source 072 * for own using or for redistribution, but you have to license the 073 * modified sources likewise under this LGPL Lesser General Public License. 074 * You mustn't delete this Copyright/Copyleft inscription in this source file. 075 * </ol> 076 * If you intent to use this source without publishing its usage, you can get 077 * a second license subscribing a special contract with the author. 078 * 079 * @author Hartmut Schorrig = hartmut.schorrig@vishia.de 080 * 081 * 082 */ 083 //@SuppressWarnings("hiding") 084 public static final String version = "2015-09-12"; 085 086 /**It contains the association to the swt widget (Control) and the {@link SwtMng} 087 * and implements some methods of {@link GralWidgImpl_ifc} which are delegate from this. 088 */ 089 private final SwtWidgetHelper swtWidgHelper; 090 091 protected Text textFieldSwt; 092 093 /**A possible prompt for the text field or null. */ 094 //Label promptSwt; 095 SwtTransparentLabel promptSwt; 096 //Text promptSwt; 097 098 private DropTarget drop; 099 100 101 private SwtTextFieldWrapper(GralTextField widgg, SwtMng mng) 102 { 103 widgg.super(widgg); //NOTE: superclass is a non static inner class of GralTextField. 104 105 Composite panelSwt = mng.getCurrentPanel(); 106 //in ctor: setPanelMng(mng); 107 //Text widgetSwt; 108 // 109 int textProperties = SWT.SINGLE; 110 if(isPasswordField()){ 111 textProperties |= SWT.PASSWORD; 112 } 113 if(posPrompt !=null) { 114 final Font promptFont; 115 GralRectangle boundsPrompt; 116 float heightPrompt = posPrompt.height(); 117 promptFont = mng.propertiesGuiSwt.getTextFontSwt(heightPrompt, GralFont.typeSansSerif, GralFont.styleNormal); //.smallPromptFont; 118 //boundsPrompt = mng.calcWidgetPosAndSize(posPrompt, boundsAll.dx, boundsAll.dy, 10,100); 119 //boundsField = mng.calcWidgetPosAndSize(posField, boundsAll.dx, boundsAll.dy, 10,100); 120 promptSwt = new SwtTransparentLabel(panelSwt, SWT.TRANSPARENT); 121 promptSwt.setFont(promptFont); 122 promptSwt.setText(prompt()); 123 promptSwt.setBackground(null); 124 Point promptSize = promptSwt.computeSize(SWT.DEFAULT, SWT.DEFAULT, true); 125 boundsPrompt = mng.calcWidgetPosAndSizeSwt(posPrompt, promptSwt.getParent(), 10,100); 126 if(promptSize.x > boundsPrompt.dx){ 127 boundsPrompt.dx = promptSize.x; //use the longer value, if the prompt text is longer as the field. 128 } 129 promptSwt.setBounds(boundsPrompt.x, boundsPrompt.y, boundsPrompt.dx, boundsPrompt.dy+1); 130 } 131 textFieldSwt = new Text(panelSwt, textProperties); 132 mng.setPosAndSizeSwt(posField,textFieldSwt, 800, 600); 133 //textFieldSwt.setBounds(boundsField.x, boundsField.y, boundsField.dx, boundsField.dy); 134 textFieldSwt.setFont(mng.propertiesGuiSwt.stdInputFont); 135 textFieldSwt.setEditable(widgg.isEditable()); 136 textFieldSwt.setBackground(mng.propertiesGuiSwt.colorSwt(GralColor.getColor("wh"))); 137 KeyListener swtKeyListener = new TextFieldKeyListener(mng.mng._impl.gralKeyListener); 138 textFieldSwt.addKeyListener(swtKeyListener); 139 textFieldSwt.setMenu(null); //default: no contextMenu, use GralMenu? 140 141 Listener[] oldMouseListener = textFieldSwt.getListeners(SWT.MouseDown); 142 for(Listener lst: oldMouseListener){ 143 textFieldSwt.removeListener(SWT.MouseDown, lst); 144 } 145 textFieldSwt.addMouseListener(mng.mouseClickForInfo); 146 textFieldSwt.addFocusListener(mng.focusListener); 147 if(widgg.isEditable()){ 148 TextFieldModifyListener modifyListener = new TextFieldModifyListener(); 149 textFieldSwt.addModifyListener(modifyListener); 150 TextFieldFocusListener focusListener = new TextFieldFocusListener(mng); 151 textFieldSwt.addFocusListener(focusListener); 152 } else { 153 154 } 155 if(prompt() != null && promptStylePosition() !=null && promptStylePosition().startsWith("r")){ 156 Rectangle swtField = textFieldSwt.getBounds(); 157 Rectangle swtPrompt = new Rectangle(swtField.x + swtField.width, swtField.y, 0, swtField.height); 158 float hight = widgg.pos().height(); 159 final Font promptFont; 160 if(hight <2.0){ 161 promptFont = mng.propertiesGuiSwt.smallPromptFont; 162 } else { 163 promptFont = mng.propertiesGuiSwt.stdInputFont; 164 } 165 promptSwt = new SwtTransparentLabel(mng.getCurrentPanel(), SWT.TRANSPARENT); 166 promptSwt.setFont(promptFont); 167 promptSwt.setText(prompt()); 168 Point promptSize = promptSwt.computeSize(SWT.DEFAULT, SWT.DEFAULT, true); 169 swtPrompt.width = promptSize.x; 170 promptSwt.setBounds(swtPrompt); 171 172 173 } 174 // 175 textFieldSwt.setData(this); 176 textFieldSwt.addPaintListener(paintListener); 177 if(!widgg.isEditable()){ 178 mng.mng.registerShowField(widgg); 179 } 180 super.wdgimpl = swtWidgHelper = new SwtWidgetHelper(textFieldSwt, mng); 181 182 mng.mng.registerWidget(widgg); 183 184 } 185 186 187 188 /**Creates a SwtTextField. It is package private, only called from the {@link SwtMng}. 189 * @param widgg 190 * <br> 191 * <b>Prompting</b>: The parameter promptStylePosition determines where a prompt is showing. 192 * <ul> 193 * <li>"t": prompt above, calculates inside position 194 * <li>"r": prompt right, calculates outside position. 195 * </ul> 196 * @param name The name to register it. 197 * @param editable false then show field 198 * @param prompt maybe null, propmt text 199 * @param promptStylePosition maybe null, prompt position 200 * @param mng 201 */ 202 static void createTextField(GralTextField widgg, GralMng mng){ 203 SwtTextFieldWrapper widgswt = new SwtTextFieldWrapper(widgg, (SwtMng)mng.impl); 204 } 205 206 207 @Override public GralRectangle getPixelPositionSize(){ return swtWidgHelper.getPixelPositionSize(); } 208 209 210 211 212 @Override 213 protected void setDropEnable(int dropType) 214 { 215 new SwtDropListener(dropType, textFieldSwt); //associated with textFieldSwt. 216 } 217 218 219 @Override 220 protected void setDragEnable(int dragType) 221 { 222 new SwtDragListener(dragType, textFieldSwt); //associated with textFieldSwt. 223 } 224 225 /* 226 @Override public void setSelection(String how){ 227 if(how.equals("|..<")){ 228 String sText = textFieldSwt.getText(); 229 int zChars = sText.length(); 230 int pos0 = 0; //zChars - 20; 231 if(pos0 < 0){ pos0 = 0; } 232 textFieldSwt.setSelection(pos0, zChars); 233 } 234 } 235 */ 236 237 238 239 240 @Override public void repaintGthread(){ 241 int catastrophicalCount = 0; 242 int chg; 243 if(textFieldSwt !=null){ //do nothing if the graphic implementation widget is removed. 244 GralWidget.DynamicData dyda = dyda(); 245 while( (chg = getChanged()) !=0){ //widgg.dyda.whatIsChanged.get(); 246 if(++catastrophicalCount > 10000) 247 throw new RuntimeException("atomic failed"); 248 if((chg & chgText) !=0 && dyda.displayedText !=null){ 249 textFieldSwt.setText(dyda.displayedText); 250 final int selectionStart, selectionEnd; 251 final int zText = dyda.displayedText.length(); 252 if(caretPos() <0){ 253 selectionEnd = dyda.displayedText.length(); selectionStart = selectionEnd; // -1; 254 } 255 else if(caretPos() >0){ 256 selectionEnd = caretPos() > zText ? zText : caretPos(); 257 selectionStart = selectionEnd; // -1; 258 } else { 259 assert(caretPos() ==0); 260 selectionEnd = selectionStart =-1; //dont call 261 } 262 if(selectionStart >=0){ 263 textFieldSwt.setSelection(selectionStart, selectionEnd); 264 } 265 } 266 if((chg & chgColorText)!=0){ 267 SwtProperties props = swtWidgHelper.mng.propertiesGuiSwt; 268 if(dyda.textColor !=null){ 269 textFieldSwt.setForeground(props.colorSwt(dyda.textColor)); 270 } 271 if(dyda.backColor !=null){ 272 textFieldSwt.setBackground(props.colorSwt(dyda.backColor)); 273 } 274 if(dyda.textFont !=null){ 275 textFieldSwt.setFont(props.fontSwt(dyda.textFont)); 276 } 277 } 278 if((chg & chgEditable) !=0){ 279 textFieldSwt.setEditable(widgg.isEditable()); 280 } 281 if((chg & chgCursor) !=0){ 282 textFieldSwt.setSelection(caretPos()); 283 } 284 if((chg & chgPrompt) !=0){ 285 promptSwt.setText(this.prompt()); 286 promptSwt.redraw(); 287 } 288 if((chg & chgVisible) !=0){ 289 textFieldSwt.setVisible(true); 290 } 291 if((chg & chgInvisible) !=0){ 292 textFieldSwt.setVisible(false); 293 } 294 if((chg & chgColorText) !=0){ 295 textFieldSwt.setForeground(swtWidgHelper.mng.getColorImpl(dyda().textColor)); } 296 if((chg & chgColorBack) !=0){ 297 textFieldSwt.setBackground(swtWidgHelper.mng.getColorImpl(dyda().backColor)); } 298 textFieldSwt.redraw(); 299 //textFieldSwt. 300 acknChanged(chg); 301 } 302 } 303 } 304 305 306 307 @Override public Text getWidgetImplementation() 308 { return textFieldSwt; 309 } 310 311 312 313 314 315 @Override public boolean setFocusGThread() 316 { return SwtWidgetHelper.setFocusOfTabSwt(textFieldSwt); 317 } 318 319 @Override public void setVisibleGThread(boolean bVisible) { super.setVisibleState(bVisible); swtWidgHelper.setVisibleGThread(bVisible); } 320 321 322 @Override public void removeWidgetImplementation() 323 { 324 if(textFieldSwt !=null){ 325 textFieldSwt.dispose(); 326 } else { 327 stop(); 328 } 329 textFieldSwt = null; 330 if(promptSwt !=null){ 331 promptSwt.dispose(); 332 promptSwt = null; 333 } 334 } 335 336 @Override public void setBoundsPixel(int x, int y, int dx, int dy) 337 { textFieldSwt.setBounds(x,y,dx,dy); 338 } 339 340 341 protected void textFieldFocusGained(){ 342 //- done in GralMng.GralMngFocusListener! super.focusGained(); //set HtmlHelp etc. 343 GralWidget_ifc.ActionChange action = getActionChange(GralWidget_ifc.ActionChangeWhen.onFocusGained); 344 if(action !=null){ 345 Object[] args = action.args(); 346 if(args == null){ action.action().exec(KeyCode.focusGained, widgg, dyda().displayedText); } 347 else { action.action().exec(KeyCode.focusGained, widgg, args, dyda().displayedText); } 348 } 349 if(dyda().displayedText !=null){ 350 textFieldSwt.setText(dyda().displayedText); 351 } 352 } 353 354 355 protected void textFieldFocusLost(){ 356 String text2 = textFieldSwt.getText(); 357 //There was a problem. Because of TextFieldModifyListener the field is already set. Newly read of getText() gets the old text. Bug of windows? 358 //dyda().displayedText = text2; //transfer the current text 359 caretPos(textFieldSwt.getCaretPosition()); 360 //TODO only invoke on changed content. 361 GralWidget_ifc.ActionChange action = getActionChange(GralWidget_ifc.ActionChangeWhen.onChangeAndFocusLost); 362 if(action !=null){ 363 Object[] args = action.args(); 364 if(args == null){ action.action().exec(KeyCode.focusLost, widgg, text2); } 365 else { action.action().exec(KeyCode.focusLost, widgg, args, text2); } 366 } 367 } 368 369 protected void paintWidget(Text swt, PaintEvent e){ 370 GC gc = e.gc; 371 //gc.d 372 int borderwidth = super.borderwidth(); 373 if(borderwidth >0) { 374 GralTextField widg = (GralTextField)super.widgg; 375 Rectangle dim = swt.getBounds(); 376 gc.setLineWidth(borderwidth); 377 Color colorLine = swtWidgHelper.mng.getColorImpl(dyda().lineColor); 378 gc.setForeground(colorLine); 379 //gc.drawLine(0, 0, dim.width, dim.height); //test of coordinates 380 Rectangle rect = new Rectangle(0,0,dim.width, dim.height); //the rect which should drawn counts from (0,0) 381 gc.drawRectangle(rect); 382 } 383 } 384 385 PaintListener paintListener = new PaintListener(){ 386 @Override public void paintControl(PaintEvent e) { 387 SwtTextFieldWrapper.this.paintWidget((Text)swtWidgHelper.widgetSwt, e); 388 } 389 }; 390 391 392 393 /**For edit able fields. 394 */ 395 private class TextFieldFocusListener extends SwtMng.SwtMngFocusListener 396 { 397 398 TextFieldFocusListener(SwtMng mng){ 399 mng.super(mng.mng); 400 } 401 402 @Override public void focusLost(FocusEvent ev){ 403 SwtTextFieldWrapper.this.textFieldFocusLost(); 404 setTouched(false); //assumes that the changed field content is processed with routine above. Can be automaticly overwritten newly be application. 405 super.focusLost(ev); 406 } 407 408 409 @Override public void focusGained(FocusEvent ev) 410 { SwtTextFieldWrapper.this.textFieldFocusGained(); 411 super.focusGained(ev); 412 } 413 } 414 415 416 417 protected class TextFieldModifyListener implements ModifyListener{ 418 @Override public void modifyText(ModifyEvent ev) { 419 String text = textFieldSwt.getText(); 420 GralWidget.DynamicData dyda = SwtTextFieldWrapper.super.dyda(); 421 if(! text.equals(dyda.displayedText)) { 422 dyda.displayedText = text; 423 setTouched(true); 424 //System.out.println("actionText"); 425 //SwtTextFieldWrapper.super.caretPos = textFieldSwt.getCaretPosition(); 426 GralWidget_ifc.ActionChange action = getActionChange(GralWidget_ifc.ActionChangeWhen.onAnyChgContent); 427 if(action !=null){ 428 Object[] args = action.args(); 429 if(args == null){ action.action().exec(KeyCode.valueChanged, widgg, text); } 430 else { action.action().exec(KeyCode.valueChanged, widgg, args, text); } 431 } 432 } 433 //if(dyda.displayedText !=null){ 434 //textFieldSwt.setText(dyda.displayedText); 435 //} 436 } 437 438 }; 439 440 441 442 protected class TextFieldKeyListener extends SwtKeyListener 443 { 444 445 public TextFieldKeyListener(GralKeyListener keyAction) 446 { super(keyAction); 447 } 448 449 @Override public final boolean specialKeysOfWidgetType(int key, GralWidget_ifc widgg, Object widgImpl){ 450 boolean bDone = true; 451 boolean bEditable = widgg.isEditable(); 452 if(bEditable && KeyCode.isWritingKey(key)){ 453 setTextChanged(); 454 } 455 if(bEditable && key != KeyCode.enter && KeyCode.isWritingOrTextNavigationKey(key)){ 456 bDone = true; 457 setTouched(true); 458 } else { 459 boolean bUserOk; 460 GralTextFieldUser_ifc user = user(); 461 if(user !=null){ 462 Point selection = textFieldSwt.getSelection(); 463 bUserOk = user.userKey(key 464 , textFieldSwt.getText() 465 , textFieldSwt.getCaretPosition() 466 , selection.x, selection.y); 467 } else bUserOk = false; 468 if(!bUserOk ){ 469 switch(key){ 470 case KeyCode.ctrl + 'a': { 471 textFieldSwt.selectAll(); 472 } break; 473 default: bDone = false; 474 } 475 } 476 } 477 return bDone; 478 } 479 }; 480 481 void stop(){} 482 483 484 485 486 487 488}