// Copyright (c) 2005-2007, Mihai Preda // Licensed under the MIT License import javax.microedition.lcdui.*; import java.util.Calendar; import java.util.Date; class DateCanvas extends ImageCanvas { private static final int schemes[][] = { {0, 0xf0f0f0, 0xffffff, 0xe0e0e0, 0x7070ff, 0xd0c0ff, 0xff8080, 0x800000, 0xff0000, 0xffc0c0, 0x00ff00, 0xa0ff90}, {0xffffff, 0x101010, 0x000000, 0x202020, 0x1010c0, 0x101050, 0xff8080, 0x800000, 0xff0000, 0x501010, 0x00ff00, 0x106010} }; //indexes in colorScheme static final int FG = 0, BG = 1, TITLEBG = 2, WEEKENDBG = 3; static final int N1 = WEEKENDBG + 1, N2 = N1 + 1; static final int M1L = N2 + 1, M1D = M1L + 1, M1 = M1D + 1, M2 = M1 + 1; static final int F1 = M2 + 1, F2 = F1 + 1; int colorSchemeNb = 0; static int colorScheme[] = null; //--- int f1, f2, m1, m2, n1, n2; void setColorScheme(int nb) { colorScheme = schemes[nb]; colorSchemeNb = nb; f1 = colorScheme[F1]; f2 = colorScheme[F2]; m1 = colorScheme[M1]; m2 = colorScheme[M2]; n1 = colorScheme[N1]; n2 = colorScheme[N2]; } String MONTH_NAMES[] = new String[12]; String DAY_INITIAL = null; static final int DAYS_IN_MONTH[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; Calendar cal = Calendar.getInstance(); int year = 0, month = 0, day = 0, nowYear, nowMonth, nowDay; short dateStart = 0; short selectedDate = 0; int startDayOfWeek = 0; int endDayOfWeek = 0; int nDaysInMonth = 0; int nRows = 0; char strDayCnt[] = new char[2]; Font regular, bold; int fontHeight; int cellsH, titleH, headerH; int sx, ex, halfEx; int sy = 0, ey = 0, halfEy = 0; int posInRegW = 0; int profileW = 0; char profileC[] = new char[3]; boolean mondayFirst = true; M period = null; int headerY, titleY, cellsY; static final int N_REGS = 31, N_DAYS = 31, N_DAYS_1 = 32; Region regions[] = new Region[N_REGS]; int outPosInReg[] = new int[3]; char strPosInReg[] = new char[5]; boolean isSmallScreen; int periodLen = 5; int lutealLen = 14; int goal = 0; protected void paint(Graphics g) { g.drawImage(screenImage, 0, 0, Graphics.TOP|Graphics.LEFT); } void switchLanguage() { if (isSmallScreen) { for (int i = 0; i < 12; ++i) { MONTH_NAMES[i] = L.s[57 + i].substring(0, 3); } } else { for (int i = 0; i < 12; ++i) { MONTH_NAMES[i] = L.s[57 + i]; } } DAY_INITIAL = L.s[69]; } void computeSizes() { isSmallScreen = (screenH < 80); int fontSize = ((Font.getFont(0, 0, Font.SIZE_LARGE).getHeight() + 6) * 8 < screenH) ? Font.SIZE_LARGE : (((Font.getFont(0, 0, Font.SIZE_MEDIUM).getHeight() + 3) * 8 < screenH) ? Font.SIZE_MEDIUM : Font.SIZE_SMALL); regular = Font.getFont(0, 0, fontSize); bold = Font.getFont(0, Font.STYLE_BOLD, fontSize); /* int fontSize = (screenH > 200) ? Font.SIZE_MEDIUM : Font.SIZE_SMALL; regular = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, fontSize); bold = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, fontSize); */ fontHeight = regular.getHeight(); sx = screenW / 7; ex = (screenW % 7); halfEx = (ex + 1) / 2; posInRegW = regular.stringWidth(" 00/00"); profileW = regular.stringWidth(" #9"); if (isSmallScreen) { titleH = fontHeight; headerH = 0; titleY = 0; } else { titleH = fontHeight + 2; headerH = fontHeight + 2; titleY = 1; } headerY = titleH; cellsY = headerY + headerH; cellsH = screenH - titleH - headerH; } //public boolean expired; DateCanvas(M period) { this.period = period; for (int i = 0; i < N_REGS; ++i) { regions[i] = new Region(); } strPosInReg[2] = '/'; computeSizes(); screenGraphics.setFont(regular); nowYear = cal.get(Calendar.YEAR); nowMonth = cal.get(Calendar.MONTH); nowDay = cal.get(Calendar.DATE); //expired = (nowYear + 41 == 2048); //expire in 2007 dateStart = (short)(period.getDate(nowYear, nowMonth, 1)); selectedDate = (short)(dateStart + nowDay - 1); } void goToday() { year = nowYear; month = nowMonth; day = nowDay; computeStartDayOfWeek(); } void drawCellsBg() { screenGraphics.setColor(colorScheme[BG]); screenGraphics.fillRect(0, cellsY, screenW, cellsH); repaint(0, cellsY, screenW, cellsH); } void fullRedraw() { if (!isSmallScreen) { drawHead(); } nRows = 0; monthChanged(); drawCursor(true); } void mark() { period.toggle(selectedDate); drawCal(); drawCursor(true); } void prevMonth() { --month; if (month < 0) { --year; month = 11; } int nDays = daysInMonth(month, year); dateStart -= nDays; startDayOfWeek = (startDayOfWeek + 35 - nDays) % 7; monthChanged(); } void nextMonth() { ++month; if (month > 11) { ++year; month = 0; } dateStart += nDaysInMonth; startDayOfWeek = (startDayOfWeek + nDaysInMonth) % 7; monthChanged(); } void computeStartDayOfWeek() { cal.set(Calendar.YEAR, year); cal.set(Calendar.MONTH, month); cal.set(Calendar.DATE, 1); startDayOfWeek = cal.get(Calendar.DAY_OF_WEEK) - (mondayFirst ? 2 : 1); if (startDayOfWeek == -1) { startDayOfWeek = 6; } } void monthChanged() { //computeStartDayOfWeek(); nDaysInMonth = daysInMonth(month, year); endDayOfWeek = (startDayOfWeek + nDaysInMonth + 6) % 7; int oldNRows = nRows; nRows = (nDaysInMonth + startDayOfWeek - 1) / 7 + 1; if (nRows != oldNRows) { drawCellsBg(); } sy = cellsH / nRows; ey = cellsH % nRows; halfEy = ey / 2; drawTitle(); drawCal(); } static char toChar(int i) { return (char)('0' + i); } char title[] = new char[20]; void drawTitle() { String m = MONTH_NAMES[month]; int len = m.length(); m.getChars(0, len, title, 0); title[len++] = ' '; if (!isSmallScreen) { title[len++] = toChar(year / 1000); title[len++] = toChar(year / 100 % 10); } title[len++] = toChar(year / 10 % 10); title[len++] = toChar(year % 10); //int width = regular.stringWidth(title); screenGraphics.setColor(colorScheme[TITLEBG]); screenGraphics.fillRect(0, 0, screenW, titleH - 1); screenGraphics.setColor(colorScheme[FG]); screenGraphics.drawChars(title, 0, len, 1, titleY, Graphics.LEFT|Graphics.TOP); int titleW = regular.charsWidth(title, 0, len); int profile = period.activeProfile; if (profile != 1 && (titleW + profileW + posInRegW < screenW)) { profileC[0] = '#'; profileC[1] = toChar(profile); screenGraphics.drawChars(profileC, 0, 2, screenW - posInRegW, titleY, Graphics.RIGHT|Graphics.TOP); } if (!isSmallScreen) { screenGraphics.drawLine(0, titleH -1, screenW, titleH -1); } repaint(0, 0, screenW, titleH); } void drawHead() { screenGraphics.setColor(colorScheme[BG]); screenGraphics.fillRect(0, headerY, screenW, headerH); screenGraphics.setColor(colorScheme[FG]); int posx = halfEx + sx / 2; int p = mondayFirst ? 0 : 6; for (int i = 0; i < 7; ++i) { if (p == 5 || p == 6) { screenGraphics.setColor(colorScheme[WEEKENDBG]); screenGraphics.fillRect(posx - sx/2, headerY, sx - 1, headerH - 2); screenGraphics.setColor(colorScheme[FG]); } screenGraphics.drawChar(DAY_INITIAL.charAt(p), posx, headerY + 1, Graphics.HCENTER | Graphics.TOP); posx += sx; ++p; if (p == 7) { p = 0; } } screenGraphics.setColor(colorScheme[FG]); screenGraphics.drawLine(0, headerY + headerH - 2, screenW, headerY + headerH - 2); repaint(0, headerY, screenW, headerH); } void incStrDayCnt() { if (strDayCnt[1] == '9') { if (strDayCnt[0] == ' ') { strDayCnt[0] = '1'; } else { ++strDayCnt[0]; } strDayCnt[1] = '0'; } else { ++strDayCnt[1]; } } static final int mix1(int c1, int c2, int p1) { return (c1 * p1 + c2 * (100 - p1)) / 100; } static final int mixedColor(int c1, int c2, int p, int l) { if (l == 0) { return c1; } int r1 = c1 >> 16; int g1 = (c1 >> 8) & 0xff; int b1 = c1 & 0xff; int r2 = c2 >> 16; int g2 = (c2 >> 8) & 0xff; int b2 = c2 & 0xff; int p1 = 100 * (l - p) / l; return (mix1(r1, r2, p1) << 16) | (mix1(g1, g2, p1) << 8) | mix1(b1, b2, p1); } int getOvl(int len) { /* int dist = 14; if (len <= 23) { dist = 13; } else if (len >= 32) { dist = 15; } */ return len - lutealLen; } int startOfRegions; int firstDay, begin; int regPos; int curRegEnd; Region curReg; boolean noinfo; int lastM, ovl, startF, lastF; int infert1, infert2; int getColor(int day) { if (day >= curRegEnd) { firstDay = curRegEnd; ++regPos; curReg = regions[regPos]; curRegEnd = firstDay + curReg.len; noinfo = curReg.noInfo; begin = firstDay - 1; if (!noinfo) { lastM = begin + periodLen; ovl = begin + getOvl(curReg.len); startF = begin + getOvl(curReg.min2) - 2; lastF = begin + getOvl(curReg.max2) + 1; infert1 = begin + getOvl(curReg.min1) - 5; infert2 = begin + getOvl(curReg.max1) + 3; //uncert = false; //curReg.drift() > 16; } } if (day == firstDay && period.isMarked((short)(dateStart + day -1))) { return -1; } if (noinfo) { return colorScheme[TITLEBG]; } if (goal != 2) { //green, fertile if (day >= startF && day < ovl) { return mixedColor(f1, f2, ovl - day, ovl - startF); } if (day >= ovl && day <= lastF) { return mixedColor(f1, f2, day - ovl, lastF - ovl); } } //red, menstruation if (day <= lastM) { return mixedColor(m1, m2, day - begin, lastM - begin); } //blue, infertile if (goal != 1) { if (day < infert1 - 1) { return n1; } if (day >= infert1 - 1 && day <= infert1) { return mixedColor(n1, n2, day - infert1 + 2, 2); } if (day >= infert2 && day <= infert2 + 1) { return mixedColor(n2, n1, day - infert2, 2); } if (day > infert2 + 1) { return n1; } } return colorScheme[TITLEBG]; } void drawOneCell(Graphics g, int col, int posx, int posy, int dayNr, char strDayNr[]) { if (col == -1) { g.setColor(colorScheme[M1L]); int x2 = posx + sx -2; int y2 = posy + sy -2; g.drawLine(posx, y2, x2, y2); g.drawLine(posx+1, y2-1, x2-1, y2-1); g.drawLine(x2, posy, x2, y2); g.drawLine(x2-1, posy + 1, x2-1, y2-1); g.setColor(colorScheme[M1D]); g.drawLine(posx, posy, posx, y2); g.drawLine(posx+1, posy+1, posx+1, y2-1); g.drawLine(posx, posy, x2, posy); g.drawLine(posx+1, posy+1, x2-1, posy+1); g.setColor(colorScheme[M1]); g.fillRect(posx + 2, posy + 2, sx - 5, sy - 5); } else { g.setColor(col); g.fillRect(posx, posy, sx - 1, sy - 1); } //System.out.println(" "+dayNr+' '+nowDay+' '+month+' '+nowMonth+' '+year+' '+nowYear); boolean isToday = ((dayNr == nowDay) && (month == nowMonth) && (year == nowYear)); if (isToday) { g.setFont(bold); } g.setColor(colorScheme[(col == -1)?BG:FG]); int startPos = isSmallScreen ? 1 : 0; g.drawChars(strDayNr, startPos, 2 - startPos, posx + (sx - 1)/2, posy + textPosIncr, Graphics.HCENTER | Graphics.TOP); if (isToday) { g.setFont(regular); } } int textPosIncr; void drawCal() { regPos = -1; startOfRegions = period.getDaysInfo((short)(dateStart), regions); curRegEnd = startOfRegions; int posy = cellsY; textPosIncr = (sy - fontHeight + (isSmallScreen ? 1 : 3)) / 2; screenGraphics.setColor(colorScheme[BG]); boolean started = false; int dayCnt = 1; strDayCnt[0] = ' '; strDayCnt[1] = '0'; int lastColor = -1; int newColor; for (int i = 0; i < nRows; ++i) { int posx = halfEx; for (int j = 0; j < 7; ++j) { if (i == 0 && j == startDayOfWeek) { started = true; } if (started) { incStrDayCnt(); int col = getColor(dayCnt); //System.out.println("" + dayCnt + " " + col); drawOneCell(screenGraphics, col, posx, posy, dayCnt, strDayCnt); if (dayCnt == nDaysInMonth) { screenGraphics.setColor(colorScheme[BG]); started = false; } ++dayCnt; } else { screenGraphics.fillRect(posx, posy, sx - 1, sy - 1); } posx += sx; } if (isSmallScreen) { screenGraphics.setColor(colorScheme[BG]); screenGraphics.drawLine(0, posy -1, screenW, posy -1); } posy += sy; } if (isSmallScreen) { screenGraphics.setColor(colorScheme[BG]); screenGraphics.fillRect(halfEx, screenH - ey -1, screenW - ex, ey + 1); repaint(halfEx, cellsY, screenW - ex, cellsH); } else { repaint(halfEx, cellsY, screenW - ex, cellsH - ey); } } int daysInMonth(int month, int year) { if (month == 1) { //february if ((((year & 3) == 0) && ((year % 100) != 0)) || (year % 400) == 0) { return 29; } return 28; } return DAYS_IN_MONTH[month]; } int getPosInRegion(int day, int out[]) { int endDay = startOfRegions + regions[0].len; int i; for (i = 0; day >= endDay; ++i) { endDay += regions[i + 1].len; } Region r = regions[i]; out[0] = day - (endDay - r.len) + 1; out[1] = r.noInfo ? 0 : r.len; //out[2] = i; return i; } public Region getCurrentRegion() { int regIdx = getPosInRegion(selectedDate - dateStart + 1, outPosInReg); return regions[regIdx]; } void drawCursor(boolean on) { int dist = (selectedDate - dateStart) + startDayOfWeek; int rowPos = dist / 7; int colPos = dist % 7; screenGraphics.setColor(colorScheme[on ? FG : BG]); int x1 = halfEx + colPos * sx - 1; int y1 = cellsY + rowPos * sy - 1; int x2 = x1 + sx + 1; int y2 = y1 + sy + 1; screenGraphics.drawRect(x1, y1, sx, sy); repaint(x1, y1, sx + 1, sy + 1); if (on) { getPosInRegion(selectedDate - dateStart + 1, outPosInReg); screenGraphics.setColor(colorScheme[TITLEBG]); screenGraphics.fillRect(screenW - posInRegW, 0, posInRegW, titleH - 1); if (outPosInReg[1] > 0) { strPosInReg[0] = (char)('0' + outPosInReg[0] / 10); strPosInReg[1] = (char)('0' + outPosInReg[0] % 10); strPosInReg[3] = (char)('0' + outPosInReg[1] / 10); strPosInReg[4] = (char)('0' + outPosInReg[1] % 10); screenGraphics.setColor(colorScheme[FG]); screenGraphics.drawChars(strPosInReg, 0, 5, screenW - 1, titleY, Graphics.RIGHT|Graphics.TOP); } repaint(screenW - posInRegW, 0, posInRegW, titleH - 1); } } void keyAction(int action, boolean moveMonths) { if (action == 0) { return; } if (action == Canvas.FIRE) { period.doMark(); return; } drawCursor(false); switch(action) { case Canvas.UP: if (moveMonths) { selectedDate -= (selectedDate - dateStart) / 7 * 7; } selectedDate -= 7; break; case Canvas.DOWN: if (moveMonths) { selectedDate += (dateStart + nDaysInMonth - 1 - selectedDate) / 7 * 7; } selectedDate += 7; break; case Canvas.LEFT: selectedDate--; /* --colPos; if (colPos < 0) { --rowPos; colPos = 6; } */ break; case Canvas.RIGHT: selectedDate++; /* ++colPos; if (colPos > 6) { ++rowPos; colPos = 0; } */ break; } if (selectedDate < dateStart) { prevMonth(); } else if (selectedDate >= dateStart + nDaysInMonth) { nextMonth(); } screenGraphics.setColor(colorScheme[FG]); drawCursor(true); } int getAction(int keyCode) { try { return getGameAction(keyCode); } catch (IllegalArgumentException e) { return Canvas.FIRE; } } protected void keyPressed(int keyCode) { //System.out.println("key " + keyCode); int action = getAction(keyCode); keyAction(action, false); } protected void keyRepeated(int keyCode) { int action = getAction(keyCode); keyAction(action, true); } }