FileDocCategorySizeDatePackage
MenuLayer.javaAPI DocphoneME MR2 API (J2ME)23283Wed May 02 18:00:20 BST 2007com.sun.midp.chameleon.layers

MenuLayer

public class MenuLayer extends ScrollablePopupLayer
A special popup layer which implements a system menu. The system menu is a collection of commands, both screen (Back, Exit, etc) and item specific commands.

Fields Summary
protected Command[]
menuCmds
The list of Commands to display in the menu.
protected int
selI
The currently selected index in the menu.
protected int
scrollIndex
The number of commands which have been scrolled off the top of the menu, normally 0 unless there are more commands than can fit on the menu.
protected SoftButtonLayer
btnLayer
The SoftButtonLayer maintains the overall set of commands and their associated listeners.
protected CascadeMenuLayer
cascadeMenu
A cascading menu which holds commands for a SubMenuCommand.
protected boolean
cascadeMenuUp
A flag indicating if a cascading menu is visible.
private static final int
PRESS_OUT_OF_BOUNDS
pointer pressed outside of the menuLayer's bounds
private static final int
PRESS_ON_TITLE
pointer pressed on the menuLayer's title area
private int
itemIndexWhenPressed
variable used in pointerInput handling
Constructors Summary
public MenuLayer()
Construct a new system menu layer.

 
    
               
      
        super();
        setBackground(MenuSkin.IMAGE_BG, MenuSkin.COLOR_BG);
        cascadeMenu = new CascadeMenuLayer();
    
Methods Summary
protected voidalignMenu()
Aligns the menu to the current screen.

        
        bounds[W] = MenuSkin.WIDTH;

        switch (MenuSkin.ALIGN_X) {
            case Graphics.LEFT:
                bounds[X] = 0;
                break;
            case Graphics.HCENTER:
                bounds[X] = (ScreenSkin.WIDTH - bounds[W]) / 2;
                break;
            case Graphics.RIGHT:
            default:
                bounds[X] = ScreenSkin.WIDTH - bounds[W];
                break;
        }
        switch (MenuSkin.ALIGN_Y) {
            case Graphics.TOP:
                bounds[Y] = 0;
                break;
            case Graphics.VCENTER:
                bounds[Y] = (ScreenSkin.HEIGHT - SoftButtonSkin.HEIGHT -
                    bounds[H]) / 2;
                break;
            case Graphics.BOTTOM:
            default:
                bounds[Y] = ScreenSkin.HEIGHT - SoftButtonSkin.HEIGHT -
                    bounds[H];
                break;
        }
    
public voiddismiss()
Cleans up the display when the cascaded menu is dismissed. Removes the layer with the menu and requests the display to be repainted.

        dismissCascadeMenu();
        selI = scrollIndex = 0;
    
public voiddismissCascadeMenu()
Cleans up the display when the cascaded menu is dismissed. Removes the layer with the menu and requests the display to be repainted.

        if (owner != null && cascadeMenuUp) {
            cascadeMenuUp = false;
            cascadeMenu.dismiss();

            setScrollInd(ScrollIndLayer.getInstance(ScrollIndSkin.MODE));

            owner.removeLayer(cascadeMenu);
            requestRepaint();
        }
    
public intgetIndex()
Gets index of highlighted command.

return
highlighted index

        return selI;
    
protected voidinitialize()
Initializes the menu parameters.

        super.initialize();
        bounds[X] = 0; // set in alignMenu()
        bounds[Y] = 0; // set in alignMenu()
        bounds[W] = MenuSkin.WIDTH;
        bounds[H] = MenuSkin.HEIGHT;
    
private intitemIndexAtPointerPosition(int x, int y)
Helper function to determine the itemIndex at the x,y position

param
x,y pointer coordinates in menuLayer's space (0,0 means left-top corner) both value can be negative as menuLayer handles the pointer event outside its bounds
return
menuItem's index since 0, or PRESS_OUT_OF_BOUNDS, PRESS_ON_TITLE

        int ret;
        if (!containsPoint(x + bounds[X], y + bounds[Y])) {
            ret = PRESS_OUT_OF_BOUNDS; 
        } else if (y < MenuSkin.ITEM_TOPOFFSET) {
            ret = PRESS_ON_TITLE;
        } else {
            ret = (y - MenuSkin.ITEM_TOPOFFSET) / MenuSkin.ITEM_HEIGHT;
        }
        return ret;
    
public booleankeyInput(int type, int keyCode)
Handles key input from a keypad. Parameters describe the type of key event and the platform-specific code for the key. (Codes are translated using the lcdui.Canvas)

param
type the type of key event
param
keyCode the numeric code assigned to the key
return
true if the input has been processed by this method, otherwise false (soft menu keys)

        // The system menu will absorb all key presses except
        // for the soft menu keys - that is, it will always
        // return 'true' indicating it has handled the key
        // event except for the soft button keys for which it
        // returns 'false'
        
        if (keyCode == EventConstants.SOFT_BUTTON1 || 
            keyCode == EventConstants.SOFT_BUTTON2) {
            return false;
        }
        
        if (type != EventConstants.PRESSED && type != EventConstants.REPEATED) {
            return true;
        }
        
        if (keyCode == Constants.KEYCODE_UP) {
            if (selI > 0) {
                selI--;
                if (selI < scrollIndex && scrollIndex > 0) {
                    scrollIndex--;
                }
                updateScrollIndicator();
                requestRepaint();
            }
        } else if (keyCode == Constants.KEYCODE_DOWN) {
            if (selI < (menuCmds.length - 1)) {
                selI++;
                if (selI >= scrollIndex + MenuSkin.MAX_ITEMS &&
                    scrollIndex < (menuCmds.length - MenuSkin.MAX_ITEMS)) {
                    scrollIndex++;
                } 
                updateScrollIndicator();
                requestRepaint();
            }
        } else if (keyCode == Constants.KEYCODE_LEFT) {
            // IMPL_NOTE : Need to add support for a "right popping"
            // sub menu if the system menu is placed on the left
            // side of the screen instead of the right
            if (btnLayer != null) { 
                showSubMenu(selI);
            }
        } else if (keyCode == Constants.KEYCODE_SELECT) {
            if (btnLayer != null && !showSubMenu(selI)) {
                btnLayer.commandSelected(menuCmds[selI]);
            }
        } else {
            int max = 0;
            switch (keyCode) {
                case Canvas.KEY_NUM1:
                    max = 1;
                    break;
                case Canvas.KEY_NUM2:
                    max = 2;
                    break;
                case Canvas.KEY_NUM3:
                    max = 3;
                    break;
                case Canvas.KEY_NUM4:
                    max = 4;
                    break;
                case Canvas.KEY_NUM5:
                    max = 5;
                    break;
                case Canvas.KEY_NUM6:
                    max = 6;
                    break;
                case Canvas.KEY_NUM7:
                    max = 7;
                    break;
                case Canvas.KEY_NUM8:
                    max = 8;
                    break;
                case Canvas.KEY_NUM9:
                    max = 9;
                    break;
            }
            if (max > 0 && menuCmds.length >= max) {
                if (btnLayer != null && !showSubMenu(max - 1)) {
                    btnLayer.commandSelected(menuCmds[max - 1]);
                }
            }
        }
        return true;
    
protected voidpaintBody(Graphics g)
Renders the body of the menu.

param
g the graphics context to be updated

        
        if (MenuSkin.TEXT_TITLE != null) {
            // IMPL_NOTE enforce MenuSkin.TITLE_MAXWIDTH based on
            // title value and font, add '...' to titles which
            // are too long to show
            g.setFont(MenuSkin.FONT_TITLE);
            g.setColor(MenuSkin.COLOR_TITLE);
            g.drawString(MenuSkin.TEXT_TITLE,
                         MenuSkin.TITLE_X,
                         MenuSkin.TITLE_Y,
                         Graphics.TOP | MenuSkin.TITLE_ALIGN);
        }
        
        if (menuCmds != null) {
                       
            int y = MenuSkin.ITEM_TOPOFFSET;
            int x = 0;
            Image arrow = null;
            
            for (int cmdIndex = scrollIndex; 
                (cmdIndex < menuCmds.length) 
                    && (cmdIndex - scrollIndex < MenuSkin.MAX_ITEMS);
                cmdIndex++)
            {
                
                if (menuCmds[cmdIndex] instanceof SubMenuCommand) {
                    arrow = MenuSkin.IMAGE_SUBMENU_ARROW;
                    if (cmdIndex == selI && !cascadeMenuUp) {
                        arrow = MenuSkin.IMAGE_SUBMENU_ARROW_HL;
                    }
                    if (arrow != null) {
                        x = arrow.getWidth() + 2;
                    }
                }
                
                if (cmdIndex == selI && !cascadeMenuUp) {
                    if (MenuSkin.IMAGE_ITEM_SEL_BG != null) {
                        // We want to draw the selected item background
                        CGraphicsUtil.draw3pcsBackground(g, 3, 
                            ((selI - scrollIndex) * MenuSkin.ITEM_HEIGHT) + 
                                MenuSkin.IMAGE_BG[0].getHeight(),
                            bounds[W] - 3,
                            MenuSkin.IMAGE_ITEM_SEL_BG);
                    } else {
                        g.setColor(MenuSkin.COLOR_BG_SEL);
                        g.fillRoundRect(MenuSkin.ITEM_ANCHOR_X - 2,
                            ((selI - scrollIndex) * MenuSkin.ITEM_HEIGHT) + 
                                MenuSkin.ITEM_TOPOFFSET,
                            MenuSkin.FONT_ITEM_SEL.stringWidth(
                                menuCmds[cmdIndex].getLabel()) + 4 + x,
                            MenuSkin.ITEM_HEIGHT,
                            3, 3);
                    }
                }
                
                if (cmdIndex < 9) {
                    g.setFont((selI == cmdIndex) ?
                               MenuSkin.FONT_ITEM_SEL :
                               MenuSkin.FONT_ITEM);
                    g.setColor((selI == cmdIndex) ? 
                               MenuSkin.COLOR_INDEX_SEL :
                               MenuSkin.COLOR_INDEX);
                    g.drawString("" + (cmdIndex + 1),
                                 MenuSkin.ITEM_INDEX_ANCHOR_X,
                                 y, Graphics.TOP | Graphics.LEFT);
                }
                
                g.setFont(MenuSkin.FONT_ITEM);                
                g.setColor((selI == cmdIndex) ? MenuSkin.COLOR_ITEM_SEL :
                           MenuSkin.COLOR_ITEM);
                 
                if (arrow != null) {
                    g.drawImage(arrow, MenuSkin.ITEM_ANCHOR_X, y + 2,
                                Graphics.TOP | Graphics.LEFT);
                    arrow = null;
                }
                g.drawString(menuCmds[cmdIndex].getLabel(),
                             MenuSkin.ITEM_ANCHOR_X + x,
                             y, Graphics.TOP | Graphics.LEFT);
                            
                x = 0;
                y += MenuSkin.ITEM_HEIGHT;                 
            }
        }       
    
public booleanpointerInput(int type, int x, int y)
Handle input from a pen tap. Parameters describe the type of pen event and the x,y location in the layer at which the event occurred. Important : the x,y location of the pen tap will already be translated into the coordinate space of the layer.

param
type the type of pen event
param
x the x coordinate of the event
param
y the y coordinate of the event

        switch (type) {
        case EventConstants.PRESSED:
            itemIndexWhenPressed =  itemIndexAtPointerPosition(x, y);

            // dismiss the menu layer if the user pressed outside the menu
            if (itemIndexWhenPressed == PRESS_OUT_OF_BOUNDS) {
                if (btnLayer != null) {
                    btnLayer.dismissMenu();
                }
            } else if (itemIndexWhenPressed >= 0) { // press on valid menu item
                selI = scrollIndex + itemIndexWhenPressed;
                requestRepaint();
                // if (btnLayer != null) btnLayer.serviceRepaints();
            }
            break;
        case EventConstants.RELEASED:
            int itemIndexWhenReleased = itemIndexAtPointerPosition(x, y);
            
            if (itemIndexWhenReleased == itemIndexWhenPressed) {
                if (itemIndexWhenPressed >= 0) {
                    if (btnLayer != null && !showSubMenu(selI)) {
                        if (selI >= 0 && selI < menuCmds.length) {
                            btnLayer.commandSelected(menuCmds[selI]);
                        }
                    }
                }
            }

            // remember to reset the variables
            itemIndexWhenPressed = PRESS_OUT_OF_BOUNDS;
            break;
        }
        // return true always as menuLayer will capture all of the pointer inputs
        return true;  
    
public voidscrollContent(int scrollType, int thumbPosition)
Scroll content inside of the Menu.

param
scrollType scrollType. Scroll type can be one of the following
see
ScrollBarLayer.SCROLL_NONE
see
ScrollBarLayer.SCROLL_PAGEUP
see
ScrollBarLayer.SCROLL_PAGEDOWN
see
ScrollBarLayer.SCROLL_LINEUP
see
ScrollBarLayer.SCROLL_LINEDOWN or
see
ScrollBarLayer.SCROLL_THUMBTRACK
param
thumbPosition

        if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
            Logging.report(Logging.INFORMATION, 
                           LogChannels.LC_HIGHUI,
                           "MenuLayer.scrollContent scrollType=" + scrollType + 
                           " thumbPosition=" + thumbPosition); 
        }
        // keep old scrollIndex
        int oldScrollIndex = scrollIndex;
        
        switch (scrollType) {
        case ScrollBarLayer.SCROLL_PAGEUP:
            uScrollViewport(Canvas.UP);
            break;
        case ScrollBarLayer.SCROLL_PAGEDOWN:
            uScrollViewport(Canvas.DOWN);
            break;
        case ScrollBarLayer.SCROLL_LINEUP:
            uScrollByLine(Canvas.UP);
            break;
        case ScrollBarLayer.SCROLL_LINEDOWN:
            uScrollByLine(Canvas.DOWN);
            break;
        case ScrollBarLayer.SCROLL_THUMBTRACK:
            uScrollAt(thumbPosition);
            break;
        default:
            break;
        }
        // only if scroll index has been changed do update
        if (oldScrollIndex != scrollIndex && scrollIndex >= 0) {

            // correct selI if required.
            // The selected item always should be on the screen
            if (selI < scrollIndex) {
                selI = scrollIndex;
            } else if (selI >= (scrollIndex + MenuSkin.MAX_ITEMS)) {
                selI = scrollIndex + MenuSkin.MAX_ITEMS - 1;
            }

            updateScrollIndicator();
            requestRepaint();
        }
    
public voidsetMenuCommands(Command[] cmdList, SoftButtonLayer btnLayer, int index)
Called typically by the SoftButtonLayer to establish the set of Commands to display on this system menu. This method will create a new copy of the array of commands passed in.

param
cmdList the set of commands to display in the menu (the commands should already be sorted by priority)
param
btnLayer the SoftButtonLayer to notify of any command selections
param
index the command index has to be highlighted. If index exceeds the number of commands the 1st command has to be highlighted.

        if (cmdList.length == 1 && cmdList[0] instanceof SubMenuCommand) {
            cmdList = ((SubMenuCommand)cmdList[0]).getSubCommands();
        }
        this.menuCmds = new Command[cmdList.length];
        System.arraycopy(cmdList, 0, this.menuCmds, 0, cmdList.length);
        // If we have fewer commands than fill up the menu,
        // we shorten the menu's height
        if (menuCmds.length < MenuSkin.MAX_ITEMS) {
            bounds[H] = MenuSkin.HEIGHT - 
                ((MenuSkin.MAX_ITEMS - menuCmds.length) 
                    * MenuSkin.ITEM_HEIGHT);
        } else {
            bounds[H] = MenuSkin.HEIGHT;
        }
        alignMenu();           
        requestRepaint();

        this.btnLayer = btnLayer;
        
        selI = index < cmdList.length ? index : 0;
    
private booleanshowSubMenu(int index)
Shows the sub menu.

param
index the offset in the array of menu commands
return
true if submenu is shown, false - otherwise

        boolean ret = false;
        if (menuCmds[index] instanceof SubMenuCommand) {
            SubMenuCommand subMenu = (SubMenuCommand)menuCmds[index];
            cascadeMenu.setMenuCommands(subMenu.getSubCommands(), this);
            cascadeMenu.setAnchorPoint(bounds[X],
                                       bounds[Y] + MenuSkin.ITEM_TOPOFFSET + 
                                       ((index - scrollIndex) *
                                        MenuSkin.ITEM_HEIGHT));
            cascadeMenuUp = true;
            owner.addLayer(cascadeMenu);
            setScrollInd(ScrollIndLayer.getInstance(ScrollIndSkin.MODE));
            // IMPL_NOTE: fix layer inrteraction in removeLayer
            btnLayer.requestRepaint();

            selI = index;
            addDirtyRegion();
            ret = true;
        }
        return ret;
    
public voidsubCommandSelected(Command cmd)
Notifies listener that a command has been selected. Dismisses the cascaded menu and the button layer.

param
cmd the command that was selected

	Command c = menuCmds[selI];
        if (c instanceof SubMenuCommand) {
            btnLayer.dismissMenu();
            ((SubMenuCommand)c).notifyListener(cmd);
        }
    
voiduScrollAt(int position)
Perform a scrolling at the given position.

param
context position

        int viewableH =  MenuSkin.ITEM_HEIGHT * menuCmds.length;
        int viewportH =  MenuSkin.ITEM_HEIGHT * MenuSkin.MAX_ITEMS;
        
        int newY = (viewableH - viewportH) * position / 100;
        if (newY < 0) {
            newY = 0;
        } else if (newY > viewableH - viewportH) {
            newY = viewableH - viewportH;
        }
        scrollIndex = newY / MenuSkin.ITEM_HEIGHT;
    
private voiduScrollByLine(int dir)
Perform a line scrolling in the given direction. This method will attempt to scroll the view to show next/previous line.

param
dir the direction of the flip, either DOWN or UP

        switch(dir) {
        case Canvas.UP:
            if (scrollIndex > 0) {
                scrollIndex--;
            }
            break;
        case Canvas.DOWN:
            if (scrollIndex < (menuCmds.length - MenuSkin.MAX_ITEMS)) {
                scrollIndex++;
            }
            break;
        }
    
private voiduScrollViewport(int dir)
Perform a page flip in the given direction. This method will attempt to scroll the view to show as much of the next page as possible. It uses the locations and bounds of the items on the page to best determine a new location - taking into account items which may lie on page boundaries as well as items which may span several pages.

param
dir the direction of the flip, either DOWN or UP

        switch (dir) {
        case Canvas.UP:
            scrollIndex -= MenuSkin.MAX_ITEMS - 1;
            if (scrollIndex < 0) {
                scrollIndex = 0;
            }
            break;
        case Canvas.DOWN:
            scrollIndex += MenuSkin.MAX_ITEMS - 1;
            if (scrollIndex > menuCmds.length - MenuSkin.MAX_ITEMS) {
                scrollIndex = menuCmds.length - MenuSkin.MAX_ITEMS;
            }
            break;
        }
    
public voidupdate(CLayer[] layers)
Update bounds of layer

param
layers - current layer can be dependant on this parameter

        alignMenu();
        if (owner != null && cascadeMenuUp) {
            cascadeMenu.update(layers);
            if (btnLayer != null) {
                showSubMenu(selI);
            }
        }
        super.update(layers);
    
public voidupdateScrollIndicator()
Updates the scroll indicator.

        if (scrollInd != null) {
            if (menuCmds.length > MenuSkin.MAX_ITEMS) {
                scrollInd.setVerticalScroll(
                  (scrollIndex * 100) / (menuCmds.length - MenuSkin.MAX_ITEMS),
                  (MenuSkin.MAX_ITEMS * 100) / menuCmds.length);
            } else {
                scrollInd.setVerticalScroll(0, 100);
            }
            super.updateScrollIndicator();
        }