View Javadoc

1   /* ====================================================================
2    *   Copyright 2003-2004 Fabrizio Giustina.
3    *
4    *   Licensed under the Apache License, Version 2.0 (the "License");
5    *   you may not use this file except in compliance with the License.
6    *   You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *   Unless required by applicable law or agreed to in writing, software
11   *   distributed under the License is distributed on an "AS IS" BASIS,
12   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *   See the License for the specific language governing permissions and
14   *   limitations under the License.
15   * ====================================================================
16   */
17  package net.sf.commonclipse.preferences;
18  
19  import org.eclipse.jface.preference.FieldEditor;
20  import org.eclipse.jface.resource.JFaceResources;
21  import org.eclipse.swt.SWT;
22  import org.eclipse.swt.events.DisposeEvent;
23  import org.eclipse.swt.events.DisposeListener;
24  import org.eclipse.swt.events.FocusAdapter;
25  import org.eclipse.swt.events.FocusEvent;
26  import org.eclipse.swt.events.KeyAdapter;
27  import org.eclipse.swt.events.KeyEvent;
28  import org.eclipse.swt.events.SelectionEvent;
29  import org.eclipse.swt.events.SelectionListener;
30  import org.eclipse.swt.graphics.GC;
31  import org.eclipse.swt.graphics.Point;
32  import org.eclipse.swt.layout.GridData;
33  import org.eclipse.swt.widgets.Combo;
34  import org.eclipse.swt.widgets.Composite;
35  
36  
37  /***
38   * Implementation identical to StringFieldEditor but using a combo instead of a Text field.
39   * @author fgiust
40   * @version $Revision: 1.4 $ ($Author: fgiust $)
41   */
42  public class ComboFieldEditor extends FieldEditor
43  {
44  
45      /***
46       * Text limit constant (value <code>-1</code>) indicating unlimited text limit and width.
47       */
48      public static final int UNLIMITED = -1;
49  
50      /***
51       * The text field, or <code>null</code> if none.
52       */
53      Combo textField;
54  
55      /***
56       * predefined values to be shown in list.
57       */
58      private String[] predefinedValues;
59  
60      /***
61       * Cached valid state.
62       */
63      private boolean isValid;
64  
65      /***
66       * Old text value.
67       */
68      private String oldValue;
69  
70      /***
71       * Width of text field in characters; initially unlimited.
72       */
73      private int widthInChars = UNLIMITED;
74  
75      /***
76       * Text limit of text field in characters; initially unlimited.
77       */
78      private int textLimit = UNLIMITED;
79  
80      /***
81       * The error message, or <code>null</code> if none.
82       */
83      private String errorMessage;
84  
85      /***
86       * Indicates whether the empty string is legal; <code>true</code> by default.
87       */
88      private boolean emptyStringAllowed = true;
89  
90      /***
91       * Creates a new string field editor.
92       */
93      protected ComboFieldEditor()
94      {
95      }
96  
97      /***
98       * Creates a string field editor. Use the method <code>setTextLimit</code> to limit the text.
99       * @param name the name of the preference this field editor works on
100      * @param labelText the label text of the field editor
101      * @param width the width of the text input field in characters, or <code>UNLIMITED</code> for no limit
102      * @param parent the parent of the field editor's control
103      */
104     public ComboFieldEditor(String name, String labelText, int width, Composite parent)
105     {
106         init(name, labelText);
107         this.widthInChars = width;
108         this.isValid = false;
109         this.errorMessage = JFaceResources.getString("StringFieldEditor.errorMessage"); //$NON-NLS-1$
110         createControl(parent);
111     }
112 
113     /***
114      * Creates a string field editor of unlimited width. Use the method <code>setTextLimit</code> to limit the text.
115      * @param name the name of the preference this field editor works on
116      * @param labelText the label text of the field editor
117      * @param parent the parent of the field editor's control
118      */
119     public ComboFieldEditor(String name, String labelText, Composite parent)
120     {
121         this(name, labelText, UNLIMITED, parent);
122     }
123 
124     /***
125      * Checks whether the text input field contains a valid value or not.
126      * @return <code>true</code> if the field value is valid, and <code>false</code> if invalid
127      */
128     protected boolean checkState()
129     {
130         boolean result = false;
131         if (this.emptyStringAllowed)
132         {
133             result = true;
134         }
135 
136         if (this.textField == null)
137         {
138             result = false;
139         }
140 
141         String txt = this.textField.getText();
142 
143         if (txt == null)
144         {
145             result = false;
146         }
147 
148         result = (txt.trim().length() > 0) || this.emptyStringAllowed;
149 
150         // call hook for subclasses
151         result = result && doCheckState();
152 
153         if (result)
154         {
155             clearErrorMessage();
156         }
157         else
158         {
159             showErrorMessage(this.errorMessage);
160         }
161 
162         return result;
163     }
164 
165     /***
166      * @see org.eclipse.jface.preference.FieldEditor#doLoad()
167      */
168     protected void doLoad()
169     {
170         if (this.textField != null)
171         {
172 
173             addDefaultOptions();
174             String value = getPreferenceStore().getString(getPreferenceName());
175             this.textField.setText(value);
176             this.oldValue = value;
177 
178         }
179     }
180 
181     /***
182      * @see org.eclipse.jface.preference.FieldEditor#doLoadDefault()
183      */
184     protected void doLoadDefault()
185     {
186         if (this.textField != null)
187         {
188             addDefaultOptions();
189             String value = getPreferenceStore().getDefaultString(getPreferenceName());
190             this.textField.setText(value);
191 
192         }
193         valueChanged();
194     }
195 
196     /***
197      * @see org.eclipse.jface.preference.FieldEditor#doStore()
198      */
199     protected void doStore()
200     {
201         getPreferenceStore().setValue(getPreferenceName(), this.textField.getText());
202     }
203 
204     /***
205      * Returns the error message that will be displayed when and if an error occurs.
206      * @return the error message, or <code>null</code> if none
207      */
208     public String getErrorMessage()
209     {
210         return this.errorMessage;
211     }
212 
213     /***
214      * @see org.eclipse.jface.preference.FieldEditor#getNumberOfControls()
215      */
216     public int getNumberOfControls()
217     {
218         return 2;
219     }
220 
221     /***
222      * Returns the field editor's value.
223      * @return the current value
224      */
225     public String getStringValue()
226     {
227         if (this.textField != null)
228         {
229             return this.textField.getText();
230         }
231         return getPreferenceStore().getString(getPreferenceName());
232     }
233 
234     /***
235      * Returns this field editor's text control.
236      * @return the text control, or <code>null</code> if no text field is created yet
237      */
238     protected Combo getTextControl()
239     {
240         return this.textField;
241     }
242 
243     /***
244      * Returns this field editor's text control.
245      * <p>
246      * The control is created if it does not yet exist
247      * </p>
248      * @param parent the parent
249      * @return the text control
250      */
251     public Combo getTextControl(Composite parent)
252     {
253         if (this.textField == null)
254         {
255             this.textField = new Combo(parent, SWT.SINGLE | SWT.BORDER);
256 
257             this.textField.setFont(parent.getFont());
258 
259             this.textField.addKeyListener(new KeyAdapter()
260             {
261 
262                 public void keyReleased(KeyEvent e)
263                 {
264                     valueChanged();
265                 }
266             });
267             this.textField.addSelectionListener(new SelectionListener()
268             {
269 
270                 public void widgetSelected(SelectionEvent e)
271                 {
272                     valueChanged();
273                 }
274 
275                 public void widgetDefaultSelected(SelectionEvent e)
276                 {
277                     valueChanged();
278                 }
279 
280             });
281             this.textField.addFocusListener(new FocusAdapter()
282             {
283 
284                 public void focusGained(FocusEvent e)
285                 {
286                     refreshValidState();
287                 }
288 
289                 public void focusLost(FocusEvent e)
290                 {
291                     valueChanged();
292                     clearErrorMessage();
293                 }
294             });
295 
296             this.textField.addDisposeListener(new DisposeListener()
297             {
298 
299                 public void widgetDisposed(DisposeEvent event)
300                 {
301                     ComboFieldEditor.this.textField = null;
302                 }
303             });
304             if (this.textLimit > 0)
305             { // Only set limits above 0 - see SWT spec
306                 this.textField.setTextLimit(this.textLimit);
307             }
308         }
309         else
310         {
311             checkParent(this.textField, parent);
312         }
313         return this.textField;
314     }
315 
316     /***
317      * Returns whether an empty string is a valid value.
318      * @return <code>true</code> if an empty string is a valid value, and <code>false</code> if an empty string is
319      * invalid
320      * @see #setEmptyStringAllowed
321      */
322     public boolean isEmptyStringAllowed()
323     {
324         return this.emptyStringAllowed;
325     }
326 
327     /***
328      * @see org.eclipse.jface.preference.FieldEditor # isValid()
329      */
330     public boolean isValid()
331     {
332         return this.isValid;
333     }
334 
335     /***
336      * @see org.eclipse.jface.preference.FieldEditor#refreshValidState()
337      */
338     protected void refreshValidState()
339     {
340         this.isValid = checkState();
341     }
342 
343     /***
344      * Sets whether the empty string is a valid value or not.
345      * @param b <code>true</code> if the empty string is allowed, and <code>false</code> if it is considered invalid
346      */
347     public void setEmptyStringAllowed(boolean b)
348     {
349         this.emptyStringAllowed = b;
350     }
351 
352     /***
353      * Sets the error message that will be displayed when and if an error occurs.
354      * @param message the error message
355      */
356     public void setErrorMessage(String message)
357     {
358         this.errorMessage = message;
359     }
360 
361     /***
362      * @see org.eclipse.jface.preference.FieldEditor#setFocus()
363      */
364     public void setFocus()
365     {
366         if (this.textField != null)
367         {
368             this.textField.setFocus();
369         }
370     }
371 
372     /***
373      * Sets this field editor's value.
374      * @param value the new value, or <code>null</code> meaning the empty string
375      */
376     public void setStringValue(String value)
377     {
378         if (this.textField != null)
379         {
380             String newValue = value;
381             if (newValue == null)
382             {
383                 newValue = ""; //$NON-NLS-1$
384             }
385 
386             this.oldValue = this.textField.getText();
387 
388             if (!this.oldValue.equals(newValue))
389             {
390                 this.textField.setText(newValue);
391 
392                 valueChanged();
393             }
394         }
395     }
396 
397     /***
398      * Sets this text field's text limit.
399      * @param limit the limit on the number of character in the text input field, or <code>UNLIMITED</code> for no
400      * limit
401      */
402     public void setTextLimit(int limit)
403     {
404         this.textLimit = limit;
405         if (this.textField != null)
406         {
407             this.textField.setTextLimit(limit);
408         }
409     }
410 
411     /***
412      * Shows the error message set via <code>setErrorMessage</code>.
413      */
414     public void showErrorMessage()
415     {
416         showErrorMessage(this.errorMessage);
417     }
418 
419     /***
420      * Informs this field editor's listener, if it has one, about a change to the value (<code>VALUE</code> property)
421      * provided that the old and new values are different.
422      * <p>
423      * This hook is <em>not</em> called when the text is initialized (or reset to the default value) from the
424      * preference store.
425      * </p>
426      */
427     protected void valueChanged()
428     {
429         setPresentsDefaultValue(false);
430         boolean oldState = this.isValid;
431         refreshValidState();
432 
433         if (this.isValid != oldState)
434         {
435             fireStateChanged(IS_VALID, oldState, this.isValid);
436         }
437 
438         String newValue = this.textField.getText();
439         if (!newValue.equals(this.oldValue))
440         {
441             fireValueChanged(VALUE, this.oldValue, newValue);
442             this.oldValue = newValue;
443         }
444     }
445 
446     /***
447      * @see org.eclipse.jface.preference.FieldEditor#setEnabled(boolean,Composite).
448      */
449     public void setEnabled(boolean enabled, Composite parent)
450     {
451         super.setEnabled(enabled, parent);
452         getTextControl(parent).setEnabled(enabled);
453     }
454 
455     /***
456      * Hook for subclasses to do specific state checks.
457      * <p>
458      * The default implementation of this framework method does nothing and returns <code>true</code>. Subclasses
459      * should override this method to specific state checks.
460      * </p>
461      * @return <code>true</code> if the field value is valid, and <code>false</code> if invalid
462      */
463     protected boolean doCheckState()
464     {
465         return true;
466     }
467 
468     /***
469      * @see org.eclipse.jface.preference.FieldEditor#adjustForNumColumns(int)
470      */
471     protected void adjustForNumColumns(int numColumns)
472     {
473         GridData gd = (GridData) this.textField.getLayoutData();
474         gd.horizontalSpan = numColumns - 1;
475         // We only grab excess space if we have to
476         // If another field editor has more columns then
477         // we assume it is setting the width.
478         gd.grabExcessHorizontalSpace = gd.horizontalSpan == 1;
479     }
480 
481     /***
482      * @see org.eclipse.jface.preference.FieldEditor#doFillIntoGrid(Composite, int)
483      */
484     protected void doFillIntoGrid(Composite parent, int numColumns)
485     {
486         getLabelControl(parent);
487 
488         this.textField = getTextControl(parent);
489         GridData gd = new GridData();
490         gd.horizontalSpan = numColumns - 1;
491         if (this.widthInChars != UNLIMITED)
492         {
493             GC gc = new GC(this.textField);
494             try
495             {
496                 Point extent = gc.textExtent("X"); //$NON-NLS-1$
497                 gd.widthHint = this.widthInChars * extent.x;
498             }
499             finally
500             {
501                 gc.dispose();
502             }
503         }
504         else
505         {
506             gd.horizontalAlignment = GridData.FILL;
507             gd.grabExcessHorizontalSpace = true;
508         }
509         this.textField.setLayoutData(gd);
510     }
511 
512     /***
513      * Sets a list of predefined values that must be shown in the combo.
514      * @param strings array of Strings added to the combo
515      */
516     public void setPredefinedValues(String[] strings)
517     {
518         this.predefinedValues = strings;
519     }
520 
521     /***
522      * Adds predefined options to the combo.
523      */
524     private void addDefaultOptions()
525     {
526         if (this.textField != null && this.predefinedValues != null)
527         {
528             this.textField.setItems(this.predefinedValues);
529         }
530     }
531 
532     /***
533      * @see org.eclipse.jface.preference.FieldEditor#clearErrorMessage()
534      */
535     protected void clearErrorMessage()
536     {
537         super.clearErrorMessage();
538     }
539 
540 }