3.11 The MonthPanel class

The MonthPanel class is responsible for laying out and configuring an iteration of DayBoxes so as to reflect the year and month required of it. It also has the responsibility to maintain knowledge of the currently highlighted day number in order that it can un-highlight it when required and to report the currently highlighted day number when asked. The class diagram for the MonthPanel class is given in Figure 3.14.

Figure 3.14 The MonthPanel class diagram.

The class indicates that an iteration of DayBox instances, called dayBoxes, is contained within each instance of this class as well as a primitive integer indicating which one is highlighted. The constructor requires only the identity of a listener object which is stored in the passUpToHere attribute. The constructor creates the MonthPanel without configuring it for a particular month, which is the responsibility of the reConfigure() method. This method will lay out the day numbers as appropriate to its year and month arguments, and highlight the day given. The actionPerformed() method is required to respond to the ActionEvents which will propagate from the contained dayBox instances, as described above. The dayIs() method is supplied to allow its instance parent, a DatePanel, to find out which date is currently highlighted. The implementation of this class, as far as the end of its constructor, is as follows.

0001  // Filename MonthPanel.java. 0002  // Provides an Panel which can be configured to show 0003  // the pattern of days in any particular month. 0004  // 0005  // Written for JI book, Chapter 3 see text. 0006  // Fintan Culwin, v0.2, August 1997. 0007   0008  package DatePanel; 0009   0010  import java.awt.*; 0011  import java.awt.event.*; 0012   0013  import DatePanel.DateUtility; 0014  import DatePanel.DayBox; 0015   0016   0017  class MonthPanel extends    Panel  0018                   implements ActionListener { 0019   0020  private static final int MAX_BOXES = 37; 0021   0022  private DayBox           dayBoxes[]  = new DayBox[ MAX_BOXES]; 0023  private int              highlighted; 0024  private int              theFirstBox; 0025  private ActionListener   passUpToHere; 0026   0027  private static String dayNames[] = { "Sun", "Mon", "Tue", "Wed",  0028                                       "Thu", "Fri", "Sat"  }; 0029   0030   0031     protected MonthPanel( ActionListener listener) { 0032   0033     int   thisOne;  0034     Label dayLabels[]  = new Label[ 7]; 0035      0036        this.setLayout( new GridLayout( 7, 7, 0, 0)); 0037        for ( thisOne = 0; thisOne < 7; thisOne++) {  0038           dayLabels[ thisOne] = new Label( dayNames[ thisOne], 0039                                            Label.CENTER); 0040           this.add( dayLabels[ thisOne]);                                  0041        } // End for.        0042   0043        for ( thisOne = 0; thisOne < MAX_BOXES; thisOne++) {  0044           dayBoxes[ thisOne] = new DayBox( thisOne, this); 0045           this.add( dayBoxes[ thisOne]); 0046        } // End for. 0047          0048        passUpToHere = listener; 0049     } // End MonthPanel constructor.

The constructor can be divided into a sequence of actions. It starts by establishing its' layout policy and then installs the day name Labels at the top of its area before creating all of the DayBox instances below the Labels.

The dayBoxes are laid out in rows of 7 and the longest month contains 31 days. The worst case scenario is illustrated in Figure 3.7 and occurs where the first day of the month falls on a Saturday. In this situation the first six boxes on the first row are inactive requiring a further 31 active boxes requiring a total of 37 DayBox instances. The DayBoxes are contained within the array dayBoxes which is declared and has its size established on line 0022. The private highlighted attribute is then declared on line 0023. The theFirstBox attribute, declared on line 0024, is used to record which is the first active box in the panel and as it is not a logical component of the class it is not shown on the class diagram. The value of this attribute will be set as the boxes are laid out in the reConfigure() method. The remaining attribute, passUpToHere, declared on line 0025, stores the identity of the listener object to which ActionEvents are to be dispatched when a date is selected.

In the constructor the layout policy of the DayPanel is specified as a 7 by 7 grid layout. The first row of this grid is used for the names of the days and, on lines 0037 to 0041, these are created as instances of the Label class. The text which each Label will display is supplied from a class wide array of Strings called dayNames which is declared and initialized on lines 0027 and 0028.

On lines 0043 to 0046 the 37 dayBoxes are created and added to the grid with each being informed of its ordinal position and its listener, as it is constructed. As a GridLayout policy is used all of the components on the Panel have to be the same size. As any day number, when rendered, will take up less width than the widest of the day names it can be guaranteed that the DayBoxes will be large enough to accommodate the day number string. Likewise as the day names and the day numbers are using the same Font they will also be high enough. This implies that there is a close coupling between the MonthPanel and the DayBox classes which is acceptable as they are not intended to be (re)used separately.

Before concluding, the MonthPanel constructor stores the identity of the listener object, passed in its listener argument, in the passUpToHere attribute. The DayPanel is constructed in an unconfigured state and has to be configured by a call of the reConfigure() method whose implementation is as follows.

0052     protected void reConfigure( int year, int month, int day) {  0053      0054     int maxDay   = DateUtility.daysThisMonthIs(   year, month); 0055     int startDay = DateUtility.firstDayOfMonthIs( year, month); 0056   0057     int thisOne; 0058   0059        theFirstBox = startDay; 0060        if ( day > maxDay) {  0061           day = maxDay; 0062        } //End if. 0063   0064        dayBoxes[ highlighted].clearHighlight(); 0065   0066        for ( thisOne = 0; thisOne < MAX_BOXES; thisOne++) {  0067           if ( (thisOne <  startDay) || 0068                (thisOne >= (startDay + maxDay)) ){      0069              dayBoxes[ thisOne].setDayNumber( 0);   0070           } else {       0071              dayBoxes[ thisOne].setDayNumber( thisOne - startDay +1); 0072           } // End if.     0073           dayBoxes[ thisOne].repaint(); 0074        } // End for. 0075         0076        dayBoxes[ theFirstBox + day -1].setHighlight(); 0077        highlighted = theFirstBox + day -1;        0078     } // End reConfigure. 

The reConfigure() method is responsible for setting the dayBoxes to indicate the year and month specified in its arguments. Its first step is to obtain the number of days in the month requested and the day of the week which theFirstDay falls on, using the appropriate DateUtility methods. The day argument indicates which day is to be highlighted and it is possible that this might specify a day number which is larger than the maximum day of the month; for example requesting the 31st day of February. To prevent this lines 0060 to 0062 limit the value of day to the maximum day of the required month.

On line 0064 any existing highlight is cleared by calling the clearHighlight() method of the dayBox[] array element indicated by the highlighed attribute. When the MonthPanel is configured for the first time none of the DayBoxes will be highlighted and so this step will clear the highlight of a box which is not currently highlighted, however as this has no adverse effects it need not be circumvented.

The loop between 0066 and 0074 sets all the dayBoxes to their appropriate state. The if condition on lines 0067 and 0068 will be true for all inactive boxes at the start and end of the array and these will have their dayNumber attribute set to zero to indicate this. All other boxes will have the appropriate dayNumber as specified on line 0071. The final steps, on lines 0076 and 0077, are concerned with highlighting the day specified and recording it in highlighted.

The dayIs() method uses the highlighted attribute and the offset to the first active box, theFirstBox, to determine the day number which the MonthPanel currently indicates, as follows.

0081     protected int dayIs(){  0082        return highlighted - theFirstBox +1; 0083     } // End dayIs;

The remaining method, actionPerformed(), is needed in order for the MonthPanel class to satisfy the requirement of the ActionListener interface. As explained above, it is called whenever a mouse release event occurs in a primed dayBox. It has to move the highlight to the dayBox indicated by the user, and propagate the event up to its listener. Its implementation is as follows.

0086     public void actionPerformed( ActionEvent event)  {  0087        dayBoxes[ highlighted].clearHighlight(); 0088        ((DayBox) event.getSource()).setHighlight(); 0089        highlighted = ((DayBox) event.getSource()).getOrdinal();  0090        passUpToHere.actionPerformed( event);   0091     } // End actionPerformed.    

On line 0087 the existing highlight is cleared and on lines 0088 and 0089 the highlight of the dayBox which generated the event is set and its ordinal location stored in highlighted. Finally, on line 0090, the event passed to this method in its argument is passed onwards to its stored passUpToHere ActionListener attribute. The ActionListener of a MonthPanel is always the DatePanel upon which it is mounted and whose implementation is as follows.


3.12 The DatePanel class

3.10 The DayBox class

A Java GUI programmer's primer
Java GUI Programmers Primer, A
ISBN: 0139088490
EAN: 2147483647
Year: 1998
Pages: 85
Authors: Fintan Culwin

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net