org.eclipse.jface.preference.StringFieldEditor Maven / Gradle / Ivy
/*******************************************************************************
* Copyright (c) 2000, 2016 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jface.preference;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Text;
/**
* A field editor for a string type preference.
*
* This class may be used as is, or subclassed as required.
*
*/
public class StringFieldEditor extends FieldEditor {
/**
* Validation strategy constant (value 0
) indicating that the
* editor should perform validation after every key stroke.
*
* @see #setValidateStrategy
*/
public static final int VALIDATE_ON_KEY_STROKE = 0;
/**
* Validation strategy constant (value 1
) indicating that
* the editor should perform validation only when the text widget
* loses focus.
*
* @see #setValidateStrategy
*/
public static final int VALIDATE_ON_FOCUS_LOST = 1;
/**
* Text limit constant (value -1
) indicating unlimited
* text limit and width.
*/
public static int UNLIMITED = -1;
/**
* Cached valid state.
*/
private boolean isValid;
/**
* Old text value.
* @since 3.4 this field is protected.
*/
protected String oldValue;
/**
* The text field, or null
if none.
*/
Text textField;
/**
* Height of text field in characters; initially 1.
*/
private int heigthInChars = 1;
/**
* Width of text field in characters; initially unlimited.
*/
private int widthInChars = UNLIMITED;
/**
* Text limit of text field in characters; initially unlimited.
*/
private int textLimit = UNLIMITED;
/**
* The error message, or null
if none.
*/
private String errorMessage;
/**
* Indicates whether the empty string is legal;
* true
by default.
*/
private boolean emptyStringAllowed = true;
/**
* The validation strategy;
* VALIDATE_ON_KEY_STROKE
by default.
*/
private int validateStrategy = VALIDATE_ON_KEY_STROKE;
/**
* Creates a new string field editor
*/
protected StringFieldEditor() {
}
/**
* Creates a string field editor. Use the method setTextLimit
to
* limit the text.
*
* @param name the name of the preference this field editor works on
* @param labelText the label text of the field editor
* @param widthInChars the width of the text input field in characters, or
* UNLIMITED
for no limit
* @param heigthInChars the height of the text input field in characters.
* @param strategy either VALIDATE_ON_KEY_STROKE
to perform on
* the fly checking (the default), or
* VALIDATE_ON_FOCUS_LOST
to perform
* validation only after the text has been typed in
* @param parent the parent of the field editor's control
* @since 3.17
*/
public StringFieldEditor(String name, String labelText, int widthInChars, int heigthInChars, int strategy,
Composite parent) {
init(name, labelText);
this.widthInChars = widthInChars;
this.heigthInChars = heigthInChars;
setValidateStrategy(strategy);
isValid = false;
errorMessage = JFaceResources.getString("StringFieldEditor.errorMessage");//$NON-NLS-1$
createControl(parent);
}
/**
* Creates a string field editor. Use the method setTextLimit
to
* limit the text.
*
* @param name the name of the preference this field editor works on
* @param labelText the label text of the field editor
* @param width the width of the text input field in characters, or
* UNLIMITED
for no limit
* @param strategy either VALIDATE_ON_KEY_STROKE
to perform on the
* fly checking (the default), or
* VALIDATE_ON_FOCUS_LOST
to perform validation
* only after the text has been typed in
* @param parent the parent of the field editor's control
* @since 2.0
*/
public StringFieldEditor(String name, String labelText, int width,
int strategy, Composite parent) {
this(name, labelText, width, 1, strategy, parent);
}
/**
* Creates a string field editor.
* Use the method setTextLimit
to limit the text.
*
* @param name the name of the preference this field editor works on
* @param labelText the label text of the field editor
* @param width the width of the text input field in characters,
* or UNLIMITED
for no limit
* @param parent the parent of the field editor's control
*/
public StringFieldEditor(String name, String labelText, int width,
Composite parent) {
this(name, labelText, width, VALIDATE_ON_KEY_STROKE, parent);
}
/**
* Creates a string field editor of unlimited width.
* Use the method setTextLimit
to limit the text.
*
* @param name the name of the preference this field editor works on
* @param labelText the label text of the field editor
* @param parent the parent of the field editor's control
*/
public StringFieldEditor(String name, String labelText, Composite parent) {
this(name, labelText, UNLIMITED, parent);
}
@Override
protected void adjustForNumColumns(int numColumns) {
GridData gd = (GridData) textField.getLayoutData();
gd.horizontalSpan = numColumns - 1;
// We only grab excess space if we have to
// If another field editor has more columns then
// we assume it is setting the width.
gd.grabExcessHorizontalSpace = gd.horizontalSpan == 1;
}
/**
* Checks whether the text input field contains a valid value or not.
*
* @return true
if the field value is valid,
* and false
if invalid
*/
protected boolean checkState() {
boolean result = false;
if (emptyStringAllowed) {
result = true;
}
if (textField == null) {
result = false;
} else {
String txt = textField.getText();
result = (txt.trim().length() > 0) || emptyStringAllowed;
}
// call hook for subclasses
result = result && doCheckState();
if (result) {
clearErrorMessage();
} else {
showErrorMessage(errorMessage);
}
return result;
}
/**
* Hook for subclasses to do specific state checks.
*
* The default implementation of this framework method does
* nothing and returns true
. Subclasses should
* override this method to specific state checks.
*
*
* @return true
if the field value is valid,
* and false
if invalid
*/
protected boolean doCheckState() {
return true;
}
/**
* Fills this field editor's basic controls into the given parent.
*
* The string field implementation of this FieldEditor
* framework method contributes the text field. Subclasses may override
* but must call super.doFillIntoGrid
.
*
*/
@Override
protected void doFillIntoGrid(Composite parent, int numColumns) {
getLabelControl(parent);
textField = getTextControl(parent);
GridData gd = new GridData();
gd.horizontalSpan = numColumns - 1;
if (widthInChars != UNLIMITED || heigthInChars > 1) {
GC gc = new GC(textField);
try {
if (widthInChars!=UNLIMITED) {
Point extent = gc.textExtent("X");//$NON-NLS-1$
gd.widthHint = widthInChars * extent.x;
} else {
gd.horizontalAlignment = GridData.FILL;
gd.grabExcessHorizontalSpace = true;
}
if (heigthInChars>1) {
gd.heightHint = heigthInChars * gc.getFontMetrics().getHeight();
}
} finally {
gc.dispose();
}
} else {
gd.horizontalAlignment = GridData.FILL;
gd.grabExcessHorizontalSpace = true;
}
textField.setLayoutData(gd);
}
@Override
protected void doLoad() {
if (textField != null) {
String value = getPreferenceStore().getString(getPreferenceName());
textField.setText(value);
oldValue = value;
}
}
@Override
protected void doLoadDefault() {
if (textField != null) {
String value = getPreferenceStore().getDefaultString(
getPreferenceName());
textField.setText(value);
}
valueChanged();
}
@Override
protected void doStore() {
getPreferenceStore().setValue(getPreferenceName(), textField.getText());
}
/**
* Returns the error message that will be displayed when and if
* an error occurs.
*
* @return the error message, or null
if none
*/
public String getErrorMessage() {
return errorMessage;
}
@Override
public int getNumberOfControls() {
return 2;
}
/**
* Returns the field editor's value.
*
* @return the current value
*/
public String getStringValue() {
if (textField != null) {
return textField.getText();
}
return getPreferenceStore().getString(getPreferenceName());
}
/**
* Returns this field editor's text control.
*
* @return the text control, or null
if no
* text field is created yet
*/
protected Text getTextControl() {
return textField;
}
/**
* Returns this field editor's text control.
*
* The control is created if it does not yet exist
*
*
* @param parent the parent
* @return the text control
*/
public Text getTextControl(Composite parent) {
if (textField == null) {
textField = createTextWidget(parent);
textField.setFont(parent.getFont());
switch (validateStrategy) {
case VALIDATE_ON_KEY_STROKE:
textField.addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(KeyEvent e) {
valueChanged();
}
});
textField.addFocusListener(new FocusAdapter() {
// Ensure that the value is checked on focus loss in case we
// missed a keyRelease or user hasn't released key.
// See https://bugs.eclipse.org/bugs/show_bug.cgi?id=214716
@Override
public void focusLost(FocusEvent e) {
valueChanged();
}
});
break;
case VALIDATE_ON_FOCUS_LOST:
textField.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
clearErrorMessage();
}
});
textField.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
refreshValidState();
}
@Override
public void focusLost(FocusEvent e) {
valueChanged();
}
});
break;
default:
Assert.isTrue(false, "Unknown validate strategy");//$NON-NLS-1$
}
textField.addDisposeListener(event -> textField = null);
if (textLimit > 0) {//Only set limits above 0 - see SWT spec
textField.setTextLimit(textLimit);
}
} else {
checkParent(textField, parent);
}
return textField;
}
/**
* Create the text widget.
*
* @param parent the parent composite
* @return The widget
* @since 3.17
*/
protected Text createTextWidget(Composite parent) {
if (heigthInChars > 1) {
return new Text(parent, SWT.BORDER | SWT.MULTI | SWT.V_SCROLL | SWT.WRAP);
}
return new Text(parent, SWT.SINGLE | SWT.BORDER);
}
/**
* Returns whether an empty string is a valid value.
*
* @return true
if an empty string is a valid value, and
* false
if an empty string is invalid
* @see #setEmptyStringAllowed
*/
public boolean isEmptyStringAllowed() {
return emptyStringAllowed;
}
@Override
public boolean isValid() {
return isValid;
}
@Override
protected void refreshValidState() {
isValid = checkState();
}
/**
* Sets whether the empty string is a valid value or not.
*
* @param b true
if the empty string is allowed,
* and false
if it is considered invalid
*/
public void setEmptyStringAllowed(boolean b) {
emptyStringAllowed = b;
}
/**
* Sets the error message that will be displayed when and if
* an error occurs.
*
* @param message the error message
*/
public void setErrorMessage(String message) {
errorMessage = message;
}
@Override
public void setFocus() {
if (textField != null) {
textField.setFocus();
}
}
/**
* Sets this field editor's value.
*
* @param value the new value, or null
meaning the empty string
*/
public void setStringValue(String value) {
if (textField != null) {
if (value == null) {
value = "";//$NON-NLS-1$
}
oldValue = textField.getText();
if (!oldValue.equals(value)) {
textField.setText(value);
valueChanged();
}
}
}
/**
* Sets this text field's text limit.
*
* @param limit the limit on the number of character in the text
* input field, or UNLIMITED
for no limit
*/
public void setTextLimit(int limit) {
textLimit = limit;
if (textField != null) {
textField.setTextLimit(limit);
}
}
/**
* Sets the strategy for validating the text.
*
* Calling this method has no effect after createPartControl
* is called. Thus this method is really only useful for subclasses to call
* in their constructor. However, it has public visibility for backward
* compatibility.
*
*
* @param value either VALIDATE_ON_KEY_STROKE
to perform
* on the fly checking (the default), or VALIDATE_ON_FOCUS_LOST
to
* perform validation only after the text has been typed in
*/
public void setValidateStrategy(int value) {
Assert.isTrue(value == VALIDATE_ON_FOCUS_LOST
|| value == VALIDATE_ON_KEY_STROKE);
validateStrategy = value;
}
/**
* Shows the error message set via setErrorMessage
.
*/
public void showErrorMessage() {
showErrorMessage(errorMessage);
}
/**
* Informs this field editor's listener, if it has one, about a change
* to the value (VALUE
property) provided that the old and
* new values are different.
*
* This hook is not called when the text is initialized
* (or reset to the default value) from the preference store.
*
*/
protected void valueChanged() {
setPresentsDefaultValue(false);
boolean oldState = isValid;
refreshValidState();
if (isValid != oldState) {
fireStateChanged(IS_VALID, oldState, isValid);
}
String newValue = textField.getText();
if (!newValue.equals(oldValue)) {
fireValueChanged(VALUE, oldValue, newValue);
oldValue = newValue;
}
}
/*
* @see FieldEditor.setEnabled(boolean,Composite).
*/
@Override
public void setEnabled(boolean enabled, Composite parent) {
super.setEnabled(enabled, parent);
getTextControl(parent).setEnabled(enabled);
}
}