home *** CD-ROM | disk | FTP | other *** search
/ Chip 1998 November / Chip_1998-11_cd.bin / tema / Cafe / jfc.bin / DateChooser.java < prev    next >
Text File  |  1998-02-26  |  67KB  |  1,995 lines

  1. /*
  2.  * @(#)DateChooser.java    1.22 11/26/97
  3.  * 
  4.  * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  * 
  19.  * @author James Gosling
  20.  * @author Brian Gerhold
  21.  * rev 1.0
  22.  * 08/14/97
  23.  */
  24.  
  25. package com.sun.java.swing;
  26.  
  27. import java.awt.*;
  28. import java.awt.event.*;
  29. import java.io.*;
  30. import java.util.*;
  31. import java.text.*;
  32. import com.sun.java.swing.border.*;
  33.  
  34.  
  35. /* The DateChooser is a Component used for fast and easy
  36.  * date and/or time selection. It is highly configurable and
  37.  * cutomizable.
  38.  */
  39.  
  40. public class DateChooser extends Container implements ActionListener,
  41.                          AdjustmentListener {
  42.   /** The most compact style.  Months will be numbered, not named. */
  43.   public static final int TINY = 0;
  44.   /** A fairly compact style.  Months will be named by their abbreviations. */
  45.   public static final int SMALL = 1;
  46.   /** A more expansive style.  Month names will not be abbreviated. */
  47.   public static final int MEDIUM = 2;
  48.   /** A verbose style that displays a full calendar and/or clock face */
  49.   public static final int LARGE = 3;
  50.                
  51.   /** Display only the date */
  52.   public static final int DATE_MODE = 0;
  53.   /** Display only the time */
  54.   public static final int TIME_MODE = 1;
  55.   /** Display both the date and time */
  56.   public static final int DATE_TIME_MODE = 2;
  57.  
  58.   /*protected*/private String[] months;  //String array of month names or numbers
  59.   /*protected*/private String[] bigmonths; //String array of fullblown month names
  60.   /*protected*/private Calendar cal;  //Calendar class to store date/time information
  61.   /*protected*/private Locale myLocale; //optional Locale for i18n purposes
  62.   /*protected*/private TimeZone myZone; //optional TimeZone for i18n purposes
  63.   /*protected*/private JButton myCalendarButton; //button for pop-up calendar
  64.   /*protected*/private JButton myClockButton; //button for pop-up clock
  65.   /*protected*/private JPopupMenu ClockPop; //pop up to hold ClockFace 
  66.   /*protected*/private Spinner month, year,day, hours, minutes, ampm; //data fields
  67.   /*protected*/private MiniCal minical; //calendar that shows in pop-up
  68.   /*protected*/private ClockFace clockface; //clockface that shows in pop-up
  69.   /*protected*/private int myMode; //mode 
  70.   /*protected*/private int myStyle; //style
  71.   /*protected*/private JPanel panel1; //holds Spinner fields and buttons for pop-ups
  72.   /*protected*/private JPanel panel2;//used only in LARGE style, used to hold cal and clock
  73.   /*protected*/private JPanel datePane; //encompasses date fields and "CAL" button
  74.   /*protected*/private JPanel timePane; //encompasses time fields and "CLOCK" button
  75.   /*protected*/private int daysInWeek; //so that the Calendar stuff is not hard-wired
  76.   /*protected*/private DateFormatSymbols dfd; //used to generate Locale specific info
  77.   /*protected*/private char[] format; //stores format of date and time fields
  78.   /*protected*/private boolean TwelveHourClock; //Locale specific
  79.   /*protected*/private int dateOrder; //derived from parsing format
  80.   /*protected*/private int timeOrder; //derived from parsing form
  81.   /*protected*/private char[] monthyear; //String separator between month and year
  82.   /*protected*/private char[] monthdate; //String separator between month and date
  83.   /*protected*/private char[] dateyear; //String separator between date and year
  84.   /*protected*/private char[] hourminute; //String separator between hour and minute
  85.  
  86.   /* generally the DateChooser will take a mode and a style in its construcor 
  87.    * however Locale and TimeZone are added as optional arguments for i18n
  88.    */
  89.  
  90.   public DateChooser(int mode, int style){
  91.     init(mode, style, TimeZone.getDefault(), Locale.getDefault());
  92.   }
  93.   public DateChooser(int mode, int style, TimeZone zone){
  94.     init(mode, style, zone, Locale.getDefault());
  95.   }
  96.   public DateChooser(int mode, int style, Locale aLocale){
  97.     init(mode, style, TimeZone.getDefault(), aLocale);
  98.   }
  99.   public DateChooser(int mode, int style, TimeZone zone, Locale aLocale) {
  100.     init(mode, style, zone, aLocale);
  101.   }
  102.  
  103.   //with optional Locale and TimeZone info set, internal constructor is called
  104.   private void init(int mode, int style, TimeZone zone, Locale aLocale) {
  105.  
  106.     //trap out of bounds arguments
  107.     if((mode < 0) || (mode > 2)){
  108.       throw new IllegalArgumentException
  109.     ("Mode for DateChooser must be 0, 1, or 2");
  110.     }
  111.     if((style < 0) || (style > 3)){
  112.       throw new IllegalArgumentException
  113.     ("Style for DateChooser must be 0, 1,2, or 3");
  114.     }
  115.     
  116.     //initialize Calendar and set DateChooser layout
  117.     cal = Calendar.getInstance(zone, aLocale);
  118.     setLayout(new BoxLayout(this,BoxLayout.Y_AXIS));
  119.  
  120.     //instantiate panel1, setLayout
  121.     panel1 = new JPanel(false);
  122.     panel1.setLayout(new FlowLayout(FlowLayout.CENTER,0,0));
  123.  
  124.     //store mode, style, timezone and Locale info
  125.     myMode = mode;
  126.     myStyle = style;
  127.     myZone = zone;
  128.     myLocale = aLocale;
  129.  
  130.     /* retrieve the format style to be used by DateFormat in determining how to
  131.      * order fields and what size strings and digits to use
  132.      */
  133.     int formatter = 0;
  134.     switch(myStyle){
  135.     case TINY:
  136.       formatter = DateFormat.SHORT;
  137.       break;
  138.     case SMALL:
  139.       formatter = DateFormat.LONG;
  140.       break;
  141.     case MEDIUM:
  142.       formatter = DateFormat.LONG;
  143.       break;
  144.     case LARGE:
  145.       formatter = DateFormat.LONG;
  146.       break;
  147.     }
  148.  
  149.     //get a DateFormat
  150.     SimpleDateFormat df = (SimpleDateFormat) DateFormat.getDateTimeInstance
  151.                           (formatter,formatter, myLocale);
  152.  
  153.     /* this checks to see if the pattern string of the date and time is missing
  154.      * any of the fields. if so, the pattern string is defaulted to LONG
  155.      */
  156.     String checkString = df.toPattern();
  157.     if((checkString.indexOf('M') == -1) || (checkString.indexOf('y') == -1) || 
  158.        (checkString.indexOf('d') == -1) || ((checkString.indexOf('h') == -1) &&
  159.        (checkString.indexOf('H') == -1))|| (checkString.indexOf('m') == -1)){
  160.       df = (SimpleDateFormat) DateFormat.getDateTimeInstance
  161.     (DateFormat.LONG,DateFormat.LONG, myLocale);
  162.     }
  163.  
  164.     //set class wide variable dfd,use it to get Locale and format specific info
  165.     dfd = df.getDateFormatSymbols();
  166.     daysInWeek = (dfd.getShortWeekdays()).length - 1;
  167.  
  168.     //set class wide variable format, used to order fields
  169.     //format = new char[((df.toPattern()).toCharArray()).length];
  170.     format = (df.toPattern()).toCharArray();
  171.  
  172.     int i; //declare a temporary int for for loops and such
  173.  
  174.     //instantiate panel2, set layout
  175.     panel2 = new JPanel(false);
  176.     panel2.setLayout(new FlowLayout(FlowLayout.CENTER,0,0));
  177.  
  178.     //mode 0 or 2 indicates that date fields will be present
  179.     if ((myMode == DATE_MODE) || (myMode == DATE_TIME_MODE)){
  180.       /* set the JPanel that will hold the date fields using a 
  181.        * protected function that may be overwritten in the event that the 
  182.        * developer desires something more than what id the default. by default
  183.        * a FilledBorderedPane is returned (see class below
  184.        */
  185.       datePane = createJPanel();
  186.       
  187.       /* dateOrder is an integer derived from parsing the format char[]
  188.        * below is the list of values and corresponding field orderings:
  189.        *
  190.        * 0: month, day, year
  191.        * 1: month, year, day
  192.        * 2: day, month, year
  193.        * 3: day, year, month
  194.        * 4: year, month, day
  195.        * 5: year, day, month
  196.        */
  197.       dateOrder = OrderParser('M','M','d','y');
  198.       
  199.       /* instaniate a pop-up Calendar. the constructor takes and int because
  200.        * in the LARGE style, the minical will be permanently added to panel2,
  201.        * thus the calendar within minical will be set in a panel. otherwise
  202.        * the calendar within minical is set in a JPopUpMenu
  203.        */
  204.       if(myStyle != LARGE){
  205.     minical = new MiniCal(0);
  206.       }
  207.       else{
  208.     minical = new MiniCal(1);
  209.       }
  210.  
  211.       /* the SpinnerLinker is a quickly hacked together class that watches all
  212.        * three spinners a set, and will cycle between them on a key event
  213.        */
  214.       SpinnerLinker linker1 = new SpinnerLinker(null,null,null);
  215.       
  216.       /* given the dateOrder, add month, day, and year to the datePane in the
  217.        * correct order with the correct StringSeparators between them. also 
  218.        * the order of the Spinners for the SpinnerLinker
  219.        */
  220.       switch(dateOrder){
  221.       case 0:
  222.     monthdate = SeparatorParser('M','M','d');
  223.     addMonth(String.copyValueOf(monthdate));
  224.     dateyear = SeparatorParser('d','d','y');
  225.     addDate(String.copyValueOf(dateyear));
  226.     addYear(null);
  227.     linker1 = new SpinnerLinker(month,day,year);
  228.     break;
  229.       case 1:
  230.     monthyear = SeparatorParser('M','M','y');
  231.     addMonth(String.copyValueOf(monthyear));
  232.     dateyear = SeparatorParser('d','d','y');
  233.     addYear(String.copyValueOf(dateyear));
  234.     addDate(null);
  235.     linker1 = new SpinnerLinker(month,year,day);
  236.     break;
  237.       case 2:
  238.     monthdate = SeparatorParser('M','M','d');
  239.     addDate(String.copyValueOf(monthdate));
  240.     monthyear = SeparatorParser('M','M','y');
  241.     addMonth(String.copyValueOf(monthyear));
  242.     addYear(null);
  243.     linker1 = new SpinnerLinker(day,month,year);
  244.     break;
  245.       case 3:
  246.     dateyear = SeparatorParser('d','d','y');
  247.     addDate(String.copyValueOf(dateyear));
  248.     monthyear = SeparatorParser('M','M','y');
  249.     addYear(String.copyValueOf(monthyear));
  250.     addMonth(null);
  251.     linker1 = new SpinnerLinker(day,year,month);
  252.     break;
  253.       case 4:
  254.     monthyear = SeparatorParser('M','M','y');
  255.     addYear(String.copyValueOf(monthyear));
  256.     monthdate = SeparatorParser('M','M','d');
  257.     addMonth(String.copyValueOf(monthdate));
  258.     addDate(null);
  259.     linker1 = new SpinnerLinker(year,month,day);
  260.     break;
  261.       case 5:
  262.     dateyear = SeparatorParser('d','d','y');
  263.     addYear(String.copyValueOf(dateyear));
  264.     monthdate = SeparatorParser('M','M','d');
  265.     addDate(String.copyValueOf(monthdate));
  266.     addMonth(null);
  267.     linker1 = new SpinnerLinker(year,day,month);
  268.     break;
  269.       }
  270.       
  271.       //set the SpinnerLinker to listen to all three spinners
  272.       month.addKeyListener(linker1);
  273.       day.addKeyListener(linker1);
  274.       year.addKeyListener(linker1);
  275.  
  276.       /*if the style is greater than tiny, add the minical (see function below
  277.        *for details)
  278.        */
  279.       if (myStyle > TINY){
  280.     addMiniCal();
  281.       }
  282.       
  283.       /* the line "setDatePanePreferences()" calls a protected function that
  284.        * sets the prefernces for the datePane, primarily just the border 
  285.        * setting. however, because the default from createJPanel is a 
  286.        * FilledBorderedPane, it is neccessary to set the fill color outside 
  287.        * setDatePanePreferences in the case that the developer desires a 
  288.        * FilledBorderedPane with a different border, but does not wish to 
  289.        * overwrite getJPanel. setFillColor cannot be called 
  290.        */
  291.       if(datePane instanceof FilledBorderedPane){
  292.     ((FilledBorderedPane)(datePane)).setFillColor
  293.       (month.getBackgroundColor());
  294.       }
  295.       setDatePanePreferences();
  296.       
  297.       panel1.add(datePane); //add the datePane to upper panel, panel1
  298.     }
  299.  
  300.     //mode 1 or 2 indicates that time fields will be present
  301.     if ((myMode == TIME_MODE) || (myMode == DATE_TIME_MODE)){
  302.       //set the JBorderPane that will hold the timefields
  303.       timePane =createJPanel();
  304.       
  305.       char ach; //a char to get individual elements of format
  306.       int g; //an indexer to run through format
  307.       ach = format[0];
  308.       g = 0;
  309.       
  310.       /* the format char[] will contain 'h' if the clock for the given Locale 
  311.        * is a twelve hour clock, or 'H' if the clock is a 24 hour clock. these
  312.        * statements iterate through format determine if the Locale's clock is
  313.        * 12 or 24 hour, then set class wide boolean
  314.        */
  315.       while((ach != 'h') && (ach != 'H')){
  316.     g++; 
  317.     ach = format[g];
  318.       }
  319.       if(ach == 'h'){
  320.     TwelveHourClock = true;
  321.       }
  322.       else{
  323.     TwelveHourClock = false;
  324.       }
  325.       
  326.       /* timeOrder is an integer derived from parsing the format char[]
  327.        * below is the list of values and corresponding field orderings:
  328.        *
  329.        * 0: hours, minutes, ampm
  330.        * 1: hours, ampm, minutes
  331.        * 2: minutes, hours, ampm
  332.        * 3: minutes, ampm, hours
  333.        * 4: ampm, hours, minutes
  334.        * 5: ampm, minutes, hours
  335.        */
  336.       timeOrder = OrderParser('h','H','m','a');
  337.       
  338.       hourminute = SeparatorParser('h','H','m'); //get String between h and m
  339.       SpinnerLinker linker2 = new SpinnerLinker(null,null,null);
  340.       
  341.       /* given the timeOrder,add hours,minutes,and ampm to the timePane in the
  342.        * correct order with the correct String Separators between them. also 
  343.        * the order of the Spinners for the SpinnerLinker, note that ampm is 
  344.        * only added for Loacles witha 12 hour clock
  345.        */
  346.       switch(timeOrder){
  347.       case 0:
  348.     addHour(String.copyValueOf(hourminute));
  349.     addMinute(null);
  350.     if(TwelveHourClock){
  351.       addAMPM();
  352.     }
  353.     linker2 = new SpinnerLinker(hours,minutes,ampm);
  354.     break;
  355.       case 1:
  356.     addHour(null);
  357.     if(TwelveHourClock){
  358.       addAMPM();
  359.     }
  360.     addMinute(null);
  361.     linker2 = new SpinnerLinker(hours,ampm,minutes);
  362.     break;
  363.       case 2:
  364.     addMinute(String.copyValueOf(hourminute));
  365.     addHour(null);
  366.     if(TwelveHourClock){
  367.       addAMPM();
  368.     }
  369.     linker2 = new SpinnerLinker(minutes,hours,ampm);
  370.     break;
  371.       case 3:
  372.     addMinute(null); 
  373.     if(TwelveHourClock){
  374.       addAMPM();
  375.     }
  376.     addHour(null);
  377.     linker2 = new SpinnerLinker(minutes,ampm,hours);
  378.     break;
  379.       case 4:
  380.     if(TwelveHourClock){
  381.       addAMPM();
  382.     }
  383.     addHour(String.copyValueOf(hourminute));
  384.     addMinute(null);
  385.     linker2 = new SpinnerLinker(ampm,hours,minutes);
  386.     break;
  387.       case 5:
  388.     if(TwelveHourClock){
  389.       addAMPM();
  390.     }
  391.     addMinute(String.copyValueOf(hourminute));
  392.     addHour(null);
  393.     linker2 = new SpinnerLinker(ampm,minutes,hours);
  394.     break;
  395.       }
  396.     
  397.       /*set the SpinnerLinker to listen to hours and minutes, and ampm if the
  398.        *Locale uses a 12 hour clock. Also calls addClock() (see below).
  399.        */
  400.       hours.addKeyListener(linker2);
  401.       minutes.addKeyListener(linker2);
  402.       if(TwelveHourClock){
  403.     ampm.addKeyListener(linker2);
  404.     addClock();
  405.       }
  406.       
  407.       //same as with datePane (see above)
  408.       if(timePane instanceof FilledBorderedPane){
  409.     ((FilledBorderedPane)(timePane)).setFillColor
  410.       (hours.getBackgroundColor());
  411.       }
  412.       setTimePanePreferences();
  413.  
  414.       panel1.add(timePane); //add the datePane to upper panel, panel1
  415.     }
  416.     add(panel1); //upper panel, panel1 to DateChooser
  417.  
  418.     // add lower panel, panel2, but only if there is anything in the panel
  419.     if((myStyle == LARGE) && ((myMode != TIME_MODE) || (TwelveHourClock))){
  420.       add(panel2);
  421.     }
  422.   }
  423.                
  424.   /*this function is designed to parse the char[] format, and return an int
  425.    *corresponding to the order of the elements (see timeOrder and dateOrder
  426.    *above for translations of ints to orders. this function takes four args:
  427.    *one for each of the letters being searched for in the parsing (a, b, c) and
  428.    *one (A) because the hours in format may be expressed as 'h' or 'H'. below
  429.    *is a translation of the order the args occur in to the int returned:
  430.    *
  431.    * 0: (a or A), b, c
  432.    * 1: (a or A), c, b
  433.    * 2: b, (a or A), c
  434.    * 3: b, c, (a or A)
  435.    * 4: c, (a or A), b
  436.    * 5: c, b, (a or A)
  437.    */
  438.   private int OrderParser(char a, char A, char b, char c){
  439.     int q;
  440.     int block;
  441.     int offset;
  442.     char ch1;
  443.     ch1 = format[0];
  444.     q = 0;
  445.     while((ch1 != a) && (ch1 != A) && (ch1 != b) && (ch1 != c)){
  446.       q++; 
  447.       ch1 = format[q];
  448.     }
  449.     q++;
  450.     if(ch1 == c) {
  451.       block = 2;
  452.       offset = subParser(a,A,b,q);
  453.     }
  454.     else if(ch1 == b){
  455.       block = 1;
  456.       offset = subParser(a,A,c,q);
  457.     }
  458.     else{
  459.       block = 0;
  460.       offset = subParser(b,b,c,q);
  461.     }
  462.     return (2*block + offset);
  463.   }
  464.  
  465.   /*this function is designed to parse the char[] format and return a char[]
  466.    *which corresponds to the chars separating the elements (a or A) and b. again
  467.    *the need for the extra arg A is the result of the fact that hours may be
  468.    *represented as 'h' or 'H'.
  469.    */
  470.   private char[] SeparatorParser(char a, char A, char b){
  471.     
  472.     int q = 0;
  473.     int count = 0;
  474.     char ch1,ch2;
  475.     char[] storage = new char[format.length];
  476.     char[] toReturn;
  477.     ch1 = format[q];
  478.     while((ch1 != a) && (ch1 !=A) && (ch1 != b)){
  479.       q++;
  480.       ch1 = format[q];
  481.     }
  482.     ch2 = ch1;
  483.     q++;
  484.     ch1 = format[q];
  485.     while(ch2 == ch1){
  486.       q++;
  487.       ch1 = format[q];
  488.     }
  489.     if(ch2 == b){
  490.       while((ch1 != a) && (ch1 != A)){
  491.       storage[count] = ch1;
  492.       count++;
  493.       q++;
  494.       ch1 = format[q];
  495.       }
  496.     }
  497.     else{
  498.       while(ch1 != b){
  499.     storage[count] = ch1;
  500.     count++;
  501.     q++;
  502.     ch1 = format[q];
  503.       }
  504.     }    
  505.     toReturn = new char[count];
  506.     for(q=0; q<count; q++){
  507.       toReturn[q] = storage[q];
  508.     }
  509.     return toReturn;
  510.   }
  511.  
  512.   /*this function is a helper function for OrderParser (see above).this function
  513.    *is designed to return an int corresponding to the order of (a or A) and b.
  514.    *again the extra arg A is the result of the fact that hours may be expressed
  515.    *as 'h' or 'H'. the int arg is the index in format that the search will begin
  516.    *at. below is a translation from the int returned to the order or the args:
  517.    *
  518.    * 0: (a or A), b
  519.    * 1: b, (a or A)
  520.    */
  521.   private int subParser(char a, char A, char b, int q)
  522.   {
  523.     char ch1;
  524.     ch1 = format[q];
  525.     while((ch1 != a) && (ch1 != A) && (ch1 != b)){
  526.       q++; 
  527.       ch1 = format[q];
  528.     }
  529.     if(ch1 == b){
  530.       return 1;
  531.     }
  532.     else{
  533.       return 0;
  534.     }
  535.   }
  536.  
  537.   /*these  add* functions are designed to instatiate the appropriate Spinner, 
  538.    *and set some of its inherent properties (those that are not subject to
  539.    *change due to developer customization. these pieces of codewere put into 
  540.    *functions because they occurred in different orders depending on the 
  541.    *dateOrder/timeOrder int. note that all Spinners are instantiated via the
  542.    *protected funtion createSpinner()and createStringSpinner. this is to allo
  543.    *developers to subclass the Spinner class, and then have that subclass added
  544.    *as the Spinner fields in the DateChooser
  545.    */
  546.   private void addMonth(String Separator){
  547.     months=new String[12];
  548.     bigmonths = new String[12];
  549.     bigmonths = dfd.getMonths();
  550.     switch (myStyle) {
  551.     case TINY:
  552.       months = new String[] {
  553.     "01", "02", "03", "04", "05", "06",
  554.       "07", "08", "09", "10", "11", "12"
  555.       };
  556.       break;
  557.     case SMALL:
  558.       months = dfd.getShortMonths();
  559.       break;
  560.     default:
  561.       months = dfd.getMonths();
  562.       break;
  563.     }
  564.     String t = months[months.length - 1];
  565.     if (t == null || t.length() <= 0) {
  566.       // workaround jdk1.1 bug
  567.       String n[] = new String[months.length - 1];
  568.       System.arraycopy(months, 0, n, 0, n.length);
  569.       months = n;
  570.     }
  571.     month = createStringSpinner(0,Separator, months);
  572.     month.setMaximum(11);
  573.     month.setValue(cal.get(Calendar.MONTH));
  574.     datePane.add(setMonthPreferences());
  575.     month.addAdjustmentListener(this);
  576.   }
  577.   private void addYear(String Separator){
  578.     year = createSpinner(1997,Separator);
  579.     year.setMinimum(1);
  580.     year.setMaximum(10000);
  581.     year.setValue(cal.get(Calendar.YEAR));
  582.     datePane.add(setYearPreferences());
  583.     year.addAdjustmentListener(this);
  584.   }
  585.   private void addDate(String Separator){
  586.     day = createSpinner(0,Separator);
  587.     if (myStyle == TINY){
  588.       day.setLeadingPad(0);
  589.     }
  590.     day.setMinimum(1);
  591.     day.setMaximum(getDaysInMonth(cal.get(Calendar.MONTH),
  592.                   cal.get(Calendar.YEAR)));
  593.     day.setDigits(2);
  594.     day.setValue(cal.get(Calendar.DATE));
  595.     datePane.add(setDayPreferences());
  596.     day.addAdjustmentListener(this);
  597.   }
  598.   private void addMiniCal(){
  599.     panel1.add(minical);
  600.     if ((myStyle == SMALL) || (myStyle == MEDIUM)){
  601.       myCalendarButton = new JButton("CAL");
  602.       myCalendarButton.addActionListener(this);
  603.       datePane.add(myCalendarButton);
  604.     }
  605.     if (myStyle == LARGE){    
  606.       minical.showMeTheMoney();
  607.       panel2.add(minical.miniPanel);
  608.     }
  609.   }
  610.   private void addHour(String Separator){
  611.     int p;
  612.     hours =createSpinner(0,Separator);
  613.     if(TwelveHourClock){
  614.       hours.setMinimum(1);
  615.       hours.setMaximum(12);
  616.     }
  617.     else{
  618.       hours.setMinimum(0);
  619.       hours.setMaximum(23);
  620.     }
  621.     hours.setDigits(2);
  622.     hours.setValue(cal.get(Calendar.HOUR));
  623.     timePane.add(setHourPreferences());
  624.   }
  625.   private void addMinute(String Separator){
  626.     int i;
  627.     minutes = createSpinner(0,Separator);
  628.     minutes.setMinimum(0);
  629.     minutes.setMaximum(59);
  630.     minutes.setDigits(2);
  631.     minutes.setValue(cal.get(Calendar.MINUTE));
  632.     timePane.add(setMinutePreferences());
  633.   }
  634.   private void addAMPM(){
  635.     String ampms[] = dfd.getAmPmStrings();    
  636.     ampm = createStringSpinner(0,null,ampms);
  637.     ampm.setValue(cal.get(Calendar.AM_PM));
  638.     timePane.add(setAMPMPreferences());
  639.   }
  640.   private void addClock(){
  641.     if ((myStyle == MEDIUM) || (myStyle == SMALL)){
  642.       clockface = new ClockFace();
  643.       myClockButton= new JButton("CLOCK");
  644.       myClockButton.addActionListener(this);
  645.       timePane.add(myClockButton);
  646.       ClockPop = new JPopupMenu();
  647.       ClockPop.setLayout(new BorderLayout());
  648.       ClockPop.add(clockface, BorderLayout.CENTER);
  649.       JButton done = new JButton("Done");
  650.       done.addActionListener(this);
  651.       ClockPop.add(done, BorderLayout.SOUTH);
  652.     }
  653.     if(myStyle == LARGE){
  654.       clockface = new ClockFace();
  655.       panel2.add(clockface);
  656.     }
  657.   }
  658.  
  659.   /*these two functions are used when instantiating the Spinner fields in the 
  660.    *DateChooser, and are overwritable in a DateChooser subclass to allow 
  661.    *developers to add extra functionality by subclassing a Spinner and having 
  662.    *that subclass added as the fields in the DateChooser
  663.    */
  664.   /*protected*/private Spinner createSpinner(int startValue, String text){
  665.     return new Spinner(startValue,text);
  666.   }
  667.   /*protected*/private StringSpinner createStringSpinner(int startValue, String text,
  668.                           String[] names){
  669.     return new StringSpinner(startValue,text,names);
  670.   }
  671.  
  672.   /*these functions are used in setting the default preferneces for the Spinner
  673.    *fields in the DateChooser. they are overwritable in a DateChooser subclass
  674.    *to allow developers to customize the properties of the individual Spinner
  675.    *fields of the DateChooser. it should be noted that all the set*Preferences
  676.    *functions return a JComponent. the reason for this is the possible need to 
  677.    *wrap the Spinner field within another JComponent and have that JComponent 
  678.    *added to the datePane/timePane instead of the Spinner itself. a good example
  679.    *of this is the setMonthPreferences, which returns a JComboBox in a MEDIUM
  680.    *style.
  681.    */
  682.   /*protected*/private JComponent setDayPreferences(){
  683.     day.setWrap(true);    
  684.     day.setBorder(new EmptyBorder(0,0,0,0));
  685.     return day;
  686.   }
  687.   /*protected*/private JComponent setMonthPreferences(){
  688.     month.setWrap(true);   
  689.     month.setBorder(new EmptyBorder(0,0,0,0));
  690.     if(myStyle == MEDIUM){
  691.       int i;
  692.       JComboBox combo = new JComboBox();
  693.       for(i = 0; i<12; i++){
  694.     combo.addPossibleValue((Object)(months[i]));
  695.       }
  696.       combo.setCurrentValueIndex(month.getValue());
  697.       combo.setEditable(true);
  698.       SyncTypeComboBoxEditor editor = new SyncTypeComboBoxEditor(combo);
  699.       editor.setEditorComponent(month);
  700.       combo.setEditor(editor);
  701.       return combo;
  702.     }
  703.     return month;    
  704.   }
  705.   /*protected*/private JComponent setYearPreferences(){
  706.     year.setWrap(false);
  707.     year.setBorder(new EmptyBorder(0,0,0,0));
  708.     return year;
  709.   }
  710.   /*protected*/private JComponent setHourPreferences(){
  711.     hours.setWrap(true);
  712.     hours.setBorder(new EmptyBorder(0,0,0,0));
  713.     if((myStyle == MEDIUM) || ((myStyle >= MEDIUM) && !(TwelveHourClock))) {
  714.       int i,j;
  715.       j = hours.getMaximum()+1;
  716.       JComboBox combo = new JComboBox();
  717.       for(i = hours.getMinimum(); i<j; i++){
  718.     combo.addPossibleValue((Object)(Integer.toString(i)));
  719.       }
  720.       combo.setCurrentValueIndex(hours.getValue()-hours.getMinimum());
  721.       combo.setEditable(true);
  722.       SyncTypeComboBoxEditor editor = new SyncTypeComboBoxEditor(combo);
  723.       editor.setEditorComponent(hours);
  724.       combo.setEditor(editor);
  725.       return combo;
  726.     }
  727.     return hours;
  728.   }
  729.   /*protected*/private JComponent setMinutePreferences(){
  730.     minutes.setWrap(true);    
  731.     minutes.setLeadingPad(0);
  732.     minutes.setBorder(new EmptyBorder(0,0,0,0));
  733.     if((myStyle == MEDIUM) || ((myStyle >= MEDIUM) && !(TwelveHourClock))) {
  734.       int j;
  735.       j = minutes.getMaximum()+1;
  736.       JComboBox combo = new JComboBox();
  737.       String minuteString;    
  738.       for (int i=0; i<60; i++){
  739.     if (i<10){
  740.       minuteString = ("0" + Integer.toString(i));
  741.     }
  742.     else{
  743.       minuteString = Integer.toString(i);
  744.     }
  745.     combo.addPossibleValue((Object)(minuteString));
  746.       }
  747.       combo.setCurrentValueIndex(minutes.getValue());
  748.       combo.setEditable(true);
  749.       SyncTypeComboBoxEditor editor = new SyncTypeComboBoxEditor(combo);
  750.       editor.setEditorComponent(minutes);
  751.       combo.setEditor(editor);
  752.       return combo;
  753.     }
  754.     return minutes;        
  755.   }
  756.   /*protected*/private JComponent setAMPMPreferences(){
  757.     ampm.setWrap(true);
  758.     ampm.setBorder(new EmptyBorder(0,0,0,0));
  759.     return ampm;
  760.   }
  761.  
  762.   /*this function is used when instatiating the JPanels datePane and 
  763.    *timePane. it is overwritable in a DateChooser subclass to account for the
  764.    *need for some extra functionality in a JPanel not provided in the
  765.    *default settings of the DateChooser. it should be noted that the 
  766.    *default return value is a FilledBorderedPane, in the construction of which
  767.    *the layout is set. because of timing issues, it is neccesary that the layout
  768.    *for the returned JPanel be set here. this is pointed out in the 
  769.    *DateChooser spec and API.
  770.    */
  771.   /*protected*/private JPanel createJPanel(){
  772.     return new FilledBorderedPane();
  773.   }
  774.  
  775.   protected static Border paneBorder = new BevelBorder(1);
  776.  
  777.   /*these set*Preferences functions set the default properties for the two
  778.    *JPanels datePane and timePane. they are overwritable in a DateChooser
  779.    *subclass to allow these preferences to be customizable
  780.    */
  781.   /*protected*/private void setDatePanePreferences(){
  782.     datePane.setBorder(paneBorder);
  783.   }
  784.   /*protected*/private void setTimePanePreferences(){
  785.     timePane.setBorder(paneBorder);
  786.   }
  787.  
  788.   /*these two enable functions are designed to add or remove the pop-up button
  789.    *(or permanent panel) of the appropriate component. this is to allow further
  790.    *diversity in the DateChooser, so that these components may be removed if
  791.    *they are unwanted or added if they are.
  792.    * /
  793.   public void setCalendarEnable(boolean enable){
  794.     if(enable){
  795.       if (minical == null){
  796.     addMiniCal();
  797.       }
  798.     }
  799.     else{
  800.       if (minical != null){
  801.     if ((myStyle == MEDIUM) || (myStyle == SMALL)){
  802.       panel1.remove(myCalendarButton);
  803.     }
  804.     else if (myStyle == LARGE){
  805.       panel2.remove(minical.miniPanel);
  806.     }
  807.     minical = null;
  808.       }
  809.     }
  810.   }
  811.   public void setClockEnable(boolean enable){
  812.     if(enable){
  813.       if (clockface== null){
  814.     addClock();
  815.       }
  816.     }
  817.     else{
  818.       if (clockface != null){
  819.     if ((myStyle == MEDIUM) || (myStyle == SMALL)){
  820.       panel1.remove(myClockButton);
  821.     }
  822.     else if (myStyle == LARGE){
  823.       panel2.remove(clockface);
  824.     }
  825.     clockface = null;
  826.       }
  827.     }
  828.   }
  829. */
  830.  
  831.   /*an accesibilty function which returns the string representation of the 
  832.    *the Calendar cal, which is storing the time and date data.
  833.    */
  834.   public String toString() {
  835.     return getDate().toString();
  836.   }
  837.  
  838.   //set the Calendar and the UI with a Date or Calendar Object
  839.   public void setDate(Date v) { cal.setTime(v); cal2UI(); }
  840.   public void setDate(Calendar v) { setDate(v.getTime()); }
  841.   public void setDate(Object v){
  842.     if (v instanceof Date) setDate((Date)v);
  843.     else if (v instanceof Calendar) setDate(((Calendar)v).getTime());
  844.     else throw new IllegalArgumentException();
  845.   }
  846.  
  847.  
  848.   //returns a Date Object storing the values of the DateChooser
  849.   public Date getDate() {
  850.     UI2cal();
  851.     return cal.getTime();
  852.   }
  853.  
  854.   /*this function is designed to set the values in the Calendar storage element
  855.    *from the values currently in the UI
  856.    */
  857.   void UI2cal() {
  858.     if ((myMode == DATE_MODE ) || (myMode == DATE_TIME_MODE)){
  859.       cal.set(Calendar.YEAR, year.getValue());
  860.       cal.set(Calendar.MONTH, month.getValue());
  861.       cal.set(Calendar.DATE, day.getValue());
  862.     }
  863.     if ((myMode == TIME_MODE ) || (myMode == DATE_TIME_MODE)){
  864.       cal.set(Calendar.HOUR, hours.getValue());
  865.       cal.set(Calendar.MINUTE, minutes.getValue());
  866.       if(TwelveHourClock){
  867.     cal.set(Calendar.AM_PM, ampm.getValue());
  868.       }
  869.     }
  870.   }
  871.                
  872.   /*this function is designed to set the values in the UI from the values in the
  873.    *Calendar storage element.
  874.    */
  875.   void cal2UI() {
  876.     if ((myMode == DATE_MODE ) || (myMode == DATE_TIME_MODE)){
  877.       year.setValue(cal.get(Calendar.YEAR));
  878.       month.setValue(cal.get(Calendar.MONTH));
  879.       day.setValue(cal.get(Calendar.DATE));
  880.       day.setMaximum(getDaysInMonth(month.getValue(),year.getValue()));
  881.     }
  882.     if ((myMode == TIME_MODE ) || (myMode == DATE_TIME_MODE)){
  883.       hours.setValue(cal.get(Calendar.HOUR));
  884.       minutes.setValue(cal.get(Calendar.MINUTE));
  885.       if(TwelveHourClock){
  886.     ampm.setValue(cal.get(Calendar.AM_PM));
  887.       }
  888.     }
  889.   }
  890.   
  891.   /*this function is designed to return the number of days in the given month
  892.    *from the given year. it is public because this is a useful function to 
  893.    *perform when dealing with dates. this almost certainly should have been
  894.    *written in GrergorianCalendar.
  895.    */
  896.   int getDaysInMonth(int m, int y){
  897.     int days = 30;
  898.     if (m ==0 || m ==2 || m ==4 || m==6 || m==7 || m==9 || m==11){
  899.       days=31;
  900.     }
  901.     else{ 
  902.       if (m ==1){
  903.     days =28;
  904.     if (y%400==0 || y%4==0 && y%100!=0){
  905.       days +=1;
  906.     }
  907.       }
  908.     }
  909.     return days;
  910.   }
  911.   
  912.   /*the DateChooser implements AdjustmentListener so that it can monitor its
  913.    *Spinner fields for important changes
  914.    */
  915.   public void adjustmentValueChanged(AdjustmentEvent e)
  916.   {
  917.  
  918.     /*if the month Spinner field wraps over, increment or decrement the year
  919.      *field appropriately
  920.      */
  921.     if(month.wrapped){
  922.       if(month.getValue() == 0){
  923.     month.wrapped = false;
  924.     year.setValue(year.getValue()+1); 
  925.       }
  926.       else{
  927.     month.wrapped = false;
  928.     year.setValue(year.getValue()-1);                
  929.       }
  930.     }
  931.     
  932.     /*if the day Spinner field wraps over, increment or decrement the month
  933.      *field appropriately
  934.      */
  935.     if(day.wrapped){
  936.       if(day.getValue() == 1){
  937.     day.wrapped = false;
  938.       month.setValue(month.getValue()+1);        
  939.     }
  940.       else{
  941.       day.wrapped = false;
  942.       month.setValue(month.getValue()-1);     
  943.       day.setValue(day.getMaximum());
  944.       }
  945.     }
  946.  
  947.     /*if the month Spinner or year Spinner has changed, it is possible that the
  948.      *value in the day Spinner field is no longer valid (too high). in this 
  949.      *event, the day is set to the maximum value for the current month in the 
  950.      *given year. also, if the minicalendar is visible, the month diplayed
  951.      *will change to the appropriate month in the appropriate year.
  952.      */
  953.     if((((Spinner)(e.getSource())) == month) || (((Spinner)(e.getSource())) == 
  954.                          year)){
  955.       int max = getDaysInMonth(month.getValue(),year.getValue()); 
  956.       if (day.getValue() > max ){
  957.     day.setValue(max);
  958.       }
  959.       day.setMaximum(max);
  960.       if(minical.dcv != null) {
  961.     if(minical.dcv.isVisible()) {
  962.       UI2cal();
  963.       minical.setCalendar();
  964.     }
  965.       }
  966.     }
  967.     
  968.     /*a close look at the add* functions above will reveal that the DateChooser
  969.      *is only added as an AdjustmentListener to the day,month,and year Spinners.
  970.      *thus this else implies that the day Spinner has changed. in the case that 
  971.      *the minicalendar is showing, this will highlight the appropriate date on 
  972.      *the minicalendar.
  973.      */
  974.     else{
  975.       if(minical.dcv != null) {
  976.     if(minical.dcv.isVisible()) {
  977.       UI2cal();
  978.       minical.dcv.highlightDate(day.getValue());
  979.     }
  980.       }
  981.     }
  982.   }
  983.  
  984.   /*the DateChooser implements ActionListener so that it can monitor its pop-up
  985.    *buttons.
  986.    */
  987.   public void actionPerformed(ActionEvent e) {
  988.  
  989.     /*if the "CAL" button is pushed, make sure the Calendar stoarge element is
  990.      *current, then show the minicalendar
  991.      */
  992.     if(e.getActionCommand().equals("CAL")){
  993.       UI2cal();
  994.       minical.showMeTheMoney();
  995.     }
  996.  
  997.     /*if the "CLOCK" button is pushed, make sure the Calendar stoarge element is
  998.      *current, then show the clockface
  999.      */
  1000.     else if(e.getActionCommand().equals("CLOCK")){    
  1001.       UI2cal();
  1002.       ClockPop.show(((JButton)(e.getSource())),0,0);    
  1003.     }
  1004.  
  1005.     /*this button lives on the ClockFace pop-up, but should be handled here 
  1006.      *because the DateChooser controls the JPopupMenu that contains the 
  1007.      *ClockFace.
  1008.      */
  1009.     else if(e.getActionCommand().equals("Done")){
  1010.       ClockPop.setVisible(false);
  1011.     }
  1012.   }
  1013.  
  1014.   /*the class minical is for now tied to the DateChooser. based on the Calendar
  1015.    *storage element, the MiniCal will instantiate a DateCanvas, which is the 
  1016.    *graphics of the MiniCal. the MiniCal also has a JLabel header which
  1017.    *displays the current month and year, as well as next and previous buttons 
  1018.    *to cycle through months. this class may prove useful as a separate class
  1019.    *altogether. in order to pull this apart, the mode argument in the 
  1020.    *constructor will need to dealt with in DateChooser itself, and the MiniCal
  1021.    *components should only be added to a JPanel, not a JPopupMenu. also the
  1022.    *cancel and ok buttons would have to be moved into the DateChooser itself.
  1023.    *MiniCal implements ActionListener so that it can monitor its buttons
  1024.    */
  1025.   public class MiniCal extends JComponent implements ActionListener{
  1026.     private JPanel miniPanel;//used only for a LARGE style DaetChooser
  1027.     private JButton cancel,ok;//buttons to accept or deny chosen date  
  1028.     private CalButton previous,next;//used to cycle through months
  1029.     private JLabel MnthYear;//display label for month and year 
  1030.     private DateCanvas.DOWHeader dow;//display for days of the week
  1031.     private DateCanvas dcv;//class that handles the graphics
  1032.     private FontMetrics fm;//for computing sizes of labels and graphics
  1033.     private JPopupMenu f;//used for all styles except LARGE
  1034.     private Font dateFont;//font for determining FontMetrics
  1035.     private int saveMonth;//these three are storage for the month, date, and
  1036.     private int saveDate;//year, in case of a "cancel"
  1037.     private int saveYear;
  1038.     private int saveHour; //daylight savings bug fix. Calendar.setTime().  
  1039.     private int myMiniCalMode;//add parts to a Popup or to a JPanel
  1040.  
  1041.     /*the constructor for the MiniCal is light because it is constructed in the
  1042.      *DateChooser regardless of whether or not it gets used.
  1043.      */
  1044.     public MiniCal(int mode) {
  1045.  
  1046.       /*store the mode. modes and meanings:
  1047.        *
  1048.        * 0: components of MiniCal will be added to a JPopupMenu (f), cancel and
  1049.        *    ok buttons will be used
  1050.        * 1: components of MiniCal will be added to a JPanel (miniPanel), cancel
  1051.        *    and ok buttons will not be used.
  1052.        *
  1053.        */
  1054.       myMiniCalMode = mode;
  1055.     }
  1056.  
  1057.     /*this is the real instantiation of the MiniCal, and occurs only when the 
  1058.      *the DateChooser requests to display the MiniCal
  1059.      */
  1060.     public void showMeTheMoney(){  
  1061.  
  1062.       /*if the mode is 0, store the date, month and year information in case of
  1063.        *a "cancel". construct the JPopupMenu.
  1064.        */
  1065.       if(myMiniCalMode == 0){
  1066.     saveMonth = cal.get(Calendar.MONTH);
  1067.     saveDate = cal.get(Calendar.DATE);
  1068.     saveYear = cal.get(Calendar.YEAR); 
  1069.     f = new JPopupMenu();
  1070.       }
  1071.  
  1072.       //else the mode is 1, construct the JPanel (miniPanel) and set its layout
  1073.       else{
  1074.     miniPanel = new JPanel(false);
  1075.     miniPanel.setLayout(new BorderLayout());
  1076.       }
  1077.  
  1078.       //construct the cycling buttons, add MiniCal as the ActionListener
  1079.       previous = new CalButton(this,"<",0);
  1080.       next = new CalButton(this,">",1);
  1081.       previous.addActionListener(this);
  1082.       next.addActionListener(this);
  1083.      
  1084.       /*the date for the current month must initially be set to 1 for purposes
  1085.        *of calculating how and where to draw the graphics.
  1086.        */ 
  1087.       cal.set(Calendar.DATE, 1);
  1088.  
  1089.       /*this is a quick hack around the problem that arises when crossing 
  1090.        *daylight savings time in Calendar and then calling setTime(). this is
  1091.        *something that should definitely be fixed in Calendar
  1092.        */
  1093.       saveHour = cal.get(Calendar.HOUR);
  1094.  
  1095.       /*recalculate the Calendar values with the DATE now set at 1. if the hour 
  1096.        *changes, set it back to what it was.
  1097.        */
  1098.       cal.setTime(cal.getTime());
  1099.       if(cal.get(Calendar.HOUR) != saveHour){
  1100.     cal.set(Calendar.HOUR,saveHour);
  1101.       }
  1102.       
  1103.       //set the month and year label, align it
  1104.       MnthYear= new JLabel((bigmonths[cal.get(Calendar.MONTH)]) + " " +
  1105.                ((new Integer(0)).toString(cal.get(Calendar.YEAR))));
  1106.       MnthYear.setHorizontalAlignment(MnthYear.CENTER);
  1107.        
  1108.       /*conctruct the DateCanvas. see DateCanvas for more details about its
  1109.        *constructor, and the args it takes. quickly, these args are: an offset
  1110.        *(what day of the week to start drawing days on), the number of days in
  1111.        *the month, the month, and the year.
  1112.        */
  1113.       dcv = new DateCanvas(cal.get(Calendar.DAY_OF_WEEK)-1,getDaysInMonth
  1114.                (cal.get(Calendar.MONTH), cal.get(Calendar.YEAR)));
  1115.    
  1116.       /*two JPanels, p1 and p2 are used to layout the MiniCal. there's probably
  1117.        *a better way to do this. p1 contains the month-year label and the 
  1118.        *cycling buttons. p2 contains the days of the week label, and the 
  1119.        *DateCanvas.
  1120.        */
  1121.       JPanel p1 = new JPanel(false);
  1122.       p1.setLayout(new BorderLayout());
  1123.       p1.add(previous, BorderLayout.WEST);
  1124.       p1.add(MnthYear, BorderLayout.NORTH);
  1125.       p1.add(next, BorderLayout.EAST);  
  1126.       JPanel p2 = new JPanel(false);
  1127.       p2.setLayout(new BorderLayout());
  1128.       p2.add(dow, BorderLayout.NORTH);
  1129.       p2.add(dcv, BorderLayout.CENTER);
  1130.    
  1131.       /*if the mode =0, add p1 and p2 to the JPopupMenu (f), as well as the 
  1132.        *cancel and ok buttons.
  1133.        */
  1134.       if(myMiniCalMode == 0){
  1135.     cancel = new JButton("Cancel");
  1136.     ok = new JButton("OK");
  1137.     cancel.addActionListener(this);
  1138.     ok.addActionListener(this);
  1139.     JPanel p3 = new JPanel(false);
  1140.     p3.setLayout(new BorderLayout());
  1141.     p3.add(cancel, BorderLayout.WEST);
  1142.     p3.add(ok, BorderLayout.EAST);
  1143.     f.setLayout(new BorderLayout());
  1144.     f.add(p1, BorderLayout.NORTH);
  1145.     f.add(p2, BorderLayout.CENTER);
  1146.     f.add(p3, BorderLayout.SOUTH);
  1147.     f.show(myCalendarButton,0,0);
  1148.       }
  1149.  
  1150.       /*else the mode is 1, p1 and p2 are added to the miniPanel. cancel and ok
  1151.        *are not used here.
  1152.        */
  1153.       else{      
  1154.     miniPanel.add(p1, BorderLayout.NORTH);
  1155.     miniPanel.add(p2, BorderLayout.CENTER);
  1156.       }
  1157.     }
  1158.     
  1159.     /*MiniCal implements ActionListener so that it can monitor its buttons:
  1160.      *cycling, cancal and ok.
  1161.      */
  1162.     public void actionPerformed(ActionEvent e) {
  1163.  
  1164.       //go to the previous month
  1165.       if(e.getActionCommand().equals("<")){
  1166.     goBack();      
  1167.       }
  1168.  
  1169.       //go to the next month
  1170.       else if(e.getActionCommand().equals(">")){
  1171.     goForward();
  1172.       }
  1173.       
  1174.       /*user has decided not to use values, reset Calendar values from storage,
  1175.        *reset the UI and hide the JPopupMenu
  1176.        */
  1177.       else if(e.getActionCommand().equals("Cancel")){
  1178.     cal.set(Calendar.MONTH,saveMonth);
  1179.     cal.set(Calendar.DATE,saveDate);
  1180.     cal.set(Calendar.YEAR,saveYear);      
  1181.     cal2UI();
  1182.     f.setVisible(false);
  1183.       }
  1184.       
  1185.       //values in Calendar are already set; set the UI, hide the JPopupMenu
  1186.       else if(e.getActionCommand().equals("OK")){
  1187.     cal2UI();
  1188.     f.setVisible(false);
  1189.       }
  1190.     }
  1191.     
  1192.     /*this function is designed to set the dateCanvas to the next month. it does
  1193.      *wrapping. the last line setCalendar() calls a function that actually
  1194.      *resets the Calendar values and repaints the Datecanvas
  1195.      */
  1196.     public void goForward(){
  1197.       if ((cal.get(Calendar.MONTH)+1) > 11 ){
  1198.     cal.set(Calendar.YEAR, cal.get(Calendar.YEAR)+1);
  1199.     cal.set(Calendar.MONTH, 0);
  1200.     day.setMaximum(31);      
  1201.       }
  1202.       else{
  1203.     cal.set(Calendar.MONTH, cal.get(Calendar.MONTH)+1);
  1204.     day.setMaximum(getDaysInMonth(cal.get(Calendar.MONTH),cal.get
  1205.                     (Calendar.YEAR)));      
  1206.       }
  1207.       setCalendar();
  1208.     }
  1209.  
  1210.     //same as goForward(), just in the other direction
  1211.     public void goBack(){
  1212.       if ((cal.get(Calendar.MONTH)-1) < 0 ){
  1213.     cal.set(Calendar.YEAR, cal.get(Calendar.YEAR)-1);
  1214.     cal.set(Calendar.MONTH, 11);
  1215.     day.setMaximum(31);
  1216.       }
  1217.       else{
  1218.     cal.set(Calendar.MONTH, cal.get(Calendar.MONTH)-1);
  1219.     day.setMaximum(getDaysInMonth(cal.get(Calendar.MONTH),cal.get
  1220.                     (Calendar.YEAR)));
  1221.       }
  1222.       setCalendar();
  1223.     }
  1224.  
  1225.     /*this function is designed to reset all the necesary aspects of the MiniCal
  1226.      *when a change occurs. it does most of the same setting and error trapping
  1227.      *as seen in showMeTheMoney()
  1228.      */
  1229.     public void setCalendar(){
  1230.       MnthYear.setText((bigmonths[cal.get(Calendar.MONTH)]) + " " +
  1231.                ((new Integer(0)).toString(cal.get(Calendar.YEAR))));    
  1232.       cal.set(Calendar.DATE, 1);
  1233.       saveHour = cal.get(Calendar.HOUR);
  1234.       cal.setTime(cal.getTime());
  1235.       if(cal.get(Calendar.HOUR) != saveHour){
  1236.     cal.set(Calendar.HOUR,saveHour);
  1237.       }      
  1238.       dcv.ChangeMonth(cal.get(Calendar.DAY_OF_WEEK)-1,getDaysInMonth
  1239.               (cal.get(Calendar.MONTH),cal.get(Calendar.YEAR)));
  1240.     }
  1241.  
  1242.     /*this subclass of the MiniCal handles the painting and sizing of the 
  1243.      *graphical calendar in MiniCal.
  1244.      */
  1245.     class DateCanvas extends JComponent {
  1246.       private int myOffset;//for which day of the week the first is on
  1247.       private int myTotal;//total days in the month
  1248.       private Graphics g2;//an extra copy of the Graphics, to alleviate too many
  1249.                           //calls to getGraphics()
  1250.       private boolean thisMonth;//whether this month has been clicked on yet
  1251.       private boolean mouseDown;//boolean for highlight dragging
  1252.       
  1253.       private int insetH;//horizontal inset, space before drawing
  1254.       private int insetV;//vertical inset+letterH, includes inset and letterH
  1255.       private int letterW;//width of a letter, gotten by fm
  1256.       private int letterH;//height of a letter, gotten by fm
  1257.       private int rectW;//width of painted canvas
  1258.       private int rectH;//height of painted canvas
  1259.       private int X,Y;//coordinates of highlighted date
  1260.       private int oldX, oldY;//coordinates of previously highlighted date
  1261.       private int StartX, StartY;//coordinated of most recent mouse event
  1262.       private int GRIDX, GRIDY;//basically indices to the array of dates
  1263.       private int date;//the current date highlighted
  1264.       private Dimension CanvasDimension;//dimension for DateCanvas
  1265.    
  1266.       /*constructor takes offset (what day of the week the first day of the 
  1267.        *month is on, and total, the number of days in the month
  1268.        */
  1269.       public DateCanvas(int offset,int total) {
  1270.     //set some initial values
  1271.     myOffset = offset;
  1272.     myTotal = total;
  1273.     thisMonth = false;
  1274.     mouseDown = false;
  1275.           
  1276.     dow = new DOWHeader();//instaniate a new days of the week header
  1277.  
  1278.     /*perhaps these should be switched over to MouseListener and 
  1279.      *MouseMotionListener. these are to listen for click and dragging on the
  1280.      *DateCanvas, for date highlighting and selection purposes.
  1281.      */
  1282.     enableEvents(AWTEvent.MOUSE_MOTION_EVENT_MASK |
  1283.              AWTEvent.MOUSE_EVENT_MASK);
  1284.       }
  1285.  
  1286.       /*some font tricks to make sure the font is actually set before the size
  1287.        *of the DateCanvas is determined. this is jag's code.
  1288.        */
  1289.       public void setFont(Font f) {
  1290.     if (f != getFont()){
  1291.       super.setFont(f);
  1292.       CanvasDimension = null;
  1293.       invalidate();
  1294.     }
  1295.       }
  1296.  
  1297.       /*calculates the size of the DateCanvas based on FontMetrics. all 
  1298.        *calculations are pretty straightforward.
  1299.        */
  1300.       public Dimension getMinimumSize(){
  1301.     if (CanvasDimension == null){
  1302.       fm = getFontMetrics(getFont());
  1303.       letterW = fm.charWidth('8');
  1304.       letterH = fm.getHeight();
  1305.       insetH = letterW / 2;
  1306.       insetV = letterH/2 + letterH;
  1307.       rectW = 2*insetH + 3*daysInWeek*letterW;
  1308.       rectH = 2*(insetV-letterH) + ((int)(1.5*letterH*6));
  1309.       CanvasDimension = new Dimension(rectW + 2, rectH + 2);
  1310.     }
  1311.     return CanvasDimension;
  1312.       }
  1313.  
  1314.       //paint this bad boy!
  1315.       public void paint(Graphics g){     
  1316.     
  1317.     //make sure the size has been determined before painting
  1318.     if (CanvasDimension == null){
  1319.       getMinimumSize();
  1320.     }
  1321.     
  1322.     //white background
  1323.     g.setColor(Color.white);
  1324.     g.fillRect(1,1, rectW-2, rectH-2);
  1325.  
  1326.     //black border
  1327.     g.setColor(Color.black);
  1328.     g.drawRect(0,0, rectW, rectH);
  1329.      
  1330.     /*this is to calculate the date squares that show up on the DateCanvas
  1331.      *but are disables because they come from the previous month
  1332.      */
  1333.     int i,row,x,disabled;
  1334.     x=0;
  1335.     row = 0;
  1336.     //get previous month
  1337.     if(cal.get(Calendar.MONTH) == 0){
  1338.       disabled = getDaysInMonth(11,cal.get(Calendar.YEAR));
  1339.     }
  1340.     else{
  1341.       disabled = getDaysInMonth(cal.get(Calendar.MONTH) -1, 
  1342.                     cal.get(Calendar.YEAR));
  1343.     }
  1344.     
  1345.     /*draw date strings from current month in appropriate places. note that
  1346.      *each square in letterW*3 wide and letterH*1.5 high.
  1347.      */
  1348.     for(i = 1; i<=myTotal; i++){
  1349.       x = (i+myOffset)-(daysInWeek*row) - 1;
  1350.       if (x > 6){
  1351.         row++;
  1352.         x=0;
  1353.       }
  1354.       g.drawString(Integer.toString(i),insetH + 3*letterW*x+2, insetV + 
  1355.                ((int)(1.5*letterH))*row);
  1356.     }
  1357.  
  1358.     /*give the disabled look to all squares except those which the current
  1359.      *month's dates are on.this is done by clearing out the white background
  1360.      */
  1361.     g.clearRect(insetH,insetV-letterH, myOffset*letterW*3,
  1362.             ((int)(letterH*1.5)));
  1363.     g.clearRect(insetH+ 3*letterW*(x+1), insetV - letterH + 
  1364.             ((int)(1.5*letterH))*row, 3*letterW*(daysInWeek-x-1), 
  1365.             ((int)(1.5*letterH*(6-row))));
  1366.     g.clearRect(insetH, insetV-letterH+((int)(1.5*letterH))*(row+1),
  1367.             3*letterW*(x+1), ((int)(1.5*letterH))*(6-row-1));
  1368.  
  1369.     //these two loops graw the grid lines on the DateCanvas
  1370.     for(i=0; i<=daysInWeek; i++){
  1371.       g.drawLine(insetH+letterW*3*i, insetV-letterH, insetH+letterW*3*i, 
  1372.              insetV-letterH+9*letterH); 
  1373.     }
  1374.     for(i=0; i<7; i++){
  1375.       g.drawLine(insetH, insetV-letterH+((int)(letterH*1.5*i)), 
  1376.              insetH+21*letterW,insetV-letterH+((int)(letterH*1.5*i)));
  1377.     }
  1378.     
  1379.     //draw strings for previous month's dates (disabled)
  1380.     for(i=disabled-myOffset+1; i<=disabled; i++){
  1381.       g.drawString(Integer.toString(i),insetH + 
  1382.                3*letterW*(i-disabled+myOffset-1)+2, insetV);
  1383.     }
  1384.  
  1385.     //reset disabled int to dates in next month
  1386.     disabled = daysInWeek*6 - (row*daysInWeek + x);
  1387.  
  1388.     //draw strings for next month's dates (disabled)
  1389.     for(i=1; i<disabled; i++){
  1390.       x++;
  1391.       if (x > 6){
  1392.         row++;
  1393.         x=0;
  1394.       }
  1395.       g.drawString(Integer.toString(i),insetH + 3*letterW*x+2, insetV + 
  1396.                ((int)(1.5*letterH))*row);
  1397.     }
  1398.  
  1399.     /*make a copy of DateCanvas Graphics, to alliviate multiple calls to 
  1400.      *getGraphics()
  1401.      */
  1402.      if(g2 == null){
  1403.       g2 = g.create(); 
  1404.     }
  1405.  
  1406.     //highlight current date
  1407.     highlightDate(day.getValue());
  1408.       }
  1409.       
  1410.       //minimumSize is the preferred size
  1411.       public Dimension getPreferredSize() {
  1412.     return getMinimumSize();
  1413.       }
  1414.     
  1415.       /*a helper function used when the month is changed, to avoid writing these
  1416.        *statements everwhere that these values need to be set.
  1417.        */
  1418.       public void ChangeMonth(int offset, int total){
  1419.     myOffset = offset;
  1420.     myTotal = total;
  1421.     paint(g2);
  1422.       }
  1423.  
  1424.       //neccessary in case the DateCanvas becomes obcured by another window
  1425.       public void update(Graphics g){
  1426.     paint(g);
  1427.       }
  1428.  
  1429.       /*this function is designed to calculate the date on the DateCanvas given
  1430.        *cooordinates x and y. the steps of the calculation are commented below
  1431.        *to facillitate understanding.
  1432.        */
  1433.       public int getDateOnComponent(int x, int y){
  1434.     //GRIDX = (int)((x - insetH)/(letterW*3));
  1435.     //GRIDY = (int)((y - insetV + letterH)/(1.5*letterH));
  1436.     //DATE = (GRIDX - myOffset) + 1 + (daysInWeek*GRIDY);
  1437.     return  (((int)((x - insetH)/(letterW*3))) - myOffset) + 1 +
  1438.              (daysInWeek*((int)((y - insetV + letterH)/(1.5*letterH))));
  1439.       }
  1440.  
  1441.       /*this function, used mostly in conjuction with the DateChooser in 
  1442.        *response to an Adjustment change in hte day Spinner, is designed to
  1443.        *highlight the passed date. calculations translating the date into
  1444.        *coordinates on the DateCanvas are stepped throug in comments below.
  1445.        */
  1446.       public void highlightDate(int newDate){
  1447.     //(date+Offset-1)%daysInWeek = GRIDX (see above)
  1448.     //(date+Offset-1)/daysInWeek-1 = GRIDY (see above)
  1449.     highlightDate(((newDate+myOffset-1)%7)*(letterW*3)+insetH+2,
  1450.               ((newDate+myOffset-1)/7-1)*((int)(1.5*letterH))+
  1451.               insetV+letterH);
  1452.       }
  1453.  
  1454.       //draw a highlight for the date at the given coordinates
  1455.       public void highlightDate(int tempX, int tempY){
  1456.     
  1457.     //get the date, and check that it is in bounds
  1458.     date = getDateOnComponent(tempX, tempY);
  1459.     if ((date > 0) && (date <= getDaysInMonth((cal).get(Calendar.MONTH),
  1460.         (cal).get(Calendar.YEAR))) && (tempX < rectW - insetH - 1) && 
  1461.         (tempX > insetH+1)){      
  1462.       cal.set(Calendar.DATE,date);
  1463.       if(thisMonth){
  1464.         oldX = X;
  1465.         oldY = Y;
  1466.         
  1467.         GRIDX = (int)((oldX - insetH)/(letterW*3));
  1468.         GRIDY = (int)((oldY - insetV + letterH)/(1.5*letterH));
  1469.         StartX = GRIDX*letterW*3+insetH;
  1470.         StartY = (int)(GRIDY*1.5*letterH) + insetV - letterH;
  1471.         g2.setColor(Color.white);
  1472.         g2.fillRect(StartX+1, StartY+1, letterW*3-1, 
  1473.             (int)(letterH*1.5)-1);
  1474.             
  1475.         g2.setColor(Color.black);
  1476.         g2.drawString((new Integer(0)).toString
  1477.               (getDateOnComponent(oldX,oldY)),insetH + 
  1478.               3*letterW*GRIDX+2,insetV + 
  1479.               ((int)(1.5*letterH))*GRIDY);
  1480.       }
  1481.       else{
  1482.         thisMonth = true;        
  1483.       }
  1484.       X = tempX;
  1485.       Y = tempY;
  1486.           
  1487.       GRIDX = (int)((X - insetH)/(letterW*3));
  1488.       GRIDY = (int)((Y - insetV + letterH)/(1.5*letterH));
  1489.       StartX = GRIDX*letterW*3+insetH;
  1490.       StartY = (int)(GRIDY*1.5*letterH) + insetV - letterH;
  1491.       
  1492.       g2.setColor(new Color(0,0,150));
  1493.       g2.fillRect(StartX + 1, StartY + 1, letterW*3 - 1, 
  1494.               (int)(letterH*1.5) - 1);
  1495.       g2.setColor(Color.white);
  1496.       g2.drawString((new Integer(0)).toString(date),insetH + 
  1497.             3*letterW*GRIDX+2,insetV + 
  1498.             ((int)(1.5*letterH))*GRIDY+1);
  1499.       g2.setColor(Color.black);
  1500.     }
  1501.       }
  1502.       
  1503.       protected void processMouseEvent(MouseEvent e){
  1504.     if (e.getID()==e.MOUSE_PRESSED) {
  1505.         
  1506.       mouseDown = true;
  1507.       int tempX = e.getX();
  1508.       int tempY = e.getY();
  1509.       highlightDate(tempX,tempY);
  1510.     }
  1511.     else if (e.getID()==e.MOUSE_RELEASED) {
  1512.       mouseDown = false;
  1513.       if ((date > 0) && (date <= getDaysInMonth((cal).get(Calendar.MONTH),
  1514.                             (cal).get(Calendar.YEAR)))){
  1515.         cal.set(Calendar.DATE,date);
  1516.       }
  1517.       if(myMiniCalMode == 1){
  1518.         cal2UI();
  1519.       }
  1520.     }
  1521.       }
  1522.       protected void processMouseMotionEvent(MouseEvent e){ 
  1523.     if (mouseDown) {
  1524.       int tempX = e.getX();
  1525.       int tempY = e.getY();
  1526.       if ((GRIDX != (int)((tempX - insetH)/(letterW*3)) || 
  1527.            (GRIDY != (int)((tempY - insetV + letterH)/(1.5*letterH))))){
  1528.         highlightDate(tempX,tempY);
  1529.       }
  1530.     }
  1531.       }  
  1532.       class DOWHeader extends JComponent{
  1533.     private String[] DOW;
  1534.     private Dimension DOWDimension;
  1535.       
  1536.     public DOWHeader(){
  1537.       char[] three = new char[3];
  1538.       char[] two = new char[2];
  1539.       DOW = new String[daysInWeek + 1];
  1540.       DOW = dfd.getShortWeekdays();
  1541.       int i;
  1542.       for (i=1; i<daysInWeek + 1; i++){
  1543.         three = (DOW[i]).toCharArray();
  1544.         two[0] = three[0];
  1545.         if((two.length > 1) && (three.length > 1)){
  1546.           two[1] = three[1];
  1547.         }
  1548.         DOW[i] = String.copyValueOf(two);
  1549.       }
  1550.     }
  1551.  
  1552.     public void setFont(Font f) {
  1553.       if (f != getFont()){
  1554.         super.setFont(f);
  1555.         DOWDimension = null;
  1556.         invalidate();
  1557.       }
  1558.     }
  1559.     
  1560.     public Dimension getMinimumSize(){
  1561.       if (DOWDimension == null){          
  1562.         fm = getFontMetrics(getFont());
  1563.         letterW = fm.charWidth('8');
  1564.         letterH = fm.getHeight();
  1565.         insetH = letterW / 2;
  1566.         insetV = letterH/2 + letterH;
  1567.         rectW = 2*insetH + 3*daysInWeek*letterW;
  1568.         rectH = 2*(insetV-letterH) + ((int)(1.5*letterH*6));
  1569.         DOWDimension = new Dimension(insetH + letterW*3*daysInWeek, insetV);
  1570.       }
  1571.       return DOWDimension;
  1572.     }
  1573.  
  1574.     public void paint(Graphics g){
  1575.       if (DOWDimension == null){
  1576.         getMinimumSize();
  1577.       }
  1578.       g.setColor(Color.black);
  1579.       int i;
  1580.       for(i = 0; i<daysInWeek; i++){
  1581.         g.drawString(DOW[i+1],insetH + letterW*3*i,insetV);
  1582.       }    
  1583.     }
  1584.  
  1585.     public Dimension getPreferredSize() {
  1586.       return getMinimumSize();
  1587.     }
  1588.       }
  1589.     }
  1590.   }
  1591.   class ClockFace extends JComponent implements AdjustmentListener {
  1592.     Dimension d = new Dimension();
  1593.     private Image img;
  1594.     int x0, y0;
  1595.     public ClockFace(){
  1596.       
  1597.       hours.addAdjustmentListener(this);
  1598.       minutes.addAdjustmentListener(this);
  1599.       enableEvents(AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
  1600.       final byte[] imageBytes = {
  1601.     (byte)0x47, (byte)0x49, (byte)0x46, (byte)0x38, (byte)0x39, (byte)0x61,
  1602.     (byte)0x50, (byte)0x0, (byte)0x50, (byte)0x0, (byte)0xb3, (byte)0x0,
  1603.     (byte)0x0, (byte)0x0, (byte)0x0, (byte)0x0, (byte)0x80, (byte)0x0,
  1604.     (byte)0x0, (byte)0x0, (byte)0x80, (byte)0x0, (byte)0x80, (byte)0x80,
  1605.     (byte)0x0, (byte)0x0, (byte)0x0, (byte)0x80, (byte)0x80, (byte)0x0,
  1606.     (byte)0x80, (byte)0x0, (byte)0x80, (byte)0x80, (byte)0x80, (byte)0x80,
  1607.     (byte)0x80, (byte)0xc0, (byte)0xc0, (byte)0xc0, (byte)0xff, (byte)0x0,
  1608.     (byte)0x0, (byte)0x0, (byte)0xff, (byte)0x0, (byte)0xff, (byte)0xff,
  1609.     (byte)0x0, (byte)0x0, (byte)0x0, (byte)0xff, (byte)0xff, (byte)0x0,
  1610.     (byte)0xff, (byte)0x0, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff,
  1611.     (byte)0xff, (byte)0x21, (byte)0xf9, (byte)0x4, (byte)0x9, (byte)0x8,
  1612.     (byte)0x0, (byte)0xf, (byte)0x0, (byte)0x2c, (byte)0x0, (byte)0x0,
  1613.     (byte)0x0, (byte)0x0, (byte)0x50, (byte)0x0, (byte)0x50, (byte)0x0,
  1614.     (byte)0x40, (byte)0x4, (byte)0xfe, (byte)0xf0, (byte)0xc9, (byte)0x49,
  1615.     (byte)0xab, (byte)0xbd, (byte)0x58, (byte)0xa6, (byte)0x9d, (byte)0xb2,
  1616.     (byte)0xff, (byte)0x60, (byte)0x38, (byte)0x1, (byte)0x24, (byte)0x20,
  1617.     (byte)0x72, (byte)0x5d, (byte)0x58, (byte)0x9a, (byte)0x62, (byte)0x2b,
  1618.     (byte)0xb1, (byte)0x6e, (byte)0x2c, (byte)0x3f, (byte)0xb0, (byte)0xcb,
  1619.     (byte)0xae, (byte)0xe4, (byte)0xac, (byte)0x67, (byte)0xf8, (byte)0xfd,
  1620.     (byte)0xd5, (byte)0xbb, (byte)0xa0, (byte)0xb0, (byte)0x86, (byte)0x2a,
  1621.     (byte)0x6e, (byte)0x82, (byte)0x8c, (byte)0xa4, (byte)0x72, (byte)0xb9,
  1622.     (byte)0xc, (byte)0x1a, (byte)0x8b, (byte)0x14, (byte)0xa0, (byte)0x70,
  1623.     (byte)0x2a, (byte)0x93, (byte)0x56, (byte)0x7a, (byte)0x39, (byte)0xea,
  1624.     (byte)0x14, (byte)0x6b, (byte)0x55, (byte)0x69, (byte)0xb5, (byte)0x5d,
  1625.     (byte)0x70, (byte)0x29, (byte)0x84, (byte)0x52, (byte)0x8d, (byte)0xbf,
  1626.     (byte)0xdb, (byte)0x95, (byte)0x86, (byte)0xf3, (byte)0x52, (byte)0xa3,
  1627.     (byte)0xab, (byte)0x35, (byte)0xb7, (byte)0x38, (byte)0xdb, (byte)0x4e,
  1628.     (byte)0xbf, (byte)0xef, (byte)0xb4, (byte)0x70, (byte)0x14, (byte)0xcf,
  1629.     (byte)0xbf, (byte)0x62, (byte)0xf4, (byte)0x3a, (byte)0x4c, (byte)0x82,
  1630.     (byte)0xc, (byte)0x77, (byte)0x56, (byte)0x80, (byte)0x33, (byte)0x83,
  1631.     (byte)0x4a, (byte)0x85, (byte)0x3c, (byte)0x7d, (byte)0x8d, (byte)0x34,
  1632.     (byte)0x2d, (byte)0x87, (byte)0x8e, (byte)0x3f, (byte)0x68, (byte)0x30,
  1633.     (byte)0x72, (byte)0x54, (byte)0x72, (byte)0x91, (byte)0x92, (byte)0x16,
  1634.     (byte)0x0, (byte)0x28, (byte)0x74, (byte)0x9a, (byte)0x8d, (byte)0x65,
  1635.     (byte)0x9f, (byte)0x76, (byte)0x1e, (byte)0xa1, (byte)0x3c, (byte)0x99,
  1636.     (byte)0x8b, (byte)0x96, (byte)0x19, (byte)0xa5, (byte)0x1e, (byte)0x6a,
  1637.     (byte)0xa7, (byte)0x55, (byte)0x7f, (byte)0x67, (byte)0x69, (byte)0x9e,
  1638.     (byte)0x7b, (byte)0x5b, (byte)0xa2, (byte)0x31, (byte)0xae, (byte)0x79,
  1639.     (byte)0x38, (byte)0xb6, (byte)0x22, (byte)0x58, (byte)0x20, (byte)0x86,
  1640.     (byte)0xbb, (byte)0x8c, (byte)0x9b, (byte)0x93, (byte)0xc0, (byte)0x97,
  1641.     (byte)0xc1, (byte)0x6f, (byte)0x89, (byte)0x78, (byte)0x44, (byte)0x4f,
  1642.     (byte)0x6c, (byte)0x3b, (byte)0x89, (byte)0x82, (byte)0x4e, (byte)0xcb,
  1643.     (byte)0x47, (byte)0x2f, (byte)0xc7, (byte)0xce, (byte)0x4d, (byte)0x94,
  1644.     (byte)0x17, (byte)0xb8, (byte)0x2d, (byte)0xcf, (byte)0xc9, (byte)0xc6,
  1645.     (byte)0xc4, (byte)0x41, (byte)0x80, (byte)0x71, (byte)0xb1, (byte)0xde,
  1646.     (byte)0x23, (byte)0xe2, (byte)0x91, (byte)0xa9, (byte)0xe3, (byte)0xb0,
  1647.     (byte)0xb3, (byte)0xbc, (byte)0xe8, (byte)0x7f, (byte)0x5f, (byte)0x86,
  1648.     (byte)0xe2, (byte)0xdf, (byte)0xf0, (byte)0xd3, (byte)0x8d, (byte)0xba,
  1649.     (byte)0x20, (byte)0xab, (byte)0xa6, (byte)0xf2, (byte)0xbb, (byte)0xe7,
  1650.     (byte)0xf, (byte)0xf7, (byte)0x6d, (byte)0xea, (byte)0xec, (byte)0xf6,
  1651.     (byte)0x98, (byte)0x1, (byte)0xd4, (byte)0xd2, (byte)0x6f, (byte)0xe0,
  1652.     (byte)0xc, (byte)0x12, (byte)0x9d, (byte)0xb2, (byte)0x8d, (byte)0xdb,
  1653.     (byte)0x57, (byte)0x70, (byte)0x9f, (byte)0xbe, (byte)0x7c, (byte)0x16,
  1654.     (byte)0xa, (byte)0x6e, (byte)0x82, (byte)0xd8, (byte)0xc7, (byte)0x61,
  1655.     (byte)0x44, (byte)0x81, (byte)0x3f, (byte)0x28, (byte)0xbe, (byte)0xa9,
  1656.     (byte)0xf4, (byte)0x6f, (byte)0x8, (byte)0x1d, (byte)0x85, (byte)0xa2,
  1657.     (byte)0xeb, (byte)0x26, (byte)0x82, (byte)0x4c, (byte)0x87, (byte)0xed,
  1658.     (byte)0x5a, (byte)0x37, (byte)0x30, (byte)0xac, (byte)0x8a, (byte)0x19,
  1659.     (byte)0x1c, (byte)0xf1, (byte)0x6d, (byte)0x65, (byte)0x49, (byte)0x1b,
  1660.     (byte)0x1a, (byte)0x1, (byte)0x5a, (byte)0xbc, (byte)0xd2, (byte)0x6b,
  1661.     (byte)0x20, (byte)0x17, (byte)0x2f, (byte)0x51, (byte)0x3a, (byte)0x2,
  1662.         (byte)0x83, (byte)0x7, (byte)0xce, (byte)0xa5, (byte)0x8d, (byte)0x94,
  1663.     (byte)0x3e, (byte)0x43, (byte)0xbe, (byte)0xc, (byte)0x2a, (byte)0xd4,
  1664.     (byte)0xf, (byte)0x9e, (byte)0x6d, (byte)0x8b, (byte)0x86, (byte)0x7e,
  1665.     (byte)0xa9, (byte)0x96, (byte)0xd4, (byte)0xe8, (byte)0xd2, (byte)0x6a,
  1666.     (byte)0x8a, (byte)0x4c, (byte)0xf2, (byte)0x8b, (byte)0x96, (byte)0x22,
  1667.     (byte)0x10, (byte)0xd4, (byte)0x24, (byte)0xd0, (byte)0xa8, (byte)0x52,
  1668.     (byte)0x45, (byte)0x72, (byte)0x95, (byte)0xd0, (byte)0xe, (byte)0xad,
  1669.     (byte)0x2c, (byte)0xa9, (byte)0x31, (byte)0xdd, (byte)0xa8, (byte)0xf4,
  1670.     (byte)0x29, (byte)0x52, (byte)0xa9, (byte)0x4e, (byte)0x89, (byte)0xfa,
  1671.     (byte)0x3a, (byte)0xa9, (byte)0xb6, (byte)0x6c, (byte)0xda, (byte)0xb6,
  1672.     (byte)0x6e, (byte)0xdf, (byte)0xe6, (byte)0x31, (byte)0xa8, (byte)0xce,
  1673.     (byte)0x5c, (byte)0xae, (byte)0x99, (byte)0x9f, (byte)0x7a, (byte)0x99,
  1674.     (byte)0xeb, (byte)0x11, (byte)0xb4, (byte)0xa6, (byte)0xaf, (byte)0x91,
  1675.     (byte)0xa2, (byte)0x74, (byte)0x2, (byte)0xed, (byte)0x3b, (byte)0x84,
  1676.     (byte)0xb0, (byte)0x4a, (byte)0x91, (byte)0x80, (byte)0x73, (byte)0x76,
  1677.     (byte)0x4c, (byte)0x8c, (byte)0x2d, (byte)0x9c, (byte)0x60, (byte)0x38,
  1678.     (byte)0x9e, (byte)0x1e, (byte)0xd7, (byte)0xc2, (byte)0xbb, (byte)0x46,
  1679.     (byte)0x5a, (byte)0xc6, (byte)0x1c, (byte)0x8c, (byte)0x21, (byte)0xc5,
  1680.     (byte)0x9c, (byte)0x20, (byte)0x11, (byte)0xf1, (byte)0xce, (byte)0x3a,
  1681.     (byte)0xa4, (byte)0x30, (byte)0x36, (byte)0x9e, (byte)0xb7, (byte)0x92,
  1682.     (byte)0x6f, (byte)0xa8, (byte)0x7a, (byte)0x70, (byte)0x55, (byte)0x89,
  1683.     (byte)0x4e, (byte)0x1d, (byte)0xd0, (byte)0xb2, (byte)0xa3, (byte)0x8,
  1684.     (byte)0x0, (byte)0x3b,     
  1685.       };
  1686.       img = Toolkit.getDefaultToolkit().createImage(imageBytes);
  1687.        if (img != null){    
  1688.      d.width = img.getWidth(this);
  1689.      d.height = img.getHeight(this);
  1690.        } 
  1691.     }
  1692.     public void adjustmentValueChanged(AdjustmentEvent e) {
  1693.       if (isShowing())
  1694.     repaint(50);
  1695.     }
  1696.     protected void processMouseMotionEvent(MouseEvent e) {
  1697.       processMouseEvent(e);
  1698.     }
  1699.     protected void processMouseEvent(MouseEvent e) {
  1700.       switch (e.getID()) {
  1701.       case e.MOUSE_PRESSED:
  1702.       case e.MOUSE_DRAGGED:
  1703.       case e.MOUSE_RELEASED:
  1704.     float theta = (float) Math.atan2(e.getX() - x0, e.getY() - y0);
  1705.     int v = 24 - (int) (theta * (12 * 4) / (2 * Math.PI) + 0.5);
  1706.     if (v < 0)
  1707.       v += 48;
  1708.     int nh = v >> 2;
  1709.     int oh = hours.getValue();
  1710.     if (oh >= 12)
  1711.       oh -= 12;
  1712.     if (nh > 9 && oh < 3 || nh < 3 && oh > 9)
  1713.       ampm.setValue(1 - ampm.getValue());
  1714.     hours.setValue(nh);
  1715.     minutes.setValue((v & 3) * 15);
  1716.     break;
  1717.       }
  1718.       return;
  1719.     }
  1720.     public void setBounds(int x, int y,
  1721.               int width, int height) {
  1722.       super.setBounds(x, y, width, height);
  1723.       x0 = width >> 1;
  1724.       y0 = height >> 1;
  1725.     }
  1726.     public Dimension getMinimumSize() {
  1727.       waitSize();
  1728.       return d;
  1729.     }
  1730.     public Dimension getPreferredSize() {
  1731.       return getMinimumSize();
  1732.     }
  1733.     private synchronized void waitSize() {
  1734.       if (img != null)
  1735.     try {
  1736.       while (d.width < 0 || d.height < 0)
  1737.         wait();
  1738.     } catch(InterruptedException i) {
  1739.     }
  1740.     }
  1741.     public synchronized boolean imageUpdate(Image img,
  1742.                         int infoflags,
  1743.                         int x, int y,
  1744.                         int width, int height) {
  1745.       if ((infoflags & (ERROR | ABORT)) != 0) {
  1746.     img = null;
  1747.     d.width = 100;
  1748.     d.height = 100;
  1749.     notifyAll();
  1750.     return false;
  1751.       }
  1752.       repaint(10);
  1753.       if ((infoflags & WIDTH) != 0)
  1754.     d.width = img.getWidth(this);
  1755.       if ((infoflags & HEIGHT) != 0)
  1756.     d.height = img.getHeight(this);
  1757.       notifyAll();
  1758.       return (infoflags & ALLBITS) == 0;
  1759.     }
  1760.     private void drawHand(Graphics g, int v,
  1761.               float scale, boolean broad) {
  1762.       float theta = (float) (v * (2 * Math.PI / 120));
  1763.       scale *= x0 < y0 ? x0 : y0;
  1764.       int st = (int) (Math.sin(theta) * scale);
  1765.       int ct = (int) (Math.cos(theta) * scale);
  1766.       if (!broad)
  1767.     g.drawLine(x0, y0, x0 + st, y0 - ct);
  1768.       else {
  1769.     int st2 = st >> 4;
  1770.     int ct2 = ct >> 4;
  1771.     g.drawLine(x0 + ct2, y0 + st2, x0 + st, y0 - ct);
  1772.     g.drawLine(x0 - ct2, y0 - st2, x0 + st, y0 - ct);
  1773.       }
  1774.     }
  1775.     int paintCnt = 0;
  1776.     public void paint(Graphics g) {
  1777.       g.setColor(Color.black);
  1778.       int m = minutes.getValue();
  1779.       if (img != null)
  1780.     {
  1781.       g.drawImage(img, x0 - (d.width >> 1),
  1782.             y0 - (d.height >> 1), this);
  1783.      
  1784.     }
  1785.       else {
  1786.     g.setColor(Color.red);
  1787.     int R = (x0 < y0 ? x0 : y0) * 8 / 10;
  1788.     g.drawArc(x0 - R, y0 - R, R * 2, R * 2, 0, 360);
  1789.     g.setColor(Color.black);
  1790.     R -= 5;
  1791.     g.drawArc(x0 - R, y0 - R, R * 2, R * 2, 90,-hours.getValue() * 30);
  1792.       }
  1793.       drawHand(g, hours.getValue() * 10 + m / 6, 0.8f, true);
  1794.       drawHand(g, m * 2, 1.0f, false);
  1795.     }
  1796.     public void update(Graphics g) {
  1797.       
  1798.       paint(g);
  1799.     }
  1800.   }
  1801. }
  1802.  
  1803. class CalButton extends JButton{
  1804.   public boolean mouseUp;
  1805.  
  1806.   private DateChooser.MiniCal myMiniCal;
  1807.   private int myDirection;
  1808.   private static ChangeCalRepeater scrollThread;
  1809.  
  1810.   public CalButton(DateChooser.MiniCal minical,String text,int direction) {
  1811.     super(text);
  1812.     myMiniCal = minical;
  1813.     myDirection = direction;
  1814.     enableEvents(AWTEvent.MOUSE_MOTION_EVENT_MASK |AWTEvent.MOUSE_EVENT_MASK);
  1815.   }
  1816.   protected void processMouseEvent(MouseEvent e){
  1817.     if (e.getID()==e.MOUSE_PRESSED){
  1818.       doSomething();
  1819.       mouseUp = false;
  1820.       if (scrollThread == null) {
  1821.     // If there isn't a scrollThread, then create
  1822.     // one and start it.
  1823.     scrollThread = new ChangeCalRepeater(this);
  1824.     scrollThread.start();
  1825.       } 
  1826.       else{
  1827.     scrollThread = null;
  1828.     scrollThread = new ChangeCalRepeater(this);
  1829.     scrollThread.start();
  1830.       }
  1831.     
  1832.     }
  1833.     else if (e.getID()==e.MOUSE_RELEASED){
  1834.       mouseUp = true;
  1835.     }
  1836.   }
  1837.   public void doSomething(){
  1838.     if (myDirection == 0){
  1839.       myMiniCal.goBack();
  1840.     }
  1841.     else {
  1842.       myMiniCal.goForward();
  1843.     }
  1844.   } 
  1845. }
  1846.  
  1847. class ChangeCalRepeater extends Thread {
  1848.   
  1849.   /**
  1850.    * Time to pause before the first scroll repeat.
  1851.    */
  1852.   static int beginPause = 650; // Reminder - make this a user definable property
  1853.   
  1854.   /**
  1855.    * Time to pause between each scroll repeat.
  1856.    */
  1857.   static int repeatPause = 100; // Reminder - make this a user definable property
  1858.   
  1859.   private boolean newChange;
  1860.   private CalButton myCalButton;
  1861.   
  1862.   ChangeCalRepeater(CalButton calbutton) {
  1863.     super("ChangeCalRepeater thread");
  1864.     newChange = true;
  1865.     myCalButton = calbutton;
  1866.   }
  1867.   
  1868.   
  1869.   /**
  1870.    * Called by Thread.start(). Starts the scroll repeater thread.
  1871.    */
  1872.   public void run (){
  1873.     //boolean shouldScroll;    // local variable for thread safety
  1874.     
  1875.     while (true){
  1876.       if (newChange) {
  1877.     try {
  1878.       // Pause before repeating. This gives the user time to release
  1879.       // the mouse button before continuous scrolling starts.
  1880.       sleep(beginPause); 
  1881.     }
  1882.     catch (java.lang.InterruptedException e) {}
  1883.     newChange = false;
  1884.       }
  1885.       if(!(myCalButton.mouseUp)){
  1886.     myCalButton.doSomething();
  1887.       }
  1888.       else{
  1889.     stop();
  1890.       }
  1891.       try {
  1892.     sleep(repeatPause); 
  1893.       } 
  1894.       catch (InterruptedException e) {}
  1895.     }
  1896.   }
  1897. }
  1898.  
  1899. class SyncTypeComboBoxEditor implements ComboBoxEditor{
  1900.   private JComboBox myJComboBox;
  1901.   private Spinner editor;
  1902.  
  1903.   public SyncTypeComboBoxEditor(JComboBox jcombobox){
  1904.     myJComboBox = jcombobox;
  1905.   }
  1906.  
  1907.   public Component getEditorComponent(){
  1908.     return editor;
  1909.   }
  1910.  
  1911.   public void setEditorComponent(Spinner newEditor){
  1912.     editor = newEditor;
  1913.   }
  1914.   
  1915.   public void setValue(Object anObject){
  1916.     try {
  1917.       //editor.setValue(myJComboBox.getCurrentValueIndex()+editor.getMinimum());
  1918.     }
  1919.     catch(IllegalComponentStateException e) {}
  1920.   }
  1921.  
  1922.   public Object getValue(){
  1923.     return editor.getText();
  1924.   }
  1925.  
  1926.   public void selectAll(){}
  1927.  
  1928.   public void addActionListener(ActionListener l){}
  1929.  
  1930.   public void removeActionListener(ActionListener l){}
  1931. }
  1932.  
  1933. class SpinnerLinker implements KeyListener{
  1934.   private Spinner sp1, sp2, sp3;
  1935.   public SpinnerLinker(Spinner spinner1, Spinner spinner2, Spinner spinner3){
  1936.     sp1 = spinner1;
  1937.     sp2 = spinner2;
  1938.     sp3 = spinner3;
  1939.   }
  1940.   public void keyTyped(KeyEvent e){}
  1941.   public void keyPressed(KeyEvent e){
  1942.     if (e.isActionKey())
  1943.       switch(e.getKeyCode()) {
  1944.       case e.VK_LEFT:    
  1945.     if(sp1.hasFocus()){
  1946.       if (sp3 != null){sp3.requestFocus();}
  1947.       else {sp2.requestFocus();}
  1948.     }
  1949.     else if(sp2.hasFocus()){
  1950.       if (sp1 != null) {sp1.requestFocus();}
  1951.       else {sp3.requestFocus();}
  1952.     }
  1953.     else{
  1954.       if(sp2 != null){sp2.requestFocus();}
  1955.       else{sp1.requestFocus();}
  1956.     }
  1957.     break;
  1958.       case e.VK_RIGHT:   
  1959.     if(sp1.hasFocus()){
  1960.       if(sp2!=null){sp2.requestFocus();}
  1961.       else {sp3.requestFocus();}
  1962.     }
  1963.     else if(sp2.hasFocus()) {
  1964.       if (sp3 != null) {sp3.requestFocus();}
  1965.       else {sp1.requestFocus();}
  1966.     }
  1967.     else {
  1968.       if (sp1 != null){sp1.requestFocus();}
  1969.       else{sp2.requestFocus();}
  1970.     }
  1971.     break;
  1972.       }
  1973.   }
  1974.   public void keyReleased(KeyEvent e){}
  1975. }
  1976.  
  1977. class FilledBorderedPane extends JPanel{
  1978.   private Color fillColor;
  1979.   public FilledBorderedPane(){
  1980.     super();
  1981.     setLayout(new FlowLayout(FlowLayout.CENTER,0,0));
  1982.   }
  1983.   public void paint(Graphics g) {
  1984.     if(fillColor != null){
  1985.         g.setColor(fillColor);
  1986.         g.fillRect(0,0,getWidth(),getHeight());
  1987.     }
  1988.     super.paint(g);
  1989.   }
  1990.   public void setFillColor(Color newColor) {fillColor = newColor;}
  1991. }
  1992.  
  1993.  
  1994.  
  1995.