/******************************************************************************* * Copyright (c) 2007, 2014 compeople AG 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: * compeople AG - initial API and implementation *******************************************************************************/ package org.eclipse.riena.internal.ui.ridgets.swt; import java.text.SimpleDateFormat; import java.util.Date; import org.eclipse.core.databinding.Binding; import org.eclipse.core.databinding.DataBindingContext; import org.eclipse.core.databinding.UpdateValueStrategy; import org.eclipse.core.databinding.beans.BeansObservables; import org.eclipse.core.databinding.beans.PojoObservables; import org.eclipse.core.databinding.conversion.IConverter; import org.eclipse.core.databinding.observable.value.DateAndTimeObservableValue; import org.eclipse.core.databinding.observable.value.IObservableValue; import org.eclipse.core.databinding.observable.value.WritableValue; import org.eclipse.core.databinding.validation.IValidator; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.IStatus; import org.eclipse.jface.databinding.swt.WidgetProperties; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.DateTime; import org.eclipse.riena.ui.ridgets.IDateTimeRidget; import org.eclipse.riena.ui.ridgets.swt.AbstractEditableRidget; import org.eclipse.riena.ui.ridgets.swt.AbstractSWTRidget; /** * Ridget for {@link DateTime} widgets. */ public class DateTimeRidget extends AbstractEditableRidget implements IDateTimeRidget { /** * This property is used by the databinding to sync ridget and model. It is always fired before its sibling {@link IDateTimeRidget#PROPERTY_DATE} to ensure * that the model is updated before any listeners try accessing it. * <p> * This property is not API. Do not use in client code. */ private static final String PROPERTY_DATE_INTERNAL = "dateInternal"; //$NON-NLS-1$ private Date date; private DataBindingContext dbc; private Binding controlBinding; @Override protected void checkUIControl(final Object uiControl) { checkType(uiControl, DateTime.class); } @Override protected void bindUIControl() { final DateTime control = getUIControl(); if (control != null) { dbc = new DataBindingContext(); final IObservableValue timeObservable; final IObservableValue dateObservable; final Date nonNullDate = getNonNullDate(getDate()); if (isTimeControl(control)) { // it is a time widget timeObservable = WidgetProperties.selection().observe(control); timeObservable.setValue(nonNullDate); dateObservable = new WritableValue(timeObservable.getRealm(), nonNullDate, Date.class); } else { // it is date/calendar widget dateObservable = WidgetProperties.selection().observe(control); dateObservable.setValue(nonNullDate); timeObservable = new WritableValue(dateObservable.getRealm(), nonNullDate, Date.class); } controlBinding = dbc.bindValue(new DateAndTimeObservableWithNullConversion(dateObservable, timeObservable), getRidgetObservable(), new UpdateValueStrategy(UpdateValueStrategy.POLICY_UPDATE).setAfterGetValidator(new EditRulesValidator()), new UpdateValueStrategy(UpdateValueStrategy.POLICY_ON_REQUEST)); } } @Override protected void unbindUIControl() { super.unbindUIControl(); if (dbc != null) { dbc.dispose(); dbc = null; } } @Override protected final IObservableValue getRidgetObservable() { return BeansObservables.observeValue(this, PROPERTY_DATE_INTERNAL); } @Override public DateTime getUIControl() { return (DateTime) super.getUIControl(); } @Override public void bindToModel(final IObservableValue observableValue) { unbindUIControl(); Assert.isNotNull(observableValue); super.bindToModel(observableValue); bindUIControl(); } @Override public void bindToModel(final Object valueHolder, final String valuePropertyName) { if (AbstractSWTRidget.isBean(valueHolder.getClass())) { bindToModel(BeansObservables.observeValue(valueHolder, valuePropertyName)); } else { bindToModel(PojoObservables.observeValue(valueHolder, valuePropertyName)); } } public Date getDate() { return date; } /** * This method is not API. Do not use in client code. * * @noreference This method is not intended to be referenced by clients. */ public final Date getDateInternal() { return getDate(); } public String getText() { final Date date = getDate(); return date != null ? SimpleDateFormat.getInstance().format(date) : ""; //$NON-NLS-1$ } public boolean isDirectWriting() { return true; } @Override public boolean isDisableMandatoryMarker() { return true; } @Override public boolean isEnabled() { return super.isEnabled() && !isOutputOnly(); } public boolean revalidate() { final Date date = getDate(); final IStatus onUpdate = checkOnUpdateRules(date, new ValidationCallback(false)); if (onUpdate.isOK()) { getValueBindingSupport().updateFromTarget(); } return !isErrorMarked(); } /** * {@inheritDoc} * <p> * Implementation note: since the underlying DateTime widget cannot be empty, a {@code null} date value will cause the widget to show the 'empty' date, but * getDate() will correctly return null. * <p> * Invoking this method will copy the given date into the ridget and the widget regardless of the validation outcome. If the date does not pass validation * the error marker will be set and the date will <b>not</b> be copied into the model. If validation passes the date will be copied into the model as well. * <p> * Because of limitations of the underlying SWT {@link DateTime} widget, the timestamp will be formatted according to the date/time format of the operating * system. See <a href="http://bugs.eclipse.org/248075">Bug #248075</a>. */ public void setDate(final Date date) { if (isChanged(this.date, date)) { final Object oldDate = this.date; // date is mutable, store / send out copies to be safe this.date = date == null ? null : new Date(date.getTime()); final Date newDate = date == null ? null : new Date(date.getTime()); firePropertyChange(PROPERTY_DATE_INTERNAL, oldDate, newDate); firePropertyChange(IDateTimeRidget.PROPERTY_DATE, oldDate, newDate); if (controlBinding != null) { controlBinding.updateModelToTarget(); // update widget } final IStatus status = checkAllRules(date, new ValidationCallback(false)); if (status.isOK()) { getValueBindingSupport().updateFromTarget(); } } } /** * This method is not API. Do not use in client code. * * @noreference This method is not intended to be referenced by clients. */ public final void setDateInternal(final Date date) { setDate(date); } /** Not supported. */ public void setDirectWriting(final boolean directWriting) { throw new UnsupportedOperationException(); } /** Not supported. */ public void setInputToUIControlConverter(final IConverter converter) { throw new UnsupportedOperationException(); } /** Not supported. */ public void setText(final String text) { throw new UnsupportedOperationException(); } @Override public void updateFromModel() { super.updateFromModel(); if (controlBinding != null) { controlBinding.updateModelToTarget(); // updateWidget } checkAllRules(getDate(), new ValidationCallback(false)); } // helping methods ////////////////// /** * Return {@code date} if non-null, otherwise return the 'empty' date value. * * @return {@code date} or new Date instance */ private Date getNonNullDate(final Date date) { return date != null ? date : new Date(0); } private boolean isChanged(final Date date1, final Date date2) { if (date1 == date2) { return false; } return date1 != null ? !date1.equals(date2) : !date2.equals(date1); } private boolean isTimeControl(final DateTime control) { return (control.getStyle() & SWT.TIME) != 0; } // helping classes ////////////////// /** * DateAndTimeObservable that handles doSetValue(null) gracefully. */ private final class DateAndTimeObservableWithNullConversion extends DateAndTimeObservableValue { public DateAndTimeObservableWithNullConversion(final IObservableValue dateObservable, final IObservableValue timeObservable) { super(dateObservable, timeObservable); } @Override protected void doSetValue(final Object value) { super.doSetValue(getNonNullDate((Date) value)); } } /** * Validator that delegates to the 'on edit' validators for this ridget. */ private final class EditRulesValidator implements IValidator { public IStatus validate(final Object value) { return checkOnEditRules(value, new ValidationCallback(true)); } } /* * (non-Javadoc) * * @see org.eclipse.riena.ui.ridgets.ITextRidget#setMultilineIgnoreEnterKey(boolean) */ @Override public void setMultilineIgnoreEnterKey(final boolean multilineIgnoreEnterKey) { // this setting has no effect on date text fields } /* * (non-Javadoc) * * @see org.eclipse.riena.ui.ridgets.ITextRidget#isMultilineIgnoreEnterKey() */ @Override public boolean isMultilineIgnoreEnterKey() { // this setting has no effect on date text fields return false; } }