
org.zkoss.zul.Timebox Maven / Gradle / Ivy
/* Timebox.java
Purpose:
Description:
History:
Jul 9, 2007 10:03:38 AM , Created by Dennis Chen
Copyright (C) 2007 Potix Corporation. All Rights Reserved.
{{IS_RIGHT
This program is distributed under LGPL Version 2.1 in the hope that
it will be useful, but WITHOUT ANY WARRANTY.
}}IS_RIGHT
*/
package org.zkoss.zul;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;
import org.zkoss.lang.Library;
import org.zkoss.lang.Objects;
import org.zkoss.lang.Strings;
import org.zkoss.text.DateFormats;
import org.zkoss.util.Locales;
import org.zkoss.util.TimeZones;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.WrongValueException;
import org.zkoss.zk.ui.sys.BooleanPropertyAccess;
import org.zkoss.zk.ui.sys.PropertyAccess;
import org.zkoss.zul.impl.DateTimeFormatInputElement;
import org.zkoss.zul.mesg.MZul;
/**
* An input box for holding a time (a java.util.Date Object), but only Hour & Minute are used.
*
* Default {@link #getZclass}: z-timebox. (since 3.5.0)
*
*
timebox supports below key events.
*
* - 0-9 : set the time digit depend on the position on the inner text box.
*
- up : increase time digit depend on the position on the inner text box.
*
- down : decrease time digit depend on the position on the inner text box.
*
- delete : clear the time to empty (null)
*
*
* Like {@link Combobox} and {@link Datebox},
* the value of a read-only time box ({@link #isReadonly}) can be changed
* by clicking the up or down button (though users cannot type anything
* in the input box).
*
*
Since 10.0.0, if cols is not set by user, it will match the formatted value at the beginning
* (or 5 when value is null).
*
* @author Dennis Chen
* @since 3.0.0
*/
public class Timebox extends DateTimeFormatInputElement {
public static final String DEFAULT_FORMAT = "HH:mm";
private boolean _btnVisible = true;
private static Date _dummyDate = new Date();
private static int DEFAULT_COLS_VALUE = Integer.MAX_VALUE;
public Timebox() {
_cols = DEFAULT_COLS_VALUE;
setFormat("");
}
public Timebox(Date date) throws WrongValueException {
this();
setValue(date);
}
public Timebox(ZonedDateTime value) throws WrongValueException {
this();
setValueInZonedDateTime(value);
}
public Timebox(LocalDateTime value) throws WrongValueException {
this();
setValueInLocalDateTime(value);
}
public Timebox(LocalTime value) throws WrongValueException {
this();
setValueInLocalTime(value);
}
/** Sets the date format.
If null or empty is specified, {@link #getDefaultFormat} is assumed.
Since 5.0.7, you could specify one of the following reserved words,
and {@link DateFormats#getTimeFormat}
will be used to retrieve the real format.
short
{@link DateFormats#getTimeFormat} with {@link DateFormat#SHORT}
medium
{@link DateFormats#getTimeFormat} with {@link DateFormat#MEDIUM}
long
{@link DateFormats#getTimeFormat} with {@link DateFormat#LONG}
full
{@link DateFormats#getTimeFormat} with {@link DateFormat#FULL}
In additions, the format could be a combination of the following pattern letters:
Letter
Date or Time Component
Presentation
Examples
*
* a
* Am/pm marker
* Text
* PM
*
* H
* Hour in day (0-23)
* Number
* 0
*
* k
* Hour in day (1-24)
* Number
* 24
*
* K
* Hour in am/pm (0-11)
* Number
* 0
*
* h
* Hour in am/pm (1-12)
* Number
* 12
*
* m
* Minute in hour
* Number
* 30
*
* s
* Second in minute
* Number
* 55
@since 5.0.0
*/
public void setFormat(String format) throws WrongValueException {
if (!Objects.equals(getFormat(), format)) {
String realformat = getRealFormat();
if (realformat.indexOf("z") != -1) {
String timezone = getFormattedTimezone();
smartUpdate("timezone", timezone);
}
super.setFormat(format != null ? format : "");
}
}
/** Returns the real format, i.e., the combination of the format patterns,
* such as hh:mm.
* As described in {@link #setFormat}, a developer could specify
* an abstract name, such as short, or an empty string as the format,
* and this method will convert it to a real time format.
* @since 5.0.7
*/
public String getRealFormat() {
final String format = getFormat();
if (format == null || format.length() == 0)
return getDefaultFormat();
int ts = Datebox.toStyle(format);
return ts != -111 ? DateFormats.getTimeFormat(ts, _locale, DEFAULT_FORMAT) : format;
}
/**
* It is meaningless to set only LocalDate in timebox.
*/
@Override
public void setValueInLocalDate(LocalDate value) throws WrongValueException {
throw new UnsupportedOperationException("need time");
}
/** Returns whether the button (on the right of the textbox) is visible.
*
Default: true.
*/
public boolean isButtonVisible() {
return _btnVisible;
}
/** Sets whether the button (on the right of the textbox) is visible.
*/
public void setButtonVisible(boolean visible) {
if (_btnVisible != visible) {
_btnVisible = visible;
smartUpdate("buttonVisible", visible);
}
}
/**
* @param constr a list of constraints separated by comma.
* Example: between 093000 and 183000, before 210000
*/
@Override
public void setConstraint(String constr) {
setConstraint(constr != null ? new SimpleLocalTimeConstraint(constr) : null);
}
/**
* Returns the default format, which is used when constructing a timebox.
*
Default: DateFormats.getTimeFormat(DEFAULT, null, "HH:mm")
* (see {@link DateFormats#getTimeFormat}).
*
*
Though you might override this method to provide your own default format,
* it is suggested to specify the format for the current thread
* with {@link DateFormats#setLocalFormatInfo}.
* @since 5.0.7
*/
protected String getDefaultFormat() {
return DateFormats.getTimeFormat(DateFormat.DEFAULT, _locale, "HH:mm");
//We use HH:mm for backward compatibility
}
protected Object coerceFromString(String value) throws WrongValueException {
//null or empty string,
if (value == null || value.length() == 0) {
return null;
}
final String fmt = getRealFormat();
final DateFormat df = getDateFormat(fmt);
final Date date;
try {
date = df.parse(value);
} catch (ParseException ex) {
throw showCustomError(new WrongValueException(this, MZul.DATE_REQUIRED, new Object[] { value, fmt }));
}
return date;
}
protected String coerceToString(Object value) {
final DateFormat df = getDateFormat(getRealFormat());
return value != null ? df.format((Date) value) : "";
}
/** Returns the date format of the time only,
*
*
Default: it uses SimpleDateFormat to format the date.
*/
protected DateFormat getDateFormat(String fmt) {
final DateFormat df = new SimpleDateFormat(fmt, _locale != null ? _locale : Locales.getCurrent());
final TimeZone tz = _tzone != null ? _tzone : TimeZones.getCurrent();
df.setTimeZone(tz);
return df;
}
private String getUnformater() {
if (org.zkoss.zk.ui.impl.Utils.markClientInfoPerDesktop(getDesktop(),
"org.zkoss.zul.Timebox.unformater.isSent")) {
return Library.getProperty("org.zkoss.zul.Timebox.unformater");
}
return null;
}
private Object[] getRealSymbols() {
if (_locale != null) {
final String localeName = _locale.toString();
if (org.zkoss.zk.ui.impl.Utils.markClientInfoPerDesktop(getDesktop(), getClass().getName() + localeName)) {
final Map map = new HashMap(2);
final Calendar cal = Calendar.getInstance(_locale);
SimpleDateFormat df = new SimpleDateFormat("a", _locale);
cal.set(Calendar.HOUR_OF_DAY, 3);
final String[] ampm = new String[2];
ampm[0] = df.format(cal.getTime());
cal.set(Calendar.HOUR_OF_DAY, 15);
ampm[1] = df.format(cal.getTime());
map.put("APM", ampm);
return new Object[] { localeName, map };
}
return new Object[] { localeName, null };
}
return null;
}
// super
public String getZclass() {
return _zclass == null ? "z-timebox" : _zclass;
}
protected void renderProperties(org.zkoss.zk.ui.sys.ContentRenderer renderer) throws java.io.IOException {
super.renderProperties(renderer);
String realformat = getRealFormat();
if (realformat.indexOf("z") != -1) {
String timezone = getFormattedTimezone();
renderer.render("timezoneAbbr", timezone);
}
if (getCols() == DEFAULT_COLS_VALUE) { // try to modify default col by value
Date value = (Date) getRawValue();
int cols = 5; // original default cols value
if (value != null) {
cols = coerceToString(value).length();
}
_cols = cols;
renderer.render("cols", getCols());
}
if (!_btnVisible)
renderer.render("buttonVisible", _btnVisible);
String unformater = getUnformater();
if (!Strings.isBlank(unformater))
renderer.render("unformater", unformater); // TODO: compress
if (_locale != null)
renderer.render("localizedSymbols", getRealSymbols());
}
private String getFormattedTimezone() {
return getDateFormat("z").format(_dummyDate);
}
//--ComponentCtrl--//
private static HashMap _properties = new HashMap(1);
static {
_properties.put("buttonVisible", new BooleanPropertyAccess() {
public void setValue(Component cmp, Boolean value) {
((Timebox) cmp).setButtonVisible(value);
}
public Boolean getValue(Component cmp) {
return ((Timebox) cmp).isButtonVisible();
}
});
}
public PropertyAccess getPropertyAccess(String prop) {
PropertyAccess pa = _properties.get(prop);
if (pa != null)
return pa;
return super.getPropertyAccess(prop);
}
}