Menu.java

/**
 * Copyright (c) 2012-2013 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.amaze.ui;

import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;

import com.nokia.example.amaze.Main;

/**
 * A generic game menu.
 */
public class Menu {
    // Constants
    private static final int MAX_MENU_ITEM_COUNT = 4;

    // Members
    private Listener _listener = null;
    private MenuItem[] _items = null;
    private Font _font = null;
    private final Image _itemBackgroundImage = Main.makeImage("/graphics/menu-item-bg.png");
    private int _width;
    private int _height;
    private int _fontHeight;
    private int _pressedIndex = -1;

    /**
     * Constructor.
     * @param width
     * @param height
     */
    public Menu(Listener listener, int width, int height) {
        _items = new MenuItem[MAX_MENU_ITEM_COUNT];
        setSize(width, height);
        _listener = listener;
        _font = Font.getDefaultFont();
        _fontHeight = _font.getHeight();
    }

    /**
     * Sets the menu size.
     * @param width
     * @param height
     */
    public void setSize(int width, int height) {
        _width = width;
        _height = height;
    }

    /**
     * Adds a new item to the menu with the given text.
     * @param text The text of the menu item.
     * @return True if the menu item was added successfully, false otherwise.
     */
    public boolean addItem(String text) {
        for (int i = 0; i < MAX_MENU_ITEM_COUNT; ++i) {
            if (_items[i] == null) {
                MenuItem item = new MenuItem(text);
                _items[i] = item;
                return true;
            }
        }
        
        return false;
    }

    /**
     * For convenience.
     * Adds the given menu item to the menu.
     * @param item The item to add.
     * @return True if the menu item was added successfully, false otherwise.
     */
    public boolean addItem(MenuItem item) {
        for (int i = 0; i < MAX_MENU_ITEM_COUNT; ++i) {
            if (_items[i] == null) {
                _items[i] = item;
                return true;
            }
        }
        
        return false;
    }    

    /**
     * Returns a menu item at the given index or null if not found.
     * @param index The index of the menu item.
     * @return A menu item or null.
     */
    public MenuItem itemAt(final int index) {
        if (_items != null && index >= 0 && index < menuItemCount()) {
            return (MenuItem)_items[index];
        }
        
        return null;
    }

    /**
     * Clears all menu items.
     */
    public void clear() {
        for (int i = 0; i < MAX_MENU_ITEM_COUNT; ++i) {
            _items[i] = null;
        }
    }

    /**
     * Handles "on pressed" events.
     * @param x
     * @param y
     */
    public void onPressed(final int x, final int y) {
        final int count = menuItemCount();
        final int startY = _height / 2 - count * MenuItem.MENU_ITEM_HEIGHT / 2;
        
        if (y < startY || y > count * MenuItem.MENU_ITEM_HEIGHT + startY) {
            setPressed(_pressedIndex, false);
            _pressedIndex = -1;
            return;
        }
        
        int prevPressedIndex = _pressedIndex;
        _pressedIndex = (y - startY) / MenuItem.MENU_ITEM_HEIGHT;
        
        if (prevPressedIndex != _pressedIndex) {
            setPressed(prevPressedIndex, false);
        }
        
        if (!setPressed(_pressedIndex, true)) {
            _pressedIndex = -1;
        }
    }

    /**
     * Handles "on released" events.
     * @param x
     * @param y
     */
    public void onReleased(final int x, final int y) {
        final int count = menuItemCount();
        final int startY = _height / 2 - count * MenuItem.MENU_ITEM_HEIGHT / 2;
        
        if (y < startY || y > count * MenuItem.MENU_ITEM_HEIGHT + startY) {
            setPressed(_pressedIndex, false);
            _pressedIndex = -1;
            return;
        }
        
        if (_listener != null
            && _pressedIndex == (y - startY) / MenuItem.MENU_ITEM_HEIGHT)
        {
            System.out.println("Menu::onReleased(): " + _pressedIndex);
            _listener.onMenuItemSelected(_pressedIndex, _items[_pressedIndex]);
        }
        
        setPressed(_pressedIndex, false);
        _pressedIndex = -1;
    }

    /**
     * Paints the menu.
     * @param graphics
     */
    public void paint(Graphics graphics) {
        final int count = menuItemCount();
        MenuItem item = null;
        String text = null;
        int y = _height / 2 - count * MenuItem.MENU_ITEM_HEIGHT / 2;
        
        for (int i = 0; i < count; ++i) {
            item = (MenuItem)_items[i];
            text = item.text();
            
            if (text == null) {
                continue;
            }
            
            if (item.pressed()) {
                graphics.setColor(MenuItem.COLOR);
                graphics.fillRect(0, y, _width, MenuItem.MENU_ITEM_HEIGHT);
                graphics.setColor(0x00000000);
            }
            else {
                graphics.drawImage(_itemBackgroundImage, 0, y,
                                   Graphics.TOP | Graphics.LEFT);
                graphics.setColor(MenuItem.COLOR);
            }
            
            graphics.drawString(text, (_width - _font.stringWidth(text)) / 2,
                    (y + MenuItem.MENU_ITEM_HEIGHT / 2) - _fontHeight / 2,
                    Graphics.TOP | Graphics.LEFT);
            y += MenuItem.MENU_ITEM_HEIGHT;
        }
    }

    /** 
     * @return The number of items in the menu.
     */
    private final int menuItemCount() {
        int retval = 0;
        
        for (int i = 0; i < MAX_MENU_ITEM_COUNT; ++i) {
            if (_items[i] != null) {
                retval++;
            }
            else {
                break;
            }
        }
        
        return retval;
    }

    /**
     * Sets the menu item with the given index pressed/unpressed.
     * @param index The index of a menu item-
     * @param pressed Whether to item should be pressed or unpressed.
     * @return True if the pressed property of the item was set successfully,
     *         false otherwise.
     */
    private boolean setPressed(int index, boolean pressed) {
        if (index >= 0 && index < menuItemCount()) {
            MenuItem item = (MenuItem)_items[index];
            
            if (item != null && !item.disabled()) {
                item.setPressed(pressed);
                return true;
            }
        }
        
        return false;
    }

    /**
     * Menu listener interface. 
     */
    public interface Listener {
        /**
         * Notification for menu item selected event.
         * @param index The index of the item in the collection.
         * @param item The selected menu item instance.
         */
        void onMenuItemSelected(int index, MenuItem item);
    }
}