Allow the + character in phone numbers. In the same time, fix a bug where other non...
[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 final static String[] FULL_DAY_ONLY_IDS = { "fullDayOnlyInfo1",
44       "fullDayOnlyInfo2", "fullDayOnlyInfo3", "fullDayOnlyInfo4" };
45   private final static long ONE_HOUR = 60 * 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 = timeDifference(time, time2);
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, ""
102           + info.gmtDiff);
103     }
104   }
105
106   public String getName() {
107     return "Calendar event";
108   }
109
110   public Grid getWidget() {
111     if (table != null) {
112       return table;
113     }
114     datePicker1.getDatePicker().setSelectedDate(new Date());
115     datePicker2.getDatePicker().setSelectedDate(new Date());
116     table = new Grid(8, 2);
117
118     table.setText(0, 0, "All day event");
119     table.setWidget(0, 1, fullDay);
120
121     table.setText(1, 0, "Event title");
122     table.setWidget(1, 1, eventName);
123
124     table.setText(2, 0, "Start date");
125     table.setWidget(2, 1, datePicker1);
126
127     table.setText(3, 0, "Time");
128     table.setWidget(3, 1, timePicker1);
129
130     table.setText(4, 0, "End date");
131     table.setWidget(4, 1, datePicker2);
132
133     table.setText(5, 0, "Time");
134     table.setWidget(5, 1, timePicker2);
135
136     table.setText(6, 0, "Time zone");
137     table.setWidget(6, 1, timeZones);
138
139     table.setText(7, 0, "Daylight savings");
140     table.setWidget(7, 1, summerTime);
141
142     table.getRowFormatter().getElement(3).setId(FULL_DAY_ONLY_IDS[0]);
143     table.getRowFormatter().getElement(5).setId(FULL_DAY_ONLY_IDS[1]);
144     table.getRowFormatter().getElement(6).setId(FULL_DAY_ONLY_IDS[2]);
145     table.getRowFormatter().getElement(7).setId(FULL_DAY_ONLY_IDS[3]);
146
147     fullDay.addClickListener(new ClickListener() {
148       public void onClick(Widget sender) {
149         CheckBox cb = (CheckBox) sender;
150         setFullDay(cb.isChecked());
151       }
152     });
153
154     return table;
155   }
156
157   private void setFullDay(boolean fullDay) {
158     for (String s : FULL_DAY_ONLY_IDS) {
159       Element element = DOM.getElementById(s);
160       String style = "";
161       if (fullDay) {
162         style = "none";
163       }
164       DOM.setStyleAttribute(element, "display", style);
165     }
166   }
167
168   public String getText() throws GeneratorException {
169     String eventName = getEventNameField();
170     String dates = getDateTimeFields();
171
172     String output = "";
173     output += "BEGIN:VEVENT\n";
174     output += eventName;
175     output += dates;
176     output += "END:VEVENT\n";
177     return output;
178   }
179
180   private String getEventNameField() throws GeneratorException {
181     String inputName = eventName.getText();
182     if (inputName.length() < 1) {
183       throw new GeneratorException("Event name must be at least 1 character.");
184     }
185     if (inputName.contains("\n")) {
186       throw new GeneratorException(
187           "Event name should not contain \\n characters.");
188     }
189     return "SUMMARY:" + inputName + "\n";
190   }
191
192   private String getDateTimeFields() throws GeneratorException {
193     if (fullDay.isChecked()) {
194       return getFullDayDateFields();
195     }
196     return getDateTimeValues();
197   }
198
199   private String getFullDayDateFields() throws GeneratorException {
200     Date date1 = datePicker1.getDatePicker().getSelectedDate();
201     Date date2 = datePicker2.getDatePicker().getSelectedDate();
202     if (null == date1 || null == date2) {
203       throw new GeneratorException("Start and end dates must be set.");
204     }
205     if (date1.after(date2)) {
206       throw new GeneratorException("Ending date is after starting date.");
207     }
208     DateTimeFormat isoFormatter = DateTimeFormat.getFormat("yyyyMMdd");
209     String output = "";
210     output += "DTSTART:" + isoFormatter.format(date1) + "\n";
211     output += "DTEND:" + isoFormatter.format(date2) + "\n";
212     return output;
213   }
214
215   private String getDateTimeValues() throws GeneratorException {
216     Date date1 = datePicker1.getDatePicker().getSelectedDate();
217     Date date2 = datePicker2.getDatePicker().getSelectedDate();
218     Date time1 = timePicker1.getDateTime();
219     Date time2 = timePicker2.getDateTime();
220     if (null == date1 || null == date2 || null == time1 || null == time2) {
221       throw new GeneratorException("Start and end dates/times must be set.");
222     }
223     String timezoneDelta = timeZones.getValue(timeZones.getSelectedIndex());
224     long diffTimeZone = Long.parseLong(timezoneDelta);
225     if (summerTime.isChecked()) {
226       diffTimeZone += ONE_HOUR;
227     }
228     Date dateTime1 = addMilliseconds(mergeDateAndTime(date1, time1),
229         -diffTimeZone);
230     Date dateTime2 = addMilliseconds(mergeDateAndTime(date2, time2),
231         -diffTimeZone);
232     if (dateTime1.after(dateTime2)) {
233       throw new GeneratorException("Ending date is after starting date.");
234     }
235     DateTimeFormat isoFormatter = DateTimeFormat
236         .getFormat("yyyyMMdd'T'kkmmss'Z'");
237     String output = "";
238     output += "DTSTART:" + isoFormatter.format(dateTime1) + "\n";
239     output += "DTEND:" + isoFormatter.format(dateTime2) + "\n";
240     return output;
241   }
242
243   private Date mergeDateAndTime(Date date, Date time) {
244     // Is that the only ugly way to do with GWT ? given that we don't
245     // have java.util.Calendar for instance
246     DateTimeFormat extractDate = DateTimeFormat.getFormat("yyyyMMdd");
247     DateTimeFormat extractTime = DateTimeFormat.getFormat("kkmm");
248     DateTimeFormat merger = DateTimeFormat.getFormat("yyyyMMddkkmmss");
249     String d = extractDate.format(date);
250     String t = extractTime.format(time) + "00";
251     return merger.parse(d + t);
252   }
253
254   public void validate(Widget widget) throws GeneratorException {
255     if (widget == eventName)
256       getEventNameField();
257     if (widget == datePicker1 || widget == timePicker1 || widget == datePicker2
258         || widget == timePicker2)
259       getDateTimeFields();
260   }
261
262   private static Date addMilliseconds(Date time1, long milliseconds) {
263     return new Date(time1.getTime() + milliseconds);
264   }
265
266   private static long timeDifference(Date time1, Date time2) {
267     return time2.getTime() - time1.getTime();
268   }
269
270   public void setFocus() {
271     eventName.setFocus(true);
272   }
273 }