    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}