/******************************************************************************* * Copyright (c) 2013 RelationWare, Benno Luthiger * 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: * RelationWare, Benno Luthiger ******************************************************************************/ package org.ripla.rap.util; import org.eclipse.core.databinding.observable.Realm; import org.eclipse.core.databinding.validation.IValidator; import org.eclipse.core.databinding.validation.ValidationStatus; import org.eclipse.core.runtime.IStatus; import org.eclipse.jface.databinding.swt.SWTObservables; import org.eclipse.rap.rwt.RWT; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CCombo; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Text; import org.ripla.rap.Activator; /** * Base class for ripla forms. * <p> * Concreted subclasses have to create the form in the method * <code>createControls()</code>. <br/> * This class creates a <code>GridLayout</code> to place the <code>SWT</code> * input widgets. Implementor classes can use the helper methods * <code>createLabel()</code>, <code>createLabelRequired()</code>, * <code>createText()</code> or <code>createCombo()</code> to create the * widgets. * </p> * <p> * Usage example: * * <pre> * private static class MyForm extends AbstractRiplaForm { * // fields, constructor etc. * @Override * protected void createControls() { * // create widgets here * } * } * * final MyForm myForm = new MyForm(parent); * myForm.create(); // creates the form with the input widgets on the parent * // composite * Button save = myForm.createFormProcessButton("Save"); * save.addSelectionListener(new SelectionAdapter() { * public void widgetSelected(final SelectionEvent inEvent) { * if (myForm.checkStatus()) { * // save input * } else { * System.out.println(myForm.getMessages()); * } * }; * }); * </pre> * * Forms using this base class are best used with <code>DataBindingHelper</code> * for input validation. E.g. the create a <code>DataBindingHelper</code> * instance and delegate the form's methods <code>checkStatus()</code> and * <code>getMessages()</code> to <code>DataBindingHelper.checkStatus()</code> * and <code>DataBindingHelper.getStatusMsg()</code> respectively. * </p> * * @author Luthiger * @see DataBindingHelper */ public abstract class AbstractRiplaForm { private static final String STYLE_LABEL = "ripla-label"; //$NON-NLS-1$ private static final String STYLE_EMPH = "ripla-label-emphasized"; //$NON-NLS-1$ private final Composite form; private final GridLayout layout; /** * AbstractRiplaForm constructor. * * @param inParent * {@link Composite} * @param inWidth * int the width of the form */ public AbstractRiplaForm(final Composite inParent, final int inWidth) { form = new Composite(inParent, SWT.NONE); layout = new GridLayout(); layout.numColumns = 2; form.setLayout(layout); final GridData lLayoutData = GridLayoutHelper.createFillLayoutData(); // lLayoutData.widthHint = inWidth; lLayoutData.grabExcessHorizontalSpace = false; form.setLayoutData(lLayoutData); } /** * Sets the style of the composite containing the form widgets. * * @param inStyleName * String the <code>RWT.CUSTOM_VARIANT</code> CSS style */ protected final void setStyle(final String inStyleName) { form.setData(RWT.CUSTOM_VARIANT, inStyleName); } /** * Set the number of columns in the <code>GridLayout</code>. * * @param inColumns * int the number of columns */ protected void setNumColums(final int inColumns) { layout.numColumns = inColumns; } /** * Creates the form on the composite passed in the constructor. */ public final void create() { Realm.runWithDefault(SWTObservables.getRealm(form.getDisplay()), new Runnable() { @Override public void run() { createControls(); } }); } /** * Classes implementing a concrete form have to create the input controls in * this method. */ protected abstract void createControls(); protected Composite getBody() { return form; } /** * Creates a <code>GridData</code> to span a widget over the specified * number of columns. Usage: * * <pre> * myWidget.setLayoutData(getColSpanData(2, 300)); * </pre> * * @param inColSpan * int the col span * @param inWidth * int the minimum width of the widget * @return {@link GridData} */ protected GridData getColSpanData(final int inColSpan, final int inWidth) { final GridData out = new GridData(SWT.FILL, SWT.TOP, true, false); out.horizontalSpan = inColSpan; out.minimumWidth = inWidth; out.widthHint = inWidth; return out; } private Label createLbl(final String inLabel) { final Label out = new Label(form, SWT.NONE); out.setText(inLabel); return out; } /** * Creates a label for a normal widget. * * @param inLabel * String the label text * @return {@link Label} the widget instance */ protected Label createLabel(final String inLabel) { final Label out = createLbl(inLabel); out.setData(RWT.CUSTOM_VARIANT, STYLE_LABEL); return out; } protected Label createLabel(final String inLabel, final int inColSpan) { final Label out = createLbl(inLabel); out.setData(RWT.CUSTOM_VARIANT, STYLE_LABEL); out.setLayoutData(getColSpanData(inColSpan, 0)); return out; } /** * Creates a label for a widget with required input. This label is displayed * bold. * * @param inLabel * String the label text * @return {@link Label} the widget instance */ protected Label createLabelRequired(final String inLabel) { final Label out = createLbl(inLabel); out.setData(RWT.CUSTOM_VARIANT, STYLE_EMPH); return out; } /** * Creates a combo for the specified items that spans the specified number * of columns. * * @param inItems * String[] the combo's selection list * @param inColSpan * int the combo's column span * @param inWidth * int the combo's minimum width * @return {@link CCombo} the widget instance */ protected CCombo createCombo(final String[] inItems, final int inColSpan, final int inWidth) { final CCombo out = new CCombo(getBody(), SWT.BORDER); out.setItems(inItems); out.setLayoutData(getColSpanData(inColSpan, inWidth)); return out; } /** * Creates a text input widget that spans the specified number of columns. * The style bits are set to <code>SWT.BORDER | SWT.SINGLE</code>. * * @param inValue * String the widgets initial value * @param inColSpan * int the combo's column span * @param inWidth * int the combo's minimum width * @return {@link Text} the widget instance */ protected Text createText(final String inValue, final int inColSpan, final int inWidth) { return createText(inValue, inColSpan, inWidth, SWT.BORDER | SWT.SINGLE); } /** * Creates a text input widget that spans the specified number of columns. * * @param inValue * String the widgets initial value * @param inColSpan * int the combo's column span * @param inWidth * int the combo's minimum width * @param inStyle * int the style bits, e.g. * <code>SWT.BORDER | SWT.SINGLE | SWT.PASSWORD</code> * @return {@link Text} */ protected Text createText(final String inValue, final int inColSpan, final int inWidth, final int inStyle) { final Text out = new Text(getBody(), inStyle); out.setLayoutData(getColSpanData(inColSpan, inWidth)); return out; } /** * Creates a validator for a mandatory field. * * @param inLabel * String the label of the required field * @return {@link IValidator} */ protected IValidator createRequiredValidator(final String inLabel) { return new RequiredValidator(inLabel); } /** * Creates the button to process the form and places it beyond the form. * * @param inLabel * String the button's label, e.g. <code>save</code> * @return {@link Button} */ public Button createFormProcessButton(final String inLabel) { new Label(form, SWT.NONE); // an empty label to fill the first column final Button out = new Button(form, SWT.PUSH); out.setText(inLabel); final GridData lData = new GridData(SWT.BEGINNING, SWT.TOP, false, false); lData.horizontalSpan = 2; out.setLayoutData(lData); return out; } // --- /** * A validator for a required field that can be extended by additional * validators. These validators are checked if the input is not empty and * before this validator returns <code>OK</code>. * <p> * Extend this class and provide additional validators through the extended * class' constructor. * </p> * * @author Luthiger */ public static class RequiredValidator implements IValidator { private final String label; private final IValidator[] additional; /** * RequiredValidator constructor. * * @param inLabel * String the input field's label * @param inAdditional * {@link IValidator}[] array of additional validators, may * be empty */ protected RequiredValidator(final String inLabel, final IValidator... inAdditional) { label = inLabel; additional = inAdditional; } @Override public IStatus validate(final Object inValue) { if (inValue != null) { if (inValue.toString().trim().length() > 0) { for (final IValidator lValidator : additional) { final IStatus lStatus = lValidator.validate(inValue); if (!lStatus.isOK()) { return lStatus; } } return ValidationStatus.ok(); } } return ValidationStatus.warning(Activator.getMessages() .getFormattedMessage("errmsg.error.not.empty", label)); } } }