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

   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_Label.h"


//==============================================================================
Label::Label (const String& componentName,
              const String& labelText)
    : Component (componentName),
      text (labelText),
      font (15.0f),
      justification (Justification::centredLeft),
      editor (0),
      textListeners (2),
      ownerComponent (0),
      deletionWatcher (0),
      editSingleClick (false),
      editDoubleClick (false),
      lossOfFocusDiscardsChanges (false)
{
    setColours (Colours::black,
                Colours::transparentBlack,
                Colours::black,
                Colours::transparentBlack,
                Colour (0x401111ee),
                Colours::transparentBlack);

    setFocusOrder (8);
}

Label::~Label()
{
    if (ownerComponent != 0 && ! deletionWatcher->hasBeenDeleted())
        ownerComponent->removeComponentListener (this);

    deleteAndZero (deletionWatcher);

    if (editor != 0)
        delete editor;
}

//==============================================================================
void Label::setText (const String& newText,
                     const bool broadcastChangeMessage)
{
    hideEditor (true);

    if (text != newText)
    {
        text = newText;

        if (broadcastChangeMessage)
            sendChangeMessage (this);

        repaint();

        if (ownerComponent != 0 && ! deletionWatcher->hasBeenDeleted())
            componentMovedOrResized (*ownerComponent, true, true);
    }
}

const String Label::getText (const bool returnActiveEditorContents) const
{
    return (returnActiveEditorContents && isBeingEdited())
                ? editor->getText()
                : text;
}

void Label::setFont (const Font& newFont)
{
    font = newFont;
    repaint();
}

const Font Label::getFont() const
{
    return font;
}

void Label::setColours (const Colour& textColour_,
                        const Colour& backgroundColour_,
                        const Colour& editorTextColour_,
                        const Colour& editorBackgroundColour_,
                        const Colour& editorHighlightColour_,
                        const Colour& outlineColour_)
{
    if (textColour != textColour_
        || backgroundColour != backgroundColour_
        || editorTextColour != editorTextColour_
        || editorBackgroundColour != editorBackgroundColour_
        || editorHighlightColour != editorHighlightColour_
        || outlineColour != outlineColour_)
    {
        textColour = textColour_;
        backgroundColour = backgroundColour_;
        editorTextColour = editorTextColour_;
        editorBackgroundColour = editorBackgroundColour_;
        editorHighlightColour = editorHighlightColour_;
        outlineColour = outlineColour_;

        repaint();
    }
}

void Label::setEditable (const bool editOnSingleClick,
                         const bool editOnDoubleClick,
                         const bool lossOfFocusDiscardsChanges_)
{
    editSingleClick = editOnSingleClick;
    editDoubleClick = editOnDoubleClick;
    lossOfFocusDiscardsChanges = lossOfFocusDiscardsChanges_;
    setWantsKeyboardFocus (editOnSingleClick || editOnDoubleClick);
}

void Label::setJustificationType (const Justification& justification_)
{
    justification = justification_;
    repaint();
}

//==============================================================================
void Label::attachToComponent (Component* owner,
                               const bool onLeft)
{
    if (ownerComponent != 0 && ! deletionWatcher->hasBeenDeleted())
        ownerComponent->removeComponentListener (this);

    deleteAndZero (deletionWatcher);
    ownerComponent = owner;

    leftOfOwnerComp = onLeft;

    if (ownerComponent != 0)
    {
        deletionWatcher = new ComponentDeletionWatcher (owner);

        setVisible (owner->isVisible());
        ownerComponent->addComponentListener (this);
        componentParentHierarchyChanged (*ownerComponent);
        componentMovedOrResized (*ownerComponent, true, true);
    }
}

void Label::componentMovedOrResized (Component& component,
                                     bool /*wasMoved*/,
                                     bool /*wasResized*/)
{
    if (leftOfOwnerComp)
    {
        setSize (jmin (getFont().getStringWidth (text) + 8, component.getX()),
                 component.getHeight());

        setTopRightPosition (component.getX(), component.getY());
    }
    else
    {
        setSize (component.getWidth(),
                 8 + roundFloatToInt (getFont().getHeight()));

        setTopLeftPosition (component.getX(), component.getY() - getHeight());
    }
}

void Label::componentParentHierarchyChanged (Component& component)
{
    if (component.getParentComponent() != 0)
        component.getParentComponent()->addChildComponent (this);
}

void Label::componentVisibilityChanged (Component& component)
{
    setVisible (component.isVisible());
}

//==============================================================================
void Label::textWasEdited()
{
}

void Label::showEditor()
{
    if (editor == 0)
    {
        addAndMakeVisible (editor = createEditorComponent());
        editor->grabKeyboardFocus();
        editor->setHighlightedRegion (0, text.length());
        editor->addListener (this);
        editor->setColours (editorTextColour, editorBackgroundColour, editorHighlightColour, outlineColour);

        resized();
        repaint();
    }
}

bool Label::updateFromTextEditorContents()
{
    jassert (editor != 0);
    const String newText (editor->getText());

    if (text != newText)
    {
        text = newText;

        sendChangeMessage (this);
        repaint();

        if (ownerComponent != 0 && ! deletionWatcher->hasBeenDeleted())
            componentMovedOrResized (*ownerComponent, true, true);

        return true;
    }

    return false;
}

void Label::hideEditor (const bool discardCurrentEditorContents)
{
    if (editor != 0)
    {
        const bool changed = (! discardCurrentEditorContents)
                               && updateFromTextEditorContents();

        deleteAndZero (editor);
        repaint();

        if (changed)
            textWasEdited();
    }
}

bool Label::isBeingEdited() const
{
    return editor != 0;
}

TextEditor* Label::createEditorComponent()
{
    TextEditor* const tc = new TextEditor (getName());
    tc->setFont (font);
    tc->setColours (editorTextColour, editorBackgroundColour, editorHighlightColour);
    tc->setText (getText());
    tc->addListener (this);
    return tc;
}

//==============================================================================
void Label::paint (Graphics& g)
{
    if (editor == 0)
    {
        g.fillAll (backgroundColour);

        const float alpha = isEnabled() ? 1.0f : 0.5f;

        g.setColour (textColour.withMultipliedAlpha (alpha));
        g.setFont (font);
        g.drawFittedText (text,
                          3, 1, getWidth() - 6, getHeight() - 2,
                          justification,
                          jmax (1, (int) (getHeight() / font.getHeight())));

        g.setColour (outlineColour.withMultipliedAlpha (alpha));
        g.drawRect (0, 0, getWidth(), getHeight());
    }
    else if (isEnabled())
    {
        g.setColour (editorBackgroundColour);
        g.drawRect (0, 0, getWidth(), getHeight());

        g.setColour (outlineColour);
        g.drawRect (0, 0, getWidth(), getHeight());
    }
}

void Label::mouseUp (const MouseEvent& e)
{
    if (editSingleClick
         && e.mouseWasClicked()
         && contains (e.x, e.y)
         && ! e.mods.isPopupMenu())
    {
        showEditor();
    }
}

void Label::mouseDoubleClick (const MouseEvent& e)
{
    if (editDoubleClick && ! e.mods.isPopupMenu())
        showEditor();
}

void Label::resized()
{
    if (editor != 0)
        editor->setBoundsInset (BorderSize (0));
}

void Label::focusGained (FocusChangeType cause)
{
    if (editSingleClick && cause == focusChangedByTabKey)
        showEditor();
}

void Label::enablementChanged()
{
    repaint();
}

void Label::addTextEditorListener (TextEditorListener* const newListener)
{
    textListeners.addIfNotAlreadyThere (newListener);
}

void Label::removeTextEditorListener (TextEditorListener* const listenerToRemove)
{
    textListeners.removeValue (listenerToRemove);
}

void Label::textEditorTextChanged (TextEditor& ed)
{
    if (editor != 0)
    {
        jassert (&ed == editor);
        const ComponentDeletionWatcher editorWatcher (editor);

        for (int i = textListeners.size(); --i >= 0;)
        {
            TextEditorListener* const tl = (TextEditorListener*) textListeners[i];

            if (tl != 0)
            {
                tl->textEditorTextChanged (ed);

                if (editorWatcher.hasBeenDeleted())
                    return;
            }
        }

        if (! (editor->hasKeyboardFocus (true) || isCurrentlyBlockedByAnotherModalComponent()))
        {
            if (lossOfFocusDiscardsChanges)
                textEditorEscapeKeyPressed (ed);
            else
                textEditorReturnKeyPressed (ed);
        }
    }
}

void Label::textEditorReturnKeyPressed (TextEditor& ed)
{
    if (editor != 0)
    {
        jassert (&ed == editor);
        const ComponentDeletionWatcher editorWatcher (editor);
        const bool changed = updateFromTextEditorContents();

        if (editorWatcher.hasBeenDeleted())
            return;

        for (int i = textListeners.size(); --i >= 0;)
        {
            TextEditorListener* const tl = (TextEditorListener*) textListeners[i];

            if (tl != 0)
            {
                tl->textEditorReturnKeyPressed (ed);

                if (editorWatcher.hasBeenDeleted())
                    return;
            }
        }

        hideEditor (true);

        if (changed)
            textWasEdited();
    }
}

void Label::textEditorEscapeKeyPressed (TextEditor& ed)
{
    if (editor != 0)
    {
        jassert (&ed == editor);
        const ComponentDeletionWatcher editorWatcher (editor);
        editor->setText (text, false);

        for (int i = textListeners.size(); --i >= 0;)
        {
            TextEditorListener* const tl = (TextEditorListener*) textListeners[i];

            if (tl != 0)
            {
                tl->textEditorEscapeKeyPressed (ed);

                if (i > 0 && editorWatcher.hasBeenDeleted())
                    return;
            }
        }

        hideEditor (true);
    }
}

void Label::textEditorFocusLost (TextEditor& editor)
{
    textEditorTextChanged (editor);
}


END_JUCE_NAMESPACE
