Migrate DateField
This commit is contained in:
Gleb Gorelov 2018-06-08 19:29:03 +04:00
parent b47218b96f
commit 471e84d16a
32 changed files with 1205 additions and 1035 deletions

View File

@ -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);
}
/**

View File

@ -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}

View File

@ -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();

View File

@ -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);
}
}

View File

@ -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"/>

View File

@ -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);

View File

@ -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 {
}
}
}
}
}*/
}

View File

@ -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();
}
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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) {

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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 = "";
}

View File

@ -32,7 +32,4 @@ public class CubaMaskedTextFieldState extends CubaTextFieldState {
@NoLayout
public boolean sendNullRepresentation = true;
@NoLayout
public boolean isTimeMask = false;
}

View File

@ -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;
}

View File

@ -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
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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
}
}

View File

@ -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

View File

@ -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;

View File

@ -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();

View File

@ -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;
}
}

View 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;
}
}

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;