001    package org.apache.myfaces.tobago.renderkit.html.scarborough.standard.tag;
002    
003    /*
004     * Licensed to the Apache Software Foundation (ASF) under one or more
005     * contributor license agreements.  See the NOTICE file distributed with
006     * this work for additional information regarding copyright ownership.
007     * The ASF licenses this file to You under the Apache License, Version 2.0
008     * (the "License"); you may not use this file except in compliance with
009     * the License.  You may obtain a copy of the License at
010     *
011     *      http://www.apache.org/licenses/LICENSE-2.0
012     *
013     * Unless required by applicable law or agreed to in writing, software
014     * distributed under the License is distributed on an "AS IS" BASIS,
015     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016     * See the License for the specific language governing permissions and
017     * limitations under the License.
018     */
019    
020    import org.apache.commons.logging.Log;
021    import org.apache.commons.logging.LogFactory;
022    import org.apache.myfaces.tobago.TobagoConstants;
023    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_ACTION_ONCLICK;
024    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_ALT;
025    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_COLUMNS;
026    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_HEIGHT;
027    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_LABEL;
028    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_LAYOUT_WIDTH;
029    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_ROWS;
030    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_WIDTH;
031    import static org.apache.myfaces.tobago.TobagoConstants.FACET_LAYOUT;
032    import static org.apache.myfaces.tobago.TobagoConstants.FACET_PICKER_POPUP;
033    import static org.apache.myfaces.tobago.TobagoConstants.RENDERER_TYPE_BOX;
034    import static org.apache.myfaces.tobago.TobagoConstants.RENDERER_TYPE_BUTTON;
035    import static org.apache.myfaces.tobago.TobagoConstants.RENDERER_TYPE_CALENDAR;
036    import static org.apache.myfaces.tobago.TobagoConstants.RENDERER_TYPE_GRID_LAYOUT;
037    import static org.apache.myfaces.tobago.TobagoConstants.RENDERER_TYPE_HIDDEN;
038    import static org.apache.myfaces.tobago.TobagoConstants.RENDERER_TYPE_IMAGE;
039    import static org.apache.myfaces.tobago.TobagoConstants.RENDERER_TYPE_PANEL;
040    import static org.apache.myfaces.tobago.TobagoConstants.RENDERER_TYPE_POPUP;
041    import static org.apache.myfaces.tobago.TobagoConstants.RENDERER_TYPE_TIME;
042    import org.apache.myfaces.tobago.compat.FacesUtils;
043    import org.apache.myfaces.tobago.component.AbstractUIPopup;
044    import org.apache.myfaces.tobago.component.CreateComponentUtils;
045    import org.apache.myfaces.tobago.component.UIBox;
046    import org.apache.myfaces.tobago.component.UIDateInput;
047    import org.apache.myfaces.tobago.component.UIDatePicker;
048    import org.apache.myfaces.tobago.component.UIGridLayout;
049    import org.apache.myfaces.tobago.component.UIHiddenInput;
050    import org.apache.myfaces.tobago.component.UIPanel;
051    import org.apache.myfaces.tobago.component.UIPopup;
052    import org.apache.myfaces.tobago.component.UITimeInput;
053    import org.apache.myfaces.tobago.config.ThemeConfig;
054    import org.apache.myfaces.tobago.context.ResourceManagerUtil;
055    import org.apache.myfaces.tobago.context.TobagoFacesContext;
056    import org.apache.myfaces.tobago.event.PopupActionListener;
057    import org.apache.myfaces.tobago.renderkit.html.StyleClasses;
058    import org.apache.myfaces.tobago.util.ComponentUtil;
059    import org.apache.myfaces.tobago.util.DateFormatUtils;
060    
061    import javax.faces.component.UICommand;
062    import javax.faces.component.UIComponent;
063    import javax.faces.component.UIGraphic;
064    import javax.faces.context.FacesContext;
065    import javax.faces.convert.Converter;
066    import javax.faces.convert.DateTimeConverter;
067    import static javax.faces.convert.DateTimeConverter.CONVERTER_ID;
068    import java.io.IOException;
069    import java.util.Map;
070    import java.util.TimeZone;
071    
072    /*
073     * Date: 30.05.2006
074     * Time: 22:21:17
075     */
076    public class DatePickerRenderer extends LinkRenderer {
077      private static final Log LOG = LogFactory.getLog(DatePickerRenderer.class);
078      public static final String CLOSE_POPUP = "closePopup";
079    
080      @Override
081      public void onComponentCreated(FacesContext context, UIComponent component) {
082        preparePicker(context, (UIDatePicker) component);
083      }
084    
085      public void preparePicker(FacesContext facesContext, UIDatePicker link) {
086        if (link.getFor() == null) {
087          link.setFor("@auto");
088        }
089        link.setImmediate(true);
090        String linkId = link.getId();
091        UIHiddenInput hidden = (UIHiddenInput)
092            CreateComponentUtils.createComponent(facesContext, UIHiddenInput.COMPONENT_TYPE, RENDERER_TYPE_HIDDEN);
093        if (linkId != null) {
094          hidden.setId(linkId + "hidden");
095        } else {
096          hidden.setId(facesContext.getViewRoot().createUniqueId());
097        }
098        link.getChildren().add(hidden);
099    
100        // create popup
101        final AbstractUIPopup popup =
102            (AbstractUIPopup) CreateComponentUtils.createComponent(facesContext, UIPopup.COMPONENT_TYPE,
103                RENDERER_TYPE_POPUP);
104        if (linkId != null) {
105          popup.setId(linkId + "popup");
106        } else {
107          popup.setId(facesContext.getViewRoot().createUniqueId());
108        }
109        popup.getAttributes().put(TobagoConstants.ATTR_ZINDEX, 10);
110    
111        link.getFacets().put(FACET_PICKER_POPUP, popup);
112    
113        popup.setRendered(false);
114    
115        final UIComponent box = CreateComponentUtils.createComponent(
116            facesContext, UIBox.COMPONENT_TYPE, RENDERER_TYPE_BOX);
117        popup.getChildren().add(box);
118        box.setId("box");
119        // TODO: set string resources in renderer
120        box.getAttributes().put(ATTR_LABEL, ResourceManagerUtil.getPropertyNotNull(
121            facesContext, "tobago", "datePickerTitle"));
122        UIComponent layout = CreateComponentUtils.createComponent(
123            facesContext, UIGridLayout.COMPONENT_TYPE, RENDERER_TYPE_GRID_LAYOUT);
124        box.getFacets().put(FACET_LAYOUT, layout);
125        layout.setId("layout");
126        layout.getAttributes().put(ATTR_ROWS, "*;fixed;fixed");
127    
128        final UIComponent calendar = CreateComponentUtils.createComponent(
129            facesContext, javax.faces.component.UIOutput.COMPONENT_TYPE,
130            RENDERER_TYPE_CALENDAR);
131    
132        calendar.setId("calendar");
133        box.getChildren().add(calendar);
134    
135        // add time input
136        final UIComponent timePanel = CreateComponentUtils.createComponent(
137            facesContext, UIPanel.COMPONENT_TYPE, RENDERER_TYPE_PANEL);
138        timePanel.setId("timePanel");
139        box.getChildren().add(timePanel);
140        layout = CreateComponentUtils.createComponent(
141            facesContext, UIGridLayout.COMPONENT_TYPE, RENDERER_TYPE_GRID_LAYOUT);
142        timePanel.getFacets().put(FACET_LAYOUT, layout);
143        layout.setId("timePanelLayout");
144        layout.getAttributes().put(ATTR_COLUMNS, "1*;fixed;1*");
145        UIComponent cell = CreateComponentUtils.createComponent(
146            facesContext, UIPanel.COMPONENT_TYPE, RENDERER_TYPE_PANEL);
147        cell.setId("cell1");
148        timePanel.getChildren().add(cell);
149    
150        final UIComponent time = CreateComponentUtils.createComponent(
151            facesContext,
152            UITimeInput.COMPONENT_TYPE,
153            RENDERER_TYPE_TIME);
154        timePanel.getChildren().add(time);
155        time.setId("time");
156    
157        cell = CreateComponentUtils.createComponent(
158            facesContext, UIPanel.COMPONENT_TYPE, RENDERER_TYPE_PANEL);
159        cell.setId("cell2");
160        timePanel.getChildren().add(cell);
161    
162    
163        UIComponent buttonPanel = CreateComponentUtils.createComponent(
164            facesContext, UIPanel.COMPONENT_TYPE, RENDERER_TYPE_PANEL);
165        buttonPanel.setId("buttonPanel");
166        layout = CreateComponentUtils.createComponent(
167            facesContext, UIGridLayout.COMPONENT_TYPE, RENDERER_TYPE_GRID_LAYOUT);
168        layout.setId("buttonPanelLayout");
169        buttonPanel.getFacets().put(FACET_LAYOUT, layout);
170        layout.getAttributes().put(ATTR_COLUMNS, "*;*");
171    //    layout.getAttributes().put(TobagoConstants.ATTR_BORDER, "1");
172    
173        box.getChildren().add(buttonPanel);
174    
175        final org.apache.myfaces.tobago.component.UICommand okButton =
176            (org.apache.myfaces.tobago.component.UICommand) CreateComponentUtils.createComponent(facesContext,
177                org.apache.myfaces.tobago.component.UIButtonCommand.COMPONENT_TYPE,
178                RENDERER_TYPE_BUTTON);
179        buttonPanel.getChildren().add(okButton);
180        okButton.setId("ok" + CLOSE_POPUP);
181        okButton.getAttributes().put(ATTR_LABEL, ResourceManagerUtil.getPropertyNotNull(
182            facesContext, "tobago", "datePickerOk"));
183    
184        final org.apache.myfaces.tobago.component.UICommand cancelButton =
185            (org.apache.myfaces.tobago.component.UICommand) CreateComponentUtils.createComponent(facesContext,
186                org.apache.myfaces.tobago.component.UIButtonCommand.COMPONENT_TYPE,
187                RENDERER_TYPE_BUTTON);
188        buttonPanel.getChildren().add(cancelButton);
189    
190        cancelButton.getAttributes().put(ATTR_LABEL, ResourceManagerUtil.getPropertyNotNull(
191            facesContext, "tobago", "datePickerCancel"));
192        cancelButton.setId(CLOSE_POPUP);
193    
194        // create image
195        UIGraphic image = (UIGraphic) CreateComponentUtils.createComponent(
196            facesContext, UIGraphic.COMPONENT_TYPE, RENDERER_TYPE_IMAGE);
197        image.setRendered(true);
198        if (linkId != null) {
199          image.setId(linkId + "image");
200        } else {
201          image.setId(facesContext.getViewRoot().createUniqueId());
202        }
203        image.setValue("image/date.gif");
204        image.getAttributes().put(ATTR_ALT, ""); //TODO: i18n
205        StyleClasses.ensureStyleClasses(image).addFullQualifiedClass("tobago-input-picker"); // XXX not a standard name
206        link.getChildren().add(image);
207      }
208    
209    
210      public void prepareRender(FacesContext facesContext, UIComponent component) throws IOException {
211        component.getAttributes().put(ATTR_LAYOUT_WIDTH, getConfiguredValue(facesContext, component, "pickerWidth"));
212        if (facesContext instanceof TobagoFacesContext) {
213          UIPopup popup = (UIPopup) component.getFacets().get(FACET_PICKER_POPUP);
214          if (popup != null) {
215            popup.getAttributes().put(ATTR_WIDTH, ThemeConfig.getValue(facesContext, component, "CalendarPopupWidth"));
216            popup.getAttributes().put(ATTR_HEIGHT, ThemeConfig.getValue(facesContext, component, "CalendarPopupHeight"));
217            ((TobagoFacesContext) facesContext).getPopups().add(popup);
218          }
219        }
220        super.prepareRender(facesContext, component);
221      }
222    
223      public void encodeBegin(FacesContext facesContext,
224                              UIComponent component) throws IOException {
225        UIDatePicker link = (UIDatePicker) component;
226    //    DatePickerController datePickerController = new DatePickerController();
227        UIDateInput dateInput = (UIDateInput) link.getForComponent();
228        if (dateInput == null) {
229          LOG.error("No required UIDateInput component found.");
230          return;
231        }
232        if (FacesUtils.hasValueBindingOrValueExpression(dateInput, TobagoConstants.ATTR_READONLY)) {
233          FacesUtils.copyValueBindingOrValueExpression(link, TobagoConstants.ATTR_DISABLED,
234              dateInput, TobagoConstants.ATTR_READONLY);
235        } else {
236          if (FacesUtils.hasValueBindingOrValueExpression(dateInput, TobagoConstants.ATTR_DISABLED)) {
237            FacesUtils.copyValueBindingOrValueExpression(link, TobagoConstants.ATTR_DISABLED,
238                dateInput, TobagoConstants.ATTR_DISABLED);
239          } else {
240            link.setDisabled(dateInput.isReadonly() || dateInput.isDisabled());
241          }
242        }
243        Map<String, Object> attributes = link.getAttributes();
244    //    link.setActionListener(datePickerController);
245    
246        UIComponent hidden = (UIComponent) link.getChildren().get(0);
247        UIPopup popup = (UIPopup) link.getFacets().get(FACET_PICKER_POPUP);
248    
249        attributes.put(ATTR_ACTION_ONCLICK, "Tobago.openPickerPopup(event, '"
250            + link.getClientId(facesContext) + "', '"
251            + hidden.getClientId(facesContext) + "', '"
252            + popup.getClientId(facesContext) + "')");
253    
254        Converter converter = getConverter(facesContext, dateInput);
255        String converterPattern = "yyyy-MM-dd"; // from calendar.js  initCalendarParse
256        if (converter instanceof DateTimeConverter) {
257          converterPattern = DateFormatUtils.findPattern((DateTimeConverter) converter);
258        } else {
259          // LOG.warn("Converter for DateRenderer is not instance of DateTimeConverter. Using default Pattern "
260          //    + converterPattern);
261        }
262    
263        UICommand okButton = (UICommand) popup.findComponent("ok" + CLOSE_POPUP);
264        attributes = okButton.getAttributes();
265        attributes.put(ATTR_ACTION_ONCLICK, "var textBox = writeIntoField2(this);Tobago.closePopup(this);textBox.focus();");
266        attributes.put(TobagoConstants.ATTR_POPUP_CLOSE, "afterSubmit");
267    
268        UICommand cancelButton = (UICommand) popup.findComponent(CLOSE_POPUP);
269        attributes = cancelButton.getAttributes();
270        attributes.put(ATTR_ACTION_ONCLICK, "var textBox = writeIntoField2(this);Tobago.closePopup(this);textBox.focus();");
271        attributes.put(TobagoConstants.ATTR_POPUP_CLOSE, "immediate");
272    
273        applyConverterPattern(facesContext, popup, converterPattern);
274    
275        if (!ComponentUtil.containsPopupActionListener(link)) {
276          link.addActionListener(new PopupActionListener(popup.getId()));
277        }
278        super.encodeBegin(facesContext, component);
279      }
280    
281      private void applyConverterPattern(FacesContext facesContext, UIPopup popup, String converterPattern) {
282        UIComponent box = (UIComponent) popup.getChildren().get(0);
283        UIComponent timePanel = box.findComponent("timePanel");
284        if (converterPattern != null && (converterPattern.indexOf('h') > -1 || converterPattern.indexOf('H') > -1)) {
285          UIComponent time = timePanel.findComponent("time");
286          int popupHeight = ComponentUtil.getIntAttribute(popup, ATTR_HEIGHT);
287          popupHeight += ThemeConfig.getValue(FacesContext.getCurrentInstance(), time, "fixedHeight");
288          popup.getAttributes().put(ATTR_HEIGHT, popupHeight);
289          DateTimeConverter dateTimeConverter
290              = (DateTimeConverter) facesContext.getApplication().createConverter(CONVERTER_ID);
291          if (converterPattern.indexOf('s') > -1) {
292            dateTimeConverter.setPattern("HH:mm:ss");
293          } else {
294            dateTimeConverter.setPattern("HH:mm");
295          }
296          dateTimeConverter.setTimeZone(TimeZone.getDefault());
297          ((UITimeInput) time).setConverter(dateTimeConverter);
298        } else {
299          timePanel.setRendered(false);
300        }
301      }
302    
303      public void encodeEnd(FacesContext facesContext, UIComponent component) throws IOException {
304        UIDatePicker link = (UIDatePicker) component;
305        UIDateInput dateInput = (UIDateInput) link.getForComponent();
306        if (dateInput != null) {
307          super.encodeEnd(facesContext, component);
308        } else {
309          LOG.error("No required UIDateInput component found.");
310        }
311      }
312    }