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