mirror of
https://gitee.com/jmix/cuba.git
synced 2024-12-02 19:27:57 +08:00
parent
b47218b96f
commit
471e84d16a
@ -113,6 +113,10 @@ public class DateTimeUtils {
|
||||
* @return the local date-time, not null
|
||||
*/
|
||||
public static LocalDateTime asLocalDateTime(Date date, ZoneId zoneId) {
|
||||
if (date instanceof java.sql.Date) {
|
||||
LocalDate localDate = ((java.sql.Date) date).toLocalDate();
|
||||
return LocalDateTime.of(localDate, LocalTime.MIDNIGHT);
|
||||
}
|
||||
return date.toInstant().atZone(zoneId).toLocalDateTime();
|
||||
}
|
||||
|
||||
@ -213,13 +217,45 @@ public class DateTimeUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a date with zero time.
|
||||
* Returns a date without time.
|
||||
*
|
||||
* @param date the date object, not null
|
||||
* @return the date, not null
|
||||
*/
|
||||
public static Date getDateWithoutTime(Date date) {
|
||||
return asDate(asLocalDate(date));
|
||||
public static Date extractDate(Date date) {
|
||||
return extractDate(date, getDefaultTimeZone());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a date without time.
|
||||
*
|
||||
* @param date the date object, not null
|
||||
* @param zoneId the time zone id, not null
|
||||
* @return the date, not null
|
||||
*/
|
||||
public static Date extractDate(Date date, ZoneId zoneId) {
|
||||
return asDate(asLocalDate(date, zoneId), zoneId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a date with "zero" date.
|
||||
*
|
||||
* @param date the date object, not null
|
||||
* @return the date, not null
|
||||
*/
|
||||
public static Date extractTime(Date date) {
|
||||
return extractTime(date, getDefaultTimeZone());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a date with "zero" date.
|
||||
*
|
||||
* @param date the date object, not null
|
||||
* @param zoneId the time zone id, not null
|
||||
* @return the date, not null
|
||||
*/
|
||||
public static Date extractTime(Date date, ZoneId zoneId) {
|
||||
return asDate(asLocalTime(date, zoneId), zoneId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -87,7 +87,7 @@ org.javassist/javassist = 3.22.0-GA
|
||||
org.hibernate/hibernate-validator = 5.4.2.Final
|
||||
org.glassfish.web/javax.el = 2.2.6
|
||||
|
||||
com.vaadin = 8.4.3-0-cuba
|
||||
com.vaadin = 8.4.3-1-cuba
|
||||
com.vaadin/vaadin-server = ${com.vaadin}
|
||||
com.vaadin/vaadin-client = ${com.vaadin}
|
||||
com.vaadin/vaadin-client-compiler = ${com.vaadin}
|
||||
|
@ -21,7 +21,32 @@ import java.util.Date;
|
||||
public interface TimeField extends Field<Date>, Buffered, Component.Focusable {
|
||||
String NAME = "timeField";
|
||||
|
||||
enum Resolution {
|
||||
SEC,
|
||||
MIN,
|
||||
HOUR
|
||||
}
|
||||
|
||||
/**
|
||||
* Return resolution of the TimeField.
|
||||
*
|
||||
* @return Resolution
|
||||
*/
|
||||
Resolution getResolution();
|
||||
/**
|
||||
* Set resolution of the TimeField.
|
||||
*
|
||||
* @param resolution resolution
|
||||
*/
|
||||
void setResolution(Resolution resolution);
|
||||
|
||||
@Deprecated
|
||||
boolean getShowSeconds();
|
||||
|
||||
/**
|
||||
* @deprecated Use either {@link #setResolution(Resolution)} or {@link #setFormat(String)}
|
||||
*/
|
||||
@Deprecated
|
||||
void setShowSeconds(boolean showSeconds);
|
||||
|
||||
String getFormat();
|
||||
|
@ -17,9 +17,13 @@
|
||||
package com.haulmont.cuba.gui.components.data;
|
||||
|
||||
import com.haulmont.chile.core.model.MetaProperty;
|
||||
import com.haulmont.cuba.core.entity.annotation.CaseConversion;
|
||||
import com.haulmont.cuba.core.entity.annotation.ConversionType;
|
||||
import com.haulmont.cuba.core.global.MessageTools;
|
||||
import com.haulmont.cuba.core.global.MetadataTools;
|
||||
import com.haulmont.cuba.core.global.TimeSource;
|
||||
import com.haulmont.cuba.core.global.UserSessionSource;
|
||||
import com.haulmont.cuba.gui.components.DateField;
|
||||
import com.haulmont.cuba.gui.components.HasRange;
|
||||
import com.haulmont.cuba.gui.components.TextInputField;
|
||||
import org.apache.commons.collections4.MapUtils;
|
||||
@ -27,6 +31,7 @@ import org.hibernate.validator.constraints.Length;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.persistence.TemporalType;
|
||||
import javax.validation.constraints.Future;
|
||||
import javax.validation.constraints.Past;
|
||||
import javax.validation.constraints.Size;
|
||||
@ -45,6 +50,9 @@ public class DataAwareComponentsTools {
|
||||
@Inject
|
||||
protected UserSessionSource sessionSource;
|
||||
|
||||
@Inject
|
||||
protected MessageTools messageTools;
|
||||
|
||||
@Inject
|
||||
protected TimeSource timeSource;
|
||||
|
||||
@ -58,7 +66,7 @@ public class DataAwareComponentsTools {
|
||||
MetaProperty metaProperty = valueSource.getMetaPropertyPath().getMetaProperty();
|
||||
Map<String, Object> annotations = metaProperty.getAnnotations();
|
||||
|
||||
String caseConversionAnnotation = com.haulmont.cuba.core.entity.annotation.CaseConversion.class.getName();
|
||||
String caseConversionAnnotation = CaseConversion.class.getName();
|
||||
//noinspection unchecked
|
||||
Map<String, Object> caseConversion = (Map<String, Object>) annotations.get(caseConversionAnnotation);
|
||||
if (MapUtils.isNotEmpty(caseConversion)) {
|
||||
@ -98,6 +106,7 @@ public class DataAwareComponentsTools {
|
||||
public void setupDateRange(HasRange component, EntityValueSource valueSource) {
|
||||
MetaProperty metaProperty = valueSource.getMetaPropertyPath().getMetaProperty();
|
||||
|
||||
// FIXME: gg, replace with LocalDateTime?
|
||||
if (metaProperty.getAnnotations().get(Past.class.getName()) != null) {
|
||||
Date currentTimestamp = timeSource.currentTimestamp();
|
||||
|
||||
@ -123,4 +132,23 @@ public class DataAwareComponentsTools {
|
||||
component.setRangeStart(calendar.getTime());
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: gg, extract to interfaces?
|
||||
public void setupDateFormat(DateField component, EntityValueSource valueSource) {
|
||||
MetaProperty metaProperty = valueSource.getMetaPropertyPath().getMetaProperty();
|
||||
|
||||
TemporalType tt = null;
|
||||
if (metaProperty.getRange().asDatatype().getJavaClass().equals(java.sql.Date.class)) {
|
||||
tt = TemporalType.DATE;
|
||||
} else if (metaProperty.getAnnotations() != null) {
|
||||
tt = (TemporalType) metaProperty.getAnnotations().get(MetadataTools.TEMPORAL_ANN_NAME);
|
||||
}
|
||||
|
||||
component.setResolution(tt == TemporalType.DATE
|
||||
? DateField.Resolution.DAY
|
||||
: DateField.Resolution.MIN);
|
||||
|
||||
String formatStr = messageTools.getDefaultDateFormat(tt);
|
||||
component.setDateFormat(formatStr);
|
||||
}
|
||||
}
|
@ -115,6 +115,14 @@
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="timeFieldResolution">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="SEC"/>
|
||||
<xs:enumeration value="MIN"/>
|
||||
<xs:enumeration value="HOUR"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="tokenListType">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="LOOKUP"/>
|
||||
@ -1476,6 +1484,7 @@
|
||||
</xs:sequence>
|
||||
<xs:attribute name="showSeconds" type="xs:boolean"/>
|
||||
<xs:attribute name="timeFormat" type="xs:string"/>
|
||||
<xs:attribute name="resolution" type="timeFieldResolution"/>
|
||||
|
||||
<xs:attributeGroup ref="hasBuffered"/>
|
||||
<xs:attributeGroup ref="hasTabIndex"/>
|
||||
|
@ -33,6 +33,12 @@ public class TimeFieldLoader extends AbstractFieldLoader<TimeField> {
|
||||
public void loadComponent() {
|
||||
super.loadComponent();
|
||||
|
||||
final String resolution = element.attributeValue("resolution");
|
||||
if (StringUtils.isNotEmpty(resolution)) {
|
||||
TimeField.Resolution res = TimeField.Resolution.valueOf(resolution);
|
||||
resultComponent.setResolution(res);
|
||||
}
|
||||
|
||||
String timeFormat = element.attributeValue("timeFormat");
|
||||
if (StringUtils.isNotEmpty(timeFormat)) {
|
||||
timeFormat = loadResourceString(timeFormat);
|
||||
|
@ -18,11 +18,8 @@
|
||||
package com.haulmont.cuba.web.widgets.client.datefield;
|
||||
|
||||
import com.haulmont.cuba.web.widgets.CubaDateField;
|
||||
import com.vaadin.client.ApplicationConnection;
|
||||
import com.vaadin.client.UIDL;
|
||||
import com.vaadin.client.communication.StateChangeEvent;
|
||||
import com.vaadin.client.ui.ShortcutActionHandler;
|
||||
import com.vaadin.v7.client.ui.datefield.PopupDateFieldConnector;
|
||||
import com.vaadin.client.ui.datefield.PopupDateFieldConnector;
|
||||
import com.vaadin.shared.ui.Connect;
|
||||
|
||||
@Connect(CubaDateField.class)
|
||||
@ -40,11 +37,12 @@ public class CubaDateFieldConnector extends PopupDateFieldConnector {
|
||||
|
||||
@Override
|
||||
public void onStateChanged(StateChangeEvent stateChangeEvent) {
|
||||
super.onStateChanged(stateChangeEvent);
|
||||
getWidget().getImpl().setMask(getState().dateMask);
|
||||
super.onStateChanged(stateChangeEvent);
|
||||
}
|
||||
|
||||
@Override
|
||||
// VAADIN8: gg, how to replace?
|
||||
/*@Override
|
||||
public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
|
||||
super.updateFromUIDL(uidl, client);
|
||||
|
||||
@ -63,5 +61,5 @@ public class CubaDateFieldConnector extends PopupDateFieldConnector {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
@ -21,7 +21,7 @@ import com.google.gwt.user.client.DOM;
|
||||
import com.google.gwt.user.client.Event;
|
||||
import com.haulmont.cuba.web.widgets.client.textfield.CubaMaskedFieldWidget;
|
||||
import com.vaadin.client.ui.ShortcutActionHandler;
|
||||
import com.vaadin.v7.client.ui.VPopupCalendar;
|
||||
import com.vaadin.client.ui.VPopupCalendar;
|
||||
|
||||
public class CubaDateFieldWidget extends VPopupCalendar implements ShortcutActionHandler.ShortcutActionHandlerOwner {
|
||||
|
||||
@ -54,8 +54,7 @@ public class CubaDateFieldWidget extends VPopupCalendar implements ShortcutActio
|
||||
super.buildDate(forceValid);
|
||||
// Update valueBeforeEdit and send onChange
|
||||
// in case of selecting date using Calendar popup
|
||||
// vaadin8
|
||||
// getImpl().valueChange(false);
|
||||
getImpl().valueChange(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -63,10 +62,10 @@ public class CubaDateFieldWidget extends VPopupCalendar implements ShortcutActio
|
||||
return (CubaMaskedFieldWidget) super.getImpl();
|
||||
}
|
||||
|
||||
// vaadin8
|
||||
/*@Override
|
||||
@Override
|
||||
protected CubaMaskedFieldWidget createImpl() {
|
||||
CubaMaskedFieldWidget cubaMaskedFieldWidget = new CubaMaskedFieldWidget() {
|
||||
|
||||
return new CubaMaskedFieldWidget() {
|
||||
|
||||
@Override
|
||||
protected boolean validateText(String text) {
|
||||
@ -90,7 +89,7 @@ public class CubaDateFieldWidget extends VPopupCalendar implements ShortcutActio
|
||||
@Override
|
||||
public void valueChange(boolean blurred) {
|
||||
String newText = getText();
|
||||
if (!prompting && newText != null
|
||||
if (newText != null
|
||||
&& !newText.equals(valueBeforeEdit)) {
|
||||
if (validateText(newText)) {
|
||||
if (!newText.equals(nullRepresentation)) {
|
||||
@ -105,10 +104,7 @@ public class CubaDateFieldWidget extends VPopupCalendar implements ShortcutActio
|
||||
}
|
||||
}
|
||||
};
|
||||
cubaMaskedFieldWidget.setImmediate(isImmediate());
|
||||
|
||||
return cubaMaskedFieldWidget;
|
||||
}*/
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBrowserEvent(Event event) {
|
||||
@ -131,7 +127,6 @@ public class CubaDateFieldWidget extends VPopupCalendar implements ShortcutActio
|
||||
}
|
||||
|
||||
public void updateTextState() {
|
||||
// vaadin8
|
||||
// getImpl().updateTextState();
|
||||
getImpl().updateTextState();
|
||||
}
|
||||
}
|
@ -40,7 +40,6 @@ public class CubaMaskedFieldConnector extends TextFieldConnector {
|
||||
super.onStateChanged(stateChangeEvent);
|
||||
|
||||
getWidget().setMask(getState().mask);
|
||||
getWidget().setTimeMask(getState().isTimeMask);
|
||||
getWidget().setMaskedMode(getState().maskedMode);
|
||||
getWidget().setSendNullRepresentation(getState().sendNullRepresentation);
|
||||
}
|
||||
|
@ -59,8 +59,6 @@ public class CubaMaskedFieldWidget extends VTextField {
|
||||
protected boolean shiftPressed = false;
|
||||
protected int shiftPressPos = -1;
|
||||
|
||||
protected boolean isTimeMask = false;
|
||||
|
||||
protected String valueBeforeEdit;
|
||||
|
||||
public CubaMaskedFieldWidget() {
|
||||
@ -121,10 +119,6 @@ public class CubaMaskedFieldWidget extends VTextField {
|
||||
this.maskedMode = maskedMode;
|
||||
}
|
||||
|
||||
public void setTimeMask(boolean isTimeMask) {
|
||||
this.isTimeMask = isTimeMask;
|
||||
}
|
||||
|
||||
public boolean isSendNullRepresentation() {
|
||||
return sendNullRepresentation;
|
||||
}
|
||||
@ -170,24 +164,22 @@ public class CubaMaskedFieldWidget extends VTextField {
|
||||
valueBuilder = maskValue(value);
|
||||
String text = valueBuilder.toString();
|
||||
if (text.equals(nullRepresentation) || valueBuilder.length() == 0) {
|
||||
getElement().addClassName(EMPTY_FIELD_CLASS);
|
||||
getElement().addClassName(getEmptyFieldClass());
|
||||
} else {
|
||||
getElement().removeClassName(EMPTY_FIELD_CLASS);
|
||||
getElement().removeClassName(getEmptyFieldClass());
|
||||
}
|
||||
|
||||
super.setText(text);
|
||||
}
|
||||
|
||||
protected void valueChange(boolean blurred) {
|
||||
protected String getEmptyFieldClass() {
|
||||
return EMPTY_FIELD_CLASS;
|
||||
}
|
||||
|
||||
public void valueChange(boolean blurred) {
|
||||
String newText = getValue();
|
||||
|
||||
if (!newText.equals(valueBeforeEdit)) {
|
||||
if (isTimeMask) {
|
||||
newText = (newText.endsWith("__") && !newText.startsWith("__"))
|
||||
? newText.replaceAll("__", "00")
|
||||
: newText;
|
||||
}
|
||||
|
||||
if (validateText(newText)) {
|
||||
valueBeforeEdit = newText;
|
||||
setValue(newText);
|
||||
@ -298,6 +290,13 @@ public class CubaMaskedFieldWidget extends VTextField {
|
||||
for (int i = 0; i < maskTest.size(); i++) {
|
||||
Mask mask = maskTest.get(i);
|
||||
if (mask != null) {
|
||||
// If text.length() equals to current char index,
|
||||
// this means that a text is shorter,
|
||||
// but this doesn't mean that it's incorrect
|
||||
if (text.length() <= i) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!mask.isValid(text.charAt(i))) {
|
||||
return false;
|
||||
}
|
||||
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2018 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.web.widgets.client.timefield;
|
||||
|
||||
import com.haulmont.cuba.web.widgets.CubaTimeField;
|
||||
import com.haulmont.cuba.web.widgets.client.textfield.CubaMaskedFieldConnector;
|
||||
import com.vaadin.client.communication.StateChangeEvent;
|
||||
import com.vaadin.shared.ui.Connect;
|
||||
|
||||
@Connect(CubaTimeField.class)
|
||||
public class CubaTimeFieldConnector extends CubaMaskedFieldConnector {
|
||||
|
||||
@Override
|
||||
public CubaTimeFieldState getState() {
|
||||
return (CubaTimeFieldState) super.getState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CubaTimeFieldWidget getWidget() {
|
||||
return (CubaTimeFieldWidget) super.getWidget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateChanged(StateChangeEvent stateChangeEvent) {
|
||||
super.onStateChanged(stateChangeEvent);
|
||||
|
||||
CubaTimeFieldWidget widget = getWidget();
|
||||
|
||||
if (stateChangeEvent.hasPropertyChanged("resolution")) {
|
||||
// Remove old stylename that indicates current resolution
|
||||
setWidgetStyleName(widget.getStylePrimaryName() + "-"
|
||||
+ widget.resolutionAsString(), false);
|
||||
|
||||
widget.setResolution(getState().resolution);
|
||||
|
||||
// Add stylename that indicates current resolution
|
||||
setWidgetStyleName(widget.getStylePrimaryName() + "-"
|
||||
+ widget.resolutionAsString(), true);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2018 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.web.widgets.client.timefield;
|
||||
|
||||
import com.haulmont.cuba.web.widgets.client.textfield.CubaMaskedFieldWidget;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class CubaTimeFieldWidget extends CubaMaskedFieldWidget {
|
||||
|
||||
public static final String CLASSNAME = "c-timefield";
|
||||
|
||||
protected static final String EMPTY_FIELD_CLASSNAME = "c-timefield-empty";
|
||||
|
||||
protected TimeResolution resolution = TimeResolution.HOUR;
|
||||
|
||||
@Override
|
||||
public void setMask(String mask) {
|
||||
super.setMask(mask);
|
||||
|
||||
// Fire value change to replace placeholders with default values
|
||||
valueChange(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void valueChange(boolean blurred) {
|
||||
String newText = getValue();
|
||||
|
||||
if (!newText.equals(valueBeforeEdit)) {
|
||||
newText = (newText.endsWith("__") && !newText.startsWith("__"))
|
||||
? newText.replaceAll("__", "00")
|
||||
: newText;
|
||||
|
||||
if (validateText(newText)) {
|
||||
valueBeforeEdit = newText;
|
||||
setValue(newText);
|
||||
} else {
|
||||
setValue(valueBeforeEdit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getEmptyFieldClass() {
|
||||
return EMPTY_FIELD_CLASSNAME;
|
||||
}
|
||||
|
||||
public TimeResolution getResolution() {
|
||||
return resolution;
|
||||
}
|
||||
|
||||
public void setResolution(TimeResolution resolution) {
|
||||
this.resolution = resolution;
|
||||
}
|
||||
|
||||
public String resolutionAsString() {
|
||||
return resolution.name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
}
|
@ -17,36 +17,27 @@
|
||||
package com.haulmont.cuba.web.widgets;
|
||||
|
||||
import com.haulmont.cuba.web.widgets.client.datefield.CubaDateFieldState;
|
||||
import com.vaadin.data.Result;
|
||||
import com.vaadin.event.Action;
|
||||
import com.vaadin.event.ActionManager;
|
||||
import com.vaadin.event.ShortcutListener;
|
||||
import com.vaadin.server.PaintException;
|
||||
import com.vaadin.server.PaintTarget;
|
||||
import com.vaadin.shared.Registration;
|
||||
import com.vaadin.v7.data.util.converter.Converter;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import com.vaadin.shared.ui.datefield.DateResolution;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.Date;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class CubaDateField extends com.vaadin.v7.ui.DateField implements Action.Container {
|
||||
public class CubaDateField extends com.vaadin.ui.DateField implements Action.Container {
|
||||
|
||||
/**
|
||||
* Keeps track of the Actions added to this component, and manages the
|
||||
* painting and handling as well.
|
||||
*/
|
||||
protected ActionManager shortcutsManager;
|
||||
|
||||
protected String lastInvalidDateString;
|
||||
// protected ActionManager shortcutsManager;
|
||||
|
||||
protected String dateString;
|
||||
|
||||
private final Date MARKER_DATE = new Date(0);
|
||||
|
||||
protected Date prevValue;
|
||||
|
||||
public CubaDateField() {
|
||||
setStyleName("c-datefield");
|
||||
}
|
||||
@ -61,23 +52,8 @@ public class CubaDateField extends com.vaadin.v7.ui.DateField implements Action.
|
||||
return (CubaDateFieldState) super.getState(markAsDirty);
|
||||
}
|
||||
|
||||
public void setModified(boolean modified) {
|
||||
if (getState(false).modified != modified) {
|
||||
getState().modified = modified;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setValue(Date newValue, boolean repaintIsNotNeeded) throws Converter.ConversionException {
|
||||
if (newValue == MARKER_DATE)
|
||||
super.setValue(prevValue, true);
|
||||
else {
|
||||
prevValue = newValue;
|
||||
super.setValue(newValue, repaintIsNotNeeded);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
// VAADIN8: gg, do we need this method?
|
||||
/*@Override
|
||||
public void changeVariables(Object source, Map<String, Object> variables) {
|
||||
lastInvalidDateString = (String) variables.get("lastInvalidDateString");
|
||||
dateString = (String) variables.get("dateString");
|
||||
@ -87,7 +63,8 @@ public class CubaDateField extends com.vaadin.v7.ui.DateField implements Action.
|
||||
if (shortcutsManager != null) {
|
||||
shortcutsManager.handleActions(variables, this);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
@Override
|
||||
public void setDateFormat(String dateFormat) {
|
||||
@ -97,42 +74,114 @@ public class CubaDateField extends com.vaadin.v7.ui.DateField implements Action.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Date handleUnparsableDateString(String dateString) throws Converter.ConversionException {
|
||||
if (Objects.equals(dateString, StringUtils.replaceChars(getState(false).dateMask, "#U", "__"))) {
|
||||
return null;
|
||||
}
|
||||
protected void updateInternal(String newDateString, Map<String, Integer> resolutions) {
|
||||
// CAUTION: copied from AbstractDateField
|
||||
Set<String> resolutionNames = getResolutions().map(Enum::name)
|
||||
.collect(Collectors.toSet());
|
||||
resolutionNames.retainAll(resolutions.keySet());
|
||||
if (!isReadOnly()
|
||||
&& (!resolutionNames.isEmpty() || newDateString != null)) {
|
||||
|
||||
markAsDirty();
|
||||
return MARKER_DATE;
|
||||
// Old and new dates
|
||||
final LocalDate oldDate = getValue();
|
||||
|
||||
LocalDate newDate;
|
||||
|
||||
String mask = StringUtils.replaceChars(getState(false).dateMask, "#U", "__");
|
||||
if ("".equals(newDateString)
|
||||
|| mask.equals(newDateString)) {
|
||||
|
||||
newDate = null;
|
||||
} else {
|
||||
newDate = reconstructDateFromFields(resolutions, oldDate);
|
||||
}
|
||||
|
||||
boolean hasChanges = !Objects.equals(dateString, newDateString)
|
||||
|| !Objects.equals(oldDate, newDate);
|
||||
|
||||
if (hasChanges) {
|
||||
dateString = newDateString;
|
||||
currentParseErrorMessage = null;
|
||||
if (newDateString == null || newDateString.isEmpty()
|
||||
|| mask.equals(newDateString)) {
|
||||
setValue(newDate, true);
|
||||
} else {
|
||||
// invalid date string
|
||||
if (resolutions.isEmpty()) {
|
||||
Result<LocalDate> parsedDate = handleUnparsableDateString(
|
||||
dateString);
|
||||
parsedDate.ifOk(v -> setValue(v, true));
|
||||
if (parsedDate.isError()) {
|
||||
dateString = null;
|
||||
currentParseErrorMessage = parsedDate
|
||||
.getMessage().orElse("Parsing error");
|
||||
|
||||
if (!isDifferentValue(null)) {
|
||||
doSetValue(null);
|
||||
} else {
|
||||
setValue(null, true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setValue(newDate, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Result<LocalDate> handleUnparsableDateString(String dateString) {
|
||||
if (Objects.equals(dateString, StringUtils.replaceChars(getState(false).dateMask, "#U", "__"))) {
|
||||
return Result.ok(null);
|
||||
}
|
||||
|
||||
return Result.error(getParseErrorMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResolution(DateResolution resolution) {
|
||||
super.setResolution(resolution);
|
||||
// By default, only visual representation is updated after the resolution is changed.
|
||||
// As a result, the actual value and the visual representation are different values.
|
||||
// But we want to update the field value and fire value change event.
|
||||
if (getValue() != null) {
|
||||
setValue(reconstructDateFromFields(getState().resolutions, getValue()), true);
|
||||
}
|
||||
}
|
||||
|
||||
// VAADIN8: gg, do we need this method?
|
||||
/*@Override
|
||||
public void paintContent(PaintTarget target) throws PaintException {
|
||||
super.paintContent(target);
|
||||
|
||||
if (shortcutsManager != null) {
|
||||
shortcutsManager.paintActions(null, target);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
@Override
|
||||
// VAADIN8: gg, do we need this method?
|
||||
/*@Override
|
||||
protected ActionManager getActionManager() {
|
||||
if (shortcutsManager == null) {
|
||||
shortcutsManager = new ActionManager(this);
|
||||
}
|
||||
return shortcutsManager;
|
||||
}
|
||||
}*/
|
||||
|
||||
@Override
|
||||
// VAADIN8: gg, do we need this method?
|
||||
/*@Override
|
||||
public Registration addShortcutListener(ShortcutListener listener) {
|
||||
Objects.requireNonNull(listener, "Listener must not be null.");
|
||||
getActionManager().addAction(listener);
|
||||
return () -> getActionManager().removeAction(listener);
|
||||
}
|
||||
}*/
|
||||
|
||||
@Override
|
||||
// VAADIN8: gg, remove?
|
||||
/*@Override
|
||||
public void removeShortcutListener(ShortcutListener listener) {
|
||||
getActionManager().removeAction(listener);
|
||||
}
|
||||
}*/
|
||||
|
||||
@Override
|
||||
public void addActionHandler(Action.Handler actionHandler) {
|
||||
|
@ -52,14 +52,6 @@ public class CubaMaskedTextField extends CubaTextField {
|
||||
return (CubaMaskedTextFieldState) super.getState(markAsDirty);
|
||||
}
|
||||
|
||||
public boolean isTimeMask() {
|
||||
return getState().isTimeMask;
|
||||
}
|
||||
|
||||
public void setTimeMask(boolean isTimeMask) {
|
||||
getState(true).isTimeMask = isTimeMask;
|
||||
}
|
||||
|
||||
public void setMask(String mask) {
|
||||
getState(true).mask = mask;
|
||||
}
|
||||
|
@ -0,0 +1,224 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2018 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.web.widgets;
|
||||
|
||||
import com.haulmont.cuba.web.widgets.client.timefield.CubaTimeFieldState;
|
||||
import com.haulmont.cuba.web.widgets.client.timefield.TimeResolution;
|
||||
import com.vaadin.event.FieldEvents;
|
||||
import com.vaadin.shared.communication.FieldRpc;
|
||||
import com.vaadin.shared.ui.textfield.AbstractTextFieldServerRpc;
|
||||
import com.vaadin.ui.AbstractField;
|
||||
import elemental.json.Json;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.time.LocalTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
public class CubaTimeField extends AbstractField<LocalTime> {
|
||||
|
||||
protected final class AbstractTextFieldServerRpcImpl implements AbstractTextFieldServerRpc {
|
||||
|
||||
@Override
|
||||
public void setText(String text, int cursorPosition) {
|
||||
updateDiffstate("text", Json.create(text));
|
||||
|
||||
LocalTime value = parseValue(text);
|
||||
setValue(value, true);
|
||||
}
|
||||
}
|
||||
|
||||
protected final class AbstractTextFieldFocusAndBlurRpcImpl implements FieldRpc.FocusAndBlurServerRpc {
|
||||
|
||||
@Override
|
||||
public void blur() {
|
||||
fireEvent(new FieldEvents.BlurEvent(CubaTimeField.this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void focus() {
|
||||
fireEvent(new FieldEvents.FocusEvent(CubaTimeField.this));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected String placeholder;
|
||||
protected String timeFormat;
|
||||
|
||||
protected LocalTime value;
|
||||
|
||||
public CubaTimeField() {
|
||||
getState(false).maskedMode = true;
|
||||
getState().resolution = TimeResolution.MINUTE;
|
||||
|
||||
registerRpc(new AbstractTextFieldServerRpcImpl());
|
||||
registerRpc(new AbstractTextFieldFocusAndBlurRpcImpl());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CubaTimeFieldState getState() {
|
||||
return (CubaTimeFieldState) super.getState();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CubaTimeFieldState getState(boolean markAsDirty) {
|
||||
return (CubaTimeFieldState) super.getState(markAsDirty);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean setValue(LocalTime value, boolean userOriginated) {
|
||||
value = applyResolutionToValue(value);
|
||||
return super.setValue(value, userOriginated);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSetValue(LocalTime value) {
|
||||
this.value = value;
|
||||
|
||||
getState().text = formatValue(value);
|
||||
}
|
||||
|
||||
protected LocalTime applyResolutionToValue(LocalTime value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
LocalTime result = LocalTime.MIDNIGHT;
|
||||
List<TimeResolution> resolutions = getResolutionsHigherOrEqualTo(getResolution())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (TimeResolution resolution : resolutions) {
|
||||
switch (resolution) {
|
||||
case HOUR:
|
||||
result = result.withHour(value.getHour());
|
||||
break;
|
||||
case MINUTE:
|
||||
result = result.withMinute(value.getMinute());
|
||||
break;
|
||||
case SECOND:
|
||||
result = result.withSecond(value.getSecond());
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Cannot detect resolution type");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalTime getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public String getTimeFormat() {
|
||||
return this.timeFormat;
|
||||
}
|
||||
|
||||
public void setTimeFormat(String timeFormat) {
|
||||
this.timeFormat = timeFormat;
|
||||
|
||||
updateTimeFormat();
|
||||
}
|
||||
|
||||
protected void updateTimeFormat() {
|
||||
String mask = StringUtils.replaceChars(timeFormat, "Hhmsa", "####U");
|
||||
placeholder = StringUtils.replaceChars(mask, "#U", "__");
|
||||
getState().mask = mask;
|
||||
}
|
||||
|
||||
public TimeResolution getResolution() {
|
||||
return getState(false).resolution;
|
||||
}
|
||||
|
||||
public void setResolution(TimeResolution resolution) {
|
||||
getState().resolution = resolution;
|
||||
|
||||
updateResolution();
|
||||
}
|
||||
|
||||
protected void updateResolution() {
|
||||
this.timeFormat = getResolutionsHigherOrEqualTo(getResolution())
|
||||
.map(this::getResolutionFormat)
|
||||
.collect(Collectors.joining(":"));
|
||||
|
||||
// By default, only visual representation is updated after the resolution is changed.
|
||||
// As a result, the actual value and the visual representation are different values.
|
||||
// Since we want to update the field value and fire value change event, we reset the value,
|
||||
// taking into account the fact that the setValue(LocalTime, boolean) method applies current
|
||||
// resolution to value.
|
||||
if (getValue() != null) {
|
||||
setValue(getValue(), true);
|
||||
}
|
||||
|
||||
updateTimeFormat();
|
||||
}
|
||||
|
||||
protected Stream<TimeResolution> getResolutionsHigherOrEqualTo(TimeResolution resolution) {
|
||||
return Stream.of(TimeResolution.values())
|
||||
.skip(resolution.ordinal())
|
||||
.sorted(Comparator.reverseOrder());
|
||||
}
|
||||
|
||||
protected String getResolutionFormat(TimeResolution resolution) {
|
||||
checkNotNull(resolution, "Resolution can't be null");
|
||||
|
||||
switch (resolution) {
|
||||
case HOUR:
|
||||
return "HH";
|
||||
case MINUTE:
|
||||
return "mm";
|
||||
case SECOND:
|
||||
return "ss";
|
||||
default:
|
||||
throw new IllegalArgumentException("Cannot detect resolution type");
|
||||
}
|
||||
}
|
||||
|
||||
protected LocalTime parseValue(String text) {
|
||||
if (StringUtils.isNotEmpty(text) && !text.equals(placeholder)) {
|
||||
DateTimeFormatter dateTimeFormatter = getDateTimeFormatter();
|
||||
return LocalTime.parse(text, dateTimeFormatter);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected String formatValue(LocalTime value) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
DateTimeFormatter dateTimeFormatter = getDateTimeFormatter();
|
||||
return value.format(dateTimeFormatter);
|
||||
}
|
||||
|
||||
protected DateTimeFormatter getDateTimeFormatter() {
|
||||
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(timeFormat);
|
||||
Locale locale = getLocale();
|
||||
if (locale != null) {
|
||||
dateTimeFormatter = dateTimeFormatter.withLocale(locale);
|
||||
}
|
||||
return dateTimeFormatter;
|
||||
}
|
||||
}
|
@ -18,9 +18,9 @@
|
||||
package com.haulmont.cuba.web.widgets.client.datefield;
|
||||
|
||||
import com.vaadin.shared.annotations.NoLayout;
|
||||
import com.vaadin.v7.shared.ui.datefield.PopupDateFieldState;
|
||||
import com.vaadin.shared.ui.datefield.LocalDateFieldState;
|
||||
|
||||
public class CubaDateFieldState extends PopupDateFieldState {
|
||||
public class CubaDateFieldState extends LocalDateFieldState {
|
||||
@NoLayout
|
||||
public String dateMask = "";
|
||||
}
|
@ -32,7 +32,4 @@ public class CubaMaskedTextFieldState extends CubaTextFieldState {
|
||||
|
||||
@NoLayout
|
||||
public boolean sendNullRepresentation = true;
|
||||
|
||||
@NoLayout
|
||||
public boolean isTimeMask = false;
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2018 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.web.widgets.client.timefield;
|
||||
|
||||
import com.haulmont.cuba.web.widgets.client.textfield.CubaMaskedTextFieldState;
|
||||
|
||||
public class CubaTimeFieldState extends CubaMaskedTextFieldState {
|
||||
{
|
||||
primaryStyleName = "c-timefield";
|
||||
}
|
||||
|
||||
public TimeResolution resolution = TimeResolution.MINUTE;
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2018 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.web.widgets.client.timefield;
|
||||
|
||||
public enum TimeResolution {
|
||||
SECOND,
|
||||
MINUTE,
|
||||
HOUR
|
||||
}
|
@ -1,160 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2017 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.web.gui.components;
|
||||
|
||||
import com.haulmont.cuba.gui.theme.ThemeConstants;
|
||||
import com.haulmont.cuba.web.App;
|
||||
import com.haulmont.cuba.web.gui.components.converters.ObjectToObjectConverter;
|
||||
import com.vaadin.server.AbstractErrorMessage;
|
||||
import com.vaadin.server.CompositeErrorMessage;
|
||||
import com.vaadin.server.ErrorMessage;
|
||||
import com.vaadin.ui.Component;
|
||||
import com.vaadin.ui.Layout;
|
||||
import com.vaadin.v7.data.util.converter.Converter;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class CubaDateFieldWrapper extends com.vaadin.v7.ui.CustomField {
|
||||
|
||||
protected final Layout composition;
|
||||
protected final WebDateField dateField;
|
||||
|
||||
protected ThemeConstants theme;
|
||||
protected boolean showBufferedExceptions = false;
|
||||
|
||||
public CubaDateFieldWrapper(WebDateField dateField, Layout composition) {
|
||||
this.dateField = dateField;
|
||||
this.composition = composition;
|
||||
|
||||
if (App.isBound()) {
|
||||
theme = App.getInstance().getThemeConstants();
|
||||
}
|
||||
|
||||
setSizeUndefined();
|
||||
//noinspection unchecked
|
||||
setConverter(new ObjectToObjectConverter());
|
||||
|
||||
setValidationVisible(false);
|
||||
setShowBufferedSourceException(false);
|
||||
setShowErrorForDisabledState(false);
|
||||
setFocusDelegate(dateField.getDateField());
|
||||
|
||||
setPrimaryStyleName("c-datefield-composition");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SourceException getCurrentBufferedSourceException() {
|
||||
if (!showBufferedExceptions) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return super.getCurrentBufferedSourceException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Component initContent() {
|
||||
return composition;
|
||||
}
|
||||
|
||||
public WebDateField getCubaField() {
|
||||
return dateField;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return dateField.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(Object newValue) throws ReadOnlyException, Converter.ConversionException {
|
||||
dateField.setValue(newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return getValue() == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void focus() {
|
||||
dateField.getDateField().focus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getType() {
|
||||
return Date.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWidth(float width, Unit unit) {
|
||||
super.setWidth(width, unit);
|
||||
|
||||
if (composition != null && dateField != null) {
|
||||
if (width < 0) {
|
||||
composition.setWidth(-1, Unit.PIXELS);
|
||||
String defaultDateFieldWidth = "-1px";
|
||||
if (theme != null) {
|
||||
defaultDateFieldWidth = theme.get("cuba.web.WebDateField.defaultDateWidth");
|
||||
}
|
||||
dateField.getDateField().setWidth(defaultDateFieldWidth);
|
||||
} else {
|
||||
composition.setWidth(100, Unit.PERCENTAGE);
|
||||
dateField.getDateField().setWidth("100%");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeight(float height, Unit unit) {
|
||||
super.setHeight(height, unit);
|
||||
|
||||
if (composition != null) {
|
||||
if (height < 0) {
|
||||
composition.setHeight(-1, Unit.PIXELS);
|
||||
} else {
|
||||
composition.setHeight(100, Unit.PERCENTAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadOnly(boolean readOnly) {
|
||||
dateField.setEditable(!readOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadOnly() {
|
||||
return !dateField.isEditableWithParent();
|
||||
}
|
||||
|
||||
public void setCompositionReadOnly(boolean readOnly) {
|
||||
super.setReadOnly(readOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ErrorMessage getErrorMessage() {
|
||||
ErrorMessage superError = super.getErrorMessage();
|
||||
if (!isReadOnly() && isRequired() && isEmpty()) {
|
||||
ErrorMessage error = AbstractErrorMessage.getErrorMessageForException(
|
||||
new com.vaadin.v7.data.Validator.EmptyValueException(getRequiredError()));
|
||||
if (error != null) {
|
||||
return new CompositeErrorMessage(superError, error);
|
||||
}
|
||||
}
|
||||
return superError;
|
||||
}
|
||||
}
|
@ -104,11 +104,6 @@ public abstract class WebAbstractViewComponent<T extends com.vaadin.ui.Component
|
||||
|
||||
protected abstract void setValueToPresentation(P value);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected V convertToModel(P componentRawValue) throws ConversionException {
|
||||
return (V) componentRawValue;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected P convertToPresentation(V modelValue) throws ConversionException {
|
||||
return (P) modelValue;
|
||||
|
@ -20,6 +20,7 @@ import com.haulmont.cuba.core.global.AppBeans;
|
||||
import com.haulmont.cuba.core.global.Configuration;
|
||||
import com.haulmont.cuba.gui.components.*;
|
||||
import com.haulmont.cuba.gui.components.ComponentContainer;
|
||||
import com.haulmont.cuba.gui.components.DateField;
|
||||
import com.haulmont.cuba.gui.components.KeyCombination.Modifier;
|
||||
import com.haulmont.cuba.gui.components.TextField;
|
||||
import com.haulmont.cuba.gui.icons.Icons;
|
||||
@ -30,16 +31,17 @@ import com.haulmont.cuba.web.WebConfig;
|
||||
import com.haulmont.cuba.web.gui.components.util.ShortcutListenerDelegate;
|
||||
import com.haulmont.cuba.web.toolkit.VersionedThemeResource;
|
||||
import com.haulmont.cuba.web.widgets.*;
|
||||
import com.haulmont.cuba.web.widgets.client.timefield.TimeResolution;
|
||||
import com.vaadin.event.Action;
|
||||
import com.vaadin.event.ShortcutAction;
|
||||
import com.vaadin.server.*;
|
||||
import com.vaadin.server.FileResource;
|
||||
import com.vaadin.server.Resource;
|
||||
import com.vaadin.shared.ui.datefield.DateResolution;
|
||||
import com.vaadin.ui.*;
|
||||
import com.vaadin.ui.Button;
|
||||
import com.vaadin.ui.Component;
|
||||
import com.vaadin.ui.TabSheet;
|
||||
import com.vaadin.v7.shared.ui.datefield.Resolution;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -352,26 +354,55 @@ public class WebComponentsHelper {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Resolution convertDateFieldResolution(com.haulmont.cuba.gui.components.DateField.Resolution resolution) {
|
||||
public static DateResolution convertDateResolution(DatePicker.Resolution resolution) {
|
||||
switch (resolution) {
|
||||
case YEAR:
|
||||
return DateResolution.YEAR;
|
||||
case MONTH:
|
||||
return DateResolution.MONTH;
|
||||
case DAY:
|
||||
default:
|
||||
return DateResolution.DAY;
|
||||
}
|
||||
}
|
||||
|
||||
public static DateResolution convertDateTimeResolution(DateField.Resolution resolution) {
|
||||
switch (resolution) {
|
||||
case YEAR:
|
||||
return DateResolution.YEAR;
|
||||
case MONTH:
|
||||
return DateResolution.MONTH;
|
||||
case DAY:
|
||||
case HOUR:
|
||||
case MIN:
|
||||
case SEC:
|
||||
default:
|
||||
return DateResolution.DAY;
|
||||
}
|
||||
}
|
||||
|
||||
public static TimeResolution convertTimeResolution(TimeField.Resolution resolution) {
|
||||
switch (resolution) {
|
||||
case SEC:
|
||||
return Resolution.SECOND;
|
||||
|
||||
return TimeResolution.SECOND;
|
||||
case HOUR:
|
||||
return Resolution.HOUR;
|
||||
|
||||
case DAY:
|
||||
return Resolution.DAY;
|
||||
|
||||
case MONTH:
|
||||
return Resolution.MONTH;
|
||||
|
||||
case YEAR:
|
||||
return Resolution.YEAR;
|
||||
|
||||
return TimeResolution.HOUR;
|
||||
case MIN:
|
||||
default:
|
||||
return Resolution.MINUTE;
|
||||
return TimeResolution.MINUTE;
|
||||
}
|
||||
}
|
||||
|
||||
public static TimeResolution convertTimeResolution(DateField.Resolution resolution) {
|
||||
switch (resolution) {
|
||||
case HOUR:
|
||||
return TimeResolution.HOUR;
|
||||
case MIN:
|
||||
return TimeResolution.MINUTE;
|
||||
case SEC:
|
||||
return TimeResolution.SECOND;
|
||||
default:
|
||||
throw new IllegalArgumentException("Can't be converted to TimeResolution: " + resolution);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,120 +16,131 @@
|
||||
*/
|
||||
package com.haulmont.cuba.web.gui.components;
|
||||
|
||||
import com.haulmont.bali.util.DateTimeUtils;
|
||||
import com.haulmont.bali.util.Preconditions;
|
||||
import com.haulmont.chile.core.datatypes.Datatypes;
|
||||
import com.haulmont.chile.core.model.MetaProperty;
|
||||
import com.haulmont.chile.core.model.utils.InstanceUtils;
|
||||
import com.haulmont.cuba.core.entity.Entity;
|
||||
import com.haulmont.cuba.core.global.*;
|
||||
import com.haulmont.chile.core.model.MetaPropertyPath;
|
||||
import com.haulmont.cuba.core.global.AppBeans;
|
||||
import com.haulmont.cuba.core.global.Messages;
|
||||
import com.haulmont.cuba.core.global.UserSessionSource;
|
||||
import com.haulmont.cuba.gui.TestIdManager;
|
||||
import com.haulmont.cuba.gui.components.DateField;
|
||||
import com.haulmont.cuba.gui.components.Frame;
|
||||
import com.haulmont.cuba.gui.data.Datasource;
|
||||
import com.haulmont.cuba.gui.data.impl.WeakItemChangeListener;
|
||||
import com.haulmont.cuba.gui.data.impl.WeakItemPropertyChangeListener;
|
||||
import com.haulmont.cuba.security.global.UserSession;
|
||||
import com.haulmont.cuba.gui.components.*;
|
||||
import com.haulmont.cuba.gui.components.data.ConversionException;
|
||||
import com.haulmont.cuba.gui.components.data.DataAwareComponentsTools;
|
||||
import com.haulmont.cuba.gui.components.data.EntityValueSource;
|
||||
import com.haulmont.cuba.gui.components.data.ValueSource;
|
||||
import com.haulmont.cuba.gui.components.data.value.DatasourceValueSource;
|
||||
import com.haulmont.cuba.gui.theme.ThemeConstants;
|
||||
import com.haulmont.cuba.web.App;
|
||||
import com.haulmont.cuba.web.AppUI;
|
||||
import com.haulmont.cuba.web.widgets.CubaDateField;
|
||||
import com.haulmont.cuba.web.widgets.CubaMaskedTextField;
|
||||
import com.haulmont.cuba.web.widgets.CubaTimeField;
|
||||
import com.vaadin.data.HasValue;
|
||||
import com.vaadin.shared.ui.datefield.DateResolution;
|
||||
import com.vaadin.ui.Layout;
|
||||
import com.vaadin.v7.data.Property;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.persistence.TemporalType;
|
||||
import javax.validation.constraints.Future;
|
||||
import javax.validation.constraints.Past;
|
||||
import java.sql.Time;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class WebDateField<V extends Date> extends WebAbstractField<CubaDateFieldWrapper, V> implements DateField<V> {
|
||||
public class WebDateField<V extends Date> extends WebAbstractViewComponent<Layout, LocalDateTime, V>
|
||||
implements DateField<V>, InitializingBean {
|
||||
|
||||
protected static final int VALIDATORS_LIST_INITIAL_CAPACITY = 2;
|
||||
protected List<Validator> validators; // lazily initialized list
|
||||
|
||||
protected Resolution resolution;
|
||||
|
||||
protected boolean updatingInstance;
|
||||
|
||||
protected CubaDateField dateField;
|
||||
protected WebTimeField timeField;
|
||||
|
||||
protected Layout innerLayout;
|
||||
protected CubaTimeField timeField;
|
||||
|
||||
protected String dateTimeFormat;
|
||||
// TODO: gg, why we have this?
|
||||
protected String dateFormat;
|
||||
protected String timeFormat;
|
||||
|
||||
protected TimeZone timeZone;
|
||||
|
||||
protected UserSession userSession;
|
||||
protected boolean editable = true;
|
||||
|
||||
protected Datasource.ItemPropertyChangeListener itemPropertyChangeListener;
|
||||
protected WeakItemPropertyChangeListener weakItemPropertyChangeListener;
|
||||
|
||||
protected Datasource.ItemChangeListener itemChangeListener;
|
||||
protected WeakItemChangeListener weakItemChangeListener;
|
||||
|
||||
protected boolean buffered = false;
|
||||
protected boolean updateTimeFieldResolution = false;
|
||||
protected ThemeConstants theme;
|
||||
|
||||
public WebDateField() {
|
||||
innerLayout = new com.vaadin.ui.CssLayout();
|
||||
innerLayout.setPrimaryStyleName("c-datefield-layout");
|
||||
component = new com.vaadin.ui.CssLayout();
|
||||
component.setPrimaryStyleName("c-datefield-layout");
|
||||
|
||||
if (App.isBound()) {
|
||||
theme = App.getInstance().getThemeConstants();
|
||||
}
|
||||
|
||||
dateField = new CubaDateField();
|
||||
dateField.setValidationVisible(false);
|
||||
dateField.setInvalidAllowed(true);
|
||||
timeField = new CubaTimeField();
|
||||
|
||||
UserSessionSource sessionSource = AppBeans.get(UserSessionSource.NAME);
|
||||
userSession = sessionSource.getUserSession();
|
||||
|
||||
Locale locale = userSession.getLocale();
|
||||
dateField.setDateFormat(Datatypes.getFormatStringsNN(locale).getDateFormat());
|
||||
dateField.setResolution(com.vaadin.v7.shared.ui.datefield.Resolution.DAY);
|
||||
|
||||
timeField = new WebTimeField();
|
||||
|
||||
CubaMaskedTextField vTimeField = (CubaMaskedTextField) timeField.getComponent();
|
||||
// vaadin8
|
||||
// vTimeField.setInvalidAllowed(false);
|
||||
// vTimeField.setInvalidCommitted(true);
|
||||
setWidthAuto();
|
||||
|
||||
dateField.addValueChangeListener(createDateValueChangeListener());
|
||||
timeField.addValueChangeListener(createTimeValueChangeListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
UserSessionSource userSessionSource = applicationContext.getBean(UserSessionSource.class);
|
||||
Locale locale = userSessionSource.getLocale();
|
||||
|
||||
dateField.setDateFormat(Datatypes.getFormatStringsNN(locale).getDateFormat());
|
||||
dateField.setResolution(DateResolution.DAY);
|
||||
|
||||
timeField.setTimeFormat(Datatypes.getFormatStringsNN(locale).getTimeFormat());
|
||||
|
||||
setResolution(Resolution.MIN);
|
||||
|
||||
component = new CubaDateFieldWrapper(this, innerLayout);
|
||||
}
|
||||
|
||||
protected Property.ValueChangeListener createDateValueChangeListener() {
|
||||
return e -> {
|
||||
if (!checkRange(constructDate())) {
|
||||
protected HasValue.ValueChangeListener<LocalDate> createDateValueChangeListener() {
|
||||
return event ->
|
||||
componentValueChanged(event.isUserOriginated());
|
||||
}
|
||||
|
||||
protected HasValue.ValueChangeListener<LocalTime> createTimeValueChangeListener() {
|
||||
return event ->
|
||||
componentValueChanged(event.isUserOriginated());
|
||||
}
|
||||
|
||||
protected void componentValueChanged(boolean isUserOriginated) {
|
||||
if (isUserOriginated) {
|
||||
V value;
|
||||
|
||||
try {
|
||||
value = constructModelValue();
|
||||
|
||||
if (!checkRange(value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
LocalDateTime presentationValue = convertToPresentation(value);
|
||||
setValueToPresentation(presentationValue);
|
||||
} catch (ConversionException ce) {
|
||||
LoggerFactory.getLogger(getClass()).trace("Unable to convert presentation value to model", ce);
|
||||
|
||||
setValidationError(ce.getLocalizedMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
updateInstance();
|
||||
V oldValue = internalValue;
|
||||
internalValue = value;
|
||||
|
||||
if (component != null) {
|
||||
// Repaint error state
|
||||
component.markAsDirty();
|
||||
if (!fieldValueEquals(value, oldValue)) {
|
||||
ValueChangeEvent event = new ValueChangeEvent(this, oldValue, value); // todo isUserOriginated
|
||||
getEventRouter().fireEvent(ValueChangeListener.class, ValueChangeListener::valueChanged, event);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected ValueChangeListener createTimeValueChangeListener() {
|
||||
return event -> {
|
||||
if (!checkRange(constructDate())) {
|
||||
return;
|
||||
}
|
||||
if (!updateTimeFieldResolution) {
|
||||
updateInstance();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public CubaDateField getDateField() {
|
||||
return dateField;
|
||||
}
|
||||
|
||||
public WebTimeField getTimeField() {
|
||||
return timeField;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -140,28 +151,41 @@ public class WebDateField<V extends Date> extends WebAbstractField<CubaDateField
|
||||
@Override
|
||||
public void setResolution(Resolution resolution) {
|
||||
this.resolution = resolution;
|
||||
__setResolution(resolution);
|
||||
setResolutionInternal(resolution);
|
||||
updateLayout();
|
||||
}
|
||||
|
||||
protected void setResolutionInternal(Resolution resolution) {
|
||||
dateField.setResolution(WebComponentsHelper.convertDateTimeResolution(resolution));
|
||||
|
||||
if (resolution.ordinal() < Resolution.DAY.ordinal()) {
|
||||
timeField.setResolution(WebComponentsHelper.convertTimeResolution(resolution));
|
||||
} else {
|
||||
// Set time field value to zero in case of resolution without time.
|
||||
// If we don't set value to zero then after changing resolution back to
|
||||
// resolution with time, we will get some value in time field
|
||||
timeField.setValue(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRangeStart(Date value) {
|
||||
dateField.setRangeStart(value);
|
||||
dateField.setRangeStart(DateTimeUtils.asLocalDate(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getRangeStart() {
|
||||
return dateField.getRangeStart();
|
||||
return dateField.getRangeStart() != null ? DateTimeUtils.asDate(dateField.getRangeStart()) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRangeEnd(Date value) {
|
||||
dateField.setRangeEnd(value);
|
||||
dateField.setRangeEnd(DateTimeUtils.asLocalDate(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getRangeEnd() {
|
||||
return dateField.getRangeEnd();
|
||||
return dateField.getRangeEnd() != null ? DateTimeUtils.asDate(dateField.getRangeEnd()) : null;
|
||||
}
|
||||
|
||||
protected boolean checkRange(Date value) {
|
||||
@ -193,17 +217,7 @@ public class WebDateField<V extends Date> extends WebAbstractField<CubaDateField
|
||||
Frame.NotificationType.TRAY);
|
||||
}
|
||||
|
||||
updatingInstance = true;
|
||||
try {
|
||||
dateField.setValue((Date) internalValue);
|
||||
if (internalValue == null) {
|
||||
timeField.setValue(null);
|
||||
} else {
|
||||
timeField.setValue(extractTime((Date) internalValue));
|
||||
}
|
||||
} finally {
|
||||
updatingInstance = false;
|
||||
}
|
||||
setValueToPresentation(DateTimeUtils.asLocalDateTime(internalValue));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -213,18 +227,19 @@ public class WebDateField<V extends Date> extends WebAbstractField<CubaDateField
|
||||
|
||||
@Override
|
||||
public void setDateFormat(String dateFormat) {
|
||||
Preconditions.checkNotNullArgument(dateFormat);
|
||||
|
||||
dateTimeFormat = dateFormat;
|
||||
|
||||
StringBuilder date = new StringBuilder(dateFormat);
|
||||
StringBuilder time = new StringBuilder(dateFormat);
|
||||
int timeStartPos = findTimeStartPos(dateFormat);
|
||||
if (timeStartPos >= 0) {
|
||||
time.delete(0, timeStartPos);
|
||||
date.delete(timeStartPos, dateFormat.length());
|
||||
|
||||
timeFormat = StringUtils.trimToEmpty(time.toString());
|
||||
timeField.setFormat(timeFormat);
|
||||
setResolution(resolution);
|
||||
} else if (resolution.ordinal() < Resolution.DAY.ordinal()) {
|
||||
setResolution(Resolution.DAY);
|
||||
timeField.setTimeFormat(timeFormat);
|
||||
}
|
||||
|
||||
this.dateFormat = StringUtils.trimToEmpty(date.toString());
|
||||
@ -241,29 +256,24 @@ public class WebDateField<V extends Date> extends WebAbstractField<CubaDateField
|
||||
TimeZone prevTimeZone = this.timeZone;
|
||||
Date value = getValue();
|
||||
this.timeZone = timeZone;
|
||||
dateField.setTimeZone(timeZone);
|
||||
dateField.setZoneId(timeZone.toZoneId());
|
||||
if (value != null && !Objects.equals(prevTimeZone, timeZone)) {
|
||||
setValueToFields(value);
|
||||
setValueToPresentation(DateTimeUtils.asLocalDateTime(value));
|
||||
}
|
||||
}
|
||||
|
||||
public void updateLayout() {
|
||||
innerLayout.removeAllComponents();
|
||||
innerLayout.addComponent(dateField);
|
||||
protected void updateLayout() {
|
||||
component.removeAllComponents();
|
||||
component.addComponent(dateField);
|
||||
|
||||
if (resolution.ordinal() < Resolution.DAY.ordinal()) {
|
||||
innerLayout.addComponent(timeField.<com.vaadin.ui.Component>getComponent());
|
||||
innerLayout.addStyleName("c-datefield-withtime");
|
||||
component.addComponent(timeField);
|
||||
component.addStyleName("c-datefield-withtime");
|
||||
} else {
|
||||
innerLayout.removeStyleName("c-datefield-withtime");
|
||||
component.removeStyleName("c-datefield-withtime");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void attachListener(CubaDateFieldWrapper component) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
protected int findTimeStartPos(String dateTimeFormat) {
|
||||
List<Integer> positions = new ArrayList<>();
|
||||
|
||||
@ -277,38 +287,60 @@ public class WebDateField<V extends Date> extends WebAbstractField<CubaDateField
|
||||
return positions.isEmpty() ? -1 : Collections.min(positions);
|
||||
}
|
||||
|
||||
protected void __setResolution(Resolution resolution) {
|
||||
if (resolution.ordinal() < Resolution.DAY.ordinal()) {
|
||||
timeField.setResolution(resolution);
|
||||
// while changing resolution, timeField loses its value, so we need to set it again
|
||||
updateTimeFieldResolution = true;
|
||||
Date value = dateField.getValue();
|
||||
@Override
|
||||
protected void setValueToPresentation(LocalDateTime value) {
|
||||
updatingInstance = true;
|
||||
try {
|
||||
if (value == null) {
|
||||
dateField.setValue(null);
|
||||
timeField.setValue(null);
|
||||
} else {
|
||||
timeField.setValue(extractTime(value));
|
||||
dateField.setValue(value.toLocalDate());
|
||||
timeField.setValue(value.toLocalTime());
|
||||
}
|
||||
updateTimeFieldResolution = false;
|
||||
} else {
|
||||
dateField.setResolution(WebComponentsHelper.convertDateFieldResolution(resolution));
|
||||
} finally {
|
||||
updatingInstance = false;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public V getValue() {
|
||||
return (V) constructDate();
|
||||
protected V constructModelValue() {
|
||||
LocalDate dateFieldValue = dateField.getValue();
|
||||
if (dateFieldValue == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
LocalTime timeValue = timeField.getValue() != null
|
||||
? timeField.getValue()
|
||||
: LocalTime.MIDNIGHT;
|
||||
LocalDateTime resultDateTime = LocalDateTime.of(dateFieldValue, timeValue);
|
||||
|
||||
Date resultDate = DateTimeUtils.asDate(resultDateTime);
|
||||
|
||||
ValueSource<V> valueSource = getValueSource();
|
||||
if (valueSource instanceof EntityValueSource) {
|
||||
MetaPropertyPath metaPropertyPath = ((DatasourceValueSource) valueSource).getMetaPropertyPath();
|
||||
MetaProperty metaProperty = metaPropertyPath.getMetaProperty();
|
||||
if (metaProperty != null) {
|
||||
Class javaClass = metaProperty.getRange().asDatatype().getJavaClass();
|
||||
if (javaClass.equals(java.sql.Date.class)) {
|
||||
return (V) new java.sql.Date(resultDate.getTime());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (V) resultDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(V value) {
|
||||
setValueToFields((Date) value);
|
||||
updateInstance();
|
||||
protected LocalDateTime convertToPresentation(V modelValue) throws ConversionException {
|
||||
return modelValue != null ? DateTimeUtils.asLocalDateTime(modelValue) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() {
|
||||
if (updatingInstance) {
|
||||
// VAADIN8: gg, implement
|
||||
/*if (updatingInstance) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -327,49 +359,42 @@ public class WebDateField<V extends Date> extends WebAbstractField<CubaDateField
|
||||
}
|
||||
|
||||
Object newValue = getValue();
|
||||
fireValueChanged(newValue);
|
||||
fireValueChanged(newValue);*/
|
||||
// super.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void discard() {
|
||||
if (getDatasource() != null && getDatasource().getItem() != null) {
|
||||
// VAADIN8: gg, implement
|
||||
/*if (getDatasource() != null && getDatasource().getItem() != null) {
|
||||
Date value = getEntityValue(getDatasource().getItem());
|
||||
setValueToFields(value);
|
||||
fireValueChanged(value);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBuffered() {
|
||||
return buffered;
|
||||
// VAADIN8: gg, implement
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBuffered(boolean buffered) {
|
||||
this.buffered = buffered;
|
||||
// VAADIN8: gg, implement
|
||||
// this.buffered = buffered;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isModified() {
|
||||
return dateField.isModified();
|
||||
// VAADIN8: gg, implement
|
||||
// return dateField.isModified();
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void setModified(boolean modified) {
|
||||
dateField.setModified(modified);
|
||||
}
|
||||
|
||||
protected void setValueToFields(Date value) {
|
||||
updatingInstance = true;
|
||||
try {
|
||||
dateField.setValueIgnoreReadOnly(value);
|
||||
if (value == null) {
|
||||
timeField.setValue(null);
|
||||
} else {
|
||||
timeField.setValue(extractTime(value));
|
||||
}
|
||||
} finally {
|
||||
updatingInstance = false;
|
||||
}
|
||||
// VAADIN8: gg,
|
||||
// dateField.setModified(modified);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -393,275 +418,49 @@ public class WebDateField<V extends Date> extends WebAbstractField<CubaDateField
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isHourUsed() {
|
||||
return resolution != null && resolution.ordinal() <= Resolution.HOUR.ordinal();
|
||||
}
|
||||
@Override
|
||||
protected void valueBindingConnected(ValueSource<V> valueSource) {
|
||||
super.valueBindingConnected(valueSource);
|
||||
|
||||
protected boolean isMinUsed() {
|
||||
return resolution != null && resolution.ordinal() <= Resolution.MIN.ordinal();
|
||||
}
|
||||
if (valueSource instanceof EntityValueSource) {
|
||||
DataAwareComponentsTools dataAwareComponentsTools = applicationContext.getBean(DataAwareComponentsTools.class);
|
||||
EntityValueSource entityValueSource = (EntityValueSource) valueSource;
|
||||
|
||||
protected void updateInstance() {
|
||||
if (updatingInstance) {
|
||||
return;
|
||||
dataAwareComponentsTools.setupDateRange(this, entityValueSource);
|
||||
dataAwareComponentsTools.setupDateFormat(this, entityValueSource);
|
||||
}
|
||||
|
||||
updatingInstance = true;
|
||||
try {
|
||||
if (getDatasource() != null && getMetaPropertyPath() != null) {
|
||||
Date value = constructDate();
|
||||
|
||||
if (!isBuffered()) {
|
||||
if (getDatasource().getItem() != null) {
|
||||
InstanceUtils.setValueEx(getDatasource().getItem(), getMetaPropertyPath().getPath(), value);
|
||||
setModified(false);
|
||||
}
|
||||
} else {
|
||||
setModified(true);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
updatingInstance = false;
|
||||
}
|
||||
|
||||
Object newValue = getValue();
|
||||
fireValueChanged(newValue);
|
||||
}
|
||||
|
||||
// todo vaadin8
|
||||
/*@Override
|
||||
public void setDatasource(Datasource datasource, String property) {
|
||||
if ((datasource == null && property != null) || (datasource != null && property == null))
|
||||
throw new IllegalArgumentException("Datasource and property should be either null or not null at the same time");
|
||||
|
||||
if (datasource == this.datasource && ((metaPropertyPath != null && metaPropertyPath.toString().equals(property)) ||
|
||||
(metaPropertyPath == null && property == null)))
|
||||
return;
|
||||
|
||||
if (this.datasource != null) {
|
||||
metaProperty = null;
|
||||
metaPropertyPath = null;
|
||||
|
||||
component.setPropertyDataSource(null);
|
||||
|
||||
//noinspection unchecked
|
||||
this.datasource.removeItemChangeListener(weakItemChangeListener);
|
||||
weakItemChangeListener = null;
|
||||
|
||||
//noinspection unchecked
|
||||
this.datasource.removeItemPropertyChangeListener(weakItemPropertyChangeListener);
|
||||
weakItemPropertyChangeListener = null;
|
||||
|
||||
this.datasource = null;
|
||||
|
||||
if (itemWrapper != null) {
|
||||
itemWrapper.unsubscribe();
|
||||
}
|
||||
|
||||
timeZone = null;
|
||||
|
||||
disableBeanValidator();
|
||||
}
|
||||
|
||||
if (datasource != null) {
|
||||
//noinspection unchecked
|
||||
this.datasource = datasource;
|
||||
|
||||
MetaClass metaClass = datasource.getMetaClass();
|
||||
resolveMetaPropertyPath(metaClass, property);
|
||||
|
||||
if (metaProperty.getRange().isDatatype()
|
||||
&& metaProperty.getRange().asDatatype().getJavaClass().equals(Date.class)
|
||||
&& timeZone == null) {
|
||||
MetadataTools metadataTools = AppBeans.get(MetadataTools.class);
|
||||
Boolean ignoreUserTimeZone = metadataTools.getMetaAnnotationValue(metaProperty, IgnoreUserTimeZone.class);
|
||||
if (!Boolean.TRUE.equals(ignoreUserTimeZone)) {
|
||||
timeZone = userSession.getTimeZone();
|
||||
dateField.setTimeZone(timeZone);
|
||||
}
|
||||
}
|
||||
|
||||
itemChangeListener = e -> {
|
||||
if (updatingInstance) {
|
||||
return;
|
||||
}
|
||||
Date value = getEntityValue(e.getItem());
|
||||
setValueToFields(value);
|
||||
fireValueChanged(value);
|
||||
};
|
||||
weakItemChangeListener = new WeakItemChangeListener(datasource, itemChangeListener);
|
||||
//noinspection unchecked
|
||||
datasource.addItemChangeListener(weakItemChangeListener);
|
||||
|
||||
itemPropertyChangeListener = e -> {
|
||||
if (updatingInstance) {
|
||||
return;
|
||||
}
|
||||
if (!isBuffered() && e.getProperty().equals(metaPropertyPath.toString())) {
|
||||
setValueToFields((Date) e.getValue());
|
||||
fireValueChanged(e.getValue());
|
||||
}
|
||||
};
|
||||
|
||||
weakItemPropertyChangeListener = new WeakItemPropertyChangeListener(datasource, itemPropertyChangeListener);
|
||||
//noinspection unchecked
|
||||
datasource.addItemPropertyChangeListener(weakItemPropertyChangeListener);
|
||||
|
||||
if (datasource.getState() == Datasource.State.VALID && datasource.getItem() != null) {
|
||||
if (property.equals(metaPropertyPath.toString())) {
|
||||
Date value = getEntityValue(datasource.getItem());
|
||||
setValueToFields(value);
|
||||
fireValueChanged(value);
|
||||
}
|
||||
}
|
||||
|
||||
initRequired(metaPropertyPath);
|
||||
initDateFormat(metaProperty);
|
||||
|
||||
if (metaProperty.isReadOnly()) {
|
||||
setEditable(false);
|
||||
}
|
||||
|
||||
initBeanValidator();
|
||||
setDateRangeByProperty(metaProperty);
|
||||
}
|
||||
}*/
|
||||
|
||||
protected void initDateFormat(MetaProperty metaProperty) {
|
||||
TemporalType tt = null;
|
||||
if (metaProperty.getRange().asDatatype().getJavaClass().equals(java.sql.Date.class)) {
|
||||
tt = TemporalType.DATE;
|
||||
} else if (metaProperty.getAnnotations() != null) {
|
||||
tt = (TemporalType) metaProperty.getAnnotations().get(MetadataTools.TEMPORAL_ANN_NAME);
|
||||
}
|
||||
|
||||
setResolution(tt == TemporalType.DATE
|
||||
? DateField.Resolution.DAY
|
||||
: Resolution.MIN);
|
||||
|
||||
MessageTools messageTools = AppBeans.get(MessageTools.NAME);
|
||||
String formatStr = messageTools.getDefaultDateFormat(tt);
|
||||
setDateFormat(formatStr);
|
||||
}
|
||||
|
||||
protected void setDateRangeByProperty(MetaProperty metaProperty) {
|
||||
if (metaProperty.getAnnotations().get(Past.class.getName()) != null) {
|
||||
TimeSource timeSource = AppBeans.get(TimeSource.NAME);
|
||||
Date currentTimestamp = timeSource.currentTimestamp();
|
||||
|
||||
Calendar calendar = Calendar.getInstance(userSession.getLocale());
|
||||
calendar.setTime(currentTimestamp);
|
||||
calendar.set(Calendar.HOUR_OF_DAY, 23);
|
||||
calendar.set(Calendar.MINUTE, 59);
|
||||
calendar.set(Calendar.SECOND, 59);
|
||||
calendar.set(Calendar.MILLISECOND, 999);
|
||||
|
||||
setRangeEnd(calendar.getTime());
|
||||
} else if (metaProperty.getAnnotations().get(Future.class.getName()) != null) {
|
||||
TimeSource timeSource = AppBeans.get(TimeSource.NAME);
|
||||
Date currentTimestamp = timeSource.currentTimestamp();
|
||||
|
||||
Calendar calendar = Calendar.getInstance(userSession.getLocale());
|
||||
calendar.setTime(currentTimestamp);
|
||||
calendar.set(Calendar.HOUR_OF_DAY, 0);
|
||||
calendar.set(Calendar.MINUTE, 0);
|
||||
calendar.set(Calendar.SECOND, 0);
|
||||
calendar.set(Calendar.MILLISECOND, 0);
|
||||
if (metaProperty.getRange().asDatatype().getJavaClass().equals(java.sql.Date.class)) {
|
||||
calendar.add(Calendar.DATE, 1);
|
||||
}
|
||||
|
||||
setRangeStart(calendar.getTime());
|
||||
}
|
||||
}
|
||||
|
||||
protected Date getEntityValue(Entity item) {
|
||||
return InstanceUtils.getValueEx(item, getMetaPropertyPath().getPath());
|
||||
}
|
||||
|
||||
protected void fireValueChanged(Object value) {
|
||||
Object oldValue = internalValue;
|
||||
|
||||
if (!Objects.equals(oldValue, value)) {
|
||||
internalValue = (V) value;
|
||||
|
||||
if (hasValidationError()) {
|
||||
setValidationError(null);
|
||||
}
|
||||
|
||||
ValueChangeEvent event = new ValueChangeEvent(this, oldValue, value);
|
||||
getEventRouter().fireEvent(ValueChangeListener.class, ValueChangeListener::valueChanged, event);
|
||||
}
|
||||
}
|
||||
|
||||
protected Date constructDate() {
|
||||
final Date datePickerDate = dateField.getValue();
|
||||
if (datePickerDate == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Calendar dateCalendar = Calendar.getInstance(userSession.getLocale());
|
||||
if (timeZone != null) {
|
||||
dateCalendar.setTimeZone(timeZone);
|
||||
}
|
||||
dateCalendar.setTime(datePickerDate);
|
||||
if (timeField.getValue() == null) {
|
||||
dateCalendar.set(Calendar.HOUR_OF_DAY, 0);
|
||||
dateCalendar.set(Calendar.MINUTE, 0);
|
||||
dateCalendar.set(Calendar.SECOND, 0);
|
||||
} else {
|
||||
Calendar timeCalendar = Calendar.getInstance(userSession.getLocale());
|
||||
timeCalendar.setTime(timeField.<Date>getValue());
|
||||
|
||||
dateCalendar.set(Calendar.HOUR_OF_DAY, timeCalendar.get(Calendar.HOUR_OF_DAY));
|
||||
dateCalendar.set(Calendar.MINUTE, timeCalendar.get(Calendar.MINUTE));
|
||||
dateCalendar.set(Calendar.SECOND, timeCalendar.get(Calendar.SECOND));
|
||||
}
|
||||
|
||||
Date resultDate = dateCalendar.getTime();
|
||||
|
||||
if (getMetaProperty() != null) {
|
||||
Class javaClass = getMetaProperty().getRange().asDatatype().getJavaClass();
|
||||
if (javaClass.equals(java.sql.Date.class)) {
|
||||
return new java.sql.Date(resultDate.getTime());
|
||||
} else if (javaClass.equals(Time.class)) {
|
||||
return new Time(resultDate.getTime());
|
||||
} else {
|
||||
return resultDate;
|
||||
}
|
||||
} else {
|
||||
return resultDate;
|
||||
}
|
||||
}
|
||||
|
||||
protected Date extractTime(Date date) {
|
||||
Calendar dateCalendar = Calendar.getInstance(userSession.getLocale());
|
||||
if (timeZone != null) {
|
||||
dateCalendar.setTimeZone(timeZone);
|
||||
}
|
||||
dateCalendar.setTime(date);
|
||||
|
||||
Calendar timeCalendar = Calendar.getInstance(userSession.getLocale());
|
||||
timeCalendar.setTimeInMillis(0);
|
||||
|
||||
timeCalendar.set(Calendar.HOUR_OF_DAY, dateCalendar.get(Calendar.HOUR_OF_DAY));
|
||||
timeCalendar.set(Calendar.MINUTE, dateCalendar.get(Calendar.MINUTE));
|
||||
timeCalendar.set(Calendar.SECOND, dateCalendar.get(Calendar.SECOND));
|
||||
|
||||
return timeCalendar.getTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setEditableToComponent(boolean editable) {
|
||||
timeField.setEditable(editable);
|
||||
dateField.setReadOnly(!editable);
|
||||
public boolean isEditable() {
|
||||
return editable;
|
||||
}
|
||||
|
||||
component.setCompositionReadOnly(!editable);
|
||||
@Override
|
||||
public void setEditable(boolean editable) {
|
||||
if (this.editable == editable) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.editable = editable;
|
||||
|
||||
boolean parentEditable = true;
|
||||
if (parent instanceof ChildEditableController) {
|
||||
parentEditable = ((ChildEditableController) parent).isEditable();
|
||||
}
|
||||
boolean finalEditable = parentEditable && editable;
|
||||
|
||||
setEditableToComponent(finalEditable);
|
||||
}
|
||||
|
||||
protected void setEditableToComponent(boolean editable) {
|
||||
timeField.setReadOnly(!editable);
|
||||
dateField.setReadOnly(!editable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void focus() {
|
||||
component.focus();
|
||||
dateField.focus();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -674,4 +473,132 @@ public class WebDateField<V extends Date> extends WebAbstractField<CubaDateField
|
||||
dateField.setTabIndex(tabIndex);
|
||||
timeField.setTabIndex(tabIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRequired() {
|
||||
return dateField.isRequiredIndicatorVisible();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRequired(boolean required) {
|
||||
dateField.setRequiredIndicatorVisible(required);
|
||||
timeField.setRequiredIndicatorVisible(required);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRequiredMessage() {
|
||||
// VAADIN8: gg, implement
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRequiredMessage(String msg) {
|
||||
// VAADIN8: gg, implement
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addValidator(Validator validator) {
|
||||
if (validators == null) {
|
||||
validators = new ArrayList<>(VALIDATORS_LIST_INITIAL_CAPACITY);
|
||||
}
|
||||
if (!validators.contains(validator)) {
|
||||
validators.add(validator);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeValidator(Validator validator) {
|
||||
if (validators != null) {
|
||||
validators.remove(validator);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Validator> getValidators() {
|
||||
if (validators == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Collections.unmodifiableCollection(validators);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
try {
|
||||
validate();
|
||||
return true;
|
||||
} catch (ValidationException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate() throws ValidationException {
|
||||
if (hasValidationError()) {
|
||||
setValidationError(null);
|
||||
}
|
||||
|
||||
if (!isVisibleRecursive() || !isEditableWithParent() || !isEnabledRecursive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isEmpty()) {
|
||||
if (isRequired()) {
|
||||
throw new RequiredValueMissingException(getRequiredMessage(), this);
|
||||
} else {
|
||||
// vaadin8 rework this PL-10701
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
V value = getValue();
|
||||
triggerValidators(value);
|
||||
}
|
||||
|
||||
protected void triggerValidators(V value) throws ValidationFailedException {
|
||||
if (validators != null) {
|
||||
try {
|
||||
for (Validator validator : validators) {
|
||||
validator.validate(value);
|
||||
}
|
||||
} catch (ValidationException e) {
|
||||
setValidationError(e.getDetailsMessage());
|
||||
|
||||
throw new ValidationFailedException(e.getDetailsMessage(), this, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContextHelpText() {
|
||||
// VAADIN8: gg, implement
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContextHelpText(String contextHelpText) {
|
||||
// VAADIN8: gg, implement
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isContextHelpTextHtmlEnabled() {
|
||||
// VAADIN8: gg, implement
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContextHelpTextHtmlEnabled(boolean enabled) {
|
||||
// VAADIN8: gg, implement
|
||||
}
|
||||
|
||||
@Override
|
||||
public Consumer<ContextHelpIconClickEvent> getContextHelpIconClickHandler() {
|
||||
// VAADIN8: gg, implement
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContextHelpIconClickHandler(Consumer<ContextHelpIconClickEvent> handler) {
|
||||
// VAADIN8: gg, implement
|
||||
}
|
||||
}
|
@ -62,20 +62,7 @@ public class WebDatePicker<V extends Date> extends WebV8AbstractField<InlineDate
|
||||
Preconditions.checkNotNullArgument(resolution);
|
||||
|
||||
this.resolution = resolution;
|
||||
DateResolution vResolution;
|
||||
switch (resolution) {
|
||||
case MONTH:
|
||||
vResolution = com.vaadin.shared.ui.datefield.DateResolution.MONTH;
|
||||
break;
|
||||
case YEAR:
|
||||
vResolution = com.vaadin.shared.ui.datefield.DateResolution.YEAR;
|
||||
break;
|
||||
case DAY:
|
||||
default:
|
||||
vResolution = com.vaadin.shared.ui.datefield.DateResolution.DAY;
|
||||
break;
|
||||
}
|
||||
|
||||
DateResolution vResolution = WebComponentsHelper.convertDateResolution(resolution);
|
||||
component.setResolution(vResolution);
|
||||
}
|
||||
|
||||
@ -116,100 +103,6 @@ public class WebDatePicker<V extends Date> extends WebV8AbstractField<InlineDate
|
||||
}
|
||||
}*/
|
||||
|
||||
/* todo
|
||||
@Override
|
||||
public void setDatasource(Datasource datasource, String property) {
|
||||
if ((datasource == null && property != null) || (datasource != null && property == null))
|
||||
throw new IllegalArgumentException("Datasource and property should be either null or not null at the same time");
|
||||
|
||||
if (datasource == this.datasource && ((metaPropertyPath != null && metaPropertyPath.toString().equals(property)) ||
|
||||
(metaPropertyPath == null && property == null)))
|
||||
return;
|
||||
|
||||
if (this.datasource != null) {
|
||||
metaProperty = null;
|
||||
metaPropertyPath = null;
|
||||
|
||||
component.setPropertyDataSource(null);
|
||||
|
||||
//noinspection unchecked
|
||||
this.datasource.removeItemChangeListener(weakItemChangeListener);
|
||||
weakItemChangeListener = null;
|
||||
|
||||
//noinspection unchecked
|
||||
this.datasource.removeItemPropertyChangeListener(weakItemPropertyChangeListener);
|
||||
weakItemPropertyChangeListener = null;
|
||||
|
||||
this.datasource = null;
|
||||
|
||||
if (itemWrapper != null) {
|
||||
itemWrapper.unsubscribe();
|
||||
}
|
||||
|
||||
disableBeanValidator();
|
||||
}
|
||||
|
||||
if (datasource != null) {
|
||||
//noinspection unchecked
|
||||
this.datasource = datasource;
|
||||
|
||||
MetaClass metaClass = datasource.getMetaClass();
|
||||
resolveMetaPropertyPath(metaClass, property);
|
||||
|
||||
component.addValueChangeListener(event -> {
|
||||
if (!checkRange(component.getValue())) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateInstance();
|
||||
});
|
||||
|
||||
itemChangeListener = e -> {
|
||||
if (updatingInstance) {
|
||||
return;
|
||||
}
|
||||
Date value = getEntityValue(e.getItem());
|
||||
setValueToFields(value);
|
||||
fireValueChanged(value);
|
||||
};
|
||||
|
||||
weakItemChangeListener = new WeakItemChangeListener(datasource, itemChangeListener);
|
||||
//noinspection unchecked
|
||||
datasource.addItemChangeListener(weakItemChangeListener);
|
||||
|
||||
itemPropertyChangeListener = e -> {
|
||||
if (updatingInstance) {
|
||||
return;
|
||||
}
|
||||
if (e.getProperty().equals(metaPropertyPath.toString())) {
|
||||
setValueToFields((Date) e.getValue());
|
||||
fireValueChanged(e.getValue());
|
||||
}
|
||||
};
|
||||
|
||||
weakItemPropertyChangeListener = new WeakItemPropertyChangeListener(datasource, itemPropertyChangeListener);
|
||||
//noinspection unchecked
|
||||
datasource.addItemPropertyChangeListener(weakItemPropertyChangeListener);
|
||||
|
||||
if (datasource.getState() == Datasource.State.VALID && datasource.getItem() != null) {
|
||||
if (property.equals(metaPropertyPath.toString())) {
|
||||
Date value = getEntityValue(datasource.getItem());
|
||||
setValueToFields(value);
|
||||
fireValueChanged(value);
|
||||
}
|
||||
}
|
||||
|
||||
initRequired(metaPropertyPath);
|
||||
|
||||
if (metaProperty.isReadOnly()) {
|
||||
setEditable(false);
|
||||
}
|
||||
|
||||
initBeanValidator();
|
||||
setDateRangeByProperty(metaProperty);
|
||||
}
|
||||
}*/
|
||||
|
||||
@Override
|
||||
protected void valueBindingConnected(ValueSource<V> valueSource) {
|
||||
super.valueBindingConnected(valueSource);
|
||||
@ -248,12 +141,12 @@ public class WebDatePicker<V extends Date> extends WebV8AbstractField<InlineDate
|
||||
|
||||
@Override
|
||||
protected LocalDate convertToPresentation(Date modelValue) throws ConversionException {
|
||||
return DateTimeUtils.asLocalDate(modelValue);
|
||||
return modelValue != null ? DateTimeUtils.asLocalDate(modelValue) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getRangeStart() {
|
||||
return DateTimeUtils.asDate(component.getRangeStart());
|
||||
return component.getRangeStart() != null ? DateTimeUtils.asDate(component.getRangeStart()) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -263,7 +156,7 @@ public class WebDatePicker<V extends Date> extends WebV8AbstractField<InlineDate
|
||||
|
||||
@Override
|
||||
public Date getRangeEnd() {
|
||||
return DateTimeUtils.asDate(component.getRangeEnd());
|
||||
return component.getRangeEnd() != null ? DateTimeUtils.asDate(component.getRangeEnd()) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -45,11 +45,6 @@ public class WebProgressBar extends WebAbstractViewComponent<com.vaadin.ui.Progr
|
||||
component.setValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Double convertToModel(Float componentRawValue) throws ConversionException {
|
||||
return componentRawValue != null ? componentRawValue.doubleValue() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Float convertToPresentation(Double modelValue) throws ConversionException {
|
||||
return modelValue != null ? modelValue.floatValue() : DEFAULT_VALUE;
|
||||
|
@ -16,16 +16,19 @@
|
||||
*/
|
||||
package com.haulmont.cuba.web.gui.components;
|
||||
|
||||
import com.haulmont.bali.util.DateTimeUtils;
|
||||
import com.haulmont.bali.util.Preconditions;
|
||||
import com.haulmont.chile.core.datatypes.Datatypes;
|
||||
import com.haulmont.chile.core.model.MetaProperty;
|
||||
import com.haulmont.chile.core.model.MetaPropertyPath;
|
||||
import com.haulmont.cuba.core.global.UserSessionSource;
|
||||
import com.haulmont.cuba.gui.components.DateField;
|
||||
import com.haulmont.cuba.gui.components.TimeField;
|
||||
import com.haulmont.cuba.gui.components.data.ConversionException;
|
||||
import com.haulmont.cuba.gui.components.data.EntityValueSource;
|
||||
import com.haulmont.cuba.gui.components.data.ValueSource;
|
||||
import com.haulmont.cuba.gui.components.data.value.DatasourceValueSource;
|
||||
import com.haulmont.cuba.web.widgets.CubaTimeField;
|
||||
import com.haulmont.cuba.web.widgets.client.timefield.TimeResolution;
|
||||
import com.haulmont.cuba.gui.theme.ThemeConstants;
|
||||
import com.haulmont.cuba.web.App;
|
||||
import com.haulmont.cuba.web.widgets.CubaMaskedTextField;
|
||||
@ -34,262 +37,99 @@ import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
import java.sql.Time;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.LocalTime;
|
||||
import java.util.Date;
|
||||
|
||||
public class WebTimeField extends WebV8AbstractField<CubaMaskedTextField, String, Date>
|
||||
public class WebTimeField extends WebV8AbstractField<CubaTimeField, LocalTime, Date>
|
||||
implements TimeField, InitializingBean {
|
||||
|
||||
protected boolean showSeconds;
|
||||
|
||||
protected String placeholder;
|
||||
protected String timeFormat;
|
||||
protected DateField.Resolution resolution;
|
||||
protected Resolution resolution = Resolution.MIN;
|
||||
|
||||
public WebTimeField() {
|
||||
resolution = DateField.Resolution.MIN;
|
||||
|
||||
component = new CubaMaskedTextField();
|
||||
component.setMaskedMode(true);
|
||||
component.setTimeMask(true);
|
||||
component = new CubaTimeField();
|
||||
|
||||
attachValueChangeListener(component);
|
||||
|
||||
// vaadin8
|
||||
// component.setInvalidAllowed(false);
|
||||
// component.setInvalidCommitted(true);
|
||||
/*component.addValidator(value -> {
|
||||
if (!(!(value instanceof String) || checkStringValue((String) value))) {
|
||||
component.markAsDirty();
|
||||
throw new com.vaadin.v7.data.Validator.InvalidValueException("Unable to parse value: " + value);
|
||||
}
|
||||
});*/
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
UserSessionSource userSessionSource = applicationContext.getBean(UserSessionSource.class);
|
||||
timeFormat = Datatypes.getFormatStringsNN(userSessionSource.getLocale()).getTimeFormat();
|
||||
setShowSeconds(timeFormat.contains("ss"));
|
||||
|
||||
String timeFormat = Datatypes.getFormatStringsNN(userSessionSource.getLocale()).getTimeFormat();
|
||||
setFormat(timeFormat);
|
||||
}
|
||||
|
||||
public boolean isAmPmUsed() {
|
||||
return timeFormat.contains("a");
|
||||
}
|
||||
|
||||
protected void updateWidth() {
|
||||
if (!App.isBound()) {
|
||||
return;
|
||||
}
|
||||
|
||||
App app = App.getInstance();
|
||||
|
||||
ThemeConstants theme = app.getThemeConstants();
|
||||
int digitWidth = theme.getInt("cuba.web.WebTimeField.digitWidth");
|
||||
int digitPadding = theme.getInt("cuba.web.WebTimeField.digitPadding");
|
||||
int separatorWidth = theme.getInt("cuba.web.WebTimeField.separatorWidth");
|
||||
|
||||
int partsCount = isAmPmUsed() ? 1 : 0;
|
||||
int newWidth = isAmPmUsed() ? digitWidth + digitPadding : digitPadding;
|
||||
if (showSeconds) {
|
||||
newWidth = newWidth + digitWidth;
|
||||
partsCount += 1;
|
||||
}
|
||||
switch (resolution) {
|
||||
case HOUR:
|
||||
partsCount += 1;
|
||||
newWidth = digitWidth + newWidth;
|
||||
break;
|
||||
case MIN:
|
||||
case SEC:
|
||||
partsCount += 2;
|
||||
newWidth = digitWidth * 2 + newWidth;
|
||||
break;
|
||||
}
|
||||
|
||||
newWidth += (partsCount - 1) * separatorWidth;
|
||||
|
||||
component.setWidth(newWidth + "px");
|
||||
}
|
||||
|
||||
// VAADIN8: gg, do we need this method?
|
||||
protected boolean checkStringValue(String value) {
|
||||
if (value.equals(placeholder) || StringUtils.isEmpty(value))
|
||||
return true;
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(timeFormat);
|
||||
sdf.setLenient(false);
|
||||
try {
|
||||
sdf.parse(value);
|
||||
return true;
|
||||
} catch (ParseException e) {
|
||||
return false;
|
||||
}
|
||||
// FIXME: gg, actually, not working
|
||||
return component.getTimeFormat().contains("a");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Date convertToModel(String componentRawValue) throws ConversionException {
|
||||
if (StringUtils.isNotEmpty(componentRawValue) && !componentRawValue.equals(placeholder)) {
|
||||
try {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(timeFormat);
|
||||
sdf.setLenient(false);
|
||||
|
||||
Date date = sdf.parse(componentRawValue);
|
||||
if (component.getComponentError() != null) {
|
||||
component.setComponentError(null);
|
||||
}
|
||||
|
||||
ValueSource<Date> valueSource = getValueSource();
|
||||
if (valueSource instanceof EntityValueSource) {
|
||||
MetaPropertyPath metaPropertyPath = ((DatasourceValueSource) valueSource).getMetaPropertyPath();
|
||||
MetaProperty metaProperty = metaPropertyPath.getMetaProperty();
|
||||
if (metaProperty != null) {
|
||||
Class javaClass = metaProperty.getRange().asDatatype().getJavaClass();
|
||||
if (javaClass.equals(java.sql.Time.class)) {
|
||||
return new Time(date.getTime());
|
||||
}
|
||||
if (javaClass.equals(java.sql.Date.class)) {
|
||||
LoggerFactory.getLogger(WebTimeField.class).warn("Do not use java.sql.Date with time field");
|
||||
return new java.sql.Date(date.getTime());
|
||||
}
|
||||
}
|
||||
}
|
||||
return date;
|
||||
} catch (Exception e) {
|
||||
LoggerFactory.getLogger(WebTimeField.class)
|
||||
.debug("Unable to parse value of component {}:\n{}", getId(), e.getMessage());
|
||||
throw new ConversionException("Invalid value");
|
||||
}
|
||||
} else {
|
||||
protected Date convertToModel(LocalTime componentRawValue) throws ConversionException {
|
||||
if (componentRawValue == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String convertToPresentation(Date modelValue) throws ConversionException {
|
||||
if (modelValue != null) {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(timeFormat);
|
||||
return sdf.format(modelValue);
|
||||
} else {
|
||||
return "";
|
||||
Date date = DateTimeUtils.asDate(componentRawValue);
|
||||
|
||||
ValueSource<Date> valueSource = getValueSource();
|
||||
if (valueSource instanceof EntityValueSource) {
|
||||
MetaPropertyPath metaPropertyPath = ((DatasourceValueSource) valueSource).getMetaPropertyPath();
|
||||
MetaProperty metaProperty = metaPropertyPath.getMetaProperty();
|
||||
if (metaProperty != null) {
|
||||
Class javaClass = metaProperty.getRange().asDatatype().getJavaClass();
|
||||
if (javaClass.equals(java.sql.Time.class)) {
|
||||
return new Time(date.getTime());
|
||||
}
|
||||
|
||||
if (javaClass.equals(java.sql.Date.class)) {
|
||||
LoggerFactory.getLogger(WebTimeField.class).warn("Do not use java.sql.Date with time field");
|
||||
return new java.sql.Date(date.getTime());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return date;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getShowSeconds() {
|
||||
return showSeconds;
|
||||
protected LocalTime convertToPresentation(Date modelValue) throws ConversionException {
|
||||
return modelValue != null ? DateTimeUtils.asLocalTime(modelValue) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFormat(String format) {
|
||||
timeFormat = format;
|
||||
showSeconds = timeFormat.contains("ss");
|
||||
updateTimeFormat();
|
||||
updateWidth();
|
||||
component.setTimeFormat(format);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFormat() {
|
||||
return timeFormat;
|
||||
return component.getTimeFormat();
|
||||
}
|
||||
|
||||
public void setResolution(DateField.Resolution resolution) {
|
||||
@Override
|
||||
public Resolution getResolution() {
|
||||
return resolution;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResolution(Resolution resolution) {
|
||||
Preconditions.checkNotNullArgument(resolution);
|
||||
|
||||
this.resolution = resolution;
|
||||
if (resolution.ordinal() <= DateField.Resolution.SEC.ordinal()) {
|
||||
setShowSeconds(true);
|
||||
} else if (resolution.ordinal() <= DateField.Resolution.MIN.ordinal()) {
|
||||
setShowSeconds(false);
|
||||
} else if (resolution.ordinal() <= DateField.Resolution.HOUR.ordinal()) {
|
||||
StringBuilder builder = new StringBuilder(timeFormat);
|
||||
if (timeFormat.contains("mm")) {
|
||||
int minutesIndex = builder.indexOf("mm");
|
||||
builder.delete(minutesIndex > 0 ? --minutesIndex : minutesIndex, minutesIndex + 3);
|
||||
timeFormat = builder.toString();
|
||||
}
|
||||
setShowSeconds(false);
|
||||
}
|
||||
TimeResolution vResolution = WebComponentsHelper.convertTimeResolution(resolution);
|
||||
component.setResolution(vResolution);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getShowSeconds() {
|
||||
return resolution == Resolution.SEC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShowSeconds(boolean showSeconds) {
|
||||
this.showSeconds = showSeconds;
|
||||
if (showSeconds) {
|
||||
if (!timeFormat.contains("ss")) {
|
||||
int minutesIndex = timeFormat.indexOf("mm");
|
||||
StringBuilder builder = new StringBuilder(timeFormat);
|
||||
builder.insert(minutesIndex + 2, ":ss");
|
||||
timeFormat = builder.toString();
|
||||
}
|
||||
} else {
|
||||
if (timeFormat.contains("ss")) {
|
||||
int secondsIndex = timeFormat.indexOf("ss");
|
||||
StringBuilder builder = new StringBuilder(timeFormat);
|
||||
builder.delete(secondsIndex > 0 ? --secondsIndex : secondsIndex, secondsIndex + 3);
|
||||
timeFormat = builder.toString();
|
||||
}
|
||||
}
|
||||
updateTimeFormat();
|
||||
updateWidth();
|
||||
setResolution(Resolution.SEC);
|
||||
}
|
||||
|
||||
protected void updateTimeFormat() {
|
||||
String mask = StringUtils.replaceChars(timeFormat, "Hhmsa", "####U");
|
||||
placeholder = StringUtils.replaceChars(mask, "#U", "__");
|
||||
component.setMask(mask);
|
||||
// vaadin8
|
||||
// component.setNullRepresentation(placeholder);
|
||||
}
|
||||
|
||||
// vaadin8
|
||||
/* todo
|
||||
@Override
|
||||
protected ItemWrapper createDatasourceWrapper(Datasource datasource, Collection<MetaPropertyPath> propertyPaths) {
|
||||
return new ItemWrapper(datasource, datasource.getMetaClass(), propertyPaths) {
|
||||
private static final long serialVersionUID = 1729450322469573679L;
|
||||
|
||||
@Override
|
||||
protected PropertyWrapper createPropertyWrapper(Object item, MetaPropertyPath propertyPath) {
|
||||
return new PropertyWrapper(item, propertyPath) {
|
||||
private static final long serialVersionUID = -4481934193197224070L;
|
||||
|
||||
@Override
|
||||
public String getFormattedValue() {
|
||||
Object value = this.getValue();
|
||||
if (value instanceof Date) {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(timeFormat);
|
||||
return sdf.format(value);
|
||||
}
|
||||
|
||||
return super.getFormattedValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object valueOf(Object newValue) throws Converter.ConversionException {
|
||||
if (newValue instanceof String) {
|
||||
if (StringUtils.isNotEmpty((String) newValue) && !newValue.equals(placeholder)) {
|
||||
try {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(timeFormat);
|
||||
Date date = sdf.parse((String) newValue);
|
||||
if (component.getComponentError() != null) {
|
||||
component.setComponentError(null);
|
||||
}
|
||||
return date;
|
||||
} catch (Exception e) {
|
||||
LoggerFactory.getLogger(WebTimeField.class).debug("Unable to parse value of component " + getId() + "\n" + e.getMessage());
|
||||
component.setComponentError(new UserError("Invalid value"));
|
||||
return null;
|
||||
}
|
||||
} else
|
||||
return null;
|
||||
} else
|
||||
return newValue;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}*/
|
||||
|
||||
@Override
|
||||
public void focus() {
|
||||
component.focus();
|
||||
|
@ -15,37 +15,39 @@
|
||||
*
|
||||
*/
|
||||
|
||||
$c-default-datefield-width: 120px;
|
||||
|
||||
@mixin halo-datefield($primary-stylename : c-datefield) {
|
||||
.#{$primary-stylename}-layout {
|
||||
white-space: nowrap;
|
||||
outline: none;
|
||||
|
||||
.c-maskedfield {
|
||||
.c-timefield {
|
||||
margin-left: $v-layout-spacing-horizontal/2;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$primary-stylename}-composition.v-has-width {
|
||||
display: inline-table !important;
|
||||
.#{$primary-stylename}-layout.v-has-width {
|
||||
display: inline-table;
|
||||
|
||||
.#{$primary-stylename}-layout {
|
||||
display: table-row;
|
||||
.#{$primary-stylename} {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.#{$primary-stylename},
|
||||
.c-maskedfield {
|
||||
display: table-cell;
|
||||
}
|
||||
.#{$primary-stylename},
|
||||
.c-timefield {
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
.v-ie9 & .c-maskedfield {
|
||||
float: right;
|
||||
}
|
||||
.v-ie9 & .c-timefield {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$primary-stylename}-composition {
|
||||
.#{$primary-stylename}-layout {
|
||||
&.borderless {
|
||||
.v-datefield,
|
||||
.c-maskedfield,
|
||||
.c-timefield,
|
||||
[class*="textfield"],
|
||||
[class*="button"] {
|
||||
@include valo-textfield-borderless-style;
|
||||
@ -54,7 +56,7 @@
|
||||
|
||||
&.tiny {
|
||||
.v-datefield,
|
||||
.c-maskedfield {
|
||||
.c-timefield {
|
||||
@include valo-datefield-style($unit-size: $v-unit-size--tiny, $bevel: null, $shadow: null, $border: null, $background-color: null, $states: normal);
|
||||
font-size: $v-font-size--tiny;
|
||||
}
|
||||
@ -62,7 +64,7 @@
|
||||
|
||||
&.small {
|
||||
.v-datefield,
|
||||
.c-maskedfield {
|
||||
.c-timefield {
|
||||
@include valo-datefield-style($unit-size: $v-unit-size--small, $bevel: null, $shadow: null, $border: null, $background-color: null, $states: normal);
|
||||
font-size: $v-font-size--small;
|
||||
}
|
||||
@ -95,7 +97,7 @@
|
||||
}
|
||||
|
||||
.#{$primary-stylename}-composition-error {
|
||||
.c-maskedfield,
|
||||
.c-timefield,
|
||||
.v-datefield-textfield {
|
||||
@include valo-textfield-error-style;
|
||||
}
|
||||
@ -109,4 +111,14 @@
|
||||
.#{$primary-stylename} .v-datefield-calendarpanel-day.v-datefield-calendarpanel-day-focused {
|
||||
border-color: $v-focus-color;
|
||||
}
|
||||
|
||||
.#{$primary-stylename}.v-datefield-day,
|
||||
.#{$primary-stylename}.v-datefield-month,
|
||||
.#{$primary-stylename}.v-datefield-year {
|
||||
width: $c-default-datefield-width;
|
||||
}
|
||||
|
||||
.#{$primary-stylename}.v-datefield-popup {
|
||||
@include valo-datefield-popup-style;
|
||||
}
|
||||
}
|
56
modules/web/themes/halo/components/timefield/timefield.scss
Normal file
56
modules/web/themes/halo/components/timefield/timefield.scss
Normal file
@ -0,0 +1,56 @@
|
||||
/*!
|
||||
* Copyright (c) 2008-2018 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
$c-timefield-hour-width: 32px;
|
||||
$c-timefield-minute-width: 58px;
|
||||
$c-timefield-second-width: 84px;
|
||||
|
||||
@mixin halo-cuba-timefield($primary-stylename: c-timefield) {
|
||||
.#{$primary-stylename} {
|
||||
@include box-defaults;
|
||||
@include valo-textfield-style;
|
||||
height: $v-unit-size;
|
||||
}
|
||||
|
||||
.#{$primary-stylename}.v-readonly {
|
||||
@include valo-textfield-readonly-style;
|
||||
}
|
||||
|
||||
.#{$primary-stylename}-error {
|
||||
@include valo-textfield-error-style;
|
||||
}
|
||||
|
||||
.#{$primary-stylename}-hour {
|
||||
width: $c-timefield-hour-width;
|
||||
}
|
||||
|
||||
.#{$primary-stylename}-minute {
|
||||
width: $c-timefield-minute-width;
|
||||
}
|
||||
|
||||
.#{$primary-stylename}-second {
|
||||
width: $c-timefield-second-width;
|
||||
}
|
||||
|
||||
// fixme: gg, do we need the following styles?
|
||||
.c-focus-move::selection {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.c-focus-move::-moz-selection {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
@ -42,6 +42,7 @@
|
||||
@import "components/textarea/textarea";
|
||||
@import "components/tokenlist/tokenlist";
|
||||
@import "components/datefield/datefield";
|
||||
@import "components/timefield/timefield";
|
||||
@import "components/currencyfield/currencyfield";
|
||||
@import "components/datepicker/datepicker";
|
||||
@import "components/grouptable/grouptable";
|
||||
@ -166,6 +167,7 @@
|
||||
@include halo-cuba-fileupload-wrapper;
|
||||
@include halo-cuba-suggestionfield;
|
||||
@include halo-cuba-capslockindicator;
|
||||
@include halo-cuba-timefield;
|
||||
|
||||
@include cuba-log-control;
|
||||
@include cuba-login-window;
|
||||
|
@ -20,7 +20,7 @@
|
||||
white-space: nowrap;
|
||||
outline: none;
|
||||
|
||||
.c-maskedfield {
|
||||
.c-timefield {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
@ -32,11 +32,11 @@
|
||||
display: table-row;
|
||||
|
||||
.#{$primary-stylename},
|
||||
.c-maskedfield {
|
||||
.c-timefield {
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
.v-ie9 & .c-maskedfield {
|
||||
.v-ie9 & .c-timefield {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,42 @@
|
||||
/*!
|
||||
* Copyright (c) 2008-2018 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
@mixin havana-cuba-timefield($primary-stylename: c-timefield) {
|
||||
@include havana-textfield($primary-stylename);
|
||||
|
||||
.v-ui,
|
||||
.v-window {
|
||||
input.#{$primary-stylename}-empty {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
input.#{$primary-stylename}-empty:focus {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
input.#{$primary-stylename}-empty.v-readonly:focus {
|
||||
color: #ccc;
|
||||
}
|
||||
}
|
||||
|
||||
.c-focus-move::selection {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.c-focus-move::-moz-selection {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
@ -46,6 +46,7 @@
|
||||
@import "components/groupbox/groupbox";
|
||||
@import "components/textfield/textfield";
|
||||
@import "components/datefield/datefield";
|
||||
@import "components/timefield/timefield";
|
||||
@import "components/treetable/treetable";
|
||||
@import "components/popupview/popupview";
|
||||
@import "components/fieldgroup/fieldgroup";
|
||||
@ -169,6 +170,7 @@
|
||||
@include havana-cuba-fileupload-wrapper;
|
||||
@include havana-cuba-image;
|
||||
@include havana-cuba-capslockindicator;
|
||||
@include havana-cuba-timefield;
|
||||
|
||||
@include cuba-login-window;
|
||||
@include cuba-menubar;
|
||||
|
Loading…
Reference in New Issue
Block a user