/******************************************************************************* * Copyright (c) 2008 Institute for Software, HSR Hochschule fuer Technik * Rapperswil, University of applied sciences and others * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Institute for Software - initial API and implementation *******************************************************************************/ package org.eclipse.cdt.internal.ui.refactoring.dialogs; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Text; import org.eclipse.cdt.internal.ui.refactoring.utils.NameHelper; /** * @author Mirko Stocker * * Text field with a description and error handling using the Validator-Callback. Can also be used for multiple inputs. * */ public class ValidatingLabeledTextField extends Composite { private static final String EMPTY_STRING = ""; //$NON-NLS-1$ private final Map<Text, Boolean> validationStatus = new HashMap<Text, Boolean>(); private final ArrayList<Listener> inputTextListeners = new ArrayList<Listener>(); private final Color errorColor = new Color(getShell().getDisplay(), new RGB(255, 208, 196)); /** * The Validator is used for feedback about the validation status of the inputs and to validate the input. */ public static abstract class Validator { /** * Is called if all input texts contain valid input. */ public void hasErrors() {} /** * Is called if any input text contains invalid input. */ public void hasNoErrors() {} /** * @param text the new value of the field * @return whether the value is valid or not */ public boolean isValidInput(String text) { return true; } public String errorMessageForEmptyField() { return Messages.ValidatingLabeledTextField_CantBeEmpty; } public String errorMessageForInvalidInput() { return Messages.ValidatingLabeledTextField_InvalidCharacters; } public String errorMessageForDuplicateValues() { return Messages.ValidatingLabeledTextField_DuplicatedNames; } public String errorIsKeywordMessage() { return Messages.ValidatingLabeledTextField_IsKeyword; } } public ValidatingLabeledTextField(Composite parent, int style) { super(parent, style); GridLayout gridLayout = new GridLayout(); gridLayout.numColumns = 4; setLayout(gridLayout); } public ValidatingLabeledTextField(Composite parent) { this(parent, SWT.NONE); } public void addElement(String description, String initialText, boolean readOnly, final Validator validator) { Label label = new Label(this, SWT.NONE); label.setText(description); label.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING)); final Text textField = new Text(this, SWT.BORDER |SWT.SINGLE); textField.setText(initialText); textField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); if(readOnly) { //readOnly inputs are always valid: validationStatus.put(textField, Boolean.TRUE); textField.setEnabled(false); return; } validationStatus.put(textField, Boolean.FALSE); final Label errorImageLabel = new Label(this, SWT.NONE); errorImageLabel.setImage(JFaceResources.getImage(Dialog.DLG_IMG_MESSAGE_ERROR)); errorImageLabel.setLayoutData(new GridData()); errorImageLabel.setVisible(false); final Label errorLabel = new Label(this, SWT.NONE); errorLabel.setLayoutData(new GridData()); final Color defaultColor = textField.getBackground(); Listener listener = new Listener(){ @SuppressWarnings("unchecked") public void checkField() { String newName = textField.getText(); boolean isEmpty = (newName.length() == 0); boolean isNameAlreadyInUse = nameAlreadyInUse(textField, newName); boolean isValid = validator.isValidInput(newName); boolean isKeyword = NameHelper.isKeyword(newName); boolean isValidName = NameHelper.isValidLocalVariableName(newName); boolean isOk = isValid && !isNameAlreadyInUse && !isEmpty && !isKeyword && isValidName; if (isOk) { setErrorStatus(EMPTY_STRING); } else if (isEmpty) { setErrorStatus(validator.errorMessageForEmptyField()); } else if (!isValid || !isValidName) { setErrorStatus(validator.errorMessageForInvalidInput()); } else if (isKeyword) { setErrorStatus(validator.errorIsKeywordMessage()); }else if (isNameAlreadyInUse) { setErrorStatus(validator.errorMessageForDuplicateValues()); } validationStatus.put(textField, isOk); if(validationStatus.values().contains(Boolean.FALSE) || isEmpty || isNameAlreadyInUse || isKeyword || !isValidName) { validator.hasErrors(); } else { validator.hasNoErrors(); } layout(); // recheck all other listeners in case duplicate names have been resolved, // but remove this first to avoid an infinite loop inputTextListeners.remove(this); for(Listener listener : (ArrayList<Listener>) inputTextListeners.clone()) { listener.handleEvent(null); } inputTextListeners.add(this); } private boolean nameAlreadyInUse(final Text textField, String newName) { for (Text text : validationStatus.keySet()) { if(text != textField && text.getText().equals(newName)) { return true; } } return false; } private void setErrorStatus(String errorMessage) { if (EMPTY_STRING.equals(errorMessage)) { textField.setBackground(defaultColor); errorLabel.setText(EMPTY_STRING); errorImageLabel.setVisible(false); } else { textField.setBackground(errorColor); errorLabel.setText(errorMessage); errorImageLabel.setVisible(true); } } public void handleEvent(Event event) { checkField(); }}; //we need to keep a list of all listeners so we get access from other textfields to resolve duplicate names inputTextListeners.add(listener); listener.handleEvent(null); textField.addListener(SWT.Modify, listener); } }