/*
* This file is part of LibrePlan
*
* Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e
* Desenvolvemento Tecnolóxico de Galicia
* Copyright (C) 2010-2012 Igalia, S.L.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.libreplan.web.orders;
import static org.libreplan.web.I18nHelper._;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.Date;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.libreplan.web.common.Util;
import org.zkoss.ganttz.util.ComponentsFinder;
import org.zkoss.util.Locales;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.WrongValueException;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.util.GenericForwardComposer;
import org.zkoss.zul.Datebox;
import org.zkoss.zul.Hbox;
import org.zkoss.zul.Textbox;
/**
* Textbox component which is transformed into a Datebox picker on demand.
* <br />
*
* @author Susana Montes Pedreira <smontes@wirelessgalicia.com>
* @author Lorenzo Tilve Álvaro <ltilve@igalia.com>
* @author Manuel Rego Casasnovas <mrego@igalia.com>
* @author Jacobo Aragunde Pérez <jaragunde@igalia.com>
*/
public class DynamicDatebox extends GenericForwardComposer {
final Getter<Date> getter;
final Setter<Date> setter;
private Textbox dateTextBox;
private Datebox dateBox;
private DateFormat dateFormat;
private boolean disabled = false;
public DynamicDatebox(Getter<Date> getter, Setter<Date> setter) {
this.setter = setter;
this.getter = getter;
this.dateFormat = DateFormat.getDateInstance(DateFormat.SHORT, Locales.getCurrent());
}
private Datebox createDateBox() {
dateBox = new Datebox();
dateBox.setFormat("short");
dateBox.setValue(getter.get());
registerOnEnterOpenDateBox(dateBox);
registerBlurListener(dateBox);
registerOnChange(dateBox);
dateTextBox.getParent().appendChild(dateBox);
return dateBox;
}
private Datebox getDateBox() {
return dateBox;
}
/**
* When a text box associated to a datebox is requested to show the datebox, the corresponding datebox is shown.
*
* @param component
* the component that has received focus
*/
private void userWantsDateBox(Component component) {
if (component == dateTextBox) {
showDateBox(dateTextBox);
}
}
private void showDateBox(Textbox associatedTextBox) {
associatedTextBox.setVisible(false);
getDateBox();
createDateBox();
dateBox.setFocus(true);
dateBox.setOpen(true);
}
/**
* When the dateBox loses focus the corresponding textbox is shown instead.
*
* @param currentDateBox
* the component that has lost focus
*/
private void dateBoxHasLostFocus(Datebox currentDateBox) {
if (currentDateBox == dateBox) {
hideDateBox(dateTextBox);
}
}
private void hideDateBox(Textbox associatedTextBox) {
dateBox.detach();
associatedTextBox.setVisible(true);
}
public void doAfterCompose(Component component) throws Exception {
findComponents((Hbox) component);
registerListeners();
updateComponents();
applyDisabledToElements(disabled);
}
private void registerListeners() {
registerOnEnterListener(dateTextBox);
Util.bind(
dateTextBox,
new Util.Getter<String>() {
@Override
public String get() {
return asString(getter.get());
}
},
new Util.Setter<String>() {
@Override
public void set(String string) {
try {
setter.set(fromString(string));
} catch (ParseException e) {
throw new WrongValueException(
dateTextBox,
_("Date format is wrong. Please, use the following format: {0}", asString(new Date())));
}
}
});
}
private void findComponents(Hbox hbox) {
List<Component> children = hbox.getChildren();
assert children.size() == 1;
dateTextBox = findTextBoxOfCell(children);
}
private static Textbox findTextBoxOfCell(List<Component> children) {
return ComponentsFinder.findComponentsOfType(Textbox.class, children).get(0);
}
private void registerOnChange(Component component) {
component.addEventListener("onChange", new EventListener() {
@Override
public void onEvent(Event event) {
updateBean();
updateComponents();
}
});
}
private void registerOnEnterListener(final Textbox textBox) {
textBox.addEventListener("onOK", new EventListener() {
@Override
public void onEvent(Event event) {
userWantsDateBox(textBox);
}
});
}
private void registerOnEnterOpenDateBox(final Datebox currentDatebox) {
currentDatebox.addEventListener("onOK", new EventListener() {
@Override
public void onEvent(Event event) {
currentDatebox.setOpen(true);
}
});
}
private void registerBlurListener(final Datebox currentDatebox) {
currentDatebox.addEventListener("onBlur", new EventListener() {
@Override
public void onEvent(Event event) {
dateBoxHasLostFocus(currentDatebox);
}
});
}
public interface Getter<Date> {
/**
* Typical get method that returns a variable.
*
* @return A variable of type Date.
*/
Date get();
}
public interface Setter<Date> {
/**
* Typical set method to store a variable.
*
* @param value
* A variable of type Date to be set.
*/
void set(Date value);
}
private void updateBean() {
Date date = getDateBox().getValue();
setter.set(date);
}
public void updateComponents() {
getDateTextBox().setValue(asString(getter.get()));
}
private String asString(Date date) {
if (date == null) {
return "";
}
return dateFormat.format(date);
}
private Date fromString(String string) throws ParseException {
if (StringUtils.isBlank(string)) {
return null;
}
return dateFormat.parse(StringUtils.trim(string));
}
public Textbox getDateTextBox() {
return dateTextBox;
}
public void setDateTextBox(Textbox dateTextBox) {
this.dateTextBox = dateTextBox;
}
public boolean isDisabled() {
return disabled;
}
public void setDisabled(boolean disabled) {
this.disabled = disabled;
applyDisabledToElements(disabled);
}
private void applyDisabledToElements(boolean disabled) {
if(dateTextBox != null) {
dateTextBox.setDisabled(disabled);
}
}
}