/*
  ==============================================================================

   This file is part of the JUCE library - "Jules' Utility Class Extensions"
   Copyright 2004-6 by Raw Material Software ltd.

  ------------------------------------------------------------------------------

   JUCE can be redistributed and/or modified under the terms of the
   GNU General Public License, as published by the Free Software Foundation;
   either version 2 of the License, or (at your option) any later version.

   JUCE is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with JUCE; if not, visit www.gnu.org/licenses or write to the
   Free Software Foundation, Inc., 59 Temple Place, Suite 330, 
   Boston, MA 02111-1307 USA

  ------------------------------------------------------------------------------

   If you'd like to release a closed-source product which uses JUCE, commercial
   licenses are also available: visit www.rawmaterialsoftware.com/juce for
   more information.

  ==============================================================================
*/

#ifndef __JUCE_POPUPMENU_JUCEHEADER__
#define __JUCE_POPUPMENU_JUCEHEADER__

#include "juce_PopupMenuCustomComponent.h"
#include "../keyboard/juce_KeyPressMappingSet.h"


//==============================================================================
/** Creates and displays a popup-menu.

    To show a popup-menu, you create one of these, add some items to it, then
    call its show() method, which returns the id of the item the user selects.

    E.g. @code
    void MyWidget::mouseDown (const MouseEvent& e)
    {
        PopupMenu m;
        m.addItem (1, "item 1");
        m.addItem (2, "item 2");

        const int result = m.show();

        if (result == 0)
        {
            // user dismissed the menu without picking anything
        }
        else if (result == 1)
        {
            // user picked item 1
        }
        else if (result == 2)
        {
            // user picked item 2
        }
    }
    @endcode

    Submenus are easy too: @code

    void MyWidget::mouseDown (const MouseEvent& e)
    {
        PopupMenu subMenu;
        subMenu.addItem (1, "item 1");
        subMenu.addItem (2, "item 2");

        PopupMenu mainMenu;
        mainMenu.addItem (3, "item 3");
        mainMenu.addSubMenu ("other choices", subMenu);

        const int result = m.show();

        ...etc
    }
    @endcode
*/
class JUCE_API  PopupMenu
{
public:
    //==============================================================================
    /** Creates an empty popup menu. */
    PopupMenu();

    /** Creates a copy of another menu. */
    PopupMenu (const PopupMenu& other);

    /** Destructor. */
    ~PopupMenu();

    /** Copies this menu from another one. */
    const PopupMenu& operator= (const PopupMenu& other);

    //==============================================================================
    /** Resets the menu, removing all its items. */
    void clear();

    /** Appends a new text item for this menu to show.

        @param itemResultId     the number that will be returned from the show() method
                                if the user picks this item. The value should never be
                                zero, because that's used to indicate that the user didn't
                                select anything.
        @param itemText         the text to show.
        @param isActive         if false, the item will be shown 'greyed-out' and can't be
                                picked
        @param isTicked         if true, the item will be shown with a tick next to it
        @param iconToUse        if this is non-zero, it should be an image that will be
                                displayed to the left of the item. This method will take its
                                own copy of the image passed-in, so there's no need to keep
                                it hanging around.
        @param keyMappingSetToTrigger     If this is non-zero and the keyMappingCommandUID value is also
                                non-zero, then if the item is chosen, the KeyPressMappingSet will
                                be automatically told to perform the given command. This is handy,
                                because it saves you having to manually handle the result of the
                                menu's show() method. Also, if there's a KeyPress associated with the
                                command, it will be displayed at the right-hand side of the menu
                                item.
        @param keyMappingCommandUID The ID of the command to link this menu item to (see the
                                keyMappingSetToTrigger parameter)

        @see addSeparator, addColouredItem, addCustomItem, addSubMenu
    */
    void addItem (const int itemResultId,
                  const String& itemText,
                  const bool isActive = true,
                  const bool isTicked = false,
                  const Image* const iconToUse = 0,
                  KeyPressMappingSet* const keyMappingSetToTrigger = 0,
                  const int keyMappingCommandUID = 0);

    /** Appends a text item with a special colour.

        This is the same as addItem(), but specifies a colour to use for the
        text, which will override the default colours that are used by the
        current look-and-feel. See addItem() for a description of the parameters.
    */
    void addColouredItem (const int itemResultId,
                          const String& itemText,
                          const Colour& itemTextColour,
                          const bool isActive = true,
                          const bool isTicked = false,
                          const Image* const iconToUse = 0,
                          KeyPressMappingSet* const keyMappingSetToTrigger = 0,
                          const int keyMappingCommandUID = 0);

    /** Appends a custom menu item.

        This will add a user-defined component to use as a menu item. The component
        passed in will be deleted by this menu when it's no longer needed.

        @see PopupMenuCustomComponent
    */
    void addCustomItem (const int itemResultId,
                        PopupMenuCustomComponent* const customComponent);

    /** Appends a custom menu item that can't be used to trigger a result.

        This will add a user-defined component to use as a menu item. Unlike the
        addCustomItem() method that takes a PopupMenuCustomComponent, this version
        can't trigger a result from it, so doesn't take a menu ID. It also doesn't
        delete the component when it's finished, so it's the caller's responsibility
        to manage the component that is passed-in.

        @see PopupMenuCustomComponent
    */
    void addCustomItem (Component* customComponent, int idealWidth, int idealHeight);

    /** Appends a sub-menu.

        If the menu that's passed in is empty, it will appear as an inactive item.
    */
    void addSubMenu (const String& subMenuName,
                     const PopupMenu& subMenu,
                     const bool isActive = true);

    /** Appends a separator to the menu, to help break it up into sections.

        The menu class is smart enough not to display separators at the top or bottom
        of the menu, and it will replace mutliple adjacent separators with a single
        one, so your code can be quite free and easy about adding these, and it'll
        always look ok.
    */
    void addSeparator();

    /** Returns the number of items that the menu currently contains.

        (This doesn't count separators).
    */
    int getNumItems() const;

    //==============================================================================
    /** Displays the menu and waits for the user to pick something.

        This will display the menu modally, and return the ID of the item that the
        user picks. If they click somewhere off the menu to get rid of it without
        choosing anything, this will return 0.

        The current location of the mouse will be used as the position to show the
        menu - to explicitly set the menu's position, use showAt() instead. Depending
        on where this point is on the screen, the menu will appear above, below or
        to the side of the point.

        @param itemIdThatMustBeVisible  if you set this to the ID of one of the menu items,
                                        then when the menu first appears, it will make sure
                                        that this item is visible. So if the menu has too many
                                        items to fit on the screen, it will be scrolled to a
                                        position where this item is visible.
        @see showAt
    */
    int show (const int itemIdThatMustBeVisible = 0);


    /** Displays the menu at a specific location.

        This is the same as show(), but uses a specific location (in global screen
        co-ordinates) rather than the current mouse position.

        @see show()
    */
    int showAt (const int screenX,
                const int screenY,
                const int itemIdThatMustBeVisible = 0);

    /** Displays the menu as if it's attached to a component such as a button.

        This is similar to showAt(), but will position it next to the given component, e.g.
        so that the menu's edge is aligned with that of the component. This is intended for
        things like buttons that trigger a pop-up menu.
    */
    int showAt (Component* componentToAttachTo,
                const int itemIdThatMustBeVisible = 0);

    //==============================================================================
    /** Specifies a look-and-feel for the menu and any sub-menus that it has.

        This can be called before show() if you need a customised menu. Be careful
        not to delete the LookAndFeel object before the menu has been deleted.
    */
    void setLookAndFeel (LookAndFeel* const newLookAndFeel);


    //==============================================================================
    juce_UseDebuggingNewOperator

private:
    friend class PopupMenuWindow;
    VoidArray items;
    LookAndFeel* lookAndFeel;

    int showMenu (const int x, const int y, const int w, const int h,
                  const int itemIdThatMustBeVisible,
                  const bool alignToRectangle);

    friend class MenuBarComponent;
    Component* createMenuComponent (const int x, const int y, const int w, const int h,
                                    const int itemIdThatMustBeVisible,
                                    const bool alignToRectangle,
                                    Component* menuBarComponent);
};

#endif   // __JUCE_POPUPMENU_JUCEHEADER__
