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 com.sun.midp.chameleon.layers;
028:
029: import com.sun.midp.chameleon.*;
030: import javax.microedition.lcdui.*;
031: import com.sun.midp.chameleon.skins.PTISkin;
032: import com.sun.midp.chameleon.skins.ScreenSkin;
033: import com.sun.midp.lcdui.EventConstants;
034: import com.sun.midp.configurator.Constants;
035: import com.sun.midp.chameleon.input.*;
036:
037: /**
038: * A "PTILayer" layer is a special kind of layer which can
039: * be visible when the predictive text input mode is active.
040: * This layer is added to a MIDPWindow when more than one match
041: * exists for the predictive input method. This layer lists the
042: * possible words to give user a chance to select word you like.
043: * User can traverse the list of words using up/down navigation
044: * keys. User may press select bhutton to accept highlighted word.
045: */
046: public class PTILayer extends PopupLayer {
047: /** Options have to be listed in the popup dialog */
048: private String[] list;
049:
050: /** Selected option number */
051: private int selId;
052:
053: /** Instance of current input mode */
054: private TextInputSession iSession;
055:
056: /** max text width visible on the screen */
057: private int widthMax;
058:
059: /** separator character between words within the list */
060: private static final String SEPARATOR = " ";
061:
062: /** pointer is clicked outside of any area */
063: private static final int OUT_OF_BOUNDS = -1;
064:
065: /** pointer is clicked to left arrow */
066: private static final int LEFT_ARROW_AREA = 0;
067:
068: /** pointer is clicked to right arrow */
069: private static final int RIGHT_ARROW_AREA = 1;
070:
071: /** pointer is clicked to the word inside of list */
072: private static final int LIST_MATCHES_AREA = 2;
073:
074: /** Flag indicates that pointer release event should be processed */
075: private boolean checkReleased; //= false;
076:
077: /**
078: * Create an instance of PTILayer
079: * @param inputSession current input session
080: */
081: public PTILayer(TextInputSession inputSession) {
082: super (PTISkin.IMAGE_BG, PTISkin.COLOR_BG);
083: iSession = inputSession;
084: }
085:
086: /**
087: * The setVisible() method is overridden in PTILayer
088: * so as not to have any effect. PopupLayers are always
089: * visible by their very nature. In order to hide a
090: * PopupLayer, it should be removed from its containing
091: * MIDPWindow.
092: * @param visible if true the pti layer has to be shown,
093: * if false the layer has to be hidden
094: */
095: public void setVisible(boolean visible) {
096: this .visible = visible;
097: }
098:
099: /** PTI layer initialization: init selected id, calculate available size */
100: protected void initialize() {
101: super .initialize();
102:
103: setAnchor();
104: selId = 0;
105: }
106:
107: /**
108: * Sets the anchor constants for rendering operation.
109: */
110: private void setAnchor() {
111: bounds[W] = ScreenSkin.WIDTH;
112: bounds[H] = PTISkin.HEIGHT;
113: bounds[X] = (ScreenSkin.WIDTH - bounds[W]) >> 1;
114: bounds[Y] = ScreenSkin.HEIGHT - bounds[H];
115: widthMax = bounds[W] - PTISkin.MARGIN;
116: if (PTISkin.LEFT_ARROW != null && PTISkin.RIGHT_ARROW != null) {
117: widthMax -= 4 * PTISkin.MARGIN
118: + PTISkin.LEFT_ARROW.getWidth()
119: + PTISkin.RIGHT_ARROW.getWidth();
120: }
121: }
122:
123: /**
124: * Set list of matches
125: * @param l list of matches
126: */
127: public synchronized void setList(String[] l) {
128: list = new String[l.length];
129: System.arraycopy(l, 0, list, 0, l.length);
130: visible = (list != null && list.length > 1);
131: // IMPL_NOTE: has to be set externally as parameter
132: selId = 0;
133: setDirty();
134: }
135:
136: /**
137: * Get list of matches
138: * @return list of matches
139: */
140: public synchronized String[] getList() {
141: return list;
142: }
143:
144: /**
145: * Handle key input from a keypad. Parameters describe
146: * the type of key event and the platform-specific
147: * code for the key. (Codes are translated using the
148: * lcdui.Canvas) UP/DOWN/SELECT key press are processed if
149: * is visible.
150: *
151: * @param type the type of key event
152: * @param keyCode the numeric code assigned to the key
153: * @return true if key has been handled by PTI layer, false otherwise
154: */
155: public boolean keyInput(int type, int keyCode) {
156: boolean ret = false;
157: String[] l = getList();
158: if (type == EventConstants.PRESSED && visible) {
159: switch (keyCode) {
160: case Constants.KEYCODE_UP:
161: case Constants.KEYCODE_LEFT:
162: selId = (selId - 1 + l.length) % l.length;
163: iSession.processKey(Canvas.UP, false);
164: ret = true;
165: break;
166: case Constants.KEYCODE_DOWN:
167: case Constants.KEYCODE_RIGHT:
168: selId = (selId + 1) % l.length;
169: iSession.processKey(Canvas.DOWN, false);
170: ret = true;
171: break;
172: case Constants.KEYCODE_SELECT:
173: iSession.processKey(keyCode, false);
174: ret = true;
175: break;
176: default:
177: break;
178: }
179: }
180: // process key by input mode
181: requestRepaint();
182: return ret;
183: }
184:
185: /**
186: * Get id of the word inside of the list selected by pointer
187: * @param x - x coordinate of pointer
188: * @param y - y coordinate of pointer
189: * @return word index in the range of 0 and list length - 1.
190: * If the pointer does not point to any word return -1
191: */
192: private int getWordIdAtPointerPosition(int x, int y) {
193: String[] l = getList();
194: int id = 0;
195: int start = PTISkin.MARGIN;
196: if (PTISkin.LEFT_ARROW != null) {
197: start += PTISkin.LEFT_ARROW.getWidth();
198: }
199:
200: while (id < l.length) {
201: int w = PTISkin.FONT.stringWidth(SEPARATOR + l[id]);
202: if (x > start && x <= start + w) {
203: break;
204: }
205: start += w;
206: id++;
207: }
208:
209: return id < l.length ? id : -1;
210: }
211:
212: /**
213: * Utility method to determine if this layer wanna handle
214: * the given point. PTI layer handles the point if it
215: * lies within the bounds of this layer. The point should be in
216: * the coordinate space of this layer's containing CWindow.
217: *
218: * @param x the "x" coordinate of the point
219: * @param y the "y" coordinate of the point
220: * @return true if the coordinate lies in the bounds of this layer
221: */
222: public boolean handlePoint(int x, int y) {
223: return containsPoint(x, y);
224: }
225:
226: /**
227: * Get the layer area the pointer is clicked in
228: * @param x - x coordinate of pointer
229: * @param y - y coordinate of pointer
230: * @return retuen the area. It can be either OUT_OF_BOUNDS or
231: * LEFT_ARROW_AREA or RIGHT_ARROW_AREA or LIST_MATCHES_AREA
232: */
233: private int getAreaAtPointerPosition(int x, int y) {
234: int area = OUT_OF_BOUNDS;
235: if (x >= PTISkin.MARGIN && x <= bounds[W] - PTISkin.MARGIN) {
236: if (PTISkin.LEFT_ARROW != null
237: && x <= PTISkin.MARGIN
238: + PTISkin.LEFT_ARROW.getWidth()) {
239: area = LEFT_ARROW_AREA;
240: } else if (PTISkin.RIGHT_ARROW != null
241: && x >= bounds[W] - PTISkin.MARGIN
242: - PTISkin.RIGHT_ARROW.getWidth()) {
243: area = RIGHT_ARROW_AREA;
244: } else {
245: area = LIST_MATCHES_AREA;
246: }
247: }
248: return area;
249: }
250:
251: /**
252: * Allow this window to process pointer input. The type of pointer input
253: * will be press, release, drag, etc. The x and y coordinates will
254: * identify the point at which the pointer event occurred in the coordinate
255: * system of this window. This window will translate the coordinates
256: * appropriately for each layer contained in this window. This method will
257: * return true if the event was processed by this window or one of its
258: * layers, false otherwise.
259: *
260: * @param type the type of pointer event (press, release, drag)
261: * @param x the x coordinate of the location of the event
262: * @param y the y coordinate of the location of the event
263: * @return true if this window or one of its layers processed the event
264: */
265: public boolean pointerInput(int type, int x, int y) {
266: if (visible) {
267: String[] l = getList();
268:
269: int area = getAreaAtPointerPosition(x, y);
270:
271: switch (type) {
272: case EventConstants.PRESSED:
273: switch (area) {
274: case LEFT_ARROW_AREA:
275: selId = (selId - 1 + l.length) % l.length;
276: iSession.processKey(Canvas.UP, false);
277: requestRepaint();
278: break;
279: case RIGHT_ARROW_AREA:
280: selId = (selId + 1) % l.length;
281: iSession.processKey(Canvas.DOWN, false);
282: requestRepaint();
283: break;
284: case LIST_MATCHES_AREA:
285: // move focus to the selected word
286: int id = getWordIdAtPointerPosition(x, y);
287: if (id >= 0) {
288: checkReleased = true;
289: int i = selId;
290: if (id > selId) {
291: while (i < id) {
292: iSession.processKey(Canvas.DOWN, false);
293: i++;
294: }
295: } else if (id < selId) {
296: while (i > id) {
297: iSession.processKey(Canvas.UP, false);
298: i--;
299: }
300: }
301: requestRepaint();
302: }
303: break;
304: }
305: break;
306: case EventConstants.RELEASED:
307: if (area == LIST_MATCHES_AREA && checkReleased
308: // IMPL_NOTE: move the focus in the standart maner,
309: // doon't move the selected item at the head of the list
310: // && getWordIdAtPointerPosition(x, y) == selId
311: ) {
312: iSession
313: .processKey(Constants.KEYCODE_SELECT, false);
314: requestRepaint();
315: }
316: checkReleased = false;
317: break;
318: default:
319: break;
320: }
321: }
322: return true;
323: }
324:
325: /**
326: * Paint layer body.
327: * @param g - Graphics
328: */
329: protected void paintBody(Graphics g) {
330: String[] l = getList();
331: if (l == null || l.length < 1)
332: return;
333:
334: // draw outer frame
335: g.setColor(PTISkin.COLOR_BDR);
336: g.drawRect(0, 0, bounds[W] - 1, bounds[H] - 1);
337:
338: // draw arrows
339: if (PTISkin.LEFT_ARROW != null) {
340: g.drawImage(PTISkin.LEFT_ARROW, PTISkin.MARGIN,
341: bounds[H] >> 1, Graphics.VCENTER | Graphics.LEFT);
342: }
343:
344: if (PTISkin.RIGHT_ARROW != null) {
345: g.drawImage(PTISkin.RIGHT_ARROW,
346: bounds[W] - PTISkin.MARGIN, bounds[H] >> 1,
347: Graphics.VCENTER | Graphics.RIGHT);
348: }
349:
350: int x = 0, y = 0;
351:
352: String text_b = "", text_a = "";
353:
354: for (int i = -1; ++i < l.length;) {
355: if (i < selId) {
356: text_a += l[i] + SEPARATOR;
357: } else if (i > selId) {
358: text_b += l[i] + SEPARATOR;
359: }
360: }
361:
362: g.translate((bounds[W] - widthMax) >> 1, 0);
363: g.setClip(0, 0, widthMax, bounds[H]);
364:
365: x = 0;
366: y = PTISkin.FONT.getHeight() < bounds[H] ? (bounds[H] - PTISkin.FONT
367: .getHeight()) >> 1
368: : 0;
369:
370: // draw before words
371: if (text_a.length() > 0) {
372: g.setColor(PTISkin.COLOR_FG);
373: g.drawString(text_a, x, y, Graphics.LEFT | Graphics.TOP);
374: x += PTISkin.FONT.stringWidth(text_a);
375: }
376:
377: if (l[selId].length() > 0) {
378: // draw highlighted word
379: // draw highlighted fill rectangle
380: g.setColor(PTISkin.COLOR_BG_HL);
381:
382: g.fillRect(x - PTISkin.FONT.stringWidth(SEPARATOR) / 2,
383: y < PTISkin.MARGIN ? y : PTISkin.MARGIN,
384: PTISkin.FONT.stringWidth(l[selId] + SEPARATOR),
385: bounds[H]
386: - (y < PTISkin.MARGIN ? y : PTISkin.MARGIN)
387: * 2);
388:
389: g.setColor(PTISkin.COLOR_FG_HL);
390: g.drawString(l[selId] + SEPARATOR, x, y, Graphics.LEFT
391: | Graphics.TOP);
392: x += PTISkin.FONT.stringWidth(l[selId] + SEPARATOR);
393: }
394:
395: // draw after words
396:
397: if (text_b.length() > 0) {
398: g.setColor(PTISkin.COLOR_FG);
399: g.drawString(text_b, x, y, Graphics.LEFT | Graphics.TOP);
400: }
401:
402: g.translate(-((bounds[W] - widthMax) >> 1), 0);
403: g.setClip(0, 0, bounds[W], bounds[H]);
404: }
405:
406: /**
407: * Update bounds of layer
408: * @param layers - current layer can be dependant on this parameter
409: */
410: public void update(CLayer[] layers) {
411: super .update(layers);
412: if (visible) {
413: setAnchor();
414: bounds[Y] -= (layers[MIDPWindow.BTN_LAYER].isVisible() ? layers[MIDPWindow.BTN_LAYER].bounds[H]
415: : 0)
416: + (layers[MIDPWindow.TICKER_LAYER].isVisible() ? layers[MIDPWindow.TICKER_LAYER].bounds[H]
417: : 0);
418:
419: }
420: }
421: }
|