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

   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.

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

#include "../../../../juce_core/basics/juce_StandardHeader.h"

BEGIN_JUCE_NAMESPACE

#include "juce_MenuBarComponent.h"
#include "../juce_Desktop.h"
#include "../lookandfeel/juce_LookAndFeel.h"


//==============================================================================
class DummyMenuComponent  : public Component
{
    DummyMenuComponent (const DummyMenuComponent&);
    const DummyMenuComponent& operator= (const DummyMenuComponent&);

public:
    DummyMenuComponent()    {}
    ~DummyMenuComponent()   {}

    void inputAttemptWhenModal()
    {
        exitModalState (0);
    }
};

//==============================================================================
MenuBarComponent::MenuBarComponent (MenuBarModel* model_)
    : model (model_),
      itemUnderMouse (-1),
      currentPopupIndex (-1),
      indexToShowAgain (-1),
      inModalState (false),
      currentPopup (0)
{
    setRepaintsOnMouseActivity (true);
    setWantsKeyboardFocus (false);
}

MenuBarComponent::~MenuBarComponent()
{
    Desktop::getInstance().removeGlobalMouseListener (this);
    deleteAndZero (currentPopup);
}

void MenuBarComponent::setModel (MenuBarModel* const newModel)
{
    if (model != newModel)
    {
        model = newModel;
        repaint();
    }
}


//==============================================================================
void MenuBarComponent::paint (Graphics& g)
{
    const bool isMouseOverBar = currentPopupIndex >= 0 || itemUnderMouse >= 0 || isMouseOver();

    getLookAndFeel().drawMenuBarBackground (g,
                                            getWidth(),
                                            getHeight(),
                                            isMouseOverBar,
                                            *this);

    if (model != 0)
    {
        for (int i = 0; i < menuNames.size(); ++i)
        {
            Graphics g2 (g);
            g2.setOrigin (xPositions [i], 0);
            g2.reduceClipRegion (0, 0, xPositions[i + 1] - xPositions[i], getHeight());

            getLookAndFeel().drawMenuBarItem (g2,
                                              xPositions[i + 1] - xPositions[i],
                                              getHeight(),
                                              i,
                                              menuNames[i],
                                              i == itemUnderMouse,
                                              i == currentPopupIndex,
                                              isMouseOverBar,
                                              *this);
        }
    }
}

void MenuBarComponent::resized()
{
    updateItems();
}

void MenuBarComponent::updateItems()
{
    if (model != 0)
        menuNames = model->getMenuBarNames (this);
    else
        menuNames.clear();

    xPositions.clear();
    int x = 2;
    xPositions.add (x);

    for (int i = 0; i < menuNames.size(); ++i)
    {
        x += getLookAndFeel().getMenuBarItemWidth (*this, i, menuNames[i]);

        xPositions.add (x);
    }
}

void MenuBarComponent::refreshMenuItems()
{
    updateItems();
    repaint();
}

int MenuBarComponent::getItemAt (const int x, const int y)
{
    for (int i = 0; i < xPositions.size(); ++i)
        if (x >= xPositions[i] && x < xPositions[i + 1])
            return reallyContains (x, y, true) ? i : -1;

    return -1;
}

void MenuBarComponent::updateItemUnderMouse (int x, int y)
{
    const int newItem = getItemAt (x, y);

    if (itemUnderMouse != newItem)
    {
        itemUnderMouse = newItem;
        repaint();
    }
}

void MenuBarComponent::hideCurrentMenu()
{
    deleteAndZero (currentPopup);
    repaint();
}

void MenuBarComponent::showMenu (int index)
{
    if (index != currentPopupIndex)
    {
        if (inModalState)
        {
            hideCurrentMenu();
            indexToShowAgain = index;
            return;
        }

        indexToShowAgain = -1;
        currentPopupIndex = -1;
        deleteAndZero (currentPopup);
        updateItems();

        Component* const prevFocused = getCurrentlyFocusedComponent();

        ComponentDeletionWatcher* prevCompDeletionChecker = 0;
        if (prevCompDeletionChecker != 0)
            prevCompDeletionChecker = new ComponentDeletionWatcher (prevFocused);

        ComponentDeletionWatcher deletionChecker (this);

        enterModalState();
        inModalState = true;

        Desktop::getInstance().addGlobalMouseListener (this);

        for (;;)
        {
            const int x = getScreenX() + xPositions [itemUnderMouse];
            const int w = xPositions [itemUnderMouse + 1] - xPositions [itemUnderMouse];

            currentPopupIndex = itemUnderMouse;
            indexToShowAgain = -1;
            repaint();

            if (itemUnderMouse >= 0 && itemUnderMouse < menuNames.size())
            {
                PopupMenu m (model->getMenuForIndex (this,
                                                     itemUnderMouse,
                                                     menuNames [itemUnderMouse]));

                currentPopup = m.createMenuComponent (x, getScreenY(),
                                                      w, getHeight(),
                                                      0, true, this);
            }

            if (currentPopup == 0)
            {
                currentPopup = new DummyMenuComponent();
                addAndMakeVisible (currentPopup);
            }

            const int result = currentPopup->runModalLoop();

            if (deletionChecker.hasBeenDeleted())
            {
                if (prevCompDeletionChecker != 0)
                    delete prevCompDeletionChecker;

                return;
            }

            const int lastPopupIndex = currentPopupIndex;
            deleteAndZero (currentPopup);
            currentPopupIndex = -1;

            if (result != 0)
            {
                topLevelIndexClicked = lastPopupIndex;
                postCommandMessage (result);
                break;
            }
            else if (indexToShowAgain >= 0)
            {
                updateItems();
                repaint();
                itemUnderMouse = indexToShowAgain;

                if (itemUnderMouse < 0 || itemUnderMouse >= menuNames.size())
                    break;
            }
            else
            {
                break;
            }
        }

        Desktop::getInstance().removeGlobalMouseListener (this);

        inModalState = false;
        exitModalState (0);

        if (prevCompDeletionChecker != 0)
        {
            if (! prevCompDeletionChecker->hasBeenDeleted())
                prevFocused->grabKeyboardFocus();

            delete prevCompDeletionChecker;
        }

        updateItemUnderMouse (getMouseXRelative(), getMouseYRelative());
        repaint();
    }
}

void MenuBarComponent::handleCommandMessage (int commandId)
{
    if (model != 0)
        model->menuItemSelected (this, commandId, topLevelIndexClicked);
}

//==============================================================================
void MenuBarComponent::mouseEnter (const MouseEvent& e)
{
    if (e.component == this)
        updateItemUnderMouse (e.x, e.y);
}

void MenuBarComponent::mouseExit (const MouseEvent& e)
{
    if (e.component == this)
        updateItemUnderMouse (e.x, e.y);
}

void MenuBarComponent::mouseDown (const MouseEvent& e)
{
    const MouseEvent e2 (e.getEventRelativeTo (this));

    if (currentPopupIndex < 0)
    {
        jassert (e.component == this);

        updateItemUnderMouse (e2.x, e2.y);
        repaint();

        currentPopupIndex = -2;
        showMenu (itemUnderMouse);
    }
}

void MenuBarComponent::mouseDrag (const MouseEvent& e)
{
    const MouseEvent e2 (e.getEventRelativeTo (this));

    const int item = getItemAt (e2.x, e2.y);

    if (item >= 0)
        showMenu (item);
}

void MenuBarComponent::mouseUp (const MouseEvent& e)
{
    const MouseEvent e2 (e.getEventRelativeTo (this));

    updateItemUnderMouse (e2.x, e2.y);
    repaint();

    if (itemUnderMouse < 0 && dynamic_cast <DummyMenuComponent*> (currentPopup) != 0)
        hideCurrentMenu();
}

void MenuBarComponent::mouseMove (const MouseEvent& e)
{
    const MouseEvent e2 (e.getEventRelativeTo (this));

    if (lastMouseX != e2.x || lastMouseY != e2.y)
    {
        if (currentPopupIndex >= 0)
        {
            const int item = getItemAt (e2.x, e2.y);

            if (item >= 0)
                showMenu (item);
        }
        else
        {
            updateItemUnderMouse (e2.x, e2.y);
        }

        lastMouseX = e2.x;
        lastMouseY = e2.y;
    }
}

void MenuBarComponent::keyPressed (const KeyPress& key)
{
    const int numMenus = menuNames.size();
    const int currentIndex = jlimit (0, menuNames.size() - 1, currentPopupIndex);

    if (key.isKeyCode (KeyPress::leftKey))
    {
        showMenu ((currentIndex + numMenus - 1) % numMenus);
    }
    else if (key.isKeyCode (KeyPress::rightKey))
    {
        showMenu ((currentIndex + 1) % numMenus);
    }
}

void MenuBarComponent::inputAttemptWhenModal()
{
    hideCurrentMenu();
}


END_JUCE_NAMESPACE
