001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package javax.microedition.lcdui;
028:
029: import java.util.Date;
030: import java.util.Calendar;
031:
032: import com.sun.midp.lcdui.*;
033: import com.sun.midp.i18n.Resource;
034: import com.sun.midp.i18n.ResourceConstants;
035: import com.sun.midp.configurator.Constants;
036: import com.sun.midp.chameleon.skins.DateEditorSkin;
037: import com.sun.midp.chameleon.skins.ScreenSkin;
038: import com.sun.midp.chameleon.skins.resources.DateEditorResources;
039: import com.sun.midp.chameleon.layers.PopupLayer;
040:
041: import javax.microedition.lcdui.game.Sprite;
042:
043: import com.sun.midp.log.Logging;
044: import com.sun.midp.log.LogChannels;
045:
046: /**
047: * A utility class for editing date/time components for a DateField.
048: */
049: class MiniDateEditor extends DateEditor {
050:
051: /**
052: * Create a new DateEditor layer.
053: *
054: * @param lf The DateFieldLFImpl that triggered this date editor
055: */
056: public MiniDateEditor(DateFieldLFImpl lf) {
057: super (lf);
058: timeComponentsOffset = 0;
059: }
060:
061: void show() {
062: super .show();
063:
064: switch (mode) {
065: case DateField.DATE:
066: case DateField.DATE_TIME:
067: focusOn = DAY_POPUP;
068: break;
069: }
070: }
071:
072: /**
073: * Hide all sub-popups triggered by this date editor.
074: * Overridden from parent class to close additional
075: * 'day of the month' popup.
076: */
077: void hideAllPopups() {
078: synchronized (Display.LCDUILock) {
079: ScreenLFImpl sLF = (ScreenLFImpl) lf.df.owner.getLF();
080: Display d = sLF.lGetCurrentDisplay();
081: if (d != null) {
082: d.hidePopup(dayPopup);
083: d.hidePopup(monthPopup);
084: d.hidePopup(yearPopup);
085: d.hidePopup(hoursPopup);
086: d.hidePopup(minutesPopup);
087: }
088: } // synchronized
089: }
090:
091: /**
092: * Populate the date components.
093: */
094: protected void populateDateComponents() {
095: super .populateDateComponents();
096:
097: // populate DAYS[]
098: int daysInMonth = daysInMonth(editDate.get(Calendar.MONTH),
099: editDate.get(Calendar.YEAR));
100: DAYS = new String[daysInMonth];
101: for (int i = 1; i <= daysInMonth; i++) {
102: DAYS[i - 1] = Integer.toString(i);
103: }
104: dayPopup = new DEPopupLayer(this , DAYS, editDate
105: .get(Calendar.DAY_OF_MONTH) - 1, true);
106: }
107:
108: /**
109: * Draw the date components.
110: *
111: * @param g The Graphics object to paint to
112: */
113: protected void drawDateComponents(Graphics g) {
114:
115: nextX = 4;
116: nextY = 0;
117:
118: int w = 0;
119: int h = 0;
120:
121: g.setFont(DateEditorSkin.FONT_POPUPS);
122: g.setColor(0);
123: g.drawString(Resource
124: .getString(ResourceConstants.LCDUI_DF_DATE_MINI),
125: nextX, nextY, Graphics.LEFT | Graphics.TOP);
126:
127: nextY = DateEditorSkin.FONT_POPUPS.getHeight() + 2;
128: g.translate(nextX, nextY);
129:
130: if (DateEditorSkin.IMAGE_MONTH_BG != null) {
131: g.drawImage(DateEditorSkin.IMAGE_TIME_BG, 0, 0,
132: Graphics.LEFT | Graphics.TOP);
133: w = DateEditorSkin.IMAGE_TIME_BG.getWidth();
134: h = DateEditorSkin.IMAGE_TIME_BG.getHeight();
135: if (focusOn == DAY_POPUP) {
136: g.setColor(DateEditorSkin.COLOR_TRAVERSE_IND);
137: g.drawRect(-2, -2, w + 3, h + 3);
138: }
139: dayPopup.setElementSize(w - 4, DateEditorSkin.FONT_POPUPS
140: .getHeight());
141: dayPopup.setBounds(g.getTranslateX(),
142: g.getTranslateY() + h, w,
143: DateEditorSkin.HEIGHT_POPUPS);
144: }
145: g.setFont(DateEditorSkin.FONT_POPUPS);
146: g.setColor(0);
147: g.drawString(DAYS[editDate.get(Calendar.DAY_OF_MONTH) - 1], 3,
148: 0, Graphics.LEFT | Graphics.TOP);
149:
150: g.translate(w + 2, 0);
151:
152: if (DateEditorSkin.IMAGE_MONTH_BG != null) {
153: g.drawImage(DateEditorSkin.IMAGE_MONTH_BG, 0, 0,
154: Graphics.LEFT | Graphics.TOP);
155: w = DateEditorSkin.IMAGE_MONTH_BG.getWidth();
156: h = DateEditorSkin.IMAGE_MONTH_BG.getHeight();
157: if (focusOn == MONTH_POPUP) {
158: g.setColor(DateEditorSkin.COLOR_TRAVERSE_IND);
159: g.drawRect(-2, -2, w + 3, h + 3);
160: }
161: monthPopup.setElementSize(w - 4, DateEditorSkin.FONT_POPUPS
162: .getHeight());
163: monthPopup.setBounds(g.getTranslateX(), g.getTranslateY()
164: + h, w, DateEditorSkin.HEIGHT_POPUPS);
165: }
166: g.setFont(DateEditorSkin.FONT_POPUPS);
167: g.setColor(0);
168: g.drawString(MONTHS[editDate.get(Calendar.MONTH)], 4, 0,
169: Graphics.LEFT | Graphics.TOP);
170:
171: g.translate(w + 2, 0);
172: if (DateEditorSkin.IMAGE_YEAR_BG != null) {
173: g.drawImage(DateEditorSkin.IMAGE_YEAR_BG, 0, 0,
174: Graphics.LEFT | Graphics.TOP);
175: w = DateEditorSkin.IMAGE_YEAR_BG.getWidth();
176: h = DateEditorSkin.IMAGE_YEAR_BG.getHeight();
177: if (focusOn == YEAR_POPUP) {
178: g.setColor(DateEditorSkin.COLOR_TRAVERSE_IND);
179: g.drawRect(-2, -2, w + 3, h + 3);
180: }
181: yearPopup.setElementSize(w - 4, DateEditorSkin.FONT_POPUPS
182: .getHeight());
183: yearPopup.setBounds(g.getTranslateX(), g.getTranslateY()
184: + h, w, DateEditorSkin.HEIGHT_POPUPS);
185: }
186:
187: g.setFont(DateEditorSkin.FONT_POPUPS);
188: g.setColor(0);
189: g.drawString(Integer.toString(editDate.get(Calendar.YEAR)), 4,
190: 0, Graphics.LEFT | Graphics.TOP);
191:
192: g.translate(-g.getTranslateX() + 4, -nextY);
193: }
194:
195: /**
196: * Draw the time components.
197: *
198: * @param g The Graphics object to paint to
199: */
200: protected void drawTimeComponents(Graphics g) {
201: nextX = 4;
202: nextY = (mode == DateField.DATE_TIME) ? 33 : 0;
203:
204: g.setFont(DateEditorSkin.FONT_POPUPS);
205: g.setColor(0);
206: g.drawString(Resource
207: .getString(ResourceConstants.LCDUI_DF_TIME_MINI),
208: nextX, nextY, Graphics.LEFT | Graphics.TOP);
209:
210: nextY += DateEditorSkin.FONT_POPUPS.getHeight();
211:
212: int w = 0;
213: int h = 0;
214:
215: g.translate(nextX, nextY);
216:
217: if (DateEditorSkin.IMAGE_TIME_BG != null) {
218: g.drawImage(DateEditorSkin.IMAGE_TIME_BG, 0, 0,
219: Graphics.LEFT | Graphics.TOP);
220: w = DateEditorSkin.IMAGE_TIME_BG.getWidth();
221: h = DateEditorSkin.IMAGE_TIME_BG.getHeight();
222: if (focusOn == HOURS_POPUP) {
223: g.setColor(DateEditorSkin.COLOR_TRAVERSE_IND);
224: g.drawRect(-2, -2, w + 3, h + 3);
225: }
226: hoursPopup.setElementSize(w - 4, DateEditorSkin.FONT_POPUPS
227: .getHeight());
228: hoursPopup.setBounds(g.getTranslateX(), g.getTranslateY()
229: + h, w, DateEditorSkin.HEIGHT_POPUPS);
230: }
231:
232: g.setFont(DateEditorSkin.FONT_POPUPS);
233: g.setColor(0);
234: g.drawString(Integer.toString(HOURS[hoursPopup
235: .getSelectedIndex()]), 3, 0, Graphics.LEFT
236: | Graphics.TOP);
237:
238: g.translate(w + 2, 0);
239: if (DateEditorSkin.IMAGE_TIME_BG != null) {
240: g.drawImage(DateEditorSkin.IMAGE_TIME_BG, 0, 0,
241: Graphics.LEFT | Graphics.TOP);
242: w = DateEditorSkin.IMAGE_TIME_BG.getWidth();
243: h = DateEditorSkin.IMAGE_TIME_BG.getHeight();
244: if (focusOn == MINUTES_POPUP) {
245: g.setColor(DateEditorSkin.COLOR_TRAVERSE_IND);
246: g.drawRect(-2, -2, w + 3, h + 3);
247: }
248: minutesPopup.setElementSize(w - 4,
249: DateEditorSkin.FONT_POPUPS.getHeight());
250: minutesPopup.setBounds(g.getTranslateX(), g.getTranslateY()
251: + h, w, DateEditorSkin.HEIGHT_POPUPS);
252: }
253:
254: g.setFont(DateEditorSkin.FONT_POPUPS);
255: g.setColor(0);
256: g.drawString(DateFieldLFImpl.twoDigits(editDate
257: .get(Calendar.MINUTE)), 3, 0, Graphics.LEFT
258: | Graphics.TOP);
259:
260: g.translate(w + 2, 0);
261:
262: nextX = (mode == DateField.TIME) ? 15 : 0;
263: nextY = 45;
264:
265: g.translate(-g.getTranslateX(), 20);
266: g.translate(10, 0);
267: paintAmPm(g);
268: }
269:
270: /**
271: * Paint the am/pm indicators.
272: *
273: * @param g The graphics context to paint to
274: */
275: protected void paintAmPm(Graphics g) {
276: int clockStartX, clockStartY;
277:
278: if (!lf.CLOCK_USES_AM_PM) {
279: clockStartY = 9;
280: } else {
281: // paint AM
282: if (DateEditorSkin.IMAGE_RADIO != null) {
283: g.drawImage(
284: (amSelected) ? DateEditorSkin.IMAGE_RADIO[1]
285: : DateEditorSkin.IMAGE_RADIO[0], 0, 0,
286: Graphics.LEFT | Graphics.TOP);
287:
288: if ((focusOn == AM_PM) && (amHilighted)) {
289: g.setColor(DateEditorSkin.COLOR_TRAVERSE_IND);
290: g.drawRect(0, 0, DateEditorSkin.IMAGE_RADIO[0]
291: .getWidth(), DateEditorSkin.IMAGE_RADIO[0]
292: .getHeight());
293: g.setColor(0);
294: }
295:
296: if (DateEditorSkin.IMAGE_AMPM != null) {
297: int w = DateEditorSkin.IMAGE_AMPM.getWidth() / 2;
298: g
299: .drawRegion(DateEditorSkin.IMAGE_AMPM, 0,
300: 0, w, DateEditorSkin.IMAGE_AMPM
301: .getHeight(),
302: Sprite.TRANS_NONE,
303: DateEditorSkin.IMAGE_RADIO[0]
304: .getWidth(),
305: (DateEditorSkin.IMAGE_RADIO[0]
306: .getHeight() / 2),
307: Graphics.VCENTER | Graphics.LEFT);
308: }
309: }
310:
311: g.translate(35, 0);
312: // paint PM
313: if (DateEditorSkin.IMAGE_RADIO != null) {
314: g.drawImage(
315: (amSelected) ? DateEditorSkin.IMAGE_RADIO[0]
316: : DateEditorSkin.IMAGE_RADIO[1], 0, 0,
317: Graphics.LEFT | Graphics.TOP);
318:
319: if ((focusOn == AM_PM) && (!amHilighted)) {
320: g.setColor(DateEditorSkin.COLOR_TRAVERSE_IND);
321: g.drawRect(0, 0, DateEditorSkin.IMAGE_RADIO[0]
322: .getWidth(), DateEditorSkin.IMAGE_RADIO[0]
323: .getHeight());
324: g.setColor(0);
325: }
326:
327: if (DateEditorSkin.IMAGE_AMPM != null) {
328: int w = DateEditorSkin.IMAGE_AMPM.getWidth() / 2;
329: g
330: .drawRegion(DateEditorSkin.IMAGE_AMPM,
331: (DateEditorSkin.IMAGE_AMPM
332: .getWidth() / 2), 0, w,
333: DateEditorSkin.IMAGE_AMPM
334: .getHeight(),
335: Sprite.TRANS_NONE,
336: DateEditorSkin.IMAGE_RADIO[0]
337: .getWidth(),
338: (DateEditorSkin.IMAGE_RADIO[0]
339: .getHeight() / 2),
340: Graphics.VCENTER | Graphics.LEFT);
341: }
342: }
343: g.translate(-35, 0);
344: clockStartY = 22;
345: }
346: }
347:
348: /**
349: * Called when select key is fired, to take further action on it,
350: * based on where the focus is on the date editor.
351: *
352: * @return true if key was handled, false otherwise
353: */
354: protected boolean selectFired() {
355: boolean done = false;
356: ScreenLFImpl sLF = (ScreenLFImpl) lf.df.owner.getLF();
357: switch (focusOn) {
358: case DAY_POPUP:
359: if (!dayPopup.open) {
360: sLF.lGetCurrentDisplay().showPopup(dayPopup);
361: dayPopup.open = !dayPopup.open;
362: done = true;
363: } else {
364: dayPopup.open = !dayPopup.open;
365: int day = dayPopup.getSelectedIndex();
366: editDate.set(Calendar.DAY_OF_MONTH, day + 1);
367: sLF.lGetCurrentDisplay().hidePopup(dayPopup);
368: }
369: break;
370: default:
371: return super .selectFired();
372: }
373: return done;
374: }
375:
376: /**
377: * Handles internal traversal within the date editor.
378: *
379: * @param code the code of this key event
380: * @return true always, since popup layers swallow all events
381: */
382: protected boolean traverseEditor(int code) {
383: // handle internal traversal
384: switch (focusOn) {
385: case DAY_POPUP:
386: switch (code) {
387: case Constants.KEYCODE_RIGHT:
388: focusOn = MONTH_POPUP;
389: break;
390: case Constants.KEYCODE_DOWN:
391: if (mode == DateField.DATE_TIME) {
392: focusOn = HOURS_POPUP;
393: }
394: break;
395: default:
396: break;
397: }
398: break;
399: case MONTH_POPUP:
400: switch (code) {
401: case Constants.KEYCODE_RIGHT:
402: focusOn = YEAR_POPUP;
403: break;
404: case Constants.KEYCODE_LEFT:
405: focusOn = DAY_POPUP;
406: break;
407: case Constants.KEYCODE_DOWN:
408: if (mode == DateField.DATE_TIME) {
409: focusOn = MINUTES_POPUP;
410: }
411: default:
412: // no-op
413: break;
414: }
415: break;
416: case YEAR_POPUP:
417: switch (code) {
418: case Constants.KEYCODE_LEFT:
419: focusOn = MONTH_POPUP;
420: break;
421: case Constants.KEYCODE_RIGHT:
422: if (mode == DateField.DATE_TIME) {
423: focusOn = HOURS_POPUP;
424: }
425: break;
426: default:
427: // no-op
428: break;
429: }
430: break;
431: case HOURS_POPUP:
432: switch (code) {
433: case Constants.KEYCODE_UP:
434: if (mode == DateField.DATE_TIME) {
435: focusOn = DAY_POPUP;
436: }
437: break;
438: case Constants.KEYCODE_DOWN:
439: focusOn = AM_PM;
440: break;
441: case Constants.KEYCODE_LEFT:
442: if (mode == DateField.DATE_TIME) {
443: focusOn = YEAR_POPUP;
444: }
445: break;
446: case Constants.KEYCODE_RIGHT:
447: focusOn = MINUTES_POPUP;
448: break;
449: default:
450: // no-op
451: break;
452: }
453: break;
454: case MINUTES_POPUP:
455: switch (code) {
456: case Constants.KEYCODE_UP:
457: if (mode == DateField.DATE_TIME) {
458: focusOn = MONTH_POPUP;
459: }
460: break;
461: case Constants.KEYCODE_DOWN:
462: focusOn = AM_PM;
463: break;
464: case Constants.KEYCODE_LEFT:
465: focusOn = HOURS_POPUP;
466: break;
467: default:
468: // no-op
469: break;
470: }
471: break;
472: case AM_PM:
473: traverseAmPm(code);
474: break;
475: default:
476: Logging.report(Logging.ERROR, LogChannels.LC_HIGHUI,
477: "DateEditor.traverseEditor(), focusOn=" + focusOn);
478: break;
479: }
480: return true;
481: }
482:
483: /**
484: * Handle internal traversal between the am/pm indicators.
485: *
486: * @param code the code of this key event
487: * @return true if internal traversal occurred, false otherwise
488: */
489: protected boolean traverseAmPm(int code) {
490: boolean traverse = false;
491: switch (code) {
492: case Constants.KEYCODE_UP:
493: focusOn = HOURS_POPUP;
494: traverse = true;
495: break;
496: case Constants.KEYCODE_LEFT:
497: if (amHilighted) {
498: if (mode == DateField.DATE_TIME) {
499: focusOn = YEAR_POPUP;
500: traverse = true;
501: }
502: } else {
503: amHilighted = true;
504: traverse = true;
505: }
506: break;
507: case Constants.KEYCODE_RIGHT:
508: if (amHilighted) {
509: amHilighted = false;
510: traverse = true;
511: }
512: break;
513: default:
514: // no-op
515: break;
516: }
517: return traverse;
518: }
519:
520: /**
521: * Constant indicating the day of the month popup, used in the process
522: * of current focus tracking inside the date editor.
523: */
524: protected static final int DAY_POPUP = 7;
525:
526: /**
527: * Static array holding the day of the month values.
528: */
529: protected static String[] DAYS;
530:
531: /** The sub-popup layer used to select day of the month value */
532: protected DEPopupLayer dayPopup;
533:
534: }
|