2 * Copyright (C) 2008 ZXing authors
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.google.zxing.web.generator.client;
19 import com.google.gwt.event.dom.client.KeyPressHandler;
20 import com.google.gwt.i18n.client.DateTimeFormat;
21 import com.google.gwt.user.client.DOM;
22 import com.google.gwt.user.client.Element;
23 import com.google.gwt.user.client.ui.ChangeListener;
24 import com.google.gwt.user.client.ui.CheckBox;
25 import com.google.gwt.user.client.ui.ClickListener;
26 import com.google.gwt.user.client.ui.Grid;
27 import com.google.gwt.user.client.ui.ListBox;
28 import com.google.gwt.user.client.ui.TextBox;
29 import com.google.gwt.user.client.ui.Widget;
30 import com.google.gwt.widgetideas.client.event.ChangeEvent;
31 import com.google.gwt.widgetideas.client.event.ChangeHandler;
32 import com.google.gwt.widgetideas.datepicker.client.DateBox;
33 import com.google.gwt.widgetideas.datepicker.client.TimePicker;
34 import com.google.zxing.web.generator.client.TimeZoneList.TimeZoneInfo;
36 import java.util.Date;
39 * A Generator for calendar events. Output is in VCal format.
41 * @author Yohann Coppel
43 public class CalendarEventGenerator implements GeneratorSource {
44 public static final String[] FULL_DAY_ONLY_IDS = { "fullDayOnlyInfo1",
45 "fullDayOnlyInfo2", "fullDayOnlyInfo3", "fullDayOnlyInfo4" };
46 private static final long ONE_HOUR = 60L * 60 * 1000;
49 TextBox eventName = new TextBox();
50 CheckBox fullDay = new CheckBox();
51 DateBox datePicker1 = new DateBox();
52 DateBox datePicker2 = new DateBox();
53 TimePicker timePicker1 = new TimePicker(new Date(), DateTimeFormat
54 .getFormat("a"), DateTimeFormat.getFormat("hh"), DateTimeFormat
55 .getFormat("mm"), null);
56 TimePicker timePicker2 = new TimePicker(new Date(), DateTimeFormat
57 .getFormat("a"), DateTimeFormat.getFormat("hh"), DateTimeFormat
58 .getFormat("mm"), null);
59 CheckBox summerTime = new CheckBox();
60 ListBox timeZones = new ListBox();
61 Date timePicker1PreviousDate = null;
63 public CalendarEventGenerator(final ChangeListener listener,
64 KeyPressHandler keyListener) {
65 eventName.addStyleName(StylesDefs.INPUT_FIELD_REQUIRED);
66 eventName.addChangeListener(listener);
67 eventName.addKeyPressHandler(keyListener);
68 datePicker1.setAnimationEnabled(true);
69 datePicker2.setAnimationEnabled(true);
71 .setDateTime(addMilliseconds(timePicker1.getDateTime(), ONE_HOUR));
72 timePicker1PreviousDate = timePicker1.getDateTime();
75 timeZones.setSelectedIndex(25);
76 timeZones.addKeyPressHandler(keyListener);
77 timePicker1.addChangeHandler(new ChangeHandler<Date>() {
78 public void onChange(ChangeEvent<Date> event) {
79 Date time = timePicker1PreviousDate;
80 Date time1 = timePicker1.getDateTime();
81 Date time2 = timePicker2.getDateTime();
82 if (time2.after(time)) {
83 // keep the same time difference if the interval is valid.
84 long diff = time2.getTime() - time.getTime();
85 timePicker2.setDateTime(addMilliseconds(time1, diff));
87 // otherwise erase the end date and set it to startdate + one hour.
88 timePicker2.setDateTime(addMilliseconds(time, ONE_HOUR));
90 // no need to call onChange for timePicker1, since it will be called
91 // for timePicker2 when changes are made.
92 // listener.onChange(timePicker1);
93 timePicker1PreviousDate = time1;
96 timePicker2.addChangeHandler(new ChangeHandler<Date>() {
97 public void onChange(ChangeEvent<Date> event) {
98 listener.onChange(timePicker2);
103 private void buildTimeZoneList() {
104 for (TimeZoneInfo info : TimeZoneList.TIMEZONES) {
105 timeZones.addItem(info.GMTRelative + ' ' + info.abreviation, String.valueOf(info.gmtDiff));
109 public String getName() {
110 return "Calendar event";
113 public Grid getWidget() {
117 datePicker1.getDatePicker().setSelectedDate(new Date());
118 datePicker2.getDatePicker().setSelectedDate(new Date());
119 table = new Grid(8, 2);
121 table.setText(0, 0, "All day event");
122 table.setWidget(0, 1, fullDay);
124 table.setText(1, 0, "Event title");
125 table.setWidget(1, 1, eventName);
127 table.setText(2, 0, "Start date");
128 table.setWidget(2, 1, datePicker1);
130 table.setText(3, 0, "Time");
131 table.setWidget(3, 1, timePicker1);
133 table.setText(4, 0, "End date");
134 table.setWidget(4, 1, datePicker2);
136 table.setText(5, 0, "Time");
137 table.setWidget(5, 1, timePicker2);
139 table.setText(6, 0, "Time zone");
140 table.setWidget(6, 1, timeZones);
142 table.setText(7, 0, "Daylight savings");
143 table.setWidget(7, 1, summerTime);
145 table.getRowFormatter().getElement(3).setId(FULL_DAY_ONLY_IDS[0]);
146 table.getRowFormatter().getElement(5).setId(FULL_DAY_ONLY_IDS[1]);
147 table.getRowFormatter().getElement(6).setId(FULL_DAY_ONLY_IDS[2]);
148 table.getRowFormatter().getElement(7).setId(FULL_DAY_ONLY_IDS[3]);
150 fullDay.addClickListener(new ClickListener() {
151 public void onClick(Widget sender) {
152 CheckBox cb = (CheckBox) sender;
153 setFullDay(cb.isChecked());
160 private void setFullDay(boolean fullDay) {
161 for (String s : FULL_DAY_ONLY_IDS) {
162 Element element = DOM.getElementById(s);
163 String style = fullDay ? "none" : "";
164 DOM.setStyleAttribute(element, "display", style);
168 public String getText() throws GeneratorException {
169 String eventName = getEventNameField();
170 String dates = getDateTimeFields();
171 StringBuilder output = new StringBuilder();
172 output.append("BEGIN:VEVENT\r\n");
173 output.append(eventName);
174 output.append(dates);
175 output.append("END:VEVENT\r\n");
176 return output.toString();
179 private String getEventNameField() throws GeneratorException {
180 String inputName = eventName.getText();
181 if (inputName.length() < 1) {
182 throw new GeneratorException("Event name must be at least 1 character.");
184 if (inputName.contains("\n")) {
185 throw new GeneratorException(
186 "Event name should not contain \\n characters.");
188 return "SUMMARY:" + inputName + "\r\n";
191 private String getDateTimeFields() throws GeneratorException {
192 if (fullDay.isChecked()) {
193 return getFullDayDateFields();
195 return getDateTimeValues();
198 private String getFullDayDateFields() throws GeneratorException {
199 Date date1 = datePicker1.getDatePicker().getSelectedDate();
200 Date date2 = datePicker2.getDatePicker().getSelectedDate();
201 if (null == date1 || null == date2) {
202 throw new GeneratorException("Start and end dates must be set.");
204 if (date1.after(date2)) {
205 throw new GeneratorException("Ending date is after starting date.");
207 DateTimeFormat isoFormatter = DateTimeFormat.getFormat("yyyyMMdd");
208 StringBuilder output = new StringBuilder();
209 output.append("DTSTART:");
210 output.append(isoFormatter.format(date1));
211 output.append("\r\n");
212 output.append("DTEND:");
213 output.append(isoFormatter.format(date2));
214 output.append("\r\n");
215 return output.toString();
218 private String getDateTimeValues() throws GeneratorException {
219 Date date1 = datePicker1.getDatePicker().getSelectedDate();
220 Date date2 = datePicker2.getDatePicker().getSelectedDate();
221 Date time1 = timePicker1.getDateTime();
222 Date time2 = timePicker2.getDateTime();
223 if (null == date1 || null == date2 || null == time1 || null == time2) {
224 throw new GeneratorException("Start and end dates/times must be set.");
226 String timezoneDelta = timeZones.getValue(timeZones.getSelectedIndex());
227 long diffTimeZone = Long.parseLong(timezoneDelta);
228 if (summerTime.isChecked()) {
229 diffTimeZone += ONE_HOUR;
231 Date dateTime1 = addMilliseconds(mergeDateAndTime(date1, time1),
233 Date dateTime2 = addMilliseconds(mergeDateAndTime(date2, time2),
235 if (dateTime1.after(dateTime2)) {
236 throw new GeneratorException("Ending date is after starting date.");
238 DateTimeFormat isoFormatter = DateTimeFormat
239 .getFormat("yyyyMMdd'T'HHmmss'Z'");
240 StringBuilder output = new StringBuilder();
241 output.append("DTSTART:");
242 output.append(isoFormatter.format(dateTime1));
243 output.append("\r\n");
244 output.append("DTEND:");
245 output.append(isoFormatter.format(dateTime2));
246 output.append("\r\n");
247 return output.toString();
250 private static Date mergeDateAndTime(Date date, Date time) {
251 // Is that the only ugly way to do with GWT ? given that we don't
252 // have java.util.Calendar for instance
253 DateTimeFormat extractDate = DateTimeFormat.getFormat("yyyyMMdd");
254 DateTimeFormat extractTime = DateTimeFormat.getFormat("HHmm");
255 DateTimeFormat merger = DateTimeFormat.getFormat("yyyyMMddHHmmss");
256 String d = extractDate.format(date);
257 String t = extractTime.format(time) + "00";
258 return merger.parse(d + t);
261 public void validate(Widget widget) throws GeneratorException {
262 if (widget == eventName) {
264 } else if (widget == datePicker1 || widget == timePicker1 || widget == datePicker2
265 || widget == timePicker2) {
270 private static Date addMilliseconds(Date time1, long milliseconds) {
271 return new Date(time1.getTime() + milliseconds);
274 public void setFocus() {
275 eventName.setFocus(true);