// 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 { ColorScheme color = new ColorScheme(); String MONTH_NAMES[] = new String[12]; String DAY_INITIAL = null; 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; Font regular, bold; int fontHeight; int cellsH, titleH, headerH; int sx, ex, halfEx; int sy = 0, ey = 0, halfEy = 0; boolean mondayFirst = true; 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]; Profile profile = null; void computeSizes() { 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); fontHeight = regular.getHeight(); sx = screenW / 7; ex = (screenW % 7); halfEx = (ex + 1) / 2; titleH = fontHeight + 2; headerH = fontHeight + 2; titleY = 1; headerY = titleH; cellsY = headerY + headerH; cellsH = screenH - titleH - headerH; } DateCanvas() { for (int i = 0; i < N_REGS; ++i) { regions[i] = new Region(); } computeSizes(); screenGraphics.setFont(regular); nowYear = cal.get(Calendar.YEAR); nowMonth = cal.get(Calendar.MONTH); nowDay = cal.get(Calendar.DATE); dateStart = Cal.getDate(nowYear, nowMonth, 1); selectedDate = (short)(dateStart + nowDay - 1); for (int i = 0; i < 12; ++i) { MONTH_NAMES[i] = L.s[57 + i]; } DAY_INITIAL = L.s[69]; goToday(); } void notifyChanged(Data data) { profile = data.activeProfile(); color.setColorScheme(profile.colorScheme); mondayFirst = data.mondayFirst; fullRedraw(); } void goToday() { year = nowYear; month = nowMonth; day = nowDay; computeStartDayOfWeek(); } void drawCellsBg() { screenGraphics.setColor(color.background); screenGraphics.fillRect(0, cellsY, screenW, cellsH); repaint(0, cellsY, screenW, cellsH); } void fullRedraw() { drawHead(); nRows = 0; monthChanged(); drawCursor(true); } void mark() { AlertType.CONFIRMATION.playSound(M.display); profile.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() { 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; drawCal(); drawTitle(); } void drawTitle() { getPosInRegion(selectedDate - dateStart + 1, outPosInReg); if (outPosInReg[1] > 0) { String posInReg = "" + outPosInReg[0] + '/' + outPosInReg[1]; } String title = MONTH_NAMES[month] + ' ' + year + " " + (outPosInReg[1]<=0 ? "" : (""+outPosInReg[0]+'/'+outPosInReg[1])) + " " + profile.name; screenGraphics.setColor(color.titleBack); screenGraphics.fillRect(0, 0, screenW, titleH - 1); screenGraphics.setColor(color.foreground); screenGraphics.drawString(title, 1, titleY, Graphics.LEFT|Graphics.TOP); screenGraphics.drawLine(0, titleH -1, screenW, titleH -1); repaint(0, 0, screenW, titleH); } void drawHead() { screenGraphics.setColor(color.background); screenGraphics.fillRect(0, headerY, screenW, headerH); screenGraphics.setColor(color.foreground); int posx = halfEx + sx / 2; int p = mondayFirst ? 0 : 6; for (int i = 0; i < 7; ++i) { if (p == 5 || p == 6) { screenGraphics.setColor(color.weekendBack); screenGraphics.fillRect(posx - sx/2, headerY, sx - 1, headerH - 2); screenGraphics.setColor(color.foreground); } screenGraphics.drawChar(DAY_INITIAL.charAt(p), posx, headerY + 1, Graphics.HCENTER | Graphics.TOP); posx += sx; ++p; if (p == 7) { p = 0; } } screenGraphics.setColor(color.foreground); screenGraphics.drawLine(0, headerY + headerH - 2, screenW, headerY + headerH - 2); repaint(0, headerY, screenW, headerH); } 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) { return len - profile.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 + profile.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 && profile.isMarked((short)(dateStart + day -1))) { return -1; } if (noinfo) { return color.titleBack; } if (profile.goal != 2) { //green, fertile if (day >= startF && day < ovl) { return mixedColor(color.fertile, color.fertileWeak, ovl - day, ovl - startF); } if (day >= ovl && day <= lastF) { return mixedColor(color.fertile, color.fertileWeak, day - ovl, lastF - ovl); } } //red, menstruation if (day <= lastM) { return mixedColor(color.red, color.redWeak, day - begin, lastM - begin); } //blue, infertile if (profile.goal != 1) { if (day < infert1 - 1) { return color.nonfertile; } if (day >= infert1 - 1 && day <= infert1) { return mixedColor(color.nonfertile, color.nonfertileWeak, day - infert1 + 2, 2); } if (day >= infert2 && day <= infert2 + 1) { return mixedColor(color.nonfertileWeak, color.nonfertile, day - infert2, 2); } if (day > infert2 + 1) { return color.nonfertile; } } return color.titleBack; } void drawOneCell(Graphics g, int col, int posx, int posy, int dayCnt) { if (col == -1) { g.setColor(color.redLight); 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(color.redDark); 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(color.red); 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 = ((dayCnt == nowDay) && (month == nowMonth) && (year == nowYear)); if (isToday) { g.setFont(bold); } g.setColor(col==-1 ? color.background : color.foreground); //colorScheme[(col == -1)?BG:FG]); g.drawString(""+dayCnt, posx + (sx - 1)/2, posy + textPosIncr, Graphics.HCENTER | Graphics.TOP); if (isToday) { g.setFont(regular); } } int textPosIncr; void drawCal() { regPos = -1; startOfRegions = profile.getDaysInfo((short)(dateStart), regions); curRegEnd = startOfRegions; int posy = cellsY; textPosIncr = (sy - fontHeight + 3) / 2; screenGraphics.setColor(color.background); boolean started = false; int dayCnt = 1; 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) { int col = getColor(dayCnt); drawOneCell(screenGraphics, col, posx, posy, dayCnt); if (dayCnt == nDaysInMonth) { screenGraphics.setColor(color.background); started = false; } ++dayCnt; } else { screenGraphics.fillRect(posx, posy, sx - 1, sy - 1); } posx += sx; } posy += sy; } repaint(halfEx, cellsY, screenW - ex, cellsH - ey); } static final private int DAYS_IN_MONTH[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; int daysInMonth(int month, int year) { if (month == 1 && (((year & 3) == 0) && ((year % 100) != 0)) || (year % 400) == 0) { return 29; } 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(on ? color.foreground : color.background); //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); } void keyAction(int action, boolean moveMonths) { if (action == 0) { return; } if (action == Canvas.FIRE) { mark(); 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--; break; case Canvas.RIGHT: selectedDate++; break; } if (selectedDate < dateStart) { prevMonth(); } else if (selectedDate >= dateStart + nDaysInMonth) { nextMonth(); } screenGraphics.setColor(color.foreground); drawCursor(true); drawTitle(); } int getAction(int keyCode) { try { return getGameAction(keyCode); } catch (IllegalArgumentException e) { return Canvas.FIRE; } } protected void keyPressed(int keyCode) { int action = getAction(keyCode); keyAction(action, false); } protected void keyRepeated(int keyCode) { int action = getAction(keyCode); keyAction(action, true); } }