OptionsMenu.java

/*
 * Copyright © 2011 Nokia Corporation. All rights reserved.
 * Nokia and Nokia Connecting People are registered trademarks of Nokia Corporation. 
 * Oracle and Java are trademarks or registered trademarks of Oracle and/or its
 * affiliates. Other product and company names mentioned herein may be trademarks
 * or trade names of their respective owners. 
 * See LICENSE.TXT for license information.
 */ 

package com.nokia.example.wordpress.components;

import com.nokia.example.wordpress.WordpressMidlet;
import com.nokia.example.wordpress.helpers.KeyCodes;
import com.nokia.example.wordpress.views.Visual;
import java.util.Vector;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;

/**
 * Options menu custom UI control
 */
public class OptionsMenu {

    protected WordpressMidlet midlet;
    private static final Font DEFAULT_FONT = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_SMALL);
    private static final Font SOFTKEY_FONT = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_SMALL);
    private final int softkeyBarHeight = SOFTKEY_FONT.getHeight();
    private final int menuItemHeight = DEFAULT_FONT.getHeight();
    private static final int margin = 10;
    private int screenHeight;
    private int screenWidth;
    public int x;
    public int y;
    public int width;
    public int height;
    private int rectWidth;
    private int rectHeight;
    private int highlightWidth;
    /**
     * Vector of Items
     */
    private Vector menuitems;
    private boolean showSelf = false;
    private int currentHighlight = 0;
    private boolean backgroundDimmed;
    private Item selectedMenuItem = null;
    /**
     * Animation parameter. Target y-coordinate for the top left menu corner.
     */
    public int targetY;
    /**
     * Animation parameter. Current y-coordinate of the top left menu corner.
     * This is animated towards targetY.
     */
    public int currentY;
    /**
     * Animation parameter. Animation goes from 0 to 1.
     */
    private float transitionCounter = 0;

    /**
     * Class describing one menu item, contains a name and info what
     * to call when user selects it.
     */
    private class Item {

        /**
         * Constructor
         * @param name Menu item name
         * @param callback Callback to call when selected
         */
        Item(String name, MenuItemCallback callback) {
            this.name = name;
            this.callback = callback;
        }

        public void select() {
            callback.select();
        }
        MenuItemCallback callback;
        String name;
    }

    /**
     * Constructor. Takes the dimensions of the target screen as parameters.
     * @param screenWidth
     * @param screenHeight
     */
    public OptionsMenu(int screenWidth, int screenHeight) {
        this.midlet = WordpressMidlet.getInstance();
        this.screenWidth = screenWidth;
        this.screenHeight = screenHeight;
        x = 0;
        menuitems = new Vector();
  }

    /**
     * Calculates the rectangle needed to display the menuitems.
     */
    private void updateDimensions() {
        rectHeight = menuitems.size() * menuItemHeight + 2 * margin;
        height = rectHeight;
        y = screenHeight - rectHeight - softkeyBarHeight;
        currentY = y;
        targetY = y;

        int l = menuitems.size();
        for (int i = 0; i < l; i++) {
            //MenuItem item = (MenuItem) menuitems.elementAt(i);
            Item item = (Item) menuitems.elementAt(i);
            int item_width = DEFAULT_FONT.stringWidth(item.name);
            if (item_width > rectWidth) {
                rectWidth = item_width;
            }
        }
        highlightWidth = rectWidth;
        rectWidth += 2 * margin;
        width = rectWidth;

    }

    /**
     * Adds one menuitem.
     * @param name Menu item name
     * @param callback Callback
     */
    protected void addItem(String name, MenuItemCallback callback) {
        menuitems.addElement(new Item(name, callback));
        updateDimensions();
    }

    /**
     * Utility for adding the Exit.
     */
    protected void addExitItem() {
        addItem("Exit", new MenuItemCallback() {

            public void select() {
                WordpressMidlet.getInstance().commandExit();
            }
        });
    }

    /**
     * Dims the screen, used before drawing the menu.
     * @param g
     */
    private void renderDimmedBackground(Graphics g) {
        int[] pixels = new int[screenWidth * screenHeight];
        for (int i = 0; i < screenWidth * screenHeight; i++) {
            pixels[i] = 0xaa000000;
        }
        g.drawRGB(pixels, 0, screenWidth, 0, 0, screenWidth, screenHeight - softkeyBarHeight, true);
        pixels = null;
    }

    /**
     * Draws the menu.
     * @param g
     */
    public void draw(Graphics g) {
        if (!showSelf) {
            return;
        }

        if (backgroundDimmed == false) {
            backgroundDimmed = true;
            renderDimmedBackground(g);
        }

        // Softkey area
        g.setFont(SOFTKEY_FONT);
        g.setColor(Visual.BACKGROUND_COLOR);
        g.fillRect(x, screenHeight - softkeyBarHeight, screenWidth, softkeyBarHeight);
        g.setColor(0);
        g.drawString("Select", screenWidth / 2, screenHeight, g.HCENTER | g.BOTTOM);
        g.drawString("Back", screenWidth, screenHeight, g.RIGHT | g.BOTTOM);

        // Menu item box
        g.setColor(0);

        int prevClipX = g.getClipX();
        int prevClipY = g.getClipY();
        int prevClipWidth = g.getClipWidth();
        int prevClipHeight = g.getClipHeight();
        // Prevent painting over softkeys
        g.setClip(x, y, rectWidth, rectHeight);

        int y = currentY;
        g.fillRoundRect(x, y, rectWidth, rectHeight, 10, 10);

        g.setColor(0);
        g.setFont(DEFAULT_FONT);
        y += margin;
        for (int i = 0; i < menuitems.size(); i++) {
            Item item = (Item) menuitems.elementAt(i);
            if (i == currentHighlight) {
                g.setColor(0x890000);
                g.fillRect(margin - 2, y, highlightWidth + 2, DEFAULT_FONT.getHeight());
            }
            g.setColor(0xFFFFFF);
            g.drawString(item.name, margin, y, g.LEFT | g.TOP);
            y += menuItemHeight;
        }

        g.setClip(prevClipX, prevClipY, prevClipWidth, prevClipHeight);

    }

    /**
     * Returns the visibility information.
     * @return
     */
    public boolean isVisible() {
        return showSelf;
    }

    /**
     * Animates the menu. Should be called in regular intervals.
     * @return true if something was animated and the screen should be updated, false otherwise.
     */
    public boolean animate() {
        if (currentY == targetY) {
            return false;
        }

        transitionCounter += 0.20f;
        if (transitionCounter > 1f) {
            transitionCounter = 1f;
        }

        if (currentY > targetY) {
            float progress = -transitionCounter * (transitionCounter - 2);
            currentY = y + rectHeight - (int) (((float) rectHeight) * progress);
            if (currentY < targetY) {
                currentY = targetY;
            }
        }
        return true;

    }

    /**
     * Stars the animation to open the menu.
     */
    void appearAnimation() {
        currentY = screenHeight - softkeyBarHeight;
        targetY = y;
        transitionCounter = 0;
    }

    /**
     * Starts the animation to close the menu. Currently the menu
     * instantly disappears.
     * @param selectedItem
     */
    private void disappearAnimation(Item selectedItem) {
        showSelf = false;
        if (selectedItem != null) {
            selectedItem.select();
        }
    }

    /**
     * Key event handler. Opens and closes the menu. Selects the focused menu item.
     * @param keyCode
     * @return
     */
    public boolean notifyKeyEvents(int keyCode) {

        switch (keyCode) {
            case KeyCodes.LEFT_SOFTKEY:
                if (!showSelf) {
                    showSelf = true;
                    backgroundDimmed = false;
                    appearAnimation();
                } else {
                    disappearAnimation((Item) menuitems.elementAt(currentHighlight));
                }
                return true;
            case KeyCodes.DOWN:
                if (showSelf) {
                    if (currentHighlight == (menuitems.size() - 1)) {
                        currentHighlight = 0;
                    } else {
                        currentHighlight++;
                    }
                    return true;
                }
                break;
            case KeyCodes.UP:
                if (showSelf) {
                    if (currentHighlight == 0) {
                        currentHighlight = (menuitems.size() - 1);
                    } else {
                        currentHighlight--;
                    }
                    return true;
                }
                break;
            case KeyCodes.MIDDLE_SOFTKEY:
                if (showSelf) {
                    disappearAnimation((Item) menuitems.elementAt(currentHighlight));
                    return true;
                }
                break;
            case KeyCodes.RIGHT_SOFTKEY:
                if (showSelf) {
                    disappearAnimation(null);
                    return true;
                }
                break;
        }
        return false;
    }
}