package au.com.vaadinutils.layout;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Set;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.vaadin.data.Property;
import com.vaadin.data.Validator;
import com.vaadin.data.Validator.InvalidValueException;
import com.vaadin.server.FontAwesome;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.CssLayout;
import com.vaadin.ui.Field;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.NativeButton;
import com.vaadin.ui.TextField;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;
import com.vaadin.ui.themes.Reindeer;
@SuppressWarnings("rawtypes")
public class TimePicker extends HorizontalLayout implements Field<Date>
{
protected static final String TIME_FORMAT = "hh:mm a";
protected static final String TIME_FORMAT2 = "hh:mma";
private static final long serialVersionUID = 1826417125815798837L;
protected static final String EMPTY = "--:--";
String headerColor = "#B2D7FF";
protected TextField displayTime = new TextField();
protected Calendar dateTime = Calendar.getInstance();
boolean isSet = false;
protected ChangedHandler changedHandler;
private String title;
protected TextField field;
private Property<Date> datasource;
// private boolean isRequired;
private String requiredErrorMessage;
private int tabIndex;
private boolean isBuffered;
protected Validator timeValidator;
Set<Validator> validators = new LinkedHashSet<>();
Set<ValueChangeListener> listeners = new LinkedHashSet<>();
protected Button pickerButton;
public TimePicker(String title)
{
setCaption(title);
this.title = title;
timeValidator = new Validator()
{
private static final long serialVersionUID = 6579163030027373837L;
@Override
public void validate(Object value) throws InvalidValueException
{
if (value == null || value.equals(EMPTY))
{
return;
}
parseDate((String) value);
}
};
buildUI(title);
}
protected void buildUI(String title)
{
field = new TextField();
field.setWidth("125");
field.setImmediate(true);
displayTime.setImmediate(true);
displayTime.addValidator(timeValidator);
field.addValidator(timeValidator);
field.addValueChangeListener(new ValueChangeListener()
{
private static final long serialVersionUID = 1L;
@SuppressWarnings("deprecation")
@Override
public void valueChange(com.vaadin.data.Property.ValueChangeEvent event)
{
final Date parsedDate = parseDate((String) event.getProperty().getValue());
if (parsedDate != null)
{
dateTime.set(Calendar.HOUR_OF_DAY, parsedDate.getHours());
dateTime.set(Calendar.MINUTE, parsedDate.getMinutes());
isSet = true;
setNewValue();
}
// TimePicker.this.valueChange(event);
}
});
this.title = title;
CssLayout hl = new CssLayout();
hl.addStyleName("v-component-group");
hl.setWidth("100%");
hl.setHeight("35");
pickerButton = new Button();
pickerButton.setIcon(FontAwesome.CLOCK_O);
hl.addComponent(pickerButton);
hl.addComponent(field);
pickerButton.addClickListener(new ClickListener()
{
private static final long serialVersionUID = 1L;
@Override
public void buttonClick(ClickEvent event)
{
showPopupTimePicker();
}
});
addComponent(hl);
}
@Override
public void setReadOnly(boolean readOnly)
{
field.setReadOnly(readOnly);
pickerButton.setEnabled(!readOnly);
super.setReadOnly(readOnly);
}
protected Date parseDate(String value)
{
if (value == null || value.equals(EMPTY))
{
return null;
}
SimpleDateFormat sdf = new SimpleDateFormat(TIME_FORMAT);
try
{
return sdf.parse(value);
}
catch (ParseException e)
{
sdf = new SimpleDateFormat(TIME_FORMAT2);
try
{
return sdf.parse(value);
}
catch (ParseException e2)
{
throw new InvalidValueException("Time format is " + TIME_FORMAT);
}
}
}
@Override
public void focus()
{
super.focus();
}
@SuppressWarnings("deprecation")
private void showPopupTimePicker()
{
try
{
if (field.getValue() != null)
{
Date value = parseDate(field.getValue());
if (value != null)
{
int hourNumber = value.getHours() % 12;
if (hourNumber == 0)
{
hourNumber = 12;
}
displayTime.setValue(field.getValue());
}
}
}
catch (Exception e)
{
logger.error(e);
clearValue();
}
final Window window = new Window(title);
window.setModal(true);
window.setResizable(false);
// window.setWidth("550");
// window.setHeight("220");
window.setClosable(false);
HorizontalLayout layout = new HorizontalLayout();
layout.setSizeFull();
layout.setSpacing(true);
layout.setStyleName(Reindeer.BUTTON_SMALL);
displayTime.setWidth("100");
displayTime.addValueChangeListener(new ValueChangeListener()
{
private static final long serialVersionUID = 1L;
@Override
public void valueChange(com.vaadin.data.Property.ValueChangeEvent event)
{
final Date parsedDate = parseDate((String) event.getProperty().getValue());
if (parsedDate != null)
{
dateTime.set(Calendar.HOUR_OF_DAY, parsedDate.getHours());
dateTime.set(Calendar.MINUTE, parsedDate.getMinutes());
isSet = true;
setNewValue();
}
}
});
VerticalLayout hourPanelLabelWrapper = new VerticalLayout();
hourPanelLabelWrapper.setWidth("225");
Label hourLabel = new Label("Hour");
hourLabel.setWidth("230");
HorizontalLayout innerHourLabelPanel = new HorizontalLayout();
innerHourLabelPanel.addComponent(hourLabel);
innerHourLabelPanel.setWidth("100");
hourPanelLabelWrapper.addComponent(innerHourLabelPanel);
VerticalLayout minuteLabelWrapper = new VerticalLayout();
minuteLabelWrapper.setWidth("60");
Label minuteLabel = new Label("Minute");
minuteLabel.setWidth("45");
VerticalLayout innerMinuteLabelPanel = new VerticalLayout();
innerMinuteLabelPanel.addComponent(minuteLabel);
innerMinuteLabelPanel.setWidth("45");
minuteLabelWrapper.addComponent(innerMinuteLabelPanel);
HorizontalLayout hourPanel = new HorizontalLayout();
HorizontalLayout amPmPanel = new HorizontalLayout();
VerticalLayout amPmButtonPanel = new VerticalLayout();
amPmPanel.addComponent(amPmButtonPanel);
addAmPmButtons(amPmButtonPanel);
HorizontalLayout hourButtonPanel = new HorizontalLayout();
hourPanel.addComponent(hourButtonPanel);
addHourButtons(hourButtonPanel, 2, 6);
HorizontalLayout minutePanel = new HorizontalLayout();
HorizontalLayout minuteButtonPanel = new HorizontalLayout();
minutePanel.addComponent(minuteButtonPanel);
addMinuteButtons(minuteButtonPanel, 2, 4);
HorizontalLayout amPmHourWrapper = new HorizontalLayout();
amPmHourWrapper.addComponent(hourPanel);
amPmHourWrapper.addComponent(amPmPanel);
hourPanelLabelWrapper.addComponent(amPmHourWrapper);
layout.addComponent(hourPanelLabelWrapper);
minuteLabelWrapper.addComponent(minutePanel);
layout.addComponent(minuteLabelWrapper);
layout.setExpandRatio(minuteLabelWrapper, 1);
HorizontalLayout okcancel = new HorizontalLayout();
okcancel.setSizeFull();
Button ok = new Button("OK");
ok.setWidth("75");
ok.addClickListener(new ClickListener()
{
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
public void buttonClick(ClickEvent event)
{
field.setValue(displayTime.getValue());
window.close();
}
});
Button cancel = new Button("Cancel");
cancel.setWidth("75");
cancel.addClickListener(new ClickListener()
{
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
public void buttonClick(ClickEvent event)
{
window.close();
}
});
Button clear = new Button("Clear");
clear.setWidth("75");
clear.addClickListener(new ClickListener()
{
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
public void buttonClick(ClickEvent event)
{
isSet = false;
clearValue();
}
});
okcancel.addComponent(displayTime);
okcancel.addComponent(cancel);
okcancel.addComponent(clear);
okcancel.addComponent(ok);
okcancel.setSpacing(true);
okcancel.setExpandRatio(displayTime, 0.50f);
VerticalLayout wrapper = new VerticalLayout();
wrapper.addComponent(layout);
wrapper.addComponent(okcancel);
window.setContent(wrapper);
wrapper.setMargin(true);
wrapper.setSpacing(true);
UI.getCurrent().addWindow(window);
}
protected void addMinuteButtons(HorizontalLayout minuteButtonPanel, int rows, int cols)
{
String[] numbers = new String[] { "00", "10", "15", "20", "30", "40", "45", "50" };
for (int col = 0; col < cols; col++)
{
VerticalLayout rowsLayout = new VerticalLayout();
for (int row = 0; row < rows; row++)
{
final NativeButton button = new NativeButton("" + numbers[row + (col * rows)]);
rowsLayout.addComponent(button);
button.setStyleName(Reindeer.BUTTON_SMALL);
button.setWidth("30");
button.addClickListener(new ClickListener()
{
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
public void buttonClick(ClickEvent event)
{
String title = button.getCaption();
dateTime.set(Calendar.MINUTE, Integer.parseInt(title));
isSet = true;
setNewValue();
}
});
}
minuteButtonPanel.addComponent(rowsLayout);
}
}
protected void addHourButtons(HorizontalLayout hourButtonPanel, int rows, int cols)
{
int[] numbers = new int[] { 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
for (int col = 0; col < cols; col++)
{
VerticalLayout rowsLayout = new VerticalLayout();
for (int row = 0; row < rows; row++)
{
final NativeButton button = new NativeButton("" + numbers[col + (row * cols)]);
rowsLayout.addComponent(button);
button.setStyleName(Reindeer.BUTTON_SMALL);
button.setWidth("30");
button.addClickListener(new ClickListener()
{
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
public void buttonClick(ClickEvent event)
{
int hourToSet = Integer.parseInt(button.getCaption());
hourToSet %= 12;
if (dateTime.get(Calendar.HOUR_OF_DAY) >= 12)
{
hourToSet += 12;
}
dateTime.set(Calendar.HOUR_OF_DAY, hourToSet);
isSet = true;
setNewValue();
}
});
}
hourButtonPanel.addComponent(rowsLayout);
}
}
protected void addAmPmButtons(VerticalLayout amPmButtonPanel)
{
final NativeButton am = new NativeButton("AM");
final NativeButton pm = new NativeButton("PM");
amPmButtonPanel.addComponent(am);
amPmButtonPanel.addComponent(pm);
am.setStyleName(Reindeer.BUTTON_SMALL);
am.setWidth("35");
am.addClickListener(new ClickListener()
{
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
public void buttonClick(ClickEvent event)
{
dateTime.set(Calendar.AM_PM, Calendar.AM);
isSet = true;
setNewValue();
}
});
pm.setStyleName(Reindeer.BUTTON_SMALL);
pm.setWidth("35");
pm.addClickListener(new ClickListener()
{
private static final long serialVersionUID = 1L;
@Override
public void buttonClick(ClickEvent event)
{
dateTime.set(Calendar.AM_PM, Calendar.PM);
isSet = true;
setNewValue();
}
});
}
public void addChangedHandler(ChangedHandler pChangedHandler)
{
this.changedHandler = pChangedHandler;
}
public void clearValue()
{
dateTime.setTime(new Date());
dateTime.set(Calendar.HOUR, 0);
dateTime.set(Calendar.MINUTE, 0);
dateTime.set(Calendar.SECOND, 0);
dateTime.set(Calendar.MILLISECOND, 0);
dateTime.set(Calendar.AM_PM, Calendar.AM);
isSet = false;
displayTime.setValue(EMPTY);
internalSetReadonlyFieldValue(EMPTY);
}
protected void internalSetReadonlyFieldValue(String value)
{
boolean isRo = field.isReadOnly();
field.setReadOnly(false);
field.setValue(value);
field.setReadOnly(isRo);
}
public final String getValueAsString()
{
if (isSet)
{
int hour = dateTime.get(Calendar.HOUR);
if (hour == 0)
{
hour = 12;
}
final int minute = dateTime.get(Calendar.MINUTE);
final String amPm = dateTime.get(Calendar.HOUR_OF_DAY) < 12 ? "AM" : "PM";
DecimalFormat df = new DecimalFormat("00");
return df.format(hour) + ":" + df.format(minute) + " " + amPm;
}
return null;
}
public void setValues(Date date)
{
if (date != null)
{
dateTime.setTime(date);
isSet = true;
displayTime.setValue(getValueAsString());
internalSetReadonlyFieldValue(getValueAsString());
}
else
{
clearValue();
}
}
protected void setNewValue()
{
displayTime.setValue(getValueAsString());
internalSetReadonlyFieldValue(getValueAsString());
if (changedHandler != null)
{
changedHandler.onChanged(getValueAsString());
}
}
@Override
public boolean isInvalidCommitted()
{
return false;
}
@Override
public void setInvalidCommitted(boolean isCommitted)
{
}
@Override
public void commit() throws SourceException, InvalidValueException
{
datasource.setValue(getValue());
}
@Override
public void discard() throws SourceException
{
setValues(datasource.getValue());
}
@Override
public void setBuffered(boolean buffered)
{
this.isBuffered = buffered;
}
@Override
public boolean isBuffered()
{
return isBuffered;
}
Logger logger = LogManager.getLogger();
@Override
public boolean isModified()
{
Date value = getValue();
if (datasource == null)
{
return false;
}
Date dsValue = datasource.getValue();
if (dsValue == null)
{
boolean ret = value != null;
if (ret)
{
logger.info("Values {} and {}", dsValue, value);
}
return ret;
}
if (value == null)
{
logger.info("Values {} and {}", dsValue, value);
return true;
}
SimpleDateFormat sdf = new SimpleDateFormat("hh:mma");
String v1 = sdf.format(dsValue);
String v2 = sdf.format(value);
boolean ret = !v1.equals(v2);
if (ret)
{
logger.info("Values {} and {}", v1, v2);
}
return ret;
}
@Override
public void addValidator(Validator validator)
{
validators.add(validator);
}
@Override
public void removeValidator(Validator validator)
{
validators.remove(validator);
}
@Override
public void removeAllValidators()
{
validators.clear();
}
@Override
public Collection<Validator> getValidators()
{
Collection<Validator> validators = new LinkedList<>();
validators.add(timeValidator);
validators.addAll(this.validators);
return validators;
}
@Override
public boolean isValid()
{
boolean valid = true;
try
{
this.validate();
}
catch (Exception e)
{
valid = false;
}
return valid;
}
@Override
public void validate() throws InvalidValueException
{
timeValidator.validate(getValueAsString());
for (Validator validator : validators)
{
validator.validate(getValue());
}
}
@Override
public boolean isInvalidAllowed()
{
return false;
}
@Override
public void setInvalidAllowed(boolean invalidValueAllowed) throws UnsupportedOperationException
{
}
@Override
public Date getValue()
{
Date value = null;
if (isSet)
{
value = dateTime.getTime();
}
return value;
}
@Override
public void setValue(Date newValue)
{
setValues(newValue);
}
@Override
public Class<Date> getType()
{
return Date.class;
}
@Override
public void addValueChangeListener(ValueChangeListener listener)
{
addListener(listener);
}
@Override
public void addListener(ValueChangeListener listener)
{
listeners.add(listener);
}
@Override
public void removeValueChangeListener(ValueChangeListener listener)
{
removeListener(listener);
}
@Override
public void removeListener(ValueChangeListener listener)
{
listeners.remove(listener);
}
@Override
public void valueChange(com.vaadin.data.Property.ValueChangeEvent event)
{
for (ValueChangeListener listener : listeners)
{
listener.valueChange(event);
}
}
@SuppressWarnings("unchecked")
@Override
public void setPropertyDataSource(Property newDataSource)
{
clearValue();
datasource = newDataSource;
if (datasource != null && datasource.getValue() != null)
{
setValues(datasource.getValue());
}
}
@Override
public Property<Date> getPropertyDataSource()
{
return datasource;
}
@Override
public int getTabIndex()
{
return tabIndex;
}
@Override
public void setTabIndex(int tabIndex)
{
this.tabIndex = tabIndex;
}
@Override
public boolean isRequired()
{
return isVisible();
}
@Override
public void setRequired(boolean required)
{
// isRequired = required;
}
@Override
public void setRequiredError(String requiredMessage)
{
requiredErrorMessage = requiredMessage;
}
@Override
public String getRequiredError()
{
return requiredErrorMessage;
}
@Override
public boolean isEmpty()
{
return false;
}
@Override
public void clear()
{
}
}