package org.atdl4j.ui.swt.widget;
import java.util.ArrayList;
import java.util.List;
import javax.xml.datatype.DatatypeConstants;
import javax.xml.datatype.XMLGregorianCalendar;
import org.apache.log4j.Logger;
import org.atdl4j.config.Atdl4jOptions;
import org.atdl4j.data.Atdl4jConstants;
import org.atdl4j.data.converter.DateTimeConverter;
import org.atdl4j.fixatdl.core.LocalMktDateT;
import org.atdl4j.fixatdl.core.MonthYearT;
import org.atdl4j.fixatdl.core.TZTimeOnlyT;
import org.atdl4j.fixatdl.core.TZTimestampT;
import org.atdl4j.fixatdl.core.UTCDateOnlyT;
import org.atdl4j.fixatdl.core.UTCTimeOnlyT;
import org.atdl4j.fixatdl.core.UTCTimestampT;
import org.atdl4j.fixatdl.core.UseT;
import org.atdl4j.fixatdl.layout.ClockT;
import org.atdl4j.ui.impl.ControlHelper;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
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.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Widget;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
/**
* Clock widget which will display differently depending on the parameter type
* used.
*
* UTCTimestampT and UTCTimeOnlyT display in the user's local time, but will be
* converted to/from UTC when outputting/reading FIX.
*
* LocalMktTimeT, TZTimestampT, and TZTimeOnlyT display in the user's local
* time, effectively 'what-you-see-is-what-you-get' in terms of the FIX output.
*
* MonthYearT are UTCDateOnlyT are displayed in UTC (GMT) time, effectively
* 'what-you-see-is-what-you-get' in terms of the FIX output.
*
* Note that users should not use a UTCDateOnlyT clock and a UTCDTimeOnlyT clock
* side-by-side, as this cannot account for time conversion across midnight.
*
* @author john.shields
*/
public class SWTClockWidget
extends AbstractSWTWidget<Comparable<DateTime>>
{
private static final Logger logger = Logger.getLogger( SWTClockWidget.class );
private org.eclipse.swt.widgets.DateTime dateClock;
private org.eclipse.swt.widgets.DateTime timeClock;
// -- enabledButton (for optional parameters) and label are mutually exclusive (use the one that is not null) --
private Button enabledButton;
private Label label;
private boolean showMonthYear;
private boolean showDay;
private boolean showTime;
private boolean useNowAsDate = false;
public Widget createWidget(Composite parent, int style)
{
if ( parameter instanceof UTCTimestampT || parameter instanceof TZTimestampT)
{
if (getAtdl4jOptions()==null||getAtdl4jOptions().isShowDateInputOnTimestampClockControl())
{
showMonthYear = true;
showDay = true;
} else {
showMonthYear = false;
showDay = false;
useNowAsDate = true;
}
showTime = true;
}
else if ( parameter instanceof UTCDateOnlyT || parameter instanceof LocalMktDateT )
{
showMonthYear = true;
showDay = true;
showTime = false;
}
else if ( parameter instanceof MonthYearT )
{
showMonthYear = true;
showDay = false;
showTime = false;
}
else if ( parameter == null || parameter instanceof UTCTimeOnlyT || parameter instanceof TZTimeOnlyT )
{
showMonthYear = false;
showDay = false;
showTime = true;
}
boolean hasLabelOrCheckbox = false;
if ( ( getAtdl4jOptions() != null ) &&
( getAtdl4jOptions().isShowEnabledCheckboxOnOptionalClockControl() ) &&
( parameter != null ) &&
( UseT.OPTIONAL.equals( parameter.getUse() ) ) )
{
hasLabelOrCheckbox = true;
enabledButton = new Button( parent, SWT.CHECK );
if ( control.getLabel() != null )
{
enabledButton.setText( control.getLabel() );
}
enabledButton.setToolTipText( "Check to enable and specify or uncheck to disable this optional parameter value" );
enabledButton.setSelection( false ); // TODO disabled by default ????
enabledButton.addSelectionListener( new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
applyEnabledSetting();
}
} );
}
else
// "required" -- use standard label without preceding checkbox
{
// label
hasLabelOrCheckbox = true;
label = new Label( parent, SWT.NONE );
if ( control.getLabel() != null )
label.setText( control.getLabel() );
}
Composite c = new Composite( parent, SWT.NONE );
GridLayout gridLayout = new GridLayout( 2, true );
gridLayout.horizontalSpacing = 2;
gridLayout.verticalSpacing = 0;
gridLayout.marginLeft = gridLayout.marginRight = 0;
gridLayout.marginTop = gridLayout.marginBottom = 0;
gridLayout.marginWidth = gridLayout.marginHeight = 0;
c.setLayout( gridLayout );
GridData controlGD = new GridData( SWT.FILL, SWT.FILL, false, false );
controlGD.horizontalSpan = hasLabelOrCheckbox ? 1 : 2;
c.setLayoutData(controlGD);
GridData clockGD = new GridData( SWT.FILL, SWT.FILL, false, false );
clockGD.horizontalSpan = (showMonthYear && showTime) ? 1 : 2;
// date clock
if ( showMonthYear )
{
dateClock = new org.eclipse.swt.widgets.DateTime( c, style | SWT.BORDER | SWT.DATE | ( showDay ? SWT.MEDIUM : SWT.SHORT ) );
dateClock.setLayoutData(clockGD);
}
// time clock
if ( showTime )
{
timeClock = new org.eclipse.swt.widgets.DateTime( c, style | SWT.BORDER | SWT.TIME | SWT.MEDIUM );
timeClock.setLayoutData(clockGD);
}
// tooltip
String tooltip = getTooltip();
if ( tooltip != null )
{
if ( showMonthYear )
dateClock.setToolTipText( tooltip );
if ( showTime )
timeClock.setToolTipText( tooltip );
if ( label != null )
label.setToolTipText( tooltip );
if ( enabledButton != null && tooltip != null )
enabledButton.setToolTipText( tooltip );
}
// init value, if applicable
setAndRenderInitValue( (XMLGregorianCalendar ) ControlHelper.getInitValue( control, getAtdl4jOptions() ), ((ClockT) control).getInitValueMode() );
if ( enabledButton != null )
{
applyEnabledSetting();
}
return parent;
}
public DateTimeZone getLocalMktTz()
throws IllegalArgumentException
{
// This will throw IllegalArgumentException if ID cannot be resolved
return DateTimeConverter.convertTimezoneToDateTimeZone( ((ClockT) control).getLocalMktTz() );
}
public DateTime getControlValueRaw()
{
if ( ( dateClock == null ) && ( timeClock == null ) )
{
return null; // disabled, no value to use
}
DateTime now = null;
if (useNowAsDate) now = new DateTime( DateTimeZone.getDefault() );
DateTime result = new DateTime( useNowAsDate ? now.getYear() : showMonthYear ? dateClock.getYear() : 1970,
useNowAsDate ? now.getMonthOfYear() : showMonthYear ? dateClock.getMonth() + 1 : 1,
useNowAsDate ? now.getDayOfMonth() : showDay ? dateClock.getDay() : 1,
showTime ? timeClock.getHours() : 0,
showTime ? timeClock.getMinutes() : 0,
showTime ? timeClock.getSeconds() : 0,
0,
DateTimeZone.getDefault() );
// Convert to UTC time for UTCTimestampT and UTCTimeOnlyT.
// Performing UTCDateT and MonthYearT coversion could produce an unexpected result.
// No conversion is needed for LocalMktTimeT, TZTimestampT, and TZTimeOnlyT.
if ( parameter == null || parameter instanceof UTCTimestampT || parameter instanceof UTCTimeOnlyT )
{
result = result.withZone( DateTimeZone.UTC );
logger.debug( "getControlValue() parameter: " + parameter + " result: " + result );
}
return result;
}
public void setValue(Comparable<DateTime> value)
{
// Convert to UTC time for UTCTimestampT and UTCTimeOnlyT.
// Performing UTCDateT and MonthYearT coversion could produce an unexpected result.
// No conversion is needed for LocalMktTimeT, TZTimestampT, and TZTimeOnlyT.
if ( parameter == null || parameter instanceof UTCTimestampT || parameter instanceof UTCTimeOnlyT )
{
logger.debug( "setValue() parameter: " + parameter + " value: " + value );
// -- no need to adjust DateTime --
}
// -- Force control to display time portion in local
DateTime tempLocalTzDateTime = ((DateTime)value).withZone( DateTimeZone.getDefault() );
if ( showMonthYear )
{
dateClock.setMonth( tempLocalTzDateTime.getMonthOfYear() - 1 );
dateClock.setYear( tempLocalTzDateTime.getYear() );
}
if ( showDay )
{
dateClock.setDay( tempLocalTzDateTime.getDayOfMonth() );
}
if ( showTime )
{
timeClock.setHours( tempLocalTzDateTime.getHourOfDay() );
timeClock.setMinutes( tempLocalTzDateTime.getMinuteOfHour() );
timeClock.setSeconds( tempLocalTzDateTime.getSecondOfMinute() );
}
}
public List<Control> getControls()
{
List<Control> widgets = new ArrayList<Control>();
if ( enabledButton != null )
{
widgets.add( enabledButton );
}
if ( label != null )
{
widgets.add( label );
}
if ( showMonthYear )
{
widgets.add( dateClock );
}
if ( showTime )
{
widgets.add( timeClock );
}
return widgets;
}
public List<Control> getControlsExcludingLabel()
{
List<Control> widgets = new ArrayList<Control>();
if ( showMonthYear )
{
widgets.add( dateClock );
}
if ( showTime )
{
widgets.add( timeClock );
}
return widgets;
}
private void applyEnabledSetting()
{
if ( enabledButton != null )
{
if ( ( dateClock != null ) && ( dateClock.isVisible() ) )
{
dateClock.setEnabled( enabledButton.getSelection() );
}
if ( ( timeClock != null ) && ( timeClock.isVisible() ) )
{
timeClock.setEnabled( enabledButton.getSelection() );
}
}
}
public void addListener(Listener listener)
{
if ( showMonthYear )
{
dateClock.addListener( SWT.Selection, listener );
}
if ( showTime )
{
timeClock.addListener( SWT.Selection, listener );
}
}
public void removeListener(Listener listener)
{
if ( showMonthYear )
{
dateClock.removeListener( SWT.Selection, listener );
}
if ( showTime )
{
timeClock.removeListener( SWT.Selection, listener );
}
}
/**
* Used when applying Clock@initValue (xs:time)
* @param aValue
* @param @InitValueMode
*/
protected void setAndRenderInitValue( XMLGregorianCalendar aValue, int aInitValueMode )
{
if ( aValue != null )
{
// -- Note that this will throw IllegalArgumentException if timezone ID
// specified cannot be resolved --
DateTimeZone tempLocalMktTz = getLocalMktTz();
logger.debug( "control.getID(): " + control.getID() + " aValue: " + aValue + " getLocalMktTz(): " + tempLocalMktTz );
// -- localMktTz is required when using/interpreting aValue --
if ( tempLocalMktTz == null )
{
throw new IllegalArgumentException( "localMktTz is required when aValue (" + aValue + ") is specified. (Control.ID: "
+ control.getID() + ")" );
}
DateTime tempNow = new DateTime( tempLocalMktTz );
DateTime tempInit = new DateTime(
( showMonthYear && aValue.getYear() != DatatypeConstants.FIELD_UNDEFINED ) ? aValue.getYear() : tempNow.getYear(),
( showMonthYear && aValue.getMonth() != DatatypeConstants.FIELD_UNDEFINED ) ? aValue.getMonth() : tempNow.getMonthOfYear(),
( showMonthYear && aValue.getDay() != DatatypeConstants.FIELD_UNDEFINED ) ? aValue.getDay() : tempNow.getDayOfMonth(),
( showMonthYear && aValue.getHour() != DatatypeConstants.FIELD_UNDEFINED ) ? aValue.getHour() : 0,
( showMonthYear && aValue.getMinute() != DatatypeConstants.FIELD_UNDEFINED ) ? aValue.getMinute() : 0,
( showMonthYear && aValue.getSecond() != DatatypeConstants.FIELD_UNDEFINED ) ? aValue.getSecond(): 0,
0,
tempLocalMktTz );
if ( ( aInitValueMode == Atdl4jConstants.CLOCK_INIT_VALUE_MODE_USE_CURRENT_TIME_IF_LATER ) &&
( tempNow.isAfter( tempInit ) ) )
{
// -- Use current time --
tempInit = tempNow;
}
// -- Make sure that the value is rendered on the display in local timezone --
setValue( tempInit.withZone( DateTimeZone.getDefault() ) );
}
}
/* (non-Javadoc)
* @see org.atdl4j.ui.Atdl4jWidget#reinit()
*/
@Override
public void processReinit( Object aControlInitValue )
{
if ( aControlInitValue != null )
{
// -- apply initValue if one has been specified --
setAndRenderInitValue( (XMLGregorianCalendar ) aControlInitValue, ((ClockT) control).getInitValueMode() );
}
else
{
// -- reinit the time to present time --
setValue( new DateTime() );
}
}
/*
*
*/
protected void processNullValueIndicatorChange(Boolean aOldNullValueInd, Boolean aNewNullValueInd)
{
// TODO ?? adjust the visual appearance of the control ??
}
/* (non-Javadoc)
* @see org.atdl4j.ui.impl.AbstractAtdl4jWidget#setFIXValue(java.lang.String)
*/
@Override
public void setFIXValue(String aFIXValue)
{
super.setFIXValue( aFIXValue );
DateTime tempFIXValueTime = getControlValueRaw();
DateTime tempCurrentTime = new DateTime();
// -- Check to see if the time set is < current time --
if ( tempCurrentTime.isAfter( tempFIXValueTime ) )
{
logger.debug( "setFIXValue(" + aFIXValue + ") resulted in time < present (" + tempFIXValueTime + " < " + tempCurrentTime + ")" );
Integer tempClockPastTimeSetFIXValueRule = getAtdl4jOptions().getClockPastTimeSetFIXValueRule( getControl() );
logger.debug( "Control: " + getControl().getID() + " tempClockPastTimeSetFIXValueRule: " + tempClockPastTimeSetFIXValueRule );
if ( Atdl4jOptions.CLOCK_PAST_TIME_SET_FIX_VALUE_RULE_USE_AS_IS.equals( tempClockPastTimeSetFIXValueRule ) )
{
// -- keep as-is --
logger.debug("Per Atdl4jOptions.CLOCK_PAST_TIME_SET_FIX_VALUE_RULE_USE_AS_IS rule -- Retaining: " + tempFIXValueTime );
}
else if ( Atdl4jOptions.CLOCK_PAST_TIME_SET_FIX_VALUE_RULE_SET_TO_CURRENT.equals( tempClockPastTimeSetFIXValueRule ) )
{
logger.debug("Per Atdl4jOptions.CLOCK_PAST_TIME_SET_FIX_VALUE_RULE_SET_TO_CURRENT rule -- Setting: " + tempCurrentTime + " ( vs. " + tempFIXValueTime + ")" );
setValue( tempCurrentTime );
}
else if ( Atdl4jOptions.CLOCK_PAST_TIME_SET_FIX_VALUE_RULE_SET_TO_NULL.equals( tempClockPastTimeSetFIXValueRule ) )
{
logger.debug("Per Atdl4jOptions.CLOCK_PAST_TIME_SET_FIX_VALUE_RULE_SET_TO_NULL rule -- Setting control to 'null value' ( vs. " + tempFIXValueTime + ")" );
setValueAsString( Atdl4jConstants.VALUE_NULL_INDICATOR );
}
}
}
}