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}