// Copyright 1996, Marimba Inc. All Rights Reserved.


// @(#)PopupMenu.java, 1.26, 12/19/96





package marimba.gui;





import marimba.persist.*;


import java.awt.*;





/**


 * A popup menu, it displays a scrollbar


 * using a scrolling container if necessary.


 *


 * @author	Klaas Waslander


 * @version 	1.26, 12/19/96


 */


public class PopupMenu extends PopupWidget {


    /**


     * The currently selected item.


     * If no item is selected, current is null.


     * @see #getCurrentItem


     * @see #select


     * @see #nextItem


     * @see #previousItem


     */


    public PopupMenuItemWidget  current;





    /**


     * The owner of this widget. When the menu is being popped


     * down it will let this widget request focus.


     * @see #getOwner


     * @see #setOwner


     */


    public Widget  owner;





    /**


     * The items can be drawn with or without


     * a focus rectangle around it when they


     * are selected.


     * @see #getItemFocus


     * @see #setItemFocus


     */


    public boolean  itemFocus = false;





    /**


     * The maximum number of visible items, if there are more


     * items a scrollbar is used. If this number is zero or


     * smaller the max number of visible items is determined


     * by the size of the menu.


     * @see #getMaxVisible


     * @see #setMaxVisible


     */


    public int  maxVisible = 0;





    /**


     * The popupmenu uses a scrolling container to


     * display the popup menu items.


     */


    protected ScrollingContainerWidget  container = new ScrollingContainerWidget();





    /** Has the user dragged with the mouse? */


    boolean  dragged = false;





    /** Should the menu popdown when the MOUSE_UP event is detected? */


    boolean  mouseUpPopDown = false;





    /** indicates whether popdown() is being executed for LOST_FOCUS event */


    boolean  poppingDown = false;





    /**


     * Get the owner.


     * @see #owner


     */


    public Widget getOwner() {


	return owner;


    }





    /**


     * Set the owner.


     * @see #owner


     */


    public void setOwner(Widget owner) {


	this.owner = owner;


    }





    /**


     * Check whether selected items are painted


     * with a focus rectangle around it.


     * @see #itemFocus


     */


    public boolean getItemFocus() {


	return itemFocus;


    }





    /**


     * Let the selected item paint with a focus


     * rectangle or not.


     * @see #itemFocus


     */


    public void setItemFocus(boolean itemFocus) {


	this.itemFocus = itemFocus;


    }





    /**


     * Get the maximum number of visible items.


     * @see #maxVisible


     */


    public int getMaxVisible() {


	return maxVisible;


    }





    /**


     * Set the maximum number of visible items.


     * @see #maxVisible


     */


    public void setMaxVisible(int maxVisible) {


	this.maxVisible = maxVisible;


    }





    /**


     * Show the menu.


     * @see #popdown


     */


    public void popup(Widget parent, int x, int y) {


	popup(parent, x, y, preferredSize().width);


    }





    /**


     * Show the menu, fixed width.


     * @see #popdown


     */


    public void popup(Widget parent, int x, int y, int width) {


	add(container);


	dragged = false;


	invalidate();


	super.popup(parent, x, y);


	int  popupHeight = 0;


	if (maxVisible > 0) {


	    for (int i = 0; i < getItemCount() && i < maxVisible; i++) {


		PopupMenuItemWidget item = getItem(i);


		Dimension d = item.preferredSize();


		popupHeight += d.height;


	    }


	    popupHeight += 2;


	} else {


	    popupHeight = preferredSize().height;


	}


	resize(width, popupHeight);


	fit();


	if (container.vert != null) {


	    container.vert.setHilite(new Color(232, 232, 232));


	}


    }





    /**


     * Fit the menu in the visible area of the parent window.


     */


    public void fit() {


	int  orgY = this.y;


	super.fit();


	if (y < orgY) {


	    int  yChange = orgY - y;


	    resize(width, height - yChange);


	    move(x, orgY);


	    if (width == preferredSize().width) {


		resize(width + container.SCROLLSIZ, height);


	    }


	}


    }





    /**


     * Add a menu item.


     * @see #select


     */


    public void add(String label) {


	add(new PopupMenuItemWidget(label));


    }





    /**


     * Add a menu item by giving the menu item directly.


     * @see #select


     */


    public void add(PopupMenuItemWidget item) {


	if (item == null) {


	    System.err.println("ERROR: add(PopupMenuItemWidget), null item specified");


	    return;


	}


	container.content.add(item);


    }





    /**


     * Layout the menu items.


     */


    public void layout() {


	// layout the scrolling container


	((GroupWidget)container.content).setLineMode(NONE);


	container.reshape(0, 0, width, height);


	container.setFillMode(SCROLLBAR);


	container.setHorzmode(NEVER);


	container.setVertmode(OPTIONAL);


	Dimension  pref = preferredSize();


	container.setContHeight(pref.height);


	container.setContWidth(pref.width);





	// layout the items


	for (int i = 0, y = 1 ; i < getItemCount() ; i++) {


	    PopupMenuItemWidget item = getItem(i);


	    Dimension d = item.preferredSize();


	    item.reshape(1, y, width-2, d.height);


	    y += d.height;


	}


    }





    /**


     * Compute the best size for this menu.


     */


    public Dimension preferredSize() {


	int w = 0, h = 0;


	for (int i = 0; i < getItemCount(); i++) {


	    PopupMenuItemWidget item = getItem(i);


	    Dimension d = item.preferredSize();


	    w = Math.max(w, d.width);


	    h += d.height;


	}


	return new Dimension(w+2, h+2);


    }





    /**


     * Get the number of items in this menu.


     */


    public int getItemCount() {


	return container.content.nwidgets;


    }





    /**


     * Get the item with the given index.


     */


    public PopupMenuItemWidget getItem(int index) {


	return (PopupMenuItemWidget)container.content.widgets[index];


    }





    /**


     * The currently selected item.


     * If none is selected, null is returned.


     * @see #current


     */


    public PopupMenuItemWidget getCurrentItem() {


	return current;


    }





    /**


     * Select next item.


     * @see #current


     */


    public void nextItem() {


	int current = indexOf(this.current);


	if (current >= getItemCount() - 1) {


	    select(0);


	} else {


	    select(current + 1);


	    if (getItem(current + 1).disabled) {


	        nextItem();


	    }


	}


    }





    /**


     * Select previous item.


     * @see #current


     */


    public void previousItem() {


	int  current = indexOf(this.current);


	if (current <= 0) {


	    select(getItemCount() - 1);


	} else {


	    select(current - 1);


	    if (getItem(current - 1).disabled) {


	        previousItem();


	    }


	}


    }





    /**


     * Return index of given item. Returns -1 if item is not found.


     * @see #select


     */


    public int indexOf(PopupMenuItemWidget item) {


	for (int i = 0 ; i < getItemCount() ; i++) {


	    if (getItem(i) == item) {


		return i;


	    }


	}


	return -1;


    }





    /**


     * Set the current item to the index-th item, with index in [0 .. getItemCount()].


     * If an invalid number is given, the first item becomes the current one.


     * @see #current


     * @see #indexOf


     * @see #add


     */


    public void select(int index) {


    	index = Math.max(0, Math.min(index, getItemCount()-1));


	if (current != null) {


	    current.select(false);


	    current = null;


	}


	current = getItem(index);


	current.select(true);


	container.focusContent(current.x, current.y, current.width, current.height);


    }





    /**


     * Hide the PopupMenu.


     * @see #popup


     */


    public void popdown() {


	poppingDown = true;


	super.popdown();


	remove(container);


	dragged = false;


	mouseUpPopDown = false;


	if (owner != null) {


	    owner.requestFocus();


	}


    }





    /**


     * Handle events.


     */


    public boolean handleEvent(Event evt) {


	switch (evt.id) {


	    case Event.LOST_FOCUS:


		if (!poppingDown) {


		    popdown();


		}


		poppingDown = false;


		break;





	    case Event.KEY_ACTION:


		switch (evt.key) {


		    case Event.UP:


			previousItem();


			return true;





		    case Event.DOWN:


			nextItem();


			return true;


		}


		break;





	    case Event.KEY_PRESS:


		switch (evt.key) {


		    case '\n':


			if (current != null) {


			    current.action();


			    current.select(false);


			    current = null;


			}


			popdown();


			return true;





		    case '\t':


			// user cannot switch focus using tab


			return true;





		    case 27:


			// user pressed the Esc key


			popdown();


			return true;


		}


		break;





	    case Event.MOUSE_DOWN:


		if (!dragged) {


		    popdown();


		}


		return true;





	    case Event.MOUSE_MOVE:


	    case Event.MOUSE_DRAG:


		if (evt.x >= 0 && evt.x <= this.width - 1 &&


			evt.y >= 0  && evt.y <= this.height - 1) {


		    Widget w = locateWidget(evt.x + x, evt.y + y);


		    PopupMenuItemWidget item = null;


		    if (w instanceof PopupMenuItemWidget) {


			item = (PopupMenuItemWidget) w;


		    }


		    if (item != current) {


			if (current != null) {


			    current.select(false);


			}


			current = item;


			if (current != null) {


			    current.select(true);


			}


		    }


		    dragged = true;


		} else {


		    if (current != null) {


			current.select(false);


			current = null;


		    }


		    dragged = false;


		}


		return true;





	    case Event.MOUSE_UP:


		if (dragged) {


		    if (current != null && !current.isDisabled()) {


			current.action();


			current.select(false);


			current = null;


		    }


		    popdown();


		    return false;


		}





		if (mouseUpPopDown) {


		    popdown();


		} else {


		    dragged = true;


		    mouseUpPopDown = true;


		}


		return true;


	}


	return super.handleEvent(evt);


    }





    /**


     * Supports input focus.


     */


    public boolean focusInterest() {


	return !disabled;


    }


}


