Menu.java

/*
 * Copyright © 2012 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 Object[] _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 Object[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);
		}
		
		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 {
		void onMenuItemSelected(int index);
	}
}