001 /*
002 * Copyright (c) Holger Pfaff - http://pfaff.ws
003 *
004 * This software maybe used for any purpose provided the
005 * above copyright notice is retained. It is supplied as is.
006 * No warranty expressed or implied - Use at your own risk.
007 */
008
009 import java.text.*;
010 import java.util.*;
011 import java.awt.*;
012 import java.awt.event.*;
013
014 /**
015 * (#)Cal.java
016 * @author Holger Pfaff
017 * @version 3.2 19-Mar-2004<br><br>
018 *
019 * A universal calendar widget to enter/display a date optionally time
020 *
021 */
022
023 public class Cal extends Awt implements ActionListener {
024
025 /**
026 * bitmask for showing date
027 */
028 public static final int DATE = 1 << 0;
029
030 /**
031 * bitmask for showing time
032 */
033 public static final int TIME = 1 << 1;
034
035 /**
036 * currend mode: TIME, DATE, DATE|TIME
037 */
038 protected int mode = 0;
039
040 /**
041 * holds current date/time
042 */
043 protected GregorianCalendar cl = new GregorianCalendar();
044
045 /**
046 * dateformat used for this widget
047 */
048 protected DateFormat df = null;
049
050 /**
051 * timeformat used for this widget
052 */
053 protected DateFormat tf = null;
054
055 /**
056 * knobs for day of month selection
057 */
058 protected Knob[] days = new Knob[42];
059
060 /**
061 * date panel
062 */
063 protected Container datp = new BorderPanel(BULK);
064
065 /**
066 * day panel
067 */
068 protected Container dayp = new BorderPanel(BULK);
069
070 /**
071 * time panel
072 */
073 protected Container timp = new BorderPanel(BULK);
074
075 /**
076 * date label - disply current date set
077 */
078 protected Caption datl = new Caption("", CENTER);
079
080 /**
081 * time label - disply current time set
082 */
083 protected Caption timl = new Caption("", CENTER);
084
085 /**
086 * default constructor - creates date date/time widget
087 */
088 public Cal() {
089 this(DATE|TIME);
090 }
091
092 /**
093 * constructor - creates specified widget
094 *
095 * @param m mode bits: TIME DATE DATE|TIME
096 */
097 public Cal(int m) {
098 this(m, new Date());
099 }
100
101 /**
102 * constructor - creates specified widget with specified initial date/time
103 *
104 * @param m mode bits: TIME DATE DATE|TIME
105 * @param d date for initialization
106 */
107 public Cal(int m, Date d) {
108 this(m, d, Locale.getDefault());
109 }
110
111 /**
112 * constructor - creates specified widget with specified initial date/time
113 * and specified locale (language)
114 *
115 * @param m mode bits: TIME DATE DATE|TIME
116 * @param d date for initialization
117 * @param l locale to use
118 */
119 public Cal(int m, Date d, Locale l) {
120 setLayout(new BorderLayout());
121
122 df = DateFormat.getDateInstance(DateFormat.LONG, l);
123 tf = DateFormat.getTimeInstance(DateFormat.SHORT, l);
124
125 cl.setTimeZone(correctTZ(cl.getTimeZone()));
126 df.setTimeZone(correctTZ(df.getTimeZone()));
127 tf.setTimeZone(correctTZ(tf.getTimeZone()));
128
129 datp.setLayout(new ColumnLayout(7, ColumnLayout.HORZ, Awt.C, ColumnLayout.ALL, ColumnLayout.NONE));
130 dayp.setLayout(new ColumnLayout(7, ColumnLayout.ALL, Awt.C, ColumnLayout.ALL, ColumnLayout.ALL));
131 timp.setLayout(new ColumnLayout(5, ColumnLayout.HORZ, Awt.C, ColumnLayout.ALL, ColumnLayout.NONE));
132
133 addKnob("<<<", "X-" + Calendar.YEAR, datp); // year decr
134 addKnob("<<", "X-" + Calendar.MONTH, datp); // month decr
135 addKnob("<", "X-" + Calendar.DAY_OF_MONTH, datp); // day decr
136 datp.add(datl);
137 addKnob(">", "X" + Calendar.DAY_OF_MONTH, datp); // day incr
138 addKnob(">>", "X" + Calendar.MONTH, datp); // month incr
139 addKnob(">>>", "X" + Calendar.YEAR, datp); // year incr
140
141 addKnob("<<", "X-" + Calendar.HOUR, timp); // hour decr
142 addKnob("<", "X-" + Calendar.MINUTE, timp); // minute decr
143 timp.add(timl);
144 addKnob(">", "X" + Calendar.MINUTE, timp); // minute incr
145 addKnob(">>", "X" + Calendar.HOUR, timp); // hour incr
146
147
148 String[] wd = new DateFormatSymbols(l).getShortWeekdays();
149 for(int i = 0; i < 7 ; ++i) {
150 dayp.add(new Caption("" + Esc + Bold + wd[i + 1], Caption.CENTER));
151 }
152
153 Insets in = new Insets(5,13,5,13);
154
155 for(int i = 0; i < 42; ++i) {
156 days[i] = new Knob();
157 days[i].setInnerInsets(in);
158 days[i].setActionCommand("" + i);
159 days[i].addActionListener(this);
160 dayp.add(days[i]);
161 }
162
163 setMode(m);
164 setDate(d);
165 }
166
167 /**
168 * used internally to create shifting knobs
169 *
170 * @param l label for this knob
171 * @param a action cmd for this knob
172 * @param p container to add knob to
173 */
174 private void addKnob(String l, String a, Container p) {
175 Knob k = new Knob(l);
176 k.setActionCommand(a);
177 k.addActionListener(this);
178 p.add(k);
179 }
180
181 /**
182 * implement actionlistener to catch knobs pressed
183 */
184 public void actionPerformed(ActionEvent e) {
185 if(e.getActionCommand().charAt(0) == 'X') {
186 int i = Integer.parseInt(e.getActionCommand().substring(1));
187 add(Math.abs(i), i/Math.abs(i));
188 } else {
189 int i = Integer.parseInt(e.getActionCommand());
190 set(Calendar.DAY_OF_MONTH, Integer.parseInt(days[i].getLabel()));
191 }
192 notifyActionListeners(new ActionEvent(this, 0, ""));
193 }
194
195 /**
196 * increment/decrement current date/time
197 *
198 * @param f field to change within calendar
199 * @param v value to add/remove
200 */
201 public void add(int f, int v) {
202 cl.add(f, v); set();
203 }
204
205 /**
206 * set current date/time
207 *
208 * @param f field to set within calendar
209 * @param v value to set
210 */
211 public void set(int f, int v) {
212 cl.set(f, v); set();
213 }
214
215 /**
216 * set current date/time
217 *
218 * @param d date to set
219 */
220 public void setDate(Date d) {
221 cl.setTime(d); set();
222 }
223
224 /**
225 * set current date/time from string using specified date format
226 *
227 * @param d date to set
228 * @param df date format to use
229 */
230 public void setDate(String d, String df) {
231 setDate(d, new SimpleDateFormat(df));
232 }
233
234 /**
235 * set current date/time from string using specified date format
236 *
237 * @param d date to set
238 * @param df date format to use
239 */
240 public void setDate(String d, DateFormat df) {
241 try {
242 df.setTimeZone(correctTZ(df.getTimeZone()));
243 setDate(df.parse(d));
244 } catch(Exception x) {
245 }
246 }
247
248 /**
249 * get this widget's current date/time
250 */
251 public Date getDate() {
252 return cl.getTime();
253 }
254
255 /**
256 * get this widget's current time as seconds from 1970
257 */
258 public long getTime() {
259 return getDate().getTime() / 1000;
260 }
261
262 /**
263 * set current date/time
264 *
265 * @param c date/time to use
266 */
267 public void setDate(Calendar c) {
268 setDate(c.getTime());
269 }
270
271 /**
272 * set current date/time
273 *
274 * @param c date/time to use
275 */
276 public void setCalendar(Calendar c) {
277 setDate(c);
278 }
279
280 /**
281 * get this widget's date/time as Calendar object
282 */
283 public Calendar getCalendar() {
284 return cl;
285 }
286
287 /**
288 * internally called if something has changed
289 */
290 protected void set() {
291 String tl = "" + Esc + Bold + tf.format(cl.getTime());
292 String dl = "" + Esc + Bold + df.format(cl.getTime());
293
294 if(tf.equals(timl.getLabel()) == false) {
295 timl.setLabel(tl);
296 }
297
298 if(dl.equals(datl.getLabel()) == false) {
299 datl.setLabel(dl);
300 GregorianCalendar c = new GregorianCalendar(cl.get(Calendar.YEAR), cl.get(Calendar.MONTH), 1, 12, 00);
301 int fwd = c.get(Calendar.DAY_OF_WEEK) - c.getMinimum(Calendar.DAY_OF_WEEK);
302 for(int i = 0; i < 42; ++i) {
303 days[i].setVisible(i >= fwd && c.get(Calendar.MONTH) == cl.get(Calendar.MONTH));
304 days[i].setActive(c.get(Calendar.DAY_OF_MONTH) == cl.get(Calendar.DAY_OF_MONTH));
305 days[i].setLabel("" + c.get(Calendar.DAY_OF_MONTH));
306 if(i >= fwd) c.add(Calendar.DAY_OF_MONTH, 1);
307 }
308 }
309 validateTree();
310 }
311
312 /**
313 * set this widget mode - TIME, DATE or DATE|TIME
314 *
315 * @param m mode bits: TIME DATE DATE|TIME
316 */
317 public void setMode(int m) {
318 mode = m; removeAll();
319 if((m & DATE) > 0) {
320 add(datp, BorderLayout.NORTH);
321 add(dayp, BorderLayout.CENTER);
322 }
323 if((m & TIME) > 0) {
324 add(timp, BorderLayout.SOUTH);
325 }
326 }
327
328 /**
329 * get this widget's mode
330 */
331 public int getMode() {
332 return mode;
333 }
334
335 /**
336 * get this widget's date/time using the specified date format
337 *
338 * @param df date format to use
339 */
340 public String toString(String df) {
341 return df.equals("time") ? "" + getTime() : toString(new SimpleDateFormat(df));
342 }
343
344 /**
345 * get this widget's date/time using the specified date format
346 *
347 * @param df date format to use
348 */
349 public String toString(DateFormat df) {
350 return df.format(getDate());
351 }
352
353 /**
354 * Java treats MET as "Middle Eastern Time" not as "Middle European time" as expected.
355 * For now just change MET to ECT
356 */
357 protected TimeZone correctTZ(TimeZone tz) {
358 return tz.equals(TimeZone.getTimeZone("MET")) ? TimeZone.getTimeZone("ECT") : tz;
359 }
360
361 /**
362 * get this widget's required size
363 */
364 public Dimension measure() {
365 return getLayout().preferredLayoutSize(this);
366 }
367 }
368