001package org.vishia.gral.swt;
002
003import java.io.IOException;
004
005import org.eclipse.swt.SWT;
006import org.eclipse.swt.events.FocusEvent;
007import org.eclipse.swt.events.FocusListener;
008import org.eclipse.swt.events.KeyListener;
009import org.eclipse.swt.events.ModifyEvent;
010import org.eclipse.swt.events.ModifyListener;
011import org.eclipse.swt.graphics.Font;
012import org.eclipse.swt.graphics.Point;
013import org.eclipse.swt.graphics.Rectangle;
014import org.eclipse.swt.widgets.Composite;
015import org.eclipse.swt.widgets.Label;
016import org.eclipse.swt.widgets.ScrollBar;
017import org.eclipse.swt.widgets.Text;
018import org.vishia.gral.base.GralGraphicTimeOrder;
019import org.vishia.gral.base.GralKeyListener;
020import org.vishia.gral.base.GralMng;
021import org.vishia.gral.base.GralPanelContent;
022import org.vishia.gral.base.GralTextBox;
023import org.vishia.gral.base.GralTextField;
024import org.vishia.gral.base.GralWidget;
025import org.vishia.gral.ifc.GralColor;
026import org.vishia.gral.ifc.GralFont;
027import org.vishia.gral.ifc.GralRectangle;
028import org.vishia.gral.ifc.GralTextFieldUser_ifc;
029import org.vishia.gral.ifc.GralUserAction;
030import org.vishia.gral.ifc.GralWidget_ifc;
031import org.vishia.gral.swt.SwtTextFieldWrapper.TextFieldKeyListener;
032import org.vishia.gral.swt.SwtTextFieldWrapper.TextFieldModifyListener;
033import org.vishia.util.KeyCode;
034
035public class SwtTextBox extends GralTextBox.GraphicImplAccess
036{
037
038  /**Version and history
039   * <ul>
040   * <li>2014-08-16 Hartmut chg: GralTextBox not abstract, using GraphicImplAccess like new concept of all GralWidgets. 
041   * <li>2012-01-06 Hartmut chg: The {@link #append(CharSequence)} etc. methods are implemented
042   *   in this super class instead in the graphic layer implementation classes. Therefore
043   *   the methods {@link #appendTextInGThread(CharSequence)} and {@link #setTextInGThread(CharSequence)}
044   *   are defined here to implement in the graphic layer. The set- and apppend methods are <b>threadsafe</b> now.
045   * </ul>
046   *
047   * <b>Copyright/Copyleft</b>:
048   * For this source the LGPL Lesser General Public License,
049   * published by the Free Software Foundation is valid.
050   * It means:
051   * <ol>
052   * <li> You can use this source without any restriction for any desired purpose.
053   * <li> You can redistribute copies of this source to everybody.
054   * <li> Every user of this source, also the user of redistribute copies
055   *    with or without payment, must accept this license for further using.
056   * <li> But the LPGL is not appropriate for a whole software product,
057   *    if this source is only a part of them. It means, the user
058   *    must publish this part of source,
059   *    but don't need to publish the whole source of the own product.
060   * <li> You can study and modify (improve) this source
061   *    for own using or for redistribution, but you have to license the
062   *    modified sources likewise under this LGPL Lesser General Public License.
063   *    You mustn't delete this Copyright/Copyleft inscription in this source file.
064   * </ol>
065   * If you are indent to use this sources without publishing its usage, you can get
066   * a second license subscribing a special contract with the author. 
067   * 
068   * @author Hartmut Schorrig = hartmut.schorrig@vishia.de
069   * 
070   * 
071   */
072  @SuppressWarnings("hiding")
073  public final static String sVersion = "2014-08-16";
074
075  
076  /**Experience: use SwtTextFieldWrapper as composite? (instead superclass)
077   * 
078   */
079  @SuppressWarnings("unused")
080  private SwtTextFieldWrapper swtText;
081  
082  /**It contains the association to the swt widget (Control) and the {@link SwtMng}
083   * and implements some methods of {@link GralWidgImpl_ifc} which are delegate from this.
084   */
085  private final SwtWidgetHelper wdgh;
086  
087  /*package private*/ Text textFieldSwt;
088  
089  /**A possible prompt for the text field or null. */
090  Label promptSwt;
091  
092  
093  protected SwtTextBox(GralTextBox widgg, SwtMng mng)
094  { widgg.super(widgg); //NOTE: superclass is a non static inner class of GralTextField. 
095    Composite panelSwt = mng.getCurrentPanel();
096    GralPanelContent gralPanel = mng.mng.getCurrentPanel();
097    
098    assert(panelSwt !=null);
099    textFieldSwt = new Text(panelSwt, SWT.MULTI|SWT.H_SCROLL|SWT.V_SCROLL); //;style);
100    textFieldSwt.setData(this);
101    super.wdgimpl = this.wdgh = new SwtWidgetHelper(textFieldSwt, mng);
102    textFieldSwt.addFocusListener(focusLstn);
103    textFieldSwt.setFont(mng.propertiesGuiSwt.stdInputFont);
104    textFieldSwt.setEditable(widgg.isEditable());
105    textFieldSwt.setBackground(mng.propertiesGuiSwt.colorSwt(0xFFFFFF));
106    textFieldSwt.addMouseListener(SwtGralMouseListener.mouseActionStd);
107    KeyListener swtKeyListener = new TextBoxKeyListener(mng.mng._impl.gralKeyListener);
108    textFieldSwt.addKeyListener(swtKeyListener);
109    TextBoxModifyListener modifyListener = new TextBoxModifyListener();
110    textFieldSwt.addModifyListener(modifyListener);
111    
112    mng.setPosAndSize_(widgg.pos(), textFieldSwt);
113    if(prompt() != null && promptStylePosition().equals("t")){
114      final int yPixelField;
115      final Font promptFont;
116      int ySize = (int)(widgg.pos().height());
117      switch(ySize){
118      case 3:  promptFont = mng.propertiesGuiSwt.smallPromptFont;
119               yPixelField = mng.propertiesGuiSwt.yPixelUnit() * 2 -3;
120               break;
121      case 2:  promptFont = mng.propertiesGuiSwt.smallPromptFont;
122               yPixelField = (int)(1.5F * mng.mng.propertiesGui.yPixelUnit());
123               break;
124      default: promptFont = mng.propertiesGuiSwt.smallPromptFont;
125               yPixelField = mng.mng.propertiesGui.yPixelUnit() * 2 -3;
126      }//switch
127      Rectangle boundsField = textFieldSwt.getBounds();
128      Rectangle boundsPrompt = new Rectangle(boundsField.x, boundsField.y-3  //occupy part of field above, only above the normal letters
129        , boundsField.width, boundsField.height );
130      
131      if(promptStylePosition().equals("t")){ 
132        boundsPrompt.height -= (yPixelField -4);
133        boundsPrompt.y -= 1;
134        
135        boundsField.y += (boundsField.height - yPixelField );
136        boundsField.height = yPixelField;
137      }
138      Label wgPrompt = new Label(panelSwt, 0);
139      //Text wgPrompt = new Text(((SwtPanel)pos.panel).getPanelImpl(), 0);
140      wgPrompt.setFont(promptFont);
141      wgPrompt.setText(prompt());
142      Point promptSize = wgPrompt.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
143      if(promptSize.x > boundsPrompt.width){
144        boundsPrompt.width = promptSize.x;  //use the longer value, if the prompt text is longer as the field.
145      }
146      textFieldSwt.setBounds(boundsField);
147      wgPrompt.setBounds(boundsPrompt);
148    } 
149    mng.mng.registerWidget(widgg);
150  }
151
152  
153  static void createTextBox(GralTextBox widgg, SwtMng mng){
154    SwtTextBox widgetSwt = new SwtTextBox(widgg, (SwtMng)mng);
155    //
156  }
157  
158
159  
160  
161  //@Override public Widget getWidgetImplementation(){ return textFieldSwt; } 
162  //@Override public boolean setFocus(){ return textFieldSwt.setFocus(); }
163
164  @Override public GralRectangle getPixelPositionSize(){ return wdgh.getPixelPositionSize(); }
165
166
167
168  
169
170  
171  @Override public Object getWidgetImplementation()
172  { return textFieldSwt;
173  }
174
175  /*
176
177  @Override public String getText()
178  {
179    String oldText = textFieldSwt.getText();
180    return oldText;
181  }
182   
183  @Override public GralColor setBackgroundColor(GralColor color)
184  { return SwtWidgetHelper.setBackgroundColor(color, textFieldSwt);
185  }
186  
187
188  @Override public GralColor setForegroundColor(GralColor color)
189  { return SwtWidgetHelper.setForegroundColor(color, textFieldSwt);
190  }
191  
192  
193
194
195
196  
197  @Override public void setTextInGThread(CharSequence text){ 
198    textFieldSwt.setText(text.toString()); 
199  }
200  
201
202  
203  @Override public void appendTextInGThread(CharSequence text){ 
204    textFieldSwt.append(text.toString()); 
205  }
206  
207  */
208  
209  
210  @Override public void setBoundsPixel(int x, int y, int dx, int dy)
211  { textFieldSwt.setBounds(x,y,dx,dy);
212  }
213  
214
215  
216  
217  @Override public void repaintGthread(){
218    int catastrophicalCount = 0;
219    int chg;
220    if(textFieldSwt !=null){ //do nothing if the graphic implementation widget is removed.
221      GralWidget.DynamicData dyda = dyda();
222      while( (chg = getChanged()) !=0){ //widgg.dyda.whatIsChanged.get();
223        if(++catastrophicalCount > 10000) 
224          throw new RuntimeException("atomic failed");
225        if((chg & chgText) !=0 && dyda.displayedText !=null){ 
226          textFieldSwt.setText(dyda.displayedText);
227          final int selectionStart, selectionEnd;
228          final int zText = dyda.displayedText.length();
229          if(caretPos() <0){
230            selectionEnd = dyda.displayedText.length(); selectionStart = selectionEnd; // -1;
231          }
232          else if(caretPos() >0){
233            selectionEnd = caretPos() > zText ? zText : caretPos();
234            selectionStart = selectionEnd; // -1;
235          } else {
236            assert(caretPos() ==0);
237            selectionEnd = selectionStart =-1;  //dont call
238          }
239          if(selectionStart >=0){
240            textFieldSwt.setSelection(selectionStart, selectionEnd);
241          }
242        }
243        if((chg & chgAddText) !=0) {
244          textFieldSwt.append(getAndClearNewText());
245        }
246        if((chg & chgColorText)!=0){
247          SwtProperties props = wdgh.mng.propertiesGuiSwt;
248          if(dyda.textColor !=null){
249            textFieldSwt.setForeground(props.colorSwt(dyda.textColor));
250          }
251          if(dyda.backColor !=null){
252            textFieldSwt.setBackground(props.colorSwt(dyda.backColor));
253          }
254          if(dyda.textFont !=null){
255            textFieldSwt.setFont(props.fontSwt(dyda.textFont));
256          }
257        }
258        if((chg & chgEditable)!=0){ 
259          textFieldSwt.setEditable(true); 
260        }
261        if((chg & chgNonEditable)!=0){ 
262          textFieldSwt.setEditable(false); 
263        }
264        
265        if((chg & chgViewTrail)!=0) {
266          ScrollBar scroll = textFieldSwt.getVerticalBar();
267          int maxScroll = scroll.getMaximum();
268          scroll.setSelection(maxScroll);
269          textFieldSwt.update();
270        }
271        if((chg & chgCursor) !=0){ 
272          textFieldSwt.setSelection(caretPos());
273        }
274        if((chg & chgVisible) !=0) {
275          textFieldSwt.getShell().setVisible(true);
276        }
277        if((chg & chgInvisible) !=0) {
278          textFieldSwt.getShell().setVisible(false);
279        }
280        if((chg & chgColorText) !=0){ textFieldSwt.setForeground(wdgh.mng.getColorImpl(dyda().textColor)); }
281        if((chg & chgColorBack) !=0){ textFieldSwt.setBackground(wdgh.mng.getColorImpl(dyda().backColor)); }
282        textFieldSwt.redraw();
283        acknChanged(chg);
284      }
285    }
286  }
287
288  
289  
290  
291
292  @Override public boolean setFocusGThread()
293  { return SwtWidgetHelper.setFocusOfTabSwt(textFieldSwt);
294  }
295
296
297  @Override public void setVisibleGThread(boolean bVisible) { super.setVisibleState(bVisible); wdgh.setVisibleGThread(bVisible); }
298
299  
300  @Override public void removeWidgetImplementation()
301  {
302    if(textFieldSwt !=null) {
303      textFieldSwt.dispose();
304      textFieldSwt = null;
305    }
306    if(promptSwt !=null){
307      promptSwt.dispose();
308      promptSwt = null;
309    }
310  }
311
312  
313  protected class TextBoxKeyListener extends SwtKeyListener
314  //protected SwtKeyListener swtKeyListener = new SwtKeyListener(SwtTextBox.this.swtWidgHelper.mng._impl.gralKeyListener)
315  {
316
317    public TextBoxKeyListener(GralKeyListener keyAction)
318    { super(keyAction);
319    }
320
321    @Override public final boolean specialKeysOfWidgetType(int key, GralWidget_ifc widgg, Object widgImpl){ 
322      boolean bDone = true;
323      if(KeyCode.isWritingKey(key)){
324        setTextChanged();
325      }
326      boolean bUserOk;
327      GralTextFieldUser_ifc user = user();
328      if(user !=null){
329        Point selection = textFieldSwt.getSelection();
330        bUserOk = user.userKey(key
331            , textFieldSwt.getText()
332            , textFieldSwt.getCaretPosition()
333            , selection.x, selection.y);
334      } else { bUserOk = false; }
335      //
336      if(!bUserOk ){  //user has not accept the key
337        if(KeyCode.isWritingOrTextNavigationKey(key)) return true;
338        switch(key){
339          case KeyCode.ctrl + 'a': { 
340            textFieldSwt.selectAll();
341          } break;
342          default: bDone = false;
343        }
344      }
345      return bDone; 
346    }
347    
348    
349    
350  }
351
352
353  
354  
355  protected class TextBoxModifyListener implements ModifyListener{
356    @Override public void modifyText(ModifyEvent ev) {
357      String text = textFieldSwt.getText();
358      SwtTextBox.super.dyda().displayedText = text;
359      //System.out.println("actionText");
360      //SwtTextFieldWrapper.super.caretPos = textFieldSwt.getCaretPosition();
361      GralWidget_ifc.ActionChange action = getActionChange(GralWidget_ifc.ActionChangeWhen.onAnyChgContent);
362      if(action !=null){
363        Object[] args = action.args();
364        if(args == null){ action.action().exec(KeyCode.valueChanged, widgg, text); }
365        else { action.action().exec(KeyCode.valueChanged, widgg, args, text); }
366      }
367      //if(dyda.displayedText !=null){
368        //textFieldSwt.setText(dyda.displayedText);
369      //}
370    }
371    
372  }
373  
374  protected FocusListener focusLstn = new FocusListener()
375  {
376    
377    @Override
378    public void focusLost(FocusEvent e)
379    {
380      System.err.println("TextBox focus lost");
381    }
382    
383    @Override
384    public void focusGained(FocusEvent e)
385    {
386      System.err.println("TextBox focus gained");
387    }
388  };
389
390  
391}