package org.atdl4j.ui.swing.widget; import java.awt.Component; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.List; import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.xml.datatype.DatatypeConstants; import javax.xml.datatype.XMLGregorianCalendar; import org.apache.log4j.Logger; 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.atdl4j.ui.swing.SwingListener; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import com.jidesoft.spinner.DateSpinner; /** * 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 SwingJideClockWidget //3/18/2010 Scott Atwell avoid compile error "type parameter org.joda.time.DateTime is not within its bound" extends AbstractSwingWidget<DateTime> extends AbstractSwingWidget<Comparable<DateTime>> { private static final Logger logger = Logger.getLogger( SwingJideClockWidget.class ); public static boolean showEnabledButton = false; public static boolean show24HourClock = true; // model attributes /** * true is a value was set either by user input or programmatically, other than a processReinit */ private boolean valueFilledIn; /** * Default is true, but can be disabled after a call to setEnabled */ private boolean enabled = true; public boolean hasLabelOrCheckbox = false; private JCheckBox enabledButton; private JLabel label; private DateSpinner dateClock; private DateSpinner timeClock; private boolean showMonthYear; private boolean showDay; private boolean showTime; private boolean useNowAsDate = false; 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 } if (!valueFilledIn) { return null; } DateTime now = null; DateTime date = null; DateTime time = null; if (useNowAsDate) now = new DateTime( DateTimeZone.getDefault() ); if (showMonthYear) date = new DateTime(dateClock.getValue()); if (showTime) time = new DateTime(timeClock.getValue()); DateTime result = new DateTime(useNowAsDate ? now.getYear() : showMonthYear ? date.getYear() : 1970, useNowAsDate ? now.getMonthOfYear() : showMonthYear ? date.getMonthOfYear() : 1, useNowAsDate ? now.getDayOfMonth() : showDay ? date.getDayOfMonth() : 1, showTime ? time.getHourOfDay() : 0, showTime ? time.getMinuteOfHour() : 0, showTime ? time.getSecondOfMinute() : 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.setValue(tempLocalTzDateTime.toDate()); if (showTime) timeClock.setValue(tempLocalTzDateTime.toDate()); valueFilledIn = true; updateFromModel(); } public List<Component> getComponents() { List<Component> widgets = new ArrayList<Component>(); 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<Component> getComponentsExcludingLabel() { List<Component> widgets = new ArrayList<Component>(); if (showMonthYear) widgets.add(dateClock); if (showTime) widgets.add(timeClock); return widgets; } public void addListener(final SwingListener listener) { if (showMonthYear) dateClock.addChangeListener(listener); if (showTime) timeClock.addChangeListener(listener); if (enabledButton != null) enabledButton.addActionListener(listener); } public void removeListener(SwingListener listener) { if (showMonthYear) dateClock.removeChangeListener(listener); if (showTime) timeClock.removeChangeListener(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(), ( showTime && aValue.getHour() != DatatypeConstants.FIELD_UNDEFINED ) ? aValue.getHour() : 0, ( showTime && aValue.getMinute() != DatatypeConstants.FIELD_UNDEFINED ) ? aValue.getMinute() : 0, ( showTime && 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.ControlUI#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() ); valueFilledIn = (enabledButton!=null?false:true); // the editor requires a value but until the // enabledButton is checked, considere that no value is filled updateFromModel(); } } /* * */ protected void processNullValueIndicatorChange(Boolean aOldNullValueInd, Boolean aNewNullValueInd) { // TODO ?? adjust the visual appearance of the control ?? } /* (non-Javadoc) * @see org.atdl4j.ui.impl.AbstractControlUI#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 ( getAtdl4jOptions().CLOCK_PAST_TIME_SET_FIX_VALUE_RULE_USE_AS_IS.equals( tempClockPastTimeSetFIXValueRule ) ) { // -- keep as-is -- logger.debug("Per Atdl4jConfig.CLOCK_PAST_TIME_SET_FIX_VALUE_RULE_USE_AS_IS rule -- Retaining: " + tempFIXValueTime ); } else if ( getAtdl4jOptions().CLOCK_PAST_TIME_SET_FIX_VALUE_RULE_SET_TO_CURRENT.equals( tempClockPastTimeSetFIXValueRule ) ) { logger.debug("Per Atdl4jConfig.CLOCK_PAST_TIME_SET_FIX_VALUE_RULE_SET_TO_CURRENT rule -- Setting: " + tempCurrentTime + " ( vs. " + tempFIXValueTime + ")" ); setValue( tempCurrentTime ); } else if ( getAtdl4jOptions().CLOCK_PAST_TIME_SET_FIX_VALUE_RULE_SET_TO_NULL.equals( tempClockPastTimeSetFIXValueRule ) ) { logger.debug("Per Atdl4jConfig.CLOCK_PAST_TIME_SET_FIX_VALUE_RULE_SET_TO_NULL rule -- Setting control to 'null value' ( vs. " + tempFIXValueTime + ")" ); setValueAsString( Atdl4jConstants.VALUE_NULL_INDICATOR ); } } } @Override protected List< ? extends Component> createBrickComponents() { List<Component> components = new ArrayList<Component>(); // tooltip String tooltip = control.getTooltip(); 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; } if ( getAtdl4jOptions() != null && getAtdl4jOptions().isShowEnabledCheckboxOnOptionalClockControl() && parameter != null && UseT.OPTIONAL.equals( parameter.getUse() ) ) { hasLabelOrCheckbox = true; enabledButton = new JCheckBox(); enabledButton.setName(getName()+"/enablebutton"); if (control.getLabel() != null) { enabledButton.setText(control.getLabel()); } enabledButton.setToolTipText("Click to enable optional parameter"); enabledButton.setSelected(false); enabledButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { updateFromView(); } }); components.add(enabledButton); } else if (control.getLabel() != null) { // add label hasLabelOrCheckbox = true; label = new JLabel(); label.setName(getName()+"/label"); label.setText(control.getLabel()); if (tooltip != null) label.setToolTipText(tooltip); components.add(label); } // date clock if (showMonthYear) { dateClock = new DateSpinner(showDay ? "dd.MM.yyyy" : "MM.yyyy"); dateClock.setName(getName()+"/dateclock"); if (tooltip != null) dateClock.setToolTipText(tooltip); components.add(dateClock); } // time clock if (showTime) { timeClock = new DateSpinner(show24HourClock ? "HH:mm:ss" : "hh:mm:ss"); timeClock.setName(getName()+"/timeclock"); if (tooltip != null) timeClock.setToolTipText(tooltip); components.add(timeClock); } // init value, if applicable setAndRenderInitValue( (XMLGregorianCalendar ) ControlHelper.getInitValue( control, getAtdl4jOptions() ), ((ClockT) control).getInitValueMode() ); updateFromModel(); return components; } @Override public void setEnabled(boolean enabled) { this.enabled = enabled; updateFromModel(); } @Override public boolean isNullValue() { if (!valueFilledIn) { return true; } else { return super.isNullValue(); } } private void updateFromView() { if (enabledButton!=null) { valueFilledIn = enabledButton.isSelected(); } else { valueFilledIn = true; } if ((timeClock != null) && (timeClock.isVisible())) { timeClock.setEnabled(valueFilledIn && enabled); } if ((dateClock != null) && (dateClock.isVisible())) { dateClock.setEnabled(valueFilledIn && enabled); } } private void updateFromModel() { if (enabledButton != null) { enabledButton.setSelected(valueFilledIn); enabledButton.setEnabled(enabled); } if ((timeClock != null) && (timeClock.isVisible())) { timeClock.setEnabled(valueFilledIn && enabled); } if ((dateClock != null) && (dateClock.isVisible())) { dateClock.setEnabled(valueFilledIn && enabled); } } }