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: */package com.sun.midp.chameleon.layers;
026:
027: import com.sun.midp.chameleon.*;
028: import javax.microedition.lcdui.*;
029: import com.sun.midp.chameleon.skins.*;
030: import com.sun.midp.chameleon.skins.resources.*;
031: import com.sun.midp.util.ResourceHandler;
032: import com.sun.midp.configurator.Constants;
033: import com.sun.midp.lcdui.EventConstants;
034:
035: public class CascadeMenuLayer extends ScrollablePopupLayer {
036:
037: /** The list of Commands to display in the menu */
038: protected Command[] menuCmds;
039:
040: /** The currently selected index in the menu */
041: protected int selI;
042:
043: /**
044: * The number of commands which have been scrolled off the
045: * top of the menu, normally 0 unless there are more commands
046: * than can fit on the menu.
047: */
048: protected int scrollIndex;
049:
050: /**
051: * The ManuLayer to which this cascading menu belongs
052: */
053: protected MenuLayer menuLayer;
054:
055: /** pointer pressed outside of the menuLayer's bounds */
056: private final static int PRESS_OUT_OF_BOUNDS = -1;
057:
058: /** variable used in pointerInput handling */
059: private int itemIndexWhenPressed = PRESS_OUT_OF_BOUNDS;
060:
061: public CascadeMenuLayer() {
062: super ();
063: setBackground(null, MenuSkin.COLOR_BG);
064: }
065:
066: public void setMenuCommands(Command[] cmdList, MenuLayer menuLayer) {
067: if (cmdList.length == 1 && cmdList[0] instanceof SubMenuCommand) {
068: cmdList = ((SubMenuCommand) cmdList[0]).getSubCommands();
069: }
070: this .menuCmds = new Command[cmdList.length];
071: System.arraycopy(cmdList, 0, this .menuCmds, 0, cmdList.length);
072: // If we have fewer commands than fill up the menu,
073: // we shorten the menu's height
074: if (menuCmds.length < MenuSkin.MAX_ITEMS) {
075: bounds[H] = MenuSkin.HEIGHT
076: - ((MenuSkin.MAX_ITEMS - menuCmds.length) * MenuSkin.ITEM_HEIGHT);
077: } else {
078: bounds[H] = MenuSkin.HEIGHT;
079: }
080: bounds[H] -= MenuSkin.ITEM_TOPOFFSET;
081: alignMenu();
082: requestRepaint();
083:
084: this .menuLayer = menuLayer;
085: selI = 0;
086: }
087:
088: public void setAnchorPoint(int x, int y) {
089: bounds[Y] = y - bounds[H] + MenuSkin.ITEM_HEIGHT + 3;
090: if (bounds[Y] < 0) {
091: bounds[Y] = 0;
092: }
093: }
094:
095: public void updateScrollIndicator() {
096: if (scrollInd != null) {
097: if (menuCmds.length > MenuSkin.MAX_ITEMS) {
098: scrollInd.setVerticalScroll((scrollIndex * 100)
099: / (menuCmds.length - MenuSkin.MAX_ITEMS),
100: (MenuSkin.MAX_ITEMS * 100) / menuCmds.length);
101: } else {
102: scrollInd.setVerticalScroll(0, 100);
103: }
104: super .updateScrollIndicator();
105: }
106: }
107:
108: /**
109: * Helper function to determine the itemIndex at the x,y position
110: *
111: * @param x,y pointer coordinates in menuLayer's space (0,0 means left-top
112: * corner) both value can be negative as menuLayer handles the pointer
113: * event outside its bounds
114: * @return menuItem's index since 0, or PRESS_OUT_OF_BOUNDS
115: *
116: */
117: private int itemIndexAtPointerPosition(int x, int y) {
118: int ret;
119: if (!containsPoint(x + bounds[X], y + bounds[Y])) {
120: // IMPL_NOTE: nothing happened. Need to be handled another way
121: ret = PRESS_OUT_OF_BOUNDS;
122: } else {
123: ret = y / MenuSkin.ITEM_HEIGHT;
124: }
125: return ret;
126: }
127:
128: /**
129: * Handle input from a pen tap. Parameters describe
130: * the type of pen event and the x,y location in the
131: * layer at which the event occurred. Important : the
132: * x,y location of the pen tap will already be translated
133: * into the coordinate space of the layer.
134: *
135: * @param type the type of pen event
136: * @param x the x coordinate of the event
137: * @param y the y coordinate of the event
138: */
139: public boolean pointerInput(int type, int x, int y) {
140: boolean consume = true;
141: switch (type) {
142: case EventConstants.PRESSED:
143: itemIndexWhenPressed = itemIndexAtPointerPosition(x, y);
144:
145: // dismiss the menu layer if the user pressed outside the menu
146: if (itemIndexWhenPressed == PRESS_OUT_OF_BOUNDS) {
147: if (menuLayer != null) {
148: menuLayer.dismissCascadeMenu();
149: }
150: consume = false;
151: } else if (itemIndexWhenPressed >= 0) { // press on valid menu item
152: selI = scrollIndex + itemIndexWhenPressed;
153: requestRepaint();
154: // if (btnLayer != null) btnLayer.serviceRepaints();
155: }
156: break;
157: case EventConstants.RELEASED:
158: int itemIndexWhenReleased = itemIndexAtPointerPosition(x, y);
159:
160: if (itemIndexWhenReleased == itemIndexWhenPressed) {
161: if (itemIndexWhenPressed >= 0) {
162: if (menuLayer != null) {
163: if (selI >= 0 && selI < menuCmds.length) {
164: menuLayer
165: .subCommandSelected(menuCmds[selI]);
166: }
167: }
168: }
169: }
170:
171: if (itemIndexWhenReleased == PRESS_OUT_OF_BOUNDS) {
172: consume = false;
173: }
174:
175: // remember to reset the variables
176: itemIndexWhenPressed = PRESS_OUT_OF_BOUNDS;
177: break;
178: }
179: // return true always as menuLayer will capture all of the pointer inputs
180: return consume;
181: }
182:
183: public boolean keyInput(int type, int keyCode) {
184: // The system menu will absorb all key presses except
185: // for the soft menu keys - that is, it will always
186: // return 'true' indicating it has handled the key
187: // event except for the soft button keys for which it
188: // returns 'false'
189:
190: if (keyCode == EventConstants.SOFT_BUTTON1
191: || keyCode == EventConstants.SOFT_BUTTON2) {
192: return false;
193: }
194:
195: if (type != EventConstants.PRESSED
196: && type != EventConstants.REPEATED) {
197: return true;
198: }
199:
200: if (keyCode == Constants.KEYCODE_UP) {
201: if (selI > 0) {
202: selI--;
203: if (selI < scrollIndex && scrollIndex > 0) {
204: scrollIndex--;
205: }
206: updateScrollIndicator();
207: requestRepaint();
208: }
209: } else if (keyCode == Constants.KEYCODE_DOWN) {
210: if (selI < (menuCmds.length - 1)) {
211: selI++;
212: if (selI >= MenuSkin.MAX_ITEMS
213: && scrollIndex < (menuCmds.length - MenuSkin.MAX_ITEMS)) {
214: scrollIndex++;
215: }
216: updateScrollIndicator();
217: requestRepaint();
218: }
219: } else if (keyCode == Constants.KEYCODE_RIGHT) {
220: menuLayer.dismissCascadeMenu();
221: } else if (keyCode == Constants.KEYCODE_SELECT) {
222: menuLayer.subCommandSelected(menuCmds[selI]);
223: } else if (menuCmds.length < 10) {
224: int max = 0;
225: switch (keyCode) {
226: case Canvas.KEY_NUM1:
227: max = 1;
228: break;
229: case Canvas.KEY_NUM2:
230: max = 2;
231: break;
232: case Canvas.KEY_NUM3:
233: max = 3;
234: break;
235: case Canvas.KEY_NUM4:
236: max = 4;
237: break;
238: case Canvas.KEY_NUM5:
239: max = 5;
240: break;
241: case Canvas.KEY_NUM6:
242: max = 6;
243: break;
244: case Canvas.KEY_NUM7:
245: max = 7;
246: break;
247: case Canvas.KEY_NUM8:
248: max = 8;
249: break;
250: case Canvas.KEY_NUM9:
251: max = 9;
252: break;
253: }
254: if (max > 0 && menuCmds.length >= max) {
255: menuLayer.subCommandSelected(menuCmds[max - 1]);
256: }
257: }
258: return true;
259: }
260:
261: /**
262: * Cleans up the display when the cascaded menu is dismissed.
263: * Removes the layer with the menu and requests the display to be
264: * repainted.
265: */
266: public void dismiss() {
267: selI = scrollIndex = 0;
268:
269: if (owner != null && scrollInd != null
270: && scrollInd.scrollable == this ) {
271: owner.removeLayer(scrollInd);
272: }
273: }
274:
275: public void setScrollInd(ScrollIndLayer scrollInd) {
276: if (ScrollIndSkin.MODE != ScrollIndResourcesConstants.MODE_BAR) {
277: super .setScrollInd(scrollInd);
278: }
279: }
280:
281: protected void initialize() {
282: super .initialize();
283: bounds[X] = 0; // set in alignMenu()
284: bounds[Y] = 0; // set in alignMenu()
285: bounds[W] = MenuSkin.WIDTH / 2;
286: bounds[H] = MenuSkin.HEIGHT - MenuSkin.ITEM_TOPOFFSET;
287: }
288:
289: protected void alignMenu() {
290: switch (MenuSkin.ALIGN_X) {
291: case Graphics.LEFT:
292: bounds[X] = 0;
293: break;
294: case Graphics.HCENTER:
295: bounds[X] = (ScreenSkin.WIDTH - bounds[W]) / 2;
296: break;
297: case Graphics.RIGHT:
298: default:
299: bounds[X] = ScreenSkin.WIDTH - bounds[W] - MenuSkin.WIDTH
300: + 5;
301: break;
302: }
303: if (bounds[X] < 0) {
304: bounds[X] = 0;
305: }
306: switch (MenuSkin.ALIGN_Y) {
307: case Graphics.TOP:
308: bounds[Y] = 0;
309: break;
310: case Graphics.VCENTER:
311: bounds[Y] = (ScreenSkin.HEIGHT - SoftButtonSkin.HEIGHT - bounds[H]) / 2;
312: break;
313: case Graphics.BOTTOM:
314: default:
315: bounds[Y] = ScreenSkin.HEIGHT - SoftButtonSkin.HEIGHT
316: - bounds[H];
317: break;
318: }
319: if (bounds[Y] < 0) {
320: bounds[Y] = 0;
321: }
322: }
323:
324: protected void paintBody(Graphics g) {
325:
326: if (menuCmds == null) {
327: return;
328: }
329:
330: int y = 0;
331:
332: for (int cmdIndex = scrollIndex; (cmdIndex < menuCmds.length)
333: && (cmdIndex - scrollIndex < MenuSkin.MAX_ITEMS); cmdIndex++) {
334:
335: if (cmdIndex == selI) {
336: if (MenuSkin.IMAGE_ITEM_SEL_BG != null) {
337: // We want to draw the selected item background
338: CGraphicsUtil
339: .draw3pcsBackground(
340: g,
341: 3,
342: ((selI - scrollIndex) * MenuSkin.ITEM_HEIGHT)
343: + MenuSkin.IMAGE_BG[0]
344: .getHeight(),
345: bounds[W] - 3,
346: MenuSkin.IMAGE_ITEM_SEL_BG);
347: } else {
348: g.setColor(MenuSkin.COLOR_BG_SEL);
349: g
350: .fillRoundRect(
351: MenuSkin.ITEM_ANCHOR_X - 2,
352: ((selI - scrollIndex) * MenuSkin.ITEM_HEIGHT),
353: MenuSkin.FONT_ITEM_SEL
354: .stringWidth(menuCmds[cmdIndex]
355: .getLabel()) + 4,
356: MenuSkin.ITEM_HEIGHT, 3, 3);
357: }
358: }
359:
360: if (cmdIndex < 9) {
361: g.setFont((selI == cmdIndex) ? MenuSkin.FONT_ITEM_SEL
362: : MenuSkin.FONT_ITEM);
363: g
364: .setColor((selI == cmdIndex) ? MenuSkin.COLOR_INDEX_SEL
365: : MenuSkin.COLOR_INDEX);
366: g.drawString("" + (cmdIndex + 1),
367: MenuSkin.ITEM_INDEX_ANCHOR_X, y, Graphics.TOP
368: | Graphics.LEFT);
369: }
370:
371: g.setFont(MenuSkin.FONT_ITEM);
372: g.setColor((selI == cmdIndex) ? MenuSkin.COLOR_ITEM_SEL
373: : MenuSkin.COLOR_ITEM);
374: g.drawString(menuCmds[cmdIndex].getLabel(),
375: MenuSkin.ITEM_ANCHOR_X, y, Graphics.TOP
376: | Graphics.LEFT);
377:
378: y += MenuSkin.ITEM_HEIGHT;
379: }
380: g.setColor(0);
381: g.drawRect(0, 0, bounds[W] - 1, bounds[H] - 1);
382: }
383:
384: /**
385: * Update bounds of layer
386: * @param layers - current layer can be dependant on this parameter
387: */
388: public void update(CLayer[] layers) {
389: alignMenu();
390: }
391: }
|