FileDocCategorySizeDatePackage
DateEditor.javaAPI DocphoneME MR2 API (J2ME)54988Wed May 02 18:00:20 BST 2007javax.microedition.lcdui

DateEditor

public class DateEditor extends com.sun.midp.chameleon.layers.PopupLayer implements CommandListener
A utility class for editing date/time components for a DateField.

Fields Summary
protected static final int[]
TRIG_TABLE
Table of trigonometric functions, in 16.16 fixed point.
protected static final int
MONTH_POPUP
Constant indicating the month popup, used in the process of current focus tracking inside the date editor.
protected static final int
YEAR_POPUP
Constant indicating the year popup, used in the process of current focus tracking inside the date editor.
protected static final int
HOURS_POPUP
Constant indicating the hour popup, used in the process of current focus tracking inside the date editor.
protected static final int
MINUTES_POPUP
Constant indicating the minutes popup, used in the process of current focus tracking inside the date editor.
protected static final int
CALENDAR
Constant indicating the calendar, used in the process of current focus tracking inside the date editor.
protected static final int
AM_PM
Constant indicating the am/pm indicators, used in the process of current focus tracking inside the date editor.
protected static String[]
MONTHS
Static array holding the localized equivalent of month names.
protected static String[]
YEARS
Static array holding the year values.
protected static int[]
HOURS
Static array holding the hour values.
protected static int[]
MINUTES
Static array holding the minute values.
protected DateFieldLFImpl
lf
The DateFieldLFImpl that triggered this date editor.
protected Calendar
editDate
The date currently being edited.
protected int
mode
The mode of the date field, that triggered this date editor.
protected boolean
initialized
Whether date field that triggered this date editor was initialized or not.
protected Command
cancel
Special command to cancel any changes and close the date editor without any impact on the datefield that triggered this editor.
protected Command
set
Special command to set/save the changes done in the editor into the datefield that triggered this editor and close the editor.
protected Command[]
commands
The command array that holds both the commands associated with the date editor.
protected int
nextX
The location x-coordinate, used to calculate where to draw the next component.
protected int
nextY
The location y-coordinate, used to calculate where to draw the next component.
protected int
lastDay
The last day of the month.
protected int
dayOffset
The day offset.
protected DEPopupLayer
monthPopup
The sub-popup layer used to select month value.
protected DEPopupLayer
yearPopup
The sub-popup layer used to select year value.
protected DEPopupLayer
hoursPopup
The sub-popup layer used to select hour value.
protected DEPopupLayer
minutesPopup
The sub-popup layer used to select minutes value.
protected int
focusOn
Keeps track of the currently focused item inside the date editor.
protected boolean
amSelected
Indicates whether am or pm is currently selected. True indicates "am" is selected and false indicates "pm" is selected.
protected boolean
amHilighted
Indicates whether am or pm is currently highlighted. True indicates "am" is highlighted and false indicates "pm" is highlighted.
protected int
hilightedDate
Currently highlighted date in the calendar.
protected int
selectedDate
Currently selected date in the calendar.
protected int
popupWidth
Width of a sub-popup in its closed state.
protected int
popupHeight
Height of a sub-popup in its closed state.
protected int
elementWidth
Width of the element within the popup in its closed state.
protected int
elementHeight
Height of the element within the popup in its closed state.
protected int
timeComponentsOffset
The location offset to draw time components used for DateField.TIME and DateField.DATE_TIME modes.
protected int
calendarTopLimit
Indicates calendar's top limit, used in traversal calculations.
protected int
calendarBottomLimit
Indicates calendar's bottom limit, used in traversal calculations.
protected int
calendarRightLimit
Indicates calendar's right limit, used in traversal calculations.
protected int
dateHilightX
Indicates x co-ordinate of previously highlighted date, used in traversal calculations.
protected int
dateHilightY
Indicates y co-ordinate of previously highlighted date, used in traversal calculations.
final int
PRESS_OUT_OF_BOUNDS
private int
itemIndexWhenPressed
private int[]
month_bounds
private int[]
year_bounds
private int[]
hours_bounds
private int[]
minutes_bounds
private int[]
calendar_bounds
private int[]
ampm_bounds
private int
pressedDate
private boolean
popUpOpen
The state of the date editor popup (Default: false = closed).
private boolean
sizeChanged
private boolean
isIitialized
Constructors Summary
public DateEditor(DateFieldLFImpl lf)
Create a new DateEditor layer.

param
lf The DateFieldLFImpl that triggered this date editor

        super(DateEditorSkin.IMAGE_BG, DateEditorSkin.COLOR_BG);
        this.lf = lf;
    
Methods Summary
public voidcallSizeChanged()

        if (monthPopup != null) { setMonthPopupLocation(); }
        if (yearPopup != null) { setYearPopupLocation(); }
        if (hoursPopup != null) { setHoursPopupLocation(); }
        if (minutesPopup != null) { setMinutesPopupLocation(); }
    
public voidcommandAction(Command cmd, Displayable s)
Handle a command action.

param
cmd The Command to handle
param
s The Displayable with the Command


        lf.uCallKeyPressed(Constants.KEYCODE_SELECT);

        if (cmd == set) {
            if (mode == DateField.TIME) {
                lf.saveDate(new Date(editDate.getTime().getTime() % (24*60*60*1000)));
            } else {
                lf.saveDate(editDate.getTime());
            }
        }

        // SYNC NOTE: Move the call to the application's
        // ItemStateListener outside LCDUILock
        Form form = null;
        synchronized (Display.LCDUILock) {
            if (lf.df.owner instanceof Form) {
                form = (Form)lf.df.owner;
            }
        }

        if (form != null) {
            form.uCallItemStateChanged(lf.df);
        }
    
protected static intcos(int angle)
Utility method to return the cosine of an angle.

param
angle The angle to compute the cosine of
return
int The cosine of the angle

        angle += 360000;
        angle %= 360;

        if (angle >= 270) {
            return TRIG_TABLE[360 - angle];
        } else if (angle >= 180) {
            return -TRIG_TABLE[angle - 180];
        } else if (angle >= 90) {
            return -TRIG_TABLE[180 - angle];
        } else {
            return TRIG_TABLE[angle];
        }
    
protected intcreateYearStrings(int startYear)
Recreates years string given a start year.

param
startYear the first year to be added to the YEARS array
return
selected year in the newly created array

        int selectedIndex = 0;
        int year = startYear;
        YEARS = new String[22];
        YEARS[0]  = Resource.getString(ResourceConstants.LCDUI_DF_YEAR_BEFORE);
        YEARS[21] = Resource.getString(ResourceConstants.LCDUI_DF_YEAR_AFTER);
        for (int i = 1; i < 21; i++) {
            if (year == editDate.get(Calendar.YEAR)) {
                selectedIndex = i;
            }
            YEARS[i] = Integer.toString(year++);
        }
        return selectedIndex;
    
protected intdaysInMonth(int month, int year)
Utility method to calculate the number of days in a month.

param
month The month to use
param
year The year the month occurs in
return
int The number of days in the month

        switch (month) {
        case Calendar.JANUARY:
        case Calendar.MARCH:
        case Calendar.MAY:
        case Calendar.JULY:
        case Calendar.AUGUST:
        case Calendar.OCTOBER:
        case Calendar.DECEMBER:
            return 31;
        case Calendar.FEBRUARY:
            if (((year % 400) == 0)
                || (((year & 3) == 0) && ((year % 100) != 0))) {
                return 29;
            }
            return 28;
        case Calendar.APRIL:
        case Calendar.JUNE:
        case Calendar.SEPTEMBER:
        case Calendar.NOVEMBER:
        default:
            return 30;
        }
    
protected voiddrawDateComponents(Graphics g)
Draw the date components.

param
g The Graphics object to paint to

        g.translate(month_bounds[X], month_bounds[Y]);
        drawMonthComponent(g);
        g.translate(-month_bounds[X], -month_bounds[Y]);

        g.translate(year_bounds[X], year_bounds[Y]);
        drawYearComonent(g);
        g.translate(-year_bounds[X], -year_bounds[Y]);
        
        g.translate(calendar_bounds[X], calendar_bounds[Y]);
        paintCalendar(g);
        g.translate(-calendar_bounds[X], -calendar_bounds[Y]);
    
protected voiddrawHoursComponent(Graphics g)
Draws hours popup content.

param
g The Graphics object to paint to

        if (DateEditorSkin.IMAGE_TIME_BG != null) {
            g.drawImage(DateEditorSkin.IMAGE_TIME_BG, 0, 0,
                        Graphics.LEFT | Graphics.TOP);
            int w = DateEditorSkin.IMAGE_TIME_BG.getWidth();
            int h = DateEditorSkin.IMAGE_TIME_BG.getHeight();
            if (focusOn == HOURS_POPUP) {
                g.setColor(DateEditorSkin.COLOR_TRAVERSE_IND);
                g.drawRect(-2, -2, w + 3, h + 3);
            }
        }

        g.setFont(DateEditorSkin.FONT_POPUPS);
        g.setColor(0);

        int hour;
        if (lf.CLOCK_USES_AM_PM) {
            hour = editDate.get(Calendar.HOUR) == 0 ?
                12 : editDate.get(Calendar.HOUR) % 12;
        } else {
            hour = editDate.get(Calendar.HOUR_OF_DAY);
        }

        g.drawString(DateFieldLFImpl.twoDigits(hour),
                     3, 0, Graphics.LEFT | Graphics.TOP);
    
protected voiddrawMinutesComponent(Graphics g)
Draws minutes popup content.

param
g The Graphics object to paint to

        if (DateEditorSkin.IMAGE_TIME_BG != null) {
            g.drawImage(DateEditorSkin.IMAGE_TIME_BG, 0, 0,
                        Graphics.LEFT | Graphics.TOP);
            int w = DateEditorSkin.IMAGE_TIME_BG.getWidth();
            int h = DateEditorSkin.IMAGE_TIME_BG.getHeight();
            if (focusOn == MINUTES_POPUP) {
                g.setColor(DateEditorSkin.COLOR_TRAVERSE_IND);
                g.drawRect(-2, -2, w + 3, h + 3);
            }
        }

        g.setFont(DateEditorSkin.FONT_POPUPS);
        g.setColor(0);
        g.drawString(DateFieldLFImpl.twoDigits(editDate.get(Calendar.MINUTE)),
                     3, 0, Graphics.LEFT | Graphics.TOP);
   
protected voiddrawMonthComponent(Graphics g)
Draws month popup content.

param
g The Graphics object to paint to

        if (DateEditorSkin.IMAGE_MONTH_BG != null) {
            g.drawImage(DateEditorSkin.IMAGE_MONTH_BG, 0, 0,
                        Graphics.LEFT | Graphics.TOP);
            int w = DateEditorSkin.IMAGE_MONTH_BG.getWidth();
            int h = DateEditorSkin.IMAGE_MONTH_BG.getHeight();
            if (focusOn == MONTH_POPUP) {
                g.setColor(DateEditorSkin.COLOR_TRAVERSE_IND);
                g.drawRect(-2, -2, w + 3, h + 3);
            }
        }
        g.setFont(DateEditorSkin.FONT_POPUPS);
        g.setColor(0);
        g.drawString(MONTHS[editDate.get(Calendar.MONTH)],
                     4, 0, Graphics.LEFT | Graphics.TOP);
    
protected voiddrawTimeComponents(Graphics g)
Draw the time components.

param
g The Graphics object to paint to

        g.translate(hours_bounds[X], hours_bounds[Y]);
        drawHoursComponent(g);
        g.translate(-hours_bounds[X], -hours_bounds[Y]);

        g.translate(minutes_bounds[X], minutes_bounds[Y]);
        drawMinutesComponent(g);
        g.translate(-minutes_bounds[X], -minutes_bounds[Y]);

        g.translate(ampm_bounds[X], ampm_bounds[Y]);
        paintAmPm(g);
        g.translate(-ampm_bounds[X], -ampm_bounds[Y]);
    
protected voiddrawYearComonent(Graphics g)
Draws year popup content.

param
g The Graphics object to paint to

        if (DateEditorSkin.IMAGE_YEAR_BG != null) {
            g.drawImage(DateEditorSkin.IMAGE_YEAR_BG, 0, 0,
                        Graphics.LEFT | Graphics.TOP);
            int w = DateEditorSkin.IMAGE_YEAR_BG.getWidth();
            int h = DateEditorSkin.IMAGE_YEAR_BG.getHeight();
            if (focusOn == YEAR_POPUP) {
                g.setColor(DateEditorSkin.COLOR_TRAVERSE_IND);
                g.drawRect(-2, -2, w + 3, h + 3);
            }
        }

        g.setFont(DateEditorSkin.FONT_POPUPS);
        g.setColor(0);
        g.drawString(Integer.toString(editDate.get(Calendar.YEAR)),
                     4, 0, Graphics.LEFT | Graphics.TOP);
    
private intgetDateAtPointerPosition(int x, int y)
Helper function to determine the date index at the x,y position

param
x pointer x coordinate
param
y pointer y coordinate
return
0 (invalid value) or 1 - lastDay(valid value) depends on the pointer position.

        int dateAt = 0;
        int transX = x - calendar_bounds[X];
        int transY = y - calendar_bounds[Y];
        int o = DateEditorSkin.IMAGE_CAL_BG.getWidth() / 7;
        int rowH = 11;
        //variable o, rowH, h is same as in paintCalendar()
        int h = DateEditorSkin.IMAGE_DATES.getHeight() / 31;

        if (transX >= 0 && transX <= calendar_bounds[W] &&
            transY >= 0 && transY <= calendar_bounds[H] &&
            transY >= h + 3) {
            int row = (transY - h - 3)  / rowH;
            int col = (transX - 1) / o;
            int row_Day1 = 0;
            int col_Day1 = dayOffset -1; //index from 0

            if (row != row_Day1 || col >= col_Day1) {
                //index from 1
                int dateAtPointer = (row - row_Day1) * 7 + (col - col_Day1) + 1;
                if (dateAtPointer <= lastDay) {
                    dateAt = dateAtPointer;
                }
            }
        }
        return dateAt;
    
voidhideAllPopups()
Hide all sub-popups triggered by this date editor.

        if (monthPopup != null) monthPopup.hide();
        if (yearPopup != null) yearPopup.hide();
        if (hoursPopup != null) hoursPopup.hide();
        if (minutesPopup != null) minutesPopup.hide();
        popUpOpen = false;
    
public voidinit()
Initialize Date editor

        mode = lf.df.mode;
        initialized = lf.df.initialized;
        editDate = Calendar.getInstance();
        Date date = lf.df.getDate();
        if (date != null) {
            editDate.setTime(date);
        }

        selectedDate = hilightedDate = editDate.get(Calendar.DATE);

        if (editDate.get(Calendar.AM_PM) == Calendar.AM) {
            amSelected = true;
            amHilighted = true;
        }

        switch (mode) {
            case DateField.DATE:
                focusOn = MONTH_POPUP;
                populateDateComponents();
                break;
            case DateField.TIME:
                focusOn = HOURS_POPUP;
                timeComponentsOffset = 0;
                populateTimeComponents();
                break;
            case DateField.DATE_TIME:
                focusOn = MONTH_POPUP;
                timeComponentsOffset = 98;
                populateDateComponents();
                populateTimeComponents();
                break;
            default:
                Logging.report(Logging.ERROR, LogChannels.LC_HIGHUI,
                               "DateEditor constructor, mode=" +mode);
                break;
        }

        // initialize the bounds(used for pointer input) with invariant
        // relative coordinate of the uppper left corner and with invalid
        // width, height values dependent on skin images
        month_bounds = new int[] {
            (mode == DateField.DATE)? 10: 4,
            5, 0, 0
        };
        year_bounds = new int[] {
            month_bounds[X] + 45,
            month_bounds[Y],
            0, 0
        };
        hours_bounds = new int[] {
            timeComponentsOffset + ((mode == DateField.TIME)? 17: 0),
            ((mode == DateField.TIME)? 10: 5),
            0, 0
        };
        minutes_bounds = new int[] {
            hours_bounds[X] + 34,
            hours_bounds[Y],
            0, 0
        };
        calendar_bounds = new int[] {
            (mode == DateField.DATE)? 10: 4,
            29, 0, 0
        };
        ampm_bounds = new int[] {
            timeComponentsOffset + ((mode == DateField.TIME)? 15: 0),
            29, 0, 0 };
        
        setCommands(commands);
        setCommandListener(this);
        sizeChanged = true;
        isIitialized = true;
    
public booleanisPopupOpen()
Return Popup layer flag

return
true if popup Layer is shown

        return popUpOpen;
    
public booleanisSizeChanged()
Return sizeChanged flag

return
true if size change iccurs

        return sizeChanged;
    
private intitemIndexAtPointerPosition(int x, int y)
Helper function to determine the focusable area Index at the x,y position

param
x x pointer coordinate
param
y y pointer coordinate
return
focusable area index, can be PRESS_OUT_OF_BOUNDS, 0, MONTH_POPUP, YEAR_POPUP, HOURS_POPUP, MINUTES_POPUP, CALENDAR, or AM_PM, depends on the pointer position.

        int area = PRESS_OUT_OF_BOUNDS;
        if (containsPoint(x + this.bounds[X], y + this.bounds[Y])) {
            if (x >= month_bounds[X] &&
                x < month_bounds[X] + month_bounds[W] &&
                y >= month_bounds[Y] &&
                y < month_bounds[Y] + month_bounds[H]) {
                area = MONTH_POPUP;
            } else if (x >= year_bounds[X] &&
                       x < year_bounds[X] + year_bounds[W] &&
                       y >= year_bounds[Y] &&
                       y < year_bounds[Y] + year_bounds[H]) {
                area = YEAR_POPUP;
            } else if (x >= hours_bounds[X] &&
                       x < hours_bounds[X] + hours_bounds[W] &&
                       y >= hours_bounds[Y] &&
                       y < hours_bounds[Y] + hours_bounds[H]) {
                area = HOURS_POPUP;
            } else if (x > minutes_bounds[X] &&
                       x < minutes_bounds[X] + minutes_bounds[W] &&
                       y >= minutes_bounds[Y] &&
                       y < minutes_bounds[Y] + minutes_bounds[H]) {
                area = MINUTES_POPUP;
            } else if (x >= calendar_bounds[X] &&
                       x < calendar_bounds[X] + calendar_bounds[W] &&
                       y >= calendar_bounds[Y] &&
                       y < calendar_bounds[Y] + calendar_bounds[H]) {
                area = CALENDAR;
            } else if (x >= ampm_bounds[X] &&
                       x < ampm_bounds[X] + ampm_bounds[W] &&
                       y >= ampm_bounds[Y] &&
                       y < ampm_bounds[Y] + ampm_bounds[H]) {
                area = AM_PM;
            } else {
                area = 0;
            }
        }
        return area; // Value 0: invaliad but inside one focusable area
    
public booleankeyInput(int type, int code)
Handles key input to the popup layer.

param
type the type of this key event (pressed, released)
param
code the code of this key event
return
true always, since popupLayers swallow all key events

        if (type == EventConstants.PRESSED && lf != null) {
            if (code == Constants.KEYCODE_SELECT) {
                selectFired();
                requestRepaint();
            } else {
                traverseEditor(code);
                requestRepaint();
            }
        }
        // PopupLayers always swallow all key events
        return (code != EventConstants.SOFT_BUTTON1 &&
                code != EventConstants.SOFT_BUTTON2);
    
protected voidpaintAmPm(Graphics g)
Paint the am/pm indicators.

param
g The graphics context to paint to

        int clockStartX, clockStartY;

        if (!lf.CLOCK_USES_AM_PM) {
            clockStartY = 9;
        } else {
            // paint AM
            if (DateEditorSkin.IMAGE_RADIO != null) {
                g.drawImage((amSelected) ?
                            DateEditorSkin.IMAGE_RADIO[1] :
                            DateEditorSkin.IMAGE_RADIO[0],
                            0, 0, Graphics.LEFT | Graphics.TOP);

                if ((focusOn == AM_PM) && (amHilighted)) {
                    g.setColor(DateEditorSkin.COLOR_TRAVERSE_IND);
                    g.drawRect(0, 0,
                               DateEditorSkin.IMAGE_RADIO[0].getWidth(),
                               DateEditorSkin.IMAGE_RADIO[0].getHeight());
                    g.setColor(0);
                }

                if (DateEditorSkin.IMAGE_AMPM != null) {
                    int w = DateEditorSkin.IMAGE_AMPM.getWidth() / 2;
                    g.drawRegion(DateEditorSkin.IMAGE_AMPM,
                                 0, 0,
                                 w, DateEditorSkin.IMAGE_AMPM.getHeight(),
                                 Sprite.TRANS_NONE,
                                 DateEditorSkin.IMAGE_RADIO[0].getWidth(),
                                 (DateEditorSkin.IMAGE_RADIO[0].getHeight()/2),
                                 Graphics.VCENTER | Graphics.LEFT);
                }
                ampm_bounds[W] = 35 *2;
                ampm_bounds[H] = DateEditorSkin.IMAGE_RADIO[0].getHeight();
            }

            g.translate(35, 0);
            // paint PM
            if (DateEditorSkin.IMAGE_RADIO != null) {
                g.drawImage((amSelected) ?
                            DateEditorSkin.IMAGE_RADIO[0] :
                            DateEditorSkin.IMAGE_RADIO[1],
                            0, 0, Graphics.LEFT | Graphics.TOP);

                if ((focusOn == AM_PM) && (!amHilighted)) {
                    g.setColor(DateEditorSkin.COLOR_TRAVERSE_IND);
                    g.drawRect(0, 0,
                               DateEditorSkin.IMAGE_RADIO[0].getWidth(),
                               DateEditorSkin.IMAGE_RADIO[0].getHeight());
                    g.setColor(0);
                }

                if (DateEditorSkin.IMAGE_AMPM != null) {
                    int w = DateEditorSkin.IMAGE_AMPM.getWidth() / 2;
                    g.drawRegion(DateEditorSkin.IMAGE_AMPM,
                                 (DateEditorSkin.IMAGE_AMPM.getWidth() / 2), 0,
                                 w, DateEditorSkin.IMAGE_AMPM.getHeight(),
                                 Sprite.TRANS_NONE,
                                 DateEditorSkin.IMAGE_RADIO[0].getWidth(),
                                 (DateEditorSkin.IMAGE_RADIO[0].getHeight()/2),
                                 Graphics.VCENTER | Graphics.LEFT);
                }
            }
            g.translate(-35, 0);
            clockStartY = 22;
        }

        clockStartX = (mode == DateField.TIME) ? 10 : 6;
        g.translate(clockStartX, clockStartY);
        if (DateEditorSkin.IMAGE_CLOCK_BG != null) {
            g.drawImage(DateEditorSkin.IMAGE_CLOCK_BG, 0, 0,
                        Graphics.LEFT | Graphics.TOP);
            paintTime(g);
        }
        g.translate(-clockStartX, -clockStartY);
    
public voidpaintBackground(Graphics g)
Paints the background of the date editor layer.

param
g The graphics context to paint to

        super.paintBackground(g);
        if (DateEditorSkin.IMAGE_BG == null) {
            g.setColor(DateEditorSkin.COLOR_BORDER);
            g.drawRect(0, 0, bounds[W] - 1, bounds[H] - 1);
            g.setColor(0);
        }
    
public voidpaintBody(Graphics g)
Paints the body (open state) of the date editor layer.

param
g The graphics context to paint to

        setDayOffset();
        lastDay = daysInMonth(editDate.get(Calendar.MONTH),
            editDate.get(Calendar.YEAR));

        nextX = 0;
        nextY = 0;

        switch (mode) {
            case DateField.DATE:
                drawDateComponents(g);
                break;
            case DateField.TIME:
                drawTimeComponents(g);
                break;
            case DateField.DATE_TIME:
                drawDateComponents(g);
                drawTimeComponents(g);
                break;
            default:
                Logging.report(Logging.ERROR, LogChannels.LC_HIGHUI,
                               "DateEditor.paintBody(), mode=" +mode);
                break;
        }
    
protected voidpaintCalendar(Graphics g)
Paint the Calendar.

param
g The Graphics context to paint to

        if (DateEditorSkin.IMAGE_CAL_BG == null ||
            DateEditorSkin.IMAGE_DATES == null)
        {
            return;
        }

        g.drawImage(DateEditorSkin.IMAGE_CAL_BG, 0, 0,
                    Graphics.LEFT | Graphics.TOP);

        if (DateEditorSkin.IMAGE_DATES == null) {
            return;
        }
        g.translate(2, 0);

        int o = DateEditorSkin.IMAGE_CAL_BG.getWidth() / 7;
        int rowH = 11;
        int h = DateEditorSkin.IMAGE_DATES.getHeight() / 31;
        int w = DateEditorSkin.IMAGE_DATES.getWidth();

        // draw calendar
        int x = 5 + ((dayOffset - 1) * o);
        int y = h + 4;

        if (hilightedDate > lastDay) {
            hilightedDate = lastDay;
        }

        calendarTopLimit = y;
        int lastCol = 7 * o;
        for (int i = 1; i <= lastDay; ++i) {
            // draw focus highlight
            if (i == hilightedDate) {
                dateHilightX = x;
                dateHilightY = y;
                g.setColor(
                    (focusOn == CALENDAR) ?
                        DateEditorSkin.COLOR_TRAVERSE_IND:
                        0);
                g.drawRect(x - 6, y - 1, w, h + 1);
            }

            g.drawRegion(DateEditorSkin.IMAGE_DATES,
                         0, ((i - 1) * h),
                         w, h,
                         Sprite.TRANS_NONE,
                         x, y,
                         Graphics.TOP | Graphics.HCENTER);

            x += o;
            if (x > lastCol) {
                calendarRightLimit = x - o;
                x = 5;
                y += rowH;
            }
        }
        calendarBottomLimit = y;
        g.translate(-2, 0);

        calendar_bounds[W]= DateEditorSkin.IMAGE_CAL_BG.getWidth();
        //add rowH as the date may be written under the calendar bg.
        calendar_bounds[H]= DateEditorSkin.IMAGE_CAL_BG.getHeight() + rowH;
    
protected voidpaintTime(Graphics g)
Paint the clock.

param
g The Graphics to paint to

        int hour   = editDate.get(Calendar.HOUR) % 12;
        int minute = editDate.get(Calendar.MINUTE);

        int minuteAngle = 90 - (minute * 6);
        int hourAngle   = 90 - (hour * 30 + (minute / 2));

        int anchorX = DateEditorSkin.IMAGE_CLOCK_BG.getWidth() / 2;
        int anchorY = DateEditorSkin.IMAGE_CLOCK_BG.getHeight() / 2;
        g.translate(anchorX, anchorY);

        g.setColor(DateEditorSkin.COLOR_CLOCKHAND_DK);
        int x = (cos(hourAngle)*anchorX / 2) >> 16;
        int y = -(sin(hourAngle)*anchorX / 2) >> 16;
        g.drawLine(0, 0, x, y);
        g.drawLine(0, 1, x, y + 1);
        g.setColor(DateEditorSkin.COLOR_CLOCKHAND_LT);
        g.drawLine(0, 2, x, y + 2);

        g.setColor(DateEditorSkin.COLOR_CLOCKHAND_DK);
        x = (cos(minuteAngle)*(anchorX - 10)) >> 16;
        y = -(sin(minuteAngle)*(anchorX - 10)) >> 16;
        g.drawLine(0, 0, x, y);
        g.drawLine(0, 1, x, y + 1);
        g.setColor(DateEditorSkin.COLOR_CLOCKHAND_LT);
        g.drawLine(0, 2, x, y + 2);

        g.translate(-anchorX, -anchorY);
    
public booleanpointerInput(int type, int x, int y)
Handles pointer input to the popup layer.

param
type the type of this key event (pressed, released)
param
x x coordinate of pointer
param
y y coordinate of pointer
return
true always, since popupLayers swallow all pointer events

        boolean consume = true;
        switch (type) {
        case EventConstants.PRESSED:
            itemIndexWhenPressed = itemIndexAtPointerPosition(x,y);
            switch (itemIndexWhenPressed) {
            case AM_PM:
                amHilighted = ( x - ampm_bounds[X] < 35);
                break;
            case CALENDAR:
                pressedDate = getDateAtPointerPosition(x, y);
                if (pressedDate > 0) {
                    hilightedDate = pressedDate;
                }
                break;
            case PRESS_OUT_OF_BOUNDS:
                commandAction(cancel, lf.df.owner);
                consume = false;
                break;
            }
            if (itemIndexWhenPressed > 0 && focusOn != itemIndexWhenPressed) {
                DEPopupLayer popup = null;
                switch (focusOn) {
                case MONTH_POPUP:
                    popup = monthPopup;
                    break;
                case YEAR_POPUP:
                    popup = yearPopup;
                    break;
                case HOURS_POPUP:
                    popup = hoursPopup;
                    break;
                case MINUTES_POPUP:
                    popup = minutesPopup;
                    break;
                default:
                    break;
                }
                if (popup != null && popup.open) {
                    popup.hide();
                }

                focusOn = itemIndexWhenPressed;
                requestRepaint();
            }
            break;
        case  EventConstants.RELEASED:
            int itemIndexWhenReleased = itemIndexAtPointerPosition(x,y);
            if (itemIndexWhenPressed == itemIndexWhenReleased) {
                if (itemIndexWhenPressed > 0) {
                    if ( (itemIndexWhenPressed == AM_PM &&
                          amHilighted == (x - ampm_bounds[X] < 35)) ||
                         (itemIndexWhenPressed == CALENDAR &&
                          pressedDate == getDateAtPointerPosition(x, y) &&
                          pressedDate > 0) ||
                         (itemIndexWhenPressed != AM_PM &&
                          itemIndexWhenPressed != CALENDAR) ) {
                        selectFired();
                        if (itemIndexWhenPressed > 0) {
                            focusOn = itemIndexWhenPressed;
                            requestRepaint();
                        }
                    }
                }
            }
            if (itemIndexWhenReleased == PRESS_OUT_OF_BOUNDS) {
                consume = false;
            }

            itemIndexWhenPressed = PRESS_OUT_OF_BOUNDS; // remember to reset the variables
            pressedDate = 0;
            break;
        }
        return consume;
    
protected voidpopulateDateComponents()
Populate the date components.

        // populate MONTHS[]
        MONTHS = new String[DateFieldLFImpl.MONTH_NAMES.length];
        for (int i = 0; i < DateFieldLFImpl.MONTH_NAMES.length; i++) {
            MONTHS[i] = DateFieldLFImpl.MONTH_NAMES[i].substring(0, 3);
        }
        monthPopup = new DEPopupLayer(this, MONTHS,
                                      editDate.get(Calendar.MONTH), true);

        // populate YEARS[]
        int selectedIndex =
            createYearStrings(editDate.get(Calendar.YEAR) - 10);
        yearPopup = new DEPopupLayer(this, YEARS, selectedIndex, false);
    
protected voidpopulateTimeComponents()
Populate the time components.

        int selectedIndex = 0;

        // populate HOURS[]
        String[] hours;
        if (lf.CLOCK_USES_AM_PM) {
            HOURS = new int[12];
            hours = new String[12];

            selectedIndex = editDate.get(Calendar.HOUR) - 1;
            if (selectedIndex < 0) {
                selectedIndex = 11;
            }

            for (int i = 0; i < 12; i++) {
                HOURS[i] = i + 1;
                hours[i] = Integer.toString(i + 1);
            }
        } else {
            HOURS = new int[24];
            hours = new String[24];
            selectedIndex = editDate.get(Calendar.HOUR_OF_DAY);
            for (int i = 0; i < 24; i++) {
                HOURS[i] = i;
                hours[i] = Integer.toString(i);
            }
        }
        hoursPopup = new DEPopupLayer(this, hours, selectedIndex, true);

        // populate MINUTES[]
        selectedIndex = 0;
        MINUTES = new int[60];
        String[] minutes = new String[60];
        int minute = editDate.get(Calendar.MINUTE);
        for (int i = 0; i < 60; i++) {
            if (i == minute) {
                selectedIndex = i;
            }
            MINUTES[i] = i;
            minutes[i] = Integer.toString(i);
        }
        minutesPopup = new DEPopupLayer(this, minutes, selectedIndex, true);
    
protected booleanselectFired()
Called when select key is fired, to take further action on it, based on where the focus is on the date editor.

return
true if key was handled, false otherwise

        boolean done = false;
        ScreenLFImpl sLF = (ScreenLFImpl)lf.df.owner.getLF();
        switch (focusOn) {

            case MONTH_POPUP:
                if (!monthPopup.open) {
                    setMonthPopupLocation();
                    monthPopup.show(sLF);
                    done = true;
                } else {
                    int month = monthPopup.getSelectedIndex();
                    lastDay = daysInMonth(month, editDate.get(Calendar.YEAR));
                    if (selectedDate > lastDay) {
                        selectedDate = lastDay;
                        editDate.set(Calendar.DATE,
                        selectedDate);
                    }
                    monthPopup.setSelectedIndex(month);
                    editDate.set(Calendar.MONTH, month);
                    monthPopup.hide();
                }
                break;
            case YEAR_POPUP:
                if (!yearPopup.open) {
                    setYearPopupLocation();
                    yearPopup.show(sLF);
                    done = true;
                } else {
                    int selectedIndex = yearPopup.getSelectedIndex();
                    if (selectedIndex == 0) {
                        createYearStrings(Integer.parseInt(YEARS[1]) - 19);
                        yearPopup.setContent(YEARS, 20);
                        yearPopup.requestRepaint();

                    } else if (selectedIndex == 21) {
                        createYearStrings(Integer.parseInt(YEARS[20]));
                        yearPopup.setContent(YEARS, 1);
                        yearPopup.requestRepaint();

                    } else {
                        int year = Integer.parseInt(YEARS[selectedIndex]);
                        lastDay = daysInMonth(
                                     editDate.get(Calendar.MONTH), year);
                        if (selectedDate > lastDay) {
                            selectedDate = lastDay;
                            editDate.set(Calendar.DATE, selectedDate);
                        }

                        yearPopup.setSelectedIndex(selectedIndex);
                        editDate.set(Calendar.YEAR, year);
                        yearPopup.hide();
                    }
                }
                break;
            case HOURS_POPUP:
                if (!hoursPopup.open) {
                    setHoursPopupLocation();
                    hoursPopup.show(sLF);
                    done = true;
                } else {
                    int selId = hoursPopup.getSelectedIndex();
                    hoursPopup.setSelectedIndex(selId);
                    int hour = HOURS[selId];
                    if ((lf.CLOCK_USES_AM_PM) && (!amSelected)) {
                        hour += 12;
                    }
                    editDate.set(Calendar.HOUR_OF_DAY, hour);
                    hoursPopup.hide();
                }
                break;
            case MINUTES_POPUP:
                if (!minutesPopup.open) {
                    setMinutesPopupLocation();
                    minutesPopup.show(sLF);
                    done = true;
                } else {
                    int selId = minutesPopup.getSelectedIndex();
                    editDate.set(Calendar.MINUTE,
                                 MINUTES[selId]);
                    minutesPopup.setSelectedIndex(selId);
                    minutesPopup.hide();
                }
                break;
            case CALENDAR:
                selectedDate = hilightedDate;
                editDate.set(Calendar.DATE, selectedDate);
                focusOn = MONTH_POPUP;
                done = true;
                break;
            case AM_PM:
                amSelected = amHilighted;
                int hour = hoursPopup.getSelectedIndex() + 1;
                if (hour == 12) {
                    if (amSelected) {
                        hour = 0;
                    }
                } else if (!amSelected) {
                    hour += 12;
                }

                editDate.set(Calendar.HOUR_OF_DAY, hour);
                done = true;
                break;
            default:
                lf.uCallKeyPressed(Constants.KEYCODE_SELECT);
                done = true;
                break;
        }
        return done;
    
protected voidsetDayOffset()
Set the day offset.

        Date save = editDate.getTime();
        editDate.set(Calendar.DATE, 1);
        dayOffset = editDate.get(Calendar.DAY_OF_WEEK);

        if (Resource.getFirstDayOfWeek() != Calendar.SUNDAY) {
            dayOffset = (dayOffset == 1) ? 7 : (dayOffset - 1);
        }
        editDate.setTime(save);
    
protected voidsetHoursPopupLocation()
Set hours popup location using upper left corner coordinate of the DateEditor layer and relative coordinates of the popup anchor.

        setPopupLocation(hoursPopup,
            DateEditorSkin.IMAGE_TIME_BG,
            hours_bounds);
    
public voidsetLocation(int x, int y)
Sets the location of the popup layer.

param
x the x-coordinate of the popup layer location
param
y the y-coordinate of the popup layer location

        if (!isIitialized) {
            init();
        }
        bounds[X] = x;
        bounds[Y] = y;
        bounds[H] = DateEditorSkin.HEIGHT;

        switch (mode) {
            case DateField.DATE:
                bounds[W] = DateEditorSkin.WIDTH_DATE;
                break;
            case DateField.TIME:
                bounds[W] = DateEditorSkin.WIDTH_TIME;
                break;
            case DateField.DATE_TIME:
                bounds[W] = DateEditorSkin.WIDTH_DATETIME;
                break;
            default:
                Logging.report(Logging.ERROR, LogChannels.LC_HIGHUI,
                               "DateEditor.setLocation(), mode=" +mode);
                break;
        }

        if (bounds[X] + bounds[W] > ScreenSkin.WIDTH) {
            bounds[X] = ScreenSkin.WIDTH - bounds[W];
        } else if (bounds[X] < 0) {
            bounds[X] = 0;
        }

        if (sizeChanged) {
            callSizeChanged();
        }
        sizeChanged = false;
    
protected voidsetMinutesPopupLocation()
Set minutes popup location using upper left corner coordinate of the DateEditor layer and relative coordinates of the popup anchor.

        setPopupLocation(minutesPopup,
            DateEditorSkin.IMAGE_TIME_BG,
            minutes_bounds);
    
protected voidsetMonthPopupLocation()
Set month popup location using upper left corner coordinate of the DateEditor layer and relative coordinates of the popup anchor.

        setPopupLocation(monthPopup,
            DateEditorSkin.IMAGE_MONTH_BG,
            month_bounds);
    
protected voidsetPopupLocation(DEPopupLayer popup, Image image, int[] bounds)
Set popup location and bounds

param
popup popup to relocate
param
image background image of popup
param
bounds relative bounds of the popup layer

        
        int x = this.bounds[X] + bounds[X];
        int y = this.bounds[Y] + bounds[Y];
        int w = image.getWidth();
        int h = image.getHeight();
        popup.setElementSize(
            w - 4, DateEditorSkin.FONT_POPUPS.getHeight());
        popup.setBounds(x, y + h, w, DateEditorSkin.HEIGHT_POPUPS);
        popup.updateScrollIndicator();
        bounds[W]= w;
        bounds[H]= h;
    
public voidsetPopupOpen(boolean popUpOpen)
Set popup Layer flag

param
popUpOpen true if popup Layer is shown

        this.popUpOpen = popUpOpen;
    
public voidsetSizeChanged()
Set sizeChanged flag to true

        this.sizeChanged = true;
    
protected voidsetYearPopupLocation()
Set year popup location using upper left corner coordinate of the DateEditor layer and relative coordinates of the popup anchor.

        setPopupLocation(yearPopup,
            DateEditorSkin.IMAGE_YEAR_BG,
            year_bounds);
    
voidshow()
Show the date editor popup.


        // refresh the edit date to value stored in DateField each time
        editDate = Calendar.getInstance();
        Date date = lf.df.getDate();
        if (date != null) {
            editDate.setTime(date);
        }

        selectedDate = hilightedDate = editDate.get(Calendar.DATE);

        amSelected = amHilighted = false;
        if (editDate.get(Calendar.AM_PM) == Calendar.AM) {
            amSelected = true;
            amHilighted = true;
        }

        switch (mode) {
            case DateField.DATE:
            case DateField.DATE_TIME:
                focusOn = MONTH_POPUP;
                break;
            case DateField.TIME:
                focusOn = HOURS_POPUP;
                break;
            default:
                Logging.report(Logging.ERROR, LogChannels.LC_HIGHUI,
                               "DateEditor.show(), mode=" +mode);
                break;
        }

        popUpOpen = true;

        ScreenLFImpl sLF = (ScreenLFImpl)lf.df.owner.getLF();
        sLF.lGetCurrentDisplay().showPopup(this);
    
protected static intsin(int angle)
Utility method to return the sin of an angle.

param
angle The angle to compute the sin of
return
int The sin of the angle

        return cos(90 - angle);
    
protected booleantraverseAmPm(int code)
Handle internal traversal between the am/pm indicators.

param
code the code of this key event
return
true if internal traversal occurred, false otherwise

        boolean traverse = false;
        switch (code) {
        case Constants.KEYCODE_UP:
            focusOn = HOURS_POPUP;
            traverse = true;
            break;
        case Constants.KEYCODE_LEFT:
            if (amHilighted) {
                focusOn = CALENDAR;
                traverse = true;
            } else {
                amHilighted = true;
                traverse = true;
            }
            break;
        case Constants.KEYCODE_RIGHT:
            if (amHilighted) {
                amHilighted = false;
                traverse = true;
            }
            break;
        default:
            // no-op
            break;
        }
        return traverse;
    
protected booleantraverseCalendar(int code)
Handle internal traversal within the calendar.

param
code the code of this key event
return
true if internal traversal occurred, false otherwise

        boolean traverse = false;

        switch (code) {
        case Constants.KEYCODE_LEFT:
            if (hilightedDate > 1) {
                hilightedDate--;
            }
            traverse = true;
            break;
        case Constants.KEYCODE_RIGHT:
            if ((hilightedDate < lastDay) &&
                (dateHilightX < calendarRightLimit)) {
                hilightedDate++;
                traverse = true;
            }
            break;
        case Constants.KEYCODE_UP:
            if (hilightedDate == 1) {
                break;
            }
            if (hilightedDate > 7) {
                hilightedDate -= 7;
                traverse = true;
            } else if (dateHilightY > calendarTopLimit) {
                hilightedDate = 1;
                traverse = true;
            }
            break;
        case Constants.KEYCODE_DOWN:
            if (hilightedDate == lastDay) {
                break;
            }
            if (hilightedDate <= (lastDay - 7)) {
                hilightedDate += 7;
                traverse = true;
            } else if (dateHilightY < calendarBottomLimit) {
                hilightedDate = lastDay;
                traverse = true;
            }
            break;
         default:
            // no-op
            break;
        }
        return traverse;
    
protected booleantraverseEditor(int code)
Handles internal traversal within the date editor.

param
code the code of this key event
return
true always, since popup layers swallow all events

        // handle internal traversal
        switch (focusOn) {
        case MONTH_POPUP:
            switch (code) {
            case Constants.KEYCODE_DOWN:
                focusOn = CALENDAR;
                break;
            case Constants.KEYCODE_RIGHT:
                focusOn = YEAR_POPUP;
                break;
            default:
                // no-op
                break;
            }
            break;
        case YEAR_POPUP:
           switch (code) {
            case Constants.KEYCODE_DOWN:
                focusOn = CALENDAR;
                break;
            case Constants.KEYCODE_LEFT:
                focusOn = MONTH_POPUP;
                break;
            case Constants.KEYCODE_RIGHT:
                if (mode == DateField.DATE_TIME) {
                    focusOn = HOURS_POPUP;
                }
                break;
            default:
                // no-op
                break;
            }
            break;
        case HOURS_POPUP:
            switch (code) {
            case Constants.KEYCODE_DOWN:
                focusOn = AM_PM;
                break;
            case Constants.KEYCODE_LEFT:
                if (mode == DateField.DATE_TIME) {
                    focusOn = YEAR_POPUP;
                }
                break;
            case Constants.KEYCODE_RIGHT:
                focusOn = MINUTES_POPUP;
                break;
            default:
                // no-op
                break;
            }
            break;
        case MINUTES_POPUP:
            switch (code) {
            case Constants.KEYCODE_DOWN:
                focusOn = AM_PM;
                break;
            case Constants.KEYCODE_LEFT:
                focusOn = HOURS_POPUP;
                break;
            default:
                // no-op
                break;
            }
            break;
        case CALENDAR:
            if (!traverseCalendar(code)) {
                switch (code) {
                case Constants.KEYCODE_RIGHT:
                    if (mode == DateField.DATE_TIME) {
                        focusOn = AM_PM;
                    }
                    break;
                case Constants.KEYCODE_UP:
                    focusOn = MONTH_POPUP;
                    break;
                default:
                    // no-op
                    break;
                }
            }
            break;
        case AM_PM:
            traverseAmPm(code);
            break;
        default:
            Logging.report(Logging.ERROR, LogChannels.LC_HIGHUI,
                           "DateEditor.traverseEditor(), focusOn=" +focusOn);
            break;
        }
        return true;