Add address line 2 support to generator
[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.i18n.client.DateTimeFormat;
20 import com.google.gwt.user.client.DOM;
21 import com.google.gwt.user.client.Element;
22 import com.google.gwt.user.client.ui.ChangeListener;
23 import com.google.gwt.user.client.ui.CheckBox;
24 import com.google.gwt.user.client.ui.ClickListener;
25 import com.google.gwt.user.client.ui.Grid;
26 import com.google.gwt.user.client.ui.ListBox;
27 import com.google.gwt.user.client.ui.TextBox;
28 import com.google.gwt.user.client.ui.Widget;
29 import com.google.gwt.widgetideas.client.event.ChangeEvent;
30 import com.google.gwt.widgetideas.client.event.ChangeHandler;
31 import com.google.gwt.widgetideas.datepicker.client.DateBox;
32 import com.google.gwt.widgetideas.datepicker.client.TimePicker;
33 import com.google.zxing.web.generator.client.TimeZoneList.TimeZoneInfo;
34
35 import java.util.Date;
36
37 /**
38  * A Generator for calendar events. Output is in VCal format.
39  * 
40  * @author Yohann Coppel
41  */
42 public class CalendarEventGenerator implements GeneratorSource {
43   public static final String[] FULL_DAY_ONLY_IDS = { "fullDayOnlyInfo1",
44       "fullDayOnlyInfo2", "fullDayOnlyInfo3", "fullDayOnlyInfo4" };
45   private static final long ONE_HOUR = 60L * 60 * 1000;
46
47   Grid table = null;
48   TextBox eventName = new TextBox();
49   CheckBox fullDay = new CheckBox();
50   DateBox datePicker1 = new DateBox();
51   DateBox datePicker2 = new DateBox();
52   TimePicker timePicker1 = new TimePicker(new Date(), DateTimeFormat
53       .getFormat("a"), DateTimeFormat.getFormat("hh"), DateTimeFormat
54       .getFormat("mm"), null);
55   TimePicker timePicker2 = new TimePicker(new Date(), DateTimeFormat
56       .getFormat("a"), DateTimeFormat.getFormat("hh"), DateTimeFormat
57       .getFormat("mm"), null);
58   CheckBox summerTime = new CheckBox();
59   ListBox timeZones = new ListBox();
60   Date timePicker1PreviousDate = null;
61
62   public CalendarEventGenerator(final ChangeListener listener) {
63     eventName.addStyleName(StylesDefs.INPUT_FIELD_REQUIRED);
64     eventName.addChangeListener(listener);
65     datePicker1.setAnimationEnabled(true);
66     datePicker2.setAnimationEnabled(true);
67     timePicker2
68         .setDateTime(addMilliseconds(timePicker1.getDateTime(), ONE_HOUR));
69     timePicker1PreviousDate = timePicker1.getDateTime();
70
71     buildTimeZoneList();
72     timeZones.setSelectedIndex(25);
73     timePicker1.addChangeHandler(new ChangeHandler<Date>() {
74       public void onChange(ChangeEvent<Date> event) {
75         Date time = timePicker1PreviousDate;
76         Date time1 = timePicker1.getDateTime();
77         Date time2 = timePicker2.getDateTime();
78         if (time2.after(time)) {
79           // keep the same time difference if the interval is valid.
80           long diff = time2.getTime() - time.getTime();
81           timePicker2.setDateTime(addMilliseconds(time1, diff));
82         } else {
83           // otherwise erase the end date and set it to startdate + one hour.
84           timePicker2.setDateTime(addMilliseconds(time, ONE_HOUR));
85         }
86         // no need to call onChange for timePicker1, since it will be called
87         // for timePicker2 when changes are made.
88         // listener.onChange(timePicker1);
89         timePicker1PreviousDate = time1;
90       }
91     });
92     timePicker2.addChangeHandler(new ChangeHandler<Date>() {
93       public void onChange(ChangeEvent<Date> event) {
94         listener.onChange(timePicker2);
95       }
96     });
97   }
98
99   private void buildTimeZoneList() {
100     for (TimeZoneInfo info : TimeZoneList.TIMEZONES) {
101       timeZones.addItem(info.GMTRelative + ' ' + info.abreviation, String.valueOf(info.gmtDiff));
102     }
103   }
104
105   public String getName() {
106     return "Calendar event";
107   }
108
109   public Grid getWidget() {
110     if (table != null) {
111       return table;
112     }
113     datePicker1.getDatePicker().setSelectedDate(new Date());
114     datePicker2.getDatePicker().setSelectedDate(new Date());
115     table = new Grid(8, 2);
116
117     table.setText(0, 0, "All day event");
118     table.setWidget(0, 1, fullDay);
119
120     table.setText(1, 0, "Event title");
121     table.setWidget(1, 1, eventName);
122
123     table.setText(2, 0, "Start date");
124     table.setWidget(2, 1, datePicker1);
125
126     table.setText(3, 0, "Time");
127     table.setWidget(3, 1, timePicker1);
128
129     table.setText(4, 0, "End date");
130     table.setWidget(4, 1, datePicker2);
131
132     table.setText(5, 0, "Time");
133     table.setWidget(5, 1, timePicker2);
134
135     table.setText(6, 0, "Time zone");
136     table.setWidget(6, 1, timeZones);
137
138     table.setText(7, 0, "Daylight savings");
139     table.setWidget(7, 1, summerTime);
140
141     table.getRowFormatter().getElement(3).setId(FULL_DAY_ONLY_IDS[0]);
142     table.getRowFormatter().getElement(5).setId(FULL_DAY_ONLY_IDS[1]);
143     table.getRowFormatter().getElement(6).setId(FULL_DAY_ONLY_IDS[2]);
144     table.getRowFormatter().getElement(7).setId(FULL_DAY_ONLY_IDS[3]);
145
146     fullDay.addClickListener(new ClickListener() {
147       public void onClick(Widget sender) {
148         CheckBox cb = (CheckBox) sender;
149         setFullDay(cb.isChecked());
150       }
151     });
152
153     return table;
154   }
155
156   private void setFullDay(boolean fullDay) {
157     for (String s : FULL_DAY_ONLY_IDS) {
158       Element element = DOM.getElementById(s);
159       String style = fullDay ? "none" : "";
160       DOM.setStyleAttribute(element, "display", style);
161     }
162   }
163
164   public String getText() throws GeneratorException {
165     String eventName = getEventNameField();
166     String dates = getDateTimeFields();
167     StringBuilder output = new StringBuilder();
168     output.append("BEGIN:VEVENT\r\n");
169     output.append(eventName);
170     output.append(dates);
171     output.append("END:VEVENT\r\n");
172     return output.toString();
173   }
174
175   private String getEventNameField() throws GeneratorException {
176     String inputName = eventName.getText();
177     if (inputName.length() < 1) {
178       throw new GeneratorException("Event name must be at least 1 character.");
179     }
180     if (inputName.contains("\n")) {
181       throw new GeneratorException(
182           "Event name should not contain \\n characters.");
183     }
184     return "SUMMARY:" + inputName + "\r\n";
185   }
186
187   private String getDateTimeFields() throws GeneratorException {
188     if (fullDay.isChecked()) {
189       return getFullDayDateFields();
190     }
191     return getDateTimeValues();
192   }
193
194   private String getFullDayDateFields() throws GeneratorException {
195     Date date1 = datePicker1.getDatePicker().getSelectedDate();
196     Date date2 = datePicker2.getDatePicker().getSelectedDate();
197     if (null == date1 || null == date2) {
198       throw new GeneratorException("Start and end dates must be set.");
199     }
200     if (date1.after(date2)) {
201       throw new GeneratorException("Ending date is after starting date.");
202     }
203     DateTimeFormat isoFormatter = DateTimeFormat.getFormat("yyyyMMdd");
204     StringBuilder output = new StringBuilder();
205     output.append("DTSTART:");
206     output.append(isoFormatter.format(date1));
207     output.append("\r\n");
208     output.append("DTEND:");
209     output.append(isoFormatter.format(date2));
210     output.append("\r\n");
211     return output.toString();
212   }
213
214   private String getDateTimeValues() throws GeneratorException {
215     Date date1 = datePicker1.getDatePicker().getSelectedDate();
216     Date date2 = datePicker2.getDatePicker().getSelectedDate();
217     Date time1 = timePicker1.getDateTime();
218     Date time2 = timePicker2.getDateTime();
219     if (null == date1 || null == date2 || null == time1 || null == time2) {
220       throw new GeneratorException("Start and end dates/times must be set.");
221     }
222     String timezoneDelta = timeZones.getValue(timeZones.getSelectedIndex());
223     long diffTimeZone = Long.parseLong(timezoneDelta);
224     if (summerTime.isChecked()) {
225       diffTimeZone += ONE_HOUR;
226     }
227     Date dateTime1 = addMilliseconds(mergeDateAndTime(date1, time1),
228         -diffTimeZone);
229     Date dateTime2 = addMilliseconds(mergeDateAndTime(date2, time2),
230         -diffTimeZone);
231     if (dateTime1.after(dateTime2)) {
232       throw new GeneratorException("Ending date is after starting date.");
233     }
234     DateTimeFormat isoFormatter = DateTimeFormat
235         .getFormat("yyyyMMdd'T'HHmmss'Z'");
236     StringBuilder output = new StringBuilder();
237     output.append("DTSTART:");
238     output.append(isoFormatter.format(dateTime1));
239     output.append("\r\n");
240     output.append("DTEND:");
241     output.append(isoFormatter.format(dateTime2));
242     output.append("\r\n");
243     return output.toString();
244   }
245
246   private static Date mergeDateAndTime(Date date, Date time) {
247     // Is that the only ugly way to do with GWT ? given that we don't
248     // have java.util.Calendar for instance
249     DateTimeFormat extractDate = DateTimeFormat.getFormat("yyyyMMdd");
250     DateTimeFormat extractTime = DateTimeFormat.getFormat("HHmm");
251     DateTimeFormat merger = DateTimeFormat.getFormat("yyyyMMddHHmmss");
252     String d = extractDate.format(date);
253     String t = extractTime.format(time) + "00";
254     return merger.parse(d + t);
255   }
256
257   public void validate(Widget widget) throws GeneratorException {
258     if (widget == eventName) {
259       getEventNameField();
260     } else if (widget == datePicker1 || widget == timePicker1 || widget == datePicker2
261         || widget == timePicker2) {
262       getDateTimeFields();
263     }
264   }
265
266   private static Date addMilliseconds(Date time1, long milliseconds) {
267     return new Date(time1.getTime() + milliseconds);
268   }
269
270   public void setFocus() {
271     eventName.setFocus(true);
272   }
273 }