package au.gov.ga.earthsci.model.ui.raster.wizard; import java.util.ArrayList; import java.util.List; import org.eclipse.jface.resource.ColorRegistry; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.wizard.WizardPage; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.ScrolledComposite; import org.eclipse.swt.events.ControlAdapter; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Text; import au.gov.ga.earthsci.common.util.Util; /** * Base class for wizard pages * <p/> * Provides convenient methods for validation and data binding. * * @author James Navin (james.navin@ga.gov.au) */ public abstract class AbstractWizardPage<P> extends WizardPage { private static final Point DEFAULT_MIN_PAGE_SIZE = new Point(300, 300); private static final int GROUP_DESCRIPTION_TOP_GAP = 5; private static final String NORMAL_TEXT_COLOR = "normalText"; //$NON-NLS-1$ private static final String ERROR_TEXT_COLOR = "errorText"; //$NON-NLS-1$ /** The parameters being collected with this page */ protected final P params; private final ColorRegistry colorRegistry; private List<Control> fields = new ArrayList<Control>(); private final Listener validationListener = new Listener() { @Override public void handleEvent(Event event) { reValidate(); } }; private ScrolledComposite scroller; protected AbstractWizardPage(P params, String title, String description) { super(title); setTitle(title); setDescription(description); this.params = params; colorRegistry = new ColorRegistry(Display.getDefault(), true); colorRegistry.put(ERROR_TEXT_COLOR, Display.getDefault().getSystemColor(SWT.COLOR_RED).getRGB()); colorRegistry.put(NORMAL_TEXT_COLOR, Display.getDefault().getSystemColor(SWT.COLOR_LIST_FOREGROUND).getRGB()); } protected void reValidate() { clearValidation(); validate(); getWizard().getContainer().updateButtons(); } @Override public void createControl(Composite parent) { Composite root = new Composite(parent, SWT.NONE); root.setLayout(new GridLayout()); scroller = new ScrolledComposite(root, SWT.H_SCROLL | SWT.V_SCROLL); scroller.setLayoutData(new GridData(GridData.FILL_BOTH)); scroller.setExpandVertical(true); scroller.setExpandHorizontal(true); scroller.setMinSize(DEFAULT_MIN_PAGE_SIZE); Composite container = new Composite(scroller, SWT.NONE); container.setLayoutData(new GridData(GridData.FILL_BOTH)); container.setLayout(new GridLayout()); addContents(container); scroller.setContent(container); scroller.addControlListener(new ControlAdapter() { @Override public void controlResized(ControlEvent e) { updateScrollerMinSize(); } }); setControl(root); } private void updateScrollerMinSize() { Rectangle r = scroller.getClientArea(); Point computeSize = scroller.getContent().computeSize(r.width, SWT.DEFAULT); scroller.setMinSize(Math.max(DEFAULT_MIN_PAGE_SIZE.x, computeSize.x), Math.max(DEFAULT_MIN_PAGE_SIZE.y, computeSize.y)); } /** * Add the contents of the page to the the container. * * @param container * The container to add contents to. Has a grid layout with 1 * column. */ abstract void addContents(Composite container); /** * Add a group to the given parent container with a given title and * description. * <p/> * The returned group will have a Grid layout with 2 columns. * * @return A {@link Group} with grid layout of 2 columns. */ protected Group addGroup(String title, String description, Composite parent) { return addGroup(title, description, parent, 2, true); } /** * Add a group to the given parent container with a given title and * description. * <p/> * The returned group will have a Grid layout with n columns. * <p/> * An optional vertical spacing can be added after the group description - * recommended for "top-level" groupings. * * @return A {@link Group} with grid layout of n columns. */ protected Group addGroup(String title, String description, Composite parent, int numColumns, boolean addSpacer) { Group result = new Group(parent, SWT.SHADOW_ETCHED_IN); result.setText(title); result.setFont(JFaceResources.getFontRegistry().getBold("default")); //$NON-NLS-1$ result.setLayout(new GridLayout(numColumns, false)); GridData gd = new GridData(GridData.FILL_HORIZONTAL); gd.grabExcessHorizontalSpace = true; result.setLayoutData(gd); Label groupDescription = new Label(result, SWT.WRAP); groupDescription.setText(description); gd = new GridData(GridData.FILL_HORIZONTAL); gd.horizontalSpan = numColumns; gd.verticalIndent = GROUP_DESCRIPTION_TOP_GAP; groupDescription.setLayoutData(gd); if (addSpacer) { Label spacer = new Label(result, SWT.NONE); gd = new GridData(GridData.FILL_HORIZONTAL); gd.horizontalSpan = numColumns; spacer.setLayoutData(gd); } return result; } /** * Perform required validation on fields. * <p/> * Invalid fields should be marked as invalid using * {@link #markInvalid(Control, String)}. * */ abstract void validate(); /** * Bind collected values to the backing parameters object */ public abstract void bind(); /** * Register a field to trigger validation on change */ protected void registerField(Control field) { field.addListener(SWT.SELECTED, validationListener); fields.add(field); } /** * Remove validation state */ protected void clearValidation() { setErrorMessage(null); for (Control field : fields) { markValid(field); } } /** * Mark the given control as valid. * * @see #markInvalid(Control, String) * * @param field * The control to mark as valid */ protected void markValid(Control field) { if (field instanceof Text) { ((Text) field).setForeground(colorRegistry.get(NORMAL_TEXT_COLOR)); } } /** * Mark the given control as invalid. This will add the given message to the * list of validation messages as appropriate. * * @param field * The control to mark as invalid * @param message * The validation message to associate with the control */ protected void markInvalid(Control field, String message) { if (field instanceof Text) { ((Text) field).setForeground(colorRegistry.get(ERROR_TEXT_COLOR)); } if (getErrorMessage() == null) { setErrorMessage(message); } } @Override public boolean canFlipToNextPage() { return getNextPage() != null && getErrorMessage() == null; } @Override public boolean isPageComplete() { return getErrorMessage() == null; } protected static Double getDoubleOrNull(String text) { if (Util.isEmpty(text)) { return null; } return Double.valueOf(text); } protected static Integer getIntegerOrNull(String text) { if (Util.isEmpty(text)) { return null; } return Integer.valueOf(text); } protected static boolean isIntegerOrEmpty(String text) { if (Util.isEmpty(text)) { return true; } try { Integer.valueOf(text); return true; } catch (Exception e) { return false; } } protected static boolean isNumericOrEmpty(String text) { if (Util.isEmpty(text)) { return true; } try { Double.valueOf(text); return true; } catch (Exception e) { return false; } } }