home *** CD-ROM | disk | FTP | other *** search
/ io Programmo 39 / IOPROG_39.ISO / SOFT / sdkjava40.exe / data1.cab / fg_Samples / Samples / ActiveX / JCalendar / JCalendar.java < prev    next >
Encoding:
Java Source  |  2000-05-04  |  35.8 KB  |  1,152 lines

  1. //////////////////////////////////////////////////////////////////////////
  2. //
  3. //  JCalendar.java
  4. //
  5. //      This example is a demonstration of a calendar object that can
  6. //      be placed in a layout. This object is a complete bean in that
  7. //      it fires events (JCalendarListener) and contains property
  8. //      customizers for setting the year and month
  9. //
  10. //  (C) Copyright 1995 - 1999 Microsoft Corporation.  All rights reserved.
  11. //
  12. //////////////////////////////////////////////////////////////////////////
  13.  
  14. import java.awt.*;
  15. import java.awt.event.*;
  16. import java.util.*;
  17. import java.beans.*;
  18. import java.io.*;
  19.  
  20.  
  21. public class JCalendar extends Canvas implements Serializable
  22. {
  23.     // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  24.     //                                        
  25.     // CONSTANT DEFINITION FOR THE CALENDAR
  26.     //                                        
  27.     // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  28.     public static final int    DAYS_IN_WEEK      =   7;
  29.     public static final int    MONTHS_IN_YEAR    =   12;
  30.  
  31.  
  32.     // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  33.     //                                        
  34.     // PROPERTY DEFINITION FOR THE CALENDAR
  35.     //                                        
  36.     // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  37.  
  38.  
  39.     // These are transient properties they should not be saved.
  40.     transient private Vector listeners = new Vector();                    // list of all event listeners
  41.  
  42.     // Not transient properties
  43.     transient private Date activeDate;                                    // The date which has the active focus.
  44.     private int firstDisplayedDay = 0;                                    // The first displayed day of the week 0-6 0=>sunday.
  45.     private String weekdayNames[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};                           
  46.                                                                         // The displayed string for the weekdays
  47.     public static final String monthNames[] = {"January", "February", "March", "April", "May", "June", "July", 
  48.     "August", "September", "October", "November", "December"};            // the displayed string for the month name.    
  49.     private Rectangle rect;                                                // the bounding rectangle of the calendar.
  50.     private int charHeight;                                                // The height of a displayed character
  51.     transient private FontMetrics fm;                                    // The font metrics for the displayed font.
  52.     private int monthYearLineHeight;                                    // The height of the line displaying month and year.
  53.     private int dayLineHeight;                                            // The height of the line displaying the day.
  54.     private int boxHeight;                                                // A box houses one date. This is its height.
  55.                                                                         // Note: 6x7 boxes will be displayed.
  56.     private int boxWidth;                                                // The width of a box
  57.     private boolean showOutline = true;                                    // calendar outline ?
  58.     private boolean showVerticalLines = true;                            //show vertical grid lines
  59.     private boolean showHorizontalLines = true;                            //show horizontal grid lines
  60.     private boolean circleActiveDate = true;                            // draw a circle around the active date
  61.  
  62.     // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  63.     //                                        
  64.     // CONSTRUCTORS FOR THE CALENDAR.
  65.     //                                        
  66.     // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  67.  
  68.     /** 
  69.      * Default constructor for Calendar without any parameters.
  70.      */
  71.     public JCalendar()
  72.     {
  73.         this(true, true, true, true, new Date() );
  74.     }
  75.  
  76.     /**
  77.      * Full fledged constructor for the calendar
  78.      *
  79.      * @param   boolean Should the outline be drawn
  80.      * @param   boolean Should the vertical grid lines be drawn
  81.      * @param   boolean Should the horizontal grid lines be drawn
  82.      * @param   boolean Should the active date be circled
  83.      * @param   Date    The starting date
  84.      */
  85.     public JCalendar(boolean outline, boolean verticalLines, boolean horizontalLines, 
  86.         boolean circleActiveDate, Date startDate)
  87.     {
  88.         // call the super class constructor to do its thing
  89.         super();            
  90.  
  91.         // initialize the properties
  92.        showOutline = outline;
  93.        showVerticalLines = verticalLines;
  94.        showHorizontalLines = horizontalLines;
  95.        this.circleActiveDate = circleActiveDate;
  96.        activeDate = startDate;
  97.  
  98.        // initalize the display variables
  99.        initDisplayVars();
  100.  
  101.        // Enable events to be received by the Calendar
  102.        // calendar listens for mouse clicks and arrow keys
  103.        enableEvents(AWTEvent.MOUSE_EVENT_MASK | AWTEvent.KEY_EVENT_MASK);
  104.  
  105.     }
  106.  
  107.  
  108.     // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  109.     // Reconstruct the class from an external storage
  110.     // These functions writeObject and readObject are required because this
  111.     // class needs special handling on de-serialization. The acitveDate has
  112.     // to be recalculated from the current date.
  113.     // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  114.  
  115.     /**
  116.      * Write the properties of the calendar to an external object.
  117.      *
  118.      * @param   ObjectOutput    The object to write the calendar properties to
  119.      */
  120.     private void writeObject(ObjectOutputStream stream) throws IOException
  121.     {
  122.         stream.defaultWriteObject();
  123.     }
  124.     
  125.     /**
  126.      * read the properties of the calendar from an external object.
  127.      *
  128.      * @param   ObjectInput     The object to read the properties from
  129.      */
  130.     private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException
  131.     {
  132.         stream.defaultReadObject();
  133.         activeDate = new Date();
  134.     }
  135.  
  136.     // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  137.     //                                        
  138.     // ACCESSOR METHODS FOR THE CUSTOMIZABLE PROPERTIES.
  139.     //                                        
  140.     // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  141.  
  142.  
  143.     //
  144.     // PROPERTY circleActiveDate   Decide whether to circle the active date or not.
  145.     //
  146.  
  147.     /**
  148.      * Tells whether the active date is being circled
  149.      *
  150.      * @return  boolean true => active date is being circled
  151.      */
  152.     public boolean getCircleActiveDate()
  153.     {
  154.         return circleActiveDate;
  155.     }
  156.  
  157.     /**
  158.      * Set the active date to be circled or not.
  159.      *
  160.      * @param   boolean true => circle the active date
  161.      */
  162.     public void setCircleActiveDate(boolean circleOrNotCircle)
  163.     {
  164.         circleActiveDate = circleOrNotCircle;
  165.  
  166.         // The calendar has to be redrawn
  167.         repaint();
  168.     }
  169.  
  170.     //
  171.     // PROPERTY showOutline         Decide whether to outline the calendar or not
  172.     //
  173.  
  174.     /**
  175.      * will the outline have to be shown
  176.      * 
  177.      * @return  boolean Is outline shown ?
  178.      */
  179.     public boolean getShowOutline()
  180.     {
  181.         return showOutline;
  182.     }
  183.  
  184.     /**
  185.      * set the showOutline property
  186.      *
  187.      * @param   boolean Is the outline to be shown ?
  188.      */
  189.     public void setShowOutline(boolean outlineOrBlank)
  190.     {
  191.         showOutline = outlineOrBlank;
  192.  
  193.         // The calendar has to be redrawn
  194.         repaint();
  195.     }
  196.  
  197.     //
  198.     // PROPERTY showVerticalLines       Decide whether to show the vertical grid lines which 
  199.     //                                  separate the dates.
  200.  
  201.     /**
  202.      * show the vertical grid lines
  203.      *
  204.      * @return  boolean Are the vertical grid lines showing
  205.      */
  206.     public boolean getShowVerticalLines()
  207.     {
  208.         return showVerticalLines;
  209.     }
  210.  
  211.     /**
  212.      * hide/show the vertical grid lines
  213.      *
  214.      * @param  boolean Should the vertical grid lines show ?
  215.      */
  216.     public void setShowVerticalLines(boolean toShowVerticalLines)
  217.     {
  218.         showVerticalLines = toShowVerticalLines;
  219.  
  220.         // Redraw the calendar with or without the vertical lines
  221.         repaint();
  222.     }
  223.  
  224.     //
  225.     // PROPERTY showHorizontalLines         Decide whether to show the horizontal grid lines which 
  226.     //                                      separate the dates.
  227.     // 
  228.  
  229.     
  230.     /**
  231.      * show the horizontal grid lines
  232.      *
  233.      * @return  boolean Are the horizontal grid lines showing
  234.      */
  235.     public boolean getShowHorizontalLines()
  236.     {
  237.         return showHorizontalLines;
  238.     }
  239.  
  240.     /**
  241.      * hide/show the horizontal grid lines
  242.      *
  243.      * @param  boolean Should the horizontal grid lines show ?
  244.      */
  245.     public void setShowHorizontalLines(boolean toShowVerticalLines)
  246.     {
  247.         showHorizontalLines = toShowVerticalLines;
  248.  
  249.         // Redraw the calendar with or without the vertical lines
  250.         repaint();
  251.     }
  252.  
  253.     //
  254.     // PROPERTY font    The calendar size has to be recalculated when the font changes thus this property
  255.     //                  in the Component class has to be overridden.
  256.     //
  257.  
  258.     /**
  259.      * change the calendar font
  260.      *
  261.      * @param   Font    The new calendar font.
  262.      */
  263.     public void setFont(Font f)
  264.     {
  265.         super.setFont(f);
  266.  
  267.         // This will resize the calendar and draw it again based on the new preferred size.
  268.         adjustSize();
  269.     }
  270.  
  271.  
  272.     //
  273.     // PROPERTY Background. The calendar's background has to be repainted when this
  274.     //                        property chnages therefore this method must be over-ridden
  275.     //
  276.     /**
  277.      * change the calendar background
  278.      *
  279.      * @param    Color    The background color
  280.      */
  281.     public void setBackground(Color c)
  282.     {
  283.         super.setBackground(c);
  284.  
  285.         // this will fill the background color
  286.         repaint();
  287.     }
  288.  
  289.     //
  290.     // PROPERTY Foreground. The calendar's foreground has to be repainted when this
  291.     //                        property chnages therefore this method must be over-ridden
  292.     //
  293.     /**
  294.      * change the calendar foreground
  295.      *
  296.      * @param    Color    The foreground color
  297.      */
  298.     public void setForeground(Color c)
  299.     {
  300.         super.setForeground(c);
  301.  
  302.         // this will fill the background color
  303.         repaint();
  304.     }
  305.  
  306.  
  307.     //
  308.     // PROPERTY date    The date which has the current focus and is currently highlighted.
  309.     //
  310.  
  311.     /**
  312.      * Returns the active date
  313.      * @return  int    The date which is currently being displayed
  314.      */
  315.     public int getDate()
  316.     {
  317.         return activeDate.getDate();
  318.     }
  319.  
  320.     /**
  321.      * Change the active date
  322.      * @param   int    The new date to be displayed
  323.      */
  324.     public void setDate(int date)
  325.     {
  326.         // don't do anything if nothing needs to be done.
  327.         if(activeDate.getDate() == date )
  328.         {
  329.             return;
  330.         }
  331.  
  332.         // First I un-highlight the currently highlighted date.
  333.  
  334.         // Erase the circle by drawing over it in the background color.
  335.         drawActiveDateCircle(getBackground());
  336.  
  337.         int oldYear = activeDate.getYear();        // The previous value of year
  338.         int oldMonth = activeDate.getMonth();      // The previous value of month
  339.         int oldDate = activeDate.getDate();        // The previous value of the date
  340.         
  341.         // Now change the date
  342.         activeDate.setDate(date);
  343.  
  344.         // I want to redraw the calendar only if the month or year change. Else I will only
  345.         // redraw the circle highlighting the active (or highlighted) date.
  346.  
  347.         if( activeDate.getMonth() != oldMonth || activeDate.getYear() != oldYear )
  348.         {
  349.             // I have to redraw the whole calendar
  350.             repaint();
  351.         }
  352.  
  353.         else
  354.         {
  355.             // I only have to redraw the circle around the highlighted date.
  356.             drawActiveDateCircle(getForeground());
  357.         }
  358.     
  359.         // Now tell the whole world that the date has changed.
  360.         if(activeDate.getYear() != oldYear)
  361.             fireYearChanged(oldYear, activeDate.getYear());
  362.  
  363.         if(activeDate.getMonth() != oldMonth)
  364.             fireMonthChanged(oldMonth, activeDate.getMonth());
  365.  
  366.         if(activeDate.getDate() != oldDate)
  367.             fireDateChanged(oldDate, activeDate.getDate());
  368.                 
  369.     }
  370.  
  371.     //
  372.     // PROPERTY year    The currently displayed year. Note: this must be a positive integer (i.e. >= 1)
  373.     //
  374.  
  375.     /**
  376.      * Get the currently displayed year
  377.      * @return  int     The year being displayed (e.g. 1997)
  378.      */
  379.     public int getYear()
  380.     {
  381.         return activeDate.getYear() + 1900;
  382.     }
  383.  
  384.     /**
  385.      * Change the year being displayed
  386.      * @param   int     The year to be displayed
  387.      */
  388.     public void setYear(int year) throws PropertyVetoException
  389.     {
  390.         
  391.         // Veto the property change if its not >= 1
  392.         if(year <= 0)
  393.             throw new PropertyVetoException("year has to be positive !",
  394.                 new PropertyChangeEvent(this, "year", new Integer(activeDate.getYear()), new Integer(year)));
  395.  
  396.         // First copy the old date
  397.         Date oldDate = cloneDate(activeDate);
  398.  
  399.         // Now change the year.
  400.         activeDate.setYear(year-1900);
  401.  
  402.         // The calendar will have to be redrawn
  403.         repaint();
  404.  
  405.         // Tell the whole world about this marvellous event !
  406.         fireJCalendarEvent(oldDate, activeDate);
  407.     }
  408.  
  409.     //
  410.     // PROPERTY month   The month being displayed in the calendar (0-11)
  411.     //
  412.  
  413.     /**
  414.      * Get the currently displayed month
  415.      * @return  int     The month being displayed (0 - 11)
  416.      */
  417.     public int getMonth()
  418.     {
  419.         return activeDate.getMonth();
  420.     }
  421.  
  422.     /**
  423.      * Change the month being displayed
  424.      * @param   int     The month to be displayed (e.g. january is 0)
  425.      */
  426.     public void setMonth(int month) throws PropertyVetoException
  427.     {
  428.         // Veto the change if its negative.
  429.         if(month < 0)
  430.             throw new PropertyVetoException("month can't be negative !",
  431.                 new PropertyChangeEvent(this, "month", new Integer(activeDate.getMonth()), new Integer(month)));
  432.  
  433.         // first copy the old date
  434.         Date oldDate = cloneDate(activeDate);
  435.  
  436.         // Now change the month and year if necessary
  437.         activeDate.setMonth( month );
  438.  
  439.         // Redraw the calendar
  440.         repaint();
  441.  
  442.         // Tell everyone that the date has changed.
  443.         fireJCalendarEvent(oldDate, activeDate);
  444.     }
  445.  
  446.     // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  447.     //                                        
  448.     // PUBLIC METHODS FOR PERFORMING ACTIONS ON THE CALENDAR
  449.     //                                        
  450.     // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  451.  
  452.     /**
  453.      * Increment year by one.
  454.      *
  455.      */
  456.     public void nextYear()
  457.     {
  458.         try 
  459.         {
  460.             // increment the year
  461.             setYear(getYear() + 1);
  462.         }
  463.         catch(PropertyVetoException e)
  464.         {
  465.             // Do nothing. This exception is not going to occur unless you go into B.C.
  466.         }
  467.     }
  468.  
  469.     /**
  470.      * Decrement year by one.
  471.      */
  472.     public void previousYear()
  473.     {
  474.         try
  475.         {
  476.             // decrement the year
  477.             setYear(getYear() - 1);
  478.         }
  479.         catch(PropertyVetoException e)
  480.         {
  481.             // Do nothing. This exception is not going to occur unless you go into B.C.
  482.         }
  483.     }
  484.  
  485.     /**
  486.      * Increment month by one.
  487.      */
  488.     public void nextMonth()
  489.     {
  490.         try
  491.         {
  492.             // increment the month
  493.             setMonth(getMonth() + 1);
  494.         }
  495.         catch (PropertyVetoException e)
  496.         {
  497.         }
  498.     }
  499.  
  500.     /**
  501.      * Decrement month by one.
  502.      */
  503.     public void previousMonth()
  504.     {
  505.         try
  506.         {
  507.             // decrement the month
  508.             setMonth(getMonth() - 1);
  509.         }
  510.         catch (PropertyVetoException e)
  511.         {
  512.             // If you are trying to go back a month from january then set the month to december !
  513.             if(getMonth() == 0) 
  514.             {
  515.                 // First make a copy of the old date
  516.                 Date oldDate = cloneDate(activeDate);
  517.  
  518.                 // Go back a year and set the month to December.
  519.                 activeDate = new Date(activeDate.getYear() - 1, 11, activeDate.getDate());
  520.  
  521.                 // Redraw the calendar.
  522.                 repaint();
  523.  
  524.                 // since setMonth()  failed we have to now notify the change in the calendar.
  525.                 fireJCalendarEvent(oldDate, activeDate);
  526.             }
  527.         }
  528.     }
  529.  
  530.     /**
  531.      * Increments the current date by one week
  532.      */
  533.     public void nextWeek()
  534.     {
  535.         setDate(activeDate.getDate() + DAYS_IN_WEEK);
  536.     }
  537.  
  538.     /**
  539.      * Decrement the date by one week
  540.      */
  541.     public void previousWeek()
  542.     {
  543.         setDate(activeDate.getDate() - DAYS_IN_WEEK);
  544.     }        
  545.  
  546.     /**
  547.      * Increments the date by one
  548.      */
  549.     public void nextDate()
  550.     {
  551.         setDate(activeDate.getDate() + 1);
  552.     }
  553.  
  554.     /**
  555.      * Decrements the date by one
  556.      */
  557.     public void previousDate()
  558.     {
  559.         setDate(activeDate.getDate() - 1);
  560.     }
  561.  
  562.     /**
  563.      * Changes the date to today
  564.      */
  565.     public void toToday()
  566.     {
  567.         // Assign today's date to activeDate.
  568.         activeDate = new Date();
  569.  
  570.         // The calendar has to be redrawn now.
  571.         repaint();
  572.     }
  573.  
  574.     /**
  575.      * Returns the first date of the month --> 1 !!
  576.      *
  577.      * @return  int the first date of the month
  578.      */
  579.     public int getFirstDate()
  580.     {
  581.         return 1;
  582.     }
  583.  
  584.     /**
  585.      * Returns the last date of the month
  586.      *
  587.      * @return  int The last date of the month
  588.      */
  589.     public int  getLastDate()
  590.     {
  591.         Date lastDate = new Date(activeDate.getYear(), activeDate.getMonth()+1, 0);
  592.  
  593.         return lastDate.getDate();
  594.     }
  595.  
  596.     // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  597.     //                                        
  598.     // METHODS FOR DISPLAYING THE CALENDAR.
  599.     //                                        
  600.     // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  601.     
  602.     /**
  603.      * Draw the calendar
  604.      * @param   Graphics    The graphics device for drawing the calendar
  605.      */
  606.     public void paint  (Graphics g)
  607.     {
  608.         // calculate the display variables
  609.         initDisplayVars();   
  610.  
  611.         // fill the nackground with the background color
  612.         g.setColor(getBackground());
  613.         g.fillRect(0,0,rect.width-1, rect.height-1);
  614.         
  615.         // restore the foreground color
  616.         g.setColor(getForeground());
  617.  
  618.         // Draw the outline of the calendar if desired.
  619.         if(showOutline)
  620.             g.drawRect(0,0, rect.width-1, rect.height-1);
  621.  
  622.         // Draw all the horizontal lines of the calendar.
  623.         drawHorizontalLines(g);
  624.  
  625.         // Draw all the vertical lines
  626.         drawVerticalLines(g);
  627.  
  628.         // We need the font metrics to calculate string
  629.         // widths and heights
  630.         fm = g.getFontMetrics();
  631.  
  632.         charHeight = fm.getAscent();  // the height of the chars
  633.      
  634.         // Displat the month and the year
  635.         displayMonthYear(g);
  636.  
  637.         // Display the days of the week
  638.         displayWeekDays(g);
  639.  
  640.         // Display the dates
  641.         displayDates(g);
  642.         
  643.     }
  644.  
  645.     //--------------------------------------------------------------------
  646.     // Private methods for displaying the calendar.
  647.     //--------------------------------------------------------------------
  648.  
  649.     /**
  650.      * Initialize the variables corresponding to the screen display.
  651.      */
  652.     private void initDisplayVars()
  653.     {
  654.         // First figure out my dimensions.
  655.         rect = getBounds();  // The bounding rectangle of this object
  656.     
  657.         // The calendar is 7 boxes wide. One for each day of the week.
  658.         boxWidth = (rect.width - 1) / DAYS_IN_WEEK;
  659.  
  660.         // The calendar has 8 lines
  661.         // 1 line for the month and the year
  662.         monthYearLineHeight = (rect.height - 1)/8;
  663.  
  664.         // 1 line for the days of the week
  665.         dayLineHeight = (rect.height - 1)/8;
  666.             
  667.         // and 6 lines for the dates.
  668.         boxHeight = (rect.height - 1) / 8;
  669.     }
  670.  
  671.     /**
  672.      * Draws the horizontal lines in the calendar
  673.      * @param   Graphics    The graphics context to draw to.
  674.      */
  675.     private void drawHorizontalLines(Graphics g)
  676.     {
  677.         // check to see if the horizontal lines have to be shown at all.
  678.         if(showHorizontalLines)
  679.         {
  680.             // The line separating the month and year from the days of the week.
  681.             g.drawLine(0, monthYearLineHeight, rect.width, monthYearLineHeight);
  682.  
  683.             // The line separating the days of the week from the calendar dates
  684.             g.drawLine(0, monthYearLineHeight + dayLineHeight, rect.width, monthYearLineHeight + dayLineHeight);
  685.  
  686.             // The 5 lines separating the 6 rows of dates.
  687.             for(int i=1; i<=5; i++)
  688.                 g.drawLine(0, monthYearLineHeight + dayLineHeight + i*boxHeight, 
  689.                             rect.width, monthYearLineHeight + dayLineHeight + i*boxHeight);
  690.         }
  691.     }
  692.  
  693.     /**
  694.      * Draws the vertical lines
  695.      *
  696.      * @param   Graphics    The graphics context to draw to.
  697.      */
  698.     private void drawVerticalLines(Graphics g)
  699.     {
  700.         // Draw the vertical lines if neccessary
  701.         if(showVerticalLines)
  702.         {
  703.             // Draw a 6 vertical lines separating the 7 columns for the weekdays
  704.             for(int i=1; i<DAYS_IN_WEEK; i++)
  705.                 g.drawLine(i*boxWidth, monthYearLineHeight, i*boxWidth, rect.height);
  706.         }
  707.     }
  708.  
  709.  
  710.     /**
  711.      * Display the month and the year
  712.      *
  713.      * @param   Graphics    The graphics context to write to
  714.      */
  715.     private void displayMonthYear(Graphics g)
  716.     {
  717.         int stringWidth;          // the width of a displayed string.
  718.  
  719.         // First write the month and the year.
  720.         String line = new String(monthNames[activeDate.getMonth()] + " " + 
  721.                                 new Integer(activeDate.getYear() + 1900).toString());
  722.             // This will hold the displayed line for the month and the year.
  723.  
  724.         // the width of this line.
  725.         stringWidth = fm.stringWidth(line);
  726.  
  727.         // Display the centered month and year.
  728.         g.drawString(line, rect.width/2 - stringWidth/2, monthYearLineHeight/2 + 
  729.                                                         charHeight/2);
  730.     }
  731.  
  732.     /**
  733.      * Display the names of the week days
  734.      *
  735.      * @param   Graphics    The graphics context to write to
  736.      */
  737.     private void displayWeekDays(Graphics g)
  738.     {
  739.         int stringWidth;        // The width of a displayed string
  740.         
  741.         // Display the weekdays.
  742.         for(int i=0; i<7; i++)
  743.         {
  744.             // find the width of the weekday
  745.             stringWidth = fm.stringWidth(weekdayNames[i]);
  746.  
  747.             // draw the weekday centered
  748.             g.drawString(weekdayNames[i], boxWidth*i + boxWidth/2 - stringWidth/2,
  749.                 monthYearLineHeight + dayLineHeight/2 + charHeight/2);
  750.         }
  751.  
  752.     }
  753.  
  754.     /**
  755.      * Fill in the dates in the calendar
  756.      *
  757.      * @param   Graphics    The graphics context to write in
  758.      */
  759.     private void displayDates(Graphics g)
  760.     {
  761.         int stringWidth;                  // the width of a displayed line.
  762.         
  763.         int firstWeekDay = new Date(activeDate.getYear(), activeDate.getMonth(),1).getDay();
  764.                     // the weekday corresponding to the first of the month.
  765.  
  766.         // Display the days. currDate starts as the first date of the month and iterates
  767.         // through each date until it reaches the next month.
  768.         for(Date currDate= new Date(activeDate.getYear(), activeDate.getMonth(),1);
  769.                 currDate.getMonth() == activeDate.getMonth();
  770.                     currDate.setDate(currDate.getDate() + 1) )
  771.         {
  772.             String str = new Integer(currDate.getDate()).toString();
  773.                             // The string version of the next displayed number.
  774.  
  775.             // the width of this number
  776.             stringWidth = fm.stringWidth(str);
  777.  
  778.             int x;  // The x coordinate of the box in which to display this day
  779.             int y;  // The y coordinate of the same.
  780.  
  781.             // The x cood is the same as the day of the week for this date.
  782.             x = currDate.getDay();
  783.  
  784.             // The  cood is the week number for this date.
  785.             y = (currDate.getDate() - 1 + firstWeekDay) / DAYS_IN_WEEK;
  786.  
  787.             // Now just draw the date centered
  788.             g.drawString(str, x*boxWidth + boxWidth/2 - stringWidth/2,
  789.                 monthYearLineHeight + dayLineHeight + boxHeight*y + boxHeight/2 + 
  790.                     charHeight/2);
  791.             
  792.             // Draw a circle around the active date if desired
  793.             if(circleActiveDate)
  794.             {
  795.                 // Draw a circle around the current date if its the actual date we are looking for.
  796.                 if(currDate.getDate() == activeDate.getDate())
  797.                 {
  798.                     // Draw an oval around this date.
  799.                     g.drawOval(x*boxWidth+1, monthYearLineHeight + dayLineHeight + boxHeight*y+1,
  800.                         boxWidth-2, boxHeight-2);
  801.                 }
  802.             }
  803.         }
  804.     }
  805.  
  806.     /**
  807.      * Draw the active date circle. Useful for erasing and redrawing the circle on the active date.
  808.      *
  809.      */
  810.     private void drawActiveDateCircle(Color c)
  811.     {
  812.         // Only if the active date NEEDS to be circled
  813.         if(circleActiveDate)
  814.         {            
  815.             int firstWeekDay = new Date(activeDate.getYear(), activeDate.getMonth(),1).getDay();
  816.                         // The day of the week for the first of the month
  817.             int x;      // The x coordinate of the box corresponding to the active date.
  818.             int y;      // The y coordinate of the same.
  819.  
  820.             // the x cood is simply the day of the week
  821.             x = activeDate.getDay();
  822.  
  823.             // the y cood is the week number
  824.             y = (activeDate.getDate() - 1 + firstWeekDay) / DAYS_IN_WEEK;
  825.  
  826.             // Get the graphics context
  827.             Graphics g = getGraphics();
  828.  
  829.             // set the color
  830.             g.setColor(c);
  831.  
  832.             // Now draw the oval around the date highlighted.
  833.             g.drawOval(x*boxWidth+1, monthYearLineHeight + dayLineHeight + boxHeight*y+1, 
  834.                 boxWidth-2, boxHeight-2);
  835.  
  836.         }       
  837.  
  838.     }
  839.  
  840.     /**
  841.      * This will set the calendar to the right size given the current
  842.      * properties
  843.      */
  844.     private void adjustSize()
  845.     {
  846.         // Get the preferred size of the calendar
  847.         Dimension d = getPreferredSize();
  848.  
  849.         // Resize the calendar
  850.         setSize(d.width, d.height);
  851.  
  852.         // Now find out who my parent is
  853.         Component p = getParent();
  854.  
  855.         if(p != null)
  856.         {
  857.             // invalidate the current layout
  858.             p.invalidate();
  859.  
  860.             // now layout the display again
  861.             p.doLayout();
  862.         }
  863.  
  864.     }
  865.  
  866.     /**
  867.      * Get the desired size of the calendar
  868.      *
  869.      * @return  Dimension   The optimal size for the calendar
  870.      */
  871.     public Dimension getPreferredSize()
  872.     {
  873.         // Figure out the font metrics
  874.         FontMetrics fm =  getFontMetrics(getFont());
  875.  
  876.         int width = 0;    // The temporary width
  877.         int max_width = 0;      // the maximum width
  878.         
  879.         
  880.         // Find maximum width of the day of the week line
  881.         for(int i=0; i<DAYS_IN_WEEK; i++)
  882.         {
  883.             // Find out the maximum width of any given day of the week.
  884.             // plus for comfort leave a space between weekday names
  885.             width = Math.max(width,fm.stringWidth(" " + weekdayNames[i]));
  886.         }
  887.  
  888.         // The max_width has to accomodate all the week of the same size
  889.         max_width = DAYS_IN_WEEK * width;
  890.  
  891.         // Maximum width of the date lines
  892.         // Each line may have upto 14 digits as fat as an 8.
  893.         width = fm.stringWidth("88888888888888");
  894.  
  895.         // choose the maximum of these two widths.
  896.         max_width = Math.max(width, max_width);
  897.  
  898.         // The height is 8 times the height of a character + space for
  899.         // inter-character space (leading).
  900.         return new Dimension(max_width, 8 * (fm.getHeight() + fm.getLeading()));
  901.     }
  902.  
  903.     /**
  904.      * Get the minimum size of the calendar
  905.      *
  906.      * @return  Dimension   The minimum size for the calendar
  907.      */
  908.     public Dimension getMinimumSize()
  909.     {
  910.         // The fontmetrics
  911.         FontMetrics fm =  getFontMetrics(getFont());
  912.  
  913.         int width = 0;    // The temporary width
  914.         int max_width = 0;      // the maximum width
  915.  
  916.  
  917.         // Find maximum width of the day of the week line
  918.         // since we want minimum possible width we simply add the weekday name widths
  919.         for(int i=0; i<DAYS_IN_WEEK; i++)
  920.             width += fm.stringWidth(weekdayNames[i]);
  921.             
  922.  
  923.         max_width = Math.max(width, max_width);
  924.  
  925.         // Maximum width of the date lines
  926.         // allow 14 digits (2 per day of the week
  927.         width = fm.stringWidth("88888888888888");
  928.  
  929.         // choose max of these two.
  930.         max_width = Math.max(width, max_width);
  931.  
  932.         // The total height is 8 times the height of a character
  933.         return new Dimension(max_width, 8 * (fm.getHeight() + fm.getLeading()));
  934.     }
  935.  
  936.             
  937.  
  938.     // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  939.     //                                        
  940.     // EVENT HOOKS FOR THE CALENDAR
  941.     //                                        
  942.     // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  943.  
  944.     /**
  945.      * Add a calendar listener
  946.      *
  947.      * @param   JCalendarListener    The listener object
  948.      */
  949.     synchronized public void addJCalendarListener(JCalendarListener l)
  950.     {
  951.         /*
  952.          * when the object is serialized the listeners vector is lost so
  953.          * a new one has to be created on deserialization
  954.          */
  955.         if(listeners == null)
  956.             listeners = new Vector();
  957.  
  958.         listeners.addElement(l);
  959.     }
  960.  
  961.     /**
  962.      * Remove a calendar listener
  963.      *
  964.      * @param   JCalendarListener    The listener object to remove
  965.      */
  966.     synchronized public void removeJCalendarListener(JCalendarListener l)
  967.     {
  968.         listeners.removeElement(l);
  969.     }
  970.  
  971.     /**
  972.      * Fires an year changed event whenever the year changes.
  973.      *
  974.      * @param   int, int    The old and the new year.
  975.      */
  976.     synchronized private void fireYearChanged(int oldYear, int newYear)
  977.     {
  978.         if(listeners == null)
  979.             return;
  980.  
  981.         for(int i=0; i< listeners.size(); i++)
  982.             ((JCalendarListener) listeners.elementAt(i)).yearChanged(oldYear, newYear);
  983.     }
  984.  
  985.     /**
  986.      * Fires a month changed event whenever the month changes.
  987.      *
  988.      * @param   int, int    The old and the new month.
  989.      */
  990.     synchronized private void fireMonthChanged(int oldMonth, int newMonth)
  991.     {
  992.         if(listeners == null)
  993.             return;
  994.             
  995.         for(int i=0; i< listeners.size(); i++)
  996.             ((JCalendarListener) listeners.elementAt(i)).monthChanged(
  997.                 monthNames[oldMonth], monthNames[newMonth]);
  998.     }
  999.  
  1000.     /**
  1001.      * Fires a date changed event whenever the date changes.
  1002.      *
  1003.      * @param   int the old date 
  1004.      * @param   int the new date
  1005.      */
  1006.     synchronized private void fireDateChanged(int oldDate, int newDate)
  1007.     {
  1008.         if(listeners == null)
  1009.             return;
  1010.  
  1011.         for(int i=0; i< listeners.size(); i++)
  1012.             ((JCalendarListener) listeners.elementAt(i)).dateChanged(oldDate, newDate);
  1013.     }
  1014.  
  1015.  
  1016.     /**
  1017.      * Fires a date, month or year change event appropriately
  1018.      *
  1019.      * @param   Date    The old value of the date
  1020.      * @param   Date    The new date
  1021.      */
  1022.     synchronized private void fireJCalendarEvent(Date oldDate, Date newDate)
  1023.     {
  1024.          
  1025.         if(oldDate.getYear() != newDate.getYear())
  1026.             fireYearChanged(oldDate.getYear(), newDate.getYear());
  1027.         
  1028.         if(oldDate.getMonth() != newDate.getMonth())
  1029.             fireMonthChanged(oldDate.getMonth(), newDate.getMonth());
  1030.        
  1031.         if(oldDate.getDate() != newDate.getDate())
  1032.             fireDateChanged(oldDate.getDate(), newDate.getDate());
  1033.  
  1034.     }
  1035.  
  1036.     // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1037.     //                                        
  1038.     // HANDLING USER INPUT TO THE CALENDAR
  1039.     //                                        
  1040.     // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1041.  
  1042.     /**
  1043.      * If the user clicks on a calendar date and it's not the currently active
  1044.      * date then make it the active date
  1045.      *
  1046.      * @param   MouseEvent  The event corresponding to the key click
  1047.      */
  1048.     protected void processMouseEvent(MouseEvent e)
  1049.     {
  1050.  
  1051.         if((e.getID()) == MouseEvent.MOUSE_PRESSED)
  1052. mouse_handler:
  1053.         {
  1054.             int y = e.getY() - dayLineHeight - monthYearLineHeight;
  1055.             // The y -cood of the box
  1056.  
  1057.             // The user clicked outside the date area and on the month or the weekday
  1058.             if(y < 0) 
  1059.                 break mouse_handler;
  1060.  
  1061.             // Find out the y cood of the box
  1062.             y /= boxHeight;
  1063.  
  1064.             int x = e.getX() / boxWidth;    // The x - cood of the box
  1065.             
  1066.             // Now figure out the actual date.
  1067.             // Note: we need to know the day of the week for the first day of the month to make
  1068.             // calculation.
  1069.             Date firstDayOfMonth = new Date(activeDate.getYear(), activeDate.getMonth(), 1);
  1070.             int date = 1 + y * 7 + x - firstDayOfMonth.getDay();
  1071.  
  1072.             // Do nothing if this click was outside the valid dates for this month.
  1073.             if(date < getFirstDate() || date > getLastDate())
  1074.                 break mouse_handler;
  1075.  
  1076.             // Now set this date.   
  1077.             setDate(date);
  1078.         }
  1079.         
  1080.         // Allow the super class (component) to continue delivery of event
  1081.         super.processMouseEvent(e);
  1082.     }
  1083.  
  1084.     /**
  1085.      * Processes keyboard input.
  1086.      *
  1087.      * Key          Action
  1088.      * ---          ------
  1089.      * up arrow     previous week
  1090.      * down arrow   next week
  1091.      * left arrow   previous day
  1092.      * right arrow  next day
  1093.      * PAGE UP      previous month
  1094.      * PAGE DOWN    next month
  1095.      *
  1096.      * @param   KeyEvent    The key event which occurred.
  1097.      */
  1098.     protected void processKeyEvent (KeyEvent e)
  1099.     {
  1100.         if(e.getID() == KeyEvent.KEY_PRESSED )
  1101.         {
  1102.             switch( e.getKeyCode() )
  1103.             {
  1104.             case KeyEvent.VK_UP:
  1105.                 previousWeek();
  1106.                 break;
  1107.  
  1108.             case KeyEvent.VK_LEFT:
  1109.                 previousDate();
  1110.                 break;
  1111.  
  1112.             case KeyEvent.VK_RIGHT:
  1113.                 nextDate();
  1114.                 break;
  1115.  
  1116.             case KeyEvent.VK_DOWN:
  1117.                 nextWeek();
  1118.                 break;
  1119.  
  1120.             case KeyEvent.VK_PAGE_UP:
  1121.                 previousMonth();
  1122.                 break;
  1123.  
  1124.             case KeyEvent.VK_PAGE_DOWN:
  1125.                 nextMonth();
  1126.                 break;
  1127.             }
  1128.         }
  1129.  
  1130.         // Let the super class continue delivery of key events.
  1131.         super.processKeyEvent(e);
  1132.     }
  1133.  
  1134.     // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1135.     //                                        
  1136.     // Miscellaneous utility functions
  1137.     //                                        
  1138.     // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1139.  
  1140.     /**
  1141.      * This funcion clones a date object. This is helpful because the
  1142.      * the Date class hasn't defined a clone method.
  1143.      *
  1144.      * @param   Date    The date to be duplicated
  1145.      * @return  Date    A "new" copy of the date object
  1146.      */
  1147.     public static Date cloneDate(Date date)
  1148.     {
  1149.         return new Date(date.getYear(), date.getMonth(), date.getDate());
  1150.     }
  1151. }
  1152.