/*
*
*
* Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*/
package javax.microedition.lcdui;
import java.util.Date;
import java.util.Calendar;
import com.sun.midp.lcdui.*;
import com.sun.midp.i18n.Resource;
import com.sun.midp.i18n.ResourceConstants;
import com.sun.midp.configurator.Constants;
import com.sun.midp.chameleon.skins.DateEditorSkin;
import com.sun.midp.chameleon.skins.ScreenSkin;
import com.sun.midp.chameleon.layers.PopupLayer;
import javax.microedition.lcdui.game.Sprite;
import com.sun.midp.log.Logging;
import com.sun.midp.log.LogChannels;
/**
* A utility class for editing date/time components for a DateField.
*/
class DateEditor extends PopupLayer implements CommandListener {
/**
* Create a new DateEditor layer.
*
* @param lf The DateFieldLFImpl that triggered this date editor
*/
public DateEditor(DateFieldLFImpl lf) {
super(DateEditorSkin.IMAGE_BG, DateEditorSkin.COLOR_BG);
this.lf = lf;
}
/**
* Initialize Date editor
*/
public void init() {
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;
}
/**
* 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
*/
public void setLocation(int x, int y) {
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;
}
/**
* Paints the background of the date editor layer.
*
* @param g The graphics context to paint to
*/
public void paintBackground(Graphics g) {
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);
}
}
/**
* Paints the body (open state) of the date editor layer.
*
* @param g The graphics context to paint to
*/
public void paintBody(Graphics g) {
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;
}
}
/**
* 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
*/
public boolean keyInput(int type, int code) {
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);
}
/**
* 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
*/
public boolean pointerInput(int type, int x, int y) {
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;
}
/**
* 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.
*/
private int getDateAtPointerPosition(int x, int y) {
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;
}
/**
* 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.
*/
private int itemIndexAtPointerPosition(int x, int y) {
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
}
/**
* Handle a command action.
*
* @param cmd The Command to handle
* @param s The Displayable with the Command
*/
public void commandAction(Command cmd, Displayable s) {
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);
}
}
// ********** package private *********** //
/**
* Show the date editor popup.
*/
void show() {
// 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);
}
/**
* Hide all sub-popups triggered by this date editor.
*/
void hideAllPopups() {
if (monthPopup != null) monthPopup.hide();
if (yearPopup != null) yearPopup.hide();
if (hoursPopup != null) hoursPopup.hide();
if (minutesPopup != null) minutesPopup.hide();
popUpOpen = false;
}
// *********** private ************ //
/**
* Populate the date components.
*/
protected void populateDateComponents() {
// 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);
}
/**
* 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
*/
protected int createYearStrings(int startYear) {
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;
}
/**
* Populate the time components.
*/
protected void populateTimeComponents() {
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);
}
/**
* Set popup location and bounds
*
* @param popup popup to relocate
* @param image background image of popup
* @param bounds relative bounds of the popup layer
*/
protected void setPopupLocation(
DEPopupLayer popup, Image image, int []bounds) {
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;
}
/**
* Set month popup location using upper left corner coordinate of the
* DateEditor layer and relative coordinates of the popup anchor.
*/
protected void setMonthPopupLocation() {
setPopupLocation(monthPopup,
DateEditorSkin.IMAGE_MONTH_BG,
month_bounds);
}
/**
* Set year popup location using upper left corner coordinate of the
* DateEditor layer and relative coordinates of the popup anchor.
*/
protected void setYearPopupLocation() {
setPopupLocation(yearPopup,
DateEditorSkin.IMAGE_YEAR_BG,
year_bounds);
}
/**
* Set hours popup location using upper left corner coordinate of the
* DateEditor layer and relative coordinates of the popup anchor.
*/
protected void setHoursPopupLocation() {
setPopupLocation(hoursPopup,
DateEditorSkin.IMAGE_TIME_BG,
hours_bounds);
}
/**
* Set minutes popup location using upper left corner coordinate of the
* DateEditor layer and relative coordinates of the popup anchor.
*/
protected void setMinutesPopupLocation() {
setPopupLocation(minutesPopup,
DateEditorSkin.IMAGE_TIME_BG,
minutes_bounds);
}
/**
* Draws month popup content.
* @param g The Graphics object to paint to
*/
protected void drawMonthComponent(Graphics g) {
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);
}
/**
* Draws year popup content.
* @param g The Graphics object to paint to
*/
protected void drawYearComonent(Graphics g) {
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);
}
/**
* Draws hours popup content.
* @param g The Graphics object to paint to
*/
protected void drawHoursComponent(Graphics g) {
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);
}
/**
* Draws minutes popup content.
* @param g The Graphics object to paint to
*/
protected void drawMinutesComponent(Graphics g) {
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);
}
/**
* Draw the date components.
* @param g The Graphics object to paint to
*/
protected void drawDateComponents(Graphics g) {
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]);
}
/**
* Draw the time components.
* @param g The Graphics object to paint to
*/
protected void drawTimeComponents(Graphics g) {
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]);
}
/**
* Paint the Calendar.
* @param g The Graphics context to paint to
*/
protected void paintCalendar(Graphics g) {
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;
}
/**
* Paint the am/pm indicators.
*
* @param g The graphics context to paint to
*/
protected void paintAmPm(Graphics g) {
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);
}
/**
* Paint the clock.
*
* @param g The Graphics to paint to
*/
protected void paintTime(Graphics g) {
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);
}
/**
* 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
*/
protected boolean selectFired() {
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;
}
/**
* Handles internal traversal within the date editor.
*
* @param code the code of this key event
* @return true always, since popup layers swallow all events
*/
protected boolean traverseEditor(int code) {
// 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;
}
/**
* Handle internal traversal between the am/pm indicators.
*
* @param code the code of this key event
* @return true if internal traversal occurred, false otherwise
*/
protected boolean traverseAmPm(int code) {
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;
}
/**
* Handle internal traversal within the calendar.
*
* @param code the code of this key event
* @return true if internal traversal occurred, false otherwise
*/
protected boolean traverseCalendar(int code) {
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;
} // traverseCalendar()
// *************** utility methods *********** //
/**
* 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
*/
protected static int cos(int 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];
}
}
/**
* 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
*/
protected static int sin(int angle) {
return cos(90 - angle);
}
/**
* 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
*/
protected int daysInMonth(int month, int year) {
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;
}
}
/**
* Set the day offset.
*/
protected void setDayOffset() {
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);
}
/**
* Return sizeChanged flag
*
* @return true if size change iccurs
*/
public boolean isSizeChanged() {
return sizeChanged;
}
/** Set sizeChanged flag to true */
public void setSizeChanged() {
this.sizeChanged = true;
}
/**
* Return Popup layer flag
*
* @return true if popup Layer is shown
*/
public boolean isPopupOpen() {
return popUpOpen;
}
/**
* Set popup Layer flag
*
* @param popUpOpen true if popup Layer is shown
*/
public void setPopupOpen(boolean popUpOpen) {
this.popUpOpen = popUpOpen;
}
public void callSizeChanged() {
if (monthPopup != null) { setMonthPopupLocation(); }
if (yearPopup != null) { setYearPopupLocation(); }
if (hoursPopup != null) { setHoursPopupLocation(); }
if (minutesPopup != null) { setMinutesPopupLocation(); }
}
// *********** attributes ************* //
/**
* Table of trigonometric functions, in 16.16 fixed point.
*/
protected static final int TRIG_TABLE[] = {
65535, // cos 0
65525, // cos 1
65495, // cos 2
65445, // cos 3
65375, // cos 4
65285, // cos 5
65175, // cos 6
65046, // cos 7
64897, // cos 8
64728, // cos 9
64539, // cos 10
64330, // cos 11
64102, // cos 12
63855, // cos 13
63588, // cos 14
63301, // cos 15
62996, // cos 16
62671, // cos 17
62327, // cos 18
61964, // cos 19
61582, // cos 20
61182, // cos 21
60762, // cos 22
60325, // cos 23
59869, // cos 24
59394, // cos 25
58902, // cos 26
58392, // cos 27
57863, // cos 28
57318, // cos 29
56754, // cos 30
56174, // cos 31
55576, // cos 32
54962, // cos 33
54330, // cos 34
53683, // cos 35
53018, // cos 36
52338, // cos 37
51642, // cos 38
50930, // cos 39
50202, // cos 40
49459, // cos 41
48701, // cos 42
47929, // cos 43
47141, // cos 44
46340, // cos 45
45524, // cos 46
44694, // cos 47
43851, // cos 48
42994, // cos 49
42125, // cos 50
41242, // cos 51
40347, // cos 52
39439, // cos 53
38520, // cos 54
37589, // cos 55
36646, // cos 56
35692, // cos 57
34728, // cos 58
33753, // cos 59
32767, // cos 60
31771, // cos 61
30766, // cos 62
29752, // cos 63
28728, // cos 64
27696, // cos 65
26655, // cos 66
25606, // cos 67
24549, // cos 68
23485, // cos 69
22414, // cos 70
21336, // cos 71
20251, // cos 72
19160, // cos 73
18063, // cos 74
16961, // cos 75
15854, // cos 76
14742, // cos 77
13625, // cos 78
12504, // cos 79
11380, // cos 80
10251, // cos 81
9120, // cos 82
7986, // cos 83
6850, // cos 84
5711, // cos 85
4571, // cos 86
3429, // cos 87
2287, // cos 88
1143, // cos 89
0 // cos 90
};
/**
* Constant indicating the month popup, used in the process of current
* focus tracking inside the date editor.
*/
protected static final int MONTH_POPUP = 1;
/**
* Constant indicating the year popup, used in the process of current
* focus tracking inside the date editor.
*/
protected static final int YEAR_POPUP = 2;
/**
* Constant indicating the hour popup, used in the process of current
* focus tracking inside the date editor.
*/
protected static final int HOURS_POPUP = 3;
/**
* Constant indicating the minutes popup, used in the process of current
* focus tracking inside the date editor.
*/
protected static final int MINUTES_POPUP = 4;
/**
* Constant indicating the calendar, used in the process of current
* focus tracking inside the date editor.
*/
protected static final int CALENDAR = 5;
/**
* Constant indicating the am/pm indicators, used in the process of
* current focus tracking inside the date editor.
*/
protected static final int AM_PM = 6;
/**
* Static array holding the localized equivalent of month names.
*/
protected static String[] MONTHS;
/**
* Static array holding the year values.
*/
protected static String[] YEARS;
/**
* Static array holding the hour values.
*/
protected static int[] HOURS;
/**
* Static array holding the minute values.
*/
protected static int[] MINUTES;
/**
* The DateFieldLFImpl that triggered this date editor.
*/
protected DateFieldLFImpl lf;
/**
* The date currently being edited.
*/
protected Calendar editDate;
/**
* The mode of the date field, that triggered this date editor.
*/
protected int mode;
/**
* Whether date field that triggered this date editor was initialized
* or not.
*/
protected boolean initialized = false;
/**
* Special command to cancel any changes and close the date editor
* without any impact on the datefield that triggered this editor.
*/
protected Command cancel =
new Command(Resource.getString(ResourceConstants.CANCEL),
Command.CANCEL, 0);
/**
* Special command to set/save the changes done in the editor into the
* datefield that triggered this editor and close the editor.
*/
protected Command set =
new Command(Resource.getString(ResourceConstants.SET),
Command.OK, 1);
/**
* The command array that holds both the commands associated with
* the date editor.
*/
protected Command[] commands = {set, cancel};
/**
* The location x-coordinate, used to calculate where to draw
* the next component.
*/
protected int nextX = 0;
/**
* The location y-coordinate, used to calculate where to draw
* the next component.
*/
protected int nextY = 0;
/** The last day of the month. */
protected int lastDay;
/** The day offset. */
protected int dayOffset;
/** The sub-popup layer used to select month value. */
protected DEPopupLayer monthPopup;
/** The sub-popup layer used to select year value. */
protected DEPopupLayer yearPopup;
/** The sub-popup layer used to select hour value. */
protected DEPopupLayer hoursPopup;
/** The sub-popup layer used to select minutes value. */
protected DEPopupLayer minutesPopup;
/** Keeps track of the currently focused item inside the date editor. */
protected int focusOn;
/**
* Indicates whether am or pm is currently selected. True indicates "am"
* is selected and false indicates "pm" is selected.
*/
protected boolean amSelected = false;
/**
* Indicates whether am or pm is currently highlighted.
* True indicates "am" is highlighted and false indicates "pm" is
* highlighted.
*/
protected boolean amHilighted = false;
/** Currently highlighted date in the calendar. */
protected int hilightedDate = 1;
/** Currently selected date in the calendar. */
protected int selectedDate = 1;
/** Width of a sub-popup in its closed state. */
protected int popupWidth;
/** Height of a sub-popup in its closed state. */
protected int popupHeight;
/** Width of the element within the popup in its closed state. */
protected int elementWidth;
/** Height of the element within the popup in its closed state. */
protected int elementHeight;
/**
* The location offset to draw time components used for DateField.TIME
* and DateField.DATE_TIME modes.
*/
protected int timeComponentsOffset;
/** Indicates calendar's top limit, used in traversal calculations. */
protected int calendarTopLimit;
/** Indicates calendar's bottom limit, used in traversal calculations. */
protected int calendarBottomLimit;
/** Indicates calendar's right limit, used in traversal calculations. */
protected int calendarRightLimit;
/**
* Indicates x co-ordinate of previously highlighted date, used in
* traversal calculations.
*/
protected int dateHilightX;
/**
* Indicates y co-ordinate of previously highlighted date, used in
* traversal calculations.
*/
protected int dateHilightY;
/*pointer pressed outside of the Layer's bounds*/
final int PRESS_OUT_OF_BOUNDS = -1;
/*variable used in pointerInput handling,indicating focused area at pressed */
private int itemIndexWhenPressed = PRESS_OUT_OF_BOUNDS;
/* bounds (in this popupLayer's coordinate space) for each focusable area*/
private int month_bounds[];
private int year_bounds[];
private int hours_bounds[];
private int minutes_bounds[];
private int calendar_bounds[];
private int ampm_bounds[];
/*date index at pressed, may be valid value or invalid value 0*/
private int pressedDate;
/**
* The state of the date editor popup (Default: false = closed).
*/
private boolean popUpOpen;
// True if size of screen was changed
private boolean sizeChanged;
// True if Date Edidor is initialized
private boolean isIitialized;
}
|