/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * 
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org 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 Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sd.hxx"

#include "taskpane/ControlContainer.hxx"

#include "taskpane/TaskPaneTreeNode.hxx"

#include <vcl/window.hxx>
#include <vcl/svapp.hxx>

namespace sd { namespace toolpanel {


ControlContainer::ControlContainer (TreeNode* pNode)
    : mpNode(pNode),
      mnActiveControlIndex((sal_uInt32)-1),
      mbMultiSelection(false)
{
}




ControlContainer::~ControlContainer (void)
{
    // Set mpNode to NULL so that no one calls it from now on.
    mpNode = NULL;
    DeleteChildren();
}




void ControlContainer::DeleteChildren (void)
{
    // Deleting the children may lead to calls back to the container.  To
    // prevent the container from accessing the just deleted children, the
    // maControlList member is first cleared (by transferring its content to
    // a local list) before the children are destroyed.
    ControlList maList;
    maList.swap(maControlList);
    ControlList::iterator I;
    ControlList::iterator Iend (maList.end());
    for (I=maList.begin(); I!=Iend; ++I)
        delete *I;

    if (mpNode != NULL)
        mpNode->FireStateChangeEvent(EID_ALL_CHILDREN_REMOVED);
}



sal_uInt32 ControlContainer::AddControl (::std::auto_ptr<TreeNode> pControl)
{
    ::osl::MutexGuard aGuard (maMutex);

    pControl->GetWindow()->Show();
    sal_uInt32 nIndex = maControlList.size();
    maControlList.push_back (pControl.get());
    pControl.release();

    ListHasChanged ();

    if (mpNode != NULL)
        mpNode->FireStateChangeEvent(EID_CHILD_ADDED, pControl.get());

    return nIndex;
}




void ControlContainer::SetExpansionState (
    UINT32 nIndex,
    ExpansionState aState)
{
    ::osl::MutexGuard aGuard (maMutex);
    
    bool bResizeNecessary (false);

    if (mbMultiSelection)
    {
        TreeNode* pControl = GetControl(nIndex);
        switch (aState)
        {
            case ES_TOGGLE:
                bResizeNecessary = pControl->Expand( ! pControl->IsExpanded());
                break;

            case ES_EXPAND:
                bResizeNecessary = pControl->Expand(true);
                break;

            case ES_COLLAPSE:
                bResizeNecessary = pControl->Expand(false);
                break;
        }
    }
    else
    {
        // When bExpansionState is true then the control to expand is the
        // one with the given index.  If bExpansionState is false and the
        // given index points to the active control then then following
        // control (in cyclic order) it is expanded.  When there is only one
        // control then that is always expanded.
        do
        {
            // Ignore a call with an invalid index. (The seperate comparison
            // with -1 is not strictly necessary but it is here just in
            // case.)
            if (nIndex>=GetControlCount() || nIndex==(sal_uInt32)-1)
                break;

            bool bExpand;
            switch (aState)
            {
                default:
                case ES_TOGGLE:
                    bExpand = ! GetControl(nIndex)->IsExpanded();
                    break;

                case ES_EXPAND:
                    bExpand = true;
                    break;

                case ES_COLLAPSE:
                    bExpand = false;
                    break;
            }
            if (bExpand)
            {
                // Make the specified control the active one and expand it.
                mnActiveControlIndex = nIndex;
            }
            else
            {
                if (nIndex == mnActiveControlIndex)
                {
                    // We have to determine a new active control since the
                    // current one is about to be collapsed.  Choose the
                    // previous one for the last and the next one for all
                    // other.
                    if (mnActiveControlIndex+1 == GetControlCount())
                        mnActiveControlIndex
                            = GetPreviousIndex(mnActiveControlIndex);
                    else
                        mnActiveControlIndex
                            = GetNextIndex (mnActiveControlIndex);
                }
            }

            // Update the expansion state of all controls.
            for (UINT32 i=0; i<GetControlCount(); i=GetNextIndex(i))
            {
                TreeNode* pControl = GetControl(i);
                bResizeNecessary |= pControl->Expand(i == mnActiveControlIndex);
            }
        }
        while (false);
    }

    if (bResizeNecessary && mpNode != NULL)
        mpNode->RequestResize();
}




void ControlContainer::SetExpansionState (
    TreeNode* pControl,
    ExpansionState aState)
{
    SetExpansionState (GetControlIndex(pControl), aState);
}




sal_uInt32 ControlContainer::GetControlIndex (TreeNode* pControlToExpand) const
{
    sal_uInt32 nIndex;
    for (nIndex=0; nIndex<GetControlCount(); nIndex++)
    {
        TreeNode* pControl = GetControl(nIndex);
        if (pControl == pControlToExpand)
            break;
    }
    return nIndex;
}




sal_uInt32 ControlContainer::GetActiveControlIndex (void) const
{
    return mnActiveControlIndex;
}




void ControlContainer::ListHasChanged (void)
{
}




sal_uInt32 ControlContainer::GetControlCount (void) const
{
    return maControlList.size();
}




sal_uInt32 ControlContainer::GetVisibleControlCount (void) const
{
    sal_uInt32 nCount (0);

    UINT32 nIndex;
    sal_uInt32 nAllCount (maControlList.size());
    for (nIndex=0; nIndex<nAllCount; nIndex=GetNextIndex(nIndex,true))
    {
        if (maControlList[nIndex]->GetWindow()->IsVisible())
            nCount += 1;
    }
    
    return nCount;
}




TreeNode* ControlContainer::GetControl (sal_uInt32 nIndex) const
{
    if (nIndex<maControlList.size() && nIndex!=(sal_uInt32)-1)
        return maControlList[nIndex];
    else
        return NULL;
}




sal_uInt32 ControlContainer::GetPreviousIndex (
    sal_uInt32 nIndex,
    bool bIncludeHidden,
    bool bCycle) const
{
    sal_uInt32 nCandidate (nIndex);

    while (true)
    {
        if (nCandidate==0)
            if ( ! bCycle)
            {
                // We have reached the head of the list of controls and must
                // not cycle to its end.
                nCandidate = maControlList.size();
                break;
            }
            else
            {
                // Cycle to the end of the list.
                nCandidate = maControlList.size() - 1;
            }
        else
            // Go to the regular predecessor.
            nCandidate -= 1;

        if (nCandidate == nIndex)
        {
            // Made one full loop and found no valid control.
            nCandidate = maControlList.size();
            break;
        }
        else if (bIncludeHidden)
        {
            // Return the candidate index regardless of whether the control
            // is hidden or not.
            break;
        }
        else if (maControlList[nCandidate]->GetWindow()->IsVisible())
        {
            // Found a visible control.
            break;
        }

        // The candidate does not meet our constraints so do one more loop.
    }
    
    return nCandidate;
}



sal_uInt32 ControlContainer::GetNextIndex (
    sal_uInt32 nIndex,
    bool bIncludeHidden,
    bool bCycle) const
{
    sal_uInt32 nCandidate (nIndex);

    while (true)
    {
        // Go to the regular successor.
        nCandidate += 1;
        if (nCandidate==maControlList.size())
        {
            if ( ! bCycle)
            {
                // We have reached the end of the list of controls and must
                // not cycle to its head.
                break;
            }
            else
            {
                // Cycle to the head of the list.
                nCandidate = 0;
            }
        }

        if (nCandidate == nIndex)
        {
            // Made one full loop and found no valid control.
            nCandidate = maControlList.size();
            break;
        }
        else if (bIncludeHidden)
        {
            // Return the candidate index regardless of whether the control
            // is hidden or not.
            break;
        }
        else if (maControlList[nCandidate]->GetWindow()->IsVisible())
        {
            // Found a visible control.
            break;
        }

        // The candidate does not meet our constraints so do one more loop.
    }
    
    return nCandidate;
}




sal_uInt32 ControlContainer::GetFirstIndex (bool bIncludeHidden)
{
    sal_uInt32 nIndex = 0;

    if (maControlList.size() == 0)
    {
        // The list is empty so there is no first element.
        nIndex = maControlList.size();
    }
    else if ( ! bIncludeHidden
        && ! maControlList[nIndex]->GetWindow()->IsVisible())
    {
        // The first element is not visible.  Go the next visible one.
        nIndex = GetNextIndex (nIndex, bIncludeHidden, false);
    }

    return nIndex;
}




sal_uInt32 ControlContainer::GetLastIndex (bool bIncludeHidden)
{
    sal_uInt32 nIndex;

    if (maControlList.size() == 0)
    {
        // The list is empty so there is no last element.
        nIndex = maControlList.size();
    }
    else
    {
        nIndex = maControlList.size() - 1;
        if ( ! bIncludeHidden
            && ! maControlList[nIndex]->GetWindow()->IsVisible())
        {
            // The last element is not visible.  Go the previous visible one.
            nIndex = GetPreviousIndex (nIndex, bIncludeHidden, false);
        }
    }
    return nIndex;
}




void ControlContainer::SetMultiSelection (bool bFlag)
{
    mbMultiSelection = bFlag;
}




void ControlContainer::SetVisibilityState (
    sal_uInt32 nControlIndex,
    VisibilityState aState)
{
    TreeNode* pControl = GetControl (nControlIndex);
    if (pControl != NULL)
    {
        bool bShow;
        switch (aState)
        {
            default:
            case VS_TOGGLE:
                bShow = ! pControl->IsShowing();
                break;
            case VS_SHOW:
                bShow = true;
                break;
            case VS_HIDE:
                bShow = false;
                break;
        }

        bool bControlWasExpanded = pControl->IsExpanded();
        if (bShow != pControl->IsShowing())
        {
            pControl->Show (bShow);

            if (bShow)
            {
                // If we just turned on the first control then expand it, too.
                // If we turned on another control collapse it.
                if (GetVisibleControlCount() == 1)
                    SetExpansionState (nControlIndex, ES_EXPAND);
                else
                    SetExpansionState (nControlIndex, ES_COLLAPSE);
            }
            else
            {
                if (GetVisibleControlCount() > 0)
                {
                    if (bControlWasExpanded)
                    {
                        // We turned off an expanded control.  Make sure that
                        // one of the still visible ones is expanded.
                        sal_uInt32 nIndex = GetNextIndex(
                            nControlIndex,
                            false,
                            false);
                        if (nIndex == GetControlCount())
                            nIndex = GetPreviousIndex(
                                nControlIndex,
                                false,
                                false);
                        SetExpansionState (nIndex, ES_EXPAND);
                    }
                }
            }

            if (mpNode != NULL)
                mpNode->RequestResize();
        }
    }
}




} } // end of namespace ::sd::toolpanel

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
