View Javadoc
1   /*
2    * Copyright (c) 2011-2024 PrimeFaces Extensions
3    *
4    *  Permission is hereby granted, free of charge, to any person obtaining a copy
5    *  of this software and associated documentation files (the "Software"), to deal
6    *  in the Software without restriction, including without limitation the rights
7    *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    *  copies of the Software, and to permit persons to whom the Software is
9    *  furnished to do so, subject to the following conditions:
10   *
11   *  The above copyright notice and this permission notice shall be included in
12   *  all copies or substantial portions of the Software.
13   *
14   *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20   *  THE SOFTWARE.
21   */
22  package org.primefaces.extensions.component.timepicker;
23  
24  import java.util.Collection;
25  import java.util.HashMap;
26  import java.util.List;
27  import java.util.Locale;
28  import java.util.Map;
29  import java.util.Map.Entry;
30  
31  import javax.faces.application.ResourceDependency;
32  import javax.faces.context.FacesContext;
33  import javax.faces.event.AjaxBehaviorEvent;
34  import javax.faces.event.FacesEvent;
35  import javax.faces.event.PhaseId;
36  
37  import org.primefaces.component.api.AbstractPrimeHtmlInputText;
38  import org.primefaces.component.api.InputHolder;
39  import org.primefaces.component.api.Widget;
40  import org.primefaces.extensions.event.BeforeShowEvent;
41  import org.primefaces.extensions.event.CloseEvent;
42  import org.primefaces.extensions.event.TimeSelectEvent;
43  import org.primefaces.util.Constants;
44  import org.primefaces.util.LangUtils;
45  import org.primefaces.util.LocaleUtils;
46  
47  /**
48   * <code>TimePicker</code> component.
49   *
50   * @author Oleg Varaksin / last modified by $Author$
51   * @version $Revision$
52   * @since 0.3
53   */
54  @ResourceDependency(library = "primefaces", name = "components.css")
55  @ResourceDependency(library = "primefaces", name = "jquery/jquery.js")
56  @ResourceDependency(library = "primefaces", name = "jquery/jquery-plugins.js")
57  @ResourceDependency(library = "primefaces", name = "core.js")
58  @ResourceDependency(library = "primefaces-extensions", name = "primefaces-extensions.js")
59  @ResourceDependency(library = "primefaces-extensions", name = "timepicker/timepicker.css")
60  @ResourceDependency(library = "primefaces-extensions", name = "timepicker/timepicker.js")
61  public class TimePicker extends AbstractPrimeHtmlInputText implements Widget, InputHolder {
62  
63      public static final String CONTAINER_CLASS = "pe-timepicker ui-widget ui-corner-all";
64      public static final String INPUT_CLASS = "ui-inputfield ui-state-default ui-corner-all";
65      public static final String UP_BUTTON_CLASS = "ui-spinner-button ui-spinner-up ui-corner-tr ui-button ui-widget ui-state-default";
66      public static final String DOWN_BUTTON_CLASS = "ui-spinner-button ui-spinner-down ui-corner-br ui-button ui-widget ui-state-default";
67      public static final String UP_ICON_CLASS = "ui-icon ui-icon-triangle-1-n";
68      public static final String DOWN_ICON_CLASS = "ui-icon ui-icon-triangle-1-s";
69      public static final String BUTTON_TRIGGER_CLASS = "pe-timepicker-trigger ui-button ui-widget ui-state-default ui-corner-all ui-button-icon-only";
70      public static final String BUTTON_TRIGGER_ICON_CLASS = "ui-button-icon-left ui-icon ui-icon-clock";
71      public static final String BUTTON_TRIGGER_TEXT_CLASS = "ui-button-text";
72  
73      public static final String TIME_MESSAGE_KEY = "javax.faces.converter.DateTimeConverter.TIME";
74      public static final String COMPONENT_TYPE = "org.primefaces.extensions.component.TimePicker";
75      public static final String COMPONENT_FAMILY = "org.primefaces.extensions.component";
76      public static final String DEFAULT_RENDERER = "org.primefaces.extensions.component.TimePickerRenderer";
77  
78      private static final List<String> UNOBSTRUSIVE_EVENT_NAMES = LangUtils.unmodifiableList(BeforeShowEvent.NAME,
79                  TimeSelectEvent.NAME, CloseEvent.NAME);
80      private static final Collection<String> EVENT_NAMES = LangUtils.concat(AbstractPrimeHtmlInputText.EVENT_NAMES, UNOBSTRUSIVE_EVENT_NAMES);
81  
82      private final Map<String, AjaxBehaviorEvent> customEvents = new HashMap<>();
83      private Locale appropriateLocale;
84  
85      @SuppressWarnings("java:S115")
86      protected enum PropertyKeys {
87  
88          //@formatter:off
89          widgetVar,
90          timeSeparator,
91          showPeriod,
92          dialogPosition,
93          inputPosition,
94          mode /* 'popup', 'spinner', 'inline' */,
95          startHours,
96          endHours,
97          startMinutes,
98          endMinutes,
99          intervalMinutes,
100         rows,
101         showHours,
102         showMinutes,
103         showCloseButton,
104         showNowButton,
105         showDeselectButton,
106         onHourShow,
107         onMinuteShow,
108         showOn,
109         locale,
110         minHour,
111         minMinute,
112         maxHour,
113         maxMinute,
114         readonlyInput,
115         size
116         //@formatter:on
117     }
118 
119     public TimePicker() {
120         setRendererType(DEFAULT_RENDERER);
121     }
122 
123     @Override
124     public String getFamily() {
125         return COMPONENT_FAMILY;
126     }
127 
128     @Override
129     public String getInputClientId() {
130         return getClientId(getFacesContext()) + "_input";
131     }
132 
133     @Override
134     public String getValidatableInputClientId() {
135         return getInputClientId();
136     }
137 
138     @Override
139     public String getLabelledBy() {
140         return (String) getStateHelper().get("labelledby");
141     }
142 
143     @Override
144     public void setLabelledBy(String labelledBy) {
145         getStateHelper().put("labelledby", labelledBy);
146     }
147 
148     public String getWidgetVar() {
149         return (String) getStateHelper().eval(PropertyKeys.widgetVar, null);
150     }
151 
152     public void setWidgetVar(final String widgetVar) {
153         getStateHelper().put(PropertyKeys.widgetVar, widgetVar);
154     }
155 
156     public String getTimeSeparator() {
157         return (String) getStateHelper().eval(PropertyKeys.timeSeparator, ":");
158     }
159 
160     public void setTimeSeparator(final String timeSeparator) {
161         getStateHelper().put(PropertyKeys.timeSeparator, timeSeparator);
162     }
163 
164     public boolean isShowPeriod() {
165         return (Boolean) getStateHelper().eval(PropertyKeys.showPeriod, false);
166     }
167 
168     public void setShowPeriod(final boolean showPeriod) {
169         getStateHelper().put(PropertyKeys.showPeriod, showPeriod);
170     }
171 
172     public String getMode() {
173         return (String) getStateHelper().eval(PropertyKeys.mode, "spinner");
174     }
175 
176     public void setMode(final String mode) {
177         getStateHelper().put(PropertyKeys.mode, mode);
178     }
179 
180     public String getDialogPosition() {
181         return (String) getStateHelper().eval(PropertyKeys.dialogPosition, "left top");
182     }
183 
184     public void setDialogPosition(final String dialogPosition) {
185         getStateHelper().put(PropertyKeys.dialogPosition, dialogPosition);
186     }
187 
188     public String getInputPosition() {
189         return (String) getStateHelper().eval(PropertyKeys.inputPosition, "left bottom");
190     }
191 
192     public void setInputPosition(final String inputPosition) {
193         getStateHelper().put(PropertyKeys.inputPosition, inputPosition);
194     }
195 
196     public int getStartHours() {
197         return (Integer) getStateHelper().eval(PropertyKeys.startHours, 0);
198     }
199 
200     public void setStartHours(final int startHours) {
201         getStateHelper().put(PropertyKeys.startHours, startHours);
202     }
203 
204     public int getEndHours() {
205         return (Integer) getStateHelper().eval(PropertyKeys.endHours, 23);
206     }
207 
208     public void setEndHours(final int endHours) {
209         getStateHelper().put(PropertyKeys.endHours, endHours);
210     }
211 
212     public int getStartMinutes() {
213         return (Integer) getStateHelper().eval(PropertyKeys.startMinutes, 0);
214     }
215 
216     public void setStartMinutes(final int startMinutes) {
217         getStateHelper().put(PropertyKeys.startMinutes, startMinutes);
218     }
219 
220     public int getEndMinutes() {
221         return (Integer) getStateHelper().eval(PropertyKeys.endMinutes, 55);
222     }
223 
224     public void setEndMinutes(final int endMinutes) {
225         getStateHelper().put(PropertyKeys.endMinutes, endMinutes);
226     }
227 
228     public int getIntervalMinutes() {
229         return (Integer) getStateHelper().eval(PropertyKeys.intervalMinutes, 5);
230     }
231 
232     public void setIntervalMinutes(final int intervalMinutes) {
233         getStateHelper().put(PropertyKeys.intervalMinutes, intervalMinutes);
234     }
235 
236     public int getRows() {
237         return (Integer) getStateHelper().eval(PropertyKeys.rows, 4);
238     }
239 
240     public void setRows(final int rows) {
241         getStateHelper().put(PropertyKeys.rows, rows);
242     }
243 
244     public boolean isShowHours() {
245         return (Boolean) getStateHelper().eval(PropertyKeys.showHours, true);
246     }
247 
248     public void setShowHours(final boolean showHours) {
249         getStateHelper().put(PropertyKeys.showHours, showHours);
250     }
251 
252     public boolean isShowMinutes() {
253         return (Boolean) getStateHelper().eval(PropertyKeys.showMinutes, true);
254     }
255 
256     public void setShowMinutes(final boolean showMinutes) {
257         getStateHelper().put(PropertyKeys.showMinutes, showMinutes);
258     }
259 
260     public boolean isShowCloseButton() {
261         return (Boolean) getStateHelper().eval(PropertyKeys.showCloseButton, false);
262     }
263 
264     public void setShowCloseButton(final boolean showCloseButton) {
265         getStateHelper().put(PropertyKeys.showCloseButton, showCloseButton);
266     }
267 
268     public boolean isShowDeselectButton() {
269         return (Boolean) getStateHelper().eval(PropertyKeys.showDeselectButton, false);
270     }
271 
272     public void setShowDeselectButton(final boolean showDeselectButton) {
273         getStateHelper().put(PropertyKeys.showDeselectButton, showDeselectButton);
274     }
275 
276     public boolean isShowNowButton() {
277         return (Boolean) getStateHelper().eval(PropertyKeys.showNowButton, false);
278     }
279 
280     public void setShowNowButton(final boolean showNowButton) {
281         getStateHelper().put(PropertyKeys.showNowButton, showNowButton);
282     }
283 
284     public String getOnHourShow() {
285         return (String) getStateHelper().eval(PropertyKeys.onHourShow, null);
286     }
287 
288     public void setOnHourShow(final String onHourShow) {
289         getStateHelper().put(PropertyKeys.onHourShow, onHourShow);
290     }
291 
292     public String getOnMinuteShow() {
293         return (String) getStateHelper().eval(PropertyKeys.onMinuteShow, null);
294     }
295 
296     public void setOnMinuteShow(final String onMinuteShow) {
297         getStateHelper().put(PropertyKeys.onMinuteShow, onMinuteShow);
298     }
299 
300     public String getShowOn() {
301         return (String) getStateHelper().eval(PropertyKeys.showOn, "focus");
302     }
303 
304     public void setShowOn(final String showOn) {
305         getStateHelper().put(PropertyKeys.showOn, showOn);
306     }
307 
308     public Object getLocale() {
309         return getStateHelper().eval(PropertyKeys.locale, null);
310     }
311 
312     public void setLocale(final Object locale) {
313         getStateHelper().put(PropertyKeys.locale, locale);
314     }
315 
316     public Integer getMinHour() {
317         return (Integer) getStateHelper().eval(PropertyKeys.minHour, null);
318     }
319 
320     public void setMinHour(final Integer minHour) {
321         getStateHelper().put(PropertyKeys.minHour, minHour);
322     }
323 
324     public Integer getMinMinute() {
325         return (Integer) getStateHelper().eval(PropertyKeys.minMinute, null);
326     }
327 
328     public void setMinMinute(final Integer minMinute) {
329         getStateHelper().put(PropertyKeys.minMinute, minMinute);
330     }
331 
332     public Integer getMaxHour() {
333         return (Integer) getStateHelper().eval(PropertyKeys.maxHour, null);
334     }
335 
336     public void setMaxHour(final Integer maxHour) {
337         getStateHelper().put(PropertyKeys.maxHour, maxHour);
338     }
339 
340     public Integer getMaxMinute() {
341         return (Integer) getStateHelper().eval(PropertyKeys.maxMinute, null);
342     }
343 
344     public void setMaxMinute(final Integer maxMinute) {
345         getStateHelper().put(PropertyKeys.maxMinute, maxMinute);
346     }
347 
348     public boolean isReadonlyInput() {
349         return (Boolean) getStateHelper().eval(PropertyKeys.readonlyInput, false);
350     }
351 
352     public void setReadonlyInput(final boolean _readonlyInput) {
353         getStateHelper().put(PropertyKeys.readonlyInput, _readonlyInput);
354     }
355 
356     @Override
357     public int getSize() {
358         return (Integer) getStateHelper().eval(PropertyKeys.size, 5);
359     }
360 
361     @Override
362     public void setSize(final int size) {
363         getStateHelper().put(PropertyKeys.size, size);
364     }
365 
366     public Locale calculateLocale() {
367         if (appropriateLocale == null) {
368             final FacesContext fc = FacesContext.getCurrentInstance();
369             appropriateLocale = LocaleUtils.resolveLocale(fc, getLocale(), getClientId(fc));
370         }
371         return appropriateLocale;
372     }
373 
374     public boolean isInline() {
375         return getMode().equalsIgnoreCase("inline");
376     }
377 
378     public boolean isSpinner() {
379         return getMode().equalsIgnoreCase("spinner");
380     }
381 
382     public boolean isShowOnButton() {
383         return !"focus".equals(getShowOn());
384     }
385 
386     @Override
387     public Collection<String> getEventNames() {
388         return EVENT_NAMES;
389     }
390 
391     @Override
392     public void queueEvent(final FacesEvent event) {
393         final FacesContext fc = FacesContext.getCurrentInstance();
394         final String eventName = fc.getExternalContext().getRequestParameterMap()
395                     .get(Constants.RequestParams.PARTIAL_BEHAVIOR_EVENT_PARAM);
396 
397         if (isSelfRequest(fc) && event instanceof AjaxBehaviorEvent) {
398             if (TimeSelectEvent.NAME.equals(eventName)) {
399                 customEvents.put(TimeSelectEvent.NAME, (AjaxBehaviorEvent) event);
400 
401                 return;
402             }
403             else if (BeforeShowEvent.NAME.equals(eventName)) {
404                 final BeforeShowEvent beforeShowEvent = new BeforeShowEvent(this,
405                             ((AjaxBehaviorEvent) event).getBehavior());
406                 beforeShowEvent.setPhaseId(event.getPhaseId());
407                 super.queueEvent(beforeShowEvent);
408 
409                 return;
410             }
411             else if (CloseEvent.NAME.equals(eventName)) {
412                 final CloseEvent closeEvent = new CloseEvent(this, ((AjaxBehaviorEvent) event).getBehavior());
413                 closeEvent.setPhaseId(event.getPhaseId());
414                 super.queueEvent(closeEvent);
415 
416                 return;
417             }
418         }
419 
420         super.queueEvent(event);
421     }
422 
423     @Override
424     public void validate(final FacesContext fc) {
425         super.validate(fc);
426 
427         if (isValid()) {
428             for (final Entry<String, AjaxBehaviorEvent> entry : customEvents.entrySet()) {
429                 final AjaxBehaviorEvent behaviorEvent = entry.getValue();
430                 final TimeSelectEvent<Object> timeSelectEvent = new TimeSelectEvent<>(this, behaviorEvent.getBehavior(), getValue());
431 
432                 if (behaviorEvent.getPhaseId().equals(PhaseId.APPLY_REQUEST_VALUES)) {
433                     timeSelectEvent.setPhaseId(PhaseId.PROCESS_VALIDATIONS);
434                 }
435                 else {
436                     timeSelectEvent.setPhaseId(PhaseId.INVOKE_APPLICATION);
437                 }
438 
439                 super.queueEvent(timeSelectEvent);
440             }
441         }
442     }
443 
444     public String getTimePatternWithoutHours() {
445         return "mm";
446     }
447 
448     public String getTimePatternWithoutMinutes() {
449         return isShowPeriod() ? "hh" : "HH";
450     }
451 
452     public String getTimePattern24() {
453         return "HH" + getTimeSeparator() + "mm";
454     }
455 
456     public String getTimePattern12() {
457         return "hh" + getTimeSeparator() + "mm a";
458     }
459 
460     private boolean isSelfRequest(final FacesContext fc) {
461         return getClientId(fc).equals(
462                     fc.getExternalContext().getRequestParameterMap().get(Constants.RequestParams.PARTIAL_SOURCE_PARAM));
463     }
464 
465     @Override
466     public Object saveState(FacesContext context) {
467         // reset component for MyFaces view pooling
468         appropriateLocale = null;
469         customEvents.clear();
470 
471         return super.saveState(context);
472     }
473 }