Fix typo in date error message
[zxing.git] / zxing.appspot.com / generator / src / com / google / zxing / web / generator / client / CalendarEventGenerator.java
1 /*
2  * Copyright (C) 2008 ZXing authors
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package com.google.zxing.web.generator.client;
18
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;
35
36 import java.util.Date;
37
38 /**
39  * A Generator for calendar events. Output is in VCal format.
40  * 
41  * @author Yohann Coppel
42  */
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;
47
48   Grid table = null;
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;
62   TextBox location = new TextBox();
63   TextBox description = new TextBox();
64
65   public CalendarEventGenerator(final ChangeListener listener,
66       KeyPressHandler keyListener) {
67     eventName.addStyleName(StylesDefs.INPUT_FIELD_REQUIRED);
68     eventName.addChangeListener(listener);
69     eventName.addKeyPressHandler(keyListener);
70     datePicker1.setAnimationEnabled(true);
71     datePicker2.setAnimationEnabled(true);
72     timePicker2
73         .setDateTime(addMilliseconds(timePicker1.getDateTime(), ONE_HOUR));
74     timePicker1PreviousDate = timePicker1.getDateTime();
75
76     buildTimeZoneList();
77     timeZones.setSelectedIndex(25);
78     timeZones.addKeyPressHandler(keyListener);
79     timePicker1.addChangeHandler(new ChangeHandler<Date>() {
80       public void onChange(ChangeEvent<Date> event) {
81         Date time = timePicker1PreviousDate;
82         Date time1 = timePicker1.getDateTime();
83         Date time2 = timePicker2.getDateTime();
84         if (time2.after(time)) {
85           // keep the same time difference if the interval is valid.
86           long diff = time2.getTime() - time.getTime();
87           timePicker2.setDateTime(addMilliseconds(time1, diff));
88         } else {
89           // otherwise erase the end date and set it to startdate + one hour.
90           timePicker2.setDateTime(addMilliseconds(time, ONE_HOUR));
91         }
92         // no need to call onChange for timePicker1, since it will be called
93         // for timePicker2 when changes are made.
94         // listener.onChange(timePicker1);
95         timePicker1PreviousDate = time1;
96       }
97     });
98     timePicker2.addChangeHandler(new ChangeHandler<Date>() {
99       public void onChange(ChangeEvent<Date> event) {
100         listener.onChange(timePicker2);
101       }
102     });
103   }
104
105   private void buildTimeZoneList() {
106     for (TimeZoneInfo info : TimeZoneList.TIMEZONES) {
107       timeZones.addItem(info.GMTRelative + ' ' + info.abreviation, String.valueOf(info.gmtDiff));
108     }
109   }
110
111   public String getName() {
112     return "Calendar event";
113   }
114
115   public Grid getWidget() {
116     if (table != null) {
117       return table;
118     }
119     datePicker1.getDatePicker().setSelectedDate(new Date());
120     datePicker2.getDatePicker().setSelectedDate(new Date());
121     table = new Grid(10, 2);
122
123     table.setText(0, 0, "All day event");
124     table.setWidget(0, 1, fullDay);
125
126     table.setText(1, 0, "Event title");
127     table.setWidget(1, 1, eventName);
128
129     table.setText(2, 0, "Start date");
130     table.setWidget(2, 1, datePicker1);
131
132     table.setText(3, 0, "Time");
133     table.setWidget(3, 1, timePicker1);
134
135     table.setText(4, 0, "End date");
136     table.setWidget(4, 1, datePicker2);
137
138     table.setText(5, 0, "Time");
139     table.setWidget(5, 1, timePicker2);
140
141     table.setText(6, 0, "Time zone");
142     table.setWidget(6, 1, timeZones);
143
144     table.setText(7, 0, "Daylight savings");
145     table.setWidget(7, 1, summerTime);
146
147     table.setText(8, 0, "Location");
148     table.setWidget(8, 1, location);
149
150     table.setText(9, 0, "Description");
151     table.setWidget(9, 1, description);
152
153     table.getRowFormatter().getElement(3).setId(FULL_DAY_ONLY_IDS[0]);
154     table.getRowFormatter().getElement(5).setId(FULL_DAY_ONLY_IDS[1]);
155     table.getRowFormatter().getElement(6).setId(FULL_DAY_ONLY_IDS[2]);
156     table.getRowFormatter().getElement(7).setId(FULL_DAY_ONLY_IDS[3]);
157
158     fullDay.addClickListener(new ClickListener() {
159       public void onClick(Widget sender) {
160         CheckBox cb = (CheckBox) sender;
161         setFullDay(cb.isChecked());
162       }
163     });
164
165     return table;
166   }
167
168   private static void setFullDay(boolean fullDay) {
169     for (String s : FULL_DAY_ONLY_IDS) {
170       Element element = DOM.getElementById(s);
171       String style = fullDay ? "none" : "";
172       DOM.setStyleAttribute(element, "display", style);
173     }
174   }
175
176   public String getText() throws GeneratorException {
177     String eventName = getEventNameField();
178     String dates = getDateTimeFields();
179     String location = getLocationField();
180     String description = getDescriptionField();
181     StringBuilder output = new StringBuilder();
182     output.append("BEGIN:VEVENT\r\n");
183     output.append(eventName);
184     output.append(dates);
185     output.append(location);
186     output.append(description);
187     output.append("END:VEVENT\r\n");
188     return output.toString();
189   }
190
191   private String getEventNameField() throws GeneratorException {
192     String inputName = eventName.getText();
193     if (inputName.length() < 1) {
194       throw new GeneratorException("Event name must be at least 1 character.");
195     }
196     if (inputName.contains("\n")) {
197       throw new GeneratorException(
198           "Event name should not contain \\n characters.");
199     }
200     return "SUMMARY:" + inputName + "\r\n";
201   }
202
203   private String getDateTimeFields() throws GeneratorException {
204     if (fullDay.isChecked()) {
205       return getFullDayDateFields();
206     }
207     return getDateTimeValues();
208   }
209
210   private String getLocationField() throws GeneratorException {
211     String locationString = location.getText();
212     if (locationString.length() < 1) {
213       return "";
214     }
215     if (locationString.contains("\n")) {
216       throw new GeneratorException(
217           "Location should not contain \\n characters.");
218     }
219     return "LOCATION:" + locationString + "\r\n";
220   }
221
222   private String getDescriptionField() throws GeneratorException {
223     String descriptionString = description.getText();
224     if (descriptionString.length() < 1) {
225       return "";
226     }
227     if (descriptionString.contains("\n")) {
228       throw new GeneratorException(
229           "Description should not contain \\n characters.");
230     }
231     return "DESCRIPTION:" + descriptionString + "\r\n";
232   }
233
234   private String getFullDayDateFields() throws GeneratorException {
235     Date date1 = datePicker1.getDatePicker().getSelectedDate();
236     Date date2 = datePicker2.getDatePicker().getSelectedDate();
237     if (null == date1 || null == date2) {
238       throw new GeneratorException("Start and end dates must be set.");
239     }
240     if (date1.after(date2)) {
241       throw new GeneratorException("Start date is after end date.");
242     }
243     DateTimeFormat isoFormatter = DateTimeFormat.getFormat("yyyyMMdd");
244     StringBuilder output = new StringBuilder();
245     output.append("DTSTART:");
246     output.append(isoFormatter.format(date1));
247     output.append("\r\n");
248     output.append("DTEND:");
249     output.append(isoFormatter.format(date2));
250     output.append("\r\n");
251     return output.toString();
252   }
253
254   private String getDateTimeValues() throws GeneratorException {
255     Date date1 = datePicker1.getDatePicker().getSelectedDate();
256     Date date2 = datePicker2.getDatePicker().getSelectedDate();
257     Date time1 = timePicker1.getDateTime();
258     Date time2 = timePicker2.getDateTime();
259     if (null == date1 || null == date2 || null == time1 || null == time2) {
260       throw new GeneratorException("Start and end dates/times must be set.");
261     }
262     String timezoneDelta = timeZones.getValue(timeZones.getSelectedIndex());
263     long diffTimeZone = Long.parseLong(timezoneDelta);
264     if (summerTime.isChecked()) {
265       diffTimeZone += ONE_HOUR;
266     }
267     Date dateTime1 = addMilliseconds(mergeDateAndTime(date1, time1),
268         -diffTimeZone);
269     Date dateTime2 = addMilliseconds(mergeDateAndTime(date2, time2),
270         -diffTimeZone);
271     if (dateTime1.after(dateTime2)) {
272       throw new GeneratorException("Ending date is after starting date.");
273     }
274     DateTimeFormat isoFormatter = DateTimeFormat
275         .getFormat("yyyyMMdd'T'HHmmss'Z'");
276     StringBuilder output = new StringBuilder();
277     output.append("DTSTART:");
278     output.append(isoFormatter.format(dateTime1));
279     output.append("\r\n");
280     output.append("DTEND:");
281     output.append(isoFormatter.format(dateTime2));
282     output.append("\r\n");
283     return output.toString();
284   }
285
286   private static Date mergeDateAndTime(Date date, Date time) {
287     // Is that the only ugly way to do with GWT ? given that we don't
288     // have java.util.Calendar for instance
289     DateTimeFormat extractDate = DateTimeFormat.getFormat("yyyyMMdd");
290     DateTimeFormat extractTime = DateTimeFormat.getFormat("HHmm");
291     DateTimeFormat merger = DateTimeFormat.getFormat("yyyyMMddHHmmss");
292     String d = extractDate.format(date);
293     String t = extractTime.format(time) + "00";
294     return merger.parse(d + t);
295   }
296
297   public void validate(Widget widget) throws GeneratorException {
298     if (widget == eventName) {
299       getEventNameField();
300     } else if (widget == datePicker1 || widget == timePicker1 || widget == datePicker2
301         || widget == timePicker2) {
302       getDateTimeFields();
303     }
304   }
305
306   private static Date addMilliseconds(Date time1, long milliseconds) {
307     return new Date(time1.getTime() + milliseconds);
308   }
309
310   public void setFocus() {
311     eventName.setFocus(true);
312   }
313 }